]>
Commit | Line | Data |
---|---|---|
1393a485 | 1 | #include "qemu/osdep.h" |
b6fcfa59 EH |
2 | #include "qemu-common.h" |
3 | #include "migration/migration.h" | |
4 | #include "migration/qemu-file.h" | |
5 | #include "migration/vmstate.h" | |
6 | #include "qemu/bitops.h" | |
6a64b644 | 7 | #include "qemu/error-report.h" |
94869d5c | 8 | #include "qemu/queue.h" |
9013dca5 | 9 | #include "trace.h" |
2c21ee76 | 10 | #include "migration/qjson.h" |
b6fcfa59 EH |
11 | |
12 | static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, | |
8118f095 | 13 | void *opaque, QJSON *vmdesc); |
b6fcfa59 EH |
14 | static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, |
15 | void *opaque); | |
16 | ||
35fc1f71 MT |
17 | static int vmstate_n_elems(void *opaque, VMStateField *field) |
18 | { | |
19 | int n_elems = 1; | |
20 | ||
21 | if (field->flags & VMS_ARRAY) { | |
22 | n_elems = field->num; | |
23 | } else if (field->flags & VMS_VARRAY_INT32) { | |
24 | n_elems = *(int32_t *)(opaque+field->num_offset); | |
25 | } else if (field->flags & VMS_VARRAY_UINT32) { | |
26 | n_elems = *(uint32_t *)(opaque+field->num_offset); | |
27 | } else if (field->flags & VMS_VARRAY_UINT16) { | |
28 | n_elems = *(uint16_t *)(opaque+field->num_offset); | |
29 | } else if (field->flags & VMS_VARRAY_UINT8) { | |
30 | n_elems = *(uint8_t *)(opaque+field->num_offset); | |
31 | } | |
32 | ||
b47d3af7 JQ |
33 | if (field->flags & VMS_MULTIPLY_ELEMENTS) { |
34 | n_elems *= field->num; | |
35 | } | |
36 | ||
023ad1a6 | 37 | trace_vmstate_n_elems(field->name, n_elems); |
35fc1f71 MT |
38 | return n_elems; |
39 | } | |
40 | ||
41 | static int vmstate_size(void *opaque, VMStateField *field) | |
42 | { | |
43 | int size = field->size; | |
44 | ||
45 | if (field->flags & VMS_VBUFFER) { | |
46 | size = *(int32_t *)(opaque+field->size_offset); | |
47 | if (field->flags & VMS_MULTIPLY) { | |
48 | size *= field->size; | |
49 | } | |
50 | } | |
51 | ||
52 | return size; | |
53 | } | |
54 | ||
f32935ea | 55 | static void *vmstate_base_addr(void *opaque, VMStateField *field, bool alloc) |
35fc1f71 MT |
56 | { |
57 | void *base_addr = opaque + field->offset; | |
58 | ||
59 | if (field->flags & VMS_POINTER) { | |
f32935ea | 60 | if (alloc && (field->flags & VMS_ALLOC)) { |
94ed706d AK |
61 | gsize size = 0; |
62 | if (field->flags & VMS_VBUFFER) { | |
63 | size = vmstate_size(opaque, field); | |
64 | } else { | |
65 | int n_elems = vmstate_n_elems(opaque, field); | |
66 | if (n_elems) { | |
67 | size = n_elems * field->size; | |
68 | } | |
69 | } | |
70 | if (size) { | |
f32935ea AK |
71 | *((void **)base_addr + field->start) = g_malloc(size); |
72 | } | |
73 | } | |
35fc1f71 MT |
74 | base_addr = *(void **)base_addr + field->start; |
75 | } | |
76 | ||
77 | return base_addr; | |
78 | } | |
79 | ||
b6fcfa59 EH |
80 | int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, |
81 | void *opaque, int version_id) | |
82 | { | |
83 | VMStateField *field = vmsd->fields; | |
a5df2a02 | 84 | int ret = 0; |
b6fcfa59 | 85 | |
a5df2a02 | 86 | trace_vmstate_load_state(vmsd->name, version_id); |
b6fcfa59 | 87 | if (version_id > vmsd->version_id) { |
cde7ee59 JD |
88 | error_report("%s: incoming version_id %d is too new " |
89 | "for local version_id %d", | |
90 | vmsd->name, version_id, vmsd->version_id); | |
a5df2a02 | 91 | trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL); |
b6fcfa59 EH |
92 | return -EINVAL; |
93 | } | |
b6fcfa59 | 94 | if (version_id < vmsd->minimum_version_id) { |
767adce2 PM |
95 | if (vmsd->load_state_old && |
96 | version_id >= vmsd->minimum_version_id_old) { | |
a5df2a02 DDAG |
97 | ret = vmsd->load_state_old(f, opaque, version_id); |
98 | trace_vmstate_load_state_end(vmsd->name, "old path", ret); | |
99 | return ret; | |
767adce2 | 100 | } |
cde7ee59 JD |
101 | error_report("%s: incoming version_id %d is too old " |
102 | "for local minimum version_id %d", | |
103 | vmsd->name, version_id, vmsd->minimum_version_id); | |
a5df2a02 | 104 | trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL); |
767adce2 | 105 | return -EINVAL; |
b6fcfa59 EH |
106 | } |
107 | if (vmsd->pre_load) { | |
108 | int ret = vmsd->pre_load(opaque); | |
109 | if (ret) { | |
110 | return ret; | |
111 | } | |
112 | } | |
113 | while (field->name) { | |
a5df2a02 | 114 | trace_vmstate_load_state_field(vmsd->name, field->name); |
b6fcfa59 EH |
115 | if ((field->field_exists && |
116 | field->field_exists(opaque, version_id)) || | |
117 | (!field->field_exists && | |
118 | field->version_id <= version_id)) { | |
f32935ea | 119 | void *base_addr = vmstate_base_addr(opaque, field, true); |
35fc1f71 MT |
120 | int i, n_elems = vmstate_n_elems(opaque, field); |
121 | int size = vmstate_size(opaque, field); | |
122 | ||
b6fcfa59 EH |
123 | for (i = 0; i < n_elems; i++) { |
124 | void *addr = base_addr + size * i; | |
125 | ||
126 | if (field->flags & VMS_ARRAY_OF_POINTER) { | |
127 | addr = *(void **)addr; | |
128 | } | |
129 | if (field->flags & VMS_STRUCT) { | |
130 | ret = vmstate_load_state(f, field->vmsd, addr, | |
131 | field->vmsd->version_id); | |
132 | } else { | |
2c21ee76 | 133 | ret = field->info->get(f, addr, size, field); |
b6fcfa59 | 134 | } |
13cde508 JQ |
135 | if (ret >= 0) { |
136 | ret = qemu_file_get_error(f); | |
137 | } | |
b6fcfa59 | 138 | if (ret < 0) { |
13cde508 | 139 | qemu_file_set_error(f, ret); |
a1771070 DDAG |
140 | error_report("Failed to load %s:%s", vmsd->name, |
141 | field->name); | |
9013dca5 | 142 | trace_vmstate_load_field_error(field->name, ret); |
b6fcfa59 EH |
143 | return ret; |
144 | } | |
145 | } | |
5bf81c8d | 146 | } else if (field->flags & VMS_MUST_EXIST) { |
6a64b644 DDAG |
147 | error_report("Input validation failed: %s/%s", |
148 | vmsd->name, field->name); | |
5bf81c8d | 149 | return -1; |
b6fcfa59 EH |
150 | } |
151 | field++; | |
152 | } | |
153 | ret = vmstate_subsection_load(f, vmsd, opaque); | |
154 | if (ret != 0) { | |
155 | return ret; | |
156 | } | |
157 | if (vmsd->post_load) { | |
a5df2a02 | 158 | ret = vmsd->post_load(opaque, version_id); |
b6fcfa59 | 159 | } |
a5df2a02 DDAG |
160 | trace_vmstate_load_state_end(vmsd->name, "end", ret); |
161 | return ret; | |
b6fcfa59 EH |
162 | } |
163 | ||
8118f095 AG |
164 | static int vmfield_name_num(VMStateField *start, VMStateField *search) |
165 | { | |
166 | VMStateField *field; | |
167 | int found = 0; | |
168 | ||
169 | for (field = start; field->name; field++) { | |
170 | if (!strcmp(field->name, search->name)) { | |
171 | if (field == search) { | |
172 | return found; | |
173 | } | |
174 | found++; | |
175 | } | |
176 | } | |
177 | ||
178 | return -1; | |
179 | } | |
180 | ||
181 | static bool vmfield_name_is_unique(VMStateField *start, VMStateField *search) | |
182 | { | |
183 | VMStateField *field; | |
184 | int found = 0; | |
185 | ||
186 | for (field = start; field->name; field++) { | |
187 | if (!strcmp(field->name, search->name)) { | |
188 | found++; | |
189 | /* name found more than once, so it's not unique */ | |
190 | if (found > 1) { | |
191 | return false; | |
192 | } | |
193 | } | |
194 | } | |
195 | ||
196 | return true; | |
197 | } | |
198 | ||
199 | static const char *vmfield_get_type_name(VMStateField *field) | |
200 | { | |
201 | const char *type = "unknown"; | |
202 | ||
203 | if (field->flags & VMS_STRUCT) { | |
204 | type = "struct"; | |
205 | } else if (field->info->name) { | |
206 | type = field->info->name; | |
207 | } | |
208 | ||
209 | return type; | |
210 | } | |
211 | ||
212 | static bool vmsd_can_compress(VMStateField *field) | |
213 | { | |
214 | if (field->field_exists) { | |
215 | /* Dynamically existing fields mess up compression */ | |
216 | return false; | |
217 | } | |
218 | ||
219 | if (field->flags & VMS_STRUCT) { | |
220 | VMStateField *sfield = field->vmsd->fields; | |
221 | while (sfield->name) { | |
222 | if (!vmsd_can_compress(sfield)) { | |
223 | /* Child elements can't compress, so can't we */ | |
224 | return false; | |
225 | } | |
226 | sfield++; | |
227 | } | |
228 | ||
229 | if (field->vmsd->subsections) { | |
230 | /* Subsections may come and go, better don't compress */ | |
231 | return false; | |
232 | } | |
233 | } | |
234 | ||
235 | return true; | |
236 | } | |
237 | ||
238 | static void vmsd_desc_field_start(const VMStateDescription *vmsd, QJSON *vmdesc, | |
239 | VMStateField *field, int i, int max) | |
240 | { | |
241 | char *name, *old_name; | |
242 | bool is_array = max > 1; | |
243 | bool can_compress = vmsd_can_compress(field); | |
244 | ||
245 | if (!vmdesc) { | |
246 | return; | |
247 | } | |
248 | ||
249 | name = g_strdup(field->name); | |
250 | ||
251 | /* Field name is not unique, need to make it unique */ | |
252 | if (!vmfield_name_is_unique(vmsd->fields, field)) { | |
253 | int num = vmfield_name_num(vmsd->fields, field); | |
254 | old_name = name; | |
255 | name = g_strdup_printf("%s[%d]", name, num); | |
256 | g_free(old_name); | |
257 | } | |
258 | ||
259 | json_start_object(vmdesc, NULL); | |
260 | json_prop_str(vmdesc, "name", name); | |
261 | if (is_array) { | |
262 | if (can_compress) { | |
263 | json_prop_int(vmdesc, "array_len", max); | |
264 | } else { | |
265 | json_prop_int(vmdesc, "index", i); | |
266 | } | |
267 | } | |
268 | json_prop_str(vmdesc, "type", vmfield_get_type_name(field)); | |
269 | ||
270 | if (field->flags & VMS_STRUCT) { | |
271 | json_start_object(vmdesc, "struct"); | |
272 | } | |
273 | ||
274 | g_free(name); | |
275 | } | |
276 | ||
277 | static void vmsd_desc_field_end(const VMStateDescription *vmsd, QJSON *vmdesc, | |
278 | VMStateField *field, size_t size, int i) | |
279 | { | |
280 | if (!vmdesc) { | |
281 | return; | |
282 | } | |
283 | ||
284 | if (field->flags & VMS_STRUCT) { | |
285 | /* We printed a struct in between, close its child object */ | |
286 | json_end_object(vmdesc); | |
287 | } | |
288 | ||
289 | json_prop_int(vmdesc, "size", size); | |
290 | json_end_object(vmdesc); | |
291 | } | |
292 | ||
df896152 JQ |
293 | |
294 | bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque) | |
295 | { | |
296 | if (vmsd->needed && !vmsd->needed(opaque)) { | |
297 | /* optional section not needed */ | |
298 | return false; | |
299 | } | |
300 | return true; | |
301 | } | |
302 | ||
303 | ||
b6fcfa59 | 304 | void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, |
8118f095 | 305 | void *opaque, QJSON *vmdesc) |
b6fcfa59 EH |
306 | { |
307 | VMStateField *field = vmsd->fields; | |
308 | ||
309 | if (vmsd->pre_save) { | |
310 | vmsd->pre_save(opaque); | |
311 | } | |
8118f095 AG |
312 | |
313 | if (vmdesc) { | |
314 | json_prop_str(vmdesc, "vmsd_name", vmsd->name); | |
315 | json_prop_int(vmdesc, "version", vmsd->version_id); | |
316 | json_start_array(vmdesc, "fields"); | |
317 | } | |
318 | ||
b6fcfa59 EH |
319 | while (field->name) { |
320 | if (!field->field_exists || | |
321 | field->field_exists(opaque, vmsd->version_id)) { | |
f32935ea | 322 | void *base_addr = vmstate_base_addr(opaque, field, false); |
35fc1f71 MT |
323 | int i, n_elems = vmstate_n_elems(opaque, field); |
324 | int size = vmstate_size(opaque, field); | |
8118f095 AG |
325 | int64_t old_offset, written_bytes; |
326 | QJSON *vmdesc_loop = vmdesc; | |
35fc1f71 | 327 | |
b6fcfa59 EH |
328 | for (i = 0; i < n_elems; i++) { |
329 | void *addr = base_addr + size * i; | |
330 | ||
8118f095 AG |
331 | vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems); |
332 | old_offset = qemu_ftell_fast(f); | |
333 | ||
b6fcfa59 EH |
334 | if (field->flags & VMS_ARRAY_OF_POINTER) { |
335 | addr = *(void **)addr; | |
336 | } | |
337 | if (field->flags & VMS_STRUCT) { | |
8118f095 | 338 | vmstate_save_state(f, field->vmsd, addr, vmdesc_loop); |
b6fcfa59 | 339 | } else { |
2c21ee76 | 340 | field->info->put(f, addr, size, field, vmdesc_loop); |
b6fcfa59 | 341 | } |
8118f095 AG |
342 | |
343 | written_bytes = qemu_ftell_fast(f) - old_offset; | |
344 | vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i); | |
345 | ||
346 | /* Compressed arrays only care about the first element */ | |
347 | if (vmdesc_loop && vmsd_can_compress(field)) { | |
348 | vmdesc_loop = NULL; | |
349 | } | |
b6fcfa59 | 350 | } |
5bf81c8d MT |
351 | } else { |
352 | if (field->flags & VMS_MUST_EXIST) { | |
6a64b644 | 353 | error_report("Output state validation failed: %s/%s", |
5bf81c8d MT |
354 | vmsd->name, field->name); |
355 | assert(!(field->flags & VMS_MUST_EXIST)); | |
356 | } | |
b6fcfa59 EH |
357 | } |
358 | field++; | |
359 | } | |
8118f095 AG |
360 | |
361 | if (vmdesc) { | |
362 | json_end_array(vmdesc); | |
363 | } | |
364 | ||
365 | vmstate_subsection_save(f, vmsd, opaque, vmdesc); | |
b6fcfa59 EH |
366 | } |
367 | ||
368 | static const VMStateDescription * | |
5cd8cada | 369 | vmstate_get_subsection(const VMStateDescription **sub, char *idstr) |
b6fcfa59 | 370 | { |
5cd8cada JQ |
371 | while (sub && *sub && (*sub)->needed) { |
372 | if (strcmp(idstr, (*sub)->name) == 0) { | |
373 | return *sub; | |
b6fcfa59 EH |
374 | } |
375 | sub++; | |
376 | } | |
377 | return NULL; | |
378 | } | |
379 | ||
380 | static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, | |
381 | void *opaque) | |
382 | { | |
a5df2a02 DDAG |
383 | trace_vmstate_subsection_load(vmsd->name); |
384 | ||
b6fcfa59 | 385 | while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) { |
7c1e52ba | 386 | char idstr[256], *idstr_ret; |
b6fcfa59 EH |
387 | int ret; |
388 | uint8_t version_id, len, size; | |
389 | const VMStateDescription *sub_vmsd; | |
390 | ||
391 | len = qemu_peek_byte(f, 1); | |
392 | if (len < strlen(vmsd->name) + 1) { | |
393 | /* subsection name has be be "section_name/a" */ | |
023ad1a6 | 394 | trace_vmstate_subsection_load_bad(vmsd->name, "(short)", ""); |
b6fcfa59 EH |
395 | return 0; |
396 | } | |
7c1e52ba | 397 | size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2); |
b6fcfa59 | 398 | if (size != len) { |
023ad1a6 | 399 | trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)", ""); |
b6fcfa59 EH |
400 | return 0; |
401 | } | |
7c1e52ba | 402 | memcpy(idstr, idstr_ret, size); |
b6fcfa59 EH |
403 | idstr[size] = 0; |
404 | ||
405 | if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) { | |
023ad1a6 DDAG |
406 | trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(prefix)"); |
407 | /* it doesn't have a valid subsection name */ | |
b6fcfa59 EH |
408 | return 0; |
409 | } | |
410 | sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr); | |
411 | if (sub_vmsd == NULL) { | |
023ad1a6 | 412 | trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(lookup)"); |
b6fcfa59 EH |
413 | return -ENOENT; |
414 | } | |
415 | qemu_file_skip(f, 1); /* subsection */ | |
416 | qemu_file_skip(f, 1); /* len */ | |
417 | qemu_file_skip(f, len); /* idstr */ | |
418 | version_id = qemu_get_be32(f); | |
419 | ||
420 | ret = vmstate_load_state(f, sub_vmsd, opaque, version_id); | |
421 | if (ret) { | |
023ad1a6 | 422 | trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)"); |
b6fcfa59 EH |
423 | return ret; |
424 | } | |
425 | } | |
a5df2a02 DDAG |
426 | |
427 | trace_vmstate_subsection_load_good(vmsd->name); | |
b6fcfa59 EH |
428 | return 0; |
429 | } | |
430 | ||
431 | static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, | |
8118f095 | 432 | void *opaque, QJSON *vmdesc) |
b6fcfa59 | 433 | { |
5cd8cada | 434 | const VMStateDescription **sub = vmsd->subsections; |
8118f095 | 435 | bool subsection_found = false; |
b6fcfa59 | 436 | |
5cd8cada JQ |
437 | while (sub && *sub && (*sub)->needed) { |
438 | if ((*sub)->needed(opaque)) { | |
439 | const VMStateDescription *vmsd = *sub; | |
b6fcfa59 EH |
440 | uint8_t len; |
441 | ||
8118f095 AG |
442 | if (vmdesc) { |
443 | /* Only create subsection array when we have any */ | |
444 | if (!subsection_found) { | |
445 | json_start_array(vmdesc, "subsections"); | |
446 | subsection_found = true; | |
447 | } | |
448 | ||
449 | json_start_object(vmdesc, NULL); | |
450 | } | |
451 | ||
b6fcfa59 EH |
452 | qemu_put_byte(f, QEMU_VM_SUBSECTION); |
453 | len = strlen(vmsd->name); | |
454 | qemu_put_byte(f, len); | |
455 | qemu_put_buffer(f, (uint8_t *)vmsd->name, len); | |
456 | qemu_put_be32(f, vmsd->version_id); | |
8118f095 AG |
457 | vmstate_save_state(f, vmsd, opaque, vmdesc); |
458 | ||
459 | if (vmdesc) { | |
460 | json_end_object(vmdesc); | |
461 | } | |
b6fcfa59 EH |
462 | } |
463 | sub++; | |
464 | } | |
8118f095 AG |
465 | |
466 | if (vmdesc && subsection_found) { | |
467 | json_end_array(vmdesc); | |
468 | } | |
b6fcfa59 EH |
469 | } |
470 | ||
471 | /* bool */ | |
472 | ||
2c21ee76 | 473 | static int get_bool(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 EH |
474 | { |
475 | bool *v = pv; | |
476 | *v = qemu_get_byte(f); | |
477 | return 0; | |
478 | } | |
479 | ||
2c21ee76 JD |
480 | static int put_bool(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
481 | QJSON *vmdesc) | |
b6fcfa59 EH |
482 | { |
483 | bool *v = pv; | |
484 | qemu_put_byte(f, *v); | |
2c21ee76 | 485 | return 0; |
b6fcfa59 EH |
486 | } |
487 | ||
488 | const VMStateInfo vmstate_info_bool = { | |
489 | .name = "bool", | |
490 | .get = get_bool, | |
491 | .put = put_bool, | |
492 | }; | |
493 | ||
494 | /* 8 bit int */ | |
495 | ||
2c21ee76 | 496 | static int get_int8(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 EH |
497 | { |
498 | int8_t *v = pv; | |
499 | qemu_get_s8s(f, v); | |
500 | return 0; | |
501 | } | |
502 | ||
2c21ee76 JD |
503 | static int put_int8(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
504 | QJSON *vmdesc) | |
b6fcfa59 EH |
505 | { |
506 | int8_t *v = pv; | |
507 | qemu_put_s8s(f, v); | |
2c21ee76 | 508 | return 0; |
b6fcfa59 EH |
509 | } |
510 | ||
511 | const VMStateInfo vmstate_info_int8 = { | |
512 | .name = "int8", | |
513 | .get = get_int8, | |
514 | .put = put_int8, | |
515 | }; | |
516 | ||
517 | /* 16 bit int */ | |
518 | ||
2c21ee76 | 519 | static int get_int16(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 EH |
520 | { |
521 | int16_t *v = pv; | |
522 | qemu_get_sbe16s(f, v); | |
523 | return 0; | |
524 | } | |
525 | ||
2c21ee76 JD |
526 | static int put_int16(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
527 | QJSON *vmdesc) | |
b6fcfa59 EH |
528 | { |
529 | int16_t *v = pv; | |
530 | qemu_put_sbe16s(f, v); | |
2c21ee76 | 531 | return 0; |
b6fcfa59 EH |
532 | } |
533 | ||
534 | const VMStateInfo vmstate_info_int16 = { | |
535 | .name = "int16", | |
536 | .get = get_int16, | |
537 | .put = put_int16, | |
538 | }; | |
539 | ||
540 | /* 32 bit int */ | |
541 | ||
2c21ee76 | 542 | static int get_int32(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 EH |
543 | { |
544 | int32_t *v = pv; | |
545 | qemu_get_sbe32s(f, v); | |
546 | return 0; | |
547 | } | |
548 | ||
2c21ee76 JD |
549 | static int put_int32(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
550 | QJSON *vmdesc) | |
b6fcfa59 EH |
551 | { |
552 | int32_t *v = pv; | |
553 | qemu_put_sbe32s(f, v); | |
2c21ee76 | 554 | return 0; |
b6fcfa59 EH |
555 | } |
556 | ||
557 | const VMStateInfo vmstate_info_int32 = { | |
558 | .name = "int32", | |
559 | .get = get_int32, | |
560 | .put = put_int32, | |
561 | }; | |
562 | ||
563 | /* 32 bit int. See that the received value is the same than the one | |
564 | in the field */ | |
565 | ||
2c21ee76 JD |
566 | static int get_int32_equal(QEMUFile *f, void *pv, size_t size, |
567 | VMStateField *field) | |
b6fcfa59 EH |
568 | { |
569 | int32_t *v = pv; | |
570 | int32_t v2; | |
571 | qemu_get_sbe32s(f, &v2); | |
572 | ||
573 | if (*v == v2) { | |
574 | return 0; | |
575 | } | |
49228e17 | 576 | error_report("%" PRIx32 " != %" PRIx32, *v, v2); |
b6fcfa59 EH |
577 | return -EINVAL; |
578 | } | |
579 | ||
580 | const VMStateInfo vmstate_info_int32_equal = { | |
581 | .name = "int32 equal", | |
582 | .get = get_int32_equal, | |
583 | .put = put_int32, | |
584 | }; | |
585 | ||
d2ef4b61 MT |
586 | /* 32 bit int. Check that the received value is non-negative |
587 | * and less than or equal to the one in the field. | |
588 | */ | |
b6fcfa59 | 589 | |
2c21ee76 | 590 | static int get_int32_le(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 | 591 | { |
24a370ef DDAG |
592 | int32_t *cur = pv; |
593 | int32_t loaded; | |
594 | qemu_get_sbe32s(f, &loaded); | |
b6fcfa59 | 595 | |
d2ef4b61 | 596 | if (loaded >= 0 && loaded <= *cur) { |
24a370ef | 597 | *cur = loaded; |
b6fcfa59 EH |
598 | return 0; |
599 | } | |
49228e17 DDAG |
600 | error_report("Invalid value %" PRId32 |
601 | " expecting positive value <= %" PRId32, | |
602 | loaded, *cur); | |
b6fcfa59 EH |
603 | return -EINVAL; |
604 | } | |
605 | ||
606 | const VMStateInfo vmstate_info_int32_le = { | |
24a370ef | 607 | .name = "int32 le", |
b6fcfa59 EH |
608 | .get = get_int32_le, |
609 | .put = put_int32, | |
610 | }; | |
611 | ||
612 | /* 64 bit int */ | |
613 | ||
2c21ee76 | 614 | static int get_int64(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 EH |
615 | { |
616 | int64_t *v = pv; | |
617 | qemu_get_sbe64s(f, v); | |
618 | return 0; | |
619 | } | |
620 | ||
2c21ee76 JD |
621 | static int put_int64(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
622 | QJSON *vmdesc) | |
b6fcfa59 EH |
623 | { |
624 | int64_t *v = pv; | |
625 | qemu_put_sbe64s(f, v); | |
2c21ee76 | 626 | return 0; |
b6fcfa59 EH |
627 | } |
628 | ||
629 | const VMStateInfo vmstate_info_int64 = { | |
630 | .name = "int64", | |
631 | .get = get_int64, | |
632 | .put = put_int64, | |
633 | }; | |
634 | ||
635 | /* 8 bit unsigned int */ | |
636 | ||
2c21ee76 | 637 | static int get_uint8(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 EH |
638 | { |
639 | uint8_t *v = pv; | |
640 | qemu_get_8s(f, v); | |
641 | return 0; | |
642 | } | |
643 | ||
2c21ee76 JD |
644 | static int put_uint8(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
645 | QJSON *vmdesc) | |
b6fcfa59 EH |
646 | { |
647 | uint8_t *v = pv; | |
648 | qemu_put_8s(f, v); | |
2c21ee76 | 649 | return 0; |
b6fcfa59 EH |
650 | } |
651 | ||
652 | const VMStateInfo vmstate_info_uint8 = { | |
653 | .name = "uint8", | |
654 | .get = get_uint8, | |
655 | .put = put_uint8, | |
656 | }; | |
657 | ||
658 | /* 16 bit unsigned int */ | |
659 | ||
2c21ee76 | 660 | static int get_uint16(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 EH |
661 | { |
662 | uint16_t *v = pv; | |
663 | qemu_get_be16s(f, v); | |
664 | return 0; | |
665 | } | |
666 | ||
2c21ee76 JD |
667 | static int put_uint16(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
668 | QJSON *vmdesc) | |
b6fcfa59 EH |
669 | { |
670 | uint16_t *v = pv; | |
671 | qemu_put_be16s(f, v); | |
2c21ee76 | 672 | return 0; |
b6fcfa59 EH |
673 | } |
674 | ||
675 | const VMStateInfo vmstate_info_uint16 = { | |
676 | .name = "uint16", | |
677 | .get = get_uint16, | |
678 | .put = put_uint16, | |
679 | }; | |
680 | ||
681 | /* 32 bit unsigned int */ | |
682 | ||
2c21ee76 | 683 | static int get_uint32(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 EH |
684 | { |
685 | uint32_t *v = pv; | |
686 | qemu_get_be32s(f, v); | |
687 | return 0; | |
688 | } | |
689 | ||
2c21ee76 JD |
690 | static int put_uint32(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
691 | QJSON *vmdesc) | |
b6fcfa59 EH |
692 | { |
693 | uint32_t *v = pv; | |
694 | qemu_put_be32s(f, v); | |
2c21ee76 | 695 | return 0; |
b6fcfa59 EH |
696 | } |
697 | ||
698 | const VMStateInfo vmstate_info_uint32 = { | |
699 | .name = "uint32", | |
700 | .get = get_uint32, | |
701 | .put = put_uint32, | |
702 | }; | |
703 | ||
704 | /* 32 bit uint. See that the received value is the same than the one | |
705 | in the field */ | |
706 | ||
2c21ee76 JD |
707 | static int get_uint32_equal(QEMUFile *f, void *pv, size_t size, |
708 | VMStateField *field) | |
b6fcfa59 EH |
709 | { |
710 | uint32_t *v = pv; | |
711 | uint32_t v2; | |
712 | qemu_get_be32s(f, &v2); | |
713 | ||
714 | if (*v == v2) { | |
715 | return 0; | |
716 | } | |
49228e17 | 717 | error_report("%" PRIx32 " != %" PRIx32, *v, v2); |
b6fcfa59 EH |
718 | return -EINVAL; |
719 | } | |
720 | ||
721 | const VMStateInfo vmstate_info_uint32_equal = { | |
722 | .name = "uint32 equal", | |
723 | .get = get_uint32_equal, | |
724 | .put = put_uint32, | |
725 | }; | |
726 | ||
727 | /* 64 bit unsigned int */ | |
728 | ||
2c21ee76 | 729 | static int get_uint64(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 EH |
730 | { |
731 | uint64_t *v = pv; | |
732 | qemu_get_be64s(f, v); | |
733 | return 0; | |
734 | } | |
735 | ||
2c21ee76 JD |
736 | static int put_uint64(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
737 | QJSON *vmdesc) | |
b6fcfa59 EH |
738 | { |
739 | uint64_t *v = pv; | |
740 | qemu_put_be64s(f, v); | |
2c21ee76 | 741 | return 0; |
b6fcfa59 EH |
742 | } |
743 | ||
744 | const VMStateInfo vmstate_info_uint64 = { | |
745 | .name = "uint64", | |
746 | .get = get_uint64, | |
747 | .put = put_uint64, | |
748 | }; | |
749 | ||
750 | /* 64 bit unsigned int. See that the received value is the same than the one | |
751 | in the field */ | |
752 | ||
2c21ee76 JD |
753 | static int get_uint64_equal(QEMUFile *f, void *pv, size_t size, |
754 | VMStateField *field) | |
b6fcfa59 EH |
755 | { |
756 | uint64_t *v = pv; | |
757 | uint64_t v2; | |
758 | qemu_get_be64s(f, &v2); | |
759 | ||
760 | if (*v == v2) { | |
761 | return 0; | |
762 | } | |
49228e17 | 763 | error_report("%" PRIx64 " != %" PRIx64, *v, v2); |
b6fcfa59 EH |
764 | return -EINVAL; |
765 | } | |
766 | ||
767 | const VMStateInfo vmstate_info_uint64_equal = { | |
768 | .name = "int64 equal", | |
769 | .get = get_uint64_equal, | |
770 | .put = put_uint64, | |
771 | }; | |
772 | ||
773 | /* 8 bit int. See that the received value is the same than the one | |
774 | in the field */ | |
775 | ||
2c21ee76 JD |
776 | static int get_uint8_equal(QEMUFile *f, void *pv, size_t size, |
777 | VMStateField *field) | |
b6fcfa59 EH |
778 | { |
779 | uint8_t *v = pv; | |
780 | uint8_t v2; | |
781 | qemu_get_8s(f, &v2); | |
782 | ||
783 | if (*v == v2) { | |
784 | return 0; | |
785 | } | |
49228e17 | 786 | error_report("%x != %x", *v, v2); |
b6fcfa59 EH |
787 | return -EINVAL; |
788 | } | |
789 | ||
790 | const VMStateInfo vmstate_info_uint8_equal = { | |
791 | .name = "uint8 equal", | |
792 | .get = get_uint8_equal, | |
793 | .put = put_uint8, | |
794 | }; | |
795 | ||
796 | /* 16 bit unsigned int int. See that the received value is the same than the one | |
797 | in the field */ | |
798 | ||
2c21ee76 JD |
799 | static int get_uint16_equal(QEMUFile *f, void *pv, size_t size, |
800 | VMStateField *field) | |
b6fcfa59 EH |
801 | { |
802 | uint16_t *v = pv; | |
803 | uint16_t v2; | |
804 | qemu_get_be16s(f, &v2); | |
805 | ||
806 | if (*v == v2) { | |
807 | return 0; | |
808 | } | |
49228e17 | 809 | error_report("%x != %x", *v, v2); |
b6fcfa59 EH |
810 | return -EINVAL; |
811 | } | |
812 | ||
813 | const VMStateInfo vmstate_info_uint16_equal = { | |
814 | .name = "uint16 equal", | |
815 | .get = get_uint16_equal, | |
816 | .put = put_uint16, | |
817 | }; | |
818 | ||
819 | /* floating point */ | |
820 | ||
2c21ee76 JD |
821 | static int get_float64(QEMUFile *f, void *pv, size_t size, |
822 | VMStateField *field) | |
b6fcfa59 EH |
823 | { |
824 | float64 *v = pv; | |
825 | ||
826 | *v = make_float64(qemu_get_be64(f)); | |
827 | return 0; | |
828 | } | |
829 | ||
2c21ee76 JD |
830 | static int put_float64(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
831 | QJSON *vmdesc) | |
b6fcfa59 EH |
832 | { |
833 | uint64_t *v = pv; | |
834 | ||
835 | qemu_put_be64(f, float64_val(*v)); | |
2c21ee76 | 836 | return 0; |
b6fcfa59 EH |
837 | } |
838 | ||
839 | const VMStateInfo vmstate_info_float64 = { | |
840 | .name = "float64", | |
841 | .get = get_float64, | |
842 | .put = put_float64, | |
843 | }; | |
844 | ||
55174749 JQ |
845 | /* CPU_DoubleU type */ |
846 | ||
2c21ee76 JD |
847 | static int get_cpudouble(QEMUFile *f, void *pv, size_t size, |
848 | VMStateField *field) | |
55174749 JQ |
849 | { |
850 | CPU_DoubleU *v = pv; | |
851 | qemu_get_be32s(f, &v->l.upper); | |
852 | qemu_get_be32s(f, &v->l.lower); | |
853 | return 0; | |
854 | } | |
855 | ||
2c21ee76 JD |
856 | static int put_cpudouble(QEMUFile *f, void *pv, size_t size, |
857 | VMStateField *field, QJSON *vmdesc) | |
55174749 JQ |
858 | { |
859 | CPU_DoubleU *v = pv; | |
860 | qemu_put_be32s(f, &v->l.upper); | |
861 | qemu_put_be32s(f, &v->l.lower); | |
2c21ee76 | 862 | return 0; |
55174749 JQ |
863 | } |
864 | ||
865 | const VMStateInfo vmstate_info_cpudouble = { | |
866 | .name = "CPU_Double_U", | |
867 | .get = get_cpudouble, | |
868 | .put = put_cpudouble, | |
869 | }; | |
870 | ||
b6fcfa59 EH |
871 | /* uint8_t buffers */ |
872 | ||
2c21ee76 JD |
873 | static int get_buffer(QEMUFile *f, void *pv, size_t size, |
874 | VMStateField *field) | |
b6fcfa59 EH |
875 | { |
876 | uint8_t *v = pv; | |
877 | qemu_get_buffer(f, v, size); | |
878 | return 0; | |
879 | } | |
880 | ||
2c21ee76 JD |
881 | static int put_buffer(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
882 | QJSON *vmdesc) | |
b6fcfa59 EH |
883 | { |
884 | uint8_t *v = pv; | |
885 | qemu_put_buffer(f, v, size); | |
2c21ee76 | 886 | return 0; |
b6fcfa59 EH |
887 | } |
888 | ||
889 | const VMStateInfo vmstate_info_buffer = { | |
890 | .name = "buffer", | |
891 | .get = get_buffer, | |
892 | .put = put_buffer, | |
893 | }; | |
894 | ||
895 | /* unused buffers: space that was used for some fields that are | |
896 | not useful anymore */ | |
897 | ||
2c21ee76 JD |
898 | static int get_unused_buffer(QEMUFile *f, void *pv, size_t size, |
899 | VMStateField *field) | |
b6fcfa59 EH |
900 | { |
901 | uint8_t buf[1024]; | |
902 | int block_len; | |
903 | ||
904 | while (size > 0) { | |
905 | block_len = MIN(sizeof(buf), size); | |
906 | size -= block_len; | |
907 | qemu_get_buffer(f, buf, block_len); | |
908 | } | |
909 | return 0; | |
910 | } | |
911 | ||
2c21ee76 JD |
912 | static int put_unused_buffer(QEMUFile *f, void *pv, size_t size, |
913 | VMStateField *field, QJSON *vmdesc) | |
b6fcfa59 EH |
914 | { |
915 | static const uint8_t buf[1024]; | |
916 | int block_len; | |
917 | ||
918 | while (size > 0) { | |
919 | block_len = MIN(sizeof(buf), size); | |
920 | size -= block_len; | |
921 | qemu_put_buffer(f, buf, block_len); | |
922 | } | |
2c21ee76 JD |
923 | |
924 | return 0; | |
b6fcfa59 EH |
925 | } |
926 | ||
927 | const VMStateInfo vmstate_info_unused_buffer = { | |
928 | .name = "unused_buffer", | |
929 | .get = get_unused_buffer, | |
930 | .put = put_unused_buffer, | |
931 | }; | |
932 | ||
933 | /* bitmaps (as defined by bitmap.h). Note that size here is the size | |
934 | * of the bitmap in bits. The on-the-wire format of a bitmap is 64 | |
935 | * bit words with the bits in big endian order. The in-memory format | |
936 | * is an array of 'unsigned long', which may be either 32 or 64 bits. | |
937 | */ | |
938 | /* This is the number of 64 bit words sent over the wire */ | |
939 | #define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64) | |
2c21ee76 | 940 | static int get_bitmap(QEMUFile *f, void *pv, size_t size, VMStateField *field) |
b6fcfa59 EH |
941 | { |
942 | unsigned long *bmp = pv; | |
943 | int i, idx = 0; | |
944 | for (i = 0; i < BITS_TO_U64S(size); i++) { | |
945 | uint64_t w = qemu_get_be64(f); | |
946 | bmp[idx++] = w; | |
947 | if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { | |
948 | bmp[idx++] = w >> 32; | |
949 | } | |
950 | } | |
951 | return 0; | |
952 | } | |
953 | ||
2c21ee76 JD |
954 | static int put_bitmap(QEMUFile *f, void *pv, size_t size, VMStateField *field, |
955 | QJSON *vmdesc) | |
b6fcfa59 EH |
956 | { |
957 | unsigned long *bmp = pv; | |
958 | int i, idx = 0; | |
959 | for (i = 0; i < BITS_TO_U64S(size); i++) { | |
960 | uint64_t w = bmp[idx++]; | |
961 | if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) { | |
962 | w |= ((uint64_t)bmp[idx++]) << 32; | |
963 | } | |
964 | qemu_put_be64(f, w); | |
965 | } | |
2c21ee76 JD |
966 | |
967 | return 0; | |
b6fcfa59 EH |
968 | } |
969 | ||
970 | const VMStateInfo vmstate_info_bitmap = { | |
971 | .name = "bitmap", | |
972 | .get = get_bitmap, | |
973 | .put = put_bitmap, | |
974 | }; | |
94869d5c JD |
975 | |
976 | /* get for QTAILQ | |
977 | * meta data about the QTAILQ is encoded in a VMStateField structure | |
978 | */ | |
979 | static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size, | |
980 | VMStateField *field) | |
981 | { | |
982 | int ret = 0; | |
983 | const VMStateDescription *vmsd = field->vmsd; | |
984 | /* size of a QTAILQ element */ | |
985 | size_t size = field->size; | |
986 | /* offset of the QTAILQ entry in a QTAILQ element */ | |
987 | size_t entry_offset = field->start; | |
988 | int version_id = field->version_id; | |
989 | void *elm; | |
990 | ||
991 | trace_get_qtailq(vmsd->name, version_id); | |
992 | if (version_id > vmsd->version_id) { | |
993 | error_report("%s %s", vmsd->name, "too new"); | |
994 | trace_get_qtailq_end(vmsd->name, "too new", -EINVAL); | |
995 | ||
996 | return -EINVAL; | |
997 | } | |
998 | if (version_id < vmsd->minimum_version_id) { | |
999 | error_report("%s %s", vmsd->name, "too old"); | |
1000 | trace_get_qtailq_end(vmsd->name, "too old", -EINVAL); | |
1001 | return -EINVAL; | |
1002 | } | |
1003 | ||
1004 | while (qemu_get_byte(f)) { | |
1005 | elm = g_malloc(size); | |
1006 | ret = vmstate_load_state(f, vmsd, elm, version_id); | |
1007 | if (ret) { | |
1008 | return ret; | |
1009 | } | |
1010 | QTAILQ_RAW_INSERT_TAIL(pv, elm, entry_offset); | |
1011 | } | |
1012 | ||
1013 | trace_get_qtailq_end(vmsd->name, "end", ret); | |
1014 | return ret; | |
1015 | } | |
1016 | ||
1017 | /* put for QTAILQ */ | |
1018 | static int put_qtailq(QEMUFile *f, void *pv, size_t unused_size, | |
1019 | VMStateField *field, QJSON *vmdesc) | |
1020 | { | |
1021 | const VMStateDescription *vmsd = field->vmsd; | |
1022 | /* offset of the QTAILQ entry in a QTAILQ element*/ | |
1023 | size_t entry_offset = field->start; | |
1024 | void *elm; | |
1025 | ||
1026 | trace_put_qtailq(vmsd->name, vmsd->version_id); | |
1027 | ||
1028 | QTAILQ_RAW_FOREACH(elm, pv, entry_offset) { | |
1029 | qemu_put_byte(f, true); | |
1030 | vmstate_save_state(f, vmsd, elm, vmdesc); | |
1031 | } | |
1032 | qemu_put_byte(f, false); | |
1033 | ||
1034 | trace_put_qtailq_end(vmsd->name, "end"); | |
1035 | ||
1036 | return 0; | |
1037 | } | |
1038 | const VMStateInfo vmstate_info_qtailq = { | |
1039 | .name = "qtailq", | |
1040 | .get = get_qtailq, | |
1041 | .put = put_qtailq, | |
1042 | }; |