From patchwork Tue Mar 25 20:17:23 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. David Alan Gilbert" X-Patchwork-Id: 333716 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 25B3B14009A for ; Wed, 26 Mar 2014 08:14:05 +1100 (EST) Received: from localhost ([::1]:44007 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WSXoD-0002Ud-Hz for incoming@patchwork.ozlabs.org; Tue, 25 Mar 2014 16:18:53 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:46218) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WSXnU-0002GG-6R for qemu-devel@nongnu.org; Tue, 25 Mar 2014 16:18:14 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WSXnN-0008J3-WB for qemu-devel@nongnu.org; Tue, 25 Mar 2014 16:18:08 -0400 Received: from mx1.redhat.com ([209.132.183.28]:15610) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WSXnN-0008Ip-Io for qemu-devel@nongnu.org; Tue, 25 Mar 2014 16:18:01 -0400 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s2PKHv0i018736 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Tue, 25 Mar 2014 16:17:58 -0400 Received: from dgilbert-t530.home.treblig.org (vpn1-7-106.ams2.redhat.com [10.36.7.106]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s2PKHSSg029837; Tue, 25 Mar 2014 16:17:55 -0400 From: "Dr. David Alan Gilbert (git)" To: qemu-devel@nongnu.org Date: Tue, 25 Mar 2014 20:17:23 +0000 Message-Id: <1395778647-30925-13-git-send-email-dgilbert@redhat.com> In-Reply-To: <1395778647-30925-1-git-send-email-dgilbert@redhat.com> References: <1395778647-30925-1-git-send-email-dgilbert@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: stefanb@linux.vnet.ibm.com, quintela@redhat.com, mdroth@linux.vnet.ibm.com, agraf@suse.de, mst@redhat.com, aliguori@amazon.com, afaerber@suse.de Subject: [Qemu-devel] [RFC PATCH 12/16] BER Visitor: Create output visitor X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: "Dr. David Alan Gilbert" Signed-off-by: Dr. David Alan Gilbert 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 --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 + * Michael Tsirkin + * + * 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 + * + * 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 + * + * 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; +}