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