* [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14
@ 2025-10-14 13:35 Paolo Bonzini
2025-10-14 13:36 ` [PULL 01/28] rust: pl011: fix warning with new clippy Paolo Bonzini
` (28 more replies)
0 siblings, 29 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:35 UTC (permalink / raw)
To: qemu-devel
The following changes since commit 94474a7733a57365d5a27efc28c05462e90e8944:
Merge tag 'pull-loongarch-20251009' of https://github.com/gaosong715/qemu into staging (2025-10-09 07:59:29 -0700)
are available in the Git repository at:
https://gitlab.com/bonzini/qemu.git tags/for-upstream
for you to fetch changes up to 7ee5875d423598ac55a0b55881d9a1ee5c3c7daf:
rust: migration: implement ToMigrationState as part of impl_vmstate_bitsized (2025-10-14 14:43:54 +0200)
----------------------------------------------------------------
* rust: fix nightly warnings
* target/i386: a smattering of fixes
* monitor: add "info accelerators"
* kvm: cleanups to kvm_cpu_synchronize_put()
* target/i386: Add TSA attack variants and verw-clear feature flag
* async: tsan bottom half fixes
* rust: migration state wrappers with support for BQL-free devices
----------------------------------------------------------------
Babu Moger (2):
target/i386: Add TSA attack variants TSA-SQ and TSA-L1
target/i386: Add TSA feature flag verw-clear
Jon Kohler (1):
i386/kvm: Expose ARCH_CAP_FB_CLEAR when invulnerable to MDS
Mathias Krause (1):
target/i386: Fix CR2 handling for non-canonical addresses
Paolo Bonzini (20):
rust: pl011: fix warning with new clippy
rust: bits: disable double_parens check
rust: migration: hide more warnings from call_func_with_field!
rust: hpet: fix fw_cfg handling
i386/cpu: Prevent delivering SIPI during SMM in TCG mode
target/i386: fix access to the T bit of the TSS
async: access bottom half flags with qatomic_read
target/i386: user: do not set up a valid LDT on reset
monitor: clarify "info accel" help message
monitor: generalize query-mshv/"info mshv" to query-accelerators/"info accelerators"
rust: bql: add BqlRefCell::get_mut()
rust: migration: do not pass raw pointer to VMStateDescription::fields
rust: migration: do not store raw pointers into VMStateSubsectionsWrapper
rust: migration: validate termination of subsection arrays
rust: migration: extract vmstate_fields_ref
rust: move VMState from bql to migration
rust: migration: add high-level migration wrappers
rust: qemu-macros: add ToMigrationState derive macro
timer: constify some functions
rust: migration: implement ToMigrationState as part of impl_vmstate_bitsized
Philippe Mathieu-Daudé (2):
accel/kvm: Introduce KvmPutState enum
accel/kvm: Factor kvm_cpu_synchronize_put() out
Thomas Ogrisegg (1):
target/i386: fix x86_64 pushw op
YiFei Zhu (1):
i386/tcg/smm_helper: Properly apply DR values on SMM entry / exit
docs/devel/rust.rst | 1 +
qapi/accelerator.json | 47 +++-
include/monitor/hmp.h | 2 +-
include/qemu/timer.h | 6 +-
include/system/kvm.h | 16 +-
target/i386/cpu.h | 8 +
accel/kvm/kvm-all.c | 47 ++--
hw/core/machine-hmp-cmds.c | 21 +-
hw/core/machine-qmp-cmds.c | 20 +-
hw/intc/apic.c | 2 -
target/arm/kvm.c | 2 +-
target/i386/cpu.c | 23 +-
target/i386/helper.c | 4 +
target/i386/kvm/kvm.c | 23 +-
target/i386/tcg/seg_helper.c | 2 +-
target/i386/tcg/system/excp_helper.c | 3 +-
target/i386/tcg/system/seg_helper.c | 1 +
target/i386/tcg/system/smm_helper.c | 10 +-
target/loongarch/kvm/kvm.c | 4 +-
target/mips/kvm.c | 6 +-
target/ppc/kvm.c | 2 +-
target/riscv/kvm/kvm-cpu.c | 2 +-
target/s390x/kvm/kvm.c | 2 +-
util/async.c | 11 +-
util/qemu-timer.c | 8 +-
target/i386/tcg/decode-new.c.inc | 2 +-
hmp-commands-info.hx | 19 +-
rust/Cargo.lock | 3 +-
rust/bql/Cargo.toml | 1 -
rust/bql/meson.build | 1 -
rust/bql/src/cell.rs | 23 +-
rust/hw/char/pl011/src/registers.rs | 1 +
rust/hw/timer/hpet/src/fw_cfg.rs | 4 +-
rust/meson.build | 2 +-
rust/migration/Cargo.toml | 2 +
rust/migration/meson.build | 7 +-
rust/migration/src/lib.rs | 5 +
rust/migration/src/migratable.rs | 442 ++++++++++++++++++++++++++++++++
rust/migration/src/vmstate.rs | 86 ++++---
rust/qemu-macros/src/lib.rs | 97 ++++++-
rust/qemu-macros/src/migration_state.rs | 298 +++++++++++++++++++++
rust/qemu-macros/src/tests.rs | 113 +++++++-
42 files changed, 1230 insertions(+), 149 deletions(-)
create mode 100644 rust/migration/src/migratable.rs
create mode 100644 rust/qemu-macros/src/migration_state.rs
--
2.51.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PULL 01/28] rust: pl011: fix warning with new clippy
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 02/28] rust: bits: disable double_parens check Paolo Bonzini
` (27 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson
Newer versions of clippy are able to see that all the variants in
the PL011 word length enum end with "Bits", and complain about it.
Allow it.
Reported-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/registers.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs
index 0c3a4d7d214..fa572811b29 100644
--- a/rust/hw/char/pl011/src/registers.rs
+++ b/rust/hw/char/pl011/src/registers.rs
@@ -255,6 +255,7 @@ pub enum Mode {
#[bitsize(2)]
#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
+#[allow(clippy::enum_variant_names)]
/// `WLEN` Word length, field of [Line Control register](LineControl).
///
/// These bits indicate the number of data bits transmitted or received in a
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 02/28] rust: bits: disable double_parens check
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
2025-10-14 13:36 ` [PULL 01/28] rust: pl011: fix warning with new clippy Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 03/28] rust: migration: hide more warnings from call_func_with_field! Paolo Bonzini
` (26 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Manos Pitsidianakis
It is showing in the output of the bits! macro when using the nightly
toolchain, though it's not clear if it is intentional or a bug.
Shut it up for now.
Link: https://github.com/rust-lang/rust-clippy/issues/15852
Reported-by: Richard Henderson <richard.henderson@linaro.org>
Suggested-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Link: https://lore.kernel.org/r/20251010145756.787800-1-pbonzini@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-macros/src/lib.rs | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/rust/qemu-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs
index 3e21b67b471..3bf315c4c0a 100644
--- a/rust/qemu-macros/src/lib.rs
+++ b/rust/qemu-macros/src/lib.rs
@@ -401,7 +401,14 @@ pub fn bits_const_internal(ts: TokenStream) -> TokenStream {
let ts = proc_macro2::TokenStream::from(ts);
let mut it = ts.into_iter();
- BitsConstInternal::parse(&mut it)
- .unwrap_or_else(syn::Error::into_compile_error)
- .into()
+ let out = BitsConstInternal::parse(&mut it).unwrap_or_else(syn::Error::into_compile_error);
+
+ // https://github.com/rust-lang/rust-clippy/issues/15852
+ quote! {
+ {
+ #[allow(clippy::double_parens)]
+ #out
+ }
+ }
+ .into()
}
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 03/28] rust: migration: hide more warnings from call_func_with_field!
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
2025-10-14 13:36 ` [PULL 01/28] rust: pl011: fix warning with new clippy Paolo Bonzini
2025-10-14 13:36 ` [PULL 02/28] rust: bits: disable double_parens check Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 04/28] rust: hpet: fix fw_cfg handling Paolo Bonzini
` (25 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson
The call_func_with_field! macro uses dead code willingly to infer
the appropriate type. This has started adding a new warning:
error: unused variable: `value__`
79 | break phantom__(&{ let value__: $typ; value__.$($field).+ })
So shut it up together with the existing unreachable_code warning.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/migration/src/vmstate.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs
index e04b19b3c9f..5f5708ad39e 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -72,6 +72,7 @@ macro_rules! call_func_with_field {
($func:expr, $typ:ty, $($field:tt).+) => {
$func(loop {
#![allow(unreachable_code)]
+ #![allow(unused_variables)]
const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::core::marker::PhantomData }
// Unreachable code is exempt from checks on uninitialized values.
// Use that trick to infer the type of this PhantomData.
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 04/28] rust: hpet: fix fw_cfg handling
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (2 preceding siblings ...)
2025-10-14 13:36 ` [PULL 03/28] rust: migration: hide more warnings from call_func_with_field! Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 05/28] target/i386: Add TSA attack variants TSA-SQ and TSA-L1 Paolo Bonzini
` (24 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Zhao Liu
HPET ids for fw_cfg are not assigned correctly, because there
is a read but no write. This is caught by nightly Rust as
an unused-assignments warning, so fix it.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/timer/hpet/src/fw_cfg.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs
index e569b57b93b..bb4ea8909ad 100644
--- a/rust/hw/timer/hpet/src/fw_cfg.rs
+++ b/rust/hw/timer/hpet/src/fw_cfg.rs
@@ -40,7 +40,7 @@ pub(crate) fn assign_hpet_id() -> Result<usize, &'static str> {
assert!(bql::is_locked());
// SAFETY: all accesses go through these methods, which guarantee
// that the accesses are protected by the BQL.
- let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) };
+ let fw_cfg = unsafe { &mut *addr_of_mut!(hpet_fw_cfg) };
if fw_cfg.count == u8::MAX {
// first instance
@@ -60,7 +60,7 @@ pub(crate) fn update_hpet_cfg(hpet_id: usize, timer_block_id: u32, address: u64)
assert!(bql::is_locked());
// SAFETY: all accesses go through these methods, which guarantee
// that the accesses are protected by the BQL.
- let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) };
+ let fw_cfg = unsafe { &mut *addr_of_mut!(hpet_fw_cfg) };
fw_cfg.hpet[hpet_id].event_timer_block_id = timer_block_id;
fw_cfg.hpet[hpet_id].address = address;
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 05/28] target/i386: Add TSA attack variants TSA-SQ and TSA-L1
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (3 preceding siblings ...)
2025-10-14 13:36 ` [PULL 04/28] rust: hpet: fix fw_cfg handling Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 06/28] target/i386: Add TSA feature flag verw-clear Paolo Bonzini
` (23 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel
Cc: Richard Henderson, Babu Moger, Borislav Petkov (AMD), Xiaoyao Li,
Zhao Liu
From: Babu Moger <babu.moger@amd.com>
Transient Scheduler Attacks (TSA) are new speculative side channel attacks
related to the execution timing of instructions under specific
microarchitectural conditions. In some cases, an attacker may be able to
use this timing information to infer data from other contexts, resulting in
information leakage.
AMD has identified two sub-variants two variants of TSA.
CPUID Fn8000_0021 ECX[1] (TSA_SQ_NO).
If this bit is 1, the CPU is not vulnerable to TSA-SQ.
CPUID Fn8000_0021 ECX[2] (TSA_L1_NO).
If this bit is 1, the CPU is not vulnerable to TSA-L1.
Add the new feature word FEAT_8000_0021_ECX and corresponding bits to
detect TSA variants.
Link: https://www.amd.com/content/dam/amd/en/documents/resources/bulletin/technical-guidance-for-mitigating-transient-scheduler-attacks.pdf
Co-developed-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Babu Moger <babu.moger@amd.com>
Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Link: https://lore.kernel.org/r/12881b2c03fa351316057ddc5f39c011074b4549.1752176771.git.babu.moger@amd.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/cpu.h | 6 ++++++
target/i386/cpu.c | 17 +++++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 8b7c173838e..f74f13534e7 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -643,6 +643,7 @@ typedef enum FeatureWord {
FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */
FEAT_8000_0021_EAX, /* CPUID[8000_0021].EAX */
FEAT_8000_0021_EBX, /* CPUID[8000_0021].EBX */
+ FEAT_8000_0021_ECX, /* CPUID[8000_0021].ECX */
FEAT_8000_0022_EAX, /* CPUID[8000_0022].EAX */
FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */
FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */
@@ -1126,6 +1127,11 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
*/
#define CPUID_8000_0021_EBX_RAPSIZE (8U << 16)
+/* CPU is not vulnerable TSA SA-SQ attack */
+#define CPUID_8000_0021_ECX_TSA_SQ_NO (1U << 1)
+/* CPU is not vulnerable TSA SA-L1 attack */
+#define CPUID_8000_0021_ECX_TSA_L1_NO (1U << 2)
+
/* Performance Monitoring Version 2 */
#define CPUID_8000_0022_EAX_PERFMON_V2 (1U << 0)
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index ab18de894e4..339881b40f8 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -1415,6 +1415,22 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
.tcg_features = 0,
.unmigratable_flags = 0,
},
+ [FEAT_8000_0021_ECX] = {
+ .type = CPUID_FEATURE_WORD,
+ .feat_names = {
+ NULL, "tsa-sq-no", "tsa-l1-no", NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ },
+ .cpuid = { .eax = 0x80000021, .reg = R_ECX, },
+ .tcg_features = 0,
+ .unmigratable_flags = 0,
+ },
[FEAT_8000_0022_EAX] = {
.type = CPUID_FEATURE_WORD,
.feat_names = {
@@ -8526,6 +8542,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*eax = *ebx = *ecx = *edx = 0;
*eax = env->features[FEAT_8000_0021_EAX];
*ebx = env->features[FEAT_8000_0021_EBX];
+ *ecx = env->features[FEAT_8000_0021_ECX];
break;
case 0x80000022:
*eax = *ebx = *ecx = *edx = 0;
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 06/28] target/i386: Add TSA feature flag verw-clear
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (4 preceding siblings ...)
2025-10-14 13:36 ` [PULL 05/28] target/i386: Add TSA attack variants TSA-SQ and TSA-L1 Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 07/28] target/i386: Fix CR2 handling for non-canonical addresses Paolo Bonzini
` (22 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel
Cc: Richard Henderson, Babu Moger, Borislav Petkov (AMD), Xiaoyao Li,
Zhao Liu
From: Babu Moger <babu.moger@amd.com>
Transient Scheduler Attacks (TSA) are new speculative side channel attacks
related to the execution timing of instructions under specific
microarchitectural conditions. In some cases, an attacker may be able to
use this timing information to infer data from other contexts, resulting in
information leakage
CPUID Fn8000_0021 EAX[5] (VERW_CLEAR). If this bit is 1, the memory form of
the VERW instruction may be used to help mitigate TSA.
Link: https://www.amd.com/content/dam/amd/en/documents/resources/bulletin/technical-guidance-for-mitigating-transient-scheduler-attacks.pdf
Co-developed-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Babu Moger <babu.moger@amd.com>
Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Link: https://lore.kernel.org/r/e6362672e3a67a9df661a8f46598335a1a2d2754.1752176771.git.babu.moger@amd.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/cpu.h | 2 ++
target/i386/cpu.c | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index f74f13534e7..ce948861a76 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1104,6 +1104,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
#define CPUID_8000_0021_EAX_FS_GS_BASE_NS (1U << 1)
/* LFENCE is always serializing */
#define CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING (1U << 2)
+/* Memory form of VERW mitigates TSA */
+#define CPUID_8000_0021_EAX_VERW_CLEAR (1U << 5)
/* Null Selector Clears Base */
#define CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE (1U << 6)
/* Automatic IBRS */
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 339881b40f8..4f99cbc5c0b 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -1397,7 +1397,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
.type = CPUID_FEATURE_WORD,
.feat_names = {
"no-nested-data-bp", "fs-gs-base-ns", "lfence-always-serializing", NULL,
- NULL, NULL, "null-sel-clr-base", NULL,
+ NULL, "verw-clear", "null-sel-clr-base", NULL,
"auto-ibrs", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 07/28] target/i386: Fix CR2 handling for non-canonical addresses
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (5 preceding siblings ...)
2025-10-14 13:36 ` [PULL 06/28] target/i386: Add TSA feature flag verw-clear Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 08/28] i386/kvm: Expose ARCH_CAP_FB_CLEAR when invulnerable to MDS Paolo Bonzini
` (21 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Mathias Krause, qemu-stable
From: Mathias Krause <minipli@grsecurity.net>
Commit 3563362ddfae ("target/i386: Introduce structures for mmu_translate")
accidentally modified CR2 for non-canonical address exceptions while these
should lead to a #GP / #SS instead -- without changing CR2.
Fix that.
A KUT test for this was submitted as [1].
[1] https://lore.kernel.org/kvm/20250612141637.131314-1-minipli@grsecurity.net/
Fixes: 3563362ddfae ("target/i386: Introduce structures for mmu_translate")
Signed-off-by: Mathias Krause <minipli@grsecurity.net>
Link: https://lore.kernel.org/r/20250612142155.132175-1-minipli@grsecurity.net
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/tcg/system/excp_helper.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c
index 50040f6fcaf..f622b5d588e 100644
--- a/target/i386/tcg/system/excp_helper.c
+++ b/target/i386/tcg/system/excp_helper.c
@@ -592,7 +592,8 @@ static bool get_physical_address(CPUX86State *env, vaddr addr,
if (sext != 0 && sext != -1) {
*err = (TranslateFault){
.exception_index = EXCP0D_GPF,
- .cr2 = addr,
+ /* non-canonical #GP doesn't change CR2 */
+ .cr2 = env->cr[2],
};
return false;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 08/28] i386/kvm: Expose ARCH_CAP_FB_CLEAR when invulnerable to MDS
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (6 preceding siblings ...)
2025-10-14 13:36 ` [PULL 07/28] target/i386: Fix CR2 handling for non-canonical addresses Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 09/28] i386/cpu: Prevent delivering SIPI during SMM in TCG mode Paolo Bonzini
` (20 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel
Cc: Richard Henderson, Jon Kohler, Pawan Gupta, Sean Christopherson,
qemu-stable
From: Jon Kohler <jon@nutanix.com>
Newer Intel hardware (Sapphire Rapids and higher) sets multiple MDS
immunity bits in MSR_IA32_ARCH_CAPABILITIES but lacks the hardware-level
MSR_ARCH_CAP_FB_CLEAR (bit 17):
ARCH_CAP_MDS_NO
ARCH_CAP_TAA_NO
ARCH_CAP_PSDP_NO
ARCH_CAP_FBSDP_NO
ARCH_CAP_SBDR_SSDP_NO
This prevents VMs with fb-clear=on from migrating from older hardware
(Cascade Lake, Ice Lake) to newer hardware, limiting live migration
capabilities. Note fb-clear was first introduced in v8.1.0 [1].
Expose MSR_ARCH_CAP_FB_CLEAR for MDS-invulnerable systems to enable
seamless migration between hardware generations.
Note: There is no impact when a guest migrates to newer hardware as
the existing bit combinations already mark the host as MMIO-immune and
disable FB_CLEAR operations in the kernel (see Linux's
arch_cap_mmio_immune() and vmx_update_fb_clear_dis()). See kernel side
discussion for [2] for additional context.
[1] 22e1094ca82 ("target/i386: add support for FB_CLEAR feature")
[2] https://patchwork.kernel.org/project/kvm/patch/20250401044931.793203-1-jon@nutanix.com/
Cc: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Jon Kohler <jon@nutanix.com>
Link: https://lore.kernel.org/r/20251008202557.4141285-1-jon@nutanix.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/kvm/kvm.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index db40caa3412..e40e374b046 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -653,6 +653,23 @@ uint64_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index)
must_be_one = (uint32_t)value;
can_be_one = (uint32_t)(value >> 32);
return can_be_one & ~must_be_one;
+ case MSR_IA32_ARCH_CAPABILITIES:
+ /*
+ * Special handling for fb-clear bit in ARCH_CAPABILITIES MSR.
+ * KVM will only report the bit if it is enabled in the host,
+ * but, for live migration capability purposes, we want to
+ * expose the bit to the guest even if it is disabled in the
+ * host, as long as the host itself is not vulnerable to
+ * the issue that the fb-clear bit is meant to mitigate.
+ */
+ if ((value & MSR_ARCH_CAP_MDS_NO) &&
+ (value & MSR_ARCH_CAP_TAA_NO) &&
+ (value & MSR_ARCH_CAP_SBDR_SSDP_NO) &&
+ (value & MSR_ARCH_CAP_FBSDP_NO) &&
+ (value & MSR_ARCH_CAP_PSDP_NO)) {
+ value |= MSR_ARCH_CAP_FB_CLEAR;
+ }
+ return value;
default:
return value;
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 09/28] i386/cpu: Prevent delivering SIPI during SMM in TCG mode
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (7 preceding siblings ...)
2025-10-14 13:36 ` [PULL 08/28] i386/kvm: Expose ARCH_CAP_FB_CLEAR when invulnerable to MDS Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-23 14:10 ` Peter Maydell
2025-10-14 13:36 ` [PULL 10/28] i386/tcg/smm_helper: Properly apply DR values on SMM entry / exit Paolo Bonzini
` (19 subsequent siblings)
28 siblings, 1 reply; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, YiFei Zhu, qemu-stable
[commit message by YiFei Zhu]
A malicious kernel may control the instruction pointer in SMM in a
multi-processor VM by sending a sequence of IPIs via APIC:
CPU0 CPU1
IPI(CPU1, MODE_INIT)
x86_cpu_exec_reset()
apic_init_reset()
s->wait_for_sipi = true
IPI(CPU1, MODE_SMI)
do_smm_enter()
env->hflags |= HF_SMM_MASK;
IPI(CPU1, MODE_STARTUP, vector)
do_cpu_sipi()
apic_sipi()
/* s->wait_for_sipi check passes */
cpu_x86_load_seg_cache_sipi(vector)
A different sequence, SMI INIT SIPI, is also buggy in TCG because
INIT is not blocked or latched during SMM. However, it is not
vulnerable to an instruction pointer control in the same way because
x86_cpu_exec_reset clears env->hflags, exiting SMM.
Fixes: a9bad65d2c1f ("target-i386: wake up processors that receive an SMI")
Analyzed-by: YiFei Zhu <zhuyifei@google.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/intc/apic.c | 2 --
target/i386/helper.c | 4 ++++
target/i386/tcg/system/seg_helper.c | 1 +
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 6d7859640c2..c7680338563 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -646,8 +646,6 @@ void apic_sipi(DeviceState *dev)
{
APICCommonState *s = APIC(dev);
- cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI);
-
if (!s->wait_for_sipi)
return;
cpu_x86_load_seg_cache_sipi(s->cpu, s->sipi_vector);
diff --git a/target/i386/helper.c b/target/i386/helper.c
index 651041ccfa6..72b2e195a31 100644
--- a/target/i386/helper.c
+++ b/target/i386/helper.c
@@ -621,6 +621,10 @@ void do_cpu_init(X86CPU *cpu)
void do_cpu_sipi(X86CPU *cpu)
{
+ CPUX86State *env = &cpu->env;
+ if (env->hflags & HF_SMM_MASK) {
+ return;
+ }
apic_sipi(cpu->apic_state);
}
diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c
index 38072e51d72..8c7856be81e 100644
--- a/target/i386/tcg/system/seg_helper.c
+++ b/target/i386/tcg/system/seg_helper.c
@@ -182,6 +182,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
apic_poll_irq(cpu->apic_state);
break;
case CPU_INTERRUPT_SIPI:
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_SIPI);
do_cpu_sipi(cpu);
break;
case CPU_INTERRUPT_SMI:
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 10/28] i386/tcg/smm_helper: Properly apply DR values on SMM entry / exit
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (8 preceding siblings ...)
2025-10-14 13:36 ` [PULL 09/28] i386/cpu: Prevent delivering SIPI during SMM in TCG mode Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 11/28] target/i386: fix x86_64 pushw op Paolo Bonzini
` (18 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, YiFei Zhu, unvariant.winter, qemu-stable
From: YiFei Zhu <zhuyifei@google.com>
do_smm_enter and helper_rsm sets the env->dr, but does not sync the
values with cpu_x86_update_dr7. A malicious kernel may control the
instruction pointer in SMM by setting a breakpoint on the SMI
entry point, and after do_smm_enter cpu->breakpoints contains the
stale breakpoint; and because IDT is not reloaded upon SMI entry,
the debug exception handler controlled by the malicious kernel
is invoked.
Fixes: 01df040b5247 ("x86: Debug register emulation (Jan Kiszka)")
Reported-by: unvariant.winter@gmail.com
Signed-off-by: YiFei Zhu <zhuyifei@google.com>
Link: https://lore.kernel.org/r/2bacb9b24e9d337dbe48791aa25d349eb9c52c3a.1758794468.git.zhuyifei@google.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/tcg/system/smm_helper.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/target/i386/tcg/system/smm_helper.c b/target/i386/tcg/system/smm_helper.c
index 251eb7856ce..fb028a8272f 100644
--- a/target/i386/tcg/system/smm_helper.c
+++ b/target/i386/tcg/system/smm_helper.c
@@ -168,7 +168,7 @@ void do_smm_enter(X86CPU *cpu)
env->cr[0] & ~(CR0_PE_MASK | CR0_EM_MASK | CR0_TS_MASK |
CR0_PG_MASK));
cpu_x86_update_cr4(env, 0);
- env->dr[7] = 0x00000400;
+ helper_set_dr(env, 7, 0x00000400);
cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase,
0xffffffff,
@@ -233,8 +233,8 @@ void helper_rsm(CPUX86State *env)
env->eip = x86_ldq_phys(cs, sm_state + 0x7f78);
cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7f70),
~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
- env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7f68);
- env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7f60);
+ helper_set_dr(env, 6, x86_ldl_phys(cs, sm_state + 0x7f68));
+ helper_set_dr(env, 7, x86_ldl_phys(cs, sm_state + 0x7f60));
cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f48));
cpu_x86_update_cr3(env, x86_ldq_phys(cs, sm_state + 0x7f50));
@@ -268,8 +268,8 @@ void helper_rsm(CPUX86State *env)
env->regs[R_EDX] = x86_ldl_phys(cs, sm_state + 0x7fd8);
env->regs[R_ECX] = x86_ldl_phys(cs, sm_state + 0x7fd4);
env->regs[R_EAX] = x86_ldl_phys(cs, sm_state + 0x7fd0);
- env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7fcc);
- env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7fc8);
+ helper_set_dr(env, 6, x86_ldl_phys(cs, sm_state + 0x7fcc));
+ helper_set_dr(env, 7, x86_ldl_phys(cs, sm_state + 0x7fc8));
env->tr.selector = x86_ldl_phys(cs, sm_state + 0x7fc4) & 0xffff;
env->tr.base = x86_ldl_phys(cs, sm_state + 0x7f64);
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 11/28] target/i386: fix x86_64 pushw op
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (9 preceding siblings ...)
2025-10-14 13:36 ` [PULL 10/28] i386/tcg/smm_helper: Properly apply DR values on SMM entry / exit Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 12/28] target/i386: fix access to the T bit of the TSS Paolo Bonzini
` (17 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Thomas Ogrisegg, qemu-stable
From: Thomas Ogrisegg <tom-bugs-qemu@fnord.at>
For x86_64 a 16 bit push op (pushw) of a memory address would generate
a 64 bit store on the stack instead of a 16 bit store.
For example:
pushw (%rax)
behaves like
pushq (%rax)
which is incorrect.
This patch fixes that.
Signed-off-by: Thomas Ogrisegg <tom-bugs-qemu@fnord.at>
Link: https://lore.kernel.org/r/20250715210307.GA1115@x1.fnord.at
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/tcg/decode-new.c.inc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc
index 51038657f0f..a50f57dbaab 100644
--- a/target/i386/tcg/decode-new.c.inc
+++ b/target/i386/tcg/decode-new.c.inc
@@ -1541,7 +1541,7 @@ static void decode_group4_5(DisasContext *s, CPUX86State *env, X86OpEntry *entry
[0x0b] = X86_OP_ENTRYr(CALLF_m, M,p),
[0x0c] = X86_OP_ENTRYr(JMP_m, E,f64, zextT0),
[0x0d] = X86_OP_ENTRYr(JMPF_m, M,p),
- [0x0e] = X86_OP_ENTRYr(PUSH, E,f64),
+ [0x0e] = X86_OP_ENTRYr(PUSH, E,d64),
};
int w = (*b & 1);
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 12/28] target/i386: fix access to the T bit of the TSS
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (10 preceding siblings ...)
2025-10-14 13:36 ` [PULL 11/28] target/i386: fix x86_64 pushw op Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 13/28] async: access bottom half flags with qatomic_read Paolo Bonzini
` (16 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, qemu-stable, Thomas Huth
The T bit is bit 0 of the 16-bit word at offset 100 of the TSS. However,
accessing it with a 32-bit word is not really correct, because bytes
102-103 contain the I/O map base address (relative to the base of the
TSS) and bits 1-15 are reserved. In particular, any task switch to a TSS that
has a nonzero I/O map base address is broken.
This fixes the eventinj and taskswitch tests in kvm-unit-tests.
Cc: qemu-stable@nongnu.org
Fixes: ad441b8b791 ("target/i386: implement TSS trap bit", 2025-05-12)
Reported-by: Thomas Huth <thuth@redhat.com>
Closes: https://gitlab.com/qemu-project/qemu/-/issues/3101
Tested-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/tcg/seg_helper.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c
index 071f3fbd83d..f49fe851cdf 100644
--- a/target/i386/tcg/seg_helper.c
+++ b/target/i386/tcg/seg_helper.c
@@ -456,7 +456,7 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector,
new_segs[i] = access_ldw(&new, tss_base + (0x48 + i * 4));
}
new_ldt = access_ldw(&new, tss_base + 0x60);
- new_trap = access_ldl(&new, tss_base + 0x64);
+ new_trap = access_ldw(&new, tss_base + 0x64) & 1;
} else {
/* 16 bit */
new_cr3 = 0;
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 13/28] async: access bottom half flags with qatomic_read
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (11 preceding siblings ...)
2025-10-14 13:36 ` [PULL 12/28] target/i386: fix access to the T bit of the TSS Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:36 ` [PULL 14/28] target/i386: user: do not set up a valid LDT on reset Paolo Bonzini
` (15 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, qemu-stable
Running test-aio-multithread under TSAN reveals data races on bh->flags.
Because bottom halves may be scheduled or canceled asynchronously,
without taking a lock, adjust aio_compute_bh_timeout() and aio_ctx_check()
to use a relaxed read to access the flags.
Use an acquire load to ensure that anything that was written prior to
qemu_bh_schedule() is visible.
Closes: https://gitlab.com/qemu-project/qemu/-/issues/2749
Closes: https://gitlab.com/qemu-project/qemu/-/issues/851
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
util/async.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/util/async.c b/util/async.c
index 2719c629ae9..a736d2cd0d0 100644
--- a/util/async.c
+++ b/util/async.c
@@ -256,8 +256,9 @@ static int64_t aio_compute_bh_timeout(BHList *head, int timeout)
QEMUBH *bh;
QSLIST_FOREACH_RCU(bh, head, next) {
- if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
- if (bh->flags & BH_IDLE) {
+ int flags = qatomic_load_acquire(&bh->flags);
+ if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
+ if (flags & BH_IDLE) {
/* idle bottom halves will be polled at least
* every 10ms */
timeout = 10000000;
@@ -335,14 +336,16 @@ aio_ctx_check(GSource *source)
aio_notify_accept(ctx);
QSLIST_FOREACH_RCU(bh, &ctx->bh_list, next) {
- if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
+ int flags = qatomic_load_acquire(&bh->flags);
+ if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
return true;
}
}
QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) {
QSLIST_FOREACH_RCU(bh, &s->bh_list, next) {
- if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
+ int flags = qatomic_load_acquire(&bh->flags);
+ if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
return true;
}
}
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 14/28] target/i386: user: do not set up a valid LDT on reset
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (12 preceding siblings ...)
2025-10-14 13:36 ` [PULL 13/28] async: access bottom half flags with qatomic_read Paolo Bonzini
@ 2025-10-14 13:36 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 15/28] monitor: clarify "info accel" help message Paolo Bonzini
` (14 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:36 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, qemu-stable
In user-mode emulation, QEMU uses the default setting of the LDT base
and limit, which places it at the bottom 64K of virtual address space.
However, by default there is no LDT at all in Linux processes, and
therefore the limit should be 0.
This is visible as a NULL pointer dereference in LSL and LAR instructions
when they try to read the LDT at an unmapped address.
Resolves: #1376
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/cpu.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 4f99cbc5c0b..455caff6b23 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -8649,7 +8649,11 @@ static void x86_cpu_reset_hold(Object *obj, ResetType type)
env->idt.limit = 0xffff;
env->gdt.limit = 0xffff;
+#if defined(CONFIG_USER_ONLY)
+ env->ldt.limit = 0;
+#else
env->ldt.limit = 0xffff;
+#endif
env->ldt.flags = DESC_P_MASK | (2 << DESC_TYPE_SHIFT);
env->tr.limit = 0xffff;
env->tr.flags = DESC_P_MASK | (11 << DESC_TYPE_SHIFT);
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 15/28] monitor: clarify "info accel" help message
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (13 preceding siblings ...)
2025-10-14 13:36 ` [PULL 14/28] target/i386: user: do not set up a valid LDT on reset Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 16/28] monitor: generalize query-mshv/"info mshv" to query-accelerators/"info accelerators" Paolo Bonzini
` (13 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Daniel P. Berrangé
In preparation for adding "info accelerators", explain that this command
is about runtime statistics.
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hmp-commands-info.hx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index eaaa880c1b3..c2aa40056bb 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -271,12 +271,12 @@ ERST
.name = "accel",
.args_type = "",
.params = "",
- .help = "show accelerator info",
+ .help = "show accelerator statistics",
},
SRST
``info accel``
- Show accelerator info.
+ Show accelerator statistics.
ERST
SRST
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 16/28] monitor: generalize query-mshv/"info mshv" to query-accelerators/"info accelerators"
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (14 preceding siblings ...)
2025-10-14 13:37 ` [PULL 15/28] monitor: clarify "info accel" help message Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 21:16 ` [EXTERNAL] " Praveen Paladugu
2025-10-14 13:37 ` [PULL 17/28] accel/kvm: Introduce KvmPutState enum Paolo Bonzini
` (12 subsequent siblings)
28 siblings, 1 reply; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel
Cc: Richard Henderson, Praveen K Paladugu, Magnus Kulke,
Markus Armbruster, Daniel P. Berrangé
The recently-introduced query-mshv command is a duplicate of query-kvm,
and neither provides a full view of which accelerators are supported
by a particular binary of QEMU and which is in use.
KVM was the first accelerator added to QEMU, predating QOM and TYPE_ACCEL,
so it got a pass. But now, instead of adding a badly designed copy, solve
the problem completely for all accelerators with a command that provides
the whole picture:
>> {"execute": "query-accelerators"}
<< {"return": {"enabled": "tcg", "present": ["kvm", "mshv", "qtest", "tcg", "xen"]}}
Cc: Praveen K Paladugu <prapal@microsoft.com>
Cc: Magnus Kulke <magnuskulke@linux.microsoft.com>
Suggested-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
qapi/accelerator.json | 47 +++++++++++++++++++++++++++++---------
include/monitor/hmp.h | 2 +-
hw/core/machine-hmp-cmds.c | 21 +++++++++--------
hw/core/machine-qmp-cmds.c | 20 ++++++++++++----
hmp-commands-info.hx | 15 ++++++++----
5 files changed, 74 insertions(+), 31 deletions(-)
diff --git a/qapi/accelerator.json b/qapi/accelerator.json
index 664e0272465..2b920608847 100644
--- a/qapi/accelerator.json
+++ b/qapi/accelerator.json
@@ -56,30 +56,55 @@
'features': [ 'unstable' ] }
##
-# @MshvInfo:
+# @Accelerator:
#
# Information about support for MSHV acceleration
#
-# @enabled: true if MSHV acceleration is active
+# @hvf: Apple Hypervisor.framework
#
-# @present: true if MSHV acceleration is built into this executable
+# @kvm: KVM
+#
+# @mshv: Hyper-V
+#
+# @nvmm: NetBSD NVMM
+#
+# @qtest: QTest (dummy accelerator)
+#
+# @tcg: TCG (dynamic translation)
+#
+# @whpx: Windows Hypervisor Platform
+#
+# @xen: Xen
#
# Since: 10.2.0
##
-{ 'struct': 'MshvInfo', 'data': {'enabled': 'bool', 'present': 'bool'} }
+{ 'enum': 'Accelerator', 'data': ['hvf', 'kvm', 'mshv', 'nvmm', 'qtest', 'tcg', 'whpx', 'xen'] }
##
-# @query-mshv:
+# @AcceleratorInfo:
#
-# Return information about MSHV acceleration
+# Information about support for various accelerators
#
-# Returns: @MshvInfo
+# @enabled: the accelerator that is in use
#
-# Since: 10.0.92
+# @present: the list of accelerators that are built into this executable
+#
+# Since: 10.2.0
+##
+{ 'struct': 'AcceleratorInfo', 'data': {'enabled': 'Accelerator', 'present': ['Accelerator']} }
+
+##
+# @query-accelerators:
+#
+# Return information about accelerators
+#
+# Returns: @AcceleratorInfo
+#
+# Since: 10.2.0
#
# .. qmp-example::
#
-# -> { "execute": "query-mshv" }
-# <- { "return": { "enabled": true, "present": true } }
+# -> { "execute": "query-accelerators" }
+# <- { "return": { "enabled": "mshv", "present": ["kvm", "mshv", "qtest", "tcg"] } }
##
-{ 'command': 'query-mshv', 'returns': 'MshvInfo' }
+{ 'command': 'query-accelerators', 'returns': 'AcceleratorInfo' }
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index 31bd812e5f4..897dfaa2b6d 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -24,7 +24,7 @@ strList *hmp_split_at_comma(const char *str);
void hmp_info_name(Monitor *mon, const QDict *qdict);
void hmp_info_version(Monitor *mon, const QDict *qdict);
void hmp_info_kvm(Monitor *mon, const QDict *qdict);
-void hmp_info_mshv(Monitor *mon, const QDict *qdict);
+void hmp_info_accelerators(Monitor *mon, const QDict *qdict);
void hmp_info_status(Monitor *mon, const QDict *qdict);
void hmp_info_uuid(Monitor *mon, const QDict *qdict);
void hmp_info_chardev(Monitor *mon, const QDict *qdict);
diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c
index 682ed9f49b8..74a56600be8 100644
--- a/hw/core/machine-hmp-cmds.c
+++ b/hw/core/machine-hmp-cmds.c
@@ -163,19 +163,22 @@ void hmp_info_kvm(Monitor *mon, const QDict *qdict)
qapi_free_KvmInfo(info);
}
-void hmp_info_mshv(Monitor *mon, const QDict *qdict)
+void hmp_info_accelerators(Monitor *mon, const QDict *qdict)
{
- MshvInfo *info;
+ AcceleratorInfo *info;
+ AcceleratorList *accel;
- info = qmp_query_mshv(NULL);
- monitor_printf(mon, "mshv support: ");
- if (info->present) {
- monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled");
- } else {
- monitor_printf(mon, "not compiled\n");
+ info = qmp_query_accelerators(NULL);
+ for (accel = info->present; accel; accel = accel->next) {
+ char trail = accel->next ? ' ' : '\n';
+ if (info->enabled == accel->value) {
+ monitor_printf(mon, "[%s]%c", Accelerator_str(accel->value), trail);
+ } else {
+ monitor_printf(mon, "%s%c", Accelerator_str(accel->value), trail);
+ }
}
- qapi_free_MshvInfo(info);
+ qapi_free_AcceleratorInfo(info);
}
void hmp_info_uuid(Monitor *mon, const QDict *qdict)
diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c
index e24bf0d97bf..51d5c230f7e 100644
--- a/hw/core/machine-qmp-cmds.c
+++ b/hw/core/machine-qmp-cmds.c
@@ -31,15 +31,25 @@
#include <sys/stat.h>
/*
- * QMP query for MSHV
+ * QMP query for enabled and present accelerators
*/
-MshvInfo *qmp_query_mshv(Error **errp)
+AcceleratorInfo *qmp_query_accelerators(Error **errp)
{
- MshvInfo *info = g_malloc0(sizeof(*info));
+ AcceleratorInfo *info = g_malloc0(sizeof(*info));
+ AccelClass *current_class = ACCEL_GET_CLASS(current_accel());
+ int i;
- info->enabled = mshv_enabled();
- info->present = accel_find("mshv");
+ for (i = ACCELERATOR__MAX; i-- > 0; ) {
+ const char *s = Accelerator_str(i);
+ AccelClass *this_class = accel_find(s);
+ if (this_class) {
+ QAPI_LIST_PREPEND(info->present, i);
+ if (this_class == current_class) {
+ info->enabled = i;
+ }
+ }
+ }
return info;
}
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index c2aa40056bb..25b4aed51f5 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -308,16 +308,21 @@ SRST
ERST
{
- .name = "mshv",
+ .name = "accelerators",
.args_type = "",
.params = "",
- .help = "show MSHV information",
- .cmd = hmp_info_mshv,
+ .help = "show present and enabled information",
+ .cmd = hmp_info_accelerators,
},
SRST
- ``info mshv``
- Show MSHV information.
+ ``info accelerators``
+ Show which accelerators are compiled into a QEMU binary, and what accelerator
+ is in use. For example::
+
+ kvm qtest [tcg]
+
+ indicates that TCG in use, and that KVM and qtest are also available.
ERST
{
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 17/28] accel/kvm: Introduce KvmPutState enum
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (15 preceding siblings ...)
2025-10-14 13:37 ` [PULL 16/28] monitor: generalize query-mshv/"info mshv" to query-accelerators/"info accelerators" Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 18/28] accel/kvm: Factor kvm_cpu_synchronize_put() out Paolo Bonzini
` (11 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel
Cc: Richard Henderson, Philippe Mathieu-Daudé,
Harsh Prateek Bora
From: Philippe Mathieu-Daudé <philmd@linaro.org>
Join the 3 KVM_PUT_*_STATE definitions in a single enum.
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Harsh Prateek Bora <harshpb@linux.ibm.com>
Link: https://lore.kernel.org/r/20251008040715.81513-3-philmd@linaro.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/system/kvm.h | 16 +++++++++-------
target/arm/kvm.c | 2 +-
target/i386/kvm/kvm.c | 6 +++---
target/loongarch/kvm/kvm.c | 4 ++--
target/mips/kvm.c | 6 +++---
target/ppc/kvm.c | 2 +-
target/riscv/kvm/kvm-cpu.c | 2 +-
target/s390x/kvm/kvm.c | 2 +-
8 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/include/system/kvm.h b/include/system/kvm.h
index 4fc09e38910..8f9eecf044c 100644
--- a/include/system/kvm.h
+++ b/include/system/kvm.h
@@ -340,14 +340,16 @@ int kvm_arch_process_async_events(CPUState *cpu);
int kvm_arch_get_registers(CPUState *cpu, Error **errp);
-/* state subset only touched by the VCPU itself during runtime */
-#define KVM_PUT_RUNTIME_STATE 1
-/* state subset modified during VCPU reset */
-#define KVM_PUT_RESET_STATE 2
-/* full state set, modified during initialization or on vmload */
-#define KVM_PUT_FULL_STATE 3
+typedef enum kvm_put_state {
+ /* state subset only touched by the VCPU itself during runtime */
+ KVM_PUT_RUNTIME_STATE = 1,
+ /* state subset modified during VCPU reset */
+ KVM_PUT_RESET_STATE = 2,
+ /* full state set, modified during initialization or on vmload */
+ KVM_PUT_FULL_STATE = 3,
+} KvmPutState;
-int kvm_arch_put_registers(CPUState *cpu, int level, Error **errp);
+int kvm_arch_put_registers(CPUState *cpu, KvmPutState level, Error **errp);
int kvm_arch_get_default_type(MachineState *ms);
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index 4f769d69b38..0d57081e69f 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -2123,7 +2123,7 @@ static int kvm_arch_put_sve(CPUState *cs)
return 0;
}
-int kvm_arch_put_registers(CPUState *cs, int level, Error **errp)
+int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
{
uint64_t val;
uint32_t fpr;
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index e40e374b046..309f043373c 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -3924,7 +3924,7 @@ static void kvm_init_msrs(X86CPU *cpu)
assert(kvm_buf_set_msrs(cpu) == 0);
}
-static int kvm_put_msrs(X86CPU *cpu, int level)
+static int kvm_put_msrs(X86CPU *cpu, KvmPutState level)
{
CPUX86State *env = &cpu->env;
int i;
@@ -5044,7 +5044,7 @@ static int kvm_get_apic(X86CPU *cpu)
return 0;
}
-static int kvm_put_vcpu_events(X86CPU *cpu, int level)
+static int kvm_put_vcpu_events(X86CPU *cpu, KvmPutState level)
{
CPUState *cs = CPU(cpu);
CPUX86State *env = &cpu->env;
@@ -5287,7 +5287,7 @@ static int kvm_get_nested_state(X86CPU *cpu)
return ret;
}
-int kvm_arch_put_registers(CPUState *cpu, int level, Error **errp)
+int kvm_arch_put_registers(CPUState *cpu, KvmPutState level, Error **errp)
{
X86CPU *x86_cpu = X86_CPU(cpu);
int ret;
diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c
index e5ea2dba9da..4e4f4e79f64 100644
--- a/target/loongarch/kvm/kvm.c
+++ b/target/loongarch/kvm/kvm.c
@@ -325,7 +325,7 @@ static int kvm_loongarch_get_csr(CPUState *cs)
return ret;
}
-static int kvm_loongarch_put_csr(CPUState *cs, int level)
+static int kvm_loongarch_put_csr(CPUState *cs, KvmPutState level)
{
int ret = 0;
CPULoongArchState *env = cpu_env(cs);
@@ -763,7 +763,7 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp)
return ret;
}
-int kvm_arch_put_registers(CPUState *cs, int level, Error **errp)
+int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
{
int ret;
static int once;
diff --git a/target/mips/kvm.c b/target/mips/kvm.c
index 450947c3fa5..912cd5dfa0e 100644
--- a/target/mips/kvm.c
+++ b/target/mips/kvm.c
@@ -590,7 +590,7 @@ static void kvm_mips_update_state(void *opaque, bool running, RunState state)
}
}
-static int kvm_mips_put_fpu_registers(CPUState *cs, int level)
+static int kvm_mips_put_fpu_registers(CPUState *cs, KvmPutState level)
{
CPUMIPSState *env = cpu_env(cs);
int err, ret = 0;
@@ -749,7 +749,7 @@ static int kvm_mips_get_fpu_registers(CPUState *cs)
}
-static int kvm_mips_put_cp0_registers(CPUState *cs, int level)
+static int kvm_mips_put_cp0_registers(CPUState *cs, KvmPutState level)
{
CPUMIPSState *env = cpu_env(cs);
int err, ret = 0;
@@ -1177,7 +1177,7 @@ static int kvm_mips_get_cp0_registers(CPUState *cs)
return ret;
}
-int kvm_arch_put_registers(CPUState *cs, int level, Error **errp)
+int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
{
CPUMIPSState *env = cpu_env(cs);
struct kvm_regs regs;
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index 2521ff65c6c..cd60893a17d 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -907,7 +907,7 @@ int kvmppc_put_books_sregs(PowerPCCPU *cpu)
return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_SREGS, &sregs);
}
-int kvm_arch_put_registers(CPUState *cs, int level, Error **errp)
+int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c
index 187c2c9501e..75ca3fb9fd9 100644
--- a/target/riscv/kvm/kvm-cpu.c
+++ b/target/riscv/kvm/kvm-cpu.c
@@ -1369,7 +1369,7 @@ int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state)
return 0;
}
-int kvm_arch_put_registers(CPUState *cs, int level, Error **errp)
+int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
{
int ret = 0;
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index 491cc5f9756..916dac1f14e 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -468,7 +468,7 @@ static int can_sync_regs(CPUState *cs, int regs)
#define KVM_SYNC_REQUIRED_REGS (KVM_SYNC_GPRS | KVM_SYNC_ACRS | \
KVM_SYNC_CRS | KVM_SYNC_PREFIX)
-int kvm_arch_put_registers(CPUState *cs, int level, Error **errp)
+int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
{
CPUS390XState *env = cpu_env(cs);
struct kvm_fpu fpu = {};
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 18/28] accel/kvm: Factor kvm_cpu_synchronize_put() out
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (16 preceding siblings ...)
2025-10-14 13:37 ` [PULL 17/28] accel/kvm: Introduce KvmPutState enum Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 19/28] rust: bql: add BqlRefCell::get_mut() Paolo Bonzini
` (10 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Philippe Mathieu-Daudé
From: Philippe Mathieu-Daudé <philmd@linaro.org>
The same code is duplicated 3 times: factor a common method.
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Link: https://lore.kernel.org/r/20251008040715.81513-4-philmd@linaro.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
accel/kvm/kvm-all.c | 47 ++++++++++++++++++---------------------------
1 file changed, 19 insertions(+), 28 deletions(-)
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 58802f7c3cc..56031925c4e 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -2937,22 +2937,32 @@ void kvm_cpu_synchronize_state(CPUState *cpu)
}
}
-static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg)
+static bool kvm_cpu_synchronize_put(CPUState *cpu, KvmPutState state,
+ const char *desc)
{
Error *err = NULL;
- int ret = kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE, &err);
+ int ret = kvm_arch_put_registers(cpu, state, &err);
if (ret) {
if (err) {
- error_reportf_err(err, "Restoring resisters after reset: ");
+ error_reportf_err(err, "Restoring resisters %s: ", desc);
} else {
- error_report("Failed to put registers after reset: %s",
+ error_report("Failed to put registers %s: %s", desc,
strerror(-ret));
}
- cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
- vm_stop(RUN_STATE_INTERNAL_ERROR);
+ return false;
}
cpu->vcpu_dirty = false;
+
+ return true;
+}
+
+static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg)
+{
+ if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RESET_STATE, "after reset")) {
+ cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
+ vm_stop(RUN_STATE_INTERNAL_ERROR);
+ }
}
void kvm_cpu_synchronize_post_reset(CPUState *cpu)
@@ -2966,19 +2976,9 @@ void kvm_cpu_synchronize_post_reset(CPUState *cpu)
static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg)
{
- Error *err = NULL;
- int ret = kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE, &err);
- if (ret) {
- if (err) {
- error_reportf_err(err, "Putting registers after init: ");
- } else {
- error_report("Failed to put registers after init: %s",
- strerror(-ret));
- }
+ if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_FULL_STATE, "after init")) {
exit(1);
}
-
- cpu->vcpu_dirty = false;
}
void kvm_cpu_synchronize_post_init(CPUState *cpu)
@@ -3168,20 +3168,11 @@ int kvm_cpu_exec(CPUState *cpu)
MemTxAttrs attrs;
if (cpu->vcpu_dirty) {
- Error *err = NULL;
- ret = kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE, &err);
- if (ret) {
- if (err) {
- error_reportf_err(err, "Putting registers after init: ");
- } else {
- error_report("Failed to put registers after init: %s",
- strerror(-ret));
- }
+ if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RUNTIME_STATE,
+ "at runtime")) {
ret = -1;
break;
}
-
- cpu->vcpu_dirty = false;
}
kvm_arch_pre_run(cpu, run);
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 19/28] rust: bql: add BqlRefCell::get_mut()
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (17 preceding siblings ...)
2025-10-14 13:37 ` [PULL 18/28] accel/kvm: Factor kvm_cpu_synchronize_put() out Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 20/28] rust: migration: do not pass raw pointer to VMStateDescription::fields Paolo Bonzini
` (9 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson
This method is rarely useful in QEMU due to the pervasiveness of
shared references, but add it for when a &mut BqlRefCell<> is used.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/bql/src/cell.rs | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/rust/bql/src/cell.rs b/rust/bql/src/cell.rs
index 24ab294b60d..54cfe6145c5 100644
--- a/rust/bql/src/cell.rs
+++ b/rust/bql/src/cell.rs
@@ -580,6 +580,23 @@ pub fn borrow_mut(&self) -> BqlRefMut<'_, T> {
}
}
+ /// Returns a mutable reference to the underlying data in this cell,
+ /// while the owner already has a mutable reference to the cell.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use bql::BqlRefCell;
+ ///
+ /// let mut c = BqlRefCell::new(5);
+ ///
+ /// *c.get_mut() = 10;
+ /// ```
+ #[inline]
+ pub const fn get_mut(&mut self) -> &mut T {
+ self.value.get_mut()
+ }
+
/// Returns a raw pointer to the underlying data in this cell.
///
/// # Examples
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 20/28] rust: migration: do not pass raw pointer to VMStateDescription::fields
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (18 preceding siblings ...)
2025-10-14 13:37 ` [PULL 19/28] rust: bql: add BqlRefCell::get_mut() Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 21/28] rust: migration: do not store raw pointers into VMStateSubsectionsWrapper Paolo Bonzini
` (8 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Zhao Liu
Pass a slice instead; a function that accepts a raw pointer should
arguably be declared as unsafe.
But since it is now much easier to forget vmstate_fields!, validate the
value (at least to some extent) before passing it to C. (Unfortunately,
doing the same for subsections would require const ptr::is_null(), which
is only stable in Rust 1.84).
Suggested-by: Zhao Liu <zhao1.liu@intel.com>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/migration/src/vmstate.rs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs
index 5f5708ad39e..2e320262f06 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -425,7 +425,7 @@ macro_rules! vmstate_fields {
..::common::zeroable::Zeroable::ZERO
}
];
- _FIELDS.as_ptr()
+ _FIELDS
}}
}
@@ -677,8 +677,11 @@ pub const fn unplug_pending<F: for<'a> FnCall<(&'a T,), bool>>(mut self, _f: &F)
}
#[must_use]
- pub const fn fields(mut self, fields: *const VMStateField) -> Self {
- self.0.fields = fields;
+ pub const fn fields(mut self, fields: &'static [VMStateField]) -> Self {
+ if fields[fields.len() - 1].flags.0 != VMStateFlags::VMS_END.0 {
+ panic!("fields are not terminated, use vmstate_fields!");
+ }
+ self.0.fields = fields.as_ptr();
self
}
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 21/28] rust: migration: do not store raw pointers into VMStateSubsectionsWrapper
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (19 preceding siblings ...)
2025-10-14 13:37 ` [PULL 20/28] rust: migration: do not pass raw pointer to VMStateDescription::fields Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 22/28] rust: migration: validate termination of subsection arrays Paolo Bonzini
` (7 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Zhao Liu
Raw pointers were used to insert a NULL one at the end of the array.
However, Option<&...> has the same layout and does not remove Sync
from the type of the array.
As an extra benefit, this enables validation of the terminator of the
subsection array, because is_null() in const context would not be stable
until Rust 1.84.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/migration/src/vmstate.rs | 29 +++++++++--------------------
1 file changed, 9 insertions(+), 20 deletions(-)
diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs
index 2e320262f06..a6ee7e93853 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -470,33 +470,21 @@ unsafe impl $crate::vmstate::VMState for $type {
};
}
-/// A transparent wrapper type for the `subsections` field of
-/// [`VMStateDescription`].
-///
-/// This is necessary to be able to declare subsection descriptions as statics,
-/// because the only way to implement `Sync` for a foreign type (and `*const`
-/// pointers are foreign types in Rust) is to create a wrapper struct and
-/// `unsafe impl Sync` for it.
-///
-/// This struct is used in the
-/// [`vm_state_subsections`](crate::vmstate_subsections) macro implementation.
-#[repr(transparent)]
-pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMStateDescription]);
-
-unsafe impl Sync for VMStateSubsectionsWrapper {}
+/// The type returned by [`vmstate_subsections!`](crate::vmstate_subsections).
+pub type VMStateSubsections = &'static [Option<&'static crate::bindings::VMStateDescription>];
/// Helper macro to declare a list of subsections ([`VMStateDescription`])
/// into a static and return a pointer to the array of pointers it created.
#[macro_export]
macro_rules! vmstate_subsections {
($($subsection:expr),*$(,)*) => {{
- static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[
+ static _SUBSECTIONS: $crate::vmstate::VMStateSubsections = &[
$({
static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection.get();
- ::core::ptr::addr_of!(_SUBSECTION)
+ Some(&_SUBSECTION)
}),*,
- ::core::ptr::null()
- ]);
+ None,
+ ];
&_SUBSECTIONS
}}
}
@@ -686,8 +674,9 @@ pub const fn fields(mut self, fields: &'static [VMStateField]) -> Self {
}
#[must_use]
- pub const fn subsections(mut self, subs: &'static VMStateSubsectionsWrapper) -> Self {
- self.0.subsections = subs.0.as_ptr();
+ pub const fn subsections(mut self, subs: &'static VMStateSubsections) -> Self {
+ let subs: *const Option<&bindings::VMStateDescription> = subs.as_ptr();
+ self.0.subsections = subs.cast::<*const bindings::VMStateDescription>();
self
}
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 22/28] rust: migration: validate termination of subsection arrays
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (20 preceding siblings ...)
2025-10-14 13:37 ` [PULL 21/28] rust: migration: do not store raw pointers into VMStateSubsectionsWrapper Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 23/28] rust: migration: extract vmstate_fields_ref Paolo Bonzini
` (6 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Zhao Liu
For consistency with fields(), validate the value (at least to some extent)
before passing it to C.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/migration/src/vmstate.rs | 3 +++
1 file changed, 3 insertions(+)
diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs
index a6ee7e93853..6b0f96c4da8 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -675,6 +675,9 @@ pub const fn fields(mut self, fields: &'static [VMStateField]) -> Self {
#[must_use]
pub const fn subsections(mut self, subs: &'static VMStateSubsections) -> Self {
+ if subs[subs.len() - 1].is_some() {
+ panic!("subsections are not terminated, use vmstate_subsections!");
+ }
let subs: *const Option<&bindings::VMStateDescription> = subs.as_ptr();
self.0.subsections = subs.cast::<*const bindings::VMStateDescription>();
self
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 23/28] rust: migration: extract vmstate_fields_ref
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (21 preceding siblings ...)
2025-10-14 13:37 ` [PULL 22/28] rust: migration: validate termination of subsection arrays Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 24/28] rust: move VMState from bql to migration Paolo Bonzini
` (5 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Zhao Liu
This is useful when building a VMState for generic structs, because you have
to avoid nested statics. Using vmstate_fields! will fail in the likely case
where the _FIELDS static uses Self from an outer item, because that is
forbidden.
The separate macros are needed because you cannot just do
.fields(vmstate_fields_ref! {
vmstate_of!(PL011State, clock),
})
The value returned by vmstate_fields_ref! is not promoted to static, which is
unfortunate but intentional (https://github.com/rust-lang/rust/issues/60502):
error[E0716]: temporary value dropped while borrowed
--> rust/hw/char/pl011/libpl011.rlib.p/structured/device.rs:743:17
|
738 | / VMStateDescriptionBuilder::<PL011State>::new()
739 | | .name(c"pl011/clock")
740 | | .version_id(1)
741 | | .minimum_version_id(1)
742 | | .needed(&PL011State::clock_needed)
743 | | .fields(vmstate_fields_ref! {
| | _________________^
744 | || vmstate_of!(PL011State, clock),
745 | || })
| ||_________^- argument requires that borrow lasts for `'static`
| |_________|
| creates a temporary value which is freed while still in use
746 | .build();
| - temporary value is freed at the end of this statement
Thus it is necessary to use the "static", whether explicitly or hidden by
vmstate_fields.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/migration/src/vmstate.rs | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs
index 6b0f96c4da8..59e665f6c3a 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -412,19 +412,30 @@ const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> {
}};
}
+/// Add a terminator to the fields in the arguments, and return
+/// a reference to the resulting array of values.
+#[macro_export]
+macro_rules! vmstate_fields_ref {
+ ($($field:expr),*$(,)*) => {
+ &[
+ $($field),*,
+ $crate::bindings::VMStateField {
+ flags: $crate::bindings::VMStateFlags::VMS_END,
+ ..::common::zeroable::Zeroable::ZERO
+ }
+ ]
+ }
+}
+
/// Helper macro to declare a list of
/// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return
/// a pointer to the array of values it created.
#[macro_export]
macro_rules! vmstate_fields {
($($field:expr),*$(,)*) => {{
- static _FIELDS: &[$crate::bindings::VMStateField] = &[
+ static _FIELDS: &[$crate::bindings::VMStateField] = $crate::vmstate_fields_ref!(
$($field),*,
- $crate::bindings::VMStateField {
- flags: $crate::bindings::VMStateFlags::VMS_END,
- ..::common::zeroable::Zeroable::ZERO
- }
- ];
+ );
_FIELDS
}}
}
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 24/28] rust: move VMState from bql to migration
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (22 preceding siblings ...)
2025-10-14 13:37 ` [PULL 23/28] rust: migration: extract vmstate_fields_ref Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 25/28] rust: migration: add high-level migration wrappers Paolo Bonzini
` (4 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Zhao Liu
The high-level wrapper Migratable<T> will contain a BqlCell,
which would introduce a circular dependency betwen the bql and
migration crates. Move the implementation of VMState for cells
to "migration", together with the implementation for std types.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/Cargo.lock | 2 +-
rust/bql/Cargo.toml | 1 -
rust/bql/meson.build | 1 -
rust/bql/src/cell.rs | 6 ------
rust/meson.build | 2 +-
rust/migration/Cargo.toml | 1 +
rust/migration/meson.build | 4 ++--
rust/migration/src/vmstate.rs | 2 ++
8 files changed, 7 insertions(+), 12 deletions(-)
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 11085133490..5c2f8ea9240 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -59,7 +59,6 @@ name = "bql"
version = "0.1.0"
dependencies = [
"glib-sys",
- "migration",
]
[[package]]
@@ -198,6 +197,7 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
name = "migration"
version = "0.1.0"
dependencies = [
+ "bql",
"common",
"glib-sys",
"util",
diff --git a/rust/bql/Cargo.toml b/rust/bql/Cargo.toml
index d5177e5f8e2..8fd81311028 100644
--- a/rust/bql/Cargo.toml
+++ b/rust/bql/Cargo.toml
@@ -13,7 +13,6 @@ repository.workspace = true
rust-version.workspace = true
[dependencies]
-migration = { path = "../migration" }
glib-sys.workspace = true
[features]
diff --git a/rust/bql/meson.build b/rust/bql/meson.build
index 22d7c9b8776..091372dd7b6 100644
--- a/rust/bql/meson.build
+++ b/rust/bql/meson.build
@@ -37,7 +37,6 @@ _bql_rs = static_library(
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
rust_args: _bql_cfg,
- link_with: [_migration_rs],
dependencies: [glib_sys_rs],
)
diff --git a/rust/bql/src/cell.rs b/rust/bql/src/cell.rs
index 54cfe6145c5..8ade7db629c 100644
--- a/rust/bql/src/cell.rs
+++ b/rust/bql/src/cell.rs
@@ -151,8 +151,6 @@
ptr::NonNull,
};
-use migration::impl_vmstate_transparent;
-
/// A mutable memory location that is protected by the Big QEMU Lock.
///
/// # Memory layout
@@ -364,8 +362,6 @@ pub fn take(&self) -> T {
}
}
-impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState);
-
/// A mutable memory location with dynamically checked borrow rules,
/// protected by the Big QEMU Lock.
///
@@ -691,8 +687,6 @@ fn from(t: T) -> BqlRefCell<T> {
}
}
-impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState);
-
struct BorrowRef<'b> {
borrow: &'b Cell<BorrowFlag>,
}
diff --git a/rust/meson.build b/rust/meson.build
index 6ba075c8c71..76e10699b37 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -29,8 +29,8 @@ subdir('qemu-macros')
subdir('common')
subdir('bits')
subdir('util')
-subdir('migration')
subdir('bql')
+subdir('migration')
subdir('qom')
subdir('system')
subdir('chardev')
diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml
index 94504f3625c..b995c4c8c88 100644
--- a/rust/migration/Cargo.toml
+++ b/rust/migration/Cargo.toml
@@ -13,6 +13,7 @@ repository.workspace = true
rust-version.workspace = true
[dependencies]
+bql = { path = "../bql" }
common = { path = "../common" }
util = { path = "../util" }
glib-sys.workspace = true
diff --git a/rust/migration/meson.build b/rust/migration/meson.build
index 18be65c92cf..845136239e8 100644
--- a/rust/migration/meson.build
+++ b/rust/migration/meson.build
@@ -37,12 +37,12 @@ _migration_rs = static_library(
),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
- link_with: [_util_rs],
+ link_with: [_util_rs, _bql_rs],
dependencies: [common_rs, glib_sys_rs],
)
migration_rs = declare_dependency(link_with: [_migration_rs],
- dependencies: [migration, qemuutil])
+ dependencies: [bql_rs, migration, qemuutil])
# Doctests are essentially integration tests, so they need the same dependencies.
# Note that running them requires the object files for C code, so place them
diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs
index 59e665f6c3a..445fe7fbc08 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -276,6 +276,8 @@ unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmsta
};
}
+impl_vmstate_transparent!(bql::BqlCell<T> where T: VMState);
+impl_vmstate_transparent!(bql::BqlRefCell<T> where T: VMState);
impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState);
impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState);
impl_vmstate_transparent!(std::pin::Pin<T> where T: VMState);
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 25/28] rust: migration: add high-level migration wrappers
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (23 preceding siblings ...)
2025-10-14 13:37 ` [PULL 24/28] rust: move VMState from bql to migration Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 26/28] rust: qemu-macros: add ToMigrationState derive macro Paolo Bonzini
` (3 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Zhao Liu
Instead of dealing with pre/post callbacks, allow devices to
implement a snapshot/restore mechanism; this has two main
advantages:
- it can be easily implemented via procedural macros
- there can be generic implementations to deal with various
kinds of interior-mutable containers, from BqlRefCell to Mutex,
so that C code does not see Rust concepts such as Mutex<>.
Using it is easy; you can implement the snapshot/restore trait
ToMigrationState and declare your state like:
regs: Migratable<Mutex<MyDeviceRegisters>>
Migratable<> allows dereferencing to the underlying object with
no run-time cost.
Note that Migratable<> actually does not accept ToMigrationState,
only the similar ToMigrationStateShared trait that the user will mostly
not care about. This is required by the fact that pre/post callbacks
take a &self, and ensures that the argument is a Mutex or BqlRefCell
(including an array or Arc<> thereof).
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
docs/devel/rust.rst | 1 +
rust/migration/meson.build | 1 +
rust/migration/src/lib.rs | 3 +
rust/migration/src/migratable.rs | 434 +++++++++++++++++++++++++++++++
4 files changed, 439 insertions(+)
create mode 100644 rust/migration/src/migratable.rs
diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index 2f0ab2e2821..79c26d9d165 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -155,6 +155,7 @@ module status
``hwcore::irq`` complete
``hwcore::qdev`` stable
``hwcore::sysbus`` stable
+``migration::migratable`` proof of concept
``migration::vmstate`` stable
``qom`` stable
``system::memory`` stable
diff --git a/rust/migration/meson.build b/rust/migration/meson.build
index 845136239e8..0d25455baa9 100644
--- a/rust/migration/meson.build
+++ b/rust/migration/meson.build
@@ -31,6 +31,7 @@ _migration_rs = static_library(
[
'src/lib.rs',
'src/bindings.rs',
+ 'src/migratable.rs',
'src/vmstate.rs',
],
{'.' : _migration_bindings_inc_rs},
diff --git a/rust/migration/src/lib.rs b/rust/migration/src/lib.rs
index 5f51dde4406..efe9896b619 100644
--- a/rust/migration/src/lib.rs
+++ b/rust/migration/src/lib.rs
@@ -2,5 +2,8 @@
pub mod bindings;
+pub mod migratable;
+pub use migratable::*;
+
pub mod vmstate;
pub use vmstate::*;
diff --git a/rust/migration/src/migratable.rs b/rust/migration/src/migratable.rs
new file mode 100644
index 00000000000..46e533c16d1
--- /dev/null
+++ b/rust/migration/src/migratable.rs
@@ -0,0 +1,434 @@
+// Copyright 2025 Red Hat, Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use std::{
+ fmt,
+ mem::size_of,
+ ptr::{self, addr_of, NonNull},
+ sync::{Arc, Mutex},
+};
+
+use bql::{BqlCell, BqlRefCell};
+use common::Zeroable;
+
+use crate::{
+ bindings, vmstate_fields_ref, vmstate_of, InvalidError, VMState, VMStateDescriptionBuilder,
+};
+
+/// Enables QEMU migration support even when a type is wrapped with
+/// synchronization primitives (like `Mutex`) that the C migration
+/// code cannot directly handle. The trait provides methods to
+/// extract essential state for migration and restore it after
+/// migration completes.
+///
+/// On top of extracting data from synchronization wrappers during save
+/// and restoring it during load, it's also possible to use `ToMigrationState`
+/// to convert runtime representations to migration-safe formats.
+///
+/// # Examples
+///
+/// ```
+/// use bql::BqlCell;
+/// use migration::{InvalidError, ToMigrationState, VMState};
+/// # use migration::VMStateField;
+///
+/// # #[derive(Debug, PartialEq, Eq)]
+/// struct DeviceState {
+/// counter: BqlCell<u32>,
+/// enabled: bool,
+/// }
+///
+/// # #[derive(Debug)]
+/// #[derive(Default)]
+/// struct DeviceMigrationState {
+/// counter: u32,
+/// enabled: bool,
+/// }
+///
+/// # unsafe impl VMState for DeviceMigrationState {
+/// # const BASE: VMStateField = ::common::Zeroable::ZERO;
+/// # }
+/// impl ToMigrationState for DeviceState {
+/// type Migrated = DeviceMigrationState;
+///
+/// fn snapshot_migration_state(
+/// &self,
+/// target: &mut Self::Migrated,
+/// ) -> Result<(), InvalidError> {
+/// target.counter = self.counter.get();
+/// target.enabled = self.enabled;
+/// Ok(())
+/// }
+///
+/// fn restore_migrated_state_mut(
+/// &mut self,
+/// source: Self::Migrated,
+/// _version_id: u8,
+/// ) -> Result<(), InvalidError> {
+/// self.counter.set(source.counter);
+/// self.enabled = source.enabled;
+/// Ok(())
+/// }
+/// }
+/// # bql::start_test();
+/// # let dev = DeviceState { counter: 10.into(), enabled: true };
+/// # let mig = dev.to_migration_state().unwrap();
+/// # assert!(matches!(*mig, DeviceMigrationState { counter: 10, enabled: true }));
+/// # let mut dev2 = DeviceState { counter: 42.into(), enabled: false };
+/// # dev2.restore_migrated_state_mut(*mig, 1).unwrap();
+/// # assert_eq!(dev2, dev);
+/// ```
+pub trait ToMigrationState {
+ /// The type used to represent the migrated state.
+ type Migrated: Default + VMState;
+
+ /// Capture the current state into a migration-safe format, failing
+ /// if the state cannot be migrated.
+ fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError>;
+
+ /// Restores state from a migrated representation, failing if the
+ /// state cannot be restored.
+ fn restore_migrated_state_mut(
+ &mut self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), InvalidError>;
+
+ /// Convenience method to combine allocation and state capture
+ /// into a single operation.
+ fn to_migration_state(&self) -> Result<Box<Self::Migrated>, InvalidError> {
+ let mut migrated = Box::<Self::Migrated>::default();
+ self.snapshot_migration_state(&mut migrated)?;
+ Ok(migrated)
+ }
+}
+
+// Implementations for primitive types. Do not use a blanket implementation
+// for all Copy types, because [T; N] is Copy if T is Copy; that would conflict
+// with the below implementation for arrays.
+macro_rules! impl_for_primitive {
+ ($($t:ty),*) => {
+ $(
+ impl ToMigrationState for $t {
+ type Migrated = Self;
+
+ fn snapshot_migration_state(
+ &self,
+ target: &mut Self::Migrated,
+ ) -> Result<(), InvalidError> {
+ *target = *self;
+ Ok(())
+ }
+
+ fn restore_migrated_state_mut(
+ &mut self,
+ source: Self::Migrated,
+ _version_id: u8,
+ ) -> Result<(), InvalidError> {
+ *self = source;
+ Ok(())
+ }
+ }
+ )*
+ };
+}
+
+impl_for_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, bool);
+
+impl<T: ToMigrationState, const N: usize> ToMigrationState for [T; N]
+where
+ [T::Migrated; N]: Default,
+{
+ type Migrated = [T::Migrated; N];
+
+ fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> {
+ for (item, target_item) in self.iter().zip(target.iter_mut()) {
+ item.snapshot_migration_state(target_item)?;
+ }
+ Ok(())
+ }
+
+ fn restore_migrated_state_mut(
+ &mut self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), InvalidError> {
+ for (item, source_item) in self.iter_mut().zip(source) {
+ item.restore_migrated_state_mut(source_item, version_id)?;
+ }
+ Ok(())
+ }
+}
+
+impl<T: ToMigrationState> ToMigrationState for Mutex<T> {
+ type Migrated = T::Migrated;
+
+ fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> {
+ self.lock().unwrap().snapshot_migration_state(target)
+ }
+
+ fn restore_migrated_state_mut(
+ &mut self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), InvalidError> {
+ self.get_mut()
+ .unwrap()
+ .restore_migrated_state_mut(source, version_id)
+ }
+}
+
+impl<T: ToMigrationState> ToMigrationState for BqlRefCell<T> {
+ type Migrated = T::Migrated;
+
+ fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> {
+ self.borrow().snapshot_migration_state(target)
+ }
+
+ fn restore_migrated_state_mut(
+ &mut self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), InvalidError> {
+ self.get_mut()
+ .restore_migrated_state_mut(source, version_id)
+ }
+}
+
+/// Extension trait for types that support migration state restoration
+/// through interior mutability.
+///
+/// This trait extends [`ToMigrationState`] for types that can restore
+/// their state without requiring mutable access. While user structs
+/// will generally use `ToMigrationState`, the device will have multiple
+/// references and therefore the device struct has to employ an interior
+/// mutability wrapper like [`Mutex`] or [`BqlRefCell`].
+///
+/// Anything that implements this trait can in turn be used within
+/// [`Migratable<T>`], which makes no assumptions on how to achieve mutable
+/// access to the runtime state.
+///
+/// # Examples
+///
+/// ```
+/// use std::sync::Mutex;
+///
+/// use migration::ToMigrationStateShared;
+///
+/// let device_state = Mutex::new(42);
+/// // Can restore without &mut access
+/// device_state.restore_migrated_state(100, 1).unwrap();
+/// assert_eq!(*device_state.lock().unwrap(), 100);
+/// ```
+pub trait ToMigrationStateShared: ToMigrationState {
+ /// Restores state from a migrated representation to an interior-mutable
+ /// object. Similar to `restore_migrated_state_mut`, but requires a
+ /// shared reference; therefore it can be used to restore a device's
+ /// state even though devices have multiple references to them.
+ fn restore_migrated_state(
+ &self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), InvalidError>;
+}
+
+impl<T: ToMigrationStateShared, const N: usize> ToMigrationStateShared for [T; N]
+where
+ [T::Migrated; N]: Default,
+{
+ fn restore_migrated_state(
+ &self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), InvalidError> {
+ for (item, source_item) in self.iter().zip(source) {
+ item.restore_migrated_state(source_item, version_id)?;
+ }
+ Ok(())
+ }
+}
+
+// Arc requires the contained object to be interior-mutable
+impl<T: ToMigrationStateShared> ToMigrationState for Arc<T> {
+ type Migrated = T::Migrated;
+
+ fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), InvalidError> {
+ (**self).snapshot_migration_state(target)
+ }
+
+ fn restore_migrated_state_mut(
+ &mut self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), InvalidError> {
+ (**self).restore_migrated_state(source, version_id)
+ }
+}
+
+impl<T: ToMigrationStateShared> ToMigrationStateShared for Arc<T> {
+ fn restore_migrated_state(
+ &self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), InvalidError> {
+ (**self).restore_migrated_state(source, version_id)
+ }
+}
+
+// Interior-mutable types. Note how they only require ToMigrationState for
+// the inner type!
+
+impl<T: ToMigrationState> ToMigrationStateShared for Mutex<T> {
+ fn restore_migrated_state(
+ &self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), InvalidError> {
+ self.lock()
+ .unwrap()
+ .restore_migrated_state_mut(source, version_id)
+ }
+}
+
+impl<T: ToMigrationState> ToMigrationStateShared for BqlRefCell<T> {
+ fn restore_migrated_state(
+ &self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), InvalidError> {
+ self.borrow_mut()
+ .restore_migrated_state_mut(source, version_id)
+ }
+}
+
+/// A wrapper that enables QEMU migration for types with shared state.
+///
+/// `Migratable<T>` provides a bridge between Rust types that use interior
+/// mutability (like `Mutex<T>`) and QEMU's C-based migration infrastructure.
+/// It manages the lifecycle of migration state and provides automatic
+/// conversion between runtime and migration representations.
+///
+/// ```ignore
+/// # use std::sync::Mutex;
+/// # use migration::Migratable;
+///
+/// pub struct DeviceRegs {
+/// status: u32,
+/// }
+///
+/// pub struct SomeDevice {
+/// // ...
+/// registers: Migratable<Mutex<DeviceRegs>>,
+/// }
+/// ```
+#[repr(C)]
+pub struct Migratable<T: ToMigrationStateShared> {
+ /// Pointer to migration state, valid only during migration operations.
+ /// C vmstate does not support NULL pointers, so no `Option<Box<>>`.
+ migration_state: BqlCell<*mut T::Migrated>,
+
+ /// The runtime state that can be accessed during normal operation
+ runtime_state: T,
+}
+
+impl<T: ToMigrationStateShared> std::ops::Deref for Migratable<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.runtime_state
+ }
+}
+
+impl<T: ToMigrationStateShared> std::ops::DerefMut for Migratable<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.runtime_state
+ }
+}
+
+impl<T: ToMigrationStateShared> Migratable<T> {
+ /// Creates a new `Migratable` wrapper around the given runtime state.
+ ///
+ /// # Returns
+ /// A new `Migratable` instance ready for use and migration
+ pub fn new(runtime_state: T) -> Self {
+ Self {
+ migration_state: BqlCell::new(ptr::null_mut()),
+ runtime_state,
+ }
+ }
+
+ fn pre_save(&self) -> Result<(), InvalidError> {
+ let state = self.runtime_state.to_migration_state()?;
+ self.migration_state.set(Box::into_raw(state));
+ Ok(())
+ }
+
+ fn post_save(&self) -> Result<(), InvalidError> {
+ let state = unsafe { Box::from_raw(self.migration_state.replace(ptr::null_mut())) };
+ drop(state);
+ Ok(())
+ }
+
+ fn pre_load(&self) -> Result<(), InvalidError> {
+ self.migration_state
+ .set(Box::into_raw(Box::<T::Migrated>::default()));
+ Ok(())
+ }
+
+ fn post_load(&self, version_id: u8) -> Result<(), InvalidError> {
+ let state = unsafe { Box::from_raw(self.migration_state.replace(ptr::null_mut())) };
+ self.runtime_state
+ .restore_migrated_state(*state, version_id)
+ }
+}
+
+impl<T: ToMigrationStateShared + fmt::Debug> fmt::Debug for Migratable<T>
+where
+ T::Migrated: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut struct_f = f.debug_struct("Migratable");
+ struct_f.field("runtime_state", &self.runtime_state);
+
+ let state = NonNull::new(self.migration_state.get()).map(|x| unsafe { x.as_ref() });
+ struct_f.field("migration_state", &state);
+ struct_f.finish()
+ }
+}
+
+impl<T: ToMigrationStateShared + Default> Default for Migratable<T> {
+ fn default() -> Self {
+ Self::new(T::default())
+ }
+}
+
+impl<T: 'static + ToMigrationStateShared> Migratable<T> {
+ const FIELD: bindings::VMStateField = vmstate_of!(Self, migration_state);
+
+ const FIELDS: &[bindings::VMStateField] = vmstate_fields_ref! {
+ Migratable::<T>::FIELD
+ };
+
+ const VMSD: &'static bindings::VMStateDescription = VMStateDescriptionBuilder::<Self>::new()
+ .version_id(1)
+ .minimum_version_id(1)
+ .pre_save(&Self::pre_save)
+ .pre_load(&Self::pre_load)
+ .post_save(&Self::post_save)
+ .post_load(&Self::post_load)
+ .fields(Self::FIELDS)
+ .build()
+ .as_ref();
+}
+
+unsafe impl<T: 'static + ToMigrationStateShared> VMState for Migratable<T> {
+ const BASE: bindings::VMStateField = {
+ bindings::VMStateField {
+ vmsd: addr_of!(*Self::VMSD),
+ size: size_of::<Self>(),
+ flags: bindings::VMStateFlags::VMS_STRUCT,
+ ..Zeroable::ZERO
+ }
+ };
+}
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 26/28] rust: qemu-macros: add ToMigrationState derive macro
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (24 preceding siblings ...)
2025-10-14 13:37 ` [PULL 25/28] rust: migration: add high-level migration wrappers Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 27/28] timer: constify some functions Paolo Bonzini
` (2 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Zhao Liu
Add a macro that recursively builds the "migrated" version
of a struct.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/Cargo.lock | 1 +
rust/migration/Cargo.toml | 1 +
rust/migration/meson.build | 2 +-
rust/migration/src/lib.rs | 2 +
rust/migration/src/migratable.rs | 12 +-
rust/qemu-macros/src/lib.rs | 88 +++++++
rust/qemu-macros/src/migration_state.rs | 298 ++++++++++++++++++++++++
rust/qemu-macros/src/tests.rs | 113 ++++++++-
8 files changed, 512 insertions(+), 5 deletions(-)
create mode 100644 rust/qemu-macros/src/migration_state.rs
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 5c2f8ea9240..0c1df625df1 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -200,6 +200,7 @@ dependencies = [
"bql",
"common",
"glib-sys",
+ "qemu_macros",
"util",
]
diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml
index b995c4c8c88..415457496d6 100644
--- a/rust/migration/Cargo.toml
+++ b/rust/migration/Cargo.toml
@@ -15,6 +15,7 @@ rust-version.workspace = true
[dependencies]
bql = { path = "../bql" }
common = { path = "../common" }
+qemu_macros = { path = "../qemu-macros" }
util = { path = "../util" }
glib-sys.workspace = true
diff --git a/rust/migration/meson.build b/rust/migration/meson.build
index 0d25455baa9..444494700ad 100644
--- a/rust/migration/meson.build
+++ b/rust/migration/meson.build
@@ -39,7 +39,7 @@ _migration_rs = static_library(
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
link_with: [_util_rs, _bql_rs],
- dependencies: [common_rs, glib_sys_rs],
+ dependencies: [common_rs, glib_sys_rs, qemu_macros],
)
migration_rs = declare_dependency(link_with: [_migration_rs],
diff --git a/rust/migration/src/lib.rs b/rust/migration/src/lib.rs
index efe9896b619..c9bdf0d4133 100644
--- a/rust/migration/src/lib.rs
+++ b/rust/migration/src/lib.rs
@@ -2,6 +2,8 @@
pub mod bindings;
+pub use qemu_macros::ToMigrationState;
+
pub mod migratable;
pub use migratable::*;
diff --git a/rust/migration/src/migratable.rs b/rust/migration/src/migratable.rs
index 46e533c16d1..ded6fe8f4a6 100644
--- a/rust/migration/src/migratable.rs
+++ b/rust/migration/src/migratable.rs
@@ -79,6 +79,10 @@
/// # dev2.restore_migrated_state_mut(*mig, 1).unwrap();
/// # assert_eq!(dev2, dev);
/// ```
+///
+/// More commonly, the trait is derived through the
+/// [`derive(ToMigrationState)`](qemu_macros::ToMigrationState) procedural
+/// macro.
pub trait ToMigrationState {
/// The type used to represent the migrated state.
type Migrated: Default + VMState;
@@ -309,13 +313,17 @@ fn restore_migrated_state(
/// It manages the lifecycle of migration state and provides automatic
/// conversion between runtime and migration representations.
///
-/// ```ignore
+/// ```
/// # use std::sync::Mutex;
-/// # use migration::Migratable;
+/// # use migration::{Migratable, ToMigrationState, VMState, VMStateField};
///
+/// #[derive(ToMigrationState)]
/// pub struct DeviceRegs {
/// status: u32,
/// }
+/// # unsafe impl VMState for DeviceRegsMigration {
+/// # const BASE: VMStateField = ::common::Zeroable::ZERO;
+/// # }
///
/// pub struct SomeDevice {
/// // ...
diff --git a/rust/qemu-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs
index 3bf315c4c0a..50239f228be 100644
--- a/rust/qemu-macros/src/lib.rs
+++ b/rust/qemu-macros/src/lib.rs
@@ -13,9 +13,13 @@
Attribute, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token,
Variant,
};
+
mod bits;
use bits::BitsConstInternal;
+mod migration_state;
+use migration_state::MigrationStateDerive;
+
#[cfg(test)]
mod tests;
@@ -412,3 +416,87 @@ pub fn bits_const_internal(ts: TokenStream) -> TokenStream {
}
.into()
}
+
+/// Derive macro for generating migration state structures and trait
+/// implementations.
+///
+/// This macro generates a migration state struct and implements the
+/// `ToMigrationState` trait for the annotated struct, enabling state
+/// serialization and restoration. Note that defining a `VMStateDescription`
+/// for the migration state struct is left to the user.
+///
+/// # Container attributes
+///
+/// The following attributes can be applied to the struct:
+///
+/// - `#[migration_state(rename = CustomName)]` - Customizes the name of the
+/// generated migration struct. By default, the generated struct is named
+/// `{OriginalName}Migration`.
+///
+/// # Field attributes
+///
+/// The following attributes can be applied to individual fields:
+///
+/// - `#[migration_state(omit)]` - Excludes the field from the migration state
+/// entirely.
+///
+/// - `#[migration_state(into(Type))]` - Converts the field using `.into()`
+/// during both serialization and restoration.
+///
+/// - `#[migration_state(try_into(Type))]` - Converts the field using
+/// `.try_into()` during both serialization and restoration. Returns
+/// `InvalidError` on conversion failure.
+///
+/// - `#[migration_state(clone)]` - Clones the field value.
+///
+/// Fields without any attributes use `ToMigrationState` recursively; note that
+/// this is a simple copy for types that implement `Copy`.
+///
+/// # Attribute compatibility
+///
+/// - `omit` cannot be used with any other attributes
+/// - only one of `into(Type)`, `try_into(Type)` can be used, but they can be
+/// coupled with `clone`.
+///
+/// # Examples
+///
+/// Basic usage:
+/// ```ignore
+/// #[derive(ToMigrationState)]
+/// struct MyStruct {
+/// field1: u32,
+/// field2: Timer,
+/// }
+/// ```
+///
+/// With attributes:
+/// ```ignore
+/// #[derive(ToMigrationState)]
+/// #[migration_state(rename = CustomMigration)]
+/// struct MyStruct {
+/// #[migration_state(omit)]
+/// runtime_field: u32,
+///
+/// #[migration_state(clone)]
+/// shared_data: String,
+///
+/// #[migration_state(into(Cow<'static, str>), clone)]
+/// converted_field: String,
+///
+/// #[migration_state(try_into(i8))]
+/// fallible_field: u32,
+///
+/// // Default: use ToMigrationState trait recursively
+/// nested_field: NestedStruct,
+///
+/// // Primitive types have a default implementation of ToMigrationState
+/// simple_field: u32,
+/// }
+/// ```
+#[proc_macro_derive(ToMigrationState, attributes(migration_state))]
+pub fn derive_to_migration_state(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ MigrationStateDerive::expand(input)
+ .unwrap_or_else(syn::Error::into_compile_error)
+ .into()
+}
diff --git a/rust/qemu-macros/src/migration_state.rs b/rust/qemu-macros/src/migration_state.rs
new file mode 100644
index 00000000000..5edf0efe687
--- /dev/null
+++ b/rust/qemu-macros/src/migration_state.rs
@@ -0,0 +1,298 @@
+use std::borrow::Cow;
+
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote, ToTokens};
+use syn::{spanned::Spanned, DeriveInput, Error, Field, Ident, Result, Type};
+
+use crate::get_fields;
+
+#[derive(Debug, Default)]
+enum ConversionMode {
+ #[default]
+ None,
+ Omit,
+ Into(Type),
+ TryInto(Type),
+ ToMigrationState,
+}
+
+impl ConversionMode {
+ fn target_type(&self, original_type: &Type) -> TokenStream {
+ match self {
+ ConversionMode::Into(ty) | ConversionMode::TryInto(ty) => ty.to_token_stream(),
+ ConversionMode::ToMigrationState => {
+ quote! { <#original_type as ToMigrationState>::Migrated }
+ }
+ _ => original_type.to_token_stream(),
+ }
+ }
+}
+
+#[derive(Debug, Default)]
+struct ContainerAttrs {
+ rename: Option<Ident>,
+}
+
+impl ContainerAttrs {
+ fn parse_from(&mut self, attrs: &[syn::Attribute]) -> Result<()> {
+ use attrs::{set, with, Attrs};
+ Attrs::new()
+ .once("rename", with::eq(set::parse(&mut self.rename)))
+ .parse_attrs("migration_state", attrs)?;
+ Ok(())
+ }
+
+ fn parse(attrs: &[syn::Attribute]) -> Result<Self> {
+ let mut container_attrs = Self::default();
+ container_attrs.parse_from(attrs)?;
+ Ok(container_attrs)
+ }
+}
+
+#[derive(Debug, Default)]
+struct FieldAttrs {
+ conversion: ConversionMode,
+ clone: bool,
+}
+
+impl FieldAttrs {
+ fn parse_from(&mut self, attrs: &[syn::Attribute]) -> Result<()> {
+ let mut omit_flag = false;
+ let mut into_type: Option<Type> = None;
+ let mut try_into_type: Option<Type> = None;
+
+ use attrs::{set, with, Attrs};
+ Attrs::new()
+ .once("omit", set::flag(&mut omit_flag))
+ .once("into", with::paren(set::parse(&mut into_type)))
+ .once("try_into", with::paren(set::parse(&mut try_into_type)))
+ .once("clone", set::flag(&mut self.clone))
+ .parse_attrs("migration_state", attrs)?;
+
+ self.conversion = match (omit_flag, into_type, try_into_type, self.clone) {
+ // Valid combinations of attributes first...
+ (true, None, None, false) => ConversionMode::Omit,
+ (false, Some(ty), None, _) => ConversionMode::Into(ty),
+ (false, None, Some(ty), _) => ConversionMode::TryInto(ty),
+ (false, None, None, true) => ConversionMode::None, // clone without conversion
+ (false, None, None, false) => ConversionMode::ToMigrationState, // default behavior
+
+ // ... then the error cases
+ (true, _, _, _) => {
+ return Err(Error::new(
+ attrs[0].span(),
+ "ToMigrationState: omit cannot be used with other attributes",
+ ));
+ }
+ (_, Some(_), Some(_), _) => {
+ return Err(Error::new(
+ attrs[0].span(),
+ "ToMigrationState: into and try_into attributes cannot be used together",
+ ));
+ }
+ };
+
+ Ok(())
+ }
+
+ fn parse(attrs: &[syn::Attribute]) -> Result<Self> {
+ let mut field_attrs = Self::default();
+ field_attrs.parse_from(attrs)?;
+ Ok(field_attrs)
+ }
+}
+
+#[derive(Debug)]
+struct MigrationStateField {
+ name: Ident,
+ original_type: Type,
+ attrs: FieldAttrs,
+}
+
+impl MigrationStateField {
+ fn maybe_clone(&self, mut value: TokenStream) -> TokenStream {
+ if self.attrs.clone {
+ value = quote! { #value.clone() };
+ }
+ value
+ }
+
+ fn generate_migration_state_field(&self) -> TokenStream {
+ let name = &self.name;
+ let field_type = self.attrs.conversion.target_type(&self.original_type);
+
+ quote! {
+ pub #name: #field_type,
+ }
+ }
+
+ fn generate_snapshot_field(&self) -> TokenStream {
+ let name = &self.name;
+ let value = self.maybe_clone(quote! { self.#name });
+
+ match &self.attrs.conversion {
+ ConversionMode::Omit => {
+ unreachable!("Omitted fields are filtered out during processing")
+ }
+ ConversionMode::None => quote! {
+ target.#name = #value;
+ },
+ ConversionMode::Into(_) => quote! {
+ target.#name = #value.into();
+ },
+ ConversionMode::TryInto(_) => quote! {
+ target.#name = #value.try_into().map_err(|_| migration::InvalidError)?;
+ },
+ ConversionMode::ToMigrationState => quote! {
+ self.#name.snapshot_migration_state(&mut target.#name)?;
+ },
+ }
+ }
+
+ fn generate_restore_field(&self) -> TokenStream {
+ let name = &self.name;
+
+ match &self.attrs.conversion {
+ ConversionMode::Omit => {
+ unreachable!("Omitted fields are filtered out during processing")
+ }
+ ConversionMode::None => quote! {
+ self.#name = #name;
+ },
+ ConversionMode::Into(_) => quote! {
+ self.#name = #name.into();
+ },
+ ConversionMode::TryInto(_) => quote! {
+ self.#name = #name.try_into().map_err(|_| migration::InvalidError)?;
+ },
+ ConversionMode::ToMigrationState => quote! {
+ self.#name.restore_migrated_state_mut(#name, _version_id)?;
+ },
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct MigrationStateDerive {
+ input: DeriveInput,
+ fields: Vec<MigrationStateField>,
+ container_attrs: ContainerAttrs,
+}
+
+impl MigrationStateDerive {
+ fn parse(input: DeriveInput) -> Result<Self> {
+ let container_attrs = ContainerAttrs::parse(&input.attrs)?;
+ let fields = get_fields(&input, "ToMigrationState")?;
+ let fields = Self::process_fields(fields)?;
+
+ Ok(Self {
+ input,
+ fields,
+ container_attrs,
+ })
+ }
+
+ fn process_fields(
+ fields: &syn::punctuated::Punctuated<Field, syn::token::Comma>,
+ ) -> Result<Vec<MigrationStateField>> {
+ let processed = fields
+ .iter()
+ .map(|field| {
+ let attrs = FieldAttrs::parse(&field.attrs)?;
+ Ok((field, attrs))
+ })
+ .collect::<Result<Vec<_>>>()?
+ .into_iter()
+ .filter(|(_, attrs)| !matches!(attrs.conversion, ConversionMode::Omit))
+ .map(|(field, attrs)| MigrationStateField {
+ name: field.ident.as_ref().unwrap().clone(),
+ original_type: field.ty.clone(),
+ attrs,
+ })
+ .collect();
+
+ Ok(processed)
+ }
+
+ fn migration_state_name(&self) -> Cow<'_, Ident> {
+ match &self.container_attrs.rename {
+ Some(rename) => Cow::Borrowed(rename),
+ None => Cow::Owned(format_ident!("{}Migration", &self.input.ident)),
+ }
+ }
+
+ fn generate_migration_state_struct(&self) -> TokenStream {
+ let name = self.migration_state_name();
+ let fields = self
+ .fields
+ .iter()
+ .map(MigrationStateField::generate_migration_state_field);
+
+ quote! {
+ #[derive(Default)]
+ pub struct #name {
+ #(#fields)*
+ }
+ }
+ }
+
+ fn generate_snapshot_migration_state(&self) -> TokenStream {
+ let fields = self
+ .fields
+ .iter()
+ .map(MigrationStateField::generate_snapshot_field);
+
+ quote! {
+ fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), migration::InvalidError> {
+ #(#fields)*
+ Ok(())
+ }
+ }
+ }
+
+ fn generate_restore_migrated_state(&self) -> TokenStream {
+ let names: Vec<_> = self.fields.iter().map(|f| &f.name).collect();
+ let fields = self
+ .fields
+ .iter()
+ .map(MigrationStateField::generate_restore_field);
+
+ // version_id could be used or not depending on conversion attributes
+ quote! {
+ #[allow(clippy::used_underscore_binding)]
+ fn restore_migrated_state_mut(&mut self, source: Self::Migrated, _version_id: u8) -> Result<(), migration::InvalidError> {
+ let Self::Migrated { #(#names),* } = source;
+ #(#fields)*
+ Ok(())
+ }
+ }
+ }
+
+ fn generate(&self) -> TokenStream {
+ let struct_name = &self.input.ident;
+ let generics = &self.input.generics;
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+ let name = self.migration_state_name();
+ let migration_state_struct = self.generate_migration_state_struct();
+ let snapshot_impl = self.generate_snapshot_migration_state();
+ let restore_impl = self.generate_restore_migrated_state();
+
+ quote! {
+ #migration_state_struct
+
+ impl #impl_generics ToMigrationState for #struct_name #ty_generics #where_clause {
+ type Migrated = #name;
+
+ #snapshot_impl
+
+ #restore_impl
+ }
+ }
+ }
+
+ pub fn expand(input: DeriveInput) -> Result<TokenStream> {
+ let tokens = Self::parse(input)?.generate();
+ Ok(tokens)
+ }
+}
diff --git a/rust/qemu-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs
index ac998d20e30..65691412ff5 100644
--- a/rust/qemu-macros/src/tests.rs
+++ b/rust/qemu-macros/src/tests.rs
@@ -7,7 +7,7 @@
use super::*;
macro_rules! derive_compile_fail {
- ($derive_fn:ident, $input:expr, $($error_msg:expr),+ $(,)?) => {{
+ ($derive_fn:path, $input:expr, $($error_msg:expr),+ $(,)?) => {{
let input: proc_macro2::TokenStream = $input;
let error_msg = &[$( quote! { ::core::compile_error! { $error_msg } } ),*];
let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> =
@@ -24,7 +24,7 @@ macro_rules! derive_compile_fail {
}
macro_rules! derive_compile {
- ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{
+ ($derive_fn:path, $input:expr, $($expected:tt)*) => {{
let input: proc_macro2::TokenStream = $input;
let expected: proc_macro2::TokenStream = $($expected)*;
let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> =
@@ -345,3 +345,112 @@ fn try_from(value: u8) -> Result<Self, u8> {
}
);
}
+
+#[test]
+fn test_derive_to_migration_state() {
+ derive_compile_fail!(
+ MigrationStateDerive::expand,
+ quote! {
+ struct MyStruct {
+ #[migration_state(omit, clone)]
+ bad: u32,
+ }
+ },
+ "ToMigrationState: omit cannot be used with other attributes"
+ );
+ derive_compile_fail!(
+ MigrationStateDerive::expand,
+ quote! {
+ struct MyStruct {
+ #[migration_state(into)]
+ bad: u32,
+ }
+ },
+ "unexpected end of input, expected parentheses"
+ );
+ derive_compile_fail!(
+ MigrationStateDerive::expand,
+ quote! {
+ struct MyStruct {
+ #[migration_state(into(String), try_into(String))]
+ bad: &'static str,
+ }
+ },
+ "ToMigrationState: into and try_into attributes cannot be used together"
+ );
+ derive_compile!(
+ MigrationStateDerive::expand,
+ quote! {
+ #[migration_state(rename = CustomMigration)]
+ struct MyStruct {
+ #[migration_state(omit)]
+ runtime_field: u32,
+
+ #[migration_state(clone)]
+ shared_data: String,
+
+ #[migration_state(into(Cow<'static, str>), clone)]
+ converted_field: String,
+
+ #[migration_state(try_into(i8))]
+ fallible_field: u32,
+
+ nested_field: NestedStruct,
+ simple_field: u32,
+ }
+ },
+ quote! {
+ #[derive(Default)]
+ pub struct CustomMigration {
+ pub shared_data: String,
+ pub converted_field: Cow<'static, str>,
+ pub fallible_field: i8,
+ pub nested_field: <NestedStruct as ToMigrationState>::Migrated,
+ pub simple_field: <u32 as ToMigrationState>::Migrated,
+ }
+ impl ToMigrationState for MyStruct {
+ type Migrated = CustomMigration;
+ fn snapshot_migration_state(
+ &self,
+ target: &mut Self::Migrated
+ ) -> Result<(), migration::InvalidError> {
+ target.shared_data = self.shared_data.clone();
+ target.converted_field = self.converted_field.clone().into();
+ target.fallible_field = self
+ .fallible_field
+ .try_into()
+ .map_err(|_| migration::InvalidError)?;
+ self.nested_field
+ .snapshot_migration_state(&mut target.nested_field)?;
+ self.simple_field
+ .snapshot_migration_state(&mut target.simple_field)?;
+ Ok(())
+ }
+ #[allow(clippy::used_underscore_binding)]
+ fn restore_migrated_state_mut(
+ &mut self,
+ source: Self::Migrated,
+ _version_id: u8
+ ) -> Result<(), migration::InvalidError> {
+ let Self::Migrated {
+ shared_data,
+ converted_field,
+ fallible_field,
+ nested_field,
+ simple_field
+ } = source;
+ self.shared_data = shared_data;
+ self.converted_field = converted_field.into();
+ self.fallible_field = fallible_field
+ .try_into()
+ .map_err(|_| migration::InvalidError)?;
+ self.nested_field
+ .restore_migrated_state_mut(nested_field, _version_id)?;
+ self.simple_field
+ .restore_migrated_state_mut(simple_field, _version_id)?;
+ Ok(())
+ }
+ }
+ }
+ );
+}
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 27/28] timer: constify some functions
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (25 preceding siblings ...)
2025-10-14 13:37 ` [PULL 26/28] rust: qemu-macros: add ToMigrationState derive macro Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 13:37 ` [PULL 28/28] rust: migration: implement ToMigrationState as part of impl_vmstate_bitsized Paolo Bonzini
2025-10-14 19:46 ` [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Richard Henderson
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Zhao Liu
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/qemu/timer.h | 6 +++---
util/qemu-timer.c | 8 ++++----
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/include/qemu/timer.h b/include/qemu/timer.h
index abd2204f3be..aec730ac257 100644
--- a/include/qemu/timer.h
+++ b/include/qemu/timer.h
@@ -699,7 +699,7 @@ void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time);
*
* Returns: true if the timer is pending
*/
-bool timer_pending(QEMUTimer *ts);
+bool timer_pending(const QEMUTimer *ts);
/**
* timer_expired:
@@ -710,7 +710,7 @@ bool timer_pending(QEMUTimer *ts);
*
* Returns: true if the timer has expired
*/
-bool timer_expired(QEMUTimer *timer_head, int64_t current_time);
+bool timer_expired(const QEMUTimer *timer_head, int64_t current_time);
/**
* timer_expire_time_ns:
@@ -720,7 +720,7 @@ bool timer_expired(QEMUTimer *timer_head, int64_t current_time);
*
* Returns: the expiry time in nanoseconds
*/
-uint64_t timer_expire_time_ns(QEMUTimer *ts);
+uint64_t timer_expire_time_ns(const QEMUTimer *ts);
/**
* timer_get:
diff --git a/util/qemu-timer.c b/util/qemu-timer.c
index 1fb48be281a..56f11b6a641 100644
--- a/util/qemu-timer.c
+++ b/util/qemu-timer.c
@@ -89,7 +89,7 @@ static inline QEMUClock *qemu_clock_ptr(QEMUClockType type)
return &qemu_clocks[type];
}
-static bool timer_expired_ns(QEMUTimer *timer_head, int64_t current_time)
+static bool timer_expired_ns(const QEMUTimer *timer_head, int64_t current_time)
{
return timer_head && (timer_head->expire_time <= current_time);
}
@@ -475,12 +475,12 @@ void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time)
timer_mod_anticipate_ns(ts, expire_time * ts->scale);
}
-bool timer_pending(QEMUTimer *ts)
+bool timer_pending(const QEMUTimer *ts)
{
return ts->expire_time >= 0;
}
-bool timer_expired(QEMUTimer *timer_head, int64_t current_time)
+bool timer_expired(const QEMUTimer *timer_head, int64_t current_time)
{
return timer_expired_ns(timer_head, current_time * timer_head->scale);
}
@@ -649,7 +649,7 @@ void init_clocks(QEMUTimerListNotifyCB *notify_cb)
#endif
}
-uint64_t timer_expire_time_ns(QEMUTimer *ts)
+uint64_t timer_expire_time_ns(const QEMUTimer *ts)
{
return timer_pending(ts) ? ts->expire_time : -1;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PULL 28/28] rust: migration: implement ToMigrationState as part of impl_vmstate_bitsized
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (26 preceding siblings ...)
2025-10-14 13:37 ` [PULL 27/28] timer: constify some functions Paolo Bonzini
@ 2025-10-14 13:37 ` Paolo Bonzini
2025-10-14 19:46 ` [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Richard Henderson
28 siblings, 0 replies; 32+ messages in thread
From: Paolo Bonzini @ 2025-10-14 13:37 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Zhao Liu
This is most likely desirable, and is the easiest way to migrate
a bit-sized value without peeking at the innards of the bilge crate.
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/migration/src/vmstate.rs | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs
index 445fe7fbc08..42e5df8d818 100644
--- a/rust/migration/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -296,6 +296,25 @@ unsafe impl $crate::vmstate::VMState for $type {
as ::bilge::prelude::Number>::UnderlyingType
as $crate::vmstate::VMState>::VARRAY_FLAG;
}
+
+ impl $crate::migratable::ToMigrationState for $type {
+ type Migrated = <<$type as ::bilge::prelude::Bitsized>::ArbitraryInt
+ as ::bilge::prelude::Number>::UnderlyingType;
+
+ fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), $crate::InvalidError> {
+ *target = Self::Migrated::from(*self);
+ Ok(())
+ }
+
+ fn restore_migrated_state_mut(
+ &mut self,
+ source: Self::Migrated,
+ version_id: u8,
+ ) -> Result<(), $crate::InvalidError> {
+ *self = Self::from(source);
+ Ok(())
+ }
+ }
};
}
--
2.51.0
^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
` (27 preceding siblings ...)
2025-10-14 13:37 ` [PULL 28/28] rust: migration: implement ToMigrationState as part of impl_vmstate_bitsized Paolo Bonzini
@ 2025-10-14 19:46 ` Richard Henderson
28 siblings, 0 replies; 32+ messages in thread
From: Richard Henderson @ 2025-10-14 19:46 UTC (permalink / raw)
To: qemu-devel
On 10/14/25 06:35, Paolo Bonzini wrote:
> The following changes since commit 94474a7733a57365d5a27efc28c05462e90e8944:
>
> Merge tag 'pull-loongarch-20251009' ofhttps://github.com/gaosong715/qemu into staging (2025-10-09 07:59:29 -0700)
>
> are available in the Git repository at:
>
> https://gitlab.com/bonzini/qemu.git tags/for-upstream
>
> for you to fetch changes up to 7ee5875d423598ac55a0b55881d9a1ee5c3c7daf:
>
> rust: migration: implement ToMigrationState as part of impl_vmstate_bitsized (2025-10-14 14:43:54 +0200)
>
> ----------------------------------------------------------------
> * rust: fix nightly warnings
> * target/i386: a smattering of fixes
> * monitor: add "info accelerators"
> * kvm: cleanups to kvm_cpu_synchronize_put()
> * target/i386: Add TSA attack variants and verw-clear feature flag
> * async: tsan bottom half fixes
> * rust: migration state wrappers with support for BQL-free devices
Applied, thanks. Please update https://wiki.qemu.org/ChangeLog/10.2 as appropriate.
r~
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [EXTERNAL] [PULL 16/28] monitor: generalize query-mshv/"info mshv" to query-accelerators/"info accelerators"
2025-10-14 13:37 ` [PULL 16/28] monitor: generalize query-mshv/"info mshv" to query-accelerators/"info accelerators" Paolo Bonzini
@ 2025-10-14 21:16 ` Praveen Paladugu
0 siblings, 0 replies; 32+ messages in thread
From: Praveen Paladugu @ 2025-10-14 21:16 UTC (permalink / raw)
To: Paolo Bonzini, qemu-devel@nongnu.org
Cc: Richard Henderson, Magnus Kulke, Markus Armbruster,
Daniel P. Berrangé
<div class="elementToProof" style="color: rgb(0, 0, 0);">Reviewed-by: Praveen K Paladugu <prapal@microsoft.com></div><div class="elementToProof" style="color: rgb(0, 0, 0);"><br></div><div class="elementToProof" style="color: rgb(0, 0, 0);">Regards,</div><div class="elementToProof" style="color: rgb(0, 0, 0);">Praveen</div>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PULL 09/28] i386/cpu: Prevent delivering SIPI during SMM in TCG mode
2025-10-14 13:36 ` [PULL 09/28] i386/cpu: Prevent delivering SIPI during SMM in TCG mode Paolo Bonzini
@ 2025-10-23 14:10 ` Peter Maydell
0 siblings, 0 replies; 32+ messages in thread
From: Peter Maydell @ 2025-10-23 14:10 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, Richard Henderson, YiFei Zhu, qemu-stable
On Tue, 14 Oct 2025 at 14:38, Paolo Bonzini <pbonzini@redhat.com> wrote:
>
> [commit message by YiFei Zhu]
>
> A malicious kernel may control the instruction pointer in SMM in a
> multi-processor VM by sending a sequence of IPIs via APIC:
>
> CPU0 CPU1
> IPI(CPU1, MODE_INIT)
> x86_cpu_exec_reset()
> apic_init_reset()
> s->wait_for_sipi = true
> IPI(CPU1, MODE_SMI)
> do_smm_enter()
> env->hflags |= HF_SMM_MASK;
> IPI(CPU1, MODE_STARTUP, vector)
> do_cpu_sipi()
> apic_sipi()
> /* s->wait_for_sipi check passes */
> cpu_x86_load_seg_cache_sipi(vector)
>
> A different sequence, SMI INIT SIPI, is also buggy in TCG because
> INIT is not blocked or latched during SMM. However, it is not
> vulnerable to an instruction pointer control in the same way because
> x86_cpu_exec_reset clears env->hflags, exiting SMM.
>
> Fixes: a9bad65d2c1f ("target-i386: wake up processors that receive an SMI")
> Analyzed-by: YiFei Zhu <zhuyifei@google.com>
> Cc: qemu-stable@nongnu.org
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
FYI, a bug report that just landed has bisected this commit
as the apparent cause of WHPX SMP boot no longer working:
https://gitlab.com/qemu-project/qemu/-/issues/3178
thanks
-- PMM
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2025-10-23 14:12 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-14 13:35 [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Paolo Bonzini
2025-10-14 13:36 ` [PULL 01/28] rust: pl011: fix warning with new clippy Paolo Bonzini
2025-10-14 13:36 ` [PULL 02/28] rust: bits: disable double_parens check Paolo Bonzini
2025-10-14 13:36 ` [PULL 03/28] rust: migration: hide more warnings from call_func_with_field! Paolo Bonzini
2025-10-14 13:36 ` [PULL 04/28] rust: hpet: fix fw_cfg handling Paolo Bonzini
2025-10-14 13:36 ` [PULL 05/28] target/i386: Add TSA attack variants TSA-SQ and TSA-L1 Paolo Bonzini
2025-10-14 13:36 ` [PULL 06/28] target/i386: Add TSA feature flag verw-clear Paolo Bonzini
2025-10-14 13:36 ` [PULL 07/28] target/i386: Fix CR2 handling for non-canonical addresses Paolo Bonzini
2025-10-14 13:36 ` [PULL 08/28] i386/kvm: Expose ARCH_CAP_FB_CLEAR when invulnerable to MDS Paolo Bonzini
2025-10-14 13:36 ` [PULL 09/28] i386/cpu: Prevent delivering SIPI during SMM in TCG mode Paolo Bonzini
2025-10-23 14:10 ` Peter Maydell
2025-10-14 13:36 ` [PULL 10/28] i386/tcg/smm_helper: Properly apply DR values on SMM entry / exit Paolo Bonzini
2025-10-14 13:36 ` [PULL 11/28] target/i386: fix x86_64 pushw op Paolo Bonzini
2025-10-14 13:36 ` [PULL 12/28] target/i386: fix access to the T bit of the TSS Paolo Bonzini
2025-10-14 13:36 ` [PULL 13/28] async: access bottom half flags with qatomic_read Paolo Bonzini
2025-10-14 13:36 ` [PULL 14/28] target/i386: user: do not set up a valid LDT on reset Paolo Bonzini
2025-10-14 13:37 ` [PULL 15/28] monitor: clarify "info accel" help message Paolo Bonzini
2025-10-14 13:37 ` [PULL 16/28] monitor: generalize query-mshv/"info mshv" to query-accelerators/"info accelerators" Paolo Bonzini
2025-10-14 21:16 ` [EXTERNAL] " Praveen Paladugu
2025-10-14 13:37 ` [PULL 17/28] accel/kvm: Introduce KvmPutState enum Paolo Bonzini
2025-10-14 13:37 ` [PULL 18/28] accel/kvm: Factor kvm_cpu_synchronize_put() out Paolo Bonzini
2025-10-14 13:37 ` [PULL 19/28] rust: bql: add BqlRefCell::get_mut() Paolo Bonzini
2025-10-14 13:37 ` [PULL 20/28] rust: migration: do not pass raw pointer to VMStateDescription::fields Paolo Bonzini
2025-10-14 13:37 ` [PULL 21/28] rust: migration: do not store raw pointers into VMStateSubsectionsWrapper Paolo Bonzini
2025-10-14 13:37 ` [PULL 22/28] rust: migration: validate termination of subsection arrays Paolo Bonzini
2025-10-14 13:37 ` [PULL 23/28] rust: migration: extract vmstate_fields_ref Paolo Bonzini
2025-10-14 13:37 ` [PULL 24/28] rust: move VMState from bql to migration Paolo Bonzini
2025-10-14 13:37 ` [PULL 25/28] rust: migration: add high-level migration wrappers Paolo Bonzini
2025-10-14 13:37 ` [PULL 26/28] rust: qemu-macros: add ToMigrationState derive macro Paolo Bonzini
2025-10-14 13:37 ` [PULL 27/28] timer: constify some functions Paolo Bonzini
2025-10-14 13:37 ` [PULL 28/28] rust: migration: implement ToMigrationState as part of impl_vmstate_bitsized Paolo Bonzini
2025-10-14 19:46 ` [PULL 00/28] Rust, i386, accelerator changes for 2025-10-14 Richard Henderson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).