Skip to content

Commit b131003

Browse files
trueptolemybonzini
authored andcommitted
rust/vmstate: Support vmstate_validate
In C version, VMSTATE_VALIDATE accepts the function pointer, which is used to check if some conditions of structure could meet, although the C version macro doesn't accept any structure as the opaque type. But it's hard to integrate VMSTATE_VALIDAE into vmstate_struct, a new macro has to be introduced to specifically handle the case corresponding to VMSTATE_VALIDATE. One of the difficulties is inferring the type of a callback by its name `test_fn`. We can't directly use `test_fn` as a parameter of test_cb_builder__() to get its type "F", because in this way, Rust compiler will be too conservative on drop check and complain "the destructor for this type cannot be evaluated in constant functions". Fortunately, PhantomData<T> could help in this case, because it is considered to never have a destructor, no matter its field type [*]. The `phantom__()` in the `call_func_with_field` macro provides a good example of using PhantomData to infer type. So copy this idea and apply it to the `vmstate_validate` macro. [*]: https://doc.rust-lang.org/std/ops/trait.Drop.html#drop-check Signed-off-by: Zhao Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 3baf82e commit b131003

File tree

1 file changed

+51
-1
lines changed

1 file changed

+51
-1
lines changed

rust/qemu-api/src/vmstate.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@
2525
//! functionality that is missing from `vmstate_of!`.
2626
2727
use core::{marker::PhantomData, mem, ptr::NonNull};
28+
use std::os::raw::{c_int, c_void};
2829

2930
pub use crate::bindings::{VMStateDescription, VMStateField};
30-
use crate::{bindings::VMStateFlags, prelude::*, qom::Owned, zeroable::Zeroable};
31+
use crate::{
32+
bindings::VMStateFlags, callbacks::FnCall, prelude::*, qom::Owned, zeroable::Zeroable,
33+
};
3134

3235
/// This macro is used to call a function with a generic argument bound
3336
/// to the type of a field. The function must take a
@@ -508,6 +511,53 @@ macro_rules! vmstate_fields {
508511
}}
509512
}
510513

514+
pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), bool>>(
515+
opaque: *mut c_void,
516+
version_id: c_int,
517+
) -> bool {
518+
let owner: &T = unsafe { &*(opaque.cast::<T>()) };
519+
let version: u8 = version_id.try_into().unwrap();
520+
// SAFETY: the opaque was passed as a reference to `T`.
521+
F::call((owner, version))
522+
}
523+
524+
pub type VMSFieldExistCb = unsafe extern "C" fn(
525+
opaque: *mut std::os::raw::c_void,
526+
version_id: std::os::raw::c_int,
527+
) -> bool;
528+
529+
#[doc(alias = "VMSTATE_VALIDATE")]
530+
#[macro_export]
531+
macro_rules! vmstate_validate {
532+
($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => {
533+
$crate::bindings::VMStateField {
534+
name: ::std::ffi::CStr::as_ptr($test_name),
535+
field_exists: {
536+
const fn test_cb_builder__<
537+
T,
538+
F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>,
539+
>(
540+
_phantom: ::core::marker::PhantomData<F>,
541+
) -> $crate::vmstate::VMSFieldExistCb {
542+
let _: () = F::ASSERT_IS_SOME;
543+
$crate::vmstate::rust_vms_test_field_exists::<T, F>
544+
}
545+
546+
const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> {
547+
::core::marker::PhantomData
548+
}
549+
Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn)))
550+
},
551+
flags: $crate::bindings::VMStateFlags(
552+
$crate::bindings::VMStateFlags::VMS_MUST_EXIST.0
553+
| $crate::bindings::VMStateFlags::VMS_ARRAY.0,
554+
),
555+
num: 0, // 0 elements: no data, only run test_fn callback
556+
..$crate::zeroable::Zeroable::ZERO
557+
}
558+
};
559+
}
560+
511561
/// A transparent wrapper type for the `subsections` field of
512562
/// [`VMStateDescription`].
513563
///

0 commit comments

Comments
 (0)