linux-user: allocate heap memory for execve arguments
[qemu/ar7.git] / migration / vmstate.c
blobff54531b44022f5491c6b1bfda3cb63bb0c4ff99
1 /*
2 * VMState interpreter
4 * Copyright (c) 2009-2017 Red Hat Inc
6 * Authors:
7 * Juan Quintela <quintela@redhat.com>
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.
13 #include "qemu/osdep.h"
14 #include "qemu-common.h"
15 #include "migration/migration.h"
16 #include "migration/qemu-file.h"
17 #include "migration/vmstate.h"
18 #include "qemu/bitops.h"
19 #include "qemu/error-report.h"
20 #include "trace.h"
21 #include "qjson.h"
23 static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
24 void *opaque, QJSON *vmdesc);
25 static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
26 void *opaque);
28 static int vmstate_n_elems(void *opaque, VMStateField *field)
30 int n_elems = 1;
32 if (field->flags & VMS_ARRAY) {
33 n_elems = field->num;
34 } else if (field->flags & VMS_VARRAY_INT32) {
35 n_elems = *(int32_t *)(opaque+field->num_offset);
36 } else if (field->flags & VMS_VARRAY_UINT32) {
37 n_elems = *(uint32_t *)(opaque+field->num_offset);
38 } else if (field->flags & VMS_VARRAY_UINT16) {
39 n_elems = *(uint16_t *)(opaque+field->num_offset);
40 } else if (field->flags & VMS_VARRAY_UINT8) {
41 n_elems = *(uint8_t *)(opaque+field->num_offset);
44 if (field->flags & VMS_MULTIPLY_ELEMENTS) {
45 n_elems *= field->num;
48 trace_vmstate_n_elems(field->name, n_elems);
49 return n_elems;
52 static int vmstate_size(void *opaque, VMStateField *field)
54 int size = field->size;
56 if (field->flags & VMS_VBUFFER) {
57 size = *(int32_t *)(opaque+field->size_offset);
58 if (field->flags & VMS_MULTIPLY) {
59 size *= field->size;
63 return size;
66 static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque)
68 if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) {
69 gsize size = vmstate_size(opaque, field);
70 size *= vmstate_n_elems(opaque, field);
71 if (size) {
72 *(void **)ptr = g_malloc(size);
77 int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
78 void *opaque, int version_id)
80 VMStateField *field = vmsd->fields;
81 int ret = 0;
83 trace_vmstate_load_state(vmsd->name, version_id);
84 if (version_id > vmsd->version_id) {
85 error_report("%s: incoming version_id %d is too new "
86 "for local version_id %d",
87 vmsd->name, version_id, vmsd->version_id);
88 trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
89 return -EINVAL;
91 if (version_id < vmsd->minimum_version_id) {
92 if (vmsd->load_state_old &&
93 version_id >= vmsd->minimum_version_id_old) {
94 ret = vmsd->load_state_old(f, opaque, version_id);
95 trace_vmstate_load_state_end(vmsd->name, "old path", ret);
96 return ret;
98 error_report("%s: incoming version_id %d is too old "
99 "for local minimum version_id %d",
100 vmsd->name, version_id, vmsd->minimum_version_id);
101 trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
102 return -EINVAL;
104 if (vmsd->pre_load) {
105 int ret = vmsd->pre_load(opaque);
106 if (ret) {
107 return ret;
110 while (field->name) {
111 trace_vmstate_load_state_field(vmsd->name, field->name);
112 if ((field->field_exists &&
113 field->field_exists(opaque, version_id)) ||
114 (!field->field_exists &&
115 field->version_id <= version_id)) {
116 void *first_elem = opaque + field->offset;
117 int i, n_elems = vmstate_n_elems(opaque, field);
118 int size = vmstate_size(opaque, field);
120 vmstate_handle_alloc(first_elem, field, opaque);
121 if (field->flags & VMS_POINTER) {
122 first_elem = *(void **)first_elem;
123 assert(first_elem || !n_elems || !size);
125 for (i = 0; i < n_elems; i++) {
126 void *curr_elem = first_elem + size * i;
128 if (field->flags & VMS_ARRAY_OF_POINTER) {
129 curr_elem = *(void **)curr_elem;
131 if (!curr_elem && size) {
132 /* if null pointer check placeholder and do not follow */
133 assert(field->flags & VMS_ARRAY_OF_POINTER);
134 ret = vmstate_info_nullptr.get(f, curr_elem, size, NULL);
135 } else if (field->flags & VMS_STRUCT) {
136 ret = vmstate_load_state(f, field->vmsd, curr_elem,
137 field->vmsd->version_id);
138 } else {
139 ret = field->info->get(f, curr_elem, size, field);
141 if (ret >= 0) {
142 ret = qemu_file_get_error(f);
144 if (ret < 0) {
145 qemu_file_set_error(f, ret);
146 error_report("Failed to load %s:%s", vmsd->name,
147 field->name);
148 trace_vmstate_load_field_error(field->name, ret);
149 return ret;
152 } else if (field->flags & VMS_MUST_EXIST) {
153 error_report("Input validation failed: %s/%s",
154 vmsd->name, field->name);
155 return -1;
157 field++;
159 ret = vmstate_subsection_load(f, vmsd, opaque);
160 if (ret != 0) {
161 return ret;
163 if (vmsd->post_load) {
164 ret = vmsd->post_load(opaque, version_id);
166 trace_vmstate_load_state_end(vmsd->name, "end", ret);
167 return ret;
170 static int vmfield_name_num(VMStateField *start, VMStateField *search)
172 VMStateField *field;
173 int found = 0;
175 for (field = start; field->name; field++) {
176 if (!strcmp(field->name, search->name)) {
177 if (field == search) {
178 return found;
180 found++;
184 return -1;
187 static bool vmfield_name_is_unique(VMStateField *start, VMStateField *search)
189 VMStateField *field;
190 int found = 0;
192 for (field = start; field->name; field++) {
193 if (!strcmp(field->name, search->name)) {
194 found++;
195 /* name found more than once, so it's not unique */
196 if (found > 1) {
197 return false;
202 return true;
205 static const char *vmfield_get_type_name(VMStateField *field)
207 const char *type = "unknown";
209 if (field->flags & VMS_STRUCT) {
210 type = "struct";
211 } else if (field->info->name) {
212 type = field->info->name;
215 return type;
218 static bool vmsd_can_compress(VMStateField *field)
220 if (field->field_exists) {
221 /* Dynamically existing fields mess up compression */
222 return false;
225 if (field->flags & VMS_STRUCT) {
226 VMStateField *sfield = field->vmsd->fields;
227 while (sfield->name) {
228 if (!vmsd_can_compress(sfield)) {
229 /* Child elements can't compress, so can't we */
230 return false;
232 sfield++;
235 if (field->vmsd->subsections) {
236 /* Subsections may come and go, better don't compress */
237 return false;
241 return true;
244 static void vmsd_desc_field_start(const VMStateDescription *vmsd, QJSON *vmdesc,
245 VMStateField *field, int i, int max)
247 char *name, *old_name;
248 bool is_array = max > 1;
249 bool can_compress = vmsd_can_compress(field);
251 if (!vmdesc) {
252 return;
255 name = g_strdup(field->name);
257 /* Field name is not unique, need to make it unique */
258 if (!vmfield_name_is_unique(vmsd->fields, field)) {
259 int num = vmfield_name_num(vmsd->fields, field);
260 old_name = name;
261 name = g_strdup_printf("%s[%d]", name, num);
262 g_free(old_name);
265 json_start_object(vmdesc, NULL);
266 json_prop_str(vmdesc, "name", name);
267 if (is_array) {
268 if (can_compress) {
269 json_prop_int(vmdesc, "array_len", max);
270 } else {
271 json_prop_int(vmdesc, "index", i);
274 json_prop_str(vmdesc, "type", vmfield_get_type_name(field));
276 if (field->flags & VMS_STRUCT) {
277 json_start_object(vmdesc, "struct");
280 g_free(name);
283 static void vmsd_desc_field_end(const VMStateDescription *vmsd, QJSON *vmdesc,
284 VMStateField *field, size_t size, int i)
286 if (!vmdesc) {
287 return;
290 if (field->flags & VMS_STRUCT) {
291 /* We printed a struct in between, close its child object */
292 json_end_object(vmdesc);
295 json_prop_int(vmdesc, "size", size);
296 json_end_object(vmdesc);
300 bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque)
302 if (vmsd->needed && !vmsd->needed(opaque)) {
303 /* optional section not needed */
304 return false;
306 return true;
310 void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
311 void *opaque, QJSON *vmdesc)
313 VMStateField *field = vmsd->fields;
315 trace_vmstate_save_state_top(vmsd->name);
317 if (vmsd->pre_save) {
318 vmsd->pre_save(opaque);
321 if (vmdesc) {
322 json_prop_str(vmdesc, "vmsd_name", vmsd->name);
323 json_prop_int(vmdesc, "version", vmsd->version_id);
324 json_start_array(vmdesc, "fields");
327 while (field->name) {
328 if (!field->field_exists ||
329 field->field_exists(opaque, vmsd->version_id)) {
330 void *first_elem = opaque + field->offset;
331 int i, n_elems = vmstate_n_elems(opaque, field);
332 int size = vmstate_size(opaque, field);
333 int64_t old_offset, written_bytes;
334 QJSON *vmdesc_loop = vmdesc;
336 trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);
337 if (field->flags & VMS_POINTER) {
338 first_elem = *(void **)first_elem;
339 assert(first_elem || !n_elems || !size);
341 for (i = 0; i < n_elems; i++) {
342 void *curr_elem = first_elem + size * i;
344 vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems);
345 old_offset = qemu_ftell_fast(f);
346 if (field->flags & VMS_ARRAY_OF_POINTER) {
347 assert(curr_elem);
348 curr_elem = *(void **)curr_elem;
350 if (!curr_elem && size) {
351 /* if null pointer write placeholder and do not follow */
352 assert(field->flags & VMS_ARRAY_OF_POINTER);
353 vmstate_info_nullptr.put(f, curr_elem, size, NULL, NULL);
354 } else if (field->flags & VMS_STRUCT) {
355 vmstate_save_state(f, field->vmsd, curr_elem, vmdesc_loop);
356 } else {
357 field->info->put(f, curr_elem, size, field, vmdesc_loop);
360 written_bytes = qemu_ftell_fast(f) - old_offset;
361 vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i);
363 /* Compressed arrays only care about the first element */
364 if (vmdesc_loop && vmsd_can_compress(field)) {
365 vmdesc_loop = NULL;
368 } else {
369 if (field->flags & VMS_MUST_EXIST) {
370 error_report("Output state validation failed: %s/%s",
371 vmsd->name, field->name);
372 assert(!(field->flags & VMS_MUST_EXIST));
375 field++;
378 if (vmdesc) {
379 json_end_array(vmdesc);
382 vmstate_subsection_save(f, vmsd, opaque, vmdesc);
385 static const VMStateDescription *
386 vmstate_get_subsection(const VMStateDescription **sub, char *idstr)
388 while (sub && *sub && (*sub)->needed) {
389 if (strcmp(idstr, (*sub)->name) == 0) {
390 return *sub;
392 sub++;
394 return NULL;
397 static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
398 void *opaque)
400 trace_vmstate_subsection_load(vmsd->name);
402 while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
403 char idstr[256], *idstr_ret;
404 int ret;
405 uint8_t version_id, len, size;
406 const VMStateDescription *sub_vmsd;
408 len = qemu_peek_byte(f, 1);
409 if (len < strlen(vmsd->name) + 1) {
410 /* subsection name has be be "section_name/a" */
411 trace_vmstate_subsection_load_bad(vmsd->name, "(short)", "");
412 return 0;
414 size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2);
415 if (size != len) {
416 trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)", "");
417 return 0;
419 memcpy(idstr, idstr_ret, size);
420 idstr[size] = 0;
422 if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
423 trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(prefix)");
424 /* it doesn't have a valid subsection name */
425 return 0;
427 sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
428 if (sub_vmsd == NULL) {
429 trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(lookup)");
430 return -ENOENT;
432 qemu_file_skip(f, 1); /* subsection */
433 qemu_file_skip(f, 1); /* len */
434 qemu_file_skip(f, len); /* idstr */
435 version_id = qemu_get_be32(f);
437 ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
438 if (ret) {
439 trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)");
440 return ret;
444 trace_vmstate_subsection_load_good(vmsd->name);
445 return 0;
448 static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
449 void *opaque, QJSON *vmdesc)
451 const VMStateDescription **sub = vmsd->subsections;
452 bool subsection_found = false;
454 trace_vmstate_subsection_save_top(vmsd->name);
455 while (sub && *sub && (*sub)->needed) {
456 if ((*sub)->needed(opaque)) {
457 const VMStateDescription *vmsdsub = *sub;
458 uint8_t len;
460 trace_vmstate_subsection_save_loop(vmsd->name, vmsdsub->name);
461 if (vmdesc) {
462 /* Only create subsection array when we have any */
463 if (!subsection_found) {
464 json_start_array(vmdesc, "subsections");
465 subsection_found = true;
468 json_start_object(vmdesc, NULL);
471 qemu_put_byte(f, QEMU_VM_SUBSECTION);
472 len = strlen(vmsdsub->name);
473 qemu_put_byte(f, len);
474 qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
475 qemu_put_be32(f, vmsdsub->version_id);
476 vmstate_save_state(f, vmsdsub, opaque, vmdesc);
478 if (vmdesc) {
479 json_end_object(vmdesc);
482 sub++;
485 if (vmdesc && subsection_found) {
486 json_end_array(vmdesc);