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