mirror of
https://github.com/mborgerson/xemu.git
synced 2026-05-06 07:03:33 +00:00
rust: vmstate: use const_refs_to_static
The VMStateDescriptionBuilder already needs const_refs_static, so use it to remove the need for vmstate_clock! and vmstate_struct!, as well as to simplify the implementation for scalars. If the consts in the VMState trait can reference to static VMStateDescription, scalars do not need the info_enum_to_ref! indirection and structs can implement the VMState trait themselves. Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Link: https://lore.kernel.org/r/20250908105005.2119297-9-pbonzini@redhat.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
@ -2,14 +2,11 @@
|
||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
mem::size_of
|
||||
};
|
||||
use std::{ffi::CStr, mem::size_of};
|
||||
|
||||
use qemu_api::{
|
||||
chardev::{CharBackend, Chardev, Event},
|
||||
impl_vmstate_forward,
|
||||
impl_vmstate_forward, impl_vmstate_struct,
|
||||
irq::{IRQState, InterruptSource},
|
||||
log::Log,
|
||||
log_mask_ln,
|
||||
@ -21,7 +18,7 @@ use qemu_api::{
|
||||
sysbus::{SysBusDevice, SysBusDeviceImpl},
|
||||
uninit_field_mut,
|
||||
vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
|
||||
vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
|
||||
vmstate_fields, vmstate_of, vmstate_subsections, vmstate_unused,
|
||||
};
|
||||
|
||||
use crate::registers::{self, Interrupt, RegisterOffset};
|
||||
@ -725,11 +722,12 @@ static VMSTATE_PL011_CLOCK: VMStateDescription<PL011State> =
|
||||
.minimum_version_id(1)
|
||||
.needed(&PL011State::clock_needed)
|
||||
.fields(vmstate_fields! {
|
||||
vmstate_clock!(PL011State, clock),
|
||||
vmstate_of!(PL011State, clock),
|
||||
})
|
||||
.build();
|
||||
|
||||
static VMSTATE_PL011_REGS: VMStateDescription<PL011Registers> =
|
||||
impl_vmstate_struct!(
|
||||
PL011Registers,
|
||||
VMStateDescriptionBuilder::<PL011Registers>::new()
|
||||
.name(c"pl011/regs")
|
||||
.version_id(2)
|
||||
@ -751,7 +749,8 @@ static VMSTATE_PL011_REGS: VMStateDescription<PL011Registers> =
|
||||
vmstate_of!(PL011Registers, read_count),
|
||||
vmstate_of!(PL011Registers, read_trigger),
|
||||
})
|
||||
.build();
|
||||
.build()
|
||||
);
|
||||
|
||||
pub const VMSTATE_PL011: VMStateDescription<PL011State> =
|
||||
VMStateDescriptionBuilder::<PL011State>::new()
|
||||
@ -761,7 +760,7 @@ pub const VMSTATE_PL011: VMStateDescription<PL011State> =
|
||||
.post_load(&PL011State::post_load)
|
||||
.fields(vmstate_fields! {
|
||||
vmstate_unused!(core::mem::size_of::<u32>()),
|
||||
vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell<PL011Registers>),
|
||||
vmstate_of!(PL011State, regs),
|
||||
})
|
||||
.subsections(vmstate_subsections! {
|
||||
VMSTATE_PL011_CLOCK
|
||||
|
||||
@ -16,6 +16,7 @@ use qemu_api::{
|
||||
qdev_prop_uint32, qdev_prop_usize,
|
||||
},
|
||||
cell::{BqlCell, BqlRefCell},
|
||||
impl_vmstate_struct,
|
||||
irq::InterruptSource,
|
||||
memory::{
|
||||
hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
|
||||
@ -28,7 +29,7 @@ use qemu_api::{
|
||||
timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
|
||||
uninit_field_mut,
|
||||
vmstate::{self, VMStateDescription, VMStateDescriptionBuilder},
|
||||
vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
|
||||
vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate,
|
||||
};
|
||||
|
||||
use crate::fw_cfg::HPETFwConfig;
|
||||
@ -964,7 +965,7 @@ static VMSTATE_HPET_OFFSET: VMStateDescription<HPETState> =
|
||||
})
|
||||
.build();
|
||||
|
||||
static VMSTATE_HPET_TIMER: VMStateDescription<HPETTimer> =
|
||||
const VMSTATE_HPET_TIMER: VMStateDescription<HPETTimer> =
|
||||
VMStateDescriptionBuilder::<HPETTimer>::new()
|
||||
.name(c"hpet_timer")
|
||||
.version_id(1)
|
||||
@ -979,6 +980,7 @@ static VMSTATE_HPET_TIMER: VMStateDescription<HPETTimer> =
|
||||
vmstate_of!(HPETTimer, qemu_timer),
|
||||
})
|
||||
.build();
|
||||
impl_vmstate_struct!(HPETTimer, VMSTATE_HPET_TIMER);
|
||||
|
||||
const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match";
|
||||
|
||||
@ -995,7 +997,7 @@ const VMSTATE_HPET: VMStateDescription<HPETState> =
|
||||
vmstate_of!(HPETState, counter),
|
||||
vmstate_of!(HPETState, num_timers_save),
|
||||
vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers),
|
||||
vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell<HPETTimer>, HPETState::validate_num_timers).with_version_id(0),
|
||||
vmstate_of!(HPETState, timers[0 .. num_timers_save], HPETState::validate_num_timers).with_version_id(0),
|
||||
})
|
||||
.subsections(vmstate_subsections!(
|
||||
VMSTATE_HPET_RTC_IRQ_LEVEL,
|
||||
|
||||
@ -95,10 +95,6 @@ macro_rules! assert_field_type {
|
||||
($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) => {
|
||||
$crate::assert_field_type!(@internal v, $ti, $t, v.$i[0]);
|
||||
};
|
||||
}
|
||||
|
||||
/// Assert that an expression matches a pattern. This can also be
|
||||
|
||||
@ -11,10 +11,11 @@
|
||||
//! migration format for a struct. This is based on the [`VMState`] trait,
|
||||
//! which is defined by all migratable types.
|
||||
//!
|
||||
//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and
|
||||
//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with
|
||||
//! the definition of the [`VMState`] trait (respectively for transparent
|
||||
//! structs and for `bilge`-defined types)
|
||||
//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward),
|
||||
//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and
|
||||
//! [`impl_vmstate_struct`](crate::impl_vmstate_struct), which help with the
|
||||
//! definition of the [`VMState`] trait (respectively for transparent structs,
|
||||
//! nested structs and `bilge`-defined types)
|
||||
//!
|
||||
//! * helper macros to declare a device model state struct, in particular
|
||||
//! [`vmstate_subsections`](crate::vmstate_subsections) and
|
||||
@ -31,7 +32,7 @@ use std::{
|
||||
fmt, io,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
ptr::NonNull,
|
||||
ptr::{addr_of, NonNull},
|
||||
};
|
||||
|
||||
pub use crate::bindings::{MigrationPriority, VMStateField};
|
||||
@ -40,6 +41,7 @@ use crate::{
|
||||
callbacks::FnCall,
|
||||
errno::{into_neg_errno, Errno},
|
||||
prelude::*,
|
||||
qdev,
|
||||
qom::Owned,
|
||||
zeroable::Zeroable,
|
||||
};
|
||||
@ -81,70 +83,6 @@ macro_rules! call_func_with_field {
|
||||
};
|
||||
}
|
||||
|
||||
/// Workaround for lack of `const_refs_static`: references to global variables
|
||||
/// can be included in a `static`, but not in a `const`; unfortunately, this
|
||||
/// is exactly what would go in the `VMStateField`'s `info` member.
|
||||
///
|
||||
/// This enum contains the contents of the `VMStateField`'s `info` member,
|
||||
/// but as an `enum` instead of a pointer.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VMStateFieldType {
|
||||
null,
|
||||
vmstate_info_bool,
|
||||
vmstate_info_int8,
|
||||
vmstate_info_int16,
|
||||
vmstate_info_int32,
|
||||
vmstate_info_int64,
|
||||
vmstate_info_uint8,
|
||||
vmstate_info_uint16,
|
||||
vmstate_info_uint32,
|
||||
vmstate_info_uint64,
|
||||
vmstate_info_timer,
|
||||
}
|
||||
|
||||
/// Workaround for lack of `const_refs_static`. Converts a `VMStateFieldType`
|
||||
/// to a `*const VMStateInfo`, for inclusion in a `VMStateField`.
|
||||
#[macro_export]
|
||||
macro_rules! info_enum_to_ref {
|
||||
($e:expr) => {
|
||||
unsafe {
|
||||
match $e {
|
||||
$crate::vmstate::VMStateFieldType::null => ::core::ptr::null(),
|
||||
$crate::vmstate::VMStateFieldType::vmstate_info_bool => {
|
||||
::core::ptr::addr_of!($crate::bindings::vmstate_info_bool)
|
||||
}
|
||||
$crate::vmstate::VMStateFieldType::vmstate_info_int8 => {
|
||||
::core::ptr::addr_of!($crate::bindings::vmstate_info_int8)
|
||||
}
|
||||
$crate::vmstate::VMStateFieldType::vmstate_info_int16 => {
|
||||
::core::ptr::addr_of!($crate::bindings::vmstate_info_int16)
|
||||
}
|
||||
$crate::vmstate::VMStateFieldType::vmstate_info_int32 => {
|
||||
::core::ptr::addr_of!($crate::bindings::vmstate_info_int32)
|
||||
}
|
||||
$crate::vmstate::VMStateFieldType::vmstate_info_int64 => {
|
||||
::core::ptr::addr_of!($crate::bindings::vmstate_info_int64)
|
||||
}
|
||||
$crate::vmstate::VMStateFieldType::vmstate_info_uint8 => {
|
||||
::core::ptr::addr_of!($crate::bindings::vmstate_info_uint8)
|
||||
}
|
||||
$crate::vmstate::VMStateFieldType::vmstate_info_uint16 => {
|
||||
::core::ptr::addr_of!($crate::bindings::vmstate_info_uint16)
|
||||
}
|
||||
$crate::vmstate::VMStateFieldType::vmstate_info_uint32 => {
|
||||
::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32)
|
||||
}
|
||||
$crate::vmstate::VMStateFieldType::vmstate_info_uint64 => {
|
||||
::core::ptr::addr_of!($crate::bindings::vmstate_info_uint64)
|
||||
}
|
||||
$crate::vmstate::VMStateFieldType::vmstate_info_timer => {
|
||||
::core::ptr::addr_of!($crate::bindings::vmstate_info_timer)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A trait for types that can be included in a device's migration stream. It
|
||||
/// provides the base contents of a `VMStateField` (minus the name and offset).
|
||||
///
|
||||
@ -155,12 +93,6 @@ macro_rules! info_enum_to_ref {
|
||||
/// to implement it except via macros that do it for you, such as
|
||||
/// `impl_vmstate_bitsized!`.
|
||||
pub unsafe trait VMState {
|
||||
/// The `info` member of a `VMStateField` is a pointer and as such cannot
|
||||
/// yet be included in the [`BASE`](VMState::BASE) associated constant;
|
||||
/// this is only allowed by Rust 1.83.0 and newer. For now, include the
|
||||
/// member as an enum which is stored in a separate constant.
|
||||
const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::null;
|
||||
|
||||
/// The base contents of a `VMStateField` (minus the name and offset) for
|
||||
/// the type that is implementing the trait.
|
||||
const BASE: VMStateField;
|
||||
@ -175,12 +107,6 @@ pub unsafe trait VMState {
|
||||
};
|
||||
}
|
||||
|
||||
/// Internal utility function to retrieve a type's `VMStateFieldType`;
|
||||
/// used by [`vmstate_of!`](crate::vmstate_of).
|
||||
pub const fn vmstate_scalar_type<T: VMState>(_: PhantomData<T>) -> VMStateFieldType {
|
||||
T::SCALAR_TYPE
|
||||
}
|
||||
|
||||
/// Internal utility function to retrieve a type's `VMStateField`;
|
||||
/// used by [`vmstate_of!`](crate::vmstate_of).
|
||||
pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField {
|
||||
@ -207,9 +133,9 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
|
||||
/// * an array of any of the above
|
||||
///
|
||||
/// In order to support other types, the trait `VMState` must be implemented
|
||||
/// for them. The macros
|
||||
/// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized)
|
||||
/// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this.
|
||||
/// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward),
|
||||
/// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and
|
||||
/// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this.
|
||||
#[macro_export]
|
||||
macro_rules! vmstate_of {
|
||||
($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => {
|
||||
@ -222,11 +148,6 @@ macro_rules! vmstate_of {
|
||||
$(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!(
|
||||
$crate::vmstate::vmstate_scalar_type,
|
||||
$struct_name,
|
||||
$field_name
|
||||
)),
|
||||
..$crate::call_func_with_field!(
|
||||
$crate::vmstate::vmstate_base,
|
||||
$struct_name,
|
||||
@ -327,8 +248,6 @@ macro_rules! impl_vmstate_forward {
|
||||
// the first field of the tuple
|
||||
($tuple:ty) => {
|
||||
unsafe impl $crate::vmstate::VMState for $tuple {
|
||||
const SCALAR_TYPE: $crate::vmstate::VMStateFieldType =
|
||||
$crate::call_func_with_field!($crate::vmstate::vmstate_scalar_type, $tuple, 0);
|
||||
const BASE: $crate::bindings::VMStateField =
|
||||
$crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0);
|
||||
}
|
||||
@ -340,7 +259,6 @@ macro_rules! impl_vmstate_forward {
|
||||
macro_rules! impl_vmstate_transparent {
|
||||
($type:ty where $base:tt: VMState $($where:tt)*) => {
|
||||
unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
|
||||
const SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE;
|
||||
const BASE: VMStateField = VMStateField {
|
||||
size: mem::size_of::<$type>(),
|
||||
..<$base as VMState>::BASE
|
||||
@ -361,10 +279,6 @@ impl_vmstate_transparent!(crate::cell::Opaque<T> where T: VMState);
|
||||
macro_rules! impl_vmstate_bitsized {
|
||||
($type:ty) => {
|
||||
unsafe impl $crate::vmstate::VMState for $type {
|
||||
const SCALAR_TYPE: $crate::vmstate::VMStateFieldType =
|
||||
<<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt
|
||||
as ::bilge::prelude::Number>::UnderlyingType
|
||||
as $crate::vmstate::VMState>::SCALAR_TYPE;
|
||||
const BASE: $crate::bindings::VMStateField =
|
||||
<<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt
|
||||
as ::bilge::prelude::Number>::UnderlyingType
|
||||
@ -382,8 +296,8 @@ macro_rules! impl_vmstate_bitsized {
|
||||
macro_rules! impl_vmstate_scalar {
|
||||
($info:ident, $type:ty$(, $varray_flag:ident)?) => {
|
||||
unsafe impl VMState for $type {
|
||||
const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info;
|
||||
const BASE: VMStateField = VMStateField {
|
||||
info: addr_of!(bindings::$info),
|
||||
size: mem::size_of::<$type>(),
|
||||
flags: VMStateFlags::VMS_SINGLE,
|
||||
..Zeroable::ZERO
|
||||
@ -404,6 +318,21 @@ impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32);
|
||||
impl_vmstate_scalar!(vmstate_info_uint64, u64);
|
||||
impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer);
|
||||
|
||||
macro_rules! impl_vmstate_c_struct {
|
||||
($type:ty, $vmsd:expr) => {
|
||||
unsafe impl VMState for $type {
|
||||
const BASE: VMStateField = $crate::bindings::VMStateField {
|
||||
vmsd: addr_of!($vmsd),
|
||||
size: mem::size_of::<$type>(),
|
||||
flags: VMStateFlags::VMS_STRUCT,
|
||||
..Zeroable::ZERO
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_vmstate_c_struct!(qdev::Clock, bindings::vmstate_clock);
|
||||
|
||||
// Pointer types using the underlying type's VMState plus VMS_POINTER
|
||||
// Note that references are not supported, though references to cells
|
||||
// could be allowed.
|
||||
@ -411,7 +340,6 @@ impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer);
|
||||
macro_rules! impl_vmstate_pointer {
|
||||
($type:ty where $base:tt: VMState $($where:tt)*) => {
|
||||
unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
|
||||
const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
|
||||
const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag();
|
||||
}
|
||||
};
|
||||
@ -430,7 +358,6 @@ impl_vmstate_pointer!(Owned<T> where T: VMState + ObjectType);
|
||||
// VMS_ARRAY/VMS_ARRAY_OF_POINTER
|
||||
|
||||
unsafe impl<T: VMState, const N: usize> VMState for [T; N] {
|
||||
const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
|
||||
const BASE: VMStateField = <T as VMState>::BASE.with_array_flag(N);
|
||||
}
|
||||
|
||||
@ -452,7 +379,7 @@ pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), b
|
||||
opaque: *mut c_void,
|
||||
version_id: c_int,
|
||||
) -> bool {
|
||||
// SAFETY: assumes vmstate_struct! is used correctly
|
||||
// SAFETY: the function is used in T's implementation of VMState
|
||||
let owner: &T = unsafe { &*(opaque.cast::<T>()) };
|
||||
let version: u8 = version_id.try_into().unwrap();
|
||||
F::call((owner, version))
|
||||
@ -480,76 +407,6 @@ macro_rules! vmstate_exist_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
|
||||
// `vmstate_of!`. While VMSTATE_CLOCK can at least try to be type-safe,
|
||||
// VMSTATE_STRUCT includes $type only for documentation purposes; it
|
||||
// is checked against $field_name and $struct_name, but not against $vmsd
|
||||
// which is what really would matter.
|
||||
#[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 $(, $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,
|
||||
$(num_offset: ::std::mem::offset_of!($struct_name, $num),)?
|
||||
offset: {
|
||||
$crate::assert_field_type!($struct_name, $field_name, $type $(, num = $num)?);
|
||||
::std::mem::offset_of!($struct_name, $field_name)
|
||||
},
|
||||
size: ::core::mem::size_of::<$type>(),
|
||||
flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
|
||||
vmsd: $vmsd.as_ref(),
|
||||
$(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
|
||||
..$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))?)?
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(alias = "VMSTATE_CLOCK")]
|
||||
#[macro_export]
|
||||
macro_rules! vmstate_clock {
|
||||
($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor: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::assert_field_type!(
|
||||
$struct_name,
|
||||
$field_name,
|
||||
$crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
|
||||
);
|
||||
::std::mem::offset_of!($struct_name, $field_name)
|
||||
},
|
||||
size: ::core::mem::size_of::<*const $crate::qdev::Clock>(),
|
||||
flags: $crate::bindings::VMStateFlags(
|
||||
$crate::bindings::VMStateFlags::VMS_STRUCT.0
|
||||
| $crate::bindings::VMStateFlags::VMS_POINTER.0,
|
||||
),
|
||||
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))?)?
|
||||
}};
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@ -584,6 +441,30 @@ macro_rules! vmstate_validate {
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper macro to allow using a struct in [`vmstate_of!`]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The [`VMStateDescription`] constant `$vmsd` must be an accurate
|
||||
/// description of the struct.
|
||||
#[macro_export]
|
||||
macro_rules! impl_vmstate_struct {
|
||||
($type:ty, $vmsd:expr) => {
|
||||
unsafe impl $crate::vmstate::VMState for $type {
|
||||
const BASE: $crate::bindings::VMStateField = {
|
||||
static VMSD: &$crate::bindings::VMStateDescription = $vmsd.as_ref();
|
||||
|
||||
$crate::bindings::VMStateField {
|
||||
vmsd: ::core::ptr::addr_of!(*VMSD),
|
||||
size: ::core::mem::size_of::<$type>(),
|
||||
flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
|
||||
..$crate::zeroable::Zeroable::ZERO
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A transparent wrapper type for the `subsections` field of
|
||||
/// [`VMStateDescription`].
|
||||
///
|
||||
@ -648,7 +529,7 @@ unsafe extern "C" fn vmstate_no_version_cb<
|
||||
>(
|
||||
opaque: *mut c_void,
|
||||
) -> c_int {
|
||||
// SAFETY: assumes vmstate_struct! is used correctly
|
||||
// SAFETY: the function is used in T's implementation of VMState
|
||||
let result = F::call((unsafe { &*(opaque.cast::<T>()) },));
|
||||
into_neg_errno(result)
|
||||
}
|
||||
@ -660,7 +541,7 @@ unsafe extern "C" fn vmstate_post_load_cb<
|
||||
opaque: *mut c_void,
|
||||
version_id: c_int,
|
||||
) -> c_int {
|
||||
// SAFETY: assumes vmstate_struct! is used correctly
|
||||
// SAFETY: the function is used in T's implementation of VMState
|
||||
let owner: &T = unsafe { &*(opaque.cast::<T>()) };
|
||||
let version: u8 = version_id.try_into().unwrap();
|
||||
let result = F::call((owner, version));
|
||||
@ -670,14 +551,14 @@ unsafe extern "C" fn vmstate_post_load_cb<
|
||||
unsafe extern "C" fn vmstate_needed_cb<T, F: for<'a> FnCall<(&'a T,), bool>>(
|
||||
opaque: *mut c_void,
|
||||
) -> bool {
|
||||
// SAFETY: assumes vmstate_struct! is used correctly
|
||||
// SAFETY: the function is used in T's implementation of VMState
|
||||
F::call((unsafe { &*(opaque.cast::<T>()) },))
|
||||
}
|
||||
|
||||
unsafe extern "C" fn vmstate_dev_unplug_pending_cb<T, F: for<'a> FnCall<(&'a T,), bool>>(
|
||||
opaque: *mut c_void,
|
||||
) -> bool {
|
||||
// SAFETY: assumes vmstate_struct! is used correctly
|
||||
// SAFETY: the function is used in T's implementation of VMState
|
||||
F::call((unsafe { &*(opaque.cast::<T>()) },))
|
||||
}
|
||||
|
||||
|
||||
@ -15,9 +15,9 @@ use qemu_api::{
|
||||
vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
|
||||
},
|
||||
cell::{BqlCell, Opaque},
|
||||
impl_vmstate_forward,
|
||||
impl_vmstate_forward, impl_vmstate_struct,
|
||||
vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField},
|
||||
vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate,
|
||||
vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate,
|
||||
};
|
||||
|
||||
const FOO_ARRAY_MAX: usize = 3;
|
||||
@ -52,6 +52,8 @@ static VMSTATE_FOOA: VMStateDescription<FooA> = VMStateDescriptionBuilder::<FooA
|
||||
})
|
||||
.build();
|
||||
|
||||
impl_vmstate_struct!(FooA, VMSTATE_FOOA);
|
||||
|
||||
#[test]
|
||||
fn test_vmstate_uint16() {
|
||||
let foo_fields: &[VMStateField] =
|
||||
@ -173,20 +175,19 @@ fn validate_foob(_state: &FooB, _version_id: u8) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
static VMSTATE_FOOB: VMStateDescription<FooB> =
|
||||
VMStateDescriptionBuilder::<FooB>::new()
|
||||
.name(c"foo_b")
|
||||
.version_id(2)
|
||||
.minimum_version_id(1)
|
||||
.fields(vmstate_fields! {
|
||||
vmstate_of!(FooB, val).with_version_id(2),
|
||||
vmstate_of!(FooB, wrap),
|
||||
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),
|
||||
})
|
||||
.build();
|
||||
static VMSTATE_FOOB: VMStateDescription<FooB> = VMStateDescriptionBuilder::<FooB>::new()
|
||||
.name(c"foo_b")
|
||||
.version_id(2)
|
||||
.minimum_version_id(1)
|
||||
.fields(vmstate_fields! {
|
||||
vmstate_of!(FooB, val).with_version_id(2),
|
||||
vmstate_of!(FooB, wrap),
|
||||
vmstate_of!(FooB, arr_a[0 .. num_a]).with_version_id(1),
|
||||
vmstate_of!(FooB, arr_a_mul[0 .. num_a_mul * 32]).with_version_id(2),
|
||||
vmstate_of!(FooB, arr_i64),
|
||||
vmstate_of!(FooB, arr_a_wrap[0 .. num_a_wrap], validate_foob),
|
||||
})
|
||||
.build();
|
||||
|
||||
#[test]
|
||||
fn test_vmstate_bool_v() {
|
||||
@ -351,9 +352,7 @@ static VMSTATE_FOOC: VMStateDescription<FooC> = VMStateDescriptionBuilder::<FooC
|
||||
.minimum_version_id(1)
|
||||
.fields(vmstate_fields! {
|
||||
vmstate_of!(FooC, ptr).with_version_id(2),
|
||||
// FIXME: Currently vmstate_struct doesn't support the pointer to structure.
|
||||
// VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>)
|
||||
vmstate_unused!(size_of::<NonNull<FooA>>()),
|
||||
vmstate_of!(FooC, ptr_a),
|
||||
vmstate_of!(FooC, arr_ptr),
|
||||
vmstate_of!(FooC, arr_ptr_wrap),
|
||||
})
|
||||
@ -385,6 +384,31 @@ fn test_vmstate_pointer() {
|
||||
assert!(foo_fields[0].field_exists.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmstate_struct_pointer() {
|
||||
let foo_fields: &[VMStateField] =
|
||||
unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) };
|
||||
|
||||
// 2st VMStateField ("ptr_a") in VMSTATE_FOOC (corresponding to
|
||||
// VMSTATE_STRUCT_POINTER)
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
|
||||
b"ptr_a\0"
|
||||
);
|
||||
assert_eq!(foo_fields[1].offset, PTR_SIZE);
|
||||
assert_eq!(foo_fields[1].num_offset, 0);
|
||||
assert_eq!(foo_fields[1].vmsd, VMSTATE_FOOA.as_ref());
|
||||
assert_eq!(foo_fields[1].version_id, 0);
|
||||
assert_eq!(foo_fields[1].size, size_of::<FooA>());
|
||||
assert_eq!(foo_fields[1].num, 0);
|
||||
assert_eq!(
|
||||
foo_fields[1].flags.0,
|
||||
VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0
|
||||
);
|
||||
assert!(foo_fields[1].info.is_null());
|
||||
assert!(foo_fields[1].field_exists.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmstate_macro_array_of_pointer() {
|
||||
let foo_fields: &[VMStateField] =
|
||||
@ -444,8 +468,7 @@ fn test_vmstate_macro_array_of_pointer_wrapped() {
|
||||
// * VMSTATE_FOOD:
|
||||
// - VMSTATE_VALIDATE
|
||||
|
||||
// Add more member fields when vmstate_of/vmstate_struct support "test"
|
||||
// parameter.
|
||||
// Add more member fields when vmstate_of support "test" parameter.
|
||||
struct FooD;
|
||||
|
||||
impl FooD {
|
||||
|
||||
Reference in New Issue
Block a user