summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS8
-rw-r--r--Makefile6
-rw-r--r--Makefile.objs1
-rw-r--r--audio/Makefile.objs30
-rw-r--r--audio/alsaaudio.c8
-rw-r--r--audio/audio.c83
-rw-r--r--audio/audio_int.h14
-rw-r--r--audio/coreaudio.c8
-rw-r--r--audio/dsoundaudio.c8
-rw-r--r--audio/noaudio.c8
-rw-r--r--audio/ossaudio.c8
-rw-r--r--audio/paaudio.c8
-rw-r--r--audio/sdlaudio.c8
-rw-r--r--audio/spiceaudio.c8
-rw-r--r--audio/wavaudio.c8
-rw-r--r--block.c138
-rw-r--r--block/Makefile.objs2
-rw-r--r--block/create.c76
-rw-r--r--block/crypto.c7
-rw-r--r--block/file-posix.c93
-rw-r--r--block/file-win32.c47
-rw-r--r--block/gluster.c135
-rw-r--r--block/iscsi.c6
-rw-r--r--block/nfs.c244
-rw-r--r--block/parallels.c17
-rw-r--r--block/qcow2-bitmap.c4
-rw-r--r--block/qcow2-cluster.c24
-rw-r--r--block/qcow2-refcount.c52
-rw-r--r--block/qcow2-snapshot.c24
-rw-r--r--block/qcow2.c552
-rw-r--r--block/qcow2.h12
-rw-r--r--block/qed-check.c1
-rw-r--r--block/qed-table.c26
-rw-r--r--block/qed.c66
-rw-r--r--block/rbd.c403
-rw-r--r--block/sheepdog.c321
-rw-r--r--block/ssh.c290
-rw-r--r--block/vdi.c6
-rw-r--r--block/vhdx.c7
-rw-r--r--block/vmdk.c7
-rwxr-xr-xconfigure12
-rw-r--r--default-configs/arm-softmmu.mak9
-rw-r--r--hmp-commands.hx7
-rw-r--r--hmp.c4
-rw-r--r--hw/arm/Makefile.objs31
-rw-r--r--hw/arm/boot.c4
-rw-r--r--hw/arm/fsl-imx7.c582
-rw-r--r--hw/arm/mcimx7d-sabre.c90
-rw-r--r--hw/arm/virt.c30
-rw-r--r--hw/arm/xlnx-zynqmp.c2
-rw-r--r--hw/display/vga.c2
-rw-r--r--hw/pci-host/Makefile.objs2
-rw-r--r--hw/pci-host/designware.c754
-rw-r--r--hw/sd/Makefile.objs2
-rw-r--r--hw/sd/sd.c55
-rw-r--r--hw/sd/sdhci.c4
-rw-r--r--hw/sd/sdmmc-internal.c72
-rw-r--r--hw/sd/sdmmc-internal.h24
-rw-r--r--hw/sd/trace-events8
-rw-r--r--hw/usb/redirect.c4
-rw-r--r--include/block/block.h2
-rw-r--r--include/block/block_int.h13
-rw-r--r--include/hw/arm/fsl-imx7.h222
-rw-r--r--include/hw/pci-host/designware.h102
-rw-r--r--include/hw/pci/pci_ids.h2
-rw-r--r--include/qapi/qmp/qdict.h6
-rw-r--r--include/qemu/log-for-trace.h35
-rw-r--r--include/qemu/log.h18
-rw-r--r--include/qemu/module.h1
-rw-r--r--include/qemu/option.h2
-rw-r--r--include/ui/console.h2
-rw-r--r--include/ui/gtk.h14
-rw-r--r--include/ui/spice-display.h9
-rw-r--r--linux-user/aarch64/target_syscall.h3
-rw-r--r--linux-user/signal.c409
-rw-r--r--linux-user/syscall.c27
-rw-r--r--migration/block.c12
-rw-r--r--migration/migration.c5
-rw-r--r--migration/ram.c8
-rw-r--r--qapi/block-core.json326
-rw-r--r--qapi/ui.json10
-rw-r--r--qobject/qdict.c34
-rwxr-xr-xscripts/create_config2
-rwxr-xr-xscripts/simpletrace.py6
-rwxr-xr-xscripts/tracetool.py2
-rw-r--r--scripts/tracetool/__init__.py52
-rw-r--r--scripts/tracetool/backend/log.py13
-rw-r--r--target/arm/cpu-qom.h2
-rw-r--r--target/arm/cpu.c103
-rw-r--r--target/arm/cpu.h11
-rw-r--r--target/arm/cpu64.c113
-rw-r--r--target/arm/kvm.c51
-rw-r--r--target/arm/kvm32.c8
-rw-r--r--target/arm/kvm64.c8
-rw-r--r--target/arm/kvm_arm.h35
-rw-r--r--target/m68k/fpu_helper.c35
-rw-r--r--target/m68k/helper.h7
-rw-r--r--target/m68k/softfloat.c1019
-rw-r--r--target/m68k/softfloat.h8
-rw-r--r--target/m68k/softfloat_fpsp_tables.h374
-rw-r--r--target/m68k/translate.c21
-rw-r--r--tests/check-qdict.c129
-rw-r--r--tests/migration-test.c17
-rwxr-xr-xtests/qemu-iotests/03052
-rw-r--r--tests/qemu-iotests/030.out4
-rw-r--r--tests/qemu-iotests/049.out8
-rwxr-xr-xtests/qemu-iotests/0595
-rwxr-xr-xtests/qemu-iotests/08022
-rw-r--r--tests/qemu-iotests/080.out58
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/0960
-rw-r--r--tests/qemu-iotests/112.out4
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1240
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1290
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1320
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1360
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1390
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1480
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1520
-rwxr-xr-xtests/qemu-iotests/1538
-rw-r--r--tests/qemu-iotests/153.out7
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/1630
-rwxr-xr-xtests/qemu-iotests/20315
-rw-r--r--tests/qemu-iotests/203.out5
-rwxr-xr-x[-rw-r--r--]tests/qemu-iotests/2050
-rwxr-xr-xtests/qemu-iotests/206436
-rw-r--r--tests/qemu-iotests/206.out209
-rwxr-xr-xtests/qemu-iotests/207261
-rw-r--r--tests/qemu-iotests/207.out75
-rw-r--r--tests/qemu-iotests/group2
-rw-r--r--tests/test-qemu-opts.c253
-rw-r--r--trace-events6
-rw-r--r--ui/Makefile.objs25
-rw-r--r--ui/console.c34
-rw-r--r--ui/gtk-egl.c63
-rw-r--r--ui/gtk.c68
-rw-r--r--ui/spice-display.c194
-rw-r--r--ui/trace-events12
-rw-r--r--ui/vnc.c10
-rw-r--r--util/coroutine-ucontext.c4
-rw-r--r--util/qemu-option.c42
140 files changed, 8263 insertions, 1369 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index f409f3b158..354a18ce49 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1100,6 +1100,14 @@ M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
 S: Maintained
 F: hw/ssi/xilinx_*
 
+SD (Secure Card)
+M: Philippe Mathieu-Daudé <f4bug@amsat.org>
+S: Odd Fixes
+F: include/hw/sd/sd*
+F: hw/sd/core.c
+F: hw/sd/sd*
+F: tests/sd*
+
 USB
 M: Gerd Hoffmann <kraxel@redhat.com>
 S: Maintained
diff --git a/Makefile b/Makefile
index 9a75c48ae0..c8116694a0 100644
--- a/Makefile
+++ b/Makefile
@@ -425,6 +425,10 @@ dummy := $(call unnest-vars,, \
                 io-obj-y \
                 common-obj-y \
                 common-obj-m \
+                ui-obj-y \
+                ui-obj-m \
+                audio-obj-y \
+                audio-obj-m \
                 trace-obj-y)
 
 include $(SRC_PATH)/tests/Makefile.include
@@ -851,7 +855,7 @@ ifneq ($(BLOBS),)
 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
 	done
 endif
-ifeq ($(CONFIG_GTK),y)
+ifeq ($(CONFIG_GTK),m)
 	$(MAKE) -C po $@
 endif
 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
diff --git a/Makefile.objs b/Makefile.objs
index d8b44a2d3c..c6c9b8fc21 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -104,6 +104,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/
 common-obj-y += migration/
 
 common-obj-y += audio/
+common-obj-m += audio/
 common-obj-y += hw/
 
 common-obj-y += replay/
diff --git a/audio/Makefile.objs b/audio/Makefile.objs
index f6ce5c6744..db4fa7f18f 100644
--- a/audio/Makefile.objs
+++ b/audio/Makefile.objs
@@ -1,19 +1,31 @@
 common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
-common-obj-$(CONFIG_AUDIO_SDL) += sdlaudio.o
-common-obj-$(CONFIG_AUDIO_OSS) += ossaudio.o
 common-obj-$(CONFIG_SPICE) += spiceaudio.o
 common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
-common-obj-$(CONFIG_AUDIO_ALSA) += alsaaudio.o
 common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
-common-obj-$(CONFIG_AUDIO_PA) += paaudio.o
 common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
 common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
 common-obj-y += wavcapture.o
 
-sdlaudio.o-cflags := $(SDL_CFLAGS)
-sdlaudio.o-libs := $(SDL_LIBS)
-alsaaudio.o-libs := $(ALSA_LIBS)
-paaudio.o-libs := $(PULSE_LIBS)
 coreaudio.o-libs := $(COREAUDIO_LIBS)
 dsoundaudio.o-libs := $(DSOUND_LIBS)
-ossaudio.o-libs := $(OSS_LIBS)
+
+# alsa module
+common-obj-$(CONFIG_AUDIO_ALSA) += alsa.mo
+alsa.mo-objs = alsaaudio.o
+alsa.mo-libs := $(ALSA_LIBS)
+
+# oss module
+common-obj-$(CONFIG_AUDIO_OSS) += oss.mo
+oss.mo-objs = ossaudio.o
+oss.mo-libs := $(OSS_LIBS)
+
+# pulseaudio module
+common-obj-$(CONFIG_AUDIO_PA) += pa.mo
+pa.mo-objs = paaudio.o
+pa.mo-libs := $(PULSE_LIBS)
+
+# sdl module
+common-obj-$(CONFIG_AUDIO_SDL) += sdl.mo
+sdl.mo-objs = sdlaudio.o
+sdl.mo-cflags := $(SDL_CFLAGS)
+sdl.mo-libs := $(SDL_LIBS)
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 92a96f8b2b..362a2276fd 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -1213,7 +1213,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
     .ctl_in   = alsa_ctl_in,
 };
 
-struct audio_driver alsa_audio_driver = {
+static struct audio_driver alsa_audio_driver = {
     .name           = "alsa",
     .descr          = "ALSA http://www.alsa-project.org",
     .options        = alsa_options,
@@ -1226,3 +1226,9 @@ struct audio_driver alsa_audio_driver = {
     .voice_size_out = sizeof (ALSAVoiceOut),
     .voice_size_in  = sizeof (ALSAVoiceIn)
 };
+
+static void register_audio_alsa(void)
+{
+    audio_driver_register(&alsa_audio_driver);
+}
+type_init(register_audio_alsa);
diff --git a/audio/audio.c b/audio/audio.c
index 7658d2af66..6eccdb17ee 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -45,15 +45,49 @@
    The 1st one is the one used by default, that is the reason
     that we generate the list.
 */
-static struct audio_driver *drvtab[] = {
-#ifdef CONFIG_SPICE
-    &spice_audio_driver,
-#endif
+static const char *audio_prio_list[] = {
+    "spice",
     CONFIG_AUDIO_DRIVERS
-    &no_audio_driver,
-    &wav_audio_driver
+    "none",
+    "wav",
 };
 
+static QLIST_HEAD(, audio_driver) audio_drivers;
+
+void audio_driver_register(audio_driver *drv)
+{
+    QLIST_INSERT_HEAD(&audio_drivers, drv, next);
+}
+
+audio_driver *audio_driver_lookup(const char *name)
+{
+    struct audio_driver *d;
+
+    QLIST_FOREACH(d, &audio_drivers, next) {
+        if (strcmp(name, d->name) == 0) {
+            return d;
+        }
+    }
+
+    audio_module_load_one(name);
+    QLIST_FOREACH(d, &audio_drivers, next) {
+        if (strcmp(name, d->name) == 0) {
+            return d;
+        }
+    }
+
+    return NULL;
+}
+
+static void audio_module_load_all(void)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(audio_prio_list); i++) {
+        audio_driver_lookup(audio_prio_list[i]);
+    }
+}
+
 struct fixed_settings {
     int enabled;
     int nb_voices;
@@ -1656,11 +1690,13 @@ static void audio_pp_nb_voices (const char *typ, int nb)
 
 void AUD_help (void)
 {
-    size_t i;
+    struct audio_driver *d;
+
+    /* make sure we print the help text for modular drivers too */
+    audio_module_load_all();
 
     audio_process_options ("AUDIO", audio_options);
-    for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
-        struct audio_driver *d = drvtab[i];
+    QLIST_FOREACH(d, &audio_drivers, next) {
         if (d->options) {
             audio_process_options (d->name, d->options);
         }
@@ -1672,8 +1708,7 @@ void AUD_help (void)
 
     printf ("Available drivers:\n");
 
-    for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
-        struct audio_driver *d = drvtab[i];
+    QLIST_FOREACH(d, &audio_drivers, next) {
 
         printf ("Name: %s\n", d->name);
         printf ("Description: %s\n", d->descr);
@@ -1807,6 +1842,7 @@ static void audio_init (void)
     const char *drvname;
     VMChangeStateEntry *e;
     AudioState *s = &glob_audio_state;
+    struct audio_driver *driver;
 
     if (s->drv) {
         return;
@@ -1842,32 +1878,27 @@ static void audio_init (void)
     }
 
     if (drvname) {
-        int found = 0;
-
-        for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
-            if (!strcmp (drvname, drvtab[i]->name)) {
-                done = !audio_driver_init (s, drvtab[i]);
-                found = 1;
-                break;
-            }
-        }
-
-        if (!found) {
+        driver = audio_driver_lookup(drvname);
+        if (driver) {
+            done = !audio_driver_init(s, driver);
+        } else {
             dolog ("Unknown audio driver `%s'\n", drvname);
             dolog ("Run with -audio-help to list available drivers\n");
         }
     }
 
     if (!done) {
-        for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) {
-            if (drvtab[i]->can_be_default) {
-                done = !audio_driver_init (s, drvtab[i]);
+        for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) {
+            driver = audio_driver_lookup(audio_prio_list[i]);
+            if (driver && driver->can_be_default) {
+                done = !audio_driver_init(s, driver);
             }
         }
     }
 
     if (!done) {
-        done = !audio_driver_init (s, &no_audio_driver);
+        driver = audio_driver_lookup("none");
+        done = !audio_driver_init(s, driver);
         assert(done);
         dolog("warning: Using timer based audio emulation\n");
     }
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 700bd43143..244b454012 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -141,6 +141,7 @@ struct SWVoiceIn {
     QLIST_ENTRY (SWVoiceIn) entries;
 };
 
+typedef struct audio_driver audio_driver;
 struct audio_driver {
     const char *name;
     const char *descr;
@@ -154,6 +155,7 @@ struct audio_driver {
     int voice_size_out;
     int voice_size_in;
     int ctl_caps;
+    QLIST_ENTRY(audio_driver) next;
 };
 
 struct audio_pcm_ops {
@@ -203,17 +205,11 @@ struct AudioState {
     int vm_running;
 };
 
-extern struct audio_driver no_audio_driver;
-extern struct audio_driver oss_audio_driver;
-extern struct audio_driver sdl_audio_driver;
-extern struct audio_driver wav_audio_driver;
-extern struct audio_driver alsa_audio_driver;
-extern struct audio_driver coreaudio_audio_driver;
-extern struct audio_driver dsound_audio_driver;
-extern struct audio_driver pa_audio_driver;
-extern struct audio_driver spice_audio_driver;
 extern const struct mixeng_volume nominal_volume;
 
+void audio_driver_register(audio_driver *drv);
+audio_driver *audio_driver_lookup(const char *name);
+
 void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
 void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
 
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index c75142084f..638c60b300 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -722,7 +722,7 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
     .ctl_out  = coreaudio_ctl_out
 };
 
-struct audio_driver coreaudio_audio_driver = {
+static struct audio_driver coreaudio_audio_driver = {
     .name           = "coreaudio",
     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
     .options        = coreaudio_options,
@@ -735,3 +735,9 @@ struct audio_driver coreaudio_audio_driver = {
     .voice_size_out = sizeof (coreaudioVoiceOut),
     .voice_size_in  = 0
 };
+
+static void register_audio_coreaudio(void)
+{
+    audio_driver_register(&coreaudio_audio_driver);
+}
+type_init(register_audio_coreaudio);
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index bc39cb9b4d..3ed73a30d1 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -890,7 +890,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
     .ctl_in   = dsound_ctl_in
 };
 
-struct audio_driver dsound_audio_driver = {
+static struct audio_driver dsound_audio_driver = {
     .name           = "dsound",
     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
     .options        = dsound_options,
@@ -903,3 +903,9 @@ struct audio_driver dsound_audio_driver = {
     .voice_size_out = sizeof (DSoundVoiceOut),
     .voice_size_in  = sizeof (DSoundVoiceIn)
 };
+
+static void register_audio_dsound(void)
+{
+    audio_driver_register(&dsound_audio_driver);
+}
+type_init(register_audio_dsound);
diff --git a/audio/noaudio.c b/audio/noaudio.c
index 9ca9eaf01f..1bfebeca7d 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -160,7 +160,7 @@ static struct audio_pcm_ops no_pcm_ops = {
     .ctl_in   = no_ctl_in
 };
 
-struct audio_driver no_audio_driver = {
+static struct audio_driver no_audio_driver = {
     .name           = "none",
     .descr          = "Timer based audio emulation",
     .options        = NULL,
@@ -173,3 +173,9 @@ struct audio_driver no_audio_driver = {
     .voice_size_out = sizeof (NoVoiceOut),
     .voice_size_in  = sizeof (NoVoiceIn)
 };
+
+static void register_audio_none(void)
+{
+    audio_driver_register(&no_audio_driver);
+}
+type_init(register_audio_none);
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index a0428881c2..6c69622b4c 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -922,7 +922,7 @@ static struct audio_pcm_ops oss_pcm_ops = {
     .ctl_in   = oss_ctl_in
 };
 
-struct audio_driver oss_audio_driver = {
+static struct audio_driver oss_audio_driver = {
     .name           = "oss",
     .descr          = "OSS http://www.opensound.com",
     .options        = oss_options,
@@ -935,3 +935,9 @@ struct audio_driver oss_audio_driver = {
     .voice_size_out = sizeof (OSSVoiceOut),
     .voice_size_in  = sizeof (OSSVoiceIn)
 };
+
+static void register_audio_oss(void)
+{
+    audio_driver_register(&oss_audio_driver);
+}
+type_init(register_audio_oss);
diff --git a/audio/paaudio.c b/audio/paaudio.c
index aa0a7477d3..949769774d 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -937,7 +937,7 @@ static struct audio_pcm_ops qpa_pcm_ops = {
     .ctl_in   = qpa_ctl_in
 };
 
-struct audio_driver pa_audio_driver = {
+static struct audio_driver pa_audio_driver = {
     .name           = "pa",
     .descr          = "http://www.pulseaudio.org/",
     .options        = qpa_options,
@@ -951,3 +951,9 @@ struct audio_driver pa_audio_driver = {
     .voice_size_in  = sizeof (PAVoiceIn),
     .ctl_caps       = VOICE_VOLUME_CAP
 };
+
+static void register_audio_pa(void)
+{
+    audio_driver_register(&pa_audio_driver);
+}
+type_init(register_audio_pa);
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index e92135bd2f..9db5ac92bc 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -500,7 +500,7 @@ static struct audio_pcm_ops sdl_pcm_ops = {
     .ctl_out  = sdl_ctl_out,
 };
 
-struct audio_driver sdl_audio_driver = {
+static struct audio_driver sdl_audio_driver = {
     .name           = "sdl",
     .descr          = "SDL http://www.libsdl.org",
     .options        = sdl_options,
@@ -513,3 +513,9 @@ struct audio_driver sdl_audio_driver = {
     .voice_size_out = sizeof (SDLVoiceOut),
     .voice_size_in  = 0
 };
+
+static void register_audio_sdl(void)
+{
+    audio_driver_register(&sdl_audio_driver);
+}
+type_init(register_audio_sdl);
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index 5580e76307..6ad0eafbc6 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -391,7 +391,7 @@ static struct audio_pcm_ops audio_callbacks = {
     .ctl_in   = line_in_ctl,
 };
 
-struct audio_driver spice_audio_driver = {
+static struct audio_driver spice_audio_driver = {
     .name           = "spice",
     .descr          = "spice audio driver",
     .options        = audio_options,
@@ -411,3 +411,9 @@ void qemu_spice_audio_init (void)
 {
     spice_audio_driver.can_be_default = 1;
 }
+
+static void register_audio_spice(void)
+{
+    audio_driver_register(&spice_audio_driver);
+}
+type_init(register_audio_spice);
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 068a595732..40adfa30c3 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -278,7 +278,7 @@ static struct audio_pcm_ops wav_pcm_ops = {
     .ctl_out  = wav_ctl_out,
 };
 
-struct audio_driver wav_audio_driver = {
+static struct audio_driver wav_audio_driver = {
     .name           = "wav",
     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
     .options        = wav_options,
@@ -291,3 +291,9 @@ struct audio_driver wav_audio_driver = {
     .voice_size_out = sizeof (WAVVoiceOut),
     .voice_size_in  = 0
 };
+
+static void register_audio_wav(void)
+{
+    audio_driver_register(&wav_audio_driver);
+}
+type_init(register_audio_wav);
diff --git a/block.c b/block.c
index 4f76714f6b..75a9fd49de 100644
--- a/block.c
+++ b/block.c
@@ -34,6 +34,8 @@
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qjson.h"
 #include "qapi/qmp/qstring.h"
+#include "qapi/qobject-output-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
 #include "sysemu/block-backend.h"
 #include "sysemu/sysemu.h"
 #include "qemu/notify.h"
@@ -368,7 +370,7 @@ BlockDriver *bdrv_find_format(const char *format_name)
     return bdrv_do_find_format(format_name);
 }
 
-static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
+int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
 {
     static const char *whitelist_rw[] = {
         CONFIG_BDRV_RW_WHITELIST
@@ -2406,6 +2408,51 @@ BdrvChild *bdrv_open_child(const char *filename,
     return c;
 }
 
+/* TODO Future callers may need to specify parent/child_role in order for
+ * option inheritance to work. Existing callers use it for the root node. */
+BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
+{
+    BlockDriverState *bs = NULL;
+    Error *local_err = NULL;
+    QObject *obj = NULL;
+    QDict *qdict = NULL;
+    const char *reference = NULL;
+    Visitor *v = NULL;
+
+    if (ref->type == QTYPE_QSTRING) {
+        reference = ref->u.reference;
+    } else {
+        BlockdevOptions *options = &ref->u.definition;
+        assert(ref->type == QTYPE_QDICT);
+
+        v = qobject_output_visitor_new(&obj);
+        visit_type_BlockdevOptions(v, NULL, &options, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            goto fail;
+        }
+        visit_complete(v, &obj);
+
+        qdict = qobject_to_qdict(obj);
+        qdict_flatten(qdict);
+
+        /* bdrv_open_inherit() defaults to the values in bdrv_flags (for
+         * compatibility with other callers) rather than what we want as the
+         * real defaults. Apply the defaults here instead. */
+        qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
+        qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
+        qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off");
+    }
+
+    bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp);
+    obj = NULL;
+
+fail:
+    qobject_decref(obj);
+    visit_free(v);
+    return bs;
+}
+
 static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
                                                    int flags,
                                                    QDict *snapshot_options,
@@ -3455,17 +3502,54 @@ static void bdrv_delete(BlockDriverState *bs)
  * free of errors) or -errno when an internal error occurred. The results of the
  * check are stored in res.
  */
-int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix)
+static int coroutine_fn bdrv_co_check(BlockDriverState *bs,
+                                      BdrvCheckResult *res, BdrvCheckMode fix)
 {
     if (bs->drv == NULL) {
         return -ENOMEDIUM;
     }
-    if (bs->drv->bdrv_check == NULL) {
+    if (bs->drv->bdrv_co_check == NULL) {
         return -ENOTSUP;
     }
 
     memset(res, 0, sizeof(*res));
-    return bs->drv->bdrv_check(bs, res, fix);
+    return bs->drv->bdrv_co_check(bs, res, fix);
+}
+
+typedef struct CheckCo {
+    BlockDriverState *bs;
+    BdrvCheckResult *res;
+    BdrvCheckMode fix;
+    int ret;
+} CheckCo;
+
+static void bdrv_check_co_entry(void *opaque)
+{
+    CheckCo *cco = opaque;
+    cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
+}
+
+int bdrv_check(BlockDriverState *bs,
+               BdrvCheckResult *res, BdrvCheckMode fix)
+{
+    Coroutine *co;
+    CheckCo cco = {
+        .bs = bs,
+        .res = res,
+        .ret = -EINPROGRESS,
+        .fix = fix,
+    };
+
+    if (qemu_in_coroutine()) {
+        /* Fast-path if already in coroutine context */
+        bdrv_check_co_entry(&cco);
+    } else {
+        co = qemu_coroutine_create(bdrv_check_co_entry, &cco);
+        qemu_coroutine_enter(co);
+        BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS);
+    }
+
+    return cco.ret;
 }
 
 /*
@@ -3635,6 +3719,11 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
         error_setg(errp, "No medium inserted");
         return -ENOMEDIUM;
     }
+    if (offset < 0) {
+        error_setg(errp, "Image size cannot be negative");
+        return -EINVAL;
+    }
+
     if (!drv->bdrv_truncate) {
         if (bs->file && drv->is_filter) {
             return bdrv_truncate(bs->file, offset, prealloc, errp);
@@ -4209,7 +4298,8 @@ void bdrv_init_with_whitelist(void)
     bdrv_init();
 }
 
-void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
+static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
+                                                  Error **errp)
 {
     BdrvChild *child, *parent;
     uint64_t perm, shared_perm;
@@ -4225,7 +4315,7 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
     }
 
     QLIST_FOREACH(child, &bs->children, next) {
-        bdrv_invalidate_cache(child->bs, &local_err);
+        bdrv_co_invalidate_cache(child->bs, &local_err);
         if (local_err) {
             error_propagate(errp, local_err);
             return;
@@ -4255,8 +4345,8 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
     }
     bdrv_set_perm(bs, perm, shared_perm);
 
-    if (bs->drv->bdrv_invalidate_cache) {
-        bs->drv->bdrv_invalidate_cache(bs, &local_err);
+    if (bs->drv->bdrv_co_invalidate_cache) {
+        bs->drv->bdrv_co_invalidate_cache(bs, &local_err);
         if (local_err) {
             bs->open_flags |= BDRV_O_INACTIVE;
             error_propagate(errp, local_err);
@@ -4282,6 +4372,38 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
     }
 }
 
+typedef struct InvalidateCacheCo {
+    BlockDriverState *bs;
+    Error **errp;
+    bool done;
+} InvalidateCacheCo;
+
+static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque)
+{
+    InvalidateCacheCo *ico = opaque;
+    bdrv_co_invalidate_cache(ico->bs, ico->errp);
+    ico->done = true;
+}
+
+void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
+{
+    Coroutine *co;
+    InvalidateCacheCo ico = {
+        .bs = bs,
+        .done = false,
+        .errp = errp
+    };
+
+    if (qemu_in_coroutine()) {
+        /* Fast-path if already in coroutine context */
+        bdrv_invalidate_cache_co_entry(&ico);
+    } else {
+        co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico);
+        qemu_coroutine_enter(co);
+        BDRV_POLL_WHILE(bs, !ico.done);
+    }
+}
+
 void bdrv_invalidate_cache_all(Error **errp)
 {
     BlockDriverState *bs;
diff --git a/block/Makefile.objs b/block/Makefile.objs
index aede94f105..d644bac60a 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -9,7 +9,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o
 block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
 block-obj-$(CONFIG_POSIX) += file-posix.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
-block-obj-y += null.o mirror.o commit.o io.o
+block-obj-y += null.o mirror.o commit.o io.o create.o
 block-obj-y += throttle-groups.o
 block-obj-$(CONFIG_LINUX) += nvme.o
 
diff --git a/block/create.c b/block/create.c
new file mode 100644
index 0000000000..8bd8a03719
--- /dev/null
+++ b/block/create.c
@@ -0,0 +1,76 @@
+/*
+ * Block layer code related to image creation
+ *
+ * Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "block/block_int.h"
+#include "qapi/qapi-commands-block-core.h"
+#include "qapi/error.h"
+
+typedef struct BlockdevCreateCo {
+    BlockDriver *drv;
+    BlockdevCreateOptions *opts;
+    int ret;
+    Error **errp;
+} BlockdevCreateCo;
+
+static void coroutine_fn bdrv_co_create_co_entry(void *opaque)
+{
+    BlockdevCreateCo *cco = opaque;
+    cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp);
+}
+
+void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp)
+{
+    const char *fmt = BlockdevDriver_str(options->driver);
+    BlockDriver *drv = bdrv_find_format(fmt);
+    Coroutine *co;
+    BlockdevCreateCo cco;
+
+    /* If the driver is in the schema, we know that it exists. But it may not
+     * be whitelisted. */
+    assert(drv);
+    if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) {
+        error_setg(errp, "Driver is not whitelisted");
+        return;
+    }
+
+    /* Call callback if it exists */
+    if (!drv->bdrv_co_create) {
+        error_setg(errp, "Driver does not support blockdev-create");
+        return;
+    }
+
+    cco = (BlockdevCreateCo) {
+        .drv = drv,
+        .opts = options,
+        .ret = -EINPROGRESS,
+        .errp = errp,
+    };
+
+    co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco);
+    qemu_coroutine_enter(co);
+    while (cco.ret == -EINPROGRESS) {
+        aio_poll(qemu_get_aio_context(), true);
+    }
+}
diff --git a/block/crypto.c b/block/crypto.c
index 17b5c0abad..e6095e7807 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -384,6 +384,12 @@ static void block_crypto_close(BlockDriverState *bs)
     qcrypto_block_free(crypto->block);
 }
 
+static int block_crypto_reopen_prepare(BDRVReopenState *state,
+                                       BlockReopenQueue *queue, Error **errp)
+{
+    /* nothing needs checking */
+    return 0;
+}
 
 /*
  * 1 MB bounce buffer gives good performance / memory tradeoff
@@ -621,6 +627,7 @@ BlockDriver bdrv_crypto_luks = {
     .bdrv_truncate      = block_crypto_truncate,
     .create_opts        = &block_crypto_create_opts_luks,
 
+    .bdrv_reopen_prepare = block_crypto_reopen_prepare,
     .bdrv_refresh_limits = block_crypto_refresh_limits,
     .bdrv_co_preadv     = block_crypto_co_preadv,
     .bdrv_co_pwritev    = block_crypto_co_pwritev,
diff --git a/block/file-posix.c b/block/file-posix.c
index 7f2cc63c60..d7fb772c14 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -1686,11 +1686,15 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
          * file systems that do not support fallocate(), trying to check if a
          * block is allocated before allocating it, so don't do that here.
          */
-        result = -posix_fallocate(fd, current_length, offset - current_length);
-        if (result != 0) {
-            /* posix_fallocate() doesn't set errno. */
-            error_setg_errno(errp, -result,
-                             "Could not preallocate new data");
+        if (offset != current_length) {
+            result = -posix_fallocate(fd, current_length, offset - current_length);
+            if (result != 0) {
+                /* posix_fallocate() doesn't set errno. */
+                error_setg_errno(errp, -result,
+                                 "Could not preallocate new data");
+            }
+        } else {
+            result = 0;
         }
         goto out;
 #endif
@@ -1982,34 +1986,25 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
     return (int64_t)st.st_blocks * 512;
 }
 
-static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
-                                           Error **errp)
+static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
 {
+    BlockdevCreateOptionsFile *file_opts;
     int fd;
     int result = 0;
-    int64_t total_size = 0;
-    bool nocow = false;
-    PreallocMode prealloc;
-    char *buf = NULL;
-    Error *local_err = NULL;
 
-    strstart(filename, "file:", &filename);
+    /* Validate options and set default values */
+    assert(options->driver == BLOCKDEV_DRIVER_FILE);
+    file_opts = &options->u.file;
 
-    /* Read out options */
-    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
-                          BDRV_SECTOR_SIZE);
-    nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
-    buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
-    prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
-                               PREALLOC_MODE_OFF, &local_err);
-    g_free(buf);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        result = -EINVAL;
-        goto out;
+    if (!file_opts->has_nocow) {
+        file_opts->nocow = false;
+    }
+    if (!file_opts->has_preallocation) {
+        file_opts->preallocation = PREALLOC_MODE_OFF;
     }
 
-    fd = qemu_open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
+    /* Create file */
+    fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
                    0644);
     if (fd < 0) {
         result = -errno;
@@ -2017,7 +2012,7 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
         goto out;
     }
 
-    if (nocow) {
+    if (file_opts->nocow) {
 #ifdef __linux__
         /* Set NOCOW flag to solve performance issue on fs like btrfs.
          * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value
@@ -2032,7 +2027,8 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
 #endif
     }
 
-    result = raw_regular_truncate(fd, total_size, prealloc, errp);
+    result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation,
+                                  errp);
     if (result < 0) {
         goto out_close;
     }
@@ -2046,6 +2042,46 @@ out:
     return result;
 }
 
+static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
+                                           Error **errp)
+{
+    BlockdevCreateOptions options;
+    int64_t total_size = 0;
+    bool nocow = false;
+    PreallocMode prealloc;
+    char *buf = NULL;
+    Error *local_err = NULL;
+
+    /* Skip file: protocol prefix */
+    strstart(filename, "file:", &filename);
+
+    /* Read out options */
+    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+                          BDRV_SECTOR_SIZE);
+    nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
+    buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
+    prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
+                               PREALLOC_MODE_OFF, &local_err);
+    g_free(buf);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return -EINVAL;
+    }
+
+    options = (BlockdevCreateOptions) {
+        .driver     = BLOCKDEV_DRIVER_FILE,
+        .u.file     = {
+            .filename           = (char *) filename,
+            .size               = total_size,
+            .has_preallocation  = true,
+            .preallocation      = prealloc,
+            .has_nocow          = true,
+            .nocow              = nocow,
+        },
+    };
+    return raw_co_create(&options, errp);
+}
+
 /*
  * Find allocation range in @bs around offset @start.
  * May change underlying file descriptor's file offset.
@@ -2277,6 +2313,7 @@ BlockDriver bdrv_file = {
     .bdrv_reopen_commit = raw_reopen_commit,
     .bdrv_reopen_abort = raw_reopen_abort,
     .bdrv_close = raw_close,
+    .bdrv_co_create = raw_co_create,
     .bdrv_co_create_opts = raw_co_create_opts,
     .bdrv_has_zero_init = bdrv_has_zero_init_1,
     .bdrv_co_block_status = raw_co_block_status,
diff --git a/block/file-win32.c b/block/file-win32.c
index 4a430d45f1..2e2f746bb1 100644
--- a/block/file-win32.c
+++ b/block/file-win32.c
@@ -553,30 +553,59 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
     return st.st_size;
 }
 
-static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
-                                           Error **errp)
+static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
 {
+    BlockdevCreateOptionsFile *file_opts;
     int fd;
-    int64_t total_size = 0;
 
-    strstart(filename, "file:", &filename);
+    assert(options->driver == BLOCKDEV_DRIVER_FILE);
+    file_opts = &options->u.file;
 
-    /* Read out options */
-    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
-                          BDRV_SECTOR_SIZE);
+    if (file_opts->has_preallocation) {
+        error_setg(errp, "Preallocation is not supported on Windows");
+        return -EINVAL;
+    }
+    if (file_opts->has_nocow) {
+        error_setg(errp, "nocow is not supported on Windows");
+        return -EINVAL;
+    }
 
-    fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+    fd = qemu_open(file_opts->filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
                    0644);
     if (fd < 0) {
         error_setg_errno(errp, errno, "Could not create file");
         return -EIO;
     }
     set_sparse(fd);
-    ftruncate(fd, total_size);
+    ftruncate(fd, file_opts->size);
     qemu_close(fd);
+
     return 0;
 }
 
+static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
+                                           Error **errp)
+{
+    BlockdevCreateOptions options;
+    int64_t total_size = 0;
+
+    strstart(filename, "file:", &filename);
+
+    /* Read out options */
+    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+                          BDRV_SECTOR_SIZE);
+
+    options = (BlockdevCreateOptions) {
+        .driver     = BLOCKDEV_DRIVER_FILE,
+        .u.file     = {
+            .filename           = (char *) filename,
+            .size               = total_size,
+            .has_preallocation  = false,
+            .has_nocow          = false,
+        },
+    };
+    return raw_co_create(&options, errp);
+}
 
 static QemuOptsList raw_create_opts = {
     .name = "raw-create-opts",
diff --git a/block/gluster.c b/block/gluster.c
index 79b4cfdf74..63d3c37d4c 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -655,9 +655,11 @@ out:
     return -errno;
 }
 
-static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
-                                      const char *filename,
-                                      QDict *options, Error **errp)
+/* Converts options given in @filename and the @options QDict into the QAPI
+ * object @gconf. */
+static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
+                              const char *filename,
+                              QDict *options, Error **errp)
 {
     int ret;
     if (filename) {
@@ -668,8 +670,7 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
                                     "[host[:port]]volume/path[?socket=...]"
                                     "[,file.debug=N]"
                                     "[,file.logfile=/path/filename.log]\n");
-            errno = -ret;
-            return NULL;
+            return ret;
         }
     } else {
         ret = qemu_gluster_parse_json(gconf, options, errp);
@@ -685,10 +686,23 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
                              "file.server.1.transport=unix,"
                              "file.server.1.socket=/var/run/glusterd.socket ..."
                              "\n");
-            errno = -ret;
-            return NULL;
+            return ret;
         }
+    }
 
+    return 0;
+}
+
+static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
+                                      const char *filename,
+                                      QDict *options, Error **errp)
+{
+    int ret;
+
+    ret = qemu_gluster_parse(gconf, filename, options, errp);
+    if (ret < 0) {
+        errno = -ret;
+        return NULL;
     }
 
     return qemu_gluster_glfs_init(gconf, errp);
@@ -1021,20 +1035,72 @@ static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset,
     return 0;
 }
 
+static int qemu_gluster_co_create(BlockdevCreateOptions *options,
+                                  Error **errp)
+{
+    BlockdevCreateOptionsGluster *opts = &options->u.gluster;
+    struct glfs *glfs;
+    struct glfs_fd *fd = NULL;
+    int ret = 0;
+
+    assert(options->driver == BLOCKDEV_DRIVER_GLUSTER);
+
+    glfs = qemu_gluster_glfs_init(opts->location, errp);
+    if (!glfs) {
+        ret = -errno;
+        goto out;
+    }
+
+    fd = glfs_creat(glfs, opts->location->path,
+                    O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
+    if (!fd) {
+        ret = -errno;
+        goto out;
+    }
+
+    ret = qemu_gluster_do_truncate(fd, opts->size, opts->preallocation, errp);
+
+out:
+    if (fd) {
+        if (glfs_close(fd) != 0 && ret == 0) {
+            ret = -errno;
+        }
+    }
+    glfs_clear_preopened(glfs);
+    return ret;
+}
+
 static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
                                                     QemuOpts *opts,
                                                     Error **errp)
 {
+    BlockdevCreateOptions *options;
+    BlockdevCreateOptionsGluster *gopts;
     BlockdevOptionsGluster *gconf;
-    struct glfs *glfs;
-    struct glfs_fd *fd = NULL;
-    int ret = 0;
-    PreallocMode prealloc;
-    int64_t total_size = 0;
     char *tmp = NULL;
     Error *local_err = NULL;
+    int ret;
+
+    options = g_new0(BlockdevCreateOptions, 1);
+    options->driver = BLOCKDEV_DRIVER_GLUSTER;
+    gopts = &options->u.gluster;
 
     gconf = g_new0(BlockdevOptionsGluster, 1);
+    gopts->location = gconf;
+
+    gopts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+                           BDRV_SECTOR_SIZE);
+
+    tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
+    gopts->preallocation = qapi_enum_parse(&PreallocMode_lookup, tmp,
+                                           PREALLOC_MODE_OFF, &local_err);
+    g_free(tmp);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        ret = -EINVAL;
+        goto fail;
+    }
+
     gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG,
                                            GLUSTER_DEBUG_DEFAULT);
     if (gconf->debug < 0) {
@@ -1050,42 +1116,19 @@ static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
     }
     gconf->has_logfile = true;
 
-    glfs = qemu_gluster_init(gconf, filename, NULL, errp);
-    if (!glfs) {
-        ret = -errno;
-        goto out;
-    }
-
-    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
-                          BDRV_SECTOR_SIZE);
-
-    tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
-    prealloc = qapi_enum_parse(&PreallocMode_lookup, tmp, PREALLOC_MODE_OFF,
-                               &local_err);
-    g_free(tmp);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        ret = -EINVAL;
-        goto out;
+    ret = qemu_gluster_parse(gconf, filename, NULL, errp);
+    if (ret < 0) {
+        goto fail;
     }
 
-    fd = glfs_creat(glfs, gconf->path,
-                    O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
-    if (!fd) {
-        ret = -errno;
-        goto out;
+    ret = qemu_gluster_co_create(options, errp);
+    if (ret < 0) {
+        goto fail;
     }
 
-    ret = qemu_gluster_do_truncate(fd, total_size, prealloc, errp);
-
-out:
-    if (fd) {
-        if (glfs_close(fd) != 0 && ret == 0) {
-            ret = -errno;
-        }
-    }
-    qapi_free_BlockdevOptionsGluster(gconf);
-    glfs_clear_preopened(glfs);
+    ret = 0;
+fail:
+    qapi_free_BlockdevCreateOptions(options);
     return ret;
 }
 
@@ -1436,6 +1479,7 @@ static BlockDriver bdrv_gluster = {
     .bdrv_reopen_commit           = qemu_gluster_reopen_commit,
     .bdrv_reopen_abort            = qemu_gluster_reopen_abort,
     .bdrv_close                   = qemu_gluster_close,
+    .bdrv_co_create               = qemu_gluster_co_create,
     .bdrv_co_create_opts          = qemu_gluster_co_create_opts,
     .bdrv_getlength               = qemu_gluster_getlength,
     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
@@ -1464,6 +1508,7 @@ static BlockDriver bdrv_gluster_tcp = {
     .bdrv_reopen_commit           = qemu_gluster_reopen_commit,
     .bdrv_reopen_abort            = qemu_gluster_reopen_abort,
     .bdrv_close                   = qemu_gluster_close,
+    .bdrv_co_create               = qemu_gluster_co_create,
     .bdrv_co_create_opts          = qemu_gluster_co_create_opts,
     .bdrv_getlength               = qemu_gluster_getlength,
     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
@@ -1492,6 +1537,7 @@ static BlockDriver bdrv_gluster_unix = {
     .bdrv_reopen_commit           = qemu_gluster_reopen_commit,
     .bdrv_reopen_abort            = qemu_gluster_reopen_abort,
     .bdrv_close                   = qemu_gluster_close,
+    .bdrv_co_create               = qemu_gluster_co_create,
     .bdrv_co_create_opts          = qemu_gluster_co_create_opts,
     .bdrv_getlength               = qemu_gluster_getlength,
     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
@@ -1526,6 +1572,7 @@ static BlockDriver bdrv_gluster_rdma = {
     .bdrv_reopen_commit           = qemu_gluster_reopen_commit,
     .bdrv_reopen_abort            = qemu_gluster_reopen_abort,
     .bdrv_close                   = qemu_gluster_close,
+    .bdrv_co_create               = qemu_gluster_co_create,
     .bdrv_co_create_opts          = qemu_gluster_co_create_opts,
     .bdrv_getlength               = qemu_gluster_getlength,
     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
diff --git a/block/iscsi.c b/block/iscsi.c
index 8bf0e87244..a82170f16e 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -2177,8 +2177,8 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
     return 0;
 }
 
-static void iscsi_invalidate_cache(BlockDriverState *bs,
-                                   Error **errp)
+static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs,
+                                                   Error **errp)
 {
     IscsiLun *iscsilun = bs->opaque;
     iscsi_allocmap_invalidate(iscsilun);
@@ -2209,7 +2209,7 @@ static BlockDriver bdrv_iscsi = {
     .create_opts            = &iscsi_create_opts,
     .bdrv_reopen_prepare    = iscsi_reopen_prepare,
     .bdrv_reopen_commit     = iscsi_reopen_commit,
-    .bdrv_invalidate_cache  = iscsi_invalidate_cache,
+    .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
 
     .bdrv_getlength  = iscsi_getlength,
     .bdrv_get_info   = iscsi_get_info,
diff --git a/block/nfs.c b/block/nfs.c
index 1d82ff5042..2577df4b26 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -367,49 +367,6 @@ static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
     return task.ret;
 }
 
-static QemuOptsList runtime_opts = {
-    .name = "nfs",
-    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
-    .desc = {
-        {
-            .name = "path",
-            .type = QEMU_OPT_STRING,
-            .help = "Path of the image on the host",
-        },
-        {
-            .name = "user",
-            .type = QEMU_OPT_NUMBER,
-            .help = "UID value to use when talking to the server",
-        },
-        {
-            .name = "group",
-            .type = QEMU_OPT_NUMBER,
-            .help = "GID value to use when talking to the server",
-        },
-        {
-            .name = "tcp-syn-count",
-            .type = QEMU_OPT_NUMBER,
-            .help = "Number of SYNs to send during the session establish",
-        },
-        {
-            .name = "readahead-size",
-            .type = QEMU_OPT_NUMBER,
-            .help = "Set the readahead size in bytes",
-        },
-        {
-            .name = "page-cache-size",
-            .type = QEMU_OPT_NUMBER,
-            .help = "Set the pagecache size in bytes",
-        },
-        {
-            .name = "debug",
-            .type = QEMU_OPT_NUMBER,
-            .help = "Set the NFS debug level (max 2)",
-        },
-        { /* end of list */ }
-    },
-};
-
 static void nfs_detach_aio_context(BlockDriverState *bs)
 {
     NFSClient *client = bs->opaque;
@@ -452,71 +409,16 @@ static void nfs_file_close(BlockDriverState *bs)
     nfs_client_close(client);
 }
 
-static NFSServer *nfs_config(QDict *options, Error **errp)
-{
-    NFSServer *server = NULL;
-    QDict *addr = NULL;
-    QObject *crumpled_addr = NULL;
-    Visitor *iv = NULL;
-    Error *local_error = NULL;
-
-    qdict_extract_subqdict(options, &addr, "server.");
-    if (!qdict_size(addr)) {
-        error_setg(errp, "NFS server address missing");
-        goto out;
-    }
-
-    crumpled_addr = qdict_crumple(addr, errp);
-    if (!crumpled_addr) {
-        goto out;
-    }
-
-    /*
-     * Caution: this works only because all scalar members of
-     * NFSServer are QString in @crumpled_addr.  The visitor expects
-     * @crumpled_addr to be typed according to the QAPI schema.  It
-     * is when @options come from -blockdev or blockdev_add.  But when
-     * they come from -drive, they're all QString.
-     */
-    iv = qobject_input_visitor_new(crumpled_addr);
-    visit_type_NFSServer(iv, NULL, &server, &local_error);
-    if (local_error) {
-        error_propagate(errp, local_error);
-        goto out;
-    }
-
-out:
-    QDECREF(addr);
-    qobject_decref(crumpled_addr);
-    visit_free(iv);
-    return server;
-}
-
-
-static int64_t nfs_client_open(NFSClient *client, QDict *options,
+static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts,
                                int flags, int open_flags, Error **errp)
 {
     int64_t ret = -EINVAL;
-    QemuOpts *opts = NULL;
-    Error *local_err = NULL;
     struct stat st;
     char *file = NULL, *strp = NULL;
 
     qemu_mutex_init(&client->mutex);
-    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
-    qemu_opts_absorb_qdict(opts, options, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        ret = -EINVAL;
-        goto fail;
-    }
 
-    client->path = g_strdup(qemu_opt_get(opts, "path"));
-    if (!client->path) {
-        ret = -EINVAL;
-        error_setg(errp, "No path was specified");
-        goto fail;
-    }
+    client->path = g_strdup(opts->path);
 
     strp = strrchr(client->path, '/');
     if (strp == NULL) {
@@ -526,12 +428,10 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
     file = g_strdup(strp);
     *strp = 0;
 
-    /* Pop the config into our state object, Exit if invalid */
-    client->server = nfs_config(options, errp);
-    if (!client->server) {
-        ret = -EINVAL;
-        goto fail;
-    }
+    /* Steal the NFSServer object from opts; set the original pointer to NULL
+     * to avoid use after free and double free. */
+    client->server = opts->server;
+    opts->server = NULL;
 
     client->context = nfs_init_context();
     if (client->context == NULL) {
@@ -539,29 +439,29 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
         goto fail;
     }
 
-    if (qemu_opt_get(opts, "user")) {
-        client->uid = qemu_opt_get_number(opts, "user", 0);
+    if (opts->has_user) {
+        client->uid = opts->user;
         nfs_set_uid(client->context, client->uid);
     }
 
-    if (qemu_opt_get(opts, "group")) {
-        client->gid = qemu_opt_get_number(opts, "group", 0);
+    if (opts->has_group) {
+        client->gid = opts->group;
         nfs_set_gid(client->context, client->gid);
     }
 
-    if (qemu_opt_get(opts, "tcp-syn-count")) {
-        client->tcp_syncnt = qemu_opt_get_number(opts, "tcp-syn-count", 0);
+    if (opts->has_tcp_syn_count) {
+        client->tcp_syncnt = opts->tcp_syn_count;
         nfs_set_tcp_syncnt(client->context, client->tcp_syncnt);
     }
 
 #ifdef LIBNFS_FEATURE_READAHEAD
-    if (qemu_opt_get(opts, "readahead-size")) {
+    if (opts->has_readahead_size) {
         if (open_flags & BDRV_O_NOCACHE) {
             error_setg(errp, "Cannot enable NFS readahead "
                              "if cache.direct = on");
             goto fail;
         }
-        client->readahead = qemu_opt_get_number(opts, "readahead-size", 0);
+        client->readahead = opts->readahead_size;
         if (client->readahead > QEMU_NFS_MAX_READAHEAD_SIZE) {
             warn_report("Truncating NFS readahead size to %d",
                         QEMU_NFS_MAX_READAHEAD_SIZE);
@@ -576,13 +476,13 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
 #endif
 
 #ifdef LIBNFS_FEATURE_PAGECACHE
-    if (qemu_opt_get(opts, "page-cache-size")) {
+    if (opts->has_page_cache_size) {
         if (open_flags & BDRV_O_NOCACHE) {
             error_setg(errp, "Cannot enable NFS pagecache "
                              "if cache.direct = on");
             goto fail;
         }
-        client->pagecache = qemu_opt_get_number(opts, "page-cache-size", 0);
+        client->pagecache = opts->page_cache_size;
         if (client->pagecache > QEMU_NFS_MAX_PAGECACHE_SIZE) {
             warn_report("Truncating NFS pagecache size to %d pages",
                         QEMU_NFS_MAX_PAGECACHE_SIZE);
@@ -595,8 +495,8 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
 #endif
 
 #ifdef LIBNFS_FEATURE_DEBUG
-    if (qemu_opt_get(opts, "debug")) {
-        client->debug = qemu_opt_get_number(opts, "debug", 0);
+    if (opts->has_debug) {
+        client->debug = opts->debug;
         /* limit the maximum debug level to avoid potential flooding
          * of our log files. */
         if (client->debug > QEMU_NFS_MAX_DEBUG_LEVEL) {
@@ -647,11 +547,53 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
 fail:
     nfs_client_close(client);
 out:
-    qemu_opts_del(opts);
     g_free(file);
     return ret;
 }
 
+static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
+                                                     Error **errp)
+{
+    BlockdevOptionsNfs *opts = NULL;
+    QObject *crumpled = NULL;
+    Visitor *v;
+    Error *local_err = NULL;
+
+    crumpled = qdict_crumple(options, errp);
+    if (crumpled == NULL) {
+        return NULL;
+    }
+
+    v = qobject_input_visitor_new_keyval(crumpled);
+    visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
+    visit_free(v);
+    qobject_decref(crumpled);
+
+    if (local_err) {
+        return NULL;
+    }
+
+    return opts;
+}
+
+static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options,
+                                     int flags, int open_flags, Error **errp)
+{
+    BlockdevOptionsNfs *opts;
+    int ret;
+
+    opts = nfs_options_qdict_to_qapi(options, errp);
+    if (opts == NULL) {
+        ret = -EINVAL;
+        goto fail;
+    }
+
+    ret = nfs_client_open(client, opts, flags, open_flags, errp);
+fail:
+    qapi_free_BlockdevOptionsNfs(opts);
+    return ret;
+}
+
 static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
                          Error **errp) {
     NFSClient *client = bs->opaque;
@@ -659,9 +601,9 @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
 
     client->aio_context = bdrv_get_aio_context(bs);
 
-    ret = nfs_client_open(client, options,
-                          (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
-                          bs->open_flags, errp);
+    ret = nfs_client_open_qdict(client, options,
+                                (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
+                                bs->open_flags, errp);
     if (ret < 0) {
         return ret;
     }
@@ -684,18 +626,43 @@ static QemuOptsList nfs_create_opts = {
     }
 };
 
-static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
-                                                Error **errp)
+static int nfs_file_co_create(BlockdevCreateOptions *options, Error **errp)
 {
-    int64_t ret, total_size;
+    BlockdevCreateOptionsNfs *opts = &options->u.nfs;
     NFSClient *client = g_new0(NFSClient, 1);
-    QDict *options = NULL;
+    int ret;
+
+    assert(options->driver == BLOCKDEV_DRIVER_NFS);
 
     client->aio_context = qemu_get_aio_context();
 
+    ret = nfs_client_open(client, opts->location, O_CREAT, 0, errp);
+    if (ret < 0) {
+        goto out;
+    }
+    ret = nfs_ftruncate(client->context, client->fh, opts->size);
+    nfs_client_close(client);
+
+out:
+    g_free(client);
+    return ret;
+}
+
+static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
+                                                Error **errp)
+{
+    BlockdevCreateOptions *create_options;
+    BlockdevCreateOptionsNfs *nfs_opts;
+    QDict *options;
+    int ret;
+
+    create_options = g_new0(BlockdevCreateOptions, 1);
+    create_options->driver = BLOCKDEV_DRIVER_NFS;
+    nfs_opts = &create_options->u.nfs;
+
     /* Read out options */
-    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
-                          BDRV_SECTOR_SIZE);
+    nfs_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+                              BDRV_SECTOR_SIZE);
 
     options = qdict_new();
     ret = nfs_parse_uri(url, options, errp);
@@ -703,15 +670,21 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
         goto out;
     }
 
-    ret = nfs_client_open(client, options, O_CREAT, 0, errp);
+    nfs_opts->location = nfs_options_qdict_to_qapi(options, errp);
+    if (nfs_opts->location == NULL) {
+        ret = -EINVAL;
+        goto out;
+    }
+
+    ret = nfs_file_co_create(create_options, errp);
     if (ret < 0) {
         goto out;
     }
-    ret = nfs_ftruncate(client->context, client->fh, total_size);
-    nfs_client_close(client);
+
+    ret = 0;
 out:
     QDECREF(options);
-    g_free(client);
+    qapi_free_BlockdevCreateOptions(create_options);
     return ret;
 }
 
@@ -876,8 +849,8 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
 }
 
 #ifdef LIBNFS_FEATURE_PAGECACHE
-static void nfs_invalidate_cache(BlockDriverState *bs,
-                                 Error **errp)
+static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
+                                                 Error **errp)
 {
     NFSClient *client = bs->opaque;
     nfs_pagecache_invalidate(client->context, client->fh);
@@ -898,6 +871,7 @@ static BlockDriver bdrv_nfs = {
 
     .bdrv_file_open                 = nfs_file_open,
     .bdrv_close                     = nfs_file_close,
+    .bdrv_co_create                 = nfs_file_co_create,
     .bdrv_co_create_opts            = nfs_file_co_create_opts,
     .bdrv_reopen_prepare            = nfs_reopen_prepare,
 
@@ -910,7 +884,7 @@ static BlockDriver bdrv_nfs = {
     .bdrv_refresh_filename          = nfs_refresh_filename,
 
 #ifdef LIBNFS_FEATURE_PAGECACHE
-    .bdrv_invalidate_cache          = nfs_invalidate_cache,
+    .bdrv_co_invalidate_cache       = nfs_co_invalidate_cache,
 #endif
 };
 
diff --git a/block/parallels.c b/block/parallels.c
index 81085795c2..c13cb619e6 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -378,8 +378,9 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
 }
 
 
-static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
-                           BdrvCheckMode fix)
+static int coroutine_fn parallels_co_check(BlockDriverState *bs,
+                                           BdrvCheckResult *res,
+                                           BdrvCheckMode fix)
 {
     BDRVParallelsState *s = bs->opaque;
     int64_t size, prev_off, high_off;
@@ -394,6 +395,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
         return size;
     }
 
+    qemu_co_mutex_lock(&s->lock);
     if (s->header_unclean) {
         fprintf(stderr, "%s image was not closed correctly\n",
                 fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR");
@@ -442,11 +444,12 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
         prev_off = off;
     }
 
+    ret = 0;
     if (flush_bat) {
         ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size);
         if (ret < 0) {
             res->check_errors++;
-            return ret;
+            goto out;
         }
     }
 
@@ -465,13 +468,15 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
             if (ret < 0) {
                 error_report_err(local_err);
                 res->check_errors++;
-                return ret;
+                goto out;
             }
             res->leaks_fixed += count;
         }
     }
 
-    return 0;
+out:
+    qemu_co_mutex_unlock(&s->lock);
+    return ret;
 }
 
 
@@ -799,7 +804,7 @@ static BlockDriver bdrv_parallels = {
     .bdrv_co_writev = parallels_co_writev,
     .supports_backing = true,
     .bdrv_co_create_opts = parallels_co_create_opts,
-    .bdrv_check     = parallels_check,
+    .bdrv_co_check  = parallels_co_check,
     .create_opts    = &parallels_create_opts,
 };
 
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index 5127276f90..3010adb909 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -110,7 +110,7 @@ static int update_header_sync(BlockDriverState *bs)
         return ret;
     }
 
-    return bdrv_flush(bs);
+    return bdrv_flush(bs->file->bs);
 }
 
 static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
@@ -882,7 +882,7 @@ static int update_ext_header_and_dir(BlockDriverState *bs,
             return ret;
         }
 
-        ret = bdrv_flush(bs->file->bs);
+        ret = qcow2_flush_caches(bs);
         if (ret < 0) {
             goto fail;
         }
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 98908c4264..1aee726c6a 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -25,6 +25,7 @@
 #include "qemu/osdep.h"
 #include <zlib.h>
 
+#include "qapi/error.h"
 #include "qemu-common.h"
 #include "block/block_int.h"
 #include "block/qcow2.h"
@@ -2092,11 +2093,21 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
     }
 
     for (i = 0; i < s->nb_snapshots; i++) {
-        int l1_sectors = DIV_ROUND_UP(s->snapshots[i].l1_size *
-                                      sizeof(uint64_t), BDRV_SECTOR_SIZE);
+        int l1_size2;
+        uint64_t *new_l1_table;
+        Error *local_err = NULL;
+
+        ret = qcow2_validate_table(bs, s->snapshots[i].l1_table_offset,
+                                   s->snapshots[i].l1_size, sizeof(uint64_t),
+                                   QCOW_MAX_L1_SIZE, "Snapshot L1 table",
+                                   &local_err);
+        if (ret < 0) {
+            error_report_err(local_err);
+            goto fail;
+        }
 
-        uint64_t *new_l1_table =
-            g_try_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE);
+        l1_size2 = s->snapshots[i].l1_size * sizeof(uint64_t);
+        new_l1_table = g_try_realloc(l1_table, l1_size2);
 
         if (!new_l1_table) {
             ret = -ENOMEM;
@@ -2105,9 +2116,8 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
 
         l1_table = new_l1_table;
 
-        ret = bdrv_read(bs->file,
-                        s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE,
-                        (void *)l1_table, l1_sectors);
+        ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset,
+                         l1_table, l1_size2);
         if (ret < 0) {
             goto fail;
         }
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 126cca3276..362deaf303 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -1171,7 +1171,35 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
     }
 }
 
+int coroutine_fn qcow2_write_caches(BlockDriverState *bs)
+{
+    BDRVQcow2State *s = bs->opaque;
+    int ret;
+
+    ret = qcow2_cache_write(bs, s->l2_table_cache);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (qcow2_need_accurate_refcounts(s)) {
+        ret = qcow2_cache_write(bs, s->refcount_block_cache);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+int coroutine_fn qcow2_flush_caches(BlockDriverState *bs)
+{
+    int ret = qcow2_write_caches(bs);
+    if (ret < 0) {
+        return ret;
+    }
 
+    return bdrv_flush(bs->file->bs);
+}
 
 /*********************************************************/
 /* snapshots and image creation */
@@ -2019,6 +2047,20 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
     /* snapshots */
     for (i = 0; i < s->nb_snapshots; i++) {
         sn = s->snapshots + i;
+        if (offset_into_cluster(s, sn->l1_table_offset)) {
+            fprintf(stderr, "ERROR snapshot %s (%s) l1_offset=%#" PRIx64 ": "
+                    "L1 table is not cluster aligned; snapshot table entry "
+                    "corrupted\n", sn->id_str, sn->name, sn->l1_table_offset);
+            res->corruptions++;
+            continue;
+        }
+        if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
+            fprintf(stderr, "ERROR snapshot %s (%s) l1_size=%#" PRIx32 ": "
+                    "L1 table is too large; snapshot table entry corrupted\n",
+                    sn->id_str, sn->name, sn->l1_size);
+            res->corruptions++;
+            continue;
+        }
         ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
                                  sn->l1_table_offset, sn->l1_size, 0, fix);
         if (ret < 0) {
@@ -2614,9 +2656,17 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
             uint64_t l1_ofs = s->snapshots[i].l1_table_offset;
             uint32_t l1_sz  = s->snapshots[i].l1_size;
             uint64_t l1_sz2 = l1_sz * sizeof(uint64_t);
-            uint64_t *l1 = g_try_malloc(l1_sz2);
+            uint64_t *l1;
             int ret;
 
+            ret = qcow2_validate_table(bs, l1_ofs, l1_sz, sizeof(uint64_t),
+                                       QCOW_MAX_L1_SIZE, "", NULL);
+            if (ret < 0) {
+                return ret;
+            }
+
+            l1 = g_try_malloc(l1_sz2);
+
             if (l1_sz2 && l1 == NULL) {
                 return -ENOMEM;
             }
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index cee25f582b..74293be470 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -465,6 +465,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
 {
     BDRVQcow2State *s = bs->opaque;
     QCowSnapshot *sn;
+    Error *local_err = NULL;
     int i, snapshot_index;
     int cur_l1_bytes, sn_l1_bytes;
     int ret;
@@ -477,6 +478,14 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
     }
     sn = &s->snapshots[snapshot_index];
 
+    ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size,
+                               sizeof(uint64_t), QCOW_MAX_L1_SIZE,
+                               "Snapshot L1 table", &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+        goto fail;
+    }
+
     if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
         error_report("qcow2: Loading snapshots with different disk "
             "size is not implemented");
@@ -602,6 +611,13 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
     }
     sn = s->snapshots[snapshot_index];
 
+    ret = qcow2_validate_table(bs, sn.l1_table_offset, sn.l1_size,
+                               sizeof(uint64_t), QCOW_MAX_L1_SIZE,
+                               "Snapshot L1 table", errp);
+    if (ret < 0) {
+        return ret;
+    }
+
     /* Remove it from the snapshot list */
     memmove(s->snapshots + snapshot_index,
             s->snapshots + snapshot_index + 1,
@@ -704,9 +720,11 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
     sn = &s->snapshots[snapshot_index];
 
     /* Allocate and read in the snapshot's L1 table */
-    if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
-        error_setg(errp, "Snapshot L1 table too large");
-        return -EFBIG;
+    ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size,
+                               sizeof(uint64_t), QCOW_MAX_L1_SIZE,
+                               "Snapshot L1 table", errp);
+    if (ret < 0) {
+        return ret;
     }
     new_l1_bytes = sn->l1_size * sizeof(uint64_t);
     new_l1_table = qemu_try_blockalign(bs->file->bs,
diff --git a/block/qcow2.c b/block/qcow2.c
index 071dc4d608..7472af6931 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -37,7 +37,8 @@
 #include "qemu/option_int.h"
 #include "qemu/cutils.h"
 #include "qemu/bswap.h"
-#include "qapi/opts-visitor.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
 #include "block/crypto.h"
 
 /*
@@ -500,7 +501,7 @@ static int qcow2_mark_clean(BlockDriverState *bs)
 
         s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY;
 
-        ret = bdrv_flush(bs);
+        ret = qcow2_flush_caches(bs);
         if (ret < 0) {
             return ret;
         }
@@ -530,7 +531,7 @@ int qcow2_mark_consistent(BlockDriverState *bs)
     BDRVQcow2State *s = bs->opaque;
 
     if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
-        int ret = bdrv_flush(bs);
+        int ret = qcow2_flush_caches(bs);
         if (ret < 0) {
             return ret;
         }
@@ -541,8 +542,9 @@ int qcow2_mark_consistent(BlockDriverState *bs)
     return 0;
 }
 
-static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
-                       BdrvCheckMode fix)
+static int coroutine_fn qcow2_co_check_locked(BlockDriverState *bs,
+                                              BdrvCheckResult *result,
+                                              BdrvCheckMode fix)
 {
     int ret = qcow2_check_refcounts(bs, result, fix);
     if (ret < 0) {
@@ -559,26 +561,36 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
     return ret;
 }
 
-static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
-                                 uint64_t entries, size_t entry_len)
+static int coroutine_fn qcow2_co_check(BlockDriverState *bs,
+                                       BdrvCheckResult *result,
+                                       BdrvCheckMode fix)
 {
     BDRVQcow2State *s = bs->opaque;
-    uint64_t size;
+    int ret;
 
-    /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
-     * because values will be passed to qemu functions taking int64_t. */
-    if (entries > INT64_MAX / entry_len) {
-        return -EINVAL;
-    }
+    qemu_co_mutex_lock(&s->lock);
+    ret = qcow2_co_check_locked(bs, result, fix);
+    qemu_co_mutex_unlock(&s->lock);
+    return ret;
+}
 
-    size = entries * entry_len;
+int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
+                         uint64_t entries, size_t entry_len,
+                         int64_t max_size_bytes, const char *table_name,
+                         Error **errp)
+{
+    BDRVQcow2State *s = bs->opaque;
 
-    if (INT64_MAX - size < offset) {
-        return -EINVAL;
+    if (entries > max_size_bytes / entry_len) {
+        error_setg(errp, "%s too large", table_name);
+        return -EFBIG;
     }
 
-    /* Tables must be cluster aligned */
-    if (offset_into_cluster(s, offset) != 0) {
+    /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
+     * because values will be passed to qemu functions taking int64_t. */
+    if ((INT64_MAX - entries * entry_len < offset) ||
+        (offset_into_cluster(s, offset) != 0)) {
+        error_setg(errp, "%s offset invalid", table_name);
         return -EINVAL;
     }
 
@@ -1118,8 +1130,9 @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options,
     return ret;
 }
 
-static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
-                         Error **errp)
+/* Called with s->lock held.  */
+static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
+                                      int flags, Error **errp)
 {
     BDRVQcow2State *s = bs->opaque;
     unsigned int len, i;
@@ -1308,47 +1321,42 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
     s->refcount_table_size =
         header.refcount_table_clusters << (s->cluster_bits - 3);
 
-    if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) {
-        error_setg(errp, "Reference count table too large");
-        ret = -EINVAL;
-        goto fail;
-    }
-
     if (header.refcount_table_clusters == 0 && !(flags & BDRV_O_CHECK)) {
         error_setg(errp, "Image does not contain a reference count table");
         ret = -EINVAL;
         goto fail;
     }
 
-    ret = validate_table_offset(bs, s->refcount_table_offset,
-                                s->refcount_table_size, sizeof(uint64_t));
+    ret = qcow2_validate_table(bs, s->refcount_table_offset,
+                               header.refcount_table_clusters,
+                               s->cluster_size, QCOW_MAX_REFTABLE_SIZE,
+                               "Reference count table", errp);
     if (ret < 0) {
-        error_setg(errp, "Invalid reference count table offset");
         goto fail;
     }
 
-    /* Snapshot table offset/length */
-    if (header.nb_snapshots > QCOW_MAX_SNAPSHOTS) {
-        error_setg(errp, "Too many snapshots");
-        ret = -EINVAL;
-        goto fail;
-    }
-
-    ret = validate_table_offset(bs, header.snapshots_offset,
-                                header.nb_snapshots,
-                                sizeof(QCowSnapshotHeader));
+    /* The total size in bytes of the snapshot table is checked in
+     * qcow2_read_snapshots() because the size of each snapshot is
+     * variable and we don't know it yet.
+     * Here we only check the offset and number of snapshots. */
+    ret = qcow2_validate_table(bs, header.snapshots_offset,
+                               header.nb_snapshots,
+                               sizeof(QCowSnapshotHeader),
+                               sizeof(QCowSnapshotHeader) * QCOW_MAX_SNAPSHOTS,
+                               "Snapshot table", errp);
     if (ret < 0) {
-        error_setg(errp, "Invalid snapshot table offset");
         goto fail;
     }
 
     /* read the level 1 table */
-    if (header.l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
-        error_setg(errp, "Active L1 table too large");
-        ret = -EFBIG;
+    ret = qcow2_validate_table(bs, header.l1_table_offset,
+                               header.l1_size, sizeof(uint64_t),
+                               QCOW_MAX_L1_SIZE, "Active L1 table", errp);
+    if (ret < 0) {
         goto fail;
     }
     s->l1_size = header.l1_size;
+    s->l1_table_offset = header.l1_table_offset;
 
     l1_vm_state_index = size_to_l1(s, header.size);
     if (l1_vm_state_index > INT_MAX) {
@@ -1366,15 +1374,6 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
         goto fail;
     }
 
-    ret = validate_table_offset(bs, header.l1_table_offset,
-                                header.l1_size, sizeof(uint64_t));
-    if (ret < 0) {
-        error_setg(errp, "Invalid L1 table offset");
-        goto fail;
-    }
-    s->l1_table_offset = header.l1_table_offset;
-
-
     if (s->l1_size > 0) {
         s->l1_table = qemu_try_blockalign(bs->file->bs,
             ROUND_UP(s->l1_size * sizeof(uint64_t), 512));
@@ -1498,8 +1497,6 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
         }
     }
 
-    /* Initialise locks */
-    qemu_co_mutex_init(&s->lock);
     bs->supported_zero_flags = header.version >= 3 ? BDRV_REQ_MAY_UNMAP : 0;
 
     /* Repair image if dirty */
@@ -1507,7 +1504,8 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
         (s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
         BdrvCheckResult result = {0};
 
-        ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
+        ret = qcow2_co_check_locked(bs, &result,
+                                    BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
         if (ret < 0 || result.check_errors) {
             if (ret >= 0) {
                 ret = -EIO;
@@ -1545,16 +1543,53 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
     return ret;
 }
 
+typedef struct QCow2OpenCo {
+    BlockDriverState *bs;
+    QDict *options;
+    int flags;
+    Error **errp;
+    int ret;
+} QCow2OpenCo;
+
+static void coroutine_fn qcow2_open_entry(void *opaque)
+{
+    QCow2OpenCo *qoc = opaque;
+    BDRVQcow2State *s = qoc->bs->opaque;
+
+    qemu_co_mutex_lock(&s->lock);
+    qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
+    qemu_co_mutex_unlock(&s->lock);
+}
+
 static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
                       Error **errp)
 {
+    BDRVQcow2State *s = bs->opaque;
+    QCow2OpenCo qoc = {
+        .bs = bs,
+        .options = options,
+        .flags = flags,
+        .errp = errp,
+        .ret = -EINPROGRESS
+    };
+
     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
                                false, errp);
     if (!bs->file) {
         return -EINVAL;
     }
 
-    return qcow2_do_open(bs, options, flags, errp);
+    /* Initialise locks */
+    qemu_co_mutex_init(&s->lock);
+
+    if (qemu_in_coroutine()) {
+        /* From bdrv_co_create.  */
+        qcow2_open_entry(&qoc);
+    } else {
+        qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc));
+        BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
+    }
+    return qoc.ret;
 }
 
 static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
@@ -2106,7 +2141,8 @@ static void qcow2_close(BlockDriverState *bs)
     qcow2_free_snapshots(bs);
 }
 
-static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
+static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
+                                                   Error **errp)
 {
     BDRVQcow2State *s = bs->opaque;
     int flags = s->flags;
@@ -2129,7 +2165,9 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
     options = qdict_clone_shallow(bs->options);
 
     flags &= ~BDRV_O_INACTIVE;
+    qemu_co_mutex_lock(&s->lock);
     ret = qcow2_do_open(bs, options, flags, &local_err);
+    qemu_co_mutex_unlock(&s->lock);
     QDECREF(options);
     if (local_err) {
         error_propagate(errp, local_err);
@@ -2412,39 +2450,26 @@ static int qcow2_crypt_method_from_format(const char *encryptfmt)
     }
 }
 
-static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
-                                   QemuOpts *opts, Error **errp)
+static int qcow2_set_up_encryption(BlockDriverState *bs,
+                                   QCryptoBlockCreateOptions *cryptoopts,
+                                   Error **errp)
 {
     BDRVQcow2State *s = bs->opaque;
-    QCryptoBlockCreateOptions *cryptoopts = NULL;
     QCryptoBlock *crypto = NULL;
-    int ret = -EINVAL;
-    QDict *options, *encryptopts;
-    int fmt;
-
-    options = qemu_opts_to_qdict(opts, NULL);
-    qdict_extract_subqdict(options, &encryptopts, "encrypt.");
-    QDECREF(options);
+    int fmt, ret;
 
-    fmt = qcow2_crypt_method_from_format(encryptfmt);
-
-    switch (fmt) {
-    case QCOW_CRYPT_LUKS:
-        cryptoopts = block_crypto_create_opts_init(
-            Q_CRYPTO_BLOCK_FORMAT_LUKS, encryptopts, errp);
+    switch (cryptoopts->format) {
+    case Q_CRYPTO_BLOCK_FORMAT_LUKS:
+        fmt = QCOW_CRYPT_LUKS;
         break;
-    case QCOW_CRYPT_AES:
-        cryptoopts = block_crypto_create_opts_init(
-            Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
+    case Q_CRYPTO_BLOCK_FORMAT_QCOW:
+        fmt = QCOW_CRYPT_AES;
         break;
     default:
-        error_setg(errp, "Unknown encryption format '%s'", encryptfmt);
-        break;
-    }
-    if (!cryptoopts) {
-        ret = -EINVAL;
-        goto out;
+        error_setg(errp, "Crypto format not supported in qcow2");
+        return -EINVAL;
     }
+
     s->crypt_method_header = fmt;
 
     crypto = qcrypto_block_create(cryptoopts, "encrypt.",
@@ -2452,8 +2477,7 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
                                   qcow2_crypto_hdr_write_func,
                                   bs, errp);
     if (!crypto) {
-        ret = -EINVAL;
-        goto out;
+        return -EINVAL;
     }
 
     ret = qcow2_update_header(bs);
@@ -2462,10 +2486,9 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
         goto out;
     }
 
+    ret = 0;
  out:
-    QDECREF(encryptopts);
     qcrypto_block_free(crypto);
-    qapi_free_QCryptoBlockCreateOptions(cryptoopts);
     return ret;
 }
 
@@ -2663,19 +2686,26 @@ static int64_t qcow2_calc_prealloc_size(int64_t total_size,
     return meta_size + aligned_total_size;
 }
 
-static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp)
+static bool validate_cluster_size(size_t cluster_size, Error **errp)
 {
-    size_t cluster_size;
-    int cluster_bits;
-
-    cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
-                                         DEFAULT_CLUSTER_SIZE);
-    cluster_bits = ctz32(cluster_size);
+    int cluster_bits = ctz32(cluster_size);
     if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
         (1 << cluster_bits) != cluster_size)
     {
         error_setg(errp, "Cluster size must be a power of two between %d and "
                    "%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
+        return false;
+    }
+    return true;
+}
+
+static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp)
+{
+    size_t cluster_size;
+
+    cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
+                                         DEFAULT_CLUSTER_SIZE);
+    if (!validate_cluster_size(cluster_size, errp)) {
         return 0;
     }
     return cluster_size;
@@ -2724,12 +2754,9 @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
 }
 
 static int coroutine_fn
-qcow2_co_create2(const char *filename, int64_t total_size,
-                 const char *backing_file, const char *backing_format,
-                 int flags, size_t cluster_size, PreallocMode prealloc,
-                 QemuOpts *opts, int version, int refcount_order,
-                 const char *encryptfmt, Error **errp)
+qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
 {
+    BlockdevCreateOptionsQcow2 *qcow2_opts;
     QDict *options;
 
     /*
@@ -2744,36 +2771,132 @@ qcow2_co_create2(const char *filename, int64_t total_size,
      * 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
      * size for any qcow2 image.
      */
-    BlockBackend *blk;
+    BlockBackend *blk = NULL;
+    BlockDriverState *bs = NULL;
     QCowHeader *header;
+    size_t cluster_size;
+    int version;
+    int refcount_order;
     uint64_t* refcount_table;
     Error *local_err = NULL;
     int ret;
 
-    if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) {
-        int64_t prealloc_size =
-            qcow2_calc_prealloc_size(total_size, cluster_size, refcount_order);
-        qemu_opt_set_number(opts, BLOCK_OPT_SIZE, prealloc_size, &error_abort);
-        qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_str(prealloc),
-                     &error_abort);
+    assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2);
+    qcow2_opts = &create_options->u.qcow2;
+
+    bs = bdrv_open_blockdev_ref(qcow2_opts->file, errp);
+    if (bs == NULL) {
+        return -EIO;
     }
 
-    ret = bdrv_create_file(filename, opts, &local_err);
-    if (ret < 0) {
-        error_propagate(errp, local_err);
-        return ret;
+    /* Validate options and set default values */
+    if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) {
+        error_setg(errp, "Image size must be a multiple of 512 bytes");
+        ret = -EINVAL;
+        goto out;
     }
 
-    blk = blk_new_open(filename, NULL, NULL,
-                       BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
-                       &local_err);
-    if (blk == NULL) {
-        error_propagate(errp, local_err);
-        return -EIO;
+    if (qcow2_opts->has_version) {
+        switch (qcow2_opts->version) {
+        case BLOCKDEV_QCOW2_VERSION_V2:
+            version = 2;
+            break;
+        case BLOCKDEV_QCOW2_VERSION_V3:
+            version = 3;
+            break;
+        default:
+            g_assert_not_reached();
+        }
+    } else {
+        version = 3;
+    }
+
+    if (qcow2_opts->has_cluster_size) {
+        cluster_size = qcow2_opts->cluster_size;
+    } else {
+        cluster_size = DEFAULT_CLUSTER_SIZE;
+    }
+
+    if (!validate_cluster_size(cluster_size, errp)) {
+        ret = -EINVAL;
+        goto out;
+    }
+
+    if (!qcow2_opts->has_preallocation) {
+        qcow2_opts->preallocation = PREALLOC_MODE_OFF;
+    }
+    if (qcow2_opts->has_backing_file &&
+        qcow2_opts->preallocation != PREALLOC_MODE_OFF)
+    {
+        error_setg(errp, "Backing file and preallocation cannot be used at "
+                   "the same time");
+        ret = -EINVAL;
+        goto out;
+    }
+    if (qcow2_opts->has_backing_fmt && !qcow2_opts->has_backing_file) {
+        error_setg(errp, "Backing format cannot be used without backing file");
+        ret = -EINVAL;
+        goto out;
+    }
+
+    if (!qcow2_opts->has_lazy_refcounts) {
+        qcow2_opts->lazy_refcounts = false;
+    }
+    if (version < 3 && qcow2_opts->lazy_refcounts) {
+        error_setg(errp, "Lazy refcounts only supported with compatibility "
+                   "level 1.1 and above (use version=v3 or greater)");
+        ret = -EINVAL;
+        goto out;
     }
 
+    if (!qcow2_opts->has_refcount_bits) {
+        qcow2_opts->refcount_bits = 16;
+    }
+    if (qcow2_opts->refcount_bits > 64 ||
+        !is_power_of_2(qcow2_opts->refcount_bits))
+    {
+        error_setg(errp, "Refcount width must be a power of two and may not "
+                   "exceed 64 bits");
+        ret = -EINVAL;
+        goto out;
+    }
+    if (version < 3 && qcow2_opts->refcount_bits != 16) {
+        error_setg(errp, "Different refcount widths than 16 bits require "
+                   "compatibility level 1.1 or above (use version=v3 or "
+                   "greater)");
+        ret = -EINVAL;
+        goto out;
+    }
+    refcount_order = ctz32(qcow2_opts->refcount_bits);
+
+
+    /* Create BlockBackend to write to the image */
+    blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
+    ret = blk_insert_bs(blk, bs, errp);
+    if (ret < 0) {
+        goto out;
+    }
     blk_set_allow_write_beyond_eof(blk, true);
 
+    /* Clear the protocol layer and preallocate it if necessary */
+    ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
+    if (ret < 0) {
+        goto out;
+    }
+
+    if (qcow2_opts->preallocation == PREALLOC_MODE_FULL ||
+        qcow2_opts->preallocation == PREALLOC_MODE_FALLOC)
+    {
+        int64_t prealloc_size =
+            qcow2_calc_prealloc_size(qcow2_opts->size, cluster_size,
+                                     refcount_order);
+
+        ret = blk_truncate(blk, prealloc_size, qcow2_opts->preallocation, errp);
+        if (ret < 0) {
+            goto out;
+        }
+    }
+
     /* Write the header */
     QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
     header = g_malloc0(cluster_size);
@@ -2793,7 +2916,7 @@ qcow2_co_create2(const char *filename, int64_t total_size,
     /* We'll update this to correct value later */
     header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
 
-    if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
+    if (qcow2_opts->lazy_refcounts) {
         header->compatible_features |=
             cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
     }
@@ -2826,7 +2949,8 @@ qcow2_co_create2(const char *filename, int64_t total_size,
      */
     options = qdict_new();
     qdict_put_str(options, "driver", "qcow2");
-    blk = blk_new_open(filename, NULL, options,
+    qdict_put_str(options, "file", bs->node_name);
+    blk = blk_new_open(NULL, NULL, options,
                        BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
                        &local_err);
     if (blk == NULL) {
@@ -2854,33 +2978,41 @@ qcow2_co_create2(const char *filename, int64_t total_size,
     }
 
     /* Okay, now that we have a valid image, let's give it the right size */
-    ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, errp);
+    ret = blk_truncate(blk, qcow2_opts->size, PREALLOC_MODE_OFF, errp);
     if (ret < 0) {
         error_prepend(errp, "Could not resize image: ");
         goto out;
     }
 
     /* Want a backing file? There you go.*/
-    if (backing_file) {
-        ret = bdrv_change_backing_file(blk_bs(blk), backing_file, backing_format);
+    if (qcow2_opts->has_backing_file) {
+        const char *backing_format = NULL;
+
+        if (qcow2_opts->has_backing_fmt) {
+            backing_format = BlockdevDriver_str(qcow2_opts->backing_fmt);
+        }
+
+        ret = bdrv_change_backing_file(blk_bs(blk), qcow2_opts->backing_file,
+                                       backing_format);
         if (ret < 0) {
             error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
-                             "with format '%s'", backing_file, backing_format);
+                             "with format '%s'", qcow2_opts->backing_file,
+                             backing_format);
             goto out;
         }
     }
 
     /* Want encryption? There you go. */
-    if (encryptfmt) {
-        ret = qcow2_set_up_encryption(blk_bs(blk), encryptfmt, opts, errp);
+    if (qcow2_opts->has_encrypt) {
+        ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp);
         if (ret < 0) {
             goto out;
         }
     }
 
     /* And if we're supposed to preallocate metadata, do that now */
-    if (prealloc != PREALLOC_MODE_OFF) {
-        ret = preallocate(blk_bs(blk), 0, total_size);
+    if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) {
+        ret = preallocate(blk_bs(blk), 0, qcow2_opts->size);
         if (ret < 0) {
             error_setg_errno(errp, -ret, "Could not preallocate metadata");
             goto out;
@@ -2898,7 +3030,8 @@ qcow2_co_create2(const char *filename, int64_t total_size,
      */
     options = qdict_new();
     qdict_put_str(options, "driver", "qcow2");
-    blk = blk_new_open(filename, NULL, options,
+    qdict_put_str(options, "file", bs->node_name);
+    blk = blk_new_open(NULL, NULL, options,
                        BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO,
                        &local_err);
     if (blk == NULL) {
@@ -2909,104 +3042,120 @@ qcow2_co_create2(const char *filename, int64_t total_size,
 
     ret = 0;
 out:
-    if (blk) {
-        blk_unref(blk);
-    }
+    blk_unref(blk);
+    bdrv_unref(bs);
     return ret;
 }
 
 static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts,
                                              Error **errp)
 {
-    char *backing_file = NULL;
-    char *backing_fmt = NULL;
-    char *buf = NULL;
-    uint64_t size = 0;
-    int flags = 0;
-    size_t cluster_size = DEFAULT_CLUSTER_SIZE;
-    PreallocMode prealloc;
-    int version;
-    uint64_t refcount_bits;
-    int refcount_order;
-    char *encryptfmt = NULL;
+    BlockdevCreateOptions *create_options = NULL;
+    QDict *qdict = NULL;
+    QObject *qobj;
+    Visitor *v;
+    BlockDriverState *bs = NULL;
     Error *local_err = NULL;
+    const char *val;
     int ret;
 
-    /* Read out options */
-    size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
-                    BDRV_SECTOR_SIZE);
-    backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
-    backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
-    encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
-    if (encryptfmt) {
-        if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
-            error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and "
-                       BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive");
-            ret = -EINVAL;
-            goto finish;
-        }
-    } else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
-        encryptfmt = g_strdup("aes");
-    }
-    cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
+    /* Only the keyval visitor supports the dotted syntax needed for
+     * encryption, so go through a QDict before getting a QAPI type. Ignore
+     * options meant for the protocol layer so that the visitor doesn't
+     * complain. */
+    qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts,
+                                        true);
+
+    /* Handle encryption options */
+    val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
+    if (val && !strcmp(val, "on")) {
+        qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow");
+    } else if (val && !strcmp(val, "off")) {
+        qdict_del(qdict, BLOCK_OPT_ENCRYPT);
+    }
+
+    val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT);
+    if (val && !strcmp(val, "aes")) {
+        qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow");
+    }
+
+    /* Convert compat=0.10/1.1 into compat=v2/v3, to be renamed into
+     * version=v2/v3 below. */
+    val = qdict_get_try_str(qdict, BLOCK_OPT_COMPAT_LEVEL);
+    if (val && !strcmp(val, "0.10")) {
+        qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v2");
+    } else if (val && !strcmp(val, "1.1")) {
+        qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3");
+    }
+
+    /* Change legacy command line options into QMP ones */
+    static const QDictRenames opt_renames[] = {
+        { BLOCK_OPT_BACKING_FILE,       "backing-file" },
+        { BLOCK_OPT_BACKING_FMT,        "backing-fmt" },
+        { BLOCK_OPT_CLUSTER_SIZE,       "cluster-size" },
+        { BLOCK_OPT_LAZY_REFCOUNTS,     "lazy-refcounts" },
+        { BLOCK_OPT_REFCOUNT_BITS,      "refcount-bits" },
+        { BLOCK_OPT_ENCRYPT,            BLOCK_OPT_ENCRYPT_FORMAT },
+        { BLOCK_OPT_COMPAT_LEVEL,       "version" },
+        { NULL, NULL },
+    };
+
+    if (!qdict_rename_keys(qdict, opt_renames, errp)) {
         ret = -EINVAL;
         goto finish;
     }
-    buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
-    prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
-                               PREALLOC_MODE_OFF, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        ret = -EINVAL;
+
+    /* Create and open the file (protocol layer) */
+    ret = bdrv_create_file(filename, opts, errp);
+    if (ret < 0) {
         goto finish;
     }
 
-    version = qcow2_opt_get_version_del(opts, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        ret = -EINVAL;
+    bs = bdrv_open(filename, NULL, NULL,
+                   BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
+    if (bs == NULL) {
+        ret = -EIO;
         goto finish;
     }
 
-    if (qemu_opt_get_bool_del(opts, BLOCK_OPT_LAZY_REFCOUNTS, false)) {
-        flags |= BLOCK_FLAG_LAZY_REFCOUNTS;
-    }
+    /* Set 'driver' and 'node' options */
+    qdict_put_str(qdict, "driver", "qcow2");
+    qdict_put_str(qdict, "file", bs->node_name);
 
-    if (backing_file && prealloc != PREALLOC_MODE_OFF) {
-        error_setg(errp, "Backing file and preallocation cannot be used at "
-                   "the same time");
+    /* Now get the QAPI type BlockdevCreateOptions */
+    qobj = qdict_crumple(qdict, errp);
+    QDECREF(qdict);
+    qdict = qobject_to_qdict(qobj);
+    if (qdict == NULL) {
         ret = -EINVAL;
         goto finish;
     }
 
-    if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
-        error_setg(errp, "Lazy refcounts only supported with compatibility "
-                   "level 1.1 and above (use compat=1.1 or greater)");
-        ret = -EINVAL;
-        goto finish;
-    }
+    v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+    visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+    visit_free(v);
 
-    refcount_bits = qcow2_opt_get_refcount_bits_del(opts, version, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         ret = -EINVAL;
         goto finish;
     }
 
-    refcount_order = ctz32(refcount_bits);
+    /* Silently round up size */
+    create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size,
+                                            BDRV_SECTOR_SIZE);
 
-    ret = qcow2_co_create2(filename, size, backing_file, backing_fmt, flags,
-                           cluster_size, prealloc, opts, version, refcount_order,
-                           encryptfmt, &local_err);
-    error_propagate(errp, local_err);
+    /* Create the qcow2 image (format layer) */
+    ret = qcow2_co_create(create_options, errp);
+    if (ret < 0) {
+        goto finish;
+    }
 
+    ret = 0;
 finish:
-    g_free(backing_file);
-    g_free(backing_fmt);
-    g_free(encryptfmt);
-    g_free(buf);
+    QDECREF(qdict);
+    bdrv_unref(bs);
+    qapi_free_BlockdevCreateOptions(create_options);
     return ret;
 }
 
@@ -3647,22 +3796,10 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
     int ret;
 
     qemu_co_mutex_lock(&s->lock);
-    ret = qcow2_cache_write(bs, s->l2_table_cache);
-    if (ret < 0) {
-        qemu_co_mutex_unlock(&s->lock);
-        return ret;
-    }
-
-    if (qcow2_need_accurate_refcounts(s)) {
-        ret = qcow2_cache_write(bs, s->refcount_block_cache);
-        if (ret < 0) {
-            qemu_co_mutex_unlock(&s->lock);
-            return ret;
-        }
-    }
+    ret = qcow2_write_caches(bs);
     qemu_co_mutex_unlock(&s->lock);
 
-    return 0;
+    return ret;
 }
 
 static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
@@ -4353,6 +4490,7 @@ BlockDriver bdrv_qcow2 = {
     .bdrv_join_options    = qcow2_join_options,
     .bdrv_child_perm      = bdrv_format_default_perms,
     .bdrv_co_create_opts  = qcow2_co_create_opts,
+    .bdrv_co_create       = qcow2_co_create,
     .bdrv_has_zero_init = bdrv_has_zero_init_1,
     .bdrv_co_block_status = qcow2_co_block_status,
 
@@ -4382,11 +4520,11 @@ BlockDriver bdrv_qcow2 = {
     .bdrv_change_backing_file   = qcow2_change_backing_file,
 
     .bdrv_refresh_limits        = qcow2_refresh_limits,
-    .bdrv_invalidate_cache      = qcow2_invalidate_cache,
+    .bdrv_co_invalidate_cache   = qcow2_co_invalidate_cache,
     .bdrv_inactivate            = qcow2_inactivate,
 
     .create_opts         = &qcow2_create_opts,
-    .bdrv_check          = qcow2_check,
+    .bdrv_co_check       = qcow2_co_check,
     .bdrv_amend_options  = qcow2_amend_options,
 
     .bdrv_detach_aio_context  = qcow2_detach_aio_context,
diff --git a/block/qcow2.h b/block/qcow2.h
index 1a84cc77b0..ccb92a9696 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -485,11 +485,6 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s)
     return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
 }
 
-static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s)
-{
-    return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
-}
-
 static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry)
 {
     if (l2_entry & QCOW_OFLAG_COMPRESSED) {
@@ -547,6 +542,11 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
                              int64_t size, const char *message_format, ...)
                              GCC_FMT_ATTR(5, 6);
 
+int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
+                         uint64_t entries, size_t entry_len,
+                         int64_t max_size_bytes, const char *table_name,
+                         Error **errp);
+
 /* qcow2-refcount.c functions */
 int qcow2_refcount_init(BlockDriverState *bs);
 void qcow2_refcount_close(BlockDriverState *bs);
@@ -576,6 +576,8 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
 int qcow2_update_snapshot_refcount(BlockDriverState *bs,
     int64_t l1_table_offset, int l1_size, int addend);
 
+int coroutine_fn qcow2_flush_caches(BlockDriverState *bs);
+int coroutine_fn qcow2_write_caches(BlockDriverState *bs);
 int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
                           BdrvCheckMode fix);
 
diff --git a/block/qed-check.c b/block/qed-check.c
index dcd4f036b8..0edac03159 100644
--- a/block/qed-check.c
+++ b/block/qed-check.c
@@ -217,6 +217,7 @@ static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result)
     qed_write_header_sync(s);
 }
 
+/* Called with table_lock held.  */
 int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
 {
     QEDCheck check = {
diff --git a/block/qed-table.c b/block/qed-table.c
index eead8b0fc7..7df5680adb 100644
--- a/block/qed-table.c
+++ b/block/qed-table.c
@@ -18,7 +18,7 @@
 #include "qed.h"
 #include "qemu/bswap.h"
 
-/* Called either from qed_check or with table_lock held.  */
+/* Called with table_lock held.  */
 static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
 {
     QEMUIOVector qiov;
@@ -33,13 +33,9 @@ static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
 
     trace_qed_read_table(s, offset, table);
 
-    if (qemu_in_coroutine()) {
-        qemu_co_mutex_unlock(&s->table_lock);
-    }
+    qemu_co_mutex_unlock(&s->table_lock);
     ret = bdrv_preadv(s->bs->file, offset, &qiov);
-    if (qemu_in_coroutine()) {
-        qemu_co_mutex_lock(&s->table_lock);
-    }
+    qemu_co_mutex_lock(&s->table_lock);
     if (ret < 0) {
         goto out;
     }
@@ -67,7 +63,7 @@ out:
  * @n:          Number of elements
  * @flush:      Whether or not to sync to disk
  *
- * Called either from qed_check or with table_lock held.
+ * Called with table_lock held.
  */
 static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
                            unsigned int index, unsigned int n, bool flush)
@@ -104,13 +100,9 @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
     /* Adjust for offset into table */
     offset += start * sizeof(uint64_t);
 
-    if (qemu_in_coroutine()) {
-        qemu_co_mutex_unlock(&s->table_lock);
-    }
+    qemu_co_mutex_unlock(&s->table_lock);
     ret = bdrv_pwritev(s->bs->file, offset, &qiov);
-    if (qemu_in_coroutine()) {
-        qemu_co_mutex_lock(&s->table_lock);
-    }
+    qemu_co_mutex_lock(&s->table_lock);
     trace_qed_write_table_cb(s, table, flush, ret);
     if (ret < 0) {
         goto out;
@@ -134,7 +126,7 @@ int qed_read_l1_table_sync(BDRVQEDState *s)
     return qed_read_table(s, s->header.l1_table_offset, s->l1_table);
 }
 
-/* Called either from qed_check or with table_lock held.  */
+/* Called with table_lock held.  */
 int qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n)
 {
     BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
@@ -148,7 +140,7 @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
     return qed_write_l1_table(s, index, n);
 }
 
-/* Called either from qed_check or with table_lock held.  */
+/* Called with table_lock held.  */
 int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
 {
     int ret;
@@ -191,7 +183,7 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset
     return qed_read_l2_table(s, request, offset);
 }
 
-/* Called either from qed_check or with table_lock held.  */
+/* Called with table_lock held.  */
 int qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
                        unsigned int index, unsigned int n, bool flush)
 {
diff --git a/block/qed.c b/block/qed.c
index 72cf2f58ab..5e6a6bfaa0 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -381,8 +381,9 @@ static void bdrv_qed_init_state(BlockDriverState *bs)
     qemu_co_queue_init(&s->allocating_write_reqs);
 }
 
-static int bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int flags,
-                            Error **errp)
+/* Called with table_lock held.  */
+static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
+                                         int flags, Error **errp)
 {
     BDRVQEDState *s = bs->opaque;
     QEDHeader le_header;
@@ -513,9 +514,35 @@ out:
     return ret;
 }
 
+typedef struct QEDOpenCo {
+    BlockDriverState *bs;
+    QDict *options;
+    int flags;
+    Error **errp;
+    int ret;
+} QEDOpenCo;
+
+static void coroutine_fn bdrv_qed_open_entry(void *opaque)
+{
+    QEDOpenCo *qoc = opaque;
+    BDRVQEDState *s = qoc->bs->opaque;
+
+    qemu_co_mutex_lock(&s->table_lock);
+    qoc->ret = bdrv_qed_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
+    qemu_co_mutex_unlock(&s->table_lock);
+}
+
 static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
                          Error **errp)
 {
+    QEDOpenCo qoc = {
+        .bs = bs,
+        .options = options,
+        .flags = flags,
+        .errp = errp,
+        .ret = -EINPROGRESS
+    };
+
     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
                                false, errp);
     if (!bs->file) {
@@ -523,7 +550,14 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
     }
 
     bdrv_qed_init_state(bs);
-    return bdrv_qed_do_open(bs, options, flags, errp);
+    if (qemu_in_coroutine()) {
+        bdrv_qed_open_entry(&qoc);
+    } else {
+        qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc));
+        BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
+    }
+    BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
+    return qoc.ret;
 }
 
 static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp)
@@ -1487,7 +1521,8 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs,
     return ret;
 }
 
-static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
+static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs,
+                                                      Error **errp)
 {
     BDRVQEDState *s = bs->opaque;
     Error *local_err = NULL;
@@ -1496,13 +1531,9 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
     bdrv_qed_close(bs);
 
     bdrv_qed_init_state(bs);
-    if (qemu_in_coroutine()) {
-        qemu_co_mutex_lock(&s->table_lock);
-    }
+    qemu_co_mutex_lock(&s->table_lock);
     ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err);
-    if (qemu_in_coroutine()) {
-        qemu_co_mutex_unlock(&s->table_lock);
-    }
+    qemu_co_mutex_unlock(&s->table_lock);
     if (local_err) {
         error_propagate(errp, local_err);
         error_prepend(errp, "Could not reopen qed layer: ");
@@ -1513,12 +1544,17 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
     }
 }
 
-static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result,
-                          BdrvCheckMode fix)
+static int bdrv_qed_co_check(BlockDriverState *bs, BdrvCheckResult *result,
+                             BdrvCheckMode fix)
 {
     BDRVQEDState *s = bs->opaque;
+    int ret;
+
+    qemu_co_mutex_lock(&s->table_lock);
+    ret = qed_check(s, result, !!fix);
+    qemu_co_mutex_unlock(&s->table_lock);
 
-    return qed_check(s, result, !!fix);
+    return ret;
 }
 
 static QemuOptsList qed_create_opts = {
@@ -1577,8 +1613,8 @@ static BlockDriver bdrv_qed = {
     .bdrv_get_info            = bdrv_qed_get_info,
     .bdrv_refresh_limits      = bdrv_qed_refresh_limits,
     .bdrv_change_backing_file = bdrv_qed_change_backing_file,
-    .bdrv_invalidate_cache    = bdrv_qed_invalidate_cache,
-    .bdrv_check               = bdrv_qed_check,
+    .bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache,
+    .bdrv_co_check            = bdrv_qed_co_check,
     .bdrv_detach_aio_context  = bdrv_qed_detach_aio_context,
     .bdrv_attach_aio_context  = bdrv_qed_attach_aio_context,
     .bdrv_co_drain_begin      = bdrv_qed_co_drain_begin,
diff --git a/block/rbd.c b/block/rbd.c
index c7dd32e213..294ed07ac4 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -24,6 +24,8 @@
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qjson.h"
 #include "qapi/qmp/qlist.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
 
 /*
  * When specifying the image filename use:
@@ -101,6 +103,11 @@ typedef struct BDRVRBDState {
     char *snap;
 } BDRVRBDState;
 
+static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
+                            BlockdevOptionsRbd *opts, bool cache,
+                            const char *keypairs, const char *secretid,
+                            Error **errp);
+
 static char *qemu_rbd_next_tok(char *src, char delim, char **p)
 {
     char *end;
@@ -268,13 +275,14 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
         key = qstring_get_str(name);
 
         ret = rados_conf_set(cluster, key, qstring_get_str(value));
-        QDECREF(name);
         QDECREF(value);
         if (ret < 0) {
             error_setg_errno(errp, -ret, "invalid conf option %s", key);
+            QDECREF(name);
             ret = -EINVAL;
             break;
         }
+        QDECREF(name);
     }
 
     QDECREF(keypairs);
@@ -325,67 +333,93 @@ static QemuOptsList runtime_opts = {
         /*
          * server.* extracted manually, see qemu_rbd_mon_host()
          */
-        {
-            .name = "password-secret",
-            .type = QEMU_OPT_STRING,
-            .help = "ID of secret providing the password",
-        },
-
-        /*
-         * Keys for qemu_rbd_parse_filename(), not in the QAPI schema
-         */
-        {
-            /*
-             * HACK: name starts with '=' so that qemu_opts_parse()
-             * can't set it
-             */
-            .name = "=keyvalue-pairs",
-            .type = QEMU_OPT_STRING,
-            .help = "Legacy rados key/value option parameters",
-        },
-        {
-            .name = "filename",
-            .type = QEMU_OPT_STRING,
-        },
         { /* end of list */ }
     },
 };
 
-static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
-                                                QemuOpts *opts,
-                                                Error **errp)
+/* FIXME Deprecate and remove keypairs or make it available in QMP.
+ * password_secret should eventually be configurable in opts->location. Support
+ * for it in .bdrv_open will make it work here as well. */
+static int qemu_rbd_do_create(BlockdevCreateOptions *options,
+                              const char *keypairs, const char *password_secret,
+                              Error **errp)
 {
-    Error *local_err = NULL;
-    int64_t bytes = 0;
-    int64_t objsize;
-    int obj_order = 0;
-    const char *pool, *image_name, *conf, *user, *keypairs;
-    const char *secretid;
+    BlockdevCreateOptionsRbd *opts = &options->u.rbd;
     rados_t cluster;
     rados_ioctx_t io_ctx;
-    QDict *options = NULL;
-    int ret = 0;
+    int obj_order = 0;
+    int ret;
 
-    secretid = qemu_opt_get(opts, "password-secret");
+    assert(options->driver == BLOCKDEV_DRIVER_RBD);
+    if (opts->location->has_snapshot) {
+        error_setg(errp, "Can't use snapshot name for image creation");
+        return -EINVAL;
+    }
 
-    /* Read out options */
-    bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
-                     BDRV_SECTOR_SIZE);
-    objsize = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, 0);
-    if (objsize) {
+    if (opts->has_cluster_size) {
+        int64_t objsize = opts->cluster_size;
         if ((objsize - 1) & objsize) {    /* not a power of 2? */
             error_setg(errp, "obj size needs to be power of 2");
-            ret = -EINVAL;
-            goto exit;
+            return -EINVAL;
         }
         if (objsize < 4096) {
             error_setg(errp, "obj size too small");
-            ret = -EINVAL;
-            goto exit;
+            return -EINVAL;
         }
         obj_order = ctz32(objsize);
     }
 
+    ret = qemu_rbd_connect(&cluster, &io_ctx, opts->location, false, keypairs,
+                           password_secret, errp);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = rbd_create(io_ctx, opts->location->image, opts->size, &obj_order);
+    if (ret < 0) {
+        error_setg_errno(errp, -ret, "error rbd create");
+        goto out;
+    }
+
+    ret = 0;
+out:
+    rados_ioctx_destroy(io_ctx);
+    rados_shutdown(cluster);
+    return ret;
+}
+
+static int qemu_rbd_co_create(BlockdevCreateOptions *options, Error **errp)
+{
+    return qemu_rbd_do_create(options, NULL, NULL, errp);
+}
+
+static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
+                                                QemuOpts *opts,
+                                                Error **errp)
+{
+    BlockdevCreateOptions *create_options;
+    BlockdevCreateOptionsRbd *rbd_opts;
+    BlockdevOptionsRbd *loc;
+    Error *local_err = NULL;
+    const char *keypairs, *password_secret;
+    QDict *options = NULL;
+    int ret = 0;
+
+    create_options = g_new0(BlockdevCreateOptions, 1);
+    create_options->driver = BLOCKDEV_DRIVER_RBD;
+    rbd_opts = &create_options->u.rbd;
+
+    rbd_opts->location = g_new0(BlockdevOptionsRbd, 1);
+
+    password_secret = qemu_opt_get(opts, "password-secret");
+
+    /* Read out options */
+    rbd_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+                              BDRV_SECTOR_SIZE);
+    rbd_opts->cluster_size = qemu_opt_get_size_del(opts,
+                                                   BLOCK_OPT_CLUSTER_SIZE, 0);
+    rbd_opts->has_cluster_size = (rbd_opts->cluster_size != 0);
+
     options = qdict_new();
     qemu_rbd_parse_filename(filename, options, &local_err);
     if (local_err) {
@@ -400,61 +434,23 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
      * or blockdev_add, its members are typed according to the QAPI
      * schema, but when they come from -drive, they're all QString.
      */
-    pool       = qdict_get_try_str(options, "pool");
-    conf       = qdict_get_try_str(options, "conf");
-    user       = qdict_get_try_str(options, "user");
-    image_name = qdict_get_try_str(options, "image");
-    keypairs   = qdict_get_try_str(options, "=keyvalue-pairs");
-
-    ret = rados_create(&cluster, user);
+    loc = rbd_opts->location;
+    loc->pool     = g_strdup(qdict_get_try_str(options, "pool"));
+    loc->conf     = g_strdup(qdict_get_try_str(options, "conf"));
+    loc->has_conf = !!loc->conf;
+    loc->user     = g_strdup(qdict_get_try_str(options, "user"));
+    loc->has_user = !!loc->user;
+    loc->image    = g_strdup(qdict_get_try_str(options, "image"));
+    keypairs      = qdict_get_try_str(options, "=keyvalue-pairs");
+
+    ret = qemu_rbd_do_create(create_options, keypairs, password_secret, errp);
     if (ret < 0) {
-        error_setg_errno(errp, -ret, "error initializing");
         goto exit;
     }
 
-    /* try default location when conf=NULL, but ignore failure */
-    ret = rados_conf_read_file(cluster, conf);
-    if (conf && ret < 0) {
-        error_setg_errno(errp, -ret, "error reading conf file %s", conf);
-        ret = -EIO;
-        goto shutdown;
-    }
-
-    ret = qemu_rbd_set_keypairs(cluster, keypairs, errp);
-    if (ret < 0) {
-        ret = -EIO;
-        goto shutdown;
-    }
-
-    if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) {
-        ret = -EIO;
-        goto shutdown;
-    }
-
-    ret = rados_connect(cluster);
-    if (ret < 0) {
-        error_setg_errno(errp, -ret, "error connecting");
-        goto shutdown;
-    }
-
-    ret = rados_ioctx_create(cluster, pool, &io_ctx);
-    if (ret < 0) {
-        error_setg_errno(errp, -ret, "error opening pool %s", pool);
-        goto shutdown;
-    }
-
-    ret = rbd_create(io_ctx, image_name, bytes, &obj_order);
-    if (ret < 0) {
-        error_setg_errno(errp, -ret, "error rbd create");
-    }
-
-    rados_ioctx_destroy(io_ctx);
-
-shutdown:
-    rados_shutdown(cluster);
-
 exit:
     QDECREF(options);
+    qapi_free_BlockdevCreateOptions(create_options);
     return ret;
 }
 
@@ -505,131 +501,83 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
     qemu_aio_unref(acb);
 }
 
-static char *qemu_rbd_mon_host(QDict *options, Error **errp)
+static char *qemu_rbd_mon_host(BlockdevOptionsRbd *opts, Error **errp)
 {
-    const char **vals = g_new(const char *, qdict_size(options) + 1);
-    char keybuf[32];
+    const char **vals;
     const char *host, *port;
     char *rados_str;
-    int i;
-
-    for (i = 0;; i++) {
-        sprintf(keybuf, "server.%d.host", i);
-        host = qdict_get_try_str(options, keybuf);
-        qdict_del(options, keybuf);
-        sprintf(keybuf, "server.%d.port", i);
-        port = qdict_get_try_str(options, keybuf);
-        qdict_del(options, keybuf);
-        if (!host && !port) {
-            break;
-        }
-        if (!host) {
-            error_setg(errp, "Parameter server.%d.host is missing", i);
-            rados_str = NULL;
-            goto out;
-        }
+    InetSocketAddressBaseList *p;
+    int i, cnt;
+
+    if (!opts->has_server) {
+        return NULL;
+    }
+
+    for (cnt = 0, p = opts->server; p; p = p->next) {
+        cnt++;
+    }
+
+    vals = g_new(const char *, cnt + 1);
+
+    for (i = 0, p = opts->server; p; p = p->next, i++) {
+        host = p->value->host;
+        port = p->value->port;
 
         if (strchr(host, ':')) {
-            vals[i] = port ? g_strdup_printf("[%s]:%s", host, port)
-                : g_strdup_printf("[%s]", host);
+            vals[i] = g_strdup_printf("[%s]:%s", host, port);
         } else {
-            vals[i] = port ? g_strdup_printf("%s:%s", host, port)
-                : g_strdup(host);
+            vals[i] = g_strdup_printf("%s:%s", host, port);
         }
     }
     vals[i] = NULL;
 
     rados_str = i ? g_strjoinv(";", (char **)vals) : NULL;
-out:
     g_strfreev((char **)vals);
     return rados_str;
 }
 
-static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
-                         Error **errp)
+static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
+                            BlockdevOptionsRbd *opts, bool cache,
+                            const char *keypairs, const char *secretid,
+                            Error **errp)
 {
-    BDRVRBDState *s = bs->opaque;
-    const char *pool, *snap, *conf, *user, *image_name, *keypairs;
-    const char *secretid, *filename;
-    QemuOpts *opts;
-    Error *local_err = NULL;
     char *mon_host = NULL;
+    Error *local_err = NULL;
     int r;
 
-    /* If we are given a filename, parse the filename, with precedence given to
-     * filename encoded options */
-    filename = qdict_get_try_str(options, "filename");
-    if (filename) {
-        warn_report("'filename' option specified. "
-                    "This is an unsupported option, and may be deprecated "
-                    "in the future");
-        qemu_rbd_parse_filename(filename, options, &local_err);
-        if (local_err) {
-            r = -EINVAL;
-            error_propagate(errp, local_err);
-            goto exit;
-        }
-    }
-
-    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
-    qemu_opts_absorb_qdict(opts, options, &local_err);
+    mon_host = qemu_rbd_mon_host(opts, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         r = -EINVAL;
         goto failed_opts;
     }
 
-    mon_host = qemu_rbd_mon_host(options, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        r = -EINVAL;
-        goto failed_opts;
-    }
-
-    secretid = qemu_opt_get(opts, "password-secret");
-
-    pool           = qemu_opt_get(opts, "pool");
-    conf           = qemu_opt_get(opts, "conf");
-    snap           = qemu_opt_get(opts, "snapshot");
-    user           = qemu_opt_get(opts, "user");
-    image_name     = qemu_opt_get(opts, "image");
-    keypairs       = qemu_opt_get(opts, "=keyvalue-pairs");
-
-    if (!pool || !image_name) {
-        error_setg(errp, "Parameters 'pool' and 'image' are required");
-        r = -EINVAL;
-        goto failed_opts;
-    }
-
-    r = rados_create(&s->cluster, user);
+    r = rados_create(cluster, opts->user);
     if (r < 0) {
         error_setg_errno(errp, -r, "error initializing");
         goto failed_opts;
     }
 
-    s->snap = g_strdup(snap);
-    s->image_name = g_strdup(image_name);
-
     /* try default location when conf=NULL, but ignore failure */
-    r = rados_conf_read_file(s->cluster, conf);
-    if (conf && r < 0) {
-        error_setg_errno(errp, -r, "error reading conf file %s", conf);
+    r = rados_conf_read_file(*cluster, opts->conf);
+    if (opts->has_conf && r < 0) {
+        error_setg_errno(errp, -r, "error reading conf file %s", opts->conf);
         goto failed_shutdown;
     }
 
-    r = qemu_rbd_set_keypairs(s->cluster, keypairs, errp);
+    r = qemu_rbd_set_keypairs(*cluster, keypairs, errp);
     if (r < 0) {
         goto failed_shutdown;
     }
 
     if (mon_host) {
-        r = rados_conf_set(s->cluster, "mon_host", mon_host);
+        r = rados_conf_set(*cluster, "mon_host", mon_host);
         if (r < 0) {
             goto failed_shutdown;
         }
     }
 
-    if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) {
+    if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) {
         r = -EIO;
         goto failed_shutdown;
     }
@@ -641,24 +589,97 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
      * librbd defaults to no caching. If write through caching cannot
      * be set up, fall back to no caching.
      */
-    if (flags & BDRV_O_NOCACHE) {
-        rados_conf_set(s->cluster, "rbd_cache", "false");
+    if (cache) {
+        rados_conf_set(*cluster, "rbd_cache", "true");
     } else {
-        rados_conf_set(s->cluster, "rbd_cache", "true");
+        rados_conf_set(*cluster, "rbd_cache", "false");
     }
 
-    r = rados_connect(s->cluster);
+    r = rados_connect(*cluster);
     if (r < 0) {
         error_setg_errno(errp, -r, "error connecting");
         goto failed_shutdown;
     }
 
-    r = rados_ioctx_create(s->cluster, pool, &s->io_ctx);
+    r = rados_ioctx_create(*cluster, opts->pool, io_ctx);
     if (r < 0) {
-        error_setg_errno(errp, -r, "error opening pool %s", pool);
+        error_setg_errno(errp, -r, "error opening pool %s", opts->pool);
         goto failed_shutdown;
     }
 
+    return 0;
+
+failed_shutdown:
+    rados_shutdown(*cluster);
+failed_opts:
+    g_free(mon_host);
+    return r;
+}
+
+static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
+                         Error **errp)
+{
+    BDRVRBDState *s = bs->opaque;
+    BlockdevOptionsRbd *opts = NULL;
+    Visitor *v;
+    QObject *crumpled = NULL;
+    Error *local_err = NULL;
+    const char *filename;
+    char *keypairs, *secretid;
+    int r;
+
+    /* If we are given a filename, parse the filename, with precedence given to
+     * filename encoded options */
+    filename = qdict_get_try_str(options, "filename");
+    if (filename) {
+        warn_report("'filename' option specified. "
+                    "This is an unsupported option, and may be deprecated "
+                    "in the future");
+        qemu_rbd_parse_filename(filename, options, &local_err);
+        qdict_del(options, "filename");
+        if (local_err) {
+            error_propagate(errp, local_err);
+            return -EINVAL;
+        }
+    }
+
+    keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
+    if (keypairs) {
+        qdict_del(options, "=keyvalue-pairs");
+    }
+
+    secretid = g_strdup(qdict_get_try_str(options, "password-secret"));
+    if (secretid) {
+        qdict_del(options, "password-secret");
+    }
+
+    /* Convert the remaining options into a QAPI object */
+    crumpled = qdict_crumple(options, errp);
+    if (crumpled == NULL) {
+        r = -EINVAL;
+        goto out;
+    }
+
+    v = qobject_input_visitor_new_keyval(crumpled);
+    visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err);
+    visit_free(v);
+    qobject_decref(crumpled);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        r = -EINVAL;
+        goto out;
+    }
+
+    r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts,
+                         !(flags & BDRV_O_NOCACHE), keypairs, secretid, errp);
+    if (r < 0) {
+        goto out;
+    }
+
+    s->snap = g_strdup(opts->snapshot);
+    s->image_name = g_strdup(opts->image);
+
     /* rbd_open is always r/w */
     r = rbd_open(s->io_ctx, s->image_name, &s->image, s->snap);
     if (r < 0) {
@@ -683,19 +704,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
         }
     }
 
-    qemu_opts_del(opts);
-    return 0;
+    r = 0;
+    goto out;
 
 failed_open:
     rados_ioctx_destroy(s->io_ctx);
-failed_shutdown:
-    rados_shutdown(s->cluster);
     g_free(s->snap);
     g_free(s->image_name);
-failed_opts:
-    qemu_opts_del(opts);
-    g_free(mon_host);
-exit:
+    rados_shutdown(s->cluster);
+out:
+    qapi_free_BlockdevOptionsRbd(opts);
+    g_free(keypairs);
+    g_free(secretid);
     return r;
 }
 
@@ -1093,8 +1113,8 @@ static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs,
 #endif
 
 #ifdef LIBRBD_SUPPORTS_INVALIDATE
-static void qemu_rbd_invalidate_cache(BlockDriverState *bs,
-                                      Error **errp)
+static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs,
+                                                      Error **errp)
 {
     BDRVRBDState *s = bs->opaque;
     int r = rbd_invalidate_cache(s->image);
@@ -1134,6 +1154,7 @@ static BlockDriver bdrv_rbd = {
     .bdrv_file_open         = qemu_rbd_open,
     .bdrv_close             = qemu_rbd_close,
     .bdrv_reopen_prepare    = qemu_rbd_reopen_prepare,
+    .bdrv_co_create         = qemu_rbd_co_create,
     .bdrv_co_create_opts    = qemu_rbd_co_create_opts,
     .bdrv_has_zero_init     = bdrv_has_zero_init_1,
     .bdrv_get_info          = qemu_rbd_getinfo,
@@ -1160,7 +1181,7 @@ static BlockDriver bdrv_rbd = {
     .bdrv_snapshot_list     = qemu_rbd_snap_list,
     .bdrv_snapshot_goto     = qemu_rbd_snap_rollback,
 #ifdef LIBRBD_SUPPORTS_INVALIDATE
-    .bdrv_invalidate_cache  = qemu_rbd_invalidate_cache,
+    .bdrv_co_invalidate_cache = qemu_rbd_co_invalidate_cache,
 #endif
 };
 
diff --git a/block/sheepdog.c b/block/sheepdog.c
index d8c10b7cac..8680b2926f 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -15,8 +15,10 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qapi/qapi-visit-sockets.h"
+#include "qapi/qapi-visit-block-core.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qobject-input-visitor.h"
+#include "qapi/qobject-output-visitor.h"
 #include "qemu/uri.h"
 #include "qemu/error-report.h"
 #include "qemu/option.h"
@@ -533,23 +535,6 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
     qemu_co_mutex_unlock(&s->queue_lock);
 }
 
-static SocketAddress *sd_socket_address(const char *path,
-                                        const char *host, const char *port)
-{
-    SocketAddress *addr = g_new0(SocketAddress, 1);
-
-    if (path) {
-        addr->type = SOCKET_ADDRESS_TYPE_UNIX;
-        addr->u.q_unix.path = g_strdup(path);
-    } else {
-        addr->type = SOCKET_ADDRESS_TYPE_INET;
-        addr->u.inet.host = g_strdup(host ?: SD_DEFAULT_ADDR);
-        addr->u.inet.port = g_strdup(port ?: stringify(SD_DEFAULT_PORT));
-    }
-
-    return addr;
-}
-
 static SocketAddress *sd_server_config(QDict *options, Error **errp)
 {
     QDict *server = NULL;
@@ -1882,6 +1867,86 @@ out_with_err_set:
     return ret;
 }
 
+static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
+                              Error **errp)
+{
+    BlockDriverState *bs;
+    Visitor *v;
+    QObject *obj = NULL;
+    QDict *qdict;
+    Error *local_err = NULL;
+    int ret;
+
+    v = qobject_output_visitor_new(&obj);
+    visit_type_BlockdevOptionsSheepdog(v, NULL, &location, &local_err);
+    visit_free(v);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        qobject_decref(obj);
+        return -EINVAL;
+    }
+
+    qdict = qobject_to_qdict(obj);
+    qdict_flatten(qdict);
+
+    qdict_put_str(qdict, "driver", "sheepdog");
+
+    bs = bdrv_open(NULL, NULL, qdict, BDRV_O_PROTOCOL | BDRV_O_RDWR, errp);
+    if (bs == NULL) {
+        ret = -EIO;
+        goto fail;
+    }
+
+    ret = sd_prealloc(bs, 0, size, errp);
+fail:
+    bdrv_unref(bs);
+    QDECREF(qdict);
+    return ret;
+}
+
+static int parse_redundancy(BDRVSheepdogState *s, SheepdogRedundancy *opt)
+{
+    struct SheepdogInode *inode = &s->inode;
+
+    switch (opt->type) {
+    case SHEEPDOG_REDUNDANCY_TYPE_FULL:
+        if (opt->u.full.copies > SD_MAX_COPIES || opt->u.full.copies < 1) {
+            return -EINVAL;
+        }
+        inode->copy_policy = 0;
+        inode->nr_copies = opt->u.full.copies;
+        return 0;
+
+    case SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED:
+    {
+        int64_t copy = opt->u.erasure_coded.data_strips;
+        int64_t parity = opt->u.erasure_coded.parity_strips;
+
+        if (copy != 2 && copy != 4 && copy != 8 && copy != 16) {
+            return -EINVAL;
+        }
+
+        if (parity >= SD_EC_MAX_STRIP || parity < 1) {
+            return -EINVAL;
+        }
+
+        /*
+         * 4 bits for parity and 4 bits for data.
+         * We have to compress upper data bits because it can't represent 16
+         */
+        inode->copy_policy = ((copy / 2) << 4) + parity;
+        inode->nr_copies = copy + parity;
+        return 0;
+    }
+
+    default:
+        g_assert_not_reached();
+    }
+
+    return -EINVAL;
+}
+
 /*
  * Sheepdog support two kinds of redundancy, full replication and erasure
  * coding.
@@ -1892,60 +1957,61 @@ out_with_err_set:
  * # create a erasure coded vdi with x data strips and y parity strips
  * -o redundancy=x:y (x must be one of {2,4,8,16} and 1 <= y < SD_EC_MAX_STRIP)
  */
-static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
+static SheepdogRedundancy *parse_redundancy_str(const char *opt)
 {
-    struct SheepdogInode *inode = &s->inode;
+    SheepdogRedundancy *redundancy;
     const char *n1, *n2;
     long copy, parity;
     char p[10];
+    int ret;
 
     pstrcpy(p, sizeof(p), opt);
     n1 = strtok(p, ":");
     n2 = strtok(NULL, ":");
 
     if (!n1) {
-        return -EINVAL;
+        return NULL;
     }
 
-    copy = strtol(n1, NULL, 10);
-    /* FIXME fix error checking by switching to qemu_strtol() */
-    if (copy > SD_MAX_COPIES || copy < 1) {
-        return -EINVAL;
-    }
-    if (!n2) {
-        inode->copy_policy = 0;
-        inode->nr_copies = copy;
-        return 0;
+    ret = qemu_strtol(n1, NULL, 10, &copy);
+    if (ret < 0) {
+        return NULL;
     }
 
-    if (copy != 2 && copy != 4 && copy != 8 && copy != 16) {
-        return -EINVAL;
-    }
+    redundancy = g_new0(SheepdogRedundancy, 1);
+    if (!n2) {
+        *redundancy = (SheepdogRedundancy) {
+            .type               = SHEEPDOG_REDUNDANCY_TYPE_FULL,
+            .u.full.copies      = copy,
+        };
+    } else {
+        ret = qemu_strtol(n2, NULL, 10, &parity);
+        if (ret < 0) {
+            return NULL;
+        }
 
-    parity = strtol(n2, NULL, 10);
-    /* FIXME fix error checking by switching to qemu_strtol() */
-    if (parity >= SD_EC_MAX_STRIP || parity < 1) {
-        return -EINVAL;
+        *redundancy = (SheepdogRedundancy) {
+            .type               = SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED,
+            .u.erasure_coded    = {
+                .data_strips    = copy,
+                .parity_strips  = parity,
+            },
+        };
     }
 
-    /*
-     * 4 bits for parity and 4 bits for data.
-     * We have to compress upper data bits because it can't represent 16
-     */
-    inode->copy_policy = ((copy / 2) << 4) + parity;
-    inode->nr_copies = copy + parity;
-
-    return 0;
+    return redundancy;
 }
 
-static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
+static int parse_block_size_shift(BDRVSheepdogState *s,
+                                  BlockdevCreateOptionsSheepdog *opts)
 {
     struct SheepdogInode *inode = &s->inode;
     uint64_t object_size;
     int obj_order;
 
-    object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0);
-    if (object_size) {
+    if (opts->has_object_size) {
+        object_size = opts->object_size;
+
         if ((object_size - 1) & object_size) {    /* not a power of 2? */
             return -EINVAL;
         }
@@ -1959,57 +2025,55 @@ static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
     return 0;
 }
 
-static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
-                                          Error **errp)
+static int sd_co_create(BlockdevCreateOptions *options, Error **errp)
 {
-    Error *err = NULL;
+    BlockdevCreateOptionsSheepdog *opts = &options->u.sheepdog;
     int ret = 0;
     uint32_t vid = 0;
     char *backing_file = NULL;
     char *buf = NULL;
     BDRVSheepdogState *s;
-    SheepdogConfig cfg;
     uint64_t max_vdi_size;
     bool prealloc = false;
 
+    assert(options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
+
     s = g_new0(BDRVSheepdogState, 1);
 
-    if (strstr(filename, "://")) {
-        sd_parse_uri(&cfg, filename, &err);
-    } else {
-        parse_vdiname(&cfg, filename, &err);
-    }
-    if (err) {
-        error_propagate(errp, err);
+    /* Steal SocketAddress from QAPI, set NULL to prevent double free */
+    s->addr = opts->location->server;
+    opts->location->server = NULL;
+
+    if (strlen(opts->location->vdi) >= sizeof(s->name)) {
+        error_setg(errp, "'vdi' string too long");
+        ret = -EINVAL;
         goto out;
     }
+    pstrcpy(s->name, sizeof(s->name), opts->location->vdi);
 
-    buf = cfg.port ? g_strdup_printf("%d", cfg.port) : NULL;
-    s->addr = sd_socket_address(cfg.path, cfg.host, buf);
-    g_free(buf);
-    strcpy(s->name, cfg.vdi);
-    sd_config_done(&cfg);
+    s->inode.vdi_size = opts->size;
+    backing_file = opts->backing_file;
 
-    s->inode.vdi_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
-                                 BDRV_SECTOR_SIZE);
-    backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
-    buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
-    if (!buf || !strcmp(buf, "off")) {
+    if (!opts->has_preallocation) {
+        opts->preallocation = PREALLOC_MODE_OFF;
+    }
+    switch (opts->preallocation) {
+    case PREALLOC_MODE_OFF:
         prealloc = false;
-    } else if (!strcmp(buf, "full")) {
+        break;
+    case PREALLOC_MODE_FULL:
         prealloc = true;
-    } else {
-        error_setg(errp, "Invalid preallocation mode: '%s'", buf);
+        break;
+    default:
+        error_setg(errp, "Preallocation mode not supported for Sheepdog");
         ret = -EINVAL;
         goto out;
     }
 
-    g_free(buf);
-    buf = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
-    if (buf) {
-        ret = parse_redundancy(s, buf);
+    if (opts->has_redundancy) {
+        ret = parse_redundancy(s, opts->redundancy);
         if (ret < 0) {
-            error_setg(errp, "Invalid redundancy mode: '%s'", buf);
+            error_setg(errp, "Invalid redundancy mode");
             goto out;
         }
     }
@@ -2021,20 +2085,20 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
         goto out;
     }
 
-    if (backing_file) {
+    if (opts->has_backing_file) {
         BlockBackend *blk;
         BDRVSheepdogState *base;
         BlockDriver *drv;
 
         /* Currently, only Sheepdog backing image is supported. */
-        drv = bdrv_find_protocol(backing_file, true, NULL);
+        drv = bdrv_find_protocol(opts->backing_file, true, NULL);
         if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) {
             error_setg(errp, "backing_file must be a sheepdog image");
             ret = -EINVAL;
             goto out;
         }
 
-        blk = blk_new_open(backing_file, NULL, NULL,
+        blk = blk_new_open(opts->backing_file, NULL, NULL,
                            BDRV_O_PROTOCOL, errp);
         if (blk == NULL) {
             ret = -EIO;
@@ -2102,28 +2166,96 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
     }
 
     if (prealloc) {
-        BlockDriverState *bs;
-        QDict *opts;
-
-        opts = qdict_new();
-        qdict_put_str(opts, "driver", "sheepdog");
-        bs = bdrv_open(filename, NULL, opts, BDRV_O_PROTOCOL | BDRV_O_RDWR,
-                       errp);
-        if (!bs) {
-            goto out;
-        }
-
-        ret = sd_prealloc(bs, 0, s->inode.vdi_size, errp);
-
-        bdrv_unref(bs);
+        ret = sd_create_prealloc(opts->location, opts->size, errp);
     }
 out:
     g_free(backing_file);
     g_free(buf);
+    g_free(s->addr);
     g_free(s);
     return ret;
 }
 
+static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
+                                          Error **errp)
+{
+    BlockdevCreateOptions *create_options = NULL;
+    QDict *qdict, *location_qdict;
+    QObject *crumpled;
+    Visitor *v;
+    const char *redundancy;
+    Error *local_err = NULL;
+    int ret;
+
+    redundancy = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
+
+    qdict = qemu_opts_to_qdict(opts, NULL);
+    qdict_put_str(qdict, "driver", "sheepdog");
+
+    location_qdict = qdict_new();
+    qdict_put(qdict, "location", location_qdict);
+
+    sd_parse_filename(filename, location_qdict, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        ret = -EINVAL;
+        goto fail;
+    }
+
+    qdict_flatten(qdict);
+
+    /* Change legacy command line options into QMP ones */
+    static const QDictRenames opt_renames[] = {
+        { BLOCK_OPT_BACKING_FILE,       "backing-file" },
+        { BLOCK_OPT_OBJECT_SIZE,        "object-size" },
+        { NULL, NULL },
+    };
+
+    if (!qdict_rename_keys(qdict, opt_renames, errp)) {
+        ret = -EINVAL;
+        goto fail;
+    }
+
+    /* Get the QAPI object */
+    crumpled = qdict_crumple(qdict, errp);
+    if (crumpled == NULL) {
+        ret = -EINVAL;
+        goto fail;
+    }
+
+    v = qobject_input_visitor_new_keyval(crumpled);
+    visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+    visit_free(v);
+    qobject_decref(crumpled);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        ret = -EINVAL;
+        goto fail;
+    }
+
+    assert(create_options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
+    create_options->u.sheepdog.size =
+        ROUND_UP(create_options->u.sheepdog.size, BDRV_SECTOR_SIZE);
+
+    if (redundancy) {
+        create_options->u.sheepdog.has_redundancy = true;
+        create_options->u.sheepdog.redundancy =
+            parse_redundancy_str(redundancy);
+        if (create_options->u.sheepdog.redundancy == NULL) {
+            error_setg(errp, "Invalid redundancy mode");
+            ret = -EINVAL;
+            goto fail;
+        }
+    }
+
+    ret = sd_co_create(create_options, errp);
+fail:
+    qapi_free_BlockdevCreateOptions(create_options);
+    QDECREF(qdict);
+    return ret;
+}
+
 static void sd_close(BlockDriverState *bs)
 {
     Error *local_err = NULL;
@@ -3103,6 +3235,7 @@ static BlockDriver bdrv_sheepdog = {
     .bdrv_reopen_commit           = sd_reopen_commit,
     .bdrv_reopen_abort            = sd_reopen_abort,
     .bdrv_close                   = sd_close,
+    .bdrv_co_create               = sd_co_create,
     .bdrv_co_create_opts          = sd_co_create_opts,
     .bdrv_has_zero_init           = bdrv_has_zero_init_1,
     .bdrv_getlength               = sd_getlength,
@@ -3139,6 +3272,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
     .bdrv_reopen_commit           = sd_reopen_commit,
     .bdrv_reopen_abort            = sd_reopen_abort,
     .bdrv_close                   = sd_close,
+    .bdrv_co_create               = sd_co_create,
     .bdrv_co_create_opts          = sd_co_create_opts,
     .bdrv_has_zero_init           = bdrv_has_zero_init_1,
     .bdrv_getlength               = sd_getlength,
@@ -3175,6 +3309,7 @@ static BlockDriver bdrv_sheepdog_unix = {
     .bdrv_reopen_commit           = sd_reopen_commit,
     .bdrv_reopen_abort            = sd_reopen_abort,
     .bdrv_close                   = sd_close,
+    .bdrv_co_create               = sd_co_create,
     .bdrv_co_create_opts          = sd_co_create_opts,
     .bdrv_has_zero_init           = bdrv_has_zero_init_1,
     .bdrv_getlength               = sd_getlength,
diff --git a/block/ssh.c b/block/ssh.c
index ff9929497d..ab3acf0c22 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -35,6 +35,7 @@
 #include "qemu/sockets.h"
 #include "qemu/uri.h"
 #include "qapi/qapi-visit-sockets.h"
+#include "qapi/qapi-visit-block-core.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qstring.h"
 #include "qapi/qobject-input-visitor.h"
@@ -430,31 +431,35 @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
 }
 
 static int check_host_key(BDRVSSHState *s, const char *host, int port,
-                          const char *host_key_check, Error **errp)
+                          SshHostKeyCheck *hkc, Error **errp)
 {
-    /* host_key_check=no */
-    if (strcmp(host_key_check, "no") == 0) {
-        return 0;
-    }
-
-    /* host_key_check=md5:xx:yy:zz:... */
-    if (strncmp(host_key_check, "md5:", 4) == 0) {
-        return check_host_key_hash(s, &host_key_check[4],
-                                   LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
-    }
+    SshHostKeyCheckMode mode;
 
-    /* host_key_check=sha1:xx:yy:zz:... */
-    if (strncmp(host_key_check, "sha1:", 5) == 0) {
-        return check_host_key_hash(s, &host_key_check[5],
-                                   LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
+    if (hkc) {
+        mode = hkc->mode;
+    } else {
+        mode = SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS;
     }
 
-    /* host_key_check=yes */
-    if (strcmp(host_key_check, "yes") == 0) {
+    switch (mode) {
+    case SSH_HOST_KEY_CHECK_MODE_NONE:
+        return 0;
+    case SSH_HOST_KEY_CHECK_MODE_HASH:
+        if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) {
+            return check_host_key_hash(s, hkc->u.hash.hash,
+                                       LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
+        } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
+            return check_host_key_hash(s, hkc->u.hash.hash,
+                                       LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
+        }
+        g_assert_not_reached();
+        break;
+    case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS:
         return check_host_key_knownhosts(s, host, port, errp);
+    default:
+        g_assert_not_reached();
     }
 
-    error_setg(errp, "unknown host_key_check setting (%s)", host_key_check);
     return -EINVAL;
 }
 
@@ -544,16 +549,6 @@ static QemuOptsList ssh_runtime_opts = {
             .help = "Port to connect to",
         },
         {
-            .name = "path",
-            .type = QEMU_OPT_STRING,
-            .help = "Path of the image on the host",
-        },
-        {
-            .name = "user",
-            .type = QEMU_OPT_STRING,
-            .help = "User as which to connect",
-        },
-        {
             .name = "host_key_check",
             .type = QEMU_OPT_STRING,
             .help = "Defines how and what to check the host key against",
@@ -562,12 +557,13 @@ static QemuOptsList ssh_runtime_opts = {
     },
 };
 
-static bool ssh_process_legacy_socket_options(QDict *output_opts,
-                                              QemuOpts *legacy_opts,
-                                              Error **errp)
+static bool ssh_process_legacy_options(QDict *output_opts,
+                                       QemuOpts *legacy_opts,
+                                       Error **errp)
 {
     const char *host = qemu_opt_get(legacy_opts, "host");
     const char *port = qemu_opt_get(legacy_opts, "port");
+    const char *host_key_check = qemu_opt_get(legacy_opts, "host_key_check");
 
     if (!host && port) {
         error_setg(errp, "port may not be used without host");
@@ -579,26 +575,56 @@ static bool ssh_process_legacy_socket_options(QDict *output_opts,
         qdict_put_str(output_opts, "server.port", port ?: stringify(22));
     }
 
+    if (host_key_check) {
+        if (strcmp(host_key_check, "no") == 0) {
+            qdict_put_str(output_opts, "host-key-check.mode", "none");
+        } else if (strncmp(host_key_check, "md5:", 4) == 0) {
+            qdict_put_str(output_opts, "host-key-check.mode", "hash");
+            qdict_put_str(output_opts, "host-key-check.type", "md5");
+            qdict_put_str(output_opts, "host-key-check.hash",
+                          &host_key_check[4]);
+        } else if (strncmp(host_key_check, "sha1:", 5) == 0) {
+            qdict_put_str(output_opts, "host-key-check.mode", "hash");
+            qdict_put_str(output_opts, "host-key-check.type", "sha1");
+            qdict_put_str(output_opts, "host-key-check.hash",
+                          &host_key_check[5]);
+        } else if (strcmp(host_key_check, "yes") == 0) {
+            qdict_put_str(output_opts, "host-key-check.mode", "known_hosts");
+        } else {
+            error_setg(errp, "unknown host_key_check setting (%s)",
+                       host_key_check);
+            return false;
+        }
+    }
+
     return true;
 }
 
-static InetSocketAddress *ssh_config(QDict *options, Error **errp)
+static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
 {
-    InetSocketAddress *inet = NULL;
-    QDict *addr = NULL;
-    QObject *crumpled_addr = NULL;
-    Visitor *iv = NULL;
-    Error *local_error = NULL;
-
-    qdict_extract_subqdict(options, &addr, "server.");
-    if (!qdict_size(addr)) {
-        error_setg(errp, "SSH server address missing");
-        goto out;
+    BlockdevOptionsSsh *result = NULL;
+    QemuOpts *opts = NULL;
+    Error *local_err = NULL;
+    QObject *crumpled;
+    const QDictEntry *e;
+    Visitor *v;
+
+    /* Translate legacy options */
+    opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
+    qemu_opts_absorb_qdict(opts, options, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto fail;
     }
 
-    crumpled_addr = qdict_crumple(addr, errp);
-    if (!crumpled_addr) {
-        goto out;
+    if (!ssh_process_legacy_options(options, opts, errp)) {
+        goto fail;
+    }
+
+    /* Create the QAPI object */
+    crumpled = qdict_crumple(options, errp);
+    if (crumpled == NULL) {
+        goto fail;
     }
 
     /*
@@ -609,51 +635,37 @@ static InetSocketAddress *ssh_config(QDict *options, Error **errp)
      * but when they come from -drive, they're all QString.  The
      * visitor expects the former.
      */
-    iv = qobject_input_visitor_new(crumpled_addr);
-    visit_type_InetSocketAddress(iv, NULL, &inet, &local_error);
-    if (local_error) {
-        error_propagate(errp, local_error);
-        goto out;
+    v = qobject_input_visitor_new(crumpled);
+    visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
+    visit_free(v);
+    qobject_decref(crumpled);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto fail;
     }
 
-out:
-    QDECREF(addr);
-    qobject_decref(crumpled_addr);
-    visit_free(iv);
-    return inet;
+    /* Remove the processed options from the QDict (the visitor processes
+     * _all_ options in the QDict) */
+    while ((e = qdict_first(options))) {
+        qdict_del(options, e->key);
+    }
+
+fail:
+    qemu_opts_del(opts);
+    return result;
 }
 
-static int connect_to_ssh(BDRVSSHState *s, QDict *options,
+static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
                           int ssh_flags, int creat_mode, Error **errp)
 {
     int r, ret;
-    QemuOpts *opts = NULL;
-    Error *local_err = NULL;
-    const char *user, *path, *host_key_check;
+    const char *user;
     long port = 0;
 
-    opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
-    qemu_opts_absorb_qdict(opts, options, &local_err);
-    if (local_err) {
-        ret = -EINVAL;
-        error_propagate(errp, local_err);
-        goto err;
-    }
-
-    if (!ssh_process_legacy_socket_options(options, opts, errp)) {
-        ret = -EINVAL;
-        goto err;
-    }
-
-    path = qemu_opt_get(opts, "path");
-    if (!path) {
-        ret = -EINVAL;
-        error_setg(errp, "No path was specified");
-        goto err;
-    }
-
-    user = qemu_opt_get(opts, "user");
-    if (!user) {
+    if (opts->has_user) {
+        user = opts->user;
+    } else {
         user = g_get_user_name();
         if (!user) {
             error_setg_errno(errp, errno, "Can't get user name");
@@ -662,17 +674,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
         }
     }
 
-    host_key_check = qemu_opt_get(opts, "host_key_check");
-    if (!host_key_check) {
-        host_key_check = "yes";
-    }
-
     /* Pop the config into our state object, Exit if invalid */
-    s->inet = ssh_config(options, errp);
-    if (!s->inet) {
-        ret = -EINVAL;
-        goto err;
-    }
+    s->inet = opts->server;
+    opts->server = NULL;
 
     if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) {
         error_setg(errp, "Use only numeric port value");
@@ -707,8 +711,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
     }
 
     /* Check the remote host's key against known_hosts. */
-    ret = check_host_key(s, s->inet->host, port, host_key_check,
-                         errp);
+    ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp);
     if (ret < 0) {
         goto err;
     }
@@ -729,16 +732,16 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
 
     /* Open the remote file. */
     DPRINTF("opening file %s flags=0x%x creat_mode=0%o",
-            path, ssh_flags, creat_mode);
-    s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode);
+            opts->path, ssh_flags, creat_mode);
+    s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags,
+                                       creat_mode);
     if (!s->sftp_handle) {
-        session_error_setg(errp, s, "failed to open remote file '%s'", path);
+        session_error_setg(errp, s, "failed to open remote file '%s'",
+                           opts->path);
         ret = -EINVAL;
         goto err;
     }
 
-    qemu_opts_del(opts);
-
     r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
     if (r < 0) {
         sftp_error_setg(errp, s, "failed to read file attributes");
@@ -764,8 +767,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
     }
     s->session = NULL;
 
-    qemu_opts_del(opts);
-
     return ret;
 }
 
@@ -773,6 +774,7 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
                          Error **errp)
 {
     BDRVSSHState *s = bs->opaque;
+    BlockdevOptionsSsh *opts;
     int ret;
     int ssh_flags;
 
@@ -783,8 +785,13 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
         ssh_flags |= LIBSSH2_FXF_WRITE;
     }
 
+    opts = ssh_parse_options(options, errp);
+    if (opts == NULL) {
+        return -EINVAL;
+    }
+
     /* Start up SSH. */
-    ret = connect_to_ssh(s, options, ssh_flags, 0, errp);
+    ret = connect_to_ssh(s, opts, ssh_flags, 0, errp);
     if (ret < 0) {
         goto err;
     }
@@ -792,6 +799,8 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
     /* Go non-blocking. */
     libssh2_session_set_blocking(s->session, 0);
 
+    qapi_free_BlockdevOptionsSsh(opts);
+
     return 0;
 
  err:
@@ -800,6 +809,8 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
     }
     s->sock = -1;
 
+    qapi_free_BlockdevOptionsSsh(opts);
+
     return ret;
 }
 
@@ -843,51 +854,71 @@ static QemuOptsList ssh_create_opts = {
     }
 };
 
+static int ssh_co_create(BlockdevCreateOptions *options, Error **errp)
+{
+    BlockdevCreateOptionsSsh *opts = &options->u.ssh;
+    BDRVSSHState s;
+    int ret;
+
+    assert(options->driver == BLOCKDEV_DRIVER_SSH);
+
+    ssh_state_init(&s);
+
+    ret = connect_to_ssh(&s, opts->location,
+                         LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
+                         LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
+                         0644, errp);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    if (opts->size > 0) {
+        ret = ssh_grow_file(&s, opts->size, errp);
+        if (ret < 0) {
+            goto fail;
+        }
+    }
+
+    ret = 0;
+fail:
+    ssh_state_free(&s);
+    return ret;
+}
+
 static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
                                            Error **errp)
 {
-    int r, ret;
-    int64_t total_size = 0;
+    BlockdevCreateOptions *create_options;
+    BlockdevCreateOptionsSsh *ssh_opts;
+    int ret;
     QDict *uri_options = NULL;
-    BDRVSSHState s;
 
-    ssh_state_init(&s);
+    create_options = g_new0(BlockdevCreateOptions, 1);
+    create_options->driver = BLOCKDEV_DRIVER_SSH;
+    ssh_opts = &create_options->u.ssh;
 
     /* Get desired file size. */
-    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
-                          BDRV_SECTOR_SIZE);
-    DPRINTF("total_size=%" PRIi64, total_size);
+    ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+                              BDRV_SECTOR_SIZE);
+    DPRINTF("total_size=%" PRIi64, ssh_opts->size);
 
     uri_options = qdict_new();
-    r = parse_uri(filename, uri_options, errp);
-    if (r < 0) {
-        ret = r;
+    ret = parse_uri(filename, uri_options, errp);
+    if (ret < 0) {
         goto out;
     }
 
-    r = connect_to_ssh(&s, uri_options,
-                       LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
-                       LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
-                       0644, errp);
-    if (r < 0) {
-        ret = r;
+    ssh_opts->location = ssh_parse_options(uri_options, errp);
+    if (ssh_opts->location == NULL) {
+        ret = -EINVAL;
         goto out;
     }
 
-    if (total_size > 0) {
-        ret = ssh_grow_file(&s, total_size, errp);
-        if (ret < 0) {
-            goto out;
-        }
-    }
-
-    ret = 0;
+    ret = ssh_co_create(create_options, errp);
 
  out:
-    ssh_state_free(&s);
-    if (uri_options != NULL) {
-        QDECREF(uri_options);
-    }
+    QDECREF(uri_options);
+    qapi_free_BlockdevCreateOptions(create_options);
     return ret;
 }
 
@@ -1249,6 +1280,7 @@ static BlockDriver bdrv_ssh = {
     .instance_size                = sizeof(BDRVSSHState),
     .bdrv_parse_filename          = ssh_parse_filename,
     .bdrv_file_open               = ssh_file_open,
+    .bdrv_co_create               = ssh_co_create,
     .bdrv_co_create_opts          = ssh_co_create_opts,
     .bdrv_close                   = ssh_close,
     .bdrv_has_zero_init           = ssh_has_zero_init,
diff --git a/block/vdi.c b/block/vdi.c
index 68592cc58d..2b5ddd0666 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -263,8 +263,8 @@ static void vdi_header_print(VdiHeader *header)
 }
 #endif
 
-static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res,
-                     BdrvCheckMode fix)
+static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
+                                     BdrvCheckMode fix)
 {
     /* TODO: additional checks possible. */
     BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
@@ -908,7 +908,7 @@ static BlockDriver bdrv_vdi = {
     .bdrv_get_info = vdi_get_info,
 
     .create_opts = &vdi_create_opts,
-    .bdrv_check = vdi_check,
+    .bdrv_co_check = vdi_co_check,
 };
 
 static void bdrv_vdi_init(void)
diff --git a/block/vhdx.c b/block/vhdx.c
index 3fbff5048b..d82350d07c 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -1944,8 +1944,9 @@ exit:
  * r/w and any log has already been replayed, so there is nothing (currently)
  * for us to do here
  */
-static int vhdx_check(BlockDriverState *bs, BdrvCheckResult *result,
-                       BdrvCheckMode fix)
+static int coroutine_fn vhdx_co_check(BlockDriverState *bs,
+                                      BdrvCheckResult *result,
+                                      BdrvCheckMode fix)
 {
     BDRVVHDXState *s = bs->opaque;
 
@@ -2006,7 +2007,7 @@ static BlockDriver bdrv_vhdx = {
     .bdrv_co_writev         = vhdx_co_writev,
     .bdrv_co_create_opts    = vhdx_co_create_opts,
     .bdrv_get_info          = vhdx_get_info,
-    .bdrv_check             = vhdx_check,
+    .bdrv_co_check          = vhdx_co_check,
     .bdrv_has_zero_init     = bdrv_has_zero_init_1,
 
     .create_opts            = &vhdx_create_opts,
diff --git a/block/vmdk.c b/block/vmdk.c
index 67342ed69b..f94c49a9c0 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -2221,8 +2221,9 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
     return info;
 }
 
-static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result,
-                      BdrvCheckMode fix)
+static int coroutine_fn vmdk_co_check(BlockDriverState *bs,
+                                      BdrvCheckResult *result,
+                                      BdrvCheckMode fix)
 {
     BDRVVmdkState *s = bs->opaque;
     VmdkExtent *extent = NULL;
@@ -2391,7 +2392,7 @@ static BlockDriver bdrv_vmdk = {
     .instance_size                = sizeof(BDRVVmdkState),
     .bdrv_probe                   = vmdk_probe,
     .bdrv_open                    = vmdk_open,
-    .bdrv_check                   = vmdk_check,
+    .bdrv_co_check                = vmdk_co_check,
     .bdrv_reopen_prepare          = vmdk_reopen_prepare,
     .bdrv_child_perm              = bdrv_format_default_perms,
     .bdrv_co_preadv               = vmdk_co_preadv,
diff --git a/configure b/configure
index f74e1f3b7c..af72fc852e 100755
--- a/configure
+++ b/configure
@@ -1692,6 +1692,7 @@ gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
 gcc_flags="-Wendif-labels -Wno-shift-negative-value $gcc_flags"
 gcc_flags="-Wno-initializer-overrides -Wexpansion-to-defined $gcc_flags"
 gcc_flags="-Wno-string-plus-int $gcc_flags"
+gcc_flags="-Wno-error=address-of-packed-member $gcc_flags"
 # Note that we do not add -Werror to gcc_flags here, because that would
 # enable it for all configure tests. If a configure test failed due
 # to -Werror this would just silently disable some features,
@@ -2874,6 +2875,7 @@ if test "$sdl" != "no" ; then
 int main( void ) { return SDL_Init (SDL_INIT_VIDEO); }
 EOF
   sdl_cflags=$($sdlconfig --cflags 2>/dev/null)
+  sdl_cflags="$sdl_cflags -Wno-undef"  # workaround 2.0.8 bug
   if test "$static" = "yes" ; then
     if $pkg_config $sdlname --exists; then
       sdl_libs=$($pkg_config $sdlname --static --libs 2>/dev/null)
@@ -3352,7 +3354,7 @@ else
 fi
 glib_modules=gthread-2.0
 if test "$modules" = yes; then
-    glib_modules="$glib_modules gmodule-2.0"
+    glib_modules="$glib_modules gmodule-export-2.0"
 fi
 
 # This workaround is required due to a bug in pkg-config file for glib as it
@@ -4860,7 +4862,6 @@ fi
 pragma_disable_unused_but_set=no
 cat > $TMPC << EOF
 #pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wstrict-prototypes"
 #pragma GCC diagnostic pop
 
@@ -5973,7 +5974,12 @@ fi
 echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak
 for drv in $audio_drv_list; do
     def=CONFIG_AUDIO_$(echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]')
-    echo "$def=y" >> $config_host_mak
+    case "$drv" in
+	alsa | oss | pa | sdl)
+	    echo "$def=m" >> $config_host_mak ;;
+	*)
+	    echo "$def=y" >> $config_host_mak ;;
+    esac
 done
 echo "ALSA_LIBS=$alsa_libs" >> $config_host_mak
 echo "PULSE_LIBS=$pulse_libs" >> $config_host_mak
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index d724106bd3..dd29e741c2 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -47,6 +47,7 @@ CONFIG_A9MPCORE=y
 CONFIG_A15MPCORE=y
 
 CONFIG_ARM_V7M=y
+CONFIG_NETDUINO2=y
 
 CONFIG_ARM_GIC=y
 CONFIG_ARM_GIC_KVM=$(CONFIG_KVM)
@@ -109,6 +110,7 @@ CONFIG_TZ_PPC=y
 CONFIG_IOTKIT=y
 CONFIG_IOTKIT_SECCTL=y
 
+CONFIG_VERSATILE=y
 CONFIG_VERSATILE_PCI=y
 CONFIG_VERSATILE_I2C=y
 
@@ -117,6 +119,7 @@ CONFIG_VFIO_XGMAC=y
 CONFIG_VFIO_AMD_XGBE=y
 
 CONFIG_SDHCI=y
+CONFIG_INTEGRATOR=y
 CONFIG_INTEGRATOR_DEBUG=y
 
 CONFIG_ALLWINNER_A10_PIT=y
@@ -126,6 +129,7 @@ CONFIG_ALLWINNER_A10=y
 CONFIG_FSL_IMX6=y
 CONFIG_FSL_IMX31=y
 CONFIG_FSL_IMX25=y
+CONFIG_FSL_IMX7=y
 
 CONFIG_IMX_I2C=y
 
@@ -140,3 +144,8 @@ CONFIG_GPIO_KEY=y
 CONFIG_MSF2=y
 CONFIG_FW_CFG_DMA=y
 CONFIG_XILINX_AXI=y
+CONFIG_PCI_DESIGNWARE=y
+
+CONFIG_STRONGARM=y
+CONFIG_HIGHBANK=y
+CONFIG_MUSICPAL=y
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 964eb515cf..1723cbe1df 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -253,9 +253,10 @@ ETEXI
 
     {
         .name       = "screendump",
-        .args_type  = "filename:F",
-        .params     = "filename",
-        .help       = "save screen into PPM image 'filename'",
+        .args_type  = "filename:F,device:s?,head:i?",
+        .params     = "filename [device [head]]",
+        .help       = "save screen from head 'head' of display device 'device' "
+                      "into PPM image 'filename'",
         .cmd        = hmp_screendump,
     },
 
diff --git a/hmp.c b/hmp.c
index 016cb5c4f1..ba9e299ee2 100644
--- a/hmp.c
+++ b/hmp.c
@@ -2140,9 +2140,11 @@ err_out:
 void hmp_screendump(Monitor *mon, const QDict *qdict)
 {
     const char *filename = qdict_get_str(qdict, "filename");
+    const char *id = qdict_get_try_str(qdict, "device");
+    int64_t head = qdict_get_try_int(qdict, "head", 0);
     Error *err = NULL;
 
-    qmp_screendump(filename, &err);
+    qmp_screendump(filename, id != NULL, id, id != NULL, head, &err);
     hmp_handle_error(mon, &err);
 }
 
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 232258160a..2885e3e234 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -1,15 +1,27 @@
-obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
-obj-$(CONFIG_DIGIC) += digic_boards.o
-obj-y += integratorcp.o mainstone.o musicpal.o nseries.o
-obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
-obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
+obj-y += boot.o virt.o sysbus-fdt.o
 obj-$(CONFIG_ACPI) += virt-acpi-build.o
-obj-y += netduino2.o
-obj-y += sysbus-fdt.o
+obj-$(CONFIG_DIGIC) += digic_boards.o
+obj-$(CONFIG_EXYNOS4) += exynos4_boards.o
+obj-$(CONFIG_HIGHBANK) += highbank.o
+obj-$(CONFIG_INTEGRATOR) += integratorcp.o
+obj-$(CONFIG_MAINSTONE) += mainstone.o
+obj-$(CONFIG_MUSICPAL) += musicpal.o
+obj-$(CONFIG_NETDUINO2) += netduino2.o
+obj-$(CONFIG_NSERIES) += nseries.o
+obj-$(CONFIG_OMAP) += omap_sx1.o palm.o
+obj-$(CONFIG_PXA2XX) += gumstix.o spitz.o tosa.o z2.o
+obj-$(CONFIG_REALVIEW) += realview.o
+obj-$(CONFIG_STELLARIS) += stellaris.o
+obj-$(CONFIG_STRONGARM) += collie.o
+obj-$(CONFIG_VERSATILE) += vexpress.o versatilepb.o
+obj-$(CONFIG_ZYNQ) += xilinx_zynq.o
 
-obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
+obj-$(CONFIG_ARM_V7M) += armv7m.o
+obj-$(CONFIG_EXYNOS4) += exynos4210.o
+obj-$(CONFIG_PXA2XX) += pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
 obj-$(CONFIG_DIGIC) += digic.o
-obj-y += omap1.o omap2.o strongarm.o
+obj-$(CONFIG_OMAP) += omap1.o omap2.o
+obj-$(CONFIG_STRONGARM) += strongarm.o
 obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
 obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
 obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
@@ -22,3 +34,4 @@ obj-$(CONFIG_MPS2) += mps2.o
 obj-$(CONFIG_MPS2) += mps2-tz.o
 obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
 obj-$(CONFIG_IOTKIT) += iotkit.o
+obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 6d0c92ab88..196c7fb242 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -829,6 +829,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
 
     load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
     if (err) {
+        error_free(err);
         return ret;
     }
 
@@ -890,7 +891,8 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
     }
 
     /* check the arm64 magic header value -- very old kernels may not have it */
-    if (memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
+    if (size > ARM64_MAGIC_OFFSET + 4 &&
+        memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
         uint64_t hdrvals[2];
 
         /* The arm64 Image header has text_offset and image_size fields at 8 and
diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c
new file mode 100644
index 0000000000..26ef36c79a
--- /dev/null
+++ b/hw/arm/fsl-imx7.c
@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * i.MX7 SoC definitions
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * Based on hw/arm/fsl-imx6.c
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "hw/arm/fsl-imx7.h"
+#include "hw/misc/unimp.h"
+#include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
+
+#define NAME_SIZE 20
+
+static void fsl_imx7_init(Object *obj)
+{
+    BusState *sysbus = sysbus_get_default();
+    FslIMX7State *s = FSL_IMX7(obj);
+    char name[NAME_SIZE];
+    int i;
+
+    if (smp_cpus > FSL_IMX7_NUM_CPUS) {
+        error_report("%s: Only %d CPUs are supported (%d requested)",
+                     TYPE_FSL_IMX7, FSL_IMX7_NUM_CPUS, smp_cpus);
+        exit(1);
+    }
+
+    for (i = 0; i < smp_cpus; i++) {
+        object_initialize(&s->cpu[i], sizeof(s->cpu[i]),
+                          ARM_CPU_TYPE_NAME("cortex-a7"));
+        snprintf(name, NAME_SIZE, "cpu%d", i);
+        object_property_add_child(obj, name, OBJECT(&s->cpu[i]),
+                                  &error_fatal);
+    }
+
+    /*
+     * A7MPCORE
+     */
+    object_initialize(&s->a7mpcore, sizeof(s->a7mpcore), TYPE_A15MPCORE_PRIV);
+    qdev_set_parent_bus(DEVICE(&s->a7mpcore), sysbus);
+    object_property_add_child(obj, "a7mpcore",
+                              OBJECT(&s->a7mpcore), &error_fatal);
+
+    /*
+     * GPIOs 1 to 7
+     */
+    for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
+        object_initialize(&s->gpio[i], sizeof(s->gpio[i]),
+                          TYPE_IMX_GPIO);
+        qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus);
+        snprintf(name, NAME_SIZE, "gpio%d", i);
+        object_property_add_child(obj, name,
+                                  OBJECT(&s->gpio[i]), &error_fatal);
+    }
+
+    /*
+     * GPT1, 2, 3, 4
+     */
+    for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) {
+        object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX7_GPT);
+        qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus);
+        snprintf(name, NAME_SIZE, "gpt%d", i);
+        object_property_add_child(obj, name, OBJECT(&s->gpt[i]),
+                                  &error_fatal);
+    }
+
+    /*
+     * CCM
+     */
+    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX7_CCM);
+    qdev_set_parent_bus(DEVICE(&s->ccm), sysbus);
+    object_property_add_child(obj, "ccm", OBJECT(&s->ccm), &error_fatal);
+
+    /*
+     * Analog
+     */
+    object_initialize(&s->analog, sizeof(s->analog), TYPE_IMX7_ANALOG);
+    qdev_set_parent_bus(DEVICE(&s->analog), sysbus);
+    object_property_add_child(obj, "analog", OBJECT(&s->analog), &error_fatal);
+
+    /*
+     * GPCv2
+     */
+    object_initialize(&s->gpcv2, sizeof(s->gpcv2), TYPE_IMX_GPCV2);
+    qdev_set_parent_bus(DEVICE(&s->gpcv2), sysbus);
+    object_property_add_child(obj, "gpcv2", OBJECT(&s->gpcv2), &error_fatal);
+
+    for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) {
+        object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI);
+        qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
+        snprintf(name, NAME_SIZE, "spi%d", i + 1);
+        object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL);
+    }
+
+
+    for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) {
+        object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C);
+        qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default());
+        snprintf(name, NAME_SIZE, "i2c%d", i + 1);
+        object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL);
+    }
+
+    /*
+     * UART
+     */
+    for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) {
+            object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL);
+            qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus);
+            snprintf(name, NAME_SIZE, "uart%d", i);
+            object_property_add_child(obj, name, OBJECT(&s->uart[i]),
+                                      &error_fatal);
+    }
+
+    /*
+     * Ethernet
+     */
+    for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) {
+            object_initialize(&s->eth[i], sizeof(s->eth[i]), TYPE_IMX_ENET);
+            qdev_set_parent_bus(DEVICE(&s->eth[i]), sysbus);
+            snprintf(name, NAME_SIZE, "eth%d", i);
+            object_property_add_child(obj, name, OBJECT(&s->eth[i]),
+                                      &error_fatal);
+    }
+
+    /*
+     * SDHCI
+     */
+    for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
+            object_initialize(&s->usdhc[i], sizeof(s->usdhc[i]),
+                              TYPE_IMX_USDHC);
+            qdev_set_parent_bus(DEVICE(&s->usdhc[i]), sysbus);
+            snprintf(name, NAME_SIZE, "usdhc%d", i);
+            object_property_add_child(obj, name, OBJECT(&s->usdhc[i]),
+                                      &error_fatal);
+    }
+
+    /*
+     * SNVS
+     */
+    object_initialize(&s->snvs, sizeof(s->snvs), TYPE_IMX7_SNVS);
+    qdev_set_parent_bus(DEVICE(&s->snvs), sysbus);
+    object_property_add_child(obj, "snvs", OBJECT(&s->snvs), &error_fatal);
+
+    /*
+     * Watchdog
+     */
+    for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) {
+            object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_IMX2_WDT);
+            qdev_set_parent_bus(DEVICE(&s->wdt[i]), sysbus);
+            snprintf(name, NAME_SIZE, "wdt%d", i);
+            object_property_add_child(obj, name, OBJECT(&s->wdt[i]),
+                                      &error_fatal);
+    }
+
+    /*
+     * GPR
+     */
+    object_initialize(&s->gpr, sizeof(s->gpr), TYPE_IMX7_GPR);
+    qdev_set_parent_bus(DEVICE(&s->gpr), sysbus);
+    object_property_add_child(obj, "gpr", OBJECT(&s->gpr), &error_fatal);
+
+    object_initialize(&s->pcie, sizeof(s->pcie), TYPE_DESIGNWARE_PCIE_HOST);
+    qdev_set_parent_bus(DEVICE(&s->pcie), sysbus);
+    object_property_add_child(obj, "pcie", OBJECT(&s->pcie), &error_fatal);
+
+    for (i = 0; i < FSL_IMX7_NUM_USBS; i++) {
+        object_initialize(&s->usb[i],
+                          sizeof(s->usb[i]), TYPE_CHIPIDEA);
+        qdev_set_parent_bus(DEVICE(&s->usb[i]), sysbus);
+        snprintf(name, NAME_SIZE, "usb%d", i);
+        object_property_add_child(obj, name,
+                                  OBJECT(&s->usb[i]), &error_fatal);
+    }
+}
+
+static void fsl_imx7_realize(DeviceState *dev, Error **errp)
+{
+    FslIMX7State *s = FSL_IMX7(dev);
+    Object *o;
+    int i;
+    qemu_irq irq;
+    char name[NAME_SIZE];
+
+    for (i = 0; i < smp_cpus; i++) {
+        o = OBJECT(&s->cpu[i]);
+
+        object_property_set_int(o, QEMU_PSCI_CONDUIT_SMC,
+                                "psci-conduit", &error_abort);
+
+        /* On uniprocessor, the CBAR is set to 0 */
+        if (smp_cpus > 1) {
+            object_property_set_int(o, FSL_IMX7_A7MPCORE_ADDR,
+                                    "reset-cbar", &error_abort);
+        }
+
+        if (i) {
+            /* Secondary CPUs start in PSCI powered-down state */
+            object_property_set_bool(o, true,
+                                     "start-powered-off", &error_abort);
+        }
+
+        object_property_set_bool(o, true, "realized", &error_abort);
+    }
+
+    /*
+     * A7MPCORE
+     */
+    object_property_set_int(OBJECT(&s->a7mpcore), smp_cpus, "num-cpu",
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->a7mpcore),
+                            FSL_IMX7_MAX_IRQ + GIC_INTERNAL,
+                            "num-irq", &error_abort);
+
+    object_property_set_bool(OBJECT(&s->a7mpcore), true, "realized",
+                             &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX7_A7MPCORE_ADDR);
+
+    for (i = 0; i < smp_cpus; i++) {
+        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore);
+        DeviceState  *d   = DEVICE(qemu_get_cpu(i));
+
+        irq = qdev_get_gpio_in(d, ARM_CPU_IRQ);
+        sysbus_connect_irq(sbd, i, irq);
+        irq = qdev_get_gpio_in(d, ARM_CPU_FIQ);
+        sysbus_connect_irq(sbd, i + smp_cpus, irq);
+    }
+
+    /*
+     * A7MPCORE DAP
+     */
+    create_unimplemented_device("a7mpcore-dap", FSL_IMX7_A7MPCORE_DAP_ADDR,
+                                0x100000);
+
+    /*
+     * GPT1, 2, 3, 4
+     */
+    for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) {
+        static const hwaddr FSL_IMX7_GPTn_ADDR[FSL_IMX7_NUM_GPTS] = {
+            FSL_IMX7_GPT1_ADDR,
+            FSL_IMX7_GPT2_ADDR,
+            FSL_IMX7_GPT3_ADDR,
+            FSL_IMX7_GPT4_ADDR,
+        };
+
+        s->gpt[i].ccm = IMX_CCM(&s->ccm);
+        object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized",
+                                 &error_abort);
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, FSL_IMX7_GPTn_ADDR[i]);
+    }
+
+    for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
+        static const hwaddr FSL_IMX7_GPIOn_ADDR[FSL_IMX7_NUM_GPIOS] = {
+            FSL_IMX7_GPIO1_ADDR,
+            FSL_IMX7_GPIO2_ADDR,
+            FSL_IMX7_GPIO3_ADDR,
+            FSL_IMX7_GPIO4_ADDR,
+            FSL_IMX7_GPIO5_ADDR,
+            FSL_IMX7_GPIO6_ADDR,
+            FSL_IMX7_GPIO7_ADDR,
+        };
+
+        object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized",
+                                 &error_abort);
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, FSL_IMX7_GPIOn_ADDR[i]);
+    }
+
+    /*
+     * IOMUXC and IOMUXC_LPSR
+     */
+    for (i = 0; i < FSL_IMX7_NUM_IOMUXCS; i++) {
+        static const hwaddr FSL_IMX7_IOMUXCn_ADDR[FSL_IMX7_NUM_IOMUXCS] = {
+            FSL_IMX7_IOMUXC_ADDR,
+            FSL_IMX7_IOMUXC_LPSR_ADDR,
+        };
+
+        snprintf(name, NAME_SIZE, "iomuxc%d", i);
+        create_unimplemented_device(name, FSL_IMX7_IOMUXCn_ADDR[i],
+                                    FSL_IMX7_IOMUXCn_SIZE);
+    }
+
+    /*
+     * CCM
+     */
+    object_property_set_bool(OBJECT(&s->ccm), true, "realized", &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX7_CCM_ADDR);
+
+    /*
+     * Analog
+     */
+    object_property_set_bool(OBJECT(&s->analog), true, "realized",
+                             &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, FSL_IMX7_ANALOG_ADDR);
+
+    /*
+     * GPCv2
+     */
+    object_property_set_bool(OBJECT(&s->gpcv2), true,
+                             "realized", &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpcv2), 0, FSL_IMX7_GPC_ADDR);
+
+    /* Initialize all ECSPI */
+    for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) {
+        static const hwaddr FSL_IMX7_SPIn_ADDR[FSL_IMX7_NUM_ECSPIS] = {
+            FSL_IMX7_ECSPI1_ADDR,
+            FSL_IMX7_ECSPI2_ADDR,
+            FSL_IMX7_ECSPI3_ADDR,
+            FSL_IMX7_ECSPI4_ADDR,
+        };
+
+        static const hwaddr FSL_IMX7_SPIn_IRQ[FSL_IMX7_NUM_ECSPIS] = {
+            FSL_IMX7_ECSPI1_IRQ,
+            FSL_IMX7_ECSPI2_IRQ,
+            FSL_IMX7_ECSPI3_IRQ,
+            FSL_IMX7_ECSPI4_IRQ,
+        };
+
+        /* Initialize the SPI */
+        object_property_set_bool(OBJECT(&s->spi[i]), true, "realized",
+                                 &error_abort);
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0,
+                        FSL_IMX7_SPIn_ADDR[i]);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0,
+                           qdev_get_gpio_in(DEVICE(&s->a7mpcore),
+                                            FSL_IMX7_SPIn_IRQ[i]));
+    }
+
+    for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) {
+        static const hwaddr FSL_IMX7_I2Cn_ADDR[FSL_IMX7_NUM_I2CS] = {
+            FSL_IMX7_I2C1_ADDR,
+            FSL_IMX7_I2C2_ADDR,
+            FSL_IMX7_I2C3_ADDR,
+            FSL_IMX7_I2C4_ADDR,
+        };
+
+        static const hwaddr FSL_IMX7_I2Cn_IRQ[FSL_IMX7_NUM_I2CS] = {
+            FSL_IMX7_I2C1_IRQ,
+            FSL_IMX7_I2C2_IRQ,
+            FSL_IMX7_I2C3_IRQ,
+            FSL_IMX7_I2C4_IRQ,
+        };
+
+        object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized",
+                                 &error_abort);
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX7_I2Cn_ADDR[i]);
+
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
+                           qdev_get_gpio_in(DEVICE(&s->a7mpcore),
+                                            FSL_IMX7_I2Cn_IRQ[i]));
+    }
+
+    /*
+     * UART
+     */
+    for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) {
+        static const hwaddr FSL_IMX7_UARTn_ADDR[FSL_IMX7_NUM_UARTS] = {
+            FSL_IMX7_UART1_ADDR,
+            FSL_IMX7_UART2_ADDR,
+            FSL_IMX7_UART3_ADDR,
+            FSL_IMX7_UART4_ADDR,
+            FSL_IMX7_UART5_ADDR,
+            FSL_IMX7_UART6_ADDR,
+            FSL_IMX7_UART7_ADDR,
+        };
+
+        static const int FSL_IMX7_UARTn_IRQ[FSL_IMX7_NUM_UARTS] = {
+            FSL_IMX7_UART1_IRQ,
+            FSL_IMX7_UART2_IRQ,
+            FSL_IMX7_UART3_IRQ,
+            FSL_IMX7_UART4_IRQ,
+            FSL_IMX7_UART5_IRQ,
+            FSL_IMX7_UART6_IRQ,
+            FSL_IMX7_UART7_IRQ,
+        };
+
+
+        if (i < MAX_SERIAL_PORTS) {
+            qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]);
+        }
+
+        object_property_set_bool(OBJECT(&s->uart[i]), true, "realized",
+                                 &error_abort);
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, FSL_IMX7_UARTn_ADDR[i]);
+
+        irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_UARTn_IRQ[i]);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, irq);
+    }
+
+    /*
+     * Ethernet
+     */
+    for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) {
+        static const hwaddr FSL_IMX7_ENETn_ADDR[FSL_IMX7_NUM_ETHS] = {
+            FSL_IMX7_ENET1_ADDR,
+            FSL_IMX7_ENET2_ADDR,
+        };
+
+        object_property_set_uint(OBJECT(&s->eth[i]), FSL_IMX7_ETH_NUM_TX_RINGS,
+                                 "tx-ring-num", &error_abort);
+        qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]);
+        object_property_set_bool(OBJECT(&s->eth[i]), true, "realized",
+                                 &error_abort);
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth[i]), 0, FSL_IMX7_ENETn_ADDR[i]);
+
+        irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 0));
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, irq);
+        irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 3));
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, irq);
+    }
+
+    /*
+     * USDHC
+     */
+    for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
+        static const hwaddr FSL_IMX7_USDHCn_ADDR[FSL_IMX7_NUM_USDHCS] = {
+            FSL_IMX7_USDHC1_ADDR,
+            FSL_IMX7_USDHC2_ADDR,
+            FSL_IMX7_USDHC3_ADDR,
+        };
+
+        static const int FSL_IMX7_USDHCn_IRQ[FSL_IMX7_NUM_USDHCS] = {
+            FSL_IMX7_USDHC1_IRQ,
+            FSL_IMX7_USDHC2_IRQ,
+            FSL_IMX7_USDHC3_IRQ,
+        };
+
+        object_property_set_bool(OBJECT(&s->usdhc[i]), true, "realized",
+                                 &error_abort);
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0,
+                        FSL_IMX7_USDHCn_ADDR[i]);
+
+        irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USDHCn_IRQ[i]);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, irq);
+    }
+
+    /*
+     * SNVS
+     */
+    object_property_set_bool(OBJECT(&s->snvs), true, "realized", &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX7_SNVS_ADDR);
+
+    /*
+     * SRC
+     */
+    create_unimplemented_device("sdma", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE);
+
+    /*
+     * Watchdog
+     */
+    for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) {
+        static const hwaddr FSL_IMX7_WDOGn_ADDR[FSL_IMX7_NUM_WDTS] = {
+            FSL_IMX7_WDOG1_ADDR,
+            FSL_IMX7_WDOG2_ADDR,
+            FSL_IMX7_WDOG3_ADDR,
+            FSL_IMX7_WDOG4_ADDR,
+        };
+
+        object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized",
+                                 &error_abort);
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX7_WDOGn_ADDR[i]);
+    }
+
+    /*
+     * SDMA
+     */
+    create_unimplemented_device("sdma", FSL_IMX7_SDMA_ADDR, FSL_IMX7_SDMA_SIZE);
+
+
+    object_property_set_bool(OBJECT(&s->gpr), true, "realized",
+                             &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0, FSL_IMX7_GPR_ADDR);
+
+    object_property_set_bool(OBJECT(&s->pcie), true,
+                             "realized", &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX7_PCIE_REG_ADDR);
+
+    irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTA_IRQ);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq);
+    irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTB_IRQ);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq);
+    irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTC_IRQ);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq);
+    irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_IRQ);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq);
+
+
+    for (i = 0; i < FSL_IMX7_NUM_USBS; i++) {
+        static const hwaddr FSL_IMX7_USBMISCn_ADDR[FSL_IMX7_NUM_USBS] = {
+            FSL_IMX7_USBMISC1_ADDR,
+            FSL_IMX7_USBMISC2_ADDR,
+            FSL_IMX7_USBMISC3_ADDR,
+        };
+
+        static const hwaddr FSL_IMX7_USBn_ADDR[FSL_IMX7_NUM_USBS] = {
+            FSL_IMX7_USB1_ADDR,
+            FSL_IMX7_USB2_ADDR,
+            FSL_IMX7_USB3_ADDR,
+        };
+
+        static const hwaddr FSL_IMX7_USBn_IRQ[FSL_IMX7_NUM_USBS] = {
+            FSL_IMX7_USB1_IRQ,
+            FSL_IMX7_USB2_IRQ,
+            FSL_IMX7_USB3_IRQ,
+        };
+
+        object_property_set_bool(OBJECT(&s->usb[i]), true, "realized",
+                                 &error_abort);
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0,
+                        FSL_IMX7_USBn_ADDR[i]);
+
+        irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USBn_IRQ[i]);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, irq);
+
+        snprintf(name, NAME_SIZE, "usbmisc%d", i);
+        create_unimplemented_device(name, FSL_IMX7_USBMISCn_ADDR[i],
+                                    FSL_IMX7_USBMISCn_SIZE);
+    }
+
+    /*
+     * ADCs
+     */
+    for (i = 0; i < FSL_IMX7_NUM_ADCS; i++) {
+        static const hwaddr FSL_IMX7_ADCn_ADDR[FSL_IMX7_NUM_ADCS] = {
+            FSL_IMX7_ADC1_ADDR,
+            FSL_IMX7_ADC2_ADDR,
+        };
+
+        snprintf(name, NAME_SIZE, "adc%d", i);
+        create_unimplemented_device(name, FSL_IMX7_ADCn_ADDR[i],
+                                    FSL_IMX7_ADCn_SIZE);
+    }
+
+    /*
+     * LCD
+     */
+    create_unimplemented_device("lcdif", FSL_IMX7_LCDIF_ADDR,
+                                FSL_IMX7_LCDIF_SIZE);
+}
+
+static void fsl_imx7_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = fsl_imx7_realize;
+
+    /* Reason: Uses serial_hds and nd_table in realize() directly */
+    dc->user_creatable = false;
+    dc->desc = "i.MX7 SOC";
+}
+
+static const TypeInfo fsl_imx7_type_info = {
+    .name = TYPE_FSL_IMX7,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(FslIMX7State),
+    .instance_init = fsl_imx7_init,
+    .class_init = fsl_imx7_class_init,
+};
+
+static void fsl_imx7_register_types(void)
+{
+    type_register_static(&fsl_imx7_type_info);
+}
+type_init(fsl_imx7_register_types)
diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c
new file mode 100644
index 0000000000..95fb409d9c
--- /dev/null
+++ b/hw/arm/mcimx7d-sabre.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * MCIMX7D_SABRE Board System emulation.
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * This code is licensed under the GPL, version 2 or later.
+ * See the file `COPYING' in the top level directory.
+ *
+ * It (partially) emulates a mcimx7d_sabre board, with a Freescale
+ * i.MX7 SoC
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "hw/arm/fsl-imx7.h"
+#include "hw/boards.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/device_tree.h"
+#include "qemu/error-report.h"
+#include "sysemu/qtest.h"
+#include "net/net.h"
+
+typedef struct {
+    FslIMX7State soc;
+    MemoryRegion ram;
+} MCIMX7Sabre;
+
+static void mcimx7d_sabre_init(MachineState *machine)
+{
+    static struct arm_boot_info boot_info;
+    MCIMX7Sabre *s = g_new0(MCIMX7Sabre, 1);
+    Object *soc;
+    int i;
+
+    if (machine->ram_size > FSL_IMX7_MMDC_SIZE) {
+        error_report("RAM size " RAM_ADDR_FMT " above max supported (%08x)",
+                     machine->ram_size, FSL_IMX7_MMDC_SIZE);
+        exit(1);
+    }
+
+    boot_info = (struct arm_boot_info) {
+        .loader_start = FSL_IMX7_MMDC_ADDR,
+        .board_id = -1,
+        .ram_size = machine->ram_size,
+        .kernel_filename = machine->kernel_filename,
+        .kernel_cmdline = machine->kernel_cmdline,
+        .initrd_filename = machine->initrd_filename,
+        .nb_cpus = smp_cpus,
+    };
+
+    object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX7);
+    soc = OBJECT(&s->soc);
+    object_property_add_child(OBJECT(machine), "soc", soc, &error_fatal);
+    object_property_set_bool(soc, true, "realized", &error_fatal);
+
+    memory_region_allocate_system_memory(&s->ram, NULL, "mcimx7d-sabre.ram",
+                                         machine->ram_size);
+    memory_region_add_subregion(get_system_memory(),
+                                FSL_IMX7_MMDC_ADDR, &s->ram);
+
+    for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
+        BusState *bus;
+        DeviceState *carddev;
+        DriveInfo *di;
+        BlockBackend *blk;
+
+        di = drive_get_next(IF_SD);
+        blk = di ? blk_by_legacy_dinfo(di) : NULL;
+        bus = qdev_get_child_bus(DEVICE(&s->soc.usdhc[i]), "sd-bus");
+        carddev = qdev_create(bus, TYPE_SD_CARD);
+        qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
+        object_property_set_bool(OBJECT(carddev), true,
+                                 "realized", &error_fatal);
+    }
+
+    if (!qtest_enabled()) {
+        arm_load_kernel(&s->soc.cpu[0], &boot_info);
+    }
+}
+
+static void mcimx7d_sabre_machine_init(MachineClass *mc)
+{
+    mc->desc = "Freescale i.MX7 DUAL SABRE (Cortex A7)";
+    mc->init = mcimx7d_sabre_init;
+    mc->max_cpus = FSL_IMX7_NUM_CPUS;
+}
+DEFINE_MACHINE("mcimx7d-sabre", mcimx7d_sabre_machine_init)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index dbb3c8036a..2c07245047 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -169,6 +169,7 @@ static const char *valid_cpus[] = {
     ARM_CPU_TYPE_NAME("cortex-a53"),
     ARM_CPU_TYPE_NAME("cortex-a57"),
     ARM_CPU_TYPE_NAME("host"),
+    ARM_CPU_TYPE_NAME("max"),
 };
 
 static bool cpu_type_valid(const char *cpu)
@@ -1206,16 +1207,23 @@ static void machvirt_init(MachineState *machine)
     /* We can probe only here because during property set
      * KVM is not available yet
      */
-    if (!vms->gic_version) {
+    if (vms->gic_version <= 0) {
+        /* "host" or "max" */
         if (!kvm_enabled()) {
-            error_report("gic-version=host requires KVM");
-            exit(1);
-        }
-
-        vms->gic_version = kvm_arm_vgic_probe();
-        if (!vms->gic_version) {
-            error_report("Unable to determine GIC version supported by host");
-            exit(1);
+            if (vms->gic_version == 0) {
+                error_report("gic-version=host requires KVM");
+                exit(1);
+            } else {
+                /* "max": currently means 3 for TCG */
+                vms->gic_version = 3;
+            }
+        } else {
+            vms->gic_version = kvm_arm_vgic_probe();
+            if (!vms->gic_version) {
+                error_report(
+                    "Unable to determine GIC version supported by host");
+                exit(1);
+            }
         }
     }
 
@@ -1479,9 +1487,11 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
         vms->gic_version = 2;
     } else if (!strcmp(value, "host")) {
         vms->gic_version = 0; /* Will probe later */
+    } else if (!strcmp(value, "max")) {
+        vms->gic_version = -1; /* Will probe later */
     } else {
         error_setg(errp, "Invalid gic-version value");
-        error_append_hint(errp, "Valid values are 3, 2, host.\n");
+        error_append_hint(errp, "Valid values are 3, 2, host, max.\n");
     }
 }
 
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index 69227fd4c9..465796e97c 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -282,6 +282,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
                                  s->virt, "has_el2", NULL);
         object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
                                 "reset-cbar", &error_abort);
+        object_property_set_int(OBJECT(&s->apu_cpu[i]), num_apus,
+                                "core-count", &error_abort);
         object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
                                  &err);
         if (err) {
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 28f298b342..72181330b8 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -1483,6 +1483,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
 
     region_start = (s->start_addr * 4);
     region_end = region_start + (ram_addr_t)s->line_offset * height;
+    region_end += width * s->get_bpp(s) / 8; /* scanline length */
+    region_end -= s->line_offset;
     if (region_end > s->vbe_size) {
         /* wraps around (can happen with cirrus vbe modes) */
         region_start = 0;
diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
index 4b69f737b5..6d6597c065 100644
--- a/hw/pci-host/Makefile.objs
+++ b/hw/pci-host/Makefile.objs
@@ -17,3 +17,5 @@ common-obj-$(CONFIG_PCI_PIIX) += piix.o
 common-obj-$(CONFIG_PCI_Q35) += q35.o
 common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
 common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o
+
+common-obj-$(CONFIG_PCI_DESIGNWARE) += designware.o
diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c
new file mode 100644
index 0000000000..29ea313798
--- /dev/null
+++ b/hw/pci-host/designware.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * Designware PCIe IP block emulation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/pci-host/designware.h"
+
+#define DESIGNWARE_PCIE_PORT_LINK_CONTROL          0x710
+#define DESIGNWARE_PCIE_PHY_DEBUG_R1               0x72C
+#define DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP  BIT(4)
+#define DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL   0x80C
+#define DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE    BIT(17)
+#define DESIGNWARE_PCIE_MSI_ADDR_LO                0x820
+#define DESIGNWARE_PCIE_MSI_ADDR_HI                0x824
+#define DESIGNWARE_PCIE_MSI_INTR0_ENABLE           0x828
+#define DESIGNWARE_PCIE_MSI_INTR0_MASK             0x82C
+#define DESIGNWARE_PCIE_MSI_INTR0_STATUS           0x830
+#define DESIGNWARE_PCIE_ATU_VIEWPORT               0x900
+#define DESIGNWARE_PCIE_ATU_REGION_INBOUND         BIT(31)
+#define DESIGNWARE_PCIE_ATU_CR1                    0x904
+#define DESIGNWARE_PCIE_ATU_TYPE_MEM               (0x0 << 0)
+#define DESIGNWARE_PCIE_ATU_CR2                    0x908
+#define DESIGNWARE_PCIE_ATU_ENABLE                 BIT(31)
+#define DESIGNWARE_PCIE_ATU_LOWER_BASE             0x90C
+#define DESIGNWARE_PCIE_ATU_UPPER_BASE             0x910
+#define DESIGNWARE_PCIE_ATU_LIMIT                  0x914
+#define DESIGNWARE_PCIE_ATU_LOWER_TARGET           0x918
+#define DESIGNWARE_PCIE_ATU_BUS(x)                 (((x) >> 24) & 0xff)
+#define DESIGNWARE_PCIE_ATU_DEVFN(x)               (((x) >> 16) & 0xff)
+#define DESIGNWARE_PCIE_ATU_UPPER_TARGET           0x91C
+
+static DesignwarePCIEHost *
+designware_pcie_root_to_host(DesignwarePCIERoot *root)
+{
+    BusState *bus = qdev_get_parent_bus(DEVICE(root));
+    return DESIGNWARE_PCIE_HOST(bus->parent);
+}
+
+static void designware_pcie_root_msi_write(void *opaque, hwaddr addr,
+                                           uint64_t val, unsigned len)
+{
+    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
+    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+
+    root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable;
+
+    if (root->msi.intr[0].status & ~root->msi.intr[0].mask) {
+        qemu_set_irq(host->pci.irqs[0], 1);
+    }
+}
+
+static const MemoryRegionOps designware_pci_host_msi_ops = {
+    .write = designware_pcie_root_msi_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root)
+
+{
+    MemoryRegion *mem   = &root->msi.iomem;
+    const uint64_t base = root->msi.base;
+    const bool enable   = root->msi.intr[0].enable;
+
+    memory_region_set_address(mem, base);
+    memory_region_set_enabled(mem, enable);
+}
+
+static DesignwarePCIEViewport *
+designware_pcie_root_get_current_viewport(DesignwarePCIERoot *root)
+{
+    const unsigned int idx = root->atu_viewport & 0xF;
+    const unsigned int dir =
+        !!(root->atu_viewport & DESIGNWARE_PCIE_ATU_REGION_INBOUND);
+    return &root->viewports[dir][idx];
+}
+
+static uint32_t
+designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len)
+{
+    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
+    DesignwarePCIEViewport *viewport =
+        designware_pcie_root_get_current_viewport(root);
+
+    uint32_t val;
+
+    switch (address) {
+    case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
+        /*
+         * Linux guest uses this register only to configure number of
+         * PCIE lane (which in our case is irrelevant) and doesn't
+         * really care about the value it reads from this register
+         */
+        val = 0xDEADBEEF;
+        break;
+
+    case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
+        /*
+         * To make sure that any code in guest waiting for speed
+         * change does not time out we always report
+         * PORT_LOGIC_SPEED_CHANGE as set
+         */
+        val = DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE;
+        break;
+
+    case DESIGNWARE_PCIE_MSI_ADDR_LO:
+        val = root->msi.base;
+        break;
+
+    case DESIGNWARE_PCIE_MSI_ADDR_HI:
+        val = root->msi.base >> 32;
+        break;
+
+    case DESIGNWARE_PCIE_MSI_INTR0_ENABLE:
+        val = root->msi.intr[0].enable;
+        break;
+
+    case DESIGNWARE_PCIE_MSI_INTR0_MASK:
+        val = root->msi.intr[0].mask;
+        break;
+
+    case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
+        val = root->msi.intr[0].status;
+        break;
+
+    case DESIGNWARE_PCIE_PHY_DEBUG_R1:
+        val = DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_VIEWPORT:
+        val = root->atu_viewport;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_LOWER_BASE:
+        val = viewport->base;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_UPPER_BASE:
+        val = viewport->base >> 32;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
+        val = viewport->target;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
+        val = viewport->target >> 32;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_LIMIT:
+        val = viewport->limit;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_CR1:
+    case DESIGNWARE_PCIE_ATU_CR2:          /* FALLTHROUGH */
+        val = viewport->cr[(address - DESIGNWARE_PCIE_ATU_CR1) /
+                           sizeof(uint32_t)];
+        break;
+
+    default:
+        val = pci_default_read_config(d, address, len);
+        break;
+    }
+
+    return val;
+}
+
+static uint64_t designware_pcie_root_data_access(void *opaque, hwaddr addr,
+                                                 uint64_t *val, unsigned len)
+{
+    DesignwarePCIEViewport *viewport = opaque;
+    DesignwarePCIERoot *root = viewport->root;
+
+    const uint8_t busnum = DESIGNWARE_PCIE_ATU_BUS(viewport->target);
+    const uint8_t devfn  = DESIGNWARE_PCIE_ATU_DEVFN(viewport->target);
+    PCIBus    *pcibus    = pci_get_bus(PCI_DEVICE(root));
+    PCIDevice *pcidev    = pci_find_device(pcibus, busnum, devfn);
+
+    if (pcidev) {
+        addr &= pci_config_size(pcidev) - 1;
+
+        if (val) {
+            pci_host_config_write_common(pcidev, addr,
+                                         pci_config_size(pcidev),
+                                         *val, len);
+        } else {
+            return pci_host_config_read_common(pcidev, addr,
+                                               pci_config_size(pcidev),
+                                               len);
+        }
+    }
+
+    return UINT64_MAX;
+}
+
+static uint64_t designware_pcie_root_data_read(void *opaque, hwaddr addr,
+                                               unsigned len)
+{
+    return designware_pcie_root_data_access(opaque, addr, NULL, len);
+}
+
+static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
+                                            uint64_t val, unsigned len)
+{
+    designware_pcie_root_data_access(opaque, addr, &val, len);
+}
+
+static const MemoryRegionOps designware_pci_host_conf_ops = {
+    .read = designware_pcie_root_data_read,
+    .write = designware_pcie_root_data_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
+                                            DesignwarePCIEViewport *viewport)
+{
+    const uint64_t target = viewport->target;
+    const uint64_t base   = viewport->base;
+    const uint64_t size   = (uint64_t)viewport->limit - base + 1;
+    const bool enabled    = viewport->cr[1] & DESIGNWARE_PCIE_ATU_ENABLE;
+
+    MemoryRegion *current, *other;
+
+    if (viewport->cr[0] == DESIGNWARE_PCIE_ATU_TYPE_MEM) {
+        current = &viewport->mem;
+        other   = &viewport->cfg;
+        memory_region_set_alias_offset(current, target);
+    } else {
+        current = &viewport->cfg;
+        other   = &viewport->mem;
+    }
+
+    /*
+     * An outbound viewport can be reconfigure from being MEM to CFG,
+     * to account for that we disable the "other" memory region that
+     * becomes unused due to that fact.
+     */
+    memory_region_set_enabled(other, false);
+    if (enabled) {
+        memory_region_set_size(current, size);
+        memory_region_set_address(current, base);
+    }
+    memory_region_set_enabled(current, enabled);
+}
+
+static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address,
+                                              uint32_t val, int len)
+{
+    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
+    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+    DesignwarePCIEViewport *viewport =
+        designware_pcie_root_get_current_viewport(root);
+
+    switch (address) {
+    case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
+    case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
+    case DESIGNWARE_PCIE_PHY_DEBUG_R1:
+        /* No-op */
+        break;
+
+    case DESIGNWARE_PCIE_MSI_ADDR_LO:
+        root->msi.base &= 0xFFFFFFFF00000000ULL;
+        root->msi.base |= val;
+        break;
+
+    case DESIGNWARE_PCIE_MSI_ADDR_HI:
+        root->msi.base &= 0x00000000FFFFFFFFULL;
+        root->msi.base |= (uint64_t)val << 32;
+        break;
+
+    case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: {
+        const bool update_msi_mapping = !root->msi.intr[0].enable ^ !!val;
+
+        root->msi.intr[0].enable = val;
+
+        if (update_msi_mapping) {
+            designware_pcie_root_update_msi_mapping(root);
+        }
+        break;
+    }
+
+    case DESIGNWARE_PCIE_MSI_INTR0_MASK:
+        root->msi.intr[0].mask = val;
+        break;
+
+    case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
+        root->msi.intr[0].status ^= val;
+        if (!root->msi.intr[0].status) {
+            qemu_set_irq(host->pci.irqs[0], 0);
+        }
+        break;
+
+    case DESIGNWARE_PCIE_ATU_VIEWPORT:
+        root->atu_viewport = val;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_LOWER_BASE:
+        viewport->base &= 0xFFFFFFFF00000000ULL;
+        viewport->base |= val;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_UPPER_BASE:
+        viewport->base &= 0x00000000FFFFFFFFULL;
+        viewport->base |= (uint64_t)val << 32;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
+        viewport->target &= 0xFFFFFFFF00000000ULL;
+        viewport->target |= val;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
+        viewport->target &= 0x00000000FFFFFFFFULL;
+        viewport->target |= val;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_LIMIT:
+        viewport->limit = val;
+        break;
+
+    case DESIGNWARE_PCIE_ATU_CR1:
+        viewport->cr[0] = val;
+        break;
+    case DESIGNWARE_PCIE_ATU_CR2:
+        viewport->cr[1] = val;
+        designware_pcie_update_viewport(root, viewport);
+        break;
+
+    default:
+        pci_bridge_write_config(d, address, val, len);
+        break;
+    }
+}
+
+static char *designware_pcie_viewport_name(const char *direction,
+                                           unsigned int i,
+                                           const char *type)
+{
+    return g_strdup_printf("PCI %s Viewport %u [%s]",
+                           direction, i, type);
+}
+
+static void designware_pcie_root_realize(PCIDevice *dev, Error **errp)
+{
+    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
+    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+    MemoryRegion *address_space = &host->pci.memory;
+    PCIBridge *br = PCI_BRIDGE(dev);
+    DesignwarePCIEViewport *viewport;
+    /*
+     * Dummy values used for initial configuration of MemoryRegions
+     * that belong to a given viewport
+     */
+    const hwaddr dummy_offset = 0;
+    const uint64_t dummy_size = 4;
+    size_t i;
+
+    br->bus_name  = "dw-pcie";
+
+    pci_set_word(dev->config + PCI_COMMAND,
+                 PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+
+    pci_config_set_interrupt_pin(dev->config, 1);
+    pci_bridge_initfn(dev, TYPE_PCIE_BUS);
+
+    pcie_port_init_reg(dev);
+
+    pcie_cap_init(dev, 0x70, PCI_EXP_TYPE_ROOT_PORT,
+                  0, &error_fatal);
+
+    msi_nonbroken = true;
+    msi_init(dev, 0x50, 32, true, true, &error_fatal);
+
+    for (i = 0; i < DESIGNWARE_PCIE_NUM_VIEWPORTS; i++) {
+        MemoryRegion *source, *destination, *mem;
+        const char *direction;
+        char *name;
+
+        viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
+        viewport->inbound = true;
+        viewport->base    = 0x0000000000000000ULL;
+        viewport->target  = 0x0000000000000000ULL;
+        viewport->limit   = UINT32_MAX;
+        viewport->cr[0]   = DESIGNWARE_PCIE_ATU_TYPE_MEM;
+
+        source      = &host->pci.address_space_root;
+        destination = get_system_memory();
+        direction   = "Inbound";
+
+        /*
+         * Configure MemoryRegion implementing PCI -> CPU memory
+         * access
+         */
+        mem  = &viewport->mem;
+        name = designware_pcie_viewport_name(direction, i, "MEM");
+        memory_region_init_alias(mem, OBJECT(root), name, destination,
+                                 dummy_offset, dummy_size);
+        memory_region_add_subregion_overlap(source, dummy_offset, mem, -1);
+        memory_region_set_enabled(mem, false);
+        g_free(name);
+
+        viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_OUTBOUND][i];
+        viewport->root    = root;
+        viewport->inbound = false;
+        viewport->base    = 0x0000000000000000ULL;
+        viewport->target  = 0x0000000000000000ULL;
+        viewport->limit   = UINT32_MAX;
+        viewport->cr[0]   = DESIGNWARE_PCIE_ATU_TYPE_MEM;
+
+        destination = &host->pci.memory;
+        direction   = "Outbound";
+        source      = get_system_memory();
+
+        /*
+         * Configure MemoryRegion implementing CPU -> PCI memory
+         * access
+         */
+        mem  = &viewport->mem;
+        name = designware_pcie_viewport_name(direction, i, "MEM");
+        memory_region_init_alias(mem, OBJECT(root), name, destination,
+                                 dummy_offset, dummy_size);
+        memory_region_add_subregion(source, dummy_offset, mem);
+        memory_region_set_enabled(mem, false);
+        g_free(name);
+
+        /*
+         * Configure MemoryRegion implementing access to configuration
+         * space
+         */
+        mem  = &viewport->cfg;
+        name = designware_pcie_viewport_name(direction, i, "CFG");
+        memory_region_init_io(&viewport->cfg, OBJECT(root),
+                              &designware_pci_host_conf_ops,
+                              viewport, name, dummy_size);
+        memory_region_add_subregion(source, dummy_offset, mem);
+        memory_region_set_enabled(mem, false);
+        g_free(name);
+    }
+
+    /*
+     * If no inbound iATU windows are configured, HW defaults to
+     * letting inbound TLPs to pass in. We emulate that by exlicitly
+     * configuring first inbound window to cover all of target's
+     * address space.
+     *
+     * NOTE: This will not work correctly for the case when first
+     * configured inbound window is window 0
+     */
+    viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][0];
+    viewport->cr[1] = DESIGNWARE_PCIE_ATU_ENABLE;
+    designware_pcie_update_viewport(root, viewport);
+
+    memory_region_init_io(&root->msi.iomem, OBJECT(root),
+                          &designware_pci_host_msi_ops,
+                          root, "pcie-msi", 0x4);
+    /*
+     * We initially place MSI interrupt I/O region a adress 0 and
+     * disable it. It'll be later moved to correct offset and enabled
+     * in designware_pcie_root_update_msi_mapping() as a part of
+     * initialization done by guest OS
+     */
+    memory_region_add_subregion(address_space, dummy_offset, &root->msi.iomem);
+    memory_region_set_enabled(&root->msi.iomem, false);
+}
+
+static void designware_pcie_set_irq(void *opaque, int irq_num, int level)
+{
+    DesignwarePCIEHost *host = DESIGNWARE_PCIE_HOST(opaque);
+
+    qemu_set_irq(host->pci.irqs[irq_num], level);
+}
+
+static const char *
+designware_pcie_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus)
+{
+    return "0000:00";
+}
+
+static const VMStateDescription vmstate_designware_pcie_msi_bank = {
+    .name = "designware-pcie-msi-bank",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(enable, DesignwarePCIEMSIBank),
+        VMSTATE_UINT32(mask, DesignwarePCIEMSIBank),
+        VMSTATE_UINT32(status, DesignwarePCIEMSIBank),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_designware_pcie_msi = {
+    .name = "designware-pcie-msi",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT64(base, DesignwarePCIEMSI),
+        VMSTATE_STRUCT_ARRAY(intr,
+                             DesignwarePCIEMSI,
+                             DESIGNWARE_PCIE_NUM_MSI_BANKS,
+                             1,
+                             vmstate_designware_pcie_msi_bank,
+                             DesignwarePCIEMSIBank),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_designware_pcie_viewport = {
+    .name = "designware-pcie-viewport",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT64(base, DesignwarePCIEViewport),
+        VMSTATE_UINT64(target, DesignwarePCIEViewport),
+        VMSTATE_UINT32(limit, DesignwarePCIEViewport),
+        VMSTATE_UINT32_ARRAY(cr, DesignwarePCIEViewport, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_designware_pcie_root = {
+    .name = "designware-pcie-root",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
+        VMSTATE_UINT32(atu_viewport, DesignwarePCIERoot),
+        VMSTATE_STRUCT_2DARRAY(viewports,
+                               DesignwarePCIERoot,
+                               2,
+                               DESIGNWARE_PCIE_NUM_VIEWPORTS,
+                               1,
+                               vmstate_designware_pcie_viewport,
+                               DesignwarePCIEViewport),
+        VMSTATE_STRUCT(msi,
+                       DesignwarePCIERoot,
+                       1,
+                       vmstate_designware_pcie_msi,
+                       DesignwarePCIEMSI),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void designware_pcie_root_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+
+    k->vendor_id = PCI_VENDOR_ID_SYNOPSYS;
+    k->device_id = 0xABCD;
+    k->revision = 0;
+    k->class_id = PCI_CLASS_BRIDGE_PCI;
+    k->is_bridge = true;
+    k->exit = pci_bridge_exitfn;
+    k->realize = designware_pcie_root_realize;
+    k->config_read = designware_pcie_root_config_read;
+    k->config_write = designware_pcie_root_config_write;
+
+    dc->reset = pci_bridge_reset;
+    /*
+     * PCI-facing part of the host bridge, not usable without the
+     * host-facing part, which can't be device_add'ed, yet.
+     */
+    dc->user_creatable = false;
+    dc->vmsd = &vmstate_designware_pcie_root;
+}
+
+static uint64_t designware_pcie_host_mmio_read(void *opaque, hwaddr addr,
+                                               unsigned int size)
+{
+    PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
+    PCIDevice *device = pci_find_device(pci->bus, 0, 0);
+
+    return pci_host_config_read_common(device,
+                                       addr,
+                                       pci_config_size(device),
+                                       size);
+}
+
+static void designware_pcie_host_mmio_write(void *opaque, hwaddr addr,
+                                            uint64_t val, unsigned int size)
+{
+    PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
+    PCIDevice *device = pci_find_device(pci->bus, 0, 0);
+
+    return pci_host_config_write_common(device,
+                                        addr,
+                                        pci_config_size(device),
+                                        val, size);
+}
+
+static const MemoryRegionOps designware_pci_mmio_ops = {
+    .read       = designware_pcie_host_mmio_read,
+    .write      = designware_pcie_host_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        /*
+         * Our device would not work correctly if the guest was doing
+         * unaligned access. This might not be a limitation on the real
+         * device but in practice there is no reason for a guest to access
+         * this device unaligned.
+         */
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+};
+
+static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque,
+                                                    int devfn)
+{
+    DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(opaque);
+
+    return &s->pci.address_space;
+}
+
+static void designware_pcie_host_realize(DeviceState *dev, Error **errp)
+{
+    PCIHostState *pci = PCI_HOST_BRIDGE(dev);
+    DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    size_t i;
+
+    for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) {
+        sysbus_init_irq(sbd, &s->pci.irqs[i]);
+    }
+
+    memory_region_init_io(&s->mmio,
+                          OBJECT(s),
+                          &designware_pci_mmio_ops,
+                          s,
+                          "pcie.reg", 4 * 1024);
+    sysbus_init_mmio(sbd, &s->mmio);
+
+    memory_region_init(&s->pci.io, OBJECT(s), "pcie-pio", 16);
+    memory_region_init(&s->pci.memory, OBJECT(s),
+                       "pcie-bus-memory",
+                       UINT64_MAX);
+
+    pci->bus = pci_register_root_bus(dev, "pcie",
+                                     designware_pcie_set_irq,
+                                     pci_swizzle_map_irq_fn,
+                                     s,
+                                     &s->pci.memory,
+                                     &s->pci.io,
+                                     0, 4,
+                                     TYPE_PCIE_BUS);
+
+    memory_region_init(&s->pci.address_space_root,
+                       OBJECT(s),
+                       "pcie-bus-address-space-root",
+                       UINT64_MAX);
+    memory_region_add_subregion(&s->pci.address_space_root,
+                                0x0, &s->pci.memory);
+    address_space_init(&s->pci.address_space,
+                       &s->pci.address_space_root,
+                       "pcie-bus-address-space");
+    pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s);
+
+    qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus));
+    qdev_init_nofail(DEVICE(&s->root));
+}
+
+static const VMStateDescription vmstate_designware_pcie_host = {
+    .name = "designware-pcie-host",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(root,
+                       DesignwarePCIEHost,
+                       1,
+                       vmstate_designware_pcie_root,
+                       DesignwarePCIERoot),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void designware_pcie_host_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
+
+    hc->root_bus_path = designware_pcie_host_root_bus_path;
+    dc->realize = designware_pcie_host_realize;
+    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+    dc->fw_name = "pci";
+    dc->vmsd = &vmstate_designware_pcie_host;
+}
+
+static void designware_pcie_host_init(Object *obj)
+{
+    DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(obj);
+    DesignwarePCIERoot *root = &s->root;
+
+    object_initialize(root, sizeof(*root), TYPE_DESIGNWARE_PCIE_ROOT);
+    object_property_add_child(obj, "root", OBJECT(root), NULL);
+    qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
+    qdev_prop_set_bit(DEVICE(root), "multifunction", false);
+}
+
+static const TypeInfo designware_pcie_root_info = {
+    .name = TYPE_DESIGNWARE_PCIE_ROOT,
+    .parent = TYPE_PCI_BRIDGE,
+    .instance_size = sizeof(DesignwarePCIERoot),
+    .class_init = designware_pcie_root_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { INTERFACE_PCIE_DEVICE },
+        { }
+    },
+};
+
+static const TypeInfo designware_pcie_host_info = {
+    .name       = TYPE_DESIGNWARE_PCIE_HOST,
+    .parent     = TYPE_PCI_HOST_BRIDGE,
+    .instance_size = sizeof(DesignwarePCIEHost),
+    .instance_init = designware_pcie_host_init,
+    .class_init = designware_pcie_host_class_init,
+};
+
+static void designware_pcie_register(void)
+{
+    type_register_static(&designware_pcie_root_info);
+    type_register_static(&designware_pcie_host_info);
+}
+type_init(designware_pcie_register)
diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs
index c2b7664264..a99d9fbb04 100644
--- a/hw/sd/Makefile.objs
+++ b/hw/sd/Makefile.objs
@@ -1,6 +1,6 @@
 common-obj-$(CONFIG_PL181) += pl181.o
 common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
-common-obj-$(CONFIG_SD) += sd.o core.o
+common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o
 common-obj-$(CONFIG_SDHCI) += sdhci.o
 
 obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 933890e86f..235e0518d6 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -120,6 +120,7 @@ struct SDState {
     qemu_irq readonly_cb;
     qemu_irq inserted_cb;
     QEMUTimer *ocr_power_timer;
+    const char *proto_name;
     bool enable;
     uint8_t dat_lines;
     bool cmd_line;
@@ -866,13 +867,19 @@ static void sd_lock_command(SDState *sd)
         sd->card_status &= ~CARD_IS_LOCKED;
 }
 
-static sd_rsp_type_t sd_normal_command(SDState *sd,
-                                       SDRequest req)
+static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
 {
     uint32_t rca = 0x0000;
     uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
 
-    trace_sdcard_normal_command(req.cmd, req.arg, sd_state_name(sd->state));
+    /* CMD55 precedes an ACMD, so we are not interested in tracing it.
+     * However there is no ACMD55, so we want to trace this particular case.
+     */
+    if (req.cmd != 55 || sd->expecting_acmd) {
+        trace_sdcard_normal_command(sd->proto_name,
+                                    sd_cmd_name(req.cmd), req.cmd,
+                                    req.arg, sd_state_name(sd->state));
+    }
 
     /* Not interpreting this as an app command */
     sd->card_status &= ~APP_CMD;
@@ -1162,6 +1169,14 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
         }
         break;
 
+    case 19:    /* CMD19: SEND_TUNING_BLOCK (SD) */
+        if (sd->state == sd_transfer_state) {
+            sd->state = sd_sendingdata_state;
+            sd->data_offset = 0;
+            return sd_r1;
+        }
+        break;
+
     case 23:    /* CMD23: SET_BLOCK_COUNT */
         switch (sd->state) {
         case sd_transfer_state:
@@ -1450,7 +1465,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
 static sd_rsp_type_t sd_app_command(SDState *sd,
                                     SDRequest req)
 {
-    trace_sdcard_app_command(req.cmd, req.arg);
+    trace_sdcard_app_command(sd->proto_name, sd_acmd_name(req.cmd),
+                             req.cmd, req.arg, sd_state_name(sd->state));
     sd->card_status |= APP_CMD;
     switch (req.cmd) {
     case 6:	/* ACMD6:  SET_BUS_WIDTH */
@@ -1765,7 +1781,9 @@ void sd_write_data(SDState *sd, uint8_t value)
     if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
         return;
 
-    trace_sdcard_write_data(sd->current_cmd, value);
+    trace_sdcard_write_data(sd->proto_name,
+                            sd_acmd_name(sd->current_cmd),
+                            sd->current_cmd, value);
     switch (sd->current_cmd) {
     case 24:	/* CMD24:  WRITE_SINGLE_BLOCK */
         sd->data[sd->data_offset ++] = value;
@@ -1883,6 +1901,20 @@ void sd_write_data(SDState *sd, uint8_t value)
     }
 }
 
+#define SD_TUNING_BLOCK_SIZE    64
+
+static const uint8_t sd_tuning_block_pattern[SD_TUNING_BLOCK_SIZE] = {
+    /* See: Physical Layer Simplified Specification Version 3.01, Table 4-2 */
+    0xff, 0x0f, 0xff, 0x00,         0x0f, 0xfc, 0xc3, 0xcc,
+    0xc3, 0x3c, 0xcc, 0xff,         0xfe, 0xff, 0xfe, 0xef,
+    0xff, 0xdf, 0xff, 0xdd,         0xff, 0xfb, 0xff, 0xfb,
+    0xbf, 0xff, 0x7f, 0xff,         0x77, 0xf7, 0xbd, 0xef,
+    0xff, 0xf0, 0xff, 0xf0,         0x0f, 0xfc, 0xcc, 0x3c,
+    0xcc, 0x33, 0xcc, 0xcf,         0xff, 0xef, 0xff, 0xee,
+    0xff, 0xfd, 0xff, 0xfd,         0xdf, 0xff, 0xbf, 0xff,
+    0xbb, 0xff, 0xf7, 0xff,         0xf7, 0x7f, 0x7b, 0xde,
+};
+
 uint8_t sd_read_data(SDState *sd)
 {
     /* TODO: Append CRCs */
@@ -1903,7 +1935,9 @@ uint8_t sd_read_data(SDState *sd)
 
     io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
 
-    trace_sdcard_read_data(sd->current_cmd, io_len);
+    trace_sdcard_read_data(sd->proto_name,
+                           sd_acmd_name(sd->current_cmd),
+                           sd->current_cmd, io_len);
     switch (sd->current_cmd) {
     case 6:	/* CMD6:   SWITCH_FUNCTION */
         ret = sd->data[sd->data_offset ++];
@@ -1960,6 +1994,13 @@ uint8_t sd_read_data(SDState *sd)
         }
         break;
 
+    case 19:    /* CMD19:  SEND_TUNING_BLOCK (SD) */
+        if (sd->data_offset >= SD_TUNING_BLOCK_SIZE - 1) {
+            sd->state = sd_transfer_state;
+        }
+        ret = sd_tuning_block_pattern[sd->data_offset++];
+        break;
+
     case 22:	/* ACMD22: SEND_NUM_WR_BLOCKS */
         ret = sd->data[sd->data_offset ++];
 
@@ -2029,6 +2070,8 @@ static void sd_realize(DeviceState *dev, Error **errp)
     SDState *sd = SD_CARD(dev);
     int ret;
 
+    sd->proto_name = sd->spi ? "SPI" : "SD";
+
     if (sd->blk && blk_is_read_only(sd->blk)) {
         error_setg(errp, "Cannot use read-only drive as SD card");
         return;
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 97b4a473c8..1b828b104d 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -433,13 +433,13 @@ static void sdhci_read_block_from_card(SDHCIState *s)
     for (index = 0; index < blk_size; index++) {
         data = sdbus_read_data(&s->sdbus);
         if (!FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
-            /* Device is not in tunning */
+            /* Device is not in tuning */
             s->fifo_buffer[index] = data;
         }
     }
 
     if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
-        /* Device is in tunning */
+        /* Device is in tuning */
         s->hostctl2 &= ~R_SDHC_HOSTCTL2_EXECUTE_TUNING_MASK;
         s->hostctl2 |= R_SDHC_HOSTCTL2_SAMPLING_CLKSEL_MASK;
         s->prnsts &= ~(SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ |
diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c
new file mode 100644
index 0000000000..2053def3f1
--- /dev/null
+++ b/hw/sd/sdmmc-internal.c
@@ -0,0 +1,72 @@
+/*
+ * SD/MMC cards common helpers
+ *
+ * Copyright (c) 2018  Philippe Mathieu-Daudé <f4bug@amsat.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "sdmmc-internal.h"
+
+const char *sd_cmd_name(uint8_t cmd)
+{
+    static const char *cmd_abbrev[SDMMC_CMD_MAX] = {
+         [0]    = "GO_IDLE_STATE",
+         [2]    = "ALL_SEND_CID",            [3]    = "SEND_RELATIVE_ADDR",
+         [4]    = "SET_DSR",                 [5]    = "IO_SEND_OP_COND",
+         [6]    = "SWITCH_FUNC",             [7]    = "SELECT/DESELECT_CARD",
+         [8]    = "SEND_IF_COND",            [9]    = "SEND_CSD",
+        [10]    = "SEND_CID",               [11]    = "VOLTAGE_SWITCH",
+        [12]    = "STOP_TRANSMISSION",      [13]    = "SEND_STATUS",
+                                            [15]    = "GO_INACTIVE_STATE",
+        [16]    = "SET_BLOCKLEN",           [17]    = "READ_SINGLE_BLOCK",
+        [18]    = "READ_MULTIPLE_BLOCK",    [19]    = "SEND_TUNING_BLOCK",
+        [20]    = "SPEED_CLASS_CONTROL",    [21]    = "DPS_spec",
+                                            [23]    = "SET_BLOCK_COUNT",
+        [24]    = "WRITE_BLOCK",            [25]    = "WRITE_MULTIPLE_BLOCK",
+        [26]    = "MANUF_RSVD",             [27]    = "PROGRAM_CSD",
+        [28]    = "SET_WRITE_PROT",         [29]    = "CLR_WRITE_PROT",
+        [30]    = "SEND_WRITE_PROT",
+        [32]    = "ERASE_WR_BLK_START",     [33]    = "ERASE_WR_BLK_END",
+        [34]    = "SW_FUNC_RSVD",           [35]    = "SW_FUNC_RSVD",
+        [36]    = "SW_FUNC_RSVD",           [37]    = "SW_FUNC_RSVD",
+        [38]    = "ERASE",
+        [40]    = "DPS_spec",
+        [42]    = "LOCK_UNLOCK",            [43]    = "Q_MANAGEMENT",
+        [44]    = "Q_TASK_INFO_A",          [45]    = "Q_TASK_INFO_B",
+        [46]    = "Q_RD_TASK",              [47]    = "Q_WR_TASK",
+        [48]    = "READ_EXTR_SINGLE",       [49]    = "WRITE_EXTR_SINGLE",
+        [50]    = "SW_FUNC_RSVD",
+        [52]    = "IO_RW_DIRECT",           [53]    = "IO_RW_EXTENDED",
+        [54]    = "SDIO_RSVD",              [55]    = "APP_CMD",
+        [56]    = "GEN_CMD",                [57]    = "SW_FUNC_RSVD",
+        [58]    = "READ_EXTR_MULTI",        [59]    = "WRITE_EXTR_MULTI",
+        [60]    = "MANUF_RSVD",             [61]    = "MANUF_RSVD",
+        [62]    = "MANUF_RSVD",             [63]    = "MANUF_RSVD",
+    };
+    return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD";
+}
+
+const char *sd_acmd_name(uint8_t cmd)
+{
+    static const char *acmd_abbrev[SDMMC_CMD_MAX] = {
+         [6] = "SET_BUS_WIDTH",
+        [13] = "SD_STATUS",
+        [14] = "DPS_spec",                  [15] = "DPS_spec",
+        [16] = "DPS_spec",
+        [18] = "SECU_spec",
+        [22] = "SEND_NUM_WR_BLOCKS",        [23] = "SET_WR_BLK_ERASE_COUNT",
+        [41] = "SD_SEND_OP_COND",
+        [42] = "SET_CLR_CARD_DETECT",
+        [51] = "SEND_SCR",
+        [52] = "SECU_spec",                 [53] = "SECU_spec",
+        [54] = "SECU_spec",
+        [56] = "SECU_spec",                 [57] = "SECU_spec",
+        [58] = "SECU_spec",                 [59] = "SECU_spec",
+    };
+
+    return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD";
+}
diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h
index 0e96cb0081..9aa04766fc 100644
--- a/hw/sd/sdmmc-internal.h
+++ b/hw/sd/sdmmc-internal.h
@@ -12,4 +12,28 @@
 
 #define SDMMC_CMD_MAX 64
 
+/**
+ * sd_cmd_name:
+ * @cmd: A SD "normal" command, up to SDMMC_CMD_MAX.
+ *
+ * Returns a human-readable name describing the command.
+ * The return value is always a static string which does not need
+ * to be freed after use.
+ *
+ * Returns: The command name of @cmd or "UNKNOWN_CMD".
+ */
+const char *sd_cmd_name(uint8_t cmd);
+
+/**
+ * sd_acmd_name:
+ * @cmd: A SD "Application-Specific" command, up to SDMMC_CMD_MAX.
+ *
+ * Returns a human-readable name describing the application command.
+ * The return value is always a static string which does not need
+ * to be freed after use.
+ *
+ * Returns: The application command name of @cmd or "UNKNOWN_ACMD".
+ */
+const char *sd_acmd_name(uint8_t cmd);
+
 #endif
diff --git a/hw/sd/trace-events b/hw/sd/trace-events
index 3040d32560..2059ace61f 100644
--- a/hw/sd/trace-events
+++ b/hw/sd/trace-events
@@ -24,8 +24,8 @@ sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of
 sdhci_capareg(const char *desc, uint16_t val) "%s: %u"
 
 # hw/sd/sd.c
-sdcard_normal_command(uint8_t cmd, uint32_t arg, const char *state) "CMD%d arg 0x%08x (state %s)"
-sdcard_app_command(uint8_t acmd, uint32_t arg) "ACMD%d arg 0x%08x"
+sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *state) "%s %20s/ CMD%02d arg 0x%08x (state %s)"
+sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *state) "%s %23s/ACMD%02d arg 0x%08x (state %s)"
 sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)"
 sdcard_powerup(void) ""
 sdcard_inquiry_cmd41(void) ""
@@ -39,8 +39,8 @@ sdcard_lock(void) ""
 sdcard_unlock(void) ""
 sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
 sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
-sdcard_write_data(uint8_t cmd, uint8_t value) "CMD%02d value 0x%02x"
-sdcard_read_data(uint8_t cmd, int length) "CMD%02d len %d"
+sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x"
+sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, int length) "%s %20s/ CMD%02d len %d"
 sdcard_set_voltage(uint16_t millivolts) "%u mV"
 
 # hw/sd/milkymist-memcard.c
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index ec174309db..65a9196c1a 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -106,10 +106,10 @@ struct USBRedirDevice {
     USBDevice dev;
     /* Properties */
     CharBackend cs;
+    bool enable_streams;
     uint8_t debug;
-    char *filter_str;
     int32_t bootindex;
-    bool enable_streams;
+    char *filter_str;
     /* Data passed from chardev the fd_read cb to the usbredirparser read cb */
     const uint8_t *read_buf;
     int read_buf_size;
diff --git a/include/block/block.h b/include/block/block.h
index 8b6db952a2..cdec3639a3 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -226,6 +226,7 @@ char *bdrv_perm_names(uint64_t perm);
 void bdrv_init(void);
 void bdrv_init_with_whitelist(void);
 bool bdrv_uses_whitelist(void);
+int bdrv_is_whitelisted(BlockDriver *drv, bool read_only);
 BlockDriver *bdrv_find_protocol(const char *filename,
                                 bool allow_protocol_prefix,
                                 Error **errp);
@@ -246,6 +247,7 @@ BdrvChild *bdrv_open_child(const char *filename,
                            BlockDriverState* parent,
                            const BdrvChildRole *child_role,
                            bool allow_none, Error **errp);
+BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp);
 void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
                          Error **errp);
 int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 64a5700f2b..27e17addba 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -129,8 +129,11 @@ struct BlockDriver {
     int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags,
                           Error **errp);
     void (*bdrv_close)(BlockDriverState *bs);
-    int coroutine_fn (*bdrv_co_create_opts)(const char *filename, QemuOpts *opts,
+    int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts,
                                        Error **errp);
+    int coroutine_fn (*bdrv_co_create_opts)(const char *filename,
+                                            QemuOpts *opts,
+                                            Error **errp);
     int (*bdrv_make_empty)(BlockDriverState *bs);
 
     void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options);
@@ -224,7 +227,8 @@ struct BlockDriver {
     /*
      * Invalidate any cached meta-data.
      */
-    void (*bdrv_invalidate_cache)(BlockDriverState *bs, Error **errp);
+    void coroutine_fn (*bdrv_co_invalidate_cache)(BlockDriverState *bs,
+                                                  Error **errp);
     int (*bdrv_inactivate)(BlockDriverState *bs);
 
     /*
@@ -306,8 +310,9 @@ struct BlockDriver {
      * Returns 0 for completed check, -errno for internal errors.
      * The check results are stored in result.
      */
-    int (*bdrv_check)(BlockDriverState *bs, BdrvCheckResult *result,
-        BdrvCheckMode fix);
+    int coroutine_fn (*bdrv_co_check)(BlockDriverState *bs,
+                                      BdrvCheckResult *result,
+                                      BdrvCheckMode fix);
 
     int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
                               BlockDriverAmendStatusCB *status_cb,
diff --git a/include/hw/arm/fsl-imx7.h b/include/hw/arm/fsl-imx7.h
new file mode 100644
index 0000000000..d848262bfd
--- /dev/null
+++ b/include/hw/arm/fsl-imx7.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * i.MX7 SoC definitions
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef FSL_IMX7_H
+#define FSL_IMX7_H
+
+#include "hw/arm/arm.h"
+#include "hw/cpu/a15mpcore.h"
+#include "hw/intc/imx_gpcv2.h"
+#include "hw/misc/imx7_ccm.h"
+#include "hw/misc/imx7_snvs.h"
+#include "hw/misc/imx7_gpr.h"
+#include "hw/misc/imx6_src.h"
+#include "hw/misc/imx2_wdt.h"
+#include "hw/gpio/imx_gpio.h"
+#include "hw/char/imx_serial.h"
+#include "hw/timer/imx_gpt.h"
+#include "hw/timer/imx_epit.h"
+#include "hw/i2c/imx_i2c.h"
+#include "hw/gpio/imx_gpio.h"
+#include "hw/sd/sdhci.h"
+#include "hw/ssi/imx_spi.h"
+#include "hw/net/imx_fec.h"
+#include "hw/pci-host/designware.h"
+#include "hw/usb/chipidea.h"
+#include "exec/memory.h"
+#include "cpu.h"
+
+#define TYPE_FSL_IMX7 "fsl,imx7"
+#define FSL_IMX7(obj) OBJECT_CHECK(FslIMX7State, (obj), TYPE_FSL_IMX7)
+
+enum FslIMX7Configuration {
+    FSL_IMX7_NUM_CPUS         = 2,
+    FSL_IMX7_NUM_UARTS        = 7,
+    FSL_IMX7_NUM_ETHS         = 2,
+    FSL_IMX7_ETH_NUM_TX_RINGS = 3,
+    FSL_IMX7_NUM_USDHCS       = 3,
+    FSL_IMX7_NUM_WDTS         = 4,
+    FSL_IMX7_NUM_GPTS         = 4,
+    FSL_IMX7_NUM_IOMUXCS      = 2,
+    FSL_IMX7_NUM_GPIOS        = 7,
+    FSL_IMX7_NUM_I2CS         = 4,
+    FSL_IMX7_NUM_ECSPIS       = 4,
+    FSL_IMX7_NUM_USBS         = 3,
+    FSL_IMX7_NUM_ADCS         = 2,
+};
+
+typedef struct FslIMX7State {
+    /*< private >*/
+    DeviceState    parent_obj;
+
+    /*< public >*/
+    ARMCPU             cpu[FSL_IMX7_NUM_CPUS];
+    A15MPPrivState     a7mpcore;
+    IMXGPTState        gpt[FSL_IMX7_NUM_GPTS];
+    IMXGPIOState       gpio[FSL_IMX7_NUM_GPIOS];
+    IMX7CCMState       ccm;
+    IMX7AnalogState    analog;
+    IMX7SNVSState      snvs;
+    IMXGPCv2State      gpcv2;
+    IMXSPIState        spi[FSL_IMX7_NUM_ECSPIS];
+    IMXI2CState        i2c[FSL_IMX7_NUM_I2CS];
+    IMXSerialState     uart[FSL_IMX7_NUM_UARTS];
+    IMXFECState        eth[FSL_IMX7_NUM_ETHS];
+    SDHCIState         usdhc[FSL_IMX7_NUM_USDHCS];
+    IMX2WdtState       wdt[FSL_IMX7_NUM_WDTS];
+    IMX7GPRState       gpr;
+    ChipideaState      usb[FSL_IMX7_NUM_USBS];
+    DesignwarePCIEHost pcie;
+} FslIMX7State;
+
+enum FslIMX7MemoryMap {
+    FSL_IMX7_MMDC_ADDR            = 0x80000000,
+    FSL_IMX7_MMDC_SIZE            = 2 * 1024 * 1024 * 1024UL,
+
+    FSL_IMX7_GPIO1_ADDR           = 0x30200000,
+    FSL_IMX7_GPIO2_ADDR           = 0x30210000,
+    FSL_IMX7_GPIO3_ADDR           = 0x30220000,
+    FSL_IMX7_GPIO4_ADDR           = 0x30230000,
+    FSL_IMX7_GPIO5_ADDR           = 0x30240000,
+    FSL_IMX7_GPIO6_ADDR           = 0x30250000,
+    FSL_IMX7_GPIO7_ADDR           = 0x30260000,
+
+    FSL_IMX7_IOMUXC_LPSR_GPR_ADDR = 0x30270000,
+
+    FSL_IMX7_WDOG1_ADDR           = 0x30280000,
+    FSL_IMX7_WDOG2_ADDR           = 0x30290000,
+    FSL_IMX7_WDOG3_ADDR           = 0x302A0000,
+    FSL_IMX7_WDOG4_ADDR           = 0x302B0000,
+
+    FSL_IMX7_IOMUXC_LPSR_ADDR     = 0x302C0000,
+
+    FSL_IMX7_GPT1_ADDR            = 0x302D0000,
+    FSL_IMX7_GPT2_ADDR            = 0x302E0000,
+    FSL_IMX7_GPT3_ADDR            = 0x302F0000,
+    FSL_IMX7_GPT4_ADDR            = 0x30300000,
+
+    FSL_IMX7_IOMUXC_ADDR          = 0x30330000,
+    FSL_IMX7_IOMUXC_GPR_ADDR      = 0x30340000,
+    FSL_IMX7_IOMUXCn_SIZE         = 0x1000,
+
+    FSL_IMX7_ANALOG_ADDR          = 0x30360000,
+    FSL_IMX7_SNVS_ADDR            = 0x30370000,
+    FSL_IMX7_CCM_ADDR             = 0x30380000,
+
+    FSL_IMX7_SRC_ADDR             = 0x30390000,
+    FSL_IMX7_SRC_SIZE             = 0x1000,
+
+    FSL_IMX7_ADC1_ADDR            = 0x30610000,
+    FSL_IMX7_ADC2_ADDR            = 0x30620000,
+    FSL_IMX7_ADCn_SIZE            = 0x1000,
+
+    FSL_IMX7_GPC_ADDR             = 0x303A0000,
+
+    FSL_IMX7_I2C1_ADDR            = 0x30A20000,
+    FSL_IMX7_I2C2_ADDR            = 0x30A30000,
+    FSL_IMX7_I2C3_ADDR            = 0x30A40000,
+    FSL_IMX7_I2C4_ADDR            = 0x30A50000,
+
+    FSL_IMX7_ECSPI1_ADDR          = 0x30820000,
+    FSL_IMX7_ECSPI2_ADDR          = 0x30830000,
+    FSL_IMX7_ECSPI3_ADDR          = 0x30840000,
+    FSL_IMX7_ECSPI4_ADDR          = 0x30630000,
+
+    FSL_IMX7_LCDIF_ADDR           = 0x30730000,
+    FSL_IMX7_LCDIF_SIZE           = 0x1000,
+
+    FSL_IMX7_UART1_ADDR           = 0x30860000,
+    /*
+     * Some versions of the reference manual claim that UART2 is @
+     * 0x30870000, but experiments with HW + DT files in upstream
+     * Linux kernel show that not to be true and that block is
+     * acutally located @ 0x30890000
+     */
+    FSL_IMX7_UART2_ADDR           = 0x30890000,
+    FSL_IMX7_UART3_ADDR           = 0x30880000,
+    FSL_IMX7_UART4_ADDR           = 0x30A60000,
+    FSL_IMX7_UART5_ADDR           = 0x30A70000,
+    FSL_IMX7_UART6_ADDR           = 0x30A80000,
+    FSL_IMX7_UART7_ADDR           = 0x30A90000,
+
+    FSL_IMX7_ENET1_ADDR           = 0x30BE0000,
+    FSL_IMX7_ENET2_ADDR           = 0x30BF0000,
+
+    FSL_IMX7_USB1_ADDR            = 0x30B10000,
+    FSL_IMX7_USBMISC1_ADDR        = 0x30B10200,
+    FSL_IMX7_USB2_ADDR            = 0x30B20000,
+    FSL_IMX7_USBMISC2_ADDR        = 0x30B20200,
+    FSL_IMX7_USB3_ADDR            = 0x30B30000,
+    FSL_IMX7_USBMISC3_ADDR        = 0x30B30200,
+    FSL_IMX7_USBMISCn_SIZE        = 0x200,
+
+    FSL_IMX7_USDHC1_ADDR          = 0x30B40000,
+    FSL_IMX7_USDHC2_ADDR          = 0x30B50000,
+    FSL_IMX7_USDHC3_ADDR          = 0x30B60000,
+
+    FSL_IMX7_SDMA_ADDR            = 0x30BD0000,
+    FSL_IMX7_SDMA_SIZE            = 0x1000,
+
+    FSL_IMX7_A7MPCORE_ADDR        = 0x31000000,
+    FSL_IMX7_A7MPCORE_DAP_ADDR    = 0x30000000,
+
+    FSL_IMX7_PCIE_REG_ADDR        = 0x33800000,
+    FSL_IMX7_PCIE_REG_SIZE        = 16 * 1024,
+
+    FSL_IMX7_GPR_ADDR             = 0x30340000,
+};
+
+enum FslIMX7IRQs {
+    FSL_IMX7_USDHC1_IRQ   = 22,
+    FSL_IMX7_USDHC2_IRQ   = 23,
+    FSL_IMX7_USDHC3_IRQ   = 24,
+
+    FSL_IMX7_UART1_IRQ    = 26,
+    FSL_IMX7_UART2_IRQ    = 27,
+    FSL_IMX7_UART3_IRQ    = 28,
+    FSL_IMX7_UART4_IRQ    = 29,
+    FSL_IMX7_UART5_IRQ    = 30,
+    FSL_IMX7_UART6_IRQ    = 16,
+
+    FSL_IMX7_ECSPI1_IRQ   = 31,
+    FSL_IMX7_ECSPI2_IRQ   = 32,
+    FSL_IMX7_ECSPI3_IRQ   = 33,
+    FSL_IMX7_ECSPI4_IRQ   = 34,
+
+    FSL_IMX7_I2C1_IRQ     = 35,
+    FSL_IMX7_I2C2_IRQ     = 36,
+    FSL_IMX7_I2C3_IRQ     = 37,
+    FSL_IMX7_I2C4_IRQ     = 38,
+
+    FSL_IMX7_USB1_IRQ     = 43,
+    FSL_IMX7_USB2_IRQ     = 42,
+    FSL_IMX7_USB3_IRQ     = 40,
+
+    FSL_IMX7_PCI_INTA_IRQ = 122,
+    FSL_IMX7_PCI_INTB_IRQ = 123,
+    FSL_IMX7_PCI_INTC_IRQ = 124,
+    FSL_IMX7_PCI_INTD_IRQ = 125,
+
+    FSL_IMX7_UART7_IRQ    = 126,
+
+#define FSL_IMX7_ENET_IRQ(i, n)  ((n) + ((i) ? 100 : 118))
+
+    FSL_IMX7_MAX_IRQ      = 128,
+};
+
+#endif /* FSL_IMX7_H */
diff --git a/include/hw/pci-host/designware.h b/include/hw/pci-host/designware.h
new file mode 100644
index 0000000000..a4f2c0695b
--- /dev/null
+++ b/include/hw/pci-host/designware.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * Designware PCIe IP block emulation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DESIGNWARE_H
+#define DESIGNWARE_H
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pcie_host.h"
+#include "hw/pci/pci_bridge.h"
+
+#define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host"
+#define DESIGNWARE_PCIE_HOST(obj) \
+     OBJECT_CHECK(DesignwarePCIEHost, (obj), TYPE_DESIGNWARE_PCIE_HOST)
+
+#define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root"
+#define DESIGNWARE_PCIE_ROOT(obj) \
+     OBJECT_CHECK(DesignwarePCIERoot, (obj), TYPE_DESIGNWARE_PCIE_ROOT)
+
+struct DesignwarePCIERoot;
+typedef struct DesignwarePCIERoot DesignwarePCIERoot;
+
+typedef struct DesignwarePCIEViewport {
+    DesignwarePCIERoot *root;
+
+    MemoryRegion cfg;
+    MemoryRegion mem;
+
+    uint64_t base;
+    uint64_t target;
+    uint32_t limit;
+    uint32_t cr[2];
+
+    bool inbound;
+} DesignwarePCIEViewport;
+
+typedef struct DesignwarePCIEMSIBank {
+    uint32_t enable;
+    uint32_t mask;
+    uint32_t status;
+} DesignwarePCIEMSIBank;
+
+typedef struct DesignwarePCIEMSI {
+    uint64_t     base;
+    MemoryRegion iomem;
+
+#define DESIGNWARE_PCIE_NUM_MSI_BANKS        1
+
+    DesignwarePCIEMSIBank intr[DESIGNWARE_PCIE_NUM_MSI_BANKS];
+} DesignwarePCIEMSI;
+
+struct DesignwarePCIERoot {
+    PCIBridge parent_obj;
+
+    uint32_t atu_viewport;
+
+#define DESIGNWARE_PCIE_VIEWPORT_OUTBOUND    0
+#define DESIGNWARE_PCIE_VIEWPORT_INBOUND     1
+#define DESIGNWARE_PCIE_NUM_VIEWPORTS        4
+
+    DesignwarePCIEViewport viewports[2][DESIGNWARE_PCIE_NUM_VIEWPORTS];
+    DesignwarePCIEMSI msi;
+};
+
+typedef struct DesignwarePCIEHost {
+    PCIHostState parent_obj;
+
+    DesignwarePCIERoot root;
+
+    struct {
+        AddressSpace address_space;
+        MemoryRegion address_space_root;
+
+        MemoryRegion memory;
+        MemoryRegion io;
+
+        qemu_irq     irqs[4];
+    } pci;
+
+    MemoryRegion mmio;
+} DesignwarePCIEHost;
+
+#endif  /* DESIGNWARE_H */
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 1dbf53627c..63acc722a9 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -269,4 +269,6 @@
 #define PCI_VENDOR_ID_VMWARE             0x15ad
 #define PCI_DEVICE_ID_VMWARE_PVRDMA      0x0820
 
+#define PCI_VENDOR_ID_SYNOPSYS           0x16C3
+
 #endif
diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
index ff6f7842c3..7c6d844549 100644
--- a/include/qapi/qmp/qdict.h
+++ b/include/qapi/qmp/qdict.h
@@ -81,4 +81,10 @@ QObject *qdict_crumple(const QDict *src, Error **errp);
 
 void qdict_join(QDict *dest, QDict *src, bool overwrite);
 
+typedef struct QDictRenames {
+    const char *from;
+    const char *to;
+} QDictRenames;
+bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp);
+
 #endif /* QDICT_H */
diff --git a/include/qemu/log-for-trace.h b/include/qemu/log-for-trace.h
new file mode 100644
index 0000000000..2f0a5b080e
--- /dev/null
+++ b/include/qemu/log-for-trace.h
@@ -0,0 +1,35 @@
+/* log-for-trace.h: logging basics required by the trace.h generated
+ * by the log trace backend.
+ *
+ * This should not be included directly by any .c file: if you
+ * need to use the logging functions include "qemu/log.h".
+ *
+ * The purpose of splitting these parts out into their own header
+ * is to catch the easy mistake where a .c file includes trace.h
+ * but forgets to include qemu/log.h. Without this split, that
+ * would result in the .c file compiling fine when the default
+ * trace backend is in use but failing to compile with any other
+ * backend.
+ *
+ * This code is licensed under the GNU General Public License,
+ * version 2 or (at your option) any later version.
+ */
+
+#ifndef QEMU_LOG_FOR_TRACE_H
+#define QEMU_LOG_FOR_TRACE_H
+
+/* Private global variable, don't use */
+extern int qemu_loglevel;
+
+#define LOG_TRACE          (1 << 15)
+
+/* Returns true if a bit is set in the current loglevel mask */
+static inline bool qemu_loglevel_mask(int mask)
+{
+    return (qemu_loglevel & mask) != 0;
+}
+
+/* main logging function */
+int GCC_FMT_ATTR(1, 2) qemu_log(const char *fmt, ...);
+
+#endif
diff --git a/include/qemu/log.h b/include/qemu/log.h
index a50e994c21..ff92a8b86a 100644
--- a/include/qemu/log.h
+++ b/include/qemu/log.h
@@ -1,10 +1,11 @@
 #ifndef QEMU_LOG_H
 #define QEMU_LOG_H
 
+/* A small part of this API is split into its own header */
+#include "qemu/log-for-trace.h"
 
-/* Private global variables, don't use */
+/* Private global variable, don't use */
 extern FILE *qemu_logfile;
-extern int qemu_loglevel;
 
 /* 
  * The new API:
@@ -41,16 +42,9 @@ static inline bool qemu_log_separate(void)
 #define CPU_LOG_MMU        (1 << 12)
 #define CPU_LOG_TB_NOCHAIN (1 << 13)
 #define CPU_LOG_PAGE       (1 << 14)
-#define LOG_TRACE          (1 << 15)
+/* LOG_TRACE (1 << 15) is defined in log-for-trace.h */
 #define CPU_LOG_TB_OP_IND  (1 << 16)
 
-/* Returns true if a bit is set in the current loglevel mask
- */
-static inline bool qemu_loglevel_mask(int mask)
-{
-    return (qemu_loglevel & mask) != 0;
-}
-
 /* Lock output for a series of related logs.  Since this is not needed
  * for a single qemu_log / qemu_log_mask / qemu_log_mask_and_addr, we
  * assume that qemu_loglevel_mask has already been tested, and that
@@ -69,10 +63,6 @@ static inline void qemu_log_unlock(void)
 
 /* Logging functions: */
 
-/* main logging function
- */
-int GCC_FMT_ATTR(1, 2) qemu_log(const char *fmt, ...);
-
 /* vfprintf-like logging function
  */
 static inline void GCC_FMT_ATTR(1, 0)
diff --git a/include/qemu/module.h b/include/qemu/module.h
index 9fea75aaeb..54300ab6e5 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -54,6 +54,7 @@ typedef enum {
 
 #define block_module_load_one(lib) module_load_one("block-", lib)
 #define ui_module_load_one(lib) module_load_one("ui-", lib)
+#define audio_module_load_one(lib) module_load_one("audio-", lib)
 
 void register_module_init(void (*fn)(void), module_init_type type);
 void register_dso_module_init(void (*fn)(void), module_init_type type);
diff --git a/include/qemu/option.h b/include/qemu/option.h
index b127fb6db6..306fdb5f7a 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -124,6 +124,8 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
                             int permit_abbrev);
 QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
                                Error **errp);
+QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict,
+                                   QemuOptsList *list, bool del);
 QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict);
 void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp);
 
diff --git a/include/ui/console.h b/include/ui/console.h
index aae9e44cb3..5fca9afcbc 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -260,6 +260,8 @@ DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height,
                                                     pixman_format_code_t format,
                                                     int linesize,
                                                     uint64_t addr);
+DisplaySurface *qemu_create_message_surface(int w, int h,
+                                            const char *msg);
 PixelFormat qemu_default_pixelformat(int bpp);
 
 DisplaySurface *qemu_create_displaysurface(int width, int height);
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 849c896eef..2922fc64b2 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -54,6 +54,9 @@ typedef struct VirtualGfxConsole {
     int x, y, w, h;
     egl_fb guest_fb;
     egl_fb win_fb;
+    egl_fb cursor_fb;
+    int cursor_x;
+    int cursor_y;
     bool y0_top;
     bool scanout_mode;
 #endif
@@ -90,6 +93,8 @@ typedef struct VirtualConsole {
     };
 } VirtualConsole;
 
+extern bool gtk_use_gl_area;
+
 /* ui/gtk.c */
 void gd_update_windowsize(VirtualConsole *vc);
 
@@ -111,6 +116,15 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
                             uint32_t backing_height,
                             uint32_t x, uint32_t y,
                             uint32_t w, uint32_t h);
+void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
+                           QemuDmaBuf *dmabuf);
+void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
+                          QemuDmaBuf *dmabuf, bool have_hot,
+                          uint32_t hot_x, uint32_t hot_y);
+void gd_egl_cursor_position(DisplayChangeListener *dcl,
+                            uint32_t pos_x, uint32_t pos_y);
+void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
+                           QemuDmaBuf *dmabuf);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
                           uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(void);
diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index 6b5c73b21c..87a84a59d4 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -122,6 +122,15 @@ struct SimpleSpiceDisplay {
     int gl_updates;
     bool have_scanout;
     bool have_surface;
+
+    QemuDmaBuf *guest_dmabuf;
+    bool guest_dmabuf_refresh;
+    bool render_cursor;
+
+    egl_fb guest_fb;
+    egl_fb blit_fb;
+    egl_fb cursor_fb;
+    bool have_hot;
 #endif
 };
 
diff --git a/linux-user/aarch64/target_syscall.h b/linux-user/aarch64/target_syscall.h
index 604ab99b14..205265e619 100644
--- a/linux-user/aarch64/target_syscall.h
+++ b/linux-user/aarch64/target_syscall.h
@@ -19,4 +19,7 @@ struct target_pt_regs {
 #define TARGET_MLOCKALL_MCL_CURRENT 1
 #define TARGET_MLOCKALL_MCL_FUTURE  2
 
+#define TARGET_PR_SVE_SET_VL  50
+#define TARGET_PR_SVE_GET_VL  51
+
 #endif /* AARCH64_TARGET_SYSCALL_H */
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 4d3f244612..2ce5d7a3c7 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -1446,35 +1446,65 @@ struct target_fpsimd_context {
     uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */
 };
 
-/*
- * Auxiliary context saved in the sigcontext.__reserved array. Not exported to
- * user space as it will change with the addition of new context. User space
- * should check the magic/size information.
- */
-struct target_aux_context {
-    struct target_fpsimd_context fpsimd;
-    /* additional context to be added before "end" */
-    struct target_aarch64_ctx end;
+#define TARGET_EXTRA_MAGIC  0x45585401
+
+struct target_extra_context {
+    struct target_aarch64_ctx head;
+    uint64_t datap; /* 16-byte aligned pointer to extra space cast to __u64 */
+    uint32_t size; /* size in bytes of the extra space */
+    uint32_t reserved[3];
+};
+
+#define TARGET_SVE_MAGIC    0x53564501
+
+struct target_sve_context {
+    struct target_aarch64_ctx head;
+    uint16_t vl;
+    uint16_t reserved[3];
+    /* The actual SVE data immediately follows.  It is layed out
+     * according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of
+     * the original struct pointer.
+     */
 };
 
+#define TARGET_SVE_VQ_BYTES  16
+
+#define TARGET_SVE_SIG_ZREG_SIZE(VQ)  ((VQ) * TARGET_SVE_VQ_BYTES)
+#define TARGET_SVE_SIG_PREG_SIZE(VQ)  ((VQ) * (TARGET_SVE_VQ_BYTES / 8))
+
+#define TARGET_SVE_SIG_REGS_OFFSET \
+    QEMU_ALIGN_UP(sizeof(struct target_sve_context), TARGET_SVE_VQ_BYTES)
+#define TARGET_SVE_SIG_ZREG_OFFSET(VQ, N) \
+    (TARGET_SVE_SIG_REGS_OFFSET + TARGET_SVE_SIG_ZREG_SIZE(VQ) * (N))
+#define TARGET_SVE_SIG_PREG_OFFSET(VQ, N) \
+    (TARGET_SVE_SIG_ZREG_OFFSET(VQ, 32) + TARGET_SVE_SIG_PREG_SIZE(VQ) * (N))
+#define TARGET_SVE_SIG_FFR_OFFSET(VQ) \
+    (TARGET_SVE_SIG_PREG_OFFSET(VQ, 16))
+#define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \
+    (TARGET_SVE_SIG_PREG_OFFSET(VQ, 17))
+
 struct target_rt_sigframe {
     struct target_siginfo info;
     struct target_ucontext uc;
+};
+
+struct target_rt_frame_record {
     uint64_t fp;
     uint64_t lr;
     uint32_t tramp[2];
 };
 
-static int target_setup_sigframe(struct target_rt_sigframe *sf,
-                                 CPUARMState *env, target_sigset_t *set)
+static void target_setup_general_frame(struct target_rt_sigframe *sf,
+                                       CPUARMState *env, target_sigset_t *set)
 {
     int i;
-    struct target_aux_context *aux =
-        (struct target_aux_context *)sf->uc.tuc_mcontext.__reserved;
 
-    /* set up the stack frame for unwinding */
-    __put_user(env->xregs[29], &sf->fp);
-    __put_user(env->xregs[30], &sf->lr);
+    __put_user(0, &sf->uc.tuc_flags);
+    __put_user(0, &sf->uc.tuc_link);
+
+    __put_user(target_sigaltstack_used.ss_sp, &sf->uc.tuc_stack.ss_sp);
+    __put_user(sas_ss_flags(env->xregs[31]), &sf->uc.tuc_stack.ss_flags);
+    __put_user(target_sigaltstack_used.ss_size, &sf->uc.tuc_stack.ss_size);
 
     for (i = 0; i < 31; i++) {
         __put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]);
@@ -1488,39 +1518,79 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf,
     for (i = 0; i < TARGET_NSIG_WORDS; i++) {
         __put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]);
     }
+}
+
+static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd,
+                                       CPUARMState *env)
+{
+    int i;
+
+    __put_user(TARGET_FPSIMD_MAGIC, &fpsimd->head.magic);
+    __put_user(sizeof(struct target_fpsimd_context), &fpsimd->head.size);
+    __put_user(vfp_get_fpsr(env), &fpsimd->fpsr);
+    __put_user(vfp_get_fpcr(env), &fpsimd->fpcr);
 
     for (i = 0; i < 32; i++) {
         uint64_t *q = aa64_vfp_qreg(env, i);
 #ifdef TARGET_WORDS_BIGENDIAN
-        __put_user(q[0], &aux->fpsimd.vregs[i * 2 + 1]);
-        __put_user(q[1], &aux->fpsimd.vregs[i * 2]);
+        __put_user(q[0], &fpsimd->vregs[i * 2 + 1]);
+        __put_user(q[1], &fpsimd->vregs[i * 2]);
 #else
-        __put_user(q[0], &aux->fpsimd.vregs[i * 2]);
-        __put_user(q[1], &aux->fpsimd.vregs[i * 2 + 1]);
+        __put_user(q[0], &fpsimd->vregs[i * 2]);
+        __put_user(q[1], &fpsimd->vregs[i * 2 + 1]);
 #endif
     }
-    __put_user(vfp_get_fpsr(env), &aux->fpsimd.fpsr);
-    __put_user(vfp_get_fpcr(env), &aux->fpsimd.fpcr);
-    __put_user(TARGET_FPSIMD_MAGIC, &aux->fpsimd.head.magic);
-    __put_user(sizeof(struct target_fpsimd_context),
-            &aux->fpsimd.head.size);
+}
 
-    /* set the "end" magic */
-    __put_user(0, &aux->end.magic);
-    __put_user(0, &aux->end.size);
+static void target_setup_extra_record(struct target_extra_context *extra,
+                                      uint64_t datap, uint32_t extra_size)
+{
+    __put_user(TARGET_EXTRA_MAGIC, &extra->head.magic);
+    __put_user(sizeof(struct target_extra_context), &extra->head.size);
+    __put_user(datap, &extra->datap);
+    __put_user(extra_size, &extra->size);
+}
 
-    return 0;
+static void target_setup_end_record(struct target_aarch64_ctx *end)
+{
+    __put_user(0, &end->magic);
+    __put_user(0, &end->size);
 }
 
-static int target_restore_sigframe(CPUARMState *env,
-                                   struct target_rt_sigframe *sf)
+static void target_setup_sve_record(struct target_sve_context *sve,
+                                    CPUARMState *env, int vq, int size)
+{
+    int i, j;
+
+    __put_user(TARGET_SVE_MAGIC, &sve->head.magic);
+    __put_user(size, &sve->head.size);
+    __put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl);
+
+    /* Note that SVE regs are stored as a byte stream, with each byte element
+     * at a subsequent address.  This corresponds to a little-endian store
+     * of our 64-bit hunks.
+     */
+    for (i = 0; i < 32; ++i) {
+        uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i);
+        for (j = 0; j < vq * 2; ++j) {
+            __put_user_e(env->vfp.zregs[i].d[j], z + j, le);
+        }
+    }
+    for (i = 0; i <= 16; ++i) {
+        uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i);
+        for (j = 0; j < vq; ++j) {
+            uint64_t r = env->vfp.pregs[i].p[j >> 2];
+            __put_user_e(r >> ((j & 3) * 16), p + j, le);
+        }
+    }
+}
+
+static void target_restore_general_frame(CPUARMState *env,
+                                         struct target_rt_sigframe *sf)
 {
     sigset_t set;
-    int i;
-    struct target_aux_context *aux =
-        (struct target_aux_context *)sf->uc.tuc_mcontext.__reserved;
-    uint32_t magic, size, fpsr, fpcr;
     uint64_t pstate;
+    int i;
 
     target_to_host_sigset(&set, &sf->uc.tuc_sigmask);
     set_sigmask(&set);
@@ -1533,34 +1603,154 @@ static int target_restore_sigframe(CPUARMState *env,
     __get_user(env->pc, &sf->uc.tuc_mcontext.pc);
     __get_user(pstate, &sf->uc.tuc_mcontext.pstate);
     pstate_write(env, pstate);
+}
 
-    __get_user(magic, &aux->fpsimd.head.magic);
-    __get_user(size, &aux->fpsimd.head.size);
+static void target_restore_fpsimd_record(CPUARMState *env,
+                                         struct target_fpsimd_context *fpsimd)
+{
+    uint32_t fpsr, fpcr;
+    int i;
 
-    if (magic != TARGET_FPSIMD_MAGIC
-        || size != sizeof(struct target_fpsimd_context)) {
-        return 1;
-    }
+    __get_user(fpsr, &fpsimd->fpsr);
+    vfp_set_fpsr(env, fpsr);
+    __get_user(fpcr, &fpsimd->fpcr);
+    vfp_set_fpcr(env, fpcr);
 
     for (i = 0; i < 32; i++) {
         uint64_t *q = aa64_vfp_qreg(env, i);
 #ifdef TARGET_WORDS_BIGENDIAN
-        __get_user(q[0], &aux->fpsimd.vregs[i * 2 + 1]);
-        __get_user(q[1], &aux->fpsimd.vregs[i * 2]);
+        __get_user(q[0], &fpsimd->vregs[i * 2 + 1]);
+        __get_user(q[1], &fpsimd->vregs[i * 2]);
 #else
-        __get_user(q[0], &aux->fpsimd.vregs[i * 2]);
-        __get_user(q[1], &aux->fpsimd.vregs[i * 2 + 1]);
+        __get_user(q[0], &fpsimd->vregs[i * 2]);
+        __get_user(q[1], &fpsimd->vregs[i * 2 + 1]);
 #endif
     }
-    __get_user(fpsr, &aux->fpsimd.fpsr);
-    vfp_set_fpsr(env, fpsr);
-    __get_user(fpcr, &aux->fpsimd.fpcr);
-    vfp_set_fpcr(env, fpcr);
+}
 
-    return 0;
+static void target_restore_sve_record(CPUARMState *env,
+                                      struct target_sve_context *sve, int vq)
+{
+    int i, j;
+
+    /* Note that SVE regs are stored as a byte stream, with each byte element
+     * at a subsequent address.  This corresponds to a little-endian load
+     * of our 64-bit hunks.
+     */
+    for (i = 0; i < 32; ++i) {
+        uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i);
+        for (j = 0; j < vq * 2; ++j) {
+            __get_user_e(env->vfp.zregs[i].d[j], z + j, le);
+        }
+    }
+    for (i = 0; i <= 16; ++i) {
+        uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i);
+        for (j = 0; j < vq; ++j) {
+            uint16_t r;
+            __get_user_e(r, p + j, le);
+            if (j & 3) {
+                env->vfp.pregs[i].p[j >> 2] |= (uint64_t)r << ((j & 3) * 16);
+            } else {
+                env->vfp.pregs[i].p[j >> 2] = r;
+            }
+        }
+    }
+}
+
+static int target_restore_sigframe(CPUARMState *env,
+                                   struct target_rt_sigframe *sf)
+{
+    struct target_aarch64_ctx *ctx, *extra = NULL;
+    struct target_fpsimd_context *fpsimd = NULL;
+    struct target_sve_context *sve = NULL;
+    uint64_t extra_datap = 0;
+    bool used_extra = false;
+    bool err = false;
+    int vq = 0, sve_size = 0;
+
+    target_restore_general_frame(env, sf);
+
+    ctx = (struct target_aarch64_ctx *)sf->uc.tuc_mcontext.__reserved;
+    while (ctx) {
+        uint32_t magic, size, extra_size;
+
+        __get_user(magic, &ctx->magic);
+        __get_user(size, &ctx->size);
+        switch (magic) {
+        case 0:
+            if (size != 0) {
+                err = true;
+                goto exit;
+            }
+            if (used_extra) {
+                ctx = NULL;
+            } else {
+                ctx = extra;
+                used_extra = true;
+            }
+            continue;
+
+        case TARGET_FPSIMD_MAGIC:
+            if (fpsimd || size != sizeof(struct target_fpsimd_context)) {
+                err = true;
+                goto exit;
+            }
+            fpsimd = (struct target_fpsimd_context *)ctx;
+            break;
+
+        case TARGET_SVE_MAGIC:
+            if (arm_feature(env, ARM_FEATURE_SVE)) {
+                vq = (env->vfp.zcr_el[1] & 0xf) + 1;
+                sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
+                if (!sve && size == sve_size) {
+                    sve = (struct target_sve_context *)ctx;
+                    break;
+                }
+            }
+            err = true;
+            goto exit;
+
+        case TARGET_EXTRA_MAGIC:
+            if (extra || size != sizeof(struct target_extra_context)) {
+                err = true;
+                goto exit;
+            }
+            __get_user(extra_datap,
+                       &((struct target_extra_context *)ctx)->datap);
+            __get_user(extra_size,
+                       &((struct target_extra_context *)ctx)->size);
+            extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0);
+            break;
+
+        default:
+            /* Unknown record -- we certainly didn't generate it.
+             * Did we in fact get out of sync?
+             */
+            err = true;
+            goto exit;
+        }
+        ctx = (void *)ctx + size;
+    }
+
+    /* Require FPSIMD always.  */
+    if (fpsimd) {
+        target_restore_fpsimd_record(env, fpsimd);
+    } else {
+        err = true;
+    }
+
+    /* SVE data, if present, overwrites FPSIMD data.  */
+    if (sve) {
+        target_restore_sve_record(env, sve, vq);
+    }
+
+ exit:
+    unlock_user(extra, extra_datap, 0);
+    return err;
 }
 
-static abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *env)
+static abi_ulong get_sigframe(struct target_sigaction *ka,
+                              CPUARMState *env, int size)
 {
     abi_ulong sp;
 
@@ -1573,34 +1763,120 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *env)
         sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
     }
 
-    sp = (sp - sizeof(struct target_rt_sigframe)) & ~15;
+    sp = (sp - size) & ~15;
 
     return sp;
 }
 
+typedef struct {
+    int total_size;
+    int extra_base;
+    int extra_size;
+    int std_end_ofs;
+    int extra_ofs;
+    int extra_end_ofs;
+} target_sigframe_layout;
+
+static int alloc_sigframe_space(int this_size, target_sigframe_layout *l)
+{
+    /* Make sure there will always be space for the end marker.  */
+    const int std_size = sizeof(struct target_rt_sigframe)
+                         - sizeof(struct target_aarch64_ctx);
+    int this_loc = l->total_size;
+
+    if (l->extra_base) {
+        /* Once we have begun an extra space, all allocations go there.  */
+        l->extra_size += this_size;
+    } else if (this_size + this_loc > std_size) {
+        /* This allocation does not fit in the standard space.  */
+        /* Allocate the extra record.  */
+        l->extra_ofs = this_loc;
+        l->total_size += sizeof(struct target_extra_context);
+
+        /* Allocate the standard end record.  */
+        l->std_end_ofs = l->total_size;
+        l->total_size += sizeof(struct target_aarch64_ctx);
+
+        /* Allocate the requested record.  */
+        l->extra_base = this_loc = l->total_size;
+        l->extra_size = this_size;
+    }
+    l->total_size += this_size;
+
+    return this_loc;
+}
+
 static void target_setup_frame(int usig, struct target_sigaction *ka,
                                target_siginfo_t *info, target_sigset_t *set,
                                CPUARMState *env)
 {
+    target_sigframe_layout layout = {
+        /* Begin with the size pointing to the reserved space.  */
+        .total_size = offsetof(struct target_rt_sigframe,
+                               uc.tuc_mcontext.__reserved),
+    };
+    int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0;
     struct target_rt_sigframe *frame;
+    struct target_rt_frame_record *fr;
     abi_ulong frame_addr, return_addr;
 
-    frame_addr = get_sigframe(ka, env);
+    /* FPSIMD record is always in the standard space.  */
+    fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context),
+                                      &layout);
+
+    /* SVE state needs saving only if it exists.  */
+    if (arm_feature(env, ARM_FEATURE_SVE)) {
+        vq = (env->vfp.zcr_el[1] & 0xf) + 1;
+        sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
+        sve_ofs = alloc_sigframe_space(sve_size, &layout);
+    }
+
+    if (layout.extra_ofs) {
+        /* Reserve space for the extra end marker.  The standard end marker
+         * will have been allocated when we allocated the extra record.
+         */
+        layout.extra_end_ofs
+            = alloc_sigframe_space(sizeof(struct target_aarch64_ctx), &layout);
+    } else {
+        /* Reserve space for the standard end marker.
+         * Do not use alloc_sigframe_space because we cheat
+         * std_size therein to reserve space for this.
+         */
+        layout.std_end_ofs = layout.total_size;
+        layout.total_size += sizeof(struct target_aarch64_ctx);
+    }
+
+    /* Reserve space for the return code.  On a real system this would
+     * be within the VDSO.  So, despite the name this is not a "real"
+     * record within the frame.
+     */
+    fr_ofs = layout.total_size;
+    layout.total_size += sizeof(struct target_rt_frame_record);
+
+    frame_addr = get_sigframe(ka, env, layout.total_size);
     trace_user_setup_frame(env, frame_addr);
     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
         goto give_sigsegv;
     }
 
-    __put_user(0, &frame->uc.tuc_flags);
-    __put_user(0, &frame->uc.tuc_link);
+    target_setup_general_frame(frame, env, set);
+    target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env);
+    target_setup_end_record((void *)frame + layout.std_end_ofs);
+    if (layout.extra_ofs) {
+        target_setup_extra_record((void *)frame + layout.extra_ofs,
+                                  frame_addr + layout.extra_base,
+                                  layout.extra_size);
+        target_setup_end_record((void *)frame + layout.extra_end_ofs);
+    }
+    if (sve_ofs) {
+        target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size);
+    }
+
+    /* Set up the stack frame for unwinding.  */
+    fr = (void *)frame + fr_ofs;
+    __put_user(env->xregs[29], &fr->fp);
+    __put_user(env->xregs[30], &fr->lr);
 
-    __put_user(target_sigaltstack_used.ss_sp,
-                      &frame->uc.tuc_stack.ss_sp);
-    __put_user(sas_ss_flags(env->xregs[31]),
-                      &frame->uc.tuc_stack.ss_flags);
-    __put_user(target_sigaltstack_used.ss_size,
-                      &frame->uc.tuc_stack.ss_size);
-    target_setup_sigframe(frame, env, set);
     if (ka->sa_flags & TARGET_SA_RESTORER) {
         return_addr = ka->sa_restorer;
     } else {
@@ -1609,13 +1885,14 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
          * Since these are instructions they need to be put as little-endian
          * regardless of target default or current CPU endianness.
          */
-        __put_user_e(0xd2801168, &frame->tramp[0], le);
-        __put_user_e(0xd4000001, &frame->tramp[1], le);
-        return_addr = frame_addr + offsetof(struct target_rt_sigframe, tramp);
+        __put_user_e(0xd2801168, &fr->tramp[0], le);
+        __put_user_e(0xd4000001, &fr->tramp[1], le);
+        return_addr = frame_addr + fr_ofs
+            + offsetof(struct target_rt_frame_record, tramp);
     }
     env->xregs[0] = usig;
     env->xregs[31] = frame_addr;
-    env->xregs[29] = env->xregs[31] + offsetof(struct target_rt_sigframe, fp);
+    env->xregs[29] = frame_addr + fr_ofs;
     env->pc = ka->_sa_handler;
     env->xregs[30] = return_addr;
     if (info) {
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index a8abfd421d..b4f7b14fbe 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -10672,6 +10672,33 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             break;
         }
 #endif
+#ifdef TARGET_AARCH64
+        case TARGET_PR_SVE_SET_VL:
+            /* We cannot support either PR_SVE_SET_VL_ONEXEC
+               or PR_SVE_VL_INHERIT.  Therefore, anything above
+               ARM_MAX_VQ results in EINVAL.  */
+            ret = -TARGET_EINVAL;
+            if (arm_feature(cpu_env, ARM_FEATURE_SVE)
+                && arg2 >= 0 && arg2 <= ARM_MAX_VQ * 16 && !(arg2 & 15)) {
+                CPUARMState *env = cpu_env;
+                int old_vq = (env->vfp.zcr_el[1] & 0xf) + 1;
+                int vq = MAX(arg2 / 16, 1);
+
+                if (vq < old_vq) {
+                    aarch64_sve_narrow_vq(env, vq);
+                }
+                env->vfp.zcr_el[1] = vq - 1;
+                ret = vq * 16;
+            }
+            break;
+        case TARGET_PR_SVE_GET_VL:
+            ret = -TARGET_EINVAL;
+            if (arm_feature(cpu_env, ARM_FEATURE_SVE)) {
+                CPUARMState *env = cpu_env;
+                ret = ((env->vfp.zcr_el[1] & 0xf) + 1) * 16;
+            }
+            break;
+#endif /* AARCH64 */
         case PR_GET_SECCOMP:
         case PR_SET_SECCOMP:
             /* Disable seccomp to prevent the target disabling syscalls we
diff --git a/migration/block.c b/migration/block.c
index 1f03946797..41b95d1dd8 100644
--- a/migration/block.c
+++ b/migration/block.c
@@ -36,7 +36,7 @@
 
 #define MAX_IS_ALLOCATED_SEARCH (65536 * BDRV_SECTOR_SIZE)
 
-#define MAX_INFLIGHT_IO 512
+#define MAX_IO_BUFFERS 512
 
 //#define DEBUG_BLK_MIGRATION
 
@@ -331,11 +331,10 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
      */
     qemu_mutex_lock_iothread();
     aio_context_acquire(blk_get_aio_context(bmds->blk));
-    blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov,
-                                0, blk_mig_read_cb, blk);
-
     bdrv_reset_dirty_bitmap(bmds->dirty_bitmap, cur_sector * BDRV_SECTOR_SIZE,
                             nr_sectors * BDRV_SECTOR_SIZE);
+    blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov,
+                                0, blk_mig_read_cb, blk);
     aio_context_release(blk_get_aio_context(bmds->blk));
     qemu_mutex_unlock_iothread();
 
@@ -776,9 +775,8 @@ static int block_save_iterate(QEMUFile *f, void *opaque)
     while ((block_mig_state.submitted +
             block_mig_state.read_done) * BLOCK_SIZE <
            qemu_file_get_rate_limit(f) &&
-           (block_mig_state.submitted +
-            block_mig_state.read_done) <
-           MAX_INFLIGHT_IO) {
+           (block_mig_state.submitted + block_mig_state.read_done) <
+           MAX_IO_BUFFERS) {
         blk_mig_unlock();
         if (block_mig_state.bulk_completed == 0) {
             /* first finish the bulk phase */
diff --git a/migration/migration.c b/migration/migration.c
index e345d0cc7e..6a4780ef6f 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -747,13 +747,15 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
 {
     MigrationState *s = migrate_get_current();
     MigrationCapabilityStatusList *cap;
+    bool cap_list[MIGRATION_CAPABILITY__MAX];
 
     if (migration_is_setup_or_active(s->state)) {
         error_setg(errp, QERR_MIGRATION_ACTIVE);
         return;
     }
 
-    if (!migrate_caps_check(s->enabled_capabilities, params, errp)) {
+    memcpy(cap_list, s->enabled_capabilities, sizeof(cap_list));
+    if (!migrate_caps_check(cap_list, params, errp)) {
         return;
     }
 
@@ -2541,6 +2543,7 @@ static void migration_instance_finalize(Object *obj)
     g_free(params->tls_hostname);
     g_free(params->tls_creds);
     qemu_sem_destroy(&ms->pause_sem);
+    error_free(ms->error);
 }
 
 static void migration_instance_init(Object *obj)
diff --git a/migration/ram.c b/migration/ram.c
index 3b6c077964..7266351fd0 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -2258,6 +2258,13 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
     int64_t t0;
     int done = 0;
 
+    if (blk_mig_bulk_active()) {
+        /* Avoid transferring ram during bulk phase of block migration as
+         * the bulk phase will usually take a long time and transferring
+         * ram updates during that time is pointless. */
+        goto out;
+    }
+
     rcu_read_lock();
     if (ram_list.version != rs->last_version) {
         ram_state_reset(rs);
@@ -2304,6 +2311,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
      */
     ram_control_after_iterate(f, RAM_CONTROL_ROUND);
 
+out:
     qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
     ram_counters.transferred += 8;
 
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 00475f08d4..524d51567a 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2553,6 +2553,63 @@
             '*encrypt': 'BlockdevQcow2Encryption' } }
 
 ##
+# @SshHostKeyCheckMode:
+#
+# @none             Don't check the host key at all
+# @hash             Compare the host key with a given hash
+# @known_hosts      Check the host key against the known_hosts file
+#
+# Since: 2.12
+##
+{ 'enum': 'SshHostKeyCheckMode',
+  'data': [ 'none', 'hash', 'known_hosts' ] }
+
+##
+# @SshHostKeyCheckHashType:
+#
+# @md5              The given hash is an md5 hash
+# @sha1             The given hash is an sha1 hash
+#
+# Since: 2.12
+##
+{ 'enum': 'SshHostKeyCheckHashType',
+  'data': [ 'md5', 'sha1' ] }
+
+##
+# @SshHostKeyHash:
+#
+# @type             The hash algorithm used for the hash
+# @hash             The expected hash value
+#
+# Since: 2.12
+##
+{ 'struct': 'SshHostKeyHash',
+  'data': { 'type': 'SshHostKeyCheckHashType',
+            'hash': 'str' }}
+
+##
+# @SshHostKeyDummy:
+#
+# For those union branches that don't need additional fields.
+#
+# Since: 2.12
+##
+{ 'struct': 'SshHostKeyDummy',
+  'data': {} }
+
+##
+# @SshHostKeyCheck:
+#
+# Since: 2.12
+##
+{ 'union': 'SshHostKeyCheck',
+  'base': { 'mode': 'SshHostKeyCheckMode' },
+  'discriminator': 'mode',
+  'data': { 'none': 'SshHostKeyDummy',
+            'hash': 'SshHostKeyHash',
+            'known_hosts': 'SshHostKeyDummy' } }
+
+##
 # @BlockdevOptionsSsh:
 #
 # @server:              host address
@@ -2562,14 +2619,16 @@
 # @user:                user as which to connect, defaults to current
 #                       local user name
 #
-# TODO: Expose the host_key_check option in QMP
+# @host-key-check:      Defines how and what to check the host key against
+#                       (default: known_hosts)
 #
 # Since: 2.9
 ##
 { 'struct': 'BlockdevOptionsSsh',
   'data': { 'server': 'InetSocketAddress',
             'path': 'str',
-            '*user': 'str' } }
+            '*user': 'str',
+            '*host-key-check': 'SshHostKeyCheck' } }
 
 
 ##
@@ -3359,6 +3418,269 @@
 { 'command': 'blockdev-del', 'data': { 'node-name': 'str' } }
 
 ##
+# @BlockdevCreateOptionsFile:
+#
+# Driver specific image creation options for file.
+#
+# @filename         Filename for the new image file
+# @size             Size of the virtual disk in bytes
+# @preallocation    Preallocation mode for the new image (default: off)
+# @nocow            Turn off copy-on-write (valid only on btrfs; default: off)
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsFile',
+  'data': { 'filename':         'str',
+            'size':             'size',
+            '*preallocation':   'PreallocMode',
+            '*nocow':           'bool' } }
+
+##
+# @BlockdevCreateOptionsGluster:
+#
+# Driver specific image creation options for gluster.
+#
+# @location         Where to store the new image file
+# @size             Size of the virtual disk in bytes
+# @preallocation    Preallocation mode for the new image (default: off)
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsGluster',
+  'data': { 'location':         'BlockdevOptionsGluster',
+            'size':             'size',
+            '*preallocation':   'PreallocMode' } }
+
+##
+# @BlockdevCreateOptionsNfs:
+#
+# Driver specific image creation options for NFS.
+#
+# @location         Where to store the new image file
+# @size             Size of the virtual disk in bytes
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsNfs',
+  'data': { 'location':         'BlockdevOptionsNfs',
+            'size':             'size' } }
+
+##
+# @BlockdevQcow2Version:
+#
+# @v2:  The original QCOW2 format as introduced in qemu 0.10 (version 2)
+# @v3:  The extended QCOW2 format as introduced in qemu 1.1 (version 3)
+#
+# Since: 2.12
+##
+{ 'enum': 'BlockdevQcow2Version',
+  'data': [ 'v2', 'v3' ] }
+
+
+##
+# @BlockdevCreateOptionsQcow2:
+#
+# Driver specific image creation options for qcow2.
+#
+# @file             Node to create the image format on
+# @size             Size of the virtual disk in bytes
+# @version          Compatibility level (default: v3)
+# @backing-file     File name of the backing file if a backing file
+#                   should be used
+# @backing-fmt      Name of the block driver to use for the backing file
+# @encrypt          Encryption options if the image should be encrypted
+# @cluster-size     qcow2 cluster size in bytes (default: 65536)
+# @preallocation    Preallocation mode for the new image (default: off)
+# @lazy-refcounts   True if refcounts may be updated lazily (default: off)
+# @refcount-bits    Width of reference counts in bits (default: 16)
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsQcow2',
+  'data': { 'file':             'BlockdevRef',
+            'size':             'size',
+            '*version':         'BlockdevQcow2Version',
+            '*backing-file':    'str',
+            '*backing-fmt':     'BlockdevDriver',
+            '*encrypt':         'QCryptoBlockCreateOptions',
+            '*cluster-size':    'size',
+            '*preallocation':   'PreallocMode',
+            '*lazy-refcounts':  'bool',
+            '*refcount-bits':   'int' } }
+
+##
+# @BlockdevCreateOptionsRbd:
+#
+# Driver specific image creation options for rbd/Ceph.
+#
+# @location         Where to store the new image file. This location cannot
+#                   point to a snapshot.
+# @size             Size of the virtual disk in bytes
+# @cluster-size     RBD object size
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsRbd',
+  'data': { 'location':         'BlockdevOptionsRbd',
+            'size':             'size',
+            '*cluster-size' :   'size' } }
+
+##
+# @SheepdogRedundancyType:
+#
+# @full             Create a fully replicated vdi with x copies
+# @erasure-coded    Create an erasure coded vdi with x data strips and
+#                   y parity strips
+#
+# Since: 2.12
+##
+{ 'enum': 'SheepdogRedundancyType',
+  'data': [ 'full', 'erasure-coded' ] }
+
+##
+# @SheepdogRedundancyFull:
+#
+# @copies           Number of copies to use (between 1 and 31)
+#
+# Since: 2.12
+##
+{ 'struct': 'SheepdogRedundancyFull',
+  'data': { 'copies': 'int' }}
+
+##
+# @SheepdogRedundancyErasureCoded:
+#
+# @data-strips      Number of data strips to use (one of {2,4,8,16})
+# @parity-strips    Number of parity strips to use (between 1 and 15)
+#
+# Since: 2.12
+##
+{ 'struct': 'SheepdogRedundancyErasureCoded',
+  'data': { 'data-strips': 'int',
+            'parity-strips': 'int' }}
+
+##
+# @SheepdogRedundancy:
+#
+# Since: 2.12
+##
+{ 'union': 'SheepdogRedundancy',
+  'base': { 'type': 'SheepdogRedundancyType' },
+  'discriminator': 'type',
+  'data': { 'full': 'SheepdogRedundancyFull',
+            'erasure-coded': 'SheepdogRedundancyErasureCoded' } }
+
+##
+# @BlockdevCreateOptionsSheepdog:
+#
+# Driver specific image creation options for Sheepdog.
+#
+# @location         Where to store the new image file
+# @size             Size of the virtual disk in bytes
+# @backing-file     File name of a base image
+# @preallocation    Preallocation mode (allowed values: off, full)
+# @redundancy       Redundancy of the image
+# @object-size      Object size of the image
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsSheepdog',
+  'data': { 'location':         'BlockdevOptionsSheepdog',
+            'size':             'size',
+            '*backing-file':    'str',
+            '*preallocation':   'PreallocMode',
+            '*redundancy':      'SheepdogRedundancy',
+            '*object-size':     'size' } }
+
+##
+# @BlockdevCreateOptionsSsh:
+#
+# Driver specific image creation options for SSH.
+#
+# @location         Where to store the new image file
+# @size             Size of the virtual disk in bytes
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsSsh',
+  'data': { 'location':         'BlockdevOptionsSsh',
+            'size':             'size' } }
+
+##
+# @BlockdevCreateNotSupported:
+#
+# This is used for all drivers that don't support creating images.
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateNotSupported', 'data': {}}
+
+##
+# @BlockdevCreateOptions:
+#
+# Options for creating an image format on a given node.
+#
+# @driver           block driver to create the image format
+#
+# Since: 2.12
+##
+{ 'union': 'BlockdevCreateOptions',
+  'base': {
+      'driver':         'BlockdevDriver' },
+  'discriminator': 'driver',
+  'data': {
+      'blkdebug':       'BlockdevCreateNotSupported',
+      'blkverify':      'BlockdevCreateNotSupported',
+      'bochs':          'BlockdevCreateNotSupported',
+      'cloop':          'BlockdevCreateNotSupported',
+      'dmg':            'BlockdevCreateNotSupported',
+      'file':           'BlockdevCreateOptionsFile',
+      'ftp':            'BlockdevCreateNotSupported',
+      'ftps':           'BlockdevCreateNotSupported',
+      'gluster':        'BlockdevCreateOptionsGluster',
+      'host_cdrom':     'BlockdevCreateNotSupported',
+      'host_device':    'BlockdevCreateNotSupported',
+      'http':           'BlockdevCreateNotSupported',
+      'https':          'BlockdevCreateNotSupported',
+      'iscsi':          'BlockdevCreateNotSupported',
+      'luks':           'BlockdevCreateNotSupported',
+      'nbd':            'BlockdevCreateNotSupported',
+      'nfs':            'BlockdevCreateOptionsNfs',
+      'null-aio':       'BlockdevCreateNotSupported',
+      'null-co':        'BlockdevCreateNotSupported',
+      'nvme':           'BlockdevCreateNotSupported',
+      'parallels':      'BlockdevCreateNotSupported',
+      'qcow2':          'BlockdevCreateOptionsQcow2',
+      'qcow':           'BlockdevCreateNotSupported',
+      'qed':            'BlockdevCreateNotSupported',
+      'quorum':         'BlockdevCreateNotSupported',
+      'raw':            'BlockdevCreateNotSupported',
+      'rbd':            'BlockdevCreateOptionsRbd',
+      'replication':    'BlockdevCreateNotSupported',
+      'sheepdog':       'BlockdevCreateOptionsSheepdog',
+      'ssh':            'BlockdevCreateOptionsSsh',
+      'throttle':       'BlockdevCreateNotSupported',
+      'vdi':            'BlockdevCreateNotSupported',
+      'vhdx':           'BlockdevCreateNotSupported',
+      'vmdk':           'BlockdevCreateNotSupported',
+      'vpc':            'BlockdevCreateNotSupported',
+      'vvfat':          'BlockdevCreateNotSupported',
+      'vxhs':           'BlockdevCreateNotSupported'
+  } }
+
+##
+# @x-blockdev-create:
+#
+# Create an image format on a given node.
+# TODO Replace with something asynchronous (block job?)
+#
+# Since: 2.12
+##
+{ 'command': 'x-blockdev-create',
+  'data': 'BlockdevCreateOptions',
+  'boxed': true }
+
+##
 # @blockdev-open-tray:
 #
 # Opens a block device's tray. If there is a block driver state tree inserted as
diff --git a/qapi/ui.json b/qapi/ui.json
index 3e82f25ac5..5d01ad4304 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -77,6 +77,13 @@
 #
 # @filename: the path of a new PPM file to store the image
 #
+# @device: ID of the display device that should be dumped. If this parameter
+#          is missing, the primary display will be used. (Since 2.12)
+#
+# @head: head to use in case the device supports multiple heads. If this
+#        parameter is missing, head #0 will be used. Also note that the head
+#        can only be specified in conjunction with the device ID. (Since 2.12)
+#
 # Returns: Nothing on success
 #
 # Since: 0.14.0
@@ -88,7 +95,8 @@
 # <- { "return": {} }
 #
 ##
-{ 'command': 'screendump', 'data': {'filename': 'str'} }
+{ 'command': 'screendump',
+  'data': {'filename': 'str', '*device': 'str', '*head': 'int'} }
 
 ##
 # == Spice
diff --git a/qobject/qdict.c b/qobject/qdict.c
index 23df84f9cd..229b8c840b 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -1072,3 +1072,37 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite)
         entry = next;
     }
 }
+
+/**
+ * qdict_rename_keys(): Rename keys in qdict according to the replacements
+ * specified in the array renames. The array must be terminated by an entry
+ * with from = NULL.
+ *
+ * The renames are performed individually in the order of the array, so entries
+ * may be renamed multiple times and may or may not conflict depending on the
+ * order of the renames array.
+ *
+ * Returns true for success, false in error cases.
+ */
+bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp)
+{
+    QObject *qobj;
+
+    while (renames->from) {
+        if (qdict_haskey(qdict, renames->from)) {
+            if (qdict_haskey(qdict, renames->to)) {
+                error_setg(errp, "'%s' and its alias '%s' can't be used at the "
+                           "same time", renames->to, renames->from);
+                return false;
+            }
+
+            qobj = qdict_get(qdict, renames->from);
+            qobject_incref(qobj);
+            qdict_put_obj(qdict, renames->to, qobj);
+            qdict_del(qdict, renames->from);
+        }
+
+        renames++;
+    }
+    return true;
+}
diff --git a/scripts/create_config b/scripts/create_config
index 603b826886..d727e5e36e 100755
--- a/scripts/create_config
+++ b/scripts/create_config
@@ -36,7 +36,7 @@ case $line in
     drivers=${line#*=}
     echo "#define CONFIG_AUDIO_DRIVERS \\"
     for drv in $drivers; do
-      echo "    &${drv}_audio_driver,\\"
+      echo "    \"${drv}\",\\"
     done
     echo ""
     ;;
diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py
index a3a6315055..9d45c6ba4e 100755
--- a/scripts/simpletrace.py
+++ b/scripts/simpletrace.py
@@ -168,7 +168,7 @@ class Analyzer(object):
 def process(events, log, analyzer, read_header=True):
     """Invoke an analyzer on each event in a log."""
     if isinstance(events, str):
-        events = read_events(open(events, 'r'))
+        events = read_events(open(events, 'r'), events)
     if isinstance(log, str):
         log = open(log, 'rb')
 
@@ -199,7 +199,7 @@ def process(events, log, analyzer, read_header=True):
         fn_argcount = len(inspect.getargspec(fn)[0]) - 1
         if fn_argcount == event_argcount + 1:
             # Include timestamp as first argument
-            return lambda _, rec: fn(*((rec[1:2],) + rec[3:3 + event_argcount]))
+            return lambda _, rec: fn(*(rec[1:2] + rec[3:3 + event_argcount]))
         elif fn_argcount == event_argcount + 2:
             # Include timestamp and pid
             return lambda _, rec: fn(*rec[1:3 + event_argcount])
@@ -233,7 +233,7 @@ def run(analyzer):
                          '<trace-file>\n' % sys.argv[0])
         sys.exit(1)
 
-    events = read_events(open(sys.argv[1], 'r'))
+    events = read_events(open(sys.argv[1], 'r'), sys.argv[1])
     process(events, sys.argv[2], analyzer, read_header=read_header)
 
 if __name__ == '__main__':
diff --git a/scripts/tracetool.py b/scripts/tracetool.py
index c55a21518b..fe2b0771f2 100755
--- a/scripts/tracetool.py
+++ b/scripts/tracetool.py
@@ -142,7 +142,7 @@ def main(args):
     events = []
     for arg in args:
         with open(arg, "r") as fh:
-            events.extend(tracetool.read_events(fh))
+            events.extend(tracetool.read_events(fh, arg))
 
     try:
         tracetool.generate(events, arg_group, arg_format, arg_backends,
diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py
index 3646c2b9fc..b20fac34a3 100644
--- a/scripts/tracetool/__init__.py
+++ b/scripts/tracetool/__init__.py
@@ -41,6 +41,51 @@ def out(*lines, **kwargs):
     lines = [ l % kwargs for l in lines ]
     sys.stdout.writelines("\n".join(lines) + "\n")
 
+# We only want to allow standard C types or fixed sized
+# integer types. We don't want QEMU specific types
+# as we can't assume trace backends can resolve all the
+# typedefs
+ALLOWED_TYPES = [
+    "int",
+    "long",
+    "short",
+    "char",
+    "bool",
+    "unsigned",
+    "signed",
+    "float",
+    "double",
+    "int8_t",
+    "uint8_t",
+    "int16_t",
+    "uint16_t",
+    "int32_t",
+    "uint32_t",
+    "int64_t",
+    "uint64_t",
+    "void",
+    "size_t",
+    "ssize_t",
+    "uintptr_t",
+    "ptrdiff_t",
+    # Magic substitution is done by tracetool
+    "TCGv",
+]
+
+def validate_type(name):
+    bits = name.split(" ")
+    for bit in bits:
+        bit = re.sub("\*", "", bit)
+        if bit == "":
+            continue
+        if bit == "const":
+            continue
+        if bit not in ALLOWED_TYPES:
+            raise ValueError("Argument type '%s' is not in whitelist. "
+                             "Only standard C types and fixed size integer "
+                             "types should be used. struct, union, and "
+                             "other complex pointer types should be "
+                             "declared as 'void *'" % name)
 
 class Arguments:
     """Event arguments description."""
@@ -87,6 +132,7 @@ class Arguments:
             else:
                 arg_type, identifier = arg.rsplit(None, 1)
 
+            validate_type(arg_type)
             res.append((arg_type, identifier))
         return Arguments(res)
 
@@ -291,13 +337,15 @@ class Event(object):
                      self)
 
 
-def read_events(fobj):
+def read_events(fobj, fname):
     """Generate the output for the given (format, backends) pair.
 
     Parameters
     ----------
     fobj : file
         Event description file.
+    fname : str
+        Name of event file
 
     Returns a list of Event objects
     """
@@ -312,7 +360,7 @@ def read_events(fobj):
         try:
             event = Event.build(line)
         except ValueError as e:
-            arg0 = 'Error on line %d: %s' % (lineno, e.args[0])
+            arg0 = 'Error at %s:%d: %s' % (fname, lineno, e.args[0])
             e.args = (arg0,) + e.args[1:]
             raise
 
diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py
index da86f6b882..78933d03ad 100644
--- a/scripts/tracetool/backend/log.py
+++ b/scripts/tracetool/backend/log.py
@@ -20,7 +20,7 @@ PUBLIC = True
 
 
 def generate_h_begin(events, group):
-    out('#include "qemu/log.h"',
+    out('#include "qemu/log-for-trace.h"',
         '')
 
 
@@ -35,14 +35,13 @@ def generate_h(event, group):
     else:
         cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper())
 
-    out('    if (%(cond)s) {',
+    out('    if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {',
         '        struct timeval _now;',
         '        gettimeofday(&_now, NULL);',
-        '        qemu_log_mask(LOG_TRACE,',
-        '                      "%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",',
-        '                      getpid(),',
-        '                      (size_t)_now.tv_sec, (size_t)_now.tv_usec',
-        '                      %(argnames)s);',
+        '        qemu_log("%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",',
+        '                 getpid(),',
+        '                 (size_t)_now.tv_sec, (size_t)_now.tv_usec',
+        '                 %(argnames)s);',
         '    }',
         cond=cond,
         name=event.name,
diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h
index a42495bac9..d135ff8e06 100644
--- a/target/arm/cpu-qom.h
+++ b/target/arm/cpu-qom.h
@@ -33,6 +33,8 @@ struct arm_boot_info;
 #define ARM_CPU_GET_CLASS(obj) \
     OBJECT_GET_CLASS(ARMCPUClass, (obj), TYPE_ARM_CPU)
 
+#define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU
+
 /**
  * ARMCPUClass:
  * @parent_realize: The parent class' realize handler.
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 6b77aaa445..022d8c5787 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -725,6 +725,19 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
     int pagebits;
     Error *local_err = NULL;
 
+    /* If we needed to query the host kernel for the CPU features
+     * then it's possible that might have failed in the initfn, but
+     * this is the first point where we can report it.
+     */
+    if (cpu->host_cpu_probe_failed) {
+        if (!kvm_enabled()) {
+            error_setg(errp, "The 'host' CPU type can only be used with KVM");
+        } else {
+            error_setg(errp, "Failed to retrieve host CPU features");
+        }
+        return;
+    }
+
     cpu_exec_realizefn(cs, &local_err);
     if (local_err != NULL) {
         error_propagate(errp, local_err);
@@ -939,6 +952,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
         cs->num_ases = 1;
     }
     cpu_address_space_init(cs, ARMASIdx_NS, "cpu-memory", cs->memory);
+
+    /* No core_count specified, default to smp_cpus. */
+    if (cpu->core_count == -1) {
+        cpu->core_count = smp_cpus;
+    }
 #endif
 
     qemu_init_vcpu(cs);
@@ -952,9 +970,19 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model)
     ObjectClass *oc;
     char *typename;
     char **cpuname;
+    const char *cpunamestr;
 
     cpuname = g_strsplit(cpu_model, ",", 1);
-    typename = g_strdup_printf(ARM_CPU_TYPE_NAME("%s"), cpuname[0]);
+    cpunamestr = cpuname[0];
+#ifdef CONFIG_USER_ONLY
+    /* For backwards compatibility usermode emulation allows "-cpu any",
+     * which has the same semantics as "-cpu max".
+     */
+    if (!strcmp(cpunamestr, "any")) {
+        cpunamestr = "max";
+    }
+#endif
+    typename = g_strdup_printf(ARM_CPU_TYPE_NAME("%s"), cpunamestr);
     oc = object_class_by_name(typename);
     g_strfreev(cpuname);
     g_free(typename);
@@ -1684,22 +1712,37 @@ static void pxa270c5_initfn(Object *obj)
     cpu->reset_sctlr = 0x00000078;
 }
 
-#ifdef CONFIG_USER_ONLY
-static void arm_any_initfn(Object *obj)
+#ifndef TARGET_AARCH64
+/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
+ * otherwise, a CPU with as many features enabled as our emulation supports.
+ * The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c;
+ * this only needs to handle 32 bits.
+ */
+static void arm_max_initfn(Object *obj)
 {
     ARMCPU *cpu = ARM_CPU(obj);
-    set_feature(&cpu->env, ARM_FEATURE_V8);
-    set_feature(&cpu->env, ARM_FEATURE_VFP4);
-    set_feature(&cpu->env, ARM_FEATURE_NEON);
-    set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
-    set_feature(&cpu->env, ARM_FEATURE_V8_AES);
-    set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
-    set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
-    set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
-    set_feature(&cpu->env, ARM_FEATURE_CRC);
-    set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
-    set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
-    cpu->midr = 0xffffffff;
+
+    if (kvm_enabled()) {
+        kvm_arm_set_cpu_features_from_host(cpu);
+    } else {
+        cortex_a15_initfn(obj);
+#ifdef CONFIG_USER_ONLY
+        /* We don't set these in system emulation mode for the moment,
+         * since we don't correctly set the ID registers to advertise them,
+         */
+        set_feature(&cpu->env, ARM_FEATURE_V8);
+        set_feature(&cpu->env, ARM_FEATURE_VFP4);
+        set_feature(&cpu->env, ARM_FEATURE_NEON);
+        set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
+        set_feature(&cpu->env, ARM_FEATURE_V8_AES);
+        set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
+        set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
+        set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
+        set_feature(&cpu->env, ARM_FEATURE_CRC);
+        set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
+        set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
+#endif
+    }
 }
 #endif
 
@@ -1751,8 +1794,11 @@ static const ARMCPUInfo arm_cpus[] = {
     { .name = "pxa270-b1",   .initfn = pxa270b1_initfn },
     { .name = "pxa270-c0",   .initfn = pxa270c0_initfn },
     { .name = "pxa270-c5",   .initfn = pxa270c5_initfn },
+#ifndef TARGET_AARCH64
+    { .name = "max",         .initfn = arm_max_initfn },
+#endif
 #ifdef CONFIG_USER_ONLY
-    { .name = "any",         .initfn = arm_any_initfn },
+    { .name = "any",         .initfn = arm_max_initfn },
 #endif
 #endif
     { .name = NULL }
@@ -1765,6 +1811,7 @@ static Property arm_cpu_properties[] = {
     DEFINE_PROP_UINT64("mp-affinity", ARMCPU,
                         mp_affinity, ARM64_AFFINITY_INVALID),
     DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID),
+    DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1),
     DEFINE_PROP_END_OF_LIST()
 };
 
@@ -1845,6 +1892,26 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
 #endif
 }
 
+#ifdef CONFIG_KVM
+static void arm_host_initfn(Object *obj)
+{
+    ARMCPU *cpu = ARM_CPU(obj);
+
+    kvm_arm_set_cpu_features_from_host(cpu);
+}
+
+static const TypeInfo host_arm_cpu_type_info = {
+    .name = TYPE_ARM_HOST_CPU,
+#ifdef TARGET_AARCH64
+    .parent = TYPE_AARCH64_CPU,
+#else
+    .parent = TYPE_ARM_CPU,
+#endif
+    .instance_init = arm_host_initfn,
+};
+
+#endif
+
 static void cpu_register(const ARMCPUInfo *info)
 {
     TypeInfo type_info = {
@@ -1889,6 +1956,10 @@ static void arm_cpu_register_types(void)
         cpu_register(info);
         info++;
     }
+
+#ifdef CONFIG_KVM
+    type_register_static(&host_arm_cpu_type_info);
+#endif
 }
 
 type_init(arm_cpu_register_types)
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 8dd6b788df..1e7e1f8a7e 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -745,6 +745,16 @@ struct ARMCPU {
     /* Uniprocessor system with MP extensions */
     bool mp_is_up;
 
+    /* True if we tried kvm_arm_host_cpu_features() during CPU instance_init
+     * and the probe failed (so we need to report the error in realize)
+     */
+    bool host_cpu_probe_failed;
+
+    /* Specify the number of cores in this CPU cluster. Used for the L2CTLR
+     * register.
+     */
+    int32_t core_count;
+
     /* The instance init functions for implementation-specific subclasses
      * set these fields to specify the implementation-dependent values of
      * various constant registers and reset values of non-constant
@@ -861,6 +871,7 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
 #ifdef TARGET_AARCH64
 int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
 int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
+void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq);
 #endif
 
 target_ulong do_arm_semihosting(CPUARMState *env);
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 4228713b19..991d764674 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -28,6 +28,7 @@
 #include "hw/arm/arm.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/kvm.h"
+#include "kvm_arm.h"
 
 static inline void set_feature(CPUARMState *env, int feature)
 {
@@ -42,8 +43,10 @@ static inline void unset_feature(CPUARMState *env, int feature)
 #ifndef CONFIG_USER_ONLY
 static uint64_t a57_a53_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri)
 {
-    /* Number of processors is in [25:24]; otherwise we RAZ */
-    return (smp_cpus - 1) << 24;
+    ARMCPU *cpu = arm_env_get_cpu(env);
+
+    /* Number of cores is in [25:24]; otherwise we RAZ */
+    return (cpu->core_count - 1) << 24;
 }
 #endif
 
@@ -212,31 +215,50 @@ static void aarch64_a53_initfn(Object *obj)
     define_arm_cp_regs(cpu, cortex_a57_a53_cp_reginfo);
 }
 
-#ifdef CONFIG_USER_ONLY
-static void aarch64_any_initfn(Object *obj)
+/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
+ * otherwise, a CPU with as many features enabled as our emulation supports.
+ * The version of '-cpu max' for qemu-system-arm is defined in cpu.c;
+ * this only needs to handle 64 bits.
+ */
+static void aarch64_max_initfn(Object *obj)
 {
     ARMCPU *cpu = ARM_CPU(obj);
 
-    set_feature(&cpu->env, ARM_FEATURE_V8);
-    set_feature(&cpu->env, ARM_FEATURE_VFP4);
-    set_feature(&cpu->env, ARM_FEATURE_NEON);
-    set_feature(&cpu->env, ARM_FEATURE_AARCH64);
-    set_feature(&cpu->env, ARM_FEATURE_V8_AES);
-    set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
-    set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
-    set_feature(&cpu->env, ARM_FEATURE_V8_SHA512);
-    set_feature(&cpu->env, ARM_FEATURE_V8_SHA3);
-    set_feature(&cpu->env, ARM_FEATURE_V8_SM3);
-    set_feature(&cpu->env, ARM_FEATURE_V8_SM4);
-    set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
-    set_feature(&cpu->env, ARM_FEATURE_CRC);
-    set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
-    set_feature(&cpu->env, ARM_FEATURE_V8_FP16);
-    set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
-    cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
-    cpu->dcz_blocksize = 7; /*  512 bytes */
-}
+    if (kvm_enabled()) {
+        kvm_arm_set_cpu_features_from_host(cpu);
+    } else {
+        aarch64_a57_initfn(obj);
+#ifdef CONFIG_USER_ONLY
+        /* We don't set these in system emulation mode for the moment,
+         * since we don't correctly set the ID registers to advertise them,
+         * and in some cases they're only available in AArch64 and not AArch32,
+         * whereas the architecture requires them to be present in both if
+         * present in either.
+         */
+        set_feature(&cpu->env, ARM_FEATURE_V8);
+        set_feature(&cpu->env, ARM_FEATURE_VFP4);
+        set_feature(&cpu->env, ARM_FEATURE_NEON);
+        set_feature(&cpu->env, ARM_FEATURE_AARCH64);
+        set_feature(&cpu->env, ARM_FEATURE_V8_AES);
+        set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
+        set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
+        set_feature(&cpu->env, ARM_FEATURE_V8_SHA512);
+        set_feature(&cpu->env, ARM_FEATURE_V8_SHA3);
+        set_feature(&cpu->env, ARM_FEATURE_V8_SM3);
+        set_feature(&cpu->env, ARM_FEATURE_V8_SM4);
+        set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
+        set_feature(&cpu->env, ARM_FEATURE_CRC);
+        set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
+        set_feature(&cpu->env, ARM_FEATURE_V8_FP16);
+        set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
+        /* For usermode -cpu max we can use a larger and more efficient DCZ
+         * blocksize since we don't have to follow what the hardware does.
+         */
+        cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
+        cpu->dcz_blocksize = 7; /*  512 bytes */
 #endif
+    }
+}
 
 typedef struct ARMCPUInfo {
     const char *name;
@@ -247,9 +269,7 @@ typedef struct ARMCPUInfo {
 static const ARMCPUInfo aarch64_cpus[] = {
     { .name = "cortex-a57",         .initfn = aarch64_a57_initfn },
     { .name = "cortex-a53",         .initfn = aarch64_a53_initfn },
-#ifdef CONFIG_USER_ONLY
-    { .name = "any",         .initfn = aarch64_any_initfn },
-#endif
+    { .name = "max",                .initfn = aarch64_max_initfn },
     { .name = NULL }
 };
 
@@ -366,3 +386,44 @@ static void aarch64_cpu_register_types(void)
 }
 
 type_init(aarch64_cpu_register_types)
+
+/* The manual says that when SVE is enabled and VQ is widened the
+ * implementation is allowed to zero the previously inaccessible
+ * portion of the registers.  The corollary to that is that when
+ * SVE is enabled and VQ is narrowed we are also allowed to zero
+ * the now inaccessible portion of the registers.
+ *
+ * The intent of this is that no predicate bit beyond VQ is ever set.
+ * Which means that some operations on predicate registers themselves
+ * may operate on full uint64_t or even unrolled across the maximum
+ * uint64_t[4].  Performing 4 bits of host arithmetic unconditionally
+ * may well be cheaper than conditionals to restrict the operation
+ * to the relevant portion of a uint16_t[16].
+ *
+ * TODO: Need to call this for changes to the real system registers
+ * and EL state changes.
+ */
+void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq)
+{
+    int i, j;
+    uint64_t pmask;
+
+    assert(vq >= 1 && vq <= ARM_MAX_VQ);
+
+    /* Zap the high bits of the zregs.  */
+    for (i = 0; i < 32; i++) {
+        memset(&env->vfp.zregs[i].d[2 * vq], 0, 16 * (ARM_MAX_VQ - vq));
+    }
+
+    /* Zap the high bits of the pregs and ffr.  */
+    pmask = 0;
+    if (vq & 3) {
+        pmask = ~(-1ULL << (16 * (vq & 3)));
+    }
+    for (j = vq / 4; j < ARM_MAX_VQ / 4; j++) {
+        for (i = 0; i < 17; ++i) {
+            env->vfp.pregs[i].p[j] &= pmask;
+        }
+        pmask = 0;
+    }
+}
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index 1219d0062b..ecc39ac295 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -33,6 +33,8 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
 
 static bool cap_has_mp_state;
 
+static ARMHostCPUFeatures arm_host_cpu_features;
+
 int kvm_arm_vcpu_init(CPUState *cs)
 {
     ARMCPU *cpu = ARM_CPU(cs);
@@ -129,44 +131,27 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray)
     }
 }
 
-static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data)
+void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
 {
-    ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc);
+    CPUARMState *env = &cpu->env;
 
-    /* All we really need to set up for the 'host' CPU
-     * is the feature bits -- we rely on the fact that the
-     * various ID register values in ARMCPU are only used for
-     * TCG CPUs.
-     */
-    if (!kvm_arm_get_host_cpu_features(ahcc)) {
-        fprintf(stderr, "Failed to retrieve host CPU features!\n");
-        abort();
+    if (!arm_host_cpu_features.dtb_compatible) {
+        if (!kvm_enabled() ||
+            !kvm_arm_get_host_cpu_features(&arm_host_cpu_features)) {
+            /* We can't report this error yet, so flag that we need to
+             * in arm_cpu_realizefn().
+             */
+            cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE;
+            cpu->host_cpu_probe_failed = true;
+            return;
+        }
     }
-}
 
-static void kvm_arm_host_cpu_initfn(Object *obj)
-{
-    ARMHostCPUClass *ahcc = ARM_HOST_CPU_GET_CLASS(obj);
-    ARMCPU *cpu = ARM_CPU(obj);
-    CPUARMState *env = &cpu->env;
-
-    cpu->kvm_target = ahcc->target;
-    cpu->dtb_compatible = ahcc->dtb_compatible;
-    env->features = ahcc->features;
+    cpu->kvm_target = arm_host_cpu_features.target;
+    cpu->dtb_compatible = arm_host_cpu_features.dtb_compatible;
+    env->features = arm_host_cpu_features.features;
 }
 
-static const TypeInfo host_arm_cpu_type_info = {
-    .name = TYPE_ARM_HOST_CPU,
-#ifdef TARGET_AARCH64
-    .parent = TYPE_AARCH64_CPU,
-#else
-    .parent = TYPE_ARM_CPU,
-#endif
-    .instance_init = kvm_arm_host_cpu_initfn,
-    .class_init = kvm_arm_host_cpu_class_init,
-    .class_size = sizeof(ARMHostCPUClass),
-};
-
 int kvm_arch_init(MachineState *ms, KVMState *s)
 {
     /* For ARM interrupt delivery is always asynchronous,
@@ -182,8 +167,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
 
     cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
 
-    type_register_static(&host_arm_cpu_type_info);
-
     return 0;
 }
 
diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c
index f77c9c494b..1740cda47d 100644
--- a/target/arm/kvm32.c
+++ b/target/arm/kvm32.c
@@ -28,7 +28,7 @@ static inline void set_feature(uint64_t *features, int feature)
     *features |= 1ULL << feature;
 }
 
-bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
+bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
 {
     /* Identify the feature bits corresponding to the host CPU, and
      * fill out the ARMHostCPUClass fields accordingly. To do this
@@ -74,13 +74,13 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
         return false;
     }
 
-    ahcc->target = init.target;
+    ahcf->target = init.target;
 
     /* This is not strictly blessed by the device tree binding docs yet,
      * but in practice the kernel does not care about this string so
      * there is no point maintaining an KVM_ARM_TARGET_* -> string table.
      */
-    ahcc->dtb_compatible = "arm,arm-v7";
+    ahcf->dtb_compatible = "arm,arm-v7";
 
     for (i = 0; i < ARRAY_SIZE(idregs); i++) {
         ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
@@ -132,7 +132,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
         set_feature(&features, ARM_FEATURE_VFP4);
     }
 
-    ahcc->features = features;
+    ahcf->features = features;
 
     return true;
 }
diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c
index ac728494a4..e0b8246283 100644
--- a/target/arm/kvm64.c
+++ b/target/arm/kvm64.c
@@ -443,7 +443,7 @@ static inline void unset_feature(uint64_t *features, int feature)
     *features &= ~(1ULL << feature);
 }
 
-bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
+bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
 {
     /* Identify the feature bits corresponding to the host CPU, and
      * fill out the ARMHostCPUClass fields accordingly. To do this
@@ -471,8 +471,8 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
         return false;
     }
 
-    ahcc->target = init.target;
-    ahcc->dtb_compatible = "arm,arm-v8";
+    ahcf->target = init.target;
+    ahcf->dtb_compatible = "arm,arm-v8";
 
     kvm_arm_destroy_scratch_host_vcpu(fdarray);
 
@@ -486,7 +486,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
     set_feature(&features, ARM_FEATURE_AARCH64);
     set_feature(&features, ARM_FEATURE_PMU);
 
-    ahcc->features = features;
+    ahcf->features = features;
 
     return true;
 }
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index cfb7e5af72..1e2364007d 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -152,20 +152,16 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try,
 void kvm_arm_destroy_scratch_host_vcpu(int *fdarray);
 
 #define TYPE_ARM_HOST_CPU "host-" TYPE_ARM_CPU
-#define ARM_HOST_CPU_CLASS(klass) \
-    OBJECT_CLASS_CHECK(ARMHostCPUClass, (klass), TYPE_ARM_HOST_CPU)
-#define ARM_HOST_CPU_GET_CLASS(obj) \
-    OBJECT_GET_CLASS(ARMHostCPUClass, (obj), TYPE_ARM_HOST_CPU)
-
-typedef struct ARMHostCPUClass {
-    /*< private >*/
-    ARMCPUClass parent_class;
-    /*< public >*/
 
+/**
+ * ARMHostCPUFeatures: information about the host CPU (identified
+ * by asking the host kernel)
+ */
+typedef struct ARMHostCPUFeatures {
     uint64_t features;
     uint32_t target;
     const char *dtb_compatible;
-} ARMHostCPUClass;
+} ARMHostCPUFeatures;
 
 /**
  * kvm_arm_get_host_cpu_features:
@@ -174,8 +170,16 @@ typedef struct ARMHostCPUClass {
  * Probe the capabilities of the host kernel's preferred CPU and fill
  * in the ARMHostCPUClass struct accordingly.
  */
-bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc);
+bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf);
 
+/**
+ * kvm_arm_set_cpu_features_from_host:
+ * @cpu: ARMCPU to set the features for
+ *
+ * Set up the ARMCPU struct fields up to match the information probed
+ * from the host CPU.
+ */
+void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu);
 
 /**
  * kvm_arm_sync_mpstate_to_kvm
@@ -200,6 +204,15 @@ void kvm_arm_pmu_init(CPUState *cs);
 
 #else
 
+static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
+{
+    /* This should never actually be called in the "not KVM" case,
+     * but set up the fields to indicate an error anyway.
+     */
+    cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE;
+    cpu->host_cpu_probe_failed = true;
+}
+
 static inline int kvm_arm_vgic_probe(void)
 {
     return 0;
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index cdb9b50462..62cbb0dff1 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -557,3 +557,38 @@ void HELPER(fscale)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1)
 {
     res->d = floatx80_scale(val1->d, val0->d, &env->fp_status);
 }
+
+void HELPER(flognp1)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+    res->d = floatx80_lognp1(val->d, &env->fp_status);
+}
+
+void HELPER(flogn)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+    res->d = floatx80_logn(val->d, &env->fp_status);
+}
+
+void HELPER(flog10)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+    res->d = floatx80_log10(val->d, &env->fp_status);
+}
+
+void HELPER(flog2)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+    res->d = floatx80_log2(val->d, &env->fp_status);
+}
+
+void HELPER(fetox)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+    res->d = floatx80_etox(val->d, &env->fp_status);
+}
+
+void HELPER(ftwotox)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+    res->d = floatx80_twotox(val->d, &env->fp_status);
+}
+
+void HELPER(ftentox)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+    res->d = floatx80_tentox(val->d, &env->fp_status);
+}
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index c348dced3a..9a9734c196 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -68,6 +68,13 @@ DEF_HELPER_4(frem, void, env, fp, fp, fp)
 DEF_HELPER_3(fgetexp, void, env, fp, fp)
 DEF_HELPER_3(fgetman, void, env, fp, fp)
 DEF_HELPER_4(fscale, void, env, fp, fp, fp)
+DEF_HELPER_3(flognp1, void, env, fp, fp)
+DEF_HELPER_3(flogn, void, env, fp, fp)
+DEF_HELPER_3(flog10, void, env, fp, fp)
+DEF_HELPER_3(flog2, void, env, fp, fp)
+DEF_HELPER_3(fetox, void, env, fp, fp)
+DEF_HELPER_3(ftwotox, void, env, fp, fp)
+DEF_HELPER_3(ftentox, void, env, fp, fp)
 
 DEF_HELPER_3(mac_move, void, env, i32, i32)
 DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c
index 9cb141900c..4bd5b9e6b7 100644
--- a/target/m68k/softfloat.c
+++ b/target/m68k/softfloat.c
@@ -21,6 +21,7 @@
 #include "qemu/osdep.h"
 #include "softfloat.h"
 #include "fpu/softfloat-macros.h"
+#include "softfloat_fpsp_tables.h"
 
 static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status)
 {
@@ -247,3 +248,1021 @@ floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status)
     return roundAndPackFloatx80(status->floatx80_rounding_precision,
                                 aSign, aExp, aSig, 0, status);
 }
+
+floatx80 floatx80_move(floatx80 a, float_status *status)
+{
+    flag aSign;
+    int32_t aExp;
+    uint64_t aSig;
+
+    aSig = extractFloatx80Frac(a);
+    aExp = extractFloatx80Exp(a);
+    aSign = extractFloatx80Sign(a);
+
+    if (aExp == 0x7FFF) {
+        if ((uint64_t)(aSig << 1)) {
+            return propagateFloatx80NaNOneArg(a, status);
+        }
+        return a;
+    }
+    if (aExp == 0) {
+        if (aSig == 0) {
+            return a;
+        }
+        normalizeRoundAndPackFloatx80(status->floatx80_rounding_precision,
+                                      aSign, aExp, aSig, 0, status);
+    }
+    return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign,
+                                aExp, aSig, 0, status);
+}
+
+/*----------------------------------------------------------------------------
+| Algorithms for transcendental functions supported by MC68881 and MC68882
+| mathematical coprocessors. The functions are derived from FPSP library.
+*----------------------------------------------------------------------------*/
+
+#define one_exp     0x3FFF
+#define one_sig     LIT64(0x8000000000000000)
+
+/*----------------------------------------------------------------------------
+ | Function for compactifying extended double-precision floating point values.
+ *----------------------------------------------------------------------------*/
+
+static int32_t floatx80_make_compact(int32_t aExp, uint64_t aSig)
+{
+    return (aExp << 16) | (aSig >> 48);
+}
+
+/*----------------------------------------------------------------------------
+ | Log base e of x plus 1
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_lognp1(floatx80 a, float_status *status)
+{
+    flag aSign;
+    int32_t aExp;
+    uint64_t aSig, fSig;
+
+    int8_t user_rnd_mode, user_rnd_prec;
+
+    int32_t compact, j, k;
+    floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu;
+
+    aSig = extractFloatx80Frac(a);
+    aExp = extractFloatx80Exp(a);
+    aSign = extractFloatx80Sign(a);
+
+    if (aExp == 0x7FFF) {
+        if ((uint64_t) (aSig << 1)) {
+            propagateFloatx80NaNOneArg(a, status);
+        }
+        if (aSign) {
+            float_raise(float_flag_invalid, status);
+            return floatx80_default_nan(status);
+        }
+        return packFloatx80(0, floatx80_infinity.high, floatx80_infinity.low);
+    }
+
+    if (aExp == 0 && aSig == 0) {
+        return packFloatx80(aSign, 0, 0);
+    }
+
+    if (aSign && aExp >= one_exp) {
+        if (aExp == one_exp && aSig == one_sig) {
+            float_raise(float_flag_divbyzero, status);
+            packFloatx80(aSign, floatx80_infinity.high, floatx80_infinity.low);
+        }
+        float_raise(float_flag_invalid, status);
+        return floatx80_default_nan(status);
+    }
+
+    if (aExp < 0x3f99 || (aExp == 0x3f99 && aSig == one_sig)) {
+        /* <= min threshold */
+        float_raise(float_flag_inexact, status);
+        return floatx80_move(a, status);
+    }
+
+    user_rnd_mode = status->float_rounding_mode;
+    user_rnd_prec = status->floatx80_rounding_precision;
+    status->float_rounding_mode = float_round_nearest_even;
+    status->floatx80_rounding_precision = 80;
+
+    compact = floatx80_make_compact(aExp, aSig);
+
+    fp0 = a; /* Z */
+    fp1 = a;
+
+    fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
+                       status), status); /* X = (1+Z) */
+
+    aExp = extractFloatx80Exp(fp0);
+    aSig = extractFloatx80Frac(fp0);
+
+    compact = floatx80_make_compact(aExp, aSig);
+
+    if (compact < 0x3FFE8000 || compact > 0x3FFFC000) {
+        /* |X| < 1/2 or |X| > 3/2 */
+        k = aExp - 0x3FFF;
+        fp1 = int32_to_floatx80(k, status);
+
+        fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000);
+        j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
+
+        f = packFloatx80(0, 0x3FFF, fSig); /* F */
+        fp0 = packFloatx80(0, 0x3FFF, aSig); /* Y */
+
+        fp0 = floatx80_sub(fp0, f, status); /* Y-F */
+
+    lp1cont1:
+        /* LP1CONT1 */
+        fp0 = floatx80_mul(fp0, log_tbl[j], status); /* FP0 IS U = (Y-F)/F */
+        logof2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC));
+        klog2 = floatx80_mul(fp1, logof2, status); /* FP1 IS K*LOG2 */
+        fp2 = floatx80_mul(fp0, fp0, status); /* FP2 IS V=U*U */
+
+        fp3 = fp2;
+        fp1 = fp2;
+
+        fp1 = floatx80_mul(fp1, float64_to_floatx80(
+                           make_float64(0x3FC2499AB5E4040B), status),
+                           status); /* V*A6 */
+        fp2 = floatx80_mul(fp2, float64_to_floatx80(
+                           make_float64(0xBFC555B5848CB7DB), status),
+                           status); /* V*A5 */
+        fp1 = floatx80_add(fp1, float64_to_floatx80(
+                           make_float64(0x3FC99999987D8730), status),
+                           status); /* A4+V*A6 */
+        fp2 = floatx80_add(fp2, float64_to_floatx80(
+                           make_float64(0xBFCFFFFFFF6F7E97), status),
+                           status); /* A3+V*A5 */
+        fp1 = floatx80_mul(fp1, fp3, status); /* V*(A4+V*A6) */
+        fp2 = floatx80_mul(fp2, fp3, status); /* V*(A3+V*A5) */
+        fp1 = floatx80_add(fp1, float64_to_floatx80(
+                           make_float64(0x3FD55555555555A4), status),
+                           status); /* A2+V*(A4+V*A6) */
+        fp2 = floatx80_add(fp2, float64_to_floatx80(
+                           make_float64(0xBFE0000000000008), status),
+                           status); /* A1+V*(A3+V*A5) */
+        fp1 = floatx80_mul(fp1, fp3, status); /* V*(A2+V*(A4+V*A6)) */
+        fp2 = floatx80_mul(fp2, fp3, status); /* V*(A1+V*(A3+V*A5)) */
+        fp1 = floatx80_mul(fp1, fp0, status); /* U*V*(A2+V*(A4+V*A6)) */
+        fp0 = floatx80_add(fp0, fp2, status); /* U+V*(A1+V*(A3+V*A5)) */
+
+        fp1 = floatx80_add(fp1, log_tbl[j + 1],
+                           status); /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */
+        fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS LOG(F) + LOG(1+U) */
+
+        status->float_rounding_mode = user_rnd_mode;
+        status->floatx80_rounding_precision = user_rnd_prec;
+
+        a = floatx80_add(fp0, klog2, status);
+
+        float_raise(float_flag_inexact, status);
+
+        return a;
+    } else if (compact < 0x3FFEF07D || compact > 0x3FFF8841) {
+        /* |X| < 1/16 or |X| > -1/16 */
+        /* LP1CARE */
+        fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000);
+        f = packFloatx80(0, 0x3FFF, fSig); /* F */
+        j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
+
+        if (compact >= 0x3FFF8000) { /* 1+Z >= 1 */
+            /* KISZERO */
+            fp0 = floatx80_sub(float32_to_floatx80(make_float32(0x3F800000),
+                               status), f, status); /* 1-F */
+            fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS Y-F = (1-F)+Z */
+            fp1 = packFloatx80(0, 0, 0); /* K = 0 */
+        } else {
+            /* KISNEG */
+            fp0 = floatx80_sub(float32_to_floatx80(make_float32(0x40000000),
+                               status), f, status); /* 2-F */
+            fp1 = floatx80_add(fp1, fp1, status); /* 2Z */
+            fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS Y-F = (2-F)+2Z */
+            fp1 = packFloatx80(1, one_exp, one_sig); /* K = -1 */
+        }
+        goto lp1cont1;
+    } else {
+        /* LP1ONE16 */
+        fp1 = floatx80_add(fp1, fp1, status); /* FP1 IS 2Z */
+        fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
+                           status), status); /* FP0 IS 1+X */
+
+        /* LP1CONT2 */
+        fp1 = floatx80_div(fp1, fp0, status); /* U */
+        saveu = fp1;
+        fp0 = floatx80_mul(fp1, fp1, status); /* FP0 IS V = U*U */
+        fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS W = V*V */
+
+        fp3 = float64_to_floatx80(make_float64(0x3F175496ADD7DAD6),
+                                  status); /* B5 */
+        fp2 = float64_to_floatx80(make_float64(0x3F3C71C2FE80C7E0),
+                                  status); /* B4 */
+        fp3 = floatx80_mul(fp3, fp1, status); /* W*B5 */
+        fp2 = floatx80_mul(fp2, fp1, status); /* W*B4 */
+        fp3 = floatx80_add(fp3, float64_to_floatx80(
+                           make_float64(0x3F624924928BCCFF), status),
+                           status); /* B3+W*B5 */
+        fp2 = floatx80_add(fp2, float64_to_floatx80(
+                           make_float64(0x3F899999999995EC), status),
+                           status); /* B2+W*B4 */
+        fp1 = floatx80_mul(fp1, fp3, status); /* W*(B3+W*B5) */
+        fp2 = floatx80_mul(fp2, fp0, status); /* V*(B2+W*B4) */
+        fp1 = floatx80_add(fp1, float64_to_floatx80(
+                           make_float64(0x3FB5555555555555), status),
+                           status); /* B1+W*(B3+W*B5) */
+
+        fp0 = floatx80_mul(fp0, saveu, status); /* FP0 IS U*V */
+        fp1 = floatx80_add(fp1, fp2,
+                           status); /* B1+W*(B3+W*B5) + V*(B2+W*B4) */
+        fp0 = floatx80_mul(fp0, fp1,
+                           status); /* U*V*([B1+W*(B3+W*B5)] + [V*(B2+W*B4)]) */
+
+        status->float_rounding_mode = user_rnd_mode;
+        status->floatx80_rounding_precision = user_rnd_prec;
+
+        a = floatx80_add(fp0, saveu, status);
+
+        /*if (!floatx80_is_zero(a)) { */
+            float_raise(float_flag_inexact, status);
+        /*} */
+
+        return a;
+    }
+}
+
+/*----------------------------------------------------------------------------
+ | Log base e
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_logn(floatx80 a, float_status *status)
+{
+    flag aSign;
+    int32_t aExp;
+    uint64_t aSig, fSig;
+
+    int8_t user_rnd_mode, user_rnd_prec;
+
+    int32_t compact, j, k, adjk;
+    floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu;
+
+    aSig = extractFloatx80Frac(a);
+    aExp = extractFloatx80Exp(a);
+    aSign = extractFloatx80Sign(a);
+
+    if (aExp == 0x7FFF) {
+        if ((uint64_t) (aSig << 1)) {
+            propagateFloatx80NaNOneArg(a, status);
+        }
+        if (aSign == 0) {
+            return packFloatx80(0, floatx80_infinity.high,
+                                floatx80_infinity.low);
+        }
+    }
+
+    adjk = 0;
+
+    if (aExp == 0) {
+        if (aSig == 0) { /* zero */
+            float_raise(float_flag_divbyzero, status);
+            return packFloatx80(1, floatx80_infinity.high,
+                                floatx80_infinity.low);
+        }
+        if ((aSig & one_sig) == 0) { /* denormal */
+            normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
+            adjk = -100;
+            aExp += 100;
+            a = packFloatx80(aSign, aExp, aSig);
+        }
+    }
+
+    if (aSign) {
+        float_raise(float_flag_invalid, status);
+        return floatx80_default_nan(status);
+    }
+
+    user_rnd_mode = status->float_rounding_mode;
+    user_rnd_prec = status->floatx80_rounding_precision;
+    status->float_rounding_mode = float_round_nearest_even;
+    status->floatx80_rounding_precision = 80;
+
+    compact = floatx80_make_compact(aExp, aSig);
+
+    if (compact < 0x3FFEF07D || compact > 0x3FFF8841) {
+        /* |X| < 15/16 or |X| > 17/16 */
+        k = aExp - 0x3FFF;
+        k += adjk;
+        fp1 = int32_to_floatx80(k, status);
+
+        fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000);
+        j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
+
+        f = packFloatx80(0, 0x3FFF, fSig); /* F */
+        fp0 = packFloatx80(0, 0x3FFF, aSig); /* Y */
+
+        fp0 = floatx80_sub(fp0, f, status); /* Y-F */
+
+        /* LP1CONT1 */
+        fp0 = floatx80_mul(fp0, log_tbl[j], status); /* FP0 IS U = (Y-F)/F */
+        logof2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC));
+        klog2 = floatx80_mul(fp1, logof2, status); /* FP1 IS K*LOG2 */
+        fp2 = floatx80_mul(fp0, fp0, status); /* FP2 IS V=U*U */
+
+        fp3 = fp2;
+        fp1 = fp2;
+
+        fp1 = floatx80_mul(fp1, float64_to_floatx80(
+                           make_float64(0x3FC2499AB5E4040B), status),
+                           status); /* V*A6 */
+        fp2 = floatx80_mul(fp2, float64_to_floatx80(
+                           make_float64(0xBFC555B5848CB7DB), status),
+                           status); /* V*A5 */
+        fp1 = floatx80_add(fp1, float64_to_floatx80(
+                           make_float64(0x3FC99999987D8730), status),
+                           status); /* A4+V*A6 */
+        fp2 = floatx80_add(fp2, float64_to_floatx80(
+                           make_float64(0xBFCFFFFFFF6F7E97), status),
+                           status); /* A3+V*A5 */
+        fp1 = floatx80_mul(fp1, fp3, status); /* V*(A4+V*A6) */
+        fp2 = floatx80_mul(fp2, fp3, status); /* V*(A3+V*A5) */
+        fp1 = floatx80_add(fp1, float64_to_floatx80(
+                           make_float64(0x3FD55555555555A4), status),
+                           status); /* A2+V*(A4+V*A6) */
+        fp2 = floatx80_add(fp2, float64_to_floatx80(
+                           make_float64(0xBFE0000000000008), status),
+                           status); /* A1+V*(A3+V*A5) */
+        fp1 = floatx80_mul(fp1, fp3, status); /* V*(A2+V*(A4+V*A6)) */
+        fp2 = floatx80_mul(fp2, fp3, status); /* V*(A1+V*(A3+V*A5)) */
+        fp1 = floatx80_mul(fp1, fp0, status); /* U*V*(A2+V*(A4+V*A6)) */
+        fp0 = floatx80_add(fp0, fp2, status); /* U+V*(A1+V*(A3+V*A5)) */
+
+        fp1 = floatx80_add(fp1, log_tbl[j + 1],
+                           status); /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */
+        fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS LOG(F) + LOG(1+U) */
+
+        status->float_rounding_mode = user_rnd_mode;
+        status->floatx80_rounding_precision = user_rnd_prec;
+
+        a = floatx80_add(fp0, klog2, status);
+
+        float_raise(float_flag_inexact, status);
+
+        return a;
+    } else { /* |X-1| >= 1/16 */
+        fp0 = a;
+        fp1 = a;
+        fp1 = floatx80_sub(fp1, float32_to_floatx80(make_float32(0x3F800000),
+                           status), status); /* FP1 IS X-1 */
+        fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
+                           status), status); /* FP0 IS X+1 */
+        fp1 = floatx80_add(fp1, fp1, status); /* FP1 IS 2(X-1) */
+
+        /* LP1CONT2 */
+        fp1 = floatx80_div(fp1, fp0, status); /* U */
+        saveu = fp1;
+        fp0 = floatx80_mul(fp1, fp1, status); /* FP0 IS V = U*U */
+        fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS W = V*V */
+
+        fp3 = float64_to_floatx80(make_float64(0x3F175496ADD7DAD6),
+                                  status); /* B5 */
+        fp2 = float64_to_floatx80(make_float64(0x3F3C71C2FE80C7E0),
+                                  status); /* B4 */
+        fp3 = floatx80_mul(fp3, fp1, status); /* W*B5 */
+        fp2 = floatx80_mul(fp2, fp1, status); /* W*B4 */
+        fp3 = floatx80_add(fp3, float64_to_floatx80(
+                           make_float64(0x3F624924928BCCFF), status),
+                           status); /* B3+W*B5 */
+        fp2 = floatx80_add(fp2, float64_to_floatx80(
+                           make_float64(0x3F899999999995EC), status),
+                           status); /* B2+W*B4 */
+        fp1 = floatx80_mul(fp1, fp3, status); /* W*(B3+W*B5) */
+        fp2 = floatx80_mul(fp2, fp0, status); /* V*(B2+W*B4) */
+        fp1 = floatx80_add(fp1, float64_to_floatx80(
+                           make_float64(0x3FB5555555555555), status),
+                           status); /* B1+W*(B3+W*B5) */
+
+        fp0 = floatx80_mul(fp0, saveu, status); /* FP0 IS U*V */
+        fp1 = floatx80_add(fp1, fp2, status); /* B1+W*(B3+W*B5) + V*(B2+W*B4) */
+        fp0 = floatx80_mul(fp0, fp1,
+                           status); /* U*V*([B1+W*(B3+W*B5)] + [V*(B2+W*B4)]) */
+
+        status->float_rounding_mode = user_rnd_mode;
+        status->floatx80_rounding_precision = user_rnd_prec;
+
+        a = floatx80_add(fp0, saveu, status);
+
+        /*if (!floatx80_is_zero(a)) { */
+            float_raise(float_flag_inexact, status);
+        /*} */
+
+        return a;
+    }
+}
+
+/*----------------------------------------------------------------------------
+ | Log base 10
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_log10(floatx80 a, float_status *status)
+{
+    flag aSign;
+    int32_t aExp;
+    uint64_t aSig;
+
+    int8_t user_rnd_mode, user_rnd_prec;
+
+    floatx80 fp0, fp1;
+
+    aSig = extractFloatx80Frac(a);
+    aExp = extractFloatx80Exp(a);
+    aSign = extractFloatx80Sign(a);
+
+    if (aExp == 0x7FFF) {
+        if ((uint64_t) (aSig << 1)) {
+            propagateFloatx80NaNOneArg(a, status);
+        }
+        if (aSign == 0) {
+            return packFloatx80(0, floatx80_infinity.high,
+                                floatx80_infinity.low);
+        }
+    }
+
+    if (aExp == 0 && aSig == 0) {
+        float_raise(float_flag_divbyzero, status);
+        return packFloatx80(1, floatx80_infinity.high,
+                            floatx80_infinity.low);
+    }
+
+    if (aSign) {
+        float_raise(float_flag_invalid, status);
+        return floatx80_default_nan(status);
+    }
+
+    user_rnd_mode = status->float_rounding_mode;
+    user_rnd_prec = status->floatx80_rounding_precision;
+    status->float_rounding_mode = float_round_nearest_even;
+    status->floatx80_rounding_precision = 80;
+
+    fp0 = floatx80_logn(a, status);
+    fp1 = packFloatx80(0, 0x3FFD, LIT64(0xDE5BD8A937287195)); /* INV_L10 */
+
+    status->float_rounding_mode = user_rnd_mode;
+    status->floatx80_rounding_precision = user_rnd_prec;
+
+    a = floatx80_mul(fp0, fp1, status); /* LOGN(X)*INV_L10 */
+
+    float_raise(float_flag_inexact, status);
+
+    return a;
+}
+
+/*----------------------------------------------------------------------------
+ | Log base 2
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_log2(floatx80 a, float_status *status)
+{
+    flag aSign;
+    int32_t aExp;
+    uint64_t aSig;
+
+    int8_t user_rnd_mode, user_rnd_prec;
+
+    floatx80 fp0, fp1;
+
+    aSig = extractFloatx80Frac(a);
+    aExp = extractFloatx80Exp(a);
+    aSign = extractFloatx80Sign(a);
+
+    if (aExp == 0x7FFF) {
+        if ((uint64_t) (aSig << 1)) {
+            propagateFloatx80NaNOneArg(a, status);
+        }
+        if (aSign == 0) {
+            return packFloatx80(0, floatx80_infinity.high,
+                                floatx80_infinity.low);
+        }
+    }
+
+    if (aExp == 0) {
+        if (aSig == 0) {
+            float_raise(float_flag_divbyzero, status);
+            return packFloatx80(1, floatx80_infinity.high,
+                                floatx80_infinity.low);
+        }
+        normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
+    }
+
+    if (aSign) {
+        float_raise(float_flag_invalid, status);
+        return floatx80_default_nan(status);
+    }
+
+    user_rnd_mode = status->float_rounding_mode;
+    user_rnd_prec = status->floatx80_rounding_precision;
+    status->float_rounding_mode = float_round_nearest_even;
+    status->floatx80_rounding_precision = 80;
+
+    if (aSig == one_sig) { /* X is 2^k */
+        status->float_rounding_mode = user_rnd_mode;
+        status->floatx80_rounding_precision = user_rnd_prec;
+
+        a = int32_to_floatx80(aExp - 0x3FFF, status);
+    } else {
+        fp0 = floatx80_logn(a, status);
+        fp1 = packFloatx80(0, 0x3FFF, LIT64(0xB8AA3B295C17F0BC)); /* INV_L2 */
+
+        status->float_rounding_mode = user_rnd_mode;
+        status->floatx80_rounding_precision = user_rnd_prec;
+
+        a = floatx80_mul(fp0, fp1, status); /* LOGN(X)*INV_L2 */
+    }
+
+    float_raise(float_flag_inexact, status);
+
+    return a;
+}
+
+/*----------------------------------------------------------------------------
+ | e to x
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_etox(floatx80 a, float_status *status)
+{
+    flag aSign;
+    int32_t aExp;
+    uint64_t aSig;
+
+    int8_t user_rnd_mode, user_rnd_prec;
+
+    int32_t compact, n, j, k, m, m1;
+    floatx80 fp0, fp1, fp2, fp3, l2, scale, adjscale;
+    flag adjflag;
+
+    aSig = extractFloatx80Frac(a);
+    aExp = extractFloatx80Exp(a);
+    aSign = extractFloatx80Sign(a);
+
+    if (aExp == 0x7FFF) {
+        if ((uint64_t) (aSig << 1)) {
+            return propagateFloatx80NaNOneArg(a, status);
+        }
+        if (aSign) {
+            return packFloatx80(0, 0, 0);
+        }
+        return packFloatx80(0, floatx80_infinity.high,
+                            floatx80_infinity.low);
+    }
+
+    if (aExp == 0 && aSig == 0) {
+        return packFloatx80(0, one_exp, one_sig);
+    }
+
+    user_rnd_mode = status->float_rounding_mode;
+    user_rnd_prec = status->floatx80_rounding_precision;
+    status->float_rounding_mode = float_round_nearest_even;
+    status->floatx80_rounding_precision = 80;
+
+    adjflag = 0;
+
+    if (aExp >= 0x3FBE) { /* |X| >= 2^(-65) */
+        compact = floatx80_make_compact(aExp, aSig);
+
+        if (compact < 0x400CB167) { /* |X| < 16380 log2 */
+            fp0 = a;
+            fp1 = a;
+            fp0 = floatx80_mul(fp0, float32_to_floatx80(
+                               make_float32(0x42B8AA3B), status),
+                               status); /* 64/log2 * X */
+            adjflag = 0;
+            n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
+            fp0 = int32_to_floatx80(n, status);
+
+            j = n & 0x3F; /* J = N mod 64 */
+            m = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
+            if (n < 0 && j) {
+                /* arithmetic right shift is division and
+                 * round towards minus infinity
+                 */
+                m--;
+            }
+            m += 0x3FFF; /* biased exponent of 2^(M) */
+
+        expcont1:
+            fp2 = fp0; /* N */
+            fp0 = floatx80_mul(fp0, float32_to_floatx80(
+                               make_float32(0xBC317218), status),
+                               status); /* N * L1, L1 = lead(-log2/64) */
+            l2 = packFloatx80(0, 0x3FDC, LIT64(0x82E308654361C4C6));
+            fp2 = floatx80_mul(fp2, l2, status); /* N * L2, L1+L2 = -log2/64 */
+            fp0 = floatx80_add(fp0, fp1, status); /* X + N*L1 */
+            fp0 = floatx80_add(fp0, fp2, status); /* R */
+
+            fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+            fp2 = float32_to_floatx80(make_float32(0x3AB60B70),
+                                      status); /* A5 */
+            fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*A5 */
+            fp3 = floatx80_mul(float32_to_floatx80(make_float32(0x3C088895),
+                               status), fp1,
+                               status); /* fp3 is S*A4 */
+            fp2 = floatx80_add(fp2, float64_to_floatx80(make_float64(
+                               0x3FA5555555554431), status),
+                               status); /* fp2 is A3+S*A5 */
+            fp3 = floatx80_add(fp3, float64_to_floatx80(make_float64(
+                               0x3FC5555555554018), status),
+                               status); /* fp3 is A2+S*A4 */
+            fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*(A3+S*A5) */
+            fp3 = floatx80_mul(fp3, fp1, status); /* fp3 is S*(A2+S*A4) */
+            fp2 = floatx80_add(fp2, float32_to_floatx80(
+                               make_float32(0x3F000000), status),
+                               status); /* fp2 is A1+S*(A3+S*A5) */
+            fp3 = floatx80_mul(fp3, fp0, status); /* fp3 IS R*S*(A2+S*A4) */
+            fp2 = floatx80_mul(fp2, fp1,
+                               status); /* fp2 IS S*(A1+S*(A3+S*A5)) */
+            fp0 = floatx80_add(fp0, fp3, status); /* fp0 IS R+R*S*(A2+S*A4) */
+            fp0 = floatx80_add(fp0, fp2, status); /* fp0 IS EXP(R) - 1 */
+
+            fp1 = exp_tbl[j];
+            fp0 = floatx80_mul(fp0, fp1, status); /* 2^(J/64)*(Exp(R)-1) */
+            fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], status),
+                               status); /* accurate 2^(J/64) */
+            fp0 = floatx80_add(fp0, fp1,
+                               status); /* 2^(J/64) + 2^(J/64)*(Exp(R)-1) */
+
+            scale = packFloatx80(0, m, one_sig);
+            if (adjflag) {
+                adjscale = packFloatx80(0, m1, one_sig);
+                fp0 = floatx80_mul(fp0, adjscale, status);
+            }
+
+            status->float_rounding_mode = user_rnd_mode;
+            status->floatx80_rounding_precision = user_rnd_prec;
+
+            a = floatx80_mul(fp0, scale, status);
+
+            float_raise(float_flag_inexact, status);
+
+            return a;
+        } else { /* |X| >= 16380 log2 */
+            if (compact > 0x400CB27C) { /* |X| >= 16480 log2 */
+                status->float_rounding_mode = user_rnd_mode;
+                status->floatx80_rounding_precision = user_rnd_prec;
+                if (aSign) {
+                    a = roundAndPackFloatx80(
+                                           status->floatx80_rounding_precision,
+                                           0, -0x1000, aSig, 0, status);
+                } else {
+                    a = roundAndPackFloatx80(
+                                           status->floatx80_rounding_precision,
+                                           0, 0x8000, aSig, 0, status);
+                }
+                float_raise(float_flag_inexact, status);
+
+                return a;
+            } else {
+                fp0 = a;
+                fp1 = a;
+                fp0 = floatx80_mul(fp0, float32_to_floatx80(
+                                   make_float32(0x42B8AA3B), status),
+                                   status); /* 64/log2 * X */
+                adjflag = 1;
+                n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
+                fp0 = int32_to_floatx80(n, status);
+
+                j = n & 0x3F; /* J = N mod 64 */
+                /* NOTE: this is really arithmetic right shift by 6 */
+                k = n / 64;
+                if (n < 0 && j) {
+                    /* arithmetic right shift is division and
+                     * round towards minus infinity
+                     */
+                    k--;
+                }
+                /* NOTE: this is really arithmetic right shift by 1 */
+                m1 = k / 2;
+                if (k < 0 && (k & 1)) {
+                    /* arithmetic right shift is division and
+                     * round towards minus infinity
+                     */
+                    m1--;
+                }
+                m = k - m1;
+                m1 += 0x3FFF; /* biased exponent of 2^(M1) */
+                m += 0x3FFF; /* biased exponent of 2^(M) */
+
+                goto expcont1;
+            }
+        }
+    } else { /* |X| < 2^(-65) */
+        status->float_rounding_mode = user_rnd_mode;
+        status->floatx80_rounding_precision = user_rnd_prec;
+
+        a = floatx80_add(a, float32_to_floatx80(make_float32(0x3F800000),
+                         status), status); /* 1 + X */
+
+        float_raise(float_flag_inexact, status);
+
+        return a;
+    }
+}
+
+/*----------------------------------------------------------------------------
+ | 2 to x
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_twotox(floatx80 a, float_status *status)
+{
+    flag aSign;
+    int32_t aExp;
+    uint64_t aSig;
+
+    int8_t user_rnd_mode, user_rnd_prec;
+
+    int32_t compact, n, j, l, m, m1;
+    floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2;
+
+    aSig = extractFloatx80Frac(a);
+    aExp = extractFloatx80Exp(a);
+    aSign = extractFloatx80Sign(a);
+
+    if (aExp == 0x7FFF) {
+        if ((uint64_t) (aSig << 1)) {
+            return propagateFloatx80NaNOneArg(a, status);
+        }
+        if (aSign) {
+            return packFloatx80(0, 0, 0);
+        }
+        return packFloatx80(0, floatx80_infinity.high,
+                            floatx80_infinity.low);
+    }
+
+    if (aExp == 0 && aSig == 0) {
+        return packFloatx80(0, one_exp, one_sig);
+    }
+
+    user_rnd_mode = status->float_rounding_mode;
+    user_rnd_prec = status->floatx80_rounding_precision;
+    status->float_rounding_mode = float_round_nearest_even;
+    status->floatx80_rounding_precision = 80;
+
+    fp0 = a;
+
+    compact = floatx80_make_compact(aExp, aSig);
+
+    if (compact < 0x3FB98000 || compact > 0x400D80C0) {
+        /* |X| > 16480 or |X| < 2^(-70) */
+        if (compact > 0x3FFF8000) { /* |X| > 16480 */
+            status->float_rounding_mode = user_rnd_mode;
+            status->floatx80_rounding_precision = user_rnd_prec;
+
+            if (aSign) {
+                return roundAndPackFloatx80(status->floatx80_rounding_precision,
+                                            0, -0x1000, aSig, 0, status);
+            } else {
+                return roundAndPackFloatx80(status->floatx80_rounding_precision,
+                                            0, 0x8000, aSig, 0, status);
+            }
+        } else { /* |X| < 2^(-70) */
+            status->float_rounding_mode = user_rnd_mode;
+            status->floatx80_rounding_precision = user_rnd_prec;
+
+            a = floatx80_add(fp0, float32_to_floatx80(
+                             make_float32(0x3F800000), status),
+                             status); /* 1 + X */
+
+            float_raise(float_flag_inexact, status);
+
+            return a;
+        }
+    } else { /* 2^(-70) <= |X| <= 16480 */
+        fp1 = fp0; /* X */
+        fp1 = floatx80_mul(fp1, float32_to_floatx80(
+                           make_float32(0x42800000), status),
+                           status); /* X * 64 */
+        n = floatx80_to_int32(fp1, status);
+        fp1 = int32_to_floatx80(n, status);
+        j = n & 0x3F;
+        l = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
+        if (n < 0 && j) {
+            /* arithmetic right shift is division and
+             * round towards minus infinity
+             */
+            l--;
+        }
+        m = l / 2; /* NOTE: this is really arithmetic right shift by 1 */
+        if (l < 0 && (l & 1)) {
+            /* arithmetic right shift is division and
+             * round towards minus infinity
+             */
+            m--;
+        }
+        m1 = l - m;
+        m1 += 0x3FFF; /* ADJFACT IS 2^(M') */
+
+        adjfact = packFloatx80(0, m1, one_sig);
+        fact1 = exp2_tbl[j];
+        fact1.high += m;
+        fact2.high = exp2_tbl2[j] >> 16;
+        fact2.high += m;
+        fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF);
+        fact2.low <<= 48;
+
+        fp1 = floatx80_mul(fp1, float32_to_floatx80(
+                           make_float32(0x3C800000), status),
+                           status); /* (1/64)*N */
+        fp0 = floatx80_sub(fp0, fp1, status); /* X - (1/64)*INT(64 X) */
+        fp2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC)); /* LOG2 */
+        fp0 = floatx80_mul(fp0, fp2, status); /* R */
+
+        /* EXPR */
+        fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+        fp2 = float64_to_floatx80(make_float64(0x3F56C16D6F7BD0B2),
+                                  status); /* A5 */
+        fp3 = float64_to_floatx80(make_float64(0x3F811112302C712C),
+                                  status); /* A4 */
+        fp2 = floatx80_mul(fp2, fp1, status); /* S*A5 */
+        fp3 = floatx80_mul(fp3, fp1, status); /* S*A4 */
+        fp2 = floatx80_add(fp2, float64_to_floatx80(
+                           make_float64(0x3FA5555555554CC1), status),
+                           status); /* A3+S*A5 */
+        fp3 = floatx80_add(fp3, float64_to_floatx80(
+                           make_float64(0x3FC5555555554A54), status),
+                           status); /* A2+S*A4 */
+        fp2 = floatx80_mul(fp2, fp1, status); /* S*(A3+S*A5) */
+        fp3 = floatx80_mul(fp3, fp1, status); /* S*(A2+S*A4) */
+        fp2 = floatx80_add(fp2, float64_to_floatx80(
+                           make_float64(0x3FE0000000000000), status),
+                           status); /* A1+S*(A3+S*A5) */
+        fp3 = floatx80_mul(fp3, fp0, status); /* R*S*(A2+S*A4) */
+
+        fp2 = floatx80_mul(fp2, fp1, status); /* S*(A1+S*(A3+S*A5)) */
+        fp0 = floatx80_add(fp0, fp3, status); /* R+R*S*(A2+S*A4) */
+        fp0 = floatx80_add(fp0, fp2, status); /* EXP(R) - 1 */
+
+        fp0 = floatx80_mul(fp0, fact1, status);
+        fp0 = floatx80_add(fp0, fact2, status);
+        fp0 = floatx80_add(fp0, fact1, status);
+
+        status->float_rounding_mode = user_rnd_mode;
+        status->floatx80_rounding_precision = user_rnd_prec;
+
+        a = floatx80_mul(fp0, adjfact, status);
+
+        float_raise(float_flag_inexact, status);
+
+        return a;
+    }
+}
+
+/*----------------------------------------------------------------------------
+ | 10 to x
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_tentox(floatx80 a, float_status *status)
+{
+    flag aSign;
+    int32_t aExp;
+    uint64_t aSig;
+
+    int8_t user_rnd_mode, user_rnd_prec;
+
+    int32_t compact, n, j, l, m, m1;
+    floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2;
+
+    aSig = extractFloatx80Frac(a);
+    aExp = extractFloatx80Exp(a);
+    aSign = extractFloatx80Sign(a);
+
+    if (aExp == 0x7FFF) {
+        if ((uint64_t) (aSig << 1)) {
+            return propagateFloatx80NaNOneArg(a, status);
+        }
+        if (aSign) {
+            return packFloatx80(0, 0, 0);
+        }
+        return packFloatx80(0, floatx80_infinity.high,
+                            floatx80_infinity.low);
+    }
+
+    if (aExp == 0 && aSig == 0) {
+        return packFloatx80(0, one_exp, one_sig);
+    }
+
+    user_rnd_mode = status->float_rounding_mode;
+    user_rnd_prec = status->floatx80_rounding_precision;
+    status->float_rounding_mode = float_round_nearest_even;
+    status->floatx80_rounding_precision = 80;
+
+    fp0 = a;
+
+    compact = floatx80_make_compact(aExp, aSig);
+
+    if (compact < 0x3FB98000 || compact > 0x400B9B07) {
+        /* |X| > 16480 LOG2/LOG10 or |X| < 2^(-70) */
+        if (compact > 0x3FFF8000) { /* |X| > 16480 */
+            status->float_rounding_mode = user_rnd_mode;
+            status->floatx80_rounding_precision = user_rnd_prec;
+
+            if (aSign) {
+                return roundAndPackFloatx80(status->floatx80_rounding_precision,
+                                            0, -0x1000, aSig, 0, status);
+            } else {
+                return roundAndPackFloatx80(status->floatx80_rounding_precision,
+                                            0, 0x8000, aSig, 0, status);
+            }
+        } else { /* |X| < 2^(-70) */
+            status->float_rounding_mode = user_rnd_mode;
+            status->floatx80_rounding_precision = user_rnd_prec;
+
+            a = floatx80_add(fp0, float32_to_floatx80(
+                             make_float32(0x3F800000), status),
+                             status); /* 1 + X */
+
+            float_raise(float_flag_inexact, status);
+
+            return a;
+        }
+    } else { /* 2^(-70) <= |X| <= 16480 LOG 2 / LOG 10 */
+        fp1 = fp0; /* X */
+        fp1 = floatx80_mul(fp1, float64_to_floatx80(
+                           make_float64(0x406A934F0979A371),
+                           status), status); /* X*64*LOG10/LOG2 */
+        n = floatx80_to_int32(fp1, status); /* N=INT(X*64*LOG10/LOG2) */
+        fp1 = int32_to_floatx80(n, status);
+
+        j = n & 0x3F;
+        l = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
+        if (n < 0 && j) {
+            /* arithmetic right shift is division and
+             * round towards minus infinity
+             */
+            l--;
+        }
+        m = l / 2; /* NOTE: this is really arithmetic right shift by 1 */
+        if (l < 0 && (l & 1)) {
+            /* arithmetic right shift is division and
+             * round towards minus infinity
+             */
+            m--;
+        }
+        m1 = l - m;
+        m1 += 0x3FFF; /* ADJFACT IS 2^(M') */
+
+        adjfact = packFloatx80(0, m1, one_sig);
+        fact1 = exp2_tbl[j];
+        fact1.high += m;
+        fact2.high = exp2_tbl2[j] >> 16;
+        fact2.high += m;
+        fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF);
+        fact2.low <<= 48;
+
+        fp2 = fp1; /* N */
+        fp1 = floatx80_mul(fp1, float64_to_floatx80(
+                           make_float64(0x3F734413509F8000), status),
+                           status); /* N*(LOG2/64LOG10)_LEAD */
+        fp3 = packFloatx80(1, 0x3FCD, LIT64(0xC0219DC1DA994FD2));
+        fp2 = floatx80_mul(fp2, fp3, status); /* N*(LOG2/64LOG10)_TRAIL */
+        fp0 = floatx80_sub(fp0, fp1, status); /* X - N L_LEAD */
+        fp0 = floatx80_sub(fp0, fp2, status); /* X - N L_TRAIL */
+        fp2 = packFloatx80(0, 0x4000, LIT64(0x935D8DDDAAA8AC17)); /* LOG10 */
+        fp0 = floatx80_mul(fp0, fp2, status); /* R */
+
+        /* EXPR */
+        fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+        fp2 = float64_to_floatx80(make_float64(0x3F56C16D6F7BD0B2),
+                                  status); /* A5 */
+        fp3 = float64_to_floatx80(make_float64(0x3F811112302C712C),
+                                  status); /* A4 */
+        fp2 = floatx80_mul(fp2, fp1, status); /* S*A5 */
+        fp3 = floatx80_mul(fp3, fp1, status); /* S*A4 */
+        fp2 = floatx80_add(fp2, float64_to_floatx80(
+                           make_float64(0x3FA5555555554CC1), status),
+                           status); /* A3+S*A5 */
+        fp3 = floatx80_add(fp3, float64_to_floatx80(
+                           make_float64(0x3FC5555555554A54), status),
+                           status); /* A2+S*A4 */
+        fp2 = floatx80_mul(fp2, fp1, status); /* S*(A3+S*A5) */
+        fp3 = floatx80_mul(fp3, fp1, status); /* S*(A2+S*A4) */
+        fp2 = floatx80_add(fp2, float64_to_floatx80(
+                           make_float64(0x3FE0000000000000), status),
+                           status); /* A1+S*(A3+S*A5) */
+        fp3 = floatx80_mul(fp3, fp0, status); /* R*S*(A2+S*A4) */
+
+        fp2 = floatx80_mul(fp2, fp1, status); /* S*(A1+S*(A3+S*A5)) */
+        fp0 = floatx80_add(fp0, fp3, status); /* R+R*S*(A2+S*A4) */
+        fp0 = floatx80_add(fp0, fp2, status); /* EXP(R) - 1 */
+
+        fp0 = floatx80_mul(fp0, fact1, status);
+        fp0 = floatx80_add(fp0, fact2, status);
+        fp0 = floatx80_add(fp0, fact1, status);
+
+        status->float_rounding_mode = user_rnd_mode;
+        status->floatx80_rounding_precision = user_rnd_prec;
+
+        a = floatx80_mul(fp0, adjfact, status);
+
+        float_raise(float_flag_inexact, status);
+
+        return a;
+    }
+}
diff --git a/target/m68k/softfloat.h b/target/m68k/softfloat.h
index 78fbc0cd0c..d28e49fe9f 100644
--- a/target/m68k/softfloat.h
+++ b/target/m68k/softfloat.h
@@ -26,4 +26,12 @@ floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status);
 floatx80 floatx80_getman(floatx80 a, float_status *status);
 floatx80 floatx80_getexp(floatx80 a, float_status *status);
 floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status);
+floatx80 floatx80_move(floatx80 a, float_status *status);
+floatx80 floatx80_lognp1(floatx80 a, float_status *status);
+floatx80 floatx80_logn(floatx80 a, float_status *status);
+floatx80 floatx80_log10(floatx80 a, float_status *status);
+floatx80 floatx80_log2(floatx80 a, float_status *status);
+floatx80 floatx80_etox(floatx80 a, float_status *status);
+floatx80 floatx80_twotox(floatx80 a, float_status *status);
+floatx80 floatx80_tentox(floatx80 a, float_status *status);
 #endif
diff --git a/target/m68k/softfloat_fpsp_tables.h b/target/m68k/softfloat_fpsp_tables.h
new file mode 100644
index 0000000000..dd76dc0373
--- /dev/null
+++ b/target/m68k/softfloat_fpsp_tables.h
@@ -0,0 +1,374 @@
+/*
+ * Ported from a work by Andreas Grabher for Previous, NeXT Computer Emulator,
+ * derived from NetBSD M68040 FPSP functions,
+ * derived from release 2a of the SoftFloat IEC/IEEE Floating-point Arithmetic
+ * Package. Those parts of the code (and some later contributions) are
+ * provided under that license, as detailed below.
+ * It has subsequently been modified by contributors to the QEMU Project,
+ * so some portions are provided under:
+ *  the SoftFloat-2a license
+ *  the BSD license
+ *  GPL-v2-or-later
+ *
+ * Any future contributions to this file will be taken to be licensed under
+ * the Softfloat-2a license unless specifically indicated otherwise.
+ */
+
+/* Portions of this work are licensed under the terms of the GNU GPL,
+ * version 2 or later. See the COPYING file in the top-level directory.
+ */
+
+#ifndef TARGET_M68K_SOFTFLOAT_FPSP_TABLES_H
+#define TARGET_M68K_SOFTFLOAT_FPSP_TABLES_H
+
+static const floatx80 log_tbl[128] = {
+    make_floatx80_init(0x3FFE, 0xFE03F80FE03F80FE),
+    make_floatx80_init(0x3FF7, 0xFF015358833C47E2),
+    make_floatx80_init(0x3FFE, 0xFA232CF252138AC0),
+    make_floatx80_init(0x3FF9, 0xBDC8D83EAD88D549),
+    make_floatx80_init(0x3FFE, 0xF6603D980F6603DA),
+    make_floatx80_init(0x3FFA, 0x9CF43DCFF5EAFD48),
+    make_floatx80_init(0x3FFE, 0xF2B9D6480F2B9D65),
+    make_floatx80_init(0x3FFA, 0xDA16EB88CB8DF614),
+    make_floatx80_init(0x3FFE, 0xEF2EB71FC4345238),
+    make_floatx80_init(0x3FFB, 0x8B29B7751BD70743),
+    make_floatx80_init(0x3FFE, 0xEBBDB2A5C1619C8C),
+    make_floatx80_init(0x3FFB, 0xA8D839F830C1FB49),
+    make_floatx80_init(0x3FFE, 0xE865AC7B7603A197),
+    make_floatx80_init(0x3FFB, 0xC61A2EB18CD907AD),
+    make_floatx80_init(0x3FFE, 0xE525982AF70C880E),
+    make_floatx80_init(0x3FFB, 0xE2F2A47ADE3A18AF),
+    make_floatx80_init(0x3FFE, 0xE1FC780E1FC780E2),
+    make_floatx80_init(0x3FFB, 0xFF64898EDF55D551),
+    make_floatx80_init(0x3FFE, 0xDEE95C4CA037BA57),
+    make_floatx80_init(0x3FFC, 0x8DB956A97B3D0148),
+    make_floatx80_init(0x3FFE, 0xDBEB61EED19C5958),
+    make_floatx80_init(0x3FFC, 0x9B8FE100F47BA1DE),
+    make_floatx80_init(0x3FFE, 0xD901B2036406C80E),
+    make_floatx80_init(0x3FFC, 0xA9372F1D0DA1BD17),
+    make_floatx80_init(0x3FFE, 0xD62B80D62B80D62C),
+    make_floatx80_init(0x3FFC, 0xB6B07F38CE90E46B),
+    make_floatx80_init(0x3FFE, 0xD3680D3680D3680D),
+    make_floatx80_init(0x3FFC, 0xC3FD032906488481),
+    make_floatx80_init(0x3FFE, 0xD0B69FCBD2580D0B),
+    make_floatx80_init(0x3FFC, 0xD11DE0FF15AB18CA),
+    make_floatx80_init(0x3FFE, 0xCE168A7725080CE1),
+    make_floatx80_init(0x3FFC, 0xDE1433A16C66B150),
+    make_floatx80_init(0x3FFE, 0xCB8727C065C393E0),
+    make_floatx80_init(0x3FFC, 0xEAE10B5A7DDC8ADD),
+    make_floatx80_init(0x3FFE, 0xC907DA4E871146AD),
+    make_floatx80_init(0x3FFC, 0xF7856E5EE2C9B291),
+    make_floatx80_init(0x3FFE, 0xC6980C6980C6980C),
+    make_floatx80_init(0x3FFD, 0x82012CA5A68206D7),
+    make_floatx80_init(0x3FFE, 0xC4372F855D824CA6),
+    make_floatx80_init(0x3FFD, 0x882C5FCD7256A8C5),
+    make_floatx80_init(0x3FFE, 0xC1E4BBD595F6E947),
+    make_floatx80_init(0x3FFD, 0x8E44C60B4CCFD7DE),
+    make_floatx80_init(0x3FFE, 0xBFA02FE80BFA02FF),
+    make_floatx80_init(0x3FFD, 0x944AD09EF4351AF6),
+    make_floatx80_init(0x3FFE, 0xBD69104707661AA3),
+    make_floatx80_init(0x3FFD, 0x9A3EECD4C3EAA6B2),
+    make_floatx80_init(0x3FFE, 0xBB3EE721A54D880C),
+    make_floatx80_init(0x3FFD, 0xA0218434353F1DE8),
+    make_floatx80_init(0x3FFE, 0xB92143FA36F5E02E),
+    make_floatx80_init(0x3FFD, 0xA5F2FCABBBC506DA),
+    make_floatx80_init(0x3FFE, 0xB70FBB5A19BE3659),
+    make_floatx80_init(0x3FFD, 0xABB3B8BA2AD362A5),
+    make_floatx80_init(0x3FFE, 0xB509E68A9B94821F),
+    make_floatx80_init(0x3FFD, 0xB1641795CE3CA97B),
+    make_floatx80_init(0x3FFE, 0xB30F63528917C80B),
+    make_floatx80_init(0x3FFD, 0xB70475515D0F1C61),
+    make_floatx80_init(0x3FFE, 0xB11FD3B80B11FD3C),
+    make_floatx80_init(0x3FFD, 0xBC952AFEEA3D13E1),
+    make_floatx80_init(0x3FFE, 0xAF3ADDC680AF3ADE),
+    make_floatx80_init(0x3FFD, 0xC2168ED0F458BA4A),
+    make_floatx80_init(0x3FFE, 0xAD602B580AD602B6),
+    make_floatx80_init(0x3FFD, 0xC788F439B3163BF1),
+    make_floatx80_init(0x3FFE, 0xAB8F69E28359CD11),
+    make_floatx80_init(0x3FFD, 0xCCECAC08BF04565D),
+    make_floatx80_init(0x3FFE, 0xA9C84A47A07F5638),
+    make_floatx80_init(0x3FFD, 0xD24204872DD85160),
+    make_floatx80_init(0x3FFE, 0xA80A80A80A80A80B),
+    make_floatx80_init(0x3FFD, 0xD78949923BC3588A),
+    make_floatx80_init(0x3FFE, 0xA655C4392D7B73A8),
+    make_floatx80_init(0x3FFD, 0xDCC2C4B49887DACC),
+    make_floatx80_init(0x3FFE, 0xA4A9CF1D96833751),
+    make_floatx80_init(0x3FFD, 0xE1EEBD3E6D6A6B9E),
+    make_floatx80_init(0x3FFE, 0xA3065E3FAE7CD0E0),
+    make_floatx80_init(0x3FFD, 0xE70D785C2F9F5BDC),
+    make_floatx80_init(0x3FFE, 0xA16B312EA8FC377D),
+    make_floatx80_init(0x3FFD, 0xEC1F392C5179F283),
+    make_floatx80_init(0x3FFE, 0x9FD809FD809FD80A),
+    make_floatx80_init(0x3FFD, 0xF12440D3E36130E6),
+    make_floatx80_init(0x3FFE, 0x9E4CAD23DD5F3A20),
+    make_floatx80_init(0x3FFD, 0xF61CCE92346600BB),
+    make_floatx80_init(0x3FFE, 0x9CC8E160C3FB19B9),
+    make_floatx80_init(0x3FFD, 0xFB091FD38145630A),
+    make_floatx80_init(0x3FFE, 0x9B4C6F9EF03A3CAA),
+    make_floatx80_init(0x3FFD, 0xFFE97042BFA4C2AD),
+    make_floatx80_init(0x3FFE, 0x99D722DABDE58F06),
+    make_floatx80_init(0x3FFE, 0x825EFCED49369330),
+    make_floatx80_init(0x3FFE, 0x9868C809868C8098),
+    make_floatx80_init(0x3FFE, 0x84C37A7AB9A905C9),
+    make_floatx80_init(0x3FFE, 0x97012E025C04B809),
+    make_floatx80_init(0x3FFE, 0x87224C2E8E645FB7),
+    make_floatx80_init(0x3FFE, 0x95A02568095A0257),
+    make_floatx80_init(0x3FFE, 0x897B8CAC9F7DE298),
+    make_floatx80_init(0x3FFE, 0x9445809445809446),
+    make_floatx80_init(0x3FFE, 0x8BCF55DEC4CD05FE),
+    make_floatx80_init(0x3FFE, 0x92F113840497889C),
+    make_floatx80_init(0x3FFE, 0x8E1DC0FB89E125E5),
+    make_floatx80_init(0x3FFE, 0x91A2B3C4D5E6F809),
+    make_floatx80_init(0x3FFE, 0x9066E68C955B6C9B),
+    make_floatx80_init(0x3FFE, 0x905A38633E06C43B),
+    make_floatx80_init(0x3FFE, 0x92AADE74C7BE59E0),
+    make_floatx80_init(0x3FFE, 0x8F1779D9FDC3A219),
+    make_floatx80_init(0x3FFE, 0x94E9BFF615845643),
+    make_floatx80_init(0x3FFE, 0x8DDA520237694809),
+    make_floatx80_init(0x3FFE, 0x9723A1B720134203),
+    make_floatx80_init(0x3FFE, 0x8CA29C046514E023),
+    make_floatx80_init(0x3FFE, 0x995899C890EB8990),
+    make_floatx80_init(0x3FFE, 0x8B70344A139BC75A),
+    make_floatx80_init(0x3FFE, 0x9B88BDAA3A3DAE2F),
+    make_floatx80_init(0x3FFE, 0x8A42F8705669DB46),
+    make_floatx80_init(0x3FFE, 0x9DB4224FFFE1157C),
+    make_floatx80_init(0x3FFE, 0x891AC73AE9819B50),
+    make_floatx80_init(0x3FFE, 0x9FDADC268B7A12DA),
+    make_floatx80_init(0x3FFE, 0x87F78087F78087F8),
+    make_floatx80_init(0x3FFE, 0xA1FCFF17CE733BD4),
+    make_floatx80_init(0x3FFE, 0x86D905447A34ACC6),
+    make_floatx80_init(0x3FFE, 0xA41A9E8F5446FB9F),
+    make_floatx80_init(0x3FFE, 0x85BF37612CEE3C9B),
+    make_floatx80_init(0x3FFE, 0xA633CD7E6771CD8B),
+    make_floatx80_init(0x3FFE, 0x84A9F9C8084A9F9D),
+    make_floatx80_init(0x3FFE, 0xA8489E600B435A5E),
+    make_floatx80_init(0x3FFE, 0x839930523FBE3368),
+    make_floatx80_init(0x3FFE, 0xAA59233CCCA4BD49),
+    make_floatx80_init(0x3FFE, 0x828CBFBEB9A020A3),
+    make_floatx80_init(0x3FFE, 0xAC656DAE6BCC4985),
+    make_floatx80_init(0x3FFE, 0x81848DA8FAF0D277),
+    make_floatx80_init(0x3FFE, 0xAE6D8EE360BB2468),
+    make_floatx80_init(0x3FFE, 0x8080808080808081),
+    make_floatx80_init(0x3FFE, 0xB07197A23C46C654)
+};
+
+static const floatx80 exp_tbl[64] = {
+    make_floatx80_init(0x3FFF, 0x8000000000000000),
+    make_floatx80_init(0x3FFF, 0x8164D1F3BC030774),
+    make_floatx80_init(0x3FFF, 0x82CD8698AC2BA1D8),
+    make_floatx80_init(0x3FFF, 0x843A28C3ACDE4048),
+    make_floatx80_init(0x3FFF, 0x85AAC367CC487B14),
+    make_floatx80_init(0x3FFF, 0x871F61969E8D1010),
+    make_floatx80_init(0x3FFF, 0x88980E8092DA8528),
+    make_floatx80_init(0x3FFF, 0x8A14D575496EFD9C),
+    make_floatx80_init(0x3FFF, 0x8B95C1E3EA8BD6E8),
+    make_floatx80_init(0x3FFF, 0x8D1ADF5B7E5BA9E4),
+    make_floatx80_init(0x3FFF, 0x8EA4398B45CD53C0),
+    make_floatx80_init(0x3FFF, 0x9031DC431466B1DC),
+    make_floatx80_init(0x3FFF, 0x91C3D373AB11C338),
+    make_floatx80_init(0x3FFF, 0x935A2B2F13E6E92C),
+    make_floatx80_init(0x3FFF, 0x94F4EFA8FEF70960),
+    make_floatx80_init(0x3FFF, 0x96942D3720185A00),
+    make_floatx80_init(0x3FFF, 0x9837F0518DB8A970),
+    make_floatx80_init(0x3FFF, 0x99E0459320B7FA64),
+    make_floatx80_init(0x3FFF, 0x9B8D39B9D54E5538),
+    make_floatx80_init(0x3FFF, 0x9D3ED9A72CFFB750),
+    make_floatx80_init(0x3FFF, 0x9EF5326091A111AC),
+    make_floatx80_init(0x3FFF, 0xA0B0510FB9714FC4),
+    make_floatx80_init(0x3FFF, 0xA27043030C496818),
+    make_floatx80_init(0x3FFF, 0xA43515AE09E680A0),
+    make_floatx80_init(0x3FFF, 0xA5FED6A9B15138EC),
+    make_floatx80_init(0x3FFF, 0xA7CD93B4E9653568),
+    make_floatx80_init(0x3FFF, 0xA9A15AB4EA7C0EF8),
+    make_floatx80_init(0x3FFF, 0xAB7A39B5A93ED338),
+    make_floatx80_init(0x3FFF, 0xAD583EEA42A14AC8),
+    make_floatx80_init(0x3FFF, 0xAF3B78AD690A4374),
+    make_floatx80_init(0x3FFF, 0xB123F581D2AC2590),
+    make_floatx80_init(0x3FFF, 0xB311C412A9112488),
+    make_floatx80_init(0x3FFF, 0xB504F333F9DE6484),
+    make_floatx80_init(0x3FFF, 0xB6FD91E328D17790),
+    make_floatx80_init(0x3FFF, 0xB8FBAF4762FB9EE8),
+    make_floatx80_init(0x3FFF, 0xBAFF5AB2133E45FC),
+    make_floatx80_init(0x3FFF, 0xBD08A39F580C36C0),
+    make_floatx80_init(0x3FFF, 0xBF1799B67A731084),
+    make_floatx80_init(0x3FFF, 0xC12C4CCA66709458),
+    make_floatx80_init(0x3FFF, 0xC346CCDA24976408),
+    make_floatx80_init(0x3FFF, 0xC5672A115506DADC),
+    make_floatx80_init(0x3FFF, 0xC78D74C8ABB9B15C),
+    make_floatx80_init(0x3FFF, 0xC9B9BD866E2F27A4),
+    make_floatx80_init(0x3FFF, 0xCBEC14FEF2727C5C),
+    make_floatx80_init(0x3FFF, 0xCE248C151F8480E4),
+    make_floatx80_init(0x3FFF, 0xD06333DAEF2B2594),
+    make_floatx80_init(0x3FFF, 0xD2A81D91F12AE45C),
+    make_floatx80_init(0x3FFF, 0xD4F35AABCFEDFA20),
+    make_floatx80_init(0x3FFF, 0xD744FCCAD69D6AF4),
+    make_floatx80_init(0x3FFF, 0xD99D15C278AFD7B4),
+    make_floatx80_init(0x3FFF, 0xDBFBB797DAF23754),
+    make_floatx80_init(0x3FFF, 0xDE60F4825E0E9124),
+    make_floatx80_init(0x3FFF, 0xE0CCDEEC2A94E110),
+    make_floatx80_init(0x3FFF, 0xE33F8972BE8A5A50),
+    make_floatx80_init(0x3FFF, 0xE5B906E77C8348A8),
+    make_floatx80_init(0x3FFF, 0xE8396A503C4BDC68),
+    make_floatx80_init(0x3FFF, 0xEAC0C6E7DD243930),
+    make_floatx80_init(0x3FFF, 0xED4F301ED9942B84),
+    make_floatx80_init(0x3FFF, 0xEFE4B99BDCDAF5CC),
+    make_floatx80_init(0x3FFF, 0xF281773C59FFB138),
+    make_floatx80_init(0x3FFF, 0xF5257D152486CC2C),
+    make_floatx80_init(0x3FFF, 0xF7D0DF730AD13BB8),
+    make_floatx80_init(0x3FFF, 0xFA83B2DB722A033C),
+    make_floatx80_init(0x3FFF, 0xFD3E0C0CF486C174)
+};
+
+static const float32 exp_tbl2[64] = {
+    const_float32(0x00000000),
+    const_float32(0x9F841A9B),
+    const_float32(0x9FC1D5B9),
+    const_float32(0xA0728369),
+    const_float32(0x1FC5C95C),
+    const_float32(0x1EE85C9F),
+    const_float32(0x9FA20729),
+    const_float32(0xA07BF9AF),
+    const_float32(0xA0020DCF),
+    const_float32(0x205A63DA),
+    const_float32(0x1EB70051),
+    const_float32(0x1F6EB029),
+    const_float32(0xA0781494),
+    const_float32(0x9EB319B0),
+    const_float32(0x2017457D),
+    const_float32(0x1F11D537),
+    const_float32(0x9FB952DD),
+    const_float32(0x1FE43087),
+    const_float32(0x1FA2A818),
+    const_float32(0x1FDE494D),
+    const_float32(0x20504890),
+    const_float32(0xA073691C),
+    const_float32(0x1F9B7A05),
+    const_float32(0xA0797126),
+    const_float32(0xA071A140),
+    const_float32(0x204F62DA),
+    const_float32(0x1F283C4A),
+    const_float32(0x9F9A7FDC),
+    const_float32(0xA05B3FAC),
+    const_float32(0x1FDF2610),
+    const_float32(0x9F705F90),
+    const_float32(0x201F678A),
+    const_float32(0x1F32FB13),
+    const_float32(0x20038B30),
+    const_float32(0x200DC3CC),
+    const_float32(0x9F8B2AE6),
+    const_float32(0xA02BBF70),
+    const_float32(0xA00BF518),
+    const_float32(0xA041DD41),
+    const_float32(0x9FDF137B),
+    const_float32(0x201F1568),
+    const_float32(0x1FC13A2E),
+    const_float32(0xA03F8F03),
+    const_float32(0x1FF4907D),
+    const_float32(0x9E6E53E4),
+    const_float32(0x1FD6D45C),
+    const_float32(0xA076EDB9),
+    const_float32(0x9FA6DE21),
+    const_float32(0x1EE69A2F),
+    const_float32(0x207F439F),
+    const_float32(0x201EC207),
+    const_float32(0x9E8BE175),
+    const_float32(0x20032C4B),
+    const_float32(0x2004DFF5),
+    const_float32(0x1E72F47A),
+    const_float32(0x1F722F22),
+    const_float32(0xA017E945),
+    const_float32(0x1F401A5B),
+    const_float32(0x9FB9A9E3),
+    const_float32(0x20744C05),
+    const_float32(0x1F773A19),
+    const_float32(0x1FFE90D5),
+    const_float32(0xA041ED22),
+    const_float32(0x1F853F3A),
+};
+
+static const floatx80 exp2_tbl[64] = {
+    make_floatx80_init(0x3FFF, 0x8000000000000000),
+    make_floatx80_init(0x3FFF, 0x8164D1F3BC030773),
+    make_floatx80_init(0x3FFF, 0x82CD8698AC2BA1D7),
+    make_floatx80_init(0x3FFF, 0x843A28C3ACDE4046),
+    make_floatx80_init(0x3FFF, 0x85AAC367CC487B15),
+    make_floatx80_init(0x3FFF, 0x871F61969E8D1010),
+    make_floatx80_init(0x3FFF, 0x88980E8092DA8527),
+    make_floatx80_init(0x3FFF, 0x8A14D575496EFD9A),
+    make_floatx80_init(0x3FFF, 0x8B95C1E3EA8BD6E7),
+    make_floatx80_init(0x3FFF, 0x8D1ADF5B7E5BA9E6),
+    make_floatx80_init(0x3FFF, 0x8EA4398B45CD53C0),
+    make_floatx80_init(0x3FFF, 0x9031DC431466B1DC),
+    make_floatx80_init(0x3FFF, 0x91C3D373AB11C336),
+    make_floatx80_init(0x3FFF, 0x935A2B2F13E6E92C),
+    make_floatx80_init(0x3FFF, 0x94F4EFA8FEF70961),
+    make_floatx80_init(0x3FFF, 0x96942D3720185A00),
+    make_floatx80_init(0x3FFF, 0x9837F0518DB8A96F),
+    make_floatx80_init(0x3FFF, 0x99E0459320B7FA65),
+    make_floatx80_init(0x3FFF, 0x9B8D39B9D54E5539),
+    make_floatx80_init(0x3FFF, 0x9D3ED9A72CFFB751),
+    make_floatx80_init(0x3FFF, 0x9EF5326091A111AE),
+    make_floatx80_init(0x3FFF, 0xA0B0510FB9714FC2),
+    make_floatx80_init(0x3FFF, 0xA27043030C496819),
+    make_floatx80_init(0x3FFF, 0xA43515AE09E6809E),
+    make_floatx80_init(0x3FFF, 0xA5FED6A9B15138EA),
+    make_floatx80_init(0x3FFF, 0xA7CD93B4E965356A),
+    make_floatx80_init(0x3FFF, 0xA9A15AB4EA7C0EF8),
+    make_floatx80_init(0x3FFF, 0xAB7A39B5A93ED337),
+    make_floatx80_init(0x3FFF, 0xAD583EEA42A14AC6),
+    make_floatx80_init(0x3FFF, 0xAF3B78AD690A4375),
+    make_floatx80_init(0x3FFF, 0xB123F581D2AC2590),
+    make_floatx80_init(0x3FFF, 0xB311C412A9112489),
+    make_floatx80_init(0x3FFF, 0xB504F333F9DE6484),
+    make_floatx80_init(0x3FFF, 0xB6FD91E328D17791),
+    make_floatx80_init(0x3FFF, 0xB8FBAF4762FB9EE9),
+    make_floatx80_init(0x3FFF, 0xBAFF5AB2133E45FB),
+    make_floatx80_init(0x3FFF, 0xBD08A39F580C36BF),
+    make_floatx80_init(0x3FFF, 0xBF1799B67A731083),
+    make_floatx80_init(0x3FFF, 0xC12C4CCA66709456),
+    make_floatx80_init(0x3FFF, 0xC346CCDA24976407),
+    make_floatx80_init(0x3FFF, 0xC5672A115506DADD),
+    make_floatx80_init(0x3FFF, 0xC78D74C8ABB9B15D),
+    make_floatx80_init(0x3FFF, 0xC9B9BD866E2F27A3),
+    make_floatx80_init(0x3FFF, 0xCBEC14FEF2727C5D),
+    make_floatx80_init(0x3FFF, 0xCE248C151F8480E4),
+    make_floatx80_init(0x3FFF, 0xD06333DAEF2B2595),
+    make_floatx80_init(0x3FFF, 0xD2A81D91F12AE45A),
+    make_floatx80_init(0x3FFF, 0xD4F35AABCFEDFA1F),
+    make_floatx80_init(0x3FFF, 0xD744FCCAD69D6AF4),
+    make_floatx80_init(0x3FFF, 0xD99D15C278AFD7B6),
+    make_floatx80_init(0x3FFF, 0xDBFBB797DAF23755),
+    make_floatx80_init(0x3FFF, 0xDE60F4825E0E9124),
+    make_floatx80_init(0x3FFF, 0xE0CCDEEC2A94E111),
+    make_floatx80_init(0x3FFF, 0xE33F8972BE8A5A51),
+    make_floatx80_init(0x3FFF, 0xE5B906E77C8348A8),
+    make_floatx80_init(0x3FFF, 0xE8396A503C4BDC68),
+    make_floatx80_init(0x3FFF, 0xEAC0C6E7DD24392F),
+    make_floatx80_init(0x3FFF, 0xED4F301ED9942B84),
+    make_floatx80_init(0x3FFF, 0xEFE4B99BDCDAF5CB),
+    make_floatx80_init(0x3FFF, 0xF281773C59FFB13A),
+    make_floatx80_init(0x3FFF, 0xF5257D152486CC2C),
+    make_floatx80_init(0x3FFF, 0xF7D0DF730AD13BB9),
+    make_floatx80_init(0x3FFF, 0xFA83B2DB722A033A),
+    make_floatx80_init(0x3FFF, 0xFD3E0C0CF486C175)
+};
+
+static const uint32_t exp2_tbl2[64] = {
+    0x3F738000, 0x3FBEF7CA, 0x3FBDF8A9, 0x3FBCD7C9,
+    0xBFBDE8DA, 0x3FBDE85C, 0x3FBEBBF1, 0x3FBB80CA,
+    0xBFBA8373, 0xBFBE9670, 0x3FBDB700, 0x3FBEEEB0,
+    0x3FBBFD6D, 0xBFBDB319, 0x3FBDBA2B, 0x3FBE91D5,
+    0x3FBE8D5A, 0xBFBCDE7B, 0xBFBEBAAF, 0xBFBD86DA,
+    0xBFBEBEDD, 0x3FBCC96E, 0xBFBEC90B, 0x3FBBD1DB,
+    0x3FBCE5EB, 0xBFBEC274, 0x3FBEA83C, 0x3FBECB00,
+    0x3FBE9301, 0xBFBD8367, 0xBFBEF05F, 0x3FBDFB3C,
+    0x3FBEB2FB, 0x3FBAE2CB, 0x3FBCDC3C, 0x3FBEE9AA,
+    0xBFBEAEFD, 0xBFBCBF51, 0x3FBEF88A, 0x3FBD83B2,
+    0x3FBDF8AB, 0xBFBDFB17, 0xBFBEFE3C, 0xBFBBB6F8,
+    0xBFBCEE53, 0xBFBDA4AE, 0x3FBC9124, 0x3FBEB243,
+    0x3FBDE69A, 0xBFB8BC61, 0x3FBDF610, 0xBFBD8BE1,
+    0x3FBACB12, 0x3FBB9BFE, 0x3FBCF2F4, 0x3FBEF22F,
+    0xBFBDBF4A, 0x3FBEC01A, 0x3FBE8CAC, 0xBFBCBB3F,
+    0x3FBEF73A, 0xBFB8B795, 0x3FBEF84B, 0xBFBEF581
+};
+#endif
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index dbb24f8d84..6d5bde0777 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -5054,6 +5054,27 @@ DISAS_INSN(fpu)
     case 0x45: /* fdsqrt */
         gen_helper_fdsqrt(cpu_env, cpu_dest, cpu_src);
         break;
+    case 0x06: /* flognp1 */
+        gen_helper_flognp1(cpu_env, cpu_dest, cpu_src);
+        break;
+    case 0x10: /* fetox */
+        gen_helper_fetox(cpu_env, cpu_dest, cpu_src);
+        break;
+    case 0x11: /* ftwotox */
+        gen_helper_ftwotox(cpu_env, cpu_dest, cpu_src);
+        break;
+    case 0x12: /* ftentox */
+        gen_helper_ftentox(cpu_env, cpu_dest, cpu_src);
+        break;
+    case 0x14: /* flogn */
+        gen_helper_flogn(cpu_env, cpu_dest, cpu_src);
+        break;
+    case 0x15: /* flog10 */
+        gen_helper_flog10(cpu_env, cpu_dest, cpu_src);
+        break;
+    case 0x16: /* flog2 */
+        gen_helper_flog2(cpu_env, cpu_dest, cpu_src);
+        break;
     case 0x18: /* fabs */
         gen_helper_fabs(cpu_env, cpu_dest, cpu_src);
         break;
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index ec628f3453..a3faea8bfc 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -665,6 +665,133 @@ static void qdict_crumple_test_empty(void)
     QDECREF(dst);
 }
 
+static int qdict_count_entries(QDict *dict)
+{
+    const QDictEntry *e;
+    int count = 0;
+
+    for (e = qdict_first(dict); e; e = qdict_next(dict, e)) {
+        count++;
+    }
+
+    return count;
+}
+
+static void qdict_rename_keys_test(void)
+{
+    QDict *dict = qdict_new();
+    QDict *copy;
+    QDictRenames *renames;
+    Error *local_err = NULL;
+
+    qdict_put_str(dict, "abc", "foo");
+    qdict_put_str(dict, "abcdef", "bar");
+    qdict_put_int(dict, "number", 42);
+    qdict_put_bool(dict, "flag", true);
+    qdict_put_null(dict, "nothing");
+
+    /* Empty rename list */
+    renames = (QDictRenames[]) {
+        { NULL, "this can be anything" }
+    };
+    copy = qdict_clone_shallow(dict);
+    qdict_rename_keys(copy, renames, &error_abort);
+
+    g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
+    g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
+    g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
+    g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
+    g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
+    g_assert_cmpint(qdict_count_entries(copy), ==, 5);
+
+    QDECREF(copy);
+
+    /* Simple rename of all entries */
+    renames = (QDictRenames[]) {
+        { "abc",        "str1" },
+        { "abcdef",     "str2" },
+        { "number",     "int" },
+        { "flag",       "bool" },
+        { "nothing",    "null" },
+        { NULL , NULL }
+    };
+    copy = qdict_clone_shallow(dict);
+    qdict_rename_keys(copy, renames, &error_abort);
+
+    g_assert(!qdict_haskey(copy, "abc"));
+    g_assert(!qdict_haskey(copy, "abcdef"));
+    g_assert(!qdict_haskey(copy, "number"));
+    g_assert(!qdict_haskey(copy, "flag"));
+    g_assert(!qdict_haskey(copy, "nothing"));
+
+    g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo");
+    g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar");
+    g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42);
+    g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true);
+    g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL);
+    g_assert_cmpint(qdict_count_entries(copy), ==, 5);
+
+    QDECREF(copy);
+
+    /* Renames are processed top to bottom */
+    renames = (QDictRenames[]) {
+        { "abc",        "tmp" },
+        { "abcdef",     "abc" },
+        { "number",     "abcdef" },
+        { "flag",       "number" },
+        { "nothing",    "flag" },
+        { "tmp",        "nothing" },
+        { NULL , NULL }
+    };
+    copy = qdict_clone_shallow(dict);
+    qdict_rename_keys(copy, renames, &error_abort);
+
+    g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo");
+    g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar");
+    g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42);
+    g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true);
+    g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL);
+    g_assert(!qdict_haskey(copy, "tmp"));
+    g_assert_cmpint(qdict_count_entries(copy), ==, 5);
+
+    QDECREF(copy);
+
+    /* Conflicting rename */
+    renames = (QDictRenames[]) {
+        { "abcdef",     "abc" },
+        { NULL , NULL }
+    };
+    copy = qdict_clone_shallow(dict);
+    qdict_rename_keys(copy, renames, &local_err);
+
+    g_assert(local_err != NULL);
+    error_free(local_err);
+    local_err = NULL;
+
+    g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
+    g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
+    g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
+    g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
+    g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
+    g_assert_cmpint(qdict_count_entries(copy), ==, 5);
+
+    QDECREF(copy);
+
+    /* Renames in an empty dict */
+    renames = (QDictRenames[]) {
+        { "abcdef",     "abc" },
+        { NULL , NULL }
+    };
+
+    QDECREF(dict);
+    dict = qdict_new();
+
+    qdict_rename_keys(dict, renames, &error_abort);
+    g_assert(qdict_first(dict) == NULL);
+
+    QDECREF(dict);
+}
+
 static void qdict_crumple_test_bad_inputs(void)
 {
     QDict *src;
@@ -880,6 +1007,8 @@ int main(int argc, char **argv)
     g_test_add_func("/public/crumple/bad_inputs",
                     qdict_crumple_test_bad_inputs);
 
+    g_test_add_func("/public/rename_keys", qdict_rename_keys_test);
+
     /* The Big one */
     if (g_test_slow()) {
         g_test_add_func("/stress/test", qdict_stress_test);
diff --git a/tests/migration-test.c b/tests/migration-test.c
index 74f9361bdd..422bf1afdf 100644
--- a/tests/migration-test.c
+++ b/tests/migration-test.c
@@ -382,7 +382,7 @@ static void migrate_start_postcopy(QTestState *who)
 }
 
 static void test_migrate_start(QTestState **from, QTestState **to,
-                               const char *uri)
+                               const char *uri, bool hide_stderr)
 {
     gchar *cmd_src, *cmd_dst;
     char *bootpath = g_strdup_printf("%s/bootsect", tmpfs);
@@ -427,6 +427,17 @@ static void test_migrate_start(QTestState **from, QTestState **to,
 
     g_free(bootpath);
 
+    if (hide_stderr) {
+        gchar *tmp;
+        tmp = g_strdup_printf("%s 2>/dev/null", cmd_src);
+        g_free(cmd_src);
+        cmd_src = tmp;
+
+        tmp = g_strdup_printf("%s 2>/dev/null", cmd_dst);
+        g_free(cmd_dst);
+        cmd_dst = tmp;
+    }
+
     *from = qtest_start(cmd_src);
     g_free(cmd_src);
 
@@ -518,7 +529,7 @@ static void test_migrate(void)
     char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
     QTestState *from, *to;
 
-    test_migrate_start(&from, &to, uri);
+    test_migrate_start(&from, &to, uri, false);
 
     migrate_set_capability(from, "postcopy-ram", "true");
     migrate_set_capability(to, "postcopy-ram", "true");
@@ -560,7 +571,7 @@ static void test_baddest(void)
     const char *status;
     bool failed;
 
-    test_migrate_start(&from, &to, "tcp:0:0");
+    test_migrate_start(&from, &to, "tcp:0:0", true);
     migrate(from, "tcp:0:0");
     do {
         rsp = wait_command(from, "{ 'execute': 'query-migrate' }");
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
index 457984b8e9..b5f88959aa 100755
--- a/tests/qemu-iotests/030
+++ b/tests/qemu-iotests/030
@@ -156,7 +156,7 @@ class TestSingleDrive(iotests.QMPTestCase):
 class TestParallelOps(iotests.QMPTestCase):
     num_ops = 4 # Number of parallel block-stream operations
     num_imgs = num_ops * 2 + 1
-    image_len = num_ops * 1024 * 1024
+    image_len = num_ops * 512 * 1024
     imgs = []
 
     def setUp(self):
@@ -176,14 +176,14 @@ class TestParallelOps(iotests.QMPTestCase):
                      '-o', 'backing_file=%s' % self.imgs[i-1], self.imgs[i])
 
         # Put data into the images we are copying data from
-        for i in range(self.num_imgs / 2):
-            img_index = i * 2 + 1
-            # Alternate between 512k and 1M.
+        odd_img_indexes = [x for x in reversed(range(self.num_imgs)) if x % 2 == 1]
+        for i in range(len(odd_img_indexes)):
+            # Alternate between 256KB and 512KB.
             # This way jobs will not finish in the same order they were created
-            num_kb = 512 + 512 * (i % 2)
+            num_kb = 256 + 256 * (i % 2)
             qemu_io('-f', iotests.imgfmt,
-                    '-c', 'write -P %d %d %d' % (i, i*1024*1024, num_kb * 1024),
-                    self.imgs[img_index])
+                    '-c', 'write -P 0xFF %dk %dk' % (i * 512, num_kb),
+                    self.imgs[odd_img_indexes[i]])
 
         # Attach the drive to the VM
         self.vm = iotests.VM()
@@ -318,12 +318,14 @@ class TestParallelOps(iotests.QMPTestCase):
         self.wait_until_completed(drive='commit-drive0')
 
     # Test a block-stream and a block-commit job in parallel
-    def test_stream_commit(self):
+    # Here the stream job is supposed to finish quickly in order to reproduce
+    # the scenario that triggers the bug fixed in 3d5d319e1221 and 1a63a907507
+    def test_stream_commit_1(self):
         self.assertLessEqual(8, self.num_imgs)
         self.assert_no_active_block_jobs()
 
         # Stream from node0 into node2
-        result = self.vm.qmp('block-stream', device='node2', job_id='node2')
+        result = self.vm.qmp('block-stream', device='node2', base_node='node0', job_id='node2')
         self.assert_qmp(result, 'return', {})
 
         # Commit from the active layer into node3
@@ -348,6 +350,38 @@ class TestParallelOps(iotests.QMPTestCase):
 
         self.assert_no_active_block_jobs()
 
+    # This is similar to test_stream_commit_1 but both jobs are slowed
+    # down so they can run in parallel for a little while.
+    def test_stream_commit_2(self):
+        self.assertLessEqual(8, self.num_imgs)
+        self.assert_no_active_block_jobs()
+
+        # Stream from node0 into node4
+        result = self.vm.qmp('block-stream', device='node4', base_node='node0', job_id='node4', speed=1024*1024)
+        self.assert_qmp(result, 'return', {})
+
+        # Commit from the active layer into node5
+        result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[5], speed=1024*1024)
+        self.assert_qmp(result, 'return', {})
+
+        # Wait for all jobs to be finished.
+        pending_jobs = ['node4', 'drive0']
+        while len(pending_jobs) > 0:
+            for event in self.vm.get_qmp_events(wait=True):
+                if event['event'] == 'BLOCK_JOB_COMPLETED':
+                    node_name = self.dictpath(event, 'data/device')
+                    self.assertTrue(node_name in pending_jobs)
+                    self.assert_qmp_absent(event, 'data/error')
+                    pending_jobs.remove(node_name)
+                if event['event'] == 'BLOCK_JOB_READY':
+                    self.assert_qmp(event, 'data/device', 'drive0')
+                    self.assert_qmp(event, 'data/type', 'commit')
+                    self.assert_qmp_absent(event, 'data/error')
+                    self.assertTrue('drive0' in pending_jobs)
+                    self.vm.qmp('block-job-complete', device='drive0')
+
+        self.assert_no_active_block_jobs()
+
     # Test the base_node parameter
     def test_stream_base_node_name(self):
         self.assert_no_active_block_jobs()
diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out
index 391c8573ca..42314e9c00 100644
--- a/tests/qemu-iotests/030.out
+++ b/tests/qemu-iotests/030.out
@@ -1,5 +1,5 @@
-.......................
+........................
 ----------------------------------------------------------------------
-Ran 23 tests
+Ran 24 tests
 
 OK
diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out
index 003247023e..0871bff564 100644
--- a/tests/qemu-iotests/049.out
+++ b/tests/qemu-iotests/049.out
@@ -166,11 +166,11 @@ qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=off refcount_bits=16
 
 qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42'
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42'
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.42 cluster_size=65536 lazy_refcounts=off refcount_bits=16
 
 qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar'
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar'
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=foobar cluster_size=65536 lazy_refcounts=off refcount_bits=16
 
 == Check preallocation option ==
@@ -182,7 +182,7 @@ qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=metadata lazy_refcounts=off refcount_bits=16
 
 qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234'
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=1234 lazy_refcounts=off refcount_bits=16
 
 == Check encryption option ==
@@ -205,7 +205,7 @@ qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=off refcount_bits=16
 
 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
+qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=on refcount_bits=16
 
 *** done
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
index 40f89eae18..530bbbe6ce 100755
--- a/tests/qemu-iotests/059
+++ b/tests/qemu-iotests/059
@@ -152,9 +152,8 @@ done
 echo
 echo "=== Testing afl image with a very large capacity ==="
 _use_sample_img afl9.vmdk.bz2
-# The sed makes this test pass on machines with little RAM
-# (and also with 32 bit builds)
-_img_info | sed -e 's/Cannot allocate memory/Invalid argument/'
+_img_info | grep -q 'Cannot allocate memory' && _notrun "Insufficent memory, skipped test"
+_img_info
 _cleanup_test_img
 
 # success, all done
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
index 1c2bd85742..4dbe68e950 100755
--- a/tests/qemu-iotests/080
+++ b/tests/qemu-iotests/080
@@ -171,12 +171,32 @@ poke_file "$TEST_IMG" "$offset_l2_table_0" "\x80\x00\x00\xff\xff\xff\x00\x00"
 { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
 
 echo
-echo "== Invalid snapshot L1 table =="
+echo "== Invalid snapshot L1 table offset =="
+_make_test_img 64M
+{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir
+poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00"
+{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
+{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
+{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
+           -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir
+{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir
+_check_test_img
+
+echo
+echo "== Invalid snapshot L1 table size =="
 _make_test_img 64M
 { $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
 { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir
 poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00"
 { $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
+{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
+{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
+           -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir
+{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir
+_check_test_img
 
 # success, all done
 echo "*** done"
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
index 6a7fda1356..4e0f7f7b92 100644
--- a/tests/qemu-iotests/080.out
+++ b/tests/qemu-iotests/080.out
@@ -18,18 +18,18 @@ can't open device TEST_DIR/t.qcow2: Reference count table too large
 
 == Misaligned refcount table ==
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-can't open device TEST_DIR/t.qcow2: Invalid reference count table offset
+can't open device TEST_DIR/t.qcow2: Reference count table offset invalid
 
 == Huge refcount offset ==
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-can't open device TEST_DIR/t.qcow2: Invalid reference count table offset
+can't open device TEST_DIR/t.qcow2: Reference count table offset invalid
 
 == Invalid snapshot table ==
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-can't open device TEST_DIR/t.qcow2: Too many snapshots
-can't open device TEST_DIR/t.qcow2: Too many snapshots
-can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset
-can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset
+can't open device TEST_DIR/t.qcow2: Snapshot table too large
+can't open device TEST_DIR/t.qcow2: Snapshot table too large
+can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid
+can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid
 
 == Hitting snapshot table size limit ==
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
@@ -41,8 +41,8 @@ read 512/512 bytes at offset 0
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
 can't open device TEST_DIR/t.qcow2: Active L1 table too large
 can't open device TEST_DIR/t.qcow2: Active L1 table too large
-can't open device TEST_DIR/t.qcow2: Invalid L1 table offset
-can't open device TEST_DIR/t.qcow2: Invalid L1 table offset
+can't open device TEST_DIR/t.qcow2: Active L1 table offset invalid
+can't open device TEST_DIR/t.qcow2: Active L1 table offset invalid
 
 == Invalid L1 table (with internal snapshot in the image) ==
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
@@ -59,9 +59,49 @@ wrote 512/512 bytes at offset 0
 qemu-img: Could not create snapshot 'test': -27 (File too large)
 qemu-img: Could not create snapshot 'test': -11 (Resource temporarily unavailable)
 
-== Invalid snapshot L1 table ==
+== Invalid snapshot L1 table offset ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid
+qemu-img: Snapshot L1 table offset invalid
+qemu-img: Error while amending options: Invalid argument
+Failed to flush the refcount block cache: Invalid argument
+write failed: Invalid argument
+qemu-img: Snapshot L1 table offset invalid
+qemu-img: Could not apply snapshot 'test': Failed to load snapshot: Invalid argument
+qemu-img: Could not delete snapshot 'test': Snapshot L1 table offset invalid
+ERROR snapshot 1 (test) l1_offset=0x400200: L1 table is not cluster aligned; snapshot table entry corrupted
+Leaked cluster 4 refcount=2 reference=1
+Leaked cluster 5 refcount=2 reference=1
+Leaked cluster 6 refcount=1 reference=0
+
+1 errors were found on the image.
+Data may be corrupted, or further writes to the image may corrupt it.
+
+3 leaked clusters were found on the image.
+This means waste of disk space, but no harm to data.
+
+== Invalid snapshot L1 table size ==
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
 wrote 512/512 bytes at offset 0
 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 qemu-img: Failed to load snapshot: Snapshot L1 table too large
+qemu-img: Snapshot L1 table too large
+qemu-img: Error while amending options: File too large
+Failed to flush the refcount block cache: File too large
+write failed: File too large
+qemu-img: Snapshot L1 table too large
+qemu-img: Could not apply snapshot 'test': Failed to load snapshot: File too large
+qemu-img: Could not delete snapshot 'test': Snapshot L1 table too large
+ERROR snapshot 1 (test) l1_size=0x10000000: L1 table is too large; snapshot table entry corrupted
+Leaked cluster 4 refcount=2 reference=1
+Leaked cluster 5 refcount=2 reference=1
+Leaked cluster 6 refcount=1 reference=0
+
+1 errors were found on the image.
+Data may be corrupted, or further writes to the image may corrupt it.
+
+3 leaked clusters were found on the image.
+This means waste of disk space, but no harm to data.
 *** done
diff --git a/tests/qemu-iotests/096 b/tests/qemu-iotests/096
index aeeb3753cf..aeeb3753cf 100644..100755
--- a/tests/qemu-iotests/096
+++ b/tests/qemu-iotests/096
diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out
index 81b04d1452..86f041075d 100644
--- a/tests/qemu-iotests/112.out
+++ b/tests/qemu-iotests/112.out
@@ -21,9 +21,9 @@ refcount bits: 16
 
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
 refcount bits: 16
-qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater)
+qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater)
+qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
 
 === Snapshot limit on refcount_bits=1 ===
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index 8e76e62f93..8e76e62f93 100644..100755
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
index 9e87e1c8d9..9e87e1c8d9 100644..100755
--- a/tests/qemu-iotests/129
+++ b/tests/qemu-iotests/129
diff --git a/tests/qemu-iotests/132 b/tests/qemu-iotests/132
index f53ef6e391..f53ef6e391 100644..100755
--- a/tests/qemu-iotests/132
+++ b/tests/qemu-iotests/132
diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136
index 88b97ea7c6..88b97ea7c6 100644..100755
--- a/tests/qemu-iotests/136
+++ b/tests/qemu-iotests/136
diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139
index cc7fe337f3..cc7fe337f3 100644..100755
--- a/tests/qemu-iotests/139
+++ b/tests/qemu-iotests/139
diff --git a/tests/qemu-iotests/148 b/tests/qemu-iotests/148
index e01b061fe7..e01b061fe7 100644..100755
--- a/tests/qemu-iotests/148
+++ b/tests/qemu-iotests/148
diff --git a/tests/qemu-iotests/152 b/tests/qemu-iotests/152
index fec546d033..fec546d033 100644..100755
--- a/tests/qemu-iotests/152
+++ b/tests/qemu-iotests/152
diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153
index fa25eb24bd..adfd02695b 100755
--- a/tests/qemu-iotests/153
+++ b/tests/qemu-iotests/153
@@ -32,6 +32,7 @@ _cleanup()
 {
     _cleanup_test_img
     rm -f "${TEST_IMG}.base"
+    rm -f "${TEST_IMG}.overlay"
     rm -f "${TEST_IMG}.convert"
     rm -f "${TEST_IMG}.a"
     rm -f "${TEST_IMG}.b"
@@ -177,8 +178,6 @@ rm -f "${TEST_IMG}.lnk" &>/dev/null
 ln -s ${TEST_IMG} "${TEST_IMG}.lnk" || echo "Failed to create link"
 _run_qemu_with_images "${TEST_IMG}.lnk" "${TEST_IMG}"
 
-echo
-echo "== Closing an image should unlock it =="
 _launch_qemu
 
 _send_qemu_cmd $QEMU_HANDLE \
@@ -193,7 +192,10 @@ _send_qemu_cmd $QEMU_HANDLE \
 
 _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
 
-echo "Closing drive"
+echo "Creating overlay with qemu-img when the guest is running should be allowed"
+_run_cmd $QEMU_IMG create -f $IMGFMT -b "${TEST_IMG}" "${TEST_IMG}.overlay"
+
+echo "== Closing an image should unlock it =="
 _send_qemu_cmd $QEMU_HANDLE \
     "{ 'execute': 'human-monitor-command',
        'arguments': { 'command-line': 'drive_del d0' } }" \
diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out
index 5b917b177c..34309cfb20 100644
--- a/tests/qemu-iotests/153.out
+++ b/tests/qemu-iotests/153.out
@@ -372,15 +372,16 @@ Is another process using the image?
 == Symbolic link ==
 QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock
 Is another process using the image?
-
-== Closing an image should unlock it ==
 {"return": {}}
 Adding drive
 
 _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
 Is another process using the image?
-Closing drive
+Creating overlay with qemu-img when the guest is running should be allowed
+
+_qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.overlay
+== Closing an image should unlock it ==
 
 _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
 Adding two and closing one
diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163
index 403842354e..403842354e 100644..100755
--- a/tests/qemu-iotests/163
+++ b/tests/qemu-iotests/163
diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203
index 2c811917d8..4874a1a0d8 100755
--- a/tests/qemu-iotests/203
+++ b/tests/qemu-iotests/203
@@ -49,11 +49,18 @@ with iotests.FilePath('disk0.img') as disk0_img_path, \
                        node_name='drive1-node', iothread='iothread0',
                        force=True))
 
+    iotests.log('Enabling migration QMP events...')
+    iotests.log(vm.qmp('migrate-set-capabilities', capabilities=[
+        {
+            'capability': 'events',
+            'state': True
+        }
+    ]))
+
     iotests.log('Starting migration...')
     iotests.log(vm.qmp('migrate', uri='exec:cat >/dev/null'))
     while True:
-        vm.get_qmp_event(wait=60.0)
-        result = vm.qmp('query-migrate')
-        status = result.get('return', {}).get('status', None)
-        if status == 'completed':
+        event = vm.event_wait('MIGRATION')
+        iotests.log(event, filters=[iotests.filter_qmp_event])
+        if event['data']['status'] == 'completed':
             break
diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out
index 3f1ff900e4..1a11f0975c 100644
--- a/tests/qemu-iotests/203.out
+++ b/tests/qemu-iotests/203.out
@@ -2,5 +2,10 @@ Launching VM...
 Setting IOThreads...
 {u'return': {}}
 {u'return': {}}
+Enabling migration QMP events...
+{u'return': {}}
 Starting migration...
 {u'return': {}}
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'}
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'}
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'}
diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205
index e7b2eae51d..e7b2eae51d 100644..100755
--- a/tests/qemu-iotests/205
+++ b/tests/qemu-iotests/205
diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206
new file mode 100755
index 0000000000..0a18b2b19a
--- /dev/null
+++ b/tests/qemu-iotests/206
@@ -0,0 +1,436 @@
+#!/bin/bash
+#
+# Test qcow2 and file image creation
+#
+# Copyright (C) 2018 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`
+status=1	# failure is the default!
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+function do_run_qemu()
+{
+    echo Testing: "$@"
+    $QEMU -nographic -qmp stdio -serial none "$@"
+    echo
+}
+
+function run_qemu()
+{
+    do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
+                          | _filter_qemu | _filter_imgfmt \
+                          | _filter_actual_image_size
+}
+
+echo
+echo "=== Successful image creation (defaults) ==="
+echo
+
+size=$((128 * 1024 * 1024))
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "file",
+      "filename": "$TEST_IMG",
+      "size": 0
+  }
+}
+{ "execute": "blockdev-add",
+  "arguments": {
+      "driver": "file",
+      "node-name": "imgfile",
+      "filename": "$TEST_IMG"
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "imgfile",
+      "size": $size
+  }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific
+
+echo
+echo "=== Successful image creation (inline blockdev-add, explicit defaults) ==="
+echo
+
+# Choose a different size to show that we got a new image
+size=$((64 * 1024 * 1024))
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "file",
+      "filename": "$TEST_IMG",
+      "size": 0,
+      "preallocation": "off",
+      "nocow": false
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": {
+          "driver": "file",
+          "filename": "$TEST_IMG"
+      },
+      "size": $size,
+      "version": "v3",
+      "cluster-size": 65536,
+      "preallocation": "off",
+      "lazy-refcounts": false,
+      "refcount-bits": 16
+  }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific
+
+echo
+echo "=== Successful image creation (v3 non-default options) ==="
+echo
+
+# Choose a different size to show that we got a new image
+size=$((32 * 1024 * 1024))
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "file",
+      "filename": "$TEST_IMG",
+      "size": 0,
+      "preallocation": "falloc",
+      "nocow": true
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": {
+          "driver": "file",
+          "filename": "$TEST_IMG"
+      },
+      "size": $size,
+      "version": "v3",
+      "cluster-size": 2097152,
+      "preallocation": "metadata",
+      "lazy-refcounts": true,
+      "refcount-bits": 1
+  }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific
+
+echo
+echo "=== Successful image creation (v2 non-default options) ==="
+echo
+
+mv $TEST_IMG $TEST_IMG.base
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "file",
+      "filename": "$TEST_IMG",
+      "size": 0
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": {
+          "driver": "file",
+          "filename": "$TEST_IMG"
+      },
+      "size": $size,
+      "backing-file": "$TEST_IMG.base",
+      "backing-fmt": "qcow2",
+      "version": "v2",
+      "cluster-size": 512
+  }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific
+
+echo
+echo "=== Successful image creation (encrypted) ==="
+echo
+
+run_qemu -object secret,id=keysec0,data="foo" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": {
+          "driver": "file",
+          "filename": "$TEST_IMG"
+      },
+      "size": $size,
+      "encrypt": {
+          "format": "luks",
+          "key-secret": "keysec0",
+          "cipher-alg": "twofish-128",
+          "cipher-mode": "ctr",
+          "ivgen-alg": "plain64",
+          "ivgen-hash-alg": "md5",
+          "hash-alg": "sha1",
+          "iter-time": 10
+      }
+  }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific | _filter_img_info --format-specific
+
+echo
+echo "=== Invalid BlockdevRef ==="
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "this doesn't exist",
+      "size": $size
+  }
+}
+{ "execute": "quit" }
+EOF
+
+
+echo
+echo "=== Invalid sizes ==="
+echo
+
+# TODO Negative image sizes aren't handled correctly, but this is a problem
+# with QAPI's implementation of the 'size' type and affects other commands as
+# well. Once this is fixed, we may want to add a test case here.
+
+# 1. Misaligned image size
+# 2. 2^64 - 512
+# 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this)
+# 4. 2^63 - 512 (generally valid, but qcow2 can't handle images this size)
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 1234
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 18446744073709551104
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 9223372036854775808
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 9223372036854775296
+  }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Invalid version ==="
+echo
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "version": "v1"
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "version": "v2",
+      "lazy-refcounts": true
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "version": "v2",
+      "refcount-bits": 8
+  }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Invalid backing file options ==="
+echo
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "backing-file": "/dev/null",
+      "preallocation": "full"
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "backing-fmt": "$IMGFMT"
+  }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Invalid cluster size ==="
+echo
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "cluster-size": 1234
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "cluster-size": 128
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "cluster-size": 4194304
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "cluster-size": 0
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 281474976710656,
+      "cluster-size": 512
+  }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Invalid refcount width ==="
+echo
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "refcount-bits": 128
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "refcount-bits": 0
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "$IMGFMT",
+      "file": "node0",
+      "size": 67108864,
+      "refcount-bits": 7
+  }
+}
+{ "execute": "quit" }
+EOF
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out
new file mode 100644
index 0000000000..042342ae9d
--- /dev/null
+++ b/tests/qemu-iotests/206.out
@@ -0,0 +1,209 @@
+QA output created by 206
+
+=== Successful image creation (defaults) ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 128M (134217728 bytes)
+cluster_size: 65536
+Format specific information:
+    compat: 1.1
+    lazy refcounts: false
+    refcount bits: 16
+    corrupt: false
+
+=== Successful image creation (inline blockdev-add, explicit defaults) ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 64M (67108864 bytes)
+cluster_size: 65536
+Format specific information:
+    compat: 1.1
+    lazy refcounts: false
+    refcount bits: 16
+    corrupt: false
+
+=== Successful image creation (v3 non-default options) ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32M (33554432 bytes)
+cluster_size: 2097152
+Format specific information:
+    compat: 1.1
+    lazy refcounts: true
+    refcount bits: 1
+    corrupt: false
+
+=== Successful image creation (v2 non-default options) ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32M (33554432 bytes)
+cluster_size: 512
+backing file: TEST_DIR/t.IMGFMT.base
+backing file format: IMGFMT
+Format specific information:
+    compat: 0.10
+    refcount bits: 16
+
+=== Successful image creation (encrypted) ===
+
+Testing: -object secret,id=keysec0,data=foo
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32M (33554432 bytes)
+Format specific information:
+    compat: 1.1
+    lazy refcounts: false
+    refcount bits: 16
+    encrypt:
+        ivgen alg: plain64
+        hash alg: sha1
+        cipher alg: twofish-128
+        uuid: 00000000-0000-0000-0000-000000000000
+        format: luks
+        cipher mode: ctr
+        slots:
+            [0]:
+                active: true
+                iters: 1024
+                key offset: 4096
+                stripes: 4000
+            [1]:
+                active: false
+                key offset: 69632
+            [2]:
+                active: false
+                key offset: 135168
+            [3]:
+                active: false
+                key offset: 200704
+            [4]:
+                active: false
+                key offset: 266240
+            [5]:
+                active: false
+                key offset: 331776
+            [6]:
+                active: false
+                key offset: 397312
+            [7]:
+                active: false
+                key offset: 462848
+        payload offset: 528384
+        master key iters: 1024
+    corrupt: false
+
+=== Invalid BlockdevRef ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Invalid sizes ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Image size must be a multiple of 512 bytes"}}
+{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}}
+{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}}
+{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Invalid version ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}}
+{"error": {"class": "GenericError", "desc": "Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)"}}
+{"error": {"class": "GenericError", "desc": "Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)"}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Invalid backing file options ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Backing file and preallocation cannot be used at the same time"}}
+{"error": {"class": "GenericError", "desc": "Backing format cannot be used without backing file"}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Invalid cluster size ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
+{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Invalid refcount width ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}}
+{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}}
+{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+*** done
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
new file mode 100755
index 0000000000..f5c77852d1
--- /dev/null
+++ b/tests/qemu-iotests/207
@@ -0,0 +1,261 @@
+#!/bin/bash
+#
+# Test ssh image creation
+#
+# Copyright (C) 2018 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`
+status=1	# failure is the default!
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt raw
+_supported_proto ssh
+_supported_os Linux
+
+function do_run_qemu()
+{
+    echo Testing: "$@"
+    $QEMU -nographic -qmp stdio -serial none "$@"
+    echo
+}
+
+function run_qemu()
+{
+    do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
+                          | _filter_qemu | _filter_imgfmt \
+                          | _filter_actual_image_size
+}
+
+echo
+echo "=== Successful image creation (defaults) ==="
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "ssh",
+      "location": {
+          "path": "$TEST_IMG_FILE",
+          "server": {
+              "host": "127.0.0.1",
+              "port": "22"
+          }
+      },
+      "size": 4194304
+  }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+echo
+TEST_IMG=$TEST_IMG_FILE _img_info | _filter_img_info
+
+echo
+echo "=== Test host-key-check options ==="
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "ssh",
+      "location": {
+          "path": "$TEST_IMG_FILE",
+          "server": {
+              "host": "127.0.0.1",
+              "port": "22"
+          },
+          "host-key-check": {
+              "mode": "none"
+          }
+      },
+      "size": 8388608
+  }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "ssh",
+      "location": {
+          "path": "$TEST_IMG_FILE",
+          "server": {
+              "host": "127.0.0.1",
+              "port": "22"
+          },
+          "host-key-check": {
+              "mode": "known_hosts"
+          }
+      },
+      "size": 4194304
+  }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+
+
+key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" |
+      cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1)
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "ssh",
+      "location": {
+          "path": "$TEST_IMG_FILE",
+          "server": {
+              "host": "127.0.0.1",
+              "port": "22"
+          },
+          "host-key-check": {
+              "mode": "hash",
+              "type": "md5",
+              "hash": "wrong"
+          }
+      },
+      "size": 8388608
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "ssh",
+      "location": {
+          "path": "$TEST_IMG_FILE",
+          "server": {
+              "host": "127.0.0.1",
+              "port": "22"
+          },
+          "host-key-check": {
+              "mode": "hash",
+              "type": "md5",
+              "hash": "$key"
+          }
+      },
+      "size": 8388608
+  }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+
+
+key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" |
+      cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1)
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "ssh",
+      "location": {
+          "path": "$TEST_IMG_FILE",
+          "server": {
+              "host": "127.0.0.1",
+              "port": "22"
+          },
+          "host-key-check": {
+              "mode": "hash",
+              "type": "sha1",
+              "hash": "wrong"
+          }
+      },
+      "size": 4194304
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "ssh",
+      "location": {
+          "path": "$TEST_IMG_FILE",
+          "server": {
+              "host": "127.0.0.1",
+              "port": "22"
+          },
+          "host-key-check": {
+              "mode": "hash",
+              "type": "sha1",
+              "hash": "$key"
+          }
+      },
+      "size": 4194304
+  }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+
+echo
+echo "=== Invalid path and user ==="
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "ssh",
+      "location": {
+          "path": "/this/is/not/an/existing/path",
+          "server": {
+              "host": "127.0.0.1",
+              "port": "22"
+          }
+      },
+      "size": 4194304
+  }
+}
+{ "execute": "x-blockdev-create",
+  "arguments": {
+      "driver": "ssh",
+      "location": {
+          "path": "$TEST_IMG_FILE",
+          "user": "invalid user",
+          "server": {
+              "host": "127.0.0.1",
+              "port": "22"
+          }
+      },
+      "size": 4194304
+  }
+}
+{ "execute": "quit" }
+EOF
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out
new file mode 100644
index 0000000000..417deee970
--- /dev/null
+++ b/tests/qemu-iotests/207.out
@@ -0,0 +1,75 @@
+QA output created by 207
+
+=== Successful image creation (defaults) ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
+file format: IMGFMT
+virtual size: 4.0M (4194304 bytes)
+
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 4.0M (4194304 bytes)
+
+=== Test host-key-check options ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
+file format: IMGFMT
+virtual size: 8.0M (8388608 bytes)
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
+file format: IMGFMT
+virtual size: 4.0M (4194304 bytes)
+Testing:
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
+file format: IMGFMT
+virtual size: 8.0M (8388608 bytes)
+Testing:
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
+file format: IMGFMT
+virtual size: 4.0M (4194304 bytes)
+
+=== Invalid path and user ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)"}}
+{"error": {"class": "GenericError", "desc": "failed to authenticate using publickey authentication and the identities held by your ssh-agent"}}
+{"return": {}}
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index a2dfe79d86..c401791fcd 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -202,3 +202,5 @@
 203 rw auto
 204 rw auto quick
 205 rw auto quick
+206 rw auto
+207 rw auto
diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
index 5d5a3daa7b..2c422abcd4 100644
--- a/tests/test-qemu-opts.c
+++ b/tests/test-qemu-opts.c
@@ -10,6 +10,7 @@
 #include "qemu/osdep.h"
 #include "qemu/cutils.h"
 #include "qemu/option.h"
+#include "qemu/option_int.h"
 #include "qapi/error.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qstring.h"
@@ -23,6 +24,8 @@ static QemuOptsList opts_list_01 = {
         {
             .name = "str1",
             .type = QEMU_OPT_STRING,
+            .help = "Help texts are preserved in qemu_opts_append",
+            .def_value_str = "default",
         },{
             .name = "str2",
             .type = QEMU_OPT_STRING,
@@ -32,6 +35,7 @@ static QemuOptsList opts_list_01 = {
         },{
             .name = "number1",
             .type = QEMU_OPT_NUMBER,
+            .help = "Having help texts only for some options is okay",
         },{
             .name = "number2",
             .type = QEMU_OPT_NUMBER,
@@ -743,6 +747,250 @@ static void test_opts_parse_size(void)
     qemu_opts_reset(&opts_list_02);
 }
 
+static void append_verify_list_01(QemuOptDesc *desc, bool with_overlapping)
+{
+    int i = 0;
+
+    if (with_overlapping) {
+        g_assert_cmpstr(desc[i].name, ==, "str1");
+        g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
+        g_assert_cmpstr(desc[i].help, ==,
+                        "Help texts are preserved in qemu_opts_append");
+        g_assert_cmpstr(desc[i].def_value_str, ==, "default");
+        i++;
+
+        g_assert_cmpstr(desc[i].name, ==, "str2");
+        g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
+        g_assert_cmpstr(desc[i].help, ==, NULL);
+        g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+        i++;
+    }
+
+    g_assert_cmpstr(desc[i].name, ==, "str3");
+    g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
+    g_assert_cmpstr(desc[i].help, ==, NULL);
+    g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+    i++;
+
+    g_assert_cmpstr(desc[i].name, ==, "number1");
+    g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER);
+    g_assert_cmpstr(desc[i].help, ==,
+                    "Having help texts only for some options is okay");
+    g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+    i++;
+
+    g_assert_cmpstr(desc[i].name, ==, "number2");
+    g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER);
+    g_assert_cmpstr(desc[i].help, ==, NULL);
+    g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+    i++;
+
+    g_assert_cmpstr(desc[i].name, ==, NULL);
+}
+
+static void append_verify_list_02(QemuOptDesc *desc)
+{
+    int i = 0;
+
+    g_assert_cmpstr(desc[i].name, ==, "str1");
+    g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
+    g_assert_cmpstr(desc[i].help, ==, NULL);
+    g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+    i++;
+
+    g_assert_cmpstr(desc[i].name, ==, "str2");
+    g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
+    g_assert_cmpstr(desc[i].help, ==, NULL);
+    g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+    i++;
+
+    g_assert_cmpstr(desc[i].name, ==, "bool1");
+    g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL);
+    g_assert_cmpstr(desc[i].help, ==, NULL);
+    g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+    i++;
+
+    g_assert_cmpstr(desc[i].name, ==, "bool2");
+    g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL);
+    g_assert_cmpstr(desc[i].help, ==, NULL);
+    g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+    i++;
+
+    g_assert_cmpstr(desc[i].name, ==, "size1");
+    g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE);
+    g_assert_cmpstr(desc[i].help, ==, NULL);
+    g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+    i++;
+
+    g_assert_cmpstr(desc[i].name, ==, "size2");
+    g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE);
+    g_assert_cmpstr(desc[i].help, ==, NULL);
+    g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+    i++;
+
+    g_assert_cmpstr(desc[i].name, ==, "size3");
+    g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE);
+    g_assert_cmpstr(desc[i].help, ==, NULL);
+    g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
+}
+
+static void test_opts_append_to_null(void)
+{
+    QemuOptsList *merged;
+
+    merged = qemu_opts_append(NULL, &opts_list_01);
+    g_assert(merged != &opts_list_01);
+
+    g_assert_cmpstr(merged->name, ==, NULL);
+    g_assert_cmpstr(merged->implied_opt_name, ==, NULL);
+    g_assert_false(merged->merge_lists);
+
+    append_verify_list_01(merged->desc, true);
+
+    qemu_opts_free(merged);
+}
+
+static void test_opts_append(void)
+{
+    QemuOptsList *first, *merged;
+
+    first = qemu_opts_append(NULL, &opts_list_02);
+    merged = qemu_opts_append(first, &opts_list_01);
+    g_assert(first != &opts_list_02);
+    g_assert(merged != &opts_list_01);
+
+    g_assert_cmpstr(merged->name, ==, NULL);
+    g_assert_cmpstr(merged->implied_opt_name, ==, NULL);
+    g_assert_false(merged->merge_lists);
+
+    append_verify_list_02(&merged->desc[0]);
+    append_verify_list_01(&merged->desc[7], false);
+
+    qemu_opts_free(merged);
+}
+
+static void test_opts_to_qdict_basic(void)
+{
+    QemuOpts *opts;
+    QDict *dict;
+
+    opts = qemu_opts_parse(&opts_list_01, "str1=foo,str2=,str3=bar,number1=42",
+                           false, &error_abort);
+    g_assert(opts != NULL);
+
+    dict = qemu_opts_to_qdict(opts, NULL);
+    g_assert(dict != NULL);
+
+    g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
+    g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
+    g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar");
+    g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42");
+    g_assert_false(qdict_haskey(dict, "number2"));
+
+    QDECREF(dict);
+    qemu_opts_del(opts);
+}
+
+static void test_opts_to_qdict_filtered(void)
+{
+    QemuOptsList *first, *merged;
+    QemuOpts *opts;
+    QDict *dict;
+
+    first = qemu_opts_append(NULL, &opts_list_02);
+    merged = qemu_opts_append(first, &opts_list_01);
+
+    opts = qemu_opts_parse(merged,
+                           "str1=foo,str2=,str3=bar,bool1=off,number1=42",
+                           false, &error_abort);
+    g_assert(opts != NULL);
+
+    /* Convert to QDict without deleting from opts */
+    dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, false);
+    g_assert(dict != NULL);
+    g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
+    g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
+    g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar");
+    g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42");
+    g_assert_false(qdict_haskey(dict, "number2"));
+    g_assert_false(qdict_haskey(dict, "bool1"));
+    QDECREF(dict);
+
+    dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false);
+    g_assert(dict != NULL);
+    g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
+    g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
+    g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off");
+    g_assert_false(qdict_haskey(dict, "str3"));
+    g_assert_false(qdict_haskey(dict, "number1"));
+    g_assert_false(qdict_haskey(dict, "number2"));
+    QDECREF(dict);
+
+    /* Now delete converted options from opts */
+    dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true);
+    g_assert(dict != NULL);
+    g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
+    g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
+    g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar");
+    g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42");
+    g_assert_false(qdict_haskey(dict, "number2"));
+    g_assert_false(qdict_haskey(dict, "bool1"));
+    QDECREF(dict);
+
+    dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true);
+    g_assert(dict != NULL);
+    g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off");
+    g_assert_false(qdict_haskey(dict, "str1"));
+    g_assert_false(qdict_haskey(dict, "str2"));
+    g_assert_false(qdict_haskey(dict, "str3"));
+    g_assert_false(qdict_haskey(dict, "number1"));
+    g_assert_false(qdict_haskey(dict, "number2"));
+    QDECREF(dict);
+
+    g_assert_true(QTAILQ_EMPTY(&opts->head));
+
+    qemu_opts_del(opts);
+    qemu_opts_free(merged);
+}
+
+static void test_opts_to_qdict_duplicates(void)
+{
+    QemuOpts *opts;
+    QemuOpt *opt;
+    QDict *dict;
+
+    opts = qemu_opts_parse(&opts_list_03, "foo=a,foo=b", false, &error_abort);
+    g_assert(opts != NULL);
+
+    /* Verify that opts has two options with the same name */
+    opt = QTAILQ_FIRST(&opts->head);
+    g_assert_cmpstr(opt->name, ==, "foo");
+    g_assert_cmpstr(opt->str , ==, "a");
+
+    opt = QTAILQ_NEXT(opt, next);
+    g_assert_cmpstr(opt->name, ==, "foo");
+    g_assert_cmpstr(opt->str , ==, "b");
+
+    opt = QTAILQ_NEXT(opt, next);
+    g_assert(opt == NULL);
+
+    /* In the conversion to QDict, the last one wins */
+    dict = qemu_opts_to_qdict(opts, NULL);
+    g_assert(dict != NULL);
+    g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b");
+    QDECREF(dict);
+
+    /* The last one still wins if entries are deleted, and both are deleted */
+    dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true);
+    g_assert(dict != NULL);
+    g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b");
+    QDECREF(dict);
+
+    g_assert_true(QTAILQ_EMPTY(&opts->head));
+
+    qemu_opts_del(opts);
+}
+
 int main(int argc, char *argv[])
 {
     register_opts();
@@ -761,6 +1009,11 @@ int main(int argc, char *argv[])
     g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool);
     g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number);
     g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size);
+    g_test_add_func("/qemu-opts/append_to_null", test_opts_append_to_null);
+    g_test_add_func("/qemu-opts/append", test_opts_append);
+    g_test_add_func("/qemu-opts/to_qdict/basic", test_opts_to_qdict_basic);
+    g_test_add_func("/qemu-opts/to_qdict/filtered", test_opts_to_qdict_filtered);
+    g_test_add_func("/qemu-opts/to_qdict/duplicates", test_opts_to_qdict_duplicates);
     g_test_run();
     return 0;
 }
diff --git a/trace-events b/trace-events
index 89fcad0fd1..855b0ab240 100644
--- a/trace-events
+++ b/trace-events
@@ -68,9 +68,9 @@ memory_region_tb_read(int cpu_index, uint64_t addr, uint64_t value, unsigned siz
 memory_region_tb_write(int cpu_index, uint64_t addr, uint64_t value, unsigned size) "cpu %d addr 0x%"PRIx64" value 0x%"PRIx64" size %u"
 memory_region_ram_device_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u"
 memory_region_ram_device_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u"
-flatview_new(FlatView *view, MemoryRegion *root) "%p (root %p)"
-flatview_destroy(FlatView *view, MemoryRegion *root) "%p (root %p)"
-flatview_destroy_rcu(FlatView *view, MemoryRegion *root) "%p (root %p)"
+flatview_new(void *view, void *root) "%p (root %p)"
+flatview_destroy(void *view, void *root) "%p (root %p)"
+flatview_destroy_rcu(void *view, void *root) "%p (root %p)"
 
 # gdbstub.c
 gdbstub_op_start(const char *device) "Starting gdbstub using device %s"
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index dcd54a5287..cc784346cb 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -38,26 +38,27 @@ common-obj-$(CONFIG_GTK) += gtk.mo
 gtk.mo-objs := gtk.o
 gtk.mo-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS)
 gtk.mo-libs := $(GTK_LIBS) $(VTE_LIBS)
+ifeq ($(CONFIG_OPENGL),y)
+gtk.mo-objs += gtk-egl.o
+gtk.mo-libs += $(OPENGL_LIBS)
+ifeq ($(CONFIG_GTK_GL),y)
+gtk.mo-objs += gtk-gl-area.o
+endif
+endif
 
 common-obj-$(CONFIG_CURSES) += curses.mo
 curses.mo-objs := curses.o
 curses.mo-cflags := $(CURSES_CFLAGS)
 curses.mo-libs := $(CURSES_LIBS)
 
-ifeq ($(CONFIG_OPENGL),y)
-common-obj-y += shader.o
-common-obj-y += console-gl.o
-common-obj-y += egl-helpers.o
-common-obj-y += egl-context.o
+common-obj-$(CONFIG_OPENGL) += shader.o
+common-obj-$(CONFIG_OPENGL) += console-gl.o
+common-obj-$(CONFIG_OPENGL) += egl-helpers.o
+common-obj-$(CONFIG_OPENGL) += egl-context.o
 common-obj-$(CONFIG_OPENGL_DMABUF) += egl-headless.o
-ifeq ($(CONFIG_GTK_GL),y)
-gtk.mo-objs += gtk-gl-area.o
-else
-gtk.mo-objs += gtk-egl.o
-gtk.mo-libs += $(OPENGL_LIBS)
-endif
-endif
 
 shader.o-libs += $(OPENGL_LIBS)
 console-gl.o-libs += $(OPENGL_LIBS)
 egl-helpers.o-libs += $(OPENGL_LIBS)
+egl-context.o-libs += $(OPENGL_LIBS)
+egl-headless.o-libs += $(OPENGL_LIBS)
diff --git a/ui/console.c b/ui/console.c
index 6ab4ff3baf..a8868fc04f 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -344,14 +344,28 @@ write_err:
     goto out;
 }
 
-void qmp_screendump(const char *filename, Error **errp)
+void qmp_screendump(const char *filename, bool has_device, const char *device,
+                    bool has_head, int64_t head, Error **errp)
 {
-    QemuConsole *con = qemu_console_lookup_by_index(0);
+    QemuConsole *con;
     DisplaySurface *surface;
 
-    if (con == NULL) {
-        error_setg(errp, "There is no QemuConsole I can screendump from.");
-        return;
+    if (has_device) {
+        con = qemu_console_lookup_by_device_name(device, has_head ? head : 0,
+                                                 errp);
+        if (!con) {
+            return;
+        }
+    } else {
+        if (has_head) {
+            error_setg(errp, "'head' must be specified together with 'device'");
+            return;
+        }
+        con = qemu_console_lookup_by_index(0);
+        if (!con) {
+            error_setg(errp, "There is no console to take a screendump from");
+            return;
+        }
     }
 
     graphic_hw_update(con);
@@ -1039,8 +1053,10 @@ void console_select(unsigned int index)
                     dcl->ops->dpy_gfx_switch(dcl, s->surface);
                 }
             }
-            dpy_gfx_update(s, 0, 0, surface_width(s->surface),
-                           surface_height(s->surface));
+            if (s->surface) {
+                dpy_gfx_update(s, 0, 0, surface_width(s->surface),
+                               surface_height(s->surface));
+            }
         }
         if (ds->have_text) {
             dpy_text_resize(s, s->width, s->height);
@@ -1370,8 +1386,8 @@ DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height,
     return surface;
 }
 
-static DisplaySurface *qemu_create_message_surface(int w, int h,
-                                                   const char *msg)
+DisplaySurface *qemu_create_message_surface(int w, int h,
+                                            const char *msg)
 {
     DisplaySurface *surface = qemu_create_displaysurface(w, h);
     pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index eb86c26a1d..9390c6762e 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -19,6 +19,7 @@
 #include "ui/console.h"
 #include "ui/gtk.h"
 #include "ui/egl-helpers.h"
+#include "ui/shader.h"
 
 #include "sysemu/sysemu.h"
 
@@ -194,6 +195,58 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
                          backing_id, false);
 }
 
+void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
+                           QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_OPENGL_DMABUF
+    egl_dmabuf_import_texture(dmabuf);
+    if (!dmabuf->texture) {
+        return;
+    }
+
+    gd_egl_scanout_texture(dcl, dmabuf->texture,
+                           false, dmabuf->width, dmabuf->height,
+                           0, 0, dmabuf->width, dmabuf->height);
+#endif
+}
+
+void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
+                          QemuDmaBuf *dmabuf, bool have_hot,
+                          uint32_t hot_x, uint32_t hot_y)
+{
+#ifdef CONFIG_OPENGL_DMABUF
+    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+    if (dmabuf) {
+        egl_dmabuf_import_texture(dmabuf);
+        if (!dmabuf->texture) {
+            return;
+        }
+        egl_fb_setup_for_tex(&vc->gfx.cursor_fb, dmabuf->width, dmabuf->height,
+                             dmabuf->texture, false);
+    } else {
+        egl_fb_destroy(&vc->gfx.cursor_fb);
+    }
+#endif
+}
+
+void gd_egl_cursor_position(DisplayChangeListener *dcl,
+                            uint32_t pos_x, uint32_t pos_y)
+{
+    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+    vc->gfx.cursor_x = pos_x;
+    vc->gfx.cursor_y = pos_y;
+}
+
+void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
+                           QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_OPENGL_DMABUF
+    egl_dmabuf_release_texture(dmabuf);
+#endif
+}
+
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
                           uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
@@ -214,7 +267,15 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
     window = gtk_widget_get_window(vc->gfx.drawing_area);
     gdk_drawable_get_size(window, &ww, &wh);
     egl_fb_setup_default(&vc->gfx.win_fb, ww, wh);
-    egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
+    if (vc->gfx.cursor_fb.texture) {
+        egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb,
+                         vc->gfx.y0_top);
+        egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb,
+                          vc->gfx.y0_top,
+                          vc->gfx.cursor_x, vc->gfx.cursor_y);
+    } else {
+        egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
+    }
 
     eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
diff --git a/ui/gtk.c b/ui/gtk.c
index 563cff32b8..ef5bc42094 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -243,6 +243,8 @@ typedef struct VCChardev {
 #define TYPE_CHARDEV_VC "chardev-vc"
 #define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
 
+bool gtk_use_gl_area;
+
 static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
 static void gd_ungrab_pointer(GtkDisplayState *s);
 static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
@@ -453,7 +455,7 @@ static void gd_update_full_redraw(VirtualConsole *vc)
     int ww, wh;
     gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
 #if defined(CONFIG_GTK_GL)
-    if (vc->gfx.gls) {
+    if (vc->gfx.gls && gtk_use_gl_area) {
         gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
         return;
     }
@@ -725,7 +727,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
     .dpy_gl_update           = gd_gl_area_scanout_flush,
 };
 
-#else
+#endif /* CONFIG_GTK_GL */
 
 static const DisplayChangeListenerOps dcl_egl_ops = {
     .dpy_name             = "gtk-egl",
@@ -742,10 +744,13 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
     .dpy_gl_ctx_get_current  = qemu_egl_get_current_context,
     .dpy_gl_scanout_disable  = gd_egl_scanout_disable,
     .dpy_gl_scanout_texture  = gd_egl_scanout_texture,
+    .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
+    .dpy_gl_cursor_dmabuf    = gd_egl_cursor_dmabuf,
+    .dpy_gl_cursor_position  = gd_egl_cursor_position,
+    .dpy_gl_release_dmabuf   = gd_egl_release_dmabuf,
     .dpy_gl_update           = gd_egl_scanout_flush,
 };
 
-#endif /* CONFIG_GTK_GL */
 #endif /* CONFIG_OPENGL */
 
 /** QEMU Events **/
@@ -844,13 +849,13 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
 
 #if defined(CONFIG_OPENGL)
     if (vc->gfx.gls) {
-#if defined(CONFIG_GTK_GL)
-        /* invoke render callback please */
-        return FALSE;
-#else
-        gd_egl_draw(vc);
-        return TRUE;
-#endif
+        if (gtk_use_gl_area) {
+            /* invoke render callback please */
+            return FALSE;
+        } else {
+            gd_egl_draw(vc);
+            return TRUE;
+        }
     }
 #endif
 
@@ -1993,7 +1998,7 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
     g_signal_connect(vc->gfx.drawing_area, "draw",
                      G_CALLBACK(gd_draw_event), vc);
 #if defined(CONFIG_GTK_GL)
-    if (display_opengl) {
+    if (gtk_use_gl_area) {
         /* wire up GtkGlArea events */
         g_signal_connect(vc->gfx.drawing_area, "render",
                          G_CALLBACK(gd_render_event), vc);
@@ -2116,26 +2121,29 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
 #if defined(CONFIG_OPENGL)
     if (display_opengl) {
 #if defined(CONFIG_GTK_GL)
-        vc->gfx.drawing_area = gtk_gl_area_new();
-        vc->gfx.dcl.ops = &dcl_gl_area_ops;
-#else
-        vc->gfx.drawing_area = gtk_drawing_area_new();
-        /*
-         * gtk_widget_set_double_buffered() was deprecated in 3.14.
-         * It is required for opengl rendering on X11 though.  A
-         * proper replacement (native opengl support) is only
-         * available in 3.16+.  Silence the warning if possible.
-         */
+        if (gtk_use_gl_area) {
+            vc->gfx.drawing_area = gtk_gl_area_new();
+            vc->gfx.dcl.ops = &dcl_gl_area_ops;
+        } else
+#endif /* CONFIG_GTK_GL */
+        {
+            vc->gfx.drawing_area = gtk_drawing_area_new();
+            /*
+             * gtk_widget_set_double_buffered() was deprecated in 3.14.
+             * It is required for opengl rendering on X11 though.  A
+             * proper replacement (native opengl support) is only
+             * available in 3.16+.  Silence the warning if possible.
+             */
 #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 #endif
-        gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
+            gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
 #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
 #pragma GCC diagnostic pop
 #endif
-        vc->gfx.dcl.ops = &dcl_egl_ops;
-#endif /* CONFIG_GTK_GL */
+            vc->gfx.dcl.ops = &dcl_egl_ops;
+        }
     } else
 #endif
     {
@@ -2436,11 +2444,15 @@ static void early_gtk_display_init(DisplayOptions *opts)
     assert(opts->type == DISPLAY_TYPE_GTK);
     if (opts->has_gl && opts->gl) {
 #if defined(CONFIG_OPENGL)
-#if defined(CONFIG_GTK_GL)
-        gtk_gl_area_init();
-#else
-        gtk_egl_init();
+#if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND)
+        if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
+            gtk_use_gl_area = true;
+            gtk_gl_area_init();
+        }
 #endif
+        {
+            gtk_egl_init();
+        }
 #endif
     }
 
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 98ccdfb687..fe734821dd 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -26,20 +26,8 @@
 
 #include "ui/spice-display.h"
 
-static int debug = 0;
 bool spice_opengl;
 
-static void GCC_FMT_ATTR(2, 3) dprint(int level, const char *fmt, ...)
-{
-    va_list args;
-
-    if (level <= debug) {
-        va_start(args, fmt);
-        vfprintf(stderr, fmt, args);
-        va_end(args);
-    }
-}
-
 int qemu_spice_rect_is_empty(const QXLRect* r)
 {
     return r->top == r->bottom || r->left == r->right;
@@ -322,8 +310,6 @@ void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
 {
     QXLDevMemSlot memslot;
 
-    dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
-
     memset(&memslot, 0, sizeof(memslot));
     memslot.slot_group_id = MEMSLOT_GROUP_HOST;
     memslot.virt_end = ~0;
@@ -347,10 +333,6 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
         ssd->buf = g_malloc(ssd->bufsize);
     }
 
-    dprint(1, "%s/%d: %ux%u (size %" PRIu64 "/%d)\n", __func__, ssd->qxl.id,
-           surface_width(ssd->ds), surface_height(ssd->ds),
-           surface_size, ssd->bufsize);
-
     surface.format     = SPICE_SURFACE_FMT_32_xRGB;
     surface.width      = surface_width(ssd->ds);
     surface.height     = surface_height(ssd->ds);
@@ -366,8 +348,6 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
 
 void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
 {
-    dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
-
     qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC);
 }
 
@@ -389,8 +369,7 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
 {
     QXLRect update_area;
 
-    dprint(2, "%s/%d: x %d y %d w %d h %d\n", __func__,
-           ssd->qxl.id, x, y, w, h);
+    trace_qemu_spice_display_update(ssd->qxl.id, x, y, w, h);
     update_area.left = x,
     update_area.right = x + w;
     update_area.top = y;
@@ -413,8 +392,10 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
         surface_height(surface) == pixman_image_get_height(ssd->surface) &&
         surface_format(surface) == pixman_image_get_format(ssd->surface)) {
         /* no-resize fast path: just swap backing store */
-        dprint(1, "%s/%d: fast (%dx%d)\n", __func__, ssd->qxl.id,
-               surface_width(surface), surface_height(surface));
+        trace_qemu_spice_display_surface(ssd->qxl.id,
+                                         surface_width(surface),
+                                         surface_height(surface),
+                                         true);
         qemu_mutex_lock(&ssd->lock);
         ssd->ds = surface;
         pixman_image_unref(ssd->surface);
@@ -427,11 +408,10 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
     }
 
     /* full mode switch */
-    dprint(1, "%s/%d: full (%dx%d -> %dx%d)\n", __func__, ssd->qxl.id,
-           ssd->surface ? pixman_image_get_width(ssd->surface)  : 0,
-           ssd->surface ? pixman_image_get_height(ssd->surface) : 0,
-           surface ? surface_width(surface)  : 0,
-           surface ? surface_height(surface) : 0);
+    trace_qemu_spice_display_surface(ssd->qxl.id,
+                                     surface ? surface_width(surface)  : 0,
+                                     surface ? surface_height(surface) : 0,
+                                     false);
 
     memset(&ssd->dirty, 0, sizeof(ssd->dirty));
     if (ssd->surface) {
@@ -495,7 +475,6 @@ void qemu_spice_cursor_refresh_bh(void *opaque)
 
 void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
 {
-    dprint(3, "%s/%d:\n", __func__, ssd->qxl.id);
     graphic_hw_update(ssd->dcl.con);
 
     qemu_mutex_lock(&ssd->lock);
@@ -505,10 +484,10 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
     }
     qemu_mutex_unlock(&ssd->lock);
 
+    trace_qemu_spice_display_refresh(ssd->qxl.id, ssd->notify);
     if (ssd->notify) {
         ssd->notify = 0;
         qemu_spice_wakeup(ssd);
-        dprint(2, "%s/%d: notify\n", __func__, ssd->qxl.id);
     }
 }
 
@@ -516,21 +495,17 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
 
 static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
 {
-    SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
-
-    dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
+    /* nothing to do */
 }
 
 static void interface_set_compression_level(QXLInstance *sin, int level)
 {
-    dprint(1, "%s/%d:\n", __func__, sin->id);
     /* nothing to do */
 }
 
 #if SPICE_NEEDS_SET_MM_TIME
 static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
 {
-    dprint(3, "%s/%d:\n", __func__, sin->id);
     /* nothing to do */
 }
 #endif
@@ -554,8 +529,6 @@ static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext)
     SimpleSpiceUpdate *update;
     int ret = false;
 
-    dprint(3, "%s/%d:\n", __func__, ssd->qxl.id);
-
     qemu_mutex_lock(&ssd->lock);
     update = QTAILQ_FIRST(&ssd->updates);
     if (update != NULL) {
@@ -570,7 +543,6 @@ static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext)
 
 static int interface_req_cmd_notification(QXLInstance *sin)
 {
-    dprint(2, "%s/%d:\n", __func__, sin->id);
     return 1;
 }
 
@@ -582,7 +554,6 @@ static void interface_release_resource(QXLInstance *sin,
     SimpleSpiceCursor *cursor;
     QXLCommandExt *ext;
 
-    dprint(2, "%s/%d:\n", __func__, ssd->qxl.id);
     ext = (void *)(intptr_t)(rext.info->id);
     switch (ext->cmd.type) {
     case QXL_CMD_DRAW:
@@ -603,8 +574,6 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext)
     SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
     int ret;
 
-    dprint(3, "%s/%d:\n", __func__, ssd->qxl.id);
-
     qemu_mutex_lock(&ssd->lock);
     if (ssd->ptr_define) {
         *ext = ssd->ptr_define->ext;
@@ -623,7 +592,6 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext)
 
 static int interface_req_cursor_notification(QXLInstance *sin)
 {
-    dprint(2, "%s:\n", __func__);
     return 1;
 }
 
@@ -680,7 +648,7 @@ static void interface_set_client_capabilities(QXLInstance *sin,
                                               uint8_t client_present,
                                               uint8_t caps[58])
 {
-    dprint(3, "%s:\n", __func__);
+    /* nothing to do */
 }
 
 static int interface_client_monitors_config(QXLInstance *sin,
@@ -705,9 +673,9 @@ static int interface_client_monitors_config(QXLInstance *sin,
         info.width  = mc->monitors[head].width;
         info.height = mc->monitors[head].height;
     }
+
+    trace_qemu_spice_ui_info(ssd->qxl.id, info.width, info.height);
     dpy_set_ui_info(ssd->dcl.con, &info);
-    dprint(1, "%s/%d: size %dx%d\n", __func__, ssd->qxl.id,
-           info.width, info.height);
     return 1;
 }
 
@@ -902,9 +870,10 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
             return;
         }
 
-        dprint(1, "%s: %dx%d (stride %d/%d, fourcc 0x%x)\n", __func__,
-               surface_width(ssd->ds), surface_height(ssd->ds),
-               surface_stride(ssd->ds), stride, fourcc);
+        trace_qemu_spice_gl_surface(ssd->qxl.id,
+                                    surface_width(ssd->ds),
+                                    surface_height(ssd->ds),
+                                    fourcc);
 
         /* note: spice server will close the fd */
         spice_qxl_gl_scanout(&ssd->qxl, fd,
@@ -932,7 +901,7 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
 {
     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
 
-    dprint(1, "%s: no framebuffer\n", __func__);
+    trace_qemu_spice_gl_scanout_disable(ssd->qxl.id);
     spice_qxl_gl_scanout(&ssd->qxl, -1, 0, 0, 0, 0, false);
     qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0);
     ssd->have_surface = false;
@@ -957,8 +926,7 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
         fprintf(stderr, "%s: failed to get fd for texture\n", __func__);
         return;
     }
-    dprint(1, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__,
-           w, h, stride, fourcc);
+    trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc);
 
     /* note: spice server will close the fd */
     spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height,
@@ -968,17 +936,133 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
     ssd->have_scanout = true;
 }
 
+static void qemu_spice_gl_scanout_dmabuf(DisplayChangeListener *dcl,
+                                         QemuDmaBuf *dmabuf)
+{
+    SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+
+    ssd->guest_dmabuf = dmabuf;
+    ssd->guest_dmabuf_refresh = true;
+
+    ssd->have_surface = false;
+    ssd->have_scanout = true;
+}
+
+static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl,
+                                        QemuDmaBuf *dmabuf, bool have_hot,
+                                        uint32_t hot_x, uint32_t hot_y)
+{
+    SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+
+    ssd->have_hot = have_hot;
+    ssd->hot_x = hot_x;
+    ssd->hot_y = hot_y;
+
+    trace_qemu_spice_gl_cursor(ssd->qxl.id, dmabuf != NULL, have_hot);
+    if (dmabuf) {
+        egl_dmabuf_import_texture(dmabuf);
+        if (!dmabuf->texture) {
+            return;
+        }
+        egl_fb_setup_for_tex(&ssd->cursor_fb, dmabuf->width, dmabuf->height,
+                             dmabuf->texture, false);
+    } else {
+        egl_fb_destroy(&ssd->cursor_fb);
+    }
+}
+
+static void qemu_spice_gl_cursor_position(DisplayChangeListener *dcl,
+                                          uint32_t pos_x, uint32_t pos_y)
+{
+    SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+
+    ssd->ptr_x = pos_x;
+    ssd->ptr_y = pos_y;
+}
+
+static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl,
+                                         QemuDmaBuf *dmabuf)
+{
+    SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+
+    if (ssd->guest_dmabuf == dmabuf) {
+        ssd->guest_dmabuf = NULL;
+        ssd->guest_dmabuf_refresh = false;
+    }
+    egl_dmabuf_release_texture(dmabuf);
+}
+
 static void qemu_spice_gl_update(DisplayChangeListener *dcl,
                                  uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+    EGLint stride = 0, fourcc = 0;
+    bool render_cursor = false;
+    bool y_0_top = false; /* FIXME */
     uint64_t cookie;
+    int fd;
 
     if (!ssd->have_scanout) {
         return;
     }
 
-    dprint(2, "%s: %dx%d+%d+%d\n", __func__, w, h, x, y);
+    if (ssd->cursor_fb.texture) {
+        render_cursor = true;
+    }
+    if (ssd->render_cursor != render_cursor) {
+        ssd->render_cursor = render_cursor;
+        ssd->guest_dmabuf_refresh = true;
+        egl_fb_destroy(&ssd->blit_fb);
+    }
+
+    if (ssd->guest_dmabuf_refresh) {
+        QemuDmaBuf *dmabuf = ssd->guest_dmabuf;
+        if (render_cursor) {
+            egl_dmabuf_import_texture(dmabuf);
+            if (!dmabuf->texture) {
+                return;
+            }
+
+            /* source framebuffer */
+            egl_fb_setup_for_tex(&ssd->guest_fb,
+                                 dmabuf->width, dmabuf->height,
+                                 dmabuf->texture, false);
+
+            /* dest framebuffer */
+            if (ssd->blit_fb.width  != dmabuf->width ||
+                ssd->blit_fb.height != dmabuf->height) {
+                trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, dmabuf->width,
+                                                  dmabuf->height);
+                egl_fb_destroy(&ssd->blit_fb);
+                egl_fb_setup_new_tex(&ssd->blit_fb,
+                                     dmabuf->width, dmabuf->height);
+                fd = egl_get_fd_for_texture(ssd->blit_fb.texture,
+                                            &stride, &fourcc);
+                spice_qxl_gl_scanout(&ssd->qxl, fd,
+                                     dmabuf->width, dmabuf->height,
+                                     stride, fourcc, false);
+            }
+        } else {
+            trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id,
+                                               dmabuf->width, dmabuf->height);
+            /* note: spice server will close the fd, so hand over a dup */
+            spice_qxl_gl_scanout(&ssd->qxl, dup(dmabuf->fd),
+                                 dmabuf->width, dmabuf->height,
+                                 dmabuf->stride, dmabuf->fourcc, false);
+        }
+        qemu_spice_gl_monitor_config(ssd, 0, 0, dmabuf->width, dmabuf->height);
+        ssd->guest_dmabuf_refresh = false;
+    }
+
+    if (render_cursor) {
+        egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb,
+                         !y_0_top);
+        egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb,
+                          !y_0_top, ssd->ptr_x, ssd->ptr_y);
+        glFlush();
+    }
+
+    trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y);
     qemu_spice_gl_block(ssd, true);
     cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
     spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie);
@@ -1000,6 +1084,10 @@ static const DisplayChangeListenerOps display_listener_gl_ops = {
 
     .dpy_gl_scanout_disable  = qemu_spice_gl_scanout_disable,
     .dpy_gl_scanout_texture  = qemu_spice_gl_scanout_texture,
+    .dpy_gl_scanout_dmabuf   = qemu_spice_gl_scanout_dmabuf,
+    .dpy_gl_cursor_dmabuf    = qemu_spice_gl_cursor_dmabuf,
+    .dpy_gl_cursor_position  = qemu_spice_gl_cursor_position,
+    .dpy_gl_release_dmabuf   = qemu_spice_gl_release_dmabuf,
     .dpy_gl_update           = qemu_spice_gl_update,
 };
 
diff --git a/ui/trace-events b/ui/trace-events
index 861b68a305..a957f363f1 100644
--- a/ui/trace-events
+++ b/ui/trace-events
@@ -75,6 +75,18 @@ qemu_spice_create_primary_surface(int qid, uint32_t sid, void *surface, int asyn
 qemu_spice_destroy_primary_surface(int qid, uint32_t sid, int async) "%d sid=%u async=%d"
 qemu_spice_wakeup(uint32_t qid) "%d"
 qemu_spice_create_update(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) "lr %d -> %d,  tb -> %d -> %d"
+qemu_spice_display_update(int qid, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "%d +%d+%d %dx%d"
+qemu_spice_display_surface(int qid, uint32_t w, uint32_t h, int fast) "%d %dx%d, fast %d"
+qemu_spice_display_refresh(int qid, int notify) "%d notify %d"
+qemu_spice_ui_info(int qid, uint32_t width, uint32_t height) "%d %dx%d"
+
+qemu_spice_gl_surface(int qid, uint32_t w, uint32_t h, uint32_t fourcc) "%d %dx%d, fourcc 0x%x"
+qemu_spice_gl_scanout_disable(int qid) "%d"
+qemu_spice_gl_scanout_texture(int qid, uint32_t w, uint32_t h, uint32_t fourcc) "%d %dx%d, fourcc 0x%x"
+qemu_spice_gl_cursor(int qid, bool enabled, bool hotspot) "%d enabled %d, hotspot %d"
+qemu_spice_gl_forward_dmabuf(int qid, uint32_t width, uint32_t height) "%d %dx%d"
+qemu_spice_gl_render_dmabuf(int qid, uint32_t width, uint32_t height) "%d %dx%d"
+qemu_spice_gl_update(int qid, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "%d +%d+%d %dx%d"
 
 # ui/keymaps.c
 keymap_parse(const char *file) "file %s"
diff --git a/ui/vnc.c b/ui/vnc.c
index 13c28cabb0..e164eb798c 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -746,9 +746,19 @@ static void vnc_update_server_surface(VncDisplay *vd)
 static void vnc_dpy_switch(DisplayChangeListener *dcl,
                            DisplaySurface *surface)
 {
+    static const char placeholder_msg[] =
+        "Display output is not active.";
+    static DisplaySurface *placeholder;
     VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
     VncState *vs;
 
+    if (surface == NULL) {
+        if (placeholder == NULL) {
+            placeholder = qemu_create_message_surface(640, 480, placeholder_msg);
+        }
+        surface = placeholder;
+    }
+
     vnc_abort_display_jobs(vd);
     vd->ds = surface;
 
diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c
index 926d3402e3..090ba21a13 100644
--- a/util/coroutine-ucontext.c
+++ b/util/coroutine-ucontext.c
@@ -170,7 +170,7 @@ Coroutine *qemu_coroutine_new(void)
 }
 
 #ifdef CONFIG_VALGRIND_H
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
+#if defined(CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE) && !defined(__clang__)
 /* Work around an unused variable in the valgrind.h macro... */
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
@@ -179,7 +179,7 @@ static inline void valgrind_stack_deregister(CoroutineUContext *co)
 {
     VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id);
 }
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
+#if defined(CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE) && !defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
 #endif
diff --git a/util/qemu-option.c b/util/qemu-option.c
index a401e936da..2b412eff5e 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -1007,14 +1007,23 @@ void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp)
 }
 
 /*
- * Convert from QemuOpts to QDict.
- * The QDict values are of type QString.
+ * Convert from QemuOpts to QDict. The QDict values are of type QString.
+ *
+ * If @list is given, only add those options to the QDict that are contained in
+ * the list. If @del is true, any options added to the QDict are removed from
+ * the QemuOpts, otherwise they remain there.
+ *
+ * If two options in @opts have the same name, they are processed in order
+ * so that the last one wins (consistent with the reverse iteration in
+ * qemu_opt_find()), but all of them are deleted if @del is true.
+ *
  * TODO We'll want to use types appropriate for opt->desc->type, but
  * this is enough for now.
  */
-QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
+QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict,
+                                   QemuOptsList *list, bool del)
 {
-    QemuOpt *opt;
+    QemuOpt *opt, *next;
 
     if (!qdict) {
         qdict = qdict_new();
@@ -1022,12 +1031,35 @@ QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
     if (opts->id) {
         qdict_put_str(qdict, "id", opts->id);
     }
-    QTAILQ_FOREACH(opt, &opts->head, next) {
+    QTAILQ_FOREACH_SAFE(opt, &opts->head, next, next) {
+        if (list) {
+            QemuOptDesc *desc;
+            bool found = false;
+            for (desc = list->desc; desc->name; desc++) {
+                if (!strcmp(desc->name, opt->name)) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                continue;
+            }
+        }
         qdict_put_str(qdict, opt->name, opt->str);
+        if (del) {
+            qemu_opt_del(opt);
+        }
     }
     return qdict;
 }
 
+/* Copy all options in a QemuOpts to the given QDict. See
+ * qemu_opts_to_qdict_filtered() for details. */
+QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
+{
+    return qemu_opts_to_qdict_filtered(opts, qdict, NULL, false);
+}
+
 /* Validate parsed opts against descriptions where no
  * descriptions were provided in the QemuOptsList.
  */