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