@@ -1004,15 +1004,12 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
return rc;
}
-static inline void *host_from_stream_offset(QEMUFile *f,
- ram_addr_t offset,
- int flags)
+static inline void *host_from_stream_offset(ramsecentry_header *rse_hdr)
{
static RAMBlock *block = NULL;
- char id[256];
- uint8_t len;
+ ram_addr_t offset = rse_hdr->addr;
- if (flags & RAM_SAVE_FLAG_CONTINUE) {
+ if (rse_hdr->flags & RAM_SAVE_FLAG_CONTINUE) {
if (!block) {
fprintf(stderr, "Ack, bad migration stream!\n");
return NULL;
@@ -1021,16 +1018,13 @@ static inline void *host_from_stream_offset(QEMUFile *f,
return memory_region_get_ram_ptr(block->mr) + offset;
}
- len = qemu_get_byte(f);
- qemu_get_buffer(f, (uint8_t *)id, len);
- id[len] = 0;
-
QTAILQ_FOREACH(block, &ram_list.blocks, next) {
- if (!strncmp(id, block->idstr, sizeof(id)))
+ if (!strncmp(rse_hdr->idstr, block->idstr, sizeof(rse_hdr->idstr))) {
return memory_region_get_ram_ptr(block->mr) + offset;
+ }
}
- fprintf(stderr, "Can't find block %s!\n", id);
+ fprintf(stderr, "Can't find block %s!\n", rse_hdr->idstr);
return NULL;
}
@@ -1051,6 +1045,9 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
int flags, ret = 0;
int error;
static uint64_t seq_iter;
+ Error *local_err = NULL;
+ ramsecentry_header rse_hdr;
+ Visitor *v = qemu_file_get_tmp_visitor(f); // TODO
seq_iter++;
@@ -1058,11 +1055,18 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
return -EINVAL;
}
- do {
- addr = qemu_get_be64(f);
-
- flags = addr & ~TARGET_PAGE_MASK;
- addr &= TARGET_PAGE_MASK;
+ visit_start_sequence_compat(v, "ramseclist", VISIT_SEQ_COMPAT_RAMSECLIST,
+ NULL, &local_err);
+ LOCAL_ERR_REPORT(return -EINVAL;);
+ while (visit_get_next_type(v, &flags, NULL, "ramseclist", &local_err),
+ (!local_err && !(flags & RAM_SAVE_FLAG_EOS))) {
+ LOCAL_ERR_REPORT(return -EINVAL;);
+ visit_start_sequence_compat(v, "ramsecentry",
+ VISIT_SEQ_COMPAT_RAMSECENTRY, &rse_hdr,
+ &local_err);
+ addr = rse_hdr.addr;
+ flags = rse_hdr.flags;
+ LOCAL_ERR_REPORT(return -EINVAL;);
if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
if (version_id == 4) {
@@ -1071,14 +1075,17 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
ram_addr_t length;
ram_addr_t total_ram_bytes = addr;
+ visit_start_list(v, "blocklist", &local_err);
while (total_ram_bytes) {
RAMBlock *block;
- uint8_t len;
+ visit_start_struct(v, NULL, "blockid", "blockid", 0,
+ &local_err);
+ visit_type_str256(v, id, "blockname", &local_err);
+ visit_type_uint64(v, &length, "blocklen", &local_err);
+ LOCAL_ERR_REPORT(ret = -EINVAL; goto done;);
+ visit_end_struct(v, &local_err);
+ LOCAL_ERR_REPORT(ret = -EINVAL; goto done;);
- len = qemu_get_byte(f);
- qemu_get_buffer(f, (uint8_t *)id, len);
- id[len] = 0;
- length = qemu_get_be64(f);
QTAILQ_FOREACH(block, &ram_list.blocks, next) {
if (!strncmp(id, block->idstr, sizeof(id))) {
@@ -1103,6 +1110,9 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
total_ram_bytes -= length;
}
+ LOCAL_ERR_REPORT(ret = -EINVAL; goto done;);
+ visit_end_list(v, &local_err);
+ LOCAL_ERR_REPORT(ret = -EINVAL; goto done;);
}
}
@@ -1110,24 +1120,25 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
void *host;
uint8_t ch;
- host = host_from_stream_offset(f, addr, flags);
+ host = host_from_stream_offset(&rse_hdr);
if (!host) {
return -EINVAL;
}
- ch = qemu_get_byte(f);
+ visit_type_uint8(v, &ch, "pagefill", &local_err);
ram_handle_compressed(host, ch, TARGET_PAGE_SIZE);
} else if (flags & RAM_SAVE_FLAG_PAGE) {
void *host;
- host = host_from_stream_offset(f, addr, flags);
+ host = host_from_stream_offset(&rse_hdr);
if (!host) {
return -EINVAL;
}
- qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
+ visit_type_buffer(v, host, TARGET_PAGE_SIZE, false, "page",
+ &local_err);
} else if (flags & RAM_SAVE_FLAG_XBZRLE) {
- void *host = host_from_stream_offset(f, addr, flags);
+ void *host = host_from_stream_offset(&rse_hdr);
if (!host) {
return -EINVAL;
}
@@ -1144,7 +1155,12 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
ret = error;
goto done;
}
- } while (!(flags & RAM_SAVE_FLAG_EOS));
+ visit_end_sequence_compat(v, "ramsecentry",
+ VISIT_SEQ_COMPAT_RAMSECENTRY, &local_err);
+ };
+ visit_end_sequence_compat(v, "ramseclist", VISIT_SEQ_COMPAT_RAMSECLIST,
+ &local_err);
+ LOCAL_ERR_REPORT(return -EINVAL;);
done:
DPRINTF("Completed load of VM with exit code %d seq iteration "
@@ -211,7 +211,8 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
qemu_get_be16s(f, &s->ar.pm1.evt.en);
qemu_get_be16s(f, &s->ar.pm1.cnt.cnt);
- ret = vmstate_load_state(f, &vmstate_apm, &s->apm, 1);
+ ret = vmstate_load_state(qemu_file_get_tmp_visitor(f), &vmstate_apm,
+ &s->apm, 1);
if (ret) {
return ret;
}
@@ -229,7 +230,7 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
qemu_get_be16s(f, &temp);
}
- ret = vmstate_load_state(f, &vmstate_pci_status,
+ ret = vmstate_load_state(qemu_file_get_tmp_visitor(f), &vmstate_pci_status,
&s->acpi_pci_hotplug.acpi_pcihp_pci_status[ACPI_PCIHP_BSEL_DEFAULT], 1);
return ret;
}
@@ -523,7 +523,8 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
int pci_device_load(PCIDevice *s, QEMUFile *f)
{
int ret;
- ret = vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id);
+ ret = vmstate_load_state(qemu_file_get_tmp_visitor(f),
+ pci_get_vmstate(s), s, s->version_id);
/* Restore the interrupt status bit. */
pci_update_irq_status(s);
return ret;
@@ -643,7 +643,8 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq)
assert(!req->active);
memset(req, 0, sizeof(*req));
- rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1);
+ rc = vmstate_load_state(qemu_file_get_tmp_visitor(f),
+ &vmstate_spapr_vscsi_req, req, 1);
if (rc) {
fprintf(stderr, "VSCSI: failed loading request tag#%u\n", sreq->tag);
return NULL;
@@ -759,7 +759,7 @@ extern const VMStateInfo vmstate_info_bitmap;
#define VMSTATE_END_OF_LIST() \
{}
-int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
+int vmstate_load_state(Visitor *v, const VMStateDescription *vmsd,
void *opaque, int version_id);
void vmstate_save_state(Visitor *v, const VMStateDescription *vmsd,
void *opaque);
@@ -436,12 +436,41 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd,
}
}
-static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
+static int vmstate_load(Visitor *v, SaveStateEntry *se, int version_id)
{
+ int ret;
+ Error *local_err = NULL;
+
if (!se->vmsd) { /* Old style */
- return se->ops->load_state(f, se->opaque, version_id);
+ QEMUFile *wrapperf;
+ bool is_iterative = se->ops->save_live_iterate != NULL;
+
+ /* Some visitors put old format things down separate QEMUFile's -
+ * but not the iterators which must use the visitors now -
+ * but TODO they need their interface changing
+ */
+
+ if (!is_iterative) {
+ visit_start_sequence_compat(v, "oldstate",
+ VISIT_SEQ_COMPAT_BLOB, &wrapperf, &local_err);
+ }
+ LOCAL_ERR_REPORT(return -EINVAL;);
+
+ ret = se->ops->load_state(is_iterative ? visitor_get_qemufile(v) :
+ wrapperf,
+ se->opaque, version_id);
+ if (!is_iterative) {
+ visit_end_sequence_compat(v, "oldstate", VISIT_SEQ_COMPAT_BLOB,
+ &local_err);
+ }
+
+ LOCAL_ERR_REPORT(return -EINVAL;);
+
+ return ret;
}
- return vmstate_load_state(f, se->vmsd, se->opaque, version_id);
+ ret = vmstate_load_state(v, se->vmsd, se->opaque, version_id);
+
+ return ret;
}
static int vmstate_save(Visitor *v, SaveStateEntry *se, int version_id)
@@ -827,57 +856,69 @@ int qemu_loadvm_state(QEMUFile *f)
QLIST_HEAD(, LoadStateEntry) loadvm_handlers =
QLIST_HEAD_INITIALIZER(loadvm_handlers);
LoadStateEntry *le, *new_le;
- uint8_t section_type;
- unsigned int v;
+ Error *local_err = NULL;
+ int32_t section_type;
+ unsigned int tmp;
int ret;
if (qemu_savevm_state_blocked(NULL)) {
return -EINVAL;
}
- v = qemu_get_be32(f);
- if (v != QEMU_VM_FILE_MAGIC) {
+ tmp = qemu_get_be32(f);
+ if (tmp != QEMU_VM_FILE_MAGIC) {
return -EINVAL;
}
- v = qemu_get_be32(f);
- if (v == QEMU_VM_FILE_VERSION_COMPAT) {
- fprintf(stderr, "SaveVM v2 format is obsolete and don't work anymore\n");
+ tmp = qemu_get_be32(f);
+ if (tmp == QEMU_VM_FILE_VERSION_COMPAT) {
+ error_report("SaveVM v2 format is obsolete and don't work anymore");
return -ENOTSUP;
}
- if (v != QEMU_VM_FILE_VERSION) {
+ if (tmp != QEMU_VM_FILE_VERSION) {
return -ENOTSUP;
}
- while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) {
- uint32_t instance_id, version_id, section_id;
+ /* TODO: Here we should be able to figure out if it's a binary file
+ * or what and open the right type of visitor
+ */
+ QemuFileBinInputVisitor *qfbiv = qemu_file_bin_input_visitor_new(f);
+ Visitor *v = qemu_file_bin_input_get_visitor(qfbiv);
+ qemu_file_set_tmp_visitor(f, v);
+
+ visit_start_sequence_compat(v, "top", VISIT_SEQ_COMPAT_BYTE0TERM,
+ NULL, &local_err);
+ while (visit_get_next_type(v, §ion_type, NULL, "section", &local_err),
+ (!local_err && section_type != QEMU_VM_EOF)) {
SaveStateEntry *se;
- char idstr[257];
- int len;
+ SectionHeader sh;
switch (section_type) {
case QEMU_VM_SECTION_START:
case QEMU_VM_SECTION_FULL:
/* Read section start */
- section_id = qemu_get_be32(f);
- len = qemu_get_byte(f);
- qemu_get_buffer(f, (uint8_t *)idstr, len);
- idstr[len] = 0;
- instance_id = qemu_get_be32(f);
- version_id = qemu_get_be32(f);
+ visit_start_sequence_compat(v,
+ (section_type == QEMU_VM_SECTION_START) ?
+ "secstart" : "secfull",
+ VISIT_SEQ_COMPAT_SECTION_HEADER, &sh, &local_err);
+ if (local_err) {
+ ret = -EINVAL;
+ goto out;
+ }
/* Find savevm section */
- se = find_se(idstr, instance_id);
+ se = find_se(sh.idstr, sh.instance_id);
if (se == NULL) {
- fprintf(stderr, "Unknown savevm section or instance '%s' %d\n", idstr, instance_id);
+ error_report("Unknown savevm section or instance '%s' %d",
+ sh.idstr, sh.instance_id);
ret = -EINVAL;
goto out;
}
/* Validate version */
- if (version_id > se->version_id) {
+ if (sh.version_id > se->version_id) {
fprintf(stderr, "savevm: unsupported version %d for '%s' v%d\n",
- version_id, idstr, se->version_id);
+ sh.version_id, sh.idstr, se->version_id);
ret = -EINVAL;
goto out;
}
@@ -886,38 +927,51 @@ int qemu_loadvm_state(QEMUFile *f)
le = g_malloc0(sizeof(*le));
le->se = se;
- le->section_id = section_id;
- le->version_id = version_id;
+ le->section_id = sh.section_id;
+ le->version_id = sh.version_id;
QLIST_INSERT_HEAD(&loadvm_handlers, le, entry);
- ret = vmstate_load(f, le->se, le->version_id);
+ ret = vmstate_load(v, le->se, le->version_id);
if (ret < 0) {
- fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n",
- instance_id, idstr);
+ error_report("warning: error while loading state for instance "
+ "0x%x of device '%s'", sh.instance_id, sh.idstr);
goto out;
}
+ visit_end_sequence_compat(v,
+ (section_type == QEMU_VM_SECTION_START) ?
+ "secstart" : "secfull",
+ VISIT_SEQ_COMPAT_SECTION_HEADER, &local_err);
break;
case QEMU_VM_SECTION_PART:
case QEMU_VM_SECTION_END:
- section_id = qemu_get_be32(f);
+ visit_start_sequence_compat(v,
+ (section_type == QEMU_VM_SECTION_PART) ? "secpart" : "secend",
+ VISIT_SEQ_COMPAT_SECTION_MIN, &sh, &local_err);
+ if (local_err) {
+ ret = -EINVAL;
+ goto out;
+ }
QLIST_FOREACH(le, &loadvm_handlers, entry) {
- if (le->section_id == section_id) {
+ if (le->section_id == sh.section_id) {
break;
}
}
if (le == NULL) {
- fprintf(stderr, "Unknown savevm section %d\n", section_id);
+ error_report("Unknown savevm section %d", sh.section_id);
ret = -EINVAL;
goto out;
}
- ret = vmstate_load(f, le->se, le->version_id);
+ ret = vmstate_load(v, le->se, le->version_id);
if (ret < 0) {
- fprintf(stderr, "qemu: warning: error while loading state section id %d\n",
- section_id);
+ error_report("warning: error while loading state section id %d",
+ sh.section_id);
goto out;
}
+ visit_end_sequence_compat(v,
+ (section_type == QEMU_VM_SECTION_PART) ? "secpart" : "secend",
+ VISIT_SEQ_COMPAT_SECTION_MIN, &local_err);
break;
default:
fprintf(stderr, "Unknown savevm section type %d\n", section_type);
@@ -936,10 +990,17 @@ out:
g_free(le);
}
+ visit_end_sequence_compat(v, "top", VISIT_SEQ_COMPAT_BYTE0TERM, &local_err);
+ if (local_err) {
+ error_report("%s", error_get_pretty(local_err));
+ ret = -EINVAL;
+ }
+
if (ret == 0) {
ret = qemu_file_get_error(f);
}
+ qemu_file_bin_input_visitor_cleanup(qfbiv);
return ret;
}
@@ -9,7 +9,7 @@
static void vmstate_subsection_save(Visitor *v, const VMStateDescription *vmsd,
void *opaque);
-static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+static int vmstate_subsection_load(Visitor *v, const VMStateDescription *vmsd,
void *opaque);
#define LOCAL_ERR_REPORT(fallout) \
@@ -19,11 +19,20 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
fallout \
}
-int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
+int vmstate_load_state(Visitor *v, const VMStateDescription *vmsd,
void *opaque, int version_id)
{
VMStateField *field = vmsd->fields;
int ret;
+ Error *local_err = NULL;
+ QEMUFile *wrapperf;
+ uint32_t tmp32;
+
+ /* BER type comes from the vmsd if it's set */
+ tmp32 = vmsd->ber_tag;
+ visit_start_sequence_compat(v, vmsd->name, VISIT_SEQ_COMPAT_VMSTATE,
+ &tmp32, &local_err);
+ LOCAL_ERR_REPORT(return -EINVAL;);
if (version_id > vmsd->version_id) {
return -EINVAL;
@@ -32,7 +41,18 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
return -EINVAL;
}
if (version_id < vmsd->minimum_version_id) {
- return vmsd->load_state_old(f, opaque, version_id);
+ /* Some visitors put old format things down separate QEMUFile's */
+ visit_start_sequence_compat(v, vmsd->name, VISIT_SEQ_COMPAT_BLOB,
+ &wrapperf, &local_err);
+ LOCAL_ERR_REPORT(return -EINVAL;);
+
+ ret = vmsd->load_state_old(visitor_get_qemufile(v), opaque, version_id);
+
+ visit_end_sequence_compat(v, vmsd->name, VISIT_SEQ_COMPAT_BLOB,
+ &local_err);
+ LOCAL_ERR_REPORT(return -EINVAL;);
+
+ return ret;
}
if (vmsd->pre_load) {
int ret = vmsd->pre_load(opaque);
@@ -69,6 +89,11 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
if (field->flags & VMS_POINTER) {
base_addr = *(void **)base_addr + field->start;
}
+
+ if (n_elems > 1) {
+ visit_start_array(v, NULL, field->name, n_elems, 0, &local_err);
+ }
+
for (i = 0; i < n_elems; i++) {
void *addr = base_addr + size * i;
@@ -76,30 +101,62 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
addr = *(void **)addr;
}
if (field->flags & VMS_STRUCT) {
- ret = vmstate_load_state(f, field->vmsd, addr,
+ ret = vmstate_load_state(v, field->vmsd, addr,
field->vmsd->version_id);
} else {
- ret = field->info->get(f, addr, size);
+ if (field->info->visit) {
+ ret = field->info->visit(v, addr, field->name, size,
+ &local_err);
+ } else {
+ /*
+ * Some visitors put old format things down separate
+ * QEMUFile's
+ */
+ visit_start_sequence_compat(v, field->name,
+ VISIT_SEQ_COMPAT_BLOB,
+ &wrapperf, &local_err);
+ LOCAL_ERR_REPORT(return -EINVAL;);
+ ret = field->info->get(wrapperf, addr, size);
+
+ visit_end_sequence_compat(v, field->name,
+ VISIT_SEQ_COMPAT_BLOB,
+ &local_err);
+ LOCAL_ERR_REPORT(return -EINVAL;);
+ }
}
if (ret < 0) {
return ret;
}
+
+ if ((i+1) != n_elems) {
+ visit_next_array(v, &local_err);
+ }
+ }
+
+ if (n_elems > 1) {
+ visit_end_array(v, &local_err);
}
+
}
field++;
}
- ret = vmstate_subsection_load(f, vmsd, opaque);
+ ret = vmstate_subsection_load(v, vmsd, opaque);
if (ret != 0) {
return ret;
}
if (vmsd->post_load) {
- return vmsd->post_load(opaque, version_id);
+ ret = vmsd->post_load(opaque, version_id);
}
- return 0;
+
+ visit_end_sequence_compat(v, vmsd->name, VISIT_SEQ_COMPAT_VMSTATE,
+ &local_err);
+ LOCAL_ERR_REPORT(return -EINVAL;);
+
+ return ret;
}
-void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
+void vmstate_save_state(Visitor *v, const VMStateDescription *vmsd,
void *opaque)
{
VMStateField *field = vmsd->fields;
@@ -208,44 +265,56 @@ static const VMStateDescription *
return NULL;
}
-static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+static int vmstate_subsection_load(Visitor *v, const VMStateDescription *vmsd,
void *opaque)
{
- while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
- char idstr[256];
+ Error *local_err = NULL;
+ int32_t section_type;
+
+ if (!vmsd->subsections) {
+ /*
+ * If the type has no subsection defined at all then skip completely
+ * Note that this means if we have conditional subsections we will
+ * expect a subseclist, even if it's empty because the conditions are
+ * all false.
+ */
+ return 0;
+ }
+
+ visit_start_sequence_compat(v, "subseclist", VISIT_SEQ_COMPAT_SUBSECLIST,
+ (char *)vmsd->name, &local_err);
+ while (visit_get_next_type(v, §ion_type, NULL, "subsec", &local_err),
+ ((local_err == NULL) && (section_type == QEMU_VM_SUBSECTION))) {
int ret;
- uint8_t version_id, len, size;
const VMStateDescription *sub_vmsd;
+ SectionHeader sh;
- len = qemu_peek_byte(f, 1);
- if (len < strlen(vmsd->name) + 1) {
- /* subsection name has be be "section_name/a" */
- return 0;
- }
- size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
- if (size != len) {
- return 0;
- }
- idstr[size] = 0;
+ visit_start_sequence_compat(v, "subsection",
+ VISIT_SEQ_COMPAT_SUBSECTION, &sh,
+ &local_err);
+ LOCAL_ERR_REPORT(return -EINVAL;);
- if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
- /* it don't have a valid subsection name */
- return 0;
- }
- sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
+ sub_vmsd = vmstate_subsection_name_lookup(vmsd->subsections, sh.idstr);
if (sub_vmsd == NULL) {
+ error_report("vmstate_subsection_load: failed to find subsection "
+ "%s in %s", sh.idstr, vmsd->name);
return -ENOENT;
}
- qemu_file_skip(f, 1); /* subsection */
- qemu_file_skip(f, 1); /* len */
- qemu_file_skip(f, len); /* idstr */
- version_id = qemu_get_be32(f);
- ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
+ ret = vmstate_load_state(v, sub_vmsd, opaque, sh.version_id);
if (ret) {
+ error_report("vmstate_subsection_load: load_state for %s failed "
+ "in %s", sh.idstr, vmsd->name);
return ret;
}
+ visit_end_sequence_compat(v, "subsection", VISIT_SEQ_COMPAT_SUBSECTION,
+ &local_err);
}
+ LOCAL_ERR_REPORT(return -EINVAL;);
+ visit_end_sequence_compat(v, "subseclist", VISIT_SEQ_COMPAT_SUBSECLIST,
+ &local_err);
+ LOCAL_ERR_REPORT(return -EINVAL;);
+
return 0;
}