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