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 "qemu-common.h"
28#include "../migration/migration.h"
29#include "migration/vmstate.h"
30#include "migration/qemu-file-types.h"
31#include "../migration/qemu-file.h"
32#include "../migration/qemu-file-channel.h"
33#include "../migration/savevm.h"
34#include "qemu/coroutine.h"
35#include "io/channel-file.h"
36
37static char temp_file[] = "/tmp/vmst.test.XXXXXX";
38static int temp_fd;
39
40
41
42static QEMUFile *open_test_file(bool write)
43{
44 int fd = dup(temp_fd);
45 QIOChannel *ioc;
46 QEMUFile *f;
47
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_fopen_channel_output(ioc);
55 } else {
56 f = qemu_fopen_channel_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 TestStruct {
288 uint32_t a, b, c, e;
289 uint64_t d, f;
290 bool skip_c_e;
291} TestStruct;
292
293static const VMStateDescription vmstate_versioned = {
294 .name = "test/versioned",
295 .version_id = 2,
296 .minimum_version_id = 1,
297 .fields = (VMStateField[]) {
298 VMSTATE_UINT32(a, TestStruct),
299 VMSTATE_UINT32_V(b, TestStruct, 2),
300
301
302 VMSTATE_UINT32(c, TestStruct),
303 VMSTATE_UINT64(d, TestStruct),
304 VMSTATE_UINT32_V(e, TestStruct, 2),
305 VMSTATE_UINT64_V(f, TestStruct, 2),
306 VMSTATE_END_OF_LIST()
307 }
308};
309
310static void test_load_v1(void)
311{
312 uint8_t buf[] = {
313 0, 0, 0, 10,
314 0, 0, 0, 30,
315 0, 0, 0, 0, 0, 0, 0, 40,
316 QEMU_VM_EOF,
317 };
318 save_buffer(buf, sizeof(buf));
319
320 QEMUFile *loading = open_test_file(false);
321 TestStruct obj = { .b = 200, .e = 500, .f = 600 };
322 vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
323 g_assert(!qemu_file_get_error(loading));
324 g_assert_cmpint(obj.a, ==, 10);
325 g_assert_cmpint(obj.b, ==, 200);
326 g_assert_cmpint(obj.c, ==, 30);
327 g_assert_cmpint(obj.d, ==, 40);
328 g_assert_cmpint(obj.e, ==, 500);
329 g_assert_cmpint(obj.f, ==, 600);
330 qemu_fclose(loading);
331}
332
333static void test_load_v2(void)
334{
335 uint8_t buf[] = {
336 0, 0, 0, 10,
337 0, 0, 0, 20,
338 0, 0, 0, 30,
339 0, 0, 0, 0, 0, 0, 0, 40,
340 0, 0, 0, 50,
341 0, 0, 0, 0, 0, 0, 0, 60,
342 QEMU_VM_EOF,
343 };
344 save_buffer(buf, sizeof(buf));
345
346 QEMUFile *loading = open_test_file(false);
347 TestStruct obj;
348 vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
349 g_assert_cmpint(obj.a, ==, 10);
350 g_assert_cmpint(obj.b, ==, 20);
351 g_assert_cmpint(obj.c, ==, 30);
352 g_assert_cmpint(obj.d, ==, 40);
353 g_assert_cmpint(obj.e, ==, 50);
354 g_assert_cmpint(obj.f, ==, 60);
355 qemu_fclose(loading);
356}
357
358static bool test_skip(void *opaque, int version_id)
359{
360 TestStruct *t = (TestStruct *)opaque;
361 return !t->skip_c_e;
362}
363
364static const VMStateDescription vmstate_skipping = {
365 .name = "test/skip",
366 .version_id = 2,
367 .minimum_version_id = 1,
368 .fields = (VMStateField[]) {
369 VMSTATE_UINT32(a, TestStruct),
370 VMSTATE_UINT32(b, TestStruct),
371 VMSTATE_UINT32_TEST(c, TestStruct, test_skip),
372 VMSTATE_UINT64(d, TestStruct),
373 VMSTATE_UINT32_TEST(e, TestStruct, test_skip),
374 VMSTATE_UINT64_V(f, TestStruct, 2),
375 VMSTATE_END_OF_LIST()
376 }
377};
378
379
380static void test_save_noskip(void)
381{
382 QEMUFile *fsave = open_test_file(true);
383 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
384 .skip_c_e = false };
385 int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
386 g_assert(!ret);
387 g_assert(!qemu_file_get_error(fsave));
388
389 uint8_t expected[] = {
390 0, 0, 0, 1,
391 0, 0, 0, 2,
392 0, 0, 0, 3,
393 0, 0, 0, 0, 0, 0, 0, 4,
394 0, 0, 0, 5,
395 0, 0, 0, 0, 0, 0, 0, 6,
396 };
397
398 qemu_fclose(fsave);
399 compare_vmstate(expected, sizeof(expected));
400}
401
402static void test_save_skip(void)
403{
404 QEMUFile *fsave = open_test_file(true);
405 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
406 .skip_c_e = true };
407 int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
408 g_assert(!ret);
409 g_assert(!qemu_file_get_error(fsave));
410
411 uint8_t expected[] = {
412 0, 0, 0, 1,
413 0, 0, 0, 2,
414 0, 0, 0, 0, 0, 0, 0, 4,
415 0, 0, 0, 0, 0, 0, 0, 6,
416 };
417
418 qemu_fclose(fsave);
419 compare_vmstate(expected, sizeof(expected));
420}
421
422static void test_load_noskip(void)
423{
424 uint8_t buf[] = {
425 0, 0, 0, 10,
426 0, 0, 0, 20,
427 0, 0, 0, 30,
428 0, 0, 0, 0, 0, 0, 0, 40,
429 0, 0, 0, 50,
430 0, 0, 0, 0, 0, 0, 0, 60,
431 QEMU_VM_EOF,
432 };
433 save_buffer(buf, sizeof(buf));
434
435 QEMUFile *loading = open_test_file(false);
436 TestStruct obj = { .skip_c_e = false };
437 vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
438 g_assert(!qemu_file_get_error(loading));
439 g_assert_cmpint(obj.a, ==, 10);
440 g_assert_cmpint(obj.b, ==, 20);
441 g_assert_cmpint(obj.c, ==, 30);
442 g_assert_cmpint(obj.d, ==, 40);
443 g_assert_cmpint(obj.e, ==, 50);
444 g_assert_cmpint(obj.f, ==, 60);
445 qemu_fclose(loading);
446}
447
448static void test_load_skip(void)
449{
450 uint8_t buf[] = {
451 0, 0, 0, 10,
452 0, 0, 0, 20,
453 0, 0, 0, 0, 0, 0, 0, 40,
454 0, 0, 0, 0, 0, 0, 0, 60,
455 QEMU_VM_EOF,
456 };
457 save_buffer(buf, sizeof(buf));
458
459 QEMUFile *loading = open_test_file(false);
460 TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
461 vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
462 g_assert(!qemu_file_get_error(loading));
463 g_assert_cmpint(obj.a, ==, 10);
464 g_assert_cmpint(obj.b, ==, 20);
465 g_assert_cmpint(obj.c, ==, 300);
466 g_assert_cmpint(obj.d, ==, 40);
467 g_assert_cmpint(obj.e, ==, 500);
468 g_assert_cmpint(obj.f, ==, 60);
469 qemu_fclose(loading);
470}
471
472typedef struct {
473 int32_t i;
474} TestStructTriv;
475
476const VMStateDescription vmsd_tst = {
477 .name = "test/tst",
478 .version_id = 1,
479 .minimum_version_id = 1,
480 .fields = (VMStateField[]) {
481 VMSTATE_INT32(i, TestStructTriv),
482 VMSTATE_END_OF_LIST()
483 }
484};
485
486
487
488#define AR_SIZE 4
489
490typedef struct {
491 TestStructTriv *ar[AR_SIZE];
492} TestArrayOfPtrToStuct;
493
494const VMStateDescription vmsd_arps = {
495 .name = "test/arps",
496 .version_id = 1,
497 .minimum_version_id = 1,
498 .fields = (VMStateField[]) {
499 VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(ar, TestArrayOfPtrToStuct,
500 AR_SIZE, 0, vmsd_tst, TestStructTriv),
501 VMSTATE_END_OF_LIST()
502 }
503};
504
505static uint8_t wire_arr_ptr_no0[] = {
506 0x00, 0x00, 0x00, 0x00,
507 0x00, 0x00, 0x00, 0x01,
508 0x00, 0x00, 0x00, 0x02,
509 0x00, 0x00, 0x00, 0x03,
510 QEMU_VM_EOF
511};
512
513static void test_arr_ptr_str_no0_save(void)
514{
515 TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
516 TestArrayOfPtrToStuct sample = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
517
518 save_vmstate(&vmsd_arps, &sample);
519 compare_vmstate(wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0));
520}
521
522static void test_arr_ptr_str_no0_load(void)
523{
524 TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
525 TestStructTriv ar[AR_SIZE] = {};
526 TestArrayOfPtrToStuct obj = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
527 int idx;
528
529 save_buffer(wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0));
530 SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1,
531 wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0)));
532 for (idx = 0; idx < AR_SIZE; ++idx) {
533
534 g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i);
535 }
536}
537
538static uint8_t wire_arr_ptr_0[] = {
539 0x00, 0x00, 0x00, 0x00,
540 VMS_NULLPTR_MARKER,
541 0x00, 0x00, 0x00, 0x02,
542 0x00, 0x00, 0x00, 0x03,
543 QEMU_VM_EOF
544};
545
546static void test_arr_ptr_str_0_save(void)
547{
548 TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
549 TestArrayOfPtrToStuct sample = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
550
551 save_vmstate(&vmsd_arps, &sample);
552 compare_vmstate(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
553}
554
555static void test_arr_ptr_str_0_load(void)
556{
557 TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 0}, {.i = 2}, {.i = 3} };
558 TestStructTriv ar[AR_SIZE] = {};
559 TestArrayOfPtrToStuct obj = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
560 int idx;
561
562 save_buffer(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
563 SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1,
564 wire_arr_ptr_0, sizeof(wire_arr_ptr_0)));
565 for (idx = 0; idx < AR_SIZE; ++idx) {
566
567 g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i);
568 }
569 for (idx = 0; idx < AR_SIZE; ++idx) {
570 if (idx == 1) {
571 g_assert_cmpint((uintptr_t)(obj.ar[idx]), ==, 0);
572 } else {
573 g_assert_cmpint((uintptr_t)(obj.ar[idx]), !=, 0);
574 }
575 }
576}
577
578typedef struct TestArrayOfPtrToInt {
579 int32_t *ar[AR_SIZE];
580} TestArrayOfPtrToInt;
581
582const VMStateDescription vmsd_arpp = {
583 .name = "test/arps",
584 .version_id = 1,
585 .minimum_version_id = 1,
586 .fields = (VMStateField[]) {
587 VMSTATE_ARRAY_OF_POINTER(ar, TestArrayOfPtrToInt,
588 AR_SIZE, 0, vmstate_info_int32, int32_t*),
589 VMSTATE_END_OF_LIST()
590 }
591};
592
593static void test_arr_ptr_prim_0_save(void)
594{
595 int32_t ar[AR_SIZE] = {0 , 1, 2, 3};
596 TestArrayOfPtrToInt sample = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
597
598 save_vmstate(&vmsd_arpp, &sample);
599 compare_vmstate(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
600}
601
602static void test_arr_ptr_prim_0_load(void)
603{
604 int32_t ar_gt[AR_SIZE] = {0, 1, 2, 3};
605 int32_t ar[AR_SIZE] = {3 , 42, 1, 0};
606 TestArrayOfPtrToInt 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_arpp, &obj, 1,
611 wire_arr_ptr_0, sizeof(wire_arr_ptr_0)));
612 for (idx = 0; idx < AR_SIZE; ++idx) {
613
614 if (idx == 1) {
615 g_assert_cmpint(42, ==, ar[idx]);
616 } else {
617 g_assert_cmpint(ar_gt[idx], ==, ar[idx]);
618 }
619 }
620}
621
622
623typedef struct TestQtailqElement TestQtailqElement;
624
625struct TestQtailqElement {
626 bool b;
627 uint8_t u8;
628 QTAILQ_ENTRY(TestQtailqElement) next;
629};
630
631typedef struct TestQtailq {
632 int16_t i16;
633 QTAILQ_HEAD(TestQtailqHead, TestQtailqElement) q;
634 int32_t i32;
635} TestQtailq;
636
637static const VMStateDescription vmstate_q_element = {
638 .name = "test/queue-element",
639 .version_id = 1,
640 .minimum_version_id = 1,
641 .fields = (VMStateField[]) {
642 VMSTATE_BOOL(b, TestQtailqElement),
643 VMSTATE_UINT8(u8, TestQtailqElement),
644 VMSTATE_END_OF_LIST()
645 },
646};
647
648static const VMStateDescription vmstate_q = {
649 .name = "test/queue",
650 .version_id = 1,
651 .minimum_version_id = 1,
652 .fields = (VMStateField[]) {
653 VMSTATE_INT16(i16, TestQtailq),
654 VMSTATE_QTAILQ_V(q, TestQtailq, 1, vmstate_q_element, TestQtailqElement,
655 next),
656 VMSTATE_INT32(i32, TestQtailq),
657 VMSTATE_END_OF_LIST()
658 }
659};
660
661uint8_t wire_q[] = {
662 0xfe, 0x0,
663 0x01,
664 0x01,
665 0x82,
666 0x01,
667 0x00,
668 0x41,
669 0x00,
670 0x00, 0x01, 0x11, 0x70,
671 QEMU_VM_EOF,
672};
673
674static void test_save_q(void)
675{
676 TestQtailq obj_q = {
677 .i16 = -512,
678 .i32 = 70000,
679 };
680
681 TestQtailqElement obj_qe1 = {
682 .b = true,
683 .u8 = 130,
684 };
685
686 TestQtailqElement obj_qe2 = {
687 .b = false,
688 .u8 = 65,
689 };
690
691 QTAILQ_INIT(&obj_q.q);
692 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe1, next);
693 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe2, next);
694
695 save_vmstate(&vmstate_q, &obj_q);
696 compare_vmstate(wire_q, sizeof(wire_q));
697}
698
699static void test_load_q(void)
700{
701 TestQtailq obj_q = {
702 .i16 = -512,
703 .i32 = 70000,
704 };
705
706 TestQtailqElement obj_qe1 = {
707 .b = true,
708 .u8 = 130,
709 };
710
711 TestQtailqElement obj_qe2 = {
712 .b = false,
713 .u8 = 65,
714 };
715
716 QTAILQ_INIT(&obj_q.q);
717 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe1, next);
718 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe2, next);
719
720 QEMUFile *fsave = open_test_file(true);
721
722 qemu_put_buffer(fsave, wire_q, sizeof(wire_q));
723 g_assert(!qemu_file_get_error(fsave));
724 qemu_fclose(fsave);
725
726 QEMUFile *fload = open_test_file(false);
727 TestQtailq tgt;
728
729 QTAILQ_INIT(&tgt.q);
730 vmstate_load_state(fload, &vmstate_q, &tgt, 1);
731 char eof = qemu_get_byte(fload);
732 g_assert(!qemu_file_get_error(fload));
733 g_assert_cmpint(tgt.i16, ==, obj_q.i16);
734 g_assert_cmpint(tgt.i32, ==, obj_q.i32);
735 g_assert_cmpint(eof, ==, QEMU_VM_EOF);
736
737 TestQtailqElement *qele_from = QTAILQ_FIRST(&obj_q.q);
738 TestQtailqElement *qlast_from = QTAILQ_LAST(&obj_q.q, TestQtailqHead);
739 TestQtailqElement *qele_to = QTAILQ_FIRST(&tgt.q);
740 TestQtailqElement *qlast_to = QTAILQ_LAST(&tgt.q, TestQtailqHead);
741
742 while (1) {
743 g_assert_cmpint(qele_to->b, ==, qele_from->b);
744 g_assert_cmpint(qele_to->u8, ==, qele_from->u8);
745 if ((qele_from == qlast_from) || (qele_to == qlast_to)) {
746 break;
747 }
748 qele_from = QTAILQ_NEXT(qele_from, next);
749 qele_to = QTAILQ_NEXT(qele_to, next);
750 }
751
752 g_assert_cmpint((uintptr_t) qele_from, ==, (uintptr_t) qlast_from);
753 g_assert_cmpint((uintptr_t) qele_to, ==, (uintptr_t) qlast_to);
754
755
756 TestQtailqElement *qele;
757 while (!QTAILQ_EMPTY(&tgt.q)) {
758 qele = QTAILQ_LAST(&tgt.q, TestQtailqHead);
759 QTAILQ_REMOVE(&tgt.q, qele, next);
760 free(qele);
761 qele = NULL;
762 }
763 qemu_fclose(fload);
764}
765
766typedef struct TmpTestStruct {
767 TestStruct *parent;
768 int64_t diff;
769} TmpTestStruct;
770
771static int tmp_child_pre_save(void *opaque)
772{
773 struct TmpTestStruct *tts = opaque;
774
775 tts->diff = tts->parent->b - tts->parent->a;
776
777 return 0;
778}
779
780static int tmp_child_post_load(void *opaque, int version_id)
781{
782 struct TmpTestStruct *tts = opaque;
783
784 tts->parent->b = tts->parent->a + tts->diff;
785
786 return 0;
787}
788
789static const VMStateDescription vmstate_tmp_back_to_parent = {
790 .name = "test/tmp_child_parent",
791 .fields = (VMStateField[]) {
792 VMSTATE_UINT64(f, TestStruct),
793 VMSTATE_END_OF_LIST()
794 }
795};
796
797static const VMStateDescription vmstate_tmp_child = {
798 .name = "test/tmp_child",
799 .pre_save = tmp_child_pre_save,
800 .post_load = tmp_child_post_load,
801 .fields = (VMStateField[]) {
802 VMSTATE_INT64(diff, TmpTestStruct),
803 VMSTATE_STRUCT_POINTER(parent, TmpTestStruct,
804 vmstate_tmp_back_to_parent, TestStruct),
805 VMSTATE_END_OF_LIST()
806 }
807};
808
809static const VMStateDescription vmstate_with_tmp = {
810 .name = "test/with_tmp",
811 .version_id = 1,
812 .fields = (VMStateField[]) {
813 VMSTATE_UINT32(a, TestStruct),
814 VMSTATE_UINT64(d, TestStruct),
815 VMSTATE_WITH_TMP(TestStruct, TmpTestStruct, vmstate_tmp_child),
816 VMSTATE_END_OF_LIST()
817 }
818};
819
820static void obj_tmp_copy(void *target, void *source)
821{
822 memcpy(target, source, sizeof(TestStruct));
823}
824
825static void test_tmp_struct(void)
826{
827 TestStruct obj, obj_clone;
828
829 uint8_t const wire_with_tmp[] = {
830 0x00, 0x00, 0x00, 0x02,
831 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
832 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
833 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
834 QEMU_VM_EOF,
835 };
836
837 memset(&obj, 0, sizeof(obj));
838 obj.a = 2;
839 obj.b = 4;
840 obj.d = 1;
841 obj.f = 8;
842 save_vmstate(&vmstate_with_tmp, &obj);
843
844 compare_vmstate(wire_with_tmp, sizeof(wire_with_tmp));
845
846 memset(&obj, 0, sizeof(obj));
847 SUCCESS(load_vmstate(&vmstate_with_tmp, &obj, &obj_clone,
848 obj_tmp_copy, 1, wire_with_tmp,
849 sizeof(wire_with_tmp)));
850 g_assert_cmpint(obj.a, ==, 2);
851 g_assert_cmpint(obj.b, ==, 4);
852 g_assert_cmpint(obj.d, ==, 1);
853 g_assert_cmpint(obj.f, ==, 8);
854}
855
856int main(int argc, char **argv)
857{
858 temp_fd = mkstemp(temp_file);
859
860 module_call_init(MODULE_INIT_QOM);
861
862 g_test_init(&argc, &argv, NULL);
863 g_test_add_func("/vmstate/simple/primitive", test_simple_primitive);
864 g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
865 g_test_add_func("/vmstate/versioned/load/v2", test_load_v2);
866 g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip);
867 g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip);
868 g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip);
869 g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip);
870 g_test_add_func("/vmstate/array/ptr/str/no0/save",
871 test_arr_ptr_str_no0_save);
872 g_test_add_func("/vmstate/array/ptr/str/no0/load",
873 test_arr_ptr_str_no0_load);
874 g_test_add_func("/vmstate/array/ptr/str/0/save", test_arr_ptr_str_0_save);
875 g_test_add_func("/vmstate/array/ptr/str/0/load",
876 test_arr_ptr_str_0_load);
877 g_test_add_func("/vmstate/array/ptr/prim/0/save",
878 test_arr_ptr_prim_0_save);
879 g_test_add_func("/vmstate/array/ptr/prim/0/load",
880 test_arr_ptr_prim_0_load);
881 g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q);
882 g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q);
883 g_test_add_func("/vmstate/tmp_struct", test_tmp_struct);
884 g_test_run();
885
886 close(temp_fd);
887 unlink(temp_file);
888
889 return 0;
890}
891