xref: /qemu/migration/vmstate.c (revision 05a248715cef192336a594afed812871a52efc1f)
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/qmp/json-writer.h"
18  #include "qemu-file.h"
19  #include "qemu/bitops.h"
20  #include "qemu/error-report.h"
21  #include "trace.h"
22  
23  static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
24                                     void *opaque, JSONWriter *vmdesc);
25  static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
26                                     void *opaque);
27  
28  static int vmstate_n_elems(void *opaque, const VMStateField *field)
29  {
30      int n_elems = 1;
31  
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);
42      }
43  
44      if (field->flags & VMS_MULTIPLY_ELEMENTS) {
45          n_elems *= field->num;
46      }
47  
48      trace_vmstate_n_elems(field->name, n_elems);
49      return n_elems;
50  }
51  
52  static int vmstate_size(void *opaque, const VMStateField *field)
53  {
54      int size = field->size;
55  
56      if (field->flags & VMS_VBUFFER) {
57          size = *(int32_t *)(opaque + field->size_offset);
58          if (field->flags & VMS_MULTIPLY) {
59              size *= field->size;
60          }
61      }
62  
63      return size;
64  }
65  
66  static void vmstate_handle_alloc(void *ptr, const VMStateField *field,
67                                   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      const 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(const VMStateField *start,
175                              const VMStateField *search)
176  {
177      const VMStateField *field;
178      int found = 0;
179  
180      for (field = start; field->name; field++) {
181          if (!strcmp(field->name, search->name)) {
182              if (field == search) {
183                  return found;
184              }
185              found++;
186          }
187      }
188  
189      return -1;
190  }
191  
192  static bool vmfield_name_is_unique(const VMStateField *start,
193                                     const VMStateField *search)
194  {
195      const VMStateField *field;
196      int found = 0;
197  
198      for (field = start; field->name; field++) {
199          if (!strcmp(field->name, search->name)) {
200              found++;
201              /* name found more than once, so it's not unique */
202              if (found > 1) {
203                  return false;
204              }
205          }
206      }
207  
208      return true;
209  }
210  
211  static const char *vmfield_get_type_name(const VMStateField *field)
212  {
213      const char *type = "unknown";
214  
215      if (field->flags & VMS_STRUCT) {
216          type = "struct";
217      } else if (field->flags & VMS_VSTRUCT) {
218          type = "vstruct";
219      } else if (field->info->name) {
220          type = field->info->name;
221      }
222  
223      return type;
224  }
225  
226  static bool vmsd_can_compress(const VMStateField *field)
227  {
228      if (field->field_exists) {
229          /* Dynamically existing fields mess up compression */
230          return false;
231      }
232  
233      if (field->flags & VMS_STRUCT) {
234          const VMStateField *sfield = field->vmsd->fields;
235          while (sfield->name) {
236              if (!vmsd_can_compress(sfield)) {
237                  /* Child elements can't compress, so can't we */
238                  return false;
239              }
240              sfield++;
241          }
242  
243          if (field->vmsd->subsections) {
244              /* Subsections may come and go, better don't compress */
245              return false;
246          }
247      }
248  
249      return true;
250  }
251  
252  static void vmsd_desc_field_start(const VMStateDescription *vmsd,
253                                    JSONWriter *vmdesc,
254                                    const VMStateField *field, int i, int max)
255  {
256      char *name, *old_name;
257      bool is_array = max > 1;
258      bool can_compress = vmsd_can_compress(field);
259  
260      if (!vmdesc) {
261          return;
262      }
263  
264      name = g_strdup(field->name);
265  
266      /* Field name is not unique, need to make it unique */
267      if (!vmfield_name_is_unique(vmsd->fields, field)) {
268          int num = vmfield_name_num(vmsd->fields, field);
269          old_name = name;
270          name = g_strdup_printf("%s[%d]", name, num);
271          g_free(old_name);
272      }
273  
274      json_writer_start_object(vmdesc, NULL);
275      json_writer_str(vmdesc, "name", name);
276      if (is_array) {
277          if (can_compress) {
278              json_writer_int64(vmdesc, "array_len", max);
279          } else {
280              json_writer_int64(vmdesc, "index", i);
281          }
282      }
283      json_writer_str(vmdesc, "type", vmfield_get_type_name(field));
284  
285      if (field->flags & VMS_STRUCT) {
286          json_writer_start_object(vmdesc, "struct");
287      }
288  
289      g_free(name);
290  }
291  
292  static void vmsd_desc_field_end(const VMStateDescription *vmsd,
293                                  JSONWriter *vmdesc,
294                                  const VMStateField *field, size_t size, int i)
295  {
296      if (!vmdesc) {
297          return;
298      }
299  
300      if (field->flags & VMS_STRUCT) {
301          /* We printed a struct in between, close its child object */
302          json_writer_end_object(vmdesc);
303      }
304  
305      json_writer_int64(vmdesc, "size", size);
306      json_writer_end_object(vmdesc);
307  }
308  
309  
310  bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque)
311  {
312      if (vmsd->needed && !vmsd->needed(opaque)) {
313          /* optional section not needed */
314          return false;
315      }
316      return true;
317  }
318  
319  
320  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
321                         void *opaque, JSONWriter *vmdesc_id)
322  {
323      return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id);
324  }
325  
326  int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
327                           void *opaque, JSONWriter *vmdesc, int version_id)
328  {
329      int ret = 0;
330      const VMStateField *field = vmsd->fields;
331  
332      trace_vmstate_save_state_top(vmsd->name);
333  
334      if (vmsd->pre_save) {
335          ret = vmsd->pre_save(opaque);
336          trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
337          if (ret) {
338              error_report("pre-save failed: %s", vmsd->name);
339              return ret;
340          }
341      }
342  
343      if (vmdesc) {
344          json_writer_str(vmdesc, "vmsd_name", vmsd->name);
345          json_writer_int64(vmdesc, "version", version_id);
346          json_writer_start_array(vmdesc, "fields");
347      }
348  
349      while (field->name) {
350          if ((field->field_exists &&
351               field->field_exists(opaque, version_id)) ||
352              (!field->field_exists &&
353               field->version_id <= version_id)) {
354              void *first_elem = opaque + field->offset;
355              int i, n_elems = vmstate_n_elems(opaque, field);
356              int size = vmstate_size(opaque, field);
357              int64_t old_offset, written_bytes;
358              JSONWriter *vmdesc_loop = vmdesc;
359  
360              trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);
361              if (field->flags & VMS_POINTER) {
362                  first_elem = *(void **)first_elem;
363                  assert(first_elem || !n_elems || !size);
364              }
365              for (i = 0; i < n_elems; i++) {
366                  void *curr_elem = first_elem + size * i;
367  
368                  vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems);
369                  old_offset = qemu_ftell_fast(f);
370                  if (field->flags & VMS_ARRAY_OF_POINTER) {
371                      assert(curr_elem);
372                      curr_elem = *(void **)curr_elem;
373                  }
374                  if (!curr_elem && size) {
375                      /* if null pointer write placeholder and do not follow */
376                      assert(field->flags & VMS_ARRAY_OF_POINTER);
377                      ret = vmstate_info_nullptr.put(f, curr_elem, size, NULL,
378                                                     NULL);
379                  } else if (field->flags & VMS_STRUCT) {
380                      ret = vmstate_save_state(f, field->vmsd, curr_elem,
381                                               vmdesc_loop);
382                  } else if (field->flags & VMS_VSTRUCT) {
383                      ret = vmstate_save_state_v(f, field->vmsd, curr_elem,
384                                                 vmdesc_loop,
385                                                 field->struct_version_id);
386                  } else {
387                      ret = field->info->put(f, curr_elem, size, field,
388                                       vmdesc_loop);
389                  }
390                  if (ret) {
391                      error_report("Save of field %s/%s failed",
392                                   vmsd->name, field->name);
393                      if (vmsd->post_save) {
394                          vmsd->post_save(opaque);
395                      }
396                      return ret;
397                  }
398  
399                  written_bytes = qemu_ftell_fast(f) - old_offset;
400                  vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i);
401  
402                  /* Compressed arrays only care about the first element */
403                  if (vmdesc_loop && vmsd_can_compress(field)) {
404                      vmdesc_loop = NULL;
405                  }
406              }
407          } else {
408              if (field->flags & VMS_MUST_EXIST) {
409                  error_report("Output state validation failed: %s/%s",
410                          vmsd->name, field->name);
411                  assert(!(field->flags & VMS_MUST_EXIST));
412              }
413          }
414          field++;
415      }
416  
417      if (vmdesc) {
418          json_writer_end_array(vmdesc);
419      }
420  
421      ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc);
422  
423      if (vmsd->post_save) {
424          int ps_ret = vmsd->post_save(opaque);
425          if (!ret) {
426              ret = ps_ret;
427          }
428      }
429      return ret;
430  }
431  
432  static const VMStateDescription *
433  vmstate_get_subsection(const VMStateDescription **sub, char *idstr)
434  {
435      while (sub && *sub) {
436          if (strcmp(idstr, (*sub)->name) == 0) {
437              return *sub;
438          }
439          sub++;
440      }
441      return NULL;
442  }
443  
444  static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
445                                     void *opaque)
446  {
447      trace_vmstate_subsection_load(vmsd->name);
448  
449      while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
450          char idstr[256], *idstr_ret;
451          int ret;
452          uint8_t version_id, len, size;
453          const VMStateDescription *sub_vmsd;
454  
455          len = qemu_peek_byte(f, 1);
456          if (len < strlen(vmsd->name) + 1) {
457              /* subsection name has be be "section_name/a" */
458              trace_vmstate_subsection_load_bad(vmsd->name, "(short)", "");
459              return 0;
460          }
461          size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2);
462          if (size != len) {
463              trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)", "");
464              return 0;
465          }
466          memcpy(idstr, idstr_ret, size);
467          idstr[size] = 0;
468  
469          if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
470              trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(prefix)");
471              /* it doesn't have a valid subsection name */
472              return 0;
473          }
474          sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
475          if (sub_vmsd == NULL) {
476              trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(lookup)");
477              return -ENOENT;
478          }
479          qemu_file_skip(f, 1); /* subsection */
480          qemu_file_skip(f, 1); /* len */
481          qemu_file_skip(f, len); /* idstr */
482          version_id = qemu_get_be32(f);
483  
484          ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
485          if (ret) {
486              trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)");
487              return ret;
488          }
489      }
490  
491      trace_vmstate_subsection_load_good(vmsd->name);
492      return 0;
493  }
494  
495  static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
496                                     void *opaque, JSONWriter *vmdesc)
497  {
498      const VMStateDescription **sub = vmsd->subsections;
499      bool vmdesc_has_subsections = false;
500      int ret = 0;
501  
502      trace_vmstate_subsection_save_top(vmsd->name);
503      while (sub && *sub) {
504          if (vmstate_save_needed(*sub, opaque)) {
505              const VMStateDescription *vmsdsub = *sub;
506              uint8_t len;
507  
508              trace_vmstate_subsection_save_loop(vmsd->name, vmsdsub->name);
509              if (vmdesc) {
510                  /* Only create subsection array when we have any */
511                  if (!vmdesc_has_subsections) {
512                      json_writer_start_array(vmdesc, "subsections");
513                      vmdesc_has_subsections = true;
514                  }
515  
516                  json_writer_start_object(vmdesc, NULL);
517              }
518  
519              qemu_put_byte(f, QEMU_VM_SUBSECTION);
520              len = strlen(vmsdsub->name);
521              qemu_put_byte(f, len);
522              qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
523              qemu_put_be32(f, vmsdsub->version_id);
524              ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc);
525              if (ret) {
526                  return ret;
527              }
528  
529              if (vmdesc) {
530                  json_writer_end_object(vmdesc);
531              }
532          }
533          sub++;
534      }
535  
536      if (vmdesc_has_subsections) {
537          json_writer_end_array(vmdesc);
538      }
539  
540      return ret;
541  }
542