diff options
author | Stefan Hajnoczi <stefanha@redhat.com> | 2025-05-05 11:26:47 -0400 |
---|---|---|
committer | Stefan Hajnoczi <stefanha@redhat.com> | 2025-05-05 11:26:47 -0400 |
commit | dc1ed8f256c446cbf33e090f0e214d0311a771a7 (patch) | |
tree | e693365000225a671eccf7fad90b2b04d7a6f348 /rust/qemu-api | |
parent | b6f568262b48ca0cb3dc018909908017551b5679 (diff) | |
parent | 03f50d7ee756eecbd4481c3008b5e01e999729c7 (diff) | |
download | qemu-dc1ed8f256c446cbf33e090f0e214d0311a771a7.zip qemu-dc1ed8f256c446cbf33e090f0e214d0311a771a7.tar.gz qemu-dc1ed8f256c446cbf33e090f0e214d0311a771a7.tar.bz2 |
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* rust: support migration of HPET device
* target/i386/hvf: fix compilation errors
* target/i386/tcg: fix some interrupt shadow cases
* hw/char/serial: remove unused prog_if compat property
* rust: centralize config in workspace root
* monitor: fix race on exiting QEMU
# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmgVQzkUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroOR8Af/Tke7kRZQyvoKURaKpVOBgP91fTQu
# IKwmX1OYe9JMPBwZV5g/++2HSaAddDzkFq90gmgTY+hpvRE3kDWOA86QtDRP4LKa
# Oq3yW48yrFiRZBAxERgRxRCsEvzlPC3cAEqCQd4fTL+cW6NVorbj4x/tQcALb47V
# cgXXVp59TW4lJk7nJUjd0mCFK1qEoIbZuuBgMn32K+fpBV/UghcoImT2giMeM24Y
# WW3olrLA9UN2fh5da7923WUvA9mSjnE0Yfdk6eKC3nCzlgMKktofwKHilm0tA6xA
# 7sJbwYTDSB9QxgnNw3WvAFAOMapJmedaSNorZdmcxCss7ed0s8hV8am9vQ==
# =LFS/
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 02 May 2025 18:12:09 EDT
# gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg: issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
# gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full]
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1
# Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83
* tag 'for-upstream' of https://gitlab.com/bonzini/qemu:
monitor: don't wake up qmp_dispatcher_co coroutine upon cleanup
rust: centralize config in workspace root
hw/char/serial: Remove unused prog_if compat property
target/i386: do not block singlestep for STI
target/i386: do not trigger IRQ shadow for LSS
target/i386/hvf: fix a compilation error
target/i386/emulate: remove rflags leftovers
rust/hpet: Support migration
rust/timer: Define NANOSECONDS_PER_SECOND binding as u64
rust/vmstate_test: Test varray with num field wrapped in BqlCell
rust: assertions: Support index field wrapped in BqlCell
vmstate: support varray for vmstate_clock!
rust/vmstate: Add support for field_exists checks
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'rust/qemu-api')
-rw-r--r-- | rust/qemu-api/Cargo.toml | 15 | ||||
-rw-r--r-- | rust/qemu-api/src/assertions.rs | 25 | ||||
-rw-r--r-- | rust/qemu-api/src/timer.rs | 2 | ||||
-rw-r--r-- | rust/qemu-api/src/vmstate.rs | 83 | ||||
-rw-r--r-- | rust/qemu-api/tests/vmstate_tests.rs | 41 |
5 files changed, 98 insertions, 68 deletions
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 57747bc..ca1b042 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -1,18 +1,17 @@ [package] name = "qemu_api" version = "0.1.0" -edition = "2021" authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"] -license = "GPL-2.0-or-later" -readme = "README.md" -homepage = "https://www.qemu.org" description = "Rust bindings for QEMU" -repository = "https://gitlab.com/qemu-project/qemu/" +readme = "README.md" resolver = "2" publish = false -keywords = [] -categories = [] -rust-version = "1.63.0" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true [dependencies] qemu_api_macros = { path = "../qemu-api-macros" } diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index eb12e94..a2d38c8 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -78,33 +78,26 @@ macro_rules! assert_same_type { /// ``` #[macro_export] macro_rules! assert_field_type { - ($t:ty, $i:tt, $ti:ty) => { + (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => { const _: () = { #[allow(unused)] - fn assert_field_type(v: $t) { - fn types_must_be_equal<T, U>(_: T) + fn assert_field_type($param_name: &$t) { + fn types_must_be_equal<T, U>(_: &T) where T: $crate::assertions::EqType<Itself = U>, { } - types_must_be_equal::<_, $ti>(v.$i); + types_must_be_equal::<_, $ti>(&$($field)*); } }; }; + ($t:ty, $i:tt, $ti:ty) => { + $crate::assert_field_type!(@internal v, $ti, $t, v.$i); + }; + ($t:ty, $i:tt, $ti:ty, num = $num:ident) => { - const _: () = { - #[allow(unused)] - fn assert_field_type(v: $t) { - fn types_must_be_equal<T, U>(_: T) - where - T: $crate::assertions::EqType<Itself = U>, - { - } - let index: usize = v.$num.try_into().unwrap(); - types_must_be_equal::<_, &$ti>(&v.$i[index]); - } - }; + $crate::assert_field_type!(@internal v, $ti, $t, v.$i[0]); }; } diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index f0b04ef..e769f8b 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -121,3 +121,5 @@ impl ClockType { pub const CLOCK_VIRTUAL: ClockType = ClockType { id: QEMUClockType::QEMU_CLOCK_VIRTUAL, }; + +pub const NANOSECONDS_PER_SECOND: u64 = 1000000000; diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 1b2b12e..9ae97c3 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -200,13 +200,14 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags /// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this. #[macro_export] macro_rules! vmstate_of { - ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(,)?) => { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => { $crate::bindings::VMStateField { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, offset: $crate::offset_of!($struct_name, $field_name), $(num_offset: $crate::offset_of!($struct_name, $num),)? + $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? // The calls to `call_func_with_field!` are the magic that // computes most of the VMStateField from the type of the field. info: $crate::info_enum_to_ref!($crate::call_func_with_field!( @@ -435,6 +436,38 @@ macro_rules! vmstate_unused { }}; } +pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), bool>>( + opaque: *mut c_void, + version_id: c_int, +) -> bool { + // SAFETY: the opaque was passed as a reference to `T`. + let owner: &T = unsafe { &*(opaque.cast::<T>()) }; + let version: u8 = version_id.try_into().unwrap(); + F::call((owner, version)) +} + +pub type VMSFieldExistCb = unsafe extern "C" fn( + opaque: *mut std::os::raw::c_void, + version_id: std::os::raw::c_int, +) -> bool; + +#[macro_export] +macro_rules! vmstate_exist_fn { + ($struct_name:ty, $test_fn:expr) => {{ + const fn test_cb_builder__<T, F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>>( + _phantom: ::core::marker::PhantomData<F>, + ) -> $crate::vmstate::VMSFieldExistCb { + let _: () = F::ASSERT_IS_SOME; + $crate::vmstate::rust_vms_test_field_exists::<T, F> + } + + const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { + ::core::marker::PhantomData + } + Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn))) + }}; +} + // FIXME: including the `vmsd` field in a `const` is not possible without // the const_refs_static feature (stabilized in Rust 1.83.0). Without it, // it is not possible to use VMS_STRUCT in a transparent manner using @@ -445,7 +478,7 @@ macro_rules! vmstate_unused { #[doc(alias = "VMSTATE_STRUCT")] #[macro_export] macro_rules! vmstate_struct { - ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(,)?) => { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(, $test_fn:expr)? $(,)?) => { $crate::bindings::VMStateField { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() @@ -458,6 +491,7 @@ macro_rules! vmstate_struct { size: ::core::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, vmsd: $vmsd, + $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? ..$crate::zeroable::Zeroable::ZERO } $(.with_varray_flag_unchecked( $crate::call_func_with_field!( @@ -473,7 +507,7 @@ macro_rules! vmstate_struct { #[doc(alias = "VMSTATE_CLOCK")] #[macro_export] macro_rules! vmstate_clock { - ($struct_name:ty, $field_name:ident) => {{ + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?) => {{ $crate::bindings::VMStateField { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() @@ -482,7 +516,7 @@ macro_rules! vmstate_clock { $crate::assert_field_type!( $struct_name, $field_name, - $crate::qom::Owned<$crate::qdev::Clock> + $crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)? ); $crate::offset_of!($struct_name, $field_name) }, @@ -493,7 +527,14 @@ macro_rules! vmstate_clock { ), vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, ..$crate::zeroable::Zeroable::ZERO - } + } $(.with_varray_flag_unchecked( + $crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num + ) + ) + $(.with_varray_multiply($factor))?)? }}; } @@ -514,43 +555,13 @@ macro_rules! vmstate_fields { }} } -pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), bool>>( - opaque: *mut c_void, - version_id: c_int, -) -> bool { - let owner: &T = unsafe { &*(opaque.cast::<T>()) }; - let version: u8 = version_id.try_into().unwrap(); - // SAFETY: the opaque was passed as a reference to `T`. - F::call((owner, version)) -} - -pub type VMSFieldExistCb = unsafe extern "C" fn( - opaque: *mut std::os::raw::c_void, - version_id: std::os::raw::c_int, -) -> bool; - #[doc(alias = "VMSTATE_VALIDATE")] #[macro_export] macro_rules! vmstate_validate { ($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => { $crate::bindings::VMStateField { name: ::std::ffi::CStr::as_ptr($test_name), - field_exists: { - const fn test_cb_builder__< - T, - F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>, - >( - _phantom: ::core::marker::PhantomData<F>, - ) -> $crate::vmstate::VMSFieldExistCb { - let _: () = F::ASSERT_IS_SOME; - $crate::vmstate::rust_vms_test_field_exists::<T, F> - } - - const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { - ::core::marker::PhantomData - } - Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn))) - }, + field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn), flags: $crate::bindings::VMStateFlags( $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0 | $crate::bindings::VMStateFlags::VMS_ARRAY.0, diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index 8b93492..f7a9311 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -28,7 +28,7 @@ const FOO_ARRAY_MAX: usize = 3; // - VMSTATE_VARRAY_UINT16_UNSAFE // - VMSTATE_VARRAY_MULTIPLY #[repr(C)] -#[derive(qemu_api_macros::offsets)] +#[derive(Default, qemu_api_macros::offsets)] struct FooA { arr: [u8; FOO_ARRAY_MAX], num: u16, @@ -147,8 +147,9 @@ fn test_vmstate_varray_multiply() { // - VMSTATE_STRUCT_VARRAY_UINT8 // - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 // - VMSTATE_ARRAY +// - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn #[repr(C)] -#[derive(qemu_api_macros::offsets)] +#[derive(Default, qemu_api_macros::offsets)] struct FooB { arr_a: [FooA; FOO_ARRAY_MAX], num_a: u8, @@ -158,6 +159,12 @@ struct FooB { val: bool, // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. arr_i64: [i64; FOO_ARRAY_MAX], + arr_a_wrap: [FooA; FOO_ARRAY_MAX], + num_a_wrap: BqlCell<u32>, +} + +fn validate_foob(_state: &FooB, _version_id: u8) -> bool { + true } static VMSTATE_FOOB: VMStateDescription = VMStateDescription { @@ -170,13 +177,14 @@ static VMSTATE_FOOB: VMStateDescription = VMStateDescription { vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), vmstate_of!(FooB, arr_i64), + vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob), }, ..Zeroable::ZERO }; #[test] fn test_vmstate_bool_v() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) assert_eq!( @@ -196,7 +204,7 @@ fn test_vmstate_bool_v() { #[test] fn test_vmstate_uint64() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) assert_eq!( @@ -216,7 +224,7 @@ fn test_vmstate_uint64() { #[test] fn test_vmstate_struct_varray_uint8() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to // VMSTATE_STRUCT_VARRAY_UINT8) @@ -240,7 +248,7 @@ fn test_vmstate_struct_varray_uint8() { #[test] fn test_vmstate_struct_varray_uint32_multiply() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) @@ -266,7 +274,7 @@ fn test_vmstate_struct_varray_uint32_multiply() { #[test] fn test_vmstate_macro_array() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to // VMSTATE_ARRAY) @@ -283,9 +291,26 @@ fn test_vmstate_macro_array() { assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); assert!(foo_fields[4].vmsd.is_null()); assert!(foo_fields[4].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint8_wrapper() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + let mut foo_b: FooB = Default::default(); + let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::<c_void>(); + + // 6th VMStateField ("arr_a_wrap") in VMSTATE_FOOB (corresponding to + // VMSTATE_STRUCT_VARRAY_UINT8). Other fields are checked in + // test_vmstate_struct_varray_uint8. + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(), + b"arr_a_wrap\0" + ); + assert_eq!(foo_fields[5].num_offset, 228); + assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) }); // The last VMStateField in VMSTATE_FOOB. - assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END); + assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END); } // =========================== Test VMSTATE_FOOC =========================== |