summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile29
-rw-r--r--Makefile.objs19
-rw-r--r--Makefile.target23
-rw-r--r--block.c232
-rw-r--r--block/Makefile.objs14
-rw-r--r--block/blkdebug.c3
-rw-r--r--block/blkverify.c114
-rw-r--r--block/bochs.c3
-rw-r--r--block/cow.c16
-rw-r--r--block/curl.c13
-rw-r--r--block/gluster.c28
-rw-r--r--block/iscsi.c142
-rw-r--r--block/nbd.c43
-rw-r--r--block/parallels.c3
-rw-r--r--block/qcow.c20
-rw-r--r--block/qcow2-cluster.c22
-rw-r--r--block/qcow2.c20
-rw-r--r--block/qed.c24
-rw-r--r--block/quorum.c870
-rw-r--r--block/sheepdog.c7
-rw-r--r--block/vdi.c29
-rw-r--r--block/vhdx.c25
-rw-r--r--block/vmdk.c142
-rw-r--r--block/vpc.c3
-rw-r--r--block/vvfat.c15
-rw-r--r--blockdev.c22
-rwxr-xr-xconfigure228
-rw-r--r--docs/qmp/qmp-events.txt36
-rw-r--r--hw/block/xen_disk.c17
-rw-r--r--hw/display/qxl.c8
-rw-r--r--hw/ide/piix.c3
-rw-r--r--hw/microblaze/petalogix_ml605_mmu.c55
-rw-r--r--hw/microblaze/petalogix_s3adsp1800_mmu.c35
-rw-r--r--hw/net/opencores_eth.c33
-rw-r--r--hw/ppc/virtex_ml507.c29
-rw-r--r--hw/xen/xen_platform.c3
-rw-r--r--hw/xtensa/xtensa_lx60.c51
-rw-r--r--include/block/block.h13
-rw-r--r--include/block/nbd.h6
-rw-r--r--include/hw/ide.h1
-rw-r--r--include/hw/xilinx.h90
-rw-r--r--include/monitor/monitor.h2
-rw-r--r--include/qemu-common.h4
-rw-r--r--include/qemu/module.h23
-rw-r--r--include/qemu/option.h2
-rw-r--r--include/qemu/osdep.h9
-rw-r--r--module-common.c10
-rw-r--r--monitor.c5
-rw-r--r--nbd.c66
-rw-r--r--os-posix.c42
-rw-r--r--os-win32.c21
-rw-r--r--qapi-schema.json21
-rw-r--r--qemu-img.c146
-rw-r--r--qemu-io.c9
-rw-r--r--qemu-nbd.c55
-rw-r--r--qga/channel-win32.c20
-rw-r--r--qga/commands-posix.c39
-rw-r--r--qga/commands-win32.c34
-rw-r--r--qga/main.c17
-rw-r--r--qga/qapi-schema.json9
-rw-r--r--qga/vss-win32/provider.cpp21
-rw-r--r--qga/vss-win32/requester.cpp70
-rw-r--r--qobject/qdict.c60
-rw-r--r--rules.mak80
-rwxr-xr-xscripts/create_config3
-rw-r--r--target-xtensa/core-dc232b.c8
-rw-r--r--target-xtensa/core-dc233c.c8
-rw-r--r--target-xtensa/core-fsf.c8
-rw-r--r--target-xtensa/cpu.c2
-rw-r--r--target-xtensa/cpu.h4
-rw-r--r--target-xtensa/helper.h1
-rw-r--r--target-xtensa/op_helper.c5
-rw-r--r--target-xtensa/overlay_tool.h17
-rw-r--r--target-xtensa/translate.c83
-rw-r--r--tests/check-qdict.c75
-rwxr-xr-xtests/qemu-iotests/04618
-rw-r--r--tests/qemu-iotests/051.out4
-rw-r--r--tests/qemu-iotests/059.out6
-rwxr-xr-xtests/qemu-iotests/081146
-rw-r--r--tests/qemu-iotests/081.out49
-rwxr-xr-xtests/qemu-iotests/082208
-rw-r--r--tests/qemu-iotests/082.out529
-rw-r--r--tests/qemu-iotests/group2
-rw-r--r--tests/tcg/xtensa/Makefile12
-rw-r--r--tests/tcg/xtensa/macros.inc6
-rw-r--r--tests/tcg/xtensa/test_b.S2
-rw-r--r--tests/tcg/xtensa/test_bi.S2
-rw-r--r--tests/tcg/xtensa/test_boolean.S2
-rw-r--r--tests/tcg/xtensa/test_break.S2
-rw-r--r--tests/tcg/xtensa/test_bz.S2
-rw-r--r--tests/tcg/xtensa/test_cache.S97
-rw-r--r--tests/tcg/xtensa/test_clamps.S2
-rw-r--r--tests/tcg/xtensa/test_extui.S2
-rw-r--r--tests/tcg/xtensa/test_fail.S2
-rw-r--r--tests/tcg/xtensa/test_interrupt.S2
-rw-r--r--tests/tcg/xtensa/test_loop.S2
-rw-r--r--tests/tcg/xtensa/test_mac16.S2
-rw-r--r--tests/tcg/xtensa/test_max.S2
-rw-r--r--tests/tcg/xtensa/test_min.S2
-rw-r--r--tests/tcg/xtensa/test_mmu.S6
-rw-r--r--tests/tcg/xtensa/test_mul16.S2
-rw-r--r--tests/tcg/xtensa/test_mul32.S2
-rw-r--r--tests/tcg/xtensa/test_nsa.S2
-rw-r--r--tests/tcg/xtensa/test_pipeline.S2
-rw-r--r--tests/tcg/xtensa/test_quo.S2
-rw-r--r--tests/tcg/xtensa/test_rem.S2
-rw-r--r--tests/tcg/xtensa/test_rst0.S2
-rw-r--r--tests/tcg/xtensa/test_s32c1i.S2
-rw-r--r--tests/tcg/xtensa/test_sar.S2
-rw-r--r--tests/tcg/xtensa/test_sext.S2
-rw-r--r--tests/tcg/xtensa/test_shift.S2
-rw-r--r--tests/tcg/xtensa/test_sr.S2
-rw-r--r--tests/tcg/xtensa/test_timer.S2
-rw-r--r--tests/tcg/xtensa/test_windowed.S2
-rw-r--r--util/iov.c106
-rw-r--r--util/module.c147
-rw-r--r--util/oslib-posix.c54
-rw-r--r--util/oslib-win32.c30
-rw-r--r--util/qemu-config.c6
-rw-r--r--util/qemu-option.c49
-rw-r--r--vl.c3
122 files changed, 3981 insertions, 1022 deletions
diff --git a/.gitignore b/.gitignore
index ff0ae64573..ef7019f35d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,6 +66,9 @@ libuser
 *.cp
 *.dvi
 *.exe
+*.dll
+*.so
+*.mo
 *.fn
 *.ky
 *.log
diff --git a/Makefile b/Makefile
index a443cd4976..a28a3c8d49 100644
--- a/Makefile
+++ b/Makefile
@@ -127,13 +127,29 @@ defconfig:
 
 ifneq ($(wildcard config-host.mak),)
 include $(SRC_PATH)/Makefile.objs
+endif
+
+dummy := $(call unnest-vars,, \
+                stub-obj-y \
+                util-obj-y \
+                qga-obj-y \
+                block-obj-y \
+                block-obj-m \
+                common-obj-y \
+                common-obj-m)
+
+ifneq ($(wildcard config-host.mak),)
 include $(SRC_PATH)/tests/Makefile
 endif
 ifeq ($(CONFIG_SMARTCARD_NSS),y)
 include $(SRC_PATH)/libcacard/Makefile
 endif
 
-all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all
+all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
+
+vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
+
+vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
 
 config-host.h: config-host.h-timestamp
 config-host.h-timestamp: config-host.mak
@@ -192,6 +208,9 @@ Makefile: $(version-obj-y) $(version-lobj-y)
 libqemustub.a: $(stub-obj-y)
 libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o
 
+block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL
+util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
+
 ######################################################################
 
 qemu-img.o: qemu-img-cmds.h
@@ -247,6 +266,8 @@ clean:
 	rm -f qemu-options.def
 	find . -name '*.[oda]' -type f -exec rm -f {} +
 	find . -name '*.l[oa]' -type f -exec rm -f {} +
+	find . -name '*$(DSOSUF)' -type f -exec rm -f {} +
+	find . -name '*.mo' -type f -exec rm -f {} +
 	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
 	rm -f fsdev/*.pod
 	rm -rf .libs */.libs
@@ -354,6 +375,12 @@ install-datadir install-localstatedir
 ifneq ($(TOOLS),)
 	$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)"
 endif
+ifneq ($(CONFIG_MODULES),)
+	$(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)"
+	for s in $(patsubst %.mo,%$(DSOSUF),$(modules-m)); do \
+		$(INSTALL_PROG) $(STRIP_OPT) $$s "$(DESTDIR)$(qemu_moddir)/$${s//\//-}"; \
+	done
+endif
 ifneq ($(HELPERS-y),)
 	$(INSTALL_DIR) "$(DESTDIR)$(libexecdir)"
 	$(INSTALL_PROG) $(STRIP_OPT) $(HELPERS-y) "$(DESTDIR)$(libexecdir)"
diff --git a/Makefile.objs b/Makefile.objs
index ac1d0e1c28..4a62913a4d 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -19,6 +19,8 @@ block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
 block-obj-y += qemu-coroutine-sleep.o
 block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
 
+block-obj-m = block/
+
 ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
 # Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
 # only pull in the actual virtio-9p device if we also enabled virtio.
@@ -41,7 +43,7 @@ libcacard-y += libcacard/vcardt.o
 # single QEMU executable should support all CPUs and machines.
 
 ifeq ($(CONFIG_SOFTMMU),y)
-common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/
+common-obj-y = blockdev.o blockdev-nbd.o block/
 common-obj-y += net/
 common-obj-y += qdev-monitor.o device-hotplug.o
 common-obj-$(CONFIG_WIN32) += os-win32.o
@@ -111,18 +113,3 @@ version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo
 # by libqemuutil.a.  These should be moved to a separate .json schema.
 qga-obj-y = qga/ qapi-types.o qapi-visit.o
 qga-vss-dll-obj-y = qga/
-
-vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
-
-vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
-
-QEMU_CFLAGS+=$(GLIB_CFLAGS)
-
-nested-vars += \
-	stub-obj-y \
-	util-obj-y \
-	qga-obj-y \
-	qga-vss-dll-obj-y \
-	block-obj-y \
-	common-obj-y
-dummy := $(call unnest-vars)
diff --git a/Makefile.target b/Makefile.target
index af6ac7eaa1..ba1234063e 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -130,8 +130,6 @@ else
 obj-y += hw/$(TARGET_BASE_ARCH)/
 endif
 
-main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
-
 GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h
 
 endif # CONFIG_SOFTMMU
@@ -139,13 +137,26 @@ endif # CONFIG_SOFTMMU
 # Workaround for http://gcc.gnu.org/PR55489, see configure.
 %/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
 
-nested-vars += obj-y
+dummy := $(call unnest-vars,,obj-y)
+
+# we are making another call to unnest-vars with different vars, protect obj-y,
+# it can be overriden in subdir Makefile.objs
+obj-y-save := $(obj-y)
 
-# This resolves all nested paths, so it must come last
+block-obj-y :=
+common-obj-y :=
 include $(SRC_PATH)/Makefile.objs
+dummy := $(call unnest-vars,.., \
+               block-obj-y \
+               block-obj-m \
+               common-obj-y \
+               common-obj-m)
+
+# Now restore obj-y
+obj-y := $(obj-y-save)
 
-all-obj-y = $(obj-y)
-all-obj-y += $(addprefix ../, $(common-obj-y))
+all-obj-y = $(obj-y) $(common-obj-y)
+all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
 
 ifndef CONFIG_HAIKU
 LIBS+=-lm
diff --git a/block.c b/block.c
index 6f4bacaa58..2fd5482572 100644
--- a/block.c
+++ b/block.c
@@ -955,53 +955,27 @@ free_and_fail:
 /*
  * Opens a file using a protocol (file, host_device, nbd, ...)
  *
- * options is a QDict of options to pass to the block drivers, or NULL for an
- * empty set of options. The reference to the QDict belongs to the block layer
- * after the call (even on failure), so if the caller intends to reuse the
- * dictionary, it needs to use QINCREF() before calling bdrv_file_open.
+ * options is an indirect pointer to a QDict of options to pass to the block
+ * drivers, or pointer to NULL for an empty set of options. If this function
+ * takes ownership of the QDict reference, it will set *options to NULL;
+ * otherwise, it will contain unused/unrecognized options after this function
+ * returns. Then, the caller is responsible for freeing it. If it intends to
+ * reuse the QDict, QINCREF() should be called beforehand.
  */
-int bdrv_file_open(BlockDriverState **pbs, const char *filename,
-                   const char *reference, QDict *options, int flags,
-                   Error **errp)
+static int bdrv_file_open(BlockDriverState *bs, const char *filename,
+                          QDict **options, int flags, Error **errp)
 {
-    BlockDriverState *bs = NULL;
     BlockDriver *drv;
     const char *drvname;
     bool allow_protocol_prefix = false;
     Error *local_err = NULL;
     int ret;
 
-    /* NULL means an empty set of options */
-    if (options == NULL) {
-        options = qdict_new();
-    }
-
-    if (reference) {
-        if (filename || qdict_size(options)) {
-            error_setg(errp, "Cannot reference an existing block device with "
-                       "additional options or a new filename");
-            return -EINVAL;
-        }
-        QDECREF(options);
-
-        bs = bdrv_lookup_bs(reference, reference, errp);
-        if (!bs) {
-            return -ENODEV;
-        }
-        bdrv_ref(bs);
-        *pbs = bs;
-        return 0;
-    }
-
-    bs = bdrv_new("");
-    bs->options = options;
-    options = qdict_clone_shallow(options);
-
     /* Fetch the file name from the options QDict if necessary */
     if (!filename) {
-        filename = qdict_get_try_str(options, "filename");
-    } else if (filename && !qdict_haskey(options, "filename")) {
-        qdict_put(options, "filename", qstring_from_str(filename));
+        filename = qdict_get_try_str(*options, "filename");
+    } else if (filename && !qdict_haskey(*options, "filename")) {
+        qdict_put(*options, "filename", qstring_from_str(filename));
         allow_protocol_prefix = true;
     } else {
         error_setg(errp, "Can't specify 'file' and 'filename' options at the "
@@ -1011,13 +985,13 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
     }
 
     /* Find the right block driver */
-    drvname = qdict_get_try_str(options, "driver");
+    drvname = qdict_get_try_str(*options, "driver");
     if (drvname) {
         drv = bdrv_find_format(drvname);
         if (!drv) {
             error_setg(errp, "Unknown driver '%s'", drvname);
         }
-        qdict_del(options, "driver");
+        qdict_del(*options, "driver");
     } else if (filename) {
         drv = bdrv_find_protocol(filename, allow_protocol_prefix);
         if (!drv) {
@@ -1036,46 +1010,30 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
 
     /* Parse the filename and open it */
     if (drv->bdrv_parse_filename && filename) {
-        drv->bdrv_parse_filename(filename, options, &local_err);
+        drv->bdrv_parse_filename(filename, *options, &local_err);
         if (local_err) {
             error_propagate(errp, local_err);
             ret = -EINVAL;
             goto fail;
         }
-        qdict_del(options, "filename");
+        qdict_del(*options, "filename");
     }
 
     if (!drv->bdrv_file_open) {
-        ret = bdrv_open(bs, filename, options, flags, drv, &local_err);
-        options = NULL;
+        ret = bdrv_open(&bs, filename, NULL, *options, flags, drv, &local_err);
+        *options = NULL;
     } else {
-        ret = bdrv_open_common(bs, NULL, options, flags, drv, &local_err);
+        ret = bdrv_open_common(bs, NULL, *options, flags, drv, &local_err);
     }
     if (ret < 0) {
         error_propagate(errp, local_err);
         goto fail;
     }
 
-    /* Check if any unknown options were used */
-    if (options && (qdict_size(options) != 0)) {
-        const QDictEntry *entry = qdict_first(options);
-        error_setg(errp, "Block protocol '%s' doesn't support the option '%s'",
-                   drv->format_name, entry->key);
-        ret = -EINVAL;
-        goto fail;
-    }
-    QDECREF(options);
-
     bs->growable = 1;
-    *pbs = bs;
     return 0;
 
 fail:
-    QDECREF(options);
-    if (!bs->drv) {
-        QDECREF(bs->options);
-    }
-    bdrv_unref(bs);
     return ret;
 }
 
@@ -1115,8 +1073,6 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
                                        sizeof(backing_filename));
     }
 
-    bs->backing_hd = bdrv_new("");
-
     if (bs->backing_format[0] != '\0') {
         back_drv = bdrv_find_format(bs->backing_format);
     }
@@ -1125,11 +1081,11 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
     back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT |
                                     BDRV_O_COPY_ON_READ);
 
-    ret = bdrv_open(bs->backing_hd,
-                    *backing_filename ? backing_filename : NULL, options,
+    assert(bs->backing_hd == NULL);
+    ret = bdrv_open(&bs->backing_hd,
+                    *backing_filename ? backing_filename : NULL, NULL, options,
                     back_flags, back_drv, &local_err);
     if (ret < 0) {
-        bdrv_unref(bs->backing_hd);
         bs->backing_hd = NULL;
         bs->open_flags |= BDRV_O_NO_BACKING;
         error_setg(errp, "Could not open backing file: %s",
@@ -1153,10 +1109,6 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
  * Opens a disk image whose options are given as BlockdevRef in another block
  * device's options.
  *
- * If force_raw is true, bdrv_file_open() will be used, thereby preventing any
- * image format auto-detection. If it is false and a filename is given,
- * bdrv_open() will be used for auto-detection.
- *
  * If allow_none is true, no image will be opened if filename is false and no
  * BlockdevRef is given. *pbs will remain unchanged and 0 will be returned.
  *
@@ -1166,16 +1118,21 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
  * BlockdevRef.
  *
  * The BlockdevRef will be removed from the options QDict.
+ *
+ * To conform with the behavior of bdrv_open(), *pbs has to be NULL.
  */
 int bdrv_open_image(BlockDriverState **pbs, const char *filename,
                     QDict *options, const char *bdref_key, int flags,
-                    bool force_raw, bool allow_none, Error **errp)
+                    bool allow_none, Error **errp)
 {
     QDict *image_options;
     int ret;
     char *bdref_key_dot;
     const char *reference;
 
+    assert(pbs);
+    assert(*pbs == NULL);
+
     bdref_key_dot = g_strdup_printf("%s.", bdref_key);
     qdict_extract_subqdict(options, &image_options, bdref_key_dot);
     g_free(bdref_key_dot);
@@ -1192,30 +1149,7 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename,
         goto done;
     }
 
-    if (filename && !force_raw) {
-        /* If a filename is given and the block driver should be detected
-           automatically (instead of using none), use bdrv_open() in order to do
-           that auto-detection. */
-        BlockDriverState *bs;
-
-        if (reference) {
-            error_setg(errp, "Cannot reference an existing block device while "
-                       "giving a filename");
-            ret = -EINVAL;
-            goto done;
-        }
-
-        bs = bdrv_new("");
-        ret = bdrv_open(bs, filename, image_options, flags, NULL, errp);
-        if (ret < 0) {
-            bdrv_unref(bs);
-        } else {
-            *pbs = bs;
-        }
-    } else {
-        ret = bdrv_file_open(pbs, filename, reference, image_options, flags,
-                             errp);
-    }
+    ret = bdrv_open(pbs, filename, reference, image_options, flags, NULL, errp);
 
 done:
     qdict_del(options, bdref_key);
@@ -1229,17 +1163,58 @@ done:
  * empty set of options. The reference to the QDict belongs to the block layer
  * after the call (even on failure), so if the caller intends to reuse the
  * dictionary, it needs to use QINCREF() before calling bdrv_open.
+ *
+ * If *pbs is NULL, a new BDS will be created with a pointer to it stored there.
+ * If it is not NULL, the referenced BDS will be reused.
+ *
+ * The reference parameter may be used to specify an existing block device which
+ * should be opened. If specified, neither options nor a filename may be given,
+ * nor can an existing BDS be reused (that is, *pbs has to be NULL).
  */
-int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
-              int flags, BlockDriver *drv, Error **errp)
+int bdrv_open(BlockDriverState **pbs, const char *filename,
+              const char *reference, QDict *options, int flags,
+              BlockDriver *drv, Error **errp)
 {
     int ret;
     /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
     char tmp_filename[PATH_MAX + 1];
-    BlockDriverState *file = NULL;
+    BlockDriverState *file = NULL, *bs;
     const char *drvname;
     Error *local_err = NULL;
 
+    assert(pbs);
+
+    if (reference) {
+        bool options_non_empty = options ? qdict_size(options) : false;
+        QDECREF(options);
+
+        if (*pbs) {
+            error_setg(errp, "Cannot reuse an existing BDS when referencing "
+                       "another block device");
+            return -EINVAL;
+        }
+
+        if (filename || options_non_empty) {
+            error_setg(errp, "Cannot reference an existing block device with "
+                       "additional options or a new filename");
+            return -EINVAL;
+        }
+
+        bs = bdrv_lookup_bs(reference, reference, errp);
+        if (!bs) {
+            return -ENODEV;
+        }
+        bdrv_ref(bs);
+        *pbs = bs;
+        return 0;
+    }
+
+    if (*pbs) {
+        bs = *pbs;
+    } else {
+        bs = bdrv_new("");
+    }
+
     /* NULL means an empty set of options */
     if (options == NULL) {
         options = qdict_new();
@@ -1248,6 +1223,19 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
     bs->options = options;
     options = qdict_clone_shallow(options);
 
+    if (flags & BDRV_O_PROTOCOL) {
+        assert(!drv);
+        ret = bdrv_file_open(bs, filename, &options, flags & ~BDRV_O_PROTOCOL,
+                             &local_err);
+        if (!ret) {
+            goto done;
+        } else if (bs->drv) {
+            goto close_and_fail;
+        } else {
+            goto fail;
+        }
+    }
+
     /* For snapshot=on, create a temporary qcow2 overlay */
     if (flags & BDRV_O_SNAPSHOT) {
         BlockDriverState *bs1;
@@ -1260,12 +1248,11 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
            instead of opening 'filename' directly */
 
         /* Get the required size from the image */
-        bs1 = bdrv_new("");
         QINCREF(options);
-        ret = bdrv_open(bs1, filename, options, BDRV_O_NO_BACKING,
+        bs1 = NULL;
+        ret = bdrv_open(&bs1, filename, NULL, options, BDRV_O_NO_BACKING,
                         drv, &local_err);
         if (ret < 0) {
-            bdrv_unref(bs1);
             goto fail;
         }
         total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;
@@ -1322,9 +1309,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
         flags |= BDRV_O_ALLOW_RDWR;
     }
 
+    assert(file == NULL);
     ret = bdrv_open_image(&file, filename, options, "file",
-                          bdrv_open_flags(bs, flags | BDRV_O_UNMAP), true, true,
-                          &local_err);
+                          bdrv_open_flags(bs, flags | BDRV_O_UNMAP) |
+                          BDRV_O_PROTOCOL, true, &local_err);
     if (ret < 0) {
         goto fail;
     }
@@ -1377,12 +1365,18 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
         }
     }
 
+done:
     /* Check if any unknown options were used */
-    if (qdict_size(options) != 0) {
+    if (options && (qdict_size(options) != 0)) {
         const QDictEntry *entry = qdict_first(options);
-        error_setg(errp, "Block format '%s' used by device '%s' doesn't "
-                   "support the option '%s'", drv->format_name, bs->device_name,
-                   entry->key);
+        if (flags & BDRV_O_PROTOCOL) {
+            error_setg(errp, "Block protocol '%s' doesn't support the option "
+                       "'%s'", drv->format_name, entry->key);
+        } else {
+            error_setg(errp, "Block format '%s' used by device '%s' doesn't "
+                       "support the option '%s'", drv->format_name,
+                       bs->device_name, entry->key);
+        }
 
         ret = -EINVAL;
         goto close_and_fail;
@@ -1393,6 +1387,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
         bdrv_dev_change_media_cb(bs, true);
     }
 
+    *pbs = bs;
     return 0;
 
 unlink_and_fail:
@@ -1406,13 +1401,24 @@ fail:
     QDECREF(bs->options);
     QDECREF(options);
     bs->options = NULL;
+    if (!*pbs) {
+        /* If *pbs is NULL, a new BDS has been created in this function and
+           needs to be freed now. Otherwise, it does not need to be closed,
+           since it has not really been opened yet. */
+        bdrv_unref(bs);
+    }
     if (local_err) {
         error_propagate(errp, local_err);
     }
     return ret;
 
 close_and_fail:
-    bdrv_close(bs);
+    /* See fail path, but now the BDS has to be always closed */
+    if (*pbs) {
+        bdrv_close(bs);
+    } else {
+        bdrv_unref(bs);
+    }
     QDECREF(options);
     if (local_err) {
         error_propagate(errp, local_err);
@@ -5290,9 +5296,8 @@ void bdrv_img_create(const char *filename, const char *fmt,
             back_flags =
                 flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
 
-            bs = bdrv_new("");
-
-            ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags,
+            bs = NULL;
+            ret = bdrv_open(&bs, backing_file->value.s, NULL, NULL, back_flags,
                             backing_drv, &local_err);
             if (ret < 0) {
                 error_setg_errno(errp, -ret, "Could not open '%s': %s",
@@ -5300,7 +5305,6 @@ void bdrv_img_create(const char *filename, const char *fmt,
                                  error_get_pretty(local_err));
                 error_free(local_err);
                 local_err = NULL;
-                bdrv_unref(bs);
                 goto out;
             }
             bdrv_get_geometry(bs, &size);
@@ -5416,11 +5420,7 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate)
     QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
         bool perm;
 
-        if (!bs->file) {
-            continue;
-        }
-
-        perm = bdrv_recurse_is_first_non_filter(bs->file, candidate);
+        perm = bdrv_recurse_is_first_non_filter(bs, candidate);
 
         /* candidate is the first non filter */
         if (perm) {
diff --git a/block/Makefile.objs b/block/Makefile.objs
index e254a2180a..fd88c03ece 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -3,6 +3,7 @@ block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-c
 block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
 block-obj-y += qed-check.o
 block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
+block-obj-$(CONFIG_QUORUM) += quorum.o
 block-obj-y += parallels.o blkdebug.o blkverify.o
 block-obj-y += snapshot.o qapi.o
 block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
@@ -24,4 +25,15 @@ common-obj-y += commit.o
 common-obj-y += mirror.o
 common-obj-y += backup.o
 
-$(obj)/curl.o: QEMU_CFLAGS+=$(CURL_CFLAGS)
+iscsi.o-cflags     := $(LIBISCSI_CFLAGS)
+iscsi.o-libs       := $(LIBISCSI_LIBS)
+curl.o-cflags      := $(CURL_CFLAGS)
+curl.o-libs        := $(CURL_LIBS)
+rbd.o-cflags       := $(RBD_CFLAGS)
+rbd.o-libs         := $(RBD_LIBS)
+gluster.o-cflags   := $(GLUSTERFS_CFLAGS)
+gluster.o-libs     := $(GLUSTERFS_LIBS)
+ssh.o-cflags       := $(LIBSSH2_CFLAGS)
+ssh.o-libs         := $(LIBSSH2_LIBS)
+qcow.o-libs        := -lz
+linux-aio.o-libs   := -laio
diff --git a/block/blkdebug.c b/block/blkdebug.c
index ee10013362..380c736101 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -410,8 +410,9 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
     s->state = 1;
 
     /* Open the backing file */
+    assert(bs->file == NULL);
     ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
-                          flags, true, false, &local_err);
+                          flags | BDRV_O_PROTOCOL, false, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         goto out;
diff --git a/block/blkverify.c b/block/blkverify.c
index 1563c88324..b98b08bedf 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -135,16 +135,18 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
     }
 
     /* Open the raw file */
+    assert(bs->file == NULL);
     ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
-                          "raw", flags, true, false, &local_err);
+                          "raw", flags | BDRV_O_PROTOCOL, false, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         goto fail;
     }
 
     /* Open the test file */
+    assert(s->test_file == NULL);
     ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
-                          "test", flags, false, false, &local_err);
+                          "test", flags, false, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         s->test_file = NULL;
@@ -171,110 +173,6 @@ static int64_t blkverify_getlength(BlockDriverState *bs)
     return bdrv_getlength(s->test_file);
 }
 
-/**
- * Check that I/O vector contents are identical
- *
- * @a:          I/O vector
- * @b:          I/O vector
- * @ret:        Offset to first mismatching byte or -1 if match
- */
-static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
-{
-    int i;
-    ssize_t offset = 0;
-
-    assert(a->niov == b->niov);
-    for (i = 0; i < a->niov; i++) {
-        size_t len = 0;
-        uint8_t *p = (uint8_t *)a->iov[i].iov_base;
-        uint8_t *q = (uint8_t *)b->iov[i].iov_base;
-
-        assert(a->iov[i].iov_len == b->iov[i].iov_len);
-        while (len < a->iov[i].iov_len && *p++ == *q++) {
-            len++;
-        }
-
-        offset += len;
-
-        if (len != a->iov[i].iov_len) {
-            return offset;
-        }
-    }
-    return -1;
-}
-
-typedef struct {
-    int src_index;
-    struct iovec *src_iov;
-    void *dest_base;
-} IOVectorSortElem;
-
-static int sortelem_cmp_src_base(const void *a, const void *b)
-{
-    const IOVectorSortElem *elem_a = a;
-    const IOVectorSortElem *elem_b = b;
-
-    /* Don't overflow */
-    if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
-        return -1;
-    } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
-        return 1;
-    } else {
-        return 0;
-    }
-}
-
-static int sortelem_cmp_src_index(const void *a, const void *b)
-{
-    const IOVectorSortElem *elem_a = a;
-    const IOVectorSortElem *elem_b = b;
-
-    return elem_a->src_index - elem_b->src_index;
-}
-
-/**
- * Copy contents of I/O vector
- *
- * The relative relationships of overlapping iovecs are preserved.  This is
- * necessary to ensure identical semantics in the cloned I/O vector.
- */
-static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src,
-                                  void *buf)
-{
-    IOVectorSortElem sortelems[src->niov];
-    void *last_end;
-    int i;
-
-    /* Sort by source iovecs by base address */
-    for (i = 0; i < src->niov; i++) {
-        sortelems[i].src_index = i;
-        sortelems[i].src_iov = &src->iov[i];
-    }
-    qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
-
-    /* Allocate buffer space taking into account overlapping iovecs */
-    last_end = NULL;
-    for (i = 0; i < src->niov; i++) {
-        struct iovec *cur = sortelems[i].src_iov;
-        ptrdiff_t rewind = 0;
-
-        /* Detect overlap */
-        if (last_end && last_end > cur->iov_base) {
-            rewind = last_end - cur->iov_base;
-        }
-
-        sortelems[i].dest_base = buf - rewind;
-        buf += cur->iov_len - MIN(rewind, cur->iov_len);
-        last_end = MAX(cur->iov_base + cur->iov_len, last_end);
-    }
-
-    /* Sort by source iovec index and build destination iovec */
-    qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
-    for (i = 0; i < src->niov; i++) {
-        qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
-    }
-}
-
 static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
                                          int64_t sector_num, QEMUIOVector *qiov,
                                          int nb_sectors,
@@ -338,7 +236,7 @@ static void blkverify_aio_cb(void *opaque, int ret)
 
 static void blkverify_verify_readv(BlkverifyAIOCB *acb)
 {
-    ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
+    ssize_t offset = qemu_iovec_compare(acb->qiov, &acb->raw_qiov);
     if (offset != -1) {
         blkverify_err(acb, "contents mismatch in sector %" PRId64,
                       acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
@@ -356,7 +254,7 @@ static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
     acb->verify = blkverify_verify_readv;
     acb->buf = qemu_blockalign(bs->file, qiov->size);
     qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
-    blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
+    qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
 
     bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
                    blkverify_aio_cb, acb);
diff --git a/block/bochs.c b/block/bochs.c
index 51d9a90577..4d6403f904 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -129,7 +129,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
         strcmp(bochs.subtype, GROWING_TYPE) ||
 	((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
 	(le32_to_cpu(bochs.version) != HEADER_V1))) {
-        return -EMEDIUMTYPE;
+        error_setg(errp, "Image not in Bochs format");
+        return -EINVAL;
     }
 
     if (le32_to_cpu(bochs.version) == HEADER_V1) {
diff --git a/block/cow.c b/block/cow.c
index 7fc0b12163..30deb88deb 100644
--- a/block/cow.c
+++ b/block/cow.c
@@ -74,7 +74,8 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags,
     }
 
     if (be32_to_cpu(cow_header.magic) != COW_MAGIC) {
-        ret = -EMEDIUMTYPE;
+        error_setg(errp, "Image not in COW format");
+        ret = -EINVAL;
         goto fail;
     }
 
@@ -82,7 +83,7 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags,
         char version[64];
         snprintf(version, sizeof(version),
                "COW version %d", cow_header.version);
-        qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+        error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
             bs->device_name, "cow", version);
         ret = -ENOTSUP;
         goto fail;
@@ -346,16 +347,15 @@ static int cow_create(const char *filename, QEMUOptionParameter *options,
 
     ret = bdrv_create_file(filename, options, &local_err);
     if (ret < 0) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         return ret;
     }
 
-    ret = bdrv_file_open(&cow_bs, filename, NULL, NULL, BDRV_O_RDWR,
-                         &local_err);
+    cow_bs = NULL;
+    ret = bdrv_open(&cow_bs, filename, NULL, NULL,
+                    BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
     if (ret < 0) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         return ret;
     }
 
diff --git a/block/curl.c b/block/curl.c
index bb1fc4ae28..3494c6d662 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -456,30 +456,27 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
     static int inited = 0;
 
     if (flags & BDRV_O_RDWR) {
-        qerror_report(ERROR_CLASS_GENERIC_ERROR,
-                      "curl block device does not support writes");
+        error_setg(errp, "curl block device does not support writes");
         return -EROFS;
     }
 
     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
     qemu_opts_absorb_qdict(opts, options, &local_err);
     if (local_err) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         goto out_noclean;
     }
 
     s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE);
     if ((s->readahead_size & 0x1ff) != 0) {
-        fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
-                s->readahead_size);
+        error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
+                   s->readahead_size);
         goto out_noclean;
     }
 
     file = qemu_opt_get(opts, "url");
     if (file == NULL) {
-        qerror_report(ERROR_CLASS_GENERIC_ERROR, "curl block driver requires "
-                      "an 'url' option");
+        error_setg(errp, "curl block driver requires an 'url' option");
         goto out_noclean;
     }
 
diff --git a/block/gluster.c b/block/gluster.c
index 58eab07829..14d390b4c7 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -127,7 +127,7 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
     }
 
     /* transport */
-    if (!strcmp(uri->scheme, "gluster")) {
+    if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
         gconf->transport = g_strdup("tcp");
     } else if (!strcmp(uri->scheme, "gluster+tcp")) {
         gconf->transport = g_strdup("tcp");
@@ -163,7 +163,7 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
         }
         gconf->server = g_strdup(qp->p[0].value);
     } else {
-        gconf->server = g_strdup(uri->server);
+        gconf->server = g_strdup(uri->server ? uri->server : "localhost");
         gconf->port = uri->port;
     }
 
@@ -175,7 +175,8 @@ out:
     return ret;
 }
 
-static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
+static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
+                                      Error **errp)
 {
     struct glfs *glfs = NULL;
     int ret;
@@ -183,8 +184,8 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
 
     ret = qemu_gluster_parseuri(gconf, filename);
     if (ret < 0) {
-        error_report("Usage: file=gluster[+transport]://[server[:port]]/"
-            "volname/image[?socket=...]");
+        error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/"
+                   "volname/image[?socket=...]");
         errno = -ret;
         goto out;
     }
@@ -211,9 +212,11 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
 
     ret = glfs_init(glfs);
     if (ret) {
-        error_report("Gluster connection failed for server=%s port=%d "
-             "volume=%s image=%s transport=%s", gconf->server, gconf->port,
-             gconf->volname, gconf->image, gconf->transport);
+        error_setg_errno(errp, errno,
+                         "Gluster connection failed for server=%s port=%d "
+                         "volume=%s image=%s transport=%s", gconf->server,
+                         gconf->port, gconf->volname, gconf->image,
+                         gconf->transport);
         goto out;
     }
     return glfs;
@@ -283,15 +286,14 @@ static int qemu_gluster_open(BlockDriverState *bs,  QDict *options,
     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
     qemu_opts_absorb_qdict(opts, options, &local_err);
     if (local_err) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         ret = -EINVAL;
         goto out;
     }
 
     filename = qemu_opt_get(opts, "filename");
 
-    s->glfs = qemu_gluster_init(gconf, filename);
+    s->glfs = qemu_gluster_init(gconf, filename, errp);
     if (!s->glfs) {
         ret = -errno;
         goto out;
@@ -389,9 +391,9 @@ static int qemu_gluster_create(const char *filename,
     int64_t total_size = 0;
     GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
 
-    glfs = qemu_gluster_init(gconf, filename);
+    glfs = qemu_gluster_init(gconf, filename, errp);
     if (!glfs) {
-        ret = -errno;
+        ret = -EINVAL;
         goto out;
     }
 
diff --git a/block/iscsi.c b/block/iscsi.c
index f8e496f8ef..41ec09709d 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -856,7 +856,8 @@ retry:
 
 #endif /* SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED */
 
-static int parse_chap(struct iscsi_context *iscsi, const char *target)
+static void parse_chap(struct iscsi_context *iscsi, const char *target,
+                       Error **errp)
 {
     QemuOptsList *list;
     QemuOpts *opts;
@@ -865,37 +866,35 @@ static int parse_chap(struct iscsi_context *iscsi, const char *target)
 
     list = qemu_find_opts("iscsi");
     if (!list) {
-        return 0;
+        return;
     }
 
     opts = qemu_opts_find(list, target);
     if (opts == NULL) {
         opts = QTAILQ_FIRST(&list->head);
         if (!opts) {
-            return 0;
+            return;
         }
     }
 
     user = qemu_opt_get(opts, "user");
     if (!user) {
-        return 0;
+        return;
     }
 
     password = qemu_opt_get(opts, "password");
     if (!password) {
-        error_report("CHAP username specified but no password was given");
-        return -1;
+        error_setg(errp, "CHAP username specified but no password was given");
+        return;
     }
 
     if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
-        error_report("Failed to set initiator username and password");
-        return -1;
+        error_setg(errp, "Failed to set initiator username and password");
     }
-
-    return 0;
 }
 
-static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
+static void parse_header_digest(struct iscsi_context *iscsi, const char *target,
+                                Error **errp)
 {
     QemuOptsList *list;
     QemuOpts *opts;
@@ -928,7 +927,7 @@ static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
     } else if (!strcmp(digest, "NONE-CRC32C")) {
         iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
     } else {
-        error_report("Invalid header-digest setting : %s", digest);
+        error_setg(errp, "Invalid header-digest setting : %s", digest);
     }
 }
 
@@ -986,12 +985,11 @@ static void iscsi_nop_timed_event(void *opaque)
 }
 #endif
 
-static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
+static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
 {
     struct scsi_task *task = NULL;
     struct scsi_readcapacity10 *rc10 = NULL;
     struct scsi_readcapacity16 *rc16 = NULL;
-    int ret = 0;
     int retries = ISCSI_CMD_RETRIES; 
 
     do {
@@ -1006,8 +1004,7 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
             if (task != NULL && task->status == SCSI_STATUS_GOOD) {
                 rc16 = scsi_datain_unmarshall(task);
                 if (rc16 == NULL) {
-                    error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
-                    ret = -EINVAL;
+                    error_setg(errp, "iSCSI: Failed to unmarshall readcapacity16 data.");
                 } else {
                     iscsilun->block_size = rc16->block_length;
                     iscsilun->num_blocks = rc16->returned_lba + 1;
@@ -1021,8 +1018,7 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
             if (task != NULL && task->status == SCSI_STATUS_GOOD) {
                 rc10 = scsi_datain_unmarshall(task);
                 if (rc10 == NULL) {
-                    error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
-                    ret = -EINVAL;
+                    error_setg(errp, "iSCSI: Failed to unmarshall readcapacity10 data.");
                 } else {
                     iscsilun->block_size = rc10->block_size;
                     if (rc10->lba == 0) {
@@ -1035,20 +1031,18 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
             }
             break;
         default:
-            return 0;
+            return;
         }
     } while (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
              && task->sense.key == SCSI_SENSE_UNIT_ATTENTION
              && retries-- > 0);
 
     if (task == NULL || task->status != SCSI_STATUS_GOOD) {
-        error_report("iSCSI: failed to send readcapacity10 command.");
-        ret = -EINVAL;
+        error_setg(errp, "iSCSI: failed to send readcapacity10 command.");
     }
     if (task) {
         scsi_free_scsi_task(task);
     }
-    return ret;
 }
 
 /* TODO Convert to fine grained options */
@@ -1065,35 +1059,36 @@ static QemuOptsList runtime_opts = {
     },
 };
 
-static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi,
-                                          int lun, int evpd, int pc) {
-        int full_size;
-        struct scsi_task *task = NULL;
-        task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64);
+static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, int lun,
+                                          int evpd, int pc, Error **errp)
+{
+    int full_size;
+    struct scsi_task *task = NULL;
+    task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64);
+    if (task == NULL || task->status != SCSI_STATUS_GOOD) {
+        goto fail;
+    }
+    full_size = scsi_datain_getfullsize(task);
+    if (full_size > task->datain.size) {
+        scsi_free_scsi_task(task);
+
+        /* we need more data for the full list */
+        task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size);
         if (task == NULL || task->status != SCSI_STATUS_GOOD) {
             goto fail;
         }
-        full_size = scsi_datain_getfullsize(task);
-        if (full_size > task->datain.size) {
-            scsi_free_scsi_task(task);
-
-            /* we need more data for the full list */
-            task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size);
-            if (task == NULL || task->status != SCSI_STATUS_GOOD) {
-                goto fail;
-            }
-        }
+    }
 
-        return task;
+    return task;
 
 fail:
-        error_report("iSCSI: Inquiry command failed : %s",
-                     iscsi_get_error(iscsi));
-        if (task) {
-            scsi_free_scsi_task(task);
-            return NULL;
-        }
+    error_setg(errp, "iSCSI: Inquiry command failed : %s",
+               iscsi_get_error(iscsi));
+    if (task) {
+        scsi_free_scsi_task(task);
         return NULL;
+    }
+    return NULL;
 }
 
 /*
@@ -1119,27 +1114,25 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
     int ret;
 
     if ((BDRV_SECTOR_SIZE % 512) != 0) {
-        error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. "
-                     "BDRV_SECTOR_SIZE(%lld) is not a multiple "
-                     "of 512", BDRV_SECTOR_SIZE);
+        error_setg(errp, "iSCSI: Invalid BDRV_SECTOR_SIZE. "
+                   "BDRV_SECTOR_SIZE(%lld) is not a multiple "
+                   "of 512", BDRV_SECTOR_SIZE);
         return -EINVAL;
     }
 
     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
     qemu_opts_absorb_qdict(opts, options, &local_err);
     if (local_err) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         ret = -EINVAL;
         goto out;
     }
 
     filename = qemu_opt_get(opts, "filename");
 
-
     iscsi_url = iscsi_parse_full_url(iscsi, filename);
     if (iscsi_url == NULL) {
-        error_report("Failed to parse URL : %s", filename);
+        error_setg(errp, "Failed to parse URL : %s", filename);
         ret = -EINVAL;
         goto out;
     }
@@ -1150,13 +1143,13 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
 
     iscsi = iscsi_create_context(initiator_name);
     if (iscsi == NULL) {
-        error_report("iSCSI: Failed to create iSCSI context.");
+        error_setg(errp, "iSCSI: Failed to create iSCSI context.");
         ret = -ENOMEM;
         goto out;
     }
 
     if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
-        error_report("iSCSI: Failed to set target name.");
+        error_setg(errp, "iSCSI: Failed to set target name.");
         ret = -EINVAL;
         goto out;
     }
@@ -1165,21 +1158,22 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
         ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,
                                               iscsi_url->passwd);
         if (ret != 0) {
-            error_report("Failed to set initiator username and password");
+            error_setg(errp, "Failed to set initiator username and password");
             ret = -EINVAL;
             goto out;
         }
     }
 
     /* check if we got CHAP username/password via the options */
-    if (parse_chap(iscsi, iscsi_url->target) != 0) {
-        error_report("iSCSI: Failed to set CHAP user/password");
+    parse_chap(iscsi, iscsi_url->target, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
         ret = -EINVAL;
         goto out;
     }
 
     if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
-        error_report("iSCSI: Failed to set session type to normal.");
+        error_setg(errp, "iSCSI: Failed to set session type to normal.");
         ret = -EINVAL;
         goto out;
     }
@@ -1187,10 +1181,15 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
     iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
 
     /* check if we got HEADER_DIGEST via the options */
-    parse_header_digest(iscsi, iscsi_url->target);
+    parse_header_digest(iscsi, iscsi_url->target, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        ret = -EINVAL;
+        goto out;
+    }
 
     if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) {
-        error_report("iSCSI: Failed to connect to LUN : %s",
+        error_setg(errp, "iSCSI: Failed to connect to LUN : %s",
             iscsi_get_error(iscsi));
         ret = -EINVAL;
         goto out;
@@ -1202,14 +1201,14 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
     task = iscsi_inquiry_sync(iscsi, iscsilun->lun, 0, 0, 36);
 
     if (task == NULL || task->status != SCSI_STATUS_GOOD) {
-        error_report("iSCSI: failed to send inquiry command.");
+        error_setg(errp, "iSCSI: failed to send inquiry command.");
         ret = -EINVAL;
         goto out;
     }
 
     inq = scsi_datain_unmarshall(task);
     if (inq == NULL) {
-        error_report("iSCSI: Failed to unmarshall inquiry data.");
+        error_setg(errp, "iSCSI: Failed to unmarshall inquiry data.");
         ret = -EINVAL;
         goto out;
     }
@@ -1217,7 +1216,9 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
     iscsilun->type = inq->periperal_device_type;
     iscsilun->has_write_same = true;
 
-    if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
+    iscsi_readcapacity_sync(iscsilun, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
         goto out;
     }
     bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
@@ -1235,14 +1236,15 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
     if (iscsilun->lbpme) {
         struct scsi_inquiry_logical_block_provisioning *inq_lbp;
         task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
-                                SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING);
+                                SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING,
+                                errp);
         if (task == NULL) {
             ret = -EINVAL;
             goto out;
         }
         inq_lbp = scsi_datain_unmarshall(task);
         if (inq_lbp == NULL) {
-            error_report("iSCSI: failed to unmarshall inquiry datain blob");
+            error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob");
             ret = -EINVAL;
             goto out;
         }
@@ -1255,14 +1257,14 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
     if (iscsilun->lbp.lbpu || iscsilun->lbp.lbpws) {
         struct scsi_inquiry_block_limits *inq_bl;
         task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
-                                SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS);
+                                SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, errp);
         if (task == NULL) {
             ret = -EINVAL;
             goto out;
         }
         inq_bl = scsi_datain_unmarshall(task);
         if (inq_bl == NULL) {
-            error_report("iSCSI: failed to unmarshall inquiry datain blob");
+            error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob");
             ret = -EINVAL;
             goto out;
         }
@@ -1353,14 +1355,16 @@ static int iscsi_reopen_prepare(BDRVReopenState *state,
 static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
 {
     IscsiLun *iscsilun = bs->opaque;
-    int ret = 0;
+    Error *local_err = NULL;
 
     if (iscsilun->type != TYPE_DISK) {
         return -ENOTSUP;
     }
 
-    if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
-        return ret;
+    iscsi_readcapacity_sync(iscsilun, &local_err);
+    if (local_err != NULL) {
+        error_free(local_err);
+        return -EIO;
     }
 
     if (offset > iscsi_getlength(bs)) {
diff --git a/block/nbd.c b/block/nbd.c
index abae506f04..55124239df 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -188,31 +188,28 @@ out:
     g_free(file);
 }
 
-static int nbd_config(BDRVNBDState *s, QDict *options, char **export)
+static void nbd_config(BDRVNBDState *s, QDict *options, char **export,
+                       Error **errp)
 {
     Error *local_err = NULL;
 
-    if (qdict_haskey(options, "path")) {
-        if (qdict_haskey(options, "host")) {
-            qerror_report(ERROR_CLASS_GENERIC_ERROR, "path and host may not "
-                          "be used at the same time.");
-            return -EINVAL;
+    if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) {
+        if (qdict_haskey(options, "path")) {
+            error_setg(errp, "path and host may not be used at the same time.");
+        } else {
+            error_setg(errp, "one of path and host must be specified.");
         }
-        s->client.is_unix = true;
-    } else if (qdict_haskey(options, "host")) {
-        s->client.is_unix = false;
-    } else {
-        return -EINVAL;
+        return;
     }
 
+    s->client.is_unix = qdict_haskey(options, "path");
     s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0,
                                       &error_abort);
 
     qemu_opts_absorb_qdict(s->socket_opts, options, &local_err);
     if (local_err) {
-        qerror_report_err(local_err);
-        error_free(local_err);
-        return -EINVAL;
+        error_propagate(errp, local_err);
+        return;
     }
 
     if (!qemu_opt_get(s->socket_opts, "port")) {
@@ -223,19 +220,17 @@ static int nbd_config(BDRVNBDState *s, QDict *options, char **export)
     if (*export) {
         qdict_del(options, "export");
     }
-
-    return 0;
 }
 
-static int nbd_establish_connection(BlockDriverState *bs)
+static int nbd_establish_connection(BlockDriverState *bs, Error **errp)
 {
     BDRVNBDState *s = bs->opaque;
     int sock;
 
     if (s->client.is_unix) {
-        sock = unix_socket_outgoing(qemu_opt_get(s->socket_opts, "path"));
+        sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL);
     } else {
-        sock = tcp_socket_outgoing_opts(s->socket_opts);
+        sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL);
         if (sock >= 0) {
             socket_set_nodelay(sock);
         }
@@ -256,17 +251,19 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
     BDRVNBDState *s = bs->opaque;
     char *export = NULL;
     int result, sock;
+    Error *local_err = NULL;
 
     /* Pop the config into our state object. Exit if invalid. */
-    result = nbd_config(s, options, &export);
-    if (result != 0) {
-        return result;
+    nbd_config(s, options, &export, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return -EINVAL;
     }
 
     /* establish TCP connection, return error if it fails
      * TODO: Configurable retry-until-timeout behaviour.
      */
-    sock = nbd_establish_connection(bs);
+    sock = nbd_establish_connection(bs, errp);
     if (sock < 0) {
         return sock;
     }
diff --git a/block/parallels.c b/block/parallels.c
index 2121e43204..3f588f58dc 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -85,7 +85,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
 
     if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
         (le32_to_cpu(ph.version) != HEADER_VERSION)) {
-        ret = -EMEDIUMTYPE;
+        error_setg(errp, "Image not in Parallels format");
+        ret = -EINVAL;
         goto fail;
     }
 
diff --git a/block/qcow.c b/block/qcow.c
index 948b0c5601..1e128becf0 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -113,23 +113,26 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
     be64_to_cpus(&header.l1_table_offset);
 
     if (header.magic != QCOW_MAGIC) {
-        ret = -EMEDIUMTYPE;
+        error_setg(errp, "Image not in qcow format");
+        ret = -EINVAL;
         goto fail;
     }
     if (header.version != QCOW_VERSION) {
         char version[64];
         snprintf(version, sizeof(version), "QCOW version %d", header.version);
-        qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
-            bs->device_name, "qcow", version);
+        error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+                  bs->device_name, "qcow", version);
         ret = -ENOTSUP;
         goto fail;
     }
 
     if (header.size <= 1 || header.cluster_bits < 9) {
+        error_setg(errp, "invalid value in qcow header");
         ret = -EINVAL;
         goto fail;
     }
     if (header.crypt_method > QCOW_CRYPT_AES) {
+        error_setg(errp, "invalid encryption method in qcow header");
         ret = -EINVAL;
         goto fail;
     }
@@ -686,16 +689,15 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options,
 
     ret = bdrv_create_file(filename, options, &local_err);
     if (ret < 0) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         return ret;
     }
 
-    ret = bdrv_file_open(&qcow_bs, filename, NULL, NULL, BDRV_O_RDWR,
-                         &local_err);
+    qcow_bs = NULL;
+    ret = bdrv_open(&qcow_bs, filename, NULL, NULL,
+                    BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
     if (ret < 0) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         return ret;
     }
 
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index c57f39dd2b..36c1bed350 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -1367,13 +1367,31 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
         uint64_t old_offset;
 
         old_offset = be64_to_cpu(l2_table[l2_index + i]);
-        if ((old_offset & L2E_OFFSET_MASK) == 0) {
+
+        /*
+         * Make sure that a discarded area reads back as zeroes for v3 images
+         * (we cannot do it for v2 without actually writing a zero-filled
+         * buffer). We can skip the operation if the cluster is already marked
+         * as zero, or if it's unallocated and we don't have a backing file.
+         *
+         * TODO We might want to use bdrv_get_block_status(bs) here, but we're
+         * holding s->lock, so that doesn't work today.
+         */
+        if (old_offset & QCOW_OFLAG_ZERO) {
+            continue;
+        }
+
+        if ((old_offset & L2E_OFFSET_MASK) == 0 && !bs->backing_hd) {
             continue;
         }
 
         /* First remove L2 entries */
         qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
-        l2_table[l2_index + i] = cpu_to_be64(0);
+        if (s->qcow_version >= 3) {
+            l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
+        } else {
+            l2_table[l2_index + i] = cpu_to_be64(0);
+        }
 
         /* Then decrease the refcount */
         qcow2_free_any_clusters(bs, old_offset, 1, type);
diff --git a/block/qcow2.c b/block/qcow2.c
index b1dbdb120e..cfe80befa0 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -449,7 +449,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
 
     if (header.magic != QCOW_MAGIC) {
         error_setg(errp, "Image is not in qcow2 format");
-        ret = -EMEDIUMTYPE;
+        ret = -EINVAL;
         goto fail;
     }
     if (header.version < 2 || header.version > 3) {
@@ -1493,7 +1493,9 @@ static int qcow2_create2(const char *filename, int64_t total_size,
         return ret;
     }
 
-    ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+    bs = NULL;
+    ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
+                    NULL, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         return ret;
@@ -1543,7 +1545,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
         goto out;
     }
 
-    bdrv_close(bs);
+    bdrv_unref(bs);
+    bs = NULL;
 
     /*
      * And now open the image and make it consistent first (i.e. increase the
@@ -1552,7 +1555,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
      */
     BlockDriver* drv = bdrv_find_format("qcow2");
     assert(drv != NULL);
-    ret = bdrv_open(bs, filename, NULL,
+    ret = bdrv_open(&bs, filename, NULL, NULL,
         BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
@@ -1599,10 +1602,11 @@ static int qcow2_create2(const char *filename, int64_t total_size,
         }
     }
 
-    bdrv_close(bs);
+    bdrv_unref(bs);
+    bs = NULL;
 
     /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
-    ret = bdrv_open(bs, filename, NULL,
+    ret = bdrv_open(&bs, filename, NULL, NULL,
                     BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
                     drv, &local_err);
     if (local_err) {
@@ -1612,7 +1616,9 @@ static int qcow2_create2(const char *filename, int64_t total_size,
 
     ret = 0;
 out:
-    bdrv_unref(bs);
+    if (bs) {
+        bdrv_unref(bs);
+    }
     return ret;
 }
 
diff --git a/block/qed.c b/block/qed.c
index b9ca7ac0da..8802ad3845 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -391,14 +391,15 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
     qed_header_le_to_cpu(&le_header, &s->header);
 
     if (s->header.magic != QED_MAGIC) {
-        return -EMEDIUMTYPE;
+        error_setg(errp, "Image not in QED format");
+        return -EINVAL;
     }
     if (s->header.features & ~QED_FEATURE_MASK) {
         /* image uses unsupported feature bits */
         char buf[64];
         snprintf(buf, sizeof(buf), "%" PRIx64,
             s->header.features & ~QED_FEATURE_MASK);
-        qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+        error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
             bs->device_name, "QED", buf);
         return -ENOTSUP;
     }
@@ -545,7 +546,8 @@ static void bdrv_qed_close(BlockDriverState *bs)
 
 static int qed_create(const char *filename, uint32_t cluster_size,
                       uint64_t image_size, uint32_t table_size,
-                      const char *backing_file, const char *backing_fmt)
+                      const char *backing_file, const char *backing_fmt,
+                      Error **errp)
 {
     QEDHeader header = {
         .magic = QED_MAGIC,
@@ -562,20 +564,20 @@ static int qed_create(const char *filename, uint32_t cluster_size,
     size_t l1_size = header.cluster_size * header.table_size;
     Error *local_err = NULL;
     int ret = 0;
-    BlockDriverState *bs = NULL;
+    BlockDriverState *bs;
 
     ret = bdrv_create_file(filename, NULL, &local_err);
     if (ret < 0) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         return ret;
     }
 
-    ret = bdrv_file_open(&bs, filename, NULL, NULL,
-                         BDRV_O_RDWR | BDRV_O_CACHE_WB, &local_err);
+    bs = NULL;
+    ret = bdrv_open(&bs, filename, NULL, NULL,
+                    BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL,
+                    &local_err);
     if (ret < 0) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         return ret;
     }
 
@@ -665,7 +667,7 @@ static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options,
     }
 
     return qed_create(filename, cluster_size, image_size, table_size,
-                      backing_file, backing_fmt);
+                      backing_file, backing_fmt, errp);
 }
 
 typedef struct {
diff --git a/block/quorum.c b/block/quorum.c
new file mode 100644
index 0000000000..6c28239718
--- /dev/null
+++ b/block/quorum.c
@@ -0,0 +1,870 @@
+/*
+ * Quorum Block filter
+ *
+ * Copyright (C) 2012-2014 Nodalink, EURL.
+ *
+ * Author:
+ *   Benoît Canet <benoit.canet@irqsave.net>
+ *
+ * Based on the design and code of blkverify.c (Copyright (C) 2010 IBM, Corp)
+ * and blkmirror.c (Copyright (C) 2011 Red Hat, Inc).
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include "block/block_int.h"
+#include "qapi/qmp/qjson.h"
+
+#define HASH_LENGTH 32
+
+#define QUORUM_OPT_VOTE_THRESHOLD "vote-threshold"
+#define QUORUM_OPT_BLKVERIFY      "blkverify"
+
+/* This union holds a vote hash value */
+typedef union QuorumVoteValue {
+    char h[HASH_LENGTH];       /* SHA-256 hash */
+    int64_t l;                 /* simpler 64 bits hash */
+} QuorumVoteValue;
+
+/* A vote item */
+typedef struct QuorumVoteItem {
+    int index;
+    QLIST_ENTRY(QuorumVoteItem) next;
+} QuorumVoteItem;
+
+/* this structure is a vote version. A version is the set of votes sharing the
+ * same vote value.
+ * The set of votes will be tracked with the items field and its cardinality is
+ * vote_count.
+ */
+typedef struct QuorumVoteVersion {
+    QuorumVoteValue value;
+    int index;
+    int vote_count;
+    QLIST_HEAD(, QuorumVoteItem) items;
+    QLIST_ENTRY(QuorumVoteVersion) next;
+} QuorumVoteVersion;
+
+/* this structure holds a group of vote versions together */
+typedef struct QuorumVotes {
+    QLIST_HEAD(, QuorumVoteVersion) vote_list;
+    bool (*compare)(QuorumVoteValue *a, QuorumVoteValue *b);
+} QuorumVotes;
+
+/* the following structure holds the state of one quorum instance */
+typedef struct BDRVQuorumState {
+    BlockDriverState **bs; /* children BlockDriverStates */
+    int num_children;      /* children count */
+    int threshold;         /* if less than threshold children reads gave the
+                            * same result a quorum error occurs.
+                            */
+    bool is_blkverify;     /* true if the driver is in blkverify mode
+                            * Writes are mirrored on two children devices.
+                            * On reads the two children devices' contents are
+                            * compared and if a difference is spotted its
+                            * location is printed and the code aborts.
+                            * It is useful to debug other block drivers by
+                            * comparing them with a reference one.
+                            */
+} BDRVQuorumState;
+
+typedef struct QuorumAIOCB QuorumAIOCB;
+
+/* Quorum will create one instance of the following structure per operation it
+ * performs on its children.
+ * So for each read/write operation coming from the upper layer there will be
+ * $children_count QuorumChildRequest.
+ */
+typedef struct QuorumChildRequest {
+    BlockDriverAIOCB *aiocb;
+    QEMUIOVector qiov;
+    uint8_t *buf;
+    int ret;
+    QuorumAIOCB *parent;
+} QuorumChildRequest;
+
+/* Quorum will use the following structure to track progress of each read/write
+ * operation received by the upper layer.
+ * This structure hold pointers to the QuorumChildRequest structures instances
+ * used to do operations on each children and track overall progress.
+ */
+struct QuorumAIOCB {
+    BlockDriverAIOCB common;
+
+    /* Request metadata */
+    uint64_t sector_num;
+    int nb_sectors;
+
+    QEMUIOVector *qiov;         /* calling IOV */
+
+    QuorumChildRequest *qcrs;   /* individual child requests */
+    int count;                  /* number of completed AIOCB */
+    int success_count;          /* number of successfully completed AIOCB */
+
+    QuorumVotes votes;
+
+    bool is_read;
+    int vote_ret;
+};
+
+static void quorum_vote(QuorumAIOCB *acb);
+
+static void quorum_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+    QuorumAIOCB *acb = container_of(blockacb, QuorumAIOCB, common);
+    BDRVQuorumState *s = acb->common.bs->opaque;
+    int i;
+
+    /* cancel all callbacks */
+    for (i = 0; i < s->num_children; i++) {
+        bdrv_aio_cancel(acb->qcrs[i].aiocb);
+    }
+
+    g_free(acb->qcrs);
+    qemu_aio_release(acb);
+}
+
+static AIOCBInfo quorum_aiocb_info = {
+    .aiocb_size         = sizeof(QuorumAIOCB),
+    .cancel             = quorum_aio_cancel,
+};
+
+static void quorum_aio_finalize(QuorumAIOCB *acb)
+{
+    BDRVQuorumState *s = acb->common.bs->opaque;
+    int i, ret = 0;
+
+    if (acb->vote_ret) {
+        ret = acb->vote_ret;
+    }
+
+    acb->common.cb(acb->common.opaque, ret);
+
+    if (acb->is_read) {
+        for (i = 0; i < s->num_children; i++) {
+            qemu_vfree(acb->qcrs[i].buf);
+            qemu_iovec_destroy(&acb->qcrs[i].qiov);
+        }
+    }
+
+    g_free(acb->qcrs);
+    qemu_aio_release(acb);
+}
+
+static bool quorum_sha256_compare(QuorumVoteValue *a, QuorumVoteValue *b)
+{
+    return !memcmp(a->h, b->h, HASH_LENGTH);
+}
+
+static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b)
+{
+    return a->l == b->l;
+}
+
+static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
+                                   BlockDriverState *bs,
+                                   QEMUIOVector *qiov,
+                                   uint64_t sector_num,
+                                   int nb_sectors,
+                                   BlockDriverCompletionFunc *cb,
+                                   void *opaque)
+{
+    QuorumAIOCB *acb = qemu_aio_get(&quorum_aiocb_info, bs, cb, opaque);
+    int i;
+
+    acb->common.bs->opaque = s;
+    acb->sector_num = sector_num;
+    acb->nb_sectors = nb_sectors;
+    acb->qiov = qiov;
+    acb->qcrs = g_new0(QuorumChildRequest, s->num_children);
+    acb->count = 0;
+    acb->success_count = 0;
+    acb->votes.compare = quorum_sha256_compare;
+    QLIST_INIT(&acb->votes.vote_list);
+    acb->is_read = false;
+    acb->vote_ret = 0;
+
+    for (i = 0; i < s->num_children; i++) {
+        acb->qcrs[i].buf = NULL;
+        acb->qcrs[i].ret = 0;
+        acb->qcrs[i].parent = acb;
+    }
+
+    return acb;
+}
+
+static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret)
+{
+    QObject *data;
+    assert(node_name);
+    data = qobject_from_jsonf("{ 'ret': %d"
+                              ", 'node-name': %s"
+                              ", 'sector-num': %" PRId64
+                              ", 'sectors-count': %d }",
+                              ret, node_name, acb->sector_num, acb->nb_sectors);
+    monitor_protocol_event(QEVENT_QUORUM_REPORT_BAD, data);
+    qobject_decref(data);
+}
+
+static void quorum_report_failure(QuorumAIOCB *acb)
+{
+    QObject *data;
+    const char *reference = acb->common.bs->device_name[0] ?
+                            acb->common.bs->device_name :
+                            acb->common.bs->node_name;
+    data = qobject_from_jsonf("{ 'reference': %s"
+                              ", 'sector-num': %" PRId64
+                              ", 'sectors-count': %d }",
+                              reference, acb->sector_num, acb->nb_sectors);
+    monitor_protocol_event(QEVENT_QUORUM_FAILURE, data);
+    qobject_decref(data);
+}
+
+static int quorum_vote_error(QuorumAIOCB *acb);
+
+static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb)
+{
+    BDRVQuorumState *s = acb->common.bs->opaque;
+
+    if (acb->success_count < s->threshold) {
+        acb->vote_ret = quorum_vote_error(acb);
+        quorum_report_failure(acb);
+        return true;
+    }
+
+    return false;
+}
+
+static void quorum_aio_cb(void *opaque, int ret)
+{
+    QuorumChildRequest *sacb = opaque;
+    QuorumAIOCB *acb = sacb->parent;
+    BDRVQuorumState *s = acb->common.bs->opaque;
+
+    sacb->ret = ret;
+    acb->count++;
+    if (ret == 0) {
+        acb->success_count++;
+    } else {
+        quorum_report_bad(acb, sacb->aiocb->bs->node_name, ret);
+    }
+    assert(acb->count <= s->num_children);
+    assert(acb->success_count <= s->num_children);
+    if (acb->count < s->num_children) {
+        return;
+    }
+
+    /* Do the vote on read */
+    if (acb->is_read) {
+        quorum_vote(acb);
+    } else {
+        quorum_has_too_much_io_failed(acb);
+    }
+
+    quorum_aio_finalize(acb);
+}
+
+static void quorum_report_bad_versions(BDRVQuorumState *s,
+                                       QuorumAIOCB *acb,
+                                       QuorumVoteValue *value)
+{
+    QuorumVoteVersion *version;
+    QuorumVoteItem *item;
+
+    QLIST_FOREACH(version, &acb->votes.vote_list, next) {
+        if (acb->votes.compare(&version->value, value)) {
+            continue;
+        }
+        QLIST_FOREACH(item, &version->items, next) {
+            quorum_report_bad(acb, s->bs[item->index]->node_name, 0);
+        }
+    }
+}
+
+static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source)
+{
+    int i;
+    assert(dest->niov == source->niov);
+    assert(dest->size == source->size);
+    for (i = 0; i < source->niov; i++) {
+        assert(dest->iov[i].iov_len == source->iov[i].iov_len);
+        memcpy(dest->iov[i].iov_base,
+               source->iov[i].iov_base,
+               source->iov[i].iov_len);
+    }
+}
+
+static void quorum_count_vote(QuorumVotes *votes,
+                              QuorumVoteValue *value,
+                              int index)
+{
+    QuorumVoteVersion *v = NULL, *version = NULL;
+    QuorumVoteItem *item;
+
+    /* look if we have something with this hash */
+    QLIST_FOREACH(v, &votes->vote_list, next) {
+        if (votes->compare(&v->value, value)) {
+            version = v;
+            break;
+        }
+    }
+
+    /* It's a version not yet in the list add it */
+    if (!version) {
+        version = g_new0(QuorumVoteVersion, 1);
+        QLIST_INIT(&version->items);
+        memcpy(&version->value, value, sizeof(version->value));
+        version->index = index;
+        version->vote_count = 0;
+        QLIST_INSERT_HEAD(&votes->vote_list, version, next);
+    }
+
+    version->vote_count++;
+
+    item = g_new0(QuorumVoteItem, 1);
+    item->index = index;
+    QLIST_INSERT_HEAD(&version->items, item, next);
+}
+
+static void quorum_free_vote_list(QuorumVotes *votes)
+{
+    QuorumVoteVersion *version, *next_version;
+    QuorumVoteItem *item, *next_item;
+
+    QLIST_FOREACH_SAFE(version, &votes->vote_list, next, next_version) {
+        QLIST_REMOVE(version, next);
+        QLIST_FOREACH_SAFE(item, &version->items, next, next_item) {
+            QLIST_REMOVE(item, next);
+            g_free(item);
+        }
+        g_free(version);
+    }
+}
+
+static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash)
+{
+    int j, ret;
+    gnutls_hash_hd_t dig;
+    QEMUIOVector *qiov = &acb->qcrs[i].qiov;
+
+    ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256);
+
+    if (ret < 0) {
+        return ret;
+    }
+
+    for (j = 0; j < qiov->niov; j++) {
+        ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len);
+        if (ret < 0) {
+            break;
+        }
+    }
+
+    gnutls_hash_deinit(dig, (void *) hash);
+    return ret;
+}
+
+static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes)
+{
+    int max = 0;
+    QuorumVoteVersion *candidate, *winner = NULL;
+
+    QLIST_FOREACH(candidate, &votes->vote_list, next) {
+        if (candidate->vote_count > max) {
+            max = candidate->vote_count;
+            winner = candidate;
+        }
+    }
+
+    return winner;
+}
+
+/* qemu_iovec_compare is handy for blkverify mode because it returns the first
+ * differing byte location. Yet it is handcoded to compare vectors one byte
+ * after another so it does not benefit from the libc SIMD optimizations.
+ * quorum_iovec_compare is written for speed and should be used in the non
+ * blkverify mode of quorum.
+ */
+static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
+{
+    int i;
+    int result;
+
+    assert(a->niov == b->niov);
+    for (i = 0; i < a->niov; i++) {
+        assert(a->iov[i].iov_len == b->iov[i].iov_len);
+        result = memcmp(a->iov[i].iov_base,
+                        b->iov[i].iov_base,
+                        a->iov[i].iov_len);
+        if (result) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb,
+                                          const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    fprintf(stderr, "quorum: sector_num=%" PRId64 " nb_sectors=%d ",
+            acb->sector_num, acb->nb_sectors);
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+    exit(1);
+}
+
+static bool quorum_compare(QuorumAIOCB *acb,
+                           QEMUIOVector *a,
+                           QEMUIOVector *b)
+{
+    BDRVQuorumState *s = acb->common.bs->opaque;
+    ssize_t offset;
+
+    /* This driver will replace blkverify in this particular case */
+    if (s->is_blkverify) {
+        offset = qemu_iovec_compare(a, b);
+        if (offset != -1) {
+            quorum_err(acb, "contents mismatch in sector %" PRId64,
+                       acb->sector_num +
+                       (uint64_t)(offset / BDRV_SECTOR_SIZE));
+        }
+        return true;
+    }
+
+    return quorum_iovec_compare(a, b);
+}
+
+/* Do a vote to get the error code */
+static int quorum_vote_error(QuorumAIOCB *acb)
+{
+    BDRVQuorumState *s = acb->common.bs->opaque;
+    QuorumVoteVersion *winner = NULL;
+    QuorumVotes error_votes;
+    QuorumVoteValue result_value;
+    int i, ret = 0;
+    bool error = false;
+
+    QLIST_INIT(&error_votes.vote_list);
+    error_votes.compare = quorum_64bits_compare;
+
+    for (i = 0; i < s->num_children; i++) {
+        ret = acb->qcrs[i].ret;
+        if (ret) {
+            error = true;
+            result_value.l = ret;
+            quorum_count_vote(&error_votes, &result_value, i);
+        }
+    }
+
+    if (error) {
+        winner = quorum_get_vote_winner(&error_votes);
+        ret = winner->value.l;
+    }
+
+    quorum_free_vote_list(&error_votes);
+
+    return ret;
+}
+
+static void quorum_vote(QuorumAIOCB *acb)
+{
+    bool quorum = true;
+    int i, j, ret;
+    QuorumVoteValue hash;
+    BDRVQuorumState *s = acb->common.bs->opaque;
+    QuorumVoteVersion *winner;
+
+    if (quorum_has_too_much_io_failed(acb)) {
+        return;
+    }
+
+    /* get the index of the first successful read */
+    for (i = 0; i < s->num_children; i++) {
+        if (!acb->qcrs[i].ret) {
+            break;
+        }
+    }
+
+    assert(i < s->num_children);
+
+    /* compare this read with all other successful reads stopping at quorum
+     * failure
+     */
+    for (j = i + 1; j < s->num_children; j++) {
+        if (acb->qcrs[j].ret) {
+            continue;
+        }
+        quorum = quorum_compare(acb, &acb->qcrs[i].qiov, &acb->qcrs[j].qiov);
+        if (!quorum) {
+            break;
+       }
+    }
+
+    /* Every successful read agrees */
+    if (quorum) {
+        quorum_copy_qiov(acb->qiov, &acb->qcrs[i].qiov);
+        return;
+    }
+
+    /* compute hashes for each successful read, also store indexes */
+    for (i = 0; i < s->num_children; i++) {
+        if (acb->qcrs[i].ret) {
+            continue;
+        }
+        ret = quorum_compute_hash(acb, i, &hash);
+        /* if ever the hash computation failed */
+        if (ret < 0) {
+            acb->vote_ret = ret;
+            goto free_exit;
+        }
+        quorum_count_vote(&acb->votes, &hash, i);
+    }
+
+    /* vote to select the most represented version */
+    winner = quorum_get_vote_winner(&acb->votes);
+
+    /* if the winner count is smaller than threshold the read fails */
+    if (winner->vote_count < s->threshold) {
+        quorum_report_failure(acb);
+        acb->vote_ret = -EIO;
+        goto free_exit;
+    }
+
+    /* we have a winner: copy it */
+    quorum_copy_qiov(acb->qiov, &acb->qcrs[winner->index].qiov);
+
+    /* some versions are bad print them */
+    quorum_report_bad_versions(s, acb, &winner->value);
+
+free_exit:
+    /* free lists */
+    quorum_free_vote_list(&acb->votes);
+}
+
+static BlockDriverAIOCB *quorum_aio_readv(BlockDriverState *bs,
+                                         int64_t sector_num,
+                                         QEMUIOVector *qiov,
+                                         int nb_sectors,
+                                         BlockDriverCompletionFunc *cb,
+                                         void *opaque)
+{
+    BDRVQuorumState *s = bs->opaque;
+    QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num,
+                                      nb_sectors, cb, opaque);
+    int i;
+
+    acb->is_read = true;
+
+    for (i = 0; i < s->num_children; i++) {
+        acb->qcrs[i].buf = qemu_blockalign(s->bs[i], qiov->size);
+        qemu_iovec_init(&acb->qcrs[i].qiov, qiov->niov);
+        qemu_iovec_clone(&acb->qcrs[i].qiov, qiov, acb->qcrs[i].buf);
+    }
+
+    for (i = 0; i < s->num_children; i++) {
+        bdrv_aio_readv(s->bs[i], sector_num, &acb->qcrs[i].qiov, nb_sectors,
+                       quorum_aio_cb, &acb->qcrs[i]);
+    }
+
+    return &acb->common;
+}
+
+static BlockDriverAIOCB *quorum_aio_writev(BlockDriverState *bs,
+                                          int64_t sector_num,
+                                          QEMUIOVector *qiov,
+                                          int nb_sectors,
+                                          BlockDriverCompletionFunc *cb,
+                                          void *opaque)
+{
+    BDRVQuorumState *s = bs->opaque;
+    QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, nb_sectors,
+                                      cb, opaque);
+    int i;
+
+    for (i = 0; i < s->num_children; i++) {
+        acb->qcrs[i].aiocb = bdrv_aio_writev(s->bs[i], sector_num, qiov,
+                                             nb_sectors, &quorum_aio_cb,
+                                             &acb->qcrs[i]);
+    }
+
+    return &acb->common;
+}
+
+static int64_t quorum_getlength(BlockDriverState *bs)
+{
+    BDRVQuorumState *s = bs->opaque;
+    int64_t result;
+    int i;
+
+    /* check that all file have the same length */
+    result = bdrv_getlength(s->bs[0]);
+    if (result < 0) {
+        return result;
+    }
+    for (i = 1; i < s->num_children; i++) {
+        int64_t value = bdrv_getlength(s->bs[i]);
+        if (value < 0) {
+            return value;
+        }
+        if (value != result) {
+            return -EIO;
+        }
+    }
+
+    return result;
+}
+
+static void quorum_invalidate_cache(BlockDriverState *bs)
+{
+    BDRVQuorumState *s = bs->opaque;
+    int i;
+
+    for (i = 0; i < s->num_children; i++) {
+        bdrv_invalidate_cache(s->bs[i]);
+    }
+}
+
+static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
+{
+    BDRVQuorumState *s = bs->opaque;
+    QuorumVoteVersion *winner = NULL;
+    QuorumVotes error_votes;
+    QuorumVoteValue result_value;
+    int i;
+    int result = 0;
+
+    QLIST_INIT(&error_votes.vote_list);
+    error_votes.compare = quorum_64bits_compare;
+
+    for (i = 0; i < s->num_children; i++) {
+        result = bdrv_co_flush(s->bs[i]);
+        result_value.l = result;
+        quorum_count_vote(&error_votes, &result_value, i);
+    }
+
+    winner = quorum_get_vote_winner(&error_votes);
+    result = winner->value.l;
+
+    quorum_free_vote_list(&error_votes);
+
+    return result;
+}
+
+static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
+                                               BlockDriverState *candidate)
+{
+    BDRVQuorumState *s = bs->opaque;
+    int i;
+
+    for (i = 0; i < s->num_children; i++) {
+        bool perm = bdrv_recurse_is_first_non_filter(s->bs[i],
+                                                     candidate);
+        if (perm) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static int quorum_valid_threshold(int threshold, int num_children, Error **errp)
+{
+
+    if (threshold < 1) {
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  "vote-threshold", "value >= 1");
+        return -ERANGE;
+    }
+
+    if (threshold > num_children) {
+        error_setg(errp, "threshold may not exceed children count");
+        return -ERANGE;
+    }
+
+    return 0;
+}
+
+static QemuOptsList quorum_runtime_opts = {
+    .name = "quorum",
+    .head = QTAILQ_HEAD_INITIALIZER(quorum_runtime_opts.head),
+    .desc = {
+        {
+            .name = QUORUM_OPT_VOTE_THRESHOLD,
+            .type = QEMU_OPT_NUMBER,
+            .help = "The number of vote needed for reaching quorum",
+        },
+        {
+            .name = QUORUM_OPT_BLKVERIFY,
+            .type = QEMU_OPT_BOOL,
+            .help = "Trigger block verify mode if set",
+        },
+        { /* end of list */ }
+    },
+};
+
+static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
+                       Error **errp)
+{
+    BDRVQuorumState *s = bs->opaque;
+    Error *local_err = NULL;
+    QemuOpts *opts;
+    bool *opened;
+    QDict *sub = NULL;
+    QList *list = NULL;
+    const QListEntry *lentry;
+    int i;
+    int ret = 0;
+
+    qdict_flatten(options);
+    qdict_extract_subqdict(options, &sub, "children.");
+    qdict_array_split(sub, &list);
+
+    if (qdict_size(sub)) {
+        error_setg(&local_err, "Invalid option children.%s",
+                   qdict_first(sub)->key);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    /* count how many different children are present */
+    s->num_children = qlist_size(list);
+    if (s->num_children < 2) {
+        error_setg(&local_err,
+                   "Number of provided children must be greater than 1");
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    opts = qemu_opts_create(&quorum_runtime_opts, NULL, 0, &error_abort);
+    qemu_opts_absorb_qdict(opts, options, &local_err);
+    if (error_is_set(&local_err)) {
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    s->threshold = qemu_opt_get_number(opts, QUORUM_OPT_VOTE_THRESHOLD, 0);
+
+    /* and validate it against s->num_children */
+    ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    /* is the driver in blkverify mode */
+    if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
+        s->num_children == 2 && s->threshold == 2) {
+        s->is_blkverify = true;
+    } else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) {
+        fprintf(stderr, "blkverify mode is set by setting blkverify=on "
+                "and using two files with vote_threshold=2\n");
+    }
+
+    /* allocate the children BlockDriverState array */
+    s->bs = g_new0(BlockDriverState *, s->num_children);
+    opened = g_new0(bool, s->num_children);
+
+    for (i = 0, lentry = qlist_first(list); lentry;
+         lentry = qlist_next(lentry), i++) {
+        QDict *d;
+        QString *string;
+
+        switch (qobject_type(lentry->value))
+        {
+            /* List of options */
+            case QTYPE_QDICT:
+                d = qobject_to_qdict(lentry->value);
+                QINCREF(d);
+                ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL,
+                                &local_err);
+                break;
+
+            /* QMP reference */
+            case QTYPE_QSTRING:
+                string = qobject_to_qstring(lentry->value);
+                ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL,
+                                flags, NULL, &local_err);
+                break;
+
+            default:
+                error_setg(&local_err, "Specification of child block device %i "
+                           "is invalid", i);
+                ret = -EINVAL;
+        }
+
+        if (ret < 0) {
+            goto close_exit;
+        }
+        opened[i] = true;
+    }
+
+    g_free(opened);
+    goto exit;
+
+close_exit:
+    /* cleanup on error */
+    for (i = 0; i < s->num_children; i++) {
+        if (!opened[i]) {
+            continue;
+        }
+        bdrv_unref(s->bs[i]);
+    }
+    g_free(s->bs);
+    g_free(opened);
+exit:
+    /* propagate error */
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+    }
+    QDECREF(list);
+    QDECREF(sub);
+    return ret;
+}
+
+static void quorum_close(BlockDriverState *bs)
+{
+    BDRVQuorumState *s = bs->opaque;
+    int i;
+
+    for (i = 0; i < s->num_children; i++) {
+        bdrv_unref(s->bs[i]);
+    }
+
+    g_free(s->bs);
+}
+
+static BlockDriver bdrv_quorum = {
+    .format_name        = "quorum",
+    .protocol_name      = "quorum",
+
+    .instance_size      = sizeof(BDRVQuorumState),
+
+    .bdrv_file_open     = quorum_open,
+    .bdrv_close         = quorum_close,
+
+    .authorizations     = { true, true },
+
+    .bdrv_co_flush_to_disk = quorum_co_flush,
+
+    .bdrv_getlength     = quorum_getlength,
+
+    .bdrv_aio_readv     = quorum_aio_readv,
+    .bdrv_aio_writev    = quorum_aio_writev,
+    .bdrv_invalidate_cache = quorum_invalidate_cache,
+
+    .bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
+};
+
+static void bdrv_quorum_init(void)
+{
+    bdrv_register(&bdrv_quorum);
+}
+
+block_init(bdrv_quorum_init);
diff --git a/block/sheepdog.c b/block/sheepdog.c
index e6c0376566..f7bd0242e5 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -1534,7 +1534,8 @@ static int sd_prealloc(const char *filename)
     Error *local_err = NULL;
     int ret;
 
-    ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+    ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
+                    NULL, &local_err);
     if (ret < 0) {
         qerror_report_err(local_err);
         error_free(local_err);
@@ -1695,7 +1696,9 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
             goto out;
         }
 
-        ret = bdrv_file_open(&bs, backing_file, NULL, NULL, 0, &local_err);
+        bs = NULL;
+        ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL,
+                        &local_err);
         if (ret < 0) {
             qerror_report_err(local_err);
             error_free(local_err);
diff --git a/block/vdi.c b/block/vdi.c
index 2d7490f173..ae49cd83ca 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -395,43 +395,50 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
     }
 
     if (header.signature != VDI_SIGNATURE) {
-        logout("bad vdi signature %08x\n", header.signature);
-        ret = -EMEDIUMTYPE;
+        error_setg(errp, "Image not in VDI format (bad signature %08x)", header.signature);
+        ret = -EINVAL;
         goto fail;
     } else if (header.version != VDI_VERSION_1_1) {
-        logout("unsupported version %u.%u\n",
-               header.version >> 16, header.version & 0xffff);
+        error_setg(errp, "unsupported VDI image (version %u.%u)",
+                   header.version >> 16, header.version & 0xffff);
         ret = -ENOTSUP;
         goto fail;
     } else if (header.offset_bmap % SECTOR_SIZE != 0) {
         /* We only support block maps which start on a sector boundary. */
-        logout("unsupported block map offset 0x%x B\n", header.offset_bmap);
+        error_setg(errp, "unsupported VDI image (unaligned block map offset "
+                   "0x%x)", header.offset_bmap);
         ret = -ENOTSUP;
         goto fail;
     } else if (header.offset_data % SECTOR_SIZE != 0) {
         /* We only support data blocks which start on a sector boundary. */
-        logout("unsupported data offset 0x%x B\n", header.offset_data);
+        error_setg(errp, "unsupported VDI image (unaligned data offset 0x%x)",
+                   header.offset_data);
         ret = -ENOTSUP;
         goto fail;
     } else if (header.sector_size != SECTOR_SIZE) {
-        logout("unsupported sector size %u B\n", header.sector_size);
+        error_setg(errp, "unsupported VDI image (sector size %u is not %u)",
+                   header.sector_size, SECTOR_SIZE);
         ret = -ENOTSUP;
         goto fail;
     } else if (header.block_size != 1 * MiB) {
-        logout("unsupported block size %u B\n", header.block_size);
+        error_setg(errp, "unsupported VDI image (sector size %u is not %u)",
+                   header.block_size, 1 * MiB);
         ret = -ENOTSUP;
         goto fail;
     } else if (header.disk_size >
                (uint64_t)header.blocks_in_image * header.block_size) {
-        logout("unsupported disk size %" PRIu64 " B\n", header.disk_size);
+        error_setg(errp, "unsupported VDI image (disk size %" PRIu64 ", "
+                   "image bitmap has room for %" PRIu64 ")",
+                   header.disk_size,
+                   (uint64_t)header.blocks_in_image * header.block_size);
         ret = -ENOTSUP;
         goto fail;
     } else if (!uuid_is_null(header.uuid_link)) {
-        logout("link uuid != 0, unsupported\n");
+        error_setg(errp, "unsupported VDI image (non-NULL link UUID)");
         ret = -ENOTSUP;
         goto fail;
     } else if (!uuid_is_null(header.uuid_parent)) {
-        logout("parent uuid != 0, unsupported\n");
+        error_setg(errp, "unsupported VDI image (non-NULL parent UUID)");
         ret = -ENOTSUP;
         goto fail;
     }
diff --git a/block/vhdx.c b/block/vhdx.c
index 55689cf641..5390ba6d0f 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -402,9 +402,10 @@ int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s,
 }
 
 /* opens the specified header block from the VHDX file header section */
-static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
+static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
+                              Error **errp)
 {
-    int ret = 0;
+    int ret;
     VHDXHeader *header1;
     VHDXHeader *header2;
     bool h1_valid = false;
@@ -462,7 +463,6 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
     } else if (!h1_valid && h2_valid) {
         s->curr_header = 1;
     } else if (!h1_valid && !h2_valid) {
-        ret = -EINVAL;
         goto fail;
     } else {
         /* If both headers are valid, then we choose the active one by the
@@ -473,27 +473,22 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
         } else if (h2_seq > h1_seq) {
             s->curr_header = 1;
         } else {
-            ret = -EINVAL;
             goto fail;
         }
     }
 
     vhdx_region_register(s, s->headers[s->curr_header]->log_offset,
                             s->headers[s->curr_header]->log_length);
-
-    ret = 0;
-
     goto exit;
 
 fail:
-    qerror_report(ERROR_CLASS_GENERIC_ERROR, "No valid VHDX header found");
+    error_setg_errno(errp, -ret, "No valid VHDX header found");
     qemu_vfree(header1);
     qemu_vfree(header2);
     s->headers[0] = NULL;
     s->headers[1] = NULL;
 exit:
     qemu_vfree(buffer);
-    return ret;
 }
 
 
@@ -878,7 +873,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
     int ret = 0;
     uint32_t i;
     uint64_t signature;
-
+    Error *local_err = NULL;
 
     s->bat = NULL;
     s->first_visible_write = true;
@@ -901,8 +896,10 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
      * header update */
     vhdx_guid_generate(&s->session_guid);
 
-    ret = vhdx_parse_header(bs, s);
-    if (ret < 0) {
+    vhdx_parse_header(bs, s, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        ret = -EINVAL;
         goto fail;
     }
 
@@ -1797,7 +1794,9 @@ static int vhdx_create(const char *filename, QEMUOptionParameter *options,
         goto exit;
     }
 
-    ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+    bs = NULL;
+    ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
+                    NULL, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         goto exit;
diff --git a/block/vmdk.c b/block/vmdk.c
index ff6f5ee911..83839f9b7a 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -526,8 +526,34 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
     return ret;
 }
 
-static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
-                               uint64_t desc_offset, Error **errp);
+static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
+                               Error **errp);
+
+static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
+                            Error **errp)
+{
+    int64_t size;
+    char *buf;
+    int ret;
+
+    size = bdrv_getlength(file);
+    if (size < 0) {
+        error_setg_errno(errp, -size, "Could not access file");
+        return NULL;
+    }
+
+    size = MIN(size, 1 << 20);  /* avoid unbounded allocation */
+    buf = g_malloc0(size + 1);
+
+    ret = bdrv_pread(file, desc_offset, buf, size);
+    if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not read from file");
+        g_free(buf);
+        return NULL;
+    }
+
+    return buf;
+}
 
 static int vmdk_open_vmdk4(BlockDriverState *bs,
                            BlockDriverState *file,
@@ -546,11 +572,18 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
         error_setg_errno(errp, -ret,
                          "Could not read header from file '%s'",
                          file->filename);
+        return -EINVAL;
     }
     if (header.capacity == 0) {
         uint64_t desc_offset = le64_to_cpu(header.desc_offset);
         if (desc_offset) {
-            return vmdk_open_desc_file(bs, flags, desc_offset << 9, errp);
+            char *buf = vmdk_read_desc(file, desc_offset << 9, errp);
+            if (!buf) {
+                return -EINVAL;
+            }
+            ret = vmdk_open_desc_file(bs, flags, buf, errp);
+            g_free(buf);
+            return ret;
         }
     }
 
@@ -609,8 +642,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
         char buf[64];
         snprintf(buf, sizeof(buf), "VMDK version %d",
                  le32_to_cpu(header.version));
-        qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
-                bs->device_name, "vmdk", buf);
+        error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+                  bs->device_name, "vmdk", buf);
         return -ENOTSUP;
     } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) {
         /* VMware KB 2064959 explains that version 3 added support for
@@ -622,7 +655,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
     }
 
     if (le32_to_cpu(header.num_gtes_per_gt) > 512) {
-        error_report("L2 table size too big");
+        error_setg(errp, "L2 table size too big");
         return -EINVAL;
     }
 
@@ -638,8 +671,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
     }
     if (bdrv_getlength(file) <
             le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE) {
-        error_report("File truncated, expecting at least %lld bytes",
-                le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE);
+        error_setg(errp, "File truncated, expecting at least %lld bytes",
+                   le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE);
         return -EINVAL;
     }
 
@@ -701,16 +734,12 @@ static int vmdk_parse_description(const char *desc, const char *opt_name,
 
 /* Open an extent file and append to bs array */
 static int vmdk_open_sparse(BlockDriverState *bs,
-                            BlockDriverState *file,
-                            int flags, Error **errp)
+                            BlockDriverState *file, int flags,
+                            char *buf, Error **errp)
 {
     uint32_t magic;
 
-    if (bdrv_pread(file, 0, &magic, sizeof(magic)) != sizeof(magic)) {
-        return -EIO;
-    }
-
-    magic = be32_to_cpu(magic);
+    magic = ldl_be_p(buf);
     switch (magic) {
         case VMDK3_MAGIC:
             return vmdk_open_vmfs_sparse(bs, file, flags, errp);
@@ -719,7 +748,8 @@ static int vmdk_open_sparse(BlockDriverState *bs,
             return vmdk_open_vmdk4(bs, file, flags, errp);
             break;
         default:
-            return -EMEDIUMTYPE;
+            error_setg(errp, "Image not in VMDK format");
+            return -EINVAL;
             break;
     }
 }
@@ -776,8 +806,9 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
 
         path_combine(extent_path, sizeof(extent_path),
                 desc_file_path, fname);
-        ret = bdrv_file_open(&extent_file, extent_path, NULL, NULL,
-                             bs->open_flags, errp);
+        extent_file = NULL;
+        ret = bdrv_open(&extent_file, extent_path, NULL, NULL,
+                        bs->open_flags | BDRV_O_PROTOCOL, NULL, errp);
         if (ret) {
             return ret;
         }
@@ -794,8 +825,14 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
             extent->flat_start_offset = flat_offset << 9;
         } else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) {
             /* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/
-            ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, errp);
+            char *buf = vmdk_read_desc(extent_file, 0, errp);
+            if (!buf) {
+                ret = -EINVAL;
+            } else {
+                ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp);
+            }
             if (ret) {
+                g_free(buf);
                 bdrv_unref(extent_file);
                 return ret;
             }
@@ -818,29 +855,16 @@ next_line:
     return 0;
 }
 
-static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
-                               uint64_t desc_offset, Error **errp)
+static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
+                               Error **errp)
 {
     int ret;
-    char *buf = NULL;
     char ct[128];
     BDRVVmdkState *s = bs->opaque;
-    int64_t size;
-
-    size = bdrv_getlength(bs->file);
-    if (size < 0) {
-        return -EINVAL;
-    }
-
-    size = MIN(size, 1 << 20);  /* avoid unbounded allocation */
-    buf = g_malloc0(size + 1);
 
-    ret = bdrv_pread(bs->file, desc_offset, buf, size);
-    if (ret < 0) {
-        goto exit;
-    }
     if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) {
-        ret = -EMEDIUMTYPE;
+        error_setg(errp, "invalid VMDK image descriptor");
+        ret = -EINVAL;
         goto exit;
     }
     if (strcmp(ct, "monolithicFlat") &&
@@ -856,24 +880,37 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
     s->desc_offset = 0;
     ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp);
 exit:
-    g_free(buf);
     return ret;
 }
 
 static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
                      Error **errp)
 {
+    char *buf = NULL;
     int ret;
     BDRVVmdkState *s = bs->opaque;
+    uint32_t magic;
 
-    if (vmdk_open_sparse(bs, bs->file, flags, errp) == 0) {
-        s->desc_offset = 0x200;
-    } else {
-        ret = vmdk_open_desc_file(bs, flags, 0, errp);
-        if (ret) {
-            goto fail;
-        }
+    buf = vmdk_read_desc(bs->file, 0, errp);
+    if (!buf) {
+        return -EINVAL;
     }
+
+    magic = ldl_be_p(buf);
+    switch (magic) {
+        case VMDK3_MAGIC:
+        case VMDK4_MAGIC:
+            ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp);
+            s->desc_offset = 0x200;
+            break;
+        default:
+            ret = vmdk_open_desc_file(bs, flags, buf, errp);
+            break;
+    }
+    if (ret) {
+        goto fail;
+    }
+
     /* try to open parent images, if exist */
     ret = vmdk_parent_open(bs);
     if (ret) {
@@ -888,10 +925,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
               QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
               "vmdk", bs->device_name, "live migration");
     migrate_add_blocker(s->migration_blocker);
-
+    g_free(buf);
     return 0;
 
 fail:
+    g_free(buf);
     g_free(s->create_type);
     s->create_type = NULL;
     vmdk_free_extents(bs);
@@ -1493,7 +1531,9 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
         goto exit;
     }
 
-    ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+    assert(bs == NULL);
+    ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
+                    NULL, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         goto exit;
@@ -1755,10 +1795,10 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
         goto exit;
     }
     if (backing_file) {
-        BlockDriverState *bs = bdrv_new("");
-        ret = bdrv_open(bs, backing_file, NULL, BDRV_O_NO_BACKING, NULL, errp);
+        BlockDriverState *bs = NULL;
+        ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_NO_BACKING, NULL,
+                        errp);
         if (ret != 0) {
-            bdrv_unref(bs);
             goto exit;
         }
         if (strcmp(bs->drv->format_name, "vmdk")) {
@@ -1831,7 +1871,9 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
             goto exit;
         }
     }
-    ret = bdrv_file_open(&new_bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+    assert(new_bs == NULL);
+    ret = bdrv_open(&new_bs, filename, NULL, NULL,
+                    BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
     if (ret < 0) {
         error_setg_errno(errp, -ret, "Could not write description");
         goto exit;
diff --git a/block/vpc.c b/block/vpc.c
index 1d326cbf44..82bf2485a5 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -190,7 +190,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
             goto fail;
         }
         if (strncmp(footer->creator, "conectix", 8)) {
-            ret = -EMEDIUMTYPE;
+            error_setg(errp, "invalid VPC image");
+            ret = -EINVAL;
             goto fail;
         }
         disk_type = VHD_FIXED;
diff --git a/block/vvfat.c b/block/vvfat.c
index a19e4ca227..f966ea5da8 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -1086,16 +1086,14 @@ DLOG(if (stderr == NULL) {
     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
     qemu_opts_absorb_qdict(opts, options, &local_err);
     if (local_err) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         ret = -EINVAL;
         goto fail;
     }
 
     dirname = qemu_opt_get(opts, "dir");
     if (!dirname) {
-        qerror_report(ERROR_CLASS_GENERIC_ERROR, "vvfat block driver requires "
-                      "a 'dir' option");
+        error_setg(errp, "vvfat block driver requires a 'dir' option");
         ret = -EINVAL;
         goto fail;
     }
@@ -1135,8 +1133,7 @@ DLOG(if (stderr == NULL) {
     case 12:
         break;
     default:
-        qerror_report(ERROR_CLASS_GENERIC_ERROR, "Valid FAT types are only "
-                      "12, 16 and 32");
+        error_setg(errp, "Valid FAT types are only 12, 16 and 32");
         ret = -EINVAL;
         goto fail;
     }
@@ -2936,15 +2933,13 @@ static int enable_write_target(BDRVVVFATState *s)
         goto err;
     }
 
-    s->qcow = bdrv_new("");
-
-    ret = bdrv_open(s->qcow, s->qcow_filename, NULL,
+    s->qcow = NULL;
+    ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL,
             BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow,
             &local_err);
     if (ret < 0) {
         qerror_report_err(local_err);
         error_free(local_err);
-        bdrv_unref(s->qcow);
         goto err;
     }
 
diff --git a/blockdev.c b/blockdev.c
index 3cc8cda2bd..357f7607ff 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -504,7 +504,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
     bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
 
     QINCREF(bs_opts);
-    ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error);
+    ret = bdrv_open(&dinfo->bdrv, file, NULL, bs_opts, bdrv_flags, drv, &error);
 
     if (ret < 0) {
         error_setg(errp, "could not open disk image %s: %s",
@@ -1330,12 +1330,12 @@ static void external_snapshot_prepare(BlkTransactionState *common,
                   qstring_from_str(snapshot_node_name));
     }
 
-    /* We will manually add the backing_hd field to the bs later */
-    state->new_bs = bdrv_new("");
     /* TODO Inherit bs->options or only take explicit options with an
      * extended QMP command? */
-    ret = bdrv_open(state->new_bs, new_image_file, options,
+    assert(state->new_bs == NULL);
+    ret = bdrv_open(&state->new_bs, new_image_file, NULL, options,
                     flags | BDRV_O_NO_BACKING, drv, &local_err);
+    /* We will manually add the backing_hd field to the bs later */
     if (ret != 0) {
         error_propagate(errp, local_err);
     }
@@ -1582,7 +1582,7 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
     Error *local_err = NULL;
     int ret;
 
-    ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv, &local_err);
+    ret = bdrv_open(&bs, filename, NULL, NULL, bdrv_flags, drv, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         return;
@@ -2018,10 +2018,9 @@ void qmp_drive_backup(const char *device, const char *target,
         return;
     }
 
-    target_bs = bdrv_new("");
-    ret = bdrv_open(target_bs, target, NULL, flags, drv, &local_err);
+    target_bs = NULL;
+    ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err);
     if (ret < 0) {
-        bdrv_unref(target_bs);
         error_propagate(errp, local_err);
         return;
     }
@@ -2162,11 +2161,10 @@ void qmp_drive_mirror(const char *device, const char *target,
     /* Mirroring takes care of copy-on-write using the source's backing
      * file.
      */
-    target_bs = bdrv_new("");
-    ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv,
-                    &local_err);
+    target_bs = NULL;
+    ret = bdrv_open(&target_bs, target, NULL, NULL, flags | BDRV_O_NO_BACKING,
+                    drv, &local_err);
     if (ret < 0) {
-        bdrv_unref(target_bs);
         error_propagate(errp, local_err);
         return;
     }
diff --git a/configure b/configure
index 39f2a1acd2..423f435550 100755
--- a/configure
+++ b/configure
@@ -12,7 +12,10 @@ else
 fi
 
 TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c"
-TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o"
+TMPB="qemu-conf-${RANDOM}-$$-${RANDOM}"
+TMPO="${TMPDIR1}/${TMPB}.o"
+TMPL="${TMPDIR1}/${TMPB}.lo"
+TMPA="${TMPDIR1}/lib${TMPB}.la"
 TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe"
 
 # NB: do not call "exit" in the trap handler; this is buggy with some shells;
@@ -86,6 +89,38 @@ compile_prog() {
   do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags
 }
 
+do_libtool() {
+    local mode=$1
+    shift
+    # Run the compiler, capturing its output to the log.
+    echo $libtool $mode --tag=CC $cc "$@" >> config.log
+    $libtool $mode --tag=CC $cc "$@" >> config.log 2>&1 || return $?
+    # Test passed. If this is an --enable-werror build, rerun
+    # the test with -Werror and bail out if it fails. This
+    # makes warning-generating-errors in configure test code
+    # obvious to developers.
+    if test "$werror" != "yes"; then
+        return 0
+    fi
+    # Don't bother rerunning the compile if we were already using -Werror
+    case "$*" in
+        *-Werror*)
+           return 0
+        ;;
+    esac
+    echo $libtool $mode --tag=CC $cc -Werror "$@" >> config.log
+    $libtool $mode --tag=CC $cc -Werror "$@" >> config.log 2>&1 && return $?
+    error_exit "configure test passed without -Werror but failed with -Werror." \
+        "This is probably a bug in the configure script. The failing command" \
+        "will be at the bottom of config.log." \
+        "You can run configure with --disable-werror to bypass this check."
+}
+
+libtool_prog() {
+    do_libtool --mode=compile $QEMU_CFLAGS -c -fPIE -DPIE -o $TMPO $TMPC || return $?
+    do_libtool --mode=link $LDFLAGS -o $TMPA $TMPL -rpath /usr/local/lib
+}
+
 # symbolically link $1 to $2.  Portable version of "ln -sf".
 symlink() {
   rm -rf "$2"
@@ -205,6 +240,9 @@ mingw32="no"
 gcov="no"
 gcov_tool="gcov"
 EXESUF=""
+DSOSUF=".so"
+LDFLAGS_SHARED="-shared"
+modules="no"
 prefix="/usr/local"
 mandir="\${prefix}/share/man"
 datadir="\${prefix}/share"
@@ -264,6 +302,7 @@ gtkabi="2.0"
 tpm="no"
 libssh2=""
 vhdx=""
+quorum="no"
 
 # parse CC options first
 for opt do
@@ -515,11 +554,10 @@ OpenBSD)
 Darwin)
   bsd="yes"
   darwin="yes"
+  LDFLAGS_SHARED="-bundle -undefined dynamic_lookup"
   if [ "$cpu" = "x86_64" ] ; then
     QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
     LDFLAGS="-arch x86_64 $LDFLAGS"
-  else
-    QEMU_CFLAGS="-mdynamic-no-pic $QEMU_CFLAGS"
   fi
   cocoa="yes"
   audio_drv_list="coreaudio"
@@ -610,6 +648,7 @@ fi
 
 if test "$mingw32" = "yes" ; then
   EXESUF=".exe"
+  DSOSUF=".dll"
   QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS"
   # enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later)
   QEMU_CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $QEMU_CFLAGS"
@@ -676,6 +715,9 @@ for opt do
   ;;
   --disable-debug-info)
   ;;
+  --enable-modules)
+      modules="yes"
+  ;;
   --cpu=*)
   ;;
   --target-list=*) target_list="$optarg"
@@ -1005,6 +1047,10 @@ for opt do
   ;;
   --disable-vhdx) vhdx="no"
   ;;
+  --disable-quorum) quorum="no"
+  ;;
+  --enable-quorum) quorum="yes"
+  ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
@@ -1131,7 +1177,8 @@ Advanced options (experts only):
   --libdir=PATH            install libraries in PATH
   --sysconfdir=PATH        install config in PATH$confsuffix
   --localstatedir=PATH     install local state in PATH (set at runtime on win32)
-  --with-confsuffix=SUFFIX suffix for QEMU data inside datadir and sysconfdir [$confsuffix]
+  --with-confsuffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir [$confsuffix]
+  --enable-modules         enable modules support
   --enable-debug-tcg       enable TCG debugging
   --disable-debug-tcg      disable TCG debugging (default)
   --enable-debug-info       enable debugging information (default)
@@ -1261,6 +1308,8 @@ Advanced options (experts only):
   --enable-libssh2         enable ssh block device support
   --disable-vhdx           disables support for the Microsoft VHDX image format
   --enable-vhdx            enable support for the Microsoft VHDX image format
+  --disable-quorum         disable quorum block filter support
+  --enable-quorum          enable quorum block filter support
 
 NOTE: The object files are built at the place where configure is launched
 EOF
@@ -1289,6 +1338,35 @@ else
     error_exit "\"$cc\" either does not exist or does not work"
 fi
 
+# Check that the C++ compiler exists and works with the C compiler
+if has $cxx; then
+    cat > $TMPC <<EOF
+int c_function(void);
+int main(void) { return c_function(); }
+EOF
+
+    compile_object
+
+    cat > $TMPC <<EOF
+extern "C" {
+   int c_function(void);
+}
+int c_function(void) { return 42; }
+EOF
+
+    if (cc=$cxx do_cc $QEMU_CFLAGS -o $TMPE $TMPC $TMPO $LDFLAGS); then
+        # C++ compiler $cxx works ok with C compiler $cc
+        :
+    else
+        echo "C++ compiler $cxx does not work with C compiler $cc"
+        echo "Disabling C++ specific optional code"
+        cxx=
+    fi
+else
+    echo "No C++ compiler available; disabling C++ specific optional code"
+    cxx=
+fi
+
 # Consult white-list to determine whether to enable werror
 # by default.  Only enable by default for git builds
 z_version=`cut -f3 -d. $source_path/VERSION`
@@ -1346,6 +1424,9 @@ if compile_prog "-Werror -fno-gcse" "" ; then
 fi
 
 if test "$static" = "yes" ; then
+  if test "$modules" = "yes" ; then
+    error_exit "static and modules are mutually incompatible"
+  fi
   if test "$pie" = "yes" ; then
     error_exit "static and pie are mutually incompatible"
   else
@@ -1399,6 +1480,32 @@ EOF
   fi
 fi
 
+# check for broken gcc and libtool in RHEL5
+if test -n "$libtool" -a "$pie" != "no" ; then
+  cat > $TMPC <<EOF
+
+void *f(unsigned char *buf, int len);
+void *g(unsigned char *buf, int len);
+
+void *
+f(unsigned char *buf, int len)
+{
+    return (void*)0L;
+}
+
+void *
+g(unsigned char *buf, int len)
+{
+    return f(buf, len);
+}
+
+EOF
+  if ! libtool_prog; then
+    echo "Disabling libtool due to broken toolchain support"
+    libtool=
+  fi
+fi
+
 ##########################################
 # __sync_fetch_and_and requires at least -march=i486. Many toolchains
 # use i686 as default anyway, but for those that don't, an explicit
@@ -1937,6 +2044,30 @@ EOF
 fi
 
 ##########################################
+# Quorum probe (check for gnutls)
+if test "$quorum" != "no" ; then
+cat > $TMPC <<EOF
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+int main(void) {char data[4096], digest[32];
+gnutls_hash_fast(GNUTLS_DIG_SHA256, data, 4096, digest);
+return 0;
+}
+EOF
+quorum_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
+quorum_tls_libs=`$pkg_config --libs gnutls 2> /dev/null`
+if compile_prog "$quorum_tls_cflags" "$quorum_tls_libs" ; then
+  qcow_tls=yes
+  libs_softmmu="$quorum_tls_libs $libs_softmmu"
+  libs_tools="$quorum_tls_libs $libs_softmmu"
+  QEMU_CFLAGS="$QEMU_CFLAGS $quorum_tls_cflags"
+else
+  echo "gnutls > 2.10.0 required to compile Quorum"
+  exit 1
+fi
+fi
+
+##########################################
 # VNC SASL detection
 if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then
   cat > $TMPC <<EOF
@@ -2318,8 +2449,6 @@ EOF
   curl_libs=`$curlconfig --libs 2>/dev/null`
   if compile_prog "$curl_cflags" "$curl_libs" ; then
     curl=yes
-    libs_tools="$curl_libs $libs_tools"
-    libs_softmmu="$curl_libs $libs_softmmu"
   else
     if test "$curl" = "yes" ; then
       feature_not_found "curl" "Install libcurl devel"
@@ -2357,13 +2486,36 @@ if test "$mingw32" = yes; then
 else
     glib_req_ver=2.12
 fi
-if $pkg_config --atleast-version=$glib_req_ver gthread-2.0; then
-    glib_cflags=`$pkg_config --cflags gthread-2.0`
-    glib_libs=`$pkg_config --libs gthread-2.0`
-    LIBS="$glib_libs $LIBS"
-    libs_qga="$glib_libs $libs_qga"
-else
-    error_exit "glib-$glib_req_ver required to compile QEMU"
+glib_modules=gthread-2.0
+if test "$modules" = yes; then
+    glib_modules="$glib_modules gmodule-2.0"
+fi
+
+for i in $glib_modules; do
+    if $pkg_config --atleast-version=$glib_req_ver $i; then
+        glib_cflags=`$pkg_config --cflags $i`
+        glib_libs=`$pkg_config --libs $i`
+        CFLAGS="$glib_cflags $CFLAGS"
+        LIBS="$glib_libs $LIBS"
+        libs_qga="$glib_libs $libs_qga"
+    else
+        error_exit "glib-$glib_req_ver $i is required to compile QEMU"
+    fi
+done
+
+##########################################
+# SHA command probe for modules
+if test "$modules" = yes; then
+    shacmd_probe="sha1sum sha1 shasum"
+    for c in $shacmd_probe; do
+        if which $c &>/dev/null; then
+            shacmd="$c"
+            break
+        fi
+    done
+    if test "$shacmd" = ""; then
+        error_exit "one of the checksum commands is required to enable modules: $shacmd_probe"
+    fi
 fi
 
 ##########################################
@@ -2474,8 +2626,6 @@ EOF
   rbd_libs="-lrbd -lrados"
   if compile_prog "" "$rbd_libs" ; then
     rbd=yes
-    libs_tools="$rbd_libs $libs_tools"
-    libs_softmmu="$rbd_libs $libs_softmmu"
   else
     if test "$rbd" = "yes" ; then
       feature_not_found "rados block device" "Install librbd/ceph devel"
@@ -2492,9 +2642,6 @@ if test "$libssh2" != "no" ; then
     libssh2_cflags=`$pkg_config libssh2 --cflags`
     libssh2_libs=`$pkg_config libssh2 --libs`
     libssh2=yes
-    libs_tools="$libssh2_libs $libs_tools"
-    libs_softmmu="$libssh2_libs $libs_softmmu"
-    QEMU_CFLAGS="$QEMU_CFLAGS $libssh2_cflags"
   else
     if test "$libssh2" = "yes" ; then
       error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2"
@@ -2540,8 +2687,6 @@ int main(void) { io_setup(0, NULL); io_set_eventfd(NULL, 0); eventfd(0, 0); retu
 EOF
   if compile_prog "" "-laio" ; then
     linux_aio=yes
-    libs_softmmu="$libs_softmmu -laio"
-    libs_tools="$libs_tools -laio"
   else
     if test "$linux_aio" = "yes" ; then
       feature_not_found "linux AIO" "Install libaio devel"
@@ -2710,9 +2855,6 @@ if test "$glusterfs" != "no" ; then
     glusterfs="yes"
     glusterfs_cflags=`$pkg_config --cflags glusterfs-api`
     glusterfs_libs=`$pkg_config --libs glusterfs-api`
-    CFLAGS="$CFLAGS $glusterfs_cflags"
-    libs_tools="$glusterfs_libs $libs_tools"
-    libs_softmmu="$glusterfs_libs $libs_softmmu"
     if $pkg_config --atleast-version=5 glusterfs-api; then
       glusterfs_discard="yes"
     fi
@@ -3083,11 +3225,9 @@ EOF
     libiscsi="yes"
     libiscsi_cflags=$($pkg_config --cflags libiscsi)
     libiscsi_libs=$($pkg_config --libs libiscsi)
-    CFLAGS="$CFLAGS $libiscsi_cflags"
-    LIBS="$LIBS $libiscsi_libs"
   elif compile_prog "" "-liscsi" ; then
     libiscsi="yes"
-    LIBS="$LIBS -liscsi"
+    libiscsi_libs="-liscsi"
   else
     if test "$libiscsi" = "yes" ; then
       feature_not_found "libiscsi" "Install libiscsi devel"
@@ -3688,6 +3828,7 @@ if test "$mingw32" = "yes" ; then
 fi
 
 qemu_confdir=$sysconfdir$confsuffix
+qemu_moddir=$libdir$confsuffix
 qemu_datadir=$datadir$confsuffix
 qemu_localedir="$datadir/locale"
 
@@ -3778,6 +3919,7 @@ echo "Install prefix    $prefix"
 echo "BIOS directory    `eval echo $qemu_datadir`"
 echo "binary directory  `eval echo $bindir`"
 echo "library directory `eval echo $libdir`"
+echo "module directory  `eval echo $qemu_moddir`"
 echo "libexec directory `eval echo $libexecdir`"
 echo "include directory `eval echo $includedir`"
 echo "config directory  `eval echo $sysconfdir`"
@@ -3804,6 +3946,7 @@ echo "python            $python"
 if test "$slirp" = "yes" ; then
     echo "smbd              $smbd"
 fi
+echo "module support    $modules"
 echo "host CPU          $cpu"
 echo "host big endian   $bigendian"
 echo "target list       $target_list"
@@ -3893,6 +4036,7 @@ echo "libssh2 support   $libssh2"
 echo "TPM passthrough   $tpm_passthrough"
 echo "QOM debugging     $qom_cast_debug"
 echo "vhdx              $vhdx"
+echo "Quorum            $quorum"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3916,6 +4060,7 @@ echo "sysconfdir=$sysconfdir" >> $config_host_mak
 echo "qemu_confdir=$qemu_confdir" >> $config_host_mak
 echo "qemu_datadir=$qemu_datadir" >> $config_host_mak
 echo "qemu_docdir=$qemu_docdir" >> $config_host_mak
+echo "qemu_moddir=$qemu_moddir" >> $config_host_mak
 if test "$mingw32" = "no" ; then
   echo "qemu_localstatedir=$local_statedir" >> $config_host_mak
 fi
@@ -4048,6 +4193,12 @@ echo "TARGET_DIRS=$target_list" >> $config_host_mak
 if [ "$docs" = "yes" ] ; then
   echo "BUILD_DOCS=yes" >> $config_host_mak
 fi
+if test "$modules" = "yes"; then
+  # $shacmd can generate a hash started with digit, which the compiler doesn't
+  # like as an symbol. So prefix it with an underscore
+  echo "CONFIG_STAMP=_`(echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ `" >> $config_host_mak
+  echo "CONFIG_MODULES=y" >> $config_host_mak
+fi
 if test "$sdl" = "yes" ; then
   echo "CONFIG_SDL=y" >> $config_host_mak
   echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak
@@ -4119,8 +4270,9 @@ if test "$bswap_h" = "yes" ; then
   echo "CONFIG_MACHINE_BSWAP_H=y" >> $config_host_mak
 fi
 if test "$curl" = "yes" ; then
-  echo "CONFIG_CURL=y" >> $config_host_mak
+  echo "CONFIG_CURL=m" >> $config_host_mak
   echo "CURL_CFLAGS=$curl_cflags" >> $config_host_mak
+  echo "CURL_LIBS=$curl_libs" >> $config_host_mak
 fi
 if test "$brlapi" = "yes" ; then
   echo "CONFIG_BRLAPI=y" >> $config_host_mak
@@ -4209,10 +4361,12 @@ if test "$glx" = "yes" ; then
 fi
 
 if test "$libiscsi" = "yes" ; then
-  echo "CONFIG_LIBISCSI=y" >> $config_host_mak
+  echo "CONFIG_LIBISCSI=m" >> $config_host_mak
   if test "$libiscsi_version" = "1.4.0"; then
     echo "CONFIG_LIBISCSI_1_4=y" >> $config_host_mak
   fi
+  echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak
+  echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak
 fi
 
 if test "$libnfs" = "yes" ; then
@@ -4237,7 +4391,9 @@ if test "$qom_cast_debug" = "yes" ; then
   echo "CONFIG_QOM_CAST_DEBUG=y" >> $config_host_mak
 fi
 if test "$rbd" = "yes" ; then
-  echo "CONFIG_RBD=y" >> $config_host_mak
+  echo "CONFIG_RBD=m" >> $config_host_mak
+  echo "RBD_CFLAGS=$rbd_cflags" >> $config_host_mak
+  echo "RBD_LIBS=$rbd_libs" >> $config_host_mak
 fi
 
 echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak
@@ -4280,7 +4436,9 @@ if test "$getauxval" = "yes" ; then
 fi
 
 if test "$glusterfs" = "yes" ; then
-  echo "CONFIG_GLUSTERFS=y" >> $config_host_mak
+  echo "CONFIG_GLUSTERFS=m" >> $config_host_mak
+  echo "GLUSTERFS_CFLAGS=$glusterfs_cflags" >> $config_host_mak
+  echo "GLUSTERFS_LIBS=$glusterfs_libs" >> $config_host_mak
 fi
 
 if test "$glusterfs_discard" = "yes" ; then
@@ -4292,7 +4450,13 @@ if test "$glusterfs_zerofill" = "yes" ; then
 fi
 
 if test "$libssh2" = "yes" ; then
-  echo "CONFIG_LIBSSH2=y" >> $config_host_mak
+  echo "CONFIG_LIBSSH2=m" >> $config_host_mak
+  echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
+  echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
+fi
+
+if test "$quorum" = "yes" ; then
+  echo "CONFIG_QUORUM=y" >> $config_host_mak
 fi
 
 if test "$virtio_blk_data_plane" = "yes" ; then
@@ -4423,6 +4587,8 @@ echo "LIBTOOLFLAGS=$LIBTOOLFLAGS" >> $config_host_mak
 echo "LIBS+=$LIBS" >> $config_host_mak
 echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak
 echo "EXESUF=$EXESUF" >> $config_host_mak
+echo "DSOSUF=$DSOSUF" >> $config_host_mak
+echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak
 echo "LIBS_QGA+=$libs_qga" >> $config_host_mak
 echo "POD2MAN=$POD2MAN" >> $config_host_mak
 echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak
diff --git a/docs/qmp/qmp-events.txt b/docs/qmp/qmp-events.txt
index a378c87583..00f95154dd 100644
--- a/docs/qmp/qmp-events.txt
+++ b/docs/qmp/qmp-events.txt
@@ -500,3 +500,39 @@ Example:
 
 Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
 followed respectively by the RESET, SHUTDOWN, or STOP events.
+
+QUORUM_FAILURE
+--------------
+
+Emitted by the Quorum block driver if it fails to establish a quorum.
+
+Data:
+
+- "reference":    device name if defined else node name.
+- "sector-num":   Number of the first sector of the failed read operation.
+- "sector-count": Failed read operation sector count.
+
+Example:
+
+{ "event": "QUORUM_FAILURE",
+     "data": { "reference": "usr1", "sector-num": 345435, "sector-count": 5 },
+     "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
+
+QUORUM_REPORT_BAD
+-----------------
+
+Emitted to report a corruption of a Quorum file.
+
+Data:
+
+- "ret":          The IO return code.
+- "node-name":    The graph node name of the block driver state.
+- "sector-num":   Number of the first sector of the failed read operation.
+- "sector-count": Failed read operation sector count.
+
+Example:
+
+{ "event": "QUORUM_REPORT_BAD",
+     "data": { "ret": 0, "node-name": "1.raw", "sector-num": 345435,
+               "sector-count": 5 },
+     "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
index 098f6c62c7..bc061e6403 100644
--- a/hw/block/xen_disk.c
+++ b/hw/block/xen_disk.c
@@ -483,7 +483,18 @@ static void qemu_aio_complete(void *opaque, int ret)
     ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
     ioreq_unmap(ioreq);
     ioreq_finish(ioreq);
-    bdrv_acct_done(ioreq->blkdev->bs, &ioreq->acct);
+    switch (ioreq->req.operation) {
+    case BLKIF_OP_WRITE:
+    case BLKIF_OP_FLUSH_DISKCACHE:
+        if (!ioreq->req.nr_segments) {
+            break;
+        }
+    case BLKIF_OP_READ:
+        bdrv_acct_done(ioreq->blkdev->bs, &ioreq->acct);
+        break;
+    default:
+        break;
+    }
     qemu_bh_schedule(ioreq->blkdev->bh);
 }
 
@@ -813,8 +824,8 @@ static int blk_connect(struct XenDevice *xendev)
             Error *local_err = NULL;
             BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto,
                                                            readonly);
-            if (bdrv_open(blkdev->bs,
-                          blkdev->filename, NULL, qflags, drv, &local_err) != 0)
+            if (bdrv_open(&blkdev->bs, blkdev->filename, NULL, NULL, qflags,
+                          drv, &local_err) != 0)
             {
                 xen_be_printf(&blkdev->xendev, 0, "error: %s\n",
                               error_get_pretty(local_err));
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 1471cc093b..2a559ebcc9 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1429,7 +1429,7 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async)
     return 1;
 }
 
-static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
+static void qxl_set_mode(PCIQXLDevice *d, unsigned int modenr, int loadvm)
 {
     pcibus_t start = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
     pcibus_t end   = d->pci.io_regions[QXL_RAM_RANGE_INDEX].size + start;
@@ -1439,6 +1439,12 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
         .mem_start = start,
         .mem_end = end
     };
+
+    if (modenr >= d->modes->n_modes) {
+        qxl_set_guest_bug(d, "mode number out of range");
+        return;
+    }
+
     QXLSurfaceCreate surface = {
         .width      = mode->x_res,
         .height     = mode->y_res,
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
index 0eda301ba9..40757eb001 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -167,7 +167,7 @@ static int pci_piix_ide_initfn(PCIDevice *dev)
     return 0;
 }
 
-static int pci_piix3_xen_ide_unplug(DeviceState *dev)
+int pci_piix3_xen_ide_unplug(DeviceState *dev)
 {
     PCIIDEState *pci_ide;
     DriveInfo *di;
@@ -266,7 +266,6 @@ static void piix3_ide_xen_class_init(ObjectClass *klass, void *data)
     k->device_id = PCI_DEVICE_ID_INTEL_82371SB_1;
     k->class_id = PCI_CLASS_STORAGE_IDE;
     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
-    dc->unplug = pci_piix3_xen_ide_unplug;
 }
 
 static const TypeInfo piix3_ide_xen_info = {
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index 37cbbfd592..40a9f5ccdb 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -32,7 +32,6 @@
 #include "sysemu/sysemu.h"
 #include "hw/devices.h"
 #include "hw/boards.h"
-#include "hw/xilinx.h"
 #include "sysemu/blockdev.h"
 #include "hw/char/serial.h"
 #include "exec/address-spaces.h"
@@ -49,6 +48,7 @@
 
 #define NUM_SPI_FLASHES 4
 
+#define SPI_BASEADDR 0x40a00000
 #define MEMORY_BASEADDR 0x50000000
 #define FLASH_BASEADDR 0x86000000
 #define INTC_BASEADDR 0x81800000
@@ -57,6 +57,13 @@
 #define AXIENET_BASEADDR 0x82780000
 #define AXIDMA_BASEADDR 0x84600000
 
+#define AXIDMA_IRQ1         0
+#define AXIDMA_IRQ0         1
+#define TIMER_IRQ           2
+#define AXIENET_IRQ         3
+#define SPI_IRQ             4
+#define UART16550_IRQ       5
+
 static void machine_cpu_reset(MicroBlazeCPU *cpu)
 {
     CPUMBState *env = &cpu->env;
@@ -111,17 +118,27 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
                           2, 0x89, 0x18, 0x0000, 0x0, 0);
 
 
-    dev = xilinx_intc_create(INTC_BASEADDR, qdev_get_gpio_in(DEVICE(cpu),
-                             MB_CPU_IRQ), 4);
+    dev = qdev_create(NULL, "xlnx.xps-intc");
+    qdev_prop_set_uint32(dev, "kind-of-intr", 1 << TIMER_IRQ);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
+                       qdev_get_gpio_in(DEVICE(cpu), MB_CPU_IRQ));
     for (i = 0; i < 32; i++) {
         irq[i] = qdev_get_gpio_in(dev, i);
     }
 
     serial_mm_init(address_space_mem, UART16550_BASEADDR + 0x1000, 2,
-                   irq[5], 115200, serial_hds[0], DEVICE_LITTLE_ENDIAN);
+                   irq[UART16550_IRQ], 115200, serial_hds[0],
+                   DEVICE_LITTLE_ENDIAN);
 
     /* 2 timers at irq 2 @ 100 Mhz.  */
-    xilinx_timer_create(TIMER_BASEADDR, irq[2], 0, 100 * 1000000);
+    dev = qdev_create(NULL, "xlnx.xps-timer");
+    qdev_prop_set_uint32(dev, "one-timer-only", 0);
+    qdev_prop_set_uint32(dev, "clock-frequency", 100 * 1000000);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]);
 
     /* axi ethernet and dma initialization. */
     qemu_check_nic_model(&nd_table[0], "xlnx.axi-ethernet");
@@ -138,16 +155,30 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
                                   "axistream-connected-target", NULL);
     cs = object_property_get_link(OBJECT(dma),
                                   "axistream-control-connected-target", NULL);
-    xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(ds),
-                            STREAM_SLAVE(cs), 0x82780000, irq[3], 0x1000,
-                            0x1000);
+    qdev_set_nic_properties(eth0, &nd_table[0]);
+    qdev_prop_set_uint32(eth0, "rxmem", 0x1000);
+    qdev_prop_set_uint32(eth0, "txmem", 0x1000);
+    object_property_set_link(OBJECT(eth0), OBJECT(ds),
+                             "axistream-connected", &error_abort);
+    object_property_set_link(OBJECT(eth0), OBJECT(cs),
+                             "axistream-control-connected", &error_abort);
+    qdev_init_nofail(eth0);
+    sysbus_mmio_map(SYS_BUS_DEVICE(eth0), 0, AXIENET_BASEADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(eth0), 0, irq[AXIENET_IRQ]);
 
     ds = object_property_get_link(OBJECT(eth0),
                                   "axistream-connected-target", NULL);
     cs = object_property_get_link(OBJECT(eth0),
                                   "axistream-control-connected-target", NULL);
-    xilinx_axidma_init(dma, STREAM_SLAVE(ds), STREAM_SLAVE(cs), 0x84600000,
-                       irq[1], irq[0], 100 * 1000000);
+    qdev_prop_set_uint32(dma, "freqhz", 100 * 1000000);
+    object_property_set_link(OBJECT(dma), OBJECT(ds),
+                             "axistream-connected", &error_abort);
+    object_property_set_link(OBJECT(dma), OBJECT(cs),
+                             "axistream-control-connected", &error_abort);
+    qdev_init_nofail(dma);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dma), 0, AXIDMA_BASEADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dma), 0, irq[AXIDMA_IRQ0]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dma), 1, irq[AXIDMA_IRQ1]);
 
     {
         SSIBus *spi;
@@ -156,8 +187,8 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
         qdev_prop_set_uint8(dev, "num-ss-bits", NUM_SPI_FLASHES);
         qdev_init_nofail(dev);
         busdev = SYS_BUS_DEVICE(dev);
-        sysbus_mmio_map(busdev, 0, 0x40a00000);
-        sysbus_connect_irq(busdev, 0, irq[4]);
+        sysbus_mmio_map(busdev, 0, SPI_BASEADDR);
+        sysbus_connect_irq(busdev, 0, irq[SPI_IRQ]);
 
         spi = (SSIBus *)qdev_get_child_bus(dev, "spi");
 
diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
index f50021506c..6c45e206ec 100644
--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
+++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
@@ -30,7 +30,6 @@
 #include "sysemu/sysemu.h"
 #include "hw/devices.h"
 #include "hw/boards.h"
-#include "hw/xilinx.h"
 #include "sysemu/blockdev.h"
 #include "exec/address-spaces.h"
 
@@ -48,6 +47,10 @@
 #define UARTLITE_BASEADDR 0x84000000
 #define ETHLITE_BASEADDR 0x81000000
 
+#define TIMER_IRQ           0
+#define ETHLITE_IRQ         1
+#define UARTLITE_IRQ        3
+
 static void machine_cpu_reset(MicroBlazeCPU *cpu)
 {
     CPUMBState *env = &cpu->env;
@@ -93,16 +96,36 @@ petalogix_s3adsp1800_init(QEMUMachineInitArgs *args)
                           FLASH_SIZE >> 16,
                           1, 0x89, 0x18, 0x0000, 0x0, 1);
 
-    dev = xilinx_intc_create(INTC_BASEADDR, qdev_get_gpio_in(DEVICE(cpu),
-                             MB_CPU_IRQ), 0xA);
+    dev = qdev_create(NULL, "xlnx.xps-intc");
+    qdev_prop_set_uint32(dev, "kind-of-intr",
+                         1 << ETHLITE_IRQ | 1 << UARTLITE_IRQ);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
+                       qdev_get_gpio_in(DEVICE(cpu), MB_CPU_IRQ));
     for (i = 0; i < 32; i++) {
         irq[i] = qdev_get_gpio_in(dev, i);
     }
 
-    sysbus_create_simple("xlnx.xps-uartlite", UARTLITE_BASEADDR, irq[3]);
+    sysbus_create_simple("xlnx.xps-uartlite", UARTLITE_BASEADDR,
+                         irq[UARTLITE_IRQ]);
+
     /* 2 timers at irq 2 @ 62 Mhz.  */
-    xilinx_timer_create(TIMER_BASEADDR, irq[0], 0, 62 * 1000000);
-    xilinx_ethlite_create(&nd_table[0], ETHLITE_BASEADDR, irq[1], 0, 0);
+    dev = qdev_create(NULL, "xlnx.xps-timer");
+    qdev_prop_set_uint32(dev, "one-timer-only", 0);
+    qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]);
+
+    qemu_check_nic_model(&nd_table[0], "xlnx.xps-ethernetlite");
+    dev = qdev_create(NULL, "xlnx.xps-ethernetlite");
+    qdev_set_nic_properties(dev, &nd_table[0]);
+    qdev_prop_set_uint32(dev, "tx-ping-pong", 0);
+    qdev_prop_set_uint32(dev, "rx-ping-pong", 0);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]);
 
     microblaze_load_kernel(cpu, ddr_base, ram_size,
                            args->initrd_filename,
diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c
index 4118d54ac8..4a443049dd 100644
--- a/hw/net/opencores_eth.c
+++ b/hw/net/opencores_eth.c
@@ -169,6 +169,7 @@ enum {
 };
 
 enum {
+    INT_SOURCE_BUSY = 0x10,
     INT_SOURCE_RXB = 0x4,
     INT_SOURCE_TXB = 0x1,
 };
@@ -351,8 +352,7 @@ static int open_eth_can_receive(NetClientState *nc)
     OpenEthState *s = qemu_get_nic_opaque(nc);
 
     return GET_REGBIT(s, MODER, RXEN) &&
-        (s->regs[TX_BD_NUM] < 0x80) &&
-        (rx_desc(s)->len_flags & RXD_E);
+        (s->regs[TX_BD_NUM] < 0x80);
 }
 
 static ssize_t open_eth_receive(NetClientState *nc,
@@ -402,6 +402,12 @@ static ssize_t open_eth_receive(NetClientState *nc,
         desc *desc = rx_desc(s);
         size_t copy_size = GET_REGBIT(s, MODER, HUGEN) ? 65536 : maxfl;
 
+        if (!(desc->len_flags & RXD_E)) {
+            open_eth_int_source_write(s,
+                    s->regs[INT_SOURCE] | INT_SOURCE_BUSY);
+            return size;
+        }
+
         desc->len_flags &= ~(RXD_CF | RXD_M | RXD_OR |
                 RXD_IS | RXD_DN | RXD_TL | RXD_SF | RXD_CRC | RXD_LC);
 
@@ -551,6 +557,15 @@ static uint64_t open_eth_reg_read(void *opaque,
     return v;
 }
 
+static void open_eth_notify_can_receive(OpenEthState *s)
+{
+    NetClientState *nc = qemu_get_queue(s->nic);
+
+    if (open_eth_can_receive(nc)) {
+        qemu_flush_queued_packets(nc);
+    }
+}
+
 static void open_eth_ro(OpenEthState *s, uint32_t val)
 {
 }
@@ -567,6 +582,7 @@ static void open_eth_moder_host_write(OpenEthState *s, uint32_t val)
 
     if (set & MODER_RXEN) {
         s->rx_desc = s->regs[TX_BD_NUM];
+        open_eth_notify_can_receive(s);
     }
     if (set & MODER_TXEN) {
         s->tx_desc = 0;
@@ -592,6 +608,18 @@ static void open_eth_int_mask_host_write(OpenEthState *s, uint32_t val)
             s->regs[INT_SOURCE] & s->regs[INT_MASK]);
 }
 
+static void open_eth_tx_bd_num_host_write(OpenEthState *s, uint32_t val)
+{
+    if (val < 0x80) {
+        bool enable = s->regs[TX_BD_NUM] == 0x80;
+
+        s->regs[TX_BD_NUM] = val;
+        if (enable) {
+            open_eth_notify_can_receive(s);
+        }
+    }
+}
+
 static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val)
 {
     unsigned fiad = GET_REGFIELD(s, MIIADDRESS, FIAD);
@@ -630,6 +658,7 @@ static void open_eth_reg_write(void *opaque,
         [MODER] = open_eth_moder_host_write,
         [INT_SOURCE] = open_eth_int_source_host_write,
         [INT_MASK] = open_eth_int_mask_host_write,
+        [TX_BD_NUM] = open_eth_tx_bd_num_host_write,
         [MIICOMMAND] = open_eth_mii_command_host_write,
         [MIITX_DATA] = open_eth_mii_tx_host_write,
         [MIISTATUS] = open_eth_ro,
diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index bdb057e36c..85a0e537b9 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -40,11 +40,19 @@
 #include "ppc405.h"
 
 #include "sysemu/blockdev.h"
-#include "hw/xilinx.h"
+#include "qapi/qmp/qerror.h"
 
 #define EPAPR_MAGIC    (0x45504150)
 #define FLASH_SIZE     (16 * 1024 * 1024)
 
+#define INTC_BASEADDR       0x81800000
+#define UART16550_BASEADDR  0x83e01003
+#define TIMER_BASEADDR      0x83c00000
+#define PFLASH_BASEADDR     0xfc000000
+
+#define TIMER_IRQ           3
+#define UART16550_IRQ       9
+
 static struct boot_info
 {
     uint32_t bootstrap_pc;
@@ -204,22 +212,31 @@ static void virtex_init(QEMUMachineInitArgs *args)
     memory_region_add_subregion(address_space_mem, ram_base, phys_ram);
 
     dinfo = drive_get(IF_PFLASH, 0, 0);
-    pflash_cfi01_register(0xfc000000, NULL, "virtex.flash", FLASH_SIZE,
+    pflash_cfi01_register(PFLASH_BASEADDR, NULL, "virtex.flash", FLASH_SIZE,
                           dinfo ? dinfo->bdrv : NULL, (64 * 1024),
                           FLASH_SIZE >> 16,
                           1, 0x89, 0x18, 0x0000, 0x0, 1);
 
     cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT];
-    dev = xilinx_intc_create(0x81800000, cpu_irq[0], 0);
+    dev = qdev_create(NULL, "xlnx.xps-intc");
+    qdev_prop_set_uint32(dev, "kind-of-intr", 0);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq[0]);
     for (i = 0; i < 32; i++) {
         irq[i] = qdev_get_gpio_in(dev, i);
     }
 
-    serial_mm_init(address_space_mem, 0x83e01003ULL, 2, irq[9], 115200,
-                   serial_hds[0], DEVICE_LITTLE_ENDIAN);
+    serial_mm_init(address_space_mem, UART16550_BASEADDR, 2, irq[UART16550_IRQ],
+                   115200, serial_hds[0], DEVICE_LITTLE_ENDIAN);
 
     /* 2 timers at irq 2 @ 62 Mhz.  */
-    xilinx_timer_create(0x83c00000, irq[3], 0, 62 * 1000000);
+    dev = qdev_create(NULL, "xlnx.xps-timer");
+    qdev_prop_set_uint32(dev, "one-timer-only", 0);
+    qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]);
 
     if (kernel_filename) {
         uint64_t entry, low, high;
diff --git a/hw/xen/xen_platform.c b/hw/xen/xen_platform.c
index 70875e4122..1d9d0e9f25 100644
--- a/hw/xen/xen_platform.c
+++ b/hw/xen/xen_platform.c
@@ -27,6 +27,7 @@
 
 #include "hw/hw.h"
 #include "hw/i386/pc.h"
+#include "hw/ide.h"
 #include "hw/pci/pci.h"
 #include "hw/irq.h"
 #include "hw/xen/xen_common.h"
@@ -110,7 +111,7 @@ static void unplug_disks(PCIBus *b, PCIDevice *d, void *o)
     if (pci_get_word(d->config + PCI_CLASS_DEVICE) ==
             PCI_CLASS_STORAGE_IDE
             && strcmp(d->name, "xen-pci-passthrough") != 0) {
-        qdev_unplug(DEVICE(d), NULL);
+        pci_piix3_xen_ide_unplug(DEVICE(d));
     }
 }
 
diff --git a/hw/xtensa/xtensa_lx60.c b/hw/xtensa/xtensa_lx60.c
index 22e124d9ec..49c58d11a3 100644
--- a/hw/xtensa/xtensa_lx60.c
+++ b/hw/xtensa/xtensa_lx60.c
@@ -40,6 +40,7 @@
 #include "xtensa_bootparam.h"
 
 typedef struct LxBoardDesc {
+    hwaddr flash_base;
     size_t flash_size;
     size_t flash_sector_size;
     size_t sram_size;
@@ -219,7 +220,7 @@ static void lx_init(const LxBoardDesc *board, QEMUMachineInitArgs *args)
 
     dinfo = drive_get(IF_PFLASH, 0, 0);
     if (dinfo) {
-        flash = pflash_cfi01_register(0xf8000000,
+        flash = pflash_cfi01_register(board->flash_base,
                 NULL, "lx60.io.flash", board->flash_size,
                 dinfo->bdrv, board->flash_sector_size,
                 board->flash_size / board->flash_sector_size,
@@ -265,7 +266,9 @@ static void lx_init(const LxBoardDesc *board, QEMUMachineInitArgs *args)
             MemoryRegion *flash_io = g_malloc(sizeof(*flash_io));
 
             memory_region_init_alias(flash_io, NULL, "lx60.flash",
-                    flash_mr, 0, board->flash_size);
+                    flash_mr, 0,
+                    board->flash_size < 0x02000000 ?
+                    board->flash_size : 0x02000000);
             memory_region_add_subregion(system_memory, 0xfe000000,
                     flash_io);
         }
@@ -275,7 +278,8 @@ static void lx_init(const LxBoardDesc *board, QEMUMachineInitArgs *args)
 static void xtensa_lx60_init(QEMUMachineInitArgs *args)
 {
     static const LxBoardDesc lx60_board = {
-        .flash_size = 0x400000,
+        .flash_base = 0xf8000000,
+        .flash_size = 0x00400000,
         .flash_sector_size = 0x10000,
         .sram_size = 0x20000,
     };
@@ -285,13 +289,36 @@ static void xtensa_lx60_init(QEMUMachineInitArgs *args)
 static void xtensa_lx200_init(QEMUMachineInitArgs *args)
 {
     static const LxBoardDesc lx200_board = {
-        .flash_size = 0x1000000,
+        .flash_base = 0xf8000000,
+        .flash_size = 0x01000000,
         .flash_sector_size = 0x20000,
         .sram_size = 0x2000000,
     };
     lx_init(&lx200_board, args);
 }
 
+static void xtensa_ml605_init(QEMUMachineInitArgs *args)
+{
+    static const LxBoardDesc ml605_board = {
+        .flash_base = 0xf8000000,
+        .flash_size = 0x02000000,
+        .flash_sector_size = 0x20000,
+        .sram_size = 0x2000000,
+    };
+    lx_init(&ml605_board, args);
+}
+
+static void xtensa_kc705_init(QEMUMachineInitArgs *args)
+{
+    static const LxBoardDesc kc705_board = {
+        .flash_base = 0xf0000000,
+        .flash_size = 0x08000000,
+        .flash_sector_size = 0x20000,
+        .sram_size = 0x2000000,
+    };
+    lx_init(&kc705_board, args);
+}
+
 static QEMUMachine xtensa_lx60_machine = {
     .name = "lx60",
     .desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
@@ -306,10 +333,26 @@ static QEMUMachine xtensa_lx200_machine = {
     .max_cpus = 4,
 };
 
+static QEMUMachine xtensa_ml605_machine = {
+    .name = "ml605",
+    .desc = "ml605 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
+    .init = xtensa_ml605_init,
+    .max_cpus = 4,
+};
+
+static QEMUMachine xtensa_kc705_machine = {
+    .name = "kc705",
+    .desc = "kc705 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
+    .init = xtensa_kc705_init,
+    .max_cpus = 4,
+};
+
 static void xtensa_lx_machines_init(void)
 {
     qemu_register_machine(&xtensa_lx60_machine);
     qemu_register_machine(&xtensa_lx200_machine);
+    qemu_register_machine(&xtensa_ml605_machine);
+    qemu_register_machine(&xtensa_kc705_machine);
 }
 
 machine_init(xtensa_lx_machines_init);
diff --git a/include/block/block.h b/include/block/block.h
index 963a61fa4c..780f48b7b3 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -102,6 +102,9 @@ typedef enum {
 #define BDRV_O_CHECK       0x1000  /* open solely for consistency check */
 #define BDRV_O_ALLOW_RDWR  0x2000  /* allow reopen to change from r/o to r/w */
 #define BDRV_O_UNMAP       0x4000  /* execute guest UNMAP/TRIM operations */
+#define BDRV_O_PROTOCOL    0x8000  /* if no block driver is explicitly given:
+                                      select an appropriate protocol driver,
+                                      ignoring the format layer */
 
 #define BDRV_O_CACHE_MASK  (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
 
@@ -183,15 +186,13 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old);
 void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
 int bdrv_parse_cache_flags(const char *mode, int *flags);
 int bdrv_parse_discard_flags(const char *mode, int *flags);
-int bdrv_file_open(BlockDriverState **pbs, const char *filename,
-                   const char *reference, QDict *options, int flags,
-                   Error **errp);
 int bdrv_open_image(BlockDriverState **pbs, const char *filename,
                     QDict *options, const char *bdref_key, int flags,
-                    bool force_raw, bool allow_none, Error **errp);
+                    bool allow_none, Error **errp);
 int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
-int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
-              int flags, BlockDriver *drv, Error **errp);
+int bdrv_open(BlockDriverState **pbs, const char *filename,
+              const char *reference, QDict *options, int flags,
+              BlockDriver *drv, Error **errp);
 BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
                                     BlockDriverState *bs, int flags);
 int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
diff --git a/include/block/nbd.h b/include/block/nbd.h
index c90f5e4d9e..79502a090b 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -62,12 +62,6 @@ enum {
 #define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024)
 
 ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read);
-int tcp_socket_incoming(const char *address, uint16_t port);
-int tcp_socket_incoming_spec(const char *address_and_port);
-int tcp_socket_outgoing_opts(QemuOpts *opts);
-int unix_socket_outgoing(const char *path);
-int unix_socket_incoming(const char *path);
-
 int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
                           off_t *size, size_t *blocksize);
 int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize);
diff --git a/include/hw/ide.h b/include/hw/ide.h
index 507e6d33cb..bc8bd321a2 100644
--- a/include/hw/ide.h
+++ b/include/hw/ide.h
@@ -17,6 +17,7 @@ void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table,
 PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
 PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
 PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
+int pci_piix3_xen_ide_unplug(DeviceState *dev);
 void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
 
 /* ide-mmio.c */
diff --git a/include/hw/xilinx.h b/include/hw/xilinx.h
deleted file mode 100644
index 9d6debe4d0..0000000000
--- a/include/hw/xilinx.h
+++ /dev/null
@@ -1,90 +0,0 @@
-#ifndef HW_XILINX_H
-#define HW_XILINX_H 1
-
-
-#include "qemu-common.h"
-#include "qapi/qmp/qerror.h"
-#include "hw/stream.h"
-#include "net/net.h"
-
-static inline DeviceState *
-xilinx_intc_create(hwaddr base, qemu_irq irq, int kind_of_intr)
-{
-    DeviceState *dev;
-
-    dev = qdev_create(NULL, "xlnx.xps-intc");
-    qdev_prop_set_uint32(dev, "kind-of-intr", kind_of_intr);
-    qdev_init_nofail(dev);
-    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
-    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
-    return dev;
-}
-
-/* OPB Timer/Counter.  */
-static inline DeviceState *
-xilinx_timer_create(hwaddr base, qemu_irq irq, int oto, int freq)
-{
-    DeviceState *dev;
-
-    dev = qdev_create(NULL, "xlnx.xps-timer");
-    qdev_prop_set_uint32(dev, "one-timer-only", oto);
-    qdev_prop_set_uint32(dev, "clock-frequency", freq);
-    qdev_init_nofail(dev);
-    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
-    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
-    return dev;
-}
-
-/* XPS Ethernet Lite MAC.  */
-static inline DeviceState *
-xilinx_ethlite_create(NICInfo *nd, hwaddr base, qemu_irq irq,
-                      int txpingpong, int rxpingpong)
-{
-    DeviceState *dev;
-
-    qemu_check_nic_model(nd, "xlnx.xps-ethernetlite");
-
-    dev = qdev_create(NULL, "xlnx.xps-ethernetlite");
-    qdev_set_nic_properties(dev, nd);
-    qdev_prop_set_uint32(dev, "tx-ping-pong", txpingpong);
-    qdev_prop_set_uint32(dev, "rx-ping-pong", rxpingpong);
-    qdev_init_nofail(dev);
-    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
-    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
-    return dev;
-}
-
-static inline void
-xilinx_axiethernet_init(DeviceState *dev, NICInfo *nd, StreamSlave *ds,
-                        StreamSlave *cs, hwaddr base, qemu_irq irq, int txmem,
-                        int rxmem)
-{
-    qdev_set_nic_properties(dev, nd);
-    qdev_prop_set_uint32(dev, "rxmem", rxmem);
-    qdev_prop_set_uint32(dev, "txmem", txmem);
-    object_property_set_link(OBJECT(dev), OBJECT(ds),
-                             "axistream-connected", &error_abort);
-    object_property_set_link(OBJECT(dev), OBJECT(cs),
-                             "axistream-control-connected", &error_abort);
-    qdev_init_nofail(dev);
-    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
-    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
-}
-
-static inline void
-xilinx_axidma_init(DeviceState *dev, StreamSlave *ds, StreamSlave *cs,
-                   hwaddr base, qemu_irq irq, qemu_irq irq2, int freqhz)
-{
-    qdev_prop_set_uint32(dev, "freqhz", freqhz);
-    object_property_set_link(OBJECT(dev), OBJECT(ds),
-                             "axistream-connected", &error_abort);
-    object_property_set_link(OBJECT(dev), OBJECT(cs),
-                             "axistream-control-connected", &error_abort);
-    qdev_init_nofail(dev);
-
-    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
-    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
-    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, irq2);
-}
-
-#endif
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 7e5f752b7a..a49ea11eb4 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -49,6 +49,8 @@ typedef enum MonitorEvent {
     QEVENT_SPICE_MIGRATE_COMPLETED,
     QEVENT_GUEST_PANICKED,
     QEVENT_BLOCK_IMAGE_CORRUPTED,
+    QEVENT_QUORUM_FAILURE,
+    QEVENT_QUORUM_REPORT_BAD,
 
     /* Add to 'monitor_event_names' array in monitor.c when
      * defining new events here */
diff --git a/include/qemu-common.h b/include/qemu-common.h
index b0e34b2e15..c8a58a873a 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -338,6 +338,8 @@ size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset,
                            const void *buf, size_t bytes);
 size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset,
                          int fillc, size_t bytes);
+ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b);
+void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf);
 
 bool buffer_is_zero(const void *buf, size_t len);
 
@@ -352,7 +354,7 @@ char *qemu_find_file(int type, const char *name);
 
 /* OS specific functions */
 void os_setup_early_signal_handling(void);
-char *os_find_datadir(const char *argv0);
+char *os_find_datadir(void);
 void os_parse_cmd_args(int index, const char *optarg);
 void os_pidfile_error(void);
 
diff --git a/include/qemu/module.h b/include/qemu/module.h
index c4ccd57166..72d94984a2 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -14,11 +14,31 @@
 #ifndef QEMU_MODULE_H
 #define QEMU_MODULE_H
 
+#include "qemu/osdep.h"
+
+#define DSO_STAMP_FUN         glue(qemu_stamp, CONFIG_STAMP)
+#define DSO_STAMP_FUN_STR     stringify(DSO_STAMP_FUN)
+
+#ifdef BUILD_DSO
+void DSO_STAMP_FUN(void);
+/* This is a dummy symbol to identify a loaded DSO as a QEMU module, so we can
+ * distinguish "version mismatch" from "not a QEMU module", when the stamp
+ * check fails during module loading */
+void qemu_module_dummy(void);
+
+#define module_init(function, type)                                         \
+static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
+{                                                                           \
+    register_dso_module_init(function, type);                               \
+}
+#else
 /* This should not be used directly.  Use block_init etc. instead.  */
 #define module_init(function, type)                                         \
-static void __attribute__((constructor)) do_qemu_init_ ## function(void) {  \
+static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
+{                                                                           \
     register_module_init(function, type);                                   \
 }
+#endif
 
 typedef enum {
     MODULE_INIT_BLOCK,
@@ -34,6 +54,7 @@ typedef enum {
 #define type_init(function) module_init(function, MODULE_INIT_QOM)
 
 void register_module_init(void (*fn)(void), module_init_type type);
+void register_dso_module_init(void (*fn)(void), module_init_type type);
 
 void module_call_init(module_init_type type);
 
diff --git a/include/qemu/option.h b/include/qemu/option.h
index 3ea871a3ba..8c0ac3485e 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -79,6 +79,8 @@ void parse_option_size(const char *name, const char *value,
 void free_option_parameters(QEMUOptionParameter *list);
 void print_option_parameters(QEMUOptionParameter *list);
 void print_option_help(QEMUOptionParameter *list);
+bool has_help_option(const char *param);
+bool is_valid_option_list(const char *param);
 
 /* ------------------------------------------------------------------ */
 
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index eac7172bcb..ffb296692d 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -215,6 +215,15 @@ bool fips_get_state(void);
  */
 char *qemu_get_local_state_pathname(const char *relative_pathname);
 
+/* Find program directory, and save it for later usage with
+ * qemu_get_exec_dir().
+ * Try OS specific API first, if not working, parse from argv0. */
+void qemu_init_exec_dir(const char *argv0);
+
+/* Get the saved exec dir.
+ * Caller needs to release the returned string by g_free() */
+char *qemu_get_exec_dir(void);
+
 /**
  * qemu_getauxval:
  * @type: the auxiliary vector key to lookup
diff --git a/module-common.c b/module-common.c
new file mode 100644
index 0000000000..50c67500b1
--- /dev/null
+++ b/module-common.c
@@ -0,0 +1,10 @@
+#include "config-host.h"
+#include "qemu/module.h"
+
+void qemu_module_dummy(void)
+{
+}
+
+void DSO_STAMP_FUN(void)
+{
+}
diff --git a/monitor.c b/monitor.c
index de90fba41e..aebcbd8beb 100644
--- a/monitor.c
+++ b/monitor.c
@@ -508,6 +508,8 @@ static const char *monitor_event_names[] = {
     [QEVENT_SPICE_MIGRATE_COMPLETED] = "SPICE_MIGRATE_COMPLETED",
     [QEVENT_GUEST_PANICKED] = "GUEST_PANICKED",
     [QEVENT_BLOCK_IMAGE_CORRUPTED] = "BLOCK_IMAGE_CORRUPTED",
+    [QEVENT_QUORUM_FAILURE] = "QUORUM_FAILURE",
+    [QEVENT_QUORUM_REPORT_BAD] = "QUORUM_REPORT_BAD",
 };
 QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX)
 
@@ -638,6 +640,9 @@ static void monitor_protocol_event_init(void)
     monitor_protocol_event_throttle(QEVENT_RTC_CHANGE, 1000);
     monitor_protocol_event_throttle(QEVENT_BALLOON_CHANGE, 1000);
     monitor_protocol_event_throttle(QEVENT_WATCHDOG, 1000);
+    /* limit the rate of quorum events to avoid hammering the management */
+    monitor_protocol_event_throttle(QEVENT_QUORUM_REPORT_BAD, 1000);
+    monitor_protocol_event_throttle(QEVENT_QUORUM_FAILURE, 1000);
 }
 
 /**
diff --git a/nbd.c b/nbd.c
index 030f56b5c7..e5084b6e7c 100644
--- a/nbd.c
+++ b/nbd.c
@@ -188,72 +188,6 @@ static ssize_t write_sync(int fd, void *buffer, size_t size)
     return ret;
 }
 
-static void combine_addr(char *buf, size_t len, const char* address,
-                         uint16_t port)
-{
-    /* If the address-part contains a colon, it's an IPv6 IP so needs [] */
-    if (strstr(address, ":")) {
-        snprintf(buf, len, "[%s]:%u", address, port);
-    } else {
-        snprintf(buf, len, "%s:%u", address, port);
-    }
-}
-
-int tcp_socket_outgoing_opts(QemuOpts *opts)
-{
-    Error *local_err = NULL;
-    int fd = inet_connect_opts(opts, &local_err, NULL, NULL);
-    if (local_err != NULL) {
-        qerror_report_err(local_err);
-        error_free(local_err);
-    }
-
-    return fd;
-}
-
-int tcp_socket_incoming(const char *address, uint16_t port)
-{
-    char address_and_port[128];
-    combine_addr(address_and_port, 128, address, port);
-    return tcp_socket_incoming_spec(address_and_port);
-}
-
-int tcp_socket_incoming_spec(const char *address_and_port)
-{
-    Error *local_err = NULL;
-    int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err);
-
-    if (local_err != NULL) {
-        qerror_report_err(local_err);
-        error_free(local_err);
-    }
-    return fd;
-}
-
-int unix_socket_incoming(const char *path)
-{
-    Error *local_err = NULL;
-    int fd = unix_listen(path, NULL, 0, &local_err);
-
-    if (local_err != NULL) {
-        qerror_report_err(local_err);
-        error_free(local_err);
-    }
-    return fd;
-}
-
-int unix_socket_outgoing(const char *path)
-{
-    Error *local_err = NULL;
-    int fd = unix_connect(path, &local_err);
-
-    if (local_err != NULL) {
-        qerror_report_err(local_err);
-        error_free(local_err);
-    }
-    return fd;
-}
-
 /* Basic flow for negotiation
 
    Server         Client
diff --git a/os-posix.c b/os-posix.c
index d39261d849..6187301481 100644
--- a/os-posix.c
+++ b/os-posix.c
@@ -84,46 +84,17 @@ void os_setup_signal_handling(void)
    running from the build tree this will be "$bindir/../pc-bios".  */
 #define SHARE_SUFFIX "/share/qemu"
 #define BUILD_SUFFIX "/pc-bios"
-char *os_find_datadir(const char *argv0)
+char *os_find_datadir(void)
 {
-    char *dir;
-    char *p = NULL;
+    char *dir, *exec_dir;
     char *res;
-    char buf[PATH_MAX];
     size_t max_len;
 
-#if defined(__linux__)
-    {
-        int len;
-        len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
-        if (len > 0) {
-            buf[len] = 0;
-            p = buf;
-        }
-    }
-#elif defined(__FreeBSD__)
-    {
-        static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
-        size_t len = sizeof(buf) - 1;
-
-        *buf = '\0';
-        if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) &&
-            *buf) {
-            buf[sizeof(buf) - 1] = '\0';
-            p = buf;
-        }
-    }
-#endif
-    /* If we don't have any way of figuring out the actual executable
-       location then try argv[0].  */
-    if (!p) {
-        p = realpath(argv0, buf);
-        if (!p) {
-            return NULL;
-        }
+    exec_dir = qemu_get_exec_dir();
+    if (exec_dir == NULL) {
+        return NULL;
     }
-    dir = dirname(p);
-    dir = dirname(dir);
+    dir = dirname(exec_dir);
 
     max_len = strlen(dir) +
         MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1;
@@ -137,6 +108,7 @@ char *os_find_datadir(const char *argv0)
         }
     }
 
+    g_free(exec_dir);
     return res;
 }
 #undef SHARE_SUFFIX
diff --git a/os-win32.c b/os-win32.c
index 50b7f6f885..5f95caac15 100644
--- a/os-win32.c
+++ b/os-win32.c
@@ -84,26 +84,9 @@ void os_setup_early_signal_handling(void)
 }
 
 /* Look for support files in the same directory as the executable.  */
-char *os_find_datadir(const char *argv0)
+char *os_find_datadir(void)
 {
-    char *p;
-    char buf[MAX_PATH];
-    DWORD len;
-
-    len = GetModuleFileName(NULL, buf, sizeof(buf) - 1);
-    if (len == 0) {
-        return NULL;
-    }
-
-    buf[len] = 0;
-    p = buf + len - 1;
-    while (p != buf && *p != '\\')
-        p--;
-    *p = 0;
-    if (access(buf, R_OK) == 0) {
-        return g_strdup(buf);
-    }
-    return NULL;
+    return qemu_get_exec_dir();
 }
 
 void os_set_line_buffering(void)
diff --git a/qapi-schema.json b/qapi-schema.json
index 473c096fa9..fcb2280053 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -4432,6 +4432,24 @@
             'raw': 'BlockdevRef' } }
 
 ##
+# @BlockdevOptionsQuorum
+#
+# Driver specific block device options for Quorum
+#
+# @blkverify:      #optional true if the driver must print content mismatch
+#
+# @children:       the children block device to use
+#
+# @vote_threshold: the vote limit under which a read will fail
+#
+# Since: 2.0
+##
+{ 'type': 'BlockdevOptionsQuorum',
+  'data': { '*blkverify': 'bool',
+            'children': [ 'BlockdevRef' ],
+            'vote-threshold': 'int' } }
+
+##
 # @BlockdevOptions
 #
 # Options for creating a block device.
@@ -4470,7 +4488,8 @@
       'vdi':        'BlockdevOptionsGenericFormat',
       'vhdx':       'BlockdevOptionsGenericFormat',
       'vmdk':       'BlockdevOptionsGenericCOWFormat',
-      'vpc':        'BlockdevOptionsGenericFormat'
+      'vpc':        'BlockdevOptionsGenericFormat',
+      'quorum':     'BlockdevOptionsQuorum'
   } }
 
 ##
diff --git a/qemu-img.c b/qemu-img.c
index 0927b090de..78fc86826c 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -250,16 +250,19 @@ static int print_block_option_help(const char *filename, const char *fmt)
         return 1;
     }
 
-    proto_drv = bdrv_find_protocol(filename, true);
-    if (!proto_drv) {
-        error_report("Unknown protocol '%s'", filename);
-        return 1;
-    }
-
     create_options = append_option_parameters(create_options,
                                               drv->create_options);
-    create_options = append_option_parameters(create_options,
-                                              proto_drv->create_options);
+
+    if (filename) {
+        proto_drv = bdrv_find_protocol(filename, true);
+        if (!proto_drv) {
+            error_report("Unknown protocol '%s'", filename);
+            return 1;
+        }
+        create_options = append_option_parameters(create_options,
+                                                  proto_drv->create_options);
+    }
+
     print_option_help(create_options);
     free_option_parameters(create_options);
     return 0;
@@ -289,7 +292,7 @@ static BlockDriverState *bdrv_new_open(const char *filename,
         drv = NULL;
     }
 
-    ret = bdrv_open(bs, filename, NULL, flags, drv, &local_err);
+    ret = bdrv_open(&bs, filename, NULL, NULL, flags, drv, &local_err);
     if (ret < 0) {
         error_report("Could not open '%s': %s", filename,
                      error_get_pretty(local_err));
@@ -310,9 +313,7 @@ static BlockDriverState *bdrv_new_open(const char *filename,
     }
     return bs;
 fail:
-    if (bs) {
-        bdrv_unref(bs);
-    }
+    bdrv_unref(bs);
     return NULL;
 }
 
@@ -371,13 +372,23 @@ static int img_create(int argc, char **argv)
         case 'e':
             error_report("option -e is deprecated, please use \'-o "
                   "encryption\' instead!");
-            return 1;
+            goto fail;
         case '6':
             error_report("option -6 is deprecated, please use \'-o "
                   "compat6\' instead!");
-            return 1;
+            goto fail;
         case 'o':
-            options = optarg;
+            if (!is_valid_option_list(optarg)) {
+                error_report("Invalid option list: %s", optarg);
+                goto fail;
+            }
+            if (!options) {
+                options = g_strdup(optarg);
+            } else {
+                char *old_options = options;
+                options = g_strdup_printf("%s,%s", options, optarg);
+                g_free(old_options);
+            }
             break;
         case 'q':
             quiet = true;
@@ -386,10 +397,16 @@ static int img_create(int argc, char **argv)
     }
 
     /* Get the filename */
+    filename = (optind < argc) ? argv[optind] : NULL;
+    if (options && has_help_option(options)) {
+        g_free(options);
+        return print_block_option_help(filename, fmt);
+    }
+
     if (optind >= argc) {
         help();
     }
-    filename = argv[optind++];
+    optind++;
 
     /* Get image size, if specified */
     if (optind < argc) {
@@ -405,7 +422,7 @@ static int img_create(int argc, char **argv)
                 error_report("kilobytes, megabytes, gigabytes, terabytes, "
                              "petabytes and exabytes.");
             }
-            return 1;
+            goto fail;
         }
         img_size = (uint64_t)sval;
     }
@@ -413,19 +430,20 @@ static int img_create(int argc, char **argv)
         help();
     }
 
-    if (options && is_help_option(options)) {
-        return print_block_option_help(filename, fmt);
-    }
-
     bdrv_img_create(filename, fmt, base_filename, base_fmt,
                     options, img_size, BDRV_O_FLAGS, &local_err, quiet);
     if (local_err) {
         error_report("%s: %s", filename, error_get_pretty(local_err));
         error_free(local_err);
-        return 1;
+        goto fail;
     }
 
+    g_free(options);
     return 0;
+
+fail:
+    g_free(options);
+    return 1;
 }
 
 static void dump_json_image_check(ImageCheck *check, bool quiet)
@@ -1150,6 +1168,9 @@ static int img_convert(int argc, char **argv)
     Error *local_err = NULL;
     QemuOpts *sn_opts = NULL;
 
+    /* Initialize before goto out */
+    qemu_progress_init(progress, 1.0);
+
     fmt = NULL;
     out_fmt = "raw";
     cache = "unsafe";
@@ -1181,13 +1202,26 @@ static int img_convert(int argc, char **argv)
         case 'e':
             error_report("option -e is deprecated, please use \'-o "
                   "encryption\' instead!");
-            return 1;
+            ret = -1;
+            goto out;
         case '6':
             error_report("option -6 is deprecated, please use \'-o "
                   "compat6\' instead!");
-            return 1;
+            ret = -1;
+            goto out;
         case 'o':
-            options = optarg;
+            if (!is_valid_option_list(optarg)) {
+                error_report("Invalid option list: %s", optarg);
+                ret = -1;
+                goto out;
+            }
+            if (!options) {
+                options = g_strdup(optarg);
+            } else {
+                char *old_options = options;
+                options = g_strdup_printf("%s,%s", options, optarg);
+                g_free(old_options);
+            }
             break;
         case 's':
             snapshot_name = optarg;
@@ -1198,7 +1232,8 @@ static int img_convert(int argc, char **argv)
                 if (!sn_opts) {
                     error_report("Failed in parsing snapshot param '%s'",
                                  optarg);
-                    return 1;
+                    ret = -1;
+                    goto out;
                 }
             } else {
                 snapshot_name = optarg;
@@ -1211,7 +1246,8 @@ static int img_convert(int argc, char **argv)
             sval = strtosz_suffix(optarg, &end, STRTOSZ_DEFSUFFIX_B);
             if (sval < 0 || *end) {
                 error_report("Invalid minimum zero buffer size for sparse output specified");
-                return 1;
+                ret = -1;
+                goto out;
             }
 
             min_sparse = sval / BDRV_SECTOR_SIZE;
@@ -1237,20 +1273,18 @@ static int img_convert(int argc, char **argv)
     }
 
     bs_n = argc - optind - 1;
-    if (bs_n < 1) {
-        help();
-    }
-
-    out_filename = argv[argc - 1];
+    out_filename = bs_n >= 1 ? argv[argc - 1] : NULL;
 
-    /* Initialize before goto out */
-    qemu_progress_init(progress, 1.0);
-
-    if (options && is_help_option(options)) {
+    if (options && has_help_option(options)) {
         ret = print_block_option_help(out_filename, out_fmt);
         goto out;
     }
 
+    if (bs_n < 1) {
+        help();
+    }
+
+
     if (bs_n > 1 && out_baseimg) {
         error_report("-B makes no sense when concatenating multiple input "
                      "images");
@@ -1639,6 +1673,7 @@ out:
     free_option_parameters(create_options);
     free_option_parameters(param);
     qemu_vfree(buf);
+    g_free(options);
     if (sn_opts) {
         qemu_opts_del(sn_opts);
     }
@@ -2314,7 +2349,7 @@ static int img_rebase(int argc, char **argv)
 
         bs_old_backing = bdrv_new("old_backing");
         bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
-        ret = bdrv_open(bs_old_backing, backing_name, NULL, BDRV_O_FLAGS,
+        ret = bdrv_open(&bs_old_backing, backing_name, NULL, NULL, BDRV_O_FLAGS,
                         old_backing_drv, &local_err);
         if (ret) {
             error_report("Could not open old backing file '%s': %s",
@@ -2324,8 +2359,8 @@ static int img_rebase(int argc, char **argv)
         }
         if (out_baseimg[0]) {
             bs_new_backing = bdrv_new("new_backing");
-            ret = bdrv_open(bs_new_backing, out_baseimg, NULL, BDRV_O_FLAGS,
-                        new_backing_drv, &local_err);
+            ret = bdrv_open(&bs_new_backing, out_baseimg, NULL, NULL,
+                            BDRV_O_FLAGS, new_backing_drv, &local_err);
             if (ret) {
                 error_report("Could not open new backing file '%s': %s",
                              out_baseimg, error_get_pretty(local_err));
@@ -2637,7 +2672,18 @@ static int img_amend(int argc, char **argv)
                 help();
                 break;
             case 'o':
-                options = optarg;
+                if (!is_valid_option_list(optarg)) {
+                    error_report("Invalid option list: %s", optarg);
+                    ret = -1;
+                    goto out;
+                }
+                if (!options) {
+                    options = g_strdup(optarg);
+                } else {
+                    char *old_options = options;
+                    options = g_strdup_printf("%s,%s", options, optarg);
+                    g_free(old_options);
+                }
                 break;
             case 'f':
                 fmt = optarg;
@@ -2648,15 +2694,21 @@ static int img_amend(int argc, char **argv)
         }
     }
 
-    if (optind != argc - 1) {
+    if (!options) {
         help();
     }
 
-    if (!options) {
-        help();
+    filename = (optind == argc - 1) ? argv[argc - 1] : NULL;
+    if (fmt && has_help_option(options)) {
+        /* If a format is explicitly specified (and possibly no filename is
+         * given), print option help here */
+        ret = print_block_option_help(filename, fmt);
+        goto out;
     }
 
-    filename = argv[argc - 1];
+    if (optind != argc - 1) {
+        help();
+    }
 
     bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, quiet);
     if (!bs) {
@@ -2667,7 +2719,8 @@ static int img_amend(int argc, char **argv)
 
     fmt = bs->drv->format_name;
 
-    if (is_help_option(options)) {
+    if (has_help_option(options)) {
+        /* If the format was auto-detected, print option help here */
         ret = print_block_option_help(filename, fmt);
         goto out;
     }
@@ -2694,6 +2747,8 @@ out:
     }
     free_option_parameters(create_options);
     free_option_parameters(options_param);
+    g_free(options);
+
     if (ret) {
         return 1;
     }
@@ -2719,6 +2774,7 @@ int main(int argc, char **argv)
 #endif
 
     error_set_progname(argv[0]);
+    qemu_init_exec_dir(argv[0]);
 
     qemu_init_main_loop();
     bdrv_init();
diff --git a/qemu-io.c b/qemu-io.c
index 7f459d8ffe..fc3860884c 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -59,7 +59,9 @@ static int openfile(char *name, int flags, int growable, QDict *opts)
     }
 
     if (growable) {
-        if (bdrv_file_open(&qemuio_bs, name, NULL, opts, flags, &local_err)) {
+        if (bdrv_open(&qemuio_bs, name, NULL, opts, flags | BDRV_O_PROTOCOL,
+                      NULL, &local_err))
+        {
             fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
                     error_get_pretty(local_err));
             error_free(local_err);
@@ -68,7 +70,9 @@ static int openfile(char *name, int flags, int growable, QDict *opts)
     } else {
         qemuio_bs = bdrv_new("hda");
 
-        if (bdrv_open(qemuio_bs, name, opts, flags, NULL, &local_err) < 0) {
+        if (bdrv_open(&qemuio_bs, name, NULL, opts, flags, NULL, &local_err)
+            < 0)
+        {
             fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
                     error_get_pretty(local_err));
             error_free(local_err);
@@ -381,6 +385,7 @@ int main(int argc, char **argv)
 #endif
 
     progname = basename(argv[0]);
+    qemu_init_exec_dir(argv[0]);
 
     while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) {
         switch (c) {
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 136e8c9c05..bdac1f3f1f 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -20,6 +20,8 @@
 #include "block/block.h"
 #include "block/nbd.h"
 #include "qemu/main-loop.h"
+#include "qemu/sockets.h"
+#include "qemu/error-report.h"
 #include "block/snapshot.h"
 
 #include <stdarg.h>
@@ -201,6 +203,56 @@ static void termsig_handler(int signum)
     qemu_notify_event();
 }
 
+static void combine_addr(char *buf, size_t len, const char* address,
+                         uint16_t port)
+{
+    /* If the address-part contains a colon, it's an IPv6 IP so needs [] */
+    if (strstr(address, ":")) {
+        snprintf(buf, len, "[%s]:%u", address, port);
+    } else {
+        snprintf(buf, len, "%s:%u", address, port);
+    }
+}
+
+static int tcp_socket_incoming(const char *address, uint16_t port)
+{
+    char address_and_port[128];
+    Error *local_err = NULL;
+
+    combine_addr(address_and_port, 128, address, port);
+    int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err);
+
+    if (local_err != NULL) {
+        qerror_report_err(local_err);
+        error_free(local_err);
+    }
+    return fd;
+}
+
+static int unix_socket_incoming(const char *path)
+{
+    Error *local_err = NULL;
+    int fd = unix_listen(path, NULL, 0, &local_err);
+
+    if (local_err != NULL) {
+        qerror_report_err(local_err);
+        error_free(local_err);
+    }
+    return fd;
+}
+
+static int unix_socket_outgoing(const char *path)
+{
+    Error *local_err = NULL;
+    int fd = unix_connect(path, &local_err);
+
+    if (local_err != NULL) {
+        qerror_report_err(local_err);
+        error_free(local_err);
+    }
+    return fd;
+}
+
 static void *show_parts(void *arg)
 {
     char *device = arg;
@@ -376,6 +428,7 @@ int main(int argc, char **argv)
     memset(&sa_sigterm, 0, sizeof(sa_sigterm));
     sa_sigterm.sa_handler = termsig_handler;
     sigaction(SIGTERM, &sa_sigterm, NULL);
+    qemu_init_exec_dir(argv[0]);
 
     while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
         switch (ch) {
@@ -597,7 +650,7 @@ int main(int argc, char **argv)
 
     bs = bdrv_new("hda");
     srcpath = argv[optind];
-    ret = bdrv_open(bs, srcpath, NULL, flags, drv, &local_err);
+    ret = bdrv_open(&bs, srcpath, NULL, NULL, flags, drv, &local_err);
     if (ret < 0) {
         errno = -ret;
         err(EXIT_FAILURE, "Failed to bdrv_open '%s': %s", argv[optind],
diff --git a/qga/channel-win32.c b/qga/channel-win32.c
index 8a303f35ec..0d5e5f511f 100644
--- a/qga/channel-win32.c
+++ b/qga/channel-win32.c
@@ -287,12 +287,22 @@ GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size)
 static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
                                 const gchar *path)
 {
-    if (method != GA_CHANNEL_VIRTIO_SERIAL) {
+    COMMTIMEOUTS comTimeOut = {0};
+    gchar newpath[MAXPATHLEN] = {0};
+    comTimeOut.ReadIntervalTimeout = 1;
+
+    if (method != GA_CHANNEL_VIRTIO_SERIAL && method != GA_CHANNEL_ISA_SERIAL) {
         g_critical("unsupported communication method");
         return false;
     }
 
-    c->handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+    if (method == GA_CHANNEL_ISA_SERIAL){
+        snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path);
+    }else {
+        g_strlcpy(newpath, path, sizeof(newpath));
+    }
+
+    c->handle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                            OPEN_EXISTING,
                            FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
     if (c->handle == INVALID_HANDLE_VALUE) {
@@ -300,6 +310,12 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
         return false;
     }
 
+    if (method == GA_CHANNEL_ISA_SERIAL && !SetCommTimeouts(c->handle,&comTimeOut)) {
+        g_critical("error setting timeout for com port: %lu",GetLastError());
+        CloseHandle(c->handle);
+        return false;
+    }
+
     return true;
 }
 
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index cae41716ca..6b5f11f83f 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -142,7 +142,7 @@ int64_t qmp_guest_get_time(Error **errp)
    return time_ns;
 }
 
-void qmp_guest_set_time(int64_t time_ns, Error **errp)
+void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
 {
     int ret;
     int status;
@@ -150,22 +150,28 @@ void qmp_guest_set_time(int64_t time_ns, Error **errp)
     Error *local_err = NULL;
     struct timeval tv;
 
-    /* year-2038 will overflow in case time_t is 32bit */
-    if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
-        error_setg(errp, "Time %" PRId64 " is too large", time_ns);
-        return;
-    }
+    /* If user has passed a time, validate and set it. */
+    if (has_time) {
+        /* year-2038 will overflow in case time_t is 32bit */
+        if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
+            error_setg(errp, "Time %" PRId64 " is too large", time_ns);
+            return;
+        }
 
-    tv.tv_sec = time_ns / 1000000000;
-    tv.tv_usec = (time_ns % 1000000000) / 1000;
+        tv.tv_sec = time_ns / 1000000000;
+        tv.tv_usec = (time_ns % 1000000000) / 1000;
 
-    ret = settimeofday(&tv, NULL);
-    if (ret < 0) {
-        error_setg_errno(errp, errno, "Failed to set time to guest");
-        return;
+        ret = settimeofday(&tv, NULL);
+        if (ret < 0) {
+            error_setg_errno(errp, errno, "Failed to set time to guest");
+            return;
+        }
     }
 
-    /* Set the Hardware Clock to the current System Time. */
+    /* Now, if user has passed a time to set and the system time is set, we
+     * just need to synchronize the hardware clock. However, if no time was
+     * passed, user is requesting the opposite: set the system time from the
+     * hardware clock. */
     pid = fork();
     if (pid == 0) {
         setsid();
@@ -173,7 +179,10 @@ void qmp_guest_set_time(int64_t time_ns, Error **errp)
         reopen_fd_to_null(1);
         reopen_fd_to_null(2);
 
-        execle("/sbin/hwclock", "hwclock", "-w", NULL, environ);
+        /* Use '/sbin/hwclock -w' to set RTC from the system time,
+         * or '/sbin/hwclock -s' to set the system time from RTC. */
+        execle("/sbin/hwclock", "hwclock", has_time ? "-w" : "-s",
+               NULL, environ);
         _exit(EXIT_FAILURE);
     } else if (pid < 0) {
         error_setg_errno(errp, errno, "failed to create child process");
@@ -525,7 +534,7 @@ struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
     if (ret == -1) {
         error_setg_errno(err, errno, "failed to seek file");
     } else {
-        seek_data = g_malloc0(sizeof(GuestFileRead));
+        seek_data = g_new0(GuestFileSeek, 1);
         seek_data->position = ftell(fh);
         seek_data->eof = feof(fh);
     }
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 50094ddfd3..0ee07b6e23 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -370,25 +370,37 @@ int64_t qmp_guest_get_time(Error **errp)
     return time_ns;
 }
 
-void qmp_guest_set_time(int64_t time_ns, Error **errp)
+void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
 {
     SYSTEMTIME ts;
     FILETIME tf;
     LONGLONG time;
 
-    if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) {
-        error_setg(errp, "Time %" PRId64 "is invalid", time_ns);
-        return;
-    }
+    if (has_time) {
+        /* Okay, user passed a time to set. Validate it. */
+        if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) {
+            error_setg(errp, "Time %" PRId64 "is invalid", time_ns);
+            return;
+        }
 
-    time = time_ns / 100 + W32_FT_OFFSET;
+        time = time_ns / 100 + W32_FT_OFFSET;
 
-    tf.dwLowDateTime = (DWORD) time;
-    tf.dwHighDateTime = (DWORD) (time >> 32);
+        tf.dwLowDateTime = (DWORD) time;
+        tf.dwHighDateTime = (DWORD) (time >> 32);
 
-    if (!FileTimeToSystemTime(&tf, &ts)) {
-        error_setg(errp, "Failed to convert system time %d", (int)GetLastError());
-        return;
+        if (!FileTimeToSystemTime(&tf, &ts)) {
+            error_setg(errp, "Failed to convert system time %d",
+                       (int)GetLastError());
+            return;
+        }
+    } else {
+        /* Otherwise read the time from RTC which contains the correct value.
+         * Hopefully. */
+        GetSystemTime(&ts);
+        if (ts.wYear < 1601 || ts.wYear > 30827) {
+            error_setg(errp, "Failed to get time");
+            return;
+        }
     }
 
     acquire_privilege(SE_SYSTEMTIME_NAME, errp);
diff --git a/qga/main.c b/qga/main.c
index c58b26a9a0..cfca291b43 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -47,9 +47,11 @@
 #ifndef _WIN32
 #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
 #define QGA_STATE_RELATIVE_DIR  "run"
+#define QGA_SERIAL_PATH_DEFAULT "/dev/ttyS0"
 #else
 #define QGA_VIRTIO_PATH_DEFAULT "\\\\.\\Global\\org.qemu.guest_agent.0"
 #define QGA_STATE_RELATIVE_DIR  "qemu-ga"
+#define QGA_SERIAL_PATH_DEFAULT "COM1"
 #endif
 #ifdef CONFIG_FSFREEZE
 #define QGA_FSFREEZE_HOOK_DEFAULT CONFIG_QEMU_CONFDIR "/fsfreeze-hook"
@@ -189,6 +191,8 @@ static void usage(const char *cmd)
 "  -m, --method      transport method: one of unix-listen, virtio-serial, or\n"
 "                    isa-serial (virtio-serial is the default)\n"
 "  -p, --path        device/socket path (the default for virtio-serial is:\n"
+"                    %s,\n"
+"                    the default for isa-serial is:\n"
 "                    %s)\n"
 "  -l, --logfile     set logfile path, logs to stderr by default\n"
 "  -f, --pidfile     specify pidfile (default is %s)\n"
@@ -215,7 +219,8 @@ static void usage(const char *cmd)
 "  -h, --help        display this help and exit\n"
 "\n"
 "Report bugs to <mdroth@linux.vnet.ibm.com>\n"
-    , cmd, QEMU_VERSION, QGA_VIRTIO_PATH_DEFAULT, dfl_pathnames.pidfile,
+    , cmd, QEMU_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT,
+    dfl_pathnames.pidfile,
 #ifdef CONFIG_FSFREEZE
     QGA_FSFREEZE_HOOK_DEFAULT,
 #endif
@@ -659,12 +664,16 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path)
     }
 
     if (path == NULL) {
-        if (strcmp(method, "virtio-serial") != 0) {
+        if (strcmp(method, "virtio-serial") == 0 ) {
+            /* try the default path for the virtio-serial port */
+            path = QGA_VIRTIO_PATH_DEFAULT;
+        } else if (strcmp(method, "isa-serial") == 0){
+            /* try the default path for the serial port - COM1 */
+            path = QGA_SERIAL_PATH_DEFAULT;
+        } else {
             g_critical("must specify a path for this channel");
             return false;
         }
-        /* try the default path for the virtio-serial port */
-        path = QGA_VIRTIO_PATH_DEFAULT;
     }
 
     if (strcmp(method, "virtio-serial") == 0) {
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 245f968bc2..80edca143a 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -120,17 +120,18 @@
 # This command tries to set guest time to the given value,
 # then sets the Hardware Clock to the current System Time.
 # This will make it easier for a guest to resynchronize
-# without waiting for NTP.
+# without waiting for NTP. If no @time is specified, then
+# the time to set is read from RTC.
 #
-# @time: time of nanoseconds, relative to the Epoch of
-#        1970-01-01 in UTC.
+# @time: #optional time of nanoseconds, relative to the Epoch
+#        of 1970-01-01 in UTC.
 #
 # Returns: Nothing on success.
 #
 # Since: 1.5
 ##
 { 'command': 'guest-set-time',
-  'data': { 'time': 'int' } }
+  'data': { '*time': 'int' } }
 
 ##
 # @GuestAgentCommandInfo:
diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp
index bf42b5e95f..d5129f8f65 100644
--- a/qga/vss-win32/provider.cpp
+++ b/qga/vss-win32/provider.cpp
@@ -278,7 +278,9 @@ STDMETHODIMP CQGAVssProvider::DeleteSnapshots(
     VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType,
     BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID)
 {
-    return E_NOTIMPL;
+    *plDeletedSnapshots = 0;
+    *pNondeletedSnapshotID = SourceObjectId;
+    return S_OK;
 }
 
 STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot(
@@ -291,8 +293,17 @@ STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot(
 STDMETHODIMP CQGAVssProvider::IsVolumeSupported(
     VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider)
 {
-    *pbSupportedByThisProvider = TRUE;
+    HANDLE hEventFrozen;
 
+    /* Check if a requester is qemu-ga by whether an event is created */
+    hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
+    if (!hEventFrozen) {
+        *pbSupportedByThisProvider = FALSE;
+        return S_OK;
+    }
+    CloseHandle(hEventFrozen);
+
+    *pbSupportedByThisProvider = TRUE;
     return S_OK;
 }
 
@@ -342,18 +353,18 @@ STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId)
     HANDLE hEventFrozen, hEventThaw, hEventTimeout;
 
     hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
-    if (hEventFrozen == INVALID_HANDLE_VALUE) {
+    if (!hEventFrozen) {
         return E_FAIL;
     }
 
     hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
-    if (hEventThaw == INVALID_HANDLE_VALUE) {
+    if (!hEventThaw) {
         CloseHandle(hEventFrozen);
         return E_FAIL;
     }
 
     hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT);
-    if (hEventTimeout == INVALID_HANDLE_VALUE) {
+    if (!hEventTimeout) {
         CloseHandle(hEventFrozen);
         CloseHandle(hEventThaw);
         return E_FAIL;
diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp
index 1e8dd3dfa8..922e74ddfc 100644
--- a/qga/vss-win32/requester.cpp
+++ b/qga/vss-win32/requester.cpp
@@ -50,10 +50,6 @@ static struct QGAVSSContext {
 
 STDAPI requester_init(void)
 {
-    vss_ctx.hEventFrozen =  INVALID_HANDLE_VALUE;
-    vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
-    vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE;
-
     COMInitializer initializer; /* to call CoInitializeSecurity */
     HRESULT hr = CoInitializeSecurity(
         NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
@@ -94,17 +90,17 @@ STDAPI requester_init(void)
 
 static void requester_cleanup(void)
 {
-    if (vss_ctx.hEventFrozen != INVALID_HANDLE_VALUE) {
+    if (vss_ctx.hEventFrozen) {
         CloseHandle(vss_ctx.hEventFrozen);
-        vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE;
+        vss_ctx.hEventFrozen = NULL;
     }
-    if (vss_ctx.hEventThaw != INVALID_HANDLE_VALUE) {
+    if (vss_ctx.hEventThaw) {
         CloseHandle(vss_ctx.hEventThaw);
-        vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
+        vss_ctx.hEventThaw = NULL;
     }
-    if (vss_ctx.hEventTimeout != INVALID_HANDLE_VALUE) {
+    if (vss_ctx.hEventTimeout) {
         CloseHandle(vss_ctx.hEventTimeout);
-        vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE;
+        vss_ctx.hEventTimeout = NULL;
     }
     if (vss_ctx.pAsyncSnapshot) {
         vss_ctx.pAsyncSnapshot->Release();
@@ -256,6 +252,32 @@ void requester_freeze(int *num_vols, ErrorSet *errset)
 
     CoInitialize(NULL);
 
+    /* Allow unrestricted access to events */
+    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
+    SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
+    sa.nLength = sizeof(sa);
+    sa.lpSecurityDescriptor = &sd;
+    sa.bInheritHandle = FALSE;
+
+    vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
+    if (!vss_ctx.hEventFrozen) {
+        err_set(errset, GetLastError(), "failed to create event %s",
+                EVENT_NAME_FROZEN);
+        goto out;
+    }
+    vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW);
+    if (!vss_ctx.hEventThaw) {
+        err_set(errset, GetLastError(), "failed to create event %s",
+                EVENT_NAME_THAW);
+        goto out;
+    }
+    vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT);
+    if (!vss_ctx.hEventTimeout) {
+        err_set(errset, GetLastError(), "failed to create event %s",
+                EVENT_NAME_TIMEOUT);
+        goto out;
+    }
+
     assert(pCreateVssBackupComponents != NULL);
     hr = pCreateVssBackupComponents(&vss_ctx.pVssbc);
     if (FAILED(hr)) {
@@ -366,32 +388,6 @@ void requester_freeze(int *num_vols, ErrorSet *errset)
         goto out;
     }
 
-    /* Allow unrestricted access to events */
-    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
-    SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
-    sa.nLength = sizeof(sa);
-    sa.lpSecurityDescriptor = &sd;
-    sa.bInheritHandle = FALSE;
-
-    vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
-    if (vss_ctx.hEventFrozen == INVALID_HANDLE_VALUE) {
-        err_set(errset, GetLastError(), "failed to create event %s",
-                EVENT_NAME_FROZEN);
-        goto out;
-    }
-    vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW);
-    if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) {
-        err_set(errset, GetLastError(), "failed to create event %s",
-                EVENT_NAME_THAW);
-        goto out;
-    }
-    vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT);
-    if (vss_ctx.hEventTimeout == INVALID_HANDLE_VALUE) {
-        err_set(errset, GetLastError(), "failed to create event %s",
-                EVENT_NAME_TIMEOUT);
-        goto out;
-    }
-
     /*
      * Start VSS quiescing operations.
      * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
@@ -443,7 +439,7 @@ void requester_thaw(int *num_vols, ErrorSet *errset)
 {
     COMPointer<IVssAsync> pAsync;
 
-    if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) {
+    if (!vss_ctx.hEventThaw) {
         /*
          * In this case, DoSnapshotSet is aborted or not started,
          * and no volumes must be frozen. We return without an error.
diff --git a/qobject/qdict.c b/qobject/qdict.c
index a3924f24bd..42ec4c0d2c 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -597,18 +597,33 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start)
     }
 }
 
+static bool qdict_has_prefixed_entries(const QDict *src, const char *start)
+{
+    const QDictEntry *entry;
+
+    for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) {
+        if (strstart(entry->key, start, NULL)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 /**
  * qdict_array_split(): This function moves array-like elements of a QDict into
- * a new QList of QDicts. Every entry in the original QDict with a key prefixed
- * "%u.", where %u designates an unsigned integer starting at 0 and
+ * a new QList. Every entry in the original QDict with a key "%u" or one
+ * prefixed "%u.", where %u designates an unsigned integer starting at 0 and
  * incrementally counting up, will be moved to a new QDict at index %u in the
- * output QList with the key prefix removed. The function terminates when there
- * is no entry in the QDict with a prefix directly (incrementally) following the
- * last one.
- * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "3.y": 1, "o.o": 7}
- *      (or {"1.x": 0, "3.y": 1, "0.a": 42, "o.o": 7, "0.b": 23})
- *       => [{"a": 42, "b": 23}, {"x": 0}]
- *      and {"3.y": 1, "o.o": 7} (remainder of the old QDict)
+ * output QList with the key prefix removed, if that prefix is "%u.". If the
+ * whole key is just "%u", the whole QObject will be moved unchanged without
+ * creating a new QDict. The function terminates when there is no entry in the
+ * QDict with a prefix directly (incrementally) following the last one; it also
+ * returns if there are both entries with "%u" and "%u." for the same index %u.
+ * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66}
+ *      (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66})
+ *       => [{"a": 42, "b": 23}, {"x": 0}, 66]
+ *      and {"4.y": 1, "o.o": 7} (remainder of the old QDict)
  */
 void qdict_array_split(QDict *src, QList **dst)
 {
@@ -617,19 +632,36 @@ void qdict_array_split(QDict *src, QList **dst)
     *dst = qlist_new();
 
     for (i = 0; i < UINT_MAX; i++) {
+        QObject *subqobj;
+        bool is_subqdict;
         QDict *subqdict;
-        char prefix[32];
+        char indexstr[32], prefix[32];
         size_t snprintf_ret;
 
+        snprintf_ret = snprintf(indexstr, 32, "%u", i);
+        assert(snprintf_ret < 32);
+
+        subqobj = qdict_get(src, indexstr);
+
         snprintf_ret = snprintf(prefix, 32, "%u.", i);
         assert(snprintf_ret < 32);
 
-        qdict_extract_subqdict(src, &subqdict, prefix);
-        if (!qdict_size(subqdict)) {
-            QDECREF(subqdict);
+        is_subqdict = qdict_has_prefixed_entries(src, prefix);
+
+        // There may be either a single subordinate object (named "%u") or
+        // multiple objects (each with a key prefixed "%u."), but not both.
+        if (!subqobj == !is_subqdict) {
             break;
         }
 
-        qlist_append_obj(*dst, QOBJECT(subqdict));
+        if (is_subqdict) {
+            qdict_extract_subqdict(src, &subqdict, prefix);
+            assert(qdict_size(subqdict) > 0);
+        } else {
+            qobject_incref(subqobj);
+            qdict_del(src, indexstr);
+        }
+
+        qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict));
     }
 }
diff --git a/rules.mak b/rules.mak
index 391d6eb8e6..9dda9f760f 100644
--- a/rules.mak
+++ b/rules.mak
@@ -22,8 +22,15 @@ QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d
 # Same as -I$(SRC_PATH) -I., but for the nested source/object directories
 QEMU_INCLUDES += -I$(<D) -I$(@D)
 
+maybe-add = $(filter-out $1, $2) $1
+extract-libs = $(strip $(sort $(foreach o,$1,$($o-libs)) \
+                  $(foreach o,$(call expand-objs,$1),$($o-libs))))
+expand-objs = $(strip $(sort $(filter %.o,$1)) \
+                  $(foreach o,$(filter %.mo,$1),$($o-objs)) \
+                  $(filter-out %.o %.mo,$1))
+
 %.o: %.c
-	$(call quiet-command,$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<,"  CC    $(TARGET_DIR)$@")
+	$(call quiet-command,$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) -c -o $@ $<,"  CC    $(TARGET_DIR)$@")
 %.o: %.rc
 	$(call quiet-command,$(WINDRES) -I. -o $@ $<,"  RC    $(TARGET_DIR)$@")
 
@@ -33,8 +40,8 @@ LINKPROG = $(or $(CXX),$(CC))
 
 ifeq ($(LIBTOOL),)
 LINK = $(call quiet-command,$(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \
-       $(sort $(filter %.o, $1)) $(filter-out %.o, $1) $(version-obj-y) \
-       $(LIBS),"  LINK  $(TARGET_DIR)$@")
+       $(call expand-objs,$1) $(version-obj-y) \
+       $(call extract-libs,$1) $(LIBS),"  LINK  $(TARGET_DIR)$@")
 else
 LIBTOOL += $(if $(V),,--quiet)
 %.lo: %.c
@@ -45,12 +52,12 @@ LIBTOOL += $(if $(V),,--quiet)
 	$(call quiet-command,$(LIBTOOL) --mode=compile --tag=CC dtrace -o $@ -G -s $<, " lt GEN $(TARGET_DIR)$@")
 
 LINK = $(call quiet-command,\
-       $(if $(filter %.lo %.la,$^),$(LIBTOOL) --mode=link --tag=CC \
+       $(if $(filter %.lo %.la,$1),$(LIBTOOL) --mode=link --tag=CC \
        )$(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \
-       $(sort $(filter %.o, $1)) $(filter-out %.o, $1) \
-       $(if $(filter %.lo %.la,$^),$(version-lobj-y),$(version-obj-y)) \
-       $(if $(filter %.lo %.la,$^),$(LIBTOOLFLAGS)) \
-       $(LIBS),$(if $(filter %.lo %.la,$^),"lt LINK ", "  LINK  ")"$(TARGET_DIR)$@")
+       $(call expand-objs,$1) \
+       $(if $(filter %.lo %.la,$1),$(version-lobj-y),$(version-obj-y)) \
+       $(if $(filter %.lo %.la,$1),$(LIBTOOLFLAGS)) \
+       $(call extract-libs,$1) $(LIBS),$(if $(filter %.lo %.la,$1),"lt LINK ", "  LINK  ")"$(TARGET_DIR)$@")
 endif
 
 %.asm: %.S
@@ -71,6 +78,16 @@ endif
 %.o: %.dtrace
 	$(call quiet-command,dtrace -o $@ -G -s $<, "  GEN   $(TARGET_DIR)$@")
 
+DSO_CFLAGS := -fPIC -DBUILD_DSO
+%$(DSOSUF): LDFLAGS += $(LDFLAGS_SHARED)
+%$(DSOSUF): %.mo libqemustub.a
+	$(call LINK,$^)
+	@# Copy to build root so modules can be loaded when program started without install
+	$(if $(findstring /,$@),$(call quiet-command,cp $@ $(subst /,-,$@), "  CP    $(subst /,-,$@)"))
+
+.PHONY: modules
+modules:
+
 %$(EXESUF): %.o
 	$(call LINK,$^)
 
@@ -146,9 +163,6 @@ clean: clean-timestamp
 
 # magic to descend into other directories
 
-obj := .
-old-nested-dirs :=
-
 define push-var
 $(eval save-$2-$1 = $(value $1))
 $(eval $1 :=)
@@ -160,11 +174,27 @@ $(eval $1 = $(value save-$2-$1) $$(subdir-$2-$1))
 $(eval save-$2-$1 :=)
 endef
 
+define fix-obj-vars
+$(foreach v,$($1), \
+	$(if $($v-cflags), \
+		$(eval $2$v-cflags := $($v-cflags)) \
+		$(eval $v-cflags := )) \
+	$(if $($v-libs), \
+		$(eval $2$v-libs := $($v-libs)) \
+		$(eval $v-libs := )) \
+	$(if $($v-objs), \
+		$(eval $2$v-objs := $(addprefix $2,$($v-objs))) \
+		$(eval $v-objs := )))
+endef
+
 define unnest-dir
 $(foreach var,$(nested-vars),$(call push-var,$(var),$1/))
-$(eval obj := $(obj)/$1)
+$(eval obj-parent-$1 := $(obj))
+$(eval obj := $(if $(obj),$(obj)/$1,$1))
 $(eval include $(SRC_PATH)/$1/Makefile.objs)
-$(eval obj := $(patsubst %/$1,%,$(obj)))
+$(foreach v,$(nested-vars),$(call fix-obj-vars,$v,$(if $(obj),$(obj)/)))
+$(eval obj := $(obj-parent-$1))
+$(eval obj-parent-$1 := )
 $(foreach var,$(nested-vars),$(call pop-var,$(var),$1/))
 endef
 
@@ -178,10 +208,34 @@ $(if $(nested-dirs),
   $(call unnest-vars-1))
 endef
 
+define process-modules
+$(foreach o,$(filter %.o,$($1)),
+	$(eval $(patsubst %.o,%.mo,$o): $o) \
+	$(eval $(patsubst %.o,%.mo,$o)-objs := $o))
+$(foreach o,$(filter-out $(modules-m), $(patsubst %.o,%.mo,$($1))), \
+    $(eval $o-objs += module-common.o)
+    $(eval $o: $($o-objs))
+    $(eval modules-objs-m += $($o-objs))
+    $(eval modules-m += $o)
+    $(eval $o:; $$(call quiet-command,touch $$@,"  GEN   $$(TARGET_DIR)$$@"))
+    $(if $(CONFIG_MODULES),$(eval modules: $(patsubst %.mo,%$(DSOSUF),$o)))) \
+$(eval modules-objs-m := $(sort $(modules-objs-m)))
+$(foreach o,$(modules-objs-m), \
+    $(if $(CONFIG_MODULES),$(eval $o-cflags := $(call maybe-add, $(DSO_CFLAGS), $($o-cflags)))))
+$(eval $(patsubst %-m,%-$(call lnot,$(CONFIG_MODULES)),$1) += $($1))
+endef
+
 define unnest-vars
+$(eval obj := $1)
+$(eval nested-vars := $2)
+$(eval old-nested-dirs := )
 $(call unnest-vars-1)
+$(if $1,$(foreach v,$(nested-vars),$(eval \
+	$v := $(addprefix $1/,$($v)))))
 $(foreach var,$(nested-vars),$(eval $(var) := $(filter-out %/, $($(var)))))
 $(shell mkdir -p $(sort $(foreach var,$(nested-vars),$(dir $($(var))))))
 $(foreach var,$(nested-vars), $(eval \
   -include $(addsuffix *.d, $(sort $(dir $($(var)))))))
+$(foreach v,$(filter %-m,$(nested-vars)), \
+    $(call process-modules,$v))
 endef
diff --git a/scripts/create_config b/scripts/create_config
index 06f5316d9d..546f889144 100755
--- a/scripts/create_config
+++ b/scripts/create_config
@@ -108,6 +108,9 @@ case $line in
     value=${line#*=}
     echo "#define $name $value"
     ;;
+ DSOSUF=*)
+    echo "#define HOST_DSOSUF \"${line#*=}\""
+    ;;
 esac
 
 done # read
diff --git a/target-xtensa/core-dc232b.c b/target-xtensa/core-dc232b.c
index 0bfcf2414c..c51e11e6d7 100644
--- a/target-xtensa/core-dc232b.c
+++ b/target-xtensa/core-dc232b.c
@@ -35,7 +35,6 @@
 
 static const XtensaConfig dc232b = {
     .name = "dc232b",
-    .options = XTENSA_OPTIONS,
     .gdb_regmap = {
         .num_regs = 120,
         .num_core_regs = 52,
@@ -43,13 +42,8 @@ static const XtensaConfig dc232b = {
 #include "core-dc232b/gdb-config.c"
         }
     },
-    .nareg = XCHAL_NUM_AREGS,
-    .ndepc = 1,
-    EXCEPTIONS_SECTION,
-    INTERRUPTS_SECTION,
-    TLB_SECTION,
-    DEBUG_SECTION,
     .clock_freq_khz = 10000,
+    DEFAULT_SECTIONS
 };
 
 REGISTER_CORE(dc232b)
diff --git a/target-xtensa/core-dc233c.c b/target-xtensa/core-dc233c.c
index 738d543e53..42dd64f031 100644
--- a/target-xtensa/core-dc233c.c
+++ b/target-xtensa/core-dc233c.c
@@ -36,7 +36,6 @@
 
 static const XtensaConfig dc233c = {
     .name = "dc233c",
-    .options = XTENSA_OPTIONS,
     .gdb_regmap = {
         .num_regs = 121,
         .num_core_regs = 52,
@@ -44,13 +43,8 @@ static const XtensaConfig dc233c = {
 #include "core-dc233c/gdb-config.c"
         }
     },
-    .nareg = XCHAL_NUM_AREGS,
-    .ndepc = 1,
-    EXCEPTIONS_SECTION,
-    INTERRUPTS_SECTION,
-    TLB_SECTION,
-    DEBUG_SECTION,
     .clock_freq_khz = 10000,
+    DEFAULT_SECTIONS
 };
 
 REGISTER_CORE(dc233c)
diff --git a/target-xtensa/core-fsf.c b/target-xtensa/core-fsf.c
index d4660edde9..6859bee062 100644
--- a/target-xtensa/core-fsf.c
+++ b/target-xtensa/core-fsf.c
@@ -35,15 +35,9 @@
 
 static const XtensaConfig fsf = {
     .name = "fsf",
-    .options = XTENSA_OPTIONS,
     /* GDB for this core is not supported currently */
-    .nareg = XCHAL_NUM_AREGS,
-    .ndepc = 1,
-    EXCEPTIONS_SECTION,
-    INTERRUPTS_SECTION,
-    TLB_SECTION,
-    DEBUG_SECTION,
     .clock_freq_khz = 10000,
+    DEFAULT_SECTIONS
 };
 
 REGISTER_CORE(fsf)
diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c
index c19d17ad04..749e20580f 100644
--- a/target-xtensa/cpu.c
+++ b/target-xtensa/cpu.c
@@ -59,6 +59,8 @@ static void xtensa_cpu_reset(CPUState *s)
     env->sregs[CACHEATTR] = 0x22222222;
     env->sregs[ATOMCTL] = xtensa_option_enabled(env->config,
             XTENSA_OPTION_ATOMCTL) ? 0x28 : 0x15;
+    env->sregs[CONFIGID0] = env->config->configid[0];
+    env->sregs[CONFIGID1] = env->config->configid[1];
 
     env->pending_irq_level = 0;
     reset_mmu(env);
diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h
index 95103e9e87..1cf5ea3aff 100644
--- a/target-xtensa/cpu.h
+++ b/target-xtensa/cpu.h
@@ -135,9 +135,11 @@ enum {
     IBREAKA = 128,
     DBREAKA = 144,
     DBREAKC = 160,
+    CONFIGID0 = 176,
     EPC1 = 177,
     DEPC = 192,
     EPS2 = 194,
+    CONFIGID1 = 208,
     EXCSAVE1 = 209,
     CPENABLE = 224,
     INTSET = 226,
@@ -321,6 +323,8 @@ typedef struct XtensaConfig {
     unsigned nibreak;
     unsigned ndbreak;
 
+    uint32_t configid[2];
+
     uint32_t clock_freq_khz;
 
     xtensa_tlb itlb;
diff --git a/target-xtensa/helper.h b/target-xtensa/helper.h
index 38d7157f34..322b04cd0a 100644
--- a/target-xtensa/helper.h
+++ b/target-xtensa/helper.h
@@ -25,6 +25,7 @@ DEF_HELPER_2(advance_ccount, void, env, i32)
 DEF_HELPER_1(check_interrupts, void, env)
 DEF_HELPER_3(check_atomctl, void, env, i32, i32)
 
+DEF_HELPER_2(itlb_hit_test, void, env, i32)
 DEF_HELPER_2(wsr_rasid, void, env, i32)
 DEF_HELPER_FLAGS_3(rtlb0, TCG_CALL_NO_RWG_SE, i32, env, i32, i32)
 DEF_HELPER_FLAGS_3(rtlb1, TCG_CALL_NO_RWG_SE, i32, env, i32, i32)
diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c
index 89a72b5678..509ba49d60 100644
--- a/target-xtensa/op_helper.c
+++ b/target-xtensa/op_helper.c
@@ -415,6 +415,11 @@ void HELPER(check_interrupts)(CPUXtensaState *env)
     check_interrupts(env);
 }
 
+void HELPER(itlb_hit_test)(CPUXtensaState *env, uint32_t vaddr)
+{
+    get_page_addr_code(env, vaddr);
+}
+
 /*!
  * Check vaddr accessibility/cache attributes and raise an exception if
  * specified by the ATOMCTL SR.
diff --git a/target-xtensa/overlay_tool.h b/target-xtensa/overlay_tool.h
index dd4f51a7b7..4c0de7f06a 100644
--- a/target-xtensa/overlay_tool.h
+++ b/target-xtensa/overlay_tool.h
@@ -319,6 +319,23 @@
     .nibreak = XCHAL_NUM_IBREAK, \
     .ndbreak = XCHAL_NUM_DBREAK
 
+#define CONFIG_SECTION \
+    .configid = { \
+        XCHAL_HW_CONFIGID0, \
+        XCHAL_HW_CONFIGID1, \
+    }
+
+#define DEFAULT_SECTIONS \
+    .options = XTENSA_OPTIONS, \
+    .nareg = XCHAL_NUM_AREGS, \
+    .ndepc = (XCHAL_XEA_VERSION >= 2), \
+    EXCEPTIONS_SECTION, \
+    INTERRUPTS_SECTION, \
+    TLB_SECTION, \
+    DEBUG_SECTION, \
+    CONFIG_SECTION
+
+
 #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI + 1 <= 2
 #define XCHAL_INTLEVEL2_VECTOR_VADDR 0
 #endif
diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c
index 2d2df33115..9f5895e021 100644
--- a/target-xtensa/translate.c
+++ b/target-xtensa/translate.c
@@ -98,12 +98,15 @@ typedef struct XtensaReg {
 
 #define XTENSA_REG(regname, opt) XTENSA_REG_ACCESS(regname, opt, SR_RWX)
 
-#define XTENSA_REG_BITS(regname, opt) { \
+#define XTENSA_REG_BITS_ACCESS(regname, opt, acc) { \
         .name = (regname), \
         .opt_bits = (opt), \
-        .access = SR_RWX, \
+        .access = (acc), \
     }
 
+#define XTENSA_REG_BITS(regname, opt) \
+    XTENSA_REG_BITS_ACCESS(regname, opt, SR_RWX)
+
 static const XtensaReg sregnames[256] = {
     [LBEG] = XTENSA_REG("LBEG", XTENSA_OPTION_LOOP),
     [LEND] = XTENSA_REG("LEND", XTENSA_OPTION_LOOP),
@@ -134,6 +137,7 @@ static const XtensaReg sregnames[256] = {
     [DBREAKA + 1] = XTENSA_REG("DBREAKA1", XTENSA_OPTION_DEBUG),
     [DBREAKC] = XTENSA_REG("DBREAKC0", XTENSA_OPTION_DEBUG),
     [DBREAKC + 1] = XTENSA_REG("DBREAKC1", XTENSA_OPTION_DEBUG),
+    [CONFIGID0] = XTENSA_REG_BITS_ACCESS("CONFIGID0", XTENSA_OPTION_ALL, SR_R),
     [EPC1] = XTENSA_REG("EPC1", XTENSA_OPTION_EXCEPTION),
     [EPC1 + 1] = XTENSA_REG("EPC2", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT),
     [EPC1 + 2] = XTENSA_REG("EPC3", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT),
@@ -148,6 +152,7 @@ static const XtensaReg sregnames[256] = {
     [EPS2 + 3] = XTENSA_REG("EPS5", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT),
     [EPS2 + 4] = XTENSA_REG("EPS6", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT),
     [EPS2 + 5] = XTENSA_REG("EPS7", XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT),
+    [CONFIGID1] = XTENSA_REG_BITS_ACCESS("CONFIGID1", XTENSA_OPTION_ALL, SR_R),
     [EXCSAVE1] = XTENSA_REG("EXCSAVE1", XTENSA_OPTION_EXCEPTION),
     [EXCSAVE1 + 1] = XTENSA_REG("EXCSAVE2",
             XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT),
@@ -922,6 +927,15 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
 #define RRRN_S RRR_S
 #define RRRN_T RRR_T
 
+#define RRI4_R RRR_R
+#define RRI4_S RRR_S
+#define RRI4_T RRR_T
+#ifdef TARGET_WORDS_BIGENDIAN
+#define RRI4_IMM4 ((b2) & 0xf)
+#else
+#define RRI4_IMM4 (((b2) & 0xf0) >> 4)
+#endif
+
 #define RRI8_R RRR_R
 #define RRI8_S RRR_S
 #define RRI8_T RRR_T
@@ -2226,6 +2240,20 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
             gen_load_store(st32, 2);
             break;
 
+#define gen_dcache_hit_test(w, shift) do { \
+            TCGv_i32 addr = tcg_temp_new_i32(); \
+            TCGv_i32 res = tcg_temp_new_i32(); \
+            gen_window_check1(dc, RRI##w##_S); \
+            tcg_gen_addi_i32(addr, cpu_R[RRI##w##_S], \
+                             RRI##w##_IMM##w << shift); \
+            tcg_gen_qemu_ld8u(res, addr, dc->cring); \
+            tcg_temp_free(addr); \
+            tcg_temp_free(res); \
+        } while (0)
+
+#define gen_dcache_hit_test4() gen_dcache_hit_test(4, 4)
+#define gen_dcache_hit_test8() gen_dcache_hit_test(8, 2)
+
         case 7: /*CACHEc*/
             if (RRI8_T < 8) {
                 HAS_OPTION(XTENSA_OPTION_DCACHE);
@@ -2233,49 +2261,69 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
 
             switch (RRI8_T) {
             case 0: /*DPFRc*/
+                gen_window_check1(dc, RRI8_S);
                 break;
 
             case 1: /*DPFWc*/
+                gen_window_check1(dc, RRI8_S);
                 break;
 
             case 2: /*DPFROc*/
+                gen_window_check1(dc, RRI8_S);
                 break;
 
             case 3: /*DPFWOc*/
+                gen_window_check1(dc, RRI8_S);
                 break;
 
             case 4: /*DHWBc*/
+                gen_dcache_hit_test8();
                 break;
 
             case 5: /*DHWBIc*/
+                gen_dcache_hit_test8();
                 break;
 
             case 6: /*DHIc*/
+                gen_check_privilege(dc);
+                gen_dcache_hit_test8();
                 break;
 
             case 7: /*DIIc*/
+                gen_check_privilege(dc);
+                gen_window_check1(dc, RRI8_S);
                 break;
 
             case 8: /*DCEc*/
                 switch (OP1) {
                 case 0: /*DPFLl*/
                     HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK);
+                    gen_check_privilege(dc);
+                    gen_dcache_hit_test4();
                     break;
 
                 case 2: /*DHUl*/
                     HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK);
+                    gen_check_privilege(dc);
+                    gen_dcache_hit_test4();
                     break;
 
                 case 3: /*DIUl*/
                     HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK);
+                    gen_check_privilege(dc);
+                    gen_window_check1(dc, RRI4_S);
                     break;
 
                 case 4: /*DIWBc*/
                     HAS_OPTION(XTENSA_OPTION_DCACHE);
+                    gen_check_privilege(dc);
+                    gen_window_check1(dc, RRI4_S);
                     break;
 
                 case 5: /*DIWBIc*/
                     HAS_OPTION(XTENSA_OPTION_DCACHE);
+                    gen_check_privilege(dc);
+                    gen_window_check1(dc, RRI4_S);
                     break;
 
                 default: /*reserved*/
@@ -2285,22 +2333,46 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
                 }
                 break;
 
+#undef gen_dcache_hit_test
+#undef gen_dcache_hit_test4
+#undef gen_dcache_hit_test8
+
+#define gen_icache_hit_test(w, shift) do { \
+            TCGv_i32 addr = tcg_temp_new_i32(); \
+            gen_window_check1(dc, RRI##w##_S); \
+            tcg_gen_movi_i32(cpu_pc, dc->pc); \
+            tcg_gen_addi_i32(addr, cpu_R[RRI##w##_S], \
+                             RRI##w##_IMM##w << shift); \
+            gen_helper_itlb_hit_test(cpu_env, addr); \
+            tcg_temp_free(addr); \
+        } while (0)
+
+#define gen_icache_hit_test4() gen_icache_hit_test(4, 4)
+#define gen_icache_hit_test8() gen_icache_hit_test(8, 2)
+
             case 12: /*IPFc*/
                 HAS_OPTION(XTENSA_OPTION_ICACHE);
+                gen_window_check1(dc, RRI8_S);
                 break;
 
             case 13: /*ICEc*/
                 switch (OP1) {
                 case 0: /*IPFLl*/
                     HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK);
+                    gen_check_privilege(dc);
+                    gen_icache_hit_test4();
                     break;
 
                 case 2: /*IHUl*/
                     HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK);
+                    gen_check_privilege(dc);
+                    gen_icache_hit_test4();
                     break;
 
                 case 3: /*IIUl*/
                     HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK);
+                    gen_check_privilege(dc);
+                    gen_window_check1(dc, RRI4_S);
                     break;
 
                 default: /*reserved*/
@@ -2311,10 +2383,13 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
 
             case 14: /*IHIc*/
                 HAS_OPTION(XTENSA_OPTION_ICACHE);
+                gen_icache_hit_test8();
                 break;
 
             case 15: /*IIIc*/
                 HAS_OPTION(XTENSA_OPTION_ICACHE);
+                gen_check_privilege(dc);
+                gen_window_check1(dc, RRI8_S);
                 break;
 
             default: /*reserved*/
@@ -2323,6 +2398,10 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
             }
             break;
 
+#undef gen_icache_hit_test
+#undef gen_icache_hit_test4
+#undef gen_icache_hit_test8
+
         case 9: /*L16SI*/
             gen_load_store(ld16s, 1);
             break;
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index 7a7461b0b2..2ad0f7827e 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -306,6 +306,7 @@ static void qdict_array_split_test(void)
 {
     QDict *test_dict = qdict_new();
     QDict *dict1, *dict2;
+    QInt *int1;
     QList *test_list;
 
     /*
@@ -313,10 +314,11 @@ static void qdict_array_split_test(void)
      *
      * {
      *     "1.x": 0,
-     *     "3.y": 1,
+     *     "4.y": 1,
      *     "0.a": 42,
      *     "o.o": 7,
-     *     "0.b": 23
+     *     "0.b": 23,
+     *     "2": 66
      * }
      *
      * to
@@ -328,13 +330,14 @@ static void qdict_array_split_test(void)
      *     },
      *     {
      *         "x": 0
-     *     }
+     *     },
+     *     66
      * ]
      *
      * and
      *
      * {
-     *     "3.y": 1,
+     *     "4.y": 1,
      *     "o.o": 7
      * }
      *
@@ -344,18 +347,21 @@ static void qdict_array_split_test(void)
      */
 
     qdict_put(test_dict, "1.x", qint_from_int(0));
-    qdict_put(test_dict, "3.y", qint_from_int(1));
+    qdict_put(test_dict, "4.y", qint_from_int(1));
     qdict_put(test_dict, "0.a", qint_from_int(42));
     qdict_put(test_dict, "o.o", qint_from_int(7));
     qdict_put(test_dict, "0.b", qint_from_int(23));
+    qdict_put(test_dict, "2", qint_from_int(66));
 
     qdict_array_split(test_dict, &test_list);
 
     dict1 = qobject_to_qdict(qlist_pop(test_list));
     dict2 = qobject_to_qdict(qlist_pop(test_list));
+    int1 = qobject_to_qint(qlist_pop(test_list));
 
     g_assert(dict1);
     g_assert(dict2);
+    g_assert(int1);
     g_assert(qlist_empty(test_list));
 
     QDECREF(test_list);
@@ -373,12 +379,69 @@ static void qdict_array_split_test(void)
 
     QDECREF(dict2);
 
-    g_assert(qdict_get_int(test_dict, "3.y") == 1);
+    g_assert(qint_get_int(int1) == 66);
+
+    QDECREF(int1);
+
+    g_assert(qdict_get_int(test_dict, "4.y") == 1);
     g_assert(qdict_get_int(test_dict, "o.o") == 7);
 
     g_assert(qdict_size(test_dict) == 2);
 
     QDECREF(test_dict);
+
+
+    /*
+     * Test the split of
+     *
+     * {
+     *     "0": 42,
+     *     "1": 23,
+     *     "1.x": 84
+     * }
+     *
+     * to
+     *
+     * [
+     *     42
+     * ]
+     *
+     * and
+     *
+     * {
+     *     "1": 23,
+     *     "1.x": 84
+     * }
+     *
+     * That is, test whether splitting stops if there is both an entry with key
+     * of "%u" and other entries with keys prefixed "%u." for the same index.
+     */
+
+    test_dict = qdict_new();
+
+    qdict_put(test_dict, "0", qint_from_int(42));
+    qdict_put(test_dict, "1", qint_from_int(23));
+    qdict_put(test_dict, "1.x", qint_from_int(84));
+
+    qdict_array_split(test_dict, &test_list);
+
+    int1 = qobject_to_qint(qlist_pop(test_list));
+
+    g_assert(int1);
+    g_assert(qlist_empty(test_list));
+
+    QDECREF(test_list);
+
+    g_assert(qint_get_int(int1) == 42);
+
+    QDECREF(int1);
+
+    g_assert(qdict_get_int(test_dict, "1") == 23);
+    g_assert(qdict_get_int(test_dict, "1.x") == 84);
+
+    g_assert(qdict_size(test_dict) == 2);
+
+    QDECREF(test_dict);
 }
 
 /*
diff --git a/tests/qemu-iotests/046 b/tests/qemu-iotests/046
index 2d44bbb187..e0be46cf2b 100755
--- a/tests/qemu-iotests/046
+++ b/tests/qemu-iotests/046
@@ -193,6 +193,16 @@ echo "== Verify image content =="
 
 function verify_io()
 {
+    if ($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep "compat: 0.10" > /dev/null); then
+        # For v2 images, discarded clusters are read from the backing file
+        # Keep the variable empty so that the backing file value can be used as
+        # the default below
+        discarded=
+    else
+        # Discarded clusters are zeroed for v3 or later
+        discarded=0
+    fi
+
     echo read -P 0 0 0x10000
 
     echo read -P 1  0x10000 0x2000
@@ -221,16 +231,16 @@ function verify_io()
     echo read -P 70 0x78000 0x6000
     echo read -P 7  0x7e000 0x2000
 
-    echo read -P 8  0x80000 0x6000
+    echo read -P ${discarded:-8} 0x80000 0x6000
     echo read -P 80 0x86000 0x2000
-    echo read -P 8  0x88000 0x2000
+    echo read -P ${discarded:-8} 0x88000 0x2000
     echo read -P 81 0x8a000 0xe000
     echo read -P 90 0x98000 0x6000
     echo read -P 9  0x9e000 0x2000
 
-    echo read -P 10  0xa0000 0x6000
+    echo read -P ${discarded:-10} 0xa0000 0x6000
     echo read -P 100 0xa6000 0x2000
-    echo read -P 10  0xa8000 0x2000
+    echo read -P ${discarded:-10} 0xa8000 0x2000
     echo read -P 101 0xaa000 0xe000
     echo read -P 110 0xb8000 0x8000
 
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 30e2dbd6d7..7de18704f8 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -231,7 +231,7 @@ Testing: -drive driver=file
 QEMU_PROG: -drive driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name
 
 Testing: -drive driver=nbd
-QEMU_PROG: -drive driver=nbd: could not open disk image ide0-hd0: Could not open image: Invalid argument
+QEMU_PROG: -drive driver=nbd: could not open disk image ide0-hd0: one of path and host must be specified.
 
 Testing: -drive driver=raw
 QEMU_PROG: -drive driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level
@@ -240,7 +240,7 @@ Testing: -drive file.driver=file
 QEMU_PROG: -drive file.driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name
 
 Testing: -drive file.driver=nbd
-QEMU_PROG: -drive file.driver=nbd: could not open disk image ide0-hd0: Could not open image: Invalid argument
+QEMU_PROG: -drive file.driver=nbd: could not open disk image ide0-hd0: one of path and host must be specified.
 
 Testing: -drive file.driver=raw
 QEMU_PROG: -drive file.driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
index 4ffeb54710..3371c867bb 100644
--- a/tests/qemu-iotests/059.out
+++ b/tests/qemu-iotests/059.out
@@ -7,8 +7,7 @@ no file open, try 'help open'
 
 === Testing too big L2 table size ===
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-L2 table size too big
-qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type
+qemu-io: can't open device TEST_DIR/t.vmdk: L2 table size too big
 no file open, try 'help open'
 
 === Testing too big L1 table size ===
@@ -2045,8 +2044,7 @@ RW 12582912 VMFS "dummy.IMGFMT" 1
 
 === Testing truncated sparse ===
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400
-qemu-img: File truncated, expecting at least 13172736 bytes
-qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'TEST_DIR/t.IMGFMT': Wrong medium type
+qemu-img: Could not open 'TEST_DIR/t.IMGFMT': File truncated, expecting at least 13172736 bytes
 
 === Testing version 3 ===
 image: TEST_DIR/iotest-version3.IMGFMT
diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081
new file mode 100755
index 0000000000..f053f11942
--- /dev/null
+++ b/tests/qemu-iotests/081
@@ -0,0 +1,146 @@
+#!/bin/bash
+#
+# Test Quorum block driver
+#
+# Copyright (C) 2013 Nodalink, SARL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=benoit@irqsave.net
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1	# failure is the default!
+
+_cleanup()
+{
+    rm -rf $TEST_DIR/1.raw
+    rm -rf $TEST_DIR/2.raw
+    rm -rf $TEST_DIR/3.raw
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt raw
+_supported_proto generic
+_supported_os Linux
+
+function do_run_qemu()
+{
+    echo Testing: "$@" | _filter_imgfmt
+    $QEMU -nographic -qmp stdio -serial none "$@"
+    echo
+}
+
+function run_qemu()
+{
+    do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu_io
+}
+
+quorum="file.driver=quorum,file.children.0.file.filename=$TEST_DIR/1.raw"
+quorum="$quorum,file.children.1.file.filename=$TEST_DIR/2.raw"
+quorum="$quorum,file.children.2.file.filename=$TEST_DIR/3.raw,file.vote-threshold=2"
+
+echo
+echo "== creating quorum files =="
+
+size=10M
+
+TEST_IMG="$TEST_DIR/1.raw" _make_test_img $size
+TEST_IMG="$TEST_DIR/2.raw" _make_test_img $size
+TEST_IMG="$TEST_DIR/3.raw" _make_test_img $size
+
+echo
+echo "== writing images =="
+
+$QEMU_IO -c "open -o $quorum" -c "write -P 0x32 0 $size" | _filter_qemu_io
+
+echo
+echo "== checking quorum write =="
+
+$QEMU_IO -c "read -P 0x32 0 $size" "$TEST_DIR/1.raw" | _filter_qemu_io
+$QEMU_IO -c "read -P 0x32 0 $size" "$TEST_DIR/2.raw" | _filter_qemu_io
+$QEMU_IO -c "read -P 0x32 0 $size" "$TEST_DIR/3.raw" | _filter_qemu_io
+
+echo
+echo "== corrupting image =="
+
+$QEMU_IO -c "write -P 0x42 0 $size" "$TEST_DIR/2.raw" | _filter_qemu_io
+
+echo
+echo "== checking quorum correction =="
+
+$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io
+
+echo
+echo "== checking mixed reference/option specification =="
+
+run_qemu -drive "file=$TEST_DIR/2.raw,format=$IMGFMT,if=none,id=drive2" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+    "arguments": {
+        "options": {
+            "driver": "quorum",
+            "id": "drive0-quorum",
+            "vote-threshold": 2,
+            "children": [
+                {
+                    "driver": "raw",
+                    "file": {
+                        "driver": "file",
+                        "filename": "$TEST_DIR/1.raw"
+                    }
+                },
+                "drive2",
+                {
+                    "driver": "raw",
+                    "file": {
+                        "driver": "file",
+                        "filename": "$TEST_DIR/3.raw"
+                    }
+                }
+            ]
+        }
+    }
+}
+{ "execute": "human-monitor-command",
+    "arguments": {
+        "command-line": 'qemu-io drive0-quorum "read -P 0x32 0 $size"'
+    }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "== breaking quorum =="
+
+$QEMU_IO -c "write -P 0x41 0 $size" "$TEST_DIR/1.raw" | _filter_qemu_io
+echo
+echo "== checking that quorum is broken =="
+
+$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io
+
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/081.out b/tests/qemu-iotests/081.out
new file mode 100644
index 0000000000..4fe2f95f63
--- /dev/null
+++ b/tests/qemu-iotests/081.out
@@ -0,0 +1,49 @@
+QA output created by 081
+
+== creating quorum files ==
+Formatting 'TEST_DIR/1.IMGFMT', fmt=IMGFMT size=10485760 
+Formatting 'TEST_DIR/2.IMGFMT', fmt=IMGFMT size=10485760 
+Formatting 'TEST_DIR/3.IMGFMT', fmt=IMGFMT size=10485760 
+
+== writing images ==
+wrote 10485760/10485760 bytes at offset 0
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== checking quorum write ==
+read 10485760/10485760 bytes at offset 0
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 10485760/10485760 bytes at offset 0
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 10485760/10485760 bytes at offset 0
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== corrupting image ==
+wrote 10485760/10485760 bytes at offset 0
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== checking quorum correction ==
+read 10485760/10485760 bytes at offset 0
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== checking mixed reference/option specification ==
+Testing: -drive file=TEST_DIR/2.IMGFMT,format=IMGFMT,if=none,id=drive2
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "QUORUM_REPORT_BAD", "data": {"node-name": "", "ret": 0, "sectors-count": 20480, "sector-num": 0}}
+read 10485760/10485760 bytes at offset 0
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+
+
+== breaking quorum ==
+wrote 10485760/10485760 bytes at offset 0
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== checking that quorum is broken ==
+qemu-io: can't open device (null): Could not read image for determining its format: Input/output error
+*** done
diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082
new file mode 100755
index 0000000000..f6eb75f624
--- /dev/null
+++ b/tests/qemu-iotests/082
@@ -0,0 +1,208 @@
+#!/bin/bash
+#
+# Test qemu-img command line parsing
+#
+# Copyright (C) 2014 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=kwolf@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+function run_qemu_img()
+{
+    echo
+    echo Testing: "$@" | _filter_testdir
+    "$QEMU_IMG" "$@" 2>&1 | _filter_testdir
+}
+
+size=128M
+
+echo
+echo === create: Options specified more than once ===
+
+# Last -f should win
+run_qemu_img create -f foo -f $IMGFMT "$TEST_IMG" $size
+run_qemu_img info "$TEST_IMG"
+
+# Multiple -o should be merged
+run_qemu_img create -f $IMGFMT -o cluster_size=4k -o lazy_refcounts=on "$TEST_IMG" $size
+run_qemu_img info "$TEST_IMG"
+
+# If the same -o key is specified more than once, the last one wins
+run_qemu_img create -f $IMGFMT -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k "$TEST_IMG" $size
+run_qemu_img info "$TEST_IMG"
+run_qemu_img create -f $IMGFMT -o cluster_size=4k,cluster_size=8k "$TEST_IMG" $size
+run_qemu_img info "$TEST_IMG"
+
+echo
+echo === create: help for -o ===
+
+# Adding the help option to a command without other -o options
+run_qemu_img create -f $IMGFMT -o help "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -o \? "$TEST_IMG" $size
+
+# Adding the help option to the same -o option
+run_qemu_img create -f $IMGFMT -o cluster_size=4k,help "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -o cluster_size=4k,\? "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -o help,cluster_size=4k "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -o \?,cluster_size=4k "$TEST_IMG" $size
+
+# Adding the help option to a separate -o option
+run_qemu_img create -f $IMGFMT -o cluster_size=4k -o help "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -o cluster_size=4k -o \? "$TEST_IMG" $size
+
+# Looks like a help option, but is part of the backing file name
+run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG",,help "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG",,\? "$TEST_IMG" $size
+
+# Try to trick qemu-img into creating escaped commas
+run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG", -o help "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG" -o ,help "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_IMG" $size
+
+# Leave out everything that isn't needed
+run_qemu_img create -f $IMGFMT -o help
+run_qemu_img create -o help
+
+echo
+echo === convert: Options specified more than once ===
+
+# We need a valid source image
+run_qemu_img create -f $IMGFMT "$TEST_IMG" $size
+
+# Last -f should win
+run_qemu_img convert -f foo -f $IMGFMT "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img info "$TEST_IMG".base
+
+# Last -O should win
+run_qemu_img convert -O foo -O $IMGFMT "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img info "$TEST_IMG".base
+
+# Multiple -o should be merged
+run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o lazy_refcounts=on "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img info "$TEST_IMG".base
+
+# If the same -o key is specified more than once, the last one wins
+run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img info "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o cluster_size=4k,cluster_size=8k "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img info "$TEST_IMG".base
+
+echo
+echo === convert: help for -o ===
+
+# Adding the help option to a command without other -o options
+run_qemu_img convert -O $IMGFMT -o help "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o \? "$TEST_IMG" "$TEST_IMG".base
+
+# Adding the help option to the same -o option
+run_qemu_img convert -O $IMGFMT -o cluster_size=4k,help "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o cluster_size=4k,\? "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o help,cluster_size=4k "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o \?,cluster_size=4k "$TEST_IMG" "$TEST_IMG".base
+
+# Adding the help option to a separate -o option
+run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o help "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o \? "$TEST_IMG" "$TEST_IMG".base
+
+# Looks like a help option, but is part of the backing file name
+run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG",,help "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG",,\? "$TEST_IMG" "$TEST_IMG".base
+
+# Try to trick qemu-img into creating escaped commas
+run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG", -o help "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG" -o ,help "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_IMG" "$TEST_IMG".base
+
+# Leave out everything that isn't needed
+run_qemu_img convert -O $IMGFMT -o help
+run_qemu_img convert -o help
+
+echo
+echo === amend: Options specified more than once ===
+
+# Last -f should win
+run_qemu_img amend -f foo -f $IMGFMT -o lazy_refcounts=on "$TEST_IMG"
+run_qemu_img info "$TEST_IMG"
+
+# Multiple -o should be merged
+run_qemu_img amend -f $IMGFMT -o size=130M -o lazy_refcounts=off "$TEST_IMG"
+run_qemu_img info "$TEST_IMG"
+
+# If the same -o key is specified more than once, the last one wins
+run_qemu_img amend -f $IMGFMT -o size=8M -o lazy_refcounts=on -o size=132M "$TEST_IMG"
+run_qemu_img info "$TEST_IMG"
+run_qemu_img amend -f $IMGFMT -o size=4M,size=148M "$TEST_IMG"
+run_qemu_img info "$TEST_IMG"
+
+echo
+echo === amend: help for -o ===
+
+# Adding the help option to a command without other -o options
+run_qemu_img amend -f $IMGFMT -o help "$TEST_IMG"
+run_qemu_img amend -f $IMGFMT -o \? "$TEST_IMG"
+
+# Adding the help option to the same -o option
+run_qemu_img amend -f $IMGFMT -o cluster_size=4k,help "$TEST_IMG"
+run_qemu_img amend -f $IMGFMT -o cluster_size=4k,\? "$TEST_IMG"
+run_qemu_img amend -f $IMGFMT -o help,cluster_size=4k "$TEST_IMG"
+run_qemu_img amend -f $IMGFMT -o \?,cluster_size=4k "$TEST_IMG"
+
+# Adding the help option to a separate -o option
+run_qemu_img amend -f $IMGFMT -o cluster_size=4k -o help "$TEST_IMG"
+run_qemu_img amend -f $IMGFMT -o cluster_size=4k -o \? "$TEST_IMG"
+
+# Looks like a help option, but is part of the backing file name
+run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG",,help "$TEST_IMG"
+run_qemu_img rebase -u -b "" -f $IMGFMT "$TEST_IMG"
+
+run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG",,\? "$TEST_IMG"
+run_qemu_img rebase -u -b "" -f $IMGFMT "$TEST_IMG"
+
+# Try to trick qemu-img into creating escaped commas
+run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG", -o help "$TEST_IMG"
+run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG" -o ,help "$TEST_IMG"
+run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_IMG"
+
+# Leave out everything that isn't needed
+run_qemu_img amend -f $IMGFMT -o help
+run_qemu_img convert -o help
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
new file mode 100644
index 0000000000..28309a0327
--- /dev/null
+++ b/tests/qemu-iotests/082.out
@@ -0,0 +1,529 @@
+QA output created by 082
+
+=== create: Options specified more than once ===
+
+Testing: create -f foo -f qcow2 TEST_DIR/t.qcow2 128M
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off 
+
+Testing: info TEST_DIR/t.qcow2
+image: TEST_DIR/t.qcow2
+file format: qcow2
+virtual size: 128M (134217728 bytes)
+disk size: 196K
+cluster_size: 65536
+Format specific information:
+    compat: 1.1
+    lazy refcounts: false
+
+Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 128M
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on 
+
+Testing: info TEST_DIR/t.qcow2
+image: TEST_DIR/t.qcow2
+file format: qcow2
+virtual size: 128M (134217728 bytes)
+disk size: 16K
+cluster_size: 4096
+Format specific information:
+    compat: 1.1
+    lazy refcounts: true
+
+Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 128M
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on 
+
+Testing: info TEST_DIR/t.qcow2
+image: TEST_DIR/t.qcow2
+file format: qcow2
+virtual size: 128M (134217728 bytes)
+disk size: 28K
+cluster_size: 8192
+Format specific information:
+    compat: 1.1
+    lazy refcounts: true
+
+Testing: create -f qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 128M
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off 
+
+Testing: info TEST_DIR/t.qcow2
+image: TEST_DIR/t.qcow2
+file format: qcow2
+virtual size: 128M (134217728 bytes)
+disk size: 28K
+cluster_size: 8192
+Format specific information:
+    compat: 1.1
+    lazy refcounts: false
+
+=== create: help for -o ===
+
+Testing: create -f qcow2 -o help TEST_DIR/t.qcow2 128M
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: create -f qcow2 -o ? TEST_DIR/t.qcow2 128M
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: create -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 128M
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: create -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 128M
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: create -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 128M
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: create -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 128M
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: create -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 128M
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: create -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 128M
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off 
+
+Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,?' encryption=off cluster_size=65536 lazy_refcounts=off 
+
+Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 128M
+qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2,
+
+Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,help TEST_DIR/t.qcow2 128M
+qemu-img: Invalid option list: ,help
+
+Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/t.qcow2 128M
+qemu-img: Invalid option list: ,,
+
+Testing: create -f qcow2 -o help
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: create -o help
+Supported options:
+size             Virtual disk size
+
+=== convert: Options specified more than once ===
+
+Testing: create -f qcow2 TEST_DIR/t.qcow2 128M
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off 
+
+Testing: convert -f foo -f qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+
+Testing: info TEST_DIR/t.qcow2.base
+image: TEST_DIR/t.qcow2.base
+file format: raw
+virtual size: 128M (134217728 bytes)
+disk size: 0
+
+Testing: convert -O foo -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+
+Testing: info TEST_DIR/t.qcow2.base
+image: TEST_DIR/t.qcow2.base
+file format: qcow2
+virtual size: 128M (134217728 bytes)
+disk size: 196K
+cluster_size: 65536
+Format specific information:
+    compat: 1.1
+    lazy refcounts: false
+
+Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+
+Testing: info TEST_DIR/t.qcow2.base
+image: TEST_DIR/t.qcow2.base
+file format: qcow2
+virtual size: 128M (134217728 bytes)
+disk size: 16K
+cluster_size: 4096
+Format specific information:
+    compat: 1.1
+    lazy refcounts: true
+
+Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+
+Testing: info TEST_DIR/t.qcow2.base
+image: TEST_DIR/t.qcow2.base
+file format: qcow2
+virtual size: 128M (134217728 bytes)
+disk size: 28K
+cluster_size: 8192
+Format specific information:
+    compat: 1.1
+    lazy refcounts: true
+
+Testing: convert -O qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+
+Testing: info TEST_DIR/t.qcow2.base
+image: TEST_DIR/t.qcow2.base
+file format: qcow2
+virtual size: 128M (134217728 bytes)
+disk size: 28K
+cluster_size: 8192
+Format specific information:
+    compat: 1.1
+    lazy refcounts: false
+
+=== convert: help for -o ===
+
+Testing: convert -O qcow2 -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: convert -O qcow2 -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: convert -O qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: convert -O qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: convert -O qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: convert -O qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: convert -O qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: convert -O qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+qemu-img: Could not open 'TEST_DIR/t.qcow2.base': Could not open backing file: Could not open 'TEST_DIR/t.qcow2,help': No such file or directory
+
+Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+qemu-img: Could not open 'TEST_DIR/t.qcow2.base': Could not open backing file: Could not open 'TEST_DIR/t.qcow2,?': No such file or directory
+
+Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2,
+
+Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+qemu-img: Invalid option list: ,help
+
+Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+qemu-img: Invalid option list: ,,
+
+Testing: convert -O qcow2 -o help
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: convert -o help
+Supported options:
+size             Virtual disk size
+
+=== amend: Options specified more than once ===
+
+Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2
+
+Testing: info TEST_DIR/t.qcow2
+image: TEST_DIR/t.qcow2
+file format: qcow2
+virtual size: 128M (134217728 bytes)
+disk size: 196K
+cluster_size: 65536
+Format specific information:
+    compat: 1.1
+    lazy refcounts: true
+
+Testing: amend -f qcow2 -o size=130M -o lazy_refcounts=off TEST_DIR/t.qcow2
+
+Testing: info TEST_DIR/t.qcow2
+image: TEST_DIR/t.qcow2
+file format: qcow2
+virtual size: 130M (136314880 bytes)
+disk size: 196K
+cluster_size: 65536
+Format specific information:
+    compat: 1.1
+    lazy refcounts: false
+
+Testing: amend -f qcow2 -o size=8M -o lazy_refcounts=on -o size=132M TEST_DIR/t.qcow2
+
+Testing: info TEST_DIR/t.qcow2
+image: TEST_DIR/t.qcow2
+file format: qcow2
+virtual size: 132M (138412032 bytes)
+disk size: 196K
+cluster_size: 65536
+Format specific information:
+    compat: 1.1
+    lazy refcounts: true
+
+Testing: amend -f qcow2 -o size=4M,size=148M TEST_DIR/t.qcow2
+
+Testing: info TEST_DIR/t.qcow2
+image: TEST_DIR/t.qcow2
+file format: qcow2
+virtual size: 148M (155189248 bytes)
+disk size: 196K
+cluster_size: 65536
+Format specific information:
+    compat: 1.1
+    lazy refcounts: true
+
+=== amend: help for -o ===
+
+Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2
+
+Testing: rebase -u -b  -f qcow2 TEST_DIR/t.qcow2
+
+Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2
+
+Testing: rebase -u -b  -f qcow2 TEST_DIR/t.qcow2
+
+Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2
+qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2,
+
+Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,help TEST_DIR/t.qcow2
+qemu-img: Invalid option list: ,help
+
+Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/t.qcow2
+qemu-img: Invalid option list: ,,
+
+Testing: amend -f qcow2 -o help
+Supported options:
+size             Virtual disk size
+compat           Compatibility level (0.10 or 1.1)
+backing_file     File name of a base image
+backing_fmt      Image format of the base image
+encryption       Encrypt the image
+cluster_size     qcow2 cluster size
+preallocation    Preallocation mode (allowed values: off, metadata)
+lazy_refcounts   Postpone refcount updates
+
+Testing: convert -o help
+Supported options:
+size             Virtual disk size
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index d8be74a17e..db127d924d 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -83,3 +83,5 @@
 074 rw auto
 077 rw auto
 079 rw auto
+081 rw auto
+082 rw auto quick
diff --git a/tests/tcg/xtensa/Makefile b/tests/tcg/xtensa/Makefile
index 1b519cae45..a70c92be7e 100644
--- a/tests/tcg/xtensa/Makefile
+++ b/tests/tcg/xtensa/Makefile
@@ -1,10 +1,11 @@
 -include ../../../config-host.mak
 
-CROSS=xtensa-dc232b-elf-
+CORE=dc232b
+CROSS=xtensa-$(CORE)-elf-
 
 ifndef XT
 SIM = ../../../xtensa-softmmu/qemu-system-xtensa
-SIMFLAGS = -M sim -cpu dc232b -nographic -semihosting $(EXTFLAGS) -kernel
+SIMFLAGS = -M sim -cpu $(CORE) -nographic -semihosting $(EXTFLAGS) -kernel
 SIMDEBUG = -s -S
 else
 SIM = xt-run
@@ -17,6 +18,8 @@ AS      = $(CROSS)gcc -x assembler-with-cpp
 LD      = $(CROSS)ld
 
 XTENSA_SRC_PATH = $(SRC_PATH)/tests/tcg/xtensa
+INCLUDE_DIRS = $(XTENSA_SRC_PATH) $(SRC_PATH)/target-xtensa/core-$(CORE)
+XTENSA_INC = $(addprefix -I,$(INCLUDE_DIRS))
 
 LDFLAGS = -T$(XTENSA_SRC_PATH)/linker.ld
 
@@ -27,6 +30,7 @@ TESTCASES += test_bi.tst
 #TESTCASES += test_boolean.tst
 TESTCASES += test_break.tst
 TESTCASES += test_bz.tst
+TESTCASES += test_cache.tst
 TESTCASES += test_clamps.tst
 TESTCASES += test_extui.tst
 TESTCASES += test_fail.tst
@@ -56,10 +60,10 @@ TESTCASES += test_windowed.tst
 all: build
 
 %.o: $(XTENSA_SRC_PATH)/%.c
-	$(CC) -I$(XTENSA_SRC_PATH) $(CFLAGS) -c $< -o $@
+	$(CC) $(XTENSA_INC) $(CFLAGS) -c $< -o $@
 
 %.o: $(XTENSA_SRC_PATH)/%.S
-	$(AS) -Wa,-I,$(XTENSA_SRC_PATH) $(ASFLAGS) -c $< -o $@
+	$(CC) $(XTENSA_INC) $(ASFLAGS) -c $< -o $@
 
 %.tst: %.o $(XTENSA_SRC_PATH)/macros.inc $(CRT) Makefile
 	$(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $< -o $@
diff --git a/tests/tcg/xtensa/macros.inc b/tests/tcg/xtensa/macros.inc
index c9be1ce516..4ebd30ab86 100644
--- a/tests/tcg/xtensa/macros.inc
+++ b/tests/tcg/xtensa/macros.inc
@@ -1,3 +1,5 @@
+#include "core-isa.h"
+
 .macro test_suite name
 .data
 status: .word result
@@ -43,8 +45,12 @@ main:
     simcall
 .endm
 
+.macro test_init
+.endm
+
 .macro test name
     //print test_\name
+    test_init
 test_\name:
 .global test_\name
 .endm
diff --git a/tests/tcg/xtensa/test_b.S b/tests/tcg/xtensa/test_b.S
index 6cbe5f1fca..8e81f956df 100644
--- a/tests/tcg/xtensa/test_b.S
+++ b/tests/tcg/xtensa/test_b.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite b
 
diff --git a/tests/tcg/xtensa/test_bi.S b/tests/tcg/xtensa/test_bi.S
index 6a5f1dffc9..4f94c0c7e6 100644
--- a/tests/tcg/xtensa/test_bi.S
+++ b/tests/tcg/xtensa/test_bi.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite bi
 
diff --git a/tests/tcg/xtensa/test_boolean.S b/tests/tcg/xtensa/test_boolean.S
index 50e6d2c22a..eac40e0973 100644
--- a/tests/tcg/xtensa/test_boolean.S
+++ b/tests/tcg/xtensa/test_boolean.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite boolean
 
diff --git a/tests/tcg/xtensa/test_break.S b/tests/tcg/xtensa/test_break.S
index 7574cbefc8..775cd7c260 100644
--- a/tests/tcg/xtensa/test_break.S
+++ b/tests/tcg/xtensa/test_break.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 #define debug_level 6
 #define debug_vector level6
diff --git a/tests/tcg/xtensa/test_bz.S b/tests/tcg/xtensa/test_bz.S
index f9ba6e22e8..b68135011e 100644
--- a/tests/tcg/xtensa/test_bz.S
+++ b/tests/tcg/xtensa/test_bz.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite bz
 
diff --git a/tests/tcg/xtensa/test_cache.S b/tests/tcg/xtensa/test_cache.S
new file mode 100644
index 0000000000..6b2df9734b
--- /dev/null
+++ b/tests/tcg/xtensa/test_cache.S
@@ -0,0 +1,97 @@
+#include "macros.inc"
+
+.purgem test_init
+.macro test_init
+    call0   cache_unlock_invalidate
+.endm
+
+test_suite cache
+
+.macro      pf_op op
+    \op     a2, 0
+    \op     a3, 0
+    \op     a4, 0
+.endm
+
+test prefetch
+    movi    a2, 0xd0000000 /* cacheable */
+    movi    a3, 0xd8000000 /* non-cacheable */
+    movi    a4, 0x00001235 /* unmapped */
+
+    pf_op   dpfr
+    pf_op   dpfro
+    pf_op   dpfw
+    pf_op   dpfwo
+    pf_op   ipf
+
+    dpfl    a2, 0
+    ipfl    a2, 0
+test_end
+
+.macro cache_fault op, addr, exc_code
+    set_vector kernel, 2f
+
+    movi    a4, \addr
+1:
+    \op     a4, 0
+    test_fail
+2:
+    rsr     a2, epc1
+    movi    a3, 1b
+    assert  eq, a2, a3
+    rsr     a2, excvaddr
+    assert  eq, a2, a4
+    rsr     a2, exccause
+    movi    a3, \exc_code
+    assert  eq, a2, a3
+.endm
+
+test dpfl_tlb_miss
+    cache_fault dpfl, 0x00002345, 24
+test_end
+
+test dhwb_tlb_miss
+    cache_fault dhwb, 0x00002345, 24
+test_end
+
+test dhwbi_tlb_miss
+    cache_fault dhwbi, 0x00002345, 24
+test_end
+
+test dhi_tlb_miss
+    cache_fault dhi, 0x00002345, 24
+test_end
+
+test dhu_tlb_miss
+    cache_fault dhu, 0x00002345, 24
+test_end
+
+
+test ipfl_tlb_miss
+    cache_fault ipfl, 0x00002345, 16
+test_end
+
+test ihu_tlb_miss
+    cache_fault ihu, 0x00002345, 16
+test_end
+
+test ihi_tlb_miss
+    cache_fault ihi, 0x00002345, 16
+test_end
+
+test_suite_end
+
+.macro cache_all op1, op2, size, linesize
+    movi    a2, 0
+    movi    a3, \size
+1:
+    \op1    a2, 0
+    \op2    a2, 0
+    addi    a2, a2, \linesize
+    bltu    a2, a3, 1b
+.endm
+
+cache_unlock_invalidate:
+    cache_all diu, dii, XCHAL_DCACHE_SIZE, XCHAL_DCACHE_LINESIZE
+    cache_all iiu, iii, XCHAL_ICACHE_SIZE, XCHAL_ICACHE_LINESIZE
+    ret
diff --git a/tests/tcg/xtensa/test_clamps.S b/tests/tcg/xtensa/test_clamps.S
index c186cc98d8..3efabfd9d3 100644
--- a/tests/tcg/xtensa/test_clamps.S
+++ b/tests/tcg/xtensa/test_clamps.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite clamps
 
diff --git a/tests/tcg/xtensa/test_extui.S b/tests/tcg/xtensa/test_extui.S
index 5d55451704..c32bb824df 100644
--- a/tests/tcg/xtensa/test_extui.S
+++ b/tests/tcg/xtensa/test_extui.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite extui
 
diff --git a/tests/tcg/xtensa/test_fail.S b/tests/tcg/xtensa/test_fail.S
index e8d1b425bc..1c26d50790 100644
--- a/tests/tcg/xtensa/test_fail.S
+++ b/tests/tcg/xtensa/test_fail.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite fail
 
diff --git a/tests/tcg/xtensa/test_interrupt.S b/tests/tcg/xtensa/test_interrupt.S
index 68b3ee1492..334ddab287 100644
--- a/tests/tcg/xtensa/test_interrupt.S
+++ b/tests/tcg/xtensa/test_interrupt.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite interrupt
 
diff --git a/tests/tcg/xtensa/test_loop.S b/tests/tcg/xtensa/test_loop.S
index 1c240e8e9b..5755578d01 100644
--- a/tests/tcg/xtensa/test_loop.S
+++ b/tests/tcg/xtensa/test_loop.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite loop
 
diff --git a/tests/tcg/xtensa/test_mac16.S b/tests/tcg/xtensa/test_mac16.S
index 5ddd160ffc..512025d842 100644
--- a/tests/tcg/xtensa/test_mac16.S
+++ b/tests/tcg/xtensa/test_mac16.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite mac16
 
diff --git a/tests/tcg/xtensa/test_max.S b/tests/tcg/xtensa/test_max.S
index 2534c9d90b..3caa207ea5 100644
--- a/tests/tcg/xtensa/test_max.S
+++ b/tests/tcg/xtensa/test_max.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite max
 
diff --git a/tests/tcg/xtensa/test_min.S b/tests/tcg/xtensa/test_min.S
index 6d9ddeb1ac..551cf591e5 100644
--- a/tests/tcg/xtensa/test_min.S
+++ b/tests/tcg/xtensa/test_min.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite min
 
diff --git a/tests/tcg/xtensa/test_mmu.S b/tests/tcg/xtensa/test_mmu.S
index 5d87fbb703..099031fd14 100644
--- a/tests/tcg/xtensa/test_mmu.S
+++ b/tests/tcg/xtensa/test_mmu.S
@@ -1,10 +1,10 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite mmu
 
-.purgem test
+.purgem test_init
 
-.macro test name
+.macro test_init
     movi    a2, 0x00000004
     idtlb   a2
     movi    a2, 0x00100004
diff --git a/tests/tcg/xtensa/test_mul16.S b/tests/tcg/xtensa/test_mul16.S
index bf94376649..98fa7042b5 100644
--- a/tests/tcg/xtensa/test_mul16.S
+++ b/tests/tcg/xtensa/test_mul16.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite mul16
 
diff --git a/tests/tcg/xtensa/test_mul32.S b/tests/tcg/xtensa/test_mul32.S
index fdaf57331b..b288ead9f6 100644
--- a/tests/tcg/xtensa/test_mul32.S
+++ b/tests/tcg/xtensa/test_mul32.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite mul32
 
diff --git a/tests/tcg/xtensa/test_nsa.S b/tests/tcg/xtensa/test_nsa.S
index a5fe5debe4..479b2e2429 100644
--- a/tests/tcg/xtensa/test_nsa.S
+++ b/tests/tcg/xtensa/test_nsa.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite nsa
 
diff --git a/tests/tcg/xtensa/test_pipeline.S b/tests/tcg/xtensa/test_pipeline.S
index 6be6085fc3..f418c11974 100644
--- a/tests/tcg/xtensa/test_pipeline.S
+++ b/tests/tcg/xtensa/test_pipeline.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 .purgem test
 .macro test name
diff --git a/tests/tcg/xtensa/test_quo.S b/tests/tcg/xtensa/test_quo.S
index 12debf1fe0..5b3ae383d0 100644
--- a/tests/tcg/xtensa/test_quo.S
+++ b/tests/tcg/xtensa/test_quo.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite quo
 
diff --git a/tests/tcg/xtensa/test_rem.S b/tests/tcg/xtensa/test_rem.S
index bb0d5fe202..6357e520d9 100644
--- a/tests/tcg/xtensa/test_rem.S
+++ b/tests/tcg/xtensa/test_rem.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite rem
 
diff --git a/tests/tcg/xtensa/test_rst0.S b/tests/tcg/xtensa/test_rst0.S
index 3eda565e8a..a73366b120 100644
--- a/tests/tcg/xtensa/test_rst0.S
+++ b/tests/tcg/xtensa/test_rst0.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite rst0
 
diff --git a/tests/tcg/xtensa/test_s32c1i.S b/tests/tcg/xtensa/test_s32c1i.S
index 4536015a84..93b575db95 100644
--- a/tests/tcg/xtensa/test_s32c1i.S
+++ b/tests/tcg/xtensa/test_s32c1i.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite s32c1i
 
diff --git a/tests/tcg/xtensa/test_sar.S b/tests/tcg/xtensa/test_sar.S
index 40c649ffb8..b615a55767 100644
--- a/tests/tcg/xtensa/test_sar.S
+++ b/tests/tcg/xtensa/test_sar.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite sar
 
diff --git a/tests/tcg/xtensa/test_sext.S b/tests/tcg/xtensa/test_sext.S
index 04dc6500c1..087a6333a4 100644
--- a/tests/tcg/xtensa/test_sext.S
+++ b/tests/tcg/xtensa/test_sext.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite sext
 
diff --git a/tests/tcg/xtensa/test_shift.S b/tests/tcg/xtensa/test_shift.S
index a8e43645b7..5df9ed4b1e 100644
--- a/tests/tcg/xtensa/test_shift.S
+++ b/tests/tcg/xtensa/test_shift.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite shift
 
diff --git a/tests/tcg/xtensa/test_sr.S b/tests/tcg/xtensa/test_sr.S
index 470c03dae2..4fac46e80f 100644
--- a/tests/tcg/xtensa/test_sr.S
+++ b/tests/tcg/xtensa/test_sr.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite sr
 
diff --git a/tests/tcg/xtensa/test_timer.S b/tests/tcg/xtensa/test_timer.S
index 1041cc6658..f8c6f7423a 100644
--- a/tests/tcg/xtensa/test_timer.S
+++ b/tests/tcg/xtensa/test_timer.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite timer
 
diff --git a/tests/tcg/xtensa/test_windowed.S b/tests/tcg/xtensa/test_windowed.S
index cb2d39e1fd..3de6d3763a 100644
--- a/tests/tcg/xtensa/test_windowed.S
+++ b/tests/tcg/xtensa/test_windowed.S
@@ -1,4 +1,4 @@
-.include "macros.inc"
+#include "macros.inc"
 
 test_suite windowed
 
diff --git a/util/iov.c b/util/iov.c
index bb46c04e4d..03934da74d 100644
--- a/util/iov.c
+++ b/util/iov.c
@@ -378,6 +378,112 @@ size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset,
     return iov_memset(qiov->iov, qiov->niov, offset, fillc, bytes);
 }
 
+/**
+ * Check that I/O vector contents are identical
+ *
+ * The IO vectors must have the same structure (same length of all parts).
+ * A typical usage is to compare vectors created with qemu_iovec_clone().
+ *
+ * @a:          I/O vector
+ * @b:          I/O vector
+ * @ret:        Offset to first mismatching byte or -1 if match
+ */
+ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
+{
+    int i;
+    ssize_t offset = 0;
+
+    assert(a->niov == b->niov);
+    for (i = 0; i < a->niov; i++) {
+        size_t len = 0;
+        uint8_t *p = (uint8_t *)a->iov[i].iov_base;
+        uint8_t *q = (uint8_t *)b->iov[i].iov_base;
+
+        assert(a->iov[i].iov_len == b->iov[i].iov_len);
+        while (len < a->iov[i].iov_len && *p++ == *q++) {
+            len++;
+        }
+
+        offset += len;
+
+        if (len != a->iov[i].iov_len) {
+            return offset;
+        }
+    }
+    return -1;
+}
+
+typedef struct {
+    int src_index;
+    struct iovec *src_iov;
+    void *dest_base;
+} IOVectorSortElem;
+
+static int sortelem_cmp_src_base(const void *a, const void *b)
+{
+    const IOVectorSortElem *elem_a = a;
+    const IOVectorSortElem *elem_b = b;
+
+    /* Don't overflow */
+    if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
+        return -1;
+    } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static int sortelem_cmp_src_index(const void *a, const void *b)
+{
+    const IOVectorSortElem *elem_a = a;
+    const IOVectorSortElem *elem_b = b;
+
+    return elem_a->src_index - elem_b->src_index;
+}
+
+/**
+ * Copy contents of I/O vector
+ *
+ * The relative relationships of overlapping iovecs are preserved.  This is
+ * necessary to ensure identical semantics in the cloned I/O vector.
+ */
+void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf)
+{
+    IOVectorSortElem sortelems[src->niov];
+    void *last_end;
+    int i;
+
+    /* Sort by source iovecs by base address */
+    for (i = 0; i < src->niov; i++) {
+        sortelems[i].src_index = i;
+        sortelems[i].src_iov = &src->iov[i];
+    }
+    qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
+
+    /* Allocate buffer space taking into account overlapping iovecs */
+    last_end = NULL;
+    for (i = 0; i < src->niov; i++) {
+        struct iovec *cur = sortelems[i].src_iov;
+        ptrdiff_t rewind = 0;
+
+        /* Detect overlap */
+        if (last_end && last_end > cur->iov_base) {
+            rewind = last_end - cur->iov_base;
+        }
+
+        sortelems[i].dest_base = buf - rewind;
+        buf += cur->iov_len - MIN(rewind, cur->iov_len);
+        last_end = MAX(cur->iov_base + cur->iov_len, last_end);
+    }
+
+    /* Sort by source iovec index and build destination iovec */
+    qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
+    for (i = 0; i < src->niov; i++) {
+        qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
+    }
+}
+
 size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
                          size_t bytes)
 {
diff --git a/util/module.c b/util/module.c
index 7acc33d076..dc08c16111 100644
--- a/util/module.c
+++ b/util/module.c
@@ -13,6 +13,10 @@
  * GNU GPL, version 2 or (at your option) any later version.
  */
 
+#include <stdlib.h>
+#ifdef CONFIG_MODULES
+#include <gmodule.h>
+#endif
 #include "qemu-common.h"
 #include "qemu/queue.h"
 #include "qemu/module.h"
@@ -21,13 +25,16 @@ typedef struct ModuleEntry
 {
     void (*init)(void);
     QTAILQ_ENTRY(ModuleEntry) node;
+    module_init_type type;
 } ModuleEntry;
 
 typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;
 
 static ModuleTypeList init_type_list[MODULE_INIT_MAX];
 
-static void init_types(void)
+static ModuleTypeList dso_init_list;
+
+static void init_lists(void)
 {
     static int inited;
     int i;
@@ -40,6 +47,8 @@ static void init_types(void)
         QTAILQ_INIT(&init_type_list[i]);
     }
 
+    QTAILQ_INIT(&dso_init_list);
+
     inited = 1;
 }
 
@@ -48,7 +57,7 @@ static ModuleTypeList *find_type(module_init_type type)
 {
     ModuleTypeList *l;
 
-    init_types();
+    init_lists();
 
     l = &init_type_list[type];
 
@@ -62,20 +71,154 @@ void register_module_init(void (*fn)(void), module_init_type type)
 
     e = g_malloc0(sizeof(*e));
     e->init = fn;
+    e->type = type;
 
     l = find_type(type);
 
     QTAILQ_INSERT_TAIL(l, e, node);
 }
 
+void register_dso_module_init(void (*fn)(void), module_init_type type)
+{
+    ModuleEntry *e;
+
+    init_lists();
+
+    e = g_malloc0(sizeof(*e));
+    e->init = fn;
+    e->type = type;
+
+    QTAILQ_INSERT_TAIL(&dso_init_list, e, node);
+}
+
+static void module_load(module_init_type type);
+
 void module_call_init(module_init_type type)
 {
     ModuleTypeList *l;
     ModuleEntry *e;
 
+    module_load(type);
     l = find_type(type);
 
     QTAILQ_FOREACH(e, l, node) {
         e->init();
     }
 }
+
+#ifdef CONFIG_MODULES
+static int module_load_file(const char *fname)
+{
+    GModule *g_module;
+    void (*sym)(void);
+    const char *dsosuf = HOST_DSOSUF;
+    int len = strlen(fname);
+    int suf_len = strlen(dsosuf);
+    ModuleEntry *e, *next;
+    int ret;
+
+    if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) {
+        /* wrong suffix */
+        ret = -EINVAL;
+        goto out;
+    }
+    if (access(fname, F_OK)) {
+        ret = -ENOENT;
+        goto out;
+    }
+
+    assert(QTAILQ_EMPTY(&dso_init_list));
+
+    g_module = g_module_open(fname, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+    if (!g_module) {
+        fprintf(stderr, "Failed to open module: %s\n",
+                g_module_error());
+        ret = -EINVAL;
+        goto out;
+    }
+    if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) {
+        fprintf(stderr, "Failed to initialize module: %s\n",
+                fname);
+        /* Print some info if this is a QEMU module (but from different build),
+         * this will make debugging user problems easier. */
+        if (g_module_symbol(g_module, "qemu_module_dummy", (gpointer *)&sym)) {
+            fprintf(stderr,
+                    "Note: only modules from the same build can be loaded.\n");
+        }
+        g_module_close(g_module);
+        ret = -EINVAL;
+    } else {
+        QTAILQ_FOREACH(e, &dso_init_list, node) {
+            register_module_init(e->init, e->type);
+        }
+        ret = 0;
+    }
+
+    QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) {
+        QTAILQ_REMOVE(&dso_init_list, e, node);
+        g_free(e);
+    }
+out:
+    return ret;
+}
+#endif
+
+void module_load(module_init_type type)
+{
+#ifdef CONFIG_MODULES
+    char *fname = NULL;
+    const char **mp;
+    static const char *block_modules[] = {
+        CONFIG_BLOCK_MODULES
+    };
+    char *exec_dir;
+    char *dirs[3];
+    int i = 0;
+    int ret;
+
+    if (!g_module_supported()) {
+        fprintf(stderr, "Module is not supported by system.\n");
+        return;
+    }
+
+    switch (type) {
+    case MODULE_INIT_BLOCK:
+        mp = block_modules;
+        break;
+    default:
+        /* no other types have dynamic modules for now*/
+        return;
+    }
+
+    exec_dir = qemu_get_exec_dir();
+    dirs[i++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR);
+    dirs[i++] = g_strdup_printf("%s/..", exec_dir ? : "");
+    dirs[i++] = g_strdup_printf("%s", exec_dir ? : "");
+    assert(i == ARRAY_SIZE(dirs));
+    g_free(exec_dir);
+    exec_dir = NULL;
+
+    for ( ; *mp; mp++) {
+        for (i = 0; i < ARRAY_SIZE(dirs); i++) {
+            fname = g_strdup_printf("%s/%s%s", dirs[i], *mp, HOST_DSOSUF);
+            ret = module_load_file(fname);
+            /* Try loading until loaded a module file */
+            if (!ret) {
+                break;
+            }
+            g_free(fname);
+            fname = NULL;
+        }
+        if (ret == -ENOENT) {
+            fprintf(stderr, "Can't find module: %s\n", *mp);
+        }
+
+        g_free(fname);
+    }
+
+    for (i = 0; i < ARRAY_SIZE(dirs); i++) {
+        g_free(dirs[i]);
+    }
+
+#endif
+}
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index d5dca4729a..c2eeb4fe40 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -57,6 +57,7 @@ extern int daemon(int, int);
 #include "trace.h"
 #include "qemu/sockets.h"
 #include <sys/mman.h>
+#include <libgen.h>
 
 #ifdef CONFIG_LINUX
 #include <sys/syscall.h>
@@ -274,3 +275,56 @@ void qemu_set_tty_echo(int fd, bool echo)
 
     tcsetattr(fd, TCSANOW, &tty);
 }
+
+static char exec_dir[PATH_MAX];
+
+void qemu_init_exec_dir(const char *argv0)
+{
+    char *dir;
+    char *p = NULL;
+    char buf[PATH_MAX];
+
+    assert(!exec_dir[0]);
+
+#if defined(__linux__)
+    {
+        int len;
+        len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
+        if (len > 0) {
+            buf[len] = 0;
+            p = buf;
+        }
+    }
+#elif defined(__FreeBSD__)
+    {
+        static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+        size_t len = sizeof(buf) - 1;
+
+        *buf = '\0';
+        if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) &&
+            *buf) {
+            buf[sizeof(buf) - 1] = '\0';
+            p = buf;
+        }
+    }
+#endif
+    /* If we don't have any way of figuring out the actual executable
+       location then try argv[0].  */
+    if (!p) {
+        if (!argv0) {
+            return;
+        }
+        p = realpath(argv0, buf);
+        if (!p) {
+            return;
+        }
+    }
+    dir = dirname(p);
+
+    pstrcpy(exec_dir, sizeof(exec_dir), dir);
+}
+
+char *qemu_get_exec_dir(void)
+{
+    return g_strdup(exec_dir);
+}
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index 50be0440f2..93f7d351d3 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -208,3 +208,33 @@ void qemu_set_tty_echo(int fd, bool echo)
                        dwMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT));
     }
 }
+
+static char exec_dir[PATH_MAX];
+
+void qemu_init_exec_dir(const char *argv0)
+{
+
+    char *p;
+    char buf[MAX_PATH];
+    DWORD len;
+
+    len = GetModuleFileName(NULL, buf, sizeof(buf) - 1);
+    if (len == 0) {
+        return;
+    }
+
+    buf[len] = 0;
+    p = buf + len - 1;
+    while (p != buf && *p != '\\') {
+        p--;
+    }
+    *p = 0;
+    if (access(buf, R_OK) == 0) {
+        pstrcpy(exec_dir, sizeof(exec_dir), buf);
+    }
+}
+
+char *qemu_get_exec_dir(void)
+{
+    return g_strdup(exec_dir);
+}
diff --git a/util/qemu-config.c b/util/qemu-config.c
index 797df71569..f6101012c0 100644
--- a/util/qemu-config.c
+++ b/util/qemu-config.c
@@ -413,6 +413,12 @@ static void config_parse_qdict_section(QDict *options, QemuOptsList *opts,
             QDict *section = qobject_to_qdict(qlist_entry_obj(list_entry));
             char *opt_name;
 
+            if (!section) {
+                error_setg(errp, "[%s] section (index %u) does not consist of "
+                           "keys", opts->name, i);
+                goto out;
+            }
+
             opt_name = g_strdup_printf("%s.%u", opts->name, i++);
             subopts = qemu_opts_create(opts, opt_name, 1, &local_err);
             g_free(opt_name);
diff --git a/util/qemu-option.c b/util/qemu-option.c
index fd76cd2ada..9d898af443 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -450,6 +450,55 @@ fail:
     return NULL;
 }
 
+bool has_help_option(const char *param)
+{
+    size_t buflen = strlen(param) + 1;
+    char *buf = g_malloc0(buflen);
+    const char *p = param;
+    bool result = false;
+
+    while (*p) {
+        p = get_opt_value(buf, buflen, p);
+        if (*p) {
+            p++;
+        }
+
+        if (is_help_option(buf)) {
+            result = true;
+            goto out;
+        }
+    }
+
+out:
+    free(buf);
+    return result;
+}
+
+bool is_valid_option_list(const char *param)
+{
+    size_t buflen = strlen(param) + 1;
+    char *buf = g_malloc0(buflen);
+    const char *p = param;
+    bool result = true;
+
+    while (*p) {
+        p = get_opt_value(buf, buflen, p);
+        if (*p && !*++p) {
+            result = false;
+            goto out;
+        }
+
+        if (!*buf || *buf == ',') {
+            result = false;
+            goto out;
+        }
+    }
+
+out:
+    free(buf);
+    return result;
+}
+
 /*
  * Prints all options of a list that have a value to stdout
  */
diff --git a/vl.c b/vl.c
index 9379d335ac..1d27b34301 100644
--- a/vl.c
+++ b/vl.c
@@ -2827,6 +2827,7 @@ int main(int argc, char **argv, char **envp)
 
     atexit(qemu_run_exit_notifiers);
     error_set_progname(argv[0]);
+    qemu_init_exec_dir(argv[0]);
 
     g_mem_set_vtable(&mem_trace);
     if (!g_thread_supported()) {
@@ -3865,7 +3866,7 @@ int main(int argc, char **argv, char **envp)
     /* If no data_dir is specified then try to find it relative to the
        executable path.  */
     if (data_dir_idx < ARRAY_SIZE(data_dir)) {
-        data_dir[data_dir_idx] = os_find_datadir(argv[0]);
+        data_dir[data_dir_idx] = os_find_datadir();
         if (data_dir[data_dir_idx] != NULL) {
             data_dir_idx++;
         }