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