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