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 migrateable types. 13 //! 14 //! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and 15 //! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with 16 //! the definition of the [`VMState`] trait (respectively for transparent 17 //! structs and for `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 use core::{marker::PhantomData, mem, ptr::NonNull}; 28 29 pub use crate::bindings::{VMStateDescription, VMStateField}; 30 use crate::{ 31 bindings::{self, VMStateFlags}, 32 prelude::*, 33 qom::Owned, 34 zeroable::Zeroable, 35 }; 36 37 /// This macro is used to call a function with a generic argument bound 38 /// to the type of a field. The function must take a 39 /// [`PhantomData`]`<T>` argument; `T` is the type of 40 /// field `$field` in the `$typ` type. 41 /// 42 /// # Examples 43 /// 44 /// ``` 45 /// # use qemu_api::call_func_with_field; 46 /// # use core::marker::PhantomData; 47 /// const fn size_of_field<T>(_: PhantomData<T>) -> usize { 48 /// std::mem::size_of::<T>() 49 /// } 50 /// 51 /// struct Foo { 52 /// x: u16, 53 /// }; 54 /// // calls size_of_field::<u16>() 55 /// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2); 56 /// ``` 57 #[macro_export] 58 macro_rules! call_func_with_field { 59 // Based on the answer by user steffahn (Frank Steffahn) at 60 // https://users.rust-lang.org/t/inferring-type-of-field/122857 61 // and used under MIT license 62 ($func:expr, $typ:ty, $($field:tt).+) => { 63 $func(loop { 64 #![allow(unreachable_code)] 65 const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::core::marker::PhantomData } 66 // Unreachable code is exempt from checks on uninitialized values. 67 // Use that trick to infer the type of this PhantomData. 68 break ::core::marker::PhantomData; 69 break phantom__(&{ let value__: $typ; value__.$($field).+ }); 70 }) 71 }; 72 } 73 74 /// Workaround for lack of `const_refs_static`: references to global variables 75 /// can be included in a `static`, but not in a `const`; unfortunately, this 76 /// is exactly what would go in the `VMStateField`'s `info` member. 77 /// 78 /// This enum contains the contents of the `VMStateField`'s `info` member, 79 /// but as an `enum` instead of a pointer. 80 #[allow(non_camel_case_types)] 81 pub enum VMStateFieldType { 82 null, 83 vmstate_info_bool, 84 vmstate_info_int8, 85 vmstate_info_int16, 86 vmstate_info_int32, 87 vmstate_info_int64, 88 vmstate_info_uint8, 89 vmstate_info_uint16, 90 vmstate_info_uint32, 91 vmstate_info_uint64, 92 vmstate_info_timer, 93 } 94 95 /// Workaround for lack of `const_refs_static`. Converts a `VMStateFieldType` 96 /// to a `*const VMStateInfo`, for inclusion in a `VMStateField`. 97 #[macro_export] 98 macro_rules! info_enum_to_ref { 99 ($e:expr) => { 100 unsafe { 101 match $e { 102 $crate::vmstate::VMStateFieldType::null => ::core::ptr::null(), 103 $crate::vmstate::VMStateFieldType::vmstate_info_bool => { 104 ::core::ptr::addr_of!($crate::bindings::vmstate_info_bool) 105 } 106 $crate::vmstate::VMStateFieldType::vmstate_info_int8 => { 107 ::core::ptr::addr_of!($crate::bindings::vmstate_info_int8) 108 } 109 $crate::vmstate::VMStateFieldType::vmstate_info_int16 => { 110 ::core::ptr::addr_of!($crate::bindings::vmstate_info_int16) 111 } 112 $crate::vmstate::VMStateFieldType::vmstate_info_int32 => { 113 ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32) 114 } 115 $crate::vmstate::VMStateFieldType::vmstate_info_int64 => { 116 ::core::ptr::addr_of!($crate::bindings::vmstate_info_int64) 117 } 118 $crate::vmstate::VMStateFieldType::vmstate_info_uint8 => { 119 ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint8) 120 } 121 $crate::vmstate::VMStateFieldType::vmstate_info_uint16 => { 122 ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint16) 123 } 124 $crate::vmstate::VMStateFieldType::vmstate_info_uint32 => { 125 ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32) 126 } 127 $crate::vmstate::VMStateFieldType::vmstate_info_uint64 => { 128 ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint64) 129 } 130 $crate::vmstate::VMStateFieldType::vmstate_info_timer => { 131 ::core::ptr::addr_of!($crate::bindings::vmstate_info_timer) 132 } 133 } 134 } 135 }; 136 } 137 138 /// A trait for types that can be included in a device's migration stream. It 139 /// provides the base contents of a `VMStateField` (minus the name and offset). 140 /// 141 /// # Safety 142 /// 143 /// The contents of this trait go straight into structs that are parsed by C 144 /// code and used to introspect into other structs. Generally, you don't need 145 /// to implement it except via macros that do it for you, such as 146 /// `impl_vmstate_bitsized!`. 147 pub unsafe trait VMState { 148 /// The `info` member of a `VMStateField` is a pointer and as such cannot 149 /// yet be included in the [`BASE`](VMState::BASE) associated constant; 150 /// this is only allowed by Rust 1.83.0 and newer. For now, include the 151 /// member as an enum which is stored in a separate constant. 152 const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::null; 153 154 /// The base contents of a `VMStateField` (minus the name and offset) for 155 /// the type that is implementing the trait. 156 const BASE: VMStateField; 157 158 /// A flag that is added to another field's `VMStateField` to specify the 159 /// length's type in a variable-sized array. If this is not a supported 160 /// type for the length (i.e. if it is not `u8`, `u16`, `u32`), using it 161 /// in a call to [`vmstate_of!`](crate::vmstate_of) will cause a 162 /// compile-time error. 163 const VARRAY_FLAG: VMStateFlags = { 164 panic!("invalid type for variable-sized array"); 165 }; 166 } 167 168 /// Internal utility function to retrieve a type's `VMStateFieldType`; 169 /// used by [`vmstate_of!`](crate::vmstate_of). 170 pub const fn vmstate_scalar_type<T: VMState>(_: PhantomData<T>) -> VMStateFieldType { 171 T::SCALAR_TYPE 172 } 173 174 /// Internal utility function to retrieve a type's `VMStateField`; 175 /// used by [`vmstate_of!`](crate::vmstate_of). 176 pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField { 177 T::BASE 178 } 179 180 /// Internal utility function to retrieve a type's `VMStateFlags` when it 181 /// is used as the element count of a `VMSTATE_VARRAY`; used by 182 /// [`vmstate_of!`](crate::vmstate_of). 183 pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags { 184 T::VARRAY_FLAG 185 } 186 187 /// Return the `VMStateField` for a field of a struct. The field must be 188 /// visible in the current scope. 189 /// 190 /// Only a limited set of types is supported out of the box: 191 /// * scalar types (integer and `bool`) 192 /// * the C struct `QEMUTimer` 193 /// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, 194 /// [`BqlCell`], [`BqlRefCell`] 195 /// * a raw pointer to any of the above 196 /// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above 197 /// * an array of any of the above 198 /// 199 /// In order to support other types, the trait `VMState` must be implemented 200 /// for them. The macros 201 /// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized) 202 /// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this. 203 #[macro_export] 204 macro_rules! vmstate_of { 205 ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(,)?) => { 206 $crate::bindings::VMStateField { 207 name: ::core::concat!(::core::stringify!($field_name), "\0") 208 .as_bytes() 209 .as_ptr() as *const ::std::os::raw::c_char, 210 offset: $crate::offset_of!($struct_name, $field_name), 211 $(num_offset: $crate::offset_of!($struct_name, $num),)? 212 // The calls to `call_func_with_field!` are the magic that 213 // computes most of the VMStateField from the type of the field. 214 info: $crate::info_enum_to_ref!($crate::call_func_with_field!( 215 $crate::vmstate::vmstate_scalar_type, 216 $struct_name, 217 $field_name 218 )), 219 ..$crate::call_func_with_field!( 220 $crate::vmstate::vmstate_base, 221 $struct_name, 222 $field_name 223 )$(.with_varray_flag($crate::call_func_with_field!( 224 $crate::vmstate::vmstate_varray_flag, 225 $struct_name, 226 $num)) 227 $(.with_varray_multiply($factor))?)? 228 } 229 }; 230 } 231 232 impl VMStateFlags { 233 const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags( 234 VMStateFlags::VMS_VARRAY_INT32.0 235 | VMStateFlags::VMS_VARRAY_UINT8.0 236 | VMStateFlags::VMS_VARRAY_UINT16.0 237 | VMStateFlags::VMS_VARRAY_UINT32.0, 238 ); 239 } 240 241 // Add a couple builder-style methods to VMStateField, allowing 242 // easy derivation of VMStateField constants from other types. 243 impl VMStateField { 244 #[must_use] 245 pub const fn with_version_id(mut self, version_id: i32) -> Self { 246 assert!(version_id >= 0); 247 self.version_id = version_id; 248 self 249 } 250 251 #[must_use] 252 pub const fn with_array_flag(mut self, num: usize) -> Self { 253 assert!(num <= 0x7FFF_FFFFusize); 254 assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0); 255 assert!((self.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0); 256 if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 { 257 self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0); 258 self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0); 259 // VMS_ARRAY_OF_POINTER flag stores the size of pointer. 260 // FIXME: *const, *mut, NonNull and Box<> have the same size as usize. 261 // Resize if more smart pointers are supported. 262 self.size = std::mem::size_of::<usize>(); 263 } 264 self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0); 265 self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0); 266 self.num = num as i32; 267 self 268 } 269 270 #[must_use] 271 pub const fn with_pointer_flag(mut self) -> Self { 272 assert!((self.flags.0 & VMStateFlags::VMS_POINTER.0) == 0); 273 self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0); 274 self 275 } 276 277 #[must_use] 278 pub const fn with_varray_flag<T: VMState>(mut self, flag: VMStateFlags) -> VMStateField { 279 assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); 280 self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); 281 self.flags = VMStateFlags(self.flags.0 | flag.0); 282 self.num = 0; // varray uses num_offset instead of num. 283 self 284 } 285 286 #[must_use] 287 pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField { 288 assert!(num <= 0x7FFF_FFFFu32); 289 self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); 290 self.num = num as i32; 291 self 292 } 293 } 294 295 /// This macro can be used (by just passing it a type) to forward the `VMState` 296 /// trait to the first field of a tuple. This is a workaround for lack of 297 /// support of nested [`offset_of`](core::mem::offset_of) until Rust 1.82.0. 298 /// 299 /// # Examples 300 /// 301 /// ``` 302 /// # use qemu_api::impl_vmstate_forward; 303 /// pub struct Fifo([u8; 16]); 304 /// impl_vmstate_forward!(Fifo); 305 /// ``` 306 #[macro_export] 307 macro_rules! impl_vmstate_forward { 308 // This is similar to impl_vmstate_transparent below, but it 309 // uses the same trick as vmstate_of! to obtain the type of 310 // the first field of the tuple 311 ($tuple:ty) => { 312 unsafe impl $crate::vmstate::VMState for $tuple { 313 const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = 314 $crate::call_func_with_field!($crate::vmstate::vmstate_scalar_type, $tuple, 0); 315 const BASE: $crate::bindings::VMStateField = 316 $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0); 317 } 318 }; 319 } 320 321 // Transparent wrappers: just use the internal type 322 323 macro_rules! impl_vmstate_transparent { 324 ($type:ty where $base:tt: VMState $($where:tt)*) => { 325 unsafe impl<$base> VMState for $type where $base: VMState $($where)* { 326 const SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE; 327 const BASE: VMStateField = VMStateField { 328 size: mem::size_of::<$type>(), 329 ..<$base as VMState>::BASE 330 }; 331 const VARRAY_FLAG: VMStateFlags = <$base as VMState>::VARRAY_FLAG; 332 } 333 }; 334 } 335 336 impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState); 337 impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState); 338 impl_vmstate_transparent!(std::pin::Pin<T> where T: VMState); 339 impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState); 340 impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState); 341 342 #[macro_export] 343 macro_rules! impl_vmstate_bitsized { 344 ($type:ty) => { 345 unsafe impl $crate::vmstate::VMState for $type { 346 const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = 347 <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt 348 as ::bilge::prelude::Number>::UnderlyingType 349 as $crate::vmstate::VMState>::SCALAR_TYPE; 350 const BASE: $crate::bindings::VMStateField = 351 <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt 352 as ::bilge::prelude::Number>::UnderlyingType 353 as $crate::vmstate::VMState>::BASE; 354 const VARRAY_FLAG: $crate::bindings::VMStateFlags = 355 <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt 356 as ::bilge::prelude::Number>::UnderlyingType 357 as $crate::vmstate::VMState>::VARRAY_FLAG; 358 } 359 }; 360 } 361 362 // Scalar types using predefined VMStateInfos 363 364 macro_rules! impl_vmstate_scalar { 365 ($info:ident, $type:ty$(, $varray_flag:ident)?) => { 366 unsafe impl VMState for $type { 367 const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info; 368 const BASE: VMStateField = VMStateField { 369 size: mem::size_of::<$type>(), 370 flags: VMStateFlags::VMS_SINGLE, 371 ..Zeroable::ZERO 372 }; 373 $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)? 374 } 375 }; 376 } 377 378 impl_vmstate_scalar!(vmstate_info_bool, bool); 379 impl_vmstate_scalar!(vmstate_info_int8, i8); 380 impl_vmstate_scalar!(vmstate_info_int16, i16); 381 impl_vmstate_scalar!(vmstate_info_int32, i32); 382 impl_vmstate_scalar!(vmstate_info_int64, i64); 383 impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8); 384 impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16); 385 impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); 386 impl_vmstate_scalar!(vmstate_info_uint64, u64); 387 impl_vmstate_scalar!(vmstate_info_timer, bindings::QEMUTimer); 388 389 // Pointer types using the underlying type's VMState plus VMS_POINTER 390 // Note that references are not supported, though references to cells 391 // could be allowed. 392 393 macro_rules! impl_vmstate_pointer { 394 ($type:ty where $base:tt: VMState $($where:tt)*) => { 395 unsafe impl<$base> VMState for $type where $base: VMState $($where)* { 396 const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE; 397 const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag(); 398 } 399 }; 400 } 401 402 impl_vmstate_pointer!(*const T where T: VMState); 403 impl_vmstate_pointer!(*mut T where T: VMState); 404 impl_vmstate_pointer!(NonNull<T> where T: VMState); 405 406 // Unlike C pointers, Box is always non-null therefore there is no need 407 // to specify VMS_ALLOC. 408 impl_vmstate_pointer!(Box<T> where T: VMState); 409 impl_vmstate_pointer!(Owned<T> where T: VMState + ObjectType); 410 411 // Arrays using the underlying type's VMState plus 412 // VMS_ARRAY/VMS_ARRAY_OF_POINTER 413 414 unsafe impl<T: VMState, const N: usize> VMState for [T; N] { 415 const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE; 416 const BASE: VMStateField = <T as VMState>::BASE.with_array_flag(N); 417 } 418 419 #[doc(alias = "VMSTATE_UNUSED")] 420 #[macro_export] 421 macro_rules! vmstate_unused { 422 ($size:expr) => {{ 423 $crate::bindings::VMStateField { 424 name: $crate::c_str!("unused").as_ptr(), 425 size: $size, 426 info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, 427 flags: $crate::bindings::VMStateFlags::VMS_BUFFER, 428 ..$crate::zeroable::Zeroable::ZERO 429 } 430 }}; 431 } 432 433 // FIXME: including the `vmsd` field in a `const` is not possible without 434 // the const_refs_static feature (stabilized in Rust 1.83.0). Without it, 435 // it is not possible to use VMS_STRUCT in a transparent manner using 436 // `vmstate_of!`. While VMSTATE_CLOCK can at least try to be type-safe, 437 // VMSTATE_STRUCT includes $type only for documentation purposes; it 438 // is checked against $field_name and $struct_name, but not against $vmsd 439 // which is what really would matter. 440 #[doc(alias = "VMSTATE_STRUCT")] 441 #[macro_export] 442 macro_rules! vmstate_struct { 443 ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(,)?) => { 444 $crate::bindings::VMStateField { 445 name: ::core::concat!(::core::stringify!($field_name), "\0") 446 .as_bytes() 447 .as_ptr() as *const ::std::os::raw::c_char, 448 $(num_offset: $crate::offset_of!($struct_name, $num),)? 449 offset: { 450 $crate::assert_field_type!($struct_name, $field_name, $type); 451 $crate::offset_of!($struct_name, $field_name) 452 }, 453 size: ::core::mem::size_of::<$type>(), 454 flags: $crate::bindings::VMStateFlags::VMS_STRUCT, 455 vmsd: $vmsd, 456 ..$crate::zeroable::Zeroable::ZERO $( 457 .with_varray_flag($crate::call_func_with_field!( 458 $crate::vmstate::vmstate_varray_flag, 459 $struct_name, 460 $num)) 461 $(.with_varray_multiply($factor))?)? 462 } 463 }; 464 } 465 466 #[doc(alias = "VMSTATE_CLOCK")] 467 #[macro_export] 468 macro_rules! vmstate_clock { 469 ($struct_name:ty, $field_name:ident) => {{ 470 $crate::bindings::VMStateField { 471 name: ::core::concat!(::core::stringify!($field_name), "\0") 472 .as_bytes() 473 .as_ptr() as *const ::std::os::raw::c_char, 474 offset: { 475 $crate::assert_field_type!( 476 $struct_name, 477 $field_name, 478 $crate::qom::Owned<$crate::qdev::Clock> 479 ); 480 $crate::offset_of!($struct_name, $field_name) 481 }, 482 size: ::core::mem::size_of::<*const $crate::qdev::Clock>(), 483 flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), 484 vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, 485 ..$crate::zeroable::Zeroable::ZERO 486 } 487 }}; 488 } 489 490 /// Helper macro to declare a list of 491 /// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return 492 /// a pointer to the array of values it created. 493 #[macro_export] 494 macro_rules! vmstate_fields { 495 ($($field:expr),*$(,)*) => {{ 496 static _FIELDS: &[$crate::bindings::VMStateField] = &[ 497 $($field),*, 498 $crate::bindings::VMStateField { 499 flags: $crate::bindings::VMStateFlags::VMS_END, 500 ..$crate::zeroable::Zeroable::ZERO 501 } 502 ]; 503 _FIELDS.as_ptr() 504 }} 505 } 506 507 /// A transparent wrapper type for the `subsections` field of 508 /// [`VMStateDescription`]. 509 /// 510 /// This is necessary to be able to declare subsection descriptions as statics, 511 /// because the only way to implement `Sync` for a foreign type (and `*const` 512 /// pointers are foreign types in Rust) is to create a wrapper struct and 513 /// `unsafe impl Sync` for it. 514 /// 515 /// This struct is used in the 516 /// [`vm_state_subsections`](crate::vmstate_subsections) macro implementation. 517 #[repr(transparent)] 518 pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMStateDescription]); 519 520 unsafe impl Sync for VMStateSubsectionsWrapper {} 521 522 /// Helper macro to declare a list of subsections ([`VMStateDescription`]) 523 /// into a static and return a pointer to the array of pointers it created. 524 #[macro_export] 525 macro_rules! vmstate_subsections { 526 ($($subsection:expr),*$(,)*) => {{ 527 static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[ 528 $({ 529 static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection; 530 ::core::ptr::addr_of!(_SUBSECTION) 531 }),*, 532 ::core::ptr::null() 533 ]); 534 _SUBSECTIONS.0.as_ptr() 535 }} 536 } 537