new file mode 100644
@@ -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 */
+
new file mode 100644
@@ -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
@@ -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
new file mode 100644
@@ -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;
+}