1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include "qemu/osdep.h"
26
27#include "qemu-common.h"
28#include "migration/migration.h"
29#include "migration/vmstate.h"
30#include "qemu/coroutine.h"
31#include "io/channel-file.h"
32
33static char temp_file[] = "/tmp/vmst.test.XXXXXX";
34static int temp_fd;
35
36
37
38
39void yield_until_fd_readable(int fd)
40{
41 fd_set fds;
42 FD_ZERO(&fds);
43 FD_SET(fd, &fds);
44 select(fd + 1, &fds, NULL, NULL, NULL);
45}
46
47
48
49static QEMUFile *open_test_file(bool write)
50{
51 int fd = dup(temp_fd);
52 QIOChannel *ioc;
53 QEMUFile *f;
54
55 lseek(fd, 0, SEEK_SET);
56 if (write) {
57 g_assert_cmpint(ftruncate(fd, 0), ==, 0);
58 }
59 ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd));
60 if (write) {
61 f = qemu_fopen_channel_output(ioc);
62 } else {
63 f = qemu_fopen_channel_input(ioc);
64 }
65 object_unref(OBJECT(ioc));
66 return f;
67}
68
69#define SUCCESS(val) \
70 g_assert_cmpint((val), ==, 0)
71
72#define FAILURE(val) \
73 g_assert_cmpint((val), !=, 0)
74
75static void save_vmstate(const VMStateDescription *desc, void *obj)
76{
77 QEMUFile *f = open_test_file(true);
78
79
80 vmstate_save_state(f, desc, obj, NULL);
81 qemu_put_byte(f, QEMU_VM_EOF);
82 g_assert(!qemu_file_get_error(f));
83 qemu_fclose(f);
84}
85
86static void save_buffer(const uint8_t *buf, size_t buf_size)
87{
88 QEMUFile *fsave = open_test_file(true);
89 qemu_put_buffer(fsave, buf, buf_size);
90 qemu_fclose(fsave);
91}
92
93static void compare_vmstate(uint8_t *wire, size_t size)
94{
95 QEMUFile *f = open_test_file(false);
96 uint8_t result[size];
97
98
99
100 g_assert_cmpint(qemu_get_buffer(f, result, sizeof(result)), ==,
101 sizeof(result));
102 g_assert(!qemu_file_get_error(f));
103
104
105
106 SUCCESS(memcmp(result, wire, sizeof(result)));
107
108
109 qemu_get_byte(f);
110 g_assert_cmpint(qemu_file_get_error(f), ==, -EIO);
111
112 qemu_fclose(f);
113}
114
115static int load_vmstate_one(const VMStateDescription *desc, void *obj,
116 int version, uint8_t *wire, size_t size)
117{
118 QEMUFile *f;
119 int ret;
120
121 f = open_test_file(true);
122 qemu_put_buffer(f, wire, size);
123 qemu_fclose(f);
124
125 f = open_test_file(false);
126 ret = vmstate_load_state(f, desc, obj, version);
127 if (ret) {
128 g_assert(qemu_file_get_error(f));
129 } else{
130 g_assert(!qemu_file_get_error(f));
131 }
132 qemu_fclose(f);
133 return ret;
134}
135
136
137static int load_vmstate(const VMStateDescription *desc,
138 void *obj, void *obj_clone,
139 void (*obj_copy)(void *, void*),
140 int version, uint8_t *wire, size_t size)
141{
142
143 obj_copy(obj_clone, obj);
144 FAILURE(load_vmstate_one(desc, obj, version, wire, 0));
145
146
147
148
149 if (size > 3) {
150
151
152 obj_copy(obj, obj_clone);
153 FAILURE(load_vmstate_one(desc, obj, version, wire, size - 2));
154
155
156 obj_copy(obj, obj_clone);
157 FAILURE(load_vmstate_one(desc, obj, version, wire, size/2));
158
159
160 obj_copy(obj, obj_clone);
161 FAILURE(load_vmstate_one(desc, obj, version, wire + (size/2), size/2));
162
163 }
164 obj_copy(obj, obj_clone);
165 return load_vmstate_one(desc, obj, version, wire, size);
166}
167
168
169
170typedef struct TestSimple {
171 bool b_1, b_2;
172 uint8_t u8_1;
173 uint16_t u16_1;
174 uint32_t u32_1;
175 uint64_t u64_1;
176 int8_t i8_1, i8_2;
177 int16_t i16_1, i16_2;
178 int32_t i32_1, i32_2;
179 int64_t i64_1, i64_2;
180} TestSimple;
181
182
183
184TestSimple obj_simple = {
185 .b_1 = true,
186 .b_2 = false,
187 .u8_1 = 130,
188 .u16_1 = 512,
189 .u32_1 = 70000,
190 .u64_1 = 12121212,
191 .i8_1 = 65,
192 .i8_2 = -65,
193 .i16_1 = 512,
194 .i16_2 = -512,
195 .i32_1 = 70000,
196 .i32_2 = -70000,
197 .i64_1 = 12121212,
198 .i64_2 = -12121212,
199};
200
201
202
203
204static const VMStateDescription vmstate_simple_primitive = {
205 .name = "simple/primitive",
206 .version_id = 1,
207 .minimum_version_id = 1,
208 .fields = (VMStateField[]) {
209 VMSTATE_BOOL(b_1, TestSimple),
210 VMSTATE_BOOL(b_2, TestSimple),
211 VMSTATE_UINT8(u8_1, TestSimple),
212 VMSTATE_UINT16(u16_1, TestSimple),
213 VMSTATE_UINT32(u32_1, TestSimple),
214 VMSTATE_UINT64(u64_1, TestSimple),
215 VMSTATE_INT8(i8_1, TestSimple),
216 VMSTATE_INT8(i8_2, TestSimple),
217 VMSTATE_INT16(i16_1, TestSimple),
218 VMSTATE_INT16(i16_2, TestSimple),
219 VMSTATE_INT32(i32_1, TestSimple),
220 VMSTATE_INT32(i32_2, TestSimple),
221 VMSTATE_INT64(i64_1, TestSimple),
222 VMSTATE_INT64(i64_2, TestSimple),
223 VMSTATE_END_OF_LIST()
224 }
225};
226
227
228
229
230
231
232
233
234
235
236
237
238
239uint8_t wire_simple_primitive[] = {
240 0x01,
241 0x00,
242 0x82,
243 0x02, 0x00,
244 0x00, 0x01, 0x11, 0x70,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c,
246 0x41,
247 0xbf,
248 0x02, 0x00,
249 0xfe, 0x0,
250 0x00, 0x01, 0x11, 0x70,
251 0xff, 0xfe, 0xee, 0x90,
252 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c,
253 0xff, 0xff, 0xff, 0xff, 0xff, 0x47, 0x0b, 0x84,
254 QEMU_VM_EOF,
255};
256
257static void obj_simple_copy(void *target, void *source)
258{
259 memcpy(target, source, sizeof(TestSimple));
260}
261
262static void test_simple_primitive(void)
263{
264 TestSimple obj, obj_clone;
265
266 memset(&obj, 0, sizeof(obj));
267 save_vmstate(&vmstate_simple_primitive, &obj_simple);
268
269 compare_vmstate(wire_simple_primitive, sizeof(wire_simple_primitive));
270
271 SUCCESS(load_vmstate(&vmstate_simple_primitive, &obj, &obj_clone,
272 obj_simple_copy, 1, wire_simple_primitive,
273 sizeof(wire_simple_primitive)));
274
275#define FIELD_EQUAL(name) g_assert_cmpint(obj.name, ==, obj_simple.name)
276
277 FIELD_EQUAL(b_1);
278 FIELD_EQUAL(b_2);
279 FIELD_EQUAL(u8_1);
280 FIELD_EQUAL(u16_1);
281 FIELD_EQUAL(u32_1);
282 FIELD_EQUAL(u64_1);
283 FIELD_EQUAL(i8_1);
284 FIELD_EQUAL(i8_2);
285 FIELD_EQUAL(i16_1);
286 FIELD_EQUAL(i16_2);
287 FIELD_EQUAL(i32_1);
288 FIELD_EQUAL(i32_2);
289 FIELD_EQUAL(i64_1);
290 FIELD_EQUAL(i64_2);
291}
292#undef FIELD_EQUAL
293
294typedef struct TestStruct {
295 uint32_t a, b, c, e;
296 uint64_t d, f;
297 bool skip_c_e;
298} TestStruct;
299
300static const VMStateDescription vmstate_versioned = {
301 .name = "test/versioned",
302 .version_id = 2,
303 .minimum_version_id = 1,
304 .fields = (VMStateField[]) {
305 VMSTATE_UINT32(a, TestStruct),
306 VMSTATE_UINT32_V(b, TestStruct, 2),
307
308
309 VMSTATE_UINT32(c, TestStruct),
310 VMSTATE_UINT64(d, TestStruct),
311 VMSTATE_UINT32_V(e, TestStruct, 2),
312 VMSTATE_UINT64_V(f, TestStruct, 2),
313 VMSTATE_END_OF_LIST()
314 }
315};
316
317static void test_load_v1(void)
318{
319 uint8_t buf[] = {
320 0, 0, 0, 10,
321 0, 0, 0, 30,
322 0, 0, 0, 0, 0, 0, 0, 40,
323 QEMU_VM_EOF,
324 };
325 save_buffer(buf, sizeof(buf));
326
327 QEMUFile *loading = open_test_file(false);
328 TestStruct obj = { .b = 200, .e = 500, .f = 600 };
329 vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
330 g_assert(!qemu_file_get_error(loading));
331 g_assert_cmpint(obj.a, ==, 10);
332 g_assert_cmpint(obj.b, ==, 200);
333 g_assert_cmpint(obj.c, ==, 30);
334 g_assert_cmpint(obj.d, ==, 40);
335 g_assert_cmpint(obj.e, ==, 500);
336 g_assert_cmpint(obj.f, ==, 600);
337 qemu_fclose(loading);
338}
339
340static void test_load_v2(void)
341{
342 uint8_t buf[] = {
343 0, 0, 0, 10,
344 0, 0, 0, 20,
345 0, 0, 0, 30,
346 0, 0, 0, 0, 0, 0, 0, 40,
347 0, 0, 0, 50,
348 0, 0, 0, 0, 0, 0, 0, 60,
349 QEMU_VM_EOF,
350 };
351 save_buffer(buf, sizeof(buf));
352
353 QEMUFile *loading = open_test_file(false);
354 TestStruct obj;
355 vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
356 g_assert_cmpint(obj.a, ==, 10);
357 g_assert_cmpint(obj.b, ==, 20);
358 g_assert_cmpint(obj.c, ==, 30);
359 g_assert_cmpint(obj.d, ==, 40);
360 g_assert_cmpint(obj.e, ==, 50);
361 g_assert_cmpint(obj.f, ==, 60);
362 qemu_fclose(loading);
363}
364
365static bool test_skip(void *opaque, int version_id)
366{
367 TestStruct *t = (TestStruct *)opaque;
368 return !t->skip_c_e;
369}
370
371static const VMStateDescription vmstate_skipping = {
372 .name = "test/skip",
373 .version_id = 2,
374 .minimum_version_id = 1,
375 .fields = (VMStateField[]) {
376 VMSTATE_UINT32(a, TestStruct),
377 VMSTATE_UINT32(b, TestStruct),
378 VMSTATE_UINT32_TEST(c, TestStruct, test_skip),
379 VMSTATE_UINT64(d, TestStruct),
380 VMSTATE_UINT32_TEST(e, TestStruct, test_skip),
381 VMSTATE_UINT64_V(f, TestStruct, 2),
382 VMSTATE_END_OF_LIST()
383 }
384};
385
386
387static void test_save_noskip(void)
388{
389 QEMUFile *fsave = open_test_file(true);
390 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
391 .skip_c_e = false };
392 vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
393 g_assert(!qemu_file_get_error(fsave));
394
395 uint8_t expected[] = {
396 0, 0, 0, 1,
397 0, 0, 0, 2,
398 0, 0, 0, 3,
399 0, 0, 0, 0, 0, 0, 0, 4,
400 0, 0, 0, 5,
401 0, 0, 0, 0, 0, 0, 0, 6,
402 };
403
404 qemu_fclose(fsave);
405 compare_vmstate(expected, sizeof(expected));
406}
407
408static void test_save_skip(void)
409{
410 QEMUFile *fsave = open_test_file(true);
411 TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
412 .skip_c_e = true };
413 vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
414 g_assert(!qemu_file_get_error(fsave));
415
416 uint8_t expected[] = {
417 0, 0, 0, 1,
418 0, 0, 0, 2,
419 0, 0, 0, 0, 0, 0, 0, 4,
420 0, 0, 0, 0, 0, 0, 0, 6,
421 };
422
423 qemu_fclose(fsave);
424 compare_vmstate(expected, sizeof(expected));
425}
426
427static void test_load_noskip(void)
428{
429 uint8_t buf[] = {
430 0, 0, 0, 10,
431 0, 0, 0, 20,
432 0, 0, 0, 30,
433 0, 0, 0, 0, 0, 0, 0, 40,
434 0, 0, 0, 50,
435 0, 0, 0, 0, 0, 0, 0, 60,
436 QEMU_VM_EOF,
437 };
438 save_buffer(buf, sizeof(buf));
439
440 QEMUFile *loading = open_test_file(false);
441 TestStruct obj = { .skip_c_e = false };
442 vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
443 g_assert(!qemu_file_get_error(loading));
444 g_assert_cmpint(obj.a, ==, 10);
445 g_assert_cmpint(obj.b, ==, 20);
446 g_assert_cmpint(obj.c, ==, 30);
447 g_assert_cmpint(obj.d, ==, 40);
448 g_assert_cmpint(obj.e, ==, 50);
449 g_assert_cmpint(obj.f, ==, 60);
450 qemu_fclose(loading);
451}
452
453static void test_load_skip(void)
454{
455 uint8_t buf[] = {
456 0, 0, 0, 10,
457 0, 0, 0, 20,
458 0, 0, 0, 0, 0, 0, 0, 40,
459 0, 0, 0, 0, 0, 0, 0, 60,
460 QEMU_VM_EOF,
461 };
462 save_buffer(buf, sizeof(buf));
463
464 QEMUFile *loading = open_test_file(false);
465 TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
466 vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
467 g_assert(!qemu_file_get_error(loading));
468 g_assert_cmpint(obj.a, ==, 10);
469 g_assert_cmpint(obj.b, ==, 20);
470 g_assert_cmpint(obj.c, ==, 300);
471 g_assert_cmpint(obj.d, ==, 40);
472 g_assert_cmpint(obj.e, ==, 500);
473 g_assert_cmpint(obj.f, ==, 60);
474 qemu_fclose(loading);
475}
476
477
478typedef struct {
479 int32_t i;
480} TestStructTriv;
481
482const VMStateDescription vmsd_tst = {
483 .name = "test/tst",
484 .version_id = 1,
485 .minimum_version_id = 1,
486 .fields = (VMStateField[]) {
487 VMSTATE_INT32(i, TestStructTriv),
488 VMSTATE_END_OF_LIST()
489 }
490};
491
492#define AR_SIZE 4
493
494typedef struct {
495 TestStructTriv *ar[AR_SIZE];
496} TestArrayOfPtrToStuct;
497
498const VMStateDescription vmsd_arps = {
499 .name = "test/arps",
500 .version_id = 1,
501 .minimum_version_id = 1,
502 .fields = (VMStateField[]) {
503 VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(ar, TestArrayOfPtrToStuct,
504 AR_SIZE, 0, vmsd_tst, TestStructTriv),
505 VMSTATE_END_OF_LIST()
506 }
507};
508static void test_arr_ptr_str_no0_save(void)
509{
510 TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
511 TestArrayOfPtrToStuct sample = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
512 uint8_t wire_sample[] = {
513 0x00, 0x00, 0x00, 0x00,
514 0x00, 0x00, 0x00, 0x01,
515 0x00, 0x00, 0x00, 0x02,
516 0x00, 0x00, 0x00, 0x03,
517 QEMU_VM_EOF
518 };
519
520 save_vmstate(&vmsd_arps, &sample);
521 compare_vmstate(wire_sample, sizeof(wire_sample));
522}
523
524static void test_arr_ptr_str_no0_load(void)
525{
526 TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} };
527 TestStructTriv ar[AR_SIZE] = {};
528 TestArrayOfPtrToStuct obj = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} };
529 int idx;
530 uint8_t wire_sample[] = {
531 0x00, 0x00, 0x00, 0x00,
532 0x00, 0x00, 0x00, 0x01,
533 0x00, 0x00, 0x00, 0x02,
534 0x00, 0x00, 0x00, 0x03,
535 QEMU_VM_EOF
536 };
537
538 save_buffer(wire_sample, sizeof(wire_sample));
539 SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1,
540 wire_sample, sizeof(wire_sample)));
541 for (idx = 0; idx < AR_SIZE; ++idx) {
542
543 g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i);
544 }
545}
546
547int main(int argc, char **argv)
548{
549 temp_fd = mkstemp(temp_file);
550
551 module_call_init(MODULE_INIT_QOM);
552
553 g_test_init(&argc, &argv, NULL);
554 g_test_add_func("/vmstate/simple/primitive", test_simple_primitive);
555 g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
556 g_test_add_func("/vmstate/versioned/load/v2", test_load_v2);
557 g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip);
558 g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip);
559 g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip);
560 g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip);
561 g_test_add_func("/vmstate/array/ptr/str/no0/save",
562 test_arr_ptr_str_no0_save);
563 g_test_add_func("/vmstate/array/ptr/str/no0/load",
564 test_arr_ptr_str_no0_load);
565 g_test_run();
566
567 close(temp_fd);
568 unlink(temp_file);
569
570 return 0;
571}
572