new file mode 100644
@@ -0,0 +1,26 @@
+/*
+ * QEMUFile Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_FILE_BINARY_OUTPUT_VISITOR_H
+#define QEMU_FILE_BINARY_OUTPUT_VISITOR_H
+
+#include "visitor.h"
+
+typedef struct QemuFileBinOutputVisitor QemuFileBinOutputVisitor;
+
+QemuFileBinOutputVisitor *qemu_file_bin_output_visitor_new(QEMUFile *f);
+void qemu_file_bin_output_visitor_cleanup(QemuFileBinOutputVisitor *d);
+
+Visitor *qemu_file_bin_output_get_visitor(QemuFileBinOutputVisitor *v);
+
+#endif
@@ -1,5 +1,5 @@
util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
util-obj-y += string-input-visitor.o string-output-visitor.o
-
+util-obj-y += qemu-file-binary-output-visitor.o
util-obj-y += opts-visitor.o
new file mode 100644
@@ -0,0 +1,564 @@
+/*
+ * QEMUFile Output Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ * Copyright 2014 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ * David Gilbert <dgilbert@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/qemu-file-binary-output-visitor.h"
+#include "qapi/visitor-impl.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "migration/migration.h"
+
+#if 0
+#define DPRINTF(fmt, ...) \
+ do { \
+ fprintf(stderr, "qfbov/%s/%d: " fmt "\n", __func__, \
+ __LINE__, ## __VA_ARGS__); \
+ } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+/* TODO: This needs sharing between input/output binary visitors */
+typedef struct {
+ size_t elem_count;
+ size_t elem_size;
+ size_t pos;
+} ArrayInfo;
+
+typedef struct {
+ Visit_seq_compat_mode mode;
+ const void *data;
+} SeqCompatInfo;
+
+typedef struct StackEntry {
+ enum {
+ QFOV_ARRAY,
+ QFOV_LIST,
+ QFOV_STRUCT,
+ QFOV_SEQCOMPAT,
+ } type;
+ ArrayInfo array_info;
+ SeqCompatInfo seqcompat_info;
+ bool is_list_head;
+ QTAILQ_ENTRY(StackEntry) node;
+} StackEntry;
+
+struct QemuFileBinOutputVisitor {
+ Visitor visitor;
+ QTAILQ_HEAD(, StackEntry) stack;
+ QEMUFile *file;
+};
+
+static QemuFileBinOutputVisitor *to_ov(Visitor *v)
+{
+ return container_of(v, QemuFileBinOutputVisitor, visitor);
+}
+
+static void qfbo_push(QemuFileBinOutputVisitor *ov, StackEntry *e)
+{
+ QTAILQ_INSERT_HEAD(&ov->stack, e, node);
+}
+
+static void qfbo_push_array(QemuFileBinOutputVisitor *ov, ArrayInfo ai)
+{
+ StackEntry *e = g_malloc0(sizeof(*e));
+ e->type = QFOV_ARRAY;
+ e->array_info = ai;
+ qfbo_push(ov, e);
+}
+
+static void qfbo_push_list(QemuFileBinOutputVisitor *ov)
+{
+ StackEntry *e = g_malloc0(sizeof(*e));
+ e->type = QFOV_LIST;
+ e->is_list_head = true;
+ qfbo_push(ov, e);
+}
+
+static void qfbo_push_seqcompat(QemuFileBinOutputVisitor *ov,
+ SeqCompatInfo sci)
+{
+ StackEntry *e = g_malloc0(sizeof(*e));
+ e->type = QFOV_SEQCOMPAT;
+ e->seqcompat_info = sci;
+ qfbo_push(ov, e);
+}
+
+static void qfbo_push_struct(QemuFileBinOutputVisitor *ov)
+{
+ StackEntry *e = g_malloc0(sizeof(*e));
+ e->type = QFOV_STRUCT;
+ qfbo_push(ov, e);
+}
+
+static StackEntry *qfbo_pop(QemuFileBinOutputVisitor *ov)
+{
+ StackEntry *e = QTAILQ_FIRST(&ov->stack);
+ QTAILQ_REMOVE(&ov->stack, e, node);
+ return e;
+}
+
+static bool qfbo_is_array(QemuFileBinOutputVisitor *ov)
+{
+ StackEntry *e = QTAILQ_FIRST(&ov->stack);
+ return e && e->type == QFOV_ARRAY;
+}
+
+static bool qfbo_is_list(QemuFileBinOutputVisitor *ov)
+{
+ StackEntry *e = QTAILQ_FIRST(&ov->stack);
+ return e && e->type == QFOV_LIST;
+}
+
+/* If we are in a seqcompat list return true and fill in
+ * sci with the compat mode
+ */
+static bool qfbo_is_seqcompat(QemuFileBinOutputVisitor *ov,
+ SeqCompatInfo *sci)
+{
+ StackEntry *e = QTAILQ_FIRST(&ov->stack);
+ if (e && e->type == QFOV_SEQCOMPAT) {
+ *sci = e->seqcompat_info;
+ return true;
+ }
+ return false;
+}
+
+static void qfbo_start_struct(Visitor *v, void **obj,
+ const char *kind, const char *name,
+ size_t unused, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+
+ qfbo_push_struct(ov);
+}
+
+static void qfbo_end_struct(Visitor *v, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ StackEntry *e = qfbo_pop(ov);
+
+ if (!e || e->type != QFOV_STRUCT) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ return;
+ }
+ g_free(e);
+}
+
+static void qfbo_start_list(Visitor *v, const char *name,
+ Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ qfbo_push_list(ov);
+}
+
+static GenericList *qfbo_next_list(Visitor *v, GenericList **list,
+ Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ GenericList *entry;
+ StackEntry *e = QTAILQ_FIRST(&ov->stack);
+
+ /* Some users don't actually have a list */
+ if (!list) {
+ return NULL;
+ }
+ entry = *list;
+
+ if (!entry || !qfbo_is_list(ov)) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ }
+
+ /* The way the list iterator is currently used unfortunately clobbers
+ * **list by subseqently assigning our return value to the same container.
+ * This can cause an infinite loop, but we can get around this by tracking
+ * a bit of state to note when we should pass back the next entry rather
+ * than the current one.
+ */
+ if (e->is_list_head) {
+ e->is_list_head = false;
+ return entry;
+ }
+
+ *list = entry->next;
+ return entry->next;
+}
+
+static void qfbo_end_list(Visitor *v, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ StackEntry *e = qfbo_pop(ov);
+ if (!e || e->type != QFOV_LIST) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ }
+ g_free(e);
+}
+
+static void qfbo_start_array(Visitor *v, void **obj,
+ const char *name,
+ size_t elem_count,
+ size_t elem_size, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ ArrayInfo ai = {
+ .elem_count = elem_count,
+ .elem_size = elem_size,
+ .pos = 0
+ };
+ qfbo_push_array(ov, ai);
+}
+
+static void qfbo_next_array(Visitor *v, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ StackEntry *e = QTAILQ_FIRST(&ov->stack);
+ if (!qfbo_is_array(ov) ||
+ e->array_info.pos >= e->array_info.elem_count) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ }
+
+ e->array_info.pos++;
+}
+
+static void qfbo_end_array(Visitor *v, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ StackEntry *e = qfbo_pop(ov);
+ if (!e || e->type != QFOV_ARRAY) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ return;
+ }
+ g_free(e);
+}
+
+static void qfbo_type_str(Visitor *v, char **obj, const char *name,
+ Error **errp)
+{
+ if (obj) {
+ g_free(*obj);
+ }
+}
+
+/* A string upto 256 bytes in length (including terminator)
+ * output as length byte (not including term) followed by text
+ * (also not including term)
+ */
+static void qfbo_type_str256(Visitor *v, char *obj, const char *name,
+ Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ size_t len = strlen(obj);
+
+ DPRINTF("qfbo_type_str256: len=%d str=%s", (int)len, obj);
+
+ if (len > 255) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return;
+ }
+ qemu_put_byte(ov->file, (uint8_t)len);
+ qemu_put_buffer(ov->file, (uint8_t *)obj, len);
+}
+
+static void qfbo_type_buffer(Visitor *v, void *data, size_t len, bool async,
+ const char *name, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ if (async) {
+ qemu_put_buffer_async(ov->file, data, len);
+ } else {
+ qemu_put_buffer(ov->file, data, len);
+ }
+}
+
+static void qfbo_type_uint8(Visitor *v, uint8_t *obj,
+ const char *name,
+ Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ qemu_put_byte(ov->file, *obj);
+}
+
+static void qfbo_type_uint16(Visitor *v, uint16_t *obj,
+ const char *name, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ qemu_put_be16(ov->file, *obj);
+}
+
+static void qfbo_type_uint32(Visitor *v, uint32_t *obj,
+ const char *name, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ qemu_put_be32(ov->file, *obj);
+}
+
+static void qfbo_type_uint64(Visitor *v, uint64_t *obj,
+ const char *name, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ qemu_put_be64(ov->file, *obj);
+}
+
+static void qfbo_type_int8(Visitor *v, int8_t *obj,
+ const char *name, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ qemu_put_byte(ov->file, *obj);
+}
+
+static void qfbo_type_int16(Visitor *v, int16_t *obj,
+ const char *name, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ qemu_put_sbe16(ov->file, *obj);
+}
+
+static void qfbo_type_int32(Visitor *v, int32_t *obj,
+ const char *name, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ qemu_put_sbe32(ov->file, *obj);
+}
+
+static void qfbo_type_int64(Visitor *v, int64_t *obj,
+ const char *name, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ qemu_put_sbe64(ov->file, *obj);
+}
+
+static void qfbo_type_bool(Visitor *v, bool *obj, const char *name,
+ Error **errp)
+{
+ uint8_t val = *obj;
+ qfbo_type_uint8(v, &val, name, errp);
+}
+
+static QEMUFile *qfbo_get_qemufile(Visitor *v)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+
+ return ov->file;
+}
+
+/* this is a bit odd - I'm using 'get_next_type' to plant the compatibility
+ * bytes in lists.
+ */
+static void qfbo_get_next_type(Visitor *v, int *kind, const int *qobjects,
+ const char *name, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ SeqCompatInfo sci;
+
+ if (qfbo_is_seqcompat(ov, &sci)) {
+ /* The binary formats used here have a byte on each entry
+ * identifying the type, or a terminator (that varies
+ * in different lists).
+ */
+ uint8_t tmpbyte;
+ tmpbyte = *kind & 0xff;
+
+ DPRINTF("qfbo_get_next_type for %s", name);
+
+ switch (sci.mode) {
+ case VISIT_SEQ_COMPAT_BYTE0TERM:
+ qemu_put_byte(ov->file, tmpbyte);
+ break;
+
+ default:
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ return;
+ }
+ return;
+ }
+
+ /* Only dealing with SeqCompat's for the moment */
+ error_set(errp, QERR_UNDEFINED_ERROR);
+}
+
+static void qfbo_start_sequence_compat(Visitor *v, const char *name,
+ Visit_seq_compat_mode compat_mode,
+ void *opaque, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ SeqCompatInfo sci = {
+ .mode = compat_mode,
+ .data = opaque
+ };
+ SectionHeader *sh;
+ ramsecentry_header *rse_hdr;
+
+ switch (compat_mode) {
+ case VISIT_SEQ_COMPAT_FILE:
+ qemu_put_be32(ov->file, QEMU_VM_FILE_MAGIC);
+ qemu_put_be32(ov->file, QEMU_VM_FILE_VERSION);
+ break;
+
+ case VISIT_SEQ_COMPAT_SECTION_HEADER:
+ /*
+ * for VM_SECTION_FULL and VM_SECTION_START
+ * 'opaque' points to a struct Sectionheader
+ */
+ sh = opaque;
+ qemu_put_be32(ov->file, sh->section_id);
+ qemu_put_byte(ov->file, strlen(sh->idstr));
+ qemu_put_buffer(ov->file, (uint8_t *)sh->idstr, strlen(sh->idstr));
+ qemu_put_be32(ov->file, sh->instance_id);
+ qemu_put_be32(ov->file, sh->version_id);
+ break;
+
+ case VISIT_SEQ_COMPAT_SECTION_MIN:
+ /* for VM_SECTION_PART/END, the section name->ID is already known */
+ sh = opaque;
+ qemu_put_be32(ov->file, sh->section_id);
+ break;
+
+ case VISIT_SEQ_COMPAT_SUBSECTION: /* An element in a subsection list */
+ sh = opaque;
+ qemu_put_byte(ov->file, QEMU_VM_SUBSECTION);
+ qemu_put_byte(ov->file, strlen(sh->idstr));
+ qemu_put_buffer(ov->file, (uint8_t *)sh->idstr, strlen(sh->idstr));
+ qemu_put_be32(ov->file, sh->version_id);
+ break;
+
+ case VISIT_SEQ_COMPAT_BYTE0TERM:
+ case VISIT_SEQ_COMPAT_SUBSECLIST:
+ case VISIT_SEQ_COMPAT_RAMSECLIST:
+ case VISIT_SEQ_COMPAT_VMSTATE:
+ /* These don't need anything in the header on the compatibility side */
+ break;
+
+ case VISIT_SEQ_COMPAT_RAMSECENTRY:
+ rse_hdr = opaque;
+ qemu_put_be64(ov->file, rse_hdr->addr | rse_hdr->flags);
+ if ((rse_hdr->flags &
+ (RAM_SAVE_FLAG_CONTINUE | RAM_SAVE_FLAG_MEM_SIZE |
+ RAM_SAVE_FLAG_HOOK)) == 0) {
+ qemu_put_byte(ov->file, strlen(rse_hdr->idstr));
+ qemu_put_buffer(ov->file, (uint8_t *)rse_hdr->idstr,
+ strlen(rse_hdr->idstr));
+ }
+ break;
+
+ case VISIT_SEQ_COMPAT_BLOB:
+ /*
+ * Nothing in the header, but opaque gets a copy of our QEMUFile -
+ * other implementations might give a different QEMUFile
+ */
+ *(QEMUFile **)opaque = ov->file;
+ break;
+
+ }
+
+ DPRINTF("qfbo_start_sequence_compat for %s\n", name);
+ qfbo_push_seqcompat(ov, sci);
+ /* We don't need to read anything at this point */
+}
+
+static void qfbo_end_sequence_compat(Visitor *v, const char *name,
+ Visit_seq_compat_mode compat_mode,
+ Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+ StackEntry *e = qfbo_pop(ov);
+ if (!e || e->type != QFOV_SEQCOMPAT) {
+ error_setg(errp, "bad struct stack %d", e ? e->type : -1);
+ if (e) {
+ g_free(e);
+ }
+ return;
+ }
+ switch (e->seqcompat_info.mode) {
+ case VISIT_SEQ_COMPAT_BYTE0TERM:
+ qemu_put_byte(ov->file, 0);
+ break;
+
+ case VISIT_SEQ_COMPAT_RAMSECLIST:
+ qemu_put_be64(ov->file, RAM_SAVE_FLAG_EOS);
+ break;
+
+ default:
+ break;
+ }
+
+ if (e->seqcompat_info.mode != compat_mode) {
+ error_setg(errp, "mismatched seqcompat mode %d/%d", compat_mode,
+ e->seqcompat_info.mode);
+ }
+
+ DPRINTF("qfbo_end_sequence_compat %s\n", name);
+ g_free(e);
+}
+
+static void qfbo_destroy(Visitor *v, Error **errp)
+{
+ QemuFileBinOutputVisitor *ov = to_ov(v);
+
+ qemu_file_bin_output_visitor_cleanup(ov);
+}
+
+Visitor *qemu_file_bin_output_get_visitor(QemuFileBinOutputVisitor *v)
+{
+ return &v->visitor;
+}
+
+void qemu_file_bin_output_visitor_cleanup(QemuFileBinOutputVisitor *ov)
+{
+ g_free(ov);
+}
+
+QemuFileBinOutputVisitor *qemu_file_bin_output_visitor_new(QEMUFile *f)
+{
+ QemuFileBinOutputVisitor *v;
+
+ v = g_malloc0(sizeof(*v));
+
+ v->file = f;
+
+ v->visitor.start_struct = qfbo_start_struct;
+ v->visitor.end_struct = qfbo_end_struct;
+ v->visitor.start_list = qfbo_start_list;
+ v->visitor.next_list = qfbo_next_list;
+ v->visitor.end_list = qfbo_end_list;
+ v->visitor.start_array = qfbo_start_array;
+ v->visitor.next_array = qfbo_next_array;
+ v->visitor.end_array = qfbo_end_array;
+ v->visitor.type_buffer = qfbo_type_buffer;
+ v->visitor.type_int = qfbo_type_int64;
+ v->visitor.type_uint8 = qfbo_type_uint8;
+ v->visitor.type_uint16 = qfbo_type_uint16;
+ v->visitor.type_uint32 = qfbo_type_uint32;
+ v->visitor.type_uint64 = qfbo_type_uint64;
+ v->visitor.type_int8 = qfbo_type_int8;
+ v->visitor.type_int16 = qfbo_type_int16;
+ v->visitor.type_int32 = qfbo_type_int32;
+ v->visitor.type_int64 = qfbo_type_int64;
+ v->visitor.type_bool = qfbo_type_bool;
+ v->visitor.type_str = qfbo_type_str;
+ v->visitor.type_str256 = qfbo_type_str256;
+ v->visitor.destroy = qfbo_destroy;
+ v->visitor.start_sequence_compat = qfbo_start_sequence_compat;
+ v->visitor.get_next_type = qfbo_get_next_type;
+ v->visitor.end_sequence_compat = qfbo_end_sequence_compat;
+ v->visitor.get_qemufile = qfbo_get_qemufile;
+
+ v->visitor.flags = VISITOR_SAVING;
+
+ QTAILQ_INIT(&v->stack);
+
+ return v;
+}