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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 = (const 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 return false; 1077 } 1078 1079 static void compare_trees(GTree *tree1, GTree *tree2, 1080 GTraverseFunc function) 1081 { 1082 struct tree_cmp_data tp = {tree1, tree2, function}; 1083 1084 assert(g_tree_nnodes(tree1) == g_tree_nnodes(tree2)); 1085 g_tree_foreach(tree1, diff_tree, &tp); 1086 g_tree_destroy(g_tree_ref(tree1)); 1087 } 1088 1089 static void diff_domain(TestGTreeDomain *d1, TestGTreeDomain *d2) 1090 { 1091 assert(d1->id == d2->id); 1092 compare_trees(d1->mappings, d2->mappings, match_interval_mapping_node); 1093 } 1094 1095 static gboolean match_domain_node(gpointer key, gpointer value, gpointer data) 1096 { 1097 uint64_t id1, id2; 1098 TestGTreeDomain *d1, *d2; 1099 struct match_node_data *d = (struct match_node_data *)data; 1100 1101 id1 = (uint64_t)(uintptr_t)key; 1102 id2 = (uint64_t)(uintptr_t)d->key; 1103 d1 = (TestGTreeDomain *)value; 1104 d2 = (TestGTreeDomain *)d->value; 1105 assert(id1 == id2); 1106 diff_domain(d1, d2); 1107 g_tree_remove(d->tree, key); 1108 return true; 1109 } 1110 1111 static void diff_iommu(TestGTreeIOMMU *iommu1, TestGTreeIOMMU *iommu2) 1112 { 1113 assert(iommu1->id == iommu2->id); 1114 compare_trees(iommu1->domains, iommu2->domains, match_domain_node); 1115 } 1116 1117 static void test_gtree_load_domain(void) 1118 { 1119 TestGTreeDomain *dest_domain = g_new0(TestGTreeDomain, 1); 1120 TestGTreeDomain *orig_domain = create_first_domain(); 1121 QEMUFile *fload, *fsave; 1122 char eof; 1123 1124 fsave = open_test_file(true); 1125 qemu_put_buffer(fsave, first_domain_dump, sizeof(first_domain_dump)); 1126 g_assert(!qemu_file_get_error(fsave)); 1127 qemu_fclose(fsave); 1128 1129 fload = open_test_file(false); 1130 1131 vmstate_load_state(fload, &vmstate_domain, dest_domain, 1); 1132 eof = qemu_get_byte(fload); 1133 g_assert(!qemu_file_get_error(fload)); 1134 g_assert_cmpint(orig_domain->id, ==, dest_domain->id); 1135 g_assert_cmpint(eof, ==, QEMU_VM_EOF); 1136 1137 diff_domain(orig_domain, dest_domain); 1138 destroy_domain(orig_domain); 1139 destroy_domain(dest_domain); 1140 qemu_fclose(fload); 1141 } 1142 1143 uint8_t iommu_dump[] = { 1144 /* iommu id */ 1145 0x00, 0x0, 0x0, 0x7, 1146 0x00, 0x0, 0x0, 0x2, /* 2 domains */ 1147 0x1,/* start of domain 5 */ 1148 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x5, /* key = 5 */ 1149 0x00, 0x0, 0x0, 0x5, /* domain1 id */ 1150 0x00, 0x0, 0x0, 0x1, /* 1 mapping */ 1151 0x1, /* start of mappings */ 1152 /* c */ 1153 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 1154 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 1155 /* map_c */ 1156 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 1157 0x00, 0x0, 0x0, 0x3, 1158 0x0, /* end of domain1 mappings*/ 1159 0x1,/* start of domain 6 */ 1160 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x6, /* key = 6 */ 1161 0x00, 0x0, 0x0, 0x6, /* domain6 id */ 1162 0x00, 0x0, 0x0, 0x2, /* 2 mappings */ 1163 0x1, /* start of a */ 1164 /* a */ 1165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 1166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 1167 /* map_a */ 1168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 1169 0x00, 0x00, 0x00, 0x01, 1170 0x1, /* start of b */ 1171 /* b */ 1172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 1173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF, 1174 /* map_b */ 1175 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 1176 0x00, 0x00, 0x00, 0x02, 1177 0x0, /* end of domain6 mappings*/ 1178 0x0, /* end of domains */ 1179 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 1180 }; 1181 1182 static TestGTreeIOMMU *create_iommu(void) 1183 { 1184 TestGTreeIOMMU *iommu = g_new0(TestGTreeIOMMU, 1); 1185 TestGTreeDomain *first_domain = create_first_domain(); 1186 TestGTreeDomain *second_domain; 1187 TestGTreeMapping *map_c; 1188 TestGTreeInterval *c; 1189 1190 iommu->id = 7; 1191 iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp, NULL, 1192 NULL, 1193 destroy_domain); 1194 1195 second_domain = g_new0(TestGTreeDomain, 1); 1196 second_domain->id = 5; 1197 second_domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, 1198 NULL, 1199 (GDestroyNotify)g_free, 1200 (GDestroyNotify)g_free); 1201 1202 g_tree_insert(iommu->domains, GUINT_TO_POINTER(6), first_domain); 1203 g_tree_insert(iommu->domains, (gpointer)0x0000000000000005, second_domain); 1204 1205 c = g_new0(TestGTreeInterval, 1); 1206 c->low = 0x1000000; 1207 c->high = 0x1FFFFFF; 1208 1209 map_c = g_new0(TestGTreeMapping, 1); 1210 map_c->phys_addr = 0xF000000; 1211 map_c->flags = 0x3; 1212 1213 g_tree_insert(second_domain->mappings, c, map_c); 1214 return iommu; 1215 } 1216 1217 static void destroy_iommu(TestGTreeIOMMU *iommu) 1218 { 1219 g_tree_destroy(iommu->domains); 1220 g_free(iommu); 1221 } 1222 1223 static void test_gtree_save_iommu(void) 1224 { 1225 TestGTreeIOMMU *iommu = create_iommu(); 1226 1227 save_vmstate(&vmstate_iommu, iommu); 1228 compare_vmstate(iommu_dump, sizeof(iommu_dump)); 1229 destroy_iommu(iommu); 1230 } 1231 1232 static void test_gtree_load_iommu(void) 1233 { 1234 TestGTreeIOMMU *dest_iommu = g_new0(TestGTreeIOMMU, 1); 1235 TestGTreeIOMMU *orig_iommu = create_iommu(); 1236 QEMUFile *fsave, *fload; 1237 char eof; 1238 1239 fsave = open_test_file(true); 1240 qemu_put_buffer(fsave, iommu_dump, sizeof(iommu_dump)); 1241 g_assert(!qemu_file_get_error(fsave)); 1242 qemu_fclose(fsave); 1243 1244 fload = open_test_file(false); 1245 vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1); 1246 eof = qemu_get_byte(fload); 1247 g_assert(!qemu_file_get_error(fload)); 1248 g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id); 1249 g_assert_cmpint(eof, ==, QEMU_VM_EOF); 1250 1251 diff_iommu(orig_iommu, dest_iommu); 1252 destroy_iommu(orig_iommu); 1253 destroy_iommu(dest_iommu); 1254 qemu_fclose(fload); 1255 } 1256 1257 static uint8_t qlist_dump[] = { 1258 0x00, 0x00, 0x00, 0x01, /* container id */ 1259 0x1, /* start of a */ 1260 0x00, 0x00, 0x00, 0x0a, 1261 0x1, /* start of b */ 1262 0x00, 0x00, 0x0b, 0x00, 1263 0x1, /* start of c */ 1264 0x00, 0x0c, 0x00, 0x00, 1265 0x1, /* start of d */ 1266 0x0d, 0x00, 0x00, 0x00, 1267 0x0, /* end of list */ 1268 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 1269 }; 1270 1271 static TestQListContainer *alloc_container(void) 1272 { 1273 TestQListElement *a = g_new(TestQListElement, 1); 1274 TestQListElement *b = g_new(TestQListElement, 1); 1275 TestQListElement *c = g_new(TestQListElement, 1); 1276 TestQListElement *d = g_new(TestQListElement, 1); 1277 TestQListContainer *container = g_new(TestQListContainer, 1); 1278 1279 a->id = 0x0a; 1280 b->id = 0x0b00; 1281 c->id = 0xc0000; 1282 d->id = 0xd000000; 1283 container->id = 1; 1284 1285 QLIST_INIT(&container->list); 1286 QLIST_INSERT_HEAD(&container->list, d, next); 1287 QLIST_INSERT_HEAD(&container->list, c, next); 1288 QLIST_INSERT_HEAD(&container->list, b, next); 1289 QLIST_INSERT_HEAD(&container->list, a, next); 1290 return container; 1291 } 1292 1293 static void free_container(TestQListContainer *container) 1294 { 1295 TestQListElement *iter, *tmp; 1296 1297 QLIST_FOREACH_SAFE(iter, &container->list, next, tmp) { 1298 QLIST_REMOVE(iter, next); 1299 g_free(iter); 1300 } 1301 g_free(container); 1302 } 1303 1304 static void compare_containers(TestQListContainer *c1, TestQListContainer *c2) 1305 { 1306 TestQListElement *first_item_c1, *first_item_c2; 1307 1308 while (!QLIST_EMPTY(&c1->list)) { 1309 first_item_c1 = QLIST_FIRST(&c1->list); 1310 first_item_c2 = QLIST_FIRST(&c2->list); 1311 assert(first_item_c2); 1312 assert(first_item_c1->id == first_item_c2->id); 1313 QLIST_REMOVE(first_item_c1, next); 1314 QLIST_REMOVE(first_item_c2, next); 1315 g_free(first_item_c1); 1316 g_free(first_item_c2); 1317 } 1318 assert(QLIST_EMPTY(&c2->list)); 1319 } 1320 1321 /* 1322 * Check the prev & next fields are correct by doing list 1323 * manipulations on the container. We will do that for both 1324 * the source and the destination containers 1325 */ 1326 static void manipulate_container(TestQListContainer *c) 1327 { 1328 TestQListElement *prev = NULL, *iter = QLIST_FIRST(&c->list); 1329 TestQListElement *elem; 1330 1331 elem = g_new(TestQListElement, 1); 1332 elem->id = 0x12; 1333 QLIST_INSERT_AFTER(iter, elem, next); 1334 1335 elem = g_new(TestQListElement, 1); 1336 elem->id = 0x13; 1337 QLIST_INSERT_HEAD(&c->list, elem, next); 1338 1339 while (iter) { 1340 prev = iter; 1341 iter = QLIST_NEXT(iter, next); 1342 } 1343 1344 elem = g_new(TestQListElement, 1); 1345 elem->id = 0x14; 1346 QLIST_INSERT_BEFORE(prev, elem, next); 1347 1348 elem = g_new(TestQListElement, 1); 1349 elem->id = 0x15; 1350 QLIST_INSERT_AFTER(prev, elem, next); 1351 1352 QLIST_REMOVE(prev, next); 1353 g_free(prev); 1354 } 1355 1356 static void test_save_qlist(void) 1357 { 1358 TestQListContainer *container = alloc_container(); 1359 1360 save_vmstate(&vmstate_container, container); 1361 compare_vmstate(qlist_dump, sizeof(qlist_dump)); 1362 free_container(container); 1363 } 1364 1365 static void test_load_qlist(void) 1366 { 1367 QEMUFile *fsave, *fload; 1368 TestQListContainer *orig_container = alloc_container(); 1369 TestQListContainer *dest_container = g_new0(TestQListContainer, 1); 1370 char eof; 1371 1372 QLIST_INIT(&dest_container->list); 1373 1374 fsave = open_test_file(true); 1375 qemu_put_buffer(fsave, qlist_dump, sizeof(qlist_dump)); 1376 g_assert(!qemu_file_get_error(fsave)); 1377 qemu_fclose(fsave); 1378 1379 fload = open_test_file(false); 1380 vmstate_load_state(fload, &vmstate_container, dest_container, 1); 1381 eof = qemu_get_byte(fload); 1382 g_assert(!qemu_file_get_error(fload)); 1383 g_assert_cmpint(eof, ==, QEMU_VM_EOF); 1384 manipulate_container(orig_container); 1385 manipulate_container(dest_container); 1386 compare_containers(orig_container, dest_container); 1387 free_container(orig_container); 1388 free_container(dest_container); 1389 qemu_fclose(fload); 1390 } 1391 1392 typedef struct TmpTestStruct { 1393 TestStruct *parent; 1394 int64_t diff; 1395 } TmpTestStruct; 1396 1397 static int tmp_child_pre_save(void *opaque) 1398 { 1399 struct TmpTestStruct *tts = opaque; 1400 1401 tts->diff = tts->parent->b - tts->parent->a; 1402 1403 return 0; 1404 } 1405 1406 static int tmp_child_post_load(void *opaque, int version_id) 1407 { 1408 struct TmpTestStruct *tts = opaque; 1409 1410 tts->parent->b = tts->parent->a + tts->diff; 1411 1412 return 0; 1413 } 1414 1415 static const VMStateDescription vmstate_tmp_back_to_parent = { 1416 .name = "test/tmp_child_parent", 1417 .fields = (const VMStateField[]) { 1418 VMSTATE_UINT64(f, TestStruct), 1419 VMSTATE_END_OF_LIST() 1420 } 1421 }; 1422 1423 static const VMStateDescription vmstate_tmp_child = { 1424 .name = "test/tmp_child", 1425 .pre_save = tmp_child_pre_save, 1426 .post_load = tmp_child_post_load, 1427 .fields = (const VMStateField[]) { 1428 VMSTATE_INT64(diff, TmpTestStruct), 1429 VMSTATE_STRUCT_POINTER(parent, TmpTestStruct, 1430 vmstate_tmp_back_to_parent, TestStruct), 1431 VMSTATE_END_OF_LIST() 1432 } 1433 }; 1434 1435 static const VMStateDescription vmstate_with_tmp = { 1436 .name = "test/with_tmp", 1437 .version_id = 1, 1438 .fields = (const VMStateField[]) { 1439 VMSTATE_UINT32(a, TestStruct), 1440 VMSTATE_UINT64(d, TestStruct), 1441 VMSTATE_WITH_TMP(TestStruct, TmpTestStruct, vmstate_tmp_child), 1442 VMSTATE_END_OF_LIST() 1443 } 1444 }; 1445 1446 static void obj_tmp_copy(void *target, void *source) 1447 { 1448 memcpy(target, source, sizeof(TestStruct)); 1449 } 1450 1451 static void test_tmp_struct(void) 1452 { 1453 TestStruct obj, obj_clone; 1454 1455 uint8_t const wire_with_tmp[] = { 1456 /* u32 a */ 0x00, 0x00, 0x00, 0x02, 1457 /* u64 d */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 1458 /* diff */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 1459 /* u64 f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 1460 QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ 1461 }; 1462 1463 memset(&obj, 0, sizeof(obj)); 1464 obj.a = 2; 1465 obj.b = 4; 1466 obj.d = 1; 1467 obj.f = 8; 1468 save_vmstate(&vmstate_with_tmp, &obj); 1469 1470 compare_vmstate(wire_with_tmp, sizeof(wire_with_tmp)); 1471 1472 memset(&obj, 0, sizeof(obj)); 1473 SUCCESS(load_vmstate(&vmstate_with_tmp, &obj, &obj_clone, 1474 obj_tmp_copy, 1, wire_with_tmp, 1475 sizeof(wire_with_tmp))); 1476 g_assert_cmpint(obj.a, ==, 2); /* From top level vmsd */ 1477 g_assert_cmpint(obj.b, ==, 4); /* from the post_load */ 1478 g_assert_cmpint(obj.d, ==, 1); /* From top level vmsd */ 1479 g_assert_cmpint(obj.f, ==, 8); /* From the child->parent */ 1480 } 1481 1482 int main(int argc, char **argv) 1483 { 1484 g_autofree char *temp_file = g_strdup_printf("%s/vmst.test.XXXXXX", 1485 g_get_tmp_dir()); 1486 temp_fd = mkstemp(temp_file); 1487 g_assert(temp_fd >= 0); 1488 1489 module_call_init(MODULE_INIT_QOM); 1490 1491 g_setenv("QTEST_SILENT_ERRORS", "1", 1); 1492 1493 g_test_init(&argc, &argv, NULL); 1494 g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); 1495 g_test_add_func("/vmstate/simple/array", test_simple_array); 1496 g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); 1497 g_test_add_func("/vmstate/versioned/load/v2", test_load_v2); 1498 g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip); 1499 g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip); 1500 g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip); 1501 g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip); 1502 g_test_add_func("/vmstate/array/ptr/str/no0/save", 1503 test_arr_ptr_str_no0_save); 1504 g_test_add_func("/vmstate/array/ptr/str/no0/load", 1505 test_arr_ptr_str_no0_load); 1506 g_test_add_func("/vmstate/array/ptr/str/0/save", test_arr_ptr_str_0_save); 1507 g_test_add_func("/vmstate/array/ptr/str/0/load", 1508 test_arr_ptr_str_0_load); 1509 g_test_add_func("/vmstate/array/ptr/prim/0/save", 1510 test_arr_ptr_prim_0_save); 1511 g_test_add_func("/vmstate/array/ptr/prim/0/load", 1512 test_arr_ptr_prim_0_load); 1513 g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q); 1514 g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q); 1515 g_test_add_func("/vmstate/gtree/save/savedomain", test_gtree_save_domain); 1516 g_test_add_func("/vmstate/gtree/load/loaddomain", test_gtree_load_domain); 1517 g_test_add_func("/vmstate/gtree/save/saveiommu", test_gtree_save_iommu); 1518 g_test_add_func("/vmstate/gtree/load/loadiommu", test_gtree_load_iommu); 1519 g_test_add_func("/vmstate/qlist/save/saveqlist", test_save_qlist); 1520 g_test_add_func("/vmstate/qlist/load/loadqlist", test_load_qlist); 1521 g_test_add_func("/vmstate/tmp_struct", test_tmp_struct); 1522 g_test_run(); 1523 1524 close(temp_fd); 1525 unlink(temp_file); 1526 1527 return 0; 1528 } 1529