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