xref: /qemu/rust/qemu-api/tests/vmstate_tests.rs (revision b103cc6e74ac92f070a0e004bd84334e845c20b5)
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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[3].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[3].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 {
412     fn validate_food_0(&self, _version_id: u8) -> bool {
413         true
414     }
415 
416     fn validate_food_1(_state: &FooD, _version_id: u8) -> bool {
417         false
418     }
419 }
420 
421 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]
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