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