diff mbox

[2/3] asn1 ber visitors

Message ID 20130226230627.432353713@linux.vnet.ibm.com
State New
Headers show

Commit Message

Joel Schopp Feb. 26, 2013, 11:03 p.m. UTC
These patches implement asn1 ber visitors for encoding and decoding data.
References: <20130226230354.982917686@linux.vnet.ibm.com>
Content-Disposition: inline; filename=asn1_all.diff

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
 Makefile.objs               |   10 
 ber/Makefile.objs           |    2 
 ber/ber-common.c            |   56 ++
 ber/ber-input-visitor.c     |  969 ++++++++++++++++++++++++++++++++++++++++++++
 ber/ber-input-visitor.h     |   30 +
 ber/ber-output-visitor.c    |  563 +++++++++++++++++++++++++
 ber/ber-output-visitor.h    |   28 +
 ber/ber.h                   |   85 +++
 include/qapi/qmp/qerror.h   |    6 
 include/qapi/visitor-impl.h |   23 +
 include/qapi/visitor.h      |   10 
 qapi/qapi-visit-core.c      |   30 +
 12 files changed, 1811 insertions(+), 1 deletion(-)

Comments

Andreas Färber Feb. 27, 2013, 8:28 a.m. UTC | #1
Am 27.02.2013 00:03, schrieb jschopp@linux.vnet.ibm.com:
> These patches implement asn1 ber visitors for encoding and decoding data.

Would be good to not be lazy and spell them correctly in at least one of
the two lines of the commit where they're being introduced.

> References: <20130226230354.982917686@linux.vnet.ibm.com>
> Content-Disposition: inline; filename=asn1_all.diff
> 
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
> ---
>  Makefile.objs               |   10 
>  ber/Makefile.objs           |    2 
>  ber/ber-common.c            |   56 ++
>  ber/ber-input-visitor.c     |  969 ++++++++++++++++++++++++++++++++++++++++++++
>  ber/ber-input-visitor.h     |   30 +
>  ber/ber-output-visitor.c    |  563 +++++++++++++++++++++++++
>  ber/ber-output-visitor.h    |   28 +
>  ber/ber.h                   |   85 +++
>  include/qapi/qmp/qerror.h   |    6 
>  include/qapi/visitor-impl.h |   23 +
>  include/qapi/visitor.h      |   10 
>  qapi/qapi-visit-core.c      |   30 +
>  12 files changed, 1811 insertions(+), 1 deletion(-)

So now we're moving from crowded directories to every IBM contributor
creating their own personal subdirectory... (cf. TPM)

I would suggest to place your headers into existing include/qapi/ for
instance and the remaining source files into qapi/ or a subdirectory
thereof rather than a new top-level directory.

I also note that you're not changing MAINTAINERS, so the new files don't
have a maintainer assigned that gets CC'ed on changes.

> 
> Index: b/Makefile.objs
> ===================================================================
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -57,7 +57,12 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
>  common-obj-$(CONFIG_LINUX) += fsdev/
>  
>  common-obj-y += migration.o migration-tcp.o
> -common-obj-y += qemu-char.o qemu-file.o #aio.o
> +
> +#ber has to be linked against qemu-file.o so we must add them on the same line
> +ber-nested-y = ber-input-visitor.o ber-output-visitor.o ber-common.o
> +ber-obj-y = $(addprefix ber/, $(ber-nested-y))
> +
> +common-obj-y += qemu-char.o qemu-file.o $(ber-obj-y) #aio.o

I don't see any justification for that statement? You should be able to
add whatever file you like on a separate Makefile line without problems.

Also I believe you are breaking dependency handling by not just doing
common-obj-y += ber/
utilising our existing object file unnesting rules.

>  common-obj-y += block-migration.o
>  common-obj-y += page_cache.o xbzrle.o
>  
> @@ -116,4 +121,7 @@ nested-vars += \
>  	qga-obj-y \
>  	block-obj-y \
>  	common-obj-y
> +# \
> +#	ber-obj-y
> +

Commented-out code.

>  dummy := $(call unnest-vars)
> Index: b/ber/ber-common.c
[snip]

I wrote an ASN.1 implementation once, and it seems to me you are mixing
up ASN.1 and BER here in your enum/array/... naming. The types are
general ASN.1 concepts and apply to other encodings as well, no?

Also I remember that BER has disjoint CER and DER flavors that I don't
see your output visitor distinguishing on brief sight, only P/C.
Input visitor ideally handles both fine.

ASN.1
- textual
- BER
  - CER
  - DER
- PER (?)
- XER

While XER being XML-based just like the textual ASN.1 notation is
everything but efficient for transmission, keeping our options open to
implement either of these later for debugging purposes (e.g., verifying
VMState format) would be nice.

Regards,
Andreas
Joel Schopp Feb. 27, 2013, 8:03 p.m. UTC | #2
Thanks for the feedback.

On 02/27/2013 02:28 AM, Andreas Färber wrote:
> Am 27.02.2013 00:03, schrieb jschopp@linux.vnet.ibm.com:
>> These patches implement asn1 ber visitors for encoding and decoding data.
>
> Would be good to not be lazy and spell them correctly in at least one of
> the two lines of the commit where they're being introduced.

I will change "asn1 ber" to "ASN.1 BER"

> I would suggest to place your headers into existing include/qapi/ for
> instance and the remaining source files into qapi/ or a subdirectory
> thereof rather than a new top-level directory.
>
> I also note that you're not changing MAINTAINERS, so the new files don't
> have a maintainer assigned that gets CC'ed on changes.

I'll update MAINTAINERS and move the headers into include.

As for the location of the source files I think where the patch puts 
them now makes most sense, I don't think qapi makes sense.  But the 
location isn't important to me so I'll put them wherever.  Anybody else 
have an opinion on the best location?

>> +ber-nested-y = ber-input-visitor.o ber-output-visitor.o ber-common.o
>> +ber-obj-y = $(addprefix ber/, $(ber-nested-y))
>> +
>> +common-obj-y += qemu-char.o qemu-file.o $(ber-obj-y) #aio.o
>
> I don't see any justification for that statement? You should be able to
> add whatever file you like on a separate Makefile line without problems.
>
> Also I believe you are breaking dependency handling by not just doing
> common-obj-y += ber/
> utilising our existing object file unnesting rules.

That's what I thought as well, but it didn't compile.  This does.

>
>>   common-obj-y += block-migration.o
>>   common-obj-y += page_cache.o xbzrle.o
>>
>> @@ -116,4 +121,7 @@ nested-vars += \
>>   	qga-obj-y \
>>   	block-obj-y \
>>   	common-obj-y
>> +# \
>> +#	ber-obj-y
>> +
>
>>
>> Index: b/Makefile.objs
>> ===================================================================
>> --- a/Makefile.objs
>> +++ b/Makefile.objs
>> @@ -57,7 +57,12 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
>>   common-obj-$(CONFIG_LINUX) += fsdev/
>>
>>   common-obj-y += migration.o migration-tcp.o
>> -common-obj-y += qemu-char.o qemu-file.o #aio.o
>> +
>> +#ber has to be linked against qemu-file.o so we must add them on the same line
>
> Commented-out code.

Will remove

>
>>   dummy := $(call unnest-vars)
>> Index: b/ber/ber-common.c
> [snip]
>
> I wrote an ASN.1 implementation once, and it seems to me you are mixing
> up ASN.1 and BER here in your enum/array/... naming. The types are
> general ASN.1 concepts and apply to other encodings as well, no?

When/if we implement other ASN.1 encodings we can revisit what to name 
them enums/array/structs, but for now it highly increases readability if 
they are named after what they are used for.

>
> Also I remember that BER has disjoint CER and DER flavors that I don't
> see your output visitor distinguishing on brief sight, only P/C.
> Input visitor ideally handles both fine.

I'd refer you to the comment on ber/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.
Paolo Bonzini Feb. 27, 2013, 8:19 p.m. UTC | #3
Il 27/02/2013 21:03, Joel Schopp ha scritto:
> 
>>> +ber-nested-y = ber-input-visitor.o ber-output-visitor.o ber-common.o
>>> +ber-obj-y = $(addprefix ber/, $(ber-nested-y))
>>> +
>>> +common-obj-y += qemu-char.o qemu-file.o $(ber-obj-y) #aio.o
>>
>> I don't see any justification for that statement? You should be able to
>> add whatever file you like on a separate Makefile line without problems.
>>
>> Also I believe you are breaking dependency handling by not just doing
>> common-obj-y += ber/
>> utilising our existing object file unnesting rules.
> 
> That's what I thought as well, but it didn't compile.  This does.

Then please ask for help.  Do not work around what you didn't understand
(it's not your fault, it's complex stuff).

Paolo
Michael Roth Feb. 27, 2013, 10:11 p.m. UTC | #4
On Wed, Feb 27, 2013 at 02:03:45PM -0600, Joel Schopp wrote:
> Thanks for the feedback.
> 
> On 02/27/2013 02:28 AM, Andreas Färber wrote:
> >Am 27.02.2013 00:03, schrieb jschopp@linux.vnet.ibm.com:
> >>These patches implement asn1 ber visitors for encoding and decoding data.
> >
> >Would be good to not be lazy and spell them correctly in at least one of
> >the two lines of the commit where they're being introduced.
> 
> I will change "asn1 ber" to "ASN.1 BER"
> 
> >I would suggest to place your headers into existing include/qapi/ for
> >instance and the remaining source files into qapi/ or a subdirectory
> >thereof rather than a new top-level directory.
> >
> >I also note that you're not changing MAINTAINERS, so the new files don't
> >have a maintainer assigned that gets CC'ed on changes.
> 
> I'll update MAINTAINERS and move the headers into include.
> 
> As for the location of the source files I think where the patch puts
> them now makes most sense, I don't think qapi makes sense.  But the
> location isn't important to me so I'll put them wherever.  Anybody
> else have an opinion on the best location?

qapi/ along with all the other visitor implementations is probably the
right place for now. If in the future we end up with dozens of
implementations pulling into ridiculous amounts of dependencies we might
want to reconsider things, but for now it's safest to assume that any
serialization format for can be selected at runtime for a Visitor user,
so they should all be made available via qapi-obj-y.

It's also possible this might fix whatever build issue you're running into
for free :)
Michael Roth Feb. 27, 2013, 10:57 p.m. UTC | #5
On Tue, Feb 26, 2013 at 05:03:56PM -0600, jschopp@linux.vnet.ibm.com wrote:
> These patches implement asn1 ber visitors for encoding and decoding data.
> References: <20130226230354.982917686@linux.vnet.ibm.com>
> Content-Disposition: inline; filename=asn1_all.diff
> 
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
> ---
>  Makefile.objs               |   10 
>  ber/Makefile.objs           |    2 
>  ber/ber-common.c            |   56 ++
>  ber/ber-input-visitor.c     |  969 ++++++++++++++++++++++++++++++++++++++++++++
>  ber/ber-input-visitor.h     |   30 +
>  ber/ber-output-visitor.c    |  563 +++++++++++++++++++++++++
>  ber/ber-output-visitor.h    |   28 +

Please break the input/output implementations out into separate patches,
and follow those up with test cases. See:

tests/test-{string,qmp}-{input,output}-visitor.c
tests/test-visitor-serialization.c

>  ber/ber.h                   |   85 +++
>  include/qapi/qmp/qerror.h   |    6 
>  include/qapi/visitor-impl.h |   23 +
>  include/qapi/visitor.h      |   10 
>  qapi/qapi-visit-core.c      |   30 +
>  12 files changed, 1811 insertions(+), 1 deletion(-)
> 
> Index: b/Makefile.objs
> ===================================================================
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -57,7 +57,12 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
>  common-obj-$(CONFIG_LINUX) += fsdev/
> 
>  common-obj-y += migration.o migration-tcp.o
> -common-obj-y += qemu-char.o qemu-file.o #aio.o
> +
> +#ber has to be linked against qemu-file.o so we must add them on the same line
> +ber-nested-y = ber-input-visitor.o ber-output-visitor.o ber-common.o
> +ber-obj-y = $(addprefix ber/, $(ber-nested-y))
> +
> +common-obj-y += qemu-char.o qemu-file.o $(ber-obj-y) #aio.o
>  common-obj-y += block-migration.o
>  common-obj-y += page_cache.o xbzrle.o
> 
> @@ -116,4 +121,7 @@ nested-vars += \
>  	qga-obj-y \
>  	block-obj-y \
>  	common-obj-y
> +# \
> +#	ber-obj-y
> +
>  dummy := $(call unnest-vars)
> Index: b/ber/ber-common.c
> ===================================================================
> --- /dev/null
> +++ b/ber/ber-common.c
> @@ -0,0 +1,56 @@
> +/*
> + * ASN.1 Basic Encoding Rules Common functions
> + *
> + * Copyright IBM, Corp. 2011
> + *
> + * Authors:
> + *  Stefan Berger     <stefanb@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#include <stdint.h>
> +
> +#include "ber.h"
> +
> +static const char *ber_type_names[] = {
> +    "BER_TYPE_EOC",
> +    "BER_TYPE_BOOLEAN",
> +    "BER_TYPE_INTEGER",
> +    "BER_TYPE_BIT_STRING",
> +    "BER_TYPE_OCTET_STRING",
> +    "BER_TYPE_NULL",
> +    "BER_TYPE_OBJECT_ID",
> +    "BER_TYPE_OBJECT_DESC",
> +    "BER_TYPE_EXTERNAL",
> +    "BER_TYPE_REAL",
> +    "BER_TYPE_ENUMERATED",
> +    "BER_TYPE_EMBEDDED",
> +    "BER_TYPE_UTF8_STRING",
> +    "BER_TYPE_RELATIVE_OID",
> +    "BER_TYPE_UNUSED_0xE",
> +    "BER_TYPE_UNUSED_0xF",
> +    "BER_TYPE_SEQUENCE",
> +    "BER_TYPE_SET",
> +    "BER_TYPE_NUMERIC_STRING",
> +    "BER_TYPE_PRINTABLE_STRING",
> +    "BER_TYPE_T61STRING",
> +    "BER_TYPE_VIDEOTEX_STRING",
> +    "BER_TYPE_IA5_STRING",
> +    "BER_TYPE_UTCTIME",
> +    "BER_TYPE_GENERALIZED_TIME",
> +    "BER_TYPE_GRAPHIC_STRING",
> +    "BER_TYPE_VISIBLE_STRING",
> +    "BER_TYPE_GENERAL_STRING",
> +    "BER_TYPE_UNIVERSAL_STRING",
> +    "BER_TYPE_CHARACTER_STRING"
> +    "BER_TYPE_BMP_STRING",
> +    "BER_TYPE_LONG_FORM",
> +};
> +
> +const char *ber_type_to_str(uint8_t ber_type)
> +{
> +    return ber_type_names[ber_type & BER_TYPE_TAG_MASK];
> +}
> Index: b/ber/ber-input-visitor.c
> ===================================================================
> --- /dev/null
> +++ b/ber/ber-input-visitor.c
> @@ -0,0 +1,969 @@
> +/*
> + * BER Input Visitor
> + *
> + * Copyright IBM, Corp. 2011
> + *
> + * Authors:
> + *  Anthony Liguori   <aliguori@us.ibm.com>
> + *  Stefan Berger     <stefanb@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#include "qemu-common.h"
> +#include "ber-input-visitor.h"
> +#include "qemu/queue.h"
> +#include "qemu-common.h"
> +#include "hw/hw.h"
> +#include "ber.h"
> +#include "include/qapi/qmp/qerror.h"
> +#include "migration/qemu-file.h"
> +#include "qapi/visitor-impl.h"
> +
> +#define AIV_STACK_SIZE 1024
> +
> +/* whether to allow the parsing of primitives that are fragmented */
> +#define BER_ALLOW_FRAGMENTED_PRIMITIVES
> +
> +/* #define BER_DEBUG */
> +
> +typedef struct StackEntry {
> +    uint64_t cur_pos;
> +} StackEntry;
> +
> +struct BERInputVisitor {
> +    Visitor visitor;
> +    QEMUFile *qfile;
> +    uint64_t cur_pos;
> +    StackEntry stack[AIV_STACK_SIZE];
> +    int nb_stack;
> +    uint64_t max_allowed_buffer_size;
> +    uint64_t largest_needed_buffer;
> +};
> +
> +static BERInputVisitor *to_biv(Visitor *v)
> +{
> +    return container_of(v, BERInputVisitor, visitor);
> +}
> +
> +static void ber_input_push(BERInputVisitor *aiv,
> +                           uint64_t cur_pos, Error **errp)
> +{
> +    aiv->stack[aiv->nb_stack].cur_pos = cur_pos;
> +    aiv->nb_stack++;
> +
> +    if (aiv->nb_stack >= AIV_STACK_SIZE) {
> +        error_set(errp, QERR_BUFFER_OVERRUN);
> +        return;
> +    }
> +}
> +
> +static uint64_t ber_input_pop(BERInputVisitor *aiv, Error **errp)
> +{
> +    aiv->nb_stack--;
> +
> +    if (aiv->nb_stack < 0) {
> +        error_set(errp, QERR_BUFFER_OVERRUN);
> +        return 0;
> +    }
> +
> +    return aiv->stack[aiv->nb_stack].cur_pos;
> +}
> +
> +/*
> + * Read a type tag from the stream. Up-to 32 bit type tags are supported
> + * for reading and otherwise an error is returned. Anything larger than that
> + * would not be reasonable and could only be abused.
> + */
> +static uint32_t ber_read_type(BERInputVisitor *aiv, uint8_t *ber_type_flags,
> +                              Error **errp)
> +{
> +    uint32_t type;
> +    uint8_t byte;
> +    uint8_t ctr = 0;
> +    char buf[128];
> +
> +    if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
> +        error_set(errp, QERR_QEMUFILE_ERROR,
> +                  "Error while reading type");
> +        return 0;
> +    }
> +    aiv->cur_pos++;
> +    type = byte;
> +
> +    *ber_type_flags = type & (BER_TYPE_P_C_MASK | BER_TYPE_CLASS_MASK);
> +
> +    if ((type & BER_TYPE_TAG_MASK) == BER_TYPE_LONG_FORM) {
> +        type = 0;
> +        while (true) {
> +            type <<= 7;
> +            if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
> +                error_set(errp, QERR_QEMUFILE_ERROR,
> +                          "Error while reading long type");
> +                return 0;
> +            }
> +            aiv->cur_pos++;
> +
> +            type |= (byte & 0x7f);
> +            if ((byte & 0x80) == 0) {
> +                break;
> +            }
> +            ctr += 7; /* read 7 bits */
> +            if (ctr >= (sizeof(type) * 8)) {
> +                /* only support 32 bit length identifiers */
> +                snprintf(buf, sizeof(buf),
> +                         "type tag is larger than 32 bit (offset %" PRIu64
> +                         ")", aiv->cur_pos);
> +                error_set(errp, QERR_INVALID_STREAM,
> +                          buf);
> +                return 0;
> +            }
> +        }
> +    } else {
> +        type &= BER_TYPE_TAG_MASK;
> +    }
> +
> +    return type;
> +}
> +
> +static uint64_t ber_read_length(BERInputVisitor *aiv, bool *is_indefinite,
> +                                Error **errp)
> +{
> +    uint8_t byte, c, int_len;
> +    uint64_t len = 0;
> +    QEMUFile *qfile = aiv->qfile;
> +    char buf[128];
> +
> +    *is_indefinite = false;
> +
> +    if (qemu_read_bytes(qfile, &byte, 1) != 1) {
> +        error_set(errp, QERR_QEMUFILE_ERROR,
> +                  "Error while reading length indicator");
> +        return ~0x0ULL;
> +    }
> +    aiv->cur_pos++;
> +
> +    if (byte == BER_LENGTH_INDEFINITE) {
> +        *is_indefinite = true;
> +        return ~0x0ULL;
> +    }
> +
> +    if (!(byte & BER_LENGTH_LONG)) {
> +        len = byte;
> +    } else {
> +        int_len = byte & BER_LENGTH_MASK;
> +        if (int_len > 8) {
> +            snprintf(buf, sizeof(buf), "ASN.1 integer length field %d > 8",
> +                     int_len);
> +            /* Length can be up to 127 byte, but it seems
> +             * safe to assume any input will be < 1TB in length. */
> +            error_set(errp, QERR_INVALID_PARAMETER, buf);
> +            return ~0x0ULL;
> +        }
> +        for (c = 0; c < int_len; c++) {
> +            len <<= 8;
> +            if (qemu_read_bytes(qfile, &byte, 1) != 1) {
> +                error_set(errp, QERR_QEMUFILE_ERROR,
> +                          "Error while reading length");
> +                return ~0x0ULL;
> +            }
> +            len |= byte;
> +        }
> +        aiv->cur_pos += int_len;
> +    }
> +
> +    if (len > aiv->max_allowed_buffer_size) {
> +        snprintf(buf, sizeof(buf),
> +                 "Length indicator (%"PRIu64") in input byte stream "
> +                 "exceeds maximum allowed length (%"PRIu64").",
> +                 len, aiv->max_allowed_buffer_size);
> +        error_set(errp,  QERR_INVALID_STREAM, buf);
> +        return ~0x0ULL;
> +    }
> +
> +    if (len > aiv->largest_needed_buffer) {
> +        aiv->largest_needed_buffer = len;
> +    }
> +
> +    return len;
> +}
> +
> +static uint64_t ber_peek_is_eoc(BERInputVisitor *biv, Error **errp)
> +{
> +    uint8_t buf[2];
> +    QEMUFile *qfile = biv->qfile;
> +
> +    if (qemu_peek_bytes(qfile, buf, 2, 0) != 2) {
> +        error_set(errp, QERR_QEMUFILE_ERROR,
> +                  "Error while peeking for EOC");
> +        return ~0x0ULL;
> +    }
> +
> +    if (buf[0] == BER_TYPE_EOC && buf[1] == 0) {
> +        return 1;
> +    }
> +
> +    return 0;
> +}
> +
> +static void ber_skip_bytes(BERInputVisitor *aiv, uint64_t to_skip,
> +                           Error **errp)
> +{
> +    uint8_t buf[1024];
> +    uint32_t skip;
> +
> +    /* skip length bytes */
> +    while (to_skip > 0) {
> +        skip = MIN(to_skip, sizeof(buf));
> +        if (qemu_read_bytes(aiv->qfile, buf, skip) != skip) {
> +            error_set(errp, QERR_QEMUFILE_ERROR,
> +                      "Error while skipping over bytes");
> +            return;
> +        }
> +        aiv->cur_pos += skip;
> +        to_skip -= skip;
> +    }
> +}
> +
> +static void ber_skip_until_eoc(BERInputVisitor *aiv, Error **errp)
> +{
> +    uint32_t ber_type_tag;
> +    uint64_t length;
> +    bool is_indefinite;
> +    uint8_t ber_type_flags;
> +    uint64_t indefinite_nesting = 1;
> +    char buf[128];
> +
> +    while (!error_is_set(errp)) {
> +        ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
> +        if (error_is_set(errp)) {
> +            return;
> +        }
> +
> +        length = ber_read_length(aiv, &is_indefinite, errp);
> +        if (error_is_set(errp)) {
> +            return;
> +        }
> +        if (ber_type_tag == BER_TYPE_EOC) {
> +            if (length) {
> +                snprintf(buf, sizeof(buf),
> +                         "ASN.1 EOC length field at offset %" PRIu64
> +                         " is invalid", aiv->cur_pos);
> +                error_set(errp, QERR_INVALID_PARAMETER, buf);
> +                return;
> +            }
> +            if (!indefinite_nesting) {
> +                snprintf(buf, sizeof(buf),
> +                         "ASN.1 EOC at offset %" PRIu64
> +                         "not within an indefinite length",
> +                         aiv->cur_pos);
> +                error_set(errp, QERR_INVALID_PARAMETER, buf);
> +                return;
> +            }
> +#ifdef BER_DEBUG
> +            fprintf(stderr, "found end! nesting=%" PRIdMAX
> +                    ", pos=%" PRIu64 "\n",
> +                    indefinite_nesting, aiv->cur_pos);
> +#endif
> +            if (!--indefinite_nesting) {
> +                return;
> +            }
> +        }
> +        if (is_indefinite) {
> +            if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_PRIMITIVE) {
> +                snprintf(buf, sizeof(buf),
> +                         "ASN.1 indefinite length in a primitive type "
> +                         "at offset %" PRIu64,
> +                         aiv->cur_pos);
> +                error_set(errp, QERR_INVALID_PARAMETER, buf);
> +                return;
> +            }
> +            if (indefinite_nesting == ~0x0ULL) {
> +                snprintf(buf, sizeof(buf),
> +                         "ASN.1 indefinite nesting level is too large "
> +                         "(offset %" PRIu64 ")",
> +                         aiv->cur_pos);
> +                error_set(errp, QERR_INVALID_PARAMETER, buf);
> +                return;
> +            }
> +            ++indefinite_nesting;
> +        } else {
> +#ifdef BER_DEBUG
> +            fprintf(stderr, "skipping type '%s' of length "
> +                    "%" PRIu64 " at %" PRIu64 ".\n",
> +                    ber_type_to_str(ber_type_tag), length, aiv->cur_pos);
> +#endif
> +            ber_skip_bytes(aiv, length, errp);
> +        }
> +    }
> +}
> +
> +static void ber_input_start_constructed(Visitor *v, uint32_t exp_ber_type,
> +                                        uint8_t exp_ber_flags, void **obj,
> +                                        const char *kind, const char *name,
> +                                        size_t size, Error **errp)
> +{
> +    BERInputVisitor *aiv = to_biv(v);
> +    uint32_t ber_type_tag;
> +    uint8_t ber_type_flags;
> +    int64_t len;
> +    bool is_indefinite;
> +    char buf[128];
> +
> +    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
> +    if (error_is_set(errp)) {
> +        return;
> +    }
> +
> +    if (ber_type_tag != exp_ber_type || ber_type_flags != exp_ber_flags) {
> +        sprintf(buf, "%s at offset %" PRIu64 "\n",
> +                ber_type_to_str(exp_ber_type), aiv->cur_pos);
> +
> +        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
> +                  ber_type_to_str(ber_type_tag),
> +                  buf);
> +        return;
> +    }
> +
> +    if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_PRIMITIVE) {
> +        snprintf(buf, sizeof(buf), "primitive type (%s)",
> +                 ber_type_to_str(ber_type_tag));
> +        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
> +                  buf, "constructed type");
> +        return;
> +    }
> +
> +    len = ber_read_length(aiv, &is_indefinite, errp);
> +    if (error_is_set(errp)) {
> +        return;
> +    }
> +
> +    if (!is_indefinite) {
> +#ifdef BER_DEBUG
> +        fprintf(stderr, "structure/set len: %" PRIi64 "\n", len);
> +#endif
> +        ber_input_push(aiv, aiv->cur_pos + len, errp);
> +    } else {
> +#ifdef BER_DEBUG
> +        fprintf(stderr, "indefinite length encoding!\n");
> +#endif
> +        ber_input_push(aiv, 0, errp);
> +    }
> +
> +    if (error_is_set(errp)) {
> +        return;
> +    }
> +
> +    if (size > 0 && *obj == NULL) {
> +        *obj = g_malloc0(size);
> +#ifdef BER_DEBUG
> +        fprintf(stderr, "for type '%s' allocated buffer at %p, size = %zu\n",
> +                ber_type_to_str(ber_type_tag), *obj, size);
> +#endif
> +    }
> +}
> +
> +static void ber_input_end_constructed(Visitor *v, Error **errp)
> +{
> +    uint64_t new_pos;
> +    BERInputVisitor *aiv = to_biv(v);
> +
> +    new_pos = ber_input_pop(aiv, errp);
> +
> +    if (new_pos != 0) {
> +#ifdef BER_DEBUG
> +        fprintf(stderr, "new_pos = %" PRIu64 "\n", new_pos);
> +#endif
> +        aiv->cur_pos = new_pos;
> +    } else {
> +#ifdef BER_DEBUG
> +        fprintf(stderr, "searching for end...\n");
> +        fprintf(stderr, "cur_pos = %" PRIu64 "\n", aiv->cur_pos);
> +#endif
> +        ber_skip_until_eoc(aiv, errp);
> +    }
> +}
> +
> +static void ber_input_start_struct(Visitor *v, void **obj, const char *kind,
> +                                   const char *name, size_t size, Error **errp)
> +{
> +    ber_input_start_constructed(v, BER_TYPE_SEQUENCE, BER_TYPE_CONSTRUCTED,
> +                                obj, kind, name, size, errp);
> +}
> +
> +static void ber_input_end_struct(Visitor *v, Error **errp)
> +{
> +    ber_input_end_constructed(v, errp);
> +}
> +
> +static void ber_input_start_array(Visitor *v, void **obj,
> +                                  const char *name, size_t elem_count,
> +                                  size_t elem_size, Error **errp)
> +{
> +    ber_input_start_constructed(v, BER_TYPE_SET, BER_TYPE_CONSTRUCTED,
> +                                obj, NULL, name,
> +                                elem_count * elem_size, errp);
> +}
> +
> +static void ber_input_next_array(Visitor *v, Error **errp)
> +{
> +    /* nothing to do here */
> +}
> +
> +static void ber_input_end_array(Visitor *v, Error **errp)
> +{
> +    ber_input_end_constructed(v, errp);
> +}
> +
> +static void ber_input_start_list(Visitor *v, const char *name,
> +                                 Error **errp)
> +{
> +    void *obj = NULL;
> +    ber_input_start_constructed(v, BER_TYPE_CUSTOM_LIST, BER_TYPE_CONSTRUCTED,
> +                                obj, NULL, name, 0, errp);
> +    g_free(obj);
> +}
> +
> +static GenericList *ber_input_next_list(Visitor *v, GenericList **list,
> +                                        Error **errp)
> +{
> +    BERInputVisitor *biv = to_biv(v);
> +    GenericList *entry;
> +    StackEntry *se = &biv->stack[biv->nb_stack - 1];
> +
> +    if (se->cur_pos == 0) {
> +        /* indefinite lenght encoding is used */
> +        se = &biv->stack[biv->nb_stack];
> +        if (ber_peek_is_eoc(biv, errp) != 0) {
> +            return NULL;
> +        }
> +    } else if (se->cur_pos <= biv->cur_pos) {
> +        return NULL;
> +    }
> +
> +    entry = g_malloc0(sizeof(*entry));
> +    if (*list) {
> +        (*list)->next = entry;
> +    }
> +
> +    return entry;
> +}
> +
> +static void ber_input_end_list(Visitor *v, Error **errp)
> +{
> +    ber_input_end_constructed(v, errp);
> +}
> +
> +static void ber_input_integer(Visitor *v, uint8_t *obj, uint8_t maxbytes,
> +                              Error **errp)
> +{
> +    BERInputVisitor *aiv = to_biv(v);
> +    uint32_t ber_type_tag;
> +    uint8_t ber_type_flags, byte;
> +    bool is_indefinite;
> +    uint64_t len;
> +    uint64_t val = 0;
> +    int c;
> +    char buf[128];
> +
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "reading int to %p\n", obj);
> +#endif
> +
> +    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
> +    if (error_is_set(errp)) {
> +        return;
> +    }
> +
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "%s: got type: 0x%02x, expected 0x%02x\n",
> +            __func__, ber_type_tag, BER_TYPE_INTEGER);
> +#endif
> +
> +    if (ber_type_tag != BER_TYPE_INTEGER || ber_type_flags != 0) {
> +        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
> +                  ber_type_to_str(ber_type_tag),
> +                  ber_type_to_str(BER_TYPE_INTEGER));
> +        return;
> +    }
> +    len = ber_read_length(aiv, &is_indefinite, errp);
> +    if (error_is_set(errp)) {
> +        return;
> +    }
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "pos: %" PRIu64 " int len: %" PRIi64 "\n",
> +            aiv->cur_pos, len);
> +#endif
> +
> +    if (is_indefinite) {
> +        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
> +                  "ASN.1 int indicator is indefinite",
> +                  "[1..8]");
> +        return;
> +    }
> +    if (len > maxbytes) {
> +        snprintf(buf, sizeof(buf), "ASN.1 integer length indicator %" PRIi64
> +                 " is larger than expected (%u bytes)\n",
> +                 len, maxbytes);
> +        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
> +                  buf, "[1..8]");
> +        return;
> +    }
> +
> +    for (c = 0; c < len ; c++) {
> +        val <<= 8;
> +        if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
> +            error_set(errp, QERR_QEMUFILE_ERROR,
> +                      "Error while reading integer");
> +            return;
> +        }
> +        val |= byte;
> +        if (c == 0 && (val & 0x80) == 0x80) {
> +            /* sign extend */
> +            val |= 0xFFFFFFFFFFFFFF00ULL;
> +        }
> +    }
> +    aiv->cur_pos += len;
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "pos: %" PRIu64 " int: %" PRIu64 "\n", aiv->cur_pos, val);
> +#endif
> +
> +    memcpy(obj, &val, maxbytes);
> +}
> +
> +static void ber_input_type_int(Visitor *v, int64_t *obj, const char *name,
> +                               Error **errp)
> +{
> +    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_input_type_uint8_t(Visitor *v, uint8_t *obj,
> +                                   const char *name, Error **errp)
> +{
> +    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_input_type_uint16_t(Visitor *v, uint16_t *obj,
> +                                    const char *name, Error **errp)
> +{
> +    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_input_type_uint32_t(Visitor *v, uint32_t *obj,
> +                                    const char *name, Error **errp)
> +{
> +    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_input_type_uint64_t(Visitor *v, uint64_t *obj,
> +                                    const char *name, Error **errp)
> +{
> +    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_input_type_int8_t(Visitor *v, int8_t *obj,
> +                                  const char *name, Error **errp)
> +{
> +    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_input_type_int16_t(Visitor *v, int16_t *obj,
> +                                   const char *name, Error **errp)
> +{
> +    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_input_type_int32_t(Visitor *v, int32_t *obj,
> +                                   const char *name, Error **errp)
> +{
> +    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_input_type_int64_t(Visitor *v, int64_t *obj,
> +                                   const char *name, Error **errp)
> +{
> +    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_input_type_bool(Visitor *v, bool *obj, const char *name,
> +                                Error **errp)
> +{
> +    BERInputVisitor *aiv = to_biv(v);
> +    uint32_t ber_type_tag;
> +    uint8_t ber_type_flags, byte;
> +    bool is_indefinite;
> +    uint64_t len;
> +    char buf[128];
> +
> +    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
> +    if (error_is_set(errp)) {
> +        return;
> +    }
> +
> +    if (ber_type_tag != BER_TYPE_BOOLEAN || ber_type_flags != 0) {
> +        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
> +                  ber_type_to_str(ber_type_tag),
> +                  ber_type_to_str(BER_TYPE_BOOLEAN));
> +        return;
> +    }
> +    len = ber_read_length(aiv, &is_indefinite, errp);
> +    if (error_is_set(errp)) {
> +        return;
> +    }
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "pos: %" PRIu64 " bool len: %" PRIi64 "\n",
> +            aiv->cur_pos, len);
> +#endif
> +
> +    if (is_indefinite || len != 1) {
> +        snprintf(buf, sizeof(buf),
> +                 "ASN.1 bool length indicator at offset %" PRIu64
> +                 " is indefinite or != 1",
> +                 aiv->cur_pos);
> +        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
> +                  buf, "1");
> +        return;
> +    }
> +    if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
> +        error_set(errp, QERR_QEMUFILE_ERROR,
> +                  "Error while reading boolean");
> +        return;
> +    }
> +    aiv->cur_pos++;
> +    *obj = byte;
> +
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "pos: %" PRIu64 " bool: %d\n", aiv->cur_pos, *obj);
> +#endif
> +}
> +
> +/* Function for recursive reading of fragmented primitives */
> +static uint32_t ber_input_fragment(BERInputVisitor *aiv,
> +                                   uint32_t exp_type_tag,
> +                                   uint8_t exp_type_flags,
> +                                   uint8_t **buffer, size_t *buffer_len,
> +                                   bool may_realloc,
> +                                   uint32_t offset, uint32_t nesting,
> +                                   bool indefinite, uint64_t max_pos,
> +                                   const char *name, Error **errp)
> +{
> +    uint32_t ber_type_tag;
> +    uint8_t ber_type_flags;
> +    uint32_t bytes_read = 0;
> +    bool is_indefinite;
> +    uint64_t len;
> +    char buf[128];
> +
> +    assert((exp_type_flags & BER_TYPE_CONSTRUCTED) == BER_TYPE_PRIMITIVE);
> +
> +    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
> +    if (error_is_set(errp)) {
> +        return 0;
> +    }
> +
> +    if (ber_type_tag != exp_type_tag) {
> +        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
> +                  ber_type_to_str(ber_type_tag & BER_TYPE_TAG_MASK),
> +                  ber_type_to_str(exp_type_tag));
> +        return 0;
> +    }
> +
> +    if ((ber_type_flags & BER_TYPE_CONSTRUCTED)) {
> +#ifndef BER_ALLOW_FRAGMENTED_PRIMITIVES
> +        error_set(errp, QERR_INVALID_STREAM,
> +                  "constructed encoding of primitive types is not supported");
> +        goto err_exit;
> +#else
> +        if (nesting == 1) {
> +            /* don't allow further nesting */
> +            error_set(errp, QERR_INVALID_STREAM, "invalid nesting");
> +            goto err_exit;
> +        }
> +        len = ber_read_length(aiv, &is_indefinite, errp);
> +        if (error_is_set(errp)) {
> +            goto err_exit;
> +        }
> +#ifdef BER_DEBUG
> +        fprintf(stderr, "pos: %" PRIu64 " string len: %" PRIi64 "\n",
> +                aiv->cur_pos, len);
> +#endif
> +
> +        if (!is_indefinite) {
> +            if ((*buffer) == NULL) {
> +                /* allocate buffer once; due to the ASN.1 overhead it
> +                 * will be bigger than what we need */
> +                *buffer = g_malloc0(len);
> +                *buffer_len = len;
> +                may_realloc = false;
> +            }
> +        }
> +#ifdef BER_DEBUG
> +        fprintf(stderr, "recursing now to read constructed type.\n");
> +        fprintf(stderr, "  is_indefinite: %d\n", is_indefinite);
> +#endif
> +        bytes_read += ber_input_fragment(aiv, exp_type_tag, exp_type_flags,
> +                                         buffer, buffer_len, may_realloc,
> +                                         offset, nesting + 1, is_indefinite,
> +                                         aiv->cur_pos + len, name, errp);
> +        return bytes_read;
> +#endif
> +    }
> +
> +    while (true) {
> +        /* Would reading the length carry us beyond what we are allowed to
> +         * read?
> +         */
> +        if (!indefinite &&
> +            max_pos != 0 &&
> +            aiv->cur_pos + 1 > max_pos) {
> +            snprintf(buf, sizeof(buf),
> +                     "data stream would cause parsing beyond "
> +                     "allowed offset at %" PRIu64,
> +                     max_pos);
> +            /* input stream is malformed */
> +            error_set(errp, QERR_INVALID_STREAM, buf);
> +            goto err_exit;
> +        }
> +
> +        /* not-constructed case */
> +        len = ber_read_length(aiv, &is_indefinite, errp);
> +        if (error_is_set(errp)) {
> +            goto err_exit;
> +        }
> +#ifdef BER_DEBUG
> +        fprintf(stderr, "pos: %" PRIu64 " string len: %" PRIi64 "\n",
> +                    aiv->cur_pos, len);
> +#endif
> +        if (is_indefinite) {
> +            snprintf(buf, sizeof(buf),
> +                     "Got indefinite type length in primitive type (%s) at"
> +                     "offset %" PRIu64,
> +                     ber_type_to_str(ber_type_tag), aiv->cur_pos);
> +            error_set(errp, QERR_INVALID_PARAMETER, buf);
> +            goto err_exit;
> +        }
> +        /* if max_pos is not set, set it here */
> +        if (!indefinite && max_pos == 0) {
> +            max_pos = aiv->cur_pos + len;
> +        }
> +
> +        /* Would reading the data carry us beyond what we are allowed to
> +         * read ?
> +         */
> +        if (!indefinite && aiv->cur_pos + len > max_pos) {
> +            /* input stream is malformed */
> +            snprintf(buf, sizeof(buf),
> +                     "data stream would cause parsing beyond "
> +                     "allowed offset at %" PRIu64,
> +                     max_pos);
> +            error_set(errp, QERR_INVALID_STREAM, buf);
> +            goto err_exit;
> +        }
> +
> +        if (offset + len > *buffer_len) {
> +            if (!may_realloc) {
> +                snprintf(buf, sizeof(buf),
> +                         "given buffer is too small (%lu < %"PRIu64")",
> +                         (unsigned long)*buffer_len, offset + len);
> +                error_set(errp, QERR_INVALID_STREAM, buf);
> +            }
> +            /* allocate one more byte for strings, set to 0 */
> +            *buffer = g_realloc(*buffer, offset + len + 1);
> +            *buffer_len = offset + len;
> +            (*buffer)[offset+len] = 0;
> +        }
> +
> +        if (qemu_read_bytes(aiv->qfile,
> +                            &((uint8_t *)*buffer)[offset], len) != len) {
> +            error_set(errp, QERR_QEMUFILE_ERROR,
> +                      "Error while reading data");
> +            goto err_exit;
> +        }
> +
> +        offset += len;
> +        bytes_read += len;
> +
> +        aiv->cur_pos += len;
> +#ifdef BER_DEBUG
> +        if (exp_type_tag == BER_TYPE_IA5_STRING) {
> +            fprintf(stderr, "pos: %" PRIu64 " string: %.*s\n", aiv->cur_pos,
> +                    offset, *buffer);
> +        }
> +#endif
> +
> +        if (nesting == 0) {
> +            break;
> +        }
> +
> +        /* indefinite length case: loop until we encounter EOC */
> +        if (indefinite) {
> +            ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
> +            if (error_is_set(errp)) {
> +                goto err_exit;
> +            }
> +
> +            if (ber_type_tag == BER_TYPE_EOC) {
> +                uint8_t byte;
> +                if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
> +                    error_set(errp, QERR_QEMUFILE_ERROR,
> +                              "Error while reading BER_TYPE_EOC length");
> +                    goto err_exit;
> +                }
> +                aiv->cur_pos++;
> +
> +                if (byte != 0) {
> +                    snprintf(buf, sizeof(buf),
> +                             "ASN.1 EOC length field is invalid at offset "
> +                             "%" PRIu64,
> +                             aiv->cur_pos);
> +                    error_set(errp, QERR_INVALID_PARAMETER, buf);
> +                    goto err_exit;
> +                }
> +                return bytes_read;
> +            }
> +
> +            if (ber_type_tag != exp_type_tag ||
> +                ber_type_flags != exp_type_flags) {
> +                snprintf(buf, sizeof(buf),
> +                         "ASN.1 type field or flags are wrong. Found "
> +                         "0x%x/%u, expected "
> +                         "0x%x/%u at offset %" PRIu64,
> +                         ber_type_tag, ber_type_flags,
> +                         exp_type_tag, exp_type_flags,
> +                         aiv->cur_pos);
> +                error_set(errp, QERR_INVALID_PARAMETER, buf);
> +                goto err_exit;
> +            }
> +            continue;
> +        }
> +
> +        /* in definite length coding case; caller told us how far to read */
> +        if (aiv->cur_pos == max_pos) {
> +            return bytes_read;
> +        }
> +
> +        ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
> +        if (error_is_set(errp)) {
> +            goto err_exit;
> +        }
> +
> +        if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_CONSTRUCTED) {
> +            error_set(errp, QERR_INVALID_PARAMETER_TYPE,
> +                      "constructed BER type",
> +                      ber_type_to_str(exp_type_tag));
> +            goto err_exit;
> +        }
> +
> +        if (ber_type_tag != exp_type_tag) {
> +            error_set(errp, QERR_INVALID_PARAMETER_TYPE,
> +                      ber_type_to_str(ber_type_tag & BER_TYPE_TAG_MASK),
> +                      ber_type_to_str(exp_type_tag));
> +            goto err_exit;
> +        }
> +    }
> +    return bytes_read;
> +
> +err_exit:
> +    if (may_realloc) {
> +        g_free(*buffer);
> +        *buffer = NULL;
> +    }
> +    return 0;
> +}
> +
> +static void ber_input_type_str(Visitor *v, char **obj, const char *name,
> +                               Error **errp)
> +{
> +    BERInputVisitor *aiv = to_biv(v);
> +    size_t buffer_len = 0;
> +
> +    ber_input_fragment(aiv, BER_TYPE_IA5_STRING, 0,
> +                       (uint8_t **)obj, &buffer_len, (*obj == NULL),
> +                       0, 0, false, 0, name, errp);
> +
> +    if (!error_is_set(errp) && *obj == NULL) {
> +        /* adjust NULL string to "" */
> +        *obj = g_strdup("");
> +    }
> +}
> +
> +static void ber_input_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
> +                                   const char *name, Error **errp)
> +{
> +    BERInputVisitor *aiv = to_biv(v);
> +
> +    ber_input_fragment(aiv, BER_TYPE_OCTET_STRING, 0,
> +                       (uint8_t **)obj, &len, (*obj == NULL),
> +                       0, 0, false, 0, name, errp);
> +
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "pos: %" PRIu64 " data at: %p data:\n",
> +            aiv->cur_pos, *obj);
> +    int i;
> +    for (i = 0; i < len; i++) {
> +        fprintf(stderr, "%02x ", (*obj)[i]);
> +        if ((i & 0xf) == 0xf) {
> +            fprintf(stderr, "\n");
> +        }
> +    }
> +    fprintf(stderr, "\n");
> +#endif
> +}
> +
> +Visitor *ber_input_get_visitor(BERInputVisitor *v)
> +{
> +    return &v->visitor;
> +}
> +
> +uint64_t ber_input_get_parser_position(BERInputVisitor *v)
> +{
> +    return v->cur_pos;
> +}
> +
> +uint64_t ber_input_get_largest_needed_buffer(BERInputVisitor *v)
> +{
> +    return v->largest_needed_buffer;
> +}
> +
> +void ber_input_visitor_cleanup(BERInputVisitor *v)
> +{
> +    g_free(v);
> +}
> +
> +BERInputVisitor *ber_input_visitor_new(QEMUFile *qfile,
> +                                       uint64_t max_allowed_buffer_size)
> +{
> +    BERInputVisitor *v;
> +
> +    v = g_malloc0(sizeof(*v));
> +
> +    v->visitor.start_struct = ber_input_start_struct;
> +    v->visitor.end_struct = ber_input_end_struct;
> +    v->visitor.start_array = ber_input_start_array;
> +    v->visitor.next_array = ber_input_next_array;
> +    v->visitor.end_array = ber_input_end_array;
> +    v->visitor.start_list = ber_input_start_list;
> +    v->visitor.next_list = ber_input_next_list;
> +    v->visitor.end_list = ber_input_end_list;
> +    v->visitor.type_int = ber_input_type_int;
> +    v->visitor.type_uint8_t = ber_input_type_uint8_t;
> +    v->visitor.type_uint16_t = ber_input_type_uint16_t;
> +    v->visitor.type_uint32_t = ber_input_type_uint32_t;
> +    v->visitor.type_uint64_t = ber_input_type_uint64_t;
> +    v->visitor.type_int8_t = ber_input_type_int8_t;
> +    v->visitor.type_int16_t = ber_input_type_int16_t;
> +    v->visitor.type_int32_t = ber_input_type_int32_t;
> +    v->visitor.type_int64_t = ber_input_type_int64_t;
> +    v->visitor.type_bool = ber_input_type_bool;
> +    v->visitor.type_str = ber_input_type_str;
> +    v->visitor.type_sized_buffer = ber_input_sized_buffer;
> +
> +    v->qfile = qfile;
> +    v->cur_pos = 0;
> +    v->max_allowed_buffer_size = max_allowed_buffer_size;
> +    v->largest_needed_buffer = 0;
> +
> +    return v;
> +}
> Index: b/ber/ber-input-visitor.h
> ===================================================================
> --- /dev/null
> +++ b/ber/ber-input-visitor.h
> @@ -0,0 +1,30 @@
> +/*
> + * BER Input Visitor header
> + *
> + * Copyright IBM, Corp. 2011
> + *
> + * Authors:
> + *  Anthony Liguori   <aliguori@us.ibm.com>
> + *  Stefan Berger     <stefanb@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#ifndef BER_INPUT_VISITOR_H
> +#define BER_INPUT_VISITOR_H
> +
> +#include "include/qapi/visitor.h"
> +
> +typedef struct BERInputVisitor BERInputVisitor;
> +
> +BERInputVisitor *ber_input_visitor_new(QEMUFile *,
> +                                       uint64_t max_allowd_buffer_size);
> +void ber_input_visitor_cleanup(BERInputVisitor *v);
> +uint64_t ber_input_get_parser_position(BERInputVisitor *v);
> +uint64_t ber_input_get_largest_needed_buffer(BERInputVisitor *v);
> +
> +Visitor *ber_input_get_visitor(BERInputVisitor *v);
> +
> +#endif
> Index: b/ber/ber-output-visitor.c
> ===================================================================
> --- /dev/null
> +++ b/ber/ber-output-visitor.c
> @@ -0,0 +1,563 @@
> +/*
> + * BER Output Visitor
> + *
> + * Copyright IBM, Corp. 2011
> + *
> + * Authors:
> + *  Anthony Liguori   <aliguori@us.ibm.com>
> + *  Stefan Berger     <stefanb@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#include "qemu-common.h"
> +#include "ber-output-visitor.h"
> +#include "qemu/queue.h"
> +#include "qemu-common.h"
> +#include "include/qapi/qmp/qerror.h"
> +#include "hw/hw.h"
> +#include "ber.h"
> +#include "qapi/visitor-impl.h"
> +
> +
> +#define CER_FRAGMENT_CHUNK_SIZE  1000
> +
> +/*#define BER_DEBUG*/
> +
> +typedef struct QStackEntry {
> +    QEMUFile *qfile;
> +    bool is_list_head;
> +    QTAILQ_ENTRY(QStackEntry) node;
> +} QStackEntry;
> +
> +typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
> +
> +struct BEROutputVisitor {
> +    Visitor visitor;
> +    QStack stack;
> +    QEMUFile *qfile;
> +
> +    BERTypePC mode;
> +};
> +
> +static BEROutputVisitor *to_aov(Visitor *v)
> +{
> +    return container_of(v, BEROutputVisitor, visitor);
> +}
> +
> +static void ber_output_push(BEROutputVisitor *qov, QEMUFile *qfile,
> +                            Error **errp)
> +{
> +    QStackEntry *e = g_malloc0(sizeof(*e));
> +
> +    e->qfile = qfile;
> +    e->is_list_head = true;
> +    QTAILQ_INSERT_HEAD(&qov->stack, e, node);
> +}
> +
> +static QEMUFile *ber_output_pop(BEROutputVisitor *qov)
> +{
> +    QStackEntry *e = QTAILQ_FIRST(&qov->stack);
> +    QEMUFile *qfile;
> +
> +    QTAILQ_REMOVE(&qov->stack, e, node);
> +    qfile = e->qfile;
> +    g_free(e);
> +
> +    return qfile;
> +}
> +
> +static unsigned int ber_encode_type(uint8_t *buffer, uint32_t buflen,
> +                                    enum ber_type_tag 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;
> +}
> +
> +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;
> +}
> +
> +static void ber_output_start_constructed(Visitor *v, uint32_t ber_type,
> +                                         Error **errp)
> +{
> +    BEROutputVisitor *aov = to_aov(v);
> +    uint8_t buf[20];
> +    unsigned int tag_bytes_written;
> +
> +    switch (aov->mode) {
> +    case BER_TYPE_PRIMITIVE:
> +        ber_output_push(aov, aov->qfile, errp);
> +        if (error_is_set(errp)) {
> +            return;
> +        }
> +        aov->qfile = qemu_bufopen("w", NULL);
> +        break;
> +    case BER_TYPE_CONSTRUCTED:
> +        ber_output_push(aov, aov->qfile, errp); /* needed for list support */
> +        if (error_is_set(errp)) {
> +            return;
> +        }
> +        tag_bytes_written = ber_encode_type(buf, sizeof(buf),
> +                                            ber_type, BER_TYPE_CONSTRUCTED,
> +                                            errp);
> +        if (error_is_set(errp)) {
> +            return;
> +        }
> +        buf[tag_bytes_written] = BER_LENGTH_INDEFINITE;
> +        if (qemu_write_bytes(aov->qfile, buf, 1 + tag_bytes_written) !=
> +            1 + tag_bytes_written) {
> +            error_set(errp, QERR_QEMUFILE_ERROR,
> +                      "Error while writing constructed type");
> +            return;
> +        }
> +    }
> +}
> +
> +static void ber_output_constructed_ber_close(BEROutputVisitor *aov,
> +                                             uint32_t ber_type,
> +                                             Error **errp)
> +{
> +    uint8_t buf[20];
> +    const QEMUSizedBuffer *qsb;
> +    uint64_t len;
> +    unsigned int num_bytes, tag_bytes_written;
> +    QEMUFile *qfile = ber_output_pop(aov);
> +
> +    tag_bytes_written = ber_encode_type(buf, sizeof(buf),
> +                                        ber_type, BER_TYPE_CONSTRUCTED,
> +                                        errp);
> +    if (error_is_set(errp)) {
> +        return;
> +    }
> +
> +    qsb = qemu_buf_get(aov->qfile);
> +    len = qsb_get_length(qsb);
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "constructed type (0x%02x, %p) has length %ld bytes\n",
> +            ber_type, aov->qfile, len);
> +#endif
> +
> +    num_bytes = ber_encode_len(&buf[tag_bytes_written],
> +                               sizeof(buf) - tag_bytes_written,
> +                               len, errp);
> +    if (error_is_set(errp)) {
> +        return;
> +    }
> +    if (qemu_write_bytes(qfile, buf, tag_bytes_written + num_bytes) !=
> +        tag_bytes_written + num_bytes ||
> +        qemu_write_bytes(qfile, qsb_get_buffer(qsb, 0),
> +                         qsb_get_length(qsb)) != qsb_get_length(qsb)) {
> +        error_set(errp, QERR_QEMUFILE_ERROR,
> +                  "Error while writing buffer");
> +        return;
> +    }
> +
> +    qemu_fclose(aov->qfile);
> +    aov->qfile = qfile;
> +    qemu_fflush(qfile);
> +}
> +
> +static void ber_output_end_constructed(Visitor *v, uint32_t ber_type,
> +                                       Error **errp)
> +{
> +    BEROutputVisitor *aov = to_aov(v);
> +    uint8_t buf[20];
> +
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "end set/struct:\n");
> +#endif
> +
> +    switch (aov->mode) {
> +    case BER_TYPE_PRIMITIVE:
> +        ber_output_constructed_ber_close(aov, ber_type, errp);
> +        break;
> +
> +    case BER_TYPE_CONSTRUCTED:
> +        ber_output_pop(aov);
> +        buf[0] = BER_TYPE_EOC;
> +        buf[1] = 0;
> +        if (qemu_write_bytes(aov->qfile, buf, 2) != 2) {
> +            error_set(errp, QERR_QEMUFILE_ERROR,
> +                      "Error while writing buffer with BER_TYPE_EOC");
> +            return;
> +        }
> +        break;
> +    }
> +}
> +
> +static void ber_output_start_struct(Visitor *v, void **obj, const char *kind,
> +                                    const char *name, size_t unused,
> +                                    Error **errp)
> +{
> +    ber_output_start_constructed(v, BER_TYPE_SEQUENCE, errp);
> +}
> +
> +static void ber_output_end_struct(Visitor *v, Error **errp)
> +{
> +    ber_output_end_constructed(v, BER_TYPE_SEQUENCE, errp);
> +}
> +
> +static void ber_output_start_array(Visitor *v, void **obj,
> +                                   const char *name, size_t elem_count,
> +                                   size_t elem_size, Error **errp)
> +{
> +    ber_output_start_constructed(v, BER_TYPE_SET, errp);
> +}
> +
> +static void ber_output_next_array(Visitor *v, Error **errp)
> +{
> +    /* nothing to do here */
> +}
> +
> +static void ber_output_end_array(Visitor *v, Error **errp)
> +{
> +    ber_output_end_constructed(v, BER_TYPE_SET, errp);
> +}
> +
> +static void ber_output_start_list(Visitor *v, const char *name,
> +                                  Error **errp)
> +{
> +    ber_output_start_constructed(v, BER_TYPE_CUSTOM_LIST, errp);
> +}
> +
> +static GenericList *ber_output_next_list(Visitor *v, GenericList **listp,
> +                                         Error **errp)
> +{
> +    GenericList *list = *listp;
> +    BEROutputVisitor *bov = to_aov(v);
> +    QStackEntry *e = QTAILQ_FIRST(&bov->stack);
> +
> +    assert(e);
> +    if (e->is_list_head) {
> +        e->is_list_head = false;
> +        return list;
> +    }
> +
> +    return list ? list->next : NULL;
> +}
> +
> +static void ber_output_end_list(Visitor *v, Error **errp)
> +{
> +    ber_output_end_constructed(v, BER_TYPE_CUSTOM_LIST, errp);
> +}
> +
> +static void ber_output_fragment(Visitor *v, uint32_t ber_type,
> +                                uint8_t *buffer,
> +                                size_t buflen, Error **errp)
> +{
> +    uint32_t offset = 0;
> +    bool fragmented = false;
> +    uint32_t chunk;
> +    unsigned int num_bytes, type_bytes;
> +    uint8_t buf[20];
> +    uint32_t chunk_size;
> +    BEROutputVisitor *aov = to_aov(v);
> +
> +    switch (aov->mode) {
> +    case BER_TYPE_CONSTRUCTED:
> +        /* X.690 9.2 */
> +        fragmented = (buflen > CER_FRAGMENT_CHUNK_SIZE);
> +        chunk_size = 1000;
> +        break;
> +    case BER_TYPE_PRIMITIVE:
> +        chunk_size = 0xffffffff;
> +        break;
> +    }
> +
> +    if (fragmented) {
> +        ber_output_start_constructed(&aov->visitor, ber_type, errp);
> +        if (error_is_set(errp)) {
> +            return;
> +        }
> +    }
> +
> +    do {
> +        chunk = (buflen - offset > chunk_size) ? chunk_size : buflen - offset;
> +
> +        type_bytes = ber_encode_type(buf, sizeof(buf), ber_type, 0,
> +                                     errp);
> +        if (error_is_set(errp)) {
> +            return;
> +        }
> +        num_bytes = ber_encode_len(&buf[type_bytes], sizeof(buf) - type_bytes,
> +                                   chunk, errp);
> +        if (error_is_set(errp)) {
> +            return;
> +        }
> +        if (qemu_write_bytes(aov->qfile, buf, type_bytes + num_bytes) !=
> +            type_bytes + num_bytes ||
> +            qemu_write_bytes(aov->qfile, &buffer[offset], chunk) != chunk) {
> +            error_set(errp, QERR_QEMUFILE_ERROR,
> +                      "Error while writing buffer");
> +            return;
> +        }
> +        offset += chunk;
> +    } while (offset < buflen);
> +
> +    if (fragmented) {
> +        ber_output_end_constructed(&aov->visitor, ber_type, errp);
> +    }
> +}
> +
> +static void ber_output_int(Visitor *v, int64_t val, uint8_t maxnumbytes,
> +                           Error **errp)
> +{
> +    uint8_t buf[20];
> +    int shift =  (maxnumbytes - 1) * 8;
> +    uint64_t mask = 0xFF80ULL << (shift - 8);
> +    bool exp_zeros;
> +    int c = 0;
> +    BEROutputVisitor *aov = to_aov(v);
> +
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "Writing int 0x%lx (signed=%d, len=%d)\n",
> +            val, is_signed, maxnumbytes);
> +#endif
> +
> +    buf[0] = BER_TYPE_INTEGER;
> +
> +    if (maxnumbytes > 1) {
> +        exp_zeros = ((mask & val) == 0) ? true : false;
> +        while (mask != 0xFF) {
> +            if (exp_zeros) {
> +                if ((mask & val) != 0) {
> +                    break;
> +                }
> +            } else {
> +                if ((mask & val) != mask) {
> +                    break;
> +                }
> +            }
> +            shift -= 8;
> +            mask >>= 8;
> +        }
> +    }
> +
> +    while (shift >= 0) {
> +        buf[2+c] = (val >> shift);
> +        c++;
> +        shift -= 8;
> +    }
> +    buf[1] = c;
> +
> +    if (qemu_write_bytes(aov->qfile, buf, 1 + 1 + c) != 1 + 1 + c) {
> +        error_set(errp, QERR_QEMUFILE_ERROR, "Error while writing integer");
> +        return;
> +    }
> +}
> +
> +static void ber_output_type_int(Visitor *v, int64_t *obj, const char *name,
> +                                Error **errp)
> +{
> +    ber_output_int(v, *obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_output_type_uint8_t(Visitor *v, uint8_t *obj,
> +                                    const char *name, Error **errp)
> +{
> +    ber_output_int(v, *obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_output_type_uint16_t(Visitor *v, uint16_t *obj,
> +                                     const char *name, Error **errp)
> +{
> +    ber_output_int(v, *obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_output_type_uint32_t(Visitor *v, uint32_t *obj,
> +                                     const char *name, Error **errp)
> +{
> +    ber_output_int(v, *obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_output_type_uint64_t(Visitor *v, uint64_t *obj,
> +                                     const char *name, Error **errp)
> +{
> +    ber_output_int(v, *obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_output_type_int8_t(Visitor *v, int8_t *obj,
> +                                   const char *name, Error **errp)
> +{
> +    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_output_type_int16_t(Visitor *v, int16_t *obj,
> +                                    const char *name, Error **errp)
> +{
> +    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_output_type_int32_t(Visitor *v, int32_t *obj,
> +                                    const char *name, Error **errp)
> +{
> +    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_output_type_int64_t(Visitor *v, int64_t *obj,
> +                                    const char *name, Error **errp)
> +{
> +    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
> +}
> +
> +static void ber_output_type_bool(Visitor *v, bool *obj, const char *name,
> +                                 Error **errp)
> +{
> +    BEROutputVisitor *aov = to_aov(v);
> +    bool b = 0;
> +
> +    switch (aov->mode) {
> +    case BER_TYPE_PRIMITIVE:
> +        b = *obj;
> +        break;
> +    case BER_TYPE_CONSTRUCTED:
> +        b = (*obj) ? 0xff : 0;
> +        break;
> +    }
> +    ber_output_fragment(v, BER_TYPE_BOOLEAN, (uint8_t *)&b, sizeof(b), errp);
> +}
> +
> +static void ber_output_type_str(Visitor *v, char **obj, const char *name,
> +                                Error **errp)
> +{
> +#ifdef BER_DEBUG
> +    fprintf(stderr, "Writing string %s, len = 0x%02x\n", *obj,
> +            (int)strlen(*obj));
> +#endif
> +    ber_output_fragment(v, BER_TYPE_IA5_STRING,
> +                        (uint8_t *)*obj,
> +                        *obj == NULL ? 0 : strlen(*obj), errp);
> +}
> +
> +static void ber_output_sized_buffer(Visitor *v, uint8_t **obj,
> +                                    size_t size, const char *name,
> +                                    Error **errp)
> +{
> +    ber_output_fragment(v, BER_TYPE_OCTET_STRING,
> +                        *obj, size, errp);
> +}
> +
> +void ber_output_visitor_cleanup(BEROutputVisitor *v)
> +{
> +    QStackEntry *e, *tmp;
> +
> +    QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
> +        QTAILQ_REMOVE(&v->stack, e, node);
> +        if (e->qfile) {
> +            qemu_fclose(e->qfile);
> +        }
> +        g_free(e);
> +    }
> +
> +    g_free(v);
> +}
> +
> +
> +Visitor *ber_output_get_visitor(BEROutputVisitor *v)
> +{
> +    return &v->visitor;
> +}
> +
> +BEROutputVisitor *ber_output_visitor_new(QEMUFile *qfile,
> +                                         BERTypePC mode)
> +{
> +    BEROutputVisitor *v;
> +
> +    v = g_malloc0(sizeof(*v));
> +
> +    v->visitor.start_struct = ber_output_start_struct;
> +    v->visitor.end_struct = ber_output_end_struct;
> +    v->visitor.start_array = ber_output_start_array;
> +    v->visitor.next_array = ber_output_next_array;
> +    v->visitor.end_array = ber_output_end_array;
> +    v->visitor.start_list = ber_output_start_list;
> +    v->visitor.next_list = ber_output_next_list;
> +    v->visitor.end_list = ber_output_end_list;
> +    v->visitor.type_int = ber_output_type_int;
> +    v->visitor.type_uint8_t = ber_output_type_uint8_t;
> +    v->visitor.type_uint16_t = ber_output_type_uint16_t;
> +    v->visitor.type_uint32_t = ber_output_type_uint32_t;
> +    v->visitor.type_uint64_t = ber_output_type_uint64_t;
> +    v->visitor.type_int8_t = ber_output_type_int8_t;
> +    v->visitor.type_int16_t = ber_output_type_int16_t;
> +    v->visitor.type_int32_t = ber_output_type_int32_t;
> +    v->visitor.type_int64_t = ber_output_type_int64_t;
> +    v->visitor.type_bool = ber_output_type_bool;
> +    v->visitor.type_str = ber_output_type_str;
> +    v->visitor.type_sized_buffer = ber_output_sized_buffer;
> +
> +    QTAILQ_INIT(&v->stack);
> +    v->qfile = qfile;
> +    v->mode = mode;
> +
> +    return v;
> +}
> Index: b/ber/ber-output-visitor.h
> ===================================================================
> --- /dev/null
> +++ b/ber/ber-output-visitor.h
> @@ -0,0 +1,28 @@
> +/*
> + * BER Output Visitor header
> + *
> + * Copyright IBM, Corp. 2011
> + *
> + * Authors:
> + *  Anthony Liguori   <aliguori@us.ibm.com>
> + *  Stefan Berger     <stefanb@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#ifndef BER_OUTPUT_VISITOR_H
> +#define BER_OUTPUT_VISITOR_H
> +
> +#include "include/qapi/visitor.h"
> +#include "ber.h"
> +
> +typedef struct BEROutputVisitor BEROutputVisitor;
> +
> +BEROutputVisitor *ber_output_visitor_new(QEMUFile *, BERTypePC mode);
> +void ber_output_visitor_cleanup(BEROutputVisitor *v);
> +
> +Visitor *ber_output_get_visitor(BEROutputVisitor *v);
> +
> +#endif
> Index: b/ber/ber.h
> ===================================================================
> --- /dev/null
> +++ b/ber/ber.h
> @@ -0,0 +1,85 @@
> +#ifndef BER_BER_H
> +#define BER_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.
> + */
> +
> +typedef enum ber_type_class {
> +    BER_TYPE_CLASS_UNIVERSAL = 0x0 << 7,
> +    BER_TYPE_CLASS_APPLICATION = 0x1 << 6,
> +    BER_TYPE_CLASS_CONTENT_SPECIFIC = 0x2 << 6,
> +    BER_TYPE_CLASS_PRIVATE = 0x3 << 6,
> +    BER_TYPE_CLASS_MASK = 0x3 << 6 /* Mask to get class */
> +} BERTypeClass;
> +
> +/* P/C bit */
> +typedef enum ber_type_p_c {
> +    BER_TYPE_PRIMITIVE = (0x0 << 5),
> +    BER_TYPE_CONSTRUCTED = (0x1 << 5),
> +    BER_TYPE_P_C_MASK = (0x1 << 5) /* Mask to get P/C bit */
> +} BERTypePC;
> +
> +typedef enum ber_type_tag {
> +    BER_TYPE_EOC              /*  P        0       0*/,
> +    BER_TYPE_BOOLEAN          /*  P        1       1*/,
> +    BER_TYPE_INTEGER          /*  P        2       2*/,
> +    BER_TYPE_BIT_STRING       /*  P/C      3       3*/,
> +    BER_TYPE_OCTET_STRING     /*  P/C      4       4*/,
> +    BER_TYPE_NULL             /*  P        5       5*/,
> +    BER_TYPE_OBJECT_ID        /*  P        6       6*/,
> +    BER_TYPE_OBJECT_DESC      /*  P        7       7*/,
> +    BER_TYPE_EXTERNAL         /*  C        8       8*/,
> +    BER_TYPE_REAL             /*  P        9       9*/,
> +    BER_TYPE_ENUMERATED       /*  P        10      A*/,
> +    BER_TYPE_EMBEDDED         /*  C        11      B*/,
> +    BER_TYPE_UTF8_STRING      /*  P/C      12      C*/,
> +    BER_TYPE_RELATIVE_OID     /*  P        13      D*/,
> +    BER_TYPE_UNUSED_0xE       /*                    */,
> +    BER_TYPE_UNUSED_0xF       /*                    */,
> +    BER_TYPE_SEQUENCE         /*  C        16      10*/,
> +    BER_TYPE_SET              /*  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      1F*/,
> +    BER_TYPE_TAG_MASK = 0x1f /* Mask to get tag */,
> +    BER_TYPE_CUSTOM_LIST = 0x20,
> +} 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;
> +
> +const char *ber_type_to_str(uint8_t ber_type);
> +
> +#endif /* BER_BER_H */
> Index: b/qapi/qapi-visit-core.c
> ===================================================================
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -67,6 +67,28 @@ void visit_end_list(Visitor *v, Error **
>      v->end_list(v, errp);
>  }
> 
> +void visit_start_array(Visitor *v, void **obj, const char *name,
> +                       size_t elem_count, size_t elem_size, Error **errp)
> +{
> +    if (!error_is_set(errp)) {
> +        v->start_array(v, obj, name, elem_count, elem_size, errp);
> +    }
> +}
> +
> +void visit_next_array(Visitor *v, Error **errp)
> +{
> +    if (!error_is_set(errp)) {
> +        v->next_array(v, errp);
> +    }
> +}
> +
> +void visit_end_array(Visitor *v, Error **errp)
> +{
> +    if (!error_is_set(errp)) {
> +        v->end_array(v, errp);
> +    }
> +}
> +
>  void visit_start_optional(Visitor *v, bool *present, const char *name,
>                            Error **errp)
>  {
> @@ -313,3 +335,11 @@ void input_type_enum(Visitor *v, int *ob
>      g_free(enum_str);
>      *obj = value;
>  }
> +
> +void visit_type_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
> +                             const char *name, Error **errp)
> +{
> +    if (!error_is_set(errp)) {
> +        v->type_sized_buffer(v, obj, len, name, errp);
> +    }
> +}
> Index: b/include/qapi/visitor.h
> ===================================================================
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -22,6 +22,10 @@ typedef struct GenericList
>      struct GenericList *next;
>  } GenericList;
> 
> +typedef struct GenericItem {
> +    void *value;
> +} GenericItem;
> +
>  typedef struct Visitor Visitor;
> 
>  void visit_start_handle(Visitor *v, void **obj, const char *kind,
> @@ -33,6 +37,10 @@ void visit_end_struct(Visitor *v, Error
>  void visit_start_list(Visitor *v, const char *name, Error **errp);
>  GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
>  void visit_end_list(Visitor *v, Error **errp);
> +void visit_start_array(Visitor *v, void **obj, const char *name,
> +                       size_t elem_count, size_t elem_size, Error **errp);
> +void visit_next_array(Visitor *v, Error **errp);
> +void visit_end_array(Visitor *v, Error **errp);
>  void visit_start_optional(Visitor *v, bool *present, const char *name,
>                            Error **errp);
>  void visit_end_optional(Visitor *v, Error **errp);
> @@ -51,5 +59,7 @@ void visit_type_size(Visitor *v, uint64_
>  void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
>  void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
>  void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
> +void visit_type_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
> +                             const char *name, Error **errp);
> 
>  #endif
> Index: b/include/qapi/visitor-impl.h
> ===================================================================
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -26,14 +26,37 @@ struct Visitor
>      GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
>      void (*end_list)(Visitor *v, Error **errp);
> 
> +    void (*start_array)(Visitor *v, void **obj, const char *name,
> +                        size_t elem_count, size_t elem_size, Error **errp);
> +    void (*next_array)(Visitor *v, Error **errp);
> +    void (*end_array)(Visitor *v, Error **errp);

There's a more recent patch to add these interfaces, they were
renamed in response to review comments from Anthony, so we should name
those accordingly. Adding Visitor interfaces for arrays also deserves
it's own patch and careful attention, so please include this patch in your
series:

http://lists.gnu.org/archive/html/qemu-devel/2012-10/msg05782.html

> +
>      void (*type_enum)(Visitor *v, int *obj, const char *strings[],
>                        const char *kind, const char *name, Error **errp);
> 
>      void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
> +    void (*type_uint8_t)(Visitor *v, uint8_t *obj, const char *name,
> +          Error **errp);
> +    void (*type_uint16_t)(Visitor *v, uint16_t *obj, const char *name,
> +          Error **errp);
> +    void (*type_uint32_t)(Visitor *v, uint32_t *obj, const char *name,
> +          Error **errp);
> +    void (*type_uint64_t)(Visitor *v, uint64_t *obj, const char *name,
> +          Error **errp);
> +    void (*type_int8_t)(Visitor *v, int8_t *obj, const char *name,
> +                        Error **errp);
> +    void (*type_int16_t)(Visitor *v, int16_t *obj, const char *name,
> +                         Error **errp);
> +    void (*type_int32_t)(Visitor *v, int32_t *obj, const char *name,
> +          Error **errp);
> +    void (*type_int64_t)(Visitor *v, int64_t *obj, const char *name,
> +          Error **errp);

These interfaces already exist under a slightly different name:

    void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
    void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);                                                                                                 
    void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp);
    void (*type_uint64)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
    void (*type_int8)(Visitor *v, int8_t *obj, const char *name, Error **errp);
    void (*type_int16)(Visitor *v, int16_t *obj, const char *name, Error **errp);
    void (*type_int32)(Visitor *v, int32_t *obj, const char *name, Error **errp);
    void (*type_int64)(Visitor *v, int64_t *obj, const char *name, Error **errp);


>      void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
>      void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
>      void (*type_number)(Visitor *v, double *obj, const char *name,
>                          Error **errp);
> +    void (*type_sized_buffer)(Visitor *v, uint8_t **obj, size_t size,
> +                              const char *name, Error **errp);

Can't {start,next,end}_array handle this already?

> 
>      /* May be NULL */
>      void (*start_optional)(Visitor *v, bool *present, const char *name,
> Index: b/include/qapi/qmp/qerror.h
> ===================================================================
> --- a/include/qapi/qmp/qerror.h
> +++ b/include/qapi/qmp/qerror.h
> @@ -249,4 +249,10 @@ void assert_no_error(Error *err);
>  #define QERR_SOCKET_CREATE_FAILED \
>      ERROR_CLASS_GENERIC_ERROR, "Failed to create socket"
> 
> +#define QERR_INVALID_STREAM \
> +    ERROR_CLASS_GENERIC_ERROR, "Data stream is invalid, error was '%s'"
> +
> +#define QERR_QEMUFILE_ERROR \
> +    ERROR_CLASS_GENERIC_ERROR, "QEMUFile has an error, error was '%s'"
> +

new policy on error classes is to avoid adding new ones and stick with
ERROR_CLASS_GENERIC_ERROR/error_setg() unless there's some special reason
not to.

>  #endif /* QERROR_H */
> Index: b/ber/Makefile.objs
> ===================================================================
> --- /dev/null
> +++ b/ber/Makefile.objs
> @@ -0,0 +1,2 @@
> +common-obj-y += ber-common.o ber-input-visitor.o ber-output-visitor.o
> +
> 
>
Stefan Berger Feb. 27, 2013, 11:24 p.m. UTC | #6
On 02/27/2013 05:57 PM, mdroth wrote:
> On Tue, Feb 26, 2013 at 05:03:56PM -0600, jschopp@linux.vnet.ibm.com wrote:
>> These patches implement asn1 ber visitors for encoding and decoding data.
>> References: <20130226230354.982917686@linux.vnet.ibm.com>
>> Content-Disposition: inline; filename=asn1_all.diff
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
>> Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
>> ---
>>   Makefile.objs               |   10
>>   ber/Makefile.objs           |    2
>>   ber/ber-common.c            |   56 ++
>>   ber/ber-input-visitor.c     |  969 ++++++++++++++++++++++++++++++++++++++++++++
>>   ber/ber-input-visitor.h     |   30 +
>>   ber/ber-output-visitor.c    |  563 +++++++++++++++++++++++++
>>   ber/ber-output-visitor.h    |   28 +
> Please break the input/output implementations out into separate patches,
> and follow those up with test cases. See:
>
> tests/test-{string,qmp}-{input,output}-visitor.c
> tests/test-visitor-serialization.c

I saw this with the other test cases... The problem with the ASN.1 
encoding is that writing ASN.1 into a buffer doesn't make much sense, 
unless of course you were to compare it against a binary string that was 
generated through other means, which I didn't do. So instead I wrote 
test cases that write the ASN.1 stream using the output visitor and then 
immediately feed the streams into the input visitor and then I compare 
the outcome against expected outcome, e.g., that a stucture's fields 
were properly filled. I hope this makes more sense in the ASN.1 case.

    Stefan
Michael Roth Feb. 27, 2013, 11:52 p.m. UTC | #7
On Wed, Feb 27, 2013 at 06:24:45PM -0500, Stefan Berger wrote:
> On 02/27/2013 05:57 PM, mdroth wrote:
> >On Tue, Feb 26, 2013 at 05:03:56PM -0600, jschopp@linux.vnet.ibm.com wrote:
> >>These patches implement asn1 ber visitors for encoding and decoding data.
> >>References: <20130226230354.982917686@linux.vnet.ibm.com>
> >>Content-Disposition: inline; filename=asn1_all.diff
> >>
> >>Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> >>Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
> >>---
> >>  Makefile.objs               |   10
> >>  ber/Makefile.objs           |    2
> >>  ber/ber-common.c            |   56 ++
> >>  ber/ber-input-visitor.c     |  969 ++++++++++++++++++++++++++++++++++++++++++++
> >>  ber/ber-input-visitor.h     |   30 +
> >>  ber/ber-output-visitor.c    |  563 +++++++++++++++++++++++++
> >>  ber/ber-output-visitor.h    |   28 +
> >Please break the input/output implementations out into separate patches,
> >and follow those up with test cases. See:
> >
> >tests/test-{string,qmp}-{input,output}-visitor.c
> >tests/test-visitor-serialization.c
> 
> I saw this with the other test cases... The problem with the ASN.1
> encoding is that writing ASN.1 into a buffer doesn't make much
> sense, unless of course you were to compare it against a binary
> string that was generated through other means, which I didn't do. So
> instead I wrote test cases that write the ASN.1 stream using the
> output visitor and then immediately feed the streams into the input
> visitor and then I compare the outcome against expected outcome,
> e.g., that a stucture's fields were properly filled. I hope this
> makes more sense in the ASN.1 case.

This is actually exactly what test-visitor-serialization.c does. It
feeds various normal/corner cases of visit_type_* into your output
visitor, takes that output and feeds it back into the input visitor,
then validates it against the original input. Just need to create
a SerializeOps implementation to drive your visitor and plug it
in like the others.

Just doing that would be a good start at least, but it would be really
nice to validate the encoding against some other reference implementation.
Have you looked into libsnacc? It seems to be the most readilly
available library. I wouldn't make it a formal build requirement, but it
would be nice to be able to execute test cases that use it if it's
present.

Even failing that, I personally wouldn't even mind just generating an
encoded blob outside the tree, and then checking that it along with the
textual description and steps to generate it, then validating the
visitors against it. It's not too far off from our "hand-written" JSON
to test the QMP visitors.

I don't think we have any reasonable assurance that our implementation
is correct otherwise.

> 
>    Stefan
> 
>
Paolo Bonzini Feb. 28, 2013, 9:27 a.m. UTC | #8
Il 27/02/2013 23:11, mdroth ha scritto:
>> > As for the location of the source files I think where the patch puts
>> > them now makes most sense, I don't think qapi makes sense.  But the
>> > location isn't important to me so I'll put them wherever.  Anybody
>> > else have an opinion on the best location?
> qapi/ along with all the other visitor implementations is probably the
> right place for now. If in the future we end up with dozens of
> implementations pulling into ridiculous amounts of dependencies we might
> want to reconsider things, but for now it's safest to assume that any
> serialization format for can be selected at runtime for a Visitor user,
> so they should all be made available via qapi-obj-y.

It's now util-obj-y, BTW, but I agree entirely.
Stefan Berger Feb. 28, 2013, 3:40 p.m. UTC | #9
On 02/27/2013 03:28 AM, Andreas Färber wrote:
> Am 27.02.2013 00:03, schrieb jschopp@linux.vnet.ibm.com:
>
> [snip]
>
> I wrote an ASN.1 implementation once, and it seems to me you are mixing
> up ASN.1 and BER here in your enum/array/... naming. The types are
> general ASN.1 concepts and apply to other encodings as well, no?

What 'naming' are you referring too?
For me this is also a while back when Michael Tsirkin and I wrote the 
code... some time in 2nd half of 2011.

>
> Also I remember that BER has disjoint CER and DER flavors that I don't
> see your output visitor distinguishing on brief sight, only P/C.

We do support definite length form of DER and the indefinite length 
encoding form of CER.


> Input visitor ideally handles both fine.

It does. The output visitor has to be configured with a flag choosing 
either one of the types of output (CER/DER).

>
> ASN.1
> - textual
> - BER
>    - CER
>    - DER
> - PER (?)
> - XER
>
> While XER being XML-based just like the textual ASN.1 notation is
> everything but efficient for transmission, keeping our options open to
> implement either of these later for debugging purposes (e.g., verifying
> VMState format) would be nice.

At least XER will require a separate implementation. I am not so 
familiar with PER except that it produces a 'packed' encoding.

    Stefan
Stefan Berger March 1, 2013, 10:10 p.m. UTC | #10
On 02/27/2013 06:52 PM, mdroth wrote:
> On Wed, Feb 27, 2013 at 06:24:45PM -0500, Stefan Berger wrote:
>
> This is actually exactly what test-visitor-serialization.c does. It
> feeds various normal/corner cases of visit_type_* into your output
> visitor, takes that output and feeds it back into the input visitor,
> then validates it against the original input. Just need to create
> a SerializeOps implementation to drive your visitor and plug it
> in like the others.
>
> Just doing that would be a good start at least, but it would be really
> nice to validate the encoding against some other reference implementation.
> Have you looked into libsnacc? It seems to be the most readilly
> available library. I wouldn't make it a formal build requirement, but it
> would be nice to be able to execute test cases that use it if it's
> present.

I have seen the documentation about libtasn1

http://www.gnu.org/software/libtasn1/

I am not familiar with libsnacc.


>
> Even failing that, I personally wouldn't even mind just generating an
> encoded blob outside the tree, and then checking that it along with the
> textual description and steps to generate it, then validating the
> visitors against it. It's not too far off from our "hand-written" JSON
> to test the QMP visitors.

Ok, so we will do that. It will lock our implementation into generating 
one type of byte stream, which is good.


>
> I don't think we have any reasonable assurance that our implementation
> is correct otherwise.

Well, at least the round-tripping of data written with the output 
visitors and then read again with the input visitors plus following 
verification gives you some aspects of correct implementation.

One design choice I have made while implementing the encoder/decoder was 
that integers will always be encoded in their respective size, meaning 
that independent of value of a 32bit integer it will be represented in 4 
bytes. This may, following ITU X.690 spec, be a application-specific or 
private encoding of an integer. See the spec section 8.3 for that.

http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf

  I have done this to avoid deflating and inflating blobs when used in 
NVRAM later on so that we can use the input visitor to find a blob with 
a given name and then switch to the output visitor and replace that blob 
with a new value. If such a blob would deflate/inflate because of the 
value of an integer all consecutive blobs in the NVRAM would have to be 
moved to maintain readability of the ASN.1 stream. The NVRAM will be one 
long ASN.1 stream in a block device. However, we could give output 
visitor users control over how the integers are to be encoded (following 
standard versus fixed-width) by passing a flag when instantiating the 
output visitor.

Regards,
    Stefan
diff mbox

Patch

Index: b/Makefile.objs
===================================================================
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -57,7 +57,12 @@  common-obj-$(CONFIG_POSIX) += os-posix.o
 common-obj-$(CONFIG_LINUX) += fsdev/
 
 common-obj-y += migration.o migration-tcp.o
-common-obj-y += qemu-char.o qemu-file.o #aio.o
+
+#ber has to be linked against qemu-file.o so we must add them on the same line
+ber-nested-y = ber-input-visitor.o ber-output-visitor.o ber-common.o
+ber-obj-y = $(addprefix ber/, $(ber-nested-y))
+
+common-obj-y += qemu-char.o qemu-file.o $(ber-obj-y) #aio.o
 common-obj-y += block-migration.o
 common-obj-y += page_cache.o xbzrle.o
 
@@ -116,4 +121,7 @@  nested-vars += \
 	qga-obj-y \
 	block-obj-y \
 	common-obj-y
+# \
+#	ber-obj-y
+
 dummy := $(call unnest-vars)
Index: b/ber/ber-common.c
===================================================================
--- /dev/null
+++ b/ber/ber-common.c
@@ -0,0 +1,56 @@ 
+/*
+ * ASN.1 Basic Encoding Rules Common functions
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Stefan Berger     <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include <stdint.h>
+
+#include "ber.h"
+
+static const char *ber_type_names[] = {
+    "BER_TYPE_EOC",
+    "BER_TYPE_BOOLEAN",
+    "BER_TYPE_INTEGER",
+    "BER_TYPE_BIT_STRING",
+    "BER_TYPE_OCTET_STRING",
+    "BER_TYPE_NULL",
+    "BER_TYPE_OBJECT_ID",
+    "BER_TYPE_OBJECT_DESC",
+    "BER_TYPE_EXTERNAL",
+    "BER_TYPE_REAL",
+    "BER_TYPE_ENUMERATED",
+    "BER_TYPE_EMBEDDED",
+    "BER_TYPE_UTF8_STRING",
+    "BER_TYPE_RELATIVE_OID",
+    "BER_TYPE_UNUSED_0xE",
+    "BER_TYPE_UNUSED_0xF",
+    "BER_TYPE_SEQUENCE",
+    "BER_TYPE_SET",
+    "BER_TYPE_NUMERIC_STRING",
+    "BER_TYPE_PRINTABLE_STRING",
+    "BER_TYPE_T61STRING",
+    "BER_TYPE_VIDEOTEX_STRING",
+    "BER_TYPE_IA5_STRING",
+    "BER_TYPE_UTCTIME",
+    "BER_TYPE_GENERALIZED_TIME",
+    "BER_TYPE_GRAPHIC_STRING",
+    "BER_TYPE_VISIBLE_STRING",
+    "BER_TYPE_GENERAL_STRING",
+    "BER_TYPE_UNIVERSAL_STRING",
+    "BER_TYPE_CHARACTER_STRING"
+    "BER_TYPE_BMP_STRING",
+    "BER_TYPE_LONG_FORM",
+};
+
+const char *ber_type_to_str(uint8_t ber_type)
+{
+    return ber_type_names[ber_type & BER_TYPE_TAG_MASK];
+}
Index: b/ber/ber-input-visitor.c
===================================================================
--- /dev/null
+++ b/ber/ber-input-visitor.c
@@ -0,0 +1,969 @@ 
+/*
+ * BER Input Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Stefan Berger     <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "ber-input-visitor.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "ber.h"
+#include "include/qapi/qmp/qerror.h"
+#include "migration/qemu-file.h"
+#include "qapi/visitor-impl.h"
+
+#define AIV_STACK_SIZE 1024
+
+/* whether to allow the parsing of primitives that are fragmented */
+#define BER_ALLOW_FRAGMENTED_PRIMITIVES
+
+/* #define BER_DEBUG */
+
+typedef struct StackEntry {
+    uint64_t cur_pos;
+} StackEntry;
+
+struct BERInputVisitor {
+    Visitor visitor;
+    QEMUFile *qfile;
+    uint64_t cur_pos;
+    StackEntry stack[AIV_STACK_SIZE];
+    int nb_stack;
+    uint64_t max_allowed_buffer_size;
+    uint64_t largest_needed_buffer;
+};
+
+static BERInputVisitor *to_biv(Visitor *v)
+{
+    return container_of(v, BERInputVisitor, visitor);
+}
+
+static void ber_input_push(BERInputVisitor *aiv,
+                           uint64_t cur_pos, Error **errp)
+{
+    aiv->stack[aiv->nb_stack].cur_pos = cur_pos;
+    aiv->nb_stack++;
+
+    if (aiv->nb_stack >= AIV_STACK_SIZE) {
+        error_set(errp, QERR_BUFFER_OVERRUN);
+        return;
+    }
+}
+
+static uint64_t ber_input_pop(BERInputVisitor *aiv, Error **errp)
+{
+    aiv->nb_stack--;
+
+    if (aiv->nb_stack < 0) {
+        error_set(errp, QERR_BUFFER_OVERRUN);
+        return 0;
+    }
+
+    return aiv->stack[aiv->nb_stack].cur_pos;
+}
+
+/*
+ * Read a type tag from the stream. Up-to 32 bit type tags are supported
+ * for reading and otherwise an error is returned. Anything larger than that
+ * would not be reasonable and could only be abused.
+ */
+static uint32_t ber_read_type(BERInputVisitor *aiv, uint8_t *ber_type_flags,
+                              Error **errp)
+{
+    uint32_t type;
+    uint8_t byte;
+    uint8_t ctr = 0;
+    char buf[128];
+
+    if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+        error_set(errp, QERR_QEMUFILE_ERROR,
+                  "Error while reading type");
+        return 0;
+    }
+    aiv->cur_pos++;
+    type = byte;
+
+    *ber_type_flags = type & (BER_TYPE_P_C_MASK | BER_TYPE_CLASS_MASK);
+
+    if ((type & BER_TYPE_TAG_MASK) == BER_TYPE_LONG_FORM) {
+        type = 0;
+        while (true) {
+            type <<= 7;
+            if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+                error_set(errp, QERR_QEMUFILE_ERROR,
+                          "Error while reading long type");
+                return 0;
+            }
+            aiv->cur_pos++;
+
+            type |= (byte & 0x7f);
+            if ((byte & 0x80) == 0) {
+                break;
+            }
+            ctr += 7; /* read 7 bits */
+            if (ctr >= (sizeof(type) * 8)) {
+                /* only support 32 bit length identifiers */
+                snprintf(buf, sizeof(buf),
+                         "type tag is larger than 32 bit (offset %" PRIu64
+                         ")", aiv->cur_pos);
+                error_set(errp, QERR_INVALID_STREAM,
+                          buf);
+                return 0;
+            }
+        }
+    } else {
+        type &= BER_TYPE_TAG_MASK;
+    }
+
+    return type;
+}
+
+static uint64_t ber_read_length(BERInputVisitor *aiv, bool *is_indefinite,
+                                Error **errp)
+{
+    uint8_t byte, c, int_len;
+    uint64_t len = 0;
+    QEMUFile *qfile = aiv->qfile;
+    char buf[128];
+
+    *is_indefinite = false;
+
+    if (qemu_read_bytes(qfile, &byte, 1) != 1) {
+        error_set(errp, QERR_QEMUFILE_ERROR,
+                  "Error while reading length indicator");
+        return ~0x0ULL;
+    }
+    aiv->cur_pos++;
+
+    if (byte == BER_LENGTH_INDEFINITE) {
+        *is_indefinite = true;
+        return ~0x0ULL;
+    }
+
+    if (!(byte & BER_LENGTH_LONG)) {
+        len = byte;
+    } else {
+        int_len = byte & BER_LENGTH_MASK;
+        if (int_len > 8) {
+            snprintf(buf, sizeof(buf), "ASN.1 integer length field %d > 8",
+                     int_len);
+            /* Length can be up to 127 byte, but it seems
+             * safe to assume any input will be < 1TB in length. */
+            error_set(errp, QERR_INVALID_PARAMETER, buf);
+            return ~0x0ULL;
+        }
+        for (c = 0; c < int_len; c++) {
+            len <<= 8;
+            if (qemu_read_bytes(qfile, &byte, 1) != 1) {
+                error_set(errp, QERR_QEMUFILE_ERROR,
+                          "Error while reading length");
+                return ~0x0ULL;
+            }
+            len |= byte;
+        }
+        aiv->cur_pos += int_len;
+    }
+
+    if (len > aiv->max_allowed_buffer_size) {
+        snprintf(buf, sizeof(buf),
+                 "Length indicator (%"PRIu64") in input byte stream "
+                 "exceeds maximum allowed length (%"PRIu64").",
+                 len, aiv->max_allowed_buffer_size);
+        error_set(errp,  QERR_INVALID_STREAM, buf);
+        return ~0x0ULL;
+    }
+
+    if (len > aiv->largest_needed_buffer) {
+        aiv->largest_needed_buffer = len;
+    }
+
+    return len;
+}
+
+static uint64_t ber_peek_is_eoc(BERInputVisitor *biv, Error **errp)
+{
+    uint8_t buf[2];
+    QEMUFile *qfile = biv->qfile;
+
+    if (qemu_peek_bytes(qfile, buf, 2, 0) != 2) {
+        error_set(errp, QERR_QEMUFILE_ERROR,
+                  "Error while peeking for EOC");
+        return ~0x0ULL;
+    }
+
+    if (buf[0] == BER_TYPE_EOC && buf[1] == 0) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static void ber_skip_bytes(BERInputVisitor *aiv, uint64_t to_skip,
+                           Error **errp)
+{
+    uint8_t buf[1024];
+    uint32_t skip;
+
+    /* skip length bytes */
+    while (to_skip > 0) {
+        skip = MIN(to_skip, sizeof(buf));
+        if (qemu_read_bytes(aiv->qfile, buf, skip) != skip) {
+            error_set(errp, QERR_QEMUFILE_ERROR,
+                      "Error while skipping over bytes");
+            return;
+        }
+        aiv->cur_pos += skip;
+        to_skip -= skip;
+    }
+}
+
+static void ber_skip_until_eoc(BERInputVisitor *aiv, Error **errp)
+{
+    uint32_t ber_type_tag;
+    uint64_t length;
+    bool is_indefinite;
+    uint8_t ber_type_flags;
+    uint64_t indefinite_nesting = 1;
+    char buf[128];
+
+    while (!error_is_set(errp)) {
+        ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+
+        length = ber_read_length(aiv, &is_indefinite, errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+        if (ber_type_tag == BER_TYPE_EOC) {
+            if (length) {
+                snprintf(buf, sizeof(buf),
+                         "ASN.1 EOC length field at offset %" PRIu64
+                         " is invalid", aiv->cur_pos);
+                error_set(errp, QERR_INVALID_PARAMETER, buf);
+                return;
+            }
+            if (!indefinite_nesting) {
+                snprintf(buf, sizeof(buf),
+                         "ASN.1 EOC at offset %" PRIu64
+                         "not within an indefinite length",
+                         aiv->cur_pos);
+                error_set(errp, QERR_INVALID_PARAMETER, buf);
+                return;
+            }
+#ifdef BER_DEBUG
+            fprintf(stderr, "found end! nesting=%" PRIdMAX
+                    ", pos=%" PRIu64 "\n",
+                    indefinite_nesting, aiv->cur_pos);
+#endif
+            if (!--indefinite_nesting) {
+                return;
+            }
+        }
+        if (is_indefinite) {
+            if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_PRIMITIVE) {
+                snprintf(buf, sizeof(buf),
+                         "ASN.1 indefinite length in a primitive type "
+                         "at offset %" PRIu64,
+                         aiv->cur_pos);
+                error_set(errp, QERR_INVALID_PARAMETER, buf);
+                return;
+            }
+            if (indefinite_nesting == ~0x0ULL) {
+                snprintf(buf, sizeof(buf),
+                         "ASN.1 indefinite nesting level is too large "
+                         "(offset %" PRIu64 ")",
+                         aiv->cur_pos);
+                error_set(errp, QERR_INVALID_PARAMETER, buf);
+                return;
+            }
+            ++indefinite_nesting;
+        } else {
+#ifdef BER_DEBUG
+            fprintf(stderr, "skipping type '%s' of length "
+                    "%" PRIu64 " at %" PRIu64 ".\n",
+                    ber_type_to_str(ber_type_tag), length, aiv->cur_pos);
+#endif
+            ber_skip_bytes(aiv, length, errp);
+        }
+    }
+}
+
+static void ber_input_start_constructed(Visitor *v, uint32_t exp_ber_type,
+                                        uint8_t exp_ber_flags, void **obj,
+                                        const char *kind, const char *name,
+                                        size_t size, Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+    uint32_t ber_type_tag;
+    uint8_t ber_type_flags;
+    int64_t len;
+    bool is_indefinite;
+    char buf[128];
+
+    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (ber_type_tag != exp_ber_type || ber_type_flags != exp_ber_flags) {
+        sprintf(buf, "%s at offset %" PRIu64 "\n",
+                ber_type_to_str(exp_ber_type), aiv->cur_pos);
+
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                  ber_type_to_str(ber_type_tag),
+                  buf);
+        return;
+    }
+
+    if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_PRIMITIVE) {
+        snprintf(buf, sizeof(buf), "primitive type (%s)",
+                 ber_type_to_str(ber_type_tag));
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                  buf, "constructed type");
+        return;
+    }
+
+    len = ber_read_length(aiv, &is_indefinite, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (!is_indefinite) {
+#ifdef BER_DEBUG
+        fprintf(stderr, "structure/set len: %" PRIi64 "\n", len);
+#endif
+        ber_input_push(aiv, aiv->cur_pos + len, errp);
+    } else {
+#ifdef BER_DEBUG
+        fprintf(stderr, "indefinite length encoding!\n");
+#endif
+        ber_input_push(aiv, 0, errp);
+    }
+
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (size > 0 && *obj == NULL) {
+        *obj = g_malloc0(size);
+#ifdef BER_DEBUG
+        fprintf(stderr, "for type '%s' allocated buffer at %p, size = %zu\n",
+                ber_type_to_str(ber_type_tag), *obj, size);
+#endif
+    }
+}
+
+static void ber_input_end_constructed(Visitor *v, Error **errp)
+{
+    uint64_t new_pos;
+    BERInputVisitor *aiv = to_biv(v);
+
+    new_pos = ber_input_pop(aiv, errp);
+
+    if (new_pos != 0) {
+#ifdef BER_DEBUG
+        fprintf(stderr, "new_pos = %" PRIu64 "\n", new_pos);
+#endif
+        aiv->cur_pos = new_pos;
+    } else {
+#ifdef BER_DEBUG
+        fprintf(stderr, "searching for end...\n");
+        fprintf(stderr, "cur_pos = %" PRIu64 "\n", aiv->cur_pos);
+#endif
+        ber_skip_until_eoc(aiv, errp);
+    }
+}
+
+static void ber_input_start_struct(Visitor *v, void **obj, const char *kind,
+                                   const char *name, size_t size, Error **errp)
+{
+    ber_input_start_constructed(v, BER_TYPE_SEQUENCE, BER_TYPE_CONSTRUCTED,
+                                obj, kind, name, size, errp);
+}
+
+static void ber_input_end_struct(Visitor *v, Error **errp)
+{
+    ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_start_array(Visitor *v, void **obj,
+                                  const char *name, size_t elem_count,
+                                  size_t elem_size, Error **errp)
+{
+    ber_input_start_constructed(v, BER_TYPE_SET, BER_TYPE_CONSTRUCTED,
+                                obj, NULL, name,
+                                elem_count * elem_size, errp);
+}
+
+static void ber_input_next_array(Visitor *v, Error **errp)
+{
+    /* nothing to do here */
+}
+
+static void ber_input_end_array(Visitor *v, Error **errp)
+{
+    ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_start_list(Visitor *v, const char *name,
+                                 Error **errp)
+{
+    void *obj = NULL;
+    ber_input_start_constructed(v, BER_TYPE_CUSTOM_LIST, BER_TYPE_CONSTRUCTED,
+                                obj, NULL, name, 0, errp);
+    g_free(obj);
+}
+
+static GenericList *ber_input_next_list(Visitor *v, GenericList **list,
+                                        Error **errp)
+{
+    BERInputVisitor *biv = to_biv(v);
+    GenericList *entry;
+    StackEntry *se = &biv->stack[biv->nb_stack - 1];
+
+    if (se->cur_pos == 0) {
+        /* indefinite lenght encoding is used */
+        se = &biv->stack[biv->nb_stack];
+        if (ber_peek_is_eoc(biv, errp) != 0) {
+            return NULL;
+        }
+    } else if (se->cur_pos <= biv->cur_pos) {
+        return NULL;
+    }
+
+    entry = g_malloc0(sizeof(*entry));
+    if (*list) {
+        (*list)->next = entry;
+    }
+
+    return entry;
+}
+
+static void ber_input_end_list(Visitor *v, Error **errp)
+{
+    ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_integer(Visitor *v, uint8_t *obj, uint8_t maxbytes,
+                              Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+    uint32_t ber_type_tag;
+    uint8_t ber_type_flags, byte;
+    bool is_indefinite;
+    uint64_t len;
+    uint64_t val = 0;
+    int c;
+    char buf[128];
+
+#ifdef BER_DEBUG
+    fprintf(stderr, "reading int to %p\n", obj);
+#endif
+
+    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+#ifdef BER_DEBUG
+    fprintf(stderr, "%s: got type: 0x%02x, expected 0x%02x\n",
+            __func__, ber_type_tag, BER_TYPE_INTEGER);
+#endif
+
+    if (ber_type_tag != BER_TYPE_INTEGER || ber_type_flags != 0) {
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                  ber_type_to_str(ber_type_tag),
+                  ber_type_to_str(BER_TYPE_INTEGER));
+        return;
+    }
+    len = ber_read_length(aiv, &is_indefinite, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+#ifdef BER_DEBUG
+    fprintf(stderr, "pos: %" PRIu64 " int len: %" PRIi64 "\n",
+            aiv->cur_pos, len);
+#endif
+
+    if (is_indefinite) {
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  "ASN.1 int indicator is indefinite",
+                  "[1..8]");
+        return;
+    }
+    if (len > maxbytes) {
+        snprintf(buf, sizeof(buf), "ASN.1 integer length indicator %" PRIi64
+                 " is larger than expected (%u bytes)\n",
+                 len, maxbytes);
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  buf, "[1..8]");
+        return;
+    }
+
+    for (c = 0; c < len ; c++) {
+        val <<= 8;
+        if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+            error_set(errp, QERR_QEMUFILE_ERROR,
+                      "Error while reading integer");
+            return;
+        }
+        val |= byte;
+        if (c == 0 && (val & 0x80) == 0x80) {
+            /* sign extend */
+            val |= 0xFFFFFFFFFFFFFF00ULL;
+        }
+    }
+    aiv->cur_pos += len;
+#ifdef BER_DEBUG
+    fprintf(stderr, "pos: %" PRIu64 " int: %" PRIu64 "\n", aiv->cur_pos, val);
+#endif
+
+    memcpy(obj, &val, maxbytes);
+}
+
+static void ber_input_type_int(Visitor *v, int64_t *obj, const char *name,
+                               Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint8_t(Visitor *v, uint8_t *obj,
+                                   const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint16_t(Visitor *v, uint16_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint32_t(Visitor *v, uint32_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint64_t(Visitor *v, uint64_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int8_t(Visitor *v, int8_t *obj,
+                                  const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int16_t(Visitor *v, int16_t *obj,
+                                   const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int32_t(Visitor *v, int32_t *obj,
+                                   const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int64_t(Visitor *v, int64_t *obj,
+                                   const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_bool(Visitor *v, bool *obj, const char *name,
+                                Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+    uint32_t ber_type_tag;
+    uint8_t ber_type_flags, byte;
+    bool is_indefinite;
+    uint64_t len;
+    char buf[128];
+
+    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (ber_type_tag != BER_TYPE_BOOLEAN || ber_type_flags != 0) {
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                  ber_type_to_str(ber_type_tag),
+                  ber_type_to_str(BER_TYPE_BOOLEAN));
+        return;
+    }
+    len = ber_read_length(aiv, &is_indefinite, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+#ifdef BER_DEBUG
+    fprintf(stderr, "pos: %" PRIu64 " bool len: %" PRIi64 "\n",
+            aiv->cur_pos, len);
+#endif
+
+    if (is_indefinite || len != 1) {
+        snprintf(buf, sizeof(buf),
+                 "ASN.1 bool length indicator at offset %" PRIu64
+                 " is indefinite or != 1",
+                 aiv->cur_pos);
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  buf, "1");
+        return;
+    }
+    if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+        error_set(errp, QERR_QEMUFILE_ERROR,
+                  "Error while reading boolean");
+        return;
+    }
+    aiv->cur_pos++;
+    *obj = byte;
+
+#ifdef BER_DEBUG
+    fprintf(stderr, "pos: %" PRIu64 " bool: %d\n", aiv->cur_pos, *obj);
+#endif
+}
+
+/* Function for recursive reading of fragmented primitives */
+static uint32_t ber_input_fragment(BERInputVisitor *aiv,
+                                   uint32_t exp_type_tag,
+                                   uint8_t exp_type_flags,
+                                   uint8_t **buffer, size_t *buffer_len,
+                                   bool may_realloc,
+                                   uint32_t offset, uint32_t nesting,
+                                   bool indefinite, uint64_t max_pos,
+                                   const char *name, Error **errp)
+{
+    uint32_t ber_type_tag;
+    uint8_t ber_type_flags;
+    uint32_t bytes_read = 0;
+    bool is_indefinite;
+    uint64_t len;
+    char buf[128];
+
+    assert((exp_type_flags & BER_TYPE_CONSTRUCTED) == BER_TYPE_PRIMITIVE);
+
+    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+    if (error_is_set(errp)) {
+        return 0;
+    }
+
+    if (ber_type_tag != exp_type_tag) {
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                  ber_type_to_str(ber_type_tag & BER_TYPE_TAG_MASK),
+                  ber_type_to_str(exp_type_tag));
+        return 0;
+    }
+
+    if ((ber_type_flags & BER_TYPE_CONSTRUCTED)) {
+#ifndef BER_ALLOW_FRAGMENTED_PRIMITIVES
+        error_set(errp, QERR_INVALID_STREAM,
+                  "constructed encoding of primitive types is not supported");
+        goto err_exit;
+#else
+        if (nesting == 1) {
+            /* don't allow further nesting */
+            error_set(errp, QERR_INVALID_STREAM, "invalid nesting");
+            goto err_exit;
+        }
+        len = ber_read_length(aiv, &is_indefinite, errp);
+        if (error_is_set(errp)) {
+            goto err_exit;
+        }
+#ifdef BER_DEBUG
+        fprintf(stderr, "pos: %" PRIu64 " string len: %" PRIi64 "\n",
+                aiv->cur_pos, len);
+#endif
+
+        if (!is_indefinite) {
+            if ((*buffer) == NULL) {
+                /* allocate buffer once; due to the ASN.1 overhead it
+                 * will be bigger than what we need */
+                *buffer = g_malloc0(len);
+                *buffer_len = len;
+                may_realloc = false;
+            }
+        }
+#ifdef BER_DEBUG
+        fprintf(stderr, "recursing now to read constructed type.\n");
+        fprintf(stderr, "  is_indefinite: %d\n", is_indefinite);
+#endif
+        bytes_read += ber_input_fragment(aiv, exp_type_tag, exp_type_flags,
+                                         buffer, buffer_len, may_realloc,
+                                         offset, nesting + 1, is_indefinite,
+                                         aiv->cur_pos + len, name, errp);
+        return bytes_read;
+#endif
+    }
+
+    while (true) {
+        /* Would reading the length carry us beyond what we are allowed to
+         * read?
+         */
+        if (!indefinite &&
+            max_pos != 0 &&
+            aiv->cur_pos + 1 > max_pos) {
+            snprintf(buf, sizeof(buf),
+                     "data stream would cause parsing beyond "
+                     "allowed offset at %" PRIu64,
+                     max_pos);
+            /* input stream is malformed */
+            error_set(errp, QERR_INVALID_STREAM, buf);
+            goto err_exit;
+        }
+
+        /* not-constructed case */
+        len = ber_read_length(aiv, &is_indefinite, errp);
+        if (error_is_set(errp)) {
+            goto err_exit;
+        }
+#ifdef BER_DEBUG
+        fprintf(stderr, "pos: %" PRIu64 " string len: %" PRIi64 "\n",
+                    aiv->cur_pos, len);
+#endif
+        if (is_indefinite) {
+            snprintf(buf, sizeof(buf),
+                     "Got indefinite type length in primitive type (%s) at"
+                     "offset %" PRIu64,
+                     ber_type_to_str(ber_type_tag), aiv->cur_pos);
+            error_set(errp, QERR_INVALID_PARAMETER, buf);
+            goto err_exit;
+        }
+        /* if max_pos is not set, set it here */
+        if (!indefinite && max_pos == 0) {
+            max_pos = aiv->cur_pos + len;
+        }
+
+        /* Would reading the data carry us beyond what we are allowed to
+         * read ?
+         */
+        if (!indefinite && aiv->cur_pos + len > max_pos) {
+            /* input stream is malformed */
+            snprintf(buf, sizeof(buf),
+                     "data stream would cause parsing beyond "
+                     "allowed offset at %" PRIu64,
+                     max_pos);
+            error_set(errp, QERR_INVALID_STREAM, buf);
+            goto err_exit;
+        }
+
+        if (offset + len > *buffer_len) {
+            if (!may_realloc) {
+                snprintf(buf, sizeof(buf),
+                         "given buffer is too small (%lu < %"PRIu64")",
+                         (unsigned long)*buffer_len, offset + len);
+                error_set(errp, QERR_INVALID_STREAM, buf);
+            }
+            /* allocate one more byte for strings, set to 0 */
+            *buffer = g_realloc(*buffer, offset + len + 1);
+            *buffer_len = offset + len;
+            (*buffer)[offset+len] = 0;
+        }
+
+        if (qemu_read_bytes(aiv->qfile,
+                            &((uint8_t *)*buffer)[offset], len) != len) {
+            error_set(errp, QERR_QEMUFILE_ERROR,
+                      "Error while reading data");
+            goto err_exit;
+        }
+
+        offset += len;
+        bytes_read += len;
+
+        aiv->cur_pos += len;
+#ifdef BER_DEBUG
+        if (exp_type_tag == BER_TYPE_IA5_STRING) {
+            fprintf(stderr, "pos: %" PRIu64 " string: %.*s\n", aiv->cur_pos,
+                    offset, *buffer);
+        }
+#endif
+
+        if (nesting == 0) {
+            break;
+        }
+
+        /* indefinite length case: loop until we encounter EOC */
+        if (indefinite) {
+            ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+            if (error_is_set(errp)) {
+                goto err_exit;
+            }
+
+            if (ber_type_tag == BER_TYPE_EOC) {
+                uint8_t byte;
+                if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+                    error_set(errp, QERR_QEMUFILE_ERROR,
+                              "Error while reading BER_TYPE_EOC length");
+                    goto err_exit;
+                }
+                aiv->cur_pos++;
+
+                if (byte != 0) {
+                    snprintf(buf, sizeof(buf),
+                             "ASN.1 EOC length field is invalid at offset "
+                             "%" PRIu64,
+                             aiv->cur_pos);
+                    error_set(errp, QERR_INVALID_PARAMETER, buf);
+                    goto err_exit;
+                }
+                return bytes_read;
+            }
+
+            if (ber_type_tag != exp_type_tag ||
+                ber_type_flags != exp_type_flags) {
+                snprintf(buf, sizeof(buf),
+                         "ASN.1 type field or flags are wrong. Found "
+                         "0x%x/%u, expected "
+                         "0x%x/%u at offset %" PRIu64,
+                         ber_type_tag, ber_type_flags,
+                         exp_type_tag, exp_type_flags,
+                         aiv->cur_pos);
+                error_set(errp, QERR_INVALID_PARAMETER, buf);
+                goto err_exit;
+            }
+            continue;
+        }
+
+        /* in definite length coding case; caller told us how far to read */
+        if (aiv->cur_pos == max_pos) {
+            return bytes_read;
+        }
+
+        ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+        if (error_is_set(errp)) {
+            goto err_exit;
+        }
+
+        if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_CONSTRUCTED) {
+            error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                      "constructed BER type",
+                      ber_type_to_str(exp_type_tag));
+            goto err_exit;
+        }
+
+        if (ber_type_tag != exp_type_tag) {
+            error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                      ber_type_to_str(ber_type_tag & BER_TYPE_TAG_MASK),
+                      ber_type_to_str(exp_type_tag));
+            goto err_exit;
+        }
+    }
+    return bytes_read;
+
+err_exit:
+    if (may_realloc) {
+        g_free(*buffer);
+        *buffer = NULL;
+    }
+    return 0;
+}
+
+static void ber_input_type_str(Visitor *v, char **obj, const char *name,
+                               Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+    size_t buffer_len = 0;
+
+    ber_input_fragment(aiv, BER_TYPE_IA5_STRING, 0,
+                       (uint8_t **)obj, &buffer_len, (*obj == NULL),
+                       0, 0, false, 0, name, errp);
+
+    if (!error_is_set(errp) && *obj == NULL) {
+        /* adjust NULL string to "" */
+        *obj = g_strdup("");
+    }
+}
+
+static void ber_input_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
+                                   const char *name, Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+
+    ber_input_fragment(aiv, BER_TYPE_OCTET_STRING, 0,
+                       (uint8_t **)obj, &len, (*obj == NULL),
+                       0, 0, false, 0, name, errp);
+
+#ifdef BER_DEBUG
+    fprintf(stderr, "pos: %" PRIu64 " data at: %p data:\n",
+            aiv->cur_pos, *obj);
+    int i;
+    for (i = 0; i < len; i++) {
+        fprintf(stderr, "%02x ", (*obj)[i]);
+        if ((i & 0xf) == 0xf) {
+            fprintf(stderr, "\n");
+        }
+    }
+    fprintf(stderr, "\n");
+#endif
+}
+
+Visitor *ber_input_get_visitor(BERInputVisitor *v)
+{
+    return &v->visitor;
+}
+
+uint64_t ber_input_get_parser_position(BERInputVisitor *v)
+{
+    return v->cur_pos;
+}
+
+uint64_t ber_input_get_largest_needed_buffer(BERInputVisitor *v)
+{
+    return v->largest_needed_buffer;
+}
+
+void ber_input_visitor_cleanup(BERInputVisitor *v)
+{
+    g_free(v);
+}
+
+BERInputVisitor *ber_input_visitor_new(QEMUFile *qfile,
+                                       uint64_t max_allowed_buffer_size)
+{
+    BERInputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->visitor.start_struct = ber_input_start_struct;
+    v->visitor.end_struct = ber_input_end_struct;
+    v->visitor.start_array = ber_input_start_array;
+    v->visitor.next_array = ber_input_next_array;
+    v->visitor.end_array = ber_input_end_array;
+    v->visitor.start_list = ber_input_start_list;
+    v->visitor.next_list = ber_input_next_list;
+    v->visitor.end_list = ber_input_end_list;
+    v->visitor.type_int = ber_input_type_int;
+    v->visitor.type_uint8_t = ber_input_type_uint8_t;
+    v->visitor.type_uint16_t = ber_input_type_uint16_t;
+    v->visitor.type_uint32_t = ber_input_type_uint32_t;
+    v->visitor.type_uint64_t = ber_input_type_uint64_t;
+    v->visitor.type_int8_t = ber_input_type_int8_t;
+    v->visitor.type_int16_t = ber_input_type_int16_t;
+    v->visitor.type_int32_t = ber_input_type_int32_t;
+    v->visitor.type_int64_t = ber_input_type_int64_t;
+    v->visitor.type_bool = ber_input_type_bool;
+    v->visitor.type_str = ber_input_type_str;
+    v->visitor.type_sized_buffer = ber_input_sized_buffer;
+
+    v->qfile = qfile;
+    v->cur_pos = 0;
+    v->max_allowed_buffer_size = max_allowed_buffer_size;
+    v->largest_needed_buffer = 0;
+
+    return v;
+}
Index: b/ber/ber-input-visitor.h
===================================================================
--- /dev/null
+++ b/ber/ber-input-visitor.h
@@ -0,0 +1,30 @@ 
+/*
+ * BER Input Visitor header
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Stefan Berger     <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef BER_INPUT_VISITOR_H
+#define BER_INPUT_VISITOR_H
+
+#include "include/qapi/visitor.h"
+
+typedef struct BERInputVisitor BERInputVisitor;
+
+BERInputVisitor *ber_input_visitor_new(QEMUFile *,
+                                       uint64_t max_allowd_buffer_size);
+void ber_input_visitor_cleanup(BERInputVisitor *v);
+uint64_t ber_input_get_parser_position(BERInputVisitor *v);
+uint64_t ber_input_get_largest_needed_buffer(BERInputVisitor *v);
+
+Visitor *ber_input_get_visitor(BERInputVisitor *v);
+
+#endif
Index: b/ber/ber-output-visitor.c
===================================================================
--- /dev/null
+++ b/ber/ber-output-visitor.c
@@ -0,0 +1,563 @@ 
+/*
+ * BER Output Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Stefan Berger     <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "ber-output-visitor.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "include/qapi/qmp/qerror.h"
+#include "hw/hw.h"
+#include "ber.h"
+#include "qapi/visitor-impl.h"
+
+
+#define CER_FRAGMENT_CHUNK_SIZE  1000
+
+/*#define BER_DEBUG*/
+
+typedef struct QStackEntry {
+    QEMUFile *qfile;
+    bool is_list_head;
+    QTAILQ_ENTRY(QStackEntry) node;
+} QStackEntry;
+
+typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
+
+struct BEROutputVisitor {
+    Visitor visitor;
+    QStack stack;
+    QEMUFile *qfile;
+
+    BERTypePC mode;
+};
+
+static BEROutputVisitor *to_aov(Visitor *v)
+{
+    return container_of(v, BEROutputVisitor, visitor);
+}
+
+static void ber_output_push(BEROutputVisitor *qov, QEMUFile *qfile,
+                            Error **errp)
+{
+    QStackEntry *e = g_malloc0(sizeof(*e));
+
+    e->qfile = qfile;
+    e->is_list_head = true;
+    QTAILQ_INSERT_HEAD(&qov->stack, e, node);
+}
+
+static QEMUFile *ber_output_pop(BEROutputVisitor *qov)
+{
+    QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+    QEMUFile *qfile;
+
+    QTAILQ_REMOVE(&qov->stack, e, node);
+    qfile = e->qfile;
+    g_free(e);
+
+    return qfile;
+}
+
+static unsigned int ber_encode_type(uint8_t *buffer, uint32_t buflen,
+                                    enum ber_type_tag 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;
+}
+
+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;
+}
+
+static void ber_output_start_constructed(Visitor *v, uint32_t ber_type,
+                                         Error **errp)
+{
+    BEROutputVisitor *aov = to_aov(v);
+    uint8_t buf[20];
+    unsigned int tag_bytes_written;
+
+    switch (aov->mode) {
+    case BER_TYPE_PRIMITIVE:
+        ber_output_push(aov, aov->qfile, errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+        aov->qfile = qemu_bufopen("w", NULL);
+        break;
+    case BER_TYPE_CONSTRUCTED:
+        ber_output_push(aov, aov->qfile, errp); /* needed for list support */
+        if (error_is_set(errp)) {
+            return;
+        }
+        tag_bytes_written = ber_encode_type(buf, sizeof(buf),
+                                            ber_type, BER_TYPE_CONSTRUCTED,
+                                            errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+        buf[tag_bytes_written] = BER_LENGTH_INDEFINITE;
+        if (qemu_write_bytes(aov->qfile, buf, 1 + tag_bytes_written) !=
+            1 + tag_bytes_written) {
+            error_set(errp, QERR_QEMUFILE_ERROR,
+                      "Error while writing constructed type");
+            return;
+        }
+    }
+}
+
+static void ber_output_constructed_ber_close(BEROutputVisitor *aov,
+                                             uint32_t ber_type,
+                                             Error **errp)
+{
+    uint8_t buf[20];
+    const QEMUSizedBuffer *qsb;
+    uint64_t len;
+    unsigned int num_bytes, tag_bytes_written;
+    QEMUFile *qfile = ber_output_pop(aov);
+
+    tag_bytes_written = ber_encode_type(buf, sizeof(buf),
+                                        ber_type, BER_TYPE_CONSTRUCTED,
+                                        errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    qsb = qemu_buf_get(aov->qfile);
+    len = qsb_get_length(qsb);
+#ifdef BER_DEBUG
+    fprintf(stderr, "constructed type (0x%02x, %p) has length %ld bytes\n",
+            ber_type, aov->qfile, len);
+#endif
+
+    num_bytes = ber_encode_len(&buf[tag_bytes_written],
+                               sizeof(buf) - tag_bytes_written,
+                               len, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+    if (qemu_write_bytes(qfile, buf, tag_bytes_written + num_bytes) !=
+        tag_bytes_written + num_bytes ||
+        qemu_write_bytes(qfile, qsb_get_buffer(qsb, 0),
+                         qsb_get_length(qsb)) != qsb_get_length(qsb)) {
+        error_set(errp, QERR_QEMUFILE_ERROR,
+                  "Error while writing buffer");
+        return;
+    }
+
+    qemu_fclose(aov->qfile);
+    aov->qfile = qfile;
+    qemu_fflush(qfile);
+}
+
+static void ber_output_end_constructed(Visitor *v, uint32_t ber_type,
+                                       Error **errp)
+{
+    BEROutputVisitor *aov = to_aov(v);
+    uint8_t buf[20];
+
+#ifdef BER_DEBUG
+    fprintf(stderr, "end set/struct:\n");
+#endif
+
+    switch (aov->mode) {
+    case BER_TYPE_PRIMITIVE:
+        ber_output_constructed_ber_close(aov, ber_type, errp);
+        break;
+
+    case BER_TYPE_CONSTRUCTED:
+        ber_output_pop(aov);
+        buf[0] = BER_TYPE_EOC;
+        buf[1] = 0;
+        if (qemu_write_bytes(aov->qfile, buf, 2) != 2) {
+            error_set(errp, QERR_QEMUFILE_ERROR,
+                      "Error while writing buffer with BER_TYPE_EOC");
+            return;
+        }
+        break;
+    }
+}
+
+static void ber_output_start_struct(Visitor *v, void **obj, const char *kind,
+                                    const char *name, size_t unused,
+                                    Error **errp)
+{
+    ber_output_start_constructed(v, BER_TYPE_SEQUENCE, errp);
+}
+
+static void ber_output_end_struct(Visitor *v, Error **errp)
+{
+    ber_output_end_constructed(v, BER_TYPE_SEQUENCE, errp);
+}
+
+static void ber_output_start_array(Visitor *v, void **obj,
+                                   const char *name, size_t elem_count,
+                                   size_t elem_size, Error **errp)
+{
+    ber_output_start_constructed(v, BER_TYPE_SET, errp);
+}
+
+static void ber_output_next_array(Visitor *v, Error **errp)
+{
+    /* nothing to do here */
+}
+
+static void ber_output_end_array(Visitor *v, Error **errp)
+{
+    ber_output_end_constructed(v, BER_TYPE_SET, errp);
+}
+
+static void ber_output_start_list(Visitor *v, const char *name,
+                                  Error **errp)
+{
+    ber_output_start_constructed(v, BER_TYPE_CUSTOM_LIST, errp);
+}
+
+static GenericList *ber_output_next_list(Visitor *v, GenericList **listp,
+                                         Error **errp)
+{
+    GenericList *list = *listp;
+    BEROutputVisitor *bov = to_aov(v);
+    QStackEntry *e = QTAILQ_FIRST(&bov->stack);
+
+    assert(e);
+    if (e->is_list_head) {
+        e->is_list_head = false;
+        return list;
+    }
+
+    return list ? list->next : NULL;
+}
+
+static void ber_output_end_list(Visitor *v, Error **errp)
+{
+    ber_output_end_constructed(v, BER_TYPE_CUSTOM_LIST, errp);
+}
+
+static void ber_output_fragment(Visitor *v, uint32_t ber_type,
+                                uint8_t *buffer,
+                                size_t buflen, Error **errp)
+{
+    uint32_t offset = 0;
+    bool fragmented = false;
+    uint32_t chunk;
+    unsigned int num_bytes, type_bytes;
+    uint8_t buf[20];
+    uint32_t chunk_size;
+    BEROutputVisitor *aov = to_aov(v);
+
+    switch (aov->mode) {
+    case BER_TYPE_CONSTRUCTED:
+        /* X.690 9.2 */
+        fragmented = (buflen > CER_FRAGMENT_CHUNK_SIZE);
+        chunk_size = 1000;
+        break;
+    case BER_TYPE_PRIMITIVE:
+        chunk_size = 0xffffffff;
+        break;
+    }
+
+    if (fragmented) {
+        ber_output_start_constructed(&aov->visitor, ber_type, errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+    }
+
+    do {
+        chunk = (buflen - offset > chunk_size) ? chunk_size : buflen - offset;
+
+        type_bytes = ber_encode_type(buf, sizeof(buf), ber_type, 0,
+                                     errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+        num_bytes = ber_encode_len(&buf[type_bytes], sizeof(buf) - type_bytes,
+                                   chunk, errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+        if (qemu_write_bytes(aov->qfile, buf, type_bytes + num_bytes) !=
+            type_bytes + num_bytes ||
+            qemu_write_bytes(aov->qfile, &buffer[offset], chunk) != chunk) {
+            error_set(errp, QERR_QEMUFILE_ERROR,
+                      "Error while writing buffer");
+            return;
+        }
+        offset += chunk;
+    } while (offset < buflen);
+
+    if (fragmented) {
+        ber_output_end_constructed(&aov->visitor, ber_type, errp);
+    }
+}
+
+static void ber_output_int(Visitor *v, int64_t val, uint8_t maxnumbytes,
+                           Error **errp)
+{
+    uint8_t buf[20];
+    int shift =  (maxnumbytes - 1) * 8;
+    uint64_t mask = 0xFF80ULL << (shift - 8);
+    bool exp_zeros;
+    int c = 0;
+    BEROutputVisitor *aov = to_aov(v);
+
+#ifdef BER_DEBUG
+    fprintf(stderr, "Writing int 0x%lx (signed=%d, len=%d)\n",
+            val, is_signed, maxnumbytes);
+#endif
+
+    buf[0] = BER_TYPE_INTEGER;
+
+    if (maxnumbytes > 1) {
+        exp_zeros = ((mask & val) == 0) ? true : false;
+        while (mask != 0xFF) {
+            if (exp_zeros) {
+                if ((mask & val) != 0) {
+                    break;
+                }
+            } else {
+                if ((mask & val) != mask) {
+                    break;
+                }
+            }
+            shift -= 8;
+            mask >>= 8;
+        }
+    }
+
+    while (shift >= 0) {
+        buf[2+c] = (val >> shift);
+        c++;
+        shift -= 8;
+    }
+    buf[1] = c;
+
+    if (qemu_write_bytes(aov->qfile, buf, 1 + 1 + c) != 1 + 1 + c) {
+        error_set(errp, QERR_QEMUFILE_ERROR, "Error while writing integer");
+        return;
+    }
+}
+
+static void ber_output_type_int(Visitor *v, int64_t *obj, const char *name,
+                                Error **errp)
+{
+    ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint8_t(Visitor *v, uint8_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint16_t(Visitor *v, uint16_t *obj,
+                                     const char *name, Error **errp)
+{
+    ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint32_t(Visitor *v, uint32_t *obj,
+                                     const char *name, Error **errp)
+{
+    ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint64_t(Visitor *v, uint64_t *obj,
+                                     const char *name, Error **errp)
+{
+    ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int8_t(Visitor *v, int8_t *obj,
+                                   const char *name, Error **errp)
+{
+    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int16_t(Visitor *v, int16_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int32_t(Visitor *v, int32_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int64_t(Visitor *v, int64_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_bool(Visitor *v, bool *obj, const char *name,
+                                 Error **errp)
+{
+    BEROutputVisitor *aov = to_aov(v);
+    bool b = 0;
+
+    switch (aov->mode) {
+    case BER_TYPE_PRIMITIVE:
+        b = *obj;
+        break;
+    case BER_TYPE_CONSTRUCTED:
+        b = (*obj) ? 0xff : 0;
+        break;
+    }
+    ber_output_fragment(v, BER_TYPE_BOOLEAN, (uint8_t *)&b, sizeof(b), errp);
+}
+
+static void ber_output_type_str(Visitor *v, char **obj, const char *name,
+                                Error **errp)
+{
+#ifdef BER_DEBUG
+    fprintf(stderr, "Writing string %s, len = 0x%02x\n", *obj,
+            (int)strlen(*obj));
+#endif
+    ber_output_fragment(v, BER_TYPE_IA5_STRING,
+                        (uint8_t *)*obj,
+                        *obj == NULL ? 0 : strlen(*obj), errp);
+}
+
+static void ber_output_sized_buffer(Visitor *v, uint8_t **obj,
+                                    size_t size, const char *name,
+                                    Error **errp)
+{
+    ber_output_fragment(v, BER_TYPE_OCTET_STRING,
+                        *obj, size, errp);
+}
+
+void ber_output_visitor_cleanup(BEROutputVisitor *v)
+{
+    QStackEntry *e, *tmp;
+
+    QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
+        QTAILQ_REMOVE(&v->stack, e, node);
+        if (e->qfile) {
+            qemu_fclose(e->qfile);
+        }
+        g_free(e);
+    }
+
+    g_free(v);
+}
+
+
+Visitor *ber_output_get_visitor(BEROutputVisitor *v)
+{
+    return &v->visitor;
+}
+
+BEROutputVisitor *ber_output_visitor_new(QEMUFile *qfile,
+                                         BERTypePC mode)
+{
+    BEROutputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->visitor.start_struct = ber_output_start_struct;
+    v->visitor.end_struct = ber_output_end_struct;
+    v->visitor.start_array = ber_output_start_array;
+    v->visitor.next_array = ber_output_next_array;
+    v->visitor.end_array = ber_output_end_array;
+    v->visitor.start_list = ber_output_start_list;
+    v->visitor.next_list = ber_output_next_list;
+    v->visitor.end_list = ber_output_end_list;
+    v->visitor.type_int = ber_output_type_int;
+    v->visitor.type_uint8_t = ber_output_type_uint8_t;
+    v->visitor.type_uint16_t = ber_output_type_uint16_t;
+    v->visitor.type_uint32_t = ber_output_type_uint32_t;
+    v->visitor.type_uint64_t = ber_output_type_uint64_t;
+    v->visitor.type_int8_t = ber_output_type_int8_t;
+    v->visitor.type_int16_t = ber_output_type_int16_t;
+    v->visitor.type_int32_t = ber_output_type_int32_t;
+    v->visitor.type_int64_t = ber_output_type_int64_t;
+    v->visitor.type_bool = ber_output_type_bool;
+    v->visitor.type_str = ber_output_type_str;
+    v->visitor.type_sized_buffer = ber_output_sized_buffer;
+
+    QTAILQ_INIT(&v->stack);
+    v->qfile = qfile;
+    v->mode = mode;
+
+    return v;
+}
Index: b/ber/ber-output-visitor.h
===================================================================
--- /dev/null
+++ b/ber/ber-output-visitor.h
@@ -0,0 +1,28 @@ 
+/*
+ * BER Output Visitor header
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Stefan Berger     <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef BER_OUTPUT_VISITOR_H
+#define BER_OUTPUT_VISITOR_H
+
+#include "include/qapi/visitor.h"
+#include "ber.h"
+
+typedef struct BEROutputVisitor BEROutputVisitor;
+
+BEROutputVisitor *ber_output_visitor_new(QEMUFile *, BERTypePC mode);
+void ber_output_visitor_cleanup(BEROutputVisitor *v);
+
+Visitor *ber_output_get_visitor(BEROutputVisitor *v);
+
+#endif
Index: b/ber/ber.h
===================================================================
--- /dev/null
+++ b/ber/ber.h
@@ -0,0 +1,85 @@ 
+#ifndef BER_BER_H
+#define BER_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.
+ */
+
+typedef enum ber_type_class {
+    BER_TYPE_CLASS_UNIVERSAL = 0x0 << 7,
+    BER_TYPE_CLASS_APPLICATION = 0x1 << 6,
+    BER_TYPE_CLASS_CONTENT_SPECIFIC = 0x2 << 6,
+    BER_TYPE_CLASS_PRIVATE = 0x3 << 6,
+    BER_TYPE_CLASS_MASK = 0x3 << 6 /* Mask to get class */
+} BERTypeClass;
+
+/* P/C bit */
+typedef enum ber_type_p_c {
+    BER_TYPE_PRIMITIVE = (0x0 << 5),
+    BER_TYPE_CONSTRUCTED = (0x1 << 5),
+    BER_TYPE_P_C_MASK = (0x1 << 5) /* Mask to get P/C bit */
+} BERTypePC;
+
+typedef enum ber_type_tag {
+    BER_TYPE_EOC              /*  P        0       0*/,
+    BER_TYPE_BOOLEAN          /*  P        1       1*/,
+    BER_TYPE_INTEGER          /*  P        2       2*/,
+    BER_TYPE_BIT_STRING       /*  P/C      3       3*/,
+    BER_TYPE_OCTET_STRING     /*  P/C      4       4*/,
+    BER_TYPE_NULL             /*  P        5       5*/,
+    BER_TYPE_OBJECT_ID        /*  P        6       6*/,
+    BER_TYPE_OBJECT_DESC      /*  P        7       7*/,
+    BER_TYPE_EXTERNAL         /*  C        8       8*/,
+    BER_TYPE_REAL             /*  P        9       9*/,
+    BER_TYPE_ENUMERATED       /*  P        10      A*/,
+    BER_TYPE_EMBEDDED         /*  C        11      B*/,
+    BER_TYPE_UTF8_STRING      /*  P/C      12      C*/,
+    BER_TYPE_RELATIVE_OID     /*  P        13      D*/,
+    BER_TYPE_UNUSED_0xE       /*                    */,
+    BER_TYPE_UNUSED_0xF       /*                    */,
+    BER_TYPE_SEQUENCE         /*  C        16      10*/,
+    BER_TYPE_SET              /*  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      1F*/,
+    BER_TYPE_TAG_MASK = 0x1f /* Mask to get tag */,
+    BER_TYPE_CUSTOM_LIST = 0x20,
+} 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;
+
+const char *ber_type_to_str(uint8_t ber_type);
+
+#endif /* BER_BER_H */
Index: b/qapi/qapi-visit-core.c
===================================================================
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -67,6 +67,28 @@  void visit_end_list(Visitor *v, Error **
     v->end_list(v, errp);
 }
 
+void visit_start_array(Visitor *v, void **obj, const char *name,
+                       size_t elem_count, size_t elem_size, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->start_array(v, obj, name, elem_count, elem_size, errp);
+    }
+}
+
+void visit_next_array(Visitor *v, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->next_array(v, errp);
+    }
+}
+
+void visit_end_array(Visitor *v, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->end_array(v, errp);
+    }
+}
+
 void visit_start_optional(Visitor *v, bool *present, const char *name,
                           Error **errp)
 {
@@ -313,3 +335,11 @@  void input_type_enum(Visitor *v, int *ob
     g_free(enum_str);
     *obj = value;
 }
+
+void visit_type_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
+                             const char *name, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->type_sized_buffer(v, obj, len, name, errp);
+    }
+}
Index: b/include/qapi/visitor.h
===================================================================
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -22,6 +22,10 @@  typedef struct GenericList
     struct GenericList *next;
 } GenericList;
 
+typedef struct GenericItem {
+    void *value;
+} GenericItem;
+
 typedef struct Visitor Visitor;
 
 void visit_start_handle(Visitor *v, void **obj, const char *kind,
@@ -33,6 +37,10 @@  void visit_end_struct(Visitor *v, Error
 void visit_start_list(Visitor *v, const char *name, Error **errp);
 GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
 void visit_end_list(Visitor *v, Error **errp);
+void visit_start_array(Visitor *v, void **obj, const char *name,
+                       size_t elem_count, size_t elem_size, Error **errp);
+void visit_next_array(Visitor *v, Error **errp);
+void visit_end_array(Visitor *v, Error **errp);
 void visit_start_optional(Visitor *v, bool *present, const char *name,
                           Error **errp);
 void visit_end_optional(Visitor *v, Error **errp);
@@ -51,5 +59,7 @@  void visit_type_size(Visitor *v, uint64_
 void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
 void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
 void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
+void visit_type_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
+                             const char *name, Error **errp);
 
 #endif
Index: b/include/qapi/visitor-impl.h
===================================================================
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -26,14 +26,37 @@  struct Visitor
     GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
     void (*end_list)(Visitor *v, Error **errp);
 
+    void (*start_array)(Visitor *v, void **obj, const char *name,
+                        size_t elem_count, size_t elem_size, Error **errp);
+    void (*next_array)(Visitor *v, Error **errp);
+    void (*end_array)(Visitor *v, Error **errp);
+
     void (*type_enum)(Visitor *v, int *obj, const char *strings[],
                       const char *kind, const char *name, Error **errp);
 
     void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
+    void (*type_uint8_t)(Visitor *v, uint8_t *obj, const char *name,
+          Error **errp);
+    void (*type_uint16_t)(Visitor *v, uint16_t *obj, const char *name,
+          Error **errp);
+    void (*type_uint32_t)(Visitor *v, uint32_t *obj, const char *name,
+          Error **errp);
+    void (*type_uint64_t)(Visitor *v, uint64_t *obj, const char *name,
+          Error **errp);
+    void (*type_int8_t)(Visitor *v, int8_t *obj, const char *name,
+                        Error **errp);
+    void (*type_int16_t)(Visitor *v, int16_t *obj, const char *name,
+                         Error **errp);
+    void (*type_int32_t)(Visitor *v, int32_t *obj, const char *name,
+          Error **errp);
+    void (*type_int64_t)(Visitor *v, int64_t *obj, const char *name,
+          Error **errp);
     void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
     void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
     void (*type_number)(Visitor *v, double *obj, const char *name,
                         Error **errp);
+    void (*type_sized_buffer)(Visitor *v, uint8_t **obj, size_t size,
+                              const char *name, Error **errp);
 
     /* May be NULL */
     void (*start_optional)(Visitor *v, bool *present, const char *name,
Index: b/include/qapi/qmp/qerror.h
===================================================================
--- a/include/qapi/qmp/qerror.h
+++ b/include/qapi/qmp/qerror.h
@@ -249,4 +249,10 @@  void assert_no_error(Error *err);
 #define QERR_SOCKET_CREATE_FAILED \
     ERROR_CLASS_GENERIC_ERROR, "Failed to create socket"
 
+#define QERR_INVALID_STREAM \
+    ERROR_CLASS_GENERIC_ERROR, "Data stream is invalid, error was '%s'"
+
+#define QERR_QEMUFILE_ERROR \
+    ERROR_CLASS_GENERIC_ERROR, "QEMUFile has an error, error was '%s'"
+
 #endif /* QERROR_H */
Index: b/ber/Makefile.objs
===================================================================
--- /dev/null
+++ b/ber/Makefile.objs
@@ -0,0 +1,2 @@ 
+common-obj-y += ber-common.o ber-input-visitor.o ber-output-visitor.o
+