1 // Copyright (C) 2025 Intel Corporation. 2 // Author(s): Zhao Liu <zhai1.liu@intel.com> 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 use std::{ffi::CStr, mem::size_of, slice}; 6 7 use qemu_api::{ 8 bindings::{ 9 vmstate_info_bool, vmstate_info_int64, vmstate_info_int8, vmstate_info_uint64, 10 vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, 11 }, 12 c_str, 13 cell::BqlCell, 14 vmstate::{VMStateDescription, VMStateField}, 15 vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, 16 zeroable::Zeroable, 17 }; 18 19 const FOO_ARRAY_MAX: usize = 3; 20 21 // =========================== Test VMSTATE_FOOA =========================== 22 // Test the use cases of the vmstate macro, corresponding to the following C 23 // macro variants: 24 // * VMSTATE_FOOA: 25 // - VMSTATE_U16 26 // - VMSTATE_UNUSED 27 // - VMSTATE_VARRAY_UINT16_UNSAFE 28 // - VMSTATE_VARRAY_MULTIPLY 29 #[repr(C)] 30 #[derive(qemu_api_macros::offsets)] 31 struct FooA { 32 arr: [u8; FOO_ARRAY_MAX], 33 num: u16, 34 arr_mul: [i8; FOO_ARRAY_MAX], 35 num_mul: u32, 36 elem: i8, 37 } 38 39 static VMSTATE_FOOA: VMStateDescription = VMStateDescription { 40 name: c_str!("foo_a").as_ptr(), 41 version_id: 1, 42 minimum_version_id: 1, 43 fields: vmstate_fields! { 44 vmstate_of!(FooA, elem), 45 vmstate_unused!(size_of::<i64>()), 46 vmstate_of!(FooA, arr[0 .. num]).with_version_id(0), 47 vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]), 48 }, 49 ..Zeroable::ZERO 50 }; 51 52 #[test] 53 fn test_vmstate_uint16() { 54 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; 55 56 // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16) 57 assert_eq!( 58 unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), 59 b"elem\0" 60 ); 61 assert_eq!(foo_fields[0].offset, 16); 62 assert_eq!(foo_fields[0].num_offset, 0); 63 assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 }); 64 assert_eq!(foo_fields[0].version_id, 0); 65 assert_eq!(foo_fields[0].size, 1); 66 assert_eq!(foo_fields[0].num, 0); 67 assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); 68 assert!(foo_fields[0].vmsd.is_null()); 69 assert!(foo_fields[0].field_exists.is_none()); 70 } 71 72 #[test] 73 fn test_vmstate_unused() { 74 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; 75 76 // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED) 77 assert_eq!( 78 unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), 79 b"unused\0" 80 ); 81 assert_eq!(foo_fields[1].offset, 0); 82 assert_eq!(foo_fields[1].num_offset, 0); 83 assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer }); 84 assert_eq!(foo_fields[1].version_id, 0); 85 assert_eq!(foo_fields[1].size, 8); 86 assert_eq!(foo_fields[1].num, 0); 87 assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER); 88 assert!(foo_fields[1].vmsd.is_null()); 89 assert!(foo_fields[1].field_exists.is_none()); 90 } 91 92 #[test] 93 fn test_vmstate_varray_uint16_unsafe() { 94 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; 95 96 // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to 97 // VMSTATE_VARRAY_UINT16_UNSAFE) 98 assert_eq!( 99 unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), 100 b"arr\0" 101 ); 102 assert_eq!(foo_fields[2].offset, 0); 103 assert_eq!(foo_fields[2].num_offset, 4); 104 assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); 105 assert_eq!(foo_fields[2].version_id, 0); 106 assert_eq!(foo_fields[2].size, 1); 107 assert_eq!(foo_fields[2].num, 0); 108 assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16); 109 assert!(foo_fields[2].vmsd.is_null()); 110 assert!(foo_fields[2].field_exists.is_none()); 111 } 112 113 #[test] 114 fn test_vmstate_varray_multiply() { 115 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; 116 117 // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to 118 // VMSTATE_VARRAY_MULTIPLY) 119 assert_eq!( 120 unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), 121 b"arr_mul\0" 122 ); 123 assert_eq!(foo_fields[3].offset, 6); 124 assert_eq!(foo_fields[3].num_offset, 12); 125 assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 }); 126 assert_eq!(foo_fields[3].version_id, 0); 127 assert_eq!(foo_fields[3].size, 1); 128 assert_eq!(foo_fields[3].num, 16); 129 assert_eq!( 130 foo_fields[3].flags.0, 131 VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 132 ); 133 assert!(foo_fields[3].vmsd.is_null()); 134 assert!(foo_fields[3].field_exists.is_none()); 135 136 // The last VMStateField in VMSTATE_FOOA. 137 assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); 138 } 139 140 // =========================== Test VMSTATE_FOOB =========================== 141 // Test the use cases of the vmstate macro, corresponding to the following C 142 // macro variants: 143 // * VMSTATE_FOOB: 144 // - VMSTATE_BOOL_V 145 // - VMSTATE_U64 146 // - VMSTATE_STRUCT_VARRAY_UINT8 147 // - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 148 // - VMSTATE_ARRAY 149 #[repr(C)] 150 #[derive(qemu_api_macros::offsets)] 151 struct FooB { 152 arr_a: [FooA; FOO_ARRAY_MAX], 153 num_a: u8, 154 arr_a_mul: [FooA; FOO_ARRAY_MAX], 155 num_a_mul: u32, 156 wrap: BqlCell<u64>, 157 val: bool, 158 // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. 159 arr_i64: [i64; FOO_ARRAY_MAX], 160 } 161 162 static VMSTATE_FOOB: VMStateDescription = VMStateDescription { 163 name: c_str!("foo_b").as_ptr(), 164 version_id: 2, 165 minimum_version_id: 1, 166 fields: vmstate_fields! { 167 vmstate_of!(FooB, val).with_version_id(2), 168 vmstate_of!(FooB, wrap), 169 vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), 170 vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), 171 vmstate_of!(FooB, arr_i64), 172 }, 173 ..Zeroable::ZERO 174 }; 175 176 #[test] 177 fn test_vmstate_bool_v() { 178 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; 179 180 // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) 181 assert_eq!( 182 unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), 183 b"val\0" 184 ); 185 assert_eq!(foo_fields[0].offset, 136); 186 assert_eq!(foo_fields[0].num_offset, 0); 187 assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool }); 188 assert_eq!(foo_fields[0].version_id, 2); 189 assert_eq!(foo_fields[0].size, 1); 190 assert_eq!(foo_fields[0].num, 0); 191 assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); 192 assert!(foo_fields[0].vmsd.is_null()); 193 assert!(foo_fields[0].field_exists.is_none()); 194 } 195 196 #[test] 197 fn test_vmstate_uint64() { 198 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; 199 200 // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) 201 assert_eq!( 202 unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), 203 b"wrap\0" 204 ); 205 assert_eq!(foo_fields[1].offset, 128); 206 assert_eq!(foo_fields[1].num_offset, 0); 207 assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 }); 208 assert_eq!(foo_fields[1].version_id, 0); 209 assert_eq!(foo_fields[1].size, 8); 210 assert_eq!(foo_fields[1].num, 0); 211 assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE); 212 assert!(foo_fields[1].vmsd.is_null()); 213 assert!(foo_fields[1].field_exists.is_none()); 214 } 215 216 #[test] 217 fn test_vmstate_struct_varray_uint8() { 218 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; 219 220 // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to 221 // VMSTATE_STRUCT_VARRAY_UINT8) 222 assert_eq!( 223 unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), 224 b"arr_a\0" 225 ); 226 assert_eq!(foo_fields[2].offset, 0); 227 assert_eq!(foo_fields[2].num_offset, 60); 228 assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. 229 assert_eq!(foo_fields[2].version_id, 1); 230 assert_eq!(foo_fields[2].size, 20); 231 assert_eq!(foo_fields[2].num, 0); 232 assert_eq!( 233 foo_fields[2].flags.0, 234 VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0 235 ); 236 assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA); 237 assert!(foo_fields[2].field_exists.is_none()); 238 } 239 240 #[test] 241 fn test_vmstate_struct_varray_uint32_multiply() { 242 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; 243 244 // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to 245 // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) 246 assert_eq!( 247 unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), 248 b"arr_a_mul\0" 249 ); 250 assert_eq!(foo_fields[3].offset, 64); 251 assert_eq!(foo_fields[3].num_offset, 124); 252 assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. 253 assert_eq!(foo_fields[3].version_id, 2); 254 assert_eq!(foo_fields[3].size, 20); 255 assert_eq!(foo_fields[3].num, 32); 256 assert_eq!( 257 foo_fields[3].flags.0, 258 VMStateFlags::VMS_STRUCT.0 259 | VMStateFlags::VMS_VARRAY_UINT32.0 260 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 261 ); 262 assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA); 263 assert!(foo_fields[3].field_exists.is_none()); 264 } 265 266 #[test] 267 fn test_vmstate_macro_array() { 268 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; 269 270 // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to 271 // VMSTATE_ARRAY) 272 assert_eq!( 273 unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(), 274 b"arr_i64\0" 275 ); 276 assert_eq!(foo_fields[4].offset, 144); 277 assert_eq!(foo_fields[4].num_offset, 0); 278 assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 }); 279 assert_eq!(foo_fields[4].version_id, 0); 280 assert_eq!(foo_fields[4].size, 8); 281 assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32); 282 assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); 283 assert!(foo_fields[4].vmsd.is_null()); 284 assert!(foo_fields[4].field_exists.is_none()); 285 286 // The last VMStateField in VMSTATE_FOOB. 287 assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END); 288 } 289