xref: /openbmc/qemu/migration/vmstate.c (revision 438c78da)
1 /*
2  * VMState interpreter
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-common.h"
15 #include "migration.h"
16 #include "migration/vmstate.h"
17 #include "savevm.h"
18 #include "qemu-file.h"
19 #include "qemu/bitops.h"
20 #include "qemu/error-report.h"
21 #include "trace.h"
22 #include "qjson.h"
23 
24 static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
25                                    void *opaque, QJSON *vmdesc);
26 static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
27                                    void *opaque);
28 
29 static int vmstate_n_elems(void *opaque, VMStateField *field)
30 {
31     int n_elems = 1;
32 
33     if (field->flags & VMS_ARRAY) {
34         n_elems = field->num;
35     } else if (field->flags & VMS_VARRAY_INT32) {
36         n_elems = *(int32_t *)(opaque+field->num_offset);
37     } else if (field->flags & VMS_VARRAY_UINT32) {
38         n_elems = *(uint32_t *)(opaque+field->num_offset);
39     } else if (field->flags & VMS_VARRAY_UINT16) {
40         n_elems = *(uint16_t *)(opaque+field->num_offset);
41     } else if (field->flags & VMS_VARRAY_UINT8) {
42         n_elems = *(uint8_t *)(opaque+field->num_offset);
43     }
44 
45     if (field->flags & VMS_MULTIPLY_ELEMENTS) {
46         n_elems *= field->num;
47     }
48 
49     trace_vmstate_n_elems(field->name, n_elems);
50     return n_elems;
51 }
52 
53 static int vmstate_size(void *opaque, VMStateField *field)
54 {
55     int size = field->size;
56 
57     if (field->flags & VMS_VBUFFER) {
58         size = *(int32_t *)(opaque+field->size_offset);
59         if (field->flags & VMS_MULTIPLY) {
60             size *= field->size;
61         }
62     }
63 
64     return size;
65 }
66 
67 static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque)
68 {
69     if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) {
70         gsize size = vmstate_size(opaque, field);
71         size *= vmstate_n_elems(opaque, field);
72         if (size) {
73             *(void **)ptr = g_malloc(size);
74         }
75     }
76 }
77 
78 int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
79                        void *opaque, int version_id)
80 {
81     VMStateField *field = vmsd->fields;
82     int ret = 0;
83 
84     trace_vmstate_load_state(vmsd->name, version_id);
85     if (version_id > vmsd->version_id) {
86         error_report("%s: incoming version_id %d is too new "
87                      "for local version_id %d",
88                      vmsd->name, version_id, vmsd->version_id);
89         trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
90         return -EINVAL;
91     }
92     if  (version_id < vmsd->minimum_version_id) {
93         if (vmsd->load_state_old &&
94             version_id >= vmsd->minimum_version_id_old) {
95             ret = vmsd->load_state_old(f, opaque, version_id);
96             trace_vmstate_load_state_end(vmsd->name, "old path", ret);
97             return ret;
98         }
99         error_report("%s: incoming version_id %d is too old "
100                      "for local minimum version_id  %d",
101                      vmsd->name, version_id, vmsd->minimum_version_id);
102         trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
103         return -EINVAL;
104     }
105     if (vmsd->pre_load) {
106         int ret = vmsd->pre_load(opaque);
107         if (ret) {
108             return ret;
109         }
110     }
111     while (field->name) {
112         trace_vmstate_load_state_field(vmsd->name, field->name);
113         if ((field->field_exists &&
114              field->field_exists(opaque, version_id)) ||
115             (!field->field_exists &&
116              field->version_id <= version_id)) {
117             void *first_elem = opaque + field->offset;
118             int i, n_elems = vmstate_n_elems(opaque, field);
119             int size = vmstate_size(opaque, field);
120 
121             vmstate_handle_alloc(first_elem, field, opaque);
122             if (field->flags & VMS_POINTER) {
123                 first_elem = *(void **)first_elem;
124                 assert(first_elem || !n_elems || !size);
125             }
126             for (i = 0; i < n_elems; i++) {
127                 void *curr_elem = first_elem + size * i;
128 
129                 if (field->flags & VMS_ARRAY_OF_POINTER) {
130                     curr_elem = *(void **)curr_elem;
131                 }
132                 if (!curr_elem && size) {
133                     /* if null pointer check placeholder and do not follow */
134                     assert(field->flags & VMS_ARRAY_OF_POINTER);
135                     ret = vmstate_info_nullptr.get(f, curr_elem, size, NULL);
136                 } else if (field->flags & VMS_STRUCT) {
137                     ret = vmstate_load_state(f, field->vmsd, curr_elem,
138                                              field->vmsd->version_id);
139                 } else if (field->flags & VMS_VSTRUCT) {
140                     ret = vmstate_load_state(f, field->vmsd, curr_elem,
141                                              field->struct_version_id);
142                 } else {
143                     ret = field->info->get(f, curr_elem, size, field);
144                 }
145                 if (ret >= 0) {
146                     ret = qemu_file_get_error(f);
147                 }
148                 if (ret < 0) {
149                     qemu_file_set_error(f, ret);
150                     error_report("Failed to load %s:%s", vmsd->name,
151                                  field->name);
152                     trace_vmstate_load_field_error(field->name, ret);
153                     return ret;
154                 }
155             }
156         } else if (field->flags & VMS_MUST_EXIST) {
157             error_report("Input validation failed: %s/%s",
158                          vmsd->name, field->name);
159             return -1;
160         }
161         field++;
162     }
163     ret = vmstate_subsection_load(f, vmsd, opaque);
164     if (ret != 0) {
165         return ret;
166     }
167     if (vmsd->post_load) {
168         ret = vmsd->post_load(opaque, version_id);
169     }
170     trace_vmstate_load_state_end(vmsd->name, "end", ret);
171     return ret;
172 }
173 
174 static int vmfield_name_num(VMStateField *start, VMStateField *search)
175 {
176     VMStateField *field;
177     int found = 0;
178 
179     for (field = start; field->name; field++) {
180         if (!strcmp(field->name, search->name)) {
181             if (field == search) {
182                 return found;
183             }
184             found++;
185         }
186     }
187 
188     return -1;
189 }
190 
191 static bool vmfield_name_is_unique(VMStateField *start, VMStateField *search)
192 {
193     VMStateField *field;
194     int found = 0;
195 
196     for (field = start; field->name; field++) {
197         if (!strcmp(field->name, search->name)) {
198             found++;
199             /* name found more than once, so it's not unique */
200             if (found > 1) {
201                 return false;
202             }
203         }
204     }
205 
206     return true;
207 }
208 
209 static const char *vmfield_get_type_name(VMStateField *field)
210 {
211     const char *type = "unknown";
212 
213     if (field->flags & VMS_STRUCT) {
214         type = "struct";
215     } else if (field->flags & VMS_VSTRUCT) {
216         type = "vstruct";
217     } else if (field->info->name) {
218         type = field->info->name;
219     }
220 
221     return type;
222 }
223 
224 static bool vmsd_can_compress(VMStateField *field)
225 {
226     if (field->field_exists) {
227         /* Dynamically existing fields mess up compression */
228         return false;
229     }
230 
231     if (field->flags & VMS_STRUCT) {
232         VMStateField *sfield = field->vmsd->fields;
233         while (sfield->name) {
234             if (!vmsd_can_compress(sfield)) {
235                 /* Child elements can't compress, so can't we */
236                 return false;
237             }
238             sfield++;
239         }
240 
241         if (field->vmsd->subsections) {
242             /* Subsections may come and go, better don't compress */
243             return false;
244         }
245     }
246 
247     return true;
248 }
249 
250 static void vmsd_desc_field_start(const VMStateDescription *vmsd, QJSON *vmdesc,
251                                   VMStateField *field, int i, int max)
252 {
253     char *name, *old_name;
254     bool is_array = max > 1;
255     bool can_compress = vmsd_can_compress(field);
256 
257     if (!vmdesc) {
258         return;
259     }
260 
261     name = g_strdup(field->name);
262 
263     /* Field name is not unique, need to make it unique */
264     if (!vmfield_name_is_unique(vmsd->fields, field)) {
265         int num = vmfield_name_num(vmsd->fields, field);
266         old_name = name;
267         name = g_strdup_printf("%s[%d]", name, num);
268         g_free(old_name);
269     }
270 
271     json_start_object(vmdesc, NULL);
272     json_prop_str(vmdesc, "name", name);
273     if (is_array) {
274         if (can_compress) {
275             json_prop_int(vmdesc, "array_len", max);
276         } else {
277             json_prop_int(vmdesc, "index", i);
278         }
279     }
280     json_prop_str(vmdesc, "type", vmfield_get_type_name(field));
281 
282     if (field->flags & VMS_STRUCT) {
283         json_start_object(vmdesc, "struct");
284     }
285 
286     g_free(name);
287 }
288 
289 static void vmsd_desc_field_end(const VMStateDescription *vmsd, QJSON *vmdesc,
290                                 VMStateField *field, size_t size, int i)
291 {
292     if (!vmdesc) {
293         return;
294     }
295 
296     if (field->flags & VMS_STRUCT) {
297         /* We printed a struct in between, close its child object */
298         json_end_object(vmdesc);
299     }
300 
301     json_prop_int(vmdesc, "size", size);
302     json_end_object(vmdesc);
303 }
304 
305 
306 bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque)
307 {
308     if (vmsd->needed && !vmsd->needed(opaque)) {
309         /* optional section not needed */
310         return false;
311     }
312     return true;
313 }
314 
315 
316 int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
317                        void *opaque, QJSON *vmdesc_id)
318 {
319     return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id);
320 }
321 
322 int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
323                          void *opaque, QJSON *vmdesc, int version_id)
324 {
325     int ret = 0;
326     VMStateField *field = vmsd->fields;
327 
328     trace_vmstate_save_state_top(vmsd->name);
329 
330     if (vmsd->pre_save) {
331         ret = vmsd->pre_save(opaque);
332         trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
333         if (ret) {
334             error_report("pre-save failed: %s", vmsd->name);
335             return ret;
336         }
337     }
338 
339     if (vmdesc) {
340         json_prop_str(vmdesc, "vmsd_name", vmsd->name);
341         json_prop_int(vmdesc, "version", version_id);
342         json_start_array(vmdesc, "fields");
343     }
344 
345     while (field->name) {
346         if ((field->field_exists &&
347              field->field_exists(opaque, version_id)) ||
348             (!field->field_exists &&
349              field->version_id <= version_id)) {
350             void *first_elem = opaque + field->offset;
351             int i, n_elems = vmstate_n_elems(opaque, field);
352             int size = vmstate_size(opaque, field);
353             int64_t old_offset, written_bytes;
354             QJSON *vmdesc_loop = vmdesc;
355 
356             trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);
357             if (field->flags & VMS_POINTER) {
358                 first_elem = *(void **)first_elem;
359                 assert(first_elem || !n_elems || !size);
360             }
361             for (i = 0; i < n_elems; i++) {
362                 void *curr_elem = first_elem + size * i;
363                 ret = 0;
364 
365                 vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems);
366                 old_offset = qemu_ftell_fast(f);
367                 if (field->flags & VMS_ARRAY_OF_POINTER) {
368                     assert(curr_elem);
369                     curr_elem = *(void **)curr_elem;
370                 }
371                 if (!curr_elem && size) {
372                     /* if null pointer write placeholder and do not follow */
373                     assert(field->flags & VMS_ARRAY_OF_POINTER);
374                     ret = vmstate_info_nullptr.put(f, curr_elem, size, NULL,
375                                                    NULL);
376                 } else if (field->flags & VMS_STRUCT) {
377                     ret = vmstate_save_state(f, field->vmsd, curr_elem,
378                                              vmdesc_loop);
379                 } else if (field->flags & VMS_VSTRUCT) {
380                     ret = vmstate_save_state_v(f, field->vmsd, curr_elem,
381                                                vmdesc_loop,
382                                                field->struct_version_id);
383                 } else {
384                     ret = field->info->put(f, curr_elem, size, field,
385                                      vmdesc_loop);
386                 }
387                 if (ret) {
388                     error_report("Save of field %s/%s failed",
389                                  vmsd->name, field->name);
390                     return ret;
391                 }
392 
393                 written_bytes = qemu_ftell_fast(f) - old_offset;
394                 vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i);
395 
396                 /* Compressed arrays only care about the first element */
397                 if (vmdesc_loop && vmsd_can_compress(field)) {
398                     vmdesc_loop = NULL;
399                 }
400             }
401         } else {
402             if (field->flags & VMS_MUST_EXIST) {
403                 error_report("Output state validation failed: %s/%s",
404                         vmsd->name, field->name);
405                 assert(!(field->flags & VMS_MUST_EXIST));
406             }
407         }
408         field++;
409     }
410 
411     if (vmdesc) {
412         json_end_array(vmdesc);
413     }
414 
415     return vmstate_subsection_save(f, vmsd, opaque, vmdesc);
416 }
417 
418 static const VMStateDescription *
419 vmstate_get_subsection(const VMStateDescription **sub, char *idstr)
420 {
421     while (sub && *sub) {
422         if (strcmp(idstr, (*sub)->name) == 0) {
423             return *sub;
424         }
425         sub++;
426     }
427     return NULL;
428 }
429 
430 static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
431                                    void *opaque)
432 {
433     trace_vmstate_subsection_load(vmsd->name);
434 
435     while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
436         char idstr[256], *idstr_ret;
437         int ret;
438         uint8_t version_id, len, size;
439         const VMStateDescription *sub_vmsd;
440 
441         len = qemu_peek_byte(f, 1);
442         if (len < strlen(vmsd->name) + 1) {
443             /* subsection name has be be "section_name/a" */
444             trace_vmstate_subsection_load_bad(vmsd->name, "(short)", "");
445             return 0;
446         }
447         size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2);
448         if (size != len) {
449             trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)", "");
450             return 0;
451         }
452         memcpy(idstr, idstr_ret, size);
453         idstr[size] = 0;
454 
455         if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
456             trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(prefix)");
457             /* it doesn't have a valid subsection name */
458             return 0;
459         }
460         sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
461         if (sub_vmsd == NULL) {
462             trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(lookup)");
463             return -ENOENT;
464         }
465         qemu_file_skip(f, 1); /* subsection */
466         qemu_file_skip(f, 1); /* len */
467         qemu_file_skip(f, len); /* idstr */
468         version_id = qemu_get_be32(f);
469 
470         ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
471         if (ret) {
472             trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)");
473             return ret;
474         }
475     }
476 
477     trace_vmstate_subsection_load_good(vmsd->name);
478     return 0;
479 }
480 
481 static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
482                                    void *opaque, QJSON *vmdesc)
483 {
484     const VMStateDescription **sub = vmsd->subsections;
485     bool subsection_found = false;
486     int ret = 0;
487 
488     trace_vmstate_subsection_save_top(vmsd->name);
489     while (sub && *sub) {
490         if (vmstate_save_needed(*sub, opaque)) {
491             const VMStateDescription *vmsdsub = *sub;
492             uint8_t len;
493 
494             trace_vmstate_subsection_save_loop(vmsd->name, vmsdsub->name);
495             if (vmdesc) {
496                 /* Only create subsection array when we have any */
497                 if (!subsection_found) {
498                     json_start_array(vmdesc, "subsections");
499                     subsection_found = true;
500                 }
501 
502                 json_start_object(vmdesc, NULL);
503             }
504 
505             qemu_put_byte(f, QEMU_VM_SUBSECTION);
506             len = strlen(vmsdsub->name);
507             qemu_put_byte(f, len);
508             qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
509             qemu_put_be32(f, vmsdsub->version_id);
510             ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc);
511             if (ret) {
512                 return ret;
513             }
514 
515             if (vmdesc) {
516                 json_end_object(vmdesc);
517             }
518         }
519         sub++;
520     }
521 
522     if (vmdesc && subsection_found) {
523         json_end_array(vmdesc);
524     }
525 
526     return ret;
527 }
528