summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml172
-rw-r--r--MAINTAINERS11
-rw-r--r--block/dirty-bitmap.c1
-rw-r--r--blockdev-nbd.c32
-rw-r--r--blockdev.c107
-rwxr-xr-xconfigure18
-rw-r--r--contrib/gitdm/domain-map2
-rw-r--r--contrib/gitdm/group-map-ibm7
-rw-r--r--contrib/gitdm/group-map-wavecomp1
-rw-r--r--hmp.c5
-rw-r--r--hw/9pfs/xen-9p-backend.c16
-rw-r--r--hw/block/Makefile.objs2
-rw-r--r--hw/block/dataplane/Makefile.objs1
-rw-r--r--hw/block/dataplane/xen-block.c827
-rw-r--r--hw/block/dataplane/xen-block.h29
-rw-r--r--hw/block/trace-events14
-rw-r--r--hw/block/xen-block.c963
-rw-r--r--hw/block/xen_disk.c1011
-rw-r--r--hw/char/xen_console.c12
-rw-r--r--hw/display/xenfb.c25
-rw-r--r--hw/i386/pc.c10
-rw-r--r--hw/i386/xen/xen-hvm.c5
-rw-r--r--hw/i386/xen/xen-mapcache.c2
-rw-r--r--hw/i386/xen/xen_platform.c2
-rw-r--r--hw/net/xen_nic.c14
-rw-r--r--hw/usb/xen-usb.c25
-rw-r--r--hw/xen/Makefile.objs2
-rw-r--r--hw/xen/trace-events26
-rw-r--r--hw/xen/xen-backend.c165
-rw-r--r--hw/xen/xen-bus-helper.c184
-rw-r--r--hw/xen/xen-bus.c1199
-rw-r--r--hw/xen/xen-common.c2
-rw-r--r--hw/xen/xen-legacy-backend.c (renamed from hw/xen/xen_backend.c)80
-rw-r--r--hw/xen/xen_devconfig.c2
-rw-r--r--hw/xen/xen_pt.c8
-rw-r--r--hw/xen/xen_pt_config_init.c6
-rw-r--r--hw/xen/xen_pt_graphics.c18
-rw-r--r--hw/xen/xen_pt_msi.c2
-rw-r--r--hw/xen/xen_pvdev.c20
-rw-r--r--hw/xenpv/Makefile.objs2
-rw-r--r--hw/xenpv/xen_domainbuild.c299
-rw-r--r--hw/xenpv/xen_domainbuild.h13
-rw-r--r--hw/xenpv/xen_machine_pv.c21
-rw-r--r--include/block/nbd.h12
-rw-r--r--include/hw/xen/xen-backend.h39
-rw-r--r--include/hw/xen/xen-block.h94
-rw-r--r--include/hw/xen/xen-bus-helper.h45
-rw-r--r--include/hw/xen/xen-bus.h137
-rw-r--r--include/hw/xen/xen-legacy-backend.h (renamed from include/hw/xen/xen_backend.h)43
-rw-r--r--include/hw/xen/xen.h3
-rw-r--r--include/hw/xen/xen_common.h19
-rw-r--r--include/hw/xen/xen_pvdev.h38
-rw-r--r--include/qemu/module.h3
-rw-r--r--nbd/server.c136
-rw-r--r--qapi/block-core.json56
-rw-r--r--qapi/block.json30
-rw-r--r--qapi/transaction.json12
-rw-r--r--qemu-doc.texi13
-rw-r--r--qemu-nbd.c26
-rw-r--r--qemu-nbd.texi4
-rw-r--r--qemu-options.hx12
-rw-r--r--target/i386/cpu.c67
-rw-r--r--target/i386/cpu.h5
-rw-r--r--target/i386/kvm.c85
-rw-r--r--tests/Makefile.include3
-rw-r--r--tests/atomic64-bench.c6
-rw-r--r--tests/atomic_add-bench.c6
-rw-r--r--tests/docker/Makefile.include13
-rw-r--r--tests/docker/dockerfiles/debian-amd64.docker5
-rw-r--r--tests/docker/dockerfiles/debian-sid.docker7
-rw-r--r--tests/docker/dockerfiles/debian.docker13
-rw-r--r--tests/docker/dockerfiles/fedora-i386-cross.docker2
-rw-r--r--tests/docker/dockerfiles/fedora.docker4
-rw-r--r--tests/docker/dockerfiles/travis.docker4
-rwxr-xr-xtests/qemu-iotests/2068
-rwxr-xr-xtests/qemu-iotests/22352
-rw-r--r--tests/qemu-iotests/223.out23
-rwxr-xr-xtests/qemu-iotests/236161
-rw-r--r--tests/qemu-iotests/236.out351
-rw-r--r--tests/qemu-iotests/group1
-rw-r--r--tests/qemu-iotests/iotests.py64
-rw-r--r--tests/qht-bench.c6
-rw-r--r--vl.c7
83 files changed, 5042 insertions, 1936 deletions
diff --git a/.travis.yml b/.travis.yml
index d472fd650b..93fd0164a0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,14 +1,13 @@
-# The current Travis default is a container based 14.04 Trust on EC2
+# The current Travis default is a VM based 16.04 Xenial on GCE
 # Additional builds with specific requirements for a full VM need to
 # be added as additional matrix: entries later on
-sudo: false
-dist: trusty
+dist: xenial
 language: c
-python:
-  - "2.6"
 compiler:
   - gcc
 cache: ccache
+
+
 addons:
   apt:
     packages:
@@ -35,10 +34,17 @@ addons:
       - libssh2-1-dev
       - liburcu-dev
       - libusb-1.0-0-dev
-      - libvte-2.90-dev
+      - libvte-2.91-dev
       - sparse
       - uuid-dev
       - gcovr
+  homebrew:
+    packages:
+      - libffi
+      - gettext
+      - glib
+      - pixman
+
 
 # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
 # to prevent IRC notifications from forks. This was created using:
@@ -49,88 +55,145 @@ notifications:
       - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM="
     on_success: change
     on_failure: always
+
+
 env:
   global:
     - SRC_DIR="."
     - BUILD_DIR="."
-    - TEST_CMD="make check"
-    - MAKEFLAGS="-j3"
-  matrix:
-    - CONFIG="--disable-system"
-    - CONFIG="--disable-user"
-    - CONFIG="--enable-debug --enable-debug-tcg"
-    - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user"
-    - CONFIG="--enable-modules --disable-linux-user"
-    - CONFIG="--with-coroutine=ucontext --disable-linux-user"
-    - CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
+    - TEST_CMD="make check -j3 V=1"
+
+
 git:
   # we want to do this ourselves
   submodules: false
-before_install:
-  - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi
-  - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi
-  - git submodule update --init --recursive capstone dtc ui/keycodemapdb
+
+
 before_script:
   - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
   - ${SRC_DIR}/configure ${CONFIG} || { cat config.log && exit 1; }
 script:
-  - make ${MAKEFLAGS} && ${TEST_CMD}
+  - make -j3 && ${TEST_CMD}
+
+
 matrix:
   include:
+    - env:
+        - CONFIG="--disable-system"
+
+
+    - env:
+        - CONFIG="--disable-user"
+
+
+    - env:
+        - CONFIG="--enable-debug --enable-debug-tcg"
+
+
+    - env:
+        - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user"
+
+
+    - env:
+        - CONFIG="--enable-modules --disable-linux-user"
+
+
+    - env:
+        - CONFIG="--with-coroutine=ucontext --disable-linux-user"
+
+
+    - env:
+        - CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
+
+
     # Test out-of-tree builds
-    - env: CONFIG="--enable-debug --enable-debug-tcg"
-           BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
+    - env:
+        - CONFIG="--enable-debug --enable-debug-tcg"
+        - BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
+
+
     # Test with Clang for compile portability (Travis uses clang-5.0)
-    - env: CONFIG="--disable-system"
+    - env:
+        - CONFIG="--disable-system"
       compiler: clang
-    - env: CONFIG="--disable-user"
+
+
+    - env:
+        - CONFIG="--disable-user"
       compiler: clang
+
+
     # gprof/gcov are GCC features
-    - env: CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
+    - env:
+        - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
       after_success:
         - ${SRC_DIR}/scripts/travis/coverage-summary.sh
-      compiler: gcc
+
+
     # We manually include builds which we disable "make check" for
-    - env: CONFIG="--enable-debug --enable-tcg-interpreter"
-           TEST_CMD=""
-      compiler: gcc
+    - env:
+        - CONFIG="--enable-debug --enable-tcg-interpreter"
+        - TEST_CMD=""
+
+
     # We don't need to exercise every backend with every front-end
-    - env: CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
-           TEST_CMD=""
-      compiler: gcc
-    - env: CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
-           TEST_CMD=""
-      compiler: gcc
-    - env: CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
-           TEST_CMD=""
-      compiler: gcc
-    - env: CONFIG="--disable-tcg"
-           TEST_CMD=""
-      compiler: gcc
+    - env:
+        - CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
+        - TEST_CMD=""
+
+
+    - env:
+        - CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
+        - TEST_CMD=""
+
+
+    - env:
+        - CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
+        - TEST_CMD=""
+
+
+    - env:
+        - CONFIG="--disable-tcg"
+        - TEST_CMD=""
+
+
     # MacOSX builds
-    - env: CONFIG="--target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
+    - env:
+        - CONFIG="--target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
       os: osx
       osx_image: xcode9.4
       compiler: clang
-    - env: CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
+
+
+    - env:
+        - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
       os: osx
       osx_image: xcode10
       compiler: clang
+
+
     # Python builds
-    - env: CONFIG="--target-list=x86_64-softmmu"
+    - env:
+        - CONFIG="--target-list=x86_64-softmmu"
       python:
         - "3.0"
-    - env: CONFIG="--target-list=x86_64-softmmu"
+
+
+    - env:
+        - CONFIG="--target-list=x86_64-softmmu"
       python:
         - "3.6"
+
+
     # Acceptance (Functional) tests
-    - env: CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu"
-           TEST_CMD="make AVOCADO_SHOW=app check-acceptance"
+    - env:
+        - CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu"
+        - TEST_CMD="make AVOCADO_SHOW=app check-acceptance"
       addons:
         apt:
           packages:
             - python3-pip
-            - python3.4-venv
+            - python3.5-venv
     # Using newer GCC with sanitizers
     - addons:
         apt:
@@ -164,7 +227,7 @@ matrix:
             - libssh2-1-dev
             - liburcu-dev
             - libusb-1.0-0-dev
-            - libvte-2.90-dev
+            - libvte-2.91-dev
             - sparse
             - uuid-dev
       language: generic
@@ -175,11 +238,8 @@ matrix:
         - TEST_CMD=""
       before_script:
         - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
+
+
     - env:
         - CONFIG="--disable-system --disable-docs"
-        - TEST_CMD="make check-tcg"
-      script:
-        - make ${MAKEFLAGS} && ${TEST_CMD} ${MAKEFLAGS}
-      sudo: required
-      dist: trusty
-      compiler: gcc
+        - TEST_CMD="make -j3 check-tcg V=1"
diff --git a/MAINTAINERS b/MAINTAINERS
index 87f90721b9..af339b86db 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -407,6 +407,7 @@ Guest CPU Cores (Xen):
 X86
 M: Stefano Stabellini <sstabellini@kernel.org>
 M: Anthony Perard <anthony.perard@citrix.com>
+M: Paul Durrant <paul.durrant@citrix.com>
 L: xen-devel@lists.xenproject.org
 S: Supported
 F: */xen*
@@ -414,10 +415,12 @@ F: hw/9pfs/xen-9p-backend.c
 F: hw/char/xen_console.c
 F: hw/display/xenfb.c
 F: hw/net/xen_nic.c
-F: hw/block/xen_*
+F: hw/block/xen*
+F: hw/block/dataplane/xen*
 F: hw/xen/
 F: hw/xenpv/
 F: hw/i386/xen/
+F: include/hw/block/dataplane/xen*
 F: include/hw/xen/
 F: include/sysemu/xen-mapcache.h
 
@@ -2494,6 +2497,12 @@ M: Daniel P. Berrange <berrange@redhat.com>
 S: Odd Fixes
 F: docs/devel/build-system.txt
 
+GIT Data Mining Config
+M: Alex Bennée <alex.bennee@linaro.org>
+S: Odd Fixes
+F: gitdm.config
+F: contrib/gitdm/*
+
 Incompatible changes
 R: libvir-list@redhat.com
 F: qemu-deprecated.texi
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
index 89fd1d7f8b..6b688394e4 100644
--- a/block/dirty-bitmap.c
+++ b/block/dirty-bitmap.c
@@ -625,7 +625,6 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
 void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup)
 {
     HBitmap *tmp = bitmap->bitmap;
-    assert(bdrv_dirty_bitmap_enabled(bitmap));
     assert(!bdrv_dirty_bitmap_readonly(bitmap));
     bitmap->bitmap = backup;
     hbitmap_free(tmp);
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 1d170c80b8..c76d5416b9 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -140,7 +140,8 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr,
 }
 
 void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
-                        bool has_writable, bool writable, Error **errp)
+                        bool has_writable, bool writable,
+                        bool has_bitmap, const char *bitmap, Error **errp)
 {
     BlockDriverState *bs = NULL;
     BlockBackend *on_eject_blk;
@@ -174,14 +175,13 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
         writable = false;
     }
 
-    exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
+    exp = nbd_export_new(bs, 0, -1, name, NULL, bitmap,
+                         writable ? 0 : NBD_FLAG_READ_ONLY,
                          NULL, false, on_eject_blk, errp);
     if (!exp) {
         return;
     }
 
-    nbd_export_set_name(exp, name);
-
     /* The list of named exports has a strong reference to this export now and
      * our only way of accessing it is through nbd_export_find(), so we can drop
      * the strong reference that is @exp. */
@@ -214,31 +214,13 @@ void qmp_nbd_server_remove(const char *name,
 
 void qmp_nbd_server_stop(Error **errp)
 {
-    nbd_export_close_all();
-
-    nbd_server_free(nbd_server);
-    nbd_server = NULL;
-}
-
-void qmp_x_nbd_server_add_bitmap(const char *name, const char *bitmap,
-                                 bool has_bitmap_export_name,
-                                 const char *bitmap_export_name,
-                                 Error **errp)
-{
-    NBDExport *exp;
-
     if (!nbd_server) {
         error_setg(errp, "NBD server not running");
         return;
     }
 
-    exp = nbd_export_find(name);
-    if (exp == NULL) {
-        error_setg(errp, "Export '%s' is not found", name);
-        return;
-    }
+    nbd_export_close_all();
 
-    nbd_export_bitmap(exp, bitmap,
-                      has_bitmap_export_name ? bitmap_export_name : bitmap,
-                      errp);
+    nbd_server_free(nbd_server);
+    nbd_server = NULL;
 }
diff --git a/blockdev.c b/blockdev.c
index 1cc893fe61..a8fa8748a9 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1339,7 +1339,7 @@ struct BlkActionState {
     const BlkActionOps *ops;
     JobTxn *block_job_txn;
     TransactionProperties *txn_props;
-    QSIMPLEQ_ENTRY(BlkActionState) entry;
+    QTAILQ_ENTRY(BlkActionState) entry;
 };
 
 /* internal snapshot private data */
@@ -1963,7 +1963,7 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common,
                                action->has_granularity, action->granularity,
                                action->has_persistent, action->persistent,
                                action->has_autoload, action->autoload,
-                               action->has_x_disabled, action->x_disabled,
+                               action->has_disabled, action->disabled,
                                &local_err);
 
     if (!local_err) {
@@ -2048,7 +2048,7 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
         return;
     }
 
-    action = common->action->u.x_block_dirty_bitmap_enable.data;
+    action = common->action->u.block_dirty_bitmap_enable.data;
     state->bitmap = block_dirty_bitmap_lookup(action->node,
                                               action->name,
                                               NULL,
@@ -2089,7 +2089,7 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
         return;
     }
 
-    action = common->action->u.x_block_dirty_bitmap_disable.data;
+    action = common->action->u.block_dirty_bitmap_disable.data;
     state->bitmap = block_dirty_bitmap_lookup(action->node,
                                               action->name,
                                               NULL,
@@ -2119,33 +2119,28 @@ static void block_dirty_bitmap_disable_abort(BlkActionState *common)
     }
 }
 
+static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(const char *node,
+                                                    const char *target,
+                                                    strList *bitmaps,
+                                                    HBitmap **backup,
+                                                    Error **errp);
+
 static void block_dirty_bitmap_merge_prepare(BlkActionState *common,
                                              Error **errp)
 {
     BlockDirtyBitmapMerge *action;
     BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
                                              common, common);
-    BdrvDirtyBitmap *merge_source;
 
     if (action_check_completion_mode(common, errp) < 0) {
         return;
     }
 
-    action = common->action->u.x_block_dirty_bitmap_merge.data;
-    state->bitmap = block_dirty_bitmap_lookup(action->node,
-                                              action->dst_name,
-                                              &state->bs,
-                                              errp);
-    if (!state->bitmap) {
-        return;
-    }
-
-    merge_source = bdrv_find_dirty_bitmap(state->bs, action->src_name);
-    if (!merge_source) {
-        return;
-    }
+    action = common->action->u.block_dirty_bitmap_merge.data;
 
-    bdrv_merge_dirty_bitmap(state->bitmap, merge_source, &state->backup, errp);
+    state->bitmap = do_block_dirty_bitmap_merge(action->node, action->target,
+                                                action->bitmaps, &state->backup,
+                                                errp);
 }
 
 static void abort_prepare(BlkActionState *common, Error **errp)
@@ -2209,17 +2204,17 @@ static const BlkActionOps actions[] = {
         .commit = block_dirty_bitmap_free_backup,
         .abort = block_dirty_bitmap_restore,
     },
-    [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = {
+    [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE] = {
         .instance_size = sizeof(BlockDirtyBitmapState),
         .prepare = block_dirty_bitmap_enable_prepare,
         .abort = block_dirty_bitmap_enable_abort,
     },
-    [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_DISABLE] = {
+    [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE] = {
         .instance_size = sizeof(BlockDirtyBitmapState),
         .prepare = block_dirty_bitmap_disable_prepare,
         .abort = block_dirty_bitmap_disable_abort,
     },
-    [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_MERGE] = {
+    [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE] = {
         .instance_size = sizeof(BlockDirtyBitmapState),
         .prepare = block_dirty_bitmap_merge_prepare,
         .commit = block_dirty_bitmap_free_backup,
@@ -2266,8 +2261,8 @@ void qmp_transaction(TransactionActionList *dev_list,
     BlkActionState *state, *next;
     Error *local_err = NULL;
 
-    QSIMPLEQ_HEAD(, BlkActionState) snap_bdrv_states;
-    QSIMPLEQ_INIT(&snap_bdrv_states);
+    QTAILQ_HEAD(, BlkActionState) snap_bdrv_states;
+    QTAILQ_INIT(&snap_bdrv_states);
 
     /* Does this transaction get canceled as a group on failure?
      * If not, we don't really need to make a JobTxn.
@@ -2298,7 +2293,7 @@ void qmp_transaction(TransactionActionList *dev_list,
         state->action = dev_info;
         state->block_job_txn = block_job_txn;
         state->txn_props = props;
-        QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
+        QTAILQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
 
         state->ops->prepare(state, &local_err);
         if (local_err) {
@@ -2307,7 +2302,7 @@ void qmp_transaction(TransactionActionList *dev_list,
         }
     }
 
-    QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) {
+    QTAILQ_FOREACH(state, &snap_bdrv_states, entry) {
         if (state->ops->commit) {
             state->ops->commit(state);
         }
@@ -2318,13 +2313,13 @@ void qmp_transaction(TransactionActionList *dev_list,
 
 delete_and_fail:
     /* failure, and it is all-or-none; roll back all operations */
-    QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) {
+    QTAILQ_FOREACH_REVERSE(state, &snap_bdrv_states, entry) {
         if (state->ops->abort) {
             state->ops->abort(state);
         }
     }
 exit:
-    QSIMPLEQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) {
+    QTAILQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) {
         if (state->ops->clean) {
             state->ops->clean(state);
         }
@@ -2935,7 +2930,7 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
     bdrv_clear_dirty_bitmap(bitmap, NULL);
 }
 
-void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name,
+void qmp_block_dirty_bitmap_enable(const char *node, const char *name,
                                    Error **errp)
 {
     BlockDriverState *bs;
@@ -2956,7 +2951,7 @@ void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name,
     bdrv_enable_dirty_bitmap(bitmap);
 }
 
-void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name,
+void qmp_block_dirty_bitmap_disable(const char *node, const char *name,
                                     Error **errp)
 {
     BlockDriverState *bs;
@@ -2977,24 +2972,56 @@ void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name,
     bdrv_disable_dirty_bitmap(bitmap);
 }
 
-void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name,
-                                    const char *src_name, Error **errp)
+static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(const char *node,
+                                                    const char *target,
+                                                    strList *bitmaps,
+                                                    HBitmap **backup,
+                                                    Error **errp)
 {
     BlockDriverState *bs;
-    BdrvDirtyBitmap *dst, *src;
+    BdrvDirtyBitmap *dst, *src, *anon;
+    strList *lst;
+    Error *local_err = NULL;
 
-    dst = block_dirty_bitmap_lookup(node, dst_name, &bs, errp);
+    dst = block_dirty_bitmap_lookup(node, target, &bs, errp);
     if (!dst) {
-        return;
+        return NULL;
     }
 
-    src = bdrv_find_dirty_bitmap(bs, src_name);
-    if (!src) {
-        error_setg(errp, "Dirty bitmap '%s' not found", src_name);
-        return;
+    anon = bdrv_create_dirty_bitmap(bs, bdrv_dirty_bitmap_granularity(dst),
+                                    NULL, errp);
+    if (!anon) {
+        return NULL;
+    }
+
+    for (lst = bitmaps; lst; lst = lst->next) {
+        src = bdrv_find_dirty_bitmap(bs, lst->value);
+        if (!src) {
+            error_setg(errp, "Dirty bitmap '%s' not found", lst->value);
+            dst = NULL;
+            goto out;
+        }
+
+        bdrv_merge_dirty_bitmap(anon, src, NULL, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            dst = NULL;
+            goto out;
+        }
     }
 
-    bdrv_merge_dirty_bitmap(dst, src, NULL, errp);
+    /* Merge into dst; dst is unchanged on failure. */
+    bdrv_merge_dirty_bitmap(dst, anon, backup, errp);
+
+ out:
+    bdrv_release_dirty_bitmap(bs, anon);
+    return dst;
+}
+
+void qmp_block_dirty_bitmap_merge(const char *node, const char *target,
+                                  strList *bitmaps, Error **errp)
+{
+    do_block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp);
 }
 
 BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node,
diff --git a/configure b/configure
index 4ea3f14883..3eee3fcf70 100755
--- a/configure
+++ b/configure
@@ -357,7 +357,6 @@ vnc_png=""
 xkbcommon=""
 xen=""
 xen_ctrl_version=""
-xen_pv_domain_build="no"
 xen_pci_passthrough=""
 linux_aio=""
 cap_ng=""
@@ -1119,10 +1118,6 @@ for opt do
   ;;
   --enable-xen-pci-passthrough) xen_pci_passthrough="yes"
   ;;
-  --disable-xen-pv-domain-build) xen_pv_domain_build="no"
-  ;;
-  --enable-xen-pv-domain-build) xen_pv_domain_build="yes"
-  ;;
   --disable-brlapi) brlapi="no"
   ;;
   --enable-brlapi) brlapi="yes"
@@ -1685,8 +1680,6 @@ Advanced options (experts only):
   --tls-priority           default TLS protocol/cipher priority string
   --enable-gprof           QEMU profiling with gprof
   --enable-profiler        profiler support
-  --enable-xen-pv-domain-build
-                           xen pv domain builder
   --enable-debug-stack-usage
                            track the maximum stack usage of stacks created by qemu_alloc_stack
 
@@ -2678,12 +2671,6 @@ if test "$xen_pci_passthrough" != "no"; then
   fi
 fi
 
-if test "$xen_pv_domain_build" = "yes" &&
-   test "$xen" != "yes"; then
-    error_exit "User requested Xen PV domain builder support" \
-	       "which requires Xen support."
-fi
-
 ##########################################
 # Windows Hypervisor Platform accelerator (WHPX) check
 if test "$whpx" != "no" ; then
@@ -6069,7 +6056,6 @@ fi
 echo "xen support       $xen"
 if test "$xen" = "yes" ; then
   echo "xen ctrl version  $xen_ctrl_version"
-  echo "pv dom build      $xen_pv_domain_build"
 fi
 echo "brlapi support    $brlapi"
 echo "bluez  support    $bluez"
@@ -6539,9 +6525,6 @@ fi
 if test "$xen" = "yes" ; then
   echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak
   echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak
-  if test "$xen_pv_domain_build" = "yes" ; then
-    echo "CONFIG_XEN_PV_DOMAIN_BUILD=y" >> $config_host_mak
-  fi
 fi
 if test "$linux_aio" = "yes" ; then
   echo "CONFIG_LINUX_AIO=y" >> $config_host_mak
@@ -7487,7 +7470,6 @@ alpha)
 esac
 
 if test "$gprof" = "yes" ; then
-  echo "CONFIG_GPROF=y" >> $config_host_mak
   echo "TARGET_GPROF=y" >> $config_target_mak
   if test "$target_linux_user" = "yes" ; then
     cflags="-p $cflags"
diff --git a/contrib/gitdm/domain-map b/contrib/gitdm/domain-map
index 8cbbcfe93d..0ab41ee27a 100644
--- a/contrib/gitdm/domain-map
+++ b/contrib/gitdm/domain-map
@@ -9,7 +9,9 @@ greensocs.com   GreenSocs
 ibm.com         IBM
 igalia.com      Igalia
 linaro.org      Linaro
+nokia.com       Nokia
 oracle.com      Oracle
+proxmox.com     Proxmox
 redhat.com      Red Hat
 siemens.com     Siemens
 sifive.com      SiFive
diff --git a/contrib/gitdm/group-map-ibm b/contrib/gitdm/group-map-ibm
index b66db5f4a8..22727319b3 100644
--- a/contrib/gitdm/group-map-ibm
+++ b/contrib/gitdm/group-map-ibm
@@ -2,5 +2,12 @@
 # Some IBM contributors submit via another domain
 #
 
+aik@ozlabs.ru
+andrew@aj.id.au
+benh@kernel.crashing.org
 clg@kaod.org
+danielhb413@gmail.com
 groug@kaod.org
+jcfaracco@gmail.com
+joel@jms.id.au
+sjitindarsingh@gmail.com
diff --git a/contrib/gitdm/group-map-wavecomp b/contrib/gitdm/group-map-wavecomp
index c571a52c65..2801a966b6 100644
--- a/contrib/gitdm/group-map-wavecomp
+++ b/contrib/gitdm/group-map-wavecomp
@@ -9,6 +9,7 @@ amarkovic@wavecomp.com
 arikalo@wavecomp.com
 dnikolic@wavecomp.com
 james.hogan@mips.com
+leon.alrae@imgtec.com
 matthew.fortune@mips.com
 paul.burton@imgtec.com
 pburton@wavecomp.com
diff --git a/hmp.c b/hmp.c
index 80aa5ab504..8da5fd8760 100644
--- a/hmp.c
+++ b/hmp.c
@@ -2326,7 +2326,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
         }
 
         qmp_nbd_server_add(info->value->device, false, NULL,
-                           true, writable, &local_err);
+                           true, writable, false, NULL, &local_err);
 
         if (local_err != NULL) {
             qmp_nbd_server_stop(NULL);
@@ -2347,7 +2347,8 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict)
     bool writable = qdict_get_try_bool(qdict, "writable", false);
     Error *local_err = NULL;
 
-    qmp_nbd_server_add(device, !!name, name, true, writable, &local_err);
+    qmp_nbd_server_add(device, !!name, name, true, writable,
+                       false, NULL, &local_err);
     hmp_handle_error(mon, &local_err);
 }
 
diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c
index 9015fe7773..25ab04d95a 100644
--- a/hw/9pfs/xen-9p-backend.c
+++ b/hw/9pfs/xen-9p-backend.c
@@ -12,7 +12,7 @@
 
 #include "hw/hw.h"
 #include "hw/9pfs/9p.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "hw/9pfs/xen-9pfs.h"
 #include "qapi/error.h"
 #include "qemu/config-file.h"
@@ -45,7 +45,7 @@ typedef struct Xen9pfsRing {
 } Xen9pfsRing;
 
 typedef struct Xen9pfsDev {
-    struct XenDevice xendev;  /* must be first */
+    struct XenLegacyDevice xendev;  /* must be first */
     V9fsState state;
     char *path;
     char *security_model;
@@ -56,7 +56,7 @@ typedef struct Xen9pfsDev {
     Xen9pfsRing *rings;
 } Xen9pfsDev;
 
-static void xen_9pfs_disconnect(struct XenDevice *xendev);
+static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev);
 
 static void xen_9pfs_in_sg(Xen9pfsRing *ring,
                            struct iovec *in_sg,
@@ -243,7 +243,7 @@ static const V9fsTransport xen_9p_transport = {
     .push_and_notify = xen_9pfs_push_and_notify,
 };
 
-static int xen_9pfs_init(struct XenDevice *xendev)
+static int xen_9pfs_init(struct XenLegacyDevice *xendev)
 {
     return 0;
 }
@@ -305,7 +305,7 @@ static void xen_9pfs_evtchn_event(void *opaque)
     qemu_bh_schedule(ring->bh);
 }
 
-static void xen_9pfs_disconnect(struct XenDevice *xendev)
+static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev)
 {
     Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev);
     int i;
@@ -321,7 +321,7 @@ static void xen_9pfs_disconnect(struct XenDevice *xendev)
     }
 }
 
-static int xen_9pfs_free(struct XenDevice *xendev)
+static int xen_9pfs_free(struct XenLegacyDevice *xendev)
 {
     Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev);
     int i;
@@ -354,7 +354,7 @@ static int xen_9pfs_free(struct XenDevice *xendev)
     return 0;
 }
 
-static int xen_9pfs_connect(struct XenDevice *xendev)
+static int xen_9pfs_connect(struct XenLegacyDevice *xendev)
 {
     Error *err = NULL;
     int i;
@@ -467,7 +467,7 @@ out:
     return -1;
 }
 
-static void xen_9pfs_alloc(struct XenDevice *xendev)
+static void xen_9pfs_alloc(struct XenLegacyDevice *xendev)
 {
     xenstore_write_be_str(xendev, "versions", VERSIONS);
     xenstore_write_be_int(xendev, "max-rings", MAX_RINGS);
diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs
index 53ce5751ae..e206b8e712 100644
--- a/hw/block/Makefile.objs
+++ b/hw/block/Makefile.objs
@@ -4,7 +4,7 @@ common-obj-$(CONFIG_SSI_M25P80) += m25p80.o
 common-obj-$(CONFIG_NAND) += nand.o
 common-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o
 common-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o
-common-obj-$(CONFIG_XEN) += xen_disk.o
+common-obj-$(CONFIG_XEN) += xen-block.o
 common-obj-$(CONFIG_ECC) += ecc.o
 common-obj-$(CONFIG_ONENAND) += onenand.o
 common-obj-$(CONFIG_NVME_PCI) += nvme.o
diff --git a/hw/block/dataplane/Makefile.objs b/hw/block/dataplane/Makefile.objs
index e786f66421..c6c68dbc00 100644
--- a/hw/block/dataplane/Makefile.objs
+++ b/hw/block/dataplane/Makefile.objs
@@ -1 +1,2 @@
 obj-y += virtio-blk.o
+obj-$(CONFIG_XEN) += xen-block.o
diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
new file mode 100644
index 0000000000..d0d8905a33
--- /dev/null
+++ b/hw/block/dataplane/xen-block.c
@@ -0,0 +1,827 @@
+/*
+ * Copyright (c) 2018  Citrix Systems Inc.
+ * (c) Gerd Hoffmann <kraxel@redhat.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; under version 2 of the License.
+ *
+ * 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/>.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/xen/xen_common.h"
+#include "hw/block/xen_blkif.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/iothread.h"
+#include "xen-block.h"
+
+typedef struct XenBlockRequest {
+    blkif_request_t req;
+    int16_t status;
+    off_t start;
+    QEMUIOVector v;
+    void *buf;
+    size_t size;
+    int presync;
+    int aio_inflight;
+    int aio_errors;
+    XenBlockDataPlane *dataplane;
+    QLIST_ENTRY(XenBlockRequest) list;
+    BlockAcctCookie acct;
+} XenBlockRequest;
+
+struct XenBlockDataPlane {
+    XenDevice *xendev;
+    XenEventChannel *event_channel;
+    unsigned int *ring_ref;
+    unsigned int nr_ring_ref;
+    void *sring;
+    int64_t file_blk;
+    int64_t file_size;
+    int protocol;
+    blkif_back_rings_t rings;
+    int more_work;
+    QLIST_HEAD(inflight_head, XenBlockRequest) inflight;
+    QLIST_HEAD(freelist_head, XenBlockRequest) freelist;
+    int requests_total;
+    int requests_inflight;
+    unsigned int max_requests;
+    BlockBackend *blk;
+    QEMUBH *bh;
+    IOThread *iothread;
+    AioContext *ctx;
+};
+
+static void reset_request(XenBlockRequest *request)
+{
+    memset(&request->req, 0, sizeof(request->req));
+    request->status = 0;
+    request->start = 0;
+    request->size = 0;
+    request->presync = 0;
+
+    request->aio_inflight = 0;
+    request->aio_errors = 0;
+
+    request->dataplane = NULL;
+    memset(&request->list, 0, sizeof(request->list));
+    memset(&request->acct, 0, sizeof(request->acct));
+
+    qemu_iovec_reset(&request->v);
+}
+
+static XenBlockRequest *xen_block_start_request(XenBlockDataPlane *dataplane)
+{
+    XenBlockRequest *request = NULL;
+
+    if (QLIST_EMPTY(&dataplane->freelist)) {
+        if (dataplane->requests_total >= dataplane->max_requests) {
+            goto out;
+        }
+        /* allocate new struct */
+        request = g_malloc0(sizeof(*request));
+        request->dataplane = dataplane;
+        /*
+         * We cannot need more pages per requests than this, and since we
+         * re-use requests, allocate the memory once here. It will be freed
+         * xen_block_dataplane_destroy() when the request list is freed.
+         */
+        request->buf = qemu_memalign(XC_PAGE_SIZE,
+                                     BLKIF_MAX_SEGMENTS_PER_REQUEST *
+                                     XC_PAGE_SIZE);
+        dataplane->requests_total++;
+        qemu_iovec_init(&request->v, 1);
+    } else {
+        /* get one from freelist */
+        request = QLIST_FIRST(&dataplane->freelist);
+        QLIST_REMOVE(request, list);
+    }
+    QLIST_INSERT_HEAD(&dataplane->inflight, request, list);
+    dataplane->requests_inflight++;
+
+out:
+    return request;
+}
+
+static void xen_block_finish_request(XenBlockRequest *request)
+{
+    XenBlockDataPlane *dataplane = request->dataplane;
+
+    QLIST_REMOVE(request, list);
+    dataplane->requests_inflight--;
+}
+
+static void xen_block_release_request(XenBlockRequest *request)
+{
+    XenBlockDataPlane *dataplane = request->dataplane;
+
+    QLIST_REMOVE(request, list);
+    reset_request(request);
+    request->dataplane = dataplane;
+    QLIST_INSERT_HEAD(&dataplane->freelist, request, list);
+    dataplane->requests_inflight--;
+}
+
+/*
+ * translate request into iovec + start offset
+ * do sanity checks along the way
+ */
+static int xen_block_parse_request(XenBlockRequest *request)
+{
+    XenBlockDataPlane *dataplane = request->dataplane;
+    size_t len;
+    int i;
+
+    switch (request->req.operation) {
+    case BLKIF_OP_READ:
+        break;
+    case BLKIF_OP_FLUSH_DISKCACHE:
+        request->presync = 1;
+        if (!request->req.nr_segments) {
+            return 0;
+        }
+        /* fall through */
+    case BLKIF_OP_WRITE:
+        break;
+    case BLKIF_OP_DISCARD:
+        return 0;
+    default:
+        error_report("error: unknown operation (%d)", request->req.operation);
+        goto err;
+    };
+
+    if (request->req.operation != BLKIF_OP_READ &&
+        blk_is_read_only(dataplane->blk)) {
+        error_report("error: write req for ro device");
+        goto err;
+    }
+
+    request->start = request->req.sector_number * dataplane->file_blk;
+    for (i = 0; i < request->req.nr_segments; i++) {
+        if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) {
+            error_report("error: nr_segments too big");
+            goto err;
+        }
+        if (request->req.seg[i].first_sect > request->req.seg[i].last_sect) {
+            error_report("error: first > last sector");
+            goto err;
+        }
+        if (request->req.seg[i].last_sect * dataplane->file_blk >=
+            XC_PAGE_SIZE) {
+            error_report("error: page crossing");
+            goto err;
+        }
+
+        len = (request->req.seg[i].last_sect -
+               request->req.seg[i].first_sect + 1) * dataplane->file_blk;
+        request->size += len;
+    }
+    if (request->start + request->size > dataplane->file_size) {
+        error_report("error: access beyond end of file");
+        goto err;
+    }
+    return 0;
+
+err:
+    request->status = BLKIF_RSP_ERROR;
+    return -1;
+}
+
+static int xen_block_copy_request(XenBlockRequest *request)
+{
+    XenBlockDataPlane *dataplane = request->dataplane;
+    XenDevice *xendev = dataplane->xendev;
+    XenDeviceGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+    int i, count;
+    int64_t file_blk = dataplane->file_blk;
+    bool to_domain = (request->req.operation == BLKIF_OP_READ);
+    void *virt = request->buf;
+    Error *local_err = NULL;
+
+    if (request->req.nr_segments == 0) {
+        return 0;
+    }
+
+    count = request->req.nr_segments;
+
+    for (i = 0; i < count; i++) {
+        if (to_domain) {
+            segs[i].dest.foreign.ref = request->req.seg[i].gref;
+            segs[i].dest.foreign.offset = request->req.seg[i].first_sect *
+                file_blk;
+            segs[i].source.virt = virt;
+        } else {
+            segs[i].source.foreign.ref = request->req.seg[i].gref;
+            segs[i].source.foreign.offset = request->req.seg[i].first_sect *
+                file_blk;
+            segs[i].dest.virt = virt;
+        }
+        segs[i].len = (request->req.seg[i].last_sect -
+                       request->req.seg[i].first_sect + 1) * file_blk;
+        virt += segs[i].len;
+    }
+
+    xen_device_copy_grant_refs(xendev, to_domain, segs, count, &local_err);
+
+    if (local_err) {
+        error_reportf_err(local_err, "failed to copy data: ");
+
+        request->aio_errors++;
+        return -1;
+    }
+
+    return 0;
+}
+
+static int xen_block_do_aio(XenBlockRequest *request);
+static int xen_block_send_response(XenBlockRequest *request);
+
+static void xen_block_complete_aio(void *opaque, int ret)
+{
+    XenBlockRequest *request = opaque;
+    XenBlockDataPlane *dataplane = request->dataplane;
+
+    aio_context_acquire(dataplane->ctx);
+
+    if (ret != 0) {
+        error_report("%s I/O error",
+                     request->req.operation == BLKIF_OP_READ ?
+                     "read" : "write");
+        request->aio_errors++;
+    }
+
+    request->aio_inflight--;
+    if (request->presync) {
+        request->presync = 0;
+        xen_block_do_aio(request);
+        goto done;
+    }
+    if (request->aio_inflight > 0) {
+        goto done;
+    }
+
+    switch (request->req.operation) {
+    case BLKIF_OP_READ:
+        /* in case of failure request->aio_errors is increased */
+        if (ret == 0) {
+            xen_block_copy_request(request);
+        }
+        break;
+    case BLKIF_OP_WRITE:
+    case BLKIF_OP_FLUSH_DISKCACHE:
+        if (!request->req.nr_segments) {
+            break;
+        }
+        break;
+    default:
+        break;
+    }
+
+    request->status = request->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
+    xen_block_finish_request(request);
+
+    switch (request->req.operation) {
+    case BLKIF_OP_WRITE:
+    case BLKIF_OP_FLUSH_DISKCACHE:
+        if (!request->req.nr_segments) {
+            break;
+        }
+    case BLKIF_OP_READ:
+        if (request->status == BLKIF_RSP_OKAY) {
+            block_acct_done(blk_get_stats(dataplane->blk), &request->acct);
+        } else {
+            block_acct_failed(blk_get_stats(dataplane->blk), &request->acct);
+        }
+        break;
+    case BLKIF_OP_DISCARD:
+    default:
+        break;
+    }
+    if (xen_block_send_response(request)) {
+        Error *local_err = NULL;
+
+        xen_device_notify_event_channel(dataplane->xendev,
+                                        dataplane->event_channel,
+                                        &local_err);
+        if (local_err) {
+            error_report_err(local_err);
+        }
+    }
+    xen_block_release_request(request);
+
+    qemu_bh_schedule(dataplane->bh);
+
+done:
+    aio_context_release(dataplane->ctx);
+}
+
+static bool xen_block_split_discard(XenBlockRequest *request,
+                                    blkif_sector_t sector_number,
+                                    uint64_t nr_sectors)
+{
+    XenBlockDataPlane *dataplane = request->dataplane;
+    int64_t byte_offset;
+    int byte_chunk;
+    uint64_t byte_remaining, limit;
+    uint64_t sec_start = sector_number;
+    uint64_t sec_count = nr_sectors;
+
+    /* Wrap around, or overflowing byte limit? */
+    if (sec_start + sec_count < sec_count ||
+        sec_start + sec_count > INT64_MAX / dataplane->file_blk) {
+        return false;
+    }
+
+    limit = BDRV_REQUEST_MAX_SECTORS * dataplane->file_blk;
+    byte_offset = sec_start * dataplane->file_blk;
+    byte_remaining = sec_count * dataplane->file_blk;
+
+    do {
+        byte_chunk = byte_remaining > limit ? limit : byte_remaining;
+        request->aio_inflight++;
+        blk_aio_pdiscard(dataplane->blk, byte_offset, byte_chunk,
+                         xen_block_complete_aio, request);
+        byte_remaining -= byte_chunk;
+        byte_offset += byte_chunk;
+    } while (byte_remaining > 0);
+
+    return true;
+}
+
+static int xen_block_do_aio(XenBlockRequest *request)
+{
+    XenBlockDataPlane *dataplane = request->dataplane;
+
+    if (request->req.nr_segments &&
+        (request->req.operation == BLKIF_OP_WRITE ||
+         request->req.operation == BLKIF_OP_FLUSH_DISKCACHE) &&
+        xen_block_copy_request(request)) {
+        goto err;
+    }
+
+    request->aio_inflight++;
+    if (request->presync) {
+        blk_aio_flush(request->dataplane->blk, xen_block_complete_aio,
+                      request);
+        return 0;
+    }
+
+    switch (request->req.operation) {
+    case BLKIF_OP_READ:
+        qemu_iovec_add(&request->v, request->buf, request->size);
+        block_acct_start(blk_get_stats(dataplane->blk), &request->acct,
+                         request->v.size, BLOCK_ACCT_READ);
+        request->aio_inflight++;
+        blk_aio_preadv(dataplane->blk, request->start, &request->v, 0,
+                       xen_block_complete_aio, request);
+        break;
+    case BLKIF_OP_WRITE:
+    case BLKIF_OP_FLUSH_DISKCACHE:
+        if (!request->req.nr_segments) {
+            break;
+        }
+
+        qemu_iovec_add(&request->v, request->buf, request->size);
+        block_acct_start(blk_get_stats(dataplane->blk), &request->acct,
+                         request->v.size,
+                         request->req.operation == BLKIF_OP_WRITE ?
+                         BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH);
+        request->aio_inflight++;
+        blk_aio_pwritev(dataplane->blk, request->start, &request->v, 0,
+                        xen_block_complete_aio, request);
+        break;
+    case BLKIF_OP_DISCARD:
+    {
+        struct blkif_request_discard *req = (void *)&request->req;
+        if (!xen_block_split_discard(request, req->sector_number,
+                                     req->nr_sectors)) {
+            goto err;
+        }
+        break;
+    }
+    default:
+        /* unknown operation (shouldn't happen -- parse catches this) */
+        goto err;
+    }
+
+    xen_block_complete_aio(request, 0);
+
+    return 0;
+
+err:
+    xen_block_finish_request(request);
+    request->status = BLKIF_RSP_ERROR;
+    return -1;
+}
+
+static int xen_block_send_response(XenBlockRequest *request)
+{
+    XenBlockDataPlane *dataplane = request->dataplane;
+    int send_notify = 0;
+    int have_requests = 0;
+    blkif_response_t *resp;
+
+    /* Place on the response ring for the relevant domain. */
+    switch (dataplane->protocol) {
+    case BLKIF_PROTOCOL_NATIVE:
+        resp = (blkif_response_t *)RING_GET_RESPONSE(
+            &dataplane->rings.native,
+            dataplane->rings.native.rsp_prod_pvt);
+        break;
+    case BLKIF_PROTOCOL_X86_32:
+        resp = (blkif_response_t *)RING_GET_RESPONSE(
+            &dataplane->rings.x86_32_part,
+            dataplane->rings.x86_32_part.rsp_prod_pvt);
+        break;
+    case BLKIF_PROTOCOL_X86_64:
+        resp = (blkif_response_t *)RING_GET_RESPONSE(
+            &dataplane->rings.x86_64_part,
+            dataplane->rings.x86_64_part.rsp_prod_pvt);
+        break;
+    default:
+        return 0;
+    }
+
+    resp->id = request->req.id;
+    resp->operation = request->req.operation;
+    resp->status = request->status;
+
+    dataplane->rings.common.rsp_prod_pvt++;
+
+    RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&dataplane->rings.common,
+                                         send_notify);
+    if (dataplane->rings.common.rsp_prod_pvt ==
+        dataplane->rings.common.req_cons) {
+        /*
+         * Tail check for pending requests. Allows frontend to avoid
+         * notifications if requests are already in flight (lower
+         * overheads and promotes batching).
+         */
+        RING_FINAL_CHECK_FOR_REQUESTS(&dataplane->rings.common,
+                                      have_requests);
+    } else if (RING_HAS_UNCONSUMED_REQUESTS(&dataplane->rings.common)) {
+        have_requests = 1;
+    }
+
+    if (have_requests) {
+        dataplane->more_work++;
+    }
+    return send_notify;
+}
+
+static int xen_block_get_request(XenBlockDataPlane *dataplane,
+                                 XenBlockRequest *request, RING_IDX rc)
+{
+    switch (dataplane->protocol) {
+    case BLKIF_PROTOCOL_NATIVE: {
+        blkif_request_t *req =
+            RING_GET_REQUEST(&dataplane->rings.native, rc);
+
+        memcpy(&request->req, req, sizeof(request->req));
+        break;
+    }
+    case BLKIF_PROTOCOL_X86_32: {
+        blkif_x86_32_request_t *req =
+            RING_GET_REQUEST(&dataplane->rings.x86_32_part, rc);
+
+        blkif_get_x86_32_req(&request->req, req);
+        break;
+    }
+    case BLKIF_PROTOCOL_X86_64: {
+        blkif_x86_64_request_t *req =
+            RING_GET_REQUEST(&dataplane->rings.x86_64_part, rc);
+
+        blkif_get_x86_64_req(&request->req, req);
+        break;
+    }
+    }
+    /* Prevent the compiler from accessing the on-ring fields instead. */
+    barrier();
+    return 0;
+}
+
+/*
+ * Threshold of in-flight requests above which we will start using
+ * blk_io_plug()/blk_io_unplug() to batch requests.
+ */
+#define IO_PLUG_THRESHOLD 1
+
+static void xen_block_handle_requests(XenBlockDataPlane *dataplane)
+{
+    RING_IDX rc, rp;
+    XenBlockRequest *request;
+    int inflight_atstart = dataplane->requests_inflight;
+    int batched = 0;
+
+    dataplane->more_work = 0;
+
+    rc = dataplane->rings.common.req_cons;
+    rp = dataplane->rings.common.sring->req_prod;
+    xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+    /*
+     * If there was more than IO_PLUG_THRESHOLD requests in flight
+     * when we got here, this is an indication that there the bottleneck
+     * is below us, so it's worth beginning to batch up I/O requests
+     * rather than submitting them immediately. The maximum number
+     * of requests we're willing to batch is the number already in
+     * flight, so it can grow up to max_requests when the bottleneck
+     * is below us.
+     */
+    if (inflight_atstart > IO_PLUG_THRESHOLD) {
+        blk_io_plug(dataplane->blk);
+    }
+    while (rc != rp) {
+        /* pull request from ring */
+        if (RING_REQUEST_CONS_OVERFLOW(&dataplane->rings.common, rc)) {
+            break;
+        }
+        request = xen_block_start_request(dataplane);
+        if (request == NULL) {
+            dataplane->more_work++;
+            break;
+        }
+        xen_block_get_request(dataplane, request, rc);
+        dataplane->rings.common.req_cons = ++rc;
+
+        /* parse them */
+        if (xen_block_parse_request(request) != 0) {
+            switch (request->req.operation) {
+            case BLKIF_OP_READ:
+                block_acct_invalid(blk_get_stats(dataplane->blk),
+                                   BLOCK_ACCT_READ);
+                break;
+            case BLKIF_OP_WRITE:
+                block_acct_invalid(blk_get_stats(dataplane->blk),
+                                   BLOCK_ACCT_WRITE);
+                break;
+            case BLKIF_OP_FLUSH_DISKCACHE:
+                block_acct_invalid(blk_get_stats(dataplane->blk),
+                                   BLOCK_ACCT_FLUSH);
+            default:
+                break;
+            };
+
+            if (xen_block_send_response(request)) {
+                Error *local_err = NULL;
+
+                xen_device_notify_event_channel(dataplane->xendev,
+                                                dataplane->event_channel,
+                                                &local_err);
+                if (local_err) {
+                    error_report_err(local_err);
+                }
+            }
+            xen_block_release_request(request);
+            continue;
+        }
+
+        if (inflight_atstart > IO_PLUG_THRESHOLD &&
+            batched >= inflight_atstart) {
+            blk_io_unplug(dataplane->blk);
+        }
+        xen_block_do_aio(request);
+        if (inflight_atstart > IO_PLUG_THRESHOLD) {
+            if (batched >= inflight_atstart) {
+                blk_io_plug(dataplane->blk);
+                batched = 0;
+            } else {
+                batched++;
+            }
+        }
+    }
+    if (inflight_atstart > IO_PLUG_THRESHOLD) {
+        blk_io_unplug(dataplane->blk);
+    }
+
+    if (dataplane->more_work &&
+        dataplane->requests_inflight < dataplane->max_requests) {
+        qemu_bh_schedule(dataplane->bh);
+    }
+}
+
+static void xen_block_dataplane_bh(void *opaque)
+{
+    XenBlockDataPlane *dataplane = opaque;
+
+    aio_context_acquire(dataplane->ctx);
+    xen_block_handle_requests(dataplane);
+    aio_context_release(dataplane->ctx);
+}
+
+static void xen_block_dataplane_event(void *opaque)
+{
+    XenBlockDataPlane *dataplane = opaque;
+
+    qemu_bh_schedule(dataplane->bh);
+}
+
+XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev,
+                                              BlockConf *conf,
+                                              IOThread *iothread)
+{
+    XenBlockDataPlane *dataplane = g_new0(XenBlockDataPlane, 1);
+
+    dataplane->xendev = xendev;
+    dataplane->file_blk = conf->logical_block_size;
+    dataplane->blk = conf->blk;
+    dataplane->file_size = blk_getlength(dataplane->blk);
+
+    QLIST_INIT(&dataplane->inflight);
+    QLIST_INIT(&dataplane->freelist);
+
+    if (iothread) {
+        dataplane->iothread = iothread;
+        object_ref(OBJECT(dataplane->iothread));
+        dataplane->ctx = iothread_get_aio_context(dataplane->iothread);
+    } else {
+        dataplane->ctx = qemu_get_aio_context();
+    }
+    dataplane->bh = aio_bh_new(dataplane->ctx, xen_block_dataplane_bh,
+                               dataplane);
+
+    return dataplane;
+}
+
+void xen_block_dataplane_destroy(XenBlockDataPlane *dataplane)
+{
+    XenBlockRequest *request;
+
+    if (!dataplane) {
+        return;
+    }
+
+    while (!QLIST_EMPTY(&dataplane->freelist)) {
+        request = QLIST_FIRST(&dataplane->freelist);
+        QLIST_REMOVE(request, list);
+        qemu_iovec_destroy(&request->v);
+        qemu_vfree(request->buf);
+        g_free(request);
+    }
+
+    qemu_bh_delete(dataplane->bh);
+    if (dataplane->iothread) {
+        object_unref(OBJECT(dataplane->iothread));
+    }
+
+    g_free(dataplane);
+}
+
+void xen_block_dataplane_stop(XenBlockDataPlane *dataplane)
+{
+    XenDevice *xendev;
+
+    if (!dataplane) {
+        return;
+    }
+
+    aio_context_acquire(dataplane->ctx);
+    blk_set_aio_context(dataplane->blk, qemu_get_aio_context());
+    aio_context_release(dataplane->ctx);
+
+    xendev = dataplane->xendev;
+
+    if (dataplane->event_channel) {
+        Error *local_err = NULL;
+
+        xen_device_unbind_event_channel(xendev, dataplane->event_channel,
+                                        &local_err);
+        dataplane->event_channel = NULL;
+
+        if (local_err) {
+            error_report_err(local_err);
+        }
+    }
+
+    if (dataplane->sring) {
+        Error *local_err = NULL;
+
+        xen_device_unmap_grant_refs(xendev, dataplane->sring,
+                                    dataplane->nr_ring_ref, &local_err);
+        dataplane->sring = NULL;
+
+        if (local_err) {
+            error_report_err(local_err);
+        }
+    }
+
+    g_free(dataplane->ring_ref);
+    dataplane->ring_ref = NULL;
+}
+
+void xen_block_dataplane_start(XenBlockDataPlane *dataplane,
+                               const unsigned int ring_ref[],
+                               unsigned int nr_ring_ref,
+                               unsigned int event_channel,
+                               unsigned int protocol,
+                               Error **errp)
+{
+    XenDevice *xendev = dataplane->xendev;
+    Error *local_err = NULL;
+    unsigned int ring_size;
+    unsigned int i;
+
+    dataplane->nr_ring_ref = nr_ring_ref;
+    dataplane->ring_ref = g_new(unsigned int, nr_ring_ref);
+
+    for (i = 0; i < nr_ring_ref; i++) {
+        dataplane->ring_ref[i] = ring_ref[i];
+    }
+
+    dataplane->protocol = protocol;
+
+    ring_size = XC_PAGE_SIZE * dataplane->nr_ring_ref;
+    switch (dataplane->protocol) {
+    case BLKIF_PROTOCOL_NATIVE:
+    {
+        dataplane->max_requests = __CONST_RING_SIZE(blkif, ring_size);
+        break;
+    }
+    case BLKIF_PROTOCOL_X86_32:
+    {
+        dataplane->max_requests = __CONST_RING_SIZE(blkif_x86_32, ring_size);
+        break;
+    }
+    case BLKIF_PROTOCOL_X86_64:
+    {
+        dataplane->max_requests = __CONST_RING_SIZE(blkif_x86_64, ring_size);
+        break;
+    }
+    default:
+        error_setg(errp, "unknown protocol %u", dataplane->protocol);
+        return;
+    }
+
+    xen_device_set_max_grant_refs(xendev, dataplane->nr_ring_ref,
+                                  &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto stop;
+    }
+
+    dataplane->sring = xen_device_map_grant_refs(xendev,
+                                              dataplane->ring_ref,
+                                              dataplane->nr_ring_ref,
+                                              PROT_READ | PROT_WRITE,
+                                              &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto stop;
+    }
+
+    switch (dataplane->protocol) {
+    case BLKIF_PROTOCOL_NATIVE:
+    {
+        blkif_sring_t *sring_native = dataplane->sring;
+
+        BACK_RING_INIT(&dataplane->rings.native, sring_native, ring_size);
+        break;
+    }
+    case BLKIF_PROTOCOL_X86_32:
+    {
+        blkif_x86_32_sring_t *sring_x86_32 = dataplane->sring;
+
+        BACK_RING_INIT(&dataplane->rings.x86_32_part, sring_x86_32,
+                       ring_size);
+        break;
+    }
+    case BLKIF_PROTOCOL_X86_64:
+    {
+        blkif_x86_64_sring_t *sring_x86_64 = dataplane->sring;
+
+        BACK_RING_INIT(&dataplane->rings.x86_64_part, sring_x86_64,
+                       ring_size);
+        break;
+    }
+    }
+
+    dataplane->event_channel =
+        xen_device_bind_event_channel(xendev, event_channel,
+                                      xen_block_dataplane_event, dataplane,
+                                      &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto stop;
+    }
+
+    aio_context_acquire(dataplane->ctx);
+    blk_set_aio_context(dataplane->blk, dataplane->ctx);
+    aio_context_release(dataplane->ctx);
+    return;
+
+stop:
+    xen_block_dataplane_stop(dataplane);
+}
diff --git a/hw/block/dataplane/xen-block.h b/hw/block/dataplane/xen-block.h
new file mode 100644
index 0000000000..d6fa6d26dd
--- /dev/null
+++ b/hw/block/dataplane/xen-block.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018  Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_BLOCK_DATAPLANE_XEN_BLOCK_H
+#define HW_BLOCK_DATAPLANE_XEN_BLOCK_H
+
+#include "hw/block/block.h"
+#include "hw/xen/xen-bus.h"
+#include "sysemu/iothread.h"
+
+typedef struct XenBlockDataPlane XenBlockDataPlane;
+
+XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev,
+                                              BlockConf *conf,
+                                              IOThread *iothread);
+void xen_block_dataplane_destroy(XenBlockDataPlane *dataplane);
+void xen_block_dataplane_start(XenBlockDataPlane *dataplane,
+                               const unsigned int ring_ref[],
+                               unsigned int nr_ring_ref,
+                               unsigned int event_channel,
+                               unsigned int protocol,
+                               Error **errp);
+void xen_block_dataplane_stop(XenBlockDataPlane *dataplane);
+
+#endif /* HW_BLOCK_DATAPLANE_XEN_BLOCK_H */
diff --git a/hw/block/trace-events b/hw/block/trace-events
index 335c092450..55e5a5500c 100644
--- a/hw/block/trace-events
+++ b/hw/block/trace-events
@@ -127,3 +127,17 @@ xen_disk_init(char *name) "%s"
 xen_disk_connect(char *name) "%s"
 xen_disk_disconnect(char *name) "%s"
 xen_disk_free(char *name) "%s"
+
+# hw/block/xen-block.c
+xen_block_realize(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u"
+xen_block_connect(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u"
+xen_block_disconnect(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u"
+xen_block_unrealize(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u"
+xen_disk_realize(void) ""
+xen_disk_unrealize(void) ""
+xen_cdrom_realize(void) ""
+xen_cdrom_unrealize(void) ""
+xen_block_blockdev_add(char *str) "%s"
+xen_block_blockdev_del(const char *node_name) "%s"
+xen_block_device_create(unsigned int number) "%u"
+xen_block_device_destroy(unsigned int number) "%u"
diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c
new file mode 100644
index 0000000000..be28b63442
--- /dev/null
+++ b/hw/block/xen-block.c
@@ -0,0 +1,963 @@
+/*
+ * Copyright (c) 2018  Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/option.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-block-core.h"
+#include "qapi/qapi-commands-misc.h"
+#include "qapi/qapi-visit-block-core.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/visitor.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qstring.h"
+#include "hw/hw.h"
+#include "hw/xen/xen_common.h"
+#include "hw/block/xen_blkif.h"
+#include "hw/xen/xen-block.h"
+#include "hw/xen/xen-backend.h"
+#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/iothread.h"
+#include "dataplane/xen-block.h"
+#include "trace.h"
+
+static char *xen_block_get_name(XenDevice *xendev, Error **errp)
+{
+    XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+    XenBlockVdev *vdev = &blockdev->props.vdev;
+
+    return g_strdup_printf("%lu", vdev->number);
+}
+
+static void xen_block_disconnect(XenDevice *xendev, Error **errp)
+{
+    XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+    const char *type = object_get_typename(OBJECT(blockdev));
+    XenBlockVdev *vdev = &blockdev->props.vdev;
+
+    trace_xen_block_disconnect(type, vdev->disk, vdev->partition);
+
+    xen_block_dataplane_stop(blockdev->dataplane);
+}
+
+static void xen_block_connect(XenDevice *xendev, Error **errp)
+{
+    XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+    const char *type = object_get_typename(OBJECT(blockdev));
+    XenBlockVdev *vdev = &blockdev->props.vdev;
+    unsigned int order, nr_ring_ref, *ring_ref, event_channel, protocol;
+    char *str;
+
+    trace_xen_block_connect(type, vdev->disk, vdev->partition);
+
+    if (xen_device_frontend_scanf(xendev, "ring-page-order", "%u",
+                                  &order) != 1) {
+        nr_ring_ref = 1;
+        ring_ref = g_new(unsigned int, nr_ring_ref);
+
+        if (xen_device_frontend_scanf(xendev, "ring-ref", "%u",
+                                      &ring_ref[0]) != 1) {
+            error_setg(errp, "failed to read ring-ref");
+            g_free(ring_ref);
+            return;
+        }
+    } else if (order <= blockdev->props.max_ring_page_order) {
+        unsigned int i;
+
+        nr_ring_ref = 1 << order;
+        ring_ref = g_new(unsigned int, nr_ring_ref);
+
+        for (i = 0; i < nr_ring_ref; i++) {
+            const char *key = g_strdup_printf("ring-ref%u", i);
+
+            if (xen_device_frontend_scanf(xendev, key, "%u",
+                                          &ring_ref[i]) != 1) {
+                error_setg(errp, "failed to read %s", key);
+                g_free((gpointer)key);
+                g_free(ring_ref);
+                return;
+            }
+
+            g_free((gpointer)key);
+        }
+    } else {
+        error_setg(errp, "invalid ring-page-order (%d)", order);
+        return;
+    }
+
+    if (xen_device_frontend_scanf(xendev, "event-channel", "%u",
+                                  &event_channel) != 1) {
+        error_setg(errp, "failed to read event-channel");
+        g_free(ring_ref);
+        return;
+    }
+
+    if (xen_device_frontend_scanf(xendev, "protocol", "%ms",
+                                  &str) != 1) {
+        protocol = BLKIF_PROTOCOL_NATIVE;
+    } else {
+        if (strcmp(str, XEN_IO_PROTO_ABI_X86_32) == 0) {
+            protocol = BLKIF_PROTOCOL_X86_32;
+        } else if (strcmp(str, XEN_IO_PROTO_ABI_X86_64) == 0) {
+            protocol = BLKIF_PROTOCOL_X86_64;
+        } else {
+            protocol = BLKIF_PROTOCOL_NATIVE;
+        }
+
+        free(str);
+    }
+
+    xen_block_dataplane_start(blockdev->dataplane, ring_ref, nr_ring_ref,
+                              event_channel, protocol, errp);
+
+    g_free(ring_ref);
+}
+
+static void xen_block_unrealize(XenDevice *xendev, Error **errp)
+{
+    XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+    XenBlockDeviceClass *blockdev_class =
+        XEN_BLOCK_DEVICE_GET_CLASS(xendev);
+    const char *type = object_get_typename(OBJECT(blockdev));
+    XenBlockVdev *vdev = &blockdev->props.vdev;
+
+    if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID) {
+        return;
+    }
+
+    trace_xen_block_unrealize(type, vdev->disk, vdev->partition);
+
+    /* Disconnect from the frontend in case this has not already happened */
+    xen_block_disconnect(xendev, NULL);
+
+    xen_block_dataplane_destroy(blockdev->dataplane);
+    blockdev->dataplane = NULL;
+
+    if (blockdev_class->unrealize) {
+        blockdev_class->unrealize(blockdev, errp);
+    }
+}
+
+static void xen_block_realize(XenDevice *xendev, Error **errp)
+{
+    XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+    XenBlockDeviceClass *blockdev_class =
+        XEN_BLOCK_DEVICE_GET_CLASS(xendev);
+    const char *type = object_get_typename(OBJECT(blockdev));
+    XenBlockVdev *vdev = &blockdev->props.vdev;
+    BlockConf *conf = &blockdev->props.conf;
+    Error *local_err = NULL;
+
+    if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID) {
+        error_setg(errp, "vdev property not set");
+        return;
+    }
+
+    trace_xen_block_realize(type, vdev->disk, vdev->partition);
+
+    if (blockdev_class->realize) {
+        blockdev_class->realize(blockdev, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            return;
+        }
+    }
+
+    /*
+     * The blkif protocol does not deal with removable media, so it must
+     * always be present, even for CDRom devices.
+     */
+    assert(conf->blk);
+    if (!blk_is_inserted(conf->blk)) {
+        error_setg(errp, "device needs media, but drive is empty");
+        return;
+    }
+
+    if (!blkconf_apply_backend_options(conf, blockdev->info & VDISK_READONLY,
+                                       false, errp)) {
+        return;
+    }
+
+    if (!(blockdev->info & VDISK_CDROM) &&
+        !blkconf_geometry(conf, NULL, 65535, 255, 255, errp)) {
+        return;
+    }
+
+    blkconf_blocksizes(conf);
+
+    if (conf->logical_block_size > conf->physical_block_size) {
+        error_setg(
+            errp, "logical_block_size > physical_block_size not supported");
+        return;
+    }
+
+    blk_set_guest_block_size(conf->blk, conf->logical_block_size);
+
+    if (conf->discard_granularity > 0) {
+        xen_device_backend_printf(xendev, "feature-discard", "%u", 1);
+    }
+
+    xen_device_backend_printf(xendev, "feature-flush-cache", "%u", 1);
+    xen_device_backend_printf(xendev, "max-ring-page-order", "%u",
+                              blockdev->props.max_ring_page_order);
+    xen_device_backend_printf(xendev, "info", "%u", blockdev->info);
+
+    xen_device_frontend_printf(xendev, "virtual-device", "%lu",
+                               vdev->number);
+    xen_device_frontend_printf(xendev, "device-type", "%s",
+                               blockdev->device_type);
+
+    xen_device_backend_printf(xendev, "sector-size", "%u",
+                              conf->logical_block_size);
+    xen_device_backend_printf(xendev, "sectors", "%lu",
+                              blk_getlength(conf->blk) /
+                              conf->logical_block_size);
+
+    blockdev->dataplane =
+        xen_block_dataplane_create(xendev, conf, blockdev->props.iothread);
+}
+
+static void xen_block_frontend_changed(XenDevice *xendev,
+                                       enum xenbus_state frontend_state,
+                                       Error **errp)
+{
+    enum xenbus_state backend_state = xen_device_backend_get_state(xendev);
+    Error *local_err = NULL;
+
+    switch (frontend_state) {
+    case XenbusStateInitialised:
+    case XenbusStateConnected:
+        if (backend_state == XenbusStateConnected) {
+            break;
+        }
+
+        xen_block_disconnect(xendev, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            break;
+        }
+
+        xen_block_connect(xendev, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            break;
+        }
+
+        xen_device_backend_set_state(xendev, XenbusStateConnected);
+        break;
+
+    case XenbusStateClosing:
+        xen_device_backend_set_state(xendev, XenbusStateClosing);
+        break;
+
+    case XenbusStateClosed:
+        xen_block_disconnect(xendev, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            break;
+        }
+
+        xen_device_backend_set_state(xendev, XenbusStateClosed);
+        break;
+
+    default:
+        break;
+    }
+}
+
+static char *disk_to_vbd_name(unsigned int disk)
+{
+    char *name, *prefix = (disk >= 26) ?
+        disk_to_vbd_name((disk / 26) - 1) : g_strdup("");
+
+    name = g_strdup_printf("%s%c", prefix, 'a' + disk % 26);
+    g_free(prefix);
+
+    return name;
+}
+
+static void xen_block_get_vdev(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    XenBlockVdev *vdev = qdev_get_prop_ptr(dev, prop);
+    char *str;
+
+    switch (vdev->type) {
+    case XEN_BLOCK_VDEV_TYPE_DP:
+        str = g_strdup_printf("d%lup%lu", vdev->disk, vdev->partition);
+        break;
+
+    case XEN_BLOCK_VDEV_TYPE_XVD:
+    case XEN_BLOCK_VDEV_TYPE_HD:
+    case XEN_BLOCK_VDEV_TYPE_SD: {
+        char *name = disk_to_vbd_name(vdev->disk);
+
+        str = g_strdup_printf("%s%s%lu",
+                              (vdev->type == XEN_BLOCK_VDEV_TYPE_XVD) ?
+                              "xvd" :
+                              (vdev->type == XEN_BLOCK_VDEV_TYPE_HD) ?
+                              "hd" :
+                              "sd",
+                              name, vdev->partition);
+        g_free(name);
+        break;
+    }
+    default:
+        error_setg(errp, "invalid vdev type");
+        return;
+    }
+
+    visit_type_str(v, name, &str, errp);
+    g_free(str);
+}
+
+static unsigned int vbd_name_to_disk(const char *name, const char **endp)
+{
+    unsigned int disk = 0;
+
+    while (*name != '\0') {
+        if (!g_ascii_isalpha(*name) || !g_ascii_islower(*name)) {
+            break;
+        }
+
+        disk *= 26;
+        disk += *name++ - 'a' + 1;
+    }
+    *endp = name;
+
+    return disk - 1;
+}
+
+static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    XenBlockVdev *vdev = qdev_get_prop_ptr(dev, prop);
+    Error *local_err = NULL;
+    char *str, *p;
+    const char *end;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_str(v, name, &str, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    p = strchr(str, 'd');
+    if (!p) {
+        goto invalid;
+    }
+
+    *p++ = '\0';
+    if (*str == '\0') {
+        vdev->type = XEN_BLOCK_VDEV_TYPE_DP;
+    } else if (strcmp(str, "xv") == 0) {
+        vdev->type = XEN_BLOCK_VDEV_TYPE_XVD;
+    } else if (strcmp(str, "h") == 0) {
+        vdev->type = XEN_BLOCK_VDEV_TYPE_HD;
+    } else if (strcmp(str, "s") == 0) {
+        vdev->type = XEN_BLOCK_VDEV_TYPE_SD;
+    } else {
+        goto invalid;
+    }
+
+    if (vdev->type == XEN_BLOCK_VDEV_TYPE_DP) {
+        if (qemu_strtoul(p, &end, 10, &vdev->disk)) {
+            goto invalid;
+        }
+
+        if (*end == 'p') {
+            p = (char *) ++end;
+            if (*end == '\0') {
+                goto invalid;
+            }
+        }
+    } else {
+        vdev->disk = vbd_name_to_disk(p, &end);
+    }
+
+    if (*end != '\0') {
+        p = (char *)end;
+
+        if (qemu_strtoul(p, &end, 10, &vdev->partition)) {
+            goto invalid;
+        }
+
+        if (*end != '\0') {
+            goto invalid;
+        }
+    } else {
+        vdev->partition = 0;
+    }
+
+    switch (vdev->type) {
+    case XEN_BLOCK_VDEV_TYPE_DP:
+    case XEN_BLOCK_VDEV_TYPE_XVD:
+        if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) {
+            vdev->number = (202 << 8) | (vdev->disk << 4) |
+                vdev->partition;
+        } else if (vdev->disk < (1 << 20) && vdev->partition < (1 << 8)) {
+            vdev->number = (1 << 28) | (vdev->disk << 8) |
+                vdev->partition;
+        } else {
+            goto invalid;
+        }
+        break;
+
+    case XEN_BLOCK_VDEV_TYPE_HD:
+        if ((vdev->disk == 0 || vdev->disk == 1) &&
+            vdev->partition < (1 << 6)) {
+            vdev->number = (3 << 8) | (vdev->disk << 6) | vdev->partition;
+        } else if ((vdev->disk == 2 || vdev->disk == 3) &&
+                   vdev->partition < (1 << 6)) {
+            vdev->number = (22 << 8) | ((vdev->disk - 2) << 6) |
+                vdev->partition;
+        } else {
+            goto invalid;
+        }
+        break;
+
+    case XEN_BLOCK_VDEV_TYPE_SD:
+        if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) {
+            vdev->number = (8 << 8) | (vdev->disk << 4) | vdev->partition;
+        } else {
+            goto invalid;
+        }
+        break;
+
+    default:
+        goto invalid;
+    }
+
+    g_free(str);
+    return;
+
+invalid:
+    error_setg(errp, "invalid virtual disk specifier");
+
+    vdev->type = XEN_BLOCK_VDEV_TYPE_INVALID;
+    g_free(str);
+}
+
+/*
+ * This property deals with 'vdev' names adhering to the Xen VBD naming
+ * scheme described in:
+ *
+ * https://xenbits.xen.org/docs/unstable/man/xen-vbd-interface.7.html
+ */
+const PropertyInfo xen_block_prop_vdev = {
+    .name  = "str",
+    .description = "Virtual Disk specifier: d*p*/xvd*/hd*/sd*",
+    .get = xen_block_get_vdev,
+    .set = xen_block_set_vdev,
+};
+
+static Property xen_block_props[] = {
+    DEFINE_PROP("vdev", XenBlockDevice, props.vdev,
+                xen_block_prop_vdev, XenBlockVdev),
+    DEFINE_BLOCK_PROPERTIES(XenBlockDevice, props.conf),
+    DEFINE_PROP_UINT32("max-ring-page-order", XenBlockDevice,
+                       props.max_ring_page_order, 4),
+    DEFINE_PROP_LINK("iothread", XenBlockDevice, props.iothread,
+                     TYPE_IOTHREAD, IOThread *),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void xen_block_class_init(ObjectClass *class, void *data)
+{
+    DeviceClass *dev_class = DEVICE_CLASS(class);
+    XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class);
+
+    xendev_class->backend = "qdisk";
+    xendev_class->device = "vbd";
+    xendev_class->get_name = xen_block_get_name;
+    xendev_class->realize = xen_block_realize;
+    xendev_class->frontend_changed = xen_block_frontend_changed;
+    xendev_class->unrealize = xen_block_unrealize;
+
+    dev_class->props = xen_block_props;
+}
+
+static const TypeInfo xen_block_type_info = {
+    .name = TYPE_XEN_BLOCK_DEVICE,
+    .parent = TYPE_XEN_DEVICE,
+    .instance_size = sizeof(XenBlockDevice),
+    .abstract = true,
+    .class_size = sizeof(XenBlockDeviceClass),
+    .class_init = xen_block_class_init,
+};
+
+static void xen_disk_unrealize(XenBlockDevice *blockdev, Error **errp)
+{
+    trace_xen_disk_unrealize();
+}
+
+static void xen_disk_realize(XenBlockDevice *blockdev, Error **errp)
+{
+    BlockConf *conf = &blockdev->props.conf;
+
+    trace_xen_disk_realize();
+
+    blockdev->device_type = "disk";
+
+    if (!conf->blk) {
+        error_setg(errp, "drive property not set");
+        return;
+    }
+
+    blockdev->info = blk_is_read_only(conf->blk) ? VDISK_READONLY : 0;
+}
+
+static void xen_disk_class_init(ObjectClass *class, void *data)
+{
+    DeviceClass *dev_class = DEVICE_CLASS(class);
+    XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class);
+
+    blockdev_class->realize = xen_disk_realize;
+    blockdev_class->unrealize = xen_disk_unrealize;
+
+    dev_class->desc = "Xen Disk Device";
+}
+
+static const TypeInfo xen_disk_type_info = {
+    .name = TYPE_XEN_DISK_DEVICE,
+    .parent = TYPE_XEN_BLOCK_DEVICE,
+    .instance_size = sizeof(XenDiskDevice),
+    .class_init = xen_disk_class_init,
+};
+
+static void xen_cdrom_unrealize(XenBlockDevice *blockdev, Error **errp)
+{
+    trace_xen_cdrom_unrealize();
+}
+
+static void xen_cdrom_realize(XenBlockDevice *blockdev, Error **errp)
+{
+    BlockConf *conf = &blockdev->props.conf;
+
+    trace_xen_cdrom_realize();
+
+    blockdev->device_type = "cdrom";
+
+    if (!conf->blk) {
+        int rc;
+
+        /* Set up an empty drive */
+        conf->blk = blk_new(0, BLK_PERM_ALL);
+
+        rc = blk_attach_dev(conf->blk, DEVICE(blockdev));
+        if (!rc) {
+            error_setg_errno(errp, -rc, "failed to create drive");
+            return;
+        }
+    }
+
+    blockdev->info = VDISK_READONLY | VDISK_CDROM;
+}
+
+static void xen_cdrom_class_init(ObjectClass *class, void *data)
+{
+    DeviceClass *dev_class = DEVICE_CLASS(class);
+    XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class);
+
+    blockdev_class->realize = xen_cdrom_realize;
+    blockdev_class->unrealize = xen_cdrom_unrealize;
+
+    dev_class->desc = "Xen CD-ROM Device";
+}
+
+static const TypeInfo xen_cdrom_type_info = {
+    .name = TYPE_XEN_CDROM_DEVICE,
+    .parent = TYPE_XEN_BLOCK_DEVICE,
+    .instance_size = sizeof(XenCDRomDevice),
+    .class_init = xen_cdrom_class_init,
+};
+
+static void xen_block_register_types(void)
+{
+    type_register_static(&xen_block_type_info);
+    type_register_static(&xen_disk_type_info);
+    type_register_static(&xen_cdrom_type_info);
+}
+
+type_init(xen_block_register_types)
+
+static void xen_block_blockdev_del(const char *node_name, Error **errp)
+{
+    trace_xen_block_blockdev_del(node_name);
+
+    qmp_blockdev_del(node_name, errp);
+}
+
+static char *xen_block_blockdev_add(const char *id, QDict *qdict,
+                                    Error **errp)
+{
+    const char *driver = qdict_get_try_str(qdict, "driver");
+    BlockdevOptions *options = NULL;
+    Error *local_err = NULL;
+    char *node_name;
+    Visitor *v;
+
+    if (!driver) {
+        error_setg(errp, "no 'driver' parameter");
+        return NULL;
+    }
+
+    node_name = g_strdup_printf("%s-%s", id, driver);
+    qdict_put_str(qdict, "node-name", node_name);
+
+    trace_xen_block_blockdev_add(node_name);
+
+    v = qobject_input_visitor_new(QOBJECT(qdict));
+    visit_type_BlockdevOptions(v, NULL, &options, &local_err);
+    visit_free(v);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto fail;
+    }
+
+    qmp_blockdev_add(options, &local_err);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto fail;
+    }
+
+    qapi_free_BlockdevOptions(options);
+
+    return node_name;
+
+fail:
+    if (options) {
+        qapi_free_BlockdevOptions(options);
+    }
+    g_free(node_name);
+
+    return NULL;
+}
+
+static void xen_block_drive_destroy(XenBlockDrive *drive, Error **errp)
+{
+    char *node_name = drive->node_name;
+
+    if (node_name) {
+        Error *local_err = NULL;
+
+        xen_block_blockdev_del(node_name, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            return;
+        }
+        g_free(node_name);
+        drive->node_name = NULL;
+    }
+    g_free(drive->id);
+    g_free(drive);
+}
+
+static XenBlockDrive *xen_block_drive_create(const char *id,
+                                             const char *device_type,
+                                             QDict *opts, Error **errp)
+{
+    const char *params = qdict_get_try_str(opts, "params");
+    const char *mode = qdict_get_try_str(opts, "mode");
+    const char *direct_io_safe = qdict_get_try_str(opts, "direct-io-safe");
+    const char *discard_enable = qdict_get_try_str(opts, "discard-enable");
+    char *driver = NULL;
+    char *filename = NULL;
+    XenBlockDrive *drive = NULL;
+    Error *local_err = NULL;
+    QDict *file_layer;
+    QDict *driver_layer;
+
+    if (params) {
+        char **v = g_strsplit(params, ":", 2);
+
+        if (v[1] == NULL) {
+            filename = g_strdup(v[0]);
+            driver = g_strdup("raw");
+        } else {
+            if (strcmp(v[0], "aio") == 0) {
+                driver = g_strdup("raw");
+            } else if (strcmp(v[0], "vhd") == 0) {
+                driver = g_strdup("vpc");
+            } else {
+                driver = g_strdup(v[0]);
+            }
+            filename = g_strdup(v[1]);
+        }
+
+        g_strfreev(v);
+    }
+
+    if (!filename) {
+        error_setg(errp, "no filename");
+        goto done;
+    }
+    assert(driver);
+
+    drive = g_new0(XenBlockDrive, 1);
+    drive->id = g_strdup(id);
+
+    file_layer = qdict_new();
+
+    qdict_put_str(file_layer, "driver", "file");
+    qdict_put_str(file_layer, "filename", filename);
+
+    if (mode && *mode != 'w') {
+        qdict_put_bool(file_layer, "read-only", true);
+    }
+
+    if (direct_io_safe) {
+        unsigned long value;
+
+        if (!qemu_strtoul(direct_io_safe, NULL, 2, &value) && !!value) {
+            QDict *cache_qdict = qdict_new();
+
+            qdict_put_bool(cache_qdict, "direct", true);
+            qdict_put_obj(file_layer, "cache", QOBJECT(cache_qdict));
+
+            qdict_put_str(file_layer, "aio", "native");
+        }
+    }
+
+    if (discard_enable) {
+        unsigned long value;
+
+        if (!qemu_strtoul(discard_enable, NULL, 2, &value) && !!value) {
+            qdict_put_str(file_layer, "discard", "unmap");
+        }
+    }
+
+    /*
+     * It is necessary to turn file locking off as an emulated device
+     * may have already opened the same image file.
+     */
+    qdict_put_str(file_layer, "locking", "off");
+
+    driver_layer = qdict_new();
+
+    qdict_put_str(driver_layer, "driver", driver);
+    qdict_put_obj(driver_layer, "file", QOBJECT(file_layer));
+
+    g_assert(!drive->node_name);
+    drive->node_name = xen_block_blockdev_add(drive->id, driver_layer,
+                                              &local_err);
+
+done:
+    g_free(driver);
+    g_free(filename);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        xen_block_drive_destroy(drive, NULL);
+        return NULL;
+    }
+
+    return drive;
+}
+
+static const char *xen_block_drive_get_node_name(XenBlockDrive *drive)
+{
+    return drive->node_name ? drive->node_name : "";
+}
+
+static void xen_block_iothread_destroy(XenBlockIOThread *iothread,
+                                       Error **errp)
+{
+    qmp_object_del(iothread->id, errp);
+
+    g_free(iothread->id);
+    g_free(iothread);
+}
+
+static XenBlockIOThread *xen_block_iothread_create(const char *id,
+                                                   Error **errp)
+{
+    XenBlockIOThread *iothread = g_new(XenBlockIOThread, 1);
+    Error *local_err = NULL;
+
+    iothread->id = g_strdup(id);
+
+    qmp_object_add(TYPE_IOTHREAD, id, false, NULL, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+
+        g_free(iothread->id);
+        g_free(iothread);
+        return NULL;
+    }
+
+    return iothread;
+}
+
+static void xen_block_device_create(XenBackendInstance *backend,
+                                    QDict *opts, Error **errp)
+{
+    XenBus *xenbus = xen_backend_get_bus(backend);
+    const char *name = xen_backend_get_name(backend);
+    unsigned long number;
+    const char *vdev, *device_type;
+    XenBlockDrive *drive = NULL;
+    XenBlockIOThread *iothread = NULL;
+    XenDevice *xendev = NULL;
+    Error *local_err = NULL;
+    const char *type;
+    XenBlockDevice *blockdev;
+
+    if (qemu_strtoul(name, NULL, 10, &number)) {
+        error_setg(errp, "failed to parse name '%s'", name);
+        goto fail;
+    }
+
+    trace_xen_block_device_create(number);
+
+    vdev = qdict_get_try_str(opts, "dev");
+    if (!vdev) {
+        error_setg(errp, "no dev parameter");
+        goto fail;
+    }
+
+    device_type = qdict_get_try_str(opts, "device-type");
+    if (!device_type) {
+        error_setg(errp, "no device-type parameter");
+        goto fail;
+    }
+
+    if (!strcmp(device_type, "disk")) {
+        type = TYPE_XEN_DISK_DEVICE;
+    } else if (!strcmp(device_type, "cdrom")) {
+        type = TYPE_XEN_CDROM_DEVICE;
+    } else {
+        error_setg(errp, "invalid device-type parameter '%s'", device_type);
+        goto fail;
+    }
+
+    drive = xen_block_drive_create(vdev, device_type, opts, &local_err);
+    if (!drive) {
+        error_propagate_prepend(errp, local_err, "failed to create drive: ");
+        goto fail;
+    }
+
+    iothread = xen_block_iothread_create(vdev, &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to create iothread: ");
+        goto fail;
+    }
+
+    xendev = XEN_DEVICE(qdev_create(BUS(xenbus), type));
+    blockdev = XEN_BLOCK_DEVICE(xendev);
+
+    object_property_set_str(OBJECT(xendev), vdev, "vdev", &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err, "failed to set 'vdev': ");
+        goto fail;
+    }
+
+    object_property_set_str(OBJECT(xendev),
+                            xen_block_drive_get_node_name(drive), "drive",
+                            &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err, "failed to set 'drive': ");
+        goto fail;
+    }
+
+    object_property_set_str(OBJECT(xendev), iothread->id, "iothread",
+                            &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to set 'iothread': ");
+        goto fail;
+    }
+
+    blockdev->iothread = iothread;
+    blockdev->drive = drive;
+
+    object_property_set_bool(OBJECT(xendev), true, "realized", &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err,
+                                "realization of device %s failed: ",
+                                type);
+        goto fail;
+    }
+
+    xen_backend_set_device(backend, xendev);
+    return;
+
+fail:
+    if (xendev) {
+        object_unparent(OBJECT(xendev));
+    }
+
+    if (iothread) {
+        xen_block_iothread_destroy(iothread, NULL);
+    }
+
+    if (drive) {
+        xen_block_drive_destroy(drive, NULL);
+    }
+}
+
+static void xen_block_device_destroy(XenBackendInstance *backend,
+                                     Error **errp)
+{
+    XenDevice *xendev = xen_backend_get_device(backend);
+    XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
+    XenBlockVdev *vdev = &blockdev->props.vdev;
+    XenBlockDrive *drive = blockdev->drive;
+    XenBlockIOThread *iothread = blockdev->iothread;
+
+    trace_xen_block_device_destroy(vdev->number);
+
+    object_unparent(OBJECT(xendev));
+
+    if (iothread) {
+        Error *local_err = NULL;
+
+        xen_block_iothread_destroy(iothread, &local_err);
+        if (local_err) {
+            error_propagate_prepend(errp, local_err,
+                                "failed to destroy iothread: ");
+            return;
+        }
+    }
+
+    if (drive) {
+        Error *local_err = NULL;
+
+        xen_block_drive_destroy(drive, &local_err);
+        if (local_err) {
+            error_propagate_prepend(errp, local_err,
+                                "failed to destroy drive: ");
+        }
+    }
+}
+
+static const XenBackendInfo xen_block_backend_info = {
+    .type = "qdisk",
+    .create = xen_block_device_create,
+    .destroy = xen_block_device_destroy,
+};
+
+static void xen_block_register_backend(void)
+{
+    xen_backend_register(&xen_block_backend_info);
+}
+
+xen_backend_init(xen_block_register_backend);
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
deleted file mode 100644
index 2a254b99d0..0000000000
--- a/hw/block/xen_disk.c
+++ /dev/null
@@ -1,1011 +0,0 @@
-/*
- *  xen paravirt block device backend
- *
- *  (c) Gerd Hoffmann <kraxel@redhat.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; under version 2 of the License.
- *
- *  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/>.
- *
- *  Contributions after 2012-01-13 are licensed under the terms of the
- *  GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "qemu/osdep.h"
-#include "qemu/units.h"
-#include <sys/ioctl.h>
-#include <sys/uio.h>
-
-#include "hw/hw.h"
-#include "hw/xen/xen_backend.h"
-#include "xen_blkif.h"
-#include "sysemu/blockdev.h"
-#include "sysemu/iothread.h"
-#include "sysemu/block-backend.h"
-#include "qapi/error.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qstring.h"
-#include "trace.h"
-
-/* ------------------------------------------------------------- */
-
-#define BLOCK_SIZE  512
-#define IOCB_COUNT  (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2)
-
-struct ioreq {
-    blkif_request_t     req;
-    int16_t             status;
-
-    /* parsed request */
-    off_t               start;
-    QEMUIOVector        v;
-    void                *buf;
-    size_t              size;
-    int                 presync;
-
-    /* aio status */
-    int                 aio_inflight;
-    int                 aio_errors;
-
-    struct XenBlkDev    *blkdev;
-    QLIST_ENTRY(ioreq)   list;
-    BlockAcctCookie     acct;
-};
-
-#define MAX_RING_PAGE_ORDER 4
-
-struct XenBlkDev {
-    struct XenDevice    xendev;  /* must be first */
-    char                *params;
-    char                *mode;
-    char                *type;
-    char                *dev;
-    char                *devtype;
-    bool                directiosafe;
-    const char          *fileproto;
-    const char          *filename;
-    unsigned int        ring_ref[1 << MAX_RING_PAGE_ORDER];
-    unsigned int        nr_ring_ref;
-    void                *sring;
-    int64_t             file_blk;
-    int64_t             file_size;
-    int                 protocol;
-    blkif_back_rings_t  rings;
-    int                 more_work;
-
-    /* request lists */
-    QLIST_HEAD(, ioreq) inflight;
-    QLIST_HEAD(, ioreq) finished;
-    QLIST_HEAD(, ioreq) freelist;
-    int                 requests_total;
-    int                 requests_inflight;
-    int                 requests_finished;
-    unsigned int        max_requests;
-
-    gboolean            feature_discard;
-
-    /* qemu block driver */
-    DriveInfo           *dinfo;
-    BlockBackend        *blk;
-    QEMUBH              *bh;
-
-    IOThread            *iothread;
-    AioContext          *ctx;
-};
-
-/* ------------------------------------------------------------- */
-
-static void ioreq_reset(struct ioreq *ioreq)
-{
-    memset(&ioreq->req, 0, sizeof(ioreq->req));
-    ioreq->status = 0;
-    ioreq->start = 0;
-    ioreq->buf = NULL;
-    ioreq->size = 0;
-    ioreq->presync = 0;
-
-    ioreq->aio_inflight = 0;
-    ioreq->aio_errors = 0;
-
-    ioreq->blkdev = NULL;
-    memset(&ioreq->list, 0, sizeof(ioreq->list));
-    memset(&ioreq->acct, 0, sizeof(ioreq->acct));
-
-    qemu_iovec_reset(&ioreq->v);
-}
-
-static struct ioreq *ioreq_start(struct XenBlkDev *blkdev)
-{
-    struct ioreq *ioreq = NULL;
-
-    if (QLIST_EMPTY(&blkdev->freelist)) {
-        if (blkdev->requests_total >= blkdev->max_requests) {
-            goto out;
-        }
-        /* allocate new struct */
-        ioreq = g_malloc0(sizeof(*ioreq));
-        ioreq->blkdev = blkdev;
-        blkdev->requests_total++;
-        qemu_iovec_init(&ioreq->v, 1);
-    } else {
-        /* get one from freelist */
-        ioreq = QLIST_FIRST(&blkdev->freelist);
-        QLIST_REMOVE(ioreq, list);
-    }
-    QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list);
-    blkdev->requests_inflight++;
-
-out:
-    return ioreq;
-}
-
-static void ioreq_finish(struct ioreq *ioreq)
-{
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-
-    QLIST_REMOVE(ioreq, list);
-    QLIST_INSERT_HEAD(&blkdev->finished, ioreq, list);
-    blkdev->requests_inflight--;
-    blkdev->requests_finished++;
-}
-
-static void ioreq_release(struct ioreq *ioreq, bool finish)
-{
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-
-    QLIST_REMOVE(ioreq, list);
-    ioreq_reset(ioreq);
-    ioreq->blkdev = blkdev;
-    QLIST_INSERT_HEAD(&blkdev->freelist, ioreq, list);
-    if (finish) {
-        blkdev->requests_finished--;
-    } else {
-        blkdev->requests_inflight--;
-    }
-}
-
-/*
- * translate request into iovec + start offset
- * do sanity checks along the way
- */
-static int ioreq_parse(struct ioreq *ioreq)
-{
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-    struct XenDevice *xendev = &blkdev->xendev;
-    size_t len;
-    int i;
-
-    xen_pv_printf(xendev, 3,
-                  "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n",
-                  ioreq->req.operation, ioreq->req.nr_segments,
-                  ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number);
-    switch (ioreq->req.operation) {
-    case BLKIF_OP_READ:
-        break;
-    case BLKIF_OP_FLUSH_DISKCACHE:
-        ioreq->presync = 1;
-        if (!ioreq->req.nr_segments) {
-            return 0;
-        }
-        /* fall through */
-    case BLKIF_OP_WRITE:
-        break;
-    case BLKIF_OP_DISCARD:
-        return 0;
-    default:
-        xen_pv_printf(xendev, 0, "error: unknown operation (%d)\n",
-                      ioreq->req.operation);
-        goto err;
-    };
-
-    if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') {
-        xen_pv_printf(xendev, 0, "error: write req for ro device\n");
-        goto err;
-    }
-
-    ioreq->start = ioreq->req.sector_number * blkdev->file_blk;
-    for (i = 0; i < ioreq->req.nr_segments; i++) {
-        if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) {
-            xen_pv_printf(xendev, 0, "error: nr_segments too big\n");
-            goto err;
-        }
-        if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) {
-            xen_pv_printf(xendev, 0, "error: first > last sector\n");
-            goto err;
-        }
-        if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) {
-            xen_pv_printf(xendev, 0, "error: page crossing\n");
-            goto err;
-        }
-
-        len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk;
-        ioreq->size += len;
-    }
-    if (ioreq->start + ioreq->size > blkdev->file_size) {
-        xen_pv_printf(xendev, 0, "error: access beyond end of file\n");
-        goto err;
-    }
-    return 0;
-
-err:
-    ioreq->status = BLKIF_RSP_ERROR;
-    return -1;
-}
-
-static int ioreq_grant_copy(struct ioreq *ioreq)
-{
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-    struct XenDevice *xendev = &blkdev->xendev;
-    XenGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST];
-    int i, count, rc;
-    int64_t file_blk = blkdev->file_blk;
-    bool to_domain = (ioreq->req.operation == BLKIF_OP_READ);
-    void *virt = ioreq->buf;
-
-    if (ioreq->req.nr_segments == 0) {
-        return 0;
-    }
-
-    count = ioreq->req.nr_segments;
-
-    for (i = 0; i < count; i++) {
-        if (to_domain) {
-            segs[i].dest.foreign.ref = ioreq->req.seg[i].gref;
-            segs[i].dest.foreign.offset = ioreq->req.seg[i].first_sect * file_blk;
-            segs[i].source.virt = virt;
-        } else {
-            segs[i].source.foreign.ref = ioreq->req.seg[i].gref;
-            segs[i].source.foreign.offset = ioreq->req.seg[i].first_sect * file_blk;
-            segs[i].dest.virt = virt;
-        }
-        segs[i].len = (ioreq->req.seg[i].last_sect
-                       - ioreq->req.seg[i].first_sect + 1) * file_blk;
-        virt += segs[i].len;
-    }
-
-    rc = xen_be_copy_grant_refs(xendev, to_domain, segs, count);
-
-    if (rc) {
-        xen_pv_printf(xendev, 0,
-                      "failed to copy data %d\n", rc);
-        ioreq->aio_errors++;
-        return -1;
-    }
-
-    return rc;
-}
-
-static int ioreq_runio_qemu_aio(struct ioreq *ioreq);
-
-static void qemu_aio_complete(void *opaque, int ret)
-{
-    struct ioreq *ioreq = opaque;
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-    struct XenDevice *xendev = &blkdev->xendev;
-
-    aio_context_acquire(blkdev->ctx);
-
-    if (ret != 0) {
-        xen_pv_printf(xendev, 0, "%s I/O error\n",
-                      ioreq->req.operation == BLKIF_OP_READ ? "read" : "write");
-        ioreq->aio_errors++;
-    }
-
-    ioreq->aio_inflight--;
-    if (ioreq->presync) {
-        ioreq->presync = 0;
-        ioreq_runio_qemu_aio(ioreq);
-        goto done;
-    }
-    if (ioreq->aio_inflight > 0) {
-        goto done;
-    }
-
-    switch (ioreq->req.operation) {
-    case BLKIF_OP_READ:
-        /* in case of failure ioreq->aio_errors is increased */
-        if (ret == 0) {
-            ioreq_grant_copy(ioreq);
-        }
-        qemu_vfree(ioreq->buf);
-        break;
-    case BLKIF_OP_WRITE:
-    case BLKIF_OP_FLUSH_DISKCACHE:
-        if (!ioreq->req.nr_segments) {
-            break;
-        }
-        qemu_vfree(ioreq->buf);
-        break;
-    default:
-        break;
-    }
-
-    ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
-    ioreq_finish(ioreq);
-
-    switch (ioreq->req.operation) {
-    case BLKIF_OP_WRITE:
-    case BLKIF_OP_FLUSH_DISKCACHE:
-        if (!ioreq->req.nr_segments) {
-            break;
-        }
-    case BLKIF_OP_READ:
-        if (ioreq->status == BLKIF_RSP_OKAY) {
-            block_acct_done(blk_get_stats(blkdev->blk), &ioreq->acct);
-        } else {
-            block_acct_failed(blk_get_stats(blkdev->blk), &ioreq->acct);
-        }
-        break;
-    case BLKIF_OP_DISCARD:
-    default:
-        break;
-    }
-    qemu_bh_schedule(blkdev->bh);
-
-done:
-    aio_context_release(blkdev->ctx);
-}
-
-static bool blk_split_discard(struct ioreq *ioreq, blkif_sector_t sector_number,
-                              uint64_t nr_sectors)
-{
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-    int64_t byte_offset;
-    int byte_chunk;
-    uint64_t byte_remaining, limit;
-    uint64_t sec_start = sector_number;
-    uint64_t sec_count = nr_sectors;
-
-    /* Wrap around, or overflowing byte limit? */
-    if (sec_start + sec_count < sec_count ||
-        sec_start + sec_count > INT64_MAX >> BDRV_SECTOR_BITS) {
-        return false;
-    }
-
-    limit = BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS;
-    byte_offset = sec_start << BDRV_SECTOR_BITS;
-    byte_remaining = sec_count << BDRV_SECTOR_BITS;
-
-    do {
-        byte_chunk = byte_remaining > limit ? limit : byte_remaining;
-        ioreq->aio_inflight++;
-        blk_aio_pdiscard(blkdev->blk, byte_offset, byte_chunk,
-                         qemu_aio_complete, ioreq);
-        byte_remaining -= byte_chunk;
-        byte_offset += byte_chunk;
-    } while (byte_remaining > 0);
-
-    return true;
-}
-
-static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
-{
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-
-    ioreq->buf = qemu_memalign(XC_PAGE_SIZE, ioreq->size);
-    if (ioreq->req.nr_segments &&
-        (ioreq->req.operation == BLKIF_OP_WRITE ||
-         ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) &&
-        ioreq_grant_copy(ioreq)) {
-        qemu_vfree(ioreq->buf);
-        goto err;
-    }
-
-    ioreq->aio_inflight++;
-    if (ioreq->presync) {
-        blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq);
-        return 0;
-    }
-
-    switch (ioreq->req.operation) {
-    case BLKIF_OP_READ:
-        qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size);
-        block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
-                         ioreq->v.size, BLOCK_ACCT_READ);
-        ioreq->aio_inflight++;
-        blk_aio_preadv(blkdev->blk, ioreq->start, &ioreq->v, 0,
-                       qemu_aio_complete, ioreq);
-        break;
-    case BLKIF_OP_WRITE:
-    case BLKIF_OP_FLUSH_DISKCACHE:
-        if (!ioreq->req.nr_segments) {
-            break;
-        }
-
-        qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size);
-        block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
-                         ioreq->v.size,
-                         ioreq->req.operation == BLKIF_OP_WRITE ?
-                         BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH);
-        ioreq->aio_inflight++;
-        blk_aio_pwritev(blkdev->blk, ioreq->start, &ioreq->v, 0,
-                        qemu_aio_complete, ioreq);
-        break;
-    case BLKIF_OP_DISCARD:
-    {
-        struct blkif_request_discard *req = (void *)&ioreq->req;
-        if (!blk_split_discard(ioreq, req->sector_number, req->nr_sectors)) {
-            goto err;
-        }
-        break;
-    }
-    default:
-        /* unknown operation (shouldn't happen -- parse catches this) */
-        goto err;
-    }
-
-    qemu_aio_complete(ioreq, 0);
-
-    return 0;
-
-err:
-    ioreq_finish(ioreq);
-    ioreq->status = BLKIF_RSP_ERROR;
-    return -1;
-}
-
-static int blk_send_response_one(struct ioreq *ioreq)
-{
-    struct XenBlkDev  *blkdev = ioreq->blkdev;
-    int               send_notify   = 0;
-    int               have_requests = 0;
-    blkif_response_t  *resp;
-
-    /* Place on the response ring for the relevant domain. */
-    switch (blkdev->protocol) {
-    case BLKIF_PROTOCOL_NATIVE:
-        resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.native,
-                                 blkdev->rings.native.rsp_prod_pvt);
-        break;
-    case BLKIF_PROTOCOL_X86_32:
-        resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.x86_32_part,
-                                 blkdev->rings.x86_32_part.rsp_prod_pvt);
-        break;
-    case BLKIF_PROTOCOL_X86_64:
-        resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.x86_64_part,
-                                 blkdev->rings.x86_64_part.rsp_prod_pvt);
-        break;
-    default:
-        return 0;
-    }
-
-    resp->id        = ioreq->req.id;
-    resp->operation = ioreq->req.operation;
-    resp->status    = ioreq->status;
-
-    blkdev->rings.common.rsp_prod_pvt++;
-
-    RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify);
-    if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) {
-        /*
-         * Tail check for pending requests. Allows frontend to avoid
-         * notifications if requests are already in flight (lower
-         * overheads and promotes batching).
-         */
-        RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests);
-    } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) {
-        have_requests = 1;
-    }
-
-    if (have_requests) {
-        blkdev->more_work++;
-    }
-    return send_notify;
-}
-
-/* walk finished list, send outstanding responses, free requests */
-static void blk_send_response_all(struct XenBlkDev *blkdev)
-{
-    struct ioreq *ioreq;
-    int send_notify = 0;
-
-    while (!QLIST_EMPTY(&blkdev->finished)) {
-        ioreq = QLIST_FIRST(&blkdev->finished);
-        send_notify += blk_send_response_one(ioreq);
-        ioreq_release(ioreq, true);
-    }
-    if (send_notify) {
-        xen_pv_send_notify(&blkdev->xendev);
-    }
-}
-
-static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc)
-{
-    switch (blkdev->protocol) {
-    case BLKIF_PROTOCOL_NATIVE:
-        memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc),
-               sizeof(ioreq->req));
-        break;
-    case BLKIF_PROTOCOL_X86_32:
-        blkif_get_x86_32_req(&ioreq->req,
-                             RING_GET_REQUEST(&blkdev->rings.x86_32_part, rc));
-        break;
-    case BLKIF_PROTOCOL_X86_64:
-        blkif_get_x86_64_req(&ioreq->req,
-                             RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc));
-        break;
-    }
-    /* Prevent the compiler from accessing the on-ring fields instead. */
-    barrier();
-    return 0;
-}
-
-static void blk_handle_requests(struct XenBlkDev *blkdev)
-{
-    RING_IDX rc, rp;
-    struct ioreq *ioreq;
-
-    blkdev->more_work = 0;
-
-    rc = blkdev->rings.common.req_cons;
-    rp = blkdev->rings.common.sring->req_prod;
-    xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
-
-    blk_send_response_all(blkdev);
-    while (rc != rp) {
-        /* pull request from ring */
-        if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) {
-            break;
-        }
-        ioreq = ioreq_start(blkdev);
-        if (ioreq == NULL) {
-            blkdev->more_work++;
-            break;
-        }
-        blk_get_request(blkdev, ioreq, rc);
-        blkdev->rings.common.req_cons = ++rc;
-
-        /* parse them */
-        if (ioreq_parse(ioreq) != 0) {
-
-            switch (ioreq->req.operation) {
-            case BLKIF_OP_READ:
-                block_acct_invalid(blk_get_stats(blkdev->blk),
-                                   BLOCK_ACCT_READ);
-                break;
-            case BLKIF_OP_WRITE:
-                block_acct_invalid(blk_get_stats(blkdev->blk),
-                                   BLOCK_ACCT_WRITE);
-                break;
-            case BLKIF_OP_FLUSH_DISKCACHE:
-                block_acct_invalid(blk_get_stats(blkdev->blk),
-                                   BLOCK_ACCT_FLUSH);
-            default:
-                break;
-            };
-
-            if (blk_send_response_one(ioreq)) {
-                xen_pv_send_notify(&blkdev->xendev);
-            }
-            ioreq_release(ioreq, false);
-            continue;
-        }
-
-        ioreq_runio_qemu_aio(ioreq);
-    }
-
-    if (blkdev->more_work && blkdev->requests_inflight < blkdev->max_requests) {
-        qemu_bh_schedule(blkdev->bh);
-    }
-}
-
-/* ------------------------------------------------------------- */
-
-static void blk_bh(void *opaque)
-{
-    struct XenBlkDev *blkdev = opaque;
-
-    aio_context_acquire(blkdev->ctx);
-    blk_handle_requests(blkdev);
-    aio_context_release(blkdev->ctx);
-}
-
-static void blk_alloc(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-    Error *err = NULL;
-
-    trace_xen_disk_alloc(xendev->name);
-
-    QLIST_INIT(&blkdev->inflight);
-    QLIST_INIT(&blkdev->finished);
-    QLIST_INIT(&blkdev->freelist);
-
-    blkdev->iothread = iothread_create(xendev->name, &err);
-    assert(!err);
-
-    blkdev->ctx = iothread_get_aio_context(blkdev->iothread);
-    blkdev->bh = aio_bh_new(blkdev->ctx, blk_bh, blkdev);
-}
-
-static void blk_parse_discard(struct XenBlkDev *blkdev)
-{
-    struct XenDevice *xendev = &blkdev->xendev;
-    int enable;
-
-    blkdev->feature_discard = true;
-
-    if (xenstore_read_be_int(xendev, "discard-enable", &enable) == 0) {
-        blkdev->feature_discard = !!enable;
-    }
-
-    if (blkdev->feature_discard) {
-        xenstore_write_be_int(xendev, "feature-discard", 1);
-    }
-}
-
-static int blk_init(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-    int info = 0;
-    char *directiosafe = NULL;
-
-    trace_xen_disk_init(xendev->name);
-
-    /* read xenstore entries */
-    if (blkdev->params == NULL) {
-        char *h = NULL;
-        blkdev->params = xenstore_read_be_str(xendev, "params");
-        if (blkdev->params != NULL) {
-            h = strchr(blkdev->params, ':');
-        }
-        if (h != NULL) {
-            blkdev->fileproto = blkdev->params;
-            blkdev->filename  = h+1;
-            *h = 0;
-        } else {
-            blkdev->fileproto = "<unset>";
-            blkdev->filename  = blkdev->params;
-        }
-    }
-    if (!strcmp("aio", blkdev->fileproto)) {
-        blkdev->fileproto = "raw";
-    }
-    if (!strcmp("vhd", blkdev->fileproto)) {
-        blkdev->fileproto = "vpc";
-    }
-    if (blkdev->mode == NULL) {
-        blkdev->mode = xenstore_read_be_str(xendev, "mode");
-    }
-    if (blkdev->type == NULL) {
-        blkdev->type = xenstore_read_be_str(xendev, "type");
-    }
-    if (blkdev->dev == NULL) {
-        blkdev->dev = xenstore_read_be_str(xendev, "dev");
-    }
-    if (blkdev->devtype == NULL) {
-        blkdev->devtype = xenstore_read_be_str(xendev, "device-type");
-    }
-    directiosafe = xenstore_read_be_str(xendev, "direct-io-safe");
-    blkdev->directiosafe = (directiosafe && atoi(directiosafe));
-
-    /* do we have all we need? */
-    if (blkdev->params == NULL ||
-        blkdev->mode == NULL   ||
-        blkdev->type == NULL   ||
-        blkdev->dev == NULL) {
-        goto out_error;
-    }
-
-    /* read-only ? */
-    if (strcmp(blkdev->mode, "w")) {
-        info  |= VDISK_READONLY;
-    }
-
-    /* cdrom ? */
-    if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) {
-        info  |= VDISK_CDROM;
-    }
-
-    blkdev->file_blk  = BLOCK_SIZE;
-
-    /* fill info
-     * blk_connect supplies sector-size and sectors
-     */
-    xenstore_write_be_int(xendev, "feature-flush-cache", 1);
-    xenstore_write_be_int(xendev, "info", info);
-
-    xenstore_write_be_int(xendev, "max-ring-page-order",
-                          MAX_RING_PAGE_ORDER);
-
-    blk_parse_discard(blkdev);
-
-    g_free(directiosafe);
-    return 0;
-
-out_error:
-    g_free(blkdev->params);
-    blkdev->params = NULL;
-    g_free(blkdev->mode);
-    blkdev->mode = NULL;
-    g_free(blkdev->type);
-    blkdev->type = NULL;
-    g_free(blkdev->dev);
-    blkdev->dev = NULL;
-    g_free(blkdev->devtype);
-    blkdev->devtype = NULL;
-    g_free(directiosafe);
-    blkdev->directiosafe = false;
-    return -1;
-}
-
-static int blk_connect(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-    int index, qflags;
-    bool readonly = true;
-    bool writethrough = true;
-    int order, ring_ref;
-    unsigned int ring_size, max_grants;
-    unsigned int i;
-
-    trace_xen_disk_connect(xendev->name);
-
-    /* read-only ? */
-    if (blkdev->directiosafe) {
-        qflags = BDRV_O_NOCACHE | BDRV_O_NATIVE_AIO;
-    } else {
-        qflags = 0;
-        writethrough = false;
-    }
-    if (strcmp(blkdev->mode, "w") == 0) {
-        qflags |= BDRV_O_RDWR;
-        readonly = false;
-    }
-    if (blkdev->feature_discard) {
-        qflags |= BDRV_O_UNMAP;
-    }
-
-    /* init qemu block driver */
-    index = (xendev->dev - 202 * 256) / 16;
-    blkdev->dinfo = drive_get(IF_XEN, 0, index);
-    if (!blkdev->dinfo) {
-        Error *local_err = NULL;
-        QDict *options = NULL;
-
-        if (strcmp(blkdev->fileproto, "<unset>")) {
-            options = qdict_new();
-            qdict_put_str(options, "driver", blkdev->fileproto);
-        }
-
-        /* setup via xenbus -> create new block driver instance */
-        xen_pv_printf(xendev, 2, "create new bdrv (xenbus setup)\n");
-        blkdev->blk = blk_new_open(blkdev->filename, NULL, options,
-                                   qflags, &local_err);
-        if (!blkdev->blk) {
-            xen_pv_printf(xendev, 0, "error: %s\n",
-                          error_get_pretty(local_err));
-            error_free(local_err);
-            return -1;
-        }
-        blk_set_enable_write_cache(blkdev->blk, !writethrough);
-    } else {
-        /* setup via qemu cmdline -> already setup for us */
-        xen_pv_printf(xendev, 2,
-                      "get configured bdrv (cmdline setup)\n");
-        blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo);
-        if (blk_is_read_only(blkdev->blk) && !readonly) {
-            xen_pv_printf(xendev, 0, "Unexpected read-only drive");
-            blkdev->blk = NULL;
-            return -1;
-        }
-        /* blkdev->blk is not create by us, we get a reference
-         * so we can blk_unref() unconditionally */
-        blk_ref(blkdev->blk);
-    }
-    blk_attach_dev_legacy(blkdev->blk, blkdev);
-    blkdev->file_size = blk_getlength(blkdev->blk);
-    if (blkdev->file_size < 0) {
-        BlockDriverState *bs = blk_bs(blkdev->blk);
-        const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL;
-        xen_pv_printf(xendev, 1, "blk_getlength: %d (%s) | drv %s\n",
-                      (int)blkdev->file_size, strerror(-blkdev->file_size),
-                      drv_name ?: "-");
-        blkdev->file_size = 0;
-    }
-
-    xen_pv_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\","
-                  " size %" PRId64 " (%" PRId64 " MB)\n",
-                  blkdev->type, blkdev->fileproto, blkdev->filename,
-                  blkdev->file_size, blkdev->file_size / MiB);
-
-    /* Fill in number of sector size and number of sectors */
-    xenstore_write_be_int(xendev, "sector-size", blkdev->file_blk);
-    xenstore_write_be_int64(xendev, "sectors",
-                            blkdev->file_size / blkdev->file_blk);
-
-    if (xenstore_read_fe_int(xendev, "ring-page-order",
-                             &order) == -1) {
-        blkdev->nr_ring_ref = 1;
-
-        if (xenstore_read_fe_int(xendev, "ring-ref",
-                                 &ring_ref) == -1) {
-            return -1;
-        }
-        blkdev->ring_ref[0] = ring_ref;
-
-    } else if (order >= 0 && order <= MAX_RING_PAGE_ORDER) {
-        blkdev->nr_ring_ref = 1 << order;
-
-        for (i = 0; i < blkdev->nr_ring_ref; i++) {
-            char *key;
-
-            key = g_strdup_printf("ring-ref%u", i);
-            if (!key) {
-                return -1;
-            }
-
-            if (xenstore_read_fe_int(xendev, key,
-                                     &ring_ref) == -1) {
-                g_free(key);
-                return -1;
-            }
-            blkdev->ring_ref[i] = ring_ref;
-
-            g_free(key);
-        }
-    } else {
-        xen_pv_printf(xendev, 0, "invalid ring-page-order: %d\n",
-                      order);
-        return -1;
-    }
-
-    if (xenstore_read_fe_int(xendev, "event-channel",
-                             &xendev->remote_port) == -1) {
-        return -1;
-    }
-
-    if (!xendev->protocol) {
-        blkdev->protocol = BLKIF_PROTOCOL_NATIVE;
-    } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) {
-        blkdev->protocol = BLKIF_PROTOCOL_NATIVE;
-    } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
-        blkdev->protocol = BLKIF_PROTOCOL_X86_32;
-    } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
-        blkdev->protocol = BLKIF_PROTOCOL_X86_64;
-    } else {
-        blkdev->protocol = BLKIF_PROTOCOL_NATIVE;
-    }
-
-    ring_size = XC_PAGE_SIZE * blkdev->nr_ring_ref;
-    switch (blkdev->protocol) {
-    case BLKIF_PROTOCOL_NATIVE:
-    {
-        blkdev->max_requests = __CONST_RING_SIZE(blkif, ring_size);
-        break;
-    }
-    case BLKIF_PROTOCOL_X86_32:
-    {
-        blkdev->max_requests = __CONST_RING_SIZE(blkif_x86_32, ring_size);
-        break;
-    }
-    case BLKIF_PROTOCOL_X86_64:
-    {
-        blkdev->max_requests = __CONST_RING_SIZE(blkif_x86_64, ring_size);
-        break;
-    }
-    default:
-        return -1;
-    }
-
-    /* Add on the number needed for the ring pages */
-    max_grants = blkdev->nr_ring_ref;
-
-    xen_be_set_max_grant_refs(xendev, max_grants);
-    blkdev->sring = xen_be_map_grant_refs(xendev, blkdev->ring_ref,
-                                          blkdev->nr_ring_ref,
-                                          PROT_READ | PROT_WRITE);
-    if (!blkdev->sring) {
-        return -1;
-    }
-
-    switch (blkdev->protocol) {
-    case BLKIF_PROTOCOL_NATIVE:
-    {
-        blkif_sring_t *sring_native = blkdev->sring;
-        BACK_RING_INIT(&blkdev->rings.native, sring_native, ring_size);
-        break;
-    }
-    case BLKIF_PROTOCOL_X86_32:
-    {
-        blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring;
-
-        BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, ring_size);
-        break;
-    }
-    case BLKIF_PROTOCOL_X86_64:
-    {
-        blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring;
-
-        BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, ring_size);
-        break;
-    }
-    }
-
-    blk_set_aio_context(blkdev->blk, blkdev->ctx);
-
-    xen_be_bind_evtchn(xendev);
-
-    xen_pv_printf(xendev, 1, "ok: proto %s, nr-ring-ref %u, "
-                  "remote port %d, local port %d\n",
-                  xendev->protocol, blkdev->nr_ring_ref,
-                  xendev->remote_port, xendev->local_port);
-    return 0;
-}
-
-static void blk_disconnect(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-
-    trace_xen_disk_disconnect(xendev->name);
-
-    aio_context_acquire(blkdev->ctx);
-
-    if (blkdev->blk) {
-        blk_set_aio_context(blkdev->blk, qemu_get_aio_context());
-        blk_detach_dev(blkdev->blk, blkdev);
-        blk_unref(blkdev->blk);
-        blkdev->blk = NULL;
-    }
-    xen_pv_unbind_evtchn(xendev);
-
-    aio_context_release(blkdev->ctx);
-
-    if (blkdev->sring) {
-        xen_be_unmap_grant_refs(xendev, blkdev->sring,
-                                blkdev->nr_ring_ref);
-        blkdev->sring = NULL;
-    }
-}
-
-static int blk_free(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-    struct ioreq *ioreq;
-
-    trace_xen_disk_free(xendev->name);
-
-    blk_disconnect(xendev);
-
-    while (!QLIST_EMPTY(&blkdev->freelist)) {
-        ioreq = QLIST_FIRST(&blkdev->freelist);
-        QLIST_REMOVE(ioreq, list);
-        qemu_iovec_destroy(&ioreq->v);
-        g_free(ioreq);
-    }
-
-    g_free(blkdev->params);
-    g_free(blkdev->mode);
-    g_free(blkdev->type);
-    g_free(blkdev->dev);
-    g_free(blkdev->devtype);
-    qemu_bh_delete(blkdev->bh);
-    iothread_destroy(blkdev->iothread);
-    return 0;
-}
-
-static void blk_event(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-
-    qemu_bh_schedule(blkdev->bh);
-}
-
-struct XenDevOps xen_blkdev_ops = {
-    .flags      = DEVOPS_FLAG_NEED_GNTDEV,
-    .size       = sizeof(struct XenBlkDev),
-    .alloc      = blk_alloc,
-    .init       = blk_init,
-    .initialise = blk_connect,
-    .disconnect = blk_disconnect,
-    .event      = blk_event,
-    .free       = blk_free,
-};
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
index b1a1e66d5a..dc6ff0e5b3 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -26,7 +26,7 @@
 #include "qapi/error.h"
 #include "hw/hw.h"
 #include "chardev/char-fe.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 
 #include <xen/io/console.h>
 
@@ -39,7 +39,7 @@ struct buffer {
 };
 
 struct XenConsole {
-    struct XenDevice  xendev;  /* must be first */
+    struct XenLegacyDevice  xendev;  /* must be first */
     struct buffer     buffer;
     char              console[XEN_BUFSIZE];
     int               ring_ref;
@@ -173,7 +173,7 @@ static void xencons_send(struct XenConsole *con)
 
 /* -------------------------------------------------------------------- */
 
-static int con_init(struct XenDevice *xendev)
+static int con_init(struct XenLegacyDevice *xendev)
 {
     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
     char *type, *dom, label[32];
@@ -222,7 +222,7 @@ out:
     return ret;
 }
 
-static int con_initialise(struct XenDevice *xendev)
+static int con_initialise(struct XenLegacyDevice *xendev)
 {
     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
     int limit;
@@ -259,7 +259,7 @@ static int con_initialise(struct XenDevice *xendev)
     return 0;
 }
 
-static void con_disconnect(struct XenDevice *xendev)
+static void con_disconnect(struct XenLegacyDevice *xendev)
 {
     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
 
@@ -276,7 +276,7 @@ static void con_disconnect(struct XenDevice *xendev)
     }
 }
 
-static void con_event(struct XenDevice *xendev)
+static void con_event(struct XenLegacyDevice *xendev)
 {
     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
 
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
index 0330dc6f61..6202f1150e 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -30,7 +30,7 @@
 #include "hw/hw.h"
 #include "ui/input.h"
 #include "ui/console.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 
 #include <xen/event_channel.h>
 #include <xen/io/fbif.h>
@@ -46,7 +46,7 @@
 /* -------------------------------------------------------------------- */
 
 struct common {
-    struct XenDevice  xendev;  /* must be first */
+    struct XenLegacyDevice  xendev;  /* must be first */
     void              *page;
 };
 
@@ -342,14 +342,14 @@ static QemuInputHandler xenfb_rel_mouse = {
     .sync  = xenfb_mouse_sync,
 };
 
-static int input_init(struct XenDevice *xendev)
+static int input_init(struct XenLegacyDevice *xendev)
 {
     xenstore_write_be_int(xendev, "feature-abs-pointer", 1);
     xenstore_write_be_int(xendev, "feature-raw-pointer", 1);
     return 0;
 }
 
-static int input_initialise(struct XenDevice *xendev)
+static int input_initialise(struct XenLegacyDevice *xendev)
 {
     struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
     int rc;
@@ -361,7 +361,7 @@ static int input_initialise(struct XenDevice *xendev)
     return 0;
 }
 
-static void input_connected(struct XenDevice *xendev)
+static void input_connected(struct XenLegacyDevice *xendev)
 {
     struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
 
@@ -395,7 +395,7 @@ static void input_connected(struct XenDevice *xendev)
     }
 }
 
-static void input_disconnect(struct XenDevice *xendev)
+static void input_disconnect(struct XenLegacyDevice *xendev)
 {
     struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
 
@@ -410,7 +410,7 @@ static void input_disconnect(struct XenDevice *xendev)
     common_unbind(&in->c);
 }
 
-static void input_event(struct XenDevice *xendev)
+static void input_event(struct XenLegacyDevice *xendev)
 {
     struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev);
     struct xenkbd_page *page = xenfb->c.page;
@@ -867,7 +867,7 @@ static void xenfb_handle_events(struct XenFB *xenfb)
     page->out_cons = cons;
 }
 
-static int fb_init(struct XenDevice *xendev)
+static int fb_init(struct XenLegacyDevice *xendev)
 {
 #ifdef XENFB_TYPE_RESIZE
     xenstore_write_be_int(xendev, "feature-resize", 1);
@@ -875,7 +875,7 @@ static int fb_init(struct XenDevice *xendev)
     return 0;
 }
 
-static int fb_initialise(struct XenDevice *xendev)
+static int fb_initialise(struct XenLegacyDevice *xendev)
 {
     struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
     struct xenfb_page *fb_page;
@@ -912,7 +912,7 @@ static int fb_initialise(struct XenDevice *xendev)
     return 0;
 }
 
-static void fb_disconnect(struct XenDevice *xendev)
+static void fb_disconnect(struct XenLegacyDevice *xendev)
 {
     struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
 
@@ -935,7 +935,8 @@ static void fb_disconnect(struct XenDevice *xendev)
     fb->bug_trigger    = 0;
 }
 
-static void fb_frontend_changed(struct XenDevice *xendev, const char *node)
+static void fb_frontend_changed(struct XenLegacyDevice *xendev,
+                                const char *node)
 {
     struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
 
@@ -953,7 +954,7 @@ static void fb_frontend_changed(struct XenDevice *xendev, const char *node)
     }
 }
 
-static void fb_event(struct XenDevice *xendev)
+static void fb_event(struct XenLegacyDevice *xendev)
 {
     struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev);
 
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index fc65049e1d..73d688f842 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -112,6 +112,16 @@ struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX};
 
 GlobalProperty pc_compat_3_1[] = {
     { "intel-iommu", "dma-drain", "off" },
+    { "Opteron_G3" "-" TYPE_X86_CPU, "rdtscp", "off" },
+    { "Opteron_G4" "-" TYPE_X86_CPU, "rdtscp", "off" },
+    { "Opteron_G5" "-" TYPE_X86_CPU, "rdtscp", "off" },
+    { "Skylake-Client" "-" TYPE_X86_CPU,      "mpx", "on" },
+    { "Skylake-Client-IBRS" "-" TYPE_X86_CPU, "mpx", "on" },
+    { "Skylake-Server" "-" TYPE_X86_CPU,      "mpx", "on" },
+    { "Skylake-Server-IBRS" "-" TYPE_X86_CPU, "mpx", "on" },
+    { "Cascadelake-Server" "-" TYPE_X86_CPU,  "mpx", "on" },
+    { "Icelake-Client" "-" TYPE_X86_CPU,      "mpx", "on" },
+    { "Icelake-Server" "-" TYPE_X86_CPU,      "mpx", "on" },
 };
 const size_t pc_compat_3_1_len = G_N_ELEMENTS(pc_compat_3_1);
 
diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c
index 8c8562f359..2939122e7c 100644
--- a/hw/i386/xen/xen-hvm.c
+++ b/hw/i386/xen/xen-hvm.c
@@ -16,7 +16,8 @@
 #include "hw/i386/pc.h"
 #include "hw/i386/apic-msidef.h"
 #include "hw/xen/xen_common.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
+#include "hw/xen/xen-bus.h"
 #include "qapi/error.h"
 #include "qapi/qapi-commands-misc.h"
 #include "qemu/error-report.h"
@@ -1484,6 +1485,8 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory)
     QLIST_INIT(&state->dev_list);
     device_listener_register(&state->device_listener);
 
+    xen_bus_init();
+
     /* Initialize backend core & drivers */
     if (xen_be_init() != 0) {
         error_report("xen backend core setup failed");
diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c
index 02e823c5a2..349f72d00c 100644
--- a/hw/i386/xen/xen-mapcache.c
+++ b/hw/i386/xen/xen-mapcache.c
@@ -14,7 +14,7 @@
 
 #include <sys/resource.h>
 
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "qemu/bitmap.h"
 
 #include <xen/hvm/params.h>
diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c
index deb7a0c374..16afb54fee 100644
--- a/hw/i386/xen/xen_platform.c
+++ b/hw/i386/xen/xen_platform.c
@@ -30,7 +30,7 @@
 #include "hw/pci/pci.h"
 #include "hw/irq.h"
 #include "hw/xen/xen_common.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "trace.h"
 #include "exec/address-spaces.h"
 #include "sysemu/block-backend.h"
diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
index 46a8dbfc90..37cda8e4be 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -28,14 +28,14 @@
 #include "net/net.h"
 #include "net/checksum.h"
 #include "net/util.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 
 #include <xen/io/netif.h>
 
 /* ------------------------------------------------------------- */
 
 struct XenNetDev {
-    struct XenDevice      xendev;  /* must be first */
+    struct XenLegacyDevice      xendev;  /* must be first */
     char                  *mac;
     int                   tx_work;
     int                   tx_ring_ref;
@@ -276,7 +276,7 @@ static NetClientInfo net_xen_info = {
     .receive = net_rx_packet,
 };
 
-static int net_init(struct XenDevice *xendev)
+static int net_init(struct XenLegacyDevice *xendev)
 {
     struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
 
@@ -308,7 +308,7 @@ static int net_init(struct XenDevice *xendev)
     return 0;
 }
 
-static int net_connect(struct XenDevice *xendev)
+static int net_connect(struct XenLegacyDevice *xendev)
 {
     struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
     int rx_copy;
@@ -363,7 +363,7 @@ static int net_connect(struct XenDevice *xendev)
     return 0;
 }
 
-static void net_disconnect(struct XenDevice *xendev)
+static void net_disconnect(struct XenLegacyDevice *xendev)
 {
     struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
 
@@ -379,14 +379,14 @@ static void net_disconnect(struct XenDevice *xendev)
     }
 }
 
-static void net_event(struct XenDevice *xendev)
+static void net_event(struct XenLegacyDevice *xendev)
 {
     struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
     net_tx_packets(netdev);
     qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
 }
 
-static int net_free(struct XenDevice *xendev)
+static int net_free(struct XenLegacyDevice *xendev)
 {
     struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
 
diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c
index 5758a105a2..b20d0cfadf 100644
--- a/hw/usb/xen-usb.c
+++ b/hw/usb/xen-usb.c
@@ -27,7 +27,7 @@
 #include "qemu/option.h"
 #include "hw/sysbus.h"
 #include "hw/usb.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "monitor/qdev.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qstring.h"
@@ -99,7 +99,7 @@ struct usbback_hotplug {
 };
 
 struct usbback_info {
-    struct XenDevice         xendev;  /* must be first */
+    struct XenLegacyDevice         xendev;  /* must be first */
     USBBus                   bus;
     void                     *urb_sring;
     void                     *conn_sring;
@@ -142,7 +142,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req)
     unsigned int nr_segs, i, prot;
     uint32_t ref[USBIF_MAX_SEGMENTS_PER_REQUEST];
     struct usbback_info *usbif = usbback_req->usbif;
-    struct XenDevice *xendev = &usbif->xendev;
+    struct XenLegacyDevice *xendev = &usbif->xendev;
     struct usbif_request_segment *seg;
     void *addr;
 
@@ -220,7 +220,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req)
 
 static int usbback_init_packet(struct usbback_req *usbback_req)
 {
-    struct XenDevice *xendev = &usbback_req->usbif->xendev;
+    struct XenLegacyDevice *xendev = &usbback_req->usbif->xendev;
     USBPacket *packet = &usbback_req->packet;
     USBDevice *dev = usbback_req->stub->dev;
     USBEndpoint *ep;
@@ -279,7 +279,7 @@ static void usbback_do_response(struct usbback_req *usbback_req, int32_t status,
 {
     struct usbback_info *usbif;
     struct usbif_urb_response *res;
-    struct XenDevice *xendev;
+    struct XenLegacyDevice *xendev;
     unsigned int notify;
 
     usbif = usbback_req->usbif;
@@ -824,7 +824,7 @@ static void usbback_process_port(struct usbback_info *usbif, unsigned port)
     g_free(busid);
 }
 
-static void usbback_disconnect(struct XenDevice *xendev)
+static void usbback_disconnect(struct XenLegacyDevice *xendev)
 {
     struct usbback_info *usbif;
     unsigned int i;
@@ -853,7 +853,7 @@ static void usbback_disconnect(struct XenDevice *xendev)
     TR_BUS(xendev, "finished\n");
 }
 
-static int usbback_connect(struct XenDevice *xendev)
+static int usbback_connect(struct XenLegacyDevice *xendev)
 {
     struct usbback_info *usbif;
     struct usbif_urb_sring *urb_sring;
@@ -913,7 +913,8 @@ static int usbback_connect(struct XenDevice *xendev)
     return 0;
 }
 
-static void usbback_backend_changed(struct XenDevice *xendev, const char *node)
+static void usbback_backend_changed(struct XenLegacyDevice *xendev,
+                                    const char *node)
 {
     struct usbback_info *usbif;
     unsigned int i;
@@ -926,7 +927,7 @@ static void usbback_backend_changed(struct XenDevice *xendev, const char *node)
     }
 }
 
-static int usbback_init(struct XenDevice *xendev)
+static int usbback_init(struct XenLegacyDevice *xendev)
 {
     struct usbback_info *usbif;
 
@@ -1005,7 +1006,7 @@ static USBPortOps xen_usb_port_ops = {
 static USBBusOps xen_usb_bus_ops = {
 };
 
-static void usbback_alloc(struct XenDevice *xendev)
+static void usbback_alloc(struct XenLegacyDevice *xendev)
 {
     struct usbback_info *usbif;
     USBPort *p;
@@ -1027,7 +1028,7 @@ static void usbback_alloc(struct XenDevice *xendev)
     usbif->bh = qemu_bh_new(usbback_bh, usbif);
 }
 
-static int usbback_free(struct XenDevice *xendev)
+static int usbback_free(struct XenLegacyDevice *xendev)
 {
     struct usbback_info *usbif;
     struct usbback_req *usbback_req;
@@ -1066,7 +1067,7 @@ static int usbback_free(struct XenDevice *xendev)
     return 0;
 }
 
-static void usbback_event(struct XenDevice *xendev)
+static void usbback_event(struct XenLegacyDevice *xendev)
 {
     struct usbback_info *usbif;
 
diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs
index 9ea5c73423..84df60a928 100644
--- a/hw/xen/Makefile.objs
+++ b/hw/xen/Makefile.objs
@@ -1,5 +1,5 @@
 # xen backend driver support
-common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o xen_pvdev.o xen-common.o
+common-obj-$(CONFIG_XEN) += xen-legacy-backend.o xen_devconfig.o xen_pvdev.o xen-common.o xen-bus.o xen-bus-helper.o xen-backend.o
 
 obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
 obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_graphics.o xen_pt_msi.o
diff --git a/hw/xen/trace-events b/hw/xen/trace-events
index c7e7a3b523..f6944624b2 100644
--- a/hw/xen/trace-events
+++ b/hw/xen/trace-events
@@ -12,3 +12,29 @@ xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id:
 xen_map_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
 xen_unmap_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
 xen_domid_restrict(int err) "err: %u"
+
+# include/hw/xen/xen-bus.c
+xen_bus_realize(void) ""
+xen_bus_unrealize(void) ""
+xen_bus_enumerate(void) ""
+xen_bus_type_enumerate(const char *type) "type: %s"
+xen_bus_backend_create(const char *type, const char *path) "type: %s path: %s"
+xen_bus_add_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s"
+xen_bus_remove_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s"
+xen_bus_watch(const char *token) "token: %s"
+xen_device_realize(const char *type, char *name) "type: %s name: %s"
+xen_device_unrealize(const char *type, char *name) "type: %s name: %s"
+xen_device_backend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s"
+xen_device_backend_online(const char *type, char *name, bool online) "type: %s name: %s -> %u"
+xen_device_backend_changed(const char *type, char *name) "type: %s name: %s"
+xen_device_frontend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s"
+xen_device_frontend_changed(const char *type, char *name) "type: %s name: %s"
+xen_device_unplug(const char *type, char *name) "type: %s name: %s"
+
+# include/hw/xen/xen-bus-helper.c
+xs_node_create(const char *node) "%s"
+xs_node_destroy(const char *node) "%s"
+xs_node_vprintf(char *path, char *value) "%s %s"
+xs_node_vscanf(char *path, char *value) "%s %s"
+xs_node_watch(char *path) "%s"
+xs_node_unwatch(char *path) "%s"
diff --git a/hw/xen/xen-backend.c b/hw/xen/xen-backend.c
new file mode 100644
index 0000000000..da065f81b7
--- /dev/null
+++ b/hw/xen/xen-backend.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2018  Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/xen/xen-backend.h"
+#include "hw/xen/xen-bus.h"
+
+typedef struct XenBackendImpl {
+    const char *type;
+    XenBackendDeviceCreate create;
+    XenBackendDeviceDestroy destroy;
+} XenBackendImpl;
+
+struct XenBackendInstance {
+    QLIST_ENTRY(XenBackendInstance) entry;
+    const XenBackendImpl *impl;
+    XenBus *xenbus;
+    char *name;
+    XenDevice *xendev;
+};
+
+static GHashTable *xen_backend_table_get(void)
+{
+    static GHashTable *table;
+
+    if (table == NULL) {
+        table = g_hash_table_new(g_str_hash, g_str_equal);
+    }
+
+    return table;
+}
+
+static void xen_backend_table_add(XenBackendImpl *impl)
+{
+    g_hash_table_insert(xen_backend_table_get(), (void *)impl->type, impl);
+}
+
+static const XenBackendImpl *xen_backend_table_lookup(const char *type)
+{
+    return g_hash_table_lookup(xen_backend_table_get(), type);
+}
+
+void xen_backend_register(const XenBackendInfo *info)
+{
+    XenBackendImpl *impl = g_new0(XenBackendImpl, 1);
+
+    g_assert(info->type);
+
+    if (xen_backend_table_lookup(info->type)) {
+        error_report("attempt to register duplicate Xen backend type '%s'",
+                     info->type);
+        abort();
+    }
+
+    if (!info->create) {
+        error_report("backend type '%s' has no creator", info->type);
+        abort();
+    }
+
+    impl->type = info->type;
+    impl->create = info->create;
+    impl->destroy = info->destroy;
+
+    xen_backend_table_add(impl);
+}
+
+static QLIST_HEAD(, XenBackendInstance) backend_list;
+
+static void xen_backend_list_add(XenBackendInstance *backend)
+{
+    QLIST_INSERT_HEAD(&backend_list, backend, entry);
+}
+
+static XenBackendInstance *xen_backend_list_find(XenDevice *xendev)
+{
+    XenBackendInstance *backend;
+
+    QLIST_FOREACH(backend, &backend_list, entry) {
+        if (backend->xendev == xendev) {
+            return backend;
+        }
+    }
+
+    return NULL;
+}
+
+static void xen_backend_list_remove(XenBackendInstance *backend)
+{
+    QLIST_REMOVE(backend, entry);
+}
+
+void xen_backend_device_create(XenBus *xenbus, const char *type,
+                               const char *name, QDict *opts, Error **errp)
+{
+    const XenBackendImpl *impl = xen_backend_table_lookup(type);
+    XenBackendInstance *backend;
+    Error *local_error = NULL;
+
+    if (!impl) {
+        return;
+    }
+
+    backend = g_new0(XenBackendInstance, 1);
+    backend->xenbus = xenbus;
+    backend->name = g_strdup(name);
+
+    impl->create(backend, opts, &local_error);
+    if (local_error) {
+        error_propagate(errp, local_error);
+        g_free(backend->name);
+        g_free(backend);
+        return;
+    }
+
+    backend->impl = impl;
+    xen_backend_list_add(backend);
+}
+
+XenBus *xen_backend_get_bus(XenBackendInstance *backend)
+{
+    return backend->xenbus;
+}
+
+const char *xen_backend_get_name(XenBackendInstance *backend)
+{
+    return backend->name;
+}
+
+void xen_backend_set_device(XenBackendInstance *backend,
+                            XenDevice *xendev)
+{
+    g_assert(!backend->xendev);
+    backend->xendev = xendev;
+}
+
+XenDevice *xen_backend_get_device(XenBackendInstance *backend)
+{
+    return backend->xendev;
+}
+
+
+bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp)
+{
+    XenBackendInstance *backend = xen_backend_list_find(xendev);
+    const XenBackendImpl *impl;
+
+    if (!backend) {
+        return false;
+    }
+
+    impl = backend->impl;
+    impl->destroy(backend, errp);
+
+    xen_backend_list_remove(backend);
+    g_free(backend->name);
+    g_free(backend);
+
+    return true;
+}
diff --git a/hw/xen/xen-bus-helper.c b/hw/xen/xen-bus-helper.c
new file mode 100644
index 0000000000..5f7a4b2612
--- /dev/null
+++ b/hw/xen/xen-bus-helper.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2018  Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/xen/xen.h"
+#include "hw/xen/xen-bus.h"
+#include "hw/xen/xen-bus-helper.h"
+#include "qapi/error.h"
+
+#include <glib/gprintf.h>
+
+struct xs_state {
+    enum xenbus_state statenum;
+    const char *statestr;
+};
+#define XS_STATE(state) { state, #state }
+
+static struct xs_state xs_state[] = {
+    XS_STATE(XenbusStateUnknown),
+    XS_STATE(XenbusStateInitialising),
+    XS_STATE(XenbusStateInitWait),
+    XS_STATE(XenbusStateInitialised),
+    XS_STATE(XenbusStateConnected),
+    XS_STATE(XenbusStateClosing),
+    XS_STATE(XenbusStateClosed),
+    XS_STATE(XenbusStateReconfiguring),
+    XS_STATE(XenbusStateReconfigured),
+};
+
+#undef XS_STATE
+
+const char *xs_strstate(enum xenbus_state state)
+{
+    unsigned int i;
+
+   for (i = 0; i < ARRAY_SIZE(xs_state); i++) {
+        if (xs_state[i].statenum == state) {
+            return xs_state[i].statestr;
+        }
+    }
+
+    return "INVALID";
+}
+
+void xs_node_create(struct xs_handle *xsh, xs_transaction_t tid,
+                    const char *node, struct xs_permissions perms[],
+                    unsigned int nr_perms, Error **errp)
+{
+    trace_xs_node_create(node);
+
+    if (!xs_write(xsh, tid, node, "", 0)) {
+        error_setg_errno(errp, errno, "failed to create node '%s'", node);
+        return;
+    }
+
+    if (!xs_set_permissions(xsh, tid, node, perms, nr_perms)) {
+        error_setg_errno(errp, errno, "failed to set node '%s' permissions",
+                         node);
+    }
+}
+
+void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid,
+                     const char *node, Error **errp)
+{
+    trace_xs_node_destroy(node);
+
+    if (!xs_rm(xsh, tid, node)) {
+        error_setg_errno(errp, errno, "failed to destroy node '%s'", node);
+    }
+}
+
+void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid,
+                     const char *node, const char *key, Error **errp,
+                     const char *fmt, va_list ap)
+{
+    char *path, *value;
+    int len;
+
+    path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+        g_strdup(key);
+    len = g_vasprintf(&value, fmt, ap);
+
+    trace_xs_node_vprintf(path, value);
+
+    if (!xs_write(xsh, tid, path, value, len)) {
+        error_setg_errno(errp, errno, "failed to write '%s' to '%s'",
+                         value, path);
+    }
+
+    g_free(value);
+    g_free(path);
+}
+
+void xs_node_printf(struct xs_handle *xsh,  xs_transaction_t tid,
+                    const char *node, const char *key, Error **errp,
+                    const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    xs_node_vprintf(xsh, tid, node, key, errp, fmt, ap);
+    va_end(ap);
+}
+
+int xs_node_vscanf(struct xs_handle *xsh,  xs_transaction_t tid,
+                   const char *node, const char *key, Error **errp,
+                   const char *fmt, va_list ap)
+{
+    char *path, *value;
+    int rc;
+
+    path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+        g_strdup(key);
+    value = xs_read(xsh, tid, path, NULL);
+
+    trace_xs_node_vscanf(path, value);
+
+    if (value) {
+        rc = vsscanf(value, fmt, ap);
+    } else {
+        error_setg_errno(errp, errno, "failed to read from '%s'",
+                         path);
+        rc = EOF;
+    }
+
+    free(value);
+    g_free(path);
+
+    return rc;
+}
+
+int xs_node_scanf(struct xs_handle *xsh,  xs_transaction_t tid,
+                  const char *node, const char *key, Error **errp,
+                  const char *fmt, ...)
+{
+    va_list ap;
+    int rc;
+
+    va_start(ap, fmt);
+    rc = xs_node_vscanf(xsh, tid, node, key, errp, fmt, ap);
+    va_end(ap);
+
+    return rc;
+}
+
+void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key,
+                   char *token, Error **errp)
+{
+    char *path;
+
+    path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+        g_strdup(key);
+
+    trace_xs_node_watch(path);
+
+    if (!xs_watch(xsh, path, token)) {
+        error_setg_errno(errp, errno, "failed to watch node '%s'", path);
+    }
+
+    g_free(path);
+}
+
+void xs_node_unwatch(struct xs_handle *xsh, const char *node,
+                     const char *key, const char *token, Error **errp)
+{
+    char *path;
+
+    path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) :
+        g_strdup(key);
+
+    trace_xs_node_unwatch(path);
+
+    if (!xs_unwatch(xsh, path, token)) {
+        error_setg_errno(errp, errno, "failed to unwatch node '%s'", path);
+    }
+
+    g_free(path);
+}
diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c
new file mode 100644
index 0000000000..3aeccec69c
--- /dev/null
+++ b/hw/xen/xen-bus.c
@@ -0,0 +1,1199 @@
+/*
+ * Copyright (c) 2018  Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qemu/uuid.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/xen/xen.h"
+#include "hw/xen/xen-backend.h"
+#include "hw/xen/xen-bus.h"
+#include "hw/xen/xen-bus-helper.h"
+#include "monitor/monitor.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+
+static char *xen_device_get_backend_path(XenDevice *xendev)
+{
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+    const char *type = object_get_typename(OBJECT(xendev));
+    const char *backend = xendev_class->backend;
+
+    if (!backend) {
+        backend = type;
+    }
+
+    return g_strdup_printf("/local/domain/%u/backend/%s/%u/%s",
+                           xenbus->backend_id, backend, xendev->frontend_id,
+                           xendev->name);
+}
+
+static char *xen_device_get_frontend_path(XenDevice *xendev)
+{
+    XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+    const char *type = object_get_typename(OBJECT(xendev));
+    const char *device = xendev_class->device;
+
+    if (!device) {
+        device = type;
+    }
+
+    return g_strdup_printf("/local/domain/%u/device/%s/%s",
+                           xendev->frontend_id, device, xendev->name);
+}
+
+static void xen_device_unplug(XenDevice *xendev, Error **errp)
+{
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    const char *type = object_get_typename(OBJECT(xendev));
+    Error *local_err = NULL;
+    xs_transaction_t tid;
+
+    trace_xen_device_unplug(type, xendev->name);
+
+    /* Mimic the way the Xen toolstack does an unplug */
+again:
+    tid = xs_transaction_start(xenbus->xsh);
+    if (tid == XBT_NULL) {
+        error_setg_errno(errp, errno, "failed xs_transaction_start");
+        return;
+    }
+
+    xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "online",
+                   &local_err, "%u", 0);
+    if (local_err) {
+        goto abort;
+    }
+
+    xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "state",
+                   &local_err, "%u", XenbusStateClosing);
+    if (local_err) {
+        goto abort;
+    }
+
+    if (!xs_transaction_end(xenbus->xsh, tid, false)) {
+        if (errno == EAGAIN) {
+            goto again;
+        }
+
+        error_setg_errno(errp, errno, "failed xs_transaction_end");
+    }
+
+    return;
+
+abort:
+    /*
+     * We only abort if there is already a failure so ignore any error
+     * from ending the transaction.
+     */
+    xs_transaction_end(xenbus->xsh, tid, true);
+    error_propagate(errp, local_err);
+}
+
+static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent)
+{
+    XenDevice *xendev = XEN_DEVICE(dev);
+
+    monitor_printf(mon, "%*sname = '%s' frontend_id = %u\n",
+                   indent, "", xendev->name, xendev->frontend_id);
+}
+
+static char *xen_bus_get_dev_path(DeviceState *dev)
+{
+    return xen_device_get_backend_path(XEN_DEVICE(dev));
+}
+
+struct XenWatch {
+    char *node, *key;
+    char *token;
+    XenWatchHandler handler;
+    void *opaque;
+    Notifier notifier;
+};
+
+static void watch_notify(Notifier *n, void *data)
+{
+    XenWatch *watch = container_of(n, XenWatch, notifier);
+    const char *token = data;
+
+    if (!strcmp(watch->token, token)) {
+        watch->handler(watch->opaque);
+    }
+}
+
+static XenWatch *new_watch(const char *node, const char *key,
+                           XenWatchHandler handler, void *opaque)
+{
+    XenWatch *watch = g_new0(XenWatch, 1);
+    QemuUUID uuid;
+
+    qemu_uuid_generate(&uuid);
+
+    watch->token = qemu_uuid_unparse_strdup(&uuid);
+    watch->node = g_strdup(node);
+    watch->key = g_strdup(key);
+    watch->handler = handler;
+    watch->opaque = opaque;
+    watch->notifier.notify = watch_notify;
+
+    return watch;
+}
+
+static void free_watch(XenWatch *watch)
+{
+    g_free(watch->token);
+    g_free(watch->key);
+    g_free(watch->node);
+
+    g_free(watch);
+}
+
+static XenWatch *xen_bus_add_watch(XenBus *xenbus, const char *node,
+                                   const char *key, XenWatchHandler handler,
+                                   void *opaque, Error **errp)
+{
+    XenWatch *watch = new_watch(node, key, handler, opaque);
+    Error *local_err = NULL;
+
+    trace_xen_bus_add_watch(watch->node, watch->key, watch->token);
+
+    notifier_list_add(&xenbus->watch_notifiers, &watch->notifier);
+
+    xs_node_watch(xenbus->xsh, node, key, watch->token, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+
+        notifier_remove(&watch->notifier);
+        free_watch(watch);
+
+        return NULL;
+    }
+
+    return watch;
+}
+
+static void xen_bus_remove_watch(XenBus *xenbus, XenWatch *watch,
+                                 Error **errp)
+{
+    trace_xen_bus_remove_watch(watch->node, watch->key, watch->token);
+
+    xs_node_unwatch(xenbus->xsh, watch->node, watch->key, watch->token,
+                    errp);
+
+    notifier_remove(&watch->notifier);
+    free_watch(watch);
+}
+
+static void xen_bus_backend_create(XenBus *xenbus, const char *type,
+                                   const char *name, char *path,
+                                   Error **errp)
+{
+    xs_transaction_t tid;
+    char **key;
+    QDict *opts;
+    unsigned int i, n;
+    Error *local_err = NULL;
+
+    trace_xen_bus_backend_create(type, path);
+
+again:
+    tid = xs_transaction_start(xenbus->xsh);
+    if (tid == XBT_NULL) {
+        error_setg(errp, "failed xs_transaction_start");
+        return;
+    }
+
+    key = xs_directory(xenbus->xsh, tid, path, &n);
+    if (!key) {
+        if (!xs_transaction_end(xenbus->xsh, tid, true)) {
+            error_setg_errno(errp, errno, "failed xs_transaction_end");
+        }
+        return;
+    }
+
+    opts = qdict_new();
+    for (i = 0; i < n; i++) {
+        char *val;
+
+        /*
+         * Assume anything found in the xenstore backend area, other than
+         * the keys created for a generic XenDevice, are parameters
+         * to be used to configure the backend.
+         */
+        if (!strcmp(key[i], "state") ||
+            !strcmp(key[i], "online") ||
+            !strcmp(key[i], "frontend") ||
+            !strcmp(key[i], "frontend-id") ||
+            !strcmp(key[i], "hotplug-status"))
+            continue;
+
+        if (xs_node_scanf(xenbus->xsh, tid, path, key[i], NULL, "%ms",
+                          &val) == 1) {
+            qdict_put_str(opts, key[i], val);
+            free(val);
+        }
+    }
+
+    free(key);
+
+    if (!xs_transaction_end(xenbus->xsh, tid, false)) {
+        qobject_unref(opts);
+
+        if (errno == EAGAIN) {
+            goto again;
+        }
+
+        error_setg_errno(errp, errno, "failed xs_transaction_end");
+        return;
+    }
+
+    xen_backend_device_create(xenbus, type, name, opts, &local_err);
+    qobject_unref(opts);
+
+    if (local_err) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to create '%s' device '%s': ",
+                                type, name);
+    }
+}
+
+static void xen_bus_type_enumerate(XenBus *xenbus, const char *type)
+{
+    char *domain_path = g_strdup_printf("backend/%s/%u", type, xen_domid);
+    char **backend;
+    unsigned int i, n;
+
+    trace_xen_bus_type_enumerate(type);
+
+    backend = xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n);
+    if (!backend) {
+        goto out;
+    }
+
+    for (i = 0; i < n; i++) {
+        char *backend_path = g_strdup_printf("%s/%s", domain_path,
+                                             backend[i]);
+        enum xenbus_state backend_state;
+
+        if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "state",
+                          NULL, "%u", &backend_state) != 1)
+            backend_state = XenbusStateUnknown;
+
+        if (backend_state == XenbusStateInitialising) {
+            Error *local_err = NULL;
+
+            xen_bus_backend_create(xenbus, type, backend[i], backend_path,
+                                   &local_err);
+            if (local_err) {
+                error_report_err(local_err);
+            }
+        }
+
+        g_free(backend_path);
+    }
+
+    free(backend);
+
+out:
+    g_free(domain_path);
+}
+
+static void xen_bus_enumerate(void *opaque)
+{
+    XenBus *xenbus = opaque;
+    char **type;
+    unsigned int i, n;
+
+    trace_xen_bus_enumerate();
+
+    type = xs_directory(xenbus->xsh, XBT_NULL, "backend", &n);
+    if (!type) {
+        return;
+    }
+
+    for (i = 0; i < n; i++) {
+        xen_bus_type_enumerate(xenbus, type[i]);
+    }
+
+    free(type);
+}
+
+static void xen_bus_unrealize(BusState *bus, Error **errp)
+{
+    XenBus *xenbus = XEN_BUS(bus);
+
+    trace_xen_bus_unrealize();
+
+    if (xenbus->backend_watch) {
+        xen_bus_remove_watch(xenbus, xenbus->backend_watch, NULL);
+        xenbus->backend_watch = NULL;
+    }
+
+    if (!xenbus->xsh) {
+        return;
+    }
+
+    qemu_set_fd_handler(xs_fileno(xenbus->xsh), NULL, NULL, NULL);
+
+    xs_close(xenbus->xsh);
+}
+
+static void xen_bus_watch(void *opaque)
+{
+    XenBus *xenbus = opaque;
+    char **v;
+    const char *token;
+
+    g_assert(xenbus->xsh);
+
+    v = xs_check_watch(xenbus->xsh);
+    if (!v) {
+        return;
+    }
+
+    token = v[XS_WATCH_TOKEN];
+
+    trace_xen_bus_watch(token);
+
+    notifier_list_notify(&xenbus->watch_notifiers, (void *)token);
+
+    free(v);
+}
+
+static void xen_bus_realize(BusState *bus, Error **errp)
+{
+    XenBus *xenbus = XEN_BUS(bus);
+    unsigned int domid;
+    Error *local_err = NULL;
+
+    trace_xen_bus_realize();
+
+    xenbus->xsh = xs_open(0);
+    if (!xenbus->xsh) {
+        error_setg_errno(errp, errno, "failed xs_open");
+        goto fail;
+    }
+
+    if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */
+                      "domid", NULL, "%u", &domid) == 1) {
+        xenbus->backend_id = domid;
+    } else {
+        xenbus->backend_id = 0; /* Assume lack of node means dom0 */
+    }
+
+    notifier_list_init(&xenbus->watch_notifiers);
+    qemu_set_fd_handler(xs_fileno(xenbus->xsh), xen_bus_watch, NULL,
+                        xenbus);
+
+    module_call_init(MODULE_INIT_XEN_BACKEND);
+
+    xenbus->backend_watch =
+        xen_bus_add_watch(xenbus, "", /* domain root node */
+                          "backend", xen_bus_enumerate, xenbus, &local_err);
+    if (local_err) {
+        /* This need not be treated as a hard error so don't propagate */
+        error_reportf_err(local_err,
+                          "failed to set up enumeration watch: ");
+    }
+
+    return;
+
+fail:
+    xen_bus_unrealize(bus, &error_abort);
+}
+
+static void xen_bus_unplug_request(HotplugHandler *hotplug,
+                                   DeviceState *dev,
+                                   Error **errp)
+{
+    XenDevice *xendev = XEN_DEVICE(dev);
+
+    xen_device_unplug(xendev, errp);
+}
+
+static void xen_bus_class_init(ObjectClass *class, void *data)
+{
+    BusClass *bus_class = BUS_CLASS(class);
+    HotplugHandlerClass *hotplug_class = HOTPLUG_HANDLER_CLASS(class);
+
+    bus_class->print_dev = xen_bus_print_dev;
+    bus_class->get_dev_path = xen_bus_get_dev_path;
+    bus_class->realize = xen_bus_realize;
+    bus_class->unrealize = xen_bus_unrealize;
+
+    hotplug_class->unplug_request = xen_bus_unplug_request;
+}
+
+static const TypeInfo xen_bus_type_info = {
+    .name = TYPE_XEN_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(XenBus),
+    .class_size = sizeof(XenBusClass),
+    .class_init = xen_bus_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_HOTPLUG_HANDLER },
+        { }
+    },
+};
+
+void xen_device_backend_printf(XenDevice *xendev, const char *key,
+                               const char *fmt, ...)
+{
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    Error *local_err = NULL;
+    va_list ap;
+
+    g_assert(xenbus->xsh);
+
+    va_start(ap, fmt);
+    xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->backend_path, key,
+                    &local_err, fmt, ap);
+    va_end(ap);
+
+    if (local_err) {
+        error_report_err(local_err);
+    }
+}
+
+static int xen_device_backend_scanf(XenDevice *xendev, const char *key,
+                                    const char *fmt, ...)
+{
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    va_list ap;
+    int rc;
+
+    g_assert(xenbus->xsh);
+
+    va_start(ap, fmt);
+    rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->backend_path, key,
+                        NULL, fmt, ap);
+    va_end(ap);
+
+    return rc;
+}
+
+void xen_device_backend_set_state(XenDevice *xendev,
+                                  enum xenbus_state state)
+{
+    const char *type = object_get_typename(OBJECT(xendev));
+
+    if (xendev->backend_state == state) {
+        return;
+    }
+
+    trace_xen_device_backend_state(type, xendev->name,
+                                   xs_strstate(state));
+
+    xendev->backend_state = state;
+    xen_device_backend_printf(xendev, "state", "%u", state);
+}
+
+enum xenbus_state xen_device_backend_get_state(XenDevice *xendev)
+{
+    return xendev->backend_state;
+}
+
+static void xen_device_backend_set_online(XenDevice *xendev, bool online)
+{
+    const char *type = object_get_typename(OBJECT(xendev));
+
+    if (xendev->backend_online == online) {
+        return;
+    }
+
+    trace_xen_device_backend_online(type, xendev->name, online);
+
+    xendev->backend_online = online;
+    xen_device_backend_printf(xendev, "online", "%u", online);
+}
+
+static void xen_device_backend_changed(void *opaque)
+{
+    XenDevice *xendev = opaque;
+    const char *type = object_get_typename(OBJECT(xendev));
+    enum xenbus_state state;
+    unsigned int online;
+
+    trace_xen_device_backend_changed(type, xendev->name);
+
+    if (xen_device_backend_scanf(xendev, "state", "%u", &state) != 1) {
+        state = XenbusStateUnknown;
+    }
+
+    xen_device_backend_set_state(xendev, state);
+
+    if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) {
+        online = 0;
+    }
+
+    xen_device_backend_set_online(xendev, !!online);
+
+    /*
+     * If the toolstack (or unplug request callback) has set the backend
+     * state to Closing, but there is no active frontend (i.e. the
+     * state is not Connected) then set the backend state to Closed.
+     */
+    if (xendev->backend_state == XenbusStateClosing &&
+        xendev->frontend_state != XenbusStateConnected) {
+        xen_device_backend_set_state(xendev, XenbusStateClosed);
+    }
+
+    /*
+     * If a backend is still 'online' then its state should be cycled
+     * back round to InitWait in order for a new frontend instance to
+     * connect. This may happen when, for example, a frontend driver is
+     * re-installed or updated.
+     * If a backend is not 'online' then the device should be destroyed.
+     */
+    if (xendev->backend_online &&
+        xendev->backend_state == XenbusStateClosed) {
+        xen_device_backend_set_state(xendev, XenbusStateInitWait);
+    } else if (!xendev->backend_online &&
+               (xendev->backend_state == XenbusStateClosed ||
+                xendev->backend_state == XenbusStateInitialising ||
+                xendev->backend_state == XenbusStateInitWait ||
+                xendev->backend_state == XenbusStateUnknown)) {
+        Error *local_err = NULL;
+
+        if (!xen_backend_try_device_destroy(xendev, &local_err)) {
+            object_unparent(OBJECT(xendev));
+        }
+
+        if (local_err) {
+            error_report_err(local_err);
+        }
+    }
+}
+
+static void xen_device_backend_create(XenDevice *xendev, Error **errp)
+{
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    struct xs_permissions perms[2];
+    Error *local_err = NULL;
+
+    xendev->backend_path = xen_device_get_backend_path(xendev);
+
+    perms[0].id = xenbus->backend_id;
+    perms[0].perms = XS_PERM_NONE;
+    perms[1].id = xendev->frontend_id;
+    perms[1].perms = XS_PERM_READ;
+
+    g_assert(xenbus->xsh);
+
+    xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, perms,
+                   ARRAY_SIZE(perms), &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to create backend: ");
+        return;
+    }
+
+    xendev->backend_state_watch =
+        xen_bus_add_watch(xenbus, xendev->backend_path,
+                          "state", xen_device_backend_changed,
+                          xendev, &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to watch backend state: ");
+        return;
+    }
+
+    xendev->backend_online_watch =
+        xen_bus_add_watch(xenbus, xendev->backend_path,
+                          "online", xen_device_backend_changed,
+                          xendev, &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to watch backend online: ");
+        return;
+    }
+}
+
+static void xen_device_backend_destroy(XenDevice *xendev)
+{
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    Error *local_err = NULL;
+
+    if (xendev->backend_online_watch) {
+        xen_bus_remove_watch(xenbus, xendev->backend_online_watch, NULL);
+        xendev->backend_online_watch = NULL;
+    }
+
+    if (xendev->backend_state_watch) {
+        xen_bus_remove_watch(xenbus, xendev->backend_state_watch, NULL);
+        xendev->backend_state_watch = NULL;
+    }
+
+    if (!xendev->backend_path) {
+        return;
+    }
+
+    g_assert(xenbus->xsh);
+
+    xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->backend_path,
+                    &local_err);
+    g_free(xendev->backend_path);
+    xendev->backend_path = NULL;
+
+    if (local_err) {
+        error_report_err(local_err);
+    }
+}
+
+void xen_device_frontend_printf(XenDevice *xendev, const char *key,
+                                const char *fmt, ...)
+{
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    Error *local_err = NULL;
+    va_list ap;
+
+    g_assert(xenbus->xsh);
+
+    va_start(ap, fmt);
+    xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key,
+                    &local_err, fmt, ap);
+    va_end(ap);
+
+    if (local_err) {
+        error_report_err(local_err);
+    }
+}
+
+int xen_device_frontend_scanf(XenDevice *xendev, const char *key,
+                              const char *fmt, ...)
+{
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    va_list ap;
+    int rc;
+
+    g_assert(xenbus->xsh);
+
+    va_start(ap, fmt);
+    rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key,
+                        NULL, fmt, ap);
+    va_end(ap);
+
+    return rc;
+}
+
+static void xen_device_frontend_set_state(XenDevice *xendev,
+                                          enum xenbus_state state)
+{
+    const char *type = object_get_typename(OBJECT(xendev));
+
+    if (xendev->frontend_state == state) {
+        return;
+    }
+
+    trace_xen_device_frontend_state(type, xendev->name,
+                                    xs_strstate(state));
+
+    xendev->frontend_state = state;
+    xen_device_frontend_printf(xendev, "state", "%u", state);
+}
+
+static void xen_device_frontend_changed(void *opaque)
+{
+    XenDevice *xendev = opaque;
+    XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+    const char *type = object_get_typename(OBJECT(xendev));
+    enum xenbus_state state;
+
+    trace_xen_device_frontend_changed(type, xendev->name);
+
+    if (xen_device_frontend_scanf(xendev, "state", "%u", &state) != 1) {
+        state = XenbusStateUnknown;
+    }
+
+    xen_device_frontend_set_state(xendev, state);
+
+    if (xendev_class->frontend_changed) {
+        Error *local_err = NULL;
+
+        xendev_class->frontend_changed(xendev, state, &local_err);
+
+        if (local_err) {
+            error_reportf_err(local_err, "frontend change error: ");
+        }
+    }
+}
+
+static void xen_device_frontend_create(XenDevice *xendev, Error **errp)
+{
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    struct xs_permissions perms[2];
+    Error *local_err = NULL;
+
+    xendev->frontend_path = xen_device_get_frontend_path(xendev);
+
+    perms[0].id = xendev->frontend_id;
+    perms[0].perms = XS_PERM_NONE;
+    perms[1].id = xenbus->backend_id;
+    perms[1].perms = XS_PERM_READ | XS_PERM_WRITE;
+
+    g_assert(xenbus->xsh);
+
+    xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, perms,
+                   ARRAY_SIZE(perms), &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to create frontend: ");
+        return;
+    }
+
+    xendev->frontend_state_watch =
+        xen_bus_add_watch(xenbus, xendev->frontend_path, "state",
+                          xen_device_frontend_changed, xendev, &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to watch frontend state: ");
+    }
+}
+
+static void xen_device_frontend_destroy(XenDevice *xendev)
+{
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    Error *local_err = NULL;
+
+    if (xendev->frontend_state_watch) {
+        xen_bus_remove_watch(xenbus, xendev->frontend_state_watch, NULL);
+        xendev->frontend_state_watch = NULL;
+    }
+
+    if (!xendev->frontend_path) {
+        return;
+    }
+
+    g_assert(xenbus->xsh);
+
+    xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->frontend_path,
+                    &local_err);
+    g_free(xendev->frontend_path);
+    xendev->frontend_path = NULL;
+
+    if (local_err) {
+        error_report_err(local_err);
+    }
+}
+
+void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs,
+                                   Error **errp)
+{
+    if (xengnttab_set_max_grants(xendev->xgth, nr_refs)) {
+        error_setg_errno(errp, errno, "xengnttab_set_max_grants failed");
+    }
+}
+
+void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs,
+                                unsigned int nr_refs, int prot,
+                                Error **errp)
+{
+    void *map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_refs,
+                                                xendev->frontend_id, refs,
+                                                prot);
+
+    if (!map) {
+        error_setg_errno(errp, errno,
+                         "xengnttab_map_domain_grant_refs failed");
+    }
+
+    return map;
+}
+
+void xen_device_unmap_grant_refs(XenDevice *xendev, void *map,
+                                 unsigned int nr_refs, Error **errp)
+{
+    if (xengnttab_unmap(xendev->xgth, map, nr_refs)) {
+        error_setg_errno(errp, errno, "xengnttab_unmap failed");
+    }
+}
+
+static void compat_copy_grant_refs(XenDevice *xendev, bool to_domain,
+                                   XenDeviceGrantCopySegment segs[],
+                                   unsigned int nr_segs, Error **errp)
+{
+    uint32_t *refs = g_new(uint32_t, nr_segs);
+    int prot = to_domain ? PROT_WRITE : PROT_READ;
+    void *map;
+    unsigned int i;
+
+    for (i = 0; i < nr_segs; i++) {
+        XenDeviceGrantCopySegment *seg = &segs[i];
+
+        refs[i] = to_domain ? seg->dest.foreign.ref :
+            seg->source.foreign.ref;
+    }
+
+    map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_segs,
+                                          xendev->frontend_id, refs,
+                                          prot);
+    if (!map) {
+        error_setg_errno(errp, errno,
+                         "xengnttab_map_domain_grant_refs failed");
+        goto done;
+    }
+
+    for (i = 0; i < nr_segs; i++) {
+        XenDeviceGrantCopySegment *seg = &segs[i];
+        void *page = map + (i * XC_PAGE_SIZE);
+
+        if (to_domain) {
+            memcpy(page + seg->dest.foreign.offset, seg->source.virt,
+                   seg->len);
+        } else {
+            memcpy(seg->dest.virt, page + seg->source.foreign.offset,
+                   seg->len);
+        }
+    }
+
+    if (xengnttab_unmap(xendev->xgth, map, nr_segs)) {
+        error_setg_errno(errp, errno, "xengnttab_unmap failed");
+    }
+
+done:
+    g_free(refs);
+}
+
+void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain,
+                                XenDeviceGrantCopySegment segs[],
+                                unsigned int nr_segs, Error **errp)
+{
+    xengnttab_grant_copy_segment_t *xengnttab_segs;
+    unsigned int i;
+
+    if (!xendev->feature_grant_copy) {
+        compat_copy_grant_refs(xendev, to_domain, segs, nr_segs, errp);
+        return;
+    }
+
+    xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs);
+
+    for (i = 0; i < nr_segs; i++) {
+        XenDeviceGrantCopySegment *seg = &segs[i];
+        xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
+
+        if (to_domain) {
+            xengnttab_seg->flags = GNTCOPY_dest_gref;
+            xengnttab_seg->dest.foreign.domid = xendev->frontend_id;
+            xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref;
+            xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset;
+            xengnttab_seg->source.virt = seg->source.virt;
+        } else {
+            xengnttab_seg->flags = GNTCOPY_source_gref;
+            xengnttab_seg->source.foreign.domid = xendev->frontend_id;
+            xengnttab_seg->source.foreign.ref = seg->source.foreign.ref;
+            xengnttab_seg->source.foreign.offset =
+                seg->source.foreign.offset;
+            xengnttab_seg->dest.virt = seg->dest.virt;
+        }
+
+        xengnttab_seg->len = seg->len;
+    }
+
+    if (xengnttab_grant_copy(xendev->xgth, nr_segs, xengnttab_segs)) {
+        error_setg_errno(errp, errno, "xengnttab_grant_copy failed");
+        goto done;
+    }
+
+    for (i = 0; i < nr_segs; i++) {
+        xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i];
+
+        if (xengnttab_seg->status != GNTST_okay) {
+            error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i);
+            break;
+        }
+    }
+
+done:
+    g_free(xengnttab_segs);
+}
+
+struct XenEventChannel {
+    evtchn_port_t local_port;
+    XenEventHandler handler;
+    void *opaque;
+    Notifier notifier;
+};
+
+static void event_notify(Notifier *n, void *data)
+{
+    XenEventChannel *channel = container_of(n, XenEventChannel, notifier);
+    unsigned long port = (unsigned long)data;
+
+    if (port == channel->local_port) {
+        channel->handler(channel->opaque);
+    }
+}
+
+XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev,
+                                               unsigned int port,
+                                               XenEventHandler handler,
+                                               void *opaque, Error **errp)
+{
+    XenEventChannel *channel = g_new0(XenEventChannel, 1);
+    xenevtchn_port_or_error_t local_port;
+
+    local_port = xenevtchn_bind_interdomain(xendev->xeh,
+                                            xendev->frontend_id,
+                                            port);
+    if (local_port < 0) {
+        error_setg_errno(errp, errno, "xenevtchn_bind_interdomain failed");
+
+        g_free(channel);
+        return NULL;
+    }
+
+    channel->local_port = local_port;
+    channel->handler = handler;
+    channel->opaque = opaque;
+    channel->notifier.notify = event_notify;
+
+    notifier_list_add(&xendev->event_notifiers, &channel->notifier);
+
+    return channel;
+}
+
+void xen_device_notify_event_channel(XenDevice *xendev,
+                                     XenEventChannel *channel,
+                                     Error **errp)
+{
+    if (!channel) {
+        error_setg(errp, "bad channel");
+        return;
+    }
+
+    if (xenevtchn_notify(xendev->xeh, channel->local_port) < 0) {
+        error_setg_errno(errp, errno, "xenevtchn_notify failed");
+    }
+}
+
+void xen_device_unbind_event_channel(XenDevice *xendev,
+                                     XenEventChannel *channel,
+                                     Error **errp)
+{
+    if (!channel) {
+        error_setg(errp, "bad channel");
+        return;
+    }
+
+    notifier_remove(&channel->notifier);
+
+    if (xenevtchn_unbind(xendev->xeh, channel->local_port) < 0) {
+        error_setg_errno(errp, errno, "xenevtchn_unbind failed");
+    }
+
+    g_free(channel);
+}
+
+static void xen_device_unrealize(DeviceState *dev, Error **errp)
+{
+    XenDevice *xendev = XEN_DEVICE(dev);
+    XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+    const char *type = object_get_typename(OBJECT(xendev));
+
+    if (!xendev->name) {
+        return;
+    }
+
+    trace_xen_device_unrealize(type, xendev->name);
+
+    if (xendev->exit.notify) {
+        qemu_remove_exit_notifier(&xendev->exit);
+        xendev->exit.notify = NULL;
+    }
+
+    if (xendev_class->unrealize) {
+        xendev_class->unrealize(xendev, errp);
+    }
+
+    xen_device_frontend_destroy(xendev);
+    xen_device_backend_destroy(xendev);
+
+    if (xendev->xeh) {
+        qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), NULL, NULL, NULL);
+        xenevtchn_close(xendev->xeh);
+        xendev->xeh = NULL;
+    }
+
+    if (xendev->xgth) {
+        xengnttab_close(xendev->xgth);
+        xendev->xgth = NULL;
+    }
+
+    g_free(xendev->name);
+    xendev->name = NULL;
+}
+
+static void xen_device_exit(Notifier *n, void *data)
+{
+    XenDevice *xendev = container_of(n, XenDevice, exit);
+
+    xen_device_unrealize(DEVICE(xendev), &error_abort);
+}
+
+static void xen_device_event(void *opaque)
+{
+    XenDevice *xendev = opaque;
+    unsigned long port = xenevtchn_pending(xendev->xeh);
+
+    notifier_list_notify(&xendev->event_notifiers, (void *)port);
+
+    xenevtchn_unmask(xendev->xeh, port);
+}
+
+static void xen_device_realize(DeviceState *dev, Error **errp)
+{
+    XenDevice *xendev = XEN_DEVICE(dev);
+    XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev);
+    XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
+    const char *type = object_get_typename(OBJECT(xendev));
+    Error *local_err = NULL;
+
+    if (xendev->frontend_id == DOMID_INVALID) {
+        xendev->frontend_id = xen_domid;
+    }
+
+    if (xendev->frontend_id >= DOMID_FIRST_RESERVED) {
+        error_setg(errp, "invalid frontend-id");
+        goto unrealize;
+    }
+
+    if (!xendev_class->get_name) {
+        error_setg(errp, "get_name method not implemented");
+        goto unrealize;
+    }
+
+    xendev->name = xendev_class->get_name(xendev, &local_err);
+    if (local_err) {
+        error_propagate_prepend(errp, local_err,
+                                "failed to get device name: ");
+        goto unrealize;
+    }
+
+    trace_xen_device_realize(type, xendev->name);
+
+    xendev->xgth = xengnttab_open(NULL, 0);
+    if (!xendev->xgth) {
+        error_setg_errno(errp, errno, "failed xengnttab_open");
+        goto unrealize;
+    }
+
+    xendev->feature_grant_copy =
+        (xengnttab_grant_copy(xendev->xgth, 0, NULL) == 0);
+
+    xendev->xeh = xenevtchn_open(NULL, 0);
+    if (!xendev->xeh) {
+        error_setg_errno(errp, errno, "failed xenevtchn_open");
+        goto unrealize;
+    }
+
+    notifier_list_init(&xendev->event_notifiers);
+    qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), xen_device_event, NULL,
+                        xendev);
+
+    xen_device_backend_create(xendev, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto unrealize;
+    }
+
+    xen_device_frontend_create(xendev, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto unrealize;
+    }
+
+    if (xendev_class->realize) {
+        xendev_class->realize(xendev, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            goto unrealize;
+        }
+    }
+
+    xen_device_backend_printf(xendev, "frontend", "%s",
+                              xendev->frontend_path);
+    xen_device_backend_printf(xendev, "frontend-id", "%u",
+                              xendev->frontend_id);
+    xen_device_backend_printf(xendev, "hotplug-status", "connected");
+
+    xen_device_backend_set_online(xendev, true);
+    xen_device_backend_set_state(xendev, XenbusStateInitWait);
+
+    xen_device_frontend_printf(xendev, "backend", "%s",
+                               xendev->backend_path);
+    xen_device_frontend_printf(xendev, "backend-id", "%u",
+                               xenbus->backend_id);
+
+    xen_device_frontend_set_state(xendev, XenbusStateInitialising);
+
+    xendev->exit.notify = xen_device_exit;
+    qemu_add_exit_notifier(&xendev->exit);
+    return;
+
+unrealize:
+    xen_device_unrealize(dev, &error_abort);
+}
+
+static Property xen_device_props[] = {
+    DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id,
+                       DOMID_INVALID),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void xen_device_class_init(ObjectClass *class, void *data)
+{
+    DeviceClass *dev_class = DEVICE_CLASS(class);
+
+    dev_class->realize = xen_device_realize;
+    dev_class->unrealize = xen_device_unrealize;
+    dev_class->props = xen_device_props;
+    dev_class->bus_type = TYPE_XEN_BUS;
+}
+
+static const TypeInfo xen_device_type_info = {
+    .name = TYPE_XEN_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(XenDevice),
+    .abstract = true,
+    .class_size = sizeof(XenDeviceClass),
+    .class_init = xen_device_class_init,
+};
+
+typedef struct XenBridge {
+    SysBusDevice busdev;
+} XenBridge;
+
+#define TYPE_XEN_BRIDGE "xen-bridge"
+
+static const TypeInfo xen_bridge_type_info = {
+    .name = TYPE_XEN_BRIDGE,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(XenBridge),
+};
+
+static void xen_register_types(void)
+{
+    type_register_static(&xen_bridge_type_info);
+    type_register_static(&xen_bus_type_info);
+    type_register_static(&xen_device_type_info);
+}
+
+type_init(xen_register_types)
+
+void xen_bus_init(void)
+{
+    DeviceState *dev = qdev_create(NULL, TYPE_XEN_BRIDGE);
+    BusState *bus = qbus_create(TYPE_XEN_BUS, dev, NULL);
+
+    qdev_init_nofail(dev);
+    qbus_set_bus_hotplug_handler(bus, &error_abort);
+}
diff --git a/hw/xen/xen-common.c b/hw/xen/xen-common.c
index 18a9045556..0e9e58f04d 100644
--- a/hw/xen/xen-common.c
+++ b/hw/xen/xen-common.c
@@ -10,7 +10,7 @@
 
 #include "qemu/osdep.h"
 #include "qemu/error-report.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "chardev/char.h"
 #include "sysemu/accel.h"
 #include "migration/misc.h"
diff --git a/hw/xen/xen_backend.c b/hw/xen/xen-legacy-backend.c
index 0bc6b1de60..36fd1e9b09 100644
--- a/hw/xen/xen_backend.c
+++ b/hw/xen/xen-legacy-backend.c
@@ -30,7 +30,7 @@
 #include "hw/boards.h"
 #include "qemu/log.h"
 #include "qapi/error.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "hw/xen/xen_pvdev.h"
 #include "monitor/qdev.h"
 
@@ -42,49 +42,54 @@ BusState *xen_sysbus;
 /* ------------------------------------------------------------- */
 
 /* public */
-struct xs_handle *xenstore = NULL;
+struct xs_handle *xenstore;
 const char *xen_protocol;
 
 /* private */
 static bool xen_feature_grant_copy;
 static int debug;
 
-int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val)
+int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node,
+                          const char *val)
 {
     return xenstore_write_str(xendev->be, node, val);
 }
 
-int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival)
+int xenstore_write_be_int(struct XenLegacyDevice *xendev, const char *node,
+                          int ival)
 {
     return xenstore_write_int(xendev->be, node, ival);
 }
 
-int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival)
+int xenstore_write_be_int64(struct XenLegacyDevice *xendev, const char *node,
+                            int64_t ival)
 {
     return xenstore_write_int64(xendev->be, node, ival);
 }
 
-char *xenstore_read_be_str(struct XenDevice *xendev, const char *node)
+char *xenstore_read_be_str(struct XenLegacyDevice *xendev, const char *node)
 {
     return xenstore_read_str(xendev->be, node);
 }
 
-int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival)
+int xenstore_read_be_int(struct XenLegacyDevice *xendev, const char *node,
+                         int *ival)
 {
     return xenstore_read_int(xendev->be, node, ival);
 }
 
-char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node)
+char *xenstore_read_fe_str(struct XenLegacyDevice *xendev, const char *node)
 {
     return xenstore_read_str(xendev->fe, node);
 }
 
-int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival)
+int xenstore_read_fe_int(struct XenLegacyDevice *xendev, const char *node,
+                         int *ival)
 {
     return xenstore_read_int(xendev->fe, node, ival);
 }
 
-int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node,
+int xenstore_read_fe_uint64(struct XenLegacyDevice *xendev, const char *node,
                             uint64_t *uval)
 {
     return xenstore_read_uint64(xendev->fe, node, uval);
@@ -92,7 +97,7 @@ int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node,
 
 /* ------------------------------------------------------------- */
 
-int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state)
+int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state)
 {
     int rc;
 
@@ -106,7 +111,7 @@ int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state)
     return 0;
 }
 
-void xen_be_set_max_grant_refs(struct XenDevice *xendev,
+void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev,
                                unsigned int nr_refs)
 {
     assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);
@@ -117,7 +122,7 @@ void xen_be_set_max_grant_refs(struct XenDevice *xendev,
     }
 }
 
-void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs,
+void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs,
                             unsigned int nr_refs, int prot)
 {
     void *ptr;
@@ -135,7 +140,7 @@ void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs,
     return ptr;
 }
 
-void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr,
+void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr,
                              unsigned int nr_refs)
 {
     assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV);
@@ -146,7 +151,7 @@ void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr,
     }
 }
 
-static int compat_copy_grant_refs(struct XenDevice *xendev,
+static int compat_copy_grant_refs(struct XenLegacyDevice *xendev,
                                   bool to_domain,
                                   XenGrantCopySegment segs[],
                                   unsigned int nr_segs)
@@ -195,7 +200,7 @@ static int compat_copy_grant_refs(struct XenDevice *xendev,
     return 0;
 }
 
-int xen_be_copy_grant_refs(struct XenDevice *xendev,
+int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev,
                            bool to_domain,
                            XenGrantCopySegment segs[],
                            unsigned int nr_segs)
@@ -259,10 +264,11 @@ int xen_be_copy_grant_refs(struct XenDevice *xendev,
 /*
  * get xen backend device, allocate a new one if it doesn't exist.
  */
-static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
-                                           struct XenDevOps *ops)
+static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom,
+                                                 int dev,
+                                                 struct XenDevOps *ops)
 {
-    struct XenDevice *xendev;
+    struct XenLegacyDevice *xendev;
 
     xendev = xen_pv_find_xendev(type, dom, dev);
     if (xendev) {
@@ -314,7 +320,8 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
  * Node specifies the changed field.  node = NULL means
  * update all fields (used for initialization).
  */
-static void xen_be_backend_changed(struct XenDevice *xendev, const char *node)
+static void xen_be_backend_changed(struct XenLegacyDevice *xendev,
+                                   const char *node)
 {
     if (node == NULL  ||  strcmp(node, "online") == 0) {
         if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) {
@@ -330,7 +337,8 @@ static void xen_be_backend_changed(struct XenDevice *xendev, const char *node)
     }
 }
 
-static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node)
+static void xen_be_frontend_changed(struct XenLegacyDevice *xendev,
+                                    const char *node)
 {
     int fe_state;
 
@@ -373,7 +381,7 @@ static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node)
  * only affects the xendev->be_state variable as xenbus should
  * already be put into that state by xend.
  */
-static int xen_be_try_setup(struct XenDevice *xendev)
+static int xen_be_try_setup(struct XenLegacyDevice *xendev)
 {
     char token[XEN_BUFSIZE];
     int be_state;
@@ -417,7 +425,7 @@ static int xen_be_try_setup(struct XenDevice *xendev)
  *
  * Goes to InitWait on success.
  */
-static int xen_be_try_init(struct XenDevice *xendev)
+static int xen_be_try_init(struct XenLegacyDevice *xendev)
 {
     int rc = 0;
 
@@ -446,7 +454,7 @@ static int xen_be_try_init(struct XenDevice *xendev)
  *
  * Goes to Connected on success.
  */
-static int xen_be_try_initialise(struct XenDevice *xendev)
+static int xen_be_try_initialise(struct XenLegacyDevice *xendev)
 {
     int rc = 0;
 
@@ -487,7 +495,7 @@ static int xen_be_try_initialise(struct XenDevice *xendev)
  * frontend being Connected.  Note that this may be called more
  * than once since the backend state is not modified.
  */
-static void xen_be_try_connected(struct XenDevice *xendev)
+static void xen_be_try_connected(struct XenLegacyDevice *xendev)
 {
     if (!xendev->ops->connected) {
         return;
@@ -510,7 +518,8 @@ static void xen_be_try_connected(struct XenDevice *xendev)
  *
  * Goes to Closed when done.
  */
-static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state)
+static void xen_be_disconnect(struct XenLegacyDevice *xendev,
+                              enum xenbus_state state)
 {
     if (xendev->be_state != XenbusStateClosing &&
         xendev->be_state != XenbusStateClosed  &&
@@ -529,7 +538,7 @@ static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state)
 /*
  * Try to reset xendev, for reconnection by another frontend instance.
  */
-static int xen_be_try_reset(struct XenDevice *xendev)
+static int xen_be_try_reset(struct XenLegacyDevice *xendev)
 {
     if (xendev->fe_state != XenbusStateInitialising) {
         return -1;
@@ -543,7 +552,7 @@ static int xen_be_try_reset(struct XenDevice *xendev)
 /*
  * state change dispatcher function
  */
-void xen_be_check_state(struct XenDevice *xendev)
+void xen_be_check_state(struct XenLegacyDevice *xendev)
 {
     int rc = 0;
 
@@ -587,7 +596,7 @@ void xen_be_check_state(struct XenDevice *xendev)
 
 static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
 {
-    struct XenDevice *xendev;
+    struct XenLegacyDevice *xendev;
     char path[XEN_BUFSIZE], token[XEN_BUFSIZE];
     char **dev = NULL;
     unsigned int cdev, j;
@@ -620,7 +629,7 @@ static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
 void xenstore_update_be(char *watch, char *type, int dom,
                         struct XenDevOps *ops)
 {
-    struct XenDevice *xendev;
+    struct XenLegacyDevice *xendev;
     char path[XEN_BUFSIZE], *bepath;
     unsigned int len, dev;
 
@@ -628,9 +637,9 @@ void xenstore_update_be(char *watch, char *type, int dom,
     if (strncmp(path, watch, len) != 0) {
         return;
     }
-    if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) {
+    if (sscanf(watch + len, "/%u/%255s", &dev, path) != 2) {
         strcpy(path, "");
-        if (sscanf(watch+len, "/%u", &dev) != 1) {
+        if (sscanf(watch + len, "/%u", &dev) != 1) {
             dev = -1;
         }
     }
@@ -651,7 +660,7 @@ void xenstore_update_be(char *watch, char *type, int dom,
     }
 }
 
-void xenstore_update_fe(char *watch, struct XenDevice *xendev)
+void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev)
 {
     char *node;
     unsigned int len;
@@ -744,7 +753,6 @@ void xen_be_register_common(void)
 
     xen_be_register("console", &xen_console_ops);
     xen_be_register("vkbd", &xen_kbdmouse_ops);
-    xen_be_register("qdisk", &xen_blkdev_ops);
 #ifdef CONFIG_VIRTFS
     xen_be_register("9pfs", &xen_9pfs_ops);
 #endif
@@ -753,7 +761,7 @@ void xen_be_register_common(void)
 #endif
 }
 
-int xen_be_bind_evtchn(struct XenDevice *xendev)
+int xen_be_bind_evtchn(struct XenLegacyDevice *xendev)
 {
     if (xendev->local_port != -1) {
         return 0;
@@ -789,7 +797,7 @@ static const TypeInfo xendev_type_info = {
     .name          = TYPE_XENBACKEND,
     .parent        = TYPE_XENSYSDEV,
     .class_init    = xendev_class_init,
-    .instance_size = sizeof(struct XenDevice),
+    .instance_size = sizeof(struct XenLegacyDevice),
 };
 
 static void xen_sysbus_class_init(ObjectClass *klass, void *data)
diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c
index 3500d88a3e..315dbc9c51 100644
--- a/hw/xen/xen_devconfig.c
+++ b/hw/xen/xen_devconfig.c
@@ -1,5 +1,5 @@
 #include "qemu/osdep.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "qemu/option.h"
 #include "sysemu/blockdev.h"
 
diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c
index f1f3a3727c..5539d56c3a 100644
--- a/hw/xen/xen_pt.c
+++ b/hw/xen/xen_pt.c
@@ -59,7 +59,7 @@
 #include "hw/pci/pci.h"
 #include "hw/xen/xen.h"
 #include "hw/i386/pc.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "xen_pt.h"
 #include "qemu/range.h"
 #include "exec/address-spaces.h"
@@ -847,6 +847,12 @@ static void xen_pt_realize(PCIDevice *d, Error **errp)
     }
 
     machine_irq = s->real_device.irq;
+    if (machine_irq == 0) {
+        XEN_PT_LOG(d, "machine irq is 0\n");
+        cmd |= PCI_COMMAND_INTX_DISABLE;
+        goto out;
+    }
+
     rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
     if (rc < 0) {
         error_setg_errno(errp, errno, "Mapping machine irq %u to"
diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c
index 47f9010c75..31ec5add1d 100644
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -15,7 +15,7 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qemu/timer.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "xen_pt.h"
 
 #define XEN_PT_MERGE_VALUE(value, data, val_mask) \
@@ -300,7 +300,9 @@ static int xen_pt_irqpin_reg_init(XenPCIPassthroughState *s,
                                   XenPTRegInfo *reg, uint32_t real_offset,
                                   uint32_t *data)
 {
-    *data = xen_pt_pci_read_intx(s);
+    if (s->real_device.irq) {
+        *data = xen_pt_pci_read_intx(s);
+    }
     return 0;
 }
 
diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c
index 135c8df1e7..b69732729b 100644
--- a/hw/xen/xen_pt_graphics.c
+++ b/hw/xen/xen_pt_graphics.c
@@ -5,7 +5,7 @@
 #include "qapi/error.h"
 #include "xen_pt.h"
 #include "xen-host-pci-device.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 
 static unsigned long igd_guest_opregion;
 static unsigned long igd_host_opregion;
@@ -185,8 +185,19 @@ void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev,
         return;
     }
 
+    if (bios_size < sizeof(struct rom_header)) {
+        error_setg(errp, "VGA: VBIOS image corrupt (too small)");
+        return;
+    }
+
     /* Currently we fixed this address as a primary. */
     rom = (struct rom_header *)bios;
+
+    if (rom->pcioffset + sizeof(struct pci_data) > bios_size) {
+        error_setg(errp, "VGA: VBIOS image corrupt (bad pcioffset field)");
+        return;
+    }
+
     pd = (void *)(bios + (unsigned char)rom->pcioffset);
 
     /* We may need to fixup Device Identification. */
@@ -194,6 +205,11 @@ void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev,
         pd->device = s->real_device.device_id;
 
         len = rom->size * 512;
+        if (len > bios_size) {
+            error_setg(errp, "VGA: VBIOS image corrupt (bad size field)");
+            return;
+        }
+
         /* Then adjust the bios checksum */
         for (c = (char *)bios; c < ((char *)bios + len); c++) {
             checksum += *c;
diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c
index cc514f9157..fb4b887b92 100644
--- a/hw/xen/xen_pt_msi.c
+++ b/hw/xen/xen_pt_msi.c
@@ -11,7 +11,7 @@
 
 #include "qemu/osdep.h"
 
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "xen_pt.h"
 #include "hw/i386/apic-msidef.h"
 
diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c
index f026556f62..6ef09cbf9d 100644
--- a/hw/xen/xen_pvdev.c
+++ b/hw/xen/xen_pvdev.c
@@ -20,7 +20,7 @@
 #include "qemu/osdep.h"
 #include "qemu/log.h"
 #include "hw/qdev-core.h"
-#include "hw/xen/xen_backend.h"
+#include "hw/xen/xen-legacy-backend.h"
 #include "hw/xen/xen_pvdev.h"
 
 /* private */
@@ -34,7 +34,7 @@ struct xs_dirs {
 static QTAILQ_HEAD(, xs_dirs) xs_cleanup =
     QTAILQ_HEAD_INITIALIZER(xs_cleanup);
 
-static QTAILQ_HEAD(, XenDevice) xendevs =
+static QTAILQ_HEAD(, XenLegacyDevice) xendevs =
     QTAILQ_HEAD_INITIALIZER(xendevs);
 
 /* ------------------------------------------------------------- */
@@ -195,7 +195,7 @@ const char *xenbus_strstate(enum xenbus_state state)
  *  2 == noisy debug messages (logfile only).
  *  3 == will flood your log (logfile only).
  */
-void xen_pv_printf(struct XenDevice *xendev, int msg_level,
+void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level,
                    const char *fmt, ...)
 {
     va_list args;
@@ -230,7 +230,7 @@ void xen_pv_printf(struct XenDevice *xendev, int msg_level,
 
 void xen_pv_evtchn_event(void *opaque)
 {
-    struct XenDevice *xendev = opaque;
+    struct XenLegacyDevice *xendev = opaque;
     evtchn_port_t port;
 
     port = xenevtchn_pending(xendev->evtchndev);
@@ -247,7 +247,7 @@ void xen_pv_evtchn_event(void *opaque)
     }
 }
 
-void xen_pv_unbind_evtchn(struct XenDevice *xendev)
+void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev)
 {
     if (xendev->local_port == -1) {
         return;
@@ -258,16 +258,16 @@ void xen_pv_unbind_evtchn(struct XenDevice *xendev)
     xendev->local_port = -1;
 }
 
-int xen_pv_send_notify(struct XenDevice *xendev)
+int xen_pv_send_notify(struct XenLegacyDevice *xendev)
 {
     return xenevtchn_notify(xendev->evtchndev, xendev->local_port);
 }
 
 /* ------------------------------------------------------------- */
 
-struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev)
+struct XenLegacyDevice *xen_pv_find_xendev(const char *type, int dom, int dev)
 {
-    struct XenDevice *xendev;
+    struct XenLegacyDevice *xendev;
 
     QTAILQ_FOREACH(xendev, &xendevs, next) {
         if (xendev->dom != dom) {
@@ -287,7 +287,7 @@ struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev)
 /*
  * release xen backend device.
  */
-void xen_pv_del_xendev(struct XenDevice *xendev)
+void xen_pv_del_xendev(struct XenLegacyDevice *xendev)
 {
     if (xendev->ops->free) {
         xendev->ops->free(xendev);
@@ -312,7 +312,7 @@ void xen_pv_del_xendev(struct XenDevice *xendev)
     qdev_unplug(&xendev->qdev, NULL);
 }
 
-void xen_pv_insert_xendev(struct XenDevice *xendev)
+void xen_pv_insert_xendev(struct XenLegacyDevice *xendev)
 {
     QTAILQ_INSERT_TAIL(&xendevs, xendev, next);
 }
diff --git a/hw/xenpv/Makefile.objs b/hw/xenpv/Makefile.objs
index bbf5873fd1..8bfa4586ab 100644
--- a/hw/xenpv/Makefile.objs
+++ b/hw/xenpv/Makefile.objs
@@ -1,4 +1,2 @@
 # Xen PV machine support
 obj-$(CONFIG_XEN) += xen_machine_pv.o
-# Xen PV machine builder support
-obj-$(CONFIG_XEN_PV_DOMAIN_BUILD) += xen_domainbuild.o
diff --git a/hw/xenpv/xen_domainbuild.c b/hw/xenpv/xen_domainbuild.c
deleted file mode 100644
index 2859280a6a..0000000000
--- a/hw/xenpv/xen_domainbuild.c
+++ /dev/null
@@ -1,299 +0,0 @@
-#include "qemu/osdep.h"
-#include "qemu/units.h"
-#include "hw/xen/xen_backend.h"
-#include "xen_domainbuild.h"
-#include "qemu/timer.h"
-#include "qemu/log.h"
-
-#include <xenguest.h>
-
-static int xenstore_domain_mkdir(char *path)
-{
-    struct xs_permissions perms_ro[] = {{
-            .id    = 0, /* set owner: dom0 */
-        },{
-            .id    = xen_domid,
-            .perms = XS_PERM_READ,
-        }};
-    struct xs_permissions perms_rw[] = {{
-            .id    = 0, /* set owner: dom0 */
-        },{
-            .id    = xen_domid,
-            .perms = XS_PERM_READ | XS_PERM_WRITE,
-        }};
-    const char *writable[] = { "device", "control", "error", NULL };
-    char subpath[256];
-    int i;
-
-    if (!xs_mkdir(xenstore, 0, path)) {
-        fprintf(stderr, "%s: xs_mkdir %s: failed\n", __func__, path);
-        return -1;
-    }
-    if (!xs_set_permissions(xenstore, 0, path, perms_ro, 2)) {
-        fprintf(stderr, "%s: xs_set_permissions failed\n", __func__);
-        return -1;
-    }
-
-    for (i = 0; writable[i]; i++) {
-        snprintf(subpath, sizeof(subpath), "%s/%s", path, writable[i]);
-        if (!xs_mkdir(xenstore, 0, subpath)) {
-            fprintf(stderr, "%s: xs_mkdir %s: failed\n", __func__, subpath);
-            return -1;
-        }
-        if (!xs_set_permissions(xenstore, 0, subpath, perms_rw, 2)) {
-            fprintf(stderr, "%s: xs_set_permissions failed\n", __func__);
-            return -1;
-        }
-    }
-    return 0;
-}
-
-int xenstore_domain_init1(const char *kernel, const char *ramdisk,
-                          const char *cmdline)
-{
-    char *dom, uuid_string[42], vm[256], path[256];
-    int i;
-
-    qemu_uuid_unparse(&qemu_uuid, uuid_string);
-    dom = xs_get_domain_path(xenstore, xen_domid);
-    snprintf(vm,  sizeof(vm),  "/vm/%s", uuid_string);
-
-    xenstore_domain_mkdir(dom);
-
-    xenstore_write_str(vm, "image/ostype",  "linux");
-    if (kernel)
-        xenstore_write_str(vm, "image/kernel",  kernel);
-    if (ramdisk)
-        xenstore_write_str(vm, "image/ramdisk", ramdisk);
-    if (cmdline)
-        xenstore_write_str(vm, "image/cmdline", cmdline);
-
-    /* name + id */
-    xenstore_write_str(vm,  "name",   qemu_name ? qemu_name : "no-name");
-    xenstore_write_str(vm,  "uuid",   uuid_string);
-    xenstore_write_str(dom, "name",   qemu_name ? qemu_name : "no-name");
-    xenstore_write_int(dom, "domid",  xen_domid);
-    xenstore_write_str(dom, "vm",     vm);
-
-    /* memory */
-    xenstore_write_int(dom, "memory/target", ram_size / KiB);
-    xenstore_write_int(vm, "memory",         ram_size / MiB);
-    xenstore_write_int(vm, "maxmem",         ram_size / MiB);
-
-    /* cpus */
-    for (i = 0; i < smp_cpus; i++) {
-        snprintf(path, sizeof(path), "cpu/%d/availability",i);
-        xenstore_write_str(dom, path, "online");
-    }
-    xenstore_write_int(vm, "vcpu_avail",  smp_cpus);
-    xenstore_write_int(vm, "vcpus",       smp_cpus);
-
-    /* vnc password */
-    xenstore_write_str(vm, "vncpassword", "" /* FIXME */);
-
-    free(dom);
-    return 0;
-}
-
-int xenstore_domain_init2(int xenstore_port, int xenstore_mfn,
-                          int console_port, int console_mfn)
-{
-    char *dom;
-
-    dom = xs_get_domain_path(xenstore, xen_domid);
-
-    /* signal new domain */
-    xs_introduce_domain(xenstore,
-                        xen_domid,
-                        xenstore_mfn,
-                        xenstore_port);
-
-    /* xenstore */
-    xenstore_write_int(dom, "store/ring-ref",   xenstore_mfn);
-    xenstore_write_int(dom, "store/port",       xenstore_port);
-
-    /* console */
-    xenstore_write_str(dom, "console/type",     "ioemu");
-    xenstore_write_int(dom, "console/limit",    128 * KiB);
-    xenstore_write_int(dom, "console/ring-ref", console_mfn);
-    xenstore_write_int(dom, "console/port",     console_port);
-    xen_config_dev_console(0);
-
-    free(dom);
-    return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-static QEMUTimer *xen_poll;
-
-/* check domain state once per second */
-static void xen_domain_poll(void *opaque)
-{
-    struct xc_dominfo info;
-    int rc;
-
-    rc = xc_domain_getinfo(xen_xc, xen_domid, 1, &info);
-    if ((rc != 1) || (info.domid != xen_domid)) {
-        qemu_log("xen: domain %d is gone\n", xen_domid);
-        goto quit;
-    }
-    if (info.dying) {
-        qemu_log("xen: domain %d is dying (%s%s)\n", xen_domid,
-                 info.crashed  ? "crashed"  : "",
-                 info.shutdown ? "shutdown" : "");
-        goto quit;
-    }
-
-    timer_mod(xen_poll, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
-    return;
-
-quit:
-    qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
-}
-
-static int xen_domain_watcher(void)
-{
-    int qemu_running = 1;
-    int fd[2], i, n, rc;
-    char byte;
-
-    if (pipe(fd) != 0) {
-        qemu_log("%s: Huh? pipe error: %s\n", __func__, strerror(errno));
-        return -1;
-    }
-    if (fork() != 0)
-        return 0; /* not child */
-
-    /* close all file handles, except stdio/out/err,
-     * our watch pipe and the xen interface handle */
-    n = getdtablesize();
-    for (i = 3; i < n; i++) {
-        if (i == fd[0])
-            continue;
-        close(i);
-    }
-
-    /*
-     * Reopen xc interface, since the original is unsafe after fork
-     * and was closed above.
-     */
-    xen_xc = xc_interface_open(0, 0, 0);
-
-    /* ignore term signals */
-    signal(SIGINT,  SIG_IGN);
-    signal(SIGTERM, SIG_IGN);
-
-    /* wait for qemu exiting */
-    while (qemu_running) {
-        rc = read(fd[0], &byte, 1);
-        switch (rc) {
-        case -1:
-            if (errno == EINTR)
-                continue;
-            qemu_log("%s: Huh? read error: %s\n", __func__, strerror(errno));
-            qemu_running = 0;
-            break;
-        case 0:
-            /* EOF -> qemu exited */
-            qemu_running = 0;
-            break;
-        default:
-            qemu_log("%s: Huh? data on the watch pipe?\n", __func__);
-            break;
-        }
-    }
-
-    /* cleanup */
-    qemu_log("%s: destroy domain %d\n", __func__, xen_domid);
-    xc_domain_destroy(xen_xc, xen_domid);
-    _exit(0);
-}
-
-/* normal cleanup */
-static void xen_domain_cleanup(void)
-{
-    char *dom;
-
-    dom = xs_get_domain_path(xenstore, xen_domid);
-    if (dom) {
-        xs_rm(xenstore, 0, dom);
-        free(dom);
-    }
-    xs_release_domain(xenstore, xen_domid);
-}
-
-int xen_domain_build_pv(const char *kernel, const char *ramdisk,
-                        const char *cmdline)
-{
-    uint32_t ssidref = 0;
-    uint32_t flags = 0;
-    xen_domain_handle_t uuid;
-    unsigned int xenstore_port = 0, console_port = 0;
-    unsigned long xenstore_mfn = 0, console_mfn = 0;
-    int rc;
-
-    memcpy(uuid, &qemu_uuid, sizeof(uuid));
-    rc = xen_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid);
-    if (rc < 0) {
-        fprintf(stderr, "xen: xc_domain_create() failed\n");
-        goto err;
-    }
-    qemu_log("xen: created domain %d\n", xen_domid);
-    atexit(xen_domain_cleanup);
-    if (xen_domain_watcher() == -1) {
-        goto err;
-    }
-
-    xenstore_domain_init1(kernel, ramdisk, cmdline);
-
-    rc = xc_domain_max_vcpus(xen_xc, xen_domid, smp_cpus);
-    if (rc < 0) {
-        fprintf(stderr, "xen: xc_domain_max_vcpus() failed\n");
-        goto err;
-    }
-
-#if 0
-    rc = xc_domain_setcpuweight(xen_xc, xen_domid, 256);
-    if (rc < 0) {
-        fprintf(stderr, "xen: xc_domain_setcpuweight() failed\n");
-        goto err;
-    }
-#endif
-
-    rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size / KiB);
-    if (rc < 0) {
-        fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n");
-        goto err;
-    }
-
-    xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0);
-    console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0);
-
-    rc = xc_linux_build(xen_xc, xen_domid, ram_size / MiB,
-                        kernel, ramdisk, cmdline,
-                        0, flags,
-                        xenstore_port, &xenstore_mfn,
-                        console_port, &console_mfn);
-    if (rc < 0) {
-        fprintf(stderr, "xen: xc_linux_build() failed\n");
-        goto err;
-    }
-
-    xenstore_domain_init2(xenstore_port, xenstore_mfn,
-                          console_port, console_mfn);
-
-    qemu_log("xen: unpausing domain %d\n", xen_domid);
-    rc = xc_domain_unpause(xen_xc, xen_domid);
-    if (rc < 0) {
-        fprintf(stderr, "xen: xc_domain_unpause() failed\n");
-        goto err;
-    }
-
-    xen_poll = timer_new_ms(QEMU_CLOCK_REALTIME, xen_domain_poll, NULL);
-    timer_mod(xen_poll, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
-    return 0;
-
-err:
-    return -1;
-}
diff --git a/hw/xenpv/xen_domainbuild.h b/hw/xenpv/xen_domainbuild.h
deleted file mode 100644
index 652d9b410f..0000000000
--- a/hw/xenpv/xen_domainbuild.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef QEMU_HW_XEN_DOMAINBUILD_H
-#define QEMU_HW_XEN_DOMAINBUILD_H
-
-#include "hw/xen/xen_common.h"
-
-int xenstore_domain_init1(const char *kernel, const char *ramdisk,
-                          const char *cmdline);
-int xenstore_domain_init2(int xenstore_port, int xenstore_mfn,
-                          int console_port, int console_mfn);
-int xen_domain_build_pv(const char *kernel, const char *ramdisk,
-                        const char *cmdline);
-
-#endif /* QEMU_HW_XEN_DOMAINBUILD_H */
diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c
index 44d67b87c4..dcaf2a01a3 100644
--- a/hw/xenpv/xen_machine_pv.c
+++ b/hw/xenpv/xen_machine_pv.c
@@ -26,8 +26,8 @@
 #include "qemu/error-report.h"
 #include "hw/hw.h"
 #include "hw/boards.h"
-#include "hw/xen/xen_backend.h"
-#include "xen_domainbuild.h"
+#include "hw/xen/xen-legacy-backend.h"
+#include "hw/xen/xen-bus.h"
 #include "sysemu/block-backend.h"
 
 static void xen_init_pv(MachineState *machine)
@@ -43,21 +43,8 @@ static void xen_init_pv(MachineState *machine)
 
     switch (xen_mode) {
     case XEN_ATTACH:
-        /* nothing to do, xend handles everything */
+        /* nothing to do, libxl handles everything */
         break;
-#ifdef CONFIG_XEN_PV_DOMAIN_BUILD
-    case XEN_CREATE: {
-        const char *kernel_filename = machine->kernel_filename;
-        const char *kernel_cmdline = machine->kernel_cmdline;
-        const char *initrd_filename = machine->initrd_filename;
-        if (xen_domain_build_pv(kernel_filename, initrd_filename,
-                                kernel_cmdline) < 0) {
-            error_report("xen pv domain creation failed");
-            exit(1);
-        }
-        break;
-    }
-#endif
     case XEN_EMULATE:
         error_report("xen emulation not implemented (yet)");
         exit(1);
@@ -93,6 +80,8 @@ static void xen_init_pv(MachineState *machine)
         xen_config_dev_nic(nd_table + i);
     }
 
+    xen_bus_init();
+
     /* config cleanup hook */
     atexit(xen_config_cleanup);
 }
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 65402d3396..1971b55789 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -295,9 +295,10 @@ typedef struct NBDExport NBDExport;
 typedef struct NBDClient NBDClient;
 
 NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
-                          uint16_t nbdflags, void (*close)(NBDExport *),
-                          bool writethrough, BlockBackend *on_eject_blk,
-                          Error **errp);
+                          const char *name, const char *description,
+                          const char *bitmap, uint16_t nbdflags,
+                          void (*close)(NBDExport *), bool writethrough,
+                          BlockBackend *on_eject_blk, Error **errp);
 void nbd_export_close(NBDExport *exp);
 void nbd_export_remove(NBDExport *exp, NbdServerRemoveMode mode, Error **errp);
 void nbd_export_get(NBDExport *exp);
@@ -306,8 +307,6 @@ void nbd_export_put(NBDExport *exp);
 BlockBackend *nbd_export_get_blockdev(NBDExport *exp);
 
 NBDExport *nbd_export_find(const char *name);
-void nbd_export_set_name(NBDExport *exp, const char *name);
-void nbd_export_set_description(NBDExport *exp, const char *description);
 void nbd_export_close_all(void);
 
 void nbd_client_new(QIOChannelSocket *sioc,
@@ -320,9 +319,6 @@ void nbd_client_put(NBDClient *client);
 void nbd_server_start(SocketAddress *addr, const char *tls_creds,
                       Error **errp);
 
-void nbd_export_bitmap(NBDExport *exp, const char *bitmap,
-                       const char *bitmap_export_name, Error **errp);
-
 /* nbd_read
  * Reads @size bytes from @ioc. Returns 0 on success.
  */
diff --git a/include/hw/xen/xen-backend.h b/include/hw/xen/xen-backend.h
new file mode 100644
index 0000000000..010d712638
--- /dev/null
+++ b/include/hw/xen/xen-backend.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018  Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_XEN_BACKEND_H
+#define HW_XEN_BACKEND_H
+
+#include "hw/xen/xen-bus.h"
+
+typedef struct XenBackendInstance XenBackendInstance;
+
+typedef void (*XenBackendDeviceCreate)(XenBackendInstance *backend,
+                                       QDict *opts, Error **errp);
+typedef void (*XenBackendDeviceDestroy)(XenBackendInstance *backend,
+                                        Error **errp);
+
+typedef struct XenBackendInfo {
+    const char *type;
+    XenBackendDeviceCreate create;
+    XenBackendDeviceDestroy destroy;
+} XenBackendInfo;
+
+XenBus *xen_backend_get_bus(XenBackendInstance *backend);
+const char *xen_backend_get_name(XenBackendInstance *backend);
+
+void xen_backend_set_device(XenBackendInstance *backend,
+                            XenDevice *xendevice);
+XenDevice *xen_backend_get_device(XenBackendInstance *backend);
+
+void xen_backend_register(const XenBackendInfo *info);
+
+void xen_backend_device_create(XenBus *xenbus, const char *type,
+                               const char *name, QDict *opts, Error **errp);
+bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp);
+
+#endif /* HW_XEN_BACKEND_H */
diff --git a/include/hw/xen/xen-block.h b/include/hw/xen/xen-block.h
new file mode 100644
index 0000000000..11d351b4b3
--- /dev/null
+++ b/include/hw/xen/xen-block.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2018  Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_XEN_BLOCK_H
+#define HW_XEN_BLOCK_H
+
+#include "hw/xen/xen-bus.h"
+#include "hw/block/block.h"
+#include "hw/block/dataplane/xen-block.h"
+#include "sysemu/iothread.h"
+
+typedef enum XenBlockVdevType {
+    XEN_BLOCK_VDEV_TYPE_INVALID,
+    XEN_BLOCK_VDEV_TYPE_DP,
+    XEN_BLOCK_VDEV_TYPE_XVD,
+    XEN_BLOCK_VDEV_TYPE_HD,
+    XEN_BLOCK_VDEV_TYPE_SD,
+    XEN_BLOCK_VDEV_TYPE__MAX
+} XenBlockVdevType;
+
+typedef struct XenBlockVdev {
+    XenBlockVdevType type;
+    unsigned long disk;
+    unsigned long partition;
+    unsigned long number;
+} XenBlockVdev;
+
+
+typedef struct XenBlockProperties {
+    XenBlockVdev vdev;
+    BlockConf conf;
+    unsigned int max_ring_page_order;
+    IOThread *iothread;
+} XenBlockProperties;
+
+typedef struct XenBlockDrive {
+    char *id;
+    char *node_name;
+} XenBlockDrive;
+
+typedef struct XenBlockIOThread {
+    char *id;
+} XenBlockIOThread;
+
+typedef struct XenBlockDevice {
+    XenDevice xendev;
+    XenBlockProperties props;
+    const char *device_type;
+    unsigned int info;
+    XenBlockDataPlane *dataplane;
+    XenBlockDrive *drive;
+    XenBlockIOThread *iothread;
+} XenBlockDevice;
+
+typedef void (*XenBlockDeviceRealize)(XenBlockDevice *blockdev, Error **errp);
+typedef void (*XenBlockDeviceUnrealize)(XenBlockDevice *blockdev, Error **errp);
+
+typedef struct XenBlockDeviceClass {
+    /*< private >*/
+    XenDeviceClass parent_class;
+    /*< public >*/
+    XenBlockDeviceRealize realize;
+    XenBlockDeviceUnrealize unrealize;
+} XenBlockDeviceClass;
+
+#define TYPE_XEN_BLOCK_DEVICE  "xen-block"
+#define XEN_BLOCK_DEVICE(obj) \
+     OBJECT_CHECK(XenBlockDevice, (obj), TYPE_XEN_BLOCK_DEVICE)
+#define XEN_BLOCK_DEVICE_CLASS(class) \
+     OBJECT_CLASS_CHECK(XenBlockDeviceClass, (class), TYPE_XEN_BLOCK_DEVICE)
+#define XEN_BLOCK_DEVICE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(XenBlockDeviceClass, (obj), TYPE_XEN_BLOCK_DEVICE)
+
+typedef struct XenDiskDevice {
+    XenBlockDevice blockdev;
+} XenDiskDevice;
+
+#define TYPE_XEN_DISK_DEVICE  "xen-disk"
+#define XEN_DISK_DEVICE(obj) \
+     OBJECT_CHECK(XenDiskDevice, (obj), TYPE_XEN_DISK_DEVICE)
+
+typedef struct XenCDRomDevice {
+    XenBlockDevice blockdev;
+} XenCDRomDevice;
+
+#define TYPE_XEN_CDROM_DEVICE  "xen-cdrom"
+#define XEN_CDROM_DEVICE(obj) \
+     OBJECT_CHECK(XenCDRomDevice, (obj), TYPE_XEN_CDROM_DEVICE)
+
+#endif /* HW_XEN_BLOCK_H */
diff --git a/include/hw/xen/xen-bus-helper.h b/include/hw/xen/xen-bus-helper.h
new file mode 100644
index 0000000000..4c0f747445
--- /dev/null
+++ b/include/hw/xen/xen-bus-helper.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018  Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_XEN_BUS_HELPER_H
+#define HW_XEN_BUS_HELPER_H
+
+#include "hw/xen/xen_common.h"
+
+const char *xs_strstate(enum xenbus_state state);
+
+void xs_node_create(struct xs_handle *xsh,  xs_transaction_t tid,
+                    const char *node, struct xs_permissions perms[],
+                    unsigned int nr_perms, Error **errp);
+void xs_node_destroy(struct xs_handle *xsh,  xs_transaction_t tid,
+                     const char *node, Error **errp);
+
+/* Write to node/key unless node is empty, in which case write to key */
+void xs_node_vprintf(struct xs_handle *xsh,  xs_transaction_t tid,
+                     const char *node, const char *key, Error **errp,
+                     const char *fmt, va_list ap)
+    GCC_FMT_ATTR(6, 0);
+void xs_node_printf(struct xs_handle *xsh,  xs_transaction_t tid,
+                    const char *node, const char *key, Error **errp,
+                    const char *fmt, ...)
+    GCC_FMT_ATTR(6, 7);
+
+/* Read from node/key unless node is empty, in which case read from key */
+int xs_node_vscanf(struct xs_handle *xsh,  xs_transaction_t tid,
+                   const char *node, const char *key, Error **errp,
+                   const char *fmt, va_list ap);
+int xs_node_scanf(struct xs_handle *xsh,  xs_transaction_t tid,
+                  const char *node, const char *key, Error **errp,
+                  const char *fmt, ...);
+
+/* Watch node/key unless node is empty, in which case watch key */
+void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key,
+                   char *token, Error **errp);
+void xs_node_unwatch(struct xs_handle *xsh, const char *node, const char *key,
+                     const char *token, Error **errp);
+
+#endif /* HW_XEN_BUS_HELPER_H */
diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h
new file mode 100644
index 0000000000..3183f10e3c
--- /dev/null
+++ b/include/hw/xen/xen-bus.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2018  Citrix Systems Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_XEN_BUS_H
+#define HW_XEN_BUS_H
+
+#include "hw/xen/xen_common.h"
+#include "hw/sysbus.h"
+#include "qemu/notify.h"
+
+typedef void (*XenWatchHandler)(void *opaque);
+
+typedef struct XenWatch XenWatch;
+
+typedef struct XenDevice {
+    DeviceState qdev;
+    domid_t frontend_id;
+    char *name;
+    char *backend_path, *frontend_path;
+    enum xenbus_state backend_state, frontend_state;
+    Notifier exit;
+    XenWatch *backend_state_watch, *frontend_state_watch;
+    bool backend_online;
+    XenWatch *backend_online_watch;
+    xengnttab_handle *xgth;
+    bool feature_grant_copy;
+    xenevtchn_handle *xeh;
+    NotifierList event_notifiers;
+} XenDevice;
+
+typedef char *(*XenDeviceGetName)(XenDevice *xendev, Error **errp);
+typedef void (*XenDeviceRealize)(XenDevice *xendev, Error **errp);
+typedef void (*XenDeviceFrontendChanged)(XenDevice *xendev,
+                                         enum xenbus_state frontend_state,
+                                         Error **errp);
+typedef void (*XenDeviceUnrealize)(XenDevice *xendev, Error **errp);
+
+typedef struct XenDeviceClass {
+    /*< private >*/
+    DeviceClass parent_class;
+    /*< public >*/
+    const char *backend;
+    const char *device;
+    XenDeviceGetName get_name;
+    XenDeviceRealize realize;
+    XenDeviceFrontendChanged frontend_changed;
+    XenDeviceUnrealize unrealize;
+} XenDeviceClass;
+
+#define TYPE_XEN_DEVICE "xen-device"
+#define XEN_DEVICE(obj) \
+     OBJECT_CHECK(XenDevice, (obj), TYPE_XEN_DEVICE)
+#define XEN_DEVICE_CLASS(class) \
+     OBJECT_CLASS_CHECK(XenDeviceClass, (class), TYPE_XEN_DEVICE)
+#define XEN_DEVICE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(XenDeviceClass, (obj), TYPE_XEN_DEVICE)
+
+typedef struct XenBus {
+    BusState qbus;
+    domid_t backend_id;
+    struct xs_handle *xsh;
+    NotifierList watch_notifiers;
+    XenWatch *backend_watch;
+} XenBus;
+
+typedef struct XenBusClass {
+    /*< private >*/
+    BusClass parent_class;
+} XenBusClass;
+
+#define TYPE_XEN_BUS "xen-bus"
+#define XEN_BUS(obj) \
+    OBJECT_CHECK(XenBus, (obj), TYPE_XEN_BUS)
+#define XEN_BUS_CLASS(class) \
+    OBJECT_CLASS_CHECK(XenBusClass, (class), TYPE_XEN_BUS)
+#define XEN_BUS_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(XenBusClass, (obj), TYPE_XEN_BUS)
+
+void xen_bus_init(void);
+
+void xen_device_backend_set_state(XenDevice *xendev,
+                                  enum xenbus_state state);
+enum xenbus_state xen_device_backend_get_state(XenDevice *xendev);
+
+void xen_device_backend_printf(XenDevice *xendev, const char *key,
+                               const char *fmt, ...)
+    GCC_FMT_ATTR(3, 4);
+void xen_device_frontend_printf(XenDevice *xendev, const char *key,
+                                const char *fmt, ...)
+    GCC_FMT_ATTR(3, 4);
+
+int xen_device_frontend_scanf(XenDevice *xendev, const char *key,
+                              const char *fmt, ...);
+
+void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs,
+                                   Error **errp);
+void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs,
+                                unsigned int nr_refs, int prot,
+                                Error **errp);
+void xen_device_unmap_grant_refs(XenDevice *xendev, void *map,
+                                 unsigned int nr_refs, Error **errp);
+
+typedef struct XenDeviceGrantCopySegment {
+    union {
+        void *virt;
+        struct {
+            uint32_t ref;
+            off_t offset;
+        } foreign;
+    } source, dest;
+    size_t len;
+} XenDeviceGrantCopySegment;
+
+void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain,
+                                XenDeviceGrantCopySegment segs[],
+                                unsigned int nr_segs, Error **errp);
+
+typedef struct XenEventChannel XenEventChannel;
+
+typedef void (*XenEventHandler)(void *opaque);
+
+XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev,
+                                               unsigned int port,
+                                               XenEventHandler handler,
+                                               void *opaque, Error **errp);
+void xen_device_notify_event_channel(XenDevice *xendev,
+                                     XenEventChannel *channel,
+                                     Error **errp);
+void xen_device_unbind_event_channel(XenDevice *xendev,
+                                     XenEventChannel *channel,
+                                     Error **errp);
+
+#endif /* HW_XEN_BUS_H */
diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen-legacy-backend.h
index 9c17fdd85d..20cb47b5bf 100644
--- a/include/hw/xen/xen_backend.h
+++ b/include/hw/xen/xen-legacy-backend.h
@@ -11,7 +11,7 @@
 #define TYPE_XENBACKEND "xen-backend"
 
 #define XENBACKEND_DEVICE(obj) \
-    OBJECT_CHECK(XenDevice, (obj), TYPE_XENBACKEND)
+    OBJECT_CHECK(XenLegacyDevice, (obj), TYPE_XENBACKEND)
 
 /* variables */
 extern struct xs_handle *xenstore;
@@ -20,32 +20,37 @@ extern DeviceState *xen_sysdev;
 extern BusState *xen_sysbus;
 
 int xenstore_mkdir(char *path, int p);
-int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val);
-int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival);
-int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival);
-char *xenstore_read_be_str(struct XenDevice *xendev, const char *node);
-int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival);
-void xenstore_update_fe(char *watch, struct XenDevice *xendev);
+int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node,
+                          const char *val);
+int xenstore_write_be_int(struct XenLegacyDevice *xendev, const char *node,
+                          int ival);
+int xenstore_write_be_int64(struct XenLegacyDevice *xendev, const char *node,
+                            int64_t ival);
+char *xenstore_read_be_str(struct XenLegacyDevice *xendev, const char *node);
+int xenstore_read_be_int(struct XenLegacyDevice *xendev, const char *node,
+                         int *ival);
+void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev);
 void xenstore_update_be(char *watch, char *type, int dom,
                         struct XenDevOps *ops);
-char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node);
-int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival);
-int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node,
+char *xenstore_read_fe_str(struct XenLegacyDevice *xendev, const char *node);
+int xenstore_read_fe_int(struct XenLegacyDevice *xendev, const char *node,
+                         int *ival);
+int xenstore_read_fe_uint64(struct XenLegacyDevice *xendev, const char *node,
                             uint64_t *uval);
 
-void xen_be_check_state(struct XenDevice *xendev);
+void xen_be_check_state(struct XenLegacyDevice *xendev);
 
 /* xen backend driver bits */
 int xen_be_init(void);
 void xen_be_register_common(void);
 int xen_be_register(const char *type, struct XenDevOps *ops);
-int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state);
-int xen_be_bind_evtchn(struct XenDevice *xendev);
-void xen_be_set_max_grant_refs(struct XenDevice *xendev,
+int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state);
+int xen_be_bind_evtchn(struct XenLegacyDevice *xendev);
+void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev,
                                unsigned int nr_refs);
-void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs,
+void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs,
                             unsigned int nr_refs, int prot);
-void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr,
+void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr,
                              unsigned int nr_refs);
 
 typedef struct XenGrantCopySegment {
@@ -59,17 +64,17 @@ typedef struct XenGrantCopySegment {
     size_t len;
 } XenGrantCopySegment;
 
-int xen_be_copy_grant_refs(struct XenDevice *xendev,
+int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev,
                            bool to_domain, XenGrantCopySegment segs[],
                            unsigned int nr_segs);
 
-static inline void *xen_be_map_grant_ref(struct XenDevice *xendev,
+static inline void *xen_be_map_grant_ref(struct XenLegacyDevice *xendev,
                                          uint32_t ref, int prot)
 {
     return xen_be_map_grant_refs(xendev, &ref, 1, prot);
 }
 
-static inline void xen_be_unmap_grant_ref(struct XenDevice *xendev,
+static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev,
                                           void *ptr)
 {
     return xen_be_unmap_grant_refs(xendev, ptr, 1);
diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h
index 7efcdaa8fe..ba039c146d 100644
--- a/include/hw/xen/xen.h
+++ b/include/hw/xen/xen.h
@@ -15,8 +15,7 @@
 /* xen-machine.c */
 enum xen_mode {
     XEN_EMULATE = 0,  // xen emulation, using xenner (default)
-    XEN_CREATE,       // create xen domain
-    XEN_ATTACH        // attach to xen domain created by xend
+    XEN_ATTACH        // attach to xen domain created by libxl
 };
 
 extern uint32_t xen_domid;
diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h
index 93f631e5bf..9a8155e172 100644
--- a/include/hw/xen/xen_common.h
+++ b/include/hw/xen/xen_common.h
@@ -32,6 +32,7 @@ extern xc_interface *xen_xc;
 typedef xc_interface xenforeignmemory_handle;
 typedef xc_evtchn xenevtchn_handle;
 typedef xc_gnttab xengnttab_handle;
+typedef evtchn_port_or_error_t xenevtchn_port_or_error_t;
 
 #define xenevtchn_open(l, f) xc_evtchn_open(l, f);
 #define xenevtchn_close(h) xc_evtchn_close(h)
@@ -661,24 +662,6 @@ static inline int xen_set_ioreq_server_state(domid_t dom,
 
 #endif
 
-#ifdef CONFIG_XEN_PV_DOMAIN_BUILD
-#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40700
-static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref,
-                                    xen_domain_handle_t handle, uint32_t flags,
-                                    uint32_t *pdomid)
-{
-    return xc_domain_create(xc, ssidref, handle, flags, pdomid);
-}
-#else
-static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref,
-                                    xen_domain_handle_t handle, uint32_t flags,
-                                    uint32_t *pdomid)
-{
-    return xc_domain_create(xc, ssidref, handle, flags, pdomid, NULL);
-}
-#endif
-#endif
-
 /* Xen before 4.8 */
 
 #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40800
diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h
index d473e9b34d..83e5174d90 100644
--- a/include/hw/xen/xen_pvdev.h
+++ b/include/hw/xen/xen_pvdev.h
@@ -6,7 +6,7 @@
 
 #define XEN_BUFSIZE 1024
 
-struct XenDevice;
+struct XenLegacyDevice;
 
 /* driver uses grant tables  ->  open gntdev device (xendev->gnttabdev) */
 #define DEVOPS_FLAG_NEED_GNTDEV   1
@@ -16,19 +16,21 @@ struct XenDevice;
 struct XenDevOps {
     size_t    size;
     uint32_t  flags;
-    void      (*alloc)(struct XenDevice *xendev);
-    int       (*init)(struct XenDevice *xendev);
-    int       (*initialise)(struct XenDevice *xendev);
-    void      (*connected)(struct XenDevice *xendev);
-    void      (*event)(struct XenDevice *xendev);
-    void      (*disconnect)(struct XenDevice *xendev);
-    int       (*free)(struct XenDevice *xendev);
-    void      (*backend_changed)(struct XenDevice *xendev, const char *node);
-    void      (*frontend_changed)(struct XenDevice *xendev, const char *node);
+    void      (*alloc)(struct XenLegacyDevice *xendev);
+    int       (*init)(struct XenLegacyDevice *xendev);
+    int       (*initialise)(struct XenLegacyDevice *xendev);
+    void      (*connected)(struct XenLegacyDevice *xendev);
+    void      (*event)(struct XenLegacyDevice *xendev);
+    void      (*disconnect)(struct XenLegacyDevice *xendev);
+    int       (*free)(struct XenLegacyDevice *xendev);
+    void      (*backend_changed)(struct XenLegacyDevice *xendev,
+                                 const char *node);
+    void      (*frontend_changed)(struct XenLegacyDevice *xendev,
+                                  const char *node);
     int       (*backend_register)(void);
 };
 
-struct XenDevice {
+struct XenLegacyDevice {
     DeviceState        qdev;
     const char         *type;
     int                dom;
@@ -49,7 +51,7 @@ struct XenDevice {
     xengnttab_handle   *gnttabdev;
 
     struct XenDevOps   *ops;
-    QTAILQ_ENTRY(XenDevice) next;
+    QTAILQ_ENTRY(XenLegacyDevice) next;
 };
 
 /* ------------------------------------------------------------- */
@@ -66,14 +68,14 @@ void xenstore_update(void *unused);
 const char *xenbus_strstate(enum xenbus_state state);
 
 void xen_pv_evtchn_event(void *opaque);
-void xen_pv_insert_xendev(struct XenDevice *xendev);
-void xen_pv_del_xendev(struct XenDevice *xendev);
-struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev);
+void xen_pv_insert_xendev(struct XenLegacyDevice *xendev);
+void xen_pv_del_xendev(struct XenLegacyDevice *xendev);
+struct XenLegacyDevice *xen_pv_find_xendev(const char *type, int dom, int dev);
 
-void xen_pv_unbind_evtchn(struct XenDevice *xendev);
-int xen_pv_send_notify(struct XenDevice *xendev);
+void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev);
+int xen_pv_send_notify(struct XenLegacyDevice *xendev);
 
-void xen_pv_printf(struct XenDevice *xendev, int msg_level,
+void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level,
                    const char *fmt, ...)  GCC_FMT_ATTR(3, 4);
 
 #endif /* QEMU_HW_XEN_PVDEV_H */
diff --git a/include/qemu/module.h b/include/qemu/module.h
index 54300ab6e5..55dd2beea8 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -44,6 +44,7 @@ typedef enum {
     MODULE_INIT_OPTS,
     MODULE_INIT_QOM,
     MODULE_INIT_TRACE,
+    MODULE_INIT_XEN_BACKEND,
     MODULE_INIT_MAX
 } module_init_type;
 
@@ -51,6 +52,8 @@ typedef enum {
 #define opts_init(function) module_init(function, MODULE_INIT_OPTS)
 #define type_init(function) module_init(function, MODULE_INIT_QOM)
 #define trace_init(function) module_init(function, MODULE_INIT_TRACE)
+#define xen_backend_init(function) module_init(function, \
+                                               MODULE_INIT_XEN_BACKEND)
 
 #define block_module_load_one(lib) module_load_one("block-", lib)
 #define ui_module_load_one(lib) module_load_one("ui-", lib)
diff --git a/nbd/server.c b/nbd/server.c
index 7af0ddffb2..e8c56607ef 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1456,9 +1456,10 @@ static void nbd_eject_notifier(Notifier *n, void *data)
 }
 
 NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
-                          uint16_t nbdflags, void (*close)(NBDExport *),
-                          bool writethrough, BlockBackend *on_eject_blk,
-                          Error **errp)
+                          const char *name, const char *description,
+                          const char *bitmap, uint16_t nbdflags,
+                          void (*close)(NBDExport *), bool writethrough,
+                          BlockBackend *on_eject_blk, Error **errp)
 {
     AioContext *ctx;
     BlockBackend *blk;
@@ -1471,6 +1472,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
      * that BDRV_O_INACTIVE is cleared and the image is ready for write
      * access since the export could be available before migration handover.
      */
+    assert(name);
     ctx = bdrv_get_aio_context(bs);
     aio_context_acquire(ctx);
     bdrv_invalidate_cache(bs, NULL);
@@ -1494,6 +1496,8 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
     QTAILQ_INIT(&exp->clients);
     exp->blk = blk;
     exp->dev_offset = dev_offset;
+    exp->name = g_strdup(name);
+    exp->description = g_strdup(description);
     exp->nbdflags = nbdflags;
     exp->size = size < 0 ? blk_getlength(blk) : size;
     if (exp->size < 0) {
@@ -1503,6 +1507,43 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
     }
     exp->size -= exp->size % BDRV_SECTOR_SIZE;
 
+    if (bitmap) {
+        BdrvDirtyBitmap *bm = NULL;
+        BlockDriverState *bs = blk_bs(blk);
+
+        while (true) {
+            bm = bdrv_find_dirty_bitmap(bs, bitmap);
+            if (bm != NULL || bs->backing == NULL) {
+                break;
+            }
+
+            bs = bs->backing->bs;
+        }
+
+        if (bm == NULL) {
+            error_setg(errp, "Bitmap '%s' is not found", bitmap);
+            goto fail;
+        }
+
+        if ((nbdflags & NBD_FLAG_READ_ONLY) && bdrv_is_writable(bs) &&
+            bdrv_dirty_bitmap_enabled(bm)) {
+            error_setg(errp,
+                       "Enabled bitmap '%s' incompatible with readonly export",
+                       bitmap);
+            goto fail;
+        }
+
+        if (bdrv_dirty_bitmap_user_locked(bm)) {
+            error_setg(errp, "Bitmap '%s' is in use", bitmap);
+            goto fail;
+        }
+
+        bdrv_dirty_bitmap_set_qmp_locked(bm, true);
+        exp->export_bitmap = bm;
+        exp->export_bitmap_context = g_strdup_printf("qemu:dirty-bitmap:%s",
+                                                     bitmap);
+    }
+
     exp->close = close;
     exp->ctx = blk_get_aio_context(blk);
     blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp);
@@ -1513,10 +1554,14 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
         exp->eject_notifier.notify = nbd_eject_notifier;
         blk_add_remove_bs_notifier(on_eject_blk, &exp->eject_notifier);
     }
+    QTAILQ_INSERT_TAIL(&exports, exp, next);
+    nbd_export_get(exp);
     return exp;
 
 fail:
     blk_unref(blk);
+    g_free(exp->name);
+    g_free(exp->description);
     g_free(exp);
     return NULL;
 }
@@ -1533,43 +1578,29 @@ NBDExport *nbd_export_find(const char *name)
     return NULL;
 }
 
-void nbd_export_set_name(NBDExport *exp, const char *name)
-{
-    if (exp->name == name) {
-        return;
-    }
-
-    nbd_export_get(exp);
-    if (exp->name != NULL) {
-        g_free(exp->name);
-        exp->name = NULL;
-        QTAILQ_REMOVE(&exports, exp, next);
-        nbd_export_put(exp);
-    }
-    if (name != NULL) {
-        nbd_export_get(exp);
-        exp->name = g_strdup(name);
-        QTAILQ_INSERT_TAIL(&exports, exp, next);
-    }
-    nbd_export_put(exp);
-}
-
-void nbd_export_set_description(NBDExport *exp, const char *description)
-{
-    g_free(exp->description);
-    exp->description = g_strdup(description);
-}
-
 void nbd_export_close(NBDExport *exp)
 {
     NBDClient *client, *next;
 
     nbd_export_get(exp);
+    /*
+     * TODO: Should we expand QMP NbdServerRemoveNode enum to allow a
+     * close mode that stops advertising the export to new clients but
+     * still permits existing clients to run to completion? Because of
+     * that possibility, nbd_export_close() can be called more than
+     * once on an export.
+     */
     QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) {
         client_close(client, true);
     }
-    nbd_export_set_name(exp, NULL);
-    nbd_export_set_description(exp, NULL);
+    if (exp->name) {
+        nbd_export_put(exp);
+        g_free(exp->name);
+        exp->name = NULL;
+        QTAILQ_REMOVE(&exports, exp, next);
+    }
+    g_free(exp->description);
+    exp->description = NULL;
     nbd_export_put(exp);
 }
 
@@ -2430,44 +2461,3 @@ void nbd_client_new(QIOChannelSocket *sioc,
     co = qemu_coroutine_create(nbd_co_client_start, client);
     qemu_coroutine_enter(co);
 }
-
-void nbd_export_bitmap(NBDExport *exp, const char *bitmap,
-                       const char *bitmap_export_name, Error **errp)
-{
-    BdrvDirtyBitmap *bm = NULL;
-    BlockDriverState *bs = blk_bs(exp->blk);
-
-    if (exp->export_bitmap) {
-        error_setg(errp, "Export bitmap is already set");
-        return;
-    }
-
-    while (true) {
-        bm = bdrv_find_dirty_bitmap(bs, bitmap);
-        if (bm != NULL || bs->backing == NULL) {
-            break;
-        }
-
-        bs = bs->backing->bs;
-    }
-
-    if (bm == NULL) {
-        error_setg(errp, "Bitmap '%s' is not found", bitmap);
-        return;
-    }
-
-    if (bdrv_dirty_bitmap_enabled(bm)) {
-        error_setg(errp, "Bitmap '%s' is enabled", bitmap);
-        return;
-    }
-
-    if (bdrv_dirty_bitmap_user_locked(bm)) {
-        error_setg(errp, "Bitmap '%s' is in use", bitmap);
-        return;
-    }
-
-    bdrv_dirty_bitmap_set_qmp_locked(bm, true);
-    exp->export_bitmap = bm;
-    exp->export_bitmap_context =
-            g_strdup_printf("qemu:dirty-bitmap:%s", bitmap_export_name);
-}
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 762000f31f..91685be6c2 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1806,29 +1806,29 @@
 #            Currently, all dirty tracking bitmaps are loaded from Qcow2 on
 #            open.
 #
-# @x-disabled: the bitmap is created in the disabled state, which means that
-#              it will not track drive changes. The bitmap may be enabled with
-#              x-block-dirty-bitmap-enable. Default is false. (Since: 3.0)
+# @disabled: the bitmap is created in the disabled state, which means that
+#            it will not track drive changes. The bitmap may be enabled with
+#            block-dirty-bitmap-enable. Default is false. (Since: 4.0)
 #
 # Since: 2.4
 ##
 { 'struct': 'BlockDirtyBitmapAdd',
   'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32',
-            '*persistent': 'bool', '*autoload': 'bool', '*x-disabled': 'bool' } }
+            '*persistent': 'bool', '*autoload': 'bool', '*disabled': 'bool' } }
 
 ##
 # @BlockDirtyBitmapMerge:
 #
 # @node: name of device/node which the bitmap is tracking
 #
-# @dst_name: name of the destination dirty bitmap
+# @target: name of the destination dirty bitmap
 #
-# @src_name: name of the source dirty bitmap
+# @bitmaps: name(s) of the source dirty bitmap(s)
 #
-# Since: 3.0
+# Since: 4.0
 ##
 { 'struct': 'BlockDirtyBitmapMerge',
-  'data': { 'node': 'str', 'dst_name': 'str', 'src_name': 'str' } }
+  'data': { 'node': 'str', 'target': 'str', 'bitmaps': ['str'] } }
 
 ##
 # @block-dirty-bitmap-add:
@@ -1899,7 +1899,7 @@
   'data': 'BlockDirtyBitmap' }
 
 ##
-# @x-block-dirty-bitmap-enable:
+# @block-dirty-bitmap-enable:
 #
 # Enables a dirty bitmap so that it will begin tracking disk changes.
 #
@@ -1907,20 +1907,20 @@
 #          If @node is not a valid block device, DeviceNotFound
 #          If @name is not found, GenericError with an explanation
 #
-# Since: 3.0
+# Since: 4.0
 #
 # Example:
 #
-# -> { "execute": "x-block-dirty-bitmap-enable",
+# -> { "execute": "block-dirty-bitmap-enable",
 #      "arguments": { "node": "drive0", "name": "bitmap0" } }
 # <- { "return": {} }
 #
 ##
-  { 'command': 'x-block-dirty-bitmap-enable',
+  { 'command': 'block-dirty-bitmap-enable',
     'data': 'BlockDirtyBitmap' }
 
 ##
-# @x-block-dirty-bitmap-disable:
+# @block-dirty-bitmap-disable:
 #
 # Disables a dirty bitmap so that it will stop tracking disk changes.
 #
@@ -1928,42 +1928,42 @@
 #          If @node is not a valid block device, DeviceNotFound
 #          If @name is not found, GenericError with an explanation
 #
-# Since: 3.0
+# Since: 4.0
 #
 # Example:
 #
-# -> { "execute": "x-block-dirty-bitmap-disable",
+# -> { "execute": "block-dirty-bitmap-disable",
 #      "arguments": { "node": "drive0", "name": "bitmap0" } }
 # <- { "return": {} }
 #
 ##
-    { 'command': 'x-block-dirty-bitmap-disable',
+    { 'command': 'block-dirty-bitmap-disable',
       'data': 'BlockDirtyBitmap' }
 
 ##
-# @x-block-dirty-bitmap-merge:
+# @block-dirty-bitmap-merge:
 #
-# FIXME: Rename @src_name and @dst_name to src-name and dst-name.
-#
-# Merge @src_name dirty bitmap to @dst_name dirty bitmap. @src_name dirty
-# bitmap is unchanged. On error, @dst_name is unchanged.
+# Merge dirty bitmaps listed in @bitmaps to the @target dirty bitmap.
+# The @bitmaps dirty bitmaps are unchanged.
+# On error, @target is unchanged.
 #
 # Returns: nothing on success
 #          If @node is not a valid block device, DeviceNotFound
-#          If @dst_name or @src_name is not found, GenericError
-#          If bitmaps has different sizes or granularities, GenericError
+#          If any bitmap in @bitmaps or @target is not found, GenericError
+#          If any of the bitmaps have different sizes or granularities,
+#              GenericError
 #
-# Since: 3.0
+# Since: 4.0
 #
 # Example:
 #
-# -> { "execute": "x-block-dirty-bitmap-merge",
-#      "arguments": { "node": "drive0", "dst_name": "bitmap0",
-#                     "src_name": "bitmap1" } }
+# -> { "execute": "block-dirty-bitmap-merge",
+#      "arguments": { "node": "drive0", "target": "bitmap0",
+#                     "bitmaps": ["bitmap1"] } }
 # <- { "return": {} }
 #
 ##
-      { 'command': 'x-block-dirty-bitmap-merge',
+      { 'command': 'block-dirty-bitmap-merge',
         'data': 'BlockDirtyBitmapMerge' }
 
 ##
diff --git a/qapi/block.json b/qapi/block.json
index 11f01f28ef..5a79d639e8 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -246,6 +246,10 @@
 #
 # @writable: Whether clients should be able to write to the device via the
 #     NBD connection (default false).
+
+# @bitmap: Also export the dirty bitmap reachable from @device, so the
+#          NBD client can use NBD_OPT_SET_META_CONTEXT with
+#          "qemu:dirty-bitmap:NAME" to inspect the bitmap. (since 4.0)
 #
 # Returns: error if the server is not running, or export with the same name
 #          already exists.
@@ -253,7 +257,8 @@
 # Since: 1.3.0
 ##
 { 'command': 'nbd-server-add',
-  'data': {'device': 'str', '*name': 'str', '*writable': 'bool'} }
+  'data': {'device': 'str', '*name': 'str', '*writable': 'bool',
+           '*bitmap': 'str' } }
 
 ##
 # @NbdServerRemoveMode:
@@ -297,29 +302,6 @@
   'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} }
 
 ##
-# @x-nbd-server-add-bitmap:
-#
-# Expose a dirty bitmap associated with the selected export. The bitmap search
-# starts at the device attached to the export, and includes all backing files.
-# The exported bitmap is then locked until the NBD export is removed.
-#
-# @name: Export name.
-#
-# @bitmap: Bitmap name to search for.
-#
-# @bitmap-export-name: How the bitmap will be seen by nbd clients
-#                      (default @bitmap)
-#
-# Note: the client must use NBD_OPT_SET_META_CONTEXT with a query of
-# "qemu:dirty-bitmap:NAME" (where NAME matches @bitmap-export-name) to access
-# the exposed bitmap.
-#
-# Since: 3.0
-##
-  { 'command': 'x-nbd-server-add-bitmap',
-    'data': {'name': 'str', 'bitmap': 'str', '*bitmap-export-name': 'str'} }
-
-##
 # @nbd-server-stop:
 #
 # Stop QEMU's embedded NBD server, and unregister all devices previously
diff --git a/qapi/transaction.json b/qapi/transaction.json
index 5875cdb16c..95edb78227 100644
--- a/qapi/transaction.json
+++ b/qapi/transaction.json
@@ -46,9 +46,9 @@
 # - @abort: since 1.6
 # - @block-dirty-bitmap-add: since 2.5
 # - @block-dirty-bitmap-clear: since 2.5
-# - @x-block-dirty-bitmap-enable: since 3.0
-# - @x-block-dirty-bitmap-disable: since 3.0
-# - @x-block-dirty-bitmap-merge: since 3.1
+# - @block-dirty-bitmap-enable: since 4.0
+# - @block-dirty-bitmap-disable: since 4.0
+# - @block-dirty-bitmap-merge: since 4.0
 # - @blockdev-backup: since 2.3
 # - @blockdev-snapshot: since 2.5
 # - @blockdev-snapshot-internal-sync: since 1.7
@@ -62,9 +62,9 @@
        'abort': 'Abort',
        'block-dirty-bitmap-add': 'BlockDirtyBitmapAdd',
        'block-dirty-bitmap-clear': 'BlockDirtyBitmap',
-       'x-block-dirty-bitmap-enable': 'BlockDirtyBitmap',
-       'x-block-dirty-bitmap-disable': 'BlockDirtyBitmap',
-       'x-block-dirty-bitmap-merge': 'BlockDirtyBitmapMerge',
+       'block-dirty-bitmap-enable': 'BlockDirtyBitmap',
+       'block-dirty-bitmap-disable': 'BlockDirtyBitmap',
+       'block-dirty-bitmap-merge': 'BlockDirtyBitmapMerge',
        'blockdev-backup': 'BlockdevBackup',
        'blockdev-snapshot': 'BlockdevSnapshot',
        'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal',
diff --git a/qemu-doc.texi b/qemu-doc.texi
index f7ad1dfe4b..16b955cbf9 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -37,6 +37,7 @@
 * QEMU System emulator for non PC targets::
 * QEMU Guest Agent::
 * QEMU User space emulator::
+* System requirements::
 * Implementation notes::
 * Deprecated features::
 * Supported build platforms::
@@ -2813,6 +2814,18 @@ Act as if the host page size was 'pagesize' bytes
 Run the emulation in single step mode.
 @end table
 
+@node System requirements
+@chapter System requirements
+
+@section KVM kernel module
+
+On x86_64 hosts, the default set of CPU features enabled by the KVM accelerator
+require the host to be running Linux v4.5 or newer.
+
+The OpteronG[345] CPU models require KVM support for RDTSCP, which was
+added with Linux 4.5 which is supported by the major distros. And even
+if RHEL7 has kernel 3.10, KVM there has the required functionality there
+to make it close to a 4.5 or newer kernel.
 
 @include qemu-tech.texi
 
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 2807e13239..51b55f2e06 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -61,7 +61,7 @@
 
 #define MBR_SIZE 512
 
-static NBDExport *exp;
+static NBDExport *export;
 static int verbose;
 static char *srcpath;
 static SocketAddress *saddr;
@@ -95,6 +95,7 @@ static void usage(const char *name)
 "Exposing part of the image:\n"
 "  -o, --offset=OFFSET       offset into the image\n"
 "  -P, --partition=NUM       only expose partition NUM\n"
+"  -B, --bitmap=NAME         expose a persistent dirty bitmap\n"
 "\n"
 "General purpose options:\n"
 "  --object type,id=ID,...   define an object such as 'secret' for providing\n"
@@ -335,7 +336,7 @@ static int nbd_can_accept(void)
     return state == RUNNING && nb_fds < shared;
 }
 
-static void nbd_export_closed(NBDExport *exp)
+static void nbd_export_closed(NBDExport *export)
 {
     assert(state == TERMINATING);
     state = TERMINATED;
@@ -509,7 +510,7 @@ int main(int argc, char **argv)
     off_t fd_size;
     QemuOpts *sn_opts = NULL;
     const char *sn_id_or_name = NULL;
-    const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:";
+    const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:";
     struct option lopt[] = {
         { "help", no_argument, NULL, 'h' },
         { "version", no_argument, NULL, 'V' },
@@ -519,6 +520,7 @@ int main(int argc, char **argv)
         { "offset", required_argument, NULL, 'o' },
         { "read-only", no_argument, NULL, 'r' },
         { "partition", required_argument, NULL, 'P' },
+        { "bitmap", required_argument, NULL, 'B' },
         { "connect", required_argument, NULL, 'c' },
         { "disconnect", no_argument, NULL, 'd' },
         { "snapshot", no_argument, NULL, 's' },
@@ -558,6 +560,7 @@ int main(int argc, char **argv)
     QDict *options = NULL;
     const char *export_name = ""; /* Default export name */
     const char *export_description = NULL;
+    const char *bitmap = NULL;
     const char *tlscredsid = NULL;
     bool imageOpts = false;
     bool writethrough = true;
@@ -695,6 +698,9 @@ int main(int argc, char **argv)
                 exit(EXIT_FAILURE);
             }
             break;
+        case 'B':
+            bitmap = optarg;
+            break;
         case 'k':
             sockpath = optarg;
             if (sockpath[0] != '/') {
@@ -1015,10 +1021,10 @@ int main(int argc, char **argv)
         }
     }
 
-    exp = nbd_export_new(bs, dev_offset, fd_size, nbdflags, nbd_export_closed,
-                         writethrough, NULL, &error_fatal);
-    nbd_export_set_name(exp, export_name);
-    nbd_export_set_description(exp, export_description);
+    export = nbd_export_new(bs, dev_offset, fd_size, export_name,
+                            export_description, bitmap, nbdflags,
+                            nbd_export_closed, writethrough, NULL,
+                            &error_fatal);
 
     if (device) {
 #if HAVE_NBD_DEVICE
@@ -1055,9 +1061,9 @@ int main(int argc, char **argv)
         main_loop_wait(false);
         if (state == TERMINATE) {
             state = TERMINATING;
-            nbd_export_close(exp);
-            nbd_export_put(exp);
-            exp = NULL;
+            nbd_export_close(export);
+            nbd_export_put(export);
+            export = NULL;
         }
     } while (state != TERMINATED);
 
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
index 9a84e81eed..96b1546006 100644
--- a/qemu-nbd.texi
+++ b/qemu-nbd.texi
@@ -45,6 +45,10 @@ auto-detecting
 Export the disk as read-only
 @item -P, --partition=@var{num}
 Only expose partition @var{num}
+@item -B, --bitmap=@var{name}
+If @var{filename} has a qcow2 persistent bitmap @var{name}, expose
+that bitmap via the ``qemu:dirty-bitmap:@var{name}'' context
+accessible through NBD_OPT_SET_META_CONTEXT.
 @item -s, --snapshot
 Use @var{filename} as an external snapshot, create a temporary
 file with backing_file=@var{filename}, redirect the write to
diff --git a/qemu-options.hx b/qemu-options.hx
index d4f3564b78..521511ec13 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3394,13 +3394,9 @@ ETEXI
 
 DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid,
     "-xen-domid id   specify xen guest domain id\n", QEMU_ARCH_ALL)
-DEF("xen-create", 0, QEMU_OPTION_xen_create,
-    "-xen-create     create domain using xen hypercalls, bypassing xend\n"
-    "                warning: should not be used when xend is in use\n",
-    QEMU_ARCH_ALL)
 DEF("xen-attach", 0, QEMU_OPTION_xen_attach,
     "-xen-attach     attach to existing xen domain\n"
-    "                xend will use this when starting QEMU\n",
+    "                libxl will use this when starting QEMU\n",
     QEMU_ARCH_ALL)
 DEF("xen-domid-restrict", 0, QEMU_OPTION_xen_domid_restrict,
     "-xen-domid-restrict     restrict set of available xen operations\n"
@@ -3411,14 +3407,10 @@ STEXI
 @item -xen-domid @var{id}
 @findex -xen-domid
 Specify xen guest domain @var{id} (XEN only).
-@item -xen-create
-@findex -xen-create
-Create domain using xen hypercalls, bypassing xend.
-Warning: should not be used when xend is in use (XEN only).
 @item -xen-attach
 @findex -xen-attach
 Attach to existing xen domain.
-xend will use this when starting QEMU (XEN only).
+libxl will use this when starting QEMU (XEN only).
 @findex -xen-domid-restrict
 Restrict set of available xen operations to specified domain id (XEN only).
 ETEXI
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index fa37203d89..2f5412592d 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -929,6 +929,13 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
          */
         .no_autoenable_flags = ~0U,
     },
+    /*
+     * .feat_names are commented out for Hyper-V enlightenments because we
+     * don't want to have two different ways for enabling them on QEMU command
+     * line. Some features (e.g. "hyperv_time", "hyperv_vapic", ...) require
+     * enabling several feature bits simultaneously, exposing these bits
+     * individually may just confuse guests.
+     */
     [FEAT_HYPERV_EAX] = {
         .type = CPUID_FEATURE_WORD,
         .feat_names = {
@@ -980,6 +987,36 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
         },
         .cpuid = { .eax = 0x40000003, .reg = R_EDX, },
     },
+    [FEAT_HV_RECOMM_EAX] = {
+        .type = CPUID_FEATURE_WORD,
+        .feat_names = {
+            NULL /* hv_recommend_pv_as_switch */,
+            NULL /* hv_recommend_pv_tlbflush_local */,
+            NULL /* hv_recommend_pv_tlbflush_remote */,
+            NULL /* hv_recommend_msr_apic_access */,
+            NULL /* hv_recommend_msr_reset */,
+            NULL /* hv_recommend_relaxed_timing */,
+            NULL /* hv_recommend_dma_remapping */,
+            NULL /* hv_recommend_int_remapping */,
+            NULL /* hv_recommend_x2apic_msrs */,
+            NULL /* hv_recommend_autoeoi_deprecation */,
+            NULL /* hv_recommend_pv_ipi */,
+            NULL /* hv_recommend_ex_hypercalls */,
+            NULL /* hv_hypervisor_is_nested */,
+            NULL /* hv_recommend_int_mbec */,
+            NULL /* hv_recommend_evmcs */,
+            NULL,
+            NULL, NULL, NULL, NULL,
+            NULL, NULL, NULL, NULL,
+            NULL, NULL, NULL, NULL,
+            NULL, NULL, NULL, NULL,
+        },
+        .cpuid = { .eax = 0x40000004, .reg = R_EAX, },
+    },
+    [FEAT_HV_NESTED_EAX] = {
+        .type = CPUID_FEATURE_WORD,
+        .cpuid = { .eax = 0x4000000A, .reg = R_EAX, },
+    },
     [FEAT_SVM] = {
         .type = CPUID_FEATURE_WORD,
         .feat_names = {
@@ -2296,7 +2333,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
             CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
             CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
             CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX,
+            CPUID_7_0_EBX_SMAP,
         /* Missing: XSAVES (not supported by some Linux versions,
          * including v4.1 to v4.12).
          * KVM doesn't yet expose any XSAVES state save component,
@@ -2343,7 +2380,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
             CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
             CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
             CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX,
+            CPUID_7_0_EBX_SMAP,
         /* Missing: XSAVES (not supported by some Linux versions,
          * including v4.1 to v4.12).
          * KVM doesn't yet expose any XSAVES state save component,
@@ -2388,7 +2425,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
             CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
             CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
             CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB |
+            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLWB |
             CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ |
             CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD |
             CPUID_7_0_EBX_AVX512VL | CPUID_7_0_EBX_CLFLUSHOPT,
@@ -2440,7 +2477,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
             CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
             CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
             CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB |
+            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLWB |
             CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ |
             CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD |
             CPUID_7_0_EBX_AVX512VL,
@@ -2490,7 +2527,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
             CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
             CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
             CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB |
+            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLWB |
             CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ |
             CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD |
             CPUID_7_0_EBX_AVX512VL | CPUID_7_0_EBX_CLFLUSHOPT |
@@ -2546,7 +2583,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
             CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
             CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
             CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_INTEL_PT,
+            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_INTEL_PT,
         .features[FEAT_7_0_ECX] =
             CPUID_7_0_ECX_VBMI | CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU |
             CPUID_7_0_ECX_OSPKE | CPUID_7_0_ECX_VBMI2 | CPUID_7_0_ECX_GFNI |
@@ -2601,7 +2638,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
             CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
             CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
             CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB |
+            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLWB |
             CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ |
             CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD |
             CPUID_7_0_EBX_AVX512VL | CPUID_7_0_EBX_CLFLUSHOPT |
@@ -2706,7 +2743,6 @@ static X86CPUDefinition builtin_x86_defs[] = {
             CPUID_DE | CPUID_FP87,
         .features[FEAT_1_ECX] =
             CPUID_EXT_CX16 | CPUID_EXT_SSE3,
-        /* Missing: CPUID_EXT2_RDTSCP */
         .features[FEAT_8000_0001_EDX] =
             CPUID_EXT2_LM | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL,
         .features[FEAT_8000_0001_ECX] =
@@ -2730,9 +2766,9 @@ static X86CPUDefinition builtin_x86_defs[] = {
         .features[FEAT_1_ECX] =
             CPUID_EXT_POPCNT | CPUID_EXT_CX16 | CPUID_EXT_MONITOR |
             CPUID_EXT_SSE3,
-        /* Missing: CPUID_EXT2_RDTSCP */
         .features[FEAT_8000_0001_EDX] =
-            CPUID_EXT2_LM | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL,
+            CPUID_EXT2_LM | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL |
+            CPUID_EXT2_RDTSCP,
         .features[FEAT_8000_0001_ECX] =
             CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A |
             CPUID_EXT3_ABM | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM,
@@ -2757,10 +2793,9 @@ static X86CPUDefinition builtin_x86_defs[] = {
             CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 |
             CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ |
             CPUID_EXT_SSE3,
-        /* Missing: CPUID_EXT2_RDTSCP */
         .features[FEAT_8000_0001_EDX] =
             CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_NX |
-            CPUID_EXT2_SYSCALL,
+            CPUID_EXT2_SYSCALL | CPUID_EXT2_RDTSCP,
         .features[FEAT_8000_0001_ECX] =
             CPUID_EXT3_FMA4 | CPUID_EXT3_XOP |
             CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE |
@@ -2788,10 +2823,9 @@ static X86CPUDefinition builtin_x86_defs[] = {
             CPUID_EXT_AES | CPUID_EXT_POPCNT | CPUID_EXT_SSE42 |
             CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_FMA |
             CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3,
-        /* Missing: CPUID_EXT2_RDTSCP */
         .features[FEAT_8000_0001_EDX] =
             CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_NX |
-            CPUID_EXT2_SYSCALL,
+            CPUID_EXT2_SYSCALL | CPUID_EXT2_RDTSCP,
         .features[FEAT_8000_0001_ECX] =
             CPUID_EXT3_TBM | CPUID_EXT3_FMA4 | CPUID_EXT3_XOP |
             CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE |
@@ -5151,6 +5185,10 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
             if (cpu->host_phys_bits) {
                 /* The user asked for us to use the host physical bits */
                 cpu->phys_bits = host_phys_bits;
+                if (cpu->host_phys_bits_limit &&
+                    cpu->phys_bits > cpu->host_phys_bits_limit) {
+                    cpu->phys_bits = cpu->host_phys_bits_limit;
+                }
             }
 
             /* Print a warning if the user set it to a value that's not the
@@ -5738,6 +5776,7 @@ static Property x86_cpu_properties[] = {
     DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
     DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0),
     DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false),
+    DEFINE_PROP_UINT8("host-phys-bits-limit", X86CPU, host_phys_bits_limit, 0),
     DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true),
     DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, UINT32_MAX),
     DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, UINT32_MAX),
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index ef41a033c5..59656a70e6 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -497,6 +497,8 @@ typedef enum FeatureWord {
     FEAT_HYPERV_EAX,    /* CPUID[4000_0003].EAX */
     FEAT_HYPERV_EBX,    /* CPUID[4000_0003].EBX */
     FEAT_HYPERV_EDX,    /* CPUID[4000_0003].EDX */
+    FEAT_HV_RECOMM_EAX, /* CPUID[4000_0004].EAX */
+    FEAT_HV_NESTED_EAX, /* CPUID[4000_000A].EAX */
     FEAT_SVM,           /* CPUID[8000_000A].EDX */
     FEAT_XSAVE,         /* CPUID[EAX=0xd,ECX=1].EAX */
     FEAT_6_EAX,         /* CPUID[6].EAX */
@@ -1459,6 +1461,9 @@ struct X86CPU {
     /* if true override the phys_bits value with a value read from the host */
     bool host_phys_bits;
 
+    /* if set, limit maximum value for phys_bits when host_phys_bits is true */
+    uint8_t host_phys_bits_limit;
+
     /* Stop SMI delivery for migration compatibility with old machines */
     bool kvm_no_smi_migration;
 
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index 739cf8c8ea..9af4542fb8 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -798,6 +798,48 @@ static int hyperv_handle_properties(CPUState *cs)
         }
         env->features[FEAT_HYPERV_EAX] |= HV_SYNTIMERS_AVAILABLE;
     }
+    if (cpu->hyperv_relaxed_timing) {
+        env->features[FEAT_HV_RECOMM_EAX] |= HV_RELAXED_TIMING_RECOMMENDED;
+    }
+    if (cpu->hyperv_vapic) {
+        env->features[FEAT_HV_RECOMM_EAX] |= HV_APIC_ACCESS_RECOMMENDED;
+    }
+    if (cpu->hyperv_tlbflush) {
+        if (kvm_check_extension(cs->kvm_state,
+                                KVM_CAP_HYPERV_TLBFLUSH) <= 0) {
+            fprintf(stderr, "Hyper-V TLB flush support "
+                    "(requested by 'hv-tlbflush' cpu flag) "
+                    " is not supported by kernel\n");
+            return -ENOSYS;
+        }
+        env->features[FEAT_HV_RECOMM_EAX] |= HV_REMOTE_TLB_FLUSH_RECOMMENDED;
+        env->features[FEAT_HV_RECOMM_EAX] |= HV_EX_PROCESSOR_MASKS_RECOMMENDED;
+    }
+    if (cpu->hyperv_ipi) {
+        if (kvm_check_extension(cs->kvm_state,
+                                KVM_CAP_HYPERV_SEND_IPI) <= 0) {
+            fprintf(stderr, "Hyper-V IPI send support "
+                    "(requested by 'hv-ipi' cpu flag) "
+                    " is not supported by kernel\n");
+            return -ENOSYS;
+        }
+        env->features[FEAT_HV_RECOMM_EAX] |= HV_CLUSTER_IPI_RECOMMENDED;
+        env->features[FEAT_HV_RECOMM_EAX] |= HV_EX_PROCESSOR_MASKS_RECOMMENDED;
+    }
+    if (cpu->hyperv_evmcs) {
+        uint16_t evmcs_version;
+
+        if (kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_ENLIGHTENED_VMCS, 0,
+                                (uintptr_t)&evmcs_version)) {
+            fprintf(stderr, "Hyper-V Enlightened VMCS "
+                    "(requested by 'hv-evmcs' cpu flag) "
+                    "is not supported by kernel\n");
+            return -ENOSYS;
+        }
+        env->features[FEAT_HV_RECOMM_EAX] |= HV_ENLIGHTENED_VMCS_RECOMMENDED;
+        env->features[FEAT_HV_NESTED_EAX] = evmcs_version;
+    }
+
     return 0;
 }
 
@@ -879,7 +921,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
     uint32_t unused;
     struct kvm_cpuid_entry2 *c;
     uint32_t signature[3];
-    uint16_t evmcs_version;
     int kvm_base = KVM_CPUID_SIGNATURE;
     int r;
     Error *local_err = NULL;
@@ -954,44 +995,8 @@ int kvm_arch_init_vcpu(CPUState *cs)
 
         c = &cpuid_data.entries[cpuid_i++];
         c->function = HV_CPUID_ENLIGHTMENT_INFO;
-        if (cpu->hyperv_relaxed_timing) {
-            c->eax |= HV_RELAXED_TIMING_RECOMMENDED;
-        }
-        if (cpu->hyperv_vapic) {
-            c->eax |= HV_APIC_ACCESS_RECOMMENDED;
-        }
-        if (cpu->hyperv_tlbflush) {
-            if (kvm_check_extension(cs->kvm_state,
-                                    KVM_CAP_HYPERV_TLBFLUSH) <= 0) {
-                fprintf(stderr, "Hyper-V TLB flush support "
-                        "(requested by 'hv-tlbflush' cpu flag) "
-                        " is not supported by kernel\n");
-                return -ENOSYS;
-            }
-            c->eax |= HV_REMOTE_TLB_FLUSH_RECOMMENDED;
-            c->eax |= HV_EX_PROCESSOR_MASKS_RECOMMENDED;
-        }
-        if (cpu->hyperv_ipi) {
-            if (kvm_check_extension(cs->kvm_state,
-                                    KVM_CAP_HYPERV_SEND_IPI) <= 0) {
-                fprintf(stderr, "Hyper-V IPI send support "
-                        "(requested by 'hv-ipi' cpu flag) "
-                        " is not supported by kernel\n");
-                return -ENOSYS;
-            }
-            c->eax |= HV_CLUSTER_IPI_RECOMMENDED;
-            c->eax |= HV_EX_PROCESSOR_MASKS_RECOMMENDED;
-        }
-        if (cpu->hyperv_evmcs) {
-            if (kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_ENLIGHTENED_VMCS, 0,
-                                    (uintptr_t)&evmcs_version)) {
-                fprintf(stderr, "Hyper-V Enlightened VMCS "
-                        "(requested by 'hv-evmcs' cpu flag) "
-                        "is not supported by kernel\n");
-                return -ENOSYS;
-            }
-            c->eax |= HV_ENLIGHTENED_VMCS_RECOMMENDED;
-        }
+
+        c->eax = env->features[FEAT_HV_RECOMM_EAX];
         c->ebx = cpu->hyperv_spinlock_attempts;
 
         c = &cpuid_data.entries[cpuid_i++];
@@ -1015,7 +1020,7 @@ int kvm_arch_init_vcpu(CPUState *cs)
 
             c = &cpuid_data.entries[cpuid_i++];
             c->function = HV_CPUID_NESTED_FEATURES;
-            c->eax = evmcs_version;
+            c->eax = env->features[FEAT_HV_NESTED_EAX];
         }
     }
 
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 601ef4f64c..f403a6571d 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -88,8 +88,7 @@ check-unit-y += tests/test-rcu-simpleq$(EXESUF)
 check-unit-y += tests/test-rcu-tailq$(EXESUF)
 check-unit-y += tests/test-qdist$(EXESUF)
 check-unit-y += tests/test-qht$(EXESUF)
-# FIXME: {test-qht-par + gprof} often break on Travis CI
-check-unit-$(call lnot,$(CONFIG_GPROF)) += tests/test-qht-par$(EXESUF)
+check-unit-y += tests/test-qht-par$(EXESUF)
 check-unit-y += tests/test-bitops$(EXESUF)
 check-unit-y += tests/test-bitcnt$(EXESUF)
 check-unit-y += tests/test-qdev-global-props$(EXESUF)
diff --git a/tests/atomic64-bench.c b/tests/atomic64-bench.c
index 71692560ed..121a8c14f4 100644
--- a/tests/atomic64-bench.c
+++ b/tests/atomic64-bench.c
@@ -74,16 +74,14 @@ static void *thread_func(void *arg)
 
 static void run_test(void)
 {
-    unsigned int remaining;
     unsigned int i;
 
     while (atomic_read(&n_ready_threads) != n_threads) {
         cpu_relax();
     }
+
     atomic_set(&test_start, true);
-    do {
-        remaining = sleep(duration);
-    } while (remaining);
+    g_usleep(duration * G_USEC_PER_SEC);
     atomic_set(&test_stop, true);
 
     for (i = 0; i < n_threads; i++) {
diff --git a/tests/atomic_add-bench.c b/tests/atomic_add-bench.c
index 2f6c72f63a..5666f6bbff 100644
--- a/tests/atomic_add-bench.c
+++ b/tests/atomic_add-bench.c
@@ -76,16 +76,14 @@ static void *thread_func(void *arg)
 
 static void run_test(void)
 {
-    unsigned int remaining;
     unsigned int i;
 
     while (atomic_read(&n_ready_threads) != n_threads) {
         cpu_relax();
     }
+
     atomic_set(&test_start, true);
-    do {
-        remaining = sleep(duration);
-    } while (remaining);
+    g_usleep(duration * G_USEC_PER_SEC);
     atomic_set(&test_stop, true);
 
     for (i = 0; i < n_threads; i++) {
diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include
index 9467e9d088..7032c68895 100644
--- a/tests/docker/Makefile.include
+++ b/tests/docker/Makefile.include
@@ -98,19 +98,6 @@ docker-image-debian-s390x-cross: docker-image-debian9
 docker-image-debian-win32-cross: docker-image-debian8-mxe
 docker-image-debian-win64-cross: docker-image-debian8-mxe
 
-# Debian SID images - we are tracking a rolling distro so we want to
-# force a re-build of the base image if we ever need to build one of
-# its children.
-ifndef SKIP_DOCKER_BUILD
-ifeq ($(HAVE_USER_DOCKER),y)
-SID_AGE=$(shell $(DOCKER_SCRIPT) check --checktype=age --olderthan=180 --quiet qemu:debian-sid)
-ifeq ($(SID_AGE),)
-else
-docker-image-debian-sid: NOCACHE=1
-endif
-endif
-endif
-
 docker-image-debian-alpha-cross: docker-image-debian-sid
 docker-image-debian-hppa-cross: docker-image-debian-sid
 docker-image-debian-m68k-cross: docker-image-debian-sid
diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker
index 24b113b76f..954fcf9606 100644
--- a/tests/docker/dockerfiles/debian-amd64.docker
+++ b/tests/docker/dockerfiles/debian-amd64.docker
@@ -24,7 +24,8 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
         libegl1-mesa-dev \
         libepoxy-dev \
         libgbm-dev
-RUN git clone https://anongit.freedesktop.org/git/virglrenderer.git /usr/src/virglrenderer
+RUN git clone https://anongit.freedesktop.org/git/virglrenderer.git /usr/src/virglrenderer && \
+    cd /usr/src/virglrenderer && git checkout virglrenderer-0.7.0
 RUN cd /usr/src/virglrenderer && ./autogen.sh && ./configure --with-glx --disable-tests && make install
 
 # netmap
@@ -35,5 +36,7 @@ RUN git clone https://github.com/luigirizzo/netmap.git /usr/src/netmap
 RUN cd /usr/src/netmap/LINUX && ./configure --no-drivers --no-apps --kernel-dir=$(ls -d /usr/src/linux-headers-*-amd64) && make install
 ENV QEMU_CONFIGURE_OPTS --enable-netmap
 
+RUN ldconfig
+
 # gcrypt
 ENV QEMU_CONFIGURE_OPTS $QEMU_CONFIGURE_OPTS --enable-gcrypt
diff --git a/tests/docker/dockerfiles/debian-sid.docker b/tests/docker/dockerfiles/debian-sid.docker
index 4e4cda0ba5..676941cb32 100644
--- a/tests/docker/dockerfiles/debian-sid.docker
+++ b/tests/docker/dockerfiles/debian-sid.docker
@@ -11,7 +11,12 @@
 # updated and trigger a re-build.
 #
 
-FROM debian:sid-slim
+# This must be earlier than the snapshot date we are aiming for
+FROM debian:sid-20181011-slim
+
+# Use a snapshot known to work (see http://snapshot.debian.org/#Usage)
+ENV DEBIAN_SNAPSHOT_DATE "20181030"
+RUN sed -i "s%^deb \(https\?://\)deb.debian.org/debian/\? \(.*\)%deb [check-valid-until=no] \1snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT_DATE} \2%" /etc/apt/sources.list
 
 # Use a snapshot known to work (see http://snapshot.debian.org/#Usage)
 ENV DEBIAN_SNAPSHOT_DATE "20181030"
diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker
deleted file mode 100644
index fd32e71b79..0000000000
--- a/tests/docker/dockerfiles/debian.docker
+++ /dev/null
@@ -1,13 +0,0 @@
-# This template is deprecated and was previously based on Jessie on QEMU 2.9.
-# Now than Stretch is out, please use qemu:debian8 as base for Jessie,
-# and qemu:debian9 for Stretch.
-#
-FROM qemu:debian9
-
-MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
-
-RUN for n in $(seq 8); do echo; done && \
-    echo "\n\t\tThis image is deprecated." && echo && \
-    echo "\tUse 'FROM qemu:debian9' to use the stable Debian Stretch image" && \
-    echo "\tor 'FROM qemu:debian8' to use old Debian Jessie." && \
-    for n in $(seq 8); do echo; done
diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker
index a4fd895b07..eb8108d118 100644
--- a/tests/docker/dockerfiles/fedora-i386-cross.docker
+++ b/tests/docker/dockerfiles/fedora-i386-cross.docker
@@ -1,4 +1,4 @@
-FROM fedora:latest
+FROM fedora:29
 ENV PACKAGES \
     gcc \
     glib2-devel.i686 \
diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker
index 1d0e3dc4ec..69d4a7f5d7 100644
--- a/tests/docker/dockerfiles/fedora.docker
+++ b/tests/docker/dockerfiles/fedora.docker
@@ -1,4 +1,4 @@
-FROM fedora:28
+FROM fedora:29
 ENV PACKAGES \
     bc \
     bison \
@@ -82,7 +82,7 @@ ENV PACKAGES \
     tar \
     usbredir-devel \
     virglrenderer-devel \
-    vte3-devel \
+    vte291-devel \
     which \
     xen-devel \
     zlib-devel
diff --git a/tests/docker/dockerfiles/travis.docker b/tests/docker/dockerfiles/travis.docker
index 03ebfb0ef2..e72dc85ca7 100644
--- a/tests/docker/dockerfiles/travis.docker
+++ b/tests/docker/dockerfiles/travis.docker
@@ -1,8 +1,8 @@
-FROM travisci/ci-garnet:packer-1512502276-986baf0
+FROM travisci/ci-sardonyx:packer-1546978056-2c98a19
 ENV DEBIAN_FRONTEND noninteractive
 ENV LANG en_US.UTF-8
 ENV LC_ALL en_US.UTF-8
-RUN cat /etc/apt/sources.list | sed "s/# deb-src/deb-src/" >> /etc/apt/sources.list
+RUN sed -i "s/# deb-src/deb-src/" /etc/apt/sources.list
 RUN apt-get update
 RUN apt-get -y build-dep qemu
 RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools gcovr
diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206
index 128c334c7c..5bb738bf23 100755
--- a/tests/qemu-iotests/206
+++ b/tests/qemu-iotests/206
@@ -26,7 +26,9 @@ from iotests import imgfmt
 iotests.verify_image_format(supported_fmts=['qcow2'])
 
 def blockdev_create(vm, options):
-    result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+    result = vm.qmp_log('blockdev-create',
+                        filters=[iotests.filter_qmp_testfiles],
+                        job_id='job0', options=options)
 
     if 'return' in result:
         assert result['return'] == {}
@@ -52,7 +54,9 @@ with iotests.FilePath('t.qcow2') as disk_path, \
                           'filename': disk_path,
                           'size': 0 })
 
-    vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
+    vm.qmp_log('blockdev-add',
+               filters=[iotests.filter_qmp_testfiles],
+               driver='file', filename=disk_path,
                node_name='imgfile')
 
     blockdev_create(vm, { 'driver': imgfmt,
diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223
index 397b865d34..773892dbe6 100755
--- a/tests/qemu-iotests/223
+++ b/tests/qemu-iotests/223
@@ -25,6 +25,7 @@ status=1 # failure is the default!
 
 _cleanup()
 {
+    nbd_server_stop
     _cleanup_test_img
     _cleanup_qemu
     rm -f "$TEST_DIR/nbd"
@@ -35,6 +36,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 . ./common.rc
 . ./common.filter
 . ./common.qemu
+. ./common.nbd
 
 _supported_fmt qcow2
 _supported_proto file # uses NBD as well
@@ -61,6 +63,8 @@ echo "=== Create partially sparse image, then add dirty bitmaps ==="
 echo
 
 # Two bitmaps, to contrast granularity issues
+# Also note that b will be disabled, while b2 is left enabled, to
+# check for read-only interactions
 _make_test_img -o cluster_size=4k 4M
 $QEMU_IO -c 'w -P 0x11 1M 2M' "$TEST_IMG" | _filter_qemu_io
 run_qemu <<EOF
@@ -107,26 +111,37 @@ echo
 
 _launch_qemu 2> >(_filter_nbd)
 
+# Intentionally provoke some errors as well, to check error handling
 silent=
 _send_qemu_cmd $QEMU_HANDLE '{"execute":"qmp_capabilities"}' "return"
 _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add",
   "arguments":{"driver":"qcow2", "node-name":"n",
     "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return"
-_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable",
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-disable",
   "arguments":{"node":"n", "name":"b"}}' "return"
-_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable",
-  "arguments":{"node":"n", "name":"b2"}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
+  "arguments":{"device":"n"}}' "error" # Attempt add without server
 _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
   "arguments":{"addr":{"type":"unix",
     "data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
+  "arguments":{"addr":{"type":"unix",
+    "data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
+  "arguments":{"device":"n", "bitmap":"b"}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
+  "arguments":{"device":"nosuch"}}' "error" # Attempt to export missing node
 _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
-  "arguments":{"device":"n"}}' "return"
-_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap",
-  "arguments":{"name":"n", "bitmap":"b"}}' "return"
+  "arguments":{"device":"n"}}' "error" # Attempt to export same name twice
 _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
-  "arguments":{"device":"n", "name":"n2"}}' "return"
-_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap",
-  "arguments":{"name":"n2", "bitmap":"b2"}}' "return"
+  "arguments":{"device":"n", "name":"n2",
+  "bitmap":"b2"}}' "error" # enabled vs. read-only
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
+  "arguments":{"device":"n", "name":"n2",
+  "bitmap":"b3"}}' "error" # Missing bitmap
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
+  "arguments":{"device":"n", "name":"n2", "writable":true,
+  "bitmap":"b2"}}' "return"
 
 echo
 echo "=== Contrast normal status to large granularity dirty-bitmap ==="
@@ -150,16 +165,33 @@ $QEMU_IMG map --output=json --image-opts \
   "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map
 
 echo
-echo "=== End NBD server ==="
+echo "=== End qemu NBD server ==="
 echo
 
 _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove",
   "arguments":{"name":"n"}}' "return"
 _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove",
   "arguments":{"name":"n2"}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove",
+  "arguments":{"name":"n2"}}' "error" # Attempt duplicate clean
 _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "error" # Again
 _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return"
 
+echo
+echo "=== Use qemu-nbd as server ==="
+echo
+
+nbd_server_start_unix_socket -r -f $IMGFMT -B b "$TEST_IMG"
+IMG="driver=nbd,server.type=unix,server.path=$nbd_unix_socket"
+$QEMU_IMG map --output=json --image-opts \
+  "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map
+
+nbd_server_start_unix_socket -f $IMGFMT -B b2 "$TEST_IMG"
+IMG="driver=nbd,server.type=unix,server.path=$nbd_unix_socket"
+$QEMU_IMG map --output=json --image-opts \
+  "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map
+
 # success, all done
 echo '*** done'
 rm -f $seq.full
diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out
index 99ca172fbb..0de5240a75 100644
--- a/tests/qemu-iotests/223.out
+++ b/tests/qemu-iotests/223.out
@@ -27,11 +27,14 @@ wrote 2097152/2097152 bytes at offset 2097152
 {"return": {}}
 {"return": {}}
 {"return": {}}
+{"error": {"class": "GenericError", "desc": "NBD server not running"}}
 {"return": {}}
+{"error": {"class": "GenericError", "desc": "NBD server already running"}}
 {"return": {}}
-{"return": {}}
-{"return": {}}
-{"return": {}}
+{"error": {"class": "GenericError", "desc": "Cannot find device=nosuch nor node_name=nosuch"}}
+{"error": {"class": "GenericError", "desc": "NBD server already has export named 'n'"}}
+{"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}}
+{"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}}
 {"return": {}}
 
 === Contrast normal status to large granularity dirty-bitmap ===
@@ -58,10 +61,22 @@ read 2097152/2097152 bytes at offset 2097152
 { "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true},
 { "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}]
 
-=== End NBD server ===
+=== End qemu NBD server ===
 
 {"return": {}}
 {"return": {}}
+{"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}}
 {"return": {}}
+{"error": {"class": "GenericError", "desc": "NBD server not running"}}
 {"return": {}}
+
+=== Use qemu-nbd as server ===
+
+[{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": false},
+{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true},
+{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}]
+[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true},
+{ "start": 512, "length": 512, "depth": 0, "zero": false, "data": false},
+{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true},
+{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}]
 *** done
diff --git a/tests/qemu-iotests/236 b/tests/qemu-iotests/236
new file mode 100755
index 0000000000..79a6381f8e
--- /dev/null
+++ b/tests/qemu-iotests/236
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+#
+# Test bitmap merges.
+#
+# Copyright (c) 2018 John Snow for 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/>.
+#
+# owner=jsnow@redhat.com
+
+import iotests
+from iotests import log
+
+iotests.verify_image_format(supported_fmts=['generic'])
+size = 64 * 1024 * 1024
+granularity = 64 * 1024
+
+patterns = [("0x5d", "0",         "64k"),
+            ("0xd5", "1M",        "64k"),
+            ("0xdc", "32M",       "64k"),
+            ("0xcd", "0x3ff0000", "64k")]  # 64M - 64K
+
+overwrite = [("0xab", "0",         "64k"), # Full overwrite
+             ("0xad", "0x00f8000", "64k"), # Partial-left (1M-32K)
+             ("0x1d", "0x2008000", "64k"), # Partial-right (32M+32K)
+             ("0xea", "0x3fe0000", "64k")] # Adjacent-left (64M - 128K)
+
+def query_bitmaps(vm):
+    res = vm.qmp("query-block")
+    return { "bitmaps": { device['device']: device.get('dirty-bitmaps', []) for
+                          device in res['return'] } }
+
+with iotests.FilePath('img') as img_path, \
+     iotests.VM() as vm:
+
+    log('--- Preparing image & VM ---\n')
+    iotests.qemu_img_create('-f', iotests.imgfmt, img_path, str(size))
+    vm.add_drive(img_path)
+    vm.launch()
+
+    log('\n--- Adding preliminary bitmaps A & B ---\n')
+    vm.qmp_log("block-dirty-bitmap-add", node="drive0",
+               name="bitmapA", granularity=granularity)
+    vm.qmp_log("block-dirty-bitmap-add", node="drive0",
+               name="bitmapB", granularity=granularity)
+
+    # Dirties 4 clusters. count=262144
+    log('\n--- Emulating writes ---\n')
+    for p in patterns:
+        cmd = "write -P%s %s %s" % p
+        log(cmd)
+        log(vm.hmp_qemu_io("drive0", cmd))
+
+    log(query_bitmaps(vm), indent=2)
+
+    log('\n--- Submitting & Aborting Transaction ---\n')
+    vm.qmp_log("transaction", indent=2, actions=[
+        { "type": "block-dirty-bitmap-disable",
+          "data": { "node": "drive0", "name": "bitmapB" }},
+        { "type": "block-dirty-bitmap-add",
+          "data": { "node": "drive0", "name": "bitmapC",
+                    "granularity": granularity }},
+        { "type": "block-dirty-bitmap-clear",
+          "data": { "node": "drive0", "name": "bitmapA" }},
+        { "type": "abort", "data": {}}
+    ])
+    log(query_bitmaps(vm), indent=2)
+
+    log('\n--- Disabling B & Adding C ---\n')
+    vm.qmp_log("transaction", indent=2, actions=[
+        { "type": "block-dirty-bitmap-disable",
+          "data": { "node": "drive0", "name": "bitmapB" }},
+        { "type": "block-dirty-bitmap-add",
+          "data": { "node": "drive0", "name": "bitmapC",
+                    "granularity": granularity }},
+        # Purely extraneous, but test that it works:
+        { "type": "block-dirty-bitmap-disable",
+          "data": { "node": "drive0", "name": "bitmapC" }},
+        { "type": "block-dirty-bitmap-enable",
+          "data": { "node": "drive0", "name": "bitmapC" }},
+    ])
+
+    log('\n--- Emulating further writes ---\n')
+    # Dirties 6 clusters, 3 of which are new in contrast to "A".
+    # A = 64 * 1024 * (4 + 3) = 458752
+    # C = 64 * 1024 * 6       = 393216
+    for p in overwrite:
+        cmd = "write -P%s %s %s" % p
+        log(cmd)
+        log(vm.hmp_qemu_io("drive0", cmd))
+
+    log('\n--- Disabling A & C ---\n')
+    vm.qmp_log("transaction", indent=2, actions=[
+        { "type": "block-dirty-bitmap-disable",
+          "data": { "node": "drive0", "name": "bitmapA" }},
+        { "type": "block-dirty-bitmap-disable",
+          "data": { "node": "drive0", "name": "bitmapC" }}
+    ])
+
+    # A: 7 clusters
+    # B: 4 clusters
+    # C: 6 clusters
+    log(query_bitmaps(vm), indent=2)
+
+    log('\n--- Submitting & Aborting Merge Transaction ---\n')
+    vm.qmp_log("transaction", indent=2, actions=[
+        { "type": "block-dirty-bitmap-add",
+          "data": { "node": "drive0", "name": "bitmapD",
+                    "disabled": True, "granularity": granularity }},
+        { "type": "block-dirty-bitmap-merge",
+          "data": { "node": "drive0", "target": "bitmapD",
+                    "bitmaps": ["bitmapB", "bitmapC"] }},
+        { "type": "abort", "data": {}}
+    ])
+    log(query_bitmaps(vm), indent=2)
+
+    log('\n--- Creating D as a merge of B & C ---\n')
+    # Good hygiene: create a disabled bitmap as a merge target.
+    vm.qmp_log("transaction", indent=2, actions=[
+        { "type": "block-dirty-bitmap-add",
+          "data": { "node": "drive0", "name": "bitmapD",
+                    "disabled": True, "granularity": granularity }},
+        { "type": "block-dirty-bitmap-merge",
+          "data": { "node": "drive0", "target": "bitmapD",
+                    "bitmaps": ["bitmapB", "bitmapC"] }}
+    ])
+
+    # A and D should now both have 7 clusters apiece.
+    # B and C remain unchanged with 4 and 6 respectively.
+    log(query_bitmaps(vm), indent=2)
+
+    # A and D should be equivalent.
+    # Some formats round the size of the disk, so don't print the checksums.
+    check_a = vm.qmp('x-debug-block-dirty-bitmap-sha256',
+                     node="drive0", name="bitmapA")['return']['sha256']
+    check_d = vm.qmp('x-debug-block-dirty-bitmap-sha256',
+                     node="drive0", name="bitmapD")['return']['sha256']
+    assert(check_a == check_d)
+
+    log('\n--- Removing bitmaps A, B, C, and D ---\n')
+    vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapA")
+    vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapB")
+    vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapC")
+    vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapD")
+
+    log('\n--- Final Query ---\n')
+    log(query_bitmaps(vm), indent=2)
+
+    log('\n--- Done ---\n')
+    vm.shutdown()
diff --git a/tests/qemu-iotests/236.out b/tests/qemu-iotests/236.out
new file mode 100644
index 0000000000..1dad24db0d
--- /dev/null
+++ b/tests/qemu-iotests/236.out
@@ -0,0 +1,351 @@
+--- Preparing image & VM ---
+
+
+--- Adding preliminary bitmaps A & B ---
+
+{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmapA", "node": "drive0"}}
+{"return": {}}
+{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmapB", "node": "drive0"}}
+{"return": {}}
+
+--- Emulating writes ---
+
+write -P0x5d 0 64k
+{"return": ""}
+write -P0xd5 1M 64k
+{"return": ""}
+write -P0xdc 32M 64k
+{"return": ""}
+write -P0xcd 0x3ff0000 64k
+{"return": ""}
+{
+  "bitmaps": {
+    "drive0": [
+      {
+        "count": 262144,
+        "granularity": 65536,
+        "name": "bitmapB",
+        "status": "active"
+      },
+      {
+        "count": 262144,
+        "granularity": 65536,
+        "name": "bitmapA",
+        "status": "active"
+      }
+    ]
+  }
+}
+
+--- Submitting & Aborting Transaction ---
+
+{
+  "execute": "transaction",
+  "arguments": {
+    "actions": [
+      {
+        "data": {
+          "node": "drive0",
+          "name": "bitmapB"
+        },
+        "type": "block-dirty-bitmap-disable"
+      },
+      {
+        "data": {
+          "node": "drive0",
+          "name": "bitmapC",
+          "granularity": 65536
+        },
+        "type": "block-dirty-bitmap-add"
+      },
+      {
+        "data": {
+          "node": "drive0",
+          "name": "bitmapA"
+        },
+        "type": "block-dirty-bitmap-clear"
+      },
+      {
+        "data": {},
+        "type": "abort"
+      }
+    ]
+  }
+}
+{
+  "error": {
+    "class": "GenericError",
+    "desc": "Transaction aborted using Abort action"
+  }
+}
+{
+  "bitmaps": {
+    "drive0": [
+      {
+        "count": 262144,
+        "granularity": 65536,
+        "name": "bitmapB",
+        "status": "active"
+      },
+      {
+        "count": 262144,
+        "granularity": 65536,
+        "name": "bitmapA",
+        "status": "active"
+      }
+    ]
+  }
+}
+
+--- Disabling B & Adding C ---
+
+{
+  "execute": "transaction",
+  "arguments": {
+    "actions": [
+      {
+        "data": {
+          "node": "drive0",
+          "name": "bitmapB"
+        },
+        "type": "block-dirty-bitmap-disable"
+      },
+      {
+        "data": {
+          "node": "drive0",
+          "name": "bitmapC",
+          "granularity": 65536
+        },
+        "type": "block-dirty-bitmap-add"
+      },
+      {
+        "data": {
+          "node": "drive0",
+          "name": "bitmapC"
+        },
+        "type": "block-dirty-bitmap-disable"
+      },
+      {
+        "data": {
+          "node": "drive0",
+          "name": "bitmapC"
+        },
+        "type": "block-dirty-bitmap-enable"
+      }
+    ]
+  }
+}
+{
+  "return": {}
+}
+
+--- Emulating further writes ---
+
+write -P0xab 0 64k
+{"return": ""}
+write -P0xad 0x00f8000 64k
+{"return": ""}
+write -P0x1d 0x2008000 64k
+{"return": ""}
+write -P0xea 0x3fe0000 64k
+{"return": ""}
+
+--- Disabling A & C ---
+
+{
+  "execute": "transaction",
+  "arguments": {
+    "actions": [
+      {
+        "data": {
+          "node": "drive0",
+          "name": "bitmapA"
+        },
+        "type": "block-dirty-bitmap-disable"
+      },
+      {
+        "data": {
+          "node": "drive0",
+          "name": "bitmapC"
+        },
+        "type": "block-dirty-bitmap-disable"
+      }
+    ]
+  }
+}
+{
+  "return": {}
+}
+{
+  "bitmaps": {
+    "drive0": [
+      {
+        "count": 393216,
+        "granularity": 65536,
+        "name": "bitmapC",
+        "status": "disabled"
+      },
+      {
+        "count": 262144,
+        "granularity": 65536,
+        "name": "bitmapB",
+        "status": "disabled"
+      },
+      {
+        "count": 458752,
+        "granularity": 65536,
+        "name": "bitmapA",
+        "status": "disabled"
+      }
+    ]
+  }
+}
+
+--- Submitting & Aborting Merge Transaction ---
+
+{
+  "execute": "transaction",
+  "arguments": {
+    "actions": [
+      {
+        "data": {
+          "node": "drive0",
+          "disabled": true,
+          "name": "bitmapD",
+          "granularity": 65536
+        },
+        "type": "block-dirty-bitmap-add"
+      },
+      {
+        "data": {
+          "node": "drive0",
+          "target": "bitmapD",
+          "bitmaps": [
+            "bitmapB",
+            "bitmapC"
+          ]
+        },
+        "type": "block-dirty-bitmap-merge"
+      },
+      {
+        "data": {},
+        "type": "abort"
+      }
+    ]
+  }
+}
+{
+  "error": {
+    "class": "GenericError",
+    "desc": "Transaction aborted using Abort action"
+  }
+}
+{
+  "bitmaps": {
+    "drive0": [
+      {
+        "count": 393216,
+        "granularity": 65536,
+        "name": "bitmapC",
+        "status": "disabled"
+      },
+      {
+        "count": 262144,
+        "granularity": 65536,
+        "name": "bitmapB",
+        "status": "disabled"
+      },
+      {
+        "count": 458752,
+        "granularity": 65536,
+        "name": "bitmapA",
+        "status": "disabled"
+      }
+    ]
+  }
+}
+
+--- Creating D as a merge of B & C ---
+
+{
+  "execute": "transaction",
+  "arguments": {
+    "actions": [
+      {
+        "data": {
+          "node": "drive0",
+          "disabled": true,
+          "name": "bitmapD",
+          "granularity": 65536
+        },
+        "type": "block-dirty-bitmap-add"
+      },
+      {
+        "data": {
+          "node": "drive0",
+          "target": "bitmapD",
+          "bitmaps": [
+            "bitmapB",
+            "bitmapC"
+          ]
+        },
+        "type": "block-dirty-bitmap-merge"
+      }
+    ]
+  }
+}
+{
+  "return": {}
+}
+{
+  "bitmaps": {
+    "drive0": [
+      {
+        "count": 458752,
+        "granularity": 65536,
+        "name": "bitmapD",
+        "status": "disabled"
+      },
+      {
+        "count": 393216,
+        "granularity": 65536,
+        "name": "bitmapC",
+        "status": "disabled"
+      },
+      {
+        "count": 262144,
+        "granularity": 65536,
+        "name": "bitmapB",
+        "status": "disabled"
+      },
+      {
+        "count": 458752,
+        "granularity": 65536,
+        "name": "bitmapA",
+        "status": "disabled"
+      }
+    ]
+  }
+}
+
+--- Removing bitmaps A, B, C, and D ---
+
+{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapA", "node": "drive0"}}
+{"return": {}}
+{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapB", "node": "drive0"}}
+{"return": {}}
+{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapC", "node": "drive0"}}
+{"return": {}}
+{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapD", "node": "drive0"}}
+{"return": {}}
+
+--- Final Query ---
+
+{
+  "bitmaps": {
+    "drive0": []
+  }
+}
+
+--- Done ---
+
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 61a6d98ebd..f6b245917a 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -233,3 +233,4 @@
 233 auto quick
 234 auto quick migration
 235 auto quick
+236 auto quick
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index d537538ba0..cbedfaf1df 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -30,6 +30,7 @@ import signal
 import logging
 import atexit
 import io
+from collections import OrderedDict
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts'))
 import qtest
@@ -63,7 +64,7 @@ socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
 debug = False
 
 luks_default_secret_object = 'secret,id=keysec0,data=' + \
-                             os.environ['IMGKEYSECRET']
+                             os.environ.get('IMGKEYSECRET', '')
 luks_default_key_secret_opt = 'key-secret=keysec0'
 
 
@@ -75,6 +76,16 @@ def qemu_img(*args):
         sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
     return exitcode
 
+def ordered_kwargs(kwargs):
+    # kwargs prior to 3.6 are not ordered, so:
+    od = OrderedDict()
+    for k, v in sorted(kwargs.items()):
+        if isinstance(v, dict):
+            od[k] = ordered_kwargs(v)
+        else:
+            od[k] = v
+    return od
+
 def qemu_img_create(*args):
     args = list(args)
 
@@ -235,10 +246,36 @@ def filter_qmp_event(event):
         event['timestamp']['microseconds'] = 'USECS'
     return event
 
+def filter_qmp(qmsg, filter_fn):
+    '''Given a string filter, filter a QMP object's values.
+    filter_fn takes a (key, value) pair.'''
+    # Iterate through either lists or dicts;
+    if isinstance(qmsg, list):
+        items = enumerate(qmsg)
+    else:
+        items = qmsg.items()
+
+    for k, v in items:
+        if isinstance(v, list) or isinstance(v, dict):
+            qmsg[k] = filter_qmp(v, filter_fn)
+        else:
+            qmsg[k] = filter_fn(k, v)
+    return qmsg
+
 def filter_testfiles(msg):
     prefix = os.path.join(test_dir, "%s-" % (os.getpid()))
     return msg.replace(prefix, 'TEST_DIR/PID-')
 
+def filter_qmp_testfiles(qmsg):
+    def _filter(key, value):
+        if key == 'filename' or key == 'backing-file':
+            return filter_testfiles(value)
+        return value
+    return filter_qmp(qmsg, _filter)
+
+def filter_generated_node_ids(msg):
+    return re.sub("#block[0-9]+", "NODE_NAME", msg)
+
 def filter_img_info(output, filename):
     lines = []
     for line in output.split('\n'):
@@ -251,11 +288,18 @@ def filter_img_info(output, filename):
         lines.append(line)
     return '\n'.join(lines)
 
-def log(msg, filters=[]):
+def log(msg, filters=[], indent=None):
+    '''Logs either a string message or a JSON serializable message (like QMP).
+    If indent is provided, JSON serializable messages are pretty-printed.'''
     for flt in filters:
         msg = flt(msg)
-    if type(msg) is dict or type(msg) is list:
-        print(json.dumps(msg, sort_keys=True))
+    if isinstance(msg, dict) or isinstance(msg, list):
+        # Python < 3.4 needs to know not to add whitespace when pretty-printing:
+        separators = (', ', ': ') if indent is None else (',', ': ')
+        # Don't sort if it's already sorted
+        do_sort = not isinstance(msg, OrderedDict)
+        print(json.dumps(msg, sort_keys=do_sort,
+                         indent=indent, separators=separators))
     else:
         print(msg)
 
@@ -444,12 +488,14 @@ class VM(qtest.QEMUQtestMachine):
             result.append(filter_qmp_event(ev))
         return result
 
-    def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs):
-        logmsg = '{"execute": "%s", "arguments": %s}' % \
-            (cmd, json.dumps(kwargs, sort_keys=True))
-        log(logmsg, filters)
+    def qmp_log(self, cmd, filters=[], indent=None, **kwargs):
+        full_cmd = OrderedDict((
+            ("execute", cmd),
+            ("arguments", ordered_kwargs(kwargs))
+        ))
+        log(full_cmd, filters, indent=indent)
         result = self.qmp(cmd, **kwargs)
-        log(json.dumps(result, sort_keys=True), filters)
+        log(result, filters, indent=indent)
         return result
 
     def run_job(self, job, auto_finalize=True, auto_dismiss=False):
diff --git a/tests/qht-bench.c b/tests/qht-bench.c
index ab4e708180..e3b512f26f 100644
--- a/tests/qht-bench.c
+++ b/tests/qht-bench.c
@@ -398,16 +398,14 @@ static void pr_stats(void)
 
 static void run_test(void)
 {
-    unsigned int remaining;
     int i;
 
     while (atomic_read(&n_ready_threads) != n_rw_threads + n_rz_threads) {
         cpu_relax();
     }
+
     atomic_set(&test_start, true);
-    do {
-        remaining = sleep(duration);
-    } while (remaining);
+    g_usleep(duration * G_USEC_PER_SEC);
     atomic_set(&test_stop, true);
 
     for (i = 0; i < n_rw_threads; i++) {
diff --git a/vl.c b/vl.c
index 9b8ea3f654..bc9fbec654 100644
--- a/vl.c
+++ b/vl.c
@@ -3856,13 +3856,6 @@ int main(int argc, char **argv, char **envp)
                 }
                 xen_domid = atoi(optarg);
                 break;
-            case QEMU_OPTION_xen_create:
-                if (!(xen_available())) {
-                    error_report("Option not supported for this target");
-                    exit(1);
-                }
-                xen_mode = XEN_CREATE;
-                break;
             case QEMU_OPTION_xen_attach:
                 if (!(xen_available())) {
                     error_report("Option not supported for this target");