diff mbox

[RFC,12/16] BER Visitor: Create output visitor

Message ID 1395778647-30925-13-git-send-email-dgilbert@redhat.com
State New
Headers show

Commit Message

Dr. David Alan Gilbert March 25, 2014, 8:17 p.m. UTC
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>

ber.h: Add VMSTATE_CPU_COMMON
---
 include/qapi/ber.h                          | 108 ++++
 include/qapi/qemu-file-ber-output-visitor.h |  26 +
 qapi/Makefile.objs                          |   1 +
 qapi/qemu-file-ber-output-visitor.c         | 916 ++++++++++++++++++++++++++++
 4 files changed, 1051 insertions(+)
 create mode 100644 include/qapi/ber.h
 create mode 100644 include/qapi/qemu-file-ber-output-visitor.h
 create mode 100644 qapi/qemu-file-ber-output-visitor.c
diff mbox

Patch

diff --git a/include/qapi/ber.h b/include/qapi/ber.h
new file mode 100644
index 0000000..960bb17
--- /dev/null
+++ b/include/qapi/ber.h
@@ -0,0 +1,108 @@ 
+/*
+ * ASN.1 Basic Encoding Rules Common functions
+ *
+ * Copyright IBM, Corp. 2011, 2013
+ * Copyright Red Hat, Inc. 2011
+ *
+ * Authors:
+ *  Stefan Berger     <stefanb@us.ibm.com>
+ *  Michael Tsirkin   <mst@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.
+ *
+ */
+#ifndef QAPI_BER_H
+#define QAPI_BER_H
+
+/*
+ * This is a subset of BER for QEMU use.
+ * QEMU will use the DER encoding always with one extension from
+ * CER: SET and SEQUENCE types can have indefinite-length encoding
+ * if the encoding is not all immediately available.
+ *
+ * We assume that SET encodings can be available or not available,
+ * and that SEQUENCE encodings are available unless a SEQUENCE includes
+ * a non-available SET.
+ *
+ * The last is an extension to allow an arbitrarily large SET
+ * to be produced online without knowing the length in advance.
+ *
+ * All types used shall be universal, with explicit tagging, to simplify
+ * use by external tools.
+ */
+
+
+#define BER_TYPE_CLASS_SHIFT  6
+#define BER_TYPE_PC_SHIFT     5
+
+typedef enum ber_type_class {
+    BER_TYPE_CLASS_UNIVERSAL = 0x0 << BER_TYPE_CLASS_SHIFT,
+    BER_TYPE_CLASS_APPLICATION = 0x1 << BER_TYPE_CLASS_SHIFT,
+    BER_TYPE_CLASS_CONTENT_SPECIFIC = 0x2 << BER_TYPE_CLASS_SHIFT,
+    BER_TYPE_CLASS_PRIVATE = 0x3 << BER_TYPE_CLASS_SHIFT,
+    BER_TYPE_CLASS_MASK = 0x3 << BER_TYPE_CLASS_SHIFT /* Mask to get class */
+} BERTypeClass;
+
+/* P/C bit */
+typedef enum ber_type_p_c {
+    BER_TYPE_PRIMITIVE = 0x0 << BER_TYPE_PC_SHIFT,
+    BER_TYPE_CONSTRUCTED = 0x1 << BER_TYPE_PC_SHIFT,
+    BER_TYPE_P_C_MASK = 0x1 << BER_TYPE_PC_SHIFT /* Mask to get P/C bit */
+} BERTypePC;
+
+typedef enum ber_type_tag {
+    BER_TYPE_EOC          =  0 /*  P        0       0*/,
+    BER_TYPE_BOOLEAN      =  1 /*  P        1       1*/,
+    BER_TYPE_INTEGER      =  2 /*  P        2       2*/,
+    BER_TYPE_OCTET_STRING =  4 /*  P/C      4       4*/,
+    BER_TYPE_NULL         =  5 /*  P        5       5*/,
+    BER_TYPE_UTF8_STRING  = 12 /*  P/C      12      C*/,
+    BER_TYPE_SEQUENCE     = 16 /*  C        16      10*/,
+    BER_TYPE_SET          = 17 /*  C        17      11*/,
+  /*BER_TYPE_NUMERIC_STRING       P/C      18      12*/
+  /*BER_TYPE_PRINTABLE_STRING     P/C      19      13*/
+  /*BER_TYPE_T61STRING            P/C      20      14*/
+  /*BER_TYPE_VIDEOTEX_STRING      P/C      21      15*/
+  /*BER_TYPE_IA5_STRING           P/C      22      16*/
+  /*BER_TYPE_UTCTIME              P/C      23      17*/
+  /*BER_TYPE_GENERALIZED_TIME     P/C      24      18*/
+  /*BER_TYPE_GRAPHIC_STRING       P/C      25      19*/
+  /*BER_TYPE_VISIBLE_STRING       P/C      26      1A*/
+  /*BER_TYPE_GENERAL_STRING       P/C      27      1B*/
+  /*BER_TYPE_UNIVERSAL_STRING     P/C      28      1C*/
+  /*BER_TYPE_CHARACTER_STRING     P/C      29      1D*/
+  /*BER_TYPE_BMP_STRING           P/C      30      1E*/
+    BER_TYPE_LONG_FORM    = 31/*  -        31      1F*/,
+    BER_TYPE_TAG_MASK = 0x1f /* Mask to get tag */,
+    BER_TYPE_CUSTOM_LIST = 0x20,
+
+    /* It would be nice to autoderive this from the spec file somehow */
+    BER_TYPE_QEMU_FILE = 1270481, /* come out as 7f cd c5 51  - the 51 is Q
+                                   * the c5 and cd being E,M but with the top
+                                   * bit set which BER requires.
+                                   */
+    BER_TYPE_QEMU_RAMSEC_ENTRY =  8914,
+    BER_TYPE_QEMU_RAMSEC_LIST  =  9810,
+    BER_TYPE_QEMU_SEC_MIN      =   211,
+    BER_TYPE_QEMU_SEC_FULL     =  2003,
+    BER_TYPE_QEMU_SUBSEC_LIST  = 10700,
+    BER_TYPE_QEMU_SUBSEC       = 10707,
+
+    /* Specific VMState types */
+    BER_TYPE_QEMU_VMSTATE_CPU_COMMON = 20000,
+} BERTypeTag;
+
+typedef enum ber_length {
+    /* Special length values */
+    BER_LENGTH_INDEFINITE = 0x1 << 7,
+    BER_LENGTH_RESERVED = 0xFF,
+    /* Anything else is either short or long */
+    BER_LENGTH_SHORT = 0x0 << 7,
+    BER_LENGTH_LONG = 0x1 << 7,
+    BER_LENGTH_SHORT_LONG_MASK = 0x1 << 7,
+    BER_LENGTH_MASK = 0x7F,
+} BERLength;
+
+#endif /* QAPI_BER_H */
+
diff --git a/include/qapi/qemu-file-ber-output-visitor.h b/include/qapi/qemu-file-ber-output-visitor.h
new file mode 100644
index 0000000..341b28a
--- /dev/null
+++ b/include/qapi/qemu-file-ber-output-visitor.h
@@ -0,0 +1,26 @@ 
+/*
+ * QEMUFile BER format output visitor
+ *
+ * Copyright 2014 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ *  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.
+ *
+ */
+
+#ifndef QEMU_FILE_BER_OUTPUT_VISITOR_H
+#define QEMU_FILE_BER_OUTPUT_VISITOR_H
+
+#include "visitor.h"
+
+typedef struct QemuFileBEROutputVisitor QemuFileBEROutputVisitor;
+
+QemuFileBEROutputVisitor *qemu_file_ber_output_visitor_new(QEMUFile *f);
+void qemu_file_ber_output_visitor_cleanup(QemuFileBEROutputVisitor *d);
+
+Visitor *qemu_file_ber_output_get_visitor(QemuFileBEROutputVisitor *v);
+
+#endif
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 3d9d47a..f8fb347 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -2,5 +2,6 @@  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 qemu-file-binary-input-visitor.o
+util-obj-y += qemu-file-ber-output-visitor.o
 util-obj-y += qemu-file-debug-output-visitor.o
 util-obj-y += opts-visitor.o
diff --git a/qapi/qemu-file-ber-output-visitor.c b/qapi/qemu-file-ber-output-visitor.c
new file mode 100644
index 0000000..b38a184
--- /dev/null
+++ b/qapi/qemu-file-ber-output-visitor.c
@@ -0,0 +1,916 @@ 
+/*
+ * QEMUFile Output Visitor
+ *
+ * Copyright 2014 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ *  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/ber.h"
+#include "qapi/qemu-file-ber-output-visitor.h"
+#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"
+
+/* Note that this can generate so much debug virt-test times out */
+#if 0
+#define DPRINTF(v, fmt, ...) \
+    do { \
+        fprintf(stderr, "qfberov/%s/%d: " fmt "\n", __func__, \
+                __LINE__, ## __VA_ARGS__); \
+    } while (0)
+#else
+#define DPRINTF(v, 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;
+    QEMUFile             *binfile;
+} 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 QemuFileBEROutputVisitor {
+    Visitor visitor;
+    QTAILQ_HEAD(, StackEntry) stack;
+    QEMUFile *file;
+};
+
+static QemuFileBEROutputVisitor *to_ov(Visitor *v)
+{
+    return container_of(v, QemuFileBEROutputVisitor, visitor);
+}
+
+/* Encode 'ber_type' into buffer, returning the number of bytes used */
+static unsigned int ber_encode_type(uint8_t *buffer, uint32_t buflen,
+                                    uint64_t ber_type,
+                                    uint8_t ber_type_flags,
+                                    Error **errp)
+{
+    unsigned int idx = 0;
+
+    if (buflen < 1) {
+        error_set(errp, QERR_BUFFER_OVERRUN);
+        return 0;
+    }
+
+    if (ber_type > BER_TYPE_LONG_FORM) {
+        int byte = 4;
+        uint32_t mask = 0x7f << (7 * byte);
+        bool do_write = false;
+
+        buffer[0] = ber_type_flags | BER_TYPE_LONG_FORM;
+
+        while (byte >= 0) {
+            if (!do_write) {
+                if ((mask & ber_type)) {
+                    do_write = true;
+                    if (1 + byte + 1 > buflen) {
+                        error_set(errp, QERR_BUFFER_OVERRUN);
+                        return 0;
+                    }
+                }
+            }
+            if (do_write) {
+                buffer[1 + idx] = (ber_type >> (7 * byte)) & 0x7f;
+                if (byte > 0) {
+                    buffer[1 + idx] |= 0x80;
+                }
+                idx++;
+            }
+            byte--;
+            mask = 0x7f << (7 * byte);
+        }
+    } else {
+        buffer[0] = ber_type | ber_type_flags;
+    }
+    return 1 + idx;
+}
+
+/* Write the header (to f) for a SEQUENCE with indefinite length
+ * of the given type/flags
+ */
+static void ber_start_indeflen_seq(QEMUFile *f, uint64_t ber_type,
+                                   uint8_t ber_type_flags,
+                                   Error **errp)
+{
+    const unsigned int BUFLEN = 16;
+    uint8_t tmpbuf[BUFLEN];
+    size_t count;
+
+    count = ber_encode_type(tmpbuf, BUFLEN, ber_type, ber_type_flags, errp);
+    if (*errp) {
+        return;
+    }
+    tmpbuf[count++] = BER_LENGTH_INDEFINITE;
+
+    qemu_put_buffer(f, tmpbuf, count);
+}
+
+/* Write the terminator (00 00) for an object of indefinite length */
+static void ber_write_eoc(QEMUFile *f)
+{
+    qemu_put_be16(f, 0);
+}
+
+static unsigned int ber_encode_len(uint8_t *buffer, uint32_t buflen,
+                                   uint64_t len, Error **errp)
+{
+    uint64_t mask = 0xFF00000000000000ULL;
+    int shift =  64 - 8;
+    int c = 0;
+
+    if (len <= 0x7f && buflen >= 1) {
+        buffer[0] = len;
+        return 1;
+    }
+
+    while (mask && (mask & len) == 0) {
+        mask >>= 8;
+        shift -= 8;
+    }
+
+    while (shift >= 0) {
+        if (1 + c + 1 > buflen) {
+            error_set(errp, QERR_BUFFER_OVERRUN);
+            return 0;
+        }
+        buffer[1+c] = len >> shift;
+        c++;
+        shift -= 8;
+    }
+
+    buffer[0] = BER_LENGTH_LONG | c;
+
+    return 1 + c;
+}
+
+/* BER integers are encoded in a packed format that gets longer depending
+ * on the value; you'd normally call this with
+ * ber_type=BER_TYPE_INTEGER ber_type_flags=BER_TYPE_CLASS_UNIVERSAL
+ */
+static unsigned int ber_write_uint(QEMUFile *f, uint64_t val, uint64_t ber_type,
+                                   uint8_t ber_type_flags, Error **errp)
+{
+    const unsigned int BUFLEN = 32; /* Can't ever run out of room for
+                                       upto 64bit type and value */
+    uint8_t buffer[BUFLEN];
+    unsigned int offset, len_offset;
+    int shift;
+
+    offset = ber_encode_type(buffer, BUFLEN, ber_type, ber_type_flags, errp);
+    if (*errp) {
+        return 0;
+    }
+
+    /* Leave a gap to put the length in */
+    len_offset = offset;
+    offset++;
+
+    /* Find the MSByte that's none-0 */
+    for (shift = (64-8); shift; shift -= 8) {
+        if (val & (0xfful << shift)) {
+            break;
+        }
+    }
+
+    /* BER integers are always (inconveniently) signed, so if the MSBit
+     * of the 1st byte to be sent is set, we must put a leading 0 in
+     */
+    if (val & (0x80ul << shift)) {
+        buffer[offset++] = 0;
+    }
+    do {
+        buffer[offset++] = (val >> shift) & 0xff;
+        shift -= 8;
+    } while (shift >= 0);
+
+    buffer[len_offset] = (offset - len_offset)-1;
+
+    qemu_put_buffer(f, buffer, offset);
+
+    return offset;
+}
+
+/* BER integers are encoded in a packed format that gets longer depending
+ * on the value; you'd normally call this with
+ * ber_type=BER_TYPE_INTEGER ber_type_flags=BER_TYPE_CLASS_UNIVERSAL
+ */
+static unsigned int ber_write_int(QEMUFile *f, int64_t val, uint64_t ber_type,
+                                   uint8_t ber_type_flags, Error **errp)
+{
+    const unsigned int BUFLEN = 32; /* Can't ever run out of room for
+                                       upto 64bit type and value */
+    uint8_t buffer[BUFLEN];
+    unsigned int offset, len_offset;
+    int shift;
+    bool isneg = val < 0;
+    /* What a byte of sign extension looks like */
+    uint8_t signbyte = isneg ? 0xff : 0;
+    uint64_t uval = (uint64_t) val;
+
+    offset = ber_encode_type(buffer, BUFLEN, ber_type, ber_type_flags, errp);
+    if (*errp) {
+        return 0;
+    }
+
+    /* Leave a gap to put the length in */
+    len_offset = offset;
+    offset++;
+
+    /* Find the MSByte that's none-sign-extension */
+    for (shift = (64-8); shift; shift -= 8) {
+        if (((uval >> shift) & 0xff) != signbyte) {
+            break;
+        }
+    }
+
+    do {
+        buffer[offset++] = (uval >> shift) & 0xff;
+        shift -= 8;
+    } while (shift >= 0);
+
+    buffer[len_offset] = (offset - len_offset)-1;
+
+    qemu_put_buffer(f, buffer, offset);
+
+    return offset;
+}
+
+/* BER string is type, length, and then data */
+static unsigned int ber_write_string(QEMUFile *f, const char* val,
+                                     uint64_t ber_type, uint8_t ber_type_flags,
+                                     Error **errp)
+{
+    const unsigned int BUFLEN = 32; /* Can't ever run out of room for
+                                       upto 64bit type and length */
+    uint8_t buffer[BUFLEN];
+    unsigned int offset;
+    uint64_t len;
+
+    offset = ber_encode_type(buffer, BUFLEN/2, ber_type, ber_type_flags, errp);
+    len = strlen(val);
+    offset += ber_encode_len(buffer+offset, BUFLEN/2, len, errp);
+    if (*errp) {
+        return 0;
+    }
+
+    qemu_put_buffer(f, buffer, offset);
+    qemu_put_buffer(f, (const uint8_t *)val, len);
+
+    return offset;
+}
+
+static void qfbero_push(QemuFileBEROutputVisitor *ov, StackEntry *e)
+{
+    QTAILQ_INSERT_HEAD(&ov->stack, e, node);
+}
+
+static void qfbero_push_array(QemuFileBEROutputVisitor *ov, ArrayInfo ai)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_ARRAY;
+    e->array_info = ai;
+    qfbero_push(ov, e);
+}
+
+static void qfbero_push_list(QemuFileBEROutputVisitor *ov)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_LIST;
+    e->is_list_head = true;
+    qfbero_push(ov, e);
+}
+
+static void qfbero_push_seqcompat(QemuFileBEROutputVisitor *ov,
+                                SeqCompatInfo sci)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_SEQCOMPAT;
+    e->seqcompat_info = sci;
+    qfbero_push(ov, e);
+}
+
+static void qfbero_push_struct(QemuFileBEROutputVisitor *ov)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_STRUCT;
+    qfbero_push(ov, e);
+}
+
+static StackEntry *qfbero_pop(QemuFileBEROutputVisitor *ov)
+{
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    QTAILQ_REMOVE(&ov->stack, e, node);
+    return e;
+}
+
+static bool qfbero_is_array(QemuFileBEROutputVisitor *ov)
+{
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    return e && e->type == QFOV_ARRAY;
+}
+
+static bool qfbero_is_list(QemuFileBEROutputVisitor *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 qfbero_is_seqcompat(QemuFileBEROutputVisitor *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 qfbero_start_struct(Visitor *v, void **obj,
+                                          const char *kind, const char *name,
+                                          size_t unused, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+
+    qfbero_push_struct(ov);
+
+    ber_start_indeflen_seq(ov->file, BER_TYPE_SEQUENCE,
+                  BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED, errp);
+}
+
+static void qfbero_end_struct(Visitor *v, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    StackEntry *e = qfbero_pop(ov);
+
+    if (!e || e->type != QFOV_STRUCT) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+
+    ber_write_eoc(ov->file);
+
+    g_free(e);
+}
+
+static void qfbero_start_list(Visitor *v, const char *name,
+                                        Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    qfbero_push_list(ov);
+
+    ber_start_indeflen_seq(ov->file, BER_TYPE_SEQUENCE,
+                  BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED, errp);
+}
+
+static GenericList *qfbero_next_list(Visitor *v, GenericList **list,
+                                           Error **errp)
+{
+    QemuFileBEROutputVisitor *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 || !qfbero_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 qfbero_end_list(Visitor *v, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    StackEntry *e = qfbero_pop(ov);
+    if (!e || e->type != QFOV_LIST) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+
+    ber_write_eoc(ov->file);
+
+    g_free(e);
+}
+
+static void qfbero_start_array(Visitor *v, void **obj,
+                                         const char *name,
+                                         size_t elem_count,
+                                         size_t elem_size, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    ArrayInfo ai = {
+        .elem_count = elem_count,
+        .elem_size = elem_size,
+        .pos = 0
+    };
+    qfbero_push_array(ov, ai);
+
+    ber_start_indeflen_seq(ov->file, BER_TYPE_SEQUENCE,
+                  BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED, errp);
+}
+
+static void qfbero_next_array(Visitor *v, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    if (!qfbero_is_array(ov) ||
+        e->array_info.pos >= e->array_info.elem_count) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+
+    e->array_info.pos++;
+}
+
+static void qfbero_end_array(Visitor *v, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    StackEntry *e = qfbero_pop(ov);
+    if (!e || e->type != QFOV_ARRAY) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    ber_write_eoc(ov->file);
+    g_free(e);
+}
+
+static void qfbero_type_str(Visitor *v, char **obj, const char *name,
+                                      Error **errp)
+{
+    if (obj) {
+        g_free(*obj);
+    }
+    assert(0);
+}
+
+/* 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 qfbero_type_str256(Visitor *v, char *obj, const char *name,
+                                      Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    ber_write_string(ov->file, obj, BER_TYPE_UTF8_STRING,
+                     BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+}
+
+static void qfbero_type_buffer(Visitor *v, void *data, size_t len, bool async,
+                                          const char *name, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    const unsigned int BUFLEN = 32; /* Can't ever run out of room for
+                                       upto 64bit type and length */
+    uint8_t buffer[BUFLEN];
+    unsigned int offset;
+
+    offset = ber_encode_type(buffer, BUFLEN/2, BER_TYPE_OCTET_STRING,
+                             BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL,
+                             errp);
+    offset += ber_encode_len(buffer+offset, BUFLEN/2, len, errp);
+    if (*errp) {
+        return;
+    }
+    qemu_put_buffer(ov->file, buffer, offset);
+
+    /* And now the data */
+    if (async) {
+        qemu_put_buffer_async(ov->file, data, len);
+    } else {
+        qemu_put_buffer(ov->file, data, len);
+    }
+}
+
+static void qfbero_type_uint8(Visitor *v, uint8_t *obj,
+                                          const char *name,
+                                          Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    ber_write_uint(ov->file, *obj, BER_TYPE_INTEGER,
+                   BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+}
+
+static void qfbero_type_uint16(Visitor *v, uint16_t *obj,
+                                           const char *name, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    ber_write_uint(ov->file, *obj, BER_TYPE_INTEGER,
+                   BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+}
+
+static void qfbero_type_uint32(Visitor *v, uint32_t *obj,
+                                           const char *name, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    ber_write_uint(ov->file, *obj, BER_TYPE_INTEGER,
+                   BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+}
+
+static void qfbero_type_uint64(Visitor *v, uint64_t *obj,
+                                           const char *name, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    ber_write_uint(ov->file, *obj, BER_TYPE_INTEGER,
+                   BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+}
+
+static void qfbero_type_int8(Visitor *v, int8_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    ber_write_int(ov->file, *obj, BER_TYPE_INTEGER,
+                  BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+}
+
+static void qfbero_type_int16(Visitor *v, int16_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    ber_write_int(ov->file, *obj, BER_TYPE_INTEGER,
+                  BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+}
+
+static void qfbero_type_int32(Visitor *v, int32_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    ber_write_int(ov->file, *obj, BER_TYPE_INTEGER,
+                  BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+}
+
+static void qfbero_type_int64(Visitor *v, int64_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    ber_write_int(ov->file, *obj, BER_TYPE_INTEGER,
+                  BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+}
+
+static void qfbero_type_bool(Visitor *v, bool *obj, const char *name,
+                                   Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    uint8_t val = *obj;
+    uint8_t buffer[3];
+
+    buffer[0] = BER_TYPE_BOOLEAN | BER_TYPE_CLASS_UNIVERSAL |
+                BER_TYPE_PRIMITIVE;
+    buffer[1] = 1; /* Length byte! */
+    buffer[2] = val ? 0xff : 0;
+    qemu_put_buffer(ov->file, buffer, 3);
+}
+
+static QEMUFile *qfbero_get_qemufile(Visitor *v)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+
+    return ov->file;
+}
+
+static void qfbero_get_next_type(Visitor *v, int *kind, const int *qobjects,
+                               const char *name, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    SeqCompatInfo sci;
+
+    if (qfbero_is_seqcompat(ov, &sci)) {
+        DPRINTF(ov, "qfbero_get_next_type for %s", name);
+
+        switch (sci.mode) {
+        case VISIT_SEQ_COMPAT_BYTE0TERM:
+            /* BER doesn't need to do anything */
+            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 qfbero_start_sequence_compat(Visitor *v, const char *name,
+                                       Visit_seq_compat_mode compat_mode,
+                                       void *opaque, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    SeqCompatInfo sci = {
+        .mode = compat_mode,
+        .data = opaque
+    };
+    SectionHeader *sh;
+    ramsecentry_header *rse_hdr;
+    uint32_t tmp32;
+
+    switch (compat_mode) {
+    case VISIT_SEQ_COMPAT_FILE:
+        ber_start_indeflen_seq(ov->file, BER_TYPE_QEMU_FILE,
+                  BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED, errp);
+        ber_write_uint(ov->file, 3 /* our version */, BER_TYPE_INTEGER,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        break;
+
+    case VISIT_SEQ_COMPAT_BYTE0TERM: /* TODO: Rename, this is actually 'top' */
+        /* Forms a sequence of 'sections' - just a normal sequence */
+        ber_start_indeflen_seq(ov->file, BER_TYPE_SEQUENCE,
+                       BER_TYPE_CONSTRUCTED | BER_TYPE_CLASS_UNIVERSAL, errp);
+        break;
+
+    case VISIT_SEQ_COMPAT_RAMSECLIST:
+        ber_start_indeflen_seq(ov->file, BER_TYPE_QEMU_RAMSEC_LIST,
+                       BER_TYPE_CONSTRUCTED | BER_TYPE_CLASS_APPLICATION, errp);
+        break;
+
+    case VISIT_SEQ_COMPAT_SECTION_HEADER:
+        sh = opaque;
+        ber_start_indeflen_seq(ov->file, BER_TYPE_QEMU_SEC_FULL,
+                  BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED, errp);
+        ber_write_string(ov->file, sh->idstr, BER_TYPE_UTF8_STRING,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        ber_write_uint(ov->file, sh->section_id, BER_TYPE_INTEGER,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        ber_write_uint(ov->file, sh->instance_id, BER_TYPE_INTEGER,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        ber_write_uint(ov->file, sh->version_id, BER_TYPE_INTEGER,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        break;
+
+    case VISIT_SEQ_COMPAT_SECTION_MIN:
+        /* for VM_SECTION_PART/END where the section name->ID is already known
+           TODO: Actually, lets route the name through anyway */
+        sh = opaque;
+        ber_start_indeflen_seq(ov->file, BER_TYPE_QEMU_SEC_MIN,
+                  BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED, errp);
+        ber_write_uint(ov->file, sh->section_id, BER_TYPE_INTEGER,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        break;
+
+    case VISIT_SEQ_COMPAT_SUBSECTION: /* An element in a subsection list */
+        sh = opaque;
+        ber_start_indeflen_seq(ov->file, BER_TYPE_QEMU_SUBSEC,
+                  BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED, errp);
+        ber_write_string(ov->file, sh->idstr, BER_TYPE_UTF8_STRING,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        ber_write_uint(ov->file, sh->version_id, BER_TYPE_INTEGER,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        break;
+
+    case VISIT_SEQ_COMPAT_SUBSECLIST:
+        ber_start_indeflen_seq(ov->file, BER_TYPE_QEMU_SUBSEC_LIST,
+                  BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED, errp);
+        break;
+
+    case VISIT_SEQ_COMPAT_RAMSECENTRY:
+        rse_hdr = opaque;
+        /* TODO: This is a bit big for the 0 page cases */
+        ber_start_indeflen_seq(ov->file, BER_TYPE_QEMU_RAMSEC_ENTRY,
+                  BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED, errp);
+        ber_write_uint(ov->file, rse_hdr->addr, BER_TYPE_INTEGER,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        ber_write_uint(ov->file, rse_hdr->flags, BER_TYPE_INTEGER,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+
+        if ((rse_hdr->flags &
+             (RAM_SAVE_FLAG_CONTINUE | RAM_SAVE_FLAG_MEM_SIZE |
+              RAM_SAVE_FLAG_HOOK)) == 0) {
+            ber_write_string(ov->file, rse_hdr->idstr, BER_TYPE_UTF8_STRING,
+                       BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        }
+        break;
+
+    case VISIT_SEQ_COMPAT_VMSTATE:
+        /*
+         * VMState's are just sequences, but the type tag is determined by
+         * the opaque, as follows
+         *   0 - a flag meaning use a universal sequence tag
+         * else - the tag to use with an BER_TYPE_CLASS_APPLICATION class
+         */
+        tmp32 = *(uint32_t *)opaque;
+        if (tmp32 == 0) {
+            ber_start_indeflen_seq(ov->file, BER_TYPE_SEQUENCE,
+                       BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED, errp);
+        } else {
+            ber_start_indeflen_seq(ov->file, (enum ber_type_tag)tmp32,
+                       BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED, errp);
+        }
+        break;
+
+    case VISIT_SEQ_COMPAT_BLOB: {
+        /* Note: There should be no other calls to this visitor until the
+        * end_sequence_compat for this blob. */
+        QemuFileBinOutputVisitor *qfbov;
+        Visitor *bv;
+
+        /* Write the blob data into a qemu_buf file - i.e. memory blob
+         * and hand that back as a QEMUFile to the caller
+         */
+        sci.binfile = qemu_bufopen("w", NULL);
+        /* and give that wrapper a binary output visitor so that it keeps
+         * substructures in compatibility mode
+         */
+        qfbov = qemu_file_bin_output_visitor_new(sci.binfile);
+        bv = qemu_file_bin_output_get_visitor(qfbov);
+        qemu_file_set_tmp_visitor(sci.binfile, bv);
+
+        *(QEMUFile **)opaque = sci.binfile;
+        break;
+        }
+
+    }
+
+    DPRINTF(qfbov, "qfbero_start_sequence_compat for %s/%d", name, compat_mode);
+    qfbero_push_seqcompat(ov, sci);
+    /* We don't need to read anything at this point */
+}
+
+static void qfbero_end_sequence_compat(Visitor *v, const char *name,
+                                     Visit_seq_compat_mode compat_mode,
+                                     Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+    StackEntry *e = qfbero_pop(ov);
+    Visitor *bv;
+    const QEMUSizedBuffer *qsb;
+    size_t len, cur_iov;
+    const unsigned int BUFLEN = 32; /* Can't ever run out of room for
+                                       upto 64bit type and length */
+    uint8_t hdrbuf[BUFLEN];
+    unsigned int offset;
+
+    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_FILE:
+    case VISIT_SEQ_COMPAT_BYTE0TERM:
+    case VISIT_SEQ_COMPAT_SECTION_MIN:
+    case VISIT_SEQ_COMPAT_SECTION_HEADER:
+    case VISIT_SEQ_COMPAT_RAMSECLIST:
+    case VISIT_SEQ_COMPAT_RAMSECENTRY:
+    case VISIT_SEQ_COMPAT_SUBSECLIST:
+    case VISIT_SEQ_COMPAT_SUBSECTION:
+    case VISIT_SEQ_COMPAT_VMSTATE:
+        ber_write_eoc(ov->file);
+        break;
+
+    case VISIT_SEQ_COMPAT_BLOB:
+        bv = qemu_file_get_tmp_visitor(e->seqcompat_info.binfile);
+        visit_destroy(bv, errp);
+        qsb  = qemu_buf_get(e->seqcompat_info.binfile);
+        len = qsb_get_length(qsb);
+
+        /* Set up headers so the blob is an OCTET_STRING */
+        offset = ber_encode_type(hdrbuf, BUFLEN/2, BER_TYPE_OCTET_STRING,
+                             BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL,
+                             errp);
+        offset += ber_encode_len(hdrbuf+offset, BUFLEN/2, len, errp);
+        DPRINTF(ov, "end blob: len=%zd", len);
+        if (*errp) {
+            qemu_fclose(e->seqcompat_info.binfile);
+            return;
+        }
+        qemu_put_buffer(ov->file, hdrbuf, offset);
+
+        /* all the data follows (concatinating the iov's) */
+        for (cur_iov = 0; cur_iov < qsb->n_iov; cur_iov++) {
+            /* The iov entries are partially filled */
+            size_t towrite = (qsb->iov[cur_iov].iov_len > len) ?
+                                  len :
+                                  qsb->iov[cur_iov].iov_len;
+            len -= towrite;
+            DPRINTF(ov, "end blob writing %zd of %zd leaving %zd", towrite, \
+                    qsb->iov[cur_iov].iov_len, len);
+
+            if (!towrite) {
+                break;
+            }
+            qemu_put_buffer(ov->file, qsb->iov[cur_iov].iov_base, towrite);
+        }
+        qemu_fclose(e->seqcompat_info.binfile);
+        break;
+    }
+
+    if (e->seqcompat_info.mode != compat_mode) {
+        error_setg(errp, "mismatched seqcompat mode %d/%d", compat_mode,
+                   e->seqcompat_info.mode);
+    }
+
+    DPRINTF(ov, "qfbero_end_sequence_compat %s/%d", name, \
+            e->seqcompat_info.mode);
+    g_free(e);
+}
+
+static void qfbero_destroy(Visitor *v, Error **errp)
+{
+    QemuFileBEROutputVisitor *ov = to_ov(v);
+
+    qemu_file_ber_output_visitor_cleanup(ov);
+}
+
+Visitor *qemu_file_ber_output_get_visitor(QemuFileBEROutputVisitor *v)
+{
+    return &v->visitor;
+}
+
+void qemu_file_ber_output_visitor_cleanup(QemuFileBEROutputVisitor *ov)
+{
+    g_free(ov);
+}
+
+QemuFileBEROutputVisitor *qemu_file_ber_output_visitor_new(QEMUFile *f)
+{
+    QemuFileBEROutputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->file = f;
+
+    v->visitor.start_struct = qfbero_start_struct;
+    v->visitor.end_struct = qfbero_end_struct;
+    v->visitor.start_list = qfbero_start_list;
+    v->visitor.next_list = qfbero_next_list;
+    v->visitor.end_list = qfbero_end_list;
+    v->visitor.start_array = qfbero_start_array;
+    v->visitor.next_array = qfbero_next_array;
+    v->visitor.end_array = qfbero_end_array;
+    v->visitor.type_buffer = qfbero_type_buffer;
+    v->visitor.type_int = qfbero_type_int64;
+    v->visitor.type_uint8 = qfbero_type_uint8;
+    v->visitor.type_uint16 = qfbero_type_uint16;
+    v->visitor.type_uint32 = qfbero_type_uint32;
+    v->visitor.type_uint64 = qfbero_type_uint64;
+    v->visitor.type_int8 = qfbero_type_int8;
+    v->visitor.type_int16 = qfbero_type_int16;
+    v->visitor.type_int32 = qfbero_type_int32;
+    v->visitor.type_int64 = qfbero_type_int64;
+    v->visitor.type_bool = qfbero_type_bool;
+    v->visitor.type_str = qfbero_type_str;
+    v->visitor.type_str256 = qfbero_type_str256;
+    v->visitor.destroy = qfbero_destroy;
+    v->visitor.start_sequence_compat = qfbero_start_sequence_compat;
+    v->visitor.get_next_type = qfbero_get_next_type;
+    v->visitor.end_sequence_compat = qfbero_end_sequence_compat;
+    v->visitor.get_qemufile = qfbero_get_qemufile;
+
+    v->visitor.flags = VISITOR_SAVING;
+
+    QTAILQ_INIT(&v->stack);
+
+    return v;
+}