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 vmstate_save_state(f, desc, obj, NULL);
74 qemu_put_byte(f, QEMU_VM_EOF);
75 g_assert(!qemu_file_get_error(f));
76 qemu_fclose(f);
77}
78
79static void save_buffer(const uint8_t *buf, size_t buf_size)
80{
81 QEMUFile *fsave = open_test_file(true);
82 qemu_put_buffer(fsave, buf, buf_size);
83 qemu_fclose(fsave);
84}
85
86static void compare_vmstate(const uint8_t *wire, size_t size)
87{
88 QEMUFile *f = open_test_file(false);
89 uint8_t result[size];
90
91
92
93 g_assert_cmpint(qemu_get_buffer(f, result, sizeof(result)), ==,
94 sizeof(result));
95 g_assert(!qemu_file_get_error(f));
96
97
98
99 SUCCESS(memcmp(result, wire, sizeof(result)));
100
101
102 qemu_get_byte(f);
103 g_assert_cmpint(qemu_file_get_error(f), ==, -EIO);
104
105 qemu_fclose(f);
106}
107
108static int load_vmstate_one(const VMStateDescription *desc, void *obj,
109 int version, const uint8_t *wire, size_t size)
110{
111 QEMUFile *f;
112 int ret;
113
114 f = open_test_file(true);
115 qemu_put_buffer(f, wire, size);
116 qemu_fclose(f);
117
118 f = open_test_file(false);
119 ret = vmstate_load_state(f, desc, obj, version);
120 if (ret) {
121 g_assert(qemu_file_get_error(f));
122 } else{
123 g_assert(!qemu_file_get_error(f));
124 }
125 qemu_fclose(f);
126 return ret;
127}
128
129
130static int load_vmstate(const VMStateDescription *desc,
131 void *obj, void *obj_clone,
132 void (*obj_copy)(void *, void*),
133 int version, const uint8_t *wire, size_t size)
134{
135
136 obj_copy(obj_clone, obj);
137 FAILURE(load_vmstate_one(desc, obj, version, wire, 0));
138
139
140
141
142 if (size > 3) {
143
144
145 obj_copy(obj, obj_clone);
146 FAILURE(load_vmstate_one(desc, obj, version, wire, size - 2));
147
148
149 obj_copy(obj, obj_clone);
150 FAILURE(load_vmstate_one(desc, obj, version, wire, size/2));
151
152
153 obj_copy(obj, obj_clone);
154 FAILURE(load_vmstate_one(desc, obj, version, wire + (size/2), size/2));
155
156 }
157 obj_copy(obj, obj_clone);
158 return load_vmstate_one(desc, obj, version, wire, size);
159}
160
161
162
163typedef struct TestSimple {
164 bool b_1, b_2;
165 uint8_t u8_1;
166 uint16_t u16_1;
167 uint32_t u32_1;
168 uint64_t u64_1;
169 int8_t i8_1, i8_2;
170 int16_t i16_1, i16_2;
171 int32_t i32_1, i32_2;
172 int64_t i64_1, i64_2;
173} TestSimple;
174
175
176
177TestSimple obj_simple = {
178 .b_1 = true,
179 .b_2 = false,
180 .u8_1 = 130,
181 .u16_1 = 512,
182 .u32_1 = 70000,
183 .u64_1 = 12121212,
184 .i8_1 = 65,
185 .i8_2 = -65,
186 .i16_1 = 512,
187 .i16_2 = -512,
188 .i32_1 = 70000,
189 .i32_2 = -70000,
190 .i64_1 = 12121212,
191 .i64_2 = -12121212,
192};
193
194
195
196
197static const VMStateDescription vmstate_simple_primitive = {
198 .name = "simple/primitive",
199 .version_id = 1,
200 .minimum_version_id = 1,
201 .fields = (VMStateField[]) {
202 VMSTATE_BOOL(b_1, TestSimple),
203 VMSTATE_BOOL(b_2, TestSimple),
204 VMSTATE_UINT8(u8_1, TestSimple),
205 VMSTATE_UINT16(u16_1, TestSimple),
206 VMSTATE_UINT32(u32_1, TestSimple),
207 VMSTATE_UINT64(u64_1, TestSimple),
208 VMSTATE_INT8(i8_1, TestSimple),
209 VMSTATE_INT8(i8_2, TestSimple),
210 VMSTATE_INT16(i16_1, TestSimple),
211 VMSTATE_INT16(i16_2, TestSimple),
212 VMSTATE_INT32(i32_1, TestSimple),
213 VMSTATE_INT32(i32_2, TestSimple),
214 VMSTATE_INT64(i64_1, TestSimple),
215 VMSTATE_INT64(i64_2, TestSimple),
216 VMSTATE_END_OF_LIST()
217 }
218};
219
220
221
222
223
224
225
226
227
228
229
230
231
232uint8_t wire_simple_primitive[] = {
233 0x01,
234 0x00,
235 0x82,
236 0x02, 0x00,
237 0x00, 0x01, 0x11, 0x70,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c,
239 0x41,
240 0xbf,
241 0x02, 0x00,
242 0xfe, 0x0,
243 0x00, 0x01, 0x11, 0x70,
244 0xff, 0xfe, 0xee, 0x90,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c,
246 0xff, 0xff, 0xff, 0xff, 0xff, 0x47, 0x0b, 0x84,
247 QEMU_VM_EOF,
248};
249
250static void obj_simple_copy(void *target, void *source)
251{
252 memcpy(target, source, sizeof(TestSimple));
253}
254
255static void test_simple_primitive(void)
256{
257 TestSimple obj, obj_clone;
258
259 memset(&obj, 0, sizeof(obj));
260 save_vmstate(&vmstate_simple_primitive, &obj_simple);
261
262 compare_vmstate(wire_simple_primitive, sizeof(wire_simple_primitive));
263
264 SUCCESS(load_vmstate(&vmstate_simple_primitive, &obj, &obj_clone,
265 obj_simple_copy, 1, wire_simple_primitive,
266 sizeof(wire_simple_primitive)));
267
268#define FIELD_EQUAL(name) g_assert_cmpint(obj.name, ==, obj_simple.name)
269
270 FIELD_EQUAL(b_1);
271 FIELD_EQUAL(b_2);
272 FIELD_EQUAL(u8_1);
273 FIELD_EQUAL(u16_1);
274 FIELD_EQUAL(u32_1);
275 FIELD_EQUAL(u64_1);
276 FIELD_EQUAL(i8_1);
277 FIELD_EQUAL(i8_2);
278 FIELD_EQUAL(i16_1);
279 FIELD_EQUAL(i16_2);
280 FIELD_EQUAL(i32_1);
281 FIELD_EQUAL(i32_2);
282 FIELD_EQUAL(i64_1);
283 FIELD_EQUAL(i64_2);
284}
285
286typedef struct TestStruct {
287 uint32_t a, b, c, e;
288 uint64_t d, f;
289 bool skip_c_e;
290} TestStruct;
291
292static const VMStateDescription vmstate_versioned = {
293 .name = "test/versioned",
294 .version_id = 2,
295 .minimum_version_id = 1,
296 .fields = (VMStateField[]) {
297 VMSTATE_UINT32(a, TestStruct),
298 VMSTATE_UINT32_V(b, TestStruct, 2),
299
300
301 VMSTATE_UINT32(c, TestStruct),
302 VMSTATE_UINT64(d, TestStruct),
303 VMSTATE_UINT32_V(e, TestStruct, 2),
304 VMSTATE_UINT64_V(f, TestStruct, 2),
305 VMSTATE_END_OF_LIST()
306 }
307};
308
309static void test_load_v1(void)
310{
311 uint8_t buf[] = {
312 0, 0, 0, 10,
313 0, 0, 0, 30,
314 0, 0, 0, 0, 0, 0, 0, 40,
315 QEMU_VM_EOF,
316 };
317 save_buffer(buf, sizeof(buf));
318
319 QEMUFile *loading = open_test_file(false);
320 TestStruct obj = { .b = 200, .e = 500, .f = 600 };
321 vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
322 g_assert(!qemu_file_get_error(loading));
323 g_assert_cmpint(obj.a, ==, 10);
324 g_assert_cmpint(obj.b, ==, 200);
325 g_assert_cmpint(obj.c, ==, 30);
326 g_assert_cmpint(obj.d, ==, 40);
327 g_assert_cmpint(obj.e, ==, 500);
328 g_assert_cmpint(obj.f, ==, 600);
329 qemu_fclose(loading);
330}
331
332static void test_load_v2(void)
333{
334 uint8_t buf[] = {
335 0, 0, 0, 10,
336 0, 0, 0, 20,
337 0, 0, 0, 30,
338 0, 0, 0, 0, 0, 0, 0, 40,
339 0, 0, 0, 50,
340 0, 0, 0, 0, 0, 0, 0, 60,
341 QEMU_VM_EOF,
342 };
343 save_buffer(buf, sizeof(buf));
344
345 QEMUFile *loading = open_test_file(false);
346 TestStruct obj;
347 vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
348 g_assert_cmpint(obj.a, ==, 10);
349 g_assert_cmpint(obj.b, ==, 20);
350 g_assert_cmpint(obj.c, ==, 30);
351 g_assert_cmpint(obj.d, ==, 40);
352 g_assert_cmpint(obj.e, ==, 50);
353 g_assert_cmpint(obj.f, ==, 60);
354 qemu_fclose(loading);
355}
356
357static bool test_skip(void *opaque, int version_id)
358{
359 TestStruct *t = (TestStruct *)opaque;
360 return !t->skip_c_e;
361}
362
363static const VMStateDescription vmstate_skipping = {
364 .name = "test/skip",
365 .version_id = 2,
366 .minimum_version_id = 1,
367 .fields = (VMStateField[]) {
368 VMSTATE_UINT32(a, TestStruct),
369 VMSTATE_UINT32(b, TestStruct),
370 VMSTATE_UINT32_TEST(c, TestStruct, test_skip),
371 VMSTATE_UINT64(d, TestStruct),
372 VMSTATE_UINT32_TEST(e, TestStruct, test_skip),
373 VMSTATE_UINT64_V(f, TestStruct, 2),
374 VMSTATE_END_OF_LIST()
375 }
376};
377
378
379static void test_save_noskip(void)
380{
381 QEMUFile *fsave = open_test_file(true);
382 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
383 .skip_c_e = false };
384 vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
385 g_assert(!qemu_file_get_error(fsave));
386
387 uint8_t expected[] = {
388 0, 0, 0, 1,
389 0, 0, 0, 2,
390 0, 0, 0, 3,
391 0, 0, 0, 0, 0, 0, 0, 4,
392 0, 0, 0, 5,
393 0, 0, 0, 0, 0, 0, 0, 6,
394 };
395
396 qemu_fclose(fsave);
397 compare_vmstate(expected, sizeof(expected));
398}
399
400static void test_save_skip(void)
401{
402 QEMUFile *fsave = open_test_file(true);
403 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
404 .skip_c_e = true };
405 vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
406 g_assert(!qemu_file_get_error(fsave));
407
408 uint8_t expected[] = {
409 0, 0, 0, 1,
410 0, 0, 0, 2,
411 0, 0, 0, 0, 0, 0, 0, 4,
412 0, 0, 0, 0, 0, 0, 0, 6,
413 };
414
415 qemu_fclose(fsave);
416 compare_vmstate(expected, sizeof(expected));
417}
418
419static void test_load_noskip(void)
420{
421 uint8_t buf[] = {
422 0, 0, 0, 10,
423 0, 0, 0, 20,
424 0, 0, 0, 30,
425 0, 0, 0, 0, 0, 0, 0, 40,
426 0, 0, 0, 50,
427 0, 0, 0, 0, 0, 0, 0, 60,
428 QEMU_VM_EOF,
429 };
430 save_buffer(buf, sizeof(buf));
431
432 QEMUFile *loading = open_test_file(false);
433 TestStruct obj = { .skip_c_e = false };
434 vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
435 g_assert(!qemu_file_get_error(loading));
436 g_assert_cmpint(obj.a, ==, 10);
437 g_assert_cmpint(obj.b, ==, 20);
438 g_assert_cmpint(obj.c, ==, 30);
439 g_assert_cmpint(obj.d, ==, 40);
440 g_assert_cmpint(obj.e, ==, 50);
441 g_assert_cmpint(obj.f, ==, 60);
442 qemu_fclose(loading);
443}
444
445static void test_load_skip(void)
446{
447 uint8_t buf[] = {
448 0, 0, 0, 10,
449 0, 0, 0, 20,
450 0, 0, 0, 0, 0, 0, 0, 40,
451 0, 0, 0, 0, 0, 0, 0, 60,
452 QEMU_VM_EOF,
453 };
454 save_buffer(buf, sizeof(buf));
455
456 QEMUFile *loading = open_test_file(false);
457 TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
458 vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
459 g_assert(!qemu_file_get_error(loading));
460 g_assert_cmpint(obj.a, ==, 10);
461 g_assert_cmpint(obj.b, ==, 20);
462 g_assert_cmpint(obj.c, ==, 300);
463 g_assert_cmpint(obj.d, ==, 40);
464 g_assert_cmpint(obj.e, ==, 500);
465 g_assert_cmpint(obj.f, ==, 60);
466 qemu_fclose(loading);
467}
468
469typedef struct {
470 int32_t i;
471} TestStructTriv;
472
473const VMStateDescription vmsd_tst = {
474 .name = "test/tst",
475 .version_id = 1,
476 .minimum_version_id = 1,
477 .fields = (VMStateField[]) {
478 VMSTATE_INT32(i, TestStructTriv),
479 VMSTATE_END_OF_LIST()
480 }
481};
482
483
484
485#define AR_SIZE 4
486
487typedef struct {
488 TestStructTriv *ar[AR_SIZE];
489} TestArrayOfPtrToStuct;
490
491const VMStateDescription vmsd_arps = {
492 .name = "test/arps",
493 .version_id = 1,
494 .minimum_version_id = 1,
495 .fields = (VMStateField[]) {
496 VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(ar, TestArrayOfPtrToStuct,
497 AR_SIZE, 0, vmsd_tst, TestStructTriv),
498 VMSTATE_END_OF_LIST()
499 }
500};
501
502static uint8_t wire_arr_ptr_no0[] = {
503 0x00, 0x00, 0x00, 0x00,
504 0x00, 0x00, 0x00, 0x01,
505 0x00, 0x00, 0x00, 0x02,
506 0x00, 0x00, 0x00, 0x03,
507 QEMU_VM_EOF
508};
509
510static void test_arr_ptr_str_no0_save(void)
511{
512 TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
513 TestArrayOfPtrToStuct sample = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
514
515 save_vmstate(&vmsd_arps, &sample);
516 compare_vmstate(wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0));
517}
518
519static void test_arr_ptr_str_no0_load(void)
520{
521 TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
522 TestStructTriv ar[AR_SIZE] = {};
523 TestArrayOfPtrToStuct obj = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
524 int idx;
525
526 save_buffer(wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0));
527 SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1,
528 wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0)));
529 for (idx = 0; idx < AR_SIZE; ++idx) {
530
531 g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i);
532 }
533}
534
535static uint8_t wire_arr_ptr_0[] = {
536 0x00, 0x00, 0x00, 0x00,
537 VMS_NULLPTR_MARKER,
538 0x00, 0x00, 0x00, 0x02,
539 0x00, 0x00, 0x00, 0x03,
540 QEMU_VM_EOF
541};
542
543static void test_arr_ptr_str_0_save(void)
544{
545 TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
546 TestArrayOfPtrToStuct sample = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
547
548 save_vmstate(&vmsd_arps, &sample);
549 compare_vmstate(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
550}
551
552static void test_arr_ptr_str_0_load(void)
553{
554 TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 0}, {.i = 2}, {.i = 3} };
555 TestStructTriv ar[AR_SIZE] = {};
556 TestArrayOfPtrToStuct obj = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
557 int idx;
558
559 save_buffer(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
560 SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1,
561 wire_arr_ptr_0, sizeof(wire_arr_ptr_0)));
562 for (idx = 0; idx < AR_SIZE; ++idx) {
563
564 g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i);
565 }
566 for (idx = 0; idx < AR_SIZE; ++idx) {
567 if (idx == 1) {
568 g_assert_cmpint((uintptr_t)(obj.ar[idx]), ==, 0);
569 } else {
570 g_assert_cmpint((uintptr_t)(obj.ar[idx]), !=, 0);
571 }
572 }
573}
574
575typedef struct TestArrayOfPtrToInt {
576 int32_t *ar[AR_SIZE];
577} TestArrayOfPtrToInt;
578
579const VMStateDescription vmsd_arpp = {
580 .name = "test/arps",
581 .version_id = 1,
582 .minimum_version_id = 1,
583 .fields = (VMStateField[]) {
584 VMSTATE_ARRAY_OF_POINTER(ar, TestArrayOfPtrToInt,
585 AR_SIZE, 0, vmstate_info_int32, int32_t*),
586 VMSTATE_END_OF_LIST()
587 }
588};
589
590static void test_arr_ptr_prim_0_save(void)
591{
592 int32_t ar[AR_SIZE] = {0 , 1, 2, 3};
593 TestArrayOfPtrToInt sample = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
594
595 save_vmstate(&vmsd_arpp, &sample);
596 compare_vmstate(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
597}
598
599static void test_arr_ptr_prim_0_load(void)
600{
601 int32_t ar_gt[AR_SIZE] = {0, 1, 2, 3};
602 int32_t ar[AR_SIZE] = {3 , 42, 1, 0};
603 TestArrayOfPtrToInt obj = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} };
604 int idx;
605
606 save_buffer(wire_arr_ptr_0, sizeof(wire_arr_ptr_0));
607 SUCCESS(load_vmstate_one(&vmsd_arpp, &obj, 1,
608 wire_arr_ptr_0, sizeof(wire_arr_ptr_0)));
609 for (idx = 0; idx < AR_SIZE; ++idx) {
610
611 if (idx == 1) {
612 g_assert_cmpint(42, ==, ar[idx]);
613 } else {
614 g_assert_cmpint(ar_gt[idx], ==, ar[idx]);
615 }
616 }
617}
618
619
620typedef struct TestQtailqElement TestQtailqElement;
621
622struct TestQtailqElement {
623 bool b;
624 uint8_t u8;
625 QTAILQ_ENTRY(TestQtailqElement) next;
626};
627
628typedef struct TestQtailq {
629 int16_t i16;
630 QTAILQ_HEAD(TestQtailqHead, TestQtailqElement) q;
631 int32_t i32;
632} TestQtailq;
633
634static const VMStateDescription vmstate_q_element = {
635 .name = "test/queue-element",
636 .version_id = 1,
637 .minimum_version_id = 1,
638 .fields = (VMStateField[]) {
639 VMSTATE_BOOL(b, TestQtailqElement),
640 VMSTATE_UINT8(u8, TestQtailqElement),
641 VMSTATE_END_OF_LIST()
642 },
643};
644
645static const VMStateDescription vmstate_q = {
646 .name = "test/queue",
647 .version_id = 1,
648 .minimum_version_id = 1,
649 .fields = (VMStateField[]) {
650 VMSTATE_INT16(i16, TestQtailq),
651 VMSTATE_QTAILQ_V(q, TestQtailq, 1, vmstate_q_element, TestQtailqElement,
652 next),
653 VMSTATE_INT32(i32, TestQtailq),
654 VMSTATE_END_OF_LIST()
655 }
656};
657
658uint8_t wire_q[] = {
659 0xfe, 0x0,
660 0x01,
661 0x01,
662 0x82,
663 0x01,
664 0x00,
665 0x41,
666 0x00,
667 0x00, 0x01, 0x11, 0x70,
668 QEMU_VM_EOF,
669};
670
671static void test_save_q(void)
672{
673 TestQtailq obj_q = {
674 .i16 = -512,
675 .i32 = 70000,
676 };
677
678 TestQtailqElement obj_qe1 = {
679 .b = true,
680 .u8 = 130,
681 };
682
683 TestQtailqElement obj_qe2 = {
684 .b = false,
685 .u8 = 65,
686 };
687
688 QTAILQ_INIT(&obj_q.q);
689 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe1, next);
690 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe2, next);
691
692 save_vmstate(&vmstate_q, &obj_q);
693 compare_vmstate(wire_q, sizeof(wire_q));
694}
695
696static void test_load_q(void)
697{
698 TestQtailq obj_q = {
699 .i16 = -512,
700 .i32 = 70000,
701 };
702
703 TestQtailqElement obj_qe1 = {
704 .b = true,
705 .u8 = 130,
706 };
707
708 TestQtailqElement obj_qe2 = {
709 .b = false,
710 .u8 = 65,
711 };
712
713 QTAILQ_INIT(&obj_q.q);
714 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe1, next);
715 QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe2, next);
716
717 QEMUFile *fsave = open_test_file(true);
718
719 qemu_put_buffer(fsave, wire_q, sizeof(wire_q));
720 g_assert(!qemu_file_get_error(fsave));
721 qemu_fclose(fsave);
722
723 QEMUFile *fload = open_test_file(false);
724 TestQtailq tgt;
725
726 QTAILQ_INIT(&tgt.q);
727 vmstate_load_state(fload, &vmstate_q, &tgt, 1);
728 char eof = qemu_get_byte(fload);
729 g_assert(!qemu_file_get_error(fload));
730 g_assert_cmpint(tgt.i16, ==, obj_q.i16);
731 g_assert_cmpint(tgt.i32, ==, obj_q.i32);
732 g_assert_cmpint(eof, ==, QEMU_VM_EOF);
733
734 TestQtailqElement *qele_from = QTAILQ_FIRST(&obj_q.q);
735 TestQtailqElement *qlast_from = QTAILQ_LAST(&obj_q.q, TestQtailqHead);
736 TestQtailqElement *qele_to = QTAILQ_FIRST(&tgt.q);
737 TestQtailqElement *qlast_to = QTAILQ_LAST(&tgt.q, TestQtailqHead);
738
739 while (1) {
740 g_assert_cmpint(qele_to->b, ==, qele_from->b);
741 g_assert_cmpint(qele_to->u8, ==, qele_from->u8);
742 if ((qele_from == qlast_from) || (qele_to == qlast_to)) {
743 break;
744 }
745 qele_from = QTAILQ_NEXT(qele_from, next);
746 qele_to = QTAILQ_NEXT(qele_to, next);
747 }
748
749 g_assert_cmpint((uintptr_t) qele_from, ==, (uintptr_t) qlast_from);
750 g_assert_cmpint((uintptr_t) qele_to, ==, (uintptr_t) qlast_to);
751
752
753 TestQtailqElement *qele;
754 while (!QTAILQ_EMPTY(&tgt.q)) {
755 qele = QTAILQ_LAST(&tgt.q, TestQtailqHead);
756 QTAILQ_REMOVE(&tgt.q, qele, next);
757 free(qele);
758 qele = NULL;
759 }
760 qemu_fclose(fload);
761}
762
763typedef struct TmpTestStruct {
764 TestStruct *parent;
765 int64_t diff;
766} TmpTestStruct;
767
768static void tmp_child_pre_save(void *opaque)
769{
770 struct TmpTestStruct *tts = opaque;
771
772 tts->diff = tts->parent->b - tts->parent->a;
773}
774
775static int tmp_child_post_load(void *opaque, int version_id)
776{
777 struct TmpTestStruct *tts = opaque;
778
779 tts->parent->b = tts->parent->a + tts->diff;
780
781 return 0;
782}
783
784static const VMStateDescription vmstate_tmp_back_to_parent = {
785 .name = "test/tmp_child_parent",
786 .fields = (VMStateField[]) {
787 VMSTATE_UINT64(f, TestStruct),
788 VMSTATE_END_OF_LIST()
789 }
790};
791
792static const VMStateDescription vmstate_tmp_child = {
793 .name = "test/tmp_child",
794 .pre_save = tmp_child_pre_save,
795 .post_load = tmp_child_post_load,
796 .fields = (VMStateField[]) {
797 VMSTATE_INT64(diff, TmpTestStruct),
798 VMSTATE_STRUCT_POINTER(parent, TmpTestStruct,
799 vmstate_tmp_back_to_parent, TestStruct),
800 VMSTATE_END_OF_LIST()
801 }
802};
803
804static const VMStateDescription vmstate_with_tmp = {
805 .name = "test/with_tmp",
806 .version_id = 1,
807 .fields = (VMStateField[]) {
808 VMSTATE_UINT32(a, TestStruct),
809 VMSTATE_UINT64(d, TestStruct),
810 VMSTATE_WITH_TMP(TestStruct, TmpTestStruct, vmstate_tmp_child),
811 VMSTATE_END_OF_LIST()
812 }
813};
814
815static void obj_tmp_copy(void *target, void *source)
816{
817 memcpy(target, source, sizeof(TestStruct));
818}
819
820static void test_tmp_struct(void)
821{
822 TestStruct obj, obj_clone;
823
824 uint8_t const wire_with_tmp[] = {
825 0x00, 0x00, 0x00, 0x02,
826 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
827 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
828 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
829 QEMU_VM_EOF,
830 };
831
832 memset(&obj, 0, sizeof(obj));
833 obj.a = 2;
834 obj.b = 4;
835 obj.d = 1;
836 obj.f = 8;
837 save_vmstate(&vmstate_with_tmp, &obj);
838
839 compare_vmstate(wire_with_tmp, sizeof(wire_with_tmp));
840
841 memset(&obj, 0, sizeof(obj));
842 SUCCESS(load_vmstate(&vmstate_with_tmp, &obj, &obj_clone,
843 obj_tmp_copy, 1, wire_with_tmp,
844 sizeof(wire_with_tmp)));
845 g_assert_cmpint(obj.a, ==, 2);
846 g_assert_cmpint(obj.b, ==, 4);
847 g_assert_cmpint(obj.d, ==, 1);
848 g_assert_cmpint(obj.f, ==, 8);
849}
850
851int main(int argc, char **argv)
852{
853 temp_fd = mkstemp(temp_file);
854
855 module_call_init(MODULE_INIT_QOM);
856
857 g_test_init(&argc, &argv, NULL);
858 g_test_add_func("/vmstate/simple/primitive", test_simple_primitive);
859 g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
860 g_test_add_func("/vmstate/versioned/load/v2", test_load_v2);
861 g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip);
862 g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip);
863 g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip);
864 g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip);
865 g_test_add_func("/vmstate/array/ptr/str/no0/save",
866 test_arr_ptr_str_no0_save);
867 g_test_add_func("/vmstate/array/ptr/str/no0/load",
868 test_arr_ptr_str_no0_load);
869 g_test_add_func("/vmstate/array/ptr/str/0/save", test_arr_ptr_str_0_save);
870 g_test_add_func("/vmstate/array/ptr/str/0/load",
871 test_arr_ptr_str_0_load);
872 g_test_add_func("/vmstate/array/ptr/prim/0/save",
873 test_arr_ptr_prim_0_save);
874 g_test_add_func("/vmstate/array/ptr/prim/0/load",
875 test_arr_ptr_prim_0_load);
876 g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q);
877 g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q);
878 g_test_add_func("/vmstate/tmp_struct", test_tmp_struct);
879 g_test_run();
880
881 close(temp_fd);
882 unlink(temp_file);
883
884 return 0;
885}
886