mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-10 19:14:58 -06:00
* 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:
commit
dc1ed8f256
19 changed files with 307 additions and 127 deletions
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)',
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 ===========================
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue