xref: /qemu/rust/qemu-api/tests/vmstate_tests.rs (revision e4fb0be1d1d6b67df7709d84d16133b64f455ce8)
119985021SZhao Liu // Copyright (C) 2025 Intel Corporation.
219985021SZhao Liu // Author(s): Zhao Liu <zhai1.liu@intel.com>
319985021SZhao Liu // SPDX-License-Identifier: GPL-2.0-or-later
419985021SZhao Liu 
5*e4fb0be1SPaolo Bonzini use std::{
6*e4fb0be1SPaolo Bonzini     ffi::{c_void, CStr},
7*e4fb0be1SPaolo Bonzini     mem::size_of,
8*e4fb0be1SPaolo Bonzini     ptr::NonNull,
9*e4fb0be1SPaolo Bonzini     slice,
10*e4fb0be1SPaolo Bonzini };
1119985021SZhao Liu 
1219985021SZhao Liu use qemu_api::{
1357c327f3SZhao Liu     bindings::{
148df1b001SZhao Liu         vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8,
158df1b001SZhao Liu         vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
1657c327f3SZhao Liu     },
1719985021SZhao Liu     c_str,
188df1b001SZhao Liu     cell::{BqlCell, Opaque},
198df1b001SZhao Liu     impl_vmstate_forward,
2019985021SZhao Liu     vmstate::{VMStateDescription, VMStateField},
219bd7e6f7SZhao Liu     vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate,
2219985021SZhao Liu     zeroable::Zeroable,
2319985021SZhao Liu };
2419985021SZhao Liu 
2519985021SZhao Liu const FOO_ARRAY_MAX: usize = 3;
2619985021SZhao Liu 
2719985021SZhao Liu // =========================== Test VMSTATE_FOOA ===========================
2819985021SZhao Liu // Test the use cases of the vmstate macro, corresponding to the following C
2919985021SZhao Liu // macro variants:
3019985021SZhao Liu //   * VMSTATE_FOOA:
3119985021SZhao Liu //     - VMSTATE_U16
3219985021SZhao Liu //     - VMSTATE_UNUSED
3319985021SZhao Liu //     - VMSTATE_VARRAY_UINT16_UNSAFE
3419985021SZhao Liu //     - VMSTATE_VARRAY_MULTIPLY
3519985021SZhao Liu #[repr(C)]
36cff1ec67SZhao Liu #[derive(Default, qemu_api_macros::offsets)]
3719985021SZhao Liu struct FooA {
3819985021SZhao Liu     arr: [u8; FOO_ARRAY_MAX],
3919985021SZhao Liu     num: u16,
4019985021SZhao Liu     arr_mul: [i8; FOO_ARRAY_MAX],
4119985021SZhao Liu     num_mul: u32,
4219985021SZhao Liu     elem: i8,
4319985021SZhao Liu }
4419985021SZhao Liu 
4519985021SZhao Liu static VMSTATE_FOOA: VMStateDescription = VMStateDescription {
4619985021SZhao Liu     name: c_str!("foo_a").as_ptr(),
4719985021SZhao Liu     version_id: 1,
4819985021SZhao Liu     minimum_version_id: 1,
4919985021SZhao Liu     fields: vmstate_fields! {
5019985021SZhao Liu         vmstate_of!(FooA, elem),
5119985021SZhao Liu         vmstate_unused!(size_of::<i64>()),
5219985021SZhao Liu         vmstate_of!(FooA, arr[0 .. num]).with_version_id(0),
5319985021SZhao Liu         vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]),
5419985021SZhao Liu     },
5519985021SZhao Liu     ..Zeroable::ZERO
5619985021SZhao Liu };
5719985021SZhao Liu 
5819985021SZhao Liu #[test]
5919985021SZhao Liu fn test_vmstate_uint16() {
6019985021SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
6119985021SZhao Liu 
6219985021SZhao Liu     // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16)
6319985021SZhao Liu     assert_eq!(
6419985021SZhao Liu         unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
6519985021SZhao Liu         b"elem\0"
6619985021SZhao Liu     );
6719985021SZhao Liu     assert_eq!(foo_fields[0].offset, 16);
6819985021SZhao Liu     assert_eq!(foo_fields[0].num_offset, 0);
6919985021SZhao Liu     assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 });
7019985021SZhao Liu     assert_eq!(foo_fields[0].version_id, 0);
7119985021SZhao Liu     assert_eq!(foo_fields[0].size, 1);
7219985021SZhao Liu     assert_eq!(foo_fields[0].num, 0);
7319985021SZhao Liu     assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE);
7419985021SZhao Liu     assert!(foo_fields[0].vmsd.is_null());
7519985021SZhao Liu     assert!(foo_fields[0].field_exists.is_none());
7619985021SZhao Liu }
7719985021SZhao Liu 
7819985021SZhao Liu #[test]
7919985021SZhao Liu fn test_vmstate_unused() {
8019985021SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
8119985021SZhao Liu 
8219985021SZhao Liu     // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED)
8319985021SZhao Liu     assert_eq!(
8419985021SZhao Liu         unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
8519985021SZhao Liu         b"unused\0"
8619985021SZhao Liu     );
8719985021SZhao Liu     assert_eq!(foo_fields[1].offset, 0);
8819985021SZhao Liu     assert_eq!(foo_fields[1].num_offset, 0);
8919985021SZhao Liu     assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer });
9019985021SZhao Liu     assert_eq!(foo_fields[1].version_id, 0);
9119985021SZhao Liu     assert_eq!(foo_fields[1].size, 8);
9219985021SZhao Liu     assert_eq!(foo_fields[1].num, 0);
9319985021SZhao Liu     assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER);
9419985021SZhao Liu     assert!(foo_fields[1].vmsd.is_null());
9519985021SZhao Liu     assert!(foo_fields[1].field_exists.is_none());
9619985021SZhao Liu }
9719985021SZhao Liu 
9819985021SZhao Liu #[test]
9919985021SZhao Liu fn test_vmstate_varray_uint16_unsafe() {
10019985021SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
10119985021SZhao Liu 
10219985021SZhao Liu     // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to
10319985021SZhao Liu     // VMSTATE_VARRAY_UINT16_UNSAFE)
10419985021SZhao Liu     assert_eq!(
10519985021SZhao Liu         unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
10619985021SZhao Liu         b"arr\0"
10719985021SZhao Liu     );
10819985021SZhao Liu     assert_eq!(foo_fields[2].offset, 0);
10919985021SZhao Liu     assert_eq!(foo_fields[2].num_offset, 4);
11019985021SZhao Liu     assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
11119985021SZhao Liu     assert_eq!(foo_fields[2].version_id, 0);
11219985021SZhao Liu     assert_eq!(foo_fields[2].size, 1);
11319985021SZhao Liu     assert_eq!(foo_fields[2].num, 0);
11419985021SZhao Liu     assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16);
11519985021SZhao Liu     assert!(foo_fields[2].vmsd.is_null());
11619985021SZhao Liu     assert!(foo_fields[2].field_exists.is_none());
11719985021SZhao Liu }
11819985021SZhao Liu 
11919985021SZhao Liu #[test]
12019985021SZhao Liu fn test_vmstate_varray_multiply() {
12119985021SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
12219985021SZhao Liu 
12319985021SZhao Liu     // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to
12419985021SZhao Liu     // VMSTATE_VARRAY_MULTIPLY)
12519985021SZhao Liu     assert_eq!(
12619985021SZhao Liu         unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
12719985021SZhao Liu         b"arr_mul\0"
12819985021SZhao Liu     );
12919985021SZhao Liu     assert_eq!(foo_fields[3].offset, 6);
13019985021SZhao Liu     assert_eq!(foo_fields[3].num_offset, 12);
13119985021SZhao Liu     assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 });
13219985021SZhao Liu     assert_eq!(foo_fields[3].version_id, 0);
13319985021SZhao Liu     assert_eq!(foo_fields[3].size, 1);
13419985021SZhao Liu     assert_eq!(foo_fields[3].num, 16);
13519985021SZhao Liu     assert_eq!(
13619985021SZhao Liu         foo_fields[3].flags.0,
13719985021SZhao Liu         VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0
13819985021SZhao Liu     );
13919985021SZhao Liu     assert!(foo_fields[3].vmsd.is_null());
14019985021SZhao Liu     assert!(foo_fields[3].field_exists.is_none());
14119985021SZhao Liu 
14219985021SZhao Liu     // The last VMStateField in VMSTATE_FOOA.
14319985021SZhao Liu     assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END);
14419985021SZhao Liu }
14557c327f3SZhao Liu 
14657c327f3SZhao Liu // =========================== Test VMSTATE_FOOB ===========================
14757c327f3SZhao Liu // Test the use cases of the vmstate macro, corresponding to the following C
14857c327f3SZhao Liu // macro variants:
14957c327f3SZhao Liu //   * VMSTATE_FOOB:
15057c327f3SZhao Liu //     - VMSTATE_BOOL_V
15157c327f3SZhao Liu //     - VMSTATE_U64
15257c327f3SZhao Liu //     - VMSTATE_STRUCT_VARRAY_UINT8
15357c327f3SZhao Liu //     - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32
15457c327f3SZhao Liu //     - VMSTATE_ARRAY
155cff1ec67SZhao Liu //     - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn
15657c327f3SZhao Liu #[repr(C)]
157cff1ec67SZhao Liu #[derive(Default, qemu_api_macros::offsets)]
15857c327f3SZhao Liu struct FooB {
15957c327f3SZhao Liu     arr_a: [FooA; FOO_ARRAY_MAX],
16057c327f3SZhao Liu     num_a: u8,
16157c327f3SZhao Liu     arr_a_mul: [FooA; FOO_ARRAY_MAX],
16257c327f3SZhao Liu     num_a_mul: u32,
16357c327f3SZhao Liu     wrap: BqlCell<u64>,
16457c327f3SZhao Liu     val: bool,
16557c327f3SZhao Liu     // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test.
16657c327f3SZhao Liu     arr_i64: [i64; FOO_ARRAY_MAX],
167cff1ec67SZhao Liu     arr_a_wrap: [FooA; FOO_ARRAY_MAX],
168cff1ec67SZhao Liu     num_a_wrap: BqlCell<u32>,
169cff1ec67SZhao Liu }
170cff1ec67SZhao Liu 
171cff1ec67SZhao Liu fn validate_foob(_state: &FooB, _version_id: u8) -> bool {
172cff1ec67SZhao Liu     true
17357c327f3SZhao Liu }
17457c327f3SZhao Liu 
17557c327f3SZhao Liu static VMSTATE_FOOB: VMStateDescription = VMStateDescription {
17657c327f3SZhao Liu     name: c_str!("foo_b").as_ptr(),
17757c327f3SZhao Liu     version_id: 2,
17857c327f3SZhao Liu     minimum_version_id: 1,
17957c327f3SZhao Liu     fields: vmstate_fields! {
18057c327f3SZhao Liu         vmstate_of!(FooB, val).with_version_id(2),
18157c327f3SZhao Liu         vmstate_of!(FooB, wrap),
18257c327f3SZhao Liu         vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1),
18357c327f3SZhao Liu         vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2),
18457c327f3SZhao Liu         vmstate_of!(FooB, arr_i64),
185cff1ec67SZhao Liu         vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob),
18657c327f3SZhao Liu     },
18757c327f3SZhao Liu     ..Zeroable::ZERO
18857c327f3SZhao Liu };
18957c327f3SZhao Liu 
19057c327f3SZhao Liu #[test]
19157c327f3SZhao Liu fn test_vmstate_bool_v() {
192cff1ec67SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
19357c327f3SZhao Liu 
19457c327f3SZhao Liu     // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V)
19557c327f3SZhao Liu     assert_eq!(
19657c327f3SZhao Liu         unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
19757c327f3SZhao Liu         b"val\0"
19857c327f3SZhao Liu     );
19957c327f3SZhao Liu     assert_eq!(foo_fields[0].offset, 136);
20057c327f3SZhao Liu     assert_eq!(foo_fields[0].num_offset, 0);
20157c327f3SZhao Liu     assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool });
20257c327f3SZhao Liu     assert_eq!(foo_fields[0].version_id, 2);
20357c327f3SZhao Liu     assert_eq!(foo_fields[0].size, 1);
20457c327f3SZhao Liu     assert_eq!(foo_fields[0].num, 0);
20557c327f3SZhao Liu     assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE);
20657c327f3SZhao Liu     assert!(foo_fields[0].vmsd.is_null());
20757c327f3SZhao Liu     assert!(foo_fields[0].field_exists.is_none());
20857c327f3SZhao Liu }
20957c327f3SZhao Liu 
21057c327f3SZhao Liu #[test]
21157c327f3SZhao Liu fn test_vmstate_uint64() {
212cff1ec67SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
21357c327f3SZhao Liu 
21457c327f3SZhao Liu     // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64)
21557c327f3SZhao Liu     assert_eq!(
21657c327f3SZhao Liu         unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
21757c327f3SZhao Liu         b"wrap\0"
21857c327f3SZhao Liu     );
21957c327f3SZhao Liu     assert_eq!(foo_fields[1].offset, 128);
22057c327f3SZhao Liu     assert_eq!(foo_fields[1].num_offset, 0);
22157c327f3SZhao Liu     assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 });
22257c327f3SZhao Liu     assert_eq!(foo_fields[1].version_id, 0);
22357c327f3SZhao Liu     assert_eq!(foo_fields[1].size, 8);
22457c327f3SZhao Liu     assert_eq!(foo_fields[1].num, 0);
22557c327f3SZhao Liu     assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE);
22657c327f3SZhao Liu     assert!(foo_fields[1].vmsd.is_null());
22757c327f3SZhao Liu     assert!(foo_fields[1].field_exists.is_none());
22857c327f3SZhao Liu }
22957c327f3SZhao Liu 
23057c327f3SZhao Liu #[test]
23157c327f3SZhao Liu fn test_vmstate_struct_varray_uint8() {
232cff1ec67SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
23357c327f3SZhao Liu 
23457c327f3SZhao Liu     // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to
23557c327f3SZhao Liu     // VMSTATE_STRUCT_VARRAY_UINT8)
23657c327f3SZhao Liu     assert_eq!(
23757c327f3SZhao Liu         unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
23857c327f3SZhao Liu         b"arr_a\0"
23957c327f3SZhao Liu     );
24057c327f3SZhao Liu     assert_eq!(foo_fields[2].offset, 0);
24157c327f3SZhao Liu     assert_eq!(foo_fields[2].num_offset, 60);
24257c327f3SZhao Liu     assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field.
24357c327f3SZhao Liu     assert_eq!(foo_fields[2].version_id, 1);
24457c327f3SZhao Liu     assert_eq!(foo_fields[2].size, 20);
24557c327f3SZhao Liu     assert_eq!(foo_fields[2].num, 0);
24657c327f3SZhao Liu     assert_eq!(
24757c327f3SZhao Liu         foo_fields[2].flags.0,
24857c327f3SZhao Liu         VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0
24957c327f3SZhao Liu     );
25057c327f3SZhao Liu     assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA);
25157c327f3SZhao Liu     assert!(foo_fields[2].field_exists.is_none());
25257c327f3SZhao Liu }
25357c327f3SZhao Liu 
25457c327f3SZhao Liu #[test]
25557c327f3SZhao Liu fn test_vmstate_struct_varray_uint32_multiply() {
256cff1ec67SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
25757c327f3SZhao Liu 
25857c327f3SZhao Liu     // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to
25957c327f3SZhao Liu     // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32)
26057c327f3SZhao Liu     assert_eq!(
26157c327f3SZhao Liu         unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
26257c327f3SZhao Liu         b"arr_a_mul\0"
26357c327f3SZhao Liu     );
26457c327f3SZhao Liu     assert_eq!(foo_fields[3].offset, 64);
26557c327f3SZhao Liu     assert_eq!(foo_fields[3].num_offset, 124);
26657c327f3SZhao Liu     assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field.
26757c327f3SZhao Liu     assert_eq!(foo_fields[3].version_id, 2);
26857c327f3SZhao Liu     assert_eq!(foo_fields[3].size, 20);
26957c327f3SZhao Liu     assert_eq!(foo_fields[3].num, 32);
27057c327f3SZhao Liu     assert_eq!(
27157c327f3SZhao Liu         foo_fields[3].flags.0,
27257c327f3SZhao Liu         VMStateFlags::VMS_STRUCT.0
27357c327f3SZhao Liu             | VMStateFlags::VMS_VARRAY_UINT32.0
27457c327f3SZhao Liu             | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0
27557c327f3SZhao Liu     );
27657c327f3SZhao Liu     assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA);
27757c327f3SZhao Liu     assert!(foo_fields[3].field_exists.is_none());
27857c327f3SZhao Liu }
27957c327f3SZhao Liu 
28057c327f3SZhao Liu #[test]
28157c327f3SZhao Liu fn test_vmstate_macro_array() {
282cff1ec67SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
28357c327f3SZhao Liu 
28457c327f3SZhao Liu     // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to
28557c327f3SZhao Liu     // VMSTATE_ARRAY)
28657c327f3SZhao Liu     assert_eq!(
28757c327f3SZhao Liu         unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(),
28857c327f3SZhao Liu         b"arr_i64\0"
28957c327f3SZhao Liu     );
29057c327f3SZhao Liu     assert_eq!(foo_fields[4].offset, 144);
29157c327f3SZhao Liu     assert_eq!(foo_fields[4].num_offset, 0);
29257c327f3SZhao Liu     assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 });
29357c327f3SZhao Liu     assert_eq!(foo_fields[4].version_id, 0);
29457c327f3SZhao Liu     assert_eq!(foo_fields[4].size, 8);
29557c327f3SZhao Liu     assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32);
29657c327f3SZhao Liu     assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY);
29757c327f3SZhao Liu     assert!(foo_fields[4].vmsd.is_null());
29857c327f3SZhao Liu     assert!(foo_fields[4].field_exists.is_none());
299cff1ec67SZhao Liu }
300cff1ec67SZhao Liu 
301cff1ec67SZhao Liu #[test]
302cff1ec67SZhao Liu fn test_vmstate_struct_varray_uint8_wrapper() {
303cff1ec67SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
304cff1ec67SZhao Liu     let mut foo_b: FooB = Default::default();
305cff1ec67SZhao Liu     let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::<c_void>();
306cff1ec67SZhao Liu 
307cff1ec67SZhao Liu     // 6th VMStateField ("arr_a_wrap") in VMSTATE_FOOB (corresponding to
308cff1ec67SZhao Liu     // VMSTATE_STRUCT_VARRAY_UINT8). Other fields are checked in
309cff1ec67SZhao Liu     // test_vmstate_struct_varray_uint8.
310cff1ec67SZhao Liu     assert_eq!(
311cff1ec67SZhao Liu         unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(),
312cff1ec67SZhao Liu         b"arr_a_wrap\0"
313cff1ec67SZhao Liu     );
314cff1ec67SZhao Liu     assert_eq!(foo_fields[5].num_offset, 228);
315cff1ec67SZhao Liu     assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) });
31657c327f3SZhao Liu 
31757c327f3SZhao Liu     // The last VMStateField in VMSTATE_FOOB.
318cff1ec67SZhao Liu     assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END);
31957c327f3SZhao Liu }
3208df1b001SZhao Liu 
3218df1b001SZhao Liu // =========================== Test VMSTATE_FOOC ===========================
3228df1b001SZhao Liu // Test the use cases of the vmstate macro, corresponding to the following C
3238df1b001SZhao Liu // macro variants:
3248df1b001SZhao Liu //   * VMSTATE_FOOC:
3258df1b001SZhao Liu //     - VMSTATE_POINTER
3268df1b001SZhao Liu //     - VMSTATE_ARRAY_OF_POINTER
3278df1b001SZhao Liu struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible.
3288df1b001SZhao Liu 
3298df1b001SZhao Liu impl_vmstate_forward!(FooCWrapper);
3308df1b001SZhao Liu 
3318df1b001SZhao Liu #[repr(C)]
3328df1b001SZhao Liu #[derive(qemu_api_macros::offsets)]
3338df1b001SZhao Liu struct FooC {
3348df1b001SZhao Liu     ptr: *const i32,
3358df1b001SZhao Liu     ptr_a: NonNull<FooA>,
3368df1b001SZhao Liu     arr_ptr: [Box<u8>; FOO_ARRAY_MAX],
3378df1b001SZhao Liu     arr_ptr_wrap: FooCWrapper,
3388df1b001SZhao Liu }
3398df1b001SZhao Liu 
3408df1b001SZhao Liu static VMSTATE_FOOC: VMStateDescription = VMStateDescription {
3418df1b001SZhao Liu     name: c_str!("foo_c").as_ptr(),
3428df1b001SZhao Liu     version_id: 3,
3438df1b001SZhao Liu     minimum_version_id: 1,
3448df1b001SZhao Liu     fields: vmstate_fields! {
3458df1b001SZhao Liu         vmstate_of!(FooC, ptr).with_version_id(2),
3468df1b001SZhao Liu         // FIXME: Currently vmstate_struct doesn't support the pointer to structure.
3478df1b001SZhao Liu         // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>)
3488df1b001SZhao Liu         vmstate_unused!(size_of::<NonNull<FooA>>()),
3498df1b001SZhao Liu         vmstate_of!(FooC, arr_ptr),
3508df1b001SZhao Liu         vmstate_of!(FooC, arr_ptr_wrap),
3518df1b001SZhao Liu     },
3528df1b001SZhao Liu     ..Zeroable::ZERO
3538df1b001SZhao Liu };
3548df1b001SZhao Liu 
3558df1b001SZhao Liu const PTR_SIZE: usize = size_of::<*mut ()>();
3568df1b001SZhao Liu 
3578df1b001SZhao Liu #[test]
3588df1b001SZhao Liu fn test_vmstate_pointer() {
3598df1b001SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
3608df1b001SZhao Liu 
3618df1b001SZhao Liu     // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER)
3628df1b001SZhao Liu     assert_eq!(
3638df1b001SZhao Liu         unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
3648df1b001SZhao Liu         b"ptr\0"
3658df1b001SZhao Liu     );
3668df1b001SZhao Liu     assert_eq!(foo_fields[0].offset, 0);
3678df1b001SZhao Liu     assert_eq!(foo_fields[0].num_offset, 0);
3688df1b001SZhao Liu     assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 });
3698df1b001SZhao Liu     assert_eq!(foo_fields[0].version_id, 2);
3708df1b001SZhao Liu     assert_eq!(foo_fields[0].size, 4);
3718df1b001SZhao Liu     assert_eq!(foo_fields[0].num, 0);
3728df1b001SZhao Liu     assert_eq!(
3738df1b001SZhao Liu         foo_fields[0].flags.0,
3748df1b001SZhao Liu         VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0
3758df1b001SZhao Liu     );
3768df1b001SZhao Liu     assert!(foo_fields[0].vmsd.is_null());
3778df1b001SZhao Liu     assert!(foo_fields[0].field_exists.is_none());
3788df1b001SZhao Liu }
3798df1b001SZhao Liu 
3808df1b001SZhao Liu #[test]
3818df1b001SZhao Liu fn test_vmstate_macro_array_of_pointer() {
3828df1b001SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
3838df1b001SZhao Liu 
3848df1b001SZhao Liu     // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to
3858df1b001SZhao Liu     // VMSTATE_ARRAY_OF_POINTER)
3868df1b001SZhao Liu     assert_eq!(
3878df1b001SZhao Liu         unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
3888df1b001SZhao Liu         b"arr_ptr\0"
3898df1b001SZhao Liu     );
3908df1b001SZhao Liu     assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE);
3918df1b001SZhao Liu     assert_eq!(foo_fields[2].num_offset, 0);
3928df1b001SZhao Liu     assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
3938df1b001SZhao Liu     assert_eq!(foo_fields[2].version_id, 0);
3948df1b001SZhao Liu     assert_eq!(foo_fields[2].size, PTR_SIZE);
3958df1b001SZhao Liu     assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32);
3968df1b001SZhao Liu     assert_eq!(
3978df1b001SZhao Liu         foo_fields[2].flags.0,
3988df1b001SZhao Liu         VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0
3998df1b001SZhao Liu     );
4008df1b001SZhao Liu     assert!(foo_fields[2].vmsd.is_null());
4018df1b001SZhao Liu     assert!(foo_fields[2].field_exists.is_none());
4028df1b001SZhao Liu }
4038df1b001SZhao Liu 
4048df1b001SZhao Liu #[test]
4058df1b001SZhao Liu fn test_vmstate_macro_array_of_pointer_wrapped() {
4068df1b001SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
4078df1b001SZhao Liu 
4088df1b001SZhao Liu     // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to
4098df1b001SZhao Liu     // VMSTATE_ARRAY_OF_POINTER)
4108df1b001SZhao Liu     assert_eq!(
4118df1b001SZhao Liu         unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
4128df1b001SZhao Liu         b"arr_ptr_wrap\0"
4138df1b001SZhao Liu     );
4148df1b001SZhao Liu     assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE);
4158df1b001SZhao Liu     assert_eq!(foo_fields[3].num_offset, 0);
416d031d2faSZhao Liu     assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_uint8 });
4178df1b001SZhao Liu     assert_eq!(foo_fields[3].version_id, 0);
4188df1b001SZhao Liu     assert_eq!(foo_fields[3].size, PTR_SIZE);
4198df1b001SZhao Liu     assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32);
4208df1b001SZhao Liu     assert_eq!(
421d031d2faSZhao Liu         foo_fields[3].flags.0,
4228df1b001SZhao Liu         VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0
4238df1b001SZhao Liu     );
4248df1b001SZhao Liu     assert!(foo_fields[3].vmsd.is_null());
4258df1b001SZhao Liu     assert!(foo_fields[3].field_exists.is_none());
4268df1b001SZhao Liu 
4278df1b001SZhao Liu     // The last VMStateField in VMSTATE_FOOC.
4288df1b001SZhao Liu     assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END);
4298df1b001SZhao Liu }
4309bd7e6f7SZhao Liu 
4319bd7e6f7SZhao Liu // =========================== Test VMSTATE_FOOD ===========================
4329bd7e6f7SZhao Liu // Test the use cases of the vmstate macro, corresponding to the following C
4339bd7e6f7SZhao Liu // macro variants:
4349bd7e6f7SZhao Liu //   * VMSTATE_FOOD:
4359bd7e6f7SZhao Liu //     - VMSTATE_VALIDATE
4369bd7e6f7SZhao Liu 
4379bd7e6f7SZhao Liu // Add more member fields when vmstate_of/vmstate_struct support "test"
4389bd7e6f7SZhao Liu // parameter.
4399bd7e6f7SZhao Liu struct FooD;
4409bd7e6f7SZhao Liu 
4419bd7e6f7SZhao Liu impl FooD {
4429bd7e6f7SZhao Liu     fn validate_food_0(&self, _version_id: u8) -> bool {
4439bd7e6f7SZhao Liu         true
4449bd7e6f7SZhao Liu     }
4459bd7e6f7SZhao Liu 
4469bd7e6f7SZhao Liu     fn validate_food_1(_state: &FooD, _version_id: u8) -> bool {
4479bd7e6f7SZhao Liu         false
4489bd7e6f7SZhao Liu     }
4499bd7e6f7SZhao Liu }
4509bd7e6f7SZhao Liu 
4519bd7e6f7SZhao Liu fn validate_food_2(_state: &FooD, _version_id: u8) -> bool {
4529bd7e6f7SZhao Liu     true
4539bd7e6f7SZhao Liu }
4549bd7e6f7SZhao Liu 
4559bd7e6f7SZhao Liu static VMSTATE_FOOD: VMStateDescription = VMStateDescription {
4569bd7e6f7SZhao Liu     name: c_str!("foo_d").as_ptr(),
4579bd7e6f7SZhao Liu     version_id: 3,
4589bd7e6f7SZhao Liu     minimum_version_id: 1,
4599bd7e6f7SZhao Liu     fields: vmstate_fields! {
4609bd7e6f7SZhao Liu         vmstate_validate!(FooD, c_str!("foo_d_0"), FooD::validate_food_0),
4619bd7e6f7SZhao Liu         vmstate_validate!(FooD, c_str!("foo_d_1"), FooD::validate_food_1),
4629bd7e6f7SZhao Liu         vmstate_validate!(FooD, c_str!("foo_d_2"), validate_food_2),
4639bd7e6f7SZhao Liu     },
4649bd7e6f7SZhao Liu     ..Zeroable::ZERO
4659bd7e6f7SZhao Liu };
4669bd7e6f7SZhao Liu 
4679bd7e6f7SZhao Liu #[test]
4689bd7e6f7SZhao Liu fn test_vmstate_validate() {
4699bd7e6f7SZhao Liu     let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) };
4709bd7e6f7SZhao Liu     let mut foo_d = FooD;
4719bd7e6f7SZhao Liu     let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::<c_void>();
4729bd7e6f7SZhao Liu 
4739bd7e6f7SZhao Liu     // 1st VMStateField in VMSTATE_FOOD
4749bd7e6f7SZhao Liu     assert_eq!(
4759bd7e6f7SZhao Liu         unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
4769bd7e6f7SZhao Liu         b"foo_d_0\0"
4779bd7e6f7SZhao Liu     );
4789bd7e6f7SZhao Liu     assert_eq!(foo_fields[0].offset, 0);
4799bd7e6f7SZhao Liu     assert_eq!(foo_fields[0].num_offset, 0);
4809bd7e6f7SZhao Liu     assert!(foo_fields[0].info.is_null());
4819bd7e6f7SZhao Liu     assert_eq!(foo_fields[0].version_id, 0);
4829bd7e6f7SZhao Liu     assert_eq!(foo_fields[0].size, 0);
4839bd7e6f7SZhao Liu     assert_eq!(foo_fields[0].num, 0);
4849bd7e6f7SZhao Liu     assert_eq!(
4859bd7e6f7SZhao Liu         foo_fields[0].flags.0,
4869bd7e6f7SZhao Liu         VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0
4879bd7e6f7SZhao Liu     );
4889bd7e6f7SZhao Liu     assert!(foo_fields[0].vmsd.is_null());
4899bd7e6f7SZhao Liu     assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) });
4909bd7e6f7SZhao Liu 
4919bd7e6f7SZhao Liu     // 2nd VMStateField in VMSTATE_FOOD
4929bd7e6f7SZhao Liu     assert_eq!(
4939bd7e6f7SZhao Liu         unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
4949bd7e6f7SZhao Liu         b"foo_d_1\0"
4959bd7e6f7SZhao Liu     );
4969bd7e6f7SZhao Liu     assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) });
4979bd7e6f7SZhao Liu 
4989bd7e6f7SZhao Liu     // 3rd VMStateField in VMSTATE_FOOD
4999bd7e6f7SZhao Liu     assert_eq!(
5009bd7e6f7SZhao Liu         unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
5019bd7e6f7SZhao Liu         b"foo_d_2\0"
5029bd7e6f7SZhao Liu     );
5039bd7e6f7SZhao Liu     assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) });
5049bd7e6f7SZhao Liu 
5059bd7e6f7SZhao Liu     // The last VMStateField in VMSTATE_FOOD.
5069bd7e6f7SZhao Liu     assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END);
5079bd7e6f7SZhao Liu }
508