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