1 | /* |
2 | * VMStateInfo's for basic typse |
3 | * |
4 | * Copyright (c) 2009-2017 Red Hat Inc |
5 | * |
6 | * Authors: |
7 | * Juan Quintela <quintela@redhat.com> |
8 | * |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
10 | * See the COPYING file in the top-level directory. |
11 | */ |
12 | |
13 | #include "qemu/osdep.h" |
14 | #include "qemu-file.h" |
15 | #include "migration.h" |
16 | #include "migration/vmstate.h" |
17 | #include "qemu/error-report.h" |
18 | #include "qemu/queue.h" |
19 | #include "trace.h" |
20 | |
21 | /* bool */ |
22 | |
23 | static int get_bool(QEMUFile *f, void *pv, size_t size, |
24 | const VMStateField *field) |
25 | { |
26 | bool *v = pv; |
27 | *v = qemu_get_byte(f); |
28 | return 0; |
29 | } |
30 | |
31 | static int put_bool(QEMUFile *f, void *pv, size_t size, |
32 | const VMStateField *field, QJSON *vmdesc) |
33 | { |
34 | bool *v = pv; |
35 | qemu_put_byte(f, *v); |
36 | return 0; |
37 | } |
38 | |
39 | const VMStateInfo vmstate_info_bool = { |
40 | .name = "bool" , |
41 | .get = get_bool, |
42 | .put = put_bool, |
43 | }; |
44 | |
45 | /* 8 bit int */ |
46 | |
47 | static int get_int8(QEMUFile *f, void *pv, size_t size, |
48 | const VMStateField *field) |
49 | { |
50 | int8_t *v = pv; |
51 | qemu_get_s8s(f, v); |
52 | return 0; |
53 | } |
54 | |
55 | static int put_int8(QEMUFile *f, void *pv, size_t size, |
56 | const VMStateField *field, QJSON *vmdesc) |
57 | { |
58 | int8_t *v = pv; |
59 | qemu_put_s8s(f, v); |
60 | return 0; |
61 | } |
62 | |
63 | const VMStateInfo vmstate_info_int8 = { |
64 | .name = "int8" , |
65 | .get = get_int8, |
66 | .put = put_int8, |
67 | }; |
68 | |
69 | /* 16 bit int */ |
70 | |
71 | static int get_int16(QEMUFile *f, void *pv, size_t size, |
72 | const VMStateField *field) |
73 | { |
74 | int16_t *v = pv; |
75 | qemu_get_sbe16s(f, v); |
76 | return 0; |
77 | } |
78 | |
79 | static int put_int16(QEMUFile *f, void *pv, size_t size, |
80 | const VMStateField *field, QJSON *vmdesc) |
81 | { |
82 | int16_t *v = pv; |
83 | qemu_put_sbe16s(f, v); |
84 | return 0; |
85 | } |
86 | |
87 | const VMStateInfo vmstate_info_int16 = { |
88 | .name = "int16" , |
89 | .get = get_int16, |
90 | .put = put_int16, |
91 | }; |
92 | |
93 | /* 32 bit int */ |
94 | |
95 | static int get_int32(QEMUFile *f, void *pv, size_t size, |
96 | const VMStateField *field) |
97 | { |
98 | int32_t *v = pv; |
99 | qemu_get_sbe32s(f, v); |
100 | return 0; |
101 | } |
102 | |
103 | static int put_int32(QEMUFile *f, void *pv, size_t size, |
104 | const VMStateField *field, QJSON *vmdesc) |
105 | { |
106 | int32_t *v = pv; |
107 | qemu_put_sbe32s(f, v); |
108 | return 0; |
109 | } |
110 | |
111 | const VMStateInfo vmstate_info_int32 = { |
112 | .name = "int32" , |
113 | .get = get_int32, |
114 | .put = put_int32, |
115 | }; |
116 | |
117 | /* 32 bit int. See that the received value is the same than the one |
118 | in the field */ |
119 | |
120 | static int get_int32_equal(QEMUFile *f, void *pv, size_t size, |
121 | const VMStateField *field) |
122 | { |
123 | int32_t *v = pv; |
124 | int32_t v2; |
125 | qemu_get_sbe32s(f, &v2); |
126 | |
127 | if (*v == v2) { |
128 | return 0; |
129 | } |
130 | error_report("%" PRIx32 " != %" PRIx32, *v, v2); |
131 | if (field->err_hint) { |
132 | error_printf("%s\n" , field->err_hint); |
133 | } |
134 | return -EINVAL; |
135 | } |
136 | |
137 | const VMStateInfo vmstate_info_int32_equal = { |
138 | .name = "int32 equal" , |
139 | .get = get_int32_equal, |
140 | .put = put_int32, |
141 | }; |
142 | |
143 | /* 32 bit int. Check that the received value is non-negative |
144 | * and less than or equal to the one in the field. |
145 | */ |
146 | |
147 | static int get_int32_le(QEMUFile *f, void *pv, size_t size, |
148 | const VMStateField *field) |
149 | { |
150 | int32_t *cur = pv; |
151 | int32_t loaded; |
152 | qemu_get_sbe32s(f, &loaded); |
153 | |
154 | if (loaded >= 0 && loaded <= *cur) { |
155 | *cur = loaded; |
156 | return 0; |
157 | } |
158 | error_report("Invalid value %" PRId32 |
159 | " expecting positive value <= %" PRId32, |
160 | loaded, *cur); |
161 | return -EINVAL; |
162 | } |
163 | |
164 | const VMStateInfo vmstate_info_int32_le = { |
165 | .name = "int32 le" , |
166 | .get = get_int32_le, |
167 | .put = put_int32, |
168 | }; |
169 | |
170 | /* 64 bit int */ |
171 | |
172 | static int get_int64(QEMUFile *f, void *pv, size_t size, |
173 | const VMStateField *field) |
174 | { |
175 | int64_t *v = pv; |
176 | qemu_get_sbe64s(f, v); |
177 | return 0; |
178 | } |
179 | |
180 | static int put_int64(QEMUFile *f, void *pv, size_t size, |
181 | const VMStateField *field, QJSON *vmdesc) |
182 | { |
183 | int64_t *v = pv; |
184 | qemu_put_sbe64s(f, v); |
185 | return 0; |
186 | } |
187 | |
188 | const VMStateInfo vmstate_info_int64 = { |
189 | .name = "int64" , |
190 | .get = get_int64, |
191 | .put = put_int64, |
192 | }; |
193 | |
194 | /* 8 bit unsigned int */ |
195 | |
196 | static int get_uint8(QEMUFile *f, void *pv, size_t size, |
197 | const VMStateField *field) |
198 | { |
199 | uint8_t *v = pv; |
200 | qemu_get_8s(f, v); |
201 | return 0; |
202 | } |
203 | |
204 | static int put_uint8(QEMUFile *f, void *pv, size_t size, |
205 | const VMStateField *field, QJSON *vmdesc) |
206 | { |
207 | uint8_t *v = pv; |
208 | qemu_put_8s(f, v); |
209 | return 0; |
210 | } |
211 | |
212 | const VMStateInfo vmstate_info_uint8 = { |
213 | .name = "uint8" , |
214 | .get = get_uint8, |
215 | .put = put_uint8, |
216 | }; |
217 | |
218 | /* 16 bit unsigned int */ |
219 | |
220 | static int get_uint16(QEMUFile *f, void *pv, size_t size, |
221 | const VMStateField *field) |
222 | { |
223 | uint16_t *v = pv; |
224 | qemu_get_be16s(f, v); |
225 | return 0; |
226 | } |
227 | |
228 | static int put_uint16(QEMUFile *f, void *pv, size_t size, |
229 | const VMStateField *field, QJSON *vmdesc) |
230 | { |
231 | uint16_t *v = pv; |
232 | qemu_put_be16s(f, v); |
233 | return 0; |
234 | } |
235 | |
236 | const VMStateInfo vmstate_info_uint16 = { |
237 | .name = "uint16" , |
238 | .get = get_uint16, |
239 | .put = put_uint16, |
240 | }; |
241 | |
242 | /* 32 bit unsigned int */ |
243 | |
244 | static int get_uint32(QEMUFile *f, void *pv, size_t size, |
245 | const VMStateField *field) |
246 | { |
247 | uint32_t *v = pv; |
248 | qemu_get_be32s(f, v); |
249 | return 0; |
250 | } |
251 | |
252 | static int put_uint32(QEMUFile *f, void *pv, size_t size, |
253 | const VMStateField *field, QJSON *vmdesc) |
254 | { |
255 | uint32_t *v = pv; |
256 | qemu_put_be32s(f, v); |
257 | return 0; |
258 | } |
259 | |
260 | const VMStateInfo vmstate_info_uint32 = { |
261 | .name = "uint32" , |
262 | .get = get_uint32, |
263 | .put = put_uint32, |
264 | }; |
265 | |
266 | /* 32 bit uint. See that the received value is the same than the one |
267 | in the field */ |
268 | |
269 | static int get_uint32_equal(QEMUFile *f, void *pv, size_t size, |
270 | const VMStateField *field) |
271 | { |
272 | uint32_t *v = pv; |
273 | uint32_t v2; |
274 | qemu_get_be32s(f, &v2); |
275 | |
276 | if (*v == v2) { |
277 | return 0; |
278 | } |
279 | error_report("%" PRIx32 " != %" PRIx32, *v, v2); |
280 | if (field->err_hint) { |
281 | error_printf("%s\n" , field->err_hint); |
282 | } |
283 | return -EINVAL; |
284 | } |
285 | |
286 | const VMStateInfo vmstate_info_uint32_equal = { |
287 | .name = "uint32 equal" , |
288 | .get = get_uint32_equal, |
289 | .put = put_uint32, |
290 | }; |
291 | |
292 | /* 64 bit unsigned int */ |
293 | |
294 | static int get_uint64(QEMUFile *f, void *pv, size_t size, |
295 | const VMStateField *field) |
296 | { |
297 | uint64_t *v = pv; |
298 | qemu_get_be64s(f, v); |
299 | return 0; |
300 | } |
301 | |
302 | static int put_uint64(QEMUFile *f, void *pv, size_t size, |
303 | const VMStateField *field, QJSON *vmdesc) |
304 | { |
305 | uint64_t *v = pv; |
306 | qemu_put_be64s(f, v); |
307 | return 0; |
308 | } |
309 | |
310 | const VMStateInfo vmstate_info_uint64 = { |
311 | .name = "uint64" , |
312 | .get = get_uint64, |
313 | .put = put_uint64, |
314 | }; |
315 | |
316 | static int get_nullptr(QEMUFile *f, void *pv, size_t size, |
317 | const VMStateField *field) |
318 | |
319 | { |
320 | if (qemu_get_byte(f) == VMS_NULLPTR_MARKER) { |
321 | return 0; |
322 | } |
323 | error_report("vmstate: get_nullptr expected VMS_NULLPTR_MARKER" ); |
324 | return -EINVAL; |
325 | } |
326 | |
327 | static int put_nullptr(QEMUFile *f, void *pv, size_t size, |
328 | const VMStateField *field, QJSON *vmdesc) |
329 | |
330 | { |
331 | if (pv == NULL) { |
332 | qemu_put_byte(f, VMS_NULLPTR_MARKER); |
333 | return 0; |
334 | } |
335 | error_report("vmstate: put_nullptr must be called with pv == NULL" ); |
336 | return -EINVAL; |
337 | } |
338 | |
339 | const VMStateInfo vmstate_info_nullptr = { |
340 | .name = "uint64" , |
341 | .get = get_nullptr, |
342 | .put = put_nullptr, |
343 | }; |
344 | |
345 | /* 64 bit unsigned int. See that the received value is the same than the one |
346 | in the field */ |
347 | |
348 | static int get_uint64_equal(QEMUFile *f, void *pv, size_t size, |
349 | const VMStateField *field) |
350 | { |
351 | uint64_t *v = pv; |
352 | uint64_t v2; |
353 | qemu_get_be64s(f, &v2); |
354 | |
355 | if (*v == v2) { |
356 | return 0; |
357 | } |
358 | error_report("%" PRIx64 " != %" PRIx64, *v, v2); |
359 | if (field->err_hint) { |
360 | error_printf("%s\n" , field->err_hint); |
361 | } |
362 | return -EINVAL; |
363 | } |
364 | |
365 | const VMStateInfo vmstate_info_uint64_equal = { |
366 | .name = "int64 equal" , |
367 | .get = get_uint64_equal, |
368 | .put = put_uint64, |
369 | }; |
370 | |
371 | /* 8 bit int. See that the received value is the same than the one |
372 | in the field */ |
373 | |
374 | static int get_uint8_equal(QEMUFile *f, void *pv, size_t size, |
375 | const VMStateField *field) |
376 | { |
377 | uint8_t *v = pv; |
378 | uint8_t v2; |
379 | qemu_get_8s(f, &v2); |
380 | |
381 | if (*v == v2) { |
382 | return 0; |
383 | } |
384 | error_report("%x != %x" , *v, v2); |
385 | if (field->err_hint) { |
386 | error_printf("%s\n" , field->err_hint); |
387 | } |
388 | return -EINVAL; |
389 | } |
390 | |
391 | const VMStateInfo vmstate_info_uint8_equal = { |
392 | .name = "uint8 equal" , |
393 | .get = get_uint8_equal, |
394 | .put = put_uint8, |
395 | }; |
396 | |
397 | /* 16 bit unsigned int int. See that the received value is the same than the one |
398 | in the field */ |
399 | |
400 | static int get_uint16_equal(QEMUFile *f, void *pv, size_t size, |
401 | const VMStateField *field) |
402 | { |
403 | uint16_t *v = pv; |
404 | uint16_t v2; |
405 | qemu_get_be16s(f, &v2); |
406 | |
407 | if (*v == v2) { |
408 | return 0; |
409 | } |
410 | error_report("%x != %x" , *v, v2); |
411 | if (field->err_hint) { |
412 | error_printf("%s\n" , field->err_hint); |
413 | } |
414 | return -EINVAL; |
415 | } |
416 | |
417 | const VMStateInfo vmstate_info_uint16_equal = { |
418 | .name = "uint16 equal" , |
419 | .get = get_uint16_equal, |
420 | .put = put_uint16, |
421 | }; |
422 | |
423 | /* floating point */ |
424 | |
425 | static int get_float64(QEMUFile *f, void *pv, size_t size, |
426 | const VMStateField *field) |
427 | { |
428 | float64 *v = pv; |
429 | |
430 | *v = make_float64(qemu_get_be64(f)); |
431 | return 0; |
432 | } |
433 | |
434 | static int put_float64(QEMUFile *f, void *pv, size_t size, |
435 | const VMStateField *field, QJSON *vmdesc) |
436 | { |
437 | uint64_t *v = pv; |
438 | |
439 | qemu_put_be64(f, float64_val(*v)); |
440 | return 0; |
441 | } |
442 | |
443 | const VMStateInfo vmstate_info_float64 = { |
444 | .name = "float64" , |
445 | .get = get_float64, |
446 | .put = put_float64, |
447 | }; |
448 | |
449 | /* CPU_DoubleU type */ |
450 | |
451 | static int get_cpudouble(QEMUFile *f, void *pv, size_t size, |
452 | const VMStateField *field) |
453 | { |
454 | CPU_DoubleU *v = pv; |
455 | qemu_get_be32s(f, &v->l.upper); |
456 | qemu_get_be32s(f, &v->l.lower); |
457 | return 0; |
458 | } |
459 | |
460 | static int put_cpudouble(QEMUFile *f, void *pv, size_t size, |
461 | const VMStateField *field, QJSON *vmdesc) |
462 | { |
463 | CPU_DoubleU *v = pv; |
464 | qemu_put_be32s(f, &v->l.upper); |
465 | qemu_put_be32s(f, &v->l.lower); |
466 | return 0; |
467 | } |
468 | |
469 | const VMStateInfo vmstate_info_cpudouble = { |
470 | .name = "CPU_Double_U" , |
471 | .get = get_cpudouble, |
472 | .put = put_cpudouble, |
473 | }; |
474 | |
475 | /* uint8_t buffers */ |
476 | |
477 | static int get_buffer(QEMUFile *f, void *pv, size_t size, |
478 | const VMStateField *field) |
479 | { |
480 | uint8_t *v = pv; |
481 | qemu_get_buffer(f, v, size); |
482 | return 0; |
483 | } |
484 | |
485 | static int put_buffer(QEMUFile *f, void *pv, size_t size, |
486 | const VMStateField *field, QJSON *vmdesc) |
487 | { |
488 | uint8_t *v = pv; |
489 | qemu_put_buffer(f, v, size); |
490 | return 0; |
491 | } |
492 | |
493 | const VMStateInfo vmstate_info_buffer = { |
494 | .name = "buffer" , |
495 | .get = get_buffer, |
496 | .put = put_buffer, |
497 | }; |
498 | |
499 | /* unused buffers: space that was used for some fields that are |
500 | not useful anymore */ |
501 | |
502 | static int get_unused_buffer(QEMUFile *f, void *pv, size_t size, |
503 | const VMStateField *field) |
504 | { |
505 | uint8_t buf[1024]; |
506 | int block_len; |
507 | |
508 | while (size > 0) { |
509 | block_len = MIN(sizeof(buf), size); |
510 | size -= block_len; |
511 | qemu_get_buffer(f, buf, block_len); |
512 | } |
513 | return 0; |
514 | } |
515 | |
516 | static int put_unused_buffer(QEMUFile *f, void *pv, size_t size, |
517 | const VMStateField *field, QJSON *vmdesc) |
518 | { |
519 | static const uint8_t buf[1024]; |
520 | int block_len; |
521 | |
522 | while (size > 0) { |
523 | block_len = MIN(sizeof(buf), size); |
524 | size -= block_len; |
525 | qemu_put_buffer(f, buf, block_len); |
526 | } |
527 | |
528 | return 0; |
529 | } |
530 | |
531 | const VMStateInfo vmstate_info_unused_buffer = { |
532 | .name = "unused_buffer" , |
533 | .get = get_unused_buffer, |
534 | .put = put_unused_buffer, |
535 | }; |
536 | |
537 | /* vmstate_info_tmp, see VMSTATE_WITH_TMP, the idea is that we allocate |
538 | * a temporary buffer and the pre_load/pre_save methods in the child vmsd |
539 | * copy stuff from the parent into the child and do calculations to fill |
540 | * in fields that don't really exist in the parent but need to be in the |
541 | * stream. |
542 | */ |
543 | static int get_tmp(QEMUFile *f, void *pv, size_t size, |
544 | const VMStateField *field) |
545 | { |
546 | int ret; |
547 | const VMStateDescription *vmsd = field->vmsd; |
548 | int version_id = field->version_id; |
549 | void *tmp = g_malloc(size); |
550 | |
551 | /* Writes the parent field which is at the start of the tmp */ |
552 | *(void **)tmp = pv; |
553 | ret = vmstate_load_state(f, vmsd, tmp, version_id); |
554 | g_free(tmp); |
555 | return ret; |
556 | } |
557 | |
558 | static int put_tmp(QEMUFile *f, void *pv, size_t size, |
559 | const VMStateField *field, QJSON *vmdesc) |
560 | { |
561 | const VMStateDescription *vmsd = field->vmsd; |
562 | void *tmp = g_malloc(size); |
563 | int ret; |
564 | |
565 | /* Writes the parent field which is at the start of the tmp */ |
566 | *(void **)tmp = pv; |
567 | ret = vmstate_save_state(f, vmsd, tmp, vmdesc); |
568 | g_free(tmp); |
569 | |
570 | return ret; |
571 | } |
572 | |
573 | const VMStateInfo vmstate_info_tmp = { |
574 | .name = "tmp" , |
575 | .get = get_tmp, |
576 | .put = put_tmp, |
577 | }; |
578 | |
579 | /* bitmaps (as defined by bitmap.h). Note that size here is the size |
580 | * of the bitmap in bits. The on-the-wire format of a bitmap is 64 |
581 | * bit words with the bits in big endian order. The in-memory format |
582 | * is an array of 'unsigned long', which may be either 32 or 64 bits. |
583 | */ |
584 | /* This is the number of 64 bit words sent over the wire */ |
585 | #define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64) |
586 | static int get_bitmap(QEMUFile *f, void *pv, size_t size, |
587 | const VMStateField *field) |
588 | { |
589 | unsigned long *bmp = pv; |
590 | int i, idx = 0; |
591 | for (i = 0; i < BITS_TO_U64S(size); i++) { |
592 | uint64_t w = qemu_get_be64(f); |
593 | bmp[idx++] = w; |
594 | if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { |
595 | bmp[idx++] = w >> 32; |
596 | } |
597 | } |
598 | return 0; |
599 | } |
600 | |
601 | static int put_bitmap(QEMUFile *f, void *pv, size_t size, |
602 | const VMStateField *field, QJSON *vmdesc) |
603 | { |
604 | unsigned long *bmp = pv; |
605 | int i, idx = 0; |
606 | for (i = 0; i < BITS_TO_U64S(size); i++) { |
607 | uint64_t w = bmp[idx++]; |
608 | if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { |
609 | w |= ((uint64_t)bmp[idx++]) << 32; |
610 | } |
611 | qemu_put_be64(f, w); |
612 | } |
613 | |
614 | return 0; |
615 | } |
616 | |
617 | const VMStateInfo vmstate_info_bitmap = { |
618 | .name = "bitmap" , |
619 | .get = get_bitmap, |
620 | .put = put_bitmap, |
621 | }; |
622 | |
623 | /* get for QTAILQ |
624 | * meta data about the QTAILQ is encoded in a VMStateField structure |
625 | */ |
626 | static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size, |
627 | const VMStateField *field) |
628 | { |
629 | int ret = 0; |
630 | const VMStateDescription *vmsd = field->vmsd; |
631 | /* size of a QTAILQ element */ |
632 | size_t size = field->size; |
633 | /* offset of the QTAILQ entry in a QTAILQ element */ |
634 | size_t entry_offset = field->start; |
635 | int version_id = field->version_id; |
636 | void *elm; |
637 | |
638 | trace_get_qtailq(vmsd->name, version_id); |
639 | if (version_id > vmsd->version_id) { |
640 | error_report("%s %s" , vmsd->name, "too new" ); |
641 | trace_get_qtailq_end(vmsd->name, "too new" , -EINVAL); |
642 | |
643 | return -EINVAL; |
644 | } |
645 | if (version_id < vmsd->minimum_version_id) { |
646 | error_report("%s %s" , vmsd->name, "too old" ); |
647 | trace_get_qtailq_end(vmsd->name, "too old" , -EINVAL); |
648 | return -EINVAL; |
649 | } |
650 | |
651 | while (qemu_get_byte(f)) { |
652 | elm = g_malloc(size); |
653 | ret = vmstate_load_state(f, vmsd, elm, version_id); |
654 | if (ret) { |
655 | return ret; |
656 | } |
657 | QTAILQ_RAW_INSERT_TAIL(pv, elm, entry_offset); |
658 | } |
659 | |
660 | trace_get_qtailq_end(vmsd->name, "end" , ret); |
661 | return ret; |
662 | } |
663 | |
664 | /* put for QTAILQ */ |
665 | static int put_qtailq(QEMUFile *f, void *pv, size_t unused_size, |
666 | const VMStateField *field, QJSON *vmdesc) |
667 | { |
668 | const VMStateDescription *vmsd = field->vmsd; |
669 | /* offset of the QTAILQ entry in a QTAILQ element*/ |
670 | size_t entry_offset = field->start; |
671 | void *elm; |
672 | int ret; |
673 | |
674 | trace_put_qtailq(vmsd->name, vmsd->version_id); |
675 | |
676 | QTAILQ_RAW_FOREACH(elm, pv, entry_offset) { |
677 | qemu_put_byte(f, true); |
678 | ret = vmstate_save_state(f, vmsd, elm, vmdesc); |
679 | if (ret) { |
680 | return ret; |
681 | } |
682 | } |
683 | qemu_put_byte(f, false); |
684 | |
685 | trace_put_qtailq_end(vmsd->name, "end" ); |
686 | |
687 | return 0; |
688 | } |
689 | const VMStateInfo vmstate_info_qtailq = { |
690 | .name = "qtailq" , |
691 | .get = get_qtailq, |
692 | .put = put_qtailq, |
693 | }; |
694 | |