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