* rust: support migration of HPET device

* target/i386/hvf: fix compilation errors
 * target/i386/tcg: fix some interrupt shadow cases
 * hw/char/serial: remove unused prog_if compat property
 * rust: centralize config in workspace root
 * monitor: fix race on exiting QEMU
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmgVQzkUHHBib256aW5p
 QHJlZGhhdC5jb20ACgkQv/vSX3jHroOR8Af/Tke7kRZQyvoKURaKpVOBgP91fTQu
 IKwmX1OYe9JMPBwZV5g/++2HSaAddDzkFq90gmgTY+hpvRE3kDWOA86QtDRP4LKa
 Oq3yW48yrFiRZBAxERgRxRCsEvzlPC3cAEqCQd4fTL+cW6NVorbj4x/tQcALb47V
 cgXXVp59TW4lJk7nJUjd0mCFK1qEoIbZuuBgMn32K+fpBV/UghcoImT2giMeM24Y
 WW3olrLA9UN2fh5da7923WUvA9mSjnE0Yfdk6eKC3nCzlgMKktofwKHilm0tA6xA
 7sJbwYTDSB9QxgnNw3WvAFAOMapJmedaSNorZdmcxCss7ed0s8hV8am9vQ==
 =LFS/
 -----END PGP SIGNATURE-----

Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging

* rust: support migration of HPET device
* target/i386/hvf: fix compilation errors
* target/i386/tcg: fix some interrupt shadow cases
* hw/char/serial: remove unused prog_if compat property
* rust: centralize config in workspace root
* monitor: fix race on exiting QEMU

# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmgVQzkUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroOR8Af/Tke7kRZQyvoKURaKpVOBgP91fTQu
# IKwmX1OYe9JMPBwZV5g/++2HSaAddDzkFq90gmgTY+hpvRE3kDWOA86QtDRP4LKa
# Oq3yW48yrFiRZBAxERgRxRCsEvzlPC3cAEqCQd4fTL+cW6NVorbj4x/tQcALb47V
# cgXXVp59TW4lJk7nJUjd0mCFK1qEoIbZuuBgMn32K+fpBV/UghcoImT2giMeM24Y
# WW3olrLA9UN2fh5da7923WUvA9mSjnE0Yfdk6eKC3nCzlgMKktofwKHilm0tA6xA
# 7sJbwYTDSB9QxgnNw3WvAFAOMapJmedaSNorZdmcxCss7ed0s8hV8am9vQ==
# =LFS/
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 02 May 2025 18:12:09 EDT
# gpg:                using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg:                issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>" [full]
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* tag 'for-upstream' of https://gitlab.com/bonzini/qemu:
  monitor: don't wake up qmp_dispatcher_co coroutine upon cleanup
  rust: centralize config in workspace root
  hw/char/serial: Remove unused prog_if compat property
  target/i386: do not block singlestep for STI
  target/i386: do not trigger IRQ shadow for LSS
  target/i386/hvf: fix a compilation error
  target/i386/emulate: remove rflags leftovers
  rust/hpet: Support migration
  rust/timer: Define NANOSECONDS_PER_SECOND binding as u64
  rust/vmstate_test: Test varray with num field wrapped in BqlCell
  rust: assertions: Support index field wrapped in BqlCell
  vmstate: support varray for vmstate_clock!
  rust/vmstate: Add support for field_exists checks

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2025-05-05 11:26:47 -04:00
commit dc1ed8f256
19 changed files with 307 additions and 127 deletions

View file

@ -153,8 +153,7 @@ QEMU includes four crates:
.. [#issues] The ``pl011`` crate is synchronized with ``hw/char/pl011.c`` .. [#issues] The ``pl011`` crate is synchronized with ``hw/char/pl011.c``
as of commit 02b1f7f61928. The ``hpet`` crate is synchronized as of as of commit 02b1f7f61928. The ``hpet`` crate is synchronized as of
commit f32352ff9e. Both are lacking tracing functionality; ``hpet`` commit 1433e38cc8. Both are lacking tracing functionality.
is also lacking support for migration.
This section explains how to work with them. This section explains how to work with them.

View file

@ -51,7 +51,6 @@ typedef struct PCIDivaSerialState {
SerialState state[PCI_SERIAL_MAX_PORTS]; SerialState state[PCI_SERIAL_MAX_PORTS];
uint32_t level[PCI_SERIAL_MAX_PORTS]; uint32_t level[PCI_SERIAL_MAX_PORTS];
qemu_irq *irqs; qemu_irq *irqs;
uint8_t prog_if;
bool disable; bool disable;
} PCIDivaSerialState; } PCIDivaSerialState;
@ -124,8 +123,8 @@ static void diva_pci_realize(PCIDevice *dev, Error **errp)
size_t i, offset = 0; size_t i, offset = 0;
size_t portmask = di.omask; size_t portmask = di.omask;
pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */
pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; pci->dev.config[PCI_INTERRUPT_PIN] = 1;
memory_region_init(&pci->membar, OBJECT(pci), "serial_ports", 4096); memory_region_init(&pci->membar, OBJECT(pci), "serial_ports", 4096);
pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &pci->membar); pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &pci->membar);
pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci, di.nports); pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci, di.nports);
@ -178,7 +177,6 @@ static const Property diva_serial_properties[] = {
DEFINE_PROP_CHR("chardev2", PCIDivaSerialState, state[1].chr), DEFINE_PROP_CHR("chardev2", PCIDivaSerialState, state[1].chr),
DEFINE_PROP_CHR("chardev3", PCIDivaSerialState, state[2].chr), DEFINE_PROP_CHR("chardev3", PCIDivaSerialState, state[2].chr),
DEFINE_PROP_CHR("chardev4", PCIDivaSerialState, state[3].chr), DEFINE_PROP_CHR("chardev4", PCIDivaSerialState, state[3].chr),
DEFINE_PROP_UINT8("prog_if", PCIDivaSerialState, prog_if, 0x02),
DEFINE_PROP_UINT32("subvendor", PCIDivaSerialState, subvendor, DEFINE_PROP_UINT32("subvendor", PCIDivaSerialState, subvendor,
PCI_DEVICE_ID_HP_DIVA_TOSCA1), PCI_DEVICE_ID_HP_DIVA_TOSCA1),
}; };

View file

@ -46,7 +46,6 @@ typedef struct PCIMultiSerialState {
SerialState state[PCI_SERIAL_MAX_PORTS]; SerialState state[PCI_SERIAL_MAX_PORTS];
uint32_t level[PCI_SERIAL_MAX_PORTS]; uint32_t level[PCI_SERIAL_MAX_PORTS];
IRQState irqs[PCI_SERIAL_MAX_PORTS]; IRQState irqs[PCI_SERIAL_MAX_PORTS];
uint8_t prog_if;
} PCIMultiSerialState; } PCIMultiSerialState;
static void multi_serial_pci_exit(PCIDevice *dev) static void multi_serial_pci_exit(PCIDevice *dev)
@ -97,8 +96,8 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp)
SerialState *s; SerialState *s;
size_t i, nports = multi_serial_get_port_count(pc); size_t i, nports = multi_serial_get_port_count(pc);
pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */
pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; pci->dev.config[PCI_INTERRUPT_PIN] = 1;
memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nports); memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nports);
pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar); pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar);
@ -133,7 +132,6 @@ static const VMStateDescription vmstate_pci_multi_serial = {
static const Property multi_2x_serial_pci_properties[] = { static const Property multi_2x_serial_pci_properties[] = {
DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr),
DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr),
DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02),
}; };
static const Property multi_4x_serial_pci_properties[] = { static const Property multi_4x_serial_pci_properties[] = {
@ -141,7 +139,6 @@ static const Property multi_4x_serial_pci_properties[] = {
DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr),
DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].chr), DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].chr),
DEFINE_PROP_CHR("chardev4", PCIMultiSerialState, state[3].chr), DEFINE_PROP_CHR("chardev4", PCIMultiSerialState, state[3].chr),
DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02),
}; };
static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, static void multi_2x_serial_pci_class_initfn(ObjectClass *klass,

View file

@ -38,7 +38,6 @@
struct PCISerialState { struct PCISerialState {
PCIDevice dev; PCIDevice dev;
SerialState state; SerialState state;
uint8_t prog_if;
}; };
#define TYPE_PCI_SERIAL "pci-serial" #define TYPE_PCI_SERIAL "pci-serial"
@ -53,8 +52,8 @@ static void serial_pci_realize(PCIDevice *dev, Error **errp)
return; return;
} }
pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */
pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; pci->dev.config[PCI_INTERRUPT_PIN] = 1;
s->irq = pci_allocate_irq(&pci->dev); s->irq = pci_allocate_irq(&pci->dev);
memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, "serial", 8); memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, "serial", 8);
@ -81,10 +80,6 @@ static const VMStateDescription vmstate_pci_serial = {
} }
}; };
static const Property serial_pci_properties[] = {
DEFINE_PROP_UINT8("prog_if", PCISerialState, prog_if, 0x02),
};
static void serial_pci_class_initfn(ObjectClass *klass, const void *data) static void serial_pci_class_initfn(ObjectClass *klass, const void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass);
@ -96,7 +91,6 @@ static void serial_pci_class_initfn(ObjectClass *klass, const void *data)
pc->revision = 1; pc->revision = 1;
pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
dc->vmsd = &vmstate_pci_serial; dc->vmsd = &vmstate_pci_serial;
device_class_set_props(dc, serial_pci_properties);
set_bit(DEVICE_CATEGORY_INPUT, dc->categories); set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
} }

View file

@ -356,7 +356,8 @@ void qmp_dispatcher_co_wake(void)
/* Write request before reading qmp_dispatcher_co_busy. */ /* Write request before reading qmp_dispatcher_co_busy. */
smp_mb__before_rmw(); smp_mb__before_rmw();
if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) { if (!qatomic_xchg(&qmp_dispatcher_co_busy, true) &&
qatomic_read(&qmp_dispatcher_co)) {
aio_co_wake(qmp_dispatcher_co); aio_co_wake(qmp_dispatcher_co);
} }
} }

View file

@ -7,6 +7,13 @@ members = [
"hw/timer/hpet", "hw/timer/hpet",
] ]
[workspace.package]
edition = "2021"
homepage = "https://www.qemu.org"
license = "GPL-2.0-or-later"
repository = "https://gitlab.com/qemu-project/qemu/"
rust-version = "1.63.0"
[workspace.lints.rust] [workspace.lints.rust]
unexpected_cfgs = { level = "deny", check-cfg = [ unexpected_cfgs = { level = "deny", check-cfg = [
'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', 'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',

View file

@ -1,15 +1,16 @@
[package] [package]
name = "pl011" name = "pl011"
version = "0.1.0" version = "0.1.0"
edition = "2021"
authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"] authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
license = "GPL-2.0-or-later"
description = "pl011 device model for QEMU" description = "pl011 device model for QEMU"
resolver = "2" resolver = "2"
publish = false publish = false
keywords = []
categories = [] edition.workspace = true
rust-version = "1.63.0" homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib] [lib]
crate-type = ["staticlib"] crate-type = ["staticlib"]

View file

@ -1,11 +1,14 @@
[package] [package]
name = "hpet" name = "hpet"
version = "0.1.0" version = "0.1.0"
edition = "2021"
authors = ["Zhao Liu <zhao1.liu@intel.com>"] authors = ["Zhao Liu <zhao1.liu@intel.com>"]
license = "GPL-2.0-or-later"
description = "IA-PC High Precision Event Timer emulation in Rust" description = "IA-PC High Precision Event Timer emulation in Rust"
rust-version = "1.63.0"
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib] [lib]
crate-type = ["staticlib"] crate-type = ["staticlib"]

View file

@ -4,6 +4,7 @@
use std::{ use std::{
ffi::CStr, ffi::CStr,
os::raw::{c_int, c_void},
pin::Pin, pin::Pin,
ptr::{addr_of_mut, null_mut, NonNull}, ptr::{addr_of_mut, null_mut, NonNull},
slice::from_ref, slice::from_ref,
@ -25,7 +26,10 @@ use qemu_api::{
qom::{ObjectImpl, ObjectType, ParentField}, qom::{ObjectImpl, ObjectType, ParentField},
qom_isa, qom_isa,
sysbus::{SysBusDevice, SysBusDeviceImpl}, sysbus::{SysBusDevice, SysBusDeviceImpl},
timer::{Timer, CLOCK_VIRTUAL}, timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
vmstate::VMStateDescription,
vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
zeroable::Zeroable,
}; };
use crate::fw_cfg::HPETFwConfig; use crate::fw_cfg::HPETFwConfig;
@ -561,6 +565,7 @@ pub struct HPETState {
#[doc(alias = "timer")] #[doc(alias = "timer")]
timers: [BqlRefCell<HPETTimer>; HPET_MAX_TIMERS as usize], timers: [BqlRefCell<HPETTimer>; HPET_MAX_TIMERS as usize],
num_timers: BqlCell<u8>, num_timers: BqlCell<u8>,
num_timers_save: BqlCell<u8>,
/// Instance id (HPET timer block ID). /// Instance id (HPET timer block ID).
hpet_id: BqlCell<usize>, hpet_id: BqlCell<usize>,
@ -839,6 +844,49 @@ impl HPETState {
} }
} }
} }
fn pre_save(&self) -> i32 {
if self.is_hpet_enabled() {
self.counter.set(self.get_ticks());
}
/*
* The number of timers must match on source and destination, but it was
* also added to the migration stream. Check that it matches the value
* that was configured.
*/
self.num_timers_save.set(self.num_timers.get());
0
}
fn post_load(&self, _version_id: u8) -> i32 {
for timer in self.timers.iter().take(self.get_num_timers()) {
let mut t = timer.borrow_mut();
t.cmp64 = t.calculate_cmp64(t.get_state().counter.get(), t.cmp);
t.last = CLOCK_VIRTUAL.get_ns() - NANOSECONDS_PER_SECOND;
}
// Recalculate the offset between the main counter and guest time
if !self.hpet_offset_saved {
self.hpet_offset
.set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns());
}
0
}
fn is_rtc_irq_level_needed(&self) -> bool {
self.rtc_irq_level.get() != 0
}
fn is_offset_needed(&self) -> bool {
self.is_hpet_enabled() && self.hpet_offset_saved
}
fn validate_num_timers(&self, _version_id: u8) -> bool {
self.num_timers.get() == self.num_timers_save.get()
}
} }
qom_isa!(HPETState: SysBusDevice, DeviceState, Object); qom_isa!(HPETState: SysBusDevice, DeviceState, Object);
@ -895,11 +943,107 @@ qemu_api::declare_properties! {
), ),
} }
unsafe extern "C" fn hpet_rtc_irq_level_needed(opaque: *mut c_void) -> bool {
// SAFETY:
// the pointer is convertible to a reference
let state: &HPETState = unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_ref() };
state.is_rtc_irq_level_needed()
}
unsafe extern "C" fn hpet_offset_needed(opaque: *mut c_void) -> bool {
// SAFETY:
// the pointer is convertible to a reference
let state: &HPETState = unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_ref() };
state.is_offset_needed()
}
unsafe extern "C" fn hpet_pre_save(opaque: *mut c_void) -> c_int {
// SAFETY:
// the pointer is convertible to a reference
let state: &mut HPETState =
unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_mut() };
state.pre_save() as c_int
}
unsafe extern "C" fn hpet_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
// SAFETY:
// the pointer is convertible to a reference
let state: &mut HPETState =
unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_mut() };
let version: u8 = version_id.try_into().unwrap();
state.post_load(version) as c_int
}
static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescription {
name: c_str!("hpet/rtc_irq_level").as_ptr(),
version_id: 1,
minimum_version_id: 1,
needed: Some(hpet_rtc_irq_level_needed),
fields: vmstate_fields! {
vmstate_of!(HPETState, rtc_irq_level),
},
..Zeroable::ZERO
};
static VMSTATE_HPET_OFFSET: VMStateDescription = VMStateDescription {
name: c_str!("hpet/offset").as_ptr(),
version_id: 1,
minimum_version_id: 1,
needed: Some(hpet_offset_needed),
fields: vmstate_fields! {
vmstate_of!(HPETState, hpet_offset),
},
..Zeroable::ZERO
};
static VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescription {
name: c_str!("hpet_timer").as_ptr(),
version_id: 1,
minimum_version_id: 1,
fields: vmstate_fields! {
vmstate_of!(HPETTimer, index),
vmstate_of!(HPETTimer, config),
vmstate_of!(HPETTimer, cmp),
vmstate_of!(HPETTimer, fsb),
vmstate_of!(HPETTimer, period),
vmstate_of!(HPETTimer, wrap_flag),
vmstate_of!(HPETTimer, qemu_timer),
},
..Zeroable::ZERO
};
const VALIDATE_TIMERS_NAME: &CStr = c_str!("num_timers must match");
static VMSTATE_HPET: VMStateDescription = VMStateDescription {
name: c_str!("hpet").as_ptr(),
version_id: 2,
minimum_version_id: 1,
pre_save: Some(hpet_pre_save),
post_load: Some(hpet_post_load),
fields: vmstate_fields! {
vmstate_of!(HPETState, config),
vmstate_of!(HPETState, int_status),
vmstate_of!(HPETState, counter),
vmstate_of!(HPETState, num_timers_save).with_version_id(2),
vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers),
vmstate_struct!(HPETState, timers[0 .. num_timers], &VMSTATE_HPET_TIMER, BqlRefCell<HPETTimer>, HPETState::validate_num_timers).with_version_id(0),
},
subsections: vmstate_subsections! {
VMSTATE_HPET_RTC_IRQ_LEVEL,
VMSTATE_HPET_OFFSET,
},
..Zeroable::ZERO
};
impl DeviceImpl for HPETState { impl DeviceImpl for HPETState {
fn properties() -> &'static [Property] { fn properties() -> &'static [Property] {
&HPET_PROPERTIES &HPET_PROPERTIES
} }
fn vmsd() -> Option<&'static VMStateDescription> {
Some(&VMSTATE_HPET)
}
const REALIZE: Option<fn(&Self)> = Some(Self::realize); const REALIZE: Option<fn(&Self)> = Some(Self::realize);
} }

View file

@ -1,15 +1,16 @@
[package] [package]
name = "qemu_api_macros" name = "qemu_api_macros"
version = "0.1.0" version = "0.1.0"
edition = "2021"
authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"] authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
license = "GPL-2.0-or-later"
description = "Rust bindings for QEMU - Utility macros" description = "Rust bindings for QEMU - Utility macros"
resolver = "2" resolver = "2"
publish = false publish = false
keywords = []
categories = [] edition.workspace = true
rust-version = "1.63.0" homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib] [lib]
proc-macro = true proc-macro = true

View file

@ -1,18 +1,17 @@
[package] [package]
name = "qemu_api" name = "qemu_api"
version = "0.1.0" version = "0.1.0"
edition = "2021"
authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"] authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
license = "GPL-2.0-or-later"
readme = "README.md"
homepage = "https://www.qemu.org"
description = "Rust bindings for QEMU" description = "Rust bindings for QEMU"
repository = "https://gitlab.com/qemu-project/qemu/" readme = "README.md"
resolver = "2" resolver = "2"
publish = false publish = false
keywords = []
categories = [] edition.workspace = true
rust-version = "1.63.0" homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[dependencies] [dependencies]
qemu_api_macros = { path = "../qemu-api-macros" } qemu_api_macros = { path = "../qemu-api-macros" }

View file

@ -78,33 +78,26 @@ macro_rules! assert_same_type {
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! assert_field_type { macro_rules! assert_field_type {
($t:ty, $i:tt, $ti:ty) => { (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => {
const _: () = { const _: () = {
#[allow(unused)] #[allow(unused)]
fn assert_field_type(v: $t) { fn assert_field_type($param_name: &$t) {
fn types_must_be_equal<T, U>(_: T) fn types_must_be_equal<T, U>(_: &T)
where where
T: $crate::assertions::EqType<Itself = U>, T: $crate::assertions::EqType<Itself = U>,
{ {
} }
types_must_be_equal::<_, $ti>(v.$i); types_must_be_equal::<_, $ti>(&$($field)*);
} }
}; };
}; };
($t:ty, $i:tt, $ti:ty) => {
$crate::assert_field_type!(@internal v, $ti, $t, v.$i);
};
($t:ty, $i:tt, $ti:ty, num = $num:ident) => { ($t:ty, $i:tt, $ti:ty, num = $num:ident) => {
const _: () = { $crate::assert_field_type!(@internal v, $ti, $t, v.$i[0]);
#[allow(unused)]
fn assert_field_type(v: $t) {
fn types_must_be_equal<T, U>(_: T)
where
T: $crate::assertions::EqType<Itself = U>,
{
}
let index: usize = v.$num.try_into().unwrap();
types_must_be_equal::<_, &$ti>(&v.$i[index]);
}
};
}; };
} }

View file

@ -121,3 +121,5 @@ impl ClockType {
pub const CLOCK_VIRTUAL: ClockType = ClockType { pub const CLOCK_VIRTUAL: ClockType = ClockType {
id: QEMUClockType::QEMU_CLOCK_VIRTUAL, id: QEMUClockType::QEMU_CLOCK_VIRTUAL,
}; };
pub const NANOSECONDS_PER_SECOND: u64 = 1000000000;

View file

@ -200,13 +200,14 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
/// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this. /// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this.
#[macro_export] #[macro_export]
macro_rules! vmstate_of { macro_rules! vmstate_of {
($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(,)?) => { ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => {
$crate::bindings::VMStateField { $crate::bindings::VMStateField {
name: ::core::concat!(::core::stringify!($field_name), "\0") name: ::core::concat!(::core::stringify!($field_name), "\0")
.as_bytes() .as_bytes()
.as_ptr() as *const ::std::os::raw::c_char, .as_ptr() as *const ::std::os::raw::c_char,
offset: $crate::offset_of!($struct_name, $field_name), offset: $crate::offset_of!($struct_name, $field_name),
$(num_offset: $crate::offset_of!($struct_name, $num),)? $(num_offset: $crate::offset_of!($struct_name, $num),)?
$(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
// The calls to `call_func_with_field!` are the magic that // The calls to `call_func_with_field!` are the magic that
// computes most of the VMStateField from the type of the field. // computes most of the VMStateField from the type of the field.
info: $crate::info_enum_to_ref!($crate::call_func_with_field!( info: $crate::info_enum_to_ref!($crate::call_func_with_field!(
@ -435,6 +436,38 @@ macro_rules! vmstate_unused {
}}; }};
} }
pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), bool>>(
opaque: *mut c_void,
version_id: c_int,
) -> bool {
// SAFETY: the opaque was passed as a reference to `T`.
let owner: &T = unsafe { &*(opaque.cast::<T>()) };
let version: u8 = version_id.try_into().unwrap();
F::call((owner, version))
}
pub type VMSFieldExistCb = unsafe extern "C" fn(
opaque: *mut std::os::raw::c_void,
version_id: std::os::raw::c_int,
) -> bool;
#[macro_export]
macro_rules! vmstate_exist_fn {
($struct_name:ty, $test_fn:expr) => {{
const fn test_cb_builder__<T, F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>>(
_phantom: ::core::marker::PhantomData<F>,
) -> $crate::vmstate::VMSFieldExistCb {
let _: () = F::ASSERT_IS_SOME;
$crate::vmstate::rust_vms_test_field_exists::<T, F>
}
const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> {
::core::marker::PhantomData
}
Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn)))
}};
}
// FIXME: including the `vmsd` field in a `const` is not possible without // FIXME: including the `vmsd` field in a `const` is not possible without
// the const_refs_static feature (stabilized in Rust 1.83.0). Without it, // the const_refs_static feature (stabilized in Rust 1.83.0). Without it,
// it is not possible to use VMS_STRUCT in a transparent manner using // it is not possible to use VMS_STRUCT in a transparent manner using
@ -445,7 +478,7 @@ macro_rules! vmstate_unused {
#[doc(alias = "VMSTATE_STRUCT")] #[doc(alias = "VMSTATE_STRUCT")]
#[macro_export] #[macro_export]
macro_rules! vmstate_struct { macro_rules! vmstate_struct {
($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(,)?) => { ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(, $test_fn:expr)? $(,)?) => {
$crate::bindings::VMStateField { $crate::bindings::VMStateField {
name: ::core::concat!(::core::stringify!($field_name), "\0") name: ::core::concat!(::core::stringify!($field_name), "\0")
.as_bytes() .as_bytes()
@ -458,6 +491,7 @@ macro_rules! vmstate_struct {
size: ::core::mem::size_of::<$type>(), size: ::core::mem::size_of::<$type>(),
flags: $crate::bindings::VMStateFlags::VMS_STRUCT, flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
vmsd: $vmsd, vmsd: $vmsd,
$(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
..$crate::zeroable::Zeroable::ZERO ..$crate::zeroable::Zeroable::ZERO
} $(.with_varray_flag_unchecked( } $(.with_varray_flag_unchecked(
$crate::call_func_with_field!( $crate::call_func_with_field!(
@ -473,7 +507,7 @@ macro_rules! vmstate_struct {
#[doc(alias = "VMSTATE_CLOCK")] #[doc(alias = "VMSTATE_CLOCK")]
#[macro_export] #[macro_export]
macro_rules! vmstate_clock { macro_rules! vmstate_clock {
($struct_name:ty, $field_name:ident) => {{ ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?) => {{
$crate::bindings::VMStateField { $crate::bindings::VMStateField {
name: ::core::concat!(::core::stringify!($field_name), "\0") name: ::core::concat!(::core::stringify!($field_name), "\0")
.as_bytes() .as_bytes()
@ -482,7 +516,7 @@ macro_rules! vmstate_clock {
$crate::assert_field_type!( $crate::assert_field_type!(
$struct_name, $struct_name,
$field_name, $field_name,
$crate::qom::Owned<$crate::qdev::Clock> $crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
); );
$crate::offset_of!($struct_name, $field_name) $crate::offset_of!($struct_name, $field_name)
}, },
@ -493,7 +527,14 @@ macro_rules! vmstate_clock {
), ),
vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) },
..$crate::zeroable::Zeroable::ZERO ..$crate::zeroable::Zeroable::ZERO
} } $(.with_varray_flag_unchecked(
$crate::call_func_with_field!(
$crate::vmstate::vmstate_varray_flag,
$struct_name,
$num
)
)
$(.with_varray_multiply($factor))?)?
}}; }};
} }
@ -514,43 +555,13 @@ macro_rules! vmstate_fields {
}} }}
} }
pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), bool>>(
opaque: *mut c_void,
version_id: c_int,
) -> bool {
let owner: &T = unsafe { &*(opaque.cast::<T>()) };
let version: u8 = version_id.try_into().unwrap();
// SAFETY: the opaque was passed as a reference to `T`.
F::call((owner, version))
}
pub type VMSFieldExistCb = unsafe extern "C" fn(
opaque: *mut std::os::raw::c_void,
version_id: std::os::raw::c_int,
) -> bool;
#[doc(alias = "VMSTATE_VALIDATE")] #[doc(alias = "VMSTATE_VALIDATE")]
#[macro_export] #[macro_export]
macro_rules! vmstate_validate { macro_rules! vmstate_validate {
($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => { ($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => {
$crate::bindings::VMStateField { $crate::bindings::VMStateField {
name: ::std::ffi::CStr::as_ptr($test_name), name: ::std::ffi::CStr::as_ptr($test_name),
field_exists: { field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),
const fn test_cb_builder__<
T,
F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>,
>(
_phantom: ::core::marker::PhantomData<F>,
) -> $crate::vmstate::VMSFieldExistCb {
let _: () = F::ASSERT_IS_SOME;
$crate::vmstate::rust_vms_test_field_exists::<T, F>
}
const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> {
::core::marker::PhantomData
}
Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn)))
},
flags: $crate::bindings::VMStateFlags( flags: $crate::bindings::VMStateFlags(
$crate::bindings::VMStateFlags::VMS_MUST_EXIST.0 $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0
| $crate::bindings::VMStateFlags::VMS_ARRAY.0, | $crate::bindings::VMStateFlags::VMS_ARRAY.0,

View file

@ -28,7 +28,7 @@ const FOO_ARRAY_MAX: usize = 3;
// - VMSTATE_VARRAY_UINT16_UNSAFE // - VMSTATE_VARRAY_UINT16_UNSAFE
// - VMSTATE_VARRAY_MULTIPLY // - VMSTATE_VARRAY_MULTIPLY
#[repr(C)] #[repr(C)]
#[derive(qemu_api_macros::offsets)] #[derive(Default, qemu_api_macros::offsets)]
struct FooA { struct FooA {
arr: [u8; FOO_ARRAY_MAX], arr: [u8; FOO_ARRAY_MAX],
num: u16, num: u16,
@ -147,8 +147,9 @@ fn test_vmstate_varray_multiply() {
// - VMSTATE_STRUCT_VARRAY_UINT8 // - VMSTATE_STRUCT_VARRAY_UINT8
// - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 // - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32
// - VMSTATE_ARRAY // - VMSTATE_ARRAY
// - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn
#[repr(C)] #[repr(C)]
#[derive(qemu_api_macros::offsets)] #[derive(Default, qemu_api_macros::offsets)]
struct FooB { struct FooB {
arr_a: [FooA; FOO_ARRAY_MAX], arr_a: [FooA; FOO_ARRAY_MAX],
num_a: u8, num_a: u8,
@ -158,6 +159,12 @@ struct FooB {
val: bool, val: bool,
// FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test.
arr_i64: [i64; FOO_ARRAY_MAX], arr_i64: [i64; FOO_ARRAY_MAX],
arr_a_wrap: [FooA; FOO_ARRAY_MAX],
num_a_wrap: BqlCell<u32>,
}
fn validate_foob(_state: &FooB, _version_id: u8) -> bool {
true
} }
static VMSTATE_FOOB: VMStateDescription = VMStateDescription { static VMSTATE_FOOB: VMStateDescription = VMStateDescription {
@ -170,13 +177,14 @@ static VMSTATE_FOOB: VMStateDescription = VMStateDescription {
vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1),
vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2),
vmstate_of!(FooB, arr_i64), vmstate_of!(FooB, arr_i64),
vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob),
}, },
..Zeroable::ZERO ..Zeroable::ZERO
}; };
#[test] #[test]
fn test_vmstate_bool_v() { fn test_vmstate_bool_v() {
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
// 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V)
assert_eq!( assert_eq!(
@ -196,7 +204,7 @@ fn test_vmstate_bool_v() {
#[test] #[test]
fn test_vmstate_uint64() { fn test_vmstate_uint64() {
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
// 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64)
assert_eq!( assert_eq!(
@ -216,7 +224,7 @@ fn test_vmstate_uint64() {
#[test] #[test]
fn test_vmstate_struct_varray_uint8() { fn test_vmstate_struct_varray_uint8() {
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
// 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to
// VMSTATE_STRUCT_VARRAY_UINT8) // VMSTATE_STRUCT_VARRAY_UINT8)
@ -240,7 +248,7 @@ fn test_vmstate_struct_varray_uint8() {
#[test] #[test]
fn test_vmstate_struct_varray_uint32_multiply() { fn test_vmstate_struct_varray_uint32_multiply() {
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
// 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to
// (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32)
@ -266,7 +274,7 @@ fn test_vmstate_struct_varray_uint32_multiply() {
#[test] #[test]
fn test_vmstate_macro_array() { fn test_vmstate_macro_array() {
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
// 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to
// VMSTATE_ARRAY) // VMSTATE_ARRAY)
@ -283,9 +291,26 @@ fn test_vmstate_macro_array() {
assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY);
assert!(foo_fields[4].vmsd.is_null()); assert!(foo_fields[4].vmsd.is_null());
assert!(foo_fields[4].field_exists.is_none()); assert!(foo_fields[4].field_exists.is_none());
}
#[test]
fn test_vmstate_struct_varray_uint8_wrapper() {
let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
let mut foo_b: FooB = Default::default();
let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::<c_void>();
// 6th VMStateField ("arr_a_wrap") in VMSTATE_FOOB (corresponding to
// VMSTATE_STRUCT_VARRAY_UINT8). Other fields are checked in
// test_vmstate_struct_varray_uint8.
assert_eq!(
unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(),
b"arr_a_wrap\0"
);
assert_eq!(foo_fields[5].num_offset, 228);
assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) });
// The last VMStateField in VMSTATE_FOOB. // The last VMStateField in VMSTATE_FOOB.
assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END); assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END);
} }
// =========================== Test VMSTATE_FOOC =========================== // =========================== Test VMSTATE_FOOC ===========================

View file

@ -1408,7 +1408,7 @@ struct decode_tbl _2op_inst[] = {
}; };
struct decode_x87_tbl invl_inst_x87 = {0x0, 0, 0, 0, 0, false, false, NULL, struct decode_x87_tbl invl_inst_x87 = {0x0, 0, 0, 0, 0, false, false, NULL,
NULL, decode_invalid, 0}; NULL, decode_invalid};
struct decode_x87_tbl _x87_inst[] = { struct decode_x87_tbl _x87_inst[] = {
{0xd8, 0, 3, X86_DECODE_CMD_FADD, 10, false, false, {0xd8, 0, 3, X86_DECODE_CMD_FADD, 10, false, false,
@ -1456,8 +1456,7 @@ struct decode_x87_tbl _x87_inst[] = {
decode_x87_modrm_st0, NULL, decode_d9_4}, decode_x87_modrm_st0, NULL, decode_d9_4},
{0xd9, 4, 0, X86_DECODE_CMD_INVL, 4, false, false, {0xd9, 4, 0, X86_DECODE_CMD_INVL, 4, false, false,
decode_x87_modrm_bytep, NULL, NULL}, decode_x87_modrm_bytep, NULL, NULL},
{0xd9, 5, 3, X86_DECODE_CMD_FLDxx, 10, false, false, NULL, NULL, NULL, {0xd9, 5, 3, X86_DECODE_CMD_FLDxx, 10, false, false, NULL, NULL, NULL},
RFLAGS_MASK_NONE},
{0xd9, 5, 0, X86_DECODE_CMD_FLDCW, 2, false, false, {0xd9, 5, 0, X86_DECODE_CMD_FLDCW, 2, false, false,
decode_x87_modrm_bytep, NULL, NULL}, decode_x87_modrm_bytep, NULL, NULL},
@ -1478,20 +1477,17 @@ struct decode_x87_tbl _x87_inst[] = {
decode_x87_modrm_st0, NULL}, decode_x87_modrm_st0, NULL},
{0xda, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, {0xda, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0,
decode_x87_modrm_st0, NULL}, decode_x87_modrm_st0, NULL},
{0xda, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, {0xda, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL},
RFLAGS_MASK_NONE},
{0xda, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0, {0xda, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0,
decode_x87_modrm_intp, NULL}, decode_x87_modrm_intp, NULL},
{0xda, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, decode_x87_modrm_st0, {0xda, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, decode_x87_modrm_st0,
decode_decode_x87_modrm_st0, NULL}, decode_decode_x87_modrm_st0, NULL},
{0xda, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0, {0xda, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0,
decode_x87_modrm_intp, NULL}, decode_x87_modrm_intp, NULL},
{0xda, 6, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, {0xda, 6, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL},
RFLAGS_MASK_NONE},
{0xda, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0, {0xda, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0,
decode_x87_modrm_intp, NULL}, decode_x87_modrm_intp, NULL},
{0xda, 7, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, {0xda, 7, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL},
RFLAGS_MASK_NONE},
{0xda, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0, {0xda, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0,
decode_x87_modrm_intp, NULL}, decode_x87_modrm_intp, NULL},
@ -1511,8 +1507,7 @@ struct decode_x87_tbl _x87_inst[] = {
decode_x87_modrm_intp, NULL, NULL}, decode_x87_modrm_intp, NULL, NULL},
{0xdb, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, {0xdb, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL,
decode_db_4}, decode_db_4},
{0xdb, 4, 0, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, {0xdb, 4, 0, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL},
RFLAGS_MASK_NONE},
{0xdb, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, false, {0xdb, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, false,
decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, decode_x87_modrm_st0, decode_x87_modrm_st0, NULL},
{0xdb, 5, 0, X86_DECODE_CMD_FLD, 10, false, false, {0xdb, 5, 0, X86_DECODE_CMD_FLD, 10, false, false,

View file

@ -76,6 +76,7 @@
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/accel.h" #include "qemu/accel.h"
#include "target/i386/cpu.h" #include "target/i386/cpu.h"
#include "exec/target_page.h"
static Error *invtsc_mig_blocker; static Error *invtsc_mig_blocker;

View file

@ -342,7 +342,7 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv
break; break;
case X86_OP_SEG: case X86_OP_SEG:
/* Note that gen_movl_seg takes care of interrupt shadow and TF. */ /* Note that gen_movl_seg takes care of interrupt shadow and TF. */
gen_movl_seg(s, op->n, s->T0); gen_movl_seg(s, op->n, v, op->n == R_SS);
break; break;
case X86_OP_INT: case X86_OP_INT:
if (op->has_ea) { if (op->has_ea) {
@ -2382,7 +2382,7 @@ static void gen_lxx_seg(DisasContext *s, X86DecodedInsn *decode, int seg)
gen_op_ld_v(s, MO_16, s->T1, s->A0); gen_op_ld_v(s, MO_16, s->T1, s->A0);
/* load the segment here to handle exceptions properly */ /* load the segment here to handle exceptions properly */
gen_movl_seg(s, seg, s->T1); gen_movl_seg(s, seg, s->T1, false);
} }
static void gen_LDS(DisasContext *s, X86DecodedInsn *decode) static void gen_LDS(DisasContext *s, X86DecodedInsn *decode)

View file

@ -2026,27 +2026,36 @@ static void gen_op_movl_seg_real(DisasContext *s, X86Seg seg_reg, TCGv seg)
/* move SRC to seg_reg and compute if the CPU state may change. Never /* move SRC to seg_reg and compute if the CPU state may change. Never
call this function with seg_reg == R_CS */ call this function with seg_reg == R_CS */
static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src) static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src, bool inhibit_irq)
{ {
if (PE(s) && !VM86(s)) { if (PE(s) && !VM86(s)) {
TCGv_i32 sel = tcg_temp_new_i32(); TCGv_i32 sel = tcg_temp_new_i32();
tcg_gen_trunc_tl_i32(sel, src); tcg_gen_trunc_tl_i32(sel, src);
gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), sel); gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), sel);
/* abort translation because the addseg value may change or
because ss32 may change. For R_SS, translation must always /* For move to DS/ES/SS, the addseg or ss32 flags may change. */
stop as a special handling must be done to disable hardware if (CODE32(s) && seg_reg < R_FS) {
interrupts for the next instruction */
if (seg_reg == R_SS) {
s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ;
} else if (CODE32(s) && seg_reg < R_FS) {
s->base.is_jmp = DISAS_EOB_NEXT; s->base.is_jmp = DISAS_EOB_NEXT;
} }
} else { } else {
gen_op_movl_seg_real(s, seg_reg, src); gen_op_movl_seg_real(s, seg_reg, src);
if (seg_reg == R_SS) { }
s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ;
} /*
* For MOV or POP to SS (but not LSS) translation must always
* stop as a special handling must be done to disable hardware
* interrupts for the next instruction.
*
* This is the last instruction, so it's okay to overwrite
* HF_TF_MASK; the next TB will start with the flag set.
*
* DISAS_EOB_INHIBIT_IRQ is a superset of DISAS_EOB_NEXT which
* might have been set above.
*/
if (inhibit_irq) {
s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ;
s->flags &= ~HF_TF_MASK;
} }
} }
@ -2297,7 +2306,7 @@ gen_eob(DisasContext *s, int mode)
if (mode == DISAS_EOB_RECHECK_TF) { if (mode == DISAS_EOB_RECHECK_TF) {
gen_helper_rechecking_single_step(tcg_env); gen_helper_rechecking_single_step(tcg_env);
tcg_gen_exit_tb(NULL, 0); tcg_gen_exit_tb(NULL, 0);
} else if ((s->flags & HF_TF_MASK) && mode != DISAS_EOB_INHIBIT_IRQ) { } else if (s->flags & HF_TF_MASK) {
gen_helper_single_step(tcg_env); gen_helper_single_step(tcg_env);
} else if (mode == DISAS_JUMP && } else if (mode == DISAS_JUMP &&
/* give irqs a chance to happen */ /* give irqs a chance to happen */