|
2 | 2 | // Author(s): Zhao Liu <[email protected]>
|
3 | 3 | // SPDX-License-Identifier: GPL-2.0-or-later
|
4 | 4 |
|
5 |
| -use std::{ffi::CStr, mem::size_of, slice}; |
| 5 | +use std::{ffi::CStr, mem::size_of, ptr::NonNull, slice}; |
6 | 6 |
|
7 | 7 | use qemu_api::{
|
8 | 8 | bindings::{
|
9 |
| - vmstate_info_bool, vmstate_info_int64, vmstate_info_int8, vmstate_info_uint64, |
10 |
| - vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, |
| 9 | + vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, |
| 10 | + vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, |
11 | 11 | },
|
12 | 12 | c_str,
|
13 |
| - cell::BqlCell, |
| 13 | + cell::{BqlCell, Opaque}, |
| 14 | + impl_vmstate_forward, |
14 | 15 | vmstate::{VMStateDescription, VMStateField},
|
15 | 16 | vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused,
|
16 | 17 | zeroable::Zeroable,
|
@@ -286,3 +287,113 @@ fn test_vmstate_macro_array() {
|
286 | 287 | // The last VMStateField in VMSTATE_FOOB.
|
287 | 288 | assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END);
|
288 | 289 | }
|
| 290 | + |
| 291 | +// =========================== Test VMSTATE_FOOC =========================== |
| 292 | +// Test the use cases of the vmstate macro, corresponding to the following C |
| 293 | +// macro variants: |
| 294 | +// * VMSTATE_FOOC: |
| 295 | +// - VMSTATE_POINTER |
| 296 | +// - VMSTATE_ARRAY_OF_POINTER |
| 297 | +struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible. |
| 298 | + |
| 299 | +impl_vmstate_forward!(FooCWrapper); |
| 300 | + |
| 301 | +#[repr(C)] |
| 302 | +#[derive(qemu_api_macros::offsets)] |
| 303 | +struct FooC { |
| 304 | + ptr: *const i32, |
| 305 | + ptr_a: NonNull<FooA>, |
| 306 | + arr_ptr: [Box<u8>; FOO_ARRAY_MAX], |
| 307 | + arr_ptr_wrap: FooCWrapper, |
| 308 | +} |
| 309 | + |
| 310 | +static VMSTATE_FOOC: VMStateDescription = VMStateDescription { |
| 311 | + name: c_str!("foo_c").as_ptr(), |
| 312 | + version_id: 3, |
| 313 | + minimum_version_id: 1, |
| 314 | + fields: vmstate_fields! { |
| 315 | + vmstate_of!(FooC, ptr).with_version_id(2), |
| 316 | + // FIXME: Currently vmstate_struct doesn't support the pointer to structure. |
| 317 | + // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>) |
| 318 | + vmstate_unused!(size_of::<NonNull<FooA>>()), |
| 319 | + vmstate_of!(FooC, arr_ptr), |
| 320 | + vmstate_of!(FooC, arr_ptr_wrap), |
| 321 | + }, |
| 322 | + ..Zeroable::ZERO |
| 323 | +}; |
| 324 | + |
| 325 | +const PTR_SIZE: usize = size_of::<*mut ()>(); |
| 326 | + |
| 327 | +#[test] |
| 328 | +fn test_vmstate_pointer() { |
| 329 | + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; |
| 330 | + |
| 331 | + // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER) |
| 332 | + assert_eq!( |
| 333 | + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), |
| 334 | + b"ptr\0" |
| 335 | + ); |
| 336 | + assert_eq!(foo_fields[0].offset, 0); |
| 337 | + assert_eq!(foo_fields[0].num_offset, 0); |
| 338 | + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 }); |
| 339 | + assert_eq!(foo_fields[0].version_id, 2); |
| 340 | + assert_eq!(foo_fields[0].size, 4); |
| 341 | + assert_eq!(foo_fields[0].num, 0); |
| 342 | + assert_eq!( |
| 343 | + foo_fields[0].flags.0, |
| 344 | + VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0 |
| 345 | + ); |
| 346 | + assert!(foo_fields[0].vmsd.is_null()); |
| 347 | + assert!(foo_fields[0].field_exists.is_none()); |
| 348 | +} |
| 349 | + |
| 350 | +#[test] |
| 351 | +fn test_vmstate_macro_array_of_pointer() { |
| 352 | + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; |
| 353 | + |
| 354 | + // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to |
| 355 | + // VMSTATE_ARRAY_OF_POINTER) |
| 356 | + assert_eq!( |
| 357 | + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), |
| 358 | + b"arr_ptr\0" |
| 359 | + ); |
| 360 | + assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE); |
| 361 | + assert_eq!(foo_fields[2].num_offset, 0); |
| 362 | + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); |
| 363 | + assert_eq!(foo_fields[2].version_id, 0); |
| 364 | + assert_eq!(foo_fields[2].size, PTR_SIZE); |
| 365 | + assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32); |
| 366 | + assert_eq!( |
| 367 | + foo_fields[2].flags.0, |
| 368 | + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 |
| 369 | + ); |
| 370 | + assert!(foo_fields[2].vmsd.is_null()); |
| 371 | + assert!(foo_fields[2].field_exists.is_none()); |
| 372 | +} |
| 373 | + |
| 374 | +#[test] |
| 375 | +fn test_vmstate_macro_array_of_pointer_wrapped() { |
| 376 | + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; |
| 377 | + |
| 378 | + // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to |
| 379 | + // VMSTATE_ARRAY_OF_POINTER) |
| 380 | + assert_eq!( |
| 381 | + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), |
| 382 | + b"arr_ptr_wrap\0" |
| 383 | + ); |
| 384 | + assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE); |
| 385 | + assert_eq!(foo_fields[3].num_offset, 0); |
| 386 | + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); |
| 387 | + assert_eq!(foo_fields[3].version_id, 0); |
| 388 | + assert_eq!(foo_fields[3].size, PTR_SIZE); |
| 389 | + assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32); |
| 390 | + assert_eq!( |
| 391 | + foo_fields[2].flags.0, |
| 392 | + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 |
| 393 | + ); |
| 394 | + assert!(foo_fields[3].vmsd.is_null()); |
| 395 | + assert!(foo_fields[3].field_exists.is_none()); |
| 396 | + |
| 397 | + // The last VMStateField in VMSTATE_FOOC. |
| 398 | + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); |
| 399 | +} |
0 commit comments