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, os::raw::c_void, ptr::NonNull, slice};
6
7 use qemu_api::{
8 bindings::{
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 },
12 c_str,
13 cell::{BqlCell, Opaque},
14 impl_vmstate_forward,
15 vmstate::{VMStateDescription, VMStateField},
16 vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate,
17 zeroable::Zeroable,
18 };
19
20 const FOO_ARRAY_MAX: usize = 3;
21
22 // =========================== Test VMSTATE_FOOA ===========================
23 // Test the use cases of the vmstate macro, corresponding to the following C
24 // macro variants:
25 // * VMSTATE_FOOA:
26 // - VMSTATE_U16
27 // - VMSTATE_UNUSED
28 // - VMSTATE_VARRAY_UINT16_UNSAFE
29 // - VMSTATE_VARRAY_MULTIPLY
30 #[repr(C)]
31 #[derive(qemu_api_macros::offsets)]
32 struct FooA {
33 arr: [u8; FOO_ARRAY_MAX],
34 num: u16,
35 arr_mul: [i8; FOO_ARRAY_MAX],
36 num_mul: u32,
37 elem: i8,
38 }
39
40 static VMSTATE_FOOA: VMStateDescription = VMStateDescription {
41 name: c_str!("foo_a").as_ptr(),
42 version_id: 1,
43 minimum_version_id: 1,
44 fields: vmstate_fields! {
45 vmstate_of!(FooA, elem),
46 vmstate_unused!(size_of::<i64>()),
47 vmstate_of!(FooA, arr[0 .. num]).with_version_id(0),
48 vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]),
49 },
50 ..Zeroable::ZERO
51 };
52
53 #[test]
test_vmstate_uint16()54 fn test_vmstate_uint16() {
55 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
56
57 // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16)
58 assert_eq!(
59 unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
60 b"elem\0"
61 );
62 assert_eq!(foo_fields[0].offset, 16);
63 assert_eq!(foo_fields[0].num_offset, 0);
64 assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 });
65 assert_eq!(foo_fields[0].version_id, 0);
66 assert_eq!(foo_fields[0].size, 1);
67 assert_eq!(foo_fields[0].num, 0);
68 assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE);
69 assert!(foo_fields[0].vmsd.is_null());
70 assert!(foo_fields[0].field_exists.is_none());
71 }
72
73 #[test]
test_vmstate_unused()74 fn test_vmstate_unused() {
75 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
76
77 // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED)
78 assert_eq!(
79 unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
80 b"unused\0"
81 );
82 assert_eq!(foo_fields[1].offset, 0);
83 assert_eq!(foo_fields[1].num_offset, 0);
84 assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer });
85 assert_eq!(foo_fields[1].version_id, 0);
86 assert_eq!(foo_fields[1].size, 8);
87 assert_eq!(foo_fields[1].num, 0);
88 assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER);
89 assert!(foo_fields[1].vmsd.is_null());
90 assert!(foo_fields[1].field_exists.is_none());
91 }
92
93 #[test]
test_vmstate_varray_uint16_unsafe()94 fn test_vmstate_varray_uint16_unsafe() {
95 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
96
97 // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to
98 // VMSTATE_VARRAY_UINT16_UNSAFE)
99 assert_eq!(
100 unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
101 b"arr\0"
102 );
103 assert_eq!(foo_fields[2].offset, 0);
104 assert_eq!(foo_fields[2].num_offset, 4);
105 assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 });
106 assert_eq!(foo_fields[2].version_id, 0);
107 assert_eq!(foo_fields[2].size, 1);
108 assert_eq!(foo_fields[2].num, 0);
109 assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16);
110 assert!(foo_fields[2].vmsd.is_null());
111 assert!(foo_fields[2].field_exists.is_none());
112 }
113
114 #[test]
test_vmstate_varray_multiply()115 fn test_vmstate_varray_multiply() {
116 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
117
118 // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to
119 // VMSTATE_VARRAY_MULTIPLY)
120 assert_eq!(
121 unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
122 b"arr_mul\0"
123 );
124 assert_eq!(foo_fields[3].offset, 6);
125 assert_eq!(foo_fields[3].num_offset, 12);
126 assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 });
127 assert_eq!(foo_fields[3].version_id, 0);
128 assert_eq!(foo_fields[3].size, 1);
129 assert_eq!(foo_fields[3].num, 16);
130 assert_eq!(
131 foo_fields[3].flags.0,
132 VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0
133 );
134 assert!(foo_fields[3].vmsd.is_null());
135 assert!(foo_fields[3].field_exists.is_none());
136
137 // The last VMStateField in VMSTATE_FOOA.
138 assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END);
139 }
140
141 // =========================== Test VMSTATE_FOOB ===========================
142 // Test the use cases of the vmstate macro, corresponding to the following C
143 // macro variants:
144 // * VMSTATE_FOOB:
145 // - VMSTATE_BOOL_V
146 // - VMSTATE_U64
147 // - VMSTATE_STRUCT_VARRAY_UINT8
148 // - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32
149 // - VMSTATE_ARRAY
150 #[repr(C)]
151 #[derive(qemu_api_macros::offsets)]
152 struct FooB {
153 arr_a: [FooA; FOO_ARRAY_MAX],
154 num_a: u8,
155 arr_a_mul: [FooA; FOO_ARRAY_MAX],
156 num_a_mul: u32,
157 wrap: BqlCell<u64>,
158 val: bool,
159 // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test.
160 arr_i64: [i64; FOO_ARRAY_MAX],
161 }
162
163 static VMSTATE_FOOB: VMStateDescription = VMStateDescription {
164 name: c_str!("foo_b").as_ptr(),
165 version_id: 2,
166 minimum_version_id: 1,
167 fields: vmstate_fields! {
168 vmstate_of!(FooB, val).with_version_id(2),
169 vmstate_of!(FooB, wrap),
170 vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1),
171 vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2),
172 vmstate_of!(FooB, arr_i64),
173 },
174 ..Zeroable::ZERO
175 };
176
177 #[test]
test_vmstate_bool_v()178 fn test_vmstate_bool_v() {
179 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) };
180
181 // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V)
182 assert_eq!(
183 unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
184 b"val\0"
185 );
186 assert_eq!(foo_fields[0].offset, 136);
187 assert_eq!(foo_fields[0].num_offset, 0);
188 assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool });
189 assert_eq!(foo_fields[0].version_id, 2);
190 assert_eq!(foo_fields[0].size, 1);
191 assert_eq!(foo_fields[0].num, 0);
192 assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE);
193 assert!(foo_fields[0].vmsd.is_null());
194 assert!(foo_fields[0].field_exists.is_none());
195 }
196
197 #[test]
test_vmstate_uint64()198 fn test_vmstate_uint64() {
199 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) };
200
201 // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64)
202 assert_eq!(
203 unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
204 b"wrap\0"
205 );
206 assert_eq!(foo_fields[1].offset, 128);
207 assert_eq!(foo_fields[1].num_offset, 0);
208 assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 });
209 assert_eq!(foo_fields[1].version_id, 0);
210 assert_eq!(foo_fields[1].size, 8);
211 assert_eq!(foo_fields[1].num, 0);
212 assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE);
213 assert!(foo_fields[1].vmsd.is_null());
214 assert!(foo_fields[1].field_exists.is_none());
215 }
216
217 #[test]
test_vmstate_struct_varray_uint8()218 fn test_vmstate_struct_varray_uint8() {
219 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) };
220
221 // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to
222 // VMSTATE_STRUCT_VARRAY_UINT8)
223 assert_eq!(
224 unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
225 b"arr_a\0"
226 );
227 assert_eq!(foo_fields[2].offset, 0);
228 assert_eq!(foo_fields[2].num_offset, 60);
229 assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field.
230 assert_eq!(foo_fields[2].version_id, 1);
231 assert_eq!(foo_fields[2].size, 20);
232 assert_eq!(foo_fields[2].num, 0);
233 assert_eq!(
234 foo_fields[2].flags.0,
235 VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0
236 );
237 assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA);
238 assert!(foo_fields[2].field_exists.is_none());
239 }
240
241 #[test]
test_vmstate_struct_varray_uint32_multiply()242 fn test_vmstate_struct_varray_uint32_multiply() {
243 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) };
244
245 // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to
246 // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32)
247 assert_eq!(
248 unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(),
249 b"arr_a_mul\0"
250 );
251 assert_eq!(foo_fields[3].offset, 64);
252 assert_eq!(foo_fields[3].num_offset, 124);
253 assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field.
254 assert_eq!(foo_fields[3].version_id, 2);
255 assert_eq!(foo_fields[3].size, 20);
256 assert_eq!(foo_fields[3].num, 32);
257 assert_eq!(
258 foo_fields[3].flags.0,
259 VMStateFlags::VMS_STRUCT.0
260 | VMStateFlags::VMS_VARRAY_UINT32.0
261 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0
262 );
263 assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA);
264 assert!(foo_fields[3].field_exists.is_none());
265 }
266
267 #[test]
test_vmstate_macro_array()268 fn test_vmstate_macro_array() {
269 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) };
270
271 // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to
272 // VMSTATE_ARRAY)
273 assert_eq!(
274 unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(),
275 b"arr_i64\0"
276 );
277 assert_eq!(foo_fields[4].offset, 144);
278 assert_eq!(foo_fields[4].num_offset, 0);
279 assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 });
280 assert_eq!(foo_fields[4].version_id, 0);
281 assert_eq!(foo_fields[4].size, 8);
282 assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32);
283 assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY);
284 assert!(foo_fields[4].vmsd.is_null());
285 assert!(foo_fields[4].field_exists.is_none());
286
287 // The last VMStateField in VMSTATE_FOOB.
288 assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END);
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]
test_vmstate_pointer()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]
test_vmstate_macro_array_of_pointer()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]
test_vmstate_macro_array_of_pointer_wrapped()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 }
400
401 // =========================== Test VMSTATE_FOOD ===========================
402 // Test the use cases of the vmstate macro, corresponding to the following C
403 // macro variants:
404 // * VMSTATE_FOOD:
405 // - VMSTATE_VALIDATE
406
407 // Add more member fields when vmstate_of/vmstate_struct support "test"
408 // parameter.
409 struct FooD;
410
411 impl FooD {
validate_food_0(&self, _version_id: u8) -> bool412 fn validate_food_0(&self, _version_id: u8) -> bool {
413 true
414 }
415
validate_food_1(_state: &FooD, _version_id: u8) -> bool416 fn validate_food_1(_state: &FooD, _version_id: u8) -> bool {
417 false
418 }
419 }
420
validate_food_2(_state: &FooD, _version_id: u8) -> bool421 fn validate_food_2(_state: &FooD, _version_id: u8) -> bool {
422 true
423 }
424
425 static VMSTATE_FOOD: VMStateDescription = VMStateDescription {
426 name: c_str!("foo_d").as_ptr(),
427 version_id: 3,
428 minimum_version_id: 1,
429 fields: vmstate_fields! {
430 vmstate_validate!(FooD, c_str!("foo_d_0"), FooD::validate_food_0),
431 vmstate_validate!(FooD, c_str!("foo_d_1"), FooD::validate_food_1),
432 vmstate_validate!(FooD, c_str!("foo_d_2"), validate_food_2),
433 },
434 ..Zeroable::ZERO
435 };
436
437 #[test]
test_vmstate_validate()438 fn test_vmstate_validate() {
439 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) };
440 let mut foo_d = FooD;
441 let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::<c_void>();
442
443 // 1st VMStateField in VMSTATE_FOOD
444 assert_eq!(
445 unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(),
446 b"foo_d_0\0"
447 );
448 assert_eq!(foo_fields[0].offset, 0);
449 assert_eq!(foo_fields[0].num_offset, 0);
450 assert!(foo_fields[0].info.is_null());
451 assert_eq!(foo_fields[0].version_id, 0);
452 assert_eq!(foo_fields[0].size, 0);
453 assert_eq!(foo_fields[0].num, 0);
454 assert_eq!(
455 foo_fields[0].flags.0,
456 VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0
457 );
458 assert!(foo_fields[0].vmsd.is_null());
459 assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) });
460
461 // 2nd VMStateField in VMSTATE_FOOD
462 assert_eq!(
463 unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
464 b"foo_d_1\0"
465 );
466 assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) });
467
468 // 3rd VMStateField in VMSTATE_FOOD
469 assert_eq!(
470 unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(),
471 b"foo_d_2\0"
472 );
473 assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) });
474
475 // The last VMStateField in VMSTATE_FOOD.
476 assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END);
477 }
478