mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-03 15:53:54 -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``
|
||||
as of commit 02b1f7f61928. The ``hpet`` crate is synchronized as of
|
||||
commit f32352ff9e. Both are lacking tracing functionality; ``hpet``
|
||||
is also lacking support for migration.
|
||||
commit 1433e38cc8. Both are lacking tracing functionality.
|
||||
|
||||
This section explains how to work with them.
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ typedef struct PCIDivaSerialState {
|
|||
SerialState state[PCI_SERIAL_MAX_PORTS];
|
||||
uint32_t level[PCI_SERIAL_MAX_PORTS];
|
||||
qemu_irq *irqs;
|
||||
uint8_t prog_if;
|
||||
bool disable;
|
||||
} PCIDivaSerialState;
|
||||
|
||||
|
@ -124,8 +123,8 @@ static void diva_pci_realize(PCIDevice *dev, Error **errp)
|
|||
size_t i, offset = 0;
|
||||
size_t portmask = di.omask;
|
||||
|
||||
pci->dev.config[PCI_CLASS_PROG] = pci->prog_if;
|
||||
pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
|
||||
pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */
|
||||
pci->dev.config[PCI_INTERRUPT_PIN] = 1;
|
||||
memory_region_init(&pci->membar, OBJECT(pci), "serial_ports", 4096);
|
||||
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);
|
||||
|
@ -178,7 +177,6 @@ static const Property diva_serial_properties[] = {
|
|||
DEFINE_PROP_CHR("chardev2", PCIDivaSerialState, state[1].chr),
|
||||
DEFINE_PROP_CHR("chardev3", PCIDivaSerialState, state[2].chr),
|
||||
DEFINE_PROP_CHR("chardev4", PCIDivaSerialState, state[3].chr),
|
||||
DEFINE_PROP_UINT8("prog_if", PCIDivaSerialState, prog_if, 0x02),
|
||||
DEFINE_PROP_UINT32("subvendor", PCIDivaSerialState, subvendor,
|
||||
PCI_DEVICE_ID_HP_DIVA_TOSCA1),
|
||||
};
|
||||
|
|
|
@ -46,7 +46,6 @@ typedef struct PCIMultiSerialState {
|
|||
SerialState state[PCI_SERIAL_MAX_PORTS];
|
||||
uint32_t level[PCI_SERIAL_MAX_PORTS];
|
||||
IRQState irqs[PCI_SERIAL_MAX_PORTS];
|
||||
uint8_t prog_if;
|
||||
} PCIMultiSerialState;
|
||||
|
||||
static void multi_serial_pci_exit(PCIDevice *dev)
|
||||
|
@ -97,8 +96,8 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp)
|
|||
SerialState *s;
|
||||
size_t i, nports = multi_serial_get_port_count(pc);
|
||||
|
||||
pci->dev.config[PCI_CLASS_PROG] = pci->prog_if;
|
||||
pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
|
||||
pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */
|
||||
pci->dev.config[PCI_INTERRUPT_PIN] = 1;
|
||||
memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nports);
|
||||
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[] = {
|
||||
DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].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[] = {
|
||||
|
@ -141,7 +139,6 @@ static const Property multi_4x_serial_pci_properties[] = {
|
|||
DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr),
|
||||
DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].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,
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
struct PCISerialState {
|
||||
PCIDevice dev;
|
||||
SerialState state;
|
||||
uint8_t prog_if;
|
||||
};
|
||||
|
||||
#define TYPE_PCI_SERIAL "pci-serial"
|
||||
|
@ -53,8 +52,8 @@ static void serial_pci_realize(PCIDevice *dev, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
pci->dev.config[PCI_CLASS_PROG] = pci->prog_if;
|
||||
pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
|
||||
pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */
|
||||
pci->dev.config[PCI_INTERRUPT_PIN] = 1;
|
||||
s->irq = pci_allocate_irq(&pci->dev);
|
||||
|
||||
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)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
@ -96,7 +91,6 @@ static void serial_pci_class_initfn(ObjectClass *klass, const void *data)
|
|||
pc->revision = 1;
|
||||
pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
|
||||
dc->vmsd = &vmstate_pci_serial;
|
||||
device_class_set_props(dc, serial_pci_properties);
|
||||
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. */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,13 @@ members = [
|
|||
"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]
|
||||
unexpected_cfgs = { level = "deny", check-cfg = [
|
||||
'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
[package]
|
||||
name = "pl011"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
|
||||
license = "GPL-2.0-or-later"
|
||||
description = "pl011 device model for QEMU"
|
||||
resolver = "2"
|
||||
publish = false
|
||||
keywords = []
|
||||
categories = []
|
||||
rust-version = "1.63.0"
|
||||
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
[package]
|
||||
name = "hpet"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Zhao Liu <zhao1.liu@intel.com>"]
|
||||
license = "GPL-2.0-or-later"
|
||||
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]
|
||||
crate-type = ["staticlib"]
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
os::raw::{c_int, c_void},
|
||||
pin::Pin,
|
||||
ptr::{addr_of_mut, null_mut, NonNull},
|
||||
slice::from_ref,
|
||||
|
@ -25,7 +26,10 @@ use qemu_api::{
|
|||
qom::{ObjectImpl, ObjectType, ParentField},
|
||||
qom_isa,
|
||||
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;
|
||||
|
@ -561,6 +565,7 @@ pub struct HPETState {
|
|||
#[doc(alias = "timer")]
|
||||
timers: [BqlRefCell<HPETTimer>; HPET_MAX_TIMERS as usize],
|
||||
num_timers: BqlCell<u8>,
|
||||
num_timers_save: BqlCell<u8>,
|
||||
|
||||
/// Instance id (HPET timer block ID).
|
||||
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);
|
||||
|
@ -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 {
|
||||
fn properties() -> &'static [Property] {
|
||||
&HPET_PROPERTIES
|
||||
}
|
||||
|
||||
fn vmsd() -> Option<&'static VMStateDescription> {
|
||||
Some(&VMSTATE_HPET)
|
||||
}
|
||||
|
||||
const REALIZE: Option<fn(&Self)> = Some(Self::realize);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
[package]
|
||||
name = "qemu_api_macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
|
||||
license = "GPL-2.0-or-later"
|
||||
description = "Rust bindings for QEMU - Utility macros"
|
||||
resolver = "2"
|
||||
publish = false
|
||||
keywords = []
|
||||
categories = []
|
||||
rust-version = "1.63.0"
|
||||
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
[package]
|
||||
name = "qemu_api"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
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"
|
||||
repository = "https://gitlab.com/qemu-project/qemu/"
|
||||
readme = "README.md"
|
||||
resolver = "2"
|
||||
publish = false
|
||||
keywords = []
|
||||
categories = []
|
||||
rust-version = "1.63.0"
|
||||
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
qemu_api_macros = { path = "../qemu-api-macros" }
|
||||
|
|
|
@ -78,33 +78,26 @@ macro_rules! assert_same_type {
|
|||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! assert_field_type {
|
||||
($t:ty, $i:tt, $ti:ty) => {
|
||||
(@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => {
|
||||
const _: () = {
|
||||
#[allow(unused)]
|
||||
fn assert_field_type(v: $t) {
|
||||
fn types_must_be_equal<T, U>(_: T)
|
||||
fn assert_field_type($param_name: &$t) {
|
||||
fn types_must_be_equal<T, U>(_: &T)
|
||||
where
|
||||
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) => {
|
||||
const _: () = {
|
||||
#[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]);
|
||||
}
|
||||
};
|
||||
$crate::assert_field_type!(@internal v, $ti, $t, v.$i[0]);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -121,3 +121,5 @@ impl ClockType {
|
|||
pub const CLOCK_VIRTUAL: ClockType = ClockType {
|
||||
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.
|
||||
#[macro_export]
|
||||
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 {
|
||||
name: ::core::concat!(::core::stringify!($field_name), "\0")
|
||||
.as_bytes()
|
||||
.as_ptr() as *const ::std::os::raw::c_char,
|
||||
offset: $crate::offset_of!($struct_name, $field_name),
|
||||
$(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
|
||||
// computes most of the VMStateField from the type of the 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
|
||||
// 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
|
||||
|
@ -445,7 +478,7 @@ macro_rules! vmstate_unused {
|
|||
#[doc(alias = "VMSTATE_STRUCT")]
|
||||
#[macro_export]
|
||||
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 {
|
||||
name: ::core::concat!(::core::stringify!($field_name), "\0")
|
||||
.as_bytes()
|
||||
|
@ -458,6 +491,7 @@ macro_rules! vmstate_struct {
|
|||
size: ::core::mem::size_of::<$type>(),
|
||||
flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
|
||||
vmsd: $vmsd,
|
||||
$(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
|
||||
..$crate::zeroable::Zeroable::ZERO
|
||||
} $(.with_varray_flag_unchecked(
|
||||
$crate::call_func_with_field!(
|
||||
|
@ -473,7 +507,7 @@ macro_rules! vmstate_struct {
|
|||
#[doc(alias = "VMSTATE_CLOCK")]
|
||||
#[macro_export]
|
||||
macro_rules! vmstate_clock {
|
||||
($struct_name:ty, $field_name:ident) => {{
|
||||
($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?) => {{
|
||||
$crate::bindings::VMStateField {
|
||||
name: ::core::concat!(::core::stringify!($field_name), "\0")
|
||||
.as_bytes()
|
||||
|
@ -482,7 +516,7 @@ macro_rules! vmstate_clock {
|
|||
$crate::assert_field_type!(
|
||||
$struct_name,
|
||||
$field_name,
|
||||
$crate::qom::Owned<$crate::qdev::Clock>
|
||||
$crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
|
||||
);
|
||||
$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) },
|
||||
..$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")]
|
||||
#[macro_export]
|
||||
macro_rules! vmstate_validate {
|
||||
($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => {
|
||||
$crate::bindings::VMStateField {
|
||||
name: ::std::ffi::CStr::as_ptr($test_name),
|
||||
field_exists: {
|
||||
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)))
|
||||
},
|
||||
field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),
|
||||
flags: $crate::bindings::VMStateFlags(
|
||||
$crate::bindings::VMStateFlags::VMS_MUST_EXIST.0
|
||||
| $crate::bindings::VMStateFlags::VMS_ARRAY.0,
|
||||
|
|
|
@ -28,7 +28,7 @@ const FOO_ARRAY_MAX: usize = 3;
|
|||
// - VMSTATE_VARRAY_UINT16_UNSAFE
|
||||
// - VMSTATE_VARRAY_MULTIPLY
|
||||
#[repr(C)]
|
||||
#[derive(qemu_api_macros::offsets)]
|
||||
#[derive(Default, qemu_api_macros::offsets)]
|
||||
struct FooA {
|
||||
arr: [u8; FOO_ARRAY_MAX],
|
||||
num: u16,
|
||||
|
@ -147,8 +147,9 @@ fn test_vmstate_varray_multiply() {
|
|||
// - VMSTATE_STRUCT_VARRAY_UINT8
|
||||
// - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32
|
||||
// - VMSTATE_ARRAY
|
||||
// - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn
|
||||
#[repr(C)]
|
||||
#[derive(qemu_api_macros::offsets)]
|
||||
#[derive(Default, qemu_api_macros::offsets)]
|
||||
struct FooB {
|
||||
arr_a: [FooA; FOO_ARRAY_MAX],
|
||||
num_a: u8,
|
||||
|
@ -158,6 +159,12 @@ struct FooB {
|
|||
val: bool,
|
||||
// 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_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 {
|
||||
|
@ -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_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2),
|
||||
vmstate_of!(FooB, arr_i64),
|
||||
vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob),
|
||||
},
|
||||
..Zeroable::ZERO
|
||||
};
|
||||
|
||||
#[test]
|
||||
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)
|
||||
assert_eq!(
|
||||
|
@ -196,7 +204,7 @@ fn test_vmstate_bool_v() {
|
|||
|
||||
#[test]
|
||||
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)
|
||||
assert_eq!(
|
||||
|
@ -216,7 +224,7 @@ fn test_vmstate_uint64() {
|
|||
|
||||
#[test]
|
||||
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
|
||||
// VMSTATE_STRUCT_VARRAY_UINT8)
|
||||
|
@ -240,7 +248,7 @@ fn test_vmstate_struct_varray_uint8() {
|
|||
|
||||
#[test]
|
||||
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
|
||||
// (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32)
|
||||
|
@ -266,7 +274,7 @@ fn test_vmstate_struct_varray_uint32_multiply() {
|
|||
|
||||
#[test]
|
||||
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
|
||||
// VMSTATE_ARRAY)
|
||||
|
@ -283,9 +291,26 @@ fn test_vmstate_macro_array() {
|
|||
assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY);
|
||||
assert!(foo_fields[4].vmsd.is_null());
|
||||
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.
|
||||
assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END);
|
||||
assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END);
|
||||
}
|
||||
|
||||
// =========================== 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,
|
||||
NULL, decode_invalid, 0};
|
||||
NULL, decode_invalid};
|
||||
|
||||
struct decode_x87_tbl _x87_inst[] = {
|
||||
{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},
|
||||
{0xd9, 4, 0, X86_DECODE_CMD_INVL, 4, false, false,
|
||||
decode_x87_modrm_bytep, NULL, NULL},
|
||||
{0xd9, 5, 3, X86_DECODE_CMD_FLDxx, 10, false, false, NULL, NULL, NULL,
|
||||
RFLAGS_MASK_NONE},
|
||||
{0xd9, 5, 3, X86_DECODE_CMD_FLDxx, 10, false, false, NULL, NULL, NULL},
|
||||
{0xd9, 5, 0, X86_DECODE_CMD_FLDCW, 2, false, false,
|
||||
decode_x87_modrm_bytep, NULL, NULL},
|
||||
|
||||
|
@ -1478,20 +1477,17 @@ struct decode_x87_tbl _x87_inst[] = {
|
|||
decode_x87_modrm_st0, NULL},
|
||||
{0xda, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0,
|
||||
decode_x87_modrm_st0, NULL},
|
||||
{0xda, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL,
|
||||
RFLAGS_MASK_NONE},
|
||||
{0xda, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL},
|
||||
{0xda, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0,
|
||||
decode_x87_modrm_intp, NULL},
|
||||
{0xda, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, decode_x87_modrm_st0,
|
||||
decode_decode_x87_modrm_st0, NULL},
|
||||
{0xda, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0,
|
||||
decode_x87_modrm_intp, NULL},
|
||||
{0xda, 6, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL,
|
||||
RFLAGS_MASK_NONE},
|
||||
{0xda, 6, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL},
|
||||
{0xda, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0,
|
||||
decode_x87_modrm_intp, NULL},
|
||||
{0xda, 7, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL,
|
||||
RFLAGS_MASK_NONE},
|
||||
{0xda, 7, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL},
|
||||
{0xda, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0,
|
||||
decode_x87_modrm_intp, NULL},
|
||||
|
||||
|
@ -1511,8 +1507,7 @@ struct decode_x87_tbl _x87_inst[] = {
|
|||
decode_x87_modrm_intp, NULL, NULL},
|
||||
{0xdb, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL,
|
||||
decode_db_4},
|
||||
{0xdb, 4, 0, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL,
|
||||
RFLAGS_MASK_NONE},
|
||||
{0xdb, 4, 0, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL},
|
||||
{0xdb, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, false,
|
||||
decode_x87_modrm_st0, decode_x87_modrm_st0, NULL},
|
||||
{0xdb, 5, 0, X86_DECODE_CMD_FLD, 10, false, false,
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
#include "qemu/main-loop.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "target/i386/cpu.h"
|
||||
#include "exec/target_page.h"
|
||||
|
||||
static Error *invtsc_mig_blocker;
|
||||
|
||||
|
|
|
@ -342,7 +342,7 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv
|
|||
break;
|
||||
case X86_OP_SEG:
|
||||
/* 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;
|
||||
case X86_OP_INT:
|
||||
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);
|
||||
|
||||
/* 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)
|
||||
|
|
|
@ -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
|
||||
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)) {
|
||||
TCGv_i32 sel = tcg_temp_new_i32();
|
||||
|
||||
tcg_gen_trunc_tl_i32(sel, src);
|
||||
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
|
||||
stop as a special handling must be done to disable hardware
|
||||
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) {
|
||||
|
||||
/* For move to DS/ES/SS, the addseg or ss32 flags may change. */
|
||||
if (CODE32(s) && seg_reg < R_FS) {
|
||||
s->base.is_jmp = DISAS_EOB_NEXT;
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
gen_helper_rechecking_single_step(tcg_env);
|
||||
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);
|
||||
} else if (mode == DISAS_JUMP &&
|
||||
/* give irqs a chance to happen */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue