1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include "qemu/osdep.h"
26
27#include "../migration/migration.h"
28#include "migration/vmstate.h"
29#include "migration/qemu-file-types.h"
30#include "../migration/qemu-file.h"
31#include "../migration/qemu-file-channel.h"
32#include "../migration/savevm.h"
33#include "qemu/coroutine.h"
34#include "qemu/module.h"
35#include "io/channel-file.h"
36
37static int temp_fd;
38
39
40
41static QEMUFile *open_test_file(bool write)
42{
43 int fd;
44 QIOChannel *ioc;
45 QEMUFile *f;
46
47 fd = dup(temp_fd);
48 g_assert(fd >= 0);
49 lseek(fd, 0, SEEK_SET);
50 if (write) {
51 g_assert_cmpint(ftruncate(fd, 0), ==, 0);
52 }
53 ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd));
54 if (write) {
55 f = qemu_fopen_channel_output(ioc);
56 } else {
57 f = qemu_fopen_channel_input(ioc);
58 }
59 object_unref(OBJECT(ioc));
60 return f;
61}
62
63#define SUCCESS(val) \
64 g_assert_cmpint((val), ==, 0)
65
66#define FAILURE(val) \
67 g_assert_cmpint((val), !=, 0)
68
69static void save_vmstate(const VMStateDescription *desc, void *obj)
70{
71 QEMUFile *f = open_test_file(true);
72
73
74 int ret = vmstate_save_state(f, desc, obj, NULL);
75 g_assert(!ret);
76 qemu_put_byte(f, QEMU_VM_EOF);
77 g_assert(!qemu_file_get_error(f));
78 qemu_fclose(f);
79}
80
81static void save_buffer(const uint8_t *buf, size_t buf_size)
82{
83 QEMUFile *fsave = open_test_file(true);
84 qemu_put_buffer(fsave, buf, buf_size);
85 qemu_fclose(fsave);
86}
87
88static void compare_vmstate(const uint8_t *wire, size_t size)
89{
90 QEMUFile *f = open_test_file(false);
91 uint8_t result[size];
92
93
94
95 g_assert_cmpint(qemu_get_buffer(f, result, sizeof(result)), ==,
96 sizeof(result));
97 g_assert(!qemu_file_get_error(f));
98
99
100
101 SUCCESS(memcmp(result, wire, sizeof(result)));
102
103
104 qemu_get_byte(f);
105 g_assert_cmpint(qemu_file_get_error(f), ==, -EIO);
106
107 qemu_fclose(f);
108}
109
110static int load_vmstate_one(const VMStateDescription *desc, void *obj,
111 int version, const uint8_t *wire, size_t size)
112{
113 QEMUFile *f;
114 int ret;
115
116 f = open_test_file(true);
117 qemu_put_buffer(f, wire, size);
118 qemu_fclose(f);
119
120 f = open_test_file(false);
121 ret = vmstate_load_state(f, desc, obj, version);
122 if (ret) {
123 g_assert(qemu_file_get_error(f));
124 } else{
125 g_assert(!qemu_file_get_error(f));
126 }
127 qemu_fclose(f);
128 return ret;
129}
130
131
132static int load_vmstate(const VMStateDescription *desc,
133 void *obj, void *obj_clone,
134 void (*obj_copy)(void *, void*),
135 int version, const uint8_t *wire, size_t size)
136{
137
138 obj_copy(obj_clone, obj);
139 FAILURE(load_vmstate_one(desc, obj, version, wire, 0));
140
141
142
143
144 if (size > 3) {
145
146
147 obj_copy(obj, obj_clone);
148 FAILURE(load_vmstate_one(desc, obj, version, wire, size - 2));
149
150
151 obj_copy(obj, obj_clone);
152 FAILURE(load_vmstate_one(desc, obj, version, wire, size/2));
153
154
155 obj_copy(obj, obj_clone);
156 FAILURE(load_vmstate_one(desc, obj, version, wire + (size/2), size/2));
157
158 }
159 obj_copy(obj, obj_clone);
160 return load_vmstate_one(desc, obj, version, wire, size);
161}
162
163
164
165typedef struct TestSimple {
166 bool b_1, b_2;
167 uint8_t u8_1;
168 uint16_t u16_1;
169 uint32_t u32_1;
170 uint64_t u64_1;
171 int8_t i8_1, i8_2;
172 int16_t i16_1, i16_2;
173 int32_t i32_1, i32_2;
174 int64_t i64_1, i64_2;
175} TestSimple;
176
177
178
179TestSimple obj_simple = {
180 .b_1 = true,
181 .b_2 = false,
182 .u8_1 = 130,
183 .u16_1 = 512,
184 .u32_1 = 70000,
185 .u64_1 = 12121212,
186 .i8_1 = 65,
187 .i8_2 = -65,
188 .i16_1 = 512,
189 .i16_2 = -512,
190 .i32_1 = 70000,
191 .i32_2 = -70000,
192 .i64_1 = 12121212,
193 .i64_2 = -12121212,
194};
195
196
197
198
199static const VMStateDescription vmstate_simple_primitive = {
200 .name = "simple/primitive",
201 .version_id = 1,
202 .minimum_version_id = 1,
203 .fields = (VMStateField[]) {
204 VMSTATE_BOOL(b_1, TestSimple),
205 VMSTATE_BOOL(b_2, TestSimple),
206 VMSTATE_UINT8(u8_1, TestSimple),
207 VMSTATE_UINT16(u16_1, TestSimple),
208 VMSTATE_UINT32(u32_1, TestSimple),
209 VMSTATE_UINT64(u64_1, TestSimple),
210 VMSTATE_INT8(i8_1, TestSimple),
211 VMSTATE_INT8(i8_2, TestSimple),
212 VMSTATE_INT16(i16_1, TestSimple),
213 VMSTATE_INT16(i16_2, TestSimple),
214 VMSTATE_INT32(i32_1, TestSimple),
215 VMSTATE_INT32(i32_2, TestSimple),
216 VMSTATE_INT64(i64_1, TestSimple),
217 VMSTATE_INT64(i64_2, TestSimple),
218 VMSTATE_END_OF_LIST()
219 }
220};
221
222
223
224
225
226
227
228
229
230
231
232
233
234uint8_t wire_simple_primitive[] = {
235 0x01,
236 0x00,
237 0x82,
238 0x02, 0x00,
239 0x00, 0x01, 0x11, 0x70,
240 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c,
241 0x41,
242 0xbf,
243 0x02, 0x00,
244 0xfe, 0x0,
245 0x00, 0x01, 0x11, 0x70,
246 0xff, 0xfe, 0xee, 0x90,
247 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c,
248 0xff, 0xff, 0xff, 0xff, 0xff, 0x47, 0x0b, 0x84,
249 QEMU_VM_EOF,
250};
251
252static void obj_simple_copy(void *target, void *source)
253{
254 memcpy(target, source, sizeof(TestSimple));
255}
256
257static void test_simple_primitive(void)
258{
259 TestSimple obj, obj_clone;
260
261 memset(&obj, 0, sizeof(obj));
262 save_vmstate(&vmstate_simple_primitive, &obj_simple);
263
264 compare_vmstate(wire_simple_primitive, sizeof(wire_simple_primitive));
265
266 SUCCESS(load_vmstate(&vmstate_simple_primitive, &obj, &obj_clone,
267 obj_simple_copy, 1, wire_simple_primitive,
268 sizeof(wire_simple_primitive)));
269
270#define FIELD_EQUAL(name) g_assert_cmpint(obj.name, ==, obj_simple.name)
271
272 FIELD_EQUAL(b_1);
273 FIELD_EQUAL(b_2);
274 FIELD_EQUAL(u8_1);
275 FIELD_EQUAL(u16_1);
276 FIELD_EQUAL(u32_1);
277 FIELD_EQUAL(u64_1);
278 FIELD_EQUAL(i8_1);
279 FIELD_EQUAL(i8_2);
280 FIELD_EQUAL(i16_1);
281 FIELD_EQUAL(i16_2);
282 FIELD_EQUAL(i32_1);
283 FIELD_EQUAL(i32_2);
284 FIELD_EQUAL(i64_1);
285 FIELD_EQUAL(i64_2);
286}
287
288typedef struct TestSimpleArray {
289 uint16_t u16_1[3];
290} TestSimpleArray;
291
292
293
294TestSimpleArray obj_simple_arr = {
295 .u16_1 = { 0x42, 0x43, 0x44 },
296};
297
298
299
300
301static const VMStateDescription vmstate_simple_arr = {
302 .name = "simple/array",
303 .version_id = 1,
304 .minimum_version_id = 1,
305 .fields = (VMStateField[]) {
306 VMSTATE_UINT16_ARRAY(u16_1, TestSimpleArray, 3),
307 VMSTATE_END_OF_LIST()
308 }
309};
310
311uint8_t wire_simple_arr[] = {
312 0x00, 0x42,
313 0x00, 0x43,
314 0x00, 0x44,
315 QEMU_VM_EOF,
316};
317
318static void obj_simple_arr_copy(void *target, void *source)
319{
320 memcpy(target, source, sizeof(TestSimpleArray));
321}
322
323static void test_simple_array(void)
324{
325 TestSimpleArray obj, obj_clone;
326
327 memset(&obj, 0, sizeof(obj));
328 save_vmstate(&vmstate_simple_arr, &obj_simple_arr);
329
330 compare_vmstate(wire_simple_arr, sizeof(wire_simple_arr));
331
332 SUCCESS(load_vmstate(&vmstate_simple_arr, &obj, &obj_clone,
333 obj_simple_arr_copy, 1, wire_simple_arr,
334 sizeof(wire_simple_arr)));
335}
336
337typedef struct TestStruct {
338 uint32_t a, b, c, e;
339 uint64_t d, f;
340 bool skip_c_e;
341} TestStruct;
342
343static const VMStateDescription vmstate_versioned = {
344 .name = "test/versioned",
345 .version_id = 2,
346 .minimum_version_id = 1,
347 .fields = (VMStateField[]) {
348 VMSTATE_UINT32(a, TestStruct),
349 VMSTATE_UINT32_V(b, TestStruct, 2),
350
351
352 VMSTATE_UINT32(c, TestStruct),
353 VMSTATE_UINT64(d, TestStruct),
354 VMSTATE_UINT32_V(e, TestStruct, 2),
355 VMSTATE_UINT64_V(f, TestStruct, 2),
356 VMSTATE_END_OF_LIST()
357 }
358};
359
360static void test_load_v1(void)
361{
362 uint8_t buf[] = {
363 0, 0, 0, 10,
364 0, 0, 0, 30,
365 0, 0, 0, 0, 0, 0, 0, 40,
366 QEMU_VM_EOF,
367 };
368 save_buffer(buf, sizeof(buf));
369
370 QEMUFile *loading = open_test_file(false);
371 TestStruct obj = { .b = 200, .e = 500, .f = 600 };
372 vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
373 g_assert(!qemu_file_get_error(loading));
374 g_assert_cmpint(obj.a, ==, 10);
375 g_assert_cmpint(obj.b, ==, 200);
376 g_assert_cmpint(obj.c, ==, 30);
377 g_assert_cmpint(obj.d, ==, 40);
378 g_assert_cmpint(obj.e, ==, 500);
379 g_assert_cmpint(obj.f, ==, 600);
380 qemu_fclose(loading);
381}
382
383static void test_load_v2(void)
384{
385 uint8_t buf[] = {
386 0, 0, 0, 10,
387 0, 0, 0, 20,
388 0, 0, 0, 30,
389 0, 0, 0, 0, 0, 0, 0, 40,
390 0, 0, 0, 50,
391 0, 0, 0, 0, 0, 0, 0, 60,
392 QEMU_VM_EOF,
393 };
394 save_buffer(buf, sizeof(buf));
395
396 QEMUFile *loading = open_test_file(false);
397 TestStruct obj;
398 vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
399 g_assert_cmpint(obj.a, ==, 10);
400 g_assert_cmpint(obj.b, ==, 20);
401 g_assert_cmpint(obj.c, ==, 30);
402 g_assert_cmpint(obj.d, ==, 40);
403 g_assert_cmpint(obj.e, ==, 50);
404 g_assert_cmpint(obj.f, ==, 60);
405 qemu_fclose(loading);
406}
407
408static bool test_skip(void *opaque, int version_id)
409{
410 TestStruct *t = (TestStruct *)opaque;
411 return !t->skip_c_e;
412}
413
414static const VMStateDescription vmstate_skipping = {
415 .name = "test/skip",
416 .version_id = 2,
417 .minimum_version_id = 1,
418 .fields = (VMStateField[]) {
419 VMSTATE_UINT32(a, TestStruct),
420 VMSTATE_UINT32(b, TestStruct),
421 VMSTATE_UINT32_TEST(c, TestStruct, test_skip),
422 VMSTATE_UINT64(d, TestStruct),
423 VMSTATE_UINT32_TEST(e, TestStruct, test_skip),
424 VMSTATE_UINT64_V(f, TestStruct, 2),
425 VMSTATE_END_OF_LIST()
426 }
427};
428
429
430static void test_save_noskip(void)
431{
432 QEMUFile *fsave = open_test_file(true);
433 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
434 .skip_c_e = false };
435 int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
436 g_assert(!ret);
437 g_assert(!qemu_file_get_error(fsave));
438
439 uint8_t expected[] = {
440 0, 0, 0, 1,
441 0, 0, 0, 2,
442 0, 0, 0, 3,
443 0, 0, 0, 0, 0, 0, 0, 4,
444 0, 0, 0, 5,
445 0, 0, 0, 0, 0, 0, 0, 6,
446 };
447
448 qemu_fclose(fsave);
449 compare_vmstate(expected, sizeof(expected));
450}
451
452static void test_save_skip(void)
453{
454 QEMUFile *fsave = open_test_file(true);
455 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
456 .skip_c_e = true };
457 int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
458 g_assert(!ret);
459 g_assert(!qemu_file_get_error(fsave));
460
461 uint8_t expected[] = {
462 0, 0, 0, 1,
463 0, 0, 0, 2,
464 0, 0, 0, 0, 0, 0, 0, 4,
465 0, 0, 0, 0, 0, 0, 0, 6,
466 };
467
468 qemu_fclose(fsave);
469 compare_vmstate(expected, sizeof(expected));
470}
471
472static void test_load_noskip(void)
473{
474 uint8_t buf[] = {
475 0, 0, 0, 10,
476 0, 0, 0, 20,
477 0, 0, 0, 30,
478 0, 0, 0, 0, 0, 0, 0, 40,
479 0, 0, 0, 50,
480 0, 0, 0, 0, 0, 0, 0, 60,
481 QEMU_VM_EOF,
482 };
483 save_buffer(buf, sizeof(buf));
484
485 QEMUFile *loading = open_test_file(false);
486 TestStruct obj = { .skip_c_e = false };
487 vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
488 g_assert(!qemu_file_get_error(loading));
489 g_assert_cmpint(obj.a, ==, 10);
490 g_assert_cmpint(obj.b, ==, 20);
491 g_assert_cmpint(obj.c, ==, 30);
492 g_assert_cmpint(obj.d, ==, 40);
493 g_assert_cmpint(obj.e, ==, 50);
494 g_assert_cmpint(obj.f, ==, 60);
495 qemu_fclose(loading);
496}
497
498static void test_load_skip(void)
499{
500 uint8_t buf[] = {
501 0, 0, 0, 10,
502 0, 0, 0, 20,
503 0, 0, 0, 0, 0, 0, 0, 40,
504 0, 0, 0, 0, 0, 0, 0, 60,
505 QEMU_VM_EOF,
506 };
507 save_buffer(buf, sizeof(buf));
508
509 QEMUFile *loading = open_test_file(false);
510 TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
511 vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
512 g_assert(!qemu_file_get_error(loading));
513 g_assert_cmpint(obj.a, ==, 10);
514 g_assert_cmpint(obj.b, ==, 20);
515 g_assert_cmpint(obj.c, ==, 300);
516 g_assert_cmpint(obj.d, ==, 40);
517 g_assert_cmpint(obj.e, ==, 500);
518 g_assert_cmpint(obj.f, ==, 60);
519 qemu_fclose(loading);
520}
521
522typedef struct {
523 int32_t i;
524} TestStructTriv;
525
526const VMStateDescription vmsd_tst = {
527 .name = "test/tst",
528 .version_id = 1,
529 .minimum_version_id = 1,
530 .fields = (VMStateField[]) {
531 VMSTATE_INT32(i, TestStructTriv),
532 VMSTATE_END_OF_LIST()
533 }
534};
535
536
537
538#define AR_SIZE 4
539
540typedef struct {
541 TestStructTriv *ar[AR_SIZE];
542} TestArrayOfPtrToStuct;
543
544const VMStateDescription vmsd_arps = {
545 .name = "test/arps",
546 .version_id = 1,
547 .minimum_version_id = 1,
548 .fields = (VMStateField[]) {
549 VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(ar, TestArrayOfPtrToStuct,
550 AR_SIZE, 0, vmsd_tst, TestStructTriv),
551 VMSTATE_END_OF_LIST()
552 }
553};
554
555static uint8_t wire_arr_ptr_no0[] = {
556 0x00, 0x00, 0x00, 0x00,
557 0x00, 0x00, 0x00, 0x01,
558 0x00, 0x00, 0x00, 0x02,
559 0x00, 0x00, 0x00, 0x03,
560 QEMU_VM_EOF
561};
562
563static void test_arr_ptr_str_no0_save(void)
564{
565 TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
566 TestArrayOfPtrToStuct sample = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
567
568 save_vmstate(&vmsd_arps, &sample);
569 compare_vmstate(wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0));
570}
571
572static void test_arr_ptr_str_no0_load(void)
573{
574 TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
575 TestStructTriv ar[AR_SIZE] = {};
576 TestArrayOfPtrToStuct obj = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
577 int idx;
578
579 save_buffer(wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0));
580 SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1,
581 wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0)));
582 for (idx = 0; idx < AR_SIZE; ++idx) {
583
584 g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i);
585 }
586}
587
588static uint8_t wire_arr_ptr_0[] = {
589 0x00, 0x00, 0x00, 0x00,
590 VMS_NULLPTR_MARKER,
591 0x00, 0x00, 0x00, 0x02,
592 0x00, 0x00, 0x00, 0x03,
593 QEMU_VM_EOF
594};
595
596static void test_arr_ptr_str_0_save(void)
597{
598 TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
599 TestArrayOfPtrToStuct sample = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
600
601 save_vmstate(&vmsd_arps, &sample);
602 compare_vmstate(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
603}
604
605static void test_arr_ptr_str_0_load(void)
606{
607 TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 0}, {.i = 2}, {.i = 3} };
608 TestStructTriv ar[AR_SIZE] = {};
609 TestArrayOfPtrToStuct obj = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
610 int idx;
611
612 save_buffer(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
613 SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1,
614 wire_arr_ptr_0, sizeof(wire_arr_ptr_0)));
615 for (idx = 0; idx < AR_SIZE; ++idx) {
616
617 g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i);
618 }
619 for (idx = 0; idx < AR_SIZE; ++idx) {
620 if (idx == 1) {
621 g_assert_cmpint((uintptr_t)(obj.ar[idx]), ==, 0);
622 } else {
623 g_assert_cmpint((uintptr_t)(obj.ar[idx]), !=, 0);
624 }
625 }
626}
627
628typedef struct TestArrayOfPtrToInt {
629 int32_t *ar[AR_SIZE];
630} TestArrayOfPtrToInt;
631
632const VMStateDescription vmsd_arpp = {
633 .name = "test/arps",
634 .version_id = 1,
635 .minimum_version_id = 1,
636 .fields = (VMStateField[]) {
637 VMSTATE_ARRAY_OF_POINTER(ar, TestArrayOfPtrToInt,
638 AR_SIZE, 0, vmstate_info_int32, int32_t*),
639 VMSTATE_END_OF_LIST()
640 }
641};
642
643static void test_arr_ptr_prim_0_save(void)
644{
645 int32_t ar[AR_SIZE] = {0 , 1, 2, 3};
646 TestArrayOfPtrToInt sample = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
647
648 save_vmstate(&vmsd_arpp, &sample);
649 compare_vmstate(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
650}
651
652static void test_arr_ptr_prim_0_load(void)
653{
654 int32_t ar_gt[AR_SIZE] = {0, 1, 2, 3};
655 int32_t ar[AR_SIZE] = {3 , 42, 1, 0};
656 TestArrayOfPtrToInt obj = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
657 int idx;
658
659 save_buffer(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
660 SUCCESS(load_vmstate_one(&vmsd_arpp, &obj, 1,
661 wire_arr_ptr_0, sizeof(wire_arr_ptr_0)));
662 for (idx = 0; idx < AR_SIZE; ++idx) {
663
664 if (idx == 1) {
665 g_assert_cmpint(42, ==, ar[idx]);
666 } else {
667 g_assert_cmpint(ar_gt[idx], ==, ar[idx]);
668 }
669 }
670}
671
672
673typedef struct TestQtailqElement TestQtailqElement;
674
675struct TestQtailqElement {
676 bool b;
677 uint8_t u8;
678 QTAILQ_ENTRY(TestQtailqElement) next;
679};
680
681typedef struct TestQtailq {
682 int16_t i16;
683 QTAILQ_HEAD(, TestQtailqElement) q;
684 int32_t i32;
685} TestQtailq;
686
687static const VMStateDescription vmstate_q_element = {
688 .name = "test/queue-element",
689 .version_id = 1,
690 .minimum_version_id = 1,
691 .fields = (VMStateField[]) {
692 VMSTATE_BOOL(b, TestQtailqElement),
693 VMSTATE_UINT8(u8, TestQtailqElement),
694 VMSTATE_END_OF_LIST()
695 },
696};
697
698static const VMStateDescription vmstate_q = {
699 .name = "test/queue",
700 .version_id = 1,
701 .minimum_version_id = 1,
702 .fields = (VMStateField[]) {
703 VMSTATE_INT16(i16, TestQtailq),
704 VMSTATE_QTAILQ_V(q, TestQtailq, 1, vmstate_q_element, TestQtailqElement,
705 next),
706 VMSTATE_INT32(i32, TestQtailq),
707 VMSTATE_END_OF_LIST()
708 }
709};
710
711uint8_t wire_q[] = {
712 0xfe, 0x0,
713 0x01,
714 0x01,
715 0x82,
716 0x01,
717 0x00,
718 0x41,
719 0x00,
720 0x00, 0x01, 0x11, 0x70,
721 QEMU_VM_EOF,
722};
723
724static void test_save_q(void)
725{
726 TestQtailq obj_q = {
727 .i16 = -512,
728 .i32 = 70000,
729 };
730
731 TestQtailqElement obj_qe1 = {
732 .b = true,
733 .u8 = 130,
734 };
735
736 TestQtailqElement obj_qe2 = {
737 .b = false,
738 .u8 = 65,
739 };
740
741 QTAILQ_INIT(&obj_q.q);
742 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe1, next);
743 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe2, next);
744
745 save_vmstate(&vmstate_q, &obj_q);
746 compare_vmstate(wire_q, sizeof(wire_q));
747}
748
749static void test_load_q(void)
750{
751 TestQtailq obj_q = {
752 .i16 = -512,
753 .i32 = 70000,
754 };
755
756 TestQtailqElement obj_qe1 = {
757 .b = true,
758 .u8 = 130,
759 };
760
761 TestQtailqElement obj_qe2 = {
762 .b = false,
763 .u8 = 65,
764 };
765
766 QTAILQ_INIT(&obj_q.q);
767 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe1, next);
768 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe2, next);
769
770 QEMUFile *fsave = open_test_file(true);
771
772 qemu_put_buffer(fsave, wire_q, sizeof(wire_q));
773 g_assert(!qemu_file_get_error(fsave));
774 qemu_fclose(fsave);
775
776 QEMUFile *fload = open_test_file(false);
777 TestQtailq tgt;
778
779 QTAILQ_INIT(&tgt.q);
780 vmstate_load_state(fload, &vmstate_q, &tgt, 1);
781 char eof = qemu_get_byte(fload);
782 g_assert(!qemu_file_get_error(fload));
783 g_assert_cmpint(tgt.i16, ==, obj_q.i16);
784 g_assert_cmpint(tgt.i32, ==, obj_q.i32);
785 g_assert_cmpint(eof, ==, QEMU_VM_EOF);
786
787 TestQtailqElement *qele_from = QTAILQ_FIRST(&obj_q.q);
788 TestQtailqElement *qlast_from = QTAILQ_LAST(&obj_q.q);
789 TestQtailqElement *qele_to = QTAILQ_FIRST(&tgt.q);
790 TestQtailqElement *qlast_to = QTAILQ_LAST(&tgt.q);
791
792 while (1) {
793 g_assert_cmpint(qele_to->b, ==, qele_from->b);
794 g_assert_cmpint(qele_to->u8, ==, qele_from->u8);
795 if ((qele_from == qlast_from) || (qele_to == qlast_to)) {
796 break;
797 }
798 qele_from = QTAILQ_NEXT(qele_from, next);
799 qele_to = QTAILQ_NEXT(qele_to, next);
800 }
801
802 g_assert_cmpint((uintptr_t) qele_from, ==, (uintptr_t) qlast_from);
803 g_assert_cmpint((uintptr_t) qele_to, ==, (uintptr_t) qlast_to);
804
805
806 TestQtailqElement *qele;
807 while (!QTAILQ_EMPTY(&tgt.q)) {
808 qele = QTAILQ_LAST(&tgt.q);
809 QTAILQ_REMOVE(&tgt.q, qele, next);
810 free(qele);
811 qele = NULL;
812 }
813 qemu_fclose(fload);
814}
815
816
817typedef struct TestGTreeInterval {
818 uint64_t low;
819 uint64_t high;
820} TestGTreeInterval;
821
822#define VMSTATE_INTERVAL \
823{ \
824 .name = "interval", \
825 .version_id = 1, \
826 .minimum_version_id = 1, \
827 .fields = (VMStateField[]) { \
828 VMSTATE_UINT64(low, TestGTreeInterval), \
829 VMSTATE_UINT64(high, TestGTreeInterval), \
830 VMSTATE_END_OF_LIST() \
831 } \
832}
833
834
835typedef struct TestGTreeMapping {
836 uint64_t phys_addr;
837 uint32_t flags;
838} TestGTreeMapping;
839
840#define VMSTATE_MAPPING \
841{ \
842 .name = "mapping", \
843 .version_id = 1, \
844 .minimum_version_id = 1, \
845 .fields = (VMStateField[]) { \
846 VMSTATE_UINT64(phys_addr, TestGTreeMapping), \
847 VMSTATE_UINT32(flags, TestGTreeMapping), \
848 VMSTATE_END_OF_LIST() \
849 }, \
850}
851
852static const VMStateDescription vmstate_interval_mapping[2] = {
853 VMSTATE_MAPPING,
854 VMSTATE_INTERVAL
855};
856
857typedef struct TestGTreeDomain {
858 int32_t id;
859 GTree *mappings;
860} TestGTreeDomain;
861
862typedef struct TestGTreeIOMMU {
863 int32_t id;
864 GTree *domains;
865} TestGTreeIOMMU;
866
867
868static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
869{
870 TestGTreeInterval *inta = (TestGTreeInterval *)a;
871 TestGTreeInterval *intb = (TestGTreeInterval *)b;
872
873 if (inta->high < intb->low) {
874 return -1;
875 } else if (intb->high < inta->low) {
876 return 1;
877 } else {
878 return 0;
879 }
880}
881
882
883static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
884{
885 guint ua = GPOINTER_TO_UINT(a);
886 guint ub = GPOINTER_TO_UINT(b);
887 return (ua > ub) - (ua < ub);
888}
889
890static void destroy_domain(gpointer data)
891{
892 TestGTreeDomain *domain = (TestGTreeDomain *)data;
893
894 g_tree_destroy(domain->mappings);
895 g_free(domain);
896}
897
898static int domain_preload(void *opaque)
899{
900 TestGTreeDomain *domain = opaque;
901
902 domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
903 NULL, g_free, g_free);
904 return 0;
905}
906
907static int iommu_preload(void *opaque)
908{
909 TestGTreeIOMMU *iommu = opaque;
910
911 iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp,
912 NULL, NULL, destroy_domain);
913 return 0;
914}
915
916static const VMStateDescription vmstate_domain = {
917 .name = "domain",
918 .version_id = 1,
919 .minimum_version_id = 1,
920 .pre_load = domain_preload,
921 .fields = (VMStateField[]) {
922 VMSTATE_INT32(id, TestGTreeDomain),
923 VMSTATE_GTREE_V(mappings, TestGTreeDomain, 1,
924 vmstate_interval_mapping,
925 TestGTreeInterval, TestGTreeMapping),
926 VMSTATE_END_OF_LIST()
927 }
928};
929
930
931
932typedef struct TestQListElement {
933 uint32_t id;
934 QLIST_ENTRY(TestQListElement) next;
935} TestQListElement;
936
937typedef struct TestQListContainer {
938 uint32_t id;
939 QLIST_HEAD(, TestQListElement) list;
940} TestQListContainer;
941
942static const VMStateDescription vmstate_qlist_element = {
943 .name = "test/queue list",
944 .version_id = 1,
945 .minimum_version_id = 1,
946 .fields = (VMStateField[]) {
947 VMSTATE_UINT32(id, TestQListElement),
948 VMSTATE_END_OF_LIST()
949 }
950};
951
952static const VMStateDescription vmstate_iommu = {
953 .name = "iommu",
954 .version_id = 1,
955 .minimum_version_id = 1,
956 .pre_load = iommu_preload,
957 .fields = (VMStateField[]) {
958 VMSTATE_INT32(id, TestGTreeIOMMU),
959 VMSTATE_GTREE_DIRECT_KEY_V(domains, TestGTreeIOMMU, 1,
960 &vmstate_domain, TestGTreeDomain),
961 VMSTATE_END_OF_LIST()
962 }
963};
964
965static const VMStateDescription vmstate_container = {
966 .name = "test/container/qlist",
967 .version_id = 1,
968 .minimum_version_id = 1,
969 .fields = (VMStateField[]) {
970 VMSTATE_UINT32(id, TestQListContainer),
971 VMSTATE_QLIST_V(list, TestQListContainer, 1, vmstate_qlist_element,
972 TestQListElement, next),
973 VMSTATE_END_OF_LIST()
974 }
975};
976
977uint8_t first_domain_dump[] = {
978
979 0x00, 0x0, 0x0, 0x6,
980 0x00, 0x0, 0x0, 0x2,
981 0x1,
982
983 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
984 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
985
986 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00,
987 0x00, 0x00, 0x00, 0x01,
988 0x1,
989
990 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
991 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF,
992
993 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
994 0x00, 0x00, 0x00, 0x02,
995 0x0,
996 QEMU_VM_EOF,
997};
998
999static TestGTreeDomain *create_first_domain(void)
1000{
1001 TestGTreeDomain *domain;
1002 TestGTreeMapping *map_a, *map_b;
1003 TestGTreeInterval *a, *b;
1004
1005 domain = g_malloc0(sizeof(TestGTreeDomain));
1006 domain->id = 6;
1007
1008 a = g_malloc0(sizeof(TestGTreeInterval));
1009 a->low = 0x1000;
1010 a->high = 0x1FFF;
1011
1012 b = g_malloc0(sizeof(TestGTreeInterval));
1013 b->low = 0x4000;
1014 b->high = 0x4FFF;
1015
1016 map_a = g_malloc0(sizeof(TestGTreeMapping));
1017 map_a->phys_addr = 0xa000;
1018 map_a->flags = 1;
1019
1020 map_b = g_malloc0(sizeof(TestGTreeMapping));
1021 map_b->phys_addr = 0xe0000;
1022 map_b->flags = 2;
1023
1024 domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, NULL,
1025 (GDestroyNotify)g_free,
1026 (GDestroyNotify)g_free);
1027 g_tree_insert(domain->mappings, a, map_a);
1028 g_tree_insert(domain->mappings, b, map_b);
1029 return domain;
1030}
1031
1032static void test_gtree_save_domain(void)
1033{
1034 TestGTreeDomain *first_domain = create_first_domain();
1035
1036 save_vmstate(&vmstate_domain, first_domain);
1037 compare_vmstate(first_domain_dump, sizeof(first_domain_dump));
1038 destroy_domain(first_domain);
1039}
1040
1041struct match_node_data {
1042 GTree *tree;
1043 gpointer key;
1044 gpointer value;
1045};
1046
1047struct tree_cmp_data {
1048 GTree *tree1;
1049 GTree *tree2;
1050 GTraverseFunc match_node;
1051};
1052
1053static gboolean match_interval_mapping_node(gpointer key,
1054 gpointer value, gpointer data)
1055{
1056 TestGTreeMapping *map_a, *map_b;
1057 TestGTreeInterval *a, *b;
1058 struct match_node_data *d = (struct match_node_data *)data;
1059 a = (TestGTreeInterval *)key;
1060 b = (TestGTreeInterval *)d->key;
1061
1062 map_a = (TestGTreeMapping *)value;
1063 map_b = (TestGTreeMapping *)d->value;
1064
1065 assert(a->low == b->low);
1066 assert(a->high == b->high);
1067 assert(map_a->phys_addr == map_b->phys_addr);
1068 assert(map_a->flags == map_b->flags);
1069 g_tree_remove(d->tree, key);
1070 return true;
1071}
1072
1073static gboolean diff_tree(gpointer key, gpointer value, gpointer data)
1074{
1075 struct tree_cmp_data *tp = (struct tree_cmp_data *)data;
1076 struct match_node_data d = {tp->tree2, key, value};
1077
1078 g_tree_foreach(tp->tree2, tp->match_node, &d);
1079 g_tree_remove(tp->tree1, key);
1080 return false;
1081}
1082
1083static void compare_trees(GTree *tree1, GTree *tree2,
1084 GTraverseFunc function)
1085{
1086 struct tree_cmp_data tp = {tree1, tree2, function};
1087
1088 g_tree_foreach(tree1, diff_tree, &tp);
1089 assert(g_tree_nnodes(tree1) == 0);
1090 assert(g_tree_nnodes(tree2) == 0);
1091}
1092
1093static void diff_domain(TestGTreeDomain *d1, TestGTreeDomain *d2)
1094{
1095 assert(d1->id == d2->id);
1096 compare_trees(d1->mappings, d2->mappings, match_interval_mapping_node);
1097}
1098
1099static gboolean match_domain_node(gpointer key, gpointer value, gpointer data)
1100{
1101 uint64_t id1, id2;
1102 TestGTreeDomain *d1, *d2;
1103 struct match_node_data *d = (struct match_node_data *)data;
1104
1105 id1 = (uint64_t)(uintptr_t)key;
1106 id2 = (uint64_t)(uintptr_t)d->key;
1107 d1 = (TestGTreeDomain *)value;
1108 d2 = (TestGTreeDomain *)d->value;
1109 assert(id1 == id2);
1110 diff_domain(d1, d2);
1111 g_tree_remove(d->tree, key);
1112 return true;
1113}
1114
1115static void diff_iommu(TestGTreeIOMMU *iommu1, TestGTreeIOMMU *iommu2)
1116{
1117 assert(iommu1->id == iommu2->id);
1118 compare_trees(iommu1->domains, iommu2->domains, match_domain_node);
1119}
1120
1121static void test_gtree_load_domain(void)
1122{
1123 TestGTreeDomain *dest_domain = g_malloc0(sizeof(TestGTreeDomain));
1124 TestGTreeDomain *orig_domain = create_first_domain();
1125 QEMUFile *fload, *fsave;
1126 char eof;
1127
1128 fsave = open_test_file(true);
1129 qemu_put_buffer(fsave, first_domain_dump, sizeof(first_domain_dump));
1130 g_assert(!qemu_file_get_error(fsave));
1131 qemu_fclose(fsave);
1132
1133 fload = open_test_file(false);
1134
1135 vmstate_load_state(fload, &vmstate_domain, dest_domain, 1);
1136 eof = qemu_get_byte(fload);
1137 g_assert(!qemu_file_get_error(fload));
1138 g_assert_cmpint(orig_domain->id, ==, dest_domain->id);
1139 g_assert_cmpint(eof, ==, QEMU_VM_EOF);
1140
1141 diff_domain(orig_domain, dest_domain);
1142 destroy_domain(orig_domain);
1143 destroy_domain(dest_domain);
1144 qemu_fclose(fload);
1145}
1146
1147uint8_t iommu_dump[] = {
1148
1149 0x00, 0x0, 0x0, 0x7,
1150 0x00, 0x0, 0x0, 0x2,
1151 0x1,
1152 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x5,
1153 0x00, 0x0, 0x0, 0x5,
1154 0x00, 0x0, 0x0, 0x1,
1155 0x1,
1156
1157 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
1158 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF,
1159
1160 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
1161 0x00, 0x0, 0x0, 0x3,
1162 0x0,
1163 0x1,
1164 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x6,
1165 0x00, 0x0, 0x0, 0x6,
1166 0x00, 0x0, 0x0, 0x2,
1167 0x1,
1168
1169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
1170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
1171
1172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00,
1173 0x00, 0x00, 0x00, 0x01,
1174 0x1,
1175
1176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
1177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF,
1178
1179 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
1180 0x00, 0x00, 0x00, 0x02,
1181 0x0,
1182 0x0,
1183 QEMU_VM_EOF,
1184};
1185
1186static TestGTreeIOMMU *create_iommu(void)
1187{
1188 TestGTreeIOMMU *iommu = g_malloc0(sizeof(TestGTreeIOMMU));
1189 TestGTreeDomain *first_domain = create_first_domain();
1190 TestGTreeDomain *second_domain;
1191 TestGTreeMapping *map_c;
1192 TestGTreeInterval *c;
1193
1194 iommu->id = 7;
1195 iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp, NULL,
1196 NULL,
1197 destroy_domain);
1198
1199 second_domain = g_malloc0(sizeof(TestGTreeDomain));
1200 second_domain->id = 5;
1201 second_domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
1202 NULL,
1203 (GDestroyNotify)g_free,
1204 (GDestroyNotify)g_free);
1205
1206 g_tree_insert(iommu->domains, GUINT_TO_POINTER(6), first_domain);
1207 g_tree_insert(iommu->domains, (gpointer)0x0000000000000005, second_domain);
1208
1209 c = g_malloc0(sizeof(TestGTreeInterval));
1210 c->low = 0x1000000;
1211 c->high = 0x1FFFFFF;
1212
1213 map_c = g_malloc0(sizeof(TestGTreeMapping));
1214 map_c->phys_addr = 0xF000000;
1215 map_c->flags = 0x3;
1216
1217 g_tree_insert(second_domain->mappings, c, map_c);
1218 return iommu;
1219}
1220
1221static void destroy_iommu(TestGTreeIOMMU *iommu)
1222{
1223 g_tree_destroy(iommu->domains);
1224 g_free(iommu);
1225}
1226
1227static void test_gtree_save_iommu(void)
1228{
1229 TestGTreeIOMMU *iommu = create_iommu();
1230
1231 save_vmstate(&vmstate_iommu, iommu);
1232 compare_vmstate(iommu_dump, sizeof(iommu_dump));
1233 destroy_iommu(iommu);
1234}
1235
1236static void test_gtree_load_iommu(void)
1237{
1238 TestGTreeIOMMU *dest_iommu = g_malloc0(sizeof(TestGTreeIOMMU));
1239 TestGTreeIOMMU *orig_iommu = create_iommu();
1240 QEMUFile *fsave, *fload;
1241 char eof;
1242
1243 fsave = open_test_file(true);
1244 qemu_put_buffer(fsave, iommu_dump, sizeof(iommu_dump));
1245 g_assert(!qemu_file_get_error(fsave));
1246 qemu_fclose(fsave);
1247
1248 fload = open_test_file(false);
1249 vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1);
1250 eof = qemu_get_byte(fload);
1251 g_assert(!qemu_file_get_error(fload));
1252 g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id);
1253 g_assert_cmpint(eof, ==, QEMU_VM_EOF);
1254
1255 diff_iommu(orig_iommu, dest_iommu);
1256 destroy_iommu(orig_iommu);
1257 destroy_iommu(dest_iommu);
1258 qemu_fclose(fload);
1259}
1260
1261static uint8_t qlist_dump[] = {
1262 0x00, 0x00, 0x00, 0x01,
1263 0x1,
1264 0x00, 0x00, 0x00, 0x0a,
1265 0x1,
1266 0x00, 0x00, 0x0b, 0x00,
1267 0x1,
1268 0x00, 0x0c, 0x00, 0x00,
1269 0x1,
1270 0x0d, 0x00, 0x00, 0x00,
1271 0x0,
1272 QEMU_VM_EOF,
1273};
1274
1275static TestQListContainer *alloc_container(void)
1276{
1277 TestQListElement *a = g_malloc(sizeof(TestQListElement));
1278 TestQListElement *b = g_malloc(sizeof(TestQListElement));
1279 TestQListElement *c = g_malloc(sizeof(TestQListElement));
1280 TestQListElement *d = g_malloc(sizeof(TestQListElement));
1281 TestQListContainer *container = g_malloc(sizeof(TestQListContainer));
1282
1283 a->id = 0x0a;
1284 b->id = 0x0b00;
1285 c->id = 0xc0000;
1286 d->id = 0xd000000;
1287 container->id = 1;
1288
1289 QLIST_INIT(&container->list);
1290 QLIST_INSERT_HEAD(&container->list, d, next);
1291 QLIST_INSERT_HEAD(&container->list, c, next);
1292 QLIST_INSERT_HEAD(&container->list, b, next);
1293 QLIST_INSERT_HEAD(&container->list, a, next);
1294 return container;
1295}
1296
1297static void free_container(TestQListContainer *container)
1298{
1299 TestQListElement *iter, *tmp;
1300
1301 QLIST_FOREACH_SAFE(iter, &container->list, next, tmp) {
1302 QLIST_REMOVE(iter, next);
1303 g_free(iter);
1304 }
1305 g_free(container);
1306}
1307
1308static void compare_containers(TestQListContainer *c1, TestQListContainer *c2)
1309{
1310 TestQListElement *first_item_c1, *first_item_c2;
1311
1312 while (!QLIST_EMPTY(&c1->list)) {
1313 first_item_c1 = QLIST_FIRST(&c1->list);
1314 first_item_c2 = QLIST_FIRST(&c2->list);
1315 assert(first_item_c2);
1316 assert(first_item_c1->id == first_item_c2->id);
1317 QLIST_REMOVE(first_item_c1, next);
1318 QLIST_REMOVE(first_item_c2, next);
1319 g_free(first_item_c1);
1320 g_free(first_item_c2);
1321 }
1322 assert(QLIST_EMPTY(&c2->list));
1323}
1324
1325
1326
1327
1328
1329
1330static void manipulate_container(TestQListContainer *c)
1331{
1332 TestQListElement *prev = NULL, *iter = QLIST_FIRST(&c->list);
1333 TestQListElement *elem;
1334
1335 elem = g_malloc(sizeof(TestQListElement));
1336 elem->id = 0x12;
1337 QLIST_INSERT_AFTER(iter, elem, next);
1338
1339 elem = g_malloc(sizeof(TestQListElement));
1340 elem->id = 0x13;
1341 QLIST_INSERT_HEAD(&c->list, elem, next);
1342
1343 while (iter) {
1344 prev = iter;
1345 iter = QLIST_NEXT(iter, next);
1346 }
1347
1348 elem = g_malloc(sizeof(TestQListElement));
1349 elem->id = 0x14;
1350 QLIST_INSERT_BEFORE(prev, elem, next);
1351
1352 elem = g_malloc(sizeof(TestQListElement));
1353 elem->id = 0x15;
1354 QLIST_INSERT_AFTER(prev, elem, next);
1355
1356 QLIST_REMOVE(prev, next);
1357 g_free(prev);
1358}
1359
1360static void test_save_qlist(void)
1361{
1362 TestQListContainer *container = alloc_container();
1363
1364 save_vmstate(&vmstate_container, container);
1365 compare_vmstate(qlist_dump, sizeof(qlist_dump));
1366 free_container(container);
1367}
1368
1369static void test_load_qlist(void)
1370{
1371 QEMUFile *fsave, *fload;
1372 TestQListContainer *orig_container = alloc_container();
1373 TestQListContainer *dest_container = g_malloc0(sizeof(TestQListContainer));
1374 char eof;
1375
1376 QLIST_INIT(&dest_container->list);
1377
1378 fsave = open_test_file(true);
1379 qemu_put_buffer(fsave, qlist_dump, sizeof(qlist_dump));
1380 g_assert(!qemu_file_get_error(fsave));
1381 qemu_fclose(fsave);
1382
1383 fload = open_test_file(false);
1384 vmstate_load_state(fload, &vmstate_container, dest_container, 1);
1385 eof = qemu_get_byte(fload);
1386 g_assert(!qemu_file_get_error(fload));
1387 g_assert_cmpint(eof, ==, QEMU_VM_EOF);
1388 manipulate_container(orig_container);
1389 manipulate_container(dest_container);
1390 compare_containers(orig_container, dest_container);
1391 free_container(orig_container);
1392 free_container(dest_container);
1393 qemu_fclose(fload);
1394}
1395
1396typedef struct TmpTestStruct {
1397 TestStruct *parent;
1398 int64_t diff;
1399} TmpTestStruct;
1400
1401static int tmp_child_pre_save(void *opaque)
1402{
1403 struct TmpTestStruct *tts = opaque;
1404
1405 tts->diff = tts->parent->b - tts->parent->a;
1406
1407 return 0;
1408}
1409
1410static int tmp_child_post_load(void *opaque, int version_id)
1411{
1412 struct TmpTestStruct *tts = opaque;
1413
1414 tts->parent->b = tts->parent->a + tts->diff;
1415
1416 return 0;
1417}
1418
1419static const VMStateDescription vmstate_tmp_back_to_parent = {
1420 .name = "test/tmp_child_parent",
1421 .fields = (VMStateField[]) {
1422 VMSTATE_UINT64(f, TestStruct),
1423 VMSTATE_END_OF_LIST()
1424 }
1425};
1426
1427static const VMStateDescription vmstate_tmp_child = {
1428 .name = "test/tmp_child",
1429 .pre_save = tmp_child_pre_save,
1430 .post_load = tmp_child_post_load,
1431 .fields = (VMStateField[]) {
1432 VMSTATE_INT64(diff, TmpTestStruct),
1433 VMSTATE_STRUCT_POINTER(parent, TmpTestStruct,
1434 vmstate_tmp_back_to_parent, TestStruct),
1435 VMSTATE_END_OF_LIST()
1436 }
1437};
1438
1439static const VMStateDescription vmstate_with_tmp = {
1440 .name = "test/with_tmp",
1441 .version_id = 1,
1442 .fields = (VMStateField[]) {
1443 VMSTATE_UINT32(a, TestStruct),
1444 VMSTATE_UINT64(d, TestStruct),
1445 VMSTATE_WITH_TMP(TestStruct, TmpTestStruct, vmstate_tmp_child),
1446 VMSTATE_END_OF_LIST()
1447 }
1448};
1449
1450static void obj_tmp_copy(void *target, void *source)
1451{
1452 memcpy(target, source, sizeof(TestStruct));
1453}
1454
1455static void test_tmp_struct(void)
1456{
1457 TestStruct obj, obj_clone;
1458
1459 uint8_t const wire_with_tmp[] = {
1460 0x00, 0x00, 0x00, 0x02,
1461 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
1462 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
1463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
1464 QEMU_VM_EOF,
1465 };
1466
1467 memset(&obj, 0, sizeof(obj));
1468 obj.a = 2;
1469 obj.b = 4;
1470 obj.d = 1;
1471 obj.f = 8;
1472 save_vmstate(&vmstate_with_tmp, &obj);
1473
1474 compare_vmstate(wire_with_tmp, sizeof(wire_with_tmp));
1475
1476 memset(&obj, 0, sizeof(obj));
1477 SUCCESS(load_vmstate(&vmstate_with_tmp, &obj, &obj_clone,
1478 obj_tmp_copy, 1, wire_with_tmp,
1479 sizeof(wire_with_tmp)));
1480 g_assert_cmpint(obj.a, ==, 2);
1481 g_assert_cmpint(obj.b, ==, 4);
1482 g_assert_cmpint(obj.d, ==, 1);
1483 g_assert_cmpint(obj.f, ==, 8);
1484}
1485
1486int main(int argc, char **argv)
1487{
1488 g_autofree char *temp_file = g_strdup_printf("%s/vmst.test.XXXXXX",
1489 g_get_tmp_dir());
1490 temp_fd = mkstemp(temp_file);
1491 g_assert(temp_fd >= 0);
1492
1493 module_call_init(MODULE_INIT_QOM);
1494
1495 g_setenv("QTEST_SILENT_ERRORS", "1", 1);
1496
1497 g_test_init(&argc, &argv, NULL);
1498 g_test_add_func("/vmstate/simple/primitive", test_simple_primitive);
1499 g_test_add_func("/vmstate/simple/array", test_simple_array);
1500 g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
1501 g_test_add_func("/vmstate/versioned/load/v2", test_load_v2);
1502 g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip);
1503 g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip);
1504 g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip);
1505 g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip);
1506 g_test_add_func("/vmstate/array/ptr/str/no0/save",
1507 test_arr_ptr_str_no0_save);
1508 g_test_add_func("/vmstate/array/ptr/str/no0/load",
1509 test_arr_ptr_str_no0_load);
1510 g_test_add_func("/vmstate/array/ptr/str/0/save", test_arr_ptr_str_0_save);
1511 g_test_add_func("/vmstate/array/ptr/str/0/load",
1512 test_arr_ptr_str_0_load);
1513 g_test_add_func("/vmstate/array/ptr/prim/0/save",
1514 test_arr_ptr_prim_0_save);
1515 g_test_add_func("/vmstate/array/ptr/prim/0/load",
1516 test_arr_ptr_prim_0_load);
1517 g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q);
1518 g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q);
1519 g_test_add_func("/vmstate/gtree/save/savedomain", test_gtree_save_domain);
1520 g_test_add_func("/vmstate/gtree/load/loaddomain", test_gtree_load_domain);
1521 g_test_add_func("/vmstate/gtree/save/saveiommu", test_gtree_save_iommu);
1522 g_test_add_func("/vmstate/gtree/load/loadiommu", test_gtree_load_iommu);
1523 g_test_add_func("/vmstate/qlist/save/saveqlist", test_save_qlist);
1524 g_test_add_func("/vmstate/qlist/load/loadqlist", test_load_qlist);
1525 g_test_add_func("/vmstate/tmp_struct", test_tmp_struct);
1526 g_test_run();
1527
1528 close(temp_fd);
1529 unlink(temp_file);
1530
1531 return 0;
1532}
1533