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