master
rs 663 lines 22.2 KB
Raw
1 // Copyright 2024, Linaro Limited
2 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
3 // SPDX-License-Identifier: GPL-2.0-or-later
4
5 //! Helper macros to declare migration state for device models.
6 //!
7 //! This module includes four families of macros:
8 //!
9 //! * [`vmstate_unused!`](crate::vmstate_unused) and
10 //! [`vmstate_of!`](crate::vmstate_of), which are used to express the
11 //! migration format for a struct. This is based on the [`VMState`] trait,
12 //! which is defined by all migratable types.
13 //!
14 //! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and
15 //! [`impl_vmstate_struct`](crate::impl_vmstate_struct), which help with the
16 //! definition of the [`VMState`] trait (respectively for transparent structs,
17 //! nested structs and `bilge`-defined types)
18 //!
19 //! * helper macros to declare a device model state struct, in particular
20 //! [`vmstate_subsections`](crate::vmstate_subsections) and
21 //! [`vmstate_fields`](crate::vmstate_fields).
22 //!
23 //! * direct equivalents to the C macros declared in
24 //! `include/migration/vmstate.h`. These are not type-safe and only provide
25 //! functionality that is missing from `vmstate_of!`.
26
27 pub use std::convert::Infallible;
28 use std::{
29 error::Error,
30 ffi::{c_int, c_void, CStr},
31 fmt, io,
32 marker::PhantomData,
33 mem,
34 ptr::{addr_of, NonNull},
35 };
36
37 use common::{
38 callbacks::FnCall,
39 errno::{into_neg_errno, Errno},
40 Zeroable,
41 };
42
43 use crate::bindings::{self, VMStateFlags};
44 pub use crate::bindings::{MigrationPriority, VMStateField, VMStateStructMember};
45
46 /// This macro is used to call a function with a generic argument bound
47 /// to the type of a field. The function must take a
48 /// [`PhantomData`]`<T>` argument; `T` is the type of
49 /// field `$field` in the `$typ` type.
50 ///
51 /// # Examples
52 ///
53 /// ```
54 /// # use migration::call_func_with_field;
55 /// # use core::marker::PhantomData;
56 /// const fn size_of_field<T>(_: PhantomData<T>) -> usize {
57 /// std::mem::size_of::<T>()
58 /// }
59 ///
60 /// struct Foo {
61 /// x: u16,
62 /// };
63 /// // calls size_of_field::<u16>()
64 /// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2);
65 /// ```
66 #[macro_export]
67 macro_rules! call_func_with_field {
68 // Based on the answer by user steffahn (Frank Steffahn) at
69 // https://users.rust-lang.org/t/inferring-type-of-field/122857
70 // and used under MIT license
71 ($func:expr, $typ:ty, $($field:tt).+) => {
72 $func(loop {
73 #![allow(unreachable_code)]
74 #![allow(unused_variables)]
75 const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::core::marker::PhantomData }
76 // Unreachable code is exempt from checks on uninitialized values.
77 // Use that trick to infer the type of this PhantomData.
78 break ::core::marker::PhantomData;
79 break phantom__(&{ let value__: $typ; value__.$($field).+ });
80 })
81 };
82 }
83
84 /// A trait for types that can be included in a device's migration stream. It
85 /// provides the base contents of a `VMStateField` (minus the name and offset).
86 ///
87 /// # Safety
88 ///
89 /// The contents of this trait go straight into structs that are parsed by C
90 /// code and used to introspect into other structs. Generally, you don't need
91 /// to implement it except via macros that do it for you, such as
92 /// `impl_vmstate_forward!`.
93 pub unsafe trait VMState {
94 /// The base contents of a `VMStateField` (minus the name and offset) for
95 /// the type that is implementing the trait.
96 const BASE: VMStateField;
97
98 /// A flag that is added to another field's `VMStateField` to specify the
99 /// length's type in a variable-sized array. If this is not a supported
100 /// type for the length (i.e. if it is not `u8`, `u16`, `u32`), using it
101 /// in a call to [`vmstate_of!`](crate::vmstate_of) will cause a
102 /// compile-time error.
103 #[doc(hidden)] // https://github.com/rust-lang/rust/issues/149635
104 const VARRAY_FLAG: VMStateFlags = {
105 panic!("invalid type for variable-sized array");
106 };
107 }
108
109 /// Internal utility function to retrieve a type's `VMStateField`;
110 /// used by [`vmstate_of!`](crate::vmstate_of).
111 pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField {
112 T::BASE
113 }
114
115 /// Internal utility function to retrieve a type's `VMStateFlags` when it
116 /// is used as the element count of a `VMSTATE_VARRAY`; used by
117 /// [`vmstate_of!`](crate::vmstate_of).
118 pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags {
119 T::VARRAY_FLAG
120 }
121
122 pub const OPAQUE: &[u8; 1048576] = &[0; 1048576];
123
124 pub const fn size_of_ptr_type<T>(_: *const T) -> usize {
125 ::core::mem::size_of::<T>()
126 }
127
128 #[macro_export]
129 macro_rules! size_of_field_type {
130 ($struct_name:ty, $($field_name:ident).+) => {
131 $crate::vmstate::size_of_ptr_type(unsafe {
132 ::core::ptr::addr_of!(
133 (*$crate::vmstate::OPAQUE.as_ptr().cast::<$struct_name>()).$($field_name).+
134 )
135 })
136 };
137 }
138
139 /// Return the `VMStateField` for a field of a struct. The field must be
140 /// visible in the current scope.
141 ///
142 /// Only a limited set of types is supported out of the box:
143 /// * scalar types (integer and `bool`)
144 /// * the C struct `QEMUTimer`
145 /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`,
146 /// [`BqlCell`], [`BqlRefCell`])
147 /// * a raw pointer to any of the above
148 /// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above
149 /// * an array of any of the above
150 ///
151 /// In order to support other types, the trait `VMState` must be implemented
152 /// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward)
153 /// and [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this.
154 ///
155 /// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html
156 /// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html
157 /// [`Owned`]: ../../qom/qom/struct.Owned.html
158 #[macro_export]
159 macro_rules! vmstate_of {
160 ($struct_name:ty, $($field_name:ident).+ $([0 .. $($num:ident).+ $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => {
161 $crate::bindings::VMStateField {
162 name: ::core::concat!(::core::stringify!($($field_name).+), "\0")
163 .as_bytes()
164 .as_ptr().cast::<::std::os::raw::c_char>(),
165 offset: ::std::mem::offset_of!($struct_name, $($field_name).+),
166 $(num_indirect: $crate::vmstate::VMStateStructMember {
167 offset: ::std::mem::offset_of!($struct_name, $($num).+) as u32,
168 size: $crate::size_of_field_type!($struct_name, $($num).+) as u8,
169 },)?
170 $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
171 // The calls to `call_func_with_field!` are the magic that
172 // computes most of the VMStateField from the type of the field.
173 ..$crate::call_func_with_field!(
174 $crate::vmstate::vmstate_base,
175 $struct_name,
176 $($field_name).+
177 )$(.with_varray_flag($crate::call_func_with_field!(
178 $crate::vmstate::vmstate_varray_flag,
179 $struct_name,
180 $($num).+)))?
181 }
182 };
183 }
184
185 /// This macro can be used (by just passing it a type) to forward the `VMState`
186 /// trait to the first field of a tuple. This is a workaround for lack of
187 /// support of nested [`offset_of`](core::mem::offset_of) until Rust 1.82.0.
188 ///
189 /// # Examples
190 ///
191 /// ```
192 /// # use migration::impl_vmstate_forward;
193 /// pub struct Fifo([u8; 16]);
194 /// impl_vmstate_forward!(Fifo);
195 /// ```
196 #[macro_export]
197 macro_rules! impl_vmstate_forward {
198 // This is similar to impl_vmstate_transparent below, but it
199 // uses the same trick as vmstate_of! to obtain the type of
200 // the first field of the tuple
201 ($tuple:ty) => {
202 unsafe impl $crate::vmstate::VMState for $tuple {
203 const BASE: $crate::bindings::VMStateField =
204 $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0);
205 }
206 };
207 }
208
209 // Transparent wrappers: just use the internal type
210
211 #[macro_export]
212 macro_rules! impl_vmstate_transparent {
213 ($type:ty where $base:tt: VMState $($where:tt)*) => {
214 unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* {
215 const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField {
216 size: ::core::mem::size_of::<$type>(),
217 ..<$base as $crate::vmstate::VMState>::BASE
218 };
219 const VARRAY_FLAG: $crate::bindings::VMStateFlags = <$base as $crate::vmstate::VMState>::VARRAY_FLAG;
220 }
221 };
222 }
223
224 impl_vmstate_transparent!(bql::BqlCell<T> where T: VMState);
225 impl_vmstate_transparent!(bql::BqlRefCell<T> where T: VMState);
226 impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState);
227 impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState);
228 impl_vmstate_transparent!(std::pin::Pin<T> where T: VMState);
229 impl_vmstate_transparent!(common::Opaque<T> where T: VMState);
230 impl_vmstate_transparent!(std::mem::ManuallyDrop<T> where T: VMState);
231
232 // Scalar types using predefined VMStateInfos
233
234 macro_rules! impl_vmstate_scalar {
235 ($info:ident, $type:ty$(, $varray_flag:ident)?) => {
236 unsafe impl $crate::vmstate::VMState for $type {
237 const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField {
238 info: addr_of!(bindings::$info),
239 size: mem::size_of::<$type>(),
240 flags: $crate::vmstate::VMStateFlags::VMS_SINGLE,
241 ..::common::zeroable::Zeroable::ZERO
242 };
243 $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)?
244 }
245 };
246 }
247
248 impl_vmstate_scalar!(vmstate_info_bool, bool);
249 impl_vmstate_scalar!(vmstate_info_int8, i8);
250 impl_vmstate_scalar!(vmstate_info_int16, i16);
251 impl_vmstate_scalar!(vmstate_info_int32, i32);
252 impl_vmstate_scalar!(vmstate_info_int64, i64);
253 impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY);
254 impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY);
255 impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY);
256 impl_vmstate_scalar!(vmstate_info_uint64, u64);
257 impl_vmstate_scalar!(vmstate_info_timer, util::timer::Timer);
258
259 #[macro_export]
260 macro_rules! impl_vmstate_c_struct {
261 ($type:ty, $vmsd:expr) => {
262 unsafe impl $crate::vmstate::VMState for $type {
263 const BASE: $crate::bindings::VMStateField = $crate::bindings::VMStateField {
264 vmsd: ::std::ptr::addr_of!($vmsd),
265 size: ::std::mem::size_of::<$type>(),
266 flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
267 ..::common::zeroable::Zeroable::ZERO
268 };
269 }
270 };
271 }
272
273 // Pointer types using the underlying type's VMState plus VMS_POINTER
274 // Note that references are not supported, though references to cells
275 // could be allowed.
276
277 #[macro_export]
278 macro_rules! impl_vmstate_pointer {
279 ($type:ty where $base:tt: VMState $($where:tt)*) => {
280 unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* {
281 const BASE: $crate::vmstate::VMStateField = <$base as $crate::vmstate::VMState>::BASE.with_pointer_flag();
282 }
283 };
284 }
285
286 impl_vmstate_pointer!(*const T where T: VMState);
287 impl_vmstate_pointer!(*mut T where T: VMState);
288 impl_vmstate_pointer!(NonNull<T> where T: VMState);
289
290 // Unlike C pointers, Box is always non-null therefore there is no need
291 // to specify VMS_ALLOC.
292 impl_vmstate_pointer!(Box<T> where T: VMState);
293
294 // Arrays using the underlying type's VMState plus
295 // VMS_ARRAY/VMS_ARRAY_OF_POINTER
296
297 unsafe impl<T: VMState, const N: usize> VMState for [T; N] {
298 const BASE: VMStateField = <T as VMState>::BASE.with_array_flag(N);
299 }
300
301 #[doc(alias = "VMSTATE_UNUSED")]
302 #[macro_export]
303 macro_rules! vmstate_unused {
304 ($size:expr) => {{
305 $crate::bindings::VMStateField {
306 name: c"unused".as_ptr(),
307 size: $size,
308 info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) },
309 flags: $crate::bindings::VMStateFlags::VMS_BUFFER,
310 ..::common::Zeroable::ZERO
311 }
312 }};
313 }
314
315 pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), bool>>(
316 opaque: *mut c_void,
317 version_id: c_int,
318 ) -> bool {
319 // SAFETY: the function is used in T's implementation of VMState
320 let owner: &T = unsafe { &*(opaque.cast::<T>()) };
321 let version: u8 = version_id.try_into().unwrap();
322 F::call((owner, version))
323 }
324
325 pub type VMSFieldExistCb = unsafe extern "C" fn(
326 opaque: *mut std::os::raw::c_void,
327 version_id: std::os::raw::c_int,
328 ) -> bool;
329
330 #[macro_export]
331 macro_rules! vmstate_exist_fn {
332 ($struct_name:ty, $test_fn:expr) => {{
333 const fn test_cb_builder__<T, F: for<'a> ::common::FnCall<(&'a T, u8), bool>>(
334 _phantom: ::core::marker::PhantomData<F>,
335 ) -> $crate::vmstate::VMSFieldExistCb {
336 const { assert!(F::IS_SOME) };
337 $crate::vmstate::rust_vms_test_field_exists::<T, F>
338 }
339
340 const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> {
341 ::core::marker::PhantomData
342 }
343 Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn)))
344 }};
345 }
346
347 /// Add a terminator to the fields in the arguments, and return
348 /// a reference to the resulting array of values.
349 #[macro_export]
350 macro_rules! vmstate_fields_ref {
351 ($($field:expr),*$(,)*) => {
352 &[
353 $($field),*,
354 $crate::bindings::VMStateField {
355 flags: $crate::bindings::VMStateFlags::VMS_END,
356 ..::common::zeroable::Zeroable::ZERO
357 }
358 ]
359 }
360 }
361
362 /// Helper macro to declare a list of
363 /// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return
364 /// a pointer to the array of values it created.
365 #[macro_export]
366 macro_rules! vmstate_fields {
367 ($($field:expr),*$(,)*) => {{
368 static _FIELDS: &[$crate::bindings::VMStateField] = $crate::vmstate_fields_ref!(
369 $($field),*,
370 );
371 _FIELDS
372 }}
373 }
374
375 #[doc(alias = "VMSTATE_VALIDATE")]
376 #[macro_export]
377 macro_rules! vmstate_validate {
378 ($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => {
379 $crate::bindings::VMStateField {
380 name: ::std::ffi::CStr::as_ptr($test_name),
381 field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),
382 flags: $crate::bindings::VMStateFlags(
383 $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0
384 | $crate::bindings::VMStateFlags::VMS_NO_STATE.0,
385 ),
386 num: 0, // 0 elements: no data, only run test_fn callback
387 ..::common::zeroable::Zeroable::ZERO
388 }
389 };
390 }
391
392 /// Helper macro to allow using a struct in [`vmstate_of!`]
393 ///
394 /// # Safety
395 ///
396 /// The [`VMStateDescription`] constant `$vmsd` must be an accurate
397 /// description of the struct.
398 #[macro_export]
399 macro_rules! impl_vmstate_struct {
400 ($type:ty, $vmsd:expr) => {
401 unsafe impl $crate::vmstate::VMState for $type {
402 const BASE: $crate::bindings::VMStateField = {
403 static VMSD: &$crate::bindings::VMStateDescription = $vmsd.as_ref();
404
405 $crate::bindings::VMStateField {
406 vmsd: ::core::ptr::addr_of!(*VMSD),
407 size: ::core::mem::size_of::<$type>(),
408 flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
409 ..common::Zeroable::ZERO
410 }
411 };
412 }
413 };
414 }
415
416 /// The type returned by [`vmstate_subsections!`](crate::vmstate_subsections).
417 pub type VMStateSubsections = &'static [Option<&'static crate::bindings::VMStateDescription>];
418
419 /// Helper macro to declare a list of subsections ([`VMStateDescription`])
420 /// into a static and return a pointer to the array of pointers it created.
421 #[macro_export]
422 macro_rules! vmstate_subsections {
423 ($($subsection:expr),*$(,)*) => {{
424 static _SUBSECTIONS: $crate::vmstate::VMStateSubsections = &[
425 $({
426 static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection.get();
427 Some(&_SUBSECTION)
428 }),*,
429 None,
430 ];
431 &_SUBSECTIONS
432 }}
433 }
434
435 pub struct VMStateDescription<T>(bindings::VMStateDescription, PhantomData<fn(&T)>);
436
437 // SAFETY: When a *const T is passed to the callbacks, the call itself
438 // is done in a thread-safe manner. The invocation is okay as long as
439 // T itself is `Sync`.
440 unsafe impl<T: Sync> Sync for VMStateDescription<T> {}
441
442 #[derive(Clone)]
443 pub struct VMStateDescriptionBuilder<T>(
444 bindings::VMStateDescription,
445 Option<*const std::os::raw::c_char>, // the name of VMStateDescription
446 PhantomData<fn(&T)>,
447 );
448
449 #[derive(Debug)]
450 pub struct InvalidError;
451
452 impl Error for InvalidError {}
453
454 impl std::fmt::Display for InvalidError {
455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456 write!(f, "invalid migration data")
457 }
458 }
459
460 impl From<InvalidError> for Errno {
461 fn from(_value: InvalidError) -> Errno {
462 io::ErrorKind::InvalidInput.into()
463 }
464 }
465
466 unsafe extern "C" fn vmstate_no_version_cb<
467 T,
468 F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>,
469 >(
470 opaque: *mut c_void,
471 ) -> c_int {
472 // SAFETY: the function is used in T's implementation of VMState
473 let result = F::call((unsafe { &*(opaque.cast::<T>()) },));
474 into_neg_errno(result)
475 }
476
477 unsafe extern "C" fn vmstate_post_save_cb<T, F: for<'a> FnCall<(&'a T,), ()>>(opaque: *mut c_void) {
478 // SAFETY: the function is used in T's implementation of VMState.
479 F::call((unsafe { &*(opaque.cast::<T>()) },));
480 }
481
482 unsafe extern "C" fn vmstate_post_load_cb<
483 T,
484 F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into<Errno>>>,
485 >(
486 opaque: *mut c_void,
487 version_id: c_int,
488 ) -> c_int {
489 // SAFETY: the function is used in T's implementation of VMState
490 let owner: &T = unsafe { &*(opaque.cast::<T>()) };
491 let version: u8 = version_id.try_into().unwrap();
492 let result = F::call((owner, version));
493 into_neg_errno(result)
494 }
495
496 unsafe extern "C" fn vmstate_needed_cb<T, F: for<'a> FnCall<(&'a T,), bool>>(
497 opaque: *mut c_void,
498 ) -> bool {
499 // SAFETY: the function is used in T's implementation of VMState
500 F::call((unsafe { &*(opaque.cast::<T>()) },))
501 }
502
503 unsafe extern "C" fn vmstate_dev_unplug_pending_cb<T, F: for<'a> FnCall<(&'a T,), bool>>(
504 opaque: *mut c_void,
505 ) -> bool {
506 // SAFETY: the function is used in T's implementation of VMState
507 F::call((unsafe { &*(opaque.cast::<T>()) },))
508 }
509
510 impl<T> VMStateDescriptionBuilder<T> {
511 #[must_use]
512 pub const fn name(mut self, name_str: &CStr) -> Self {
513 self.1 = Some(::std::ffi::CStr::as_ptr(name_str));
514 self
515 }
516
517 #[must_use]
518 pub const fn unmigratable(mut self) -> Self {
519 self.0.unmigratable = true;
520 self
521 }
522
523 #[must_use]
524 pub const fn early_setup(mut self) -> Self {
525 self.0.early_setup = true;
526 self
527 }
528
529 #[must_use]
530 pub const fn version_id(mut self, version: u8) -> Self {
531 self.0.version_id = version as c_int;
532 self
533 }
534
535 #[must_use]
536 pub const fn minimum_version_id(mut self, min_version: u8) -> Self {
537 self.0.minimum_version_id = min_version as c_int;
538 self
539 }
540
541 #[must_use]
542 pub const fn priority(mut self, priority: MigrationPriority) -> Self {
543 self.0.priority = priority;
544 self
545 }
546
547 #[must_use]
548 pub const fn pre_load<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>>(
549 mut self,
550 _f: &F,
551 ) -> Self {
552 self.0.pre_load = if F::IS_SOME {
553 Some(vmstate_no_version_cb::<T, F>)
554 } else {
555 None
556 };
557 self
558 }
559
560 #[must_use]
561 pub const fn post_load<F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into<Errno>>>>(
562 mut self,
563 _f: &F,
564 ) -> Self {
565 self.0.post_load = if F::IS_SOME {
566 Some(vmstate_post_load_cb::<T, F>)
567 } else {
568 None
569 };
570 self
571 }
572
573 #[must_use]
574 pub const fn pre_save<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>>(
575 mut self,
576 _f: &F,
577 ) -> Self {
578 self.0.pre_save = if F::IS_SOME {
579 Some(vmstate_no_version_cb::<T, F>)
580 } else {
581 None
582 };
583 self
584 }
585
586 #[must_use]
587 pub const fn post_save<F: for<'a> FnCall<(&'a T,), ()>>(mut self, _f: &F) -> Self {
588 self.0.post_save = if F::IS_SOME {
589 Some(vmstate_post_save_cb::<T, F>)
590 } else {
591 None
592 };
593 self
594 }
595
596 #[must_use]
597 pub const fn needed<F: for<'a> FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self {
598 self.0.needed = if F::IS_SOME {
599 Some(vmstate_needed_cb::<T, F>)
600 } else {
601 None
602 };
603 self
604 }
605
606 #[must_use]
607 pub const fn unplug_pending<F: for<'a> FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self {
608 self.0.dev_unplug_pending = if F::IS_SOME {
609 Some(vmstate_dev_unplug_pending_cb::<T, F>)
610 } else {
611 None
612 };
613 self
614 }
615
616 #[must_use]
617 pub const fn fields(mut self, fields: &'static [VMStateField]) -> Self {
618 if fields[fields.len() - 1].flags.0 != VMStateFlags::VMS_END.0 {
619 panic!("fields are not terminated, use vmstate_fields!");
620 }
621 self.0.fields = fields.as_ptr();
622 self
623 }
624
625 #[must_use]
626 pub const fn subsections(mut self, subs: &'static VMStateSubsections) -> Self {
627 if subs[subs.len() - 1].is_some() {
628 panic!("subsections are not terminated, use vmstate_subsections!");
629 }
630 let subs: *const Option<&bindings::VMStateDescription> = subs.as_ptr();
631 self.0.subsections = subs.cast::<*const bindings::VMStateDescription>();
632 self
633 }
634
635 #[must_use]
636 pub const fn build(mut self) -> VMStateDescription<T> {
637 // FIXME: is_null()/as_ref() become const since v1.84.
638 assert!(self.1.is_some(), "VMStateDescription requires name field!");
639 self.0.name = self.1.unwrap();
640 VMStateDescription::<T>(self.0, PhantomData)
641 }
642
643 #[must_use]
644 pub const fn new() -> Self {
645 Self(bindings::VMStateDescription::ZERO, None, PhantomData)
646 }
647 }
648
649 impl<T> Default for VMStateDescriptionBuilder<T> {
650 fn default() -> Self {
651 Self::new()
652 }
653 }
654
655 impl<T> VMStateDescription<T> {
656 pub const fn get(&self) -> bindings::VMStateDescription {
657 self.0
658 }
659
660 pub const fn as_ref(&self) -> &bindings::VMStateDescription {
661 &self.0
662 }
663 }