summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS6
-rw-r--r--block-migration.c44
-rw-r--r--block.c390
-rw-r--r--block/Makefile.objs2
-rw-r--r--block/archipelago.c28
-rw-r--r--block/backup.c2
-rw-r--r--block/blkdebug.c18
-rw-r--r--block/blkverify.c18
-rw-r--r--block/block-backend.c631
-rw-r--r--block/commit.c2
-rw-r--r--block/curl.c6
-rw-r--r--block/iscsi.c10
-rw-r--r--block/linux-aio.c8
-rw-r--r--block/mirror.c9
-rw-r--r--block/nbd.c44
-rw-r--r--block/null.c34
-rw-r--r--block/qapi.c27
-rw-r--r--block/qcow.c4
-rw-r--r--block/qcow2-refcount.c1
-rw-r--r--block/qcow2.c4
-rw-r--r--block/qed-gencb.c4
-rw-r--r--block/qed-table.c10
-rw-r--r--block/qed.c46
-rw-r--r--block/qed.h12
-rw-r--r--block/quorum.c42
-rw-r--r--block/raw-aio.h8
-rw-r--r--block/raw-posix.c38
-rw-r--r--block/raw-win32.c16
-rw-r--r--block/raw_bsd.c8
-rw-r--r--block/rbd.c56
-rw-r--r--block/sheepdog.c4
-rw-r--r--block/stream.c2
-rw-r--r--block/vdi.c2
-rw-r--r--block/vhdx.c2
-rw-r--r--block/vmdk.c4
-rw-r--r--block/vpc.c2
-rw-r--r--block/vvfat.c4
-rw-r--r--block/win32-aio.c6
-rw-r--r--blockdev.c197
-rw-r--r--blockjob.c7
-rwxr-xr-xconfigure6
-rw-r--r--device-hotplug.c3
-rw-r--r--disas/mips.c211
-rw-r--r--dma-helpers.c67
-rw-r--r--docs/blkdebug.txt8
-rw-r--r--fpu/softfloat.c37
-rw-r--r--hw/arm/collie.c10
-rw-r--r--hw/arm/gumstix.c6
-rw-r--r--hw/arm/highbank.c2
-rw-r--r--hw/arm/mainstone.c8
-rw-r--r--hw/arm/musicpal.c13
-rw-r--r--hw/arm/nseries.c7
-rw-r--r--hw/arm/omap1.c4
-rw-r--r--hw/arm/omap2.c4
-rw-r--r--hw/arm/omap_sx1.c10
-rw-r--r--hw/arm/pxa2xx.c7
-rw-r--r--hw/arm/realview.c2
-rw-r--r--hw/arm/spitz.c6
-rw-r--r--hw/arm/tosa.c3
-rw-r--r--hw/arm/versatilepb.c5
-rw-r--r--hw/arm/vexpress.c5
-rw-r--r--hw/arm/virt.c4
-rw-r--r--hw/arm/xilinx_zynq.c5
-rw-r--r--hw/arm/z2.c8
-rw-r--r--hw/block/block.c23
-rw-r--r--hw/block/dataplane/virtio-blk.c35
-rw-r--r--hw/block/dataplane/virtio-blk.h2
-rw-r--r--hw/block/fdc.c74
-rw-r--r--hw/block/hd-geometry.c24
-rw-r--r--hw/block/m25p80.c31
-rw-r--r--hw/block/nand.c50
-rw-r--r--hw/block/nvme.c19
-rw-r--r--hw/block/nvme.h2
-rw-r--r--hw/block/onenand.c67
-rw-r--r--hw/block/pflash_cfi01.c24
-rw-r--r--hw/block/pflash_cfi02.c24
-rw-r--r--hw/block/virtio-blk.c149
-rw-r--r--hw/block/xen_disk.c72
-rw-r--r--hw/core/qdev-properties-system.c26
-rw-r--r--hw/core/qdev-properties.c2
-rw-r--r--hw/cris/axis_dev88.c4
-rw-r--r--hw/display/qxl.c5
-rw-r--r--hw/display/tc6393xb.c4
-rw-r--r--hw/i386/pc.c2
-rw-r--r--hw/i386/pc_piix.c2
-rw-r--r--hw/i386/pc_sysfw.c10
-rw-r--r--hw/i386/xen/xen_platform.c5
-rw-r--r--hw/ide/ahci.c33
-rw-r--r--hw/ide/ahci.h2
-rw-r--r--hw/ide/atapi.c33
-rw-r--r--hw/ide/cmd646.c2
-rw-r--r--hw/ide/core.c198
-rw-r--r--hw/ide/ich.c2
-rw-r--r--hw/ide/internal.h16
-rw-r--r--hw/ide/isa.c2
-rw-r--r--hw/ide/macio.c50
-rw-r--r--hw/ide/microdrive.c4
-rw-r--r--hw/ide/mmio.c2
-rw-r--r--hw/ide/pci.c6
-rw-r--r--hw/ide/pci.h2
-rw-r--r--hw/ide/piix.c11
-rw-r--r--hw/ide/qdev.c13
-rw-r--r--hw/ide/via.c2
-rw-r--r--hw/isa/pc87312.c7
-rw-r--r--hw/lm32/lm32_boards.c14
-rw-r--r--hw/lm32/milkymist.c8
-rw-r--r--hw/microblaze/petalogix_ml605_mmu.c6
-rw-r--r--hw/microblaze/petalogix_s3adsp1800_mmu.c6
-rw-r--r--hw/mips/mips_fulong2e.c2
-rw-r--r--hw/mips/mips_jazz.c2
-rw-r--r--hw/mips/mips_malta.c18
-rw-r--r--hw/mips/mips_r4k.c6
-rw-r--r--hw/nvram/spapr_nvram.c17
-rw-r--r--hw/pci/pci-hotplug-old.c11
-rw-r--r--hw/ppc/mac.h2
-rw-r--r--hw/ppc/mac_newworld.c2
-rw-r--r--hw/ppc/mac_oldworld.c2
-rw-r--r--hw/ppc/ppc405_boards.c27
-rw-r--r--hw/ppc/prep.c2
-rw-r--r--hw/ppc/spapr.c4
-rw-r--r--hw/ppc/virtex_ml507.c6
-rw-r--r--hw/s390x/s390-virtio-bus.c2
-rw-r--r--hw/s390x/s390-virtio.c2
-rw-r--r--hw/s390x/virtio-ccw.c2
-rw-r--r--hw/scsi/megasas.c15
-rw-r--r--hw/scsi/scsi-bus.c15
-rw-r--r--hw/scsi/scsi-disk.c184
-rw-r--r--hw/scsi/scsi-generic.c37
-rw-r--r--hw/scsi/virtio-scsi-dataplane.c3
-rw-r--r--hw/scsi/virtio-scsi.c15
-rw-r--r--hw/sd/milkymist-memcard.c7
-rw-r--r--hw/sd/omap_mmc.c8
-rw-r--r--hw/sd/pl181.c3
-rw-r--r--hw/sd/pxa2xx_mmci.c4
-rw-r--r--hw/sd/sd.c60
-rw-r--r--hw/sd/sdhci.c3
-rw-r--r--hw/sd/ssi-sd.c3
-rw-r--r--hw/sh4/r2d.c6
-rw-r--r--hw/sparc/sun4m.c2
-rw-r--r--hw/sparc64/sun4u.c2
-rw-r--r--hw/tpm/tpm_tis.c2
-rw-r--r--hw/tricore/tricore_testboard.c2
-rw-r--r--hw/usb/dev-hid.c174
-rw-r--r--hw/usb/dev-storage.c19
-rw-r--r--hw/usb/hcd-xhci.c6
-rw-r--r--hw/virtio/virtio-pci.c2
-rw-r--r--hw/xen/xen_devconfig.c1
-rw-r--r--hw/xenpv/xen_machine_pv.c2
-rw-r--r--hw/xtensa/xtfpga.c5
-rw-r--r--include/block/aio.h14
-rw-r--r--include/block/block.h90
-rw-r--r--include/block/block_int.h44
-rw-r--r--include/block/blockjob.h4
-rw-r--r--include/block/qapi.h3
-rw-r--r--include/block/thread-pool.h4
-rw-r--r--include/fpu/softfloat.h4
-rw-r--r--include/hw/arm/omap.h4
-rw-r--r--include/hw/arm/pxa.h2
-rw-r--r--include/hw/block/block.h6
-rw-r--r--include/hw/block/flash.h6
-rw-r--r--include/hw/i386/pc.h8
-rw-r--r--include/hw/isa/pc87312.h3
-rw-r--r--include/hw/qdev-properties.h8
-rw-r--r--include/hw/scsi/scsi.h6
-rw-r--r--include/hw/sd.h2
-rw-r--r--include/hw/virtio/virtio-blk.h10
-rw-r--r--include/monitor/monitor.h4
-rw-r--r--include/qemu/typedefs.h1
-rw-r--r--include/sysemu/block-backend.h142
-rw-r--r--include/sysemu/blockdev.h14
-rw-r--r--include/sysemu/dma.h30
-rw-r--r--monitor.c38
-rw-r--r--qemu-img.c150
-rw-r--r--qemu-io.c15
-rw-r--r--qemu-nbd.c8
-rw-r--r--stubs/Makefile.objs1
-rw-r--r--stubs/blockdev.c12
-rw-r--r--target-mips/cpu.h31
-rw-r--r--target-mips/dsp_helper.c26
-rw-r--r--target-mips/helper.h52
-rw-r--r--target-mips/mips-defs.h28
-rw-r--r--target-mips/op_helper.c239
-rw-r--r--target-mips/translate.c3852
-rw-r--r--target-mips/translate_init.c30
-rw-r--r--target-tricore/helper.h7
-rw-r--r--target-tricore/op_helper.c128
-rw-r--r--target-tricore/translate.c1305
-rw-r--r--target-tricore/tricore-opcodes.h4
-rw-r--r--tests/test-thread-pool.c2
-rw-r--r--thread-pool.c10
-rw-r--r--trace-events8
-rw-r--r--ui/gtk.c13
192 files changed, 7449 insertions, 2880 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 206bf7ea45..8eed800a37 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -198,7 +198,9 @@ M: Cornelia Huck <cornelia.huck@de.ibm.com>
 M: Alexander Graf <agraf@suse.de>
 S: Maintained
 F: target-s390x/kvm.c
-F: hw/intc/s390_flic.[hc]
+F: hw/intc/s390_flic.c
+F: hw/intc/s390_flic_kvm.c
+F: include/hw/s390x/s390_flic.h
 
 X86
 M: Marcelo Tosatti <mtosatti@redhat.com>
@@ -521,6 +523,8 @@ F: hw/s390x/s390-virtio-ccw.c
 F: hw/s390x/css.[hc]
 F: hw/s390x/sclp*.[hc]
 F: hw/s390x/ipl*.[hc]
+F: include/hw/s390x/
+F: pc-bios/s390-ccw/
 T: git git://github.com/cohuck/qemu virtio-ccw-upstr
 
 UniCore32 Machines
diff --git a/block-migration.c b/block-migration.c
index 3ad31a2c70..08db01a364 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -14,7 +14,9 @@
  */
 
 #include "qemu-common.h"
-#include "block/block_int.h"
+#include "block/block.h"
+#include "qemu/error-report.h"
+#include "qemu/main-loop.h"
 #include "hw/hw.h"
 #include "qemu/queue.h"
 #include "qemu/timer.h"
@@ -70,7 +72,7 @@ typedef struct BlkMigBlock {
     int nr_sectors;
     struct iovec iov;
     QEMUIOVector qiov;
-    BlockDriverAIOCB *aiocb;
+    BlockAIOCB *aiocb;
 
     /* Protected by block migration lock.  */
     int ret;
@@ -130,9 +132,9 @@ static void blk_send(QEMUFile *f, BlkMigBlock * blk)
                      | flags);
 
     /* device name */
-    len = strlen(blk->bmds->bs->device_name);
+    len = strlen(bdrv_get_device_name(blk->bmds->bs));
     qemu_put_byte(f, len);
-    qemu_put_buffer(f, (uint8_t *)blk->bmds->bs->device_name, len);
+    qemu_put_buffer(f, (uint8_t *)bdrv_get_device_name(blk->bmds->bs), len);
 
     /* if a block is zero we need to flush here since the network
      * bandwidth is now a lot higher than the storage device bandwidth.
@@ -343,12 +345,25 @@ static void unset_dirty_tracking(void)
     }
 }
 
-static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
+static void init_blk_migration(QEMUFile *f)
 {
+    BlockDriverState *bs;
     BlkMigDevState *bmds;
     int64_t sectors;
 
-    if (!bdrv_is_read_only(bs)) {
+    block_mig_state.submitted = 0;
+    block_mig_state.read_done = 0;
+    block_mig_state.transferred = 0;
+    block_mig_state.total_sector_sum = 0;
+    block_mig_state.prev_progress = -1;
+    block_mig_state.bulk_completed = 0;
+    block_mig_state.zero_blocks = migrate_zero_blocks();
+
+    for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) {
+        if (bdrv_is_read_only(bs)) {
+            continue;
+        }
+
         sectors = bdrv_nb_sectors(bs);
         if (sectors <= 0) {
             return;
@@ -369,28 +384,15 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
 
         if (bmds->shared_base) {
             DPRINTF("Start migration for %s with shared base image\n",
-                    bs->device_name);
+                    bdrv_get_device_name(bs));
         } else {
-            DPRINTF("Start full migration for %s\n", bs->device_name);
+            DPRINTF("Start full migration for %s\n", bdrv_get_device_name(bs));
         }
 
         QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry);
     }
 }
 
-static void init_blk_migration(QEMUFile *f)
-{
-    block_mig_state.submitted = 0;
-    block_mig_state.read_done = 0;
-    block_mig_state.transferred = 0;
-    block_mig_state.total_sector_sum = 0;
-    block_mig_state.prev_progress = -1;
-    block_mig_state.bulk_completed = 0;
-    block_mig_state.zero_blocks = migrate_zero_blocks();
-
-    bdrv_iterate(init_blk_migration_it, NULL);
-}
-
 /* Called with no lock taken.  */
 
 static int blk_mig_save_bulked_block(QEMUFile *f)
diff --git a/block.c b/block.c
index 27533f3a81..773e87ef08 100644
--- a/block.c
+++ b/block.c
@@ -28,8 +28,8 @@
 #include "block/blockjob.h"
 #include "qemu/module.h"
 #include "qapi/qmp/qjson.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/sysemu.h"
-#include "sysemu/blockdev.h"    /* FIXME layering violation */
 #include "qemu/notify.h"
 #include "block/coroutine.h"
 #include "block/qapi.h"
@@ -58,15 +58,12 @@ struct BdrvDirtyBitmap {
 
 #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
 
-#define COROUTINE_POOL_RESERVATION 64 /* number of coroutines to reserve */
-
-static void bdrv_dev_change_media_cb(BlockDriverState *bs, bool load);
-static BlockDriverAIOCB *bdrv_aio_readv_em(BlockDriverState *bs,
+static BlockAIOCB *bdrv_aio_readv_em(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque);
-static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs,
+        BlockCompletionFunc *cb, void *opaque);
+static BlockAIOCB *bdrv_aio_writev_em(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque);
+        BlockCompletionFunc *cb, void *opaque);
 static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs,
                                          int64_t sector_num, int nb_sectors,
                                          QEMUIOVector *iov);
@@ -79,14 +76,14 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
 static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
     int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
     BdrvRequestFlags flags);
-static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
-                                               int64_t sector_num,
-                                               QEMUIOVector *qiov,
-                                               int nb_sectors,
-                                               BdrvRequestFlags flags,
-                                               BlockDriverCompletionFunc *cb,
-                                               void *opaque,
-                                               bool is_write);
+static BlockAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
+                                         int64_t sector_num,
+                                         QEMUIOVector *qiov,
+                                         int nb_sectors,
+                                         BdrvRequestFlags flags,
+                                         BlockCompletionFunc *cb,
+                                         void *opaque,
+                                         bool is_write);
 static void coroutine_fn bdrv_co_do_rw(void *opaque);
 static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
     int64_t sector_num, int nb_sectors, BdrvRequestFlags flags);
@@ -335,35 +332,21 @@ void bdrv_register(BlockDriver *bdrv)
     QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list);
 }
 
-/* create a new block device (by default it is empty) */
-BlockDriverState *bdrv_new(const char *device_name, Error **errp)
+BlockDriverState *bdrv_new_root(void)
 {
-    BlockDriverState *bs;
-    int i;
+    BlockDriverState *bs = bdrv_new();
 
-    if (*device_name && !id_wellformed(device_name)) {
-        error_setg(errp, "Invalid device name");
-        return NULL;
-    }
+    QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list);
+    return bs;
+}
 
-    if (bdrv_find(device_name)) {
-        error_setg(errp, "Device with id '%s' already exists",
-                   device_name);
-        return NULL;
-    }
-    if (bdrv_find_node(device_name)) {
-        error_setg(errp,
-                   "Device name '%s' conflicts with an existing node name",
-                   device_name);
-        return NULL;
-    }
+BlockDriverState *bdrv_new(void)
+{
+    BlockDriverState *bs;
+    int i;
 
     bs = g_new0(BlockDriverState, 1);
     QLIST_INIT(&bs->dirty_bitmaps);
-    pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
-    if (device_name[0] != '\0') {
-        QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list);
-    }
     for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
         QLIST_INIT(&bs->op_blockers[i]);
     }
@@ -875,7 +858,7 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
     }
 
     /* takes care of avoiding namespaces collisions */
-    if (bdrv_find(node_name)) {
+    if (blk_by_name(node_name)) {
         error_setg(errp, "node-name=%s is conflicting with a device id",
                    node_name);
         return;
@@ -1159,7 +1142,7 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
     } else if (backing_hd) {
         error_setg(&bs->backing_blocker,
                    "device is used as backing hd of '%s'",
-                   bs->device_name);
+                   bdrv_get_device_name(bs));
     }
 
     bs->backing_hd = backing_hd;
@@ -1224,7 +1207,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
         goto free_exit;
     }
 
-    backing_hd = bdrv_new("", errp);
+    backing_hd = bdrv_new();
 
     if (bs->backing_format[0] != '\0') {
         back_drv = bdrv_find_format(bs->backing_format);
@@ -1353,7 +1336,7 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
     qdict_put(snapshot_options, "file.filename",
               qstring_from_str(tmp_filename));
 
-    bs_snapshot = bdrv_new("", &error_abort);
+    bs_snapshot = bdrv_new();
 
     ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options,
                     flags, bdrv_qcow2, &local_err);
@@ -1424,7 +1407,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
     if (*pbs) {
         bs = *pbs;
     } else {
-        bs = bdrv_new("", &error_abort);
+        bs = bdrv_new();
     }
 
     /* NULL means an empty set of options */
@@ -1533,7 +1516,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
         } else {
             error_setg(errp, "Block format '%s' used by device '%s' doesn't "
                        "support the option '%s'", drv->format_name,
-                       bs->device_name, entry->key);
+                       bdrv_get_device_name(bs), entry->key);
         }
 
         ret = -EINVAL;
@@ -1541,7 +1524,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
     }
 
     if (!bdrv_key_required(bs)) {
-        bdrv_dev_change_media_cb(bs, true);
+        if (bs->blk) {
+            blk_dev_change_media_cb(bs->blk, true);
+        }
     } else if (!runstate_check(RUN_STATE_PRELAUNCH)
                && !runstate_check(RUN_STATE_INMIGRATE)
                && !runstate_check(RUN_STATE_PAUSED)) { /* HACK */
@@ -1740,7 +1725,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
     if (!(reopen_state->bs->open_flags & BDRV_O_ALLOW_RDWR) &&
         reopen_state->flags & BDRV_O_RDWR) {
         error_set(errp, QERR_DEVICE_IS_READ_ONLY,
-                  reopen_state->bs->device_name);
+                  bdrv_get_device_name(reopen_state->bs));
         goto error;
     }
 
@@ -1767,7 +1752,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
         /* It is currently mandatory to have a bdrv_reopen_prepare()
          * handler for each supported drv. */
         error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
-                  drv->format_name, reopen_state->bs->device_name,
+                  drv->format_name, bdrv_get_device_name(reopen_state->bs),
                  "reopening of file");
         ret = -1;
         goto error;
@@ -1866,7 +1851,9 @@ void bdrv_close(BlockDriverState *bs)
         }
     }
 
-    bdrv_dev_change_media_cb(bs, false);
+    if (bs->blk) {
+        blk_dev_change_media_cb(bs->blk, false);
+    }
 
     /*throttling disk I/O limits*/
     if (bs->io_limits_enabled) {
@@ -1955,10 +1942,17 @@ void bdrv_drain_all(void)
    Also, NULL terminate the device_name to prevent double remove */
 void bdrv_make_anon(BlockDriverState *bs)
 {
-    if (bs->device_name[0] != '\0') {
+    /*
+     * Take care to remove bs from bdrv_states only when it's actually
+     * in it.  Note that bs->device_list.tqe_prev is initially null,
+     * and gets set to non-null by QTAILQ_INSERT_TAIL().  Establish
+     * the useful invariant "bs in bdrv_states iff bs->tqe_prev" by
+     * resetting it to null on remove.
+     */
+    if (bs->device_list.tqe_prev) {
         QTAILQ_REMOVE(&bdrv_states, bs, device_list);
+        bs->device_list.tqe_prev = NULL;
     }
-    bs->device_name[0] = '\0';
     if (bs->node_name[0] != '\0') {
         QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list);
     }
@@ -1978,9 +1972,6 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
     /* move some fields that need to stay attached to the device */
 
     /* dev info */
-    bs_dest->dev_ops            = bs_src->dev_ops;
-    bs_dest->dev_opaque         = bs_src->dev_opaque;
-    bs_dest->dev                = bs_src->dev;
     bs_dest->guest_block_size   = bs_src->guest_block_size;
     bs_dest->copy_on_read       = bs_src->copy_on_read;
 
@@ -2012,9 +2003,9 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
     bs_dest->job                = bs_src->job;
 
     /* keep the same entry in bdrv_states */
-    pstrcpy(bs_dest->device_name, sizeof(bs_dest->device_name),
-            bs_src->device_name);
     bs_dest->device_list = bs_src->device_list;
+    bs_dest->blk = bs_src->blk;
+
     memcpy(bs_dest->op_blockers, bs_src->op_blockers,
            sizeof(bs_dest->op_blockers));
 }
@@ -2027,7 +2018,7 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
  * This will modify the BlockDriverState fields, and swap contents
  * between bs_new and bs_old. Both bs_new and bs_old are modified.
  *
- * bs_new is required to be anonymous.
+ * bs_new must not be attached to a BlockBackend.
  *
  * This function does not create any image files.
  */
@@ -2046,11 +2037,10 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
         QTAILQ_REMOVE(&graph_bdrv_states, bs_old, node_list);
     }
 
-    /* bs_new must be anonymous and shouldn't have anything fancy enabled */
-    assert(bs_new->device_name[0] == '\0');
+    /* bs_new must be unattached and shouldn't have anything fancy enabled */
+    assert(!bs_new->blk);
     assert(QLIST_EMPTY(&bs_new->dirty_bitmaps));
     assert(bs_new->job == NULL);
-    assert(bs_new->dev == NULL);
     assert(bs_new->io_limits_enabled == false);
     assert(!throttle_have_timer(&bs_new->throttle_state));
 
@@ -2063,11 +2053,10 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
     bdrv_move_feature_fields(bs_old, bs_new);
     bdrv_move_feature_fields(bs_new, &tmp);
 
-    /* bs_new shouldn't be in bdrv_states even after the swap!  */
-    assert(bs_new->device_name[0] == '\0');
+    /* bs_new must remain unattached */
+    assert(!bs_new->blk);
 
     /* Check a few fields that should remain attached to the device */
-    assert(bs_new->dev == NULL);
     assert(bs_new->job == NULL);
     assert(bs_new->io_limits_enabled == false);
     assert(!throttle_have_timer(&bs_new->throttle_state));
@@ -2091,7 +2080,7 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
  * This will modify the BlockDriverState fields, and swap contents
  * between bs_new and bs_top. Both bs_new and bs_top are modified.
  *
- * bs_new is required to be anonymous.
+ * bs_new must not be attached to a BlockBackend.
  *
  * This function does not create any image files.
  */
@@ -2106,7 +2095,6 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
 
 static void bdrv_delete(BlockDriverState *bs)
 {
-    assert(!bs->dev);
     assert(!bs->job);
     assert(bdrv_op_blocker_is_empty(bs));
     assert(!bs->refcnt);
@@ -2117,109 +2105,9 @@ static void bdrv_delete(BlockDriverState *bs)
     /* remove from list, if necessary */
     bdrv_make_anon(bs);
 
-    drive_info_del(drive_get_by_blockdev(bs));
     g_free(bs);
 }
 
-int bdrv_attach_dev(BlockDriverState *bs, void *dev)
-/* TODO change to DeviceState *dev when all users are qdevified */
-{
-    if (bs->dev) {
-        return -EBUSY;
-    }
-    bs->dev = dev;
-    bdrv_iostatus_reset(bs);
-
-    /* We're expecting I/O from the device so bump up coroutine pool size */
-    qemu_coroutine_adjust_pool_size(COROUTINE_POOL_RESERVATION);
-    return 0;
-}
-
-/* TODO qdevified devices don't use this, remove when devices are qdevified */
-void bdrv_attach_dev_nofail(BlockDriverState *bs, void *dev)
-{
-    if (bdrv_attach_dev(bs, dev) < 0) {
-        abort();
-    }
-}
-
-void bdrv_detach_dev(BlockDriverState *bs, void *dev)
-/* TODO change to DeviceState *dev when all users are qdevified */
-{
-    assert(bs->dev == dev);
-    bs->dev = NULL;
-    bs->dev_ops = NULL;
-    bs->dev_opaque = NULL;
-    bs->guest_block_size = 512;
-    qemu_coroutine_adjust_pool_size(-COROUTINE_POOL_RESERVATION);
-}
-
-/* TODO change to return DeviceState * when all users are qdevified */
-void *bdrv_get_attached_dev(BlockDriverState *bs)
-{
-    return bs->dev;
-}
-
-void bdrv_set_dev_ops(BlockDriverState *bs, const BlockDevOps *ops,
-                      void *opaque)
-{
-    bs->dev_ops = ops;
-    bs->dev_opaque = opaque;
-}
-
-static void bdrv_dev_change_media_cb(BlockDriverState *bs, bool load)
-{
-    if (bs->dev_ops && bs->dev_ops->change_media_cb) {
-        bool tray_was_closed = !bdrv_dev_is_tray_open(bs);
-        bs->dev_ops->change_media_cb(bs->dev_opaque, load);
-        if (tray_was_closed) {
-            /* tray open */
-            qapi_event_send_device_tray_moved(bdrv_get_device_name(bs),
-                                              true, &error_abort);
-        }
-        if (load) {
-            /* tray close */
-            qapi_event_send_device_tray_moved(bdrv_get_device_name(bs),
-                                              false, &error_abort);
-        }
-    }
-}
-
-bool bdrv_dev_has_removable_media(BlockDriverState *bs)
-{
-    return !bs->dev || (bs->dev_ops && bs->dev_ops->change_media_cb);
-}
-
-void bdrv_dev_eject_request(BlockDriverState *bs, bool force)
-{
-    if (bs->dev_ops && bs->dev_ops->eject_request_cb) {
-        bs->dev_ops->eject_request_cb(bs->dev_opaque, force);
-    }
-}
-
-bool bdrv_dev_is_tray_open(BlockDriverState *bs)
-{
-    if (bs->dev_ops && bs->dev_ops->is_tray_open) {
-        return bs->dev_ops->is_tray_open(bs->dev_opaque);
-    }
-    return false;
-}
-
-static void bdrv_dev_resize_cb(BlockDriverState *bs)
-{
-    if (bs->dev_ops && bs->dev_ops->resize_cb) {
-        bs->dev_ops->resize_cb(bs->dev_opaque);
-    }
-}
-
-bool bdrv_dev_is_medium_locked(BlockDriverState *bs)
-{
-    if (bs->dev_ops && bs->dev_ops->is_medium_locked) {
-        return bs->dev_ops->is_medium_locked(bs->dev_opaque);
-    }
-    return false;
-}
-
 /*
  * Run consistency checks on an image
  *
@@ -3553,7 +3441,9 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
     ret = drv->bdrv_truncate(bs, offset);
     if (ret == 0) {
         ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
-        bdrv_dev_resize_cb(bs);
+        if (bs->blk) {
+            blk_dev_resize_cb(bs->blk);
+        }
     }
     return ret;
 }
@@ -3754,8 +3644,10 @@ int bdrv_set_key(BlockDriverState *bs, const char *key)
         bs->valid_key = 0;
     } else if (!bs->valid_key) {
         bs->valid_key = 1;
-        /* call the change callback now, we skipped it on open */
-        bdrv_dev_change_media_cb(bs, true);
+        if (bs->blk) {
+            /* call the change callback now, we skipped it on open */
+            blk_dev_change_media_cb(bs->blk, true);
+        }
     }
     return ret;
 }
@@ -3803,16 +3695,12 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
 }
 
 /* This function is to find block backend bs */
+/* TODO convert callers to blk_by_name(), then remove */
 BlockDriverState *bdrv_find(const char *name)
 {
-    BlockDriverState *bs;
+    BlockBackend *blk = blk_by_name(name);
 
-    QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
-        if (!strcmp(name, bs->device_name)) {
-            return bs;
-        }
-    }
-    return NULL;
+    return blk ? blk_bs(blk) : NULL;
 }
 
 /* This function is to find a node in the bs graph */
@@ -3851,13 +3739,14 @@ BlockDriverState *bdrv_lookup_bs(const char *device,
                                  const char *node_name,
                                  Error **errp)
 {
-    BlockDriverState *bs = NULL;
+    BlockBackend *blk;
+    BlockDriverState *bs;
 
     if (device) {
-        bs = bdrv_find(device);
+        blk = blk_by_name(device);
 
-        if (bs) {
-            return bs;
+        if (blk) {
+            return blk_bs(blk);
         }
     }
 
@@ -3894,18 +3783,10 @@ BlockDriverState *bdrv_next(BlockDriverState *bs)
     return QTAILQ_NEXT(bs, device_list);
 }
 
-void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs), void *opaque)
+/* TODO check what callers really want: bs->node_name or blk_name() */
+const char *bdrv_get_device_name(const BlockDriverState *bs)
 {
-    BlockDriverState *bs;
-
-    QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
-        it(opaque, bs);
-    }
-}
-
-const char *bdrv_get_device_name(BlockDriverState *bs)
-{
-    return bs->device_name;
+    return bs->blk ? blk_name(bs->blk) : "";
 }
 
 int bdrv_get_flags(BlockDriverState *bs)
@@ -4432,9 +4313,9 @@ int bdrv_get_backing_file_depth(BlockDriverState *bs)
 /**************************************************************/
 /* async I/Os */
 
-BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
-                                 QEMUIOVector *qiov, int nb_sectors,
-                                 BlockDriverCompletionFunc *cb, void *opaque)
+BlockAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
+                           QEMUIOVector *qiov, int nb_sectors,
+                           BlockCompletionFunc *cb, void *opaque)
 {
     trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque);
 
@@ -4442,9 +4323,9 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
                                  cb, opaque, false);
 }
 
-BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
-                                  QEMUIOVector *qiov, int nb_sectors,
-                                  BlockDriverCompletionFunc *cb, void *opaque)
+BlockAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
+                            QEMUIOVector *qiov, int nb_sectors,
+                            BlockCompletionFunc *cb, void *opaque)
 {
     trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque);
 
@@ -4452,9 +4333,9 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
                                  cb, opaque, true);
 }
 
-BlockDriverAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs,
+BlockAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs,
         int64_t sector_num, int nb_sectors, BdrvRequestFlags flags,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     trace_bdrv_aio_write_zeroes(bs, sector_num, nb_sectors, flags, opaque);
 
@@ -4469,7 +4350,7 @@ typedef struct MultiwriteCB {
     int num_requests;
     int num_callbacks;
     struct {
-        BlockDriverCompletionFunc *cb;
+        BlockCompletionFunc *cb;
         void *opaque;
         QEMUIOVector *free_qiov;
     } callbacks[];
@@ -4646,7 +4527,7 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
     return 0;
 }
 
-void bdrv_aio_cancel(BlockDriverAIOCB *acb)
+void bdrv_aio_cancel(BlockAIOCB *acb)
 {
     qemu_aio_ref(acb);
     bdrv_aio_cancel_async(acb);
@@ -4665,7 +4546,7 @@ void bdrv_aio_cancel(BlockDriverAIOCB *acb)
 /* Async version of aio cancel. The caller is not blocked if the acb implements
  * cancel_async, otherwise we do nothing and let the request normally complete.
  * In either case the completion callback must be called. */
-void bdrv_aio_cancel_async(BlockDriverAIOCB *acb)
+void bdrv_aio_cancel_async(BlockAIOCB *acb)
 {
     if (acb->aiocb_info->cancel_async) {
         acb->aiocb_info->cancel_async(acb);
@@ -4675,23 +4556,23 @@ void bdrv_aio_cancel_async(BlockDriverAIOCB *acb)
 /**************************************************************/
 /* async block device emulation */
 
-typedef struct BlockDriverAIOCBSync {
-    BlockDriverAIOCB common;
+typedef struct BlockAIOCBSync {
+    BlockAIOCB common;
     QEMUBH *bh;
     int ret;
     /* vector translation state */
     QEMUIOVector *qiov;
     uint8_t *bounce;
     int is_write;
-} BlockDriverAIOCBSync;
+} BlockAIOCBSync;
 
 static const AIOCBInfo bdrv_em_aiocb_info = {
-    .aiocb_size         = sizeof(BlockDriverAIOCBSync),
+    .aiocb_size         = sizeof(BlockAIOCBSync),
 };
 
 static void bdrv_aio_bh_cb(void *opaque)
 {
-    BlockDriverAIOCBSync *acb = opaque;
+    BlockAIOCBSync *acb = opaque;
 
     if (!acb->is_write && acb->ret >= 0) {
         qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
@@ -4703,16 +4584,16 @@ static void bdrv_aio_bh_cb(void *opaque)
     qemu_aio_unref(acb);
 }
 
-static BlockDriverAIOCB *bdrv_aio_rw_vector(BlockDriverState *bs,
-                                            int64_t sector_num,
-                                            QEMUIOVector *qiov,
-                                            int nb_sectors,
-                                            BlockDriverCompletionFunc *cb,
-                                            void *opaque,
-                                            int is_write)
+static BlockAIOCB *bdrv_aio_rw_vector(BlockDriverState *bs,
+                                      int64_t sector_num,
+                                      QEMUIOVector *qiov,
+                                      int nb_sectors,
+                                      BlockCompletionFunc *cb,
+                                      void *opaque,
+                                      int is_write)
 
 {
-    BlockDriverAIOCBSync *acb;
+    BlockAIOCBSync *acb;
 
     acb = qemu_aio_get(&bdrv_em_aiocb_info, bs, cb, opaque);
     acb->is_write = is_write;
@@ -4734,36 +4615,36 @@ static BlockDriverAIOCB *bdrv_aio_rw_vector(BlockDriverState *bs,
     return &acb->common;
 }
 
-static BlockDriverAIOCB *bdrv_aio_readv_em(BlockDriverState *bs,
+static BlockAIOCB *bdrv_aio_readv_em(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
 }
 
-static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs,
+static BlockAIOCB *bdrv_aio_writev_em(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
 }
 
 
-typedef struct BlockDriverAIOCBCoroutine {
-    BlockDriverAIOCB common;
+typedef struct BlockAIOCBCoroutine {
+    BlockAIOCB common;
     BlockRequest req;
     bool is_write;
     bool *done;
     QEMUBH* bh;
-} BlockDriverAIOCBCoroutine;
+} BlockAIOCBCoroutine;
 
 static const AIOCBInfo bdrv_em_co_aiocb_info = {
-    .aiocb_size         = sizeof(BlockDriverAIOCBCoroutine),
+    .aiocb_size         = sizeof(BlockAIOCBCoroutine),
 };
 
 static void bdrv_co_em_bh(void *opaque)
 {
-    BlockDriverAIOCBCoroutine *acb = opaque;
+    BlockAIOCBCoroutine *acb = opaque;
 
     acb->common.cb(acb->common.opaque, acb->req.error);
 
@@ -4774,7 +4655,7 @@ static void bdrv_co_em_bh(void *opaque)
 /* Invoke bdrv_co_do_readv/bdrv_co_do_writev */
 static void coroutine_fn bdrv_co_do_rw(void *opaque)
 {
-    BlockDriverAIOCBCoroutine *acb = opaque;
+    BlockAIOCBCoroutine *acb = opaque;
     BlockDriverState *bs = acb->common.bs;
 
     if (!acb->is_write) {
@@ -4789,17 +4670,17 @@ static void coroutine_fn bdrv_co_do_rw(void *opaque)
     qemu_bh_schedule(acb->bh);
 }
 
-static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
-                                               int64_t sector_num,
-                                               QEMUIOVector *qiov,
-                                               int nb_sectors,
-                                               BdrvRequestFlags flags,
-                                               BlockDriverCompletionFunc *cb,
-                                               void *opaque,
-                                               bool is_write)
+static BlockAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
+                                         int64_t sector_num,
+                                         QEMUIOVector *qiov,
+                                         int nb_sectors,
+                                         BdrvRequestFlags flags,
+                                         BlockCompletionFunc *cb,
+                                         void *opaque,
+                                         bool is_write)
 {
     Coroutine *co;
-    BlockDriverAIOCBCoroutine *acb;
+    BlockAIOCBCoroutine *acb;
 
     acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
     acb->req.sector = sector_num;
@@ -4816,7 +4697,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
 
 static void coroutine_fn bdrv_aio_flush_co_entry(void *opaque)
 {
-    BlockDriverAIOCBCoroutine *acb = opaque;
+    BlockAIOCBCoroutine *acb = opaque;
     BlockDriverState *bs = acb->common.bs;
 
     acb->req.error = bdrv_co_flush(bs);
@@ -4824,13 +4705,13 @@ static void coroutine_fn bdrv_aio_flush_co_entry(void *opaque)
     qemu_bh_schedule(acb->bh);
 }
 
-BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
-        BlockDriverCompletionFunc *cb, void *opaque)
+BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs,
+        BlockCompletionFunc *cb, void *opaque)
 {
     trace_bdrv_aio_flush(bs, opaque);
 
     Coroutine *co;
-    BlockDriverAIOCBCoroutine *acb;
+    BlockAIOCBCoroutine *acb;
 
     acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
 
@@ -4842,7 +4723,7 @@ BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
 
 static void coroutine_fn bdrv_aio_discard_co_entry(void *opaque)
 {
-    BlockDriverAIOCBCoroutine *acb = opaque;
+    BlockAIOCBCoroutine *acb = opaque;
     BlockDriverState *bs = acb->common.bs;
 
     acb->req.error = bdrv_co_discard(bs, acb->req.sector, acb->req.nb_sectors);
@@ -4850,12 +4731,12 @@ static void coroutine_fn bdrv_aio_discard_co_entry(void *opaque)
     qemu_bh_schedule(acb->bh);
 }
 
-BlockDriverAIOCB *bdrv_aio_discard(BlockDriverState *bs,
+BlockAIOCB *bdrv_aio_discard(BlockDriverState *bs,
         int64_t sector_num, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     Coroutine *co;
-    BlockDriverAIOCBCoroutine *acb;
+    BlockAIOCBCoroutine *acb;
 
     trace_bdrv_aio_discard(bs, sector_num, nb_sectors, opaque);
 
@@ -4880,9 +4761,9 @@ void bdrv_init_with_whitelist(void)
 }
 
 void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs,
-                   BlockDriverCompletionFunc *cb, void *opaque)
+                   BlockCompletionFunc *cb, void *opaque)
 {
-    BlockDriverAIOCB *acb;
+    BlockAIOCB *acb;
 
     acb = g_slice_alloc(aiocb_info->aiocb_size);
     acb->aiocb_info = aiocb_info;
@@ -4895,13 +4776,13 @@ void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs,
 
 void qemu_aio_ref(void *p)
 {
-    BlockDriverAIOCB *acb = p;
+    BlockAIOCB *acb = p;
     acb->refcnt++;
 }
 
 void qemu_aio_unref(void *p)
 {
-    BlockDriverAIOCB *acb = p;
+    BlockAIOCB *acb = p;
     assert(acb->refcnt > 0);
     if (--acb->refcnt == 0) {
         g_slice_free1(acb->aiocb_info->aiocb_size, acb);
@@ -4931,7 +4812,7 @@ static int coroutine_fn bdrv_co_io_em(BlockDriverState *bs, int64_t sector_num,
     CoroutineIOCompletion co = {
         .coroutine = qemu_coroutine_self(),
     };
-    BlockDriverAIOCB *acb;
+    BlockAIOCB *acb;
 
     if (is_write) {
         acb = bs->drv->bdrv_aio_writev(bs, sector_num, iov, nb_sectors,
@@ -4997,7 +4878,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
     if (bs->drv->bdrv_co_flush_to_disk) {
         ret = bs->drv->bdrv_co_flush_to_disk(bs);
     } else if (bs->drv->bdrv_aio_flush) {
-        BlockDriverAIOCB *acb;
+        BlockAIOCB *acb;
         CoroutineIOCompletion co = {
             .coroutine = qemu_coroutine_self(),
         };
@@ -5172,7 +5053,7 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
         if (bs->drv->bdrv_co_discard) {
             ret = bs->drv->bdrv_co_discard(bs, sector_num, num);
         } else {
-            BlockDriverAIOCB *acb;
+            BlockAIOCB *acb;
             CoroutineIOCompletion co = {
                 .coroutine = qemu_coroutine_self(),
             };
@@ -5259,13 +5140,15 @@ int bdrv_media_changed(BlockDriverState *bs)
 void bdrv_eject(BlockDriverState *bs, bool eject_flag)
 {
     BlockDriver *drv = bs->drv;
+    const char *device_name;
 
     if (drv && drv->bdrv_eject) {
         drv->bdrv_eject(bs, eject_flag);
     }
 
-    if (bs->device_name[0] != '\0') {
-        qapi_event_send_device_tray_moved(bdrv_get_device_name(bs),
+    device_name = bdrv_get_device_name(bs);
+    if (device_name[0] != '\0') {
+        qapi_event_send_device_tray_moved(device_name,
                                           eject_flag, &error_abort);
     }
 }
@@ -5296,9 +5179,9 @@ int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
     return -ENOTSUP;
 }
 
-BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
+BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
         unsigned long int req, void *buf,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     BlockDriver *drv = bs->drv;
 
@@ -5475,7 +5358,8 @@ bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp)
         blocker = QLIST_FIRST(&bs->op_blockers[op]);
         if (errp) {
             error_setg(errp, "Device '%s' is busy: %s",
-                       bs->device_name, error_get_pretty(blocker->reason));
+                       bdrv_get_device_name(bs),
+                       error_get_pretty(blocker->reason));
         }
         return true;
     }
diff --git a/block/Makefile.objs b/block/Makefile.objs
index a833ed5745..27911b6b88 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -5,7 +5,7 @@ block-obj-y += qed-check.o
 block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
 block-obj-$(CONFIG_QUORUM) += quorum.o
 block-obj-y += parallels.o blkdebug.o blkverify.o
-block-obj-y += snapshot.o qapi.o
+block-obj-y += block-backend.o snapshot.o qapi.o
 block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
 block-obj-$(CONFIG_POSIX) += raw-posix.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
diff --git a/block/archipelago.c b/block/archipelago.c
index 73d91a422f..a8114b528b 100644
--- a/block/archipelago.c
+++ b/block/archipelago.c
@@ -86,7 +86,7 @@ typedef enum {
 } ARCHIPCmd;
 
 typedef struct ArchipelagoAIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     QEMUBH *bh;
     struct BDRVArchipelagoState *s;
     QEMUIOVector *qiov;
@@ -856,13 +856,13 @@ err_exit:
     return ret;
 }
 
-static BlockDriverAIOCB *qemu_archipelago_aio_rw(BlockDriverState *bs,
-                                                 int64_t sector_num,
-                                                 QEMUIOVector *qiov,
-                                                 int nb_sectors,
-                                                 BlockDriverCompletionFunc *cb,
-                                                 void *opaque,
-                                                 int op)
+static BlockAIOCB *qemu_archipelago_aio_rw(BlockDriverState *bs,
+                                           int64_t sector_num,
+                                           QEMUIOVector *qiov,
+                                           int nb_sectors,
+                                           BlockCompletionFunc *cb,
+                                           void *opaque,
+                                           int op)
 {
     ArchipelagoAIOCB *aio_cb;
     BDRVArchipelagoState *s = bs->opaque;
@@ -894,17 +894,17 @@ err_exit:
     return NULL;
 }
 
-static BlockDriverAIOCB *qemu_archipelago_aio_readv(BlockDriverState *bs,
+static BlockAIOCB *qemu_archipelago_aio_readv(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     return qemu_archipelago_aio_rw(bs, sector_num, qiov, nb_sectors, cb,
                                    opaque, ARCHIP_OP_READ);
 }
 
-static BlockDriverAIOCB *qemu_archipelago_aio_writev(BlockDriverState *bs,
+static BlockAIOCB *qemu_archipelago_aio_writev(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     return qemu_archipelago_aio_rw(bs, sector_num, qiov, nb_sectors, cb,
                                    opaque, ARCHIP_OP_WRITE);
@@ -1052,8 +1052,8 @@ static QemuOptsList qemu_archipelago_create_opts = {
     }
 };
 
-static BlockDriverAIOCB *qemu_archipelago_aio_flush(BlockDriverState *bs,
-        BlockDriverCompletionFunc *cb, void *opaque)
+static BlockAIOCB *qemu_archipelago_aio_flush(BlockDriverState *bs,
+        BlockCompletionFunc *cb, void *opaque)
 {
     return qemu_archipelago_aio_rw(bs, 0, NULL, 0, cb, opaque,
                                    ARCHIP_OP_FLUSH);
diff --git a/block/backup.c b/block/backup.c
index d0b02255ce..e334740161 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -353,7 +353,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
                   int64_t speed, MirrorSyncMode sync_mode,
                   BlockdevOnError on_source_error,
                   BlockdevOnError on_target_error,
-                  BlockDriverCompletionFunc *cb, void *opaque,
+                  BlockCompletionFunc *cb, void *opaque,
                   Error **errp)
 {
     int64_t len;
diff --git a/block/blkdebug.c b/block/blkdebug.c
index f8fbb0f3d4..e046b920fb 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -41,7 +41,7 @@ typedef struct BDRVBlkdebugState {
 } BDRVBlkdebugState;
 
 typedef struct BlkdebugAIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     QEMUBH *bh;
     int ret;
 } BlkdebugAIOCB;
@@ -463,8 +463,8 @@ static void error_callback_bh(void *opaque)
     qemu_aio_unref(acb);
 }
 
-static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
-    BlockDriverCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
+static BlockAIOCB *inject_error(BlockDriverState *bs,
+    BlockCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
 {
     BDRVBlkdebugState *s = bs->opaque;
     int error = rule->options.inject.error;
@@ -489,9 +489,9 @@ static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
     return &acb->common;
 }
 
-static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
+static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-    BlockDriverCompletionFunc *cb, void *opaque)
+    BlockCompletionFunc *cb, void *opaque)
 {
     BDRVBlkdebugState *s = bs->opaque;
     BlkdebugRule *rule = NULL;
@@ -511,9 +511,9 @@ static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
     return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
 }
 
-static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
+static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-    BlockDriverCompletionFunc *cb, void *opaque)
+    BlockCompletionFunc *cb, void *opaque)
 {
     BDRVBlkdebugState *s = bs->opaque;
     BlkdebugRule *rule = NULL;
@@ -533,8 +533,8 @@ static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
     return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
 }
 
-static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
-    BlockDriverCompletionFunc *cb, void *opaque)
+static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
+    BlockCompletionFunc *cb, void *opaque)
 {
     BDRVBlkdebugState *s = bs->opaque;
     BlkdebugRule *rule = NULL;
diff --git a/block/blkverify.c b/block/blkverify.c
index 7d64a23a09..438dff8bcb 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -19,7 +19,7 @@ typedef struct {
 
 typedef struct BlkverifyAIOCB BlkverifyAIOCB;
 struct BlkverifyAIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     QEMUBH *bh;
 
     /* Request metadata */
@@ -165,7 +165,7 @@ static int64_t blkverify_getlength(BlockDriverState *bs)
 static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
                                          int64_t sector_num, QEMUIOVector *qiov,
                                          int nb_sectors,
-                                         BlockDriverCompletionFunc *cb,
+                                         BlockCompletionFunc *cb,
                                          void *opaque)
 {
     BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aiocb_info, bs, cb, opaque);
@@ -229,9 +229,9 @@ static void blkverify_verify_readv(BlkverifyAIOCB *acb)
     }
 }
 
-static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
+static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     BDRVBlkverifyState *s = bs->opaque;
     BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov,
@@ -249,9 +249,9 @@ static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
     return &acb->common;
 }
 
-static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs,
+static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     BDRVBlkverifyState *s = bs->opaque;
     BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov,
@@ -264,9 +264,9 @@ static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs,
     return &acb->common;
 }
 
-static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs,
-                                             BlockDriverCompletionFunc *cb,
-                                             void *opaque)
+static BlockAIOCB *blkverify_aio_flush(BlockDriverState *bs,
+                                       BlockCompletionFunc *cb,
+                                       void *opaque)
 {
     BDRVBlkverifyState *s = bs->opaque;
 
diff --git a/block/block-backend.c b/block/block-backend.c
new file mode 100644
index 0000000000..d0692b18e9
--- /dev/null
+++ b/block/block-backend.c
@@ -0,0 +1,631 @@
+/*
+ * QEMU Block backends
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * Authors:
+ *  Markus Armbruster <armbru@redhat.com>,
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1
+ * or later.  See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "sysemu/block-backend.h"
+#include "block/block_int.h"
+#include "sysemu/blockdev.h"
+#include "qapi-event.h"
+
+/* Number of coroutines to reserve per attached device model */
+#define COROUTINE_POOL_RESERVATION 64
+
+struct BlockBackend {
+    char *name;
+    int refcnt;
+    BlockDriverState *bs;
+    DriveInfo *legacy_dinfo;    /* null unless created by drive_new() */
+    QTAILQ_ENTRY(BlockBackend) link; /* for blk_backends */
+
+    void *dev;                  /* attached device model, if any */
+    /* TODO change to DeviceState when all users are qdevified */
+    const BlockDevOps *dev_ops;
+    void *dev_opaque;
+};
+
+static void drive_info_del(DriveInfo *dinfo);
+
+/* All the BlockBackends (except for hidden ones) */
+static QTAILQ_HEAD(, BlockBackend) blk_backends =
+    QTAILQ_HEAD_INITIALIZER(blk_backends);
+
+/*
+ * Create a new BlockBackend with @name, with a reference count of one.
+ * @name must not be null or empty.
+ * Fail if a BlockBackend with this name already exists.
+ * Store an error through @errp on failure, unless it's null.
+ * Return the new BlockBackend on success, null on failure.
+ */
+BlockBackend *blk_new(const char *name, Error **errp)
+{
+    BlockBackend *blk;
+
+    assert(name && name[0]);
+    if (!id_wellformed(name)) {
+        error_setg(errp, "Invalid device name");
+        return NULL;
+    }
+    if (blk_by_name(name)) {
+        error_setg(errp, "Device with id '%s' already exists", name);
+        return NULL;
+    }
+    if (bdrv_find_node(name)) {
+        error_setg(errp,
+                   "Device name '%s' conflicts with an existing node name",
+                   name);
+        return NULL;
+    }
+
+    blk = g_new0(BlockBackend, 1);
+    blk->name = g_strdup(name);
+    blk->refcnt = 1;
+    QTAILQ_INSERT_TAIL(&blk_backends, blk, link);
+    return blk;
+}
+
+/*
+ * Create a new BlockBackend with a new BlockDriverState attached.
+ * Otherwise just like blk_new(), which see.
+ */
+BlockBackend *blk_new_with_bs(const char *name, Error **errp)
+{
+    BlockBackend *blk;
+    BlockDriverState *bs;
+
+    blk = blk_new(name, errp);
+    if (!blk) {
+        return NULL;
+    }
+
+    bs = bdrv_new_root();
+    blk->bs = bs;
+    bs->blk = blk;
+    return blk;
+}
+
+static void blk_delete(BlockBackend *blk)
+{
+    assert(!blk->refcnt);
+    assert(!blk->dev);
+    if (blk->bs) {
+        assert(blk->bs->blk == blk);
+        blk->bs->blk = NULL;
+        bdrv_unref(blk->bs);
+        blk->bs = NULL;
+    }
+    /* Avoid double-remove after blk_hide_on_behalf_of_do_drive_del() */
+    if (blk->name[0]) {
+        QTAILQ_REMOVE(&blk_backends, blk, link);
+    }
+    g_free(blk->name);
+    drive_info_del(blk->legacy_dinfo);
+    g_free(blk);
+}
+
+static void drive_info_del(DriveInfo *dinfo)
+{
+    if (!dinfo) {
+        return;
+    }
+    qemu_opts_del(dinfo->opts);
+    g_free(dinfo->serial);
+    g_free(dinfo);
+}
+
+/*
+ * Increment @blk's reference count.
+ * @blk must not be null.
+ */
+void blk_ref(BlockBackend *blk)
+{
+    blk->refcnt++;
+}
+
+/*
+ * Decrement @blk's reference count.
+ * If this drops it to zero, destroy @blk.
+ * For convenience, do nothing if @blk is null.
+ */
+void blk_unref(BlockBackend *blk)
+{
+    if (blk) {
+        assert(blk->refcnt > 0);
+        if (!--blk->refcnt) {
+            blk_delete(blk);
+        }
+    }
+}
+
+/*
+ * Return the BlockBackend after @blk.
+ * If @blk is null, return the first one.
+ * Else, return @blk's next sibling, which may be null.
+ *
+ * To iterate over all BlockBackends, do
+ * for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
+ *     ...
+ * }
+ */
+BlockBackend *blk_next(BlockBackend *blk)
+{
+    return blk ? QTAILQ_NEXT(blk, link) : QTAILQ_FIRST(&blk_backends);
+}
+
+/*
+ * Return @blk's name, a non-null string.
+ * Wart: the name is empty iff @blk has been hidden with
+ * blk_hide_on_behalf_of_do_drive_del().
+ */
+const char *blk_name(BlockBackend *blk)
+{
+    return blk->name;
+}
+
+/*
+ * Return the BlockBackend with name @name if it exists, else null.
+ * @name must not be null.
+ */
+BlockBackend *blk_by_name(const char *name)
+{
+    BlockBackend *blk;
+
+    assert(name);
+    QTAILQ_FOREACH(blk, &blk_backends, link) {
+        if (!strcmp(name, blk->name)) {
+            return blk;
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Return the BlockDriverState attached to @blk if any, else null.
+ */
+BlockDriverState *blk_bs(BlockBackend *blk)
+{
+    return blk->bs;
+}
+
+/*
+ * Return @blk's DriveInfo if any, else null.
+ */
+DriveInfo *blk_legacy_dinfo(BlockBackend *blk)
+{
+    return blk->legacy_dinfo;
+}
+
+/*
+ * Set @blk's DriveInfo to @dinfo, and return it.
+ * @blk must not have a DriveInfo set already.
+ * No other BlockBackend may have the same DriveInfo set.
+ */
+DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo)
+{
+    assert(!blk->legacy_dinfo);
+    return blk->legacy_dinfo = dinfo;
+}
+
+/*
+ * Return the BlockBackend with DriveInfo @dinfo.
+ * It must exist.
+ */
+BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo)
+{
+    BlockBackend *blk;
+
+    QTAILQ_FOREACH(blk, &blk_backends, link) {
+        if (blk->legacy_dinfo == dinfo) {
+            return blk;
+        }
+    }
+    abort();
+}
+
+/*
+ * Hide @blk.
+ * @blk must not have been hidden already.
+ * Make attached BlockDriverState, if any, anonymous.
+ * Once hidden, @blk is invisible to all functions that don't receive
+ * it as argument.  For example, blk_by_name() won't return it.
+ * Strictly for use by do_drive_del().
+ * TODO get rid of it!
+ */
+void blk_hide_on_behalf_of_do_drive_del(BlockBackend *blk)
+{
+    QTAILQ_REMOVE(&blk_backends, blk, link);
+    blk->name[0] = 0;
+    if (blk->bs) {
+        bdrv_make_anon(blk->bs);
+    }
+}
+
+/*
+ * Attach device model @dev to @blk.
+ * Return 0 on success, -EBUSY when a device model is attached already.
+ */
+int blk_attach_dev(BlockBackend *blk, void *dev)
+/* TODO change to DeviceState *dev when all users are qdevified */
+{
+    if (blk->dev) {
+        return -EBUSY;
+    }
+    blk_ref(blk);
+    blk->dev = dev;
+    bdrv_iostatus_reset(blk->bs);
+
+    /* We're expecting I/O from the device so bump up coroutine pool size */
+    qemu_coroutine_adjust_pool_size(COROUTINE_POOL_RESERVATION);
+    return 0;
+}
+
+/*
+ * Attach device model @dev to @blk.
+ * @blk must not have a device model attached already.
+ * TODO qdevified devices don't use this, remove when devices are qdevified
+ */
+void blk_attach_dev_nofail(BlockBackend *blk, void *dev)
+{
+    if (blk_attach_dev(blk, dev) < 0) {
+        abort();
+    }
+}
+
+/*
+ * Detach device model @dev from @blk.
+ * @dev must be currently attached to @blk.
+ */
+void blk_detach_dev(BlockBackend *blk, void *dev)
+/* TODO change to DeviceState *dev when all users are qdevified */
+{
+    assert(blk->dev == dev);
+    blk->dev = NULL;
+    blk->dev_ops = NULL;
+    blk->dev_opaque = NULL;
+    bdrv_set_guest_block_size(blk->bs, 512);
+    qemu_coroutine_adjust_pool_size(-COROUTINE_POOL_RESERVATION);
+    blk_unref(blk);
+}
+
+/*
+ * Return the device model attached to @blk if any, else null.
+ */
+void *blk_get_attached_dev(BlockBackend *blk)
+/* TODO change to return DeviceState * when all users are qdevified */
+{
+    return blk->dev;
+}
+
+/*
+ * Set @blk's device model callbacks to @ops.
+ * @opaque is the opaque argument to pass to the callbacks.
+ * This is for use by device models.
+ */
+void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
+                     void *opaque)
+{
+    blk->dev_ops = ops;
+    blk->dev_opaque = opaque;
+}
+
+/*
+ * Notify @blk's attached device model of media change.
+ * If @load is true, notify of media load.
+ * Else, notify of media eject.
+ * Also send DEVICE_TRAY_MOVED events as appropriate.
+ */
+void blk_dev_change_media_cb(BlockBackend *blk, bool load)
+{
+    if (blk->dev_ops && blk->dev_ops->change_media_cb) {
+        bool tray_was_closed = !blk_dev_is_tray_open(blk);
+
+        blk->dev_ops->change_media_cb(blk->dev_opaque, load);
+        if (tray_was_closed) {
+            /* tray open */
+            qapi_event_send_device_tray_moved(blk_name(blk),
+                                              true, &error_abort);
+        }
+        if (load) {
+            /* tray close */
+            qapi_event_send_device_tray_moved(blk_name(blk),
+                                              false, &error_abort);
+        }
+    }
+}
+
+/*
+ * Does @blk's attached device model have removable media?
+ * %true if no device model is attached.
+ */
+bool blk_dev_has_removable_media(BlockBackend *blk)
+{
+    return !blk->dev || (blk->dev_ops && blk->dev_ops->change_media_cb);
+}
+
+/*
+ * Notify @blk's attached device model of a media eject request.
+ * If @force is true, the medium is about to be yanked out forcefully.
+ */
+void blk_dev_eject_request(BlockBackend *blk, bool force)
+{
+    if (blk->dev_ops && blk->dev_ops->eject_request_cb) {
+        blk->dev_ops->eject_request_cb(blk->dev_opaque, force);
+    }
+}
+
+/*
+ * Does @blk's attached device model have a tray, and is it open?
+ */
+bool blk_dev_is_tray_open(BlockBackend *blk)
+{
+    if (blk->dev_ops && blk->dev_ops->is_tray_open) {
+        return blk->dev_ops->is_tray_open(blk->dev_opaque);
+    }
+    return false;
+}
+
+/*
+ * Does @blk's attached device model have the medium locked?
+ * %false if the device model has no such lock.
+ */
+bool blk_dev_is_medium_locked(BlockBackend *blk)
+{
+    if (blk->dev_ops && blk->dev_ops->is_medium_locked) {
+        return blk->dev_ops->is_medium_locked(blk->dev_opaque);
+    }
+    return false;
+}
+
+/*
+ * Notify @blk's attached device model of a backend size change.
+ */
+void blk_dev_resize_cb(BlockBackend *blk)
+{
+    if (blk->dev_ops && blk->dev_ops->resize_cb) {
+        blk->dev_ops->resize_cb(blk->dev_opaque);
+    }
+}
+
+void blk_iostatus_enable(BlockBackend *blk)
+{
+    bdrv_iostatus_enable(blk->bs);
+}
+
+int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf,
+             int nb_sectors)
+{
+    return bdrv_read(blk->bs, sector_num, buf, nb_sectors);
+}
+
+int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf,
+                         int nb_sectors)
+{
+    return bdrv_read_unthrottled(blk->bs, sector_num, buf, nb_sectors);
+}
+
+int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf,
+              int nb_sectors)
+{
+    return bdrv_write(blk->bs, sector_num, buf, nb_sectors);
+}
+
+BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num,
+                                 int nb_sectors, BdrvRequestFlags flags,
+                                 BlockCompletionFunc *cb, void *opaque)
+{
+    return bdrv_aio_write_zeroes(blk->bs, sector_num, nb_sectors, flags,
+                                 cb, opaque);
+}
+
+int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count)
+{
+    return bdrv_pread(blk->bs, offset, buf, count);
+}
+
+int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count)
+{
+    return bdrv_pwrite(blk->bs, offset, buf, count);
+}
+
+int64_t blk_getlength(BlockBackend *blk)
+{
+    return bdrv_getlength(blk->bs);
+}
+
+void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr)
+{
+    bdrv_get_geometry(blk->bs, nb_sectors_ptr);
+}
+
+BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num,
+                          QEMUIOVector *iov, int nb_sectors,
+                          BlockCompletionFunc *cb, void *opaque)
+{
+    return bdrv_aio_readv(blk->bs, sector_num, iov, nb_sectors, cb, opaque);
+}
+
+BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num,
+                           QEMUIOVector *iov, int nb_sectors,
+                           BlockCompletionFunc *cb, void *opaque)
+{
+    return bdrv_aio_writev(blk->bs, sector_num, iov, nb_sectors, cb, opaque);
+}
+
+BlockAIOCB *blk_aio_flush(BlockBackend *blk,
+                          BlockCompletionFunc *cb, void *opaque)
+{
+    return bdrv_aio_flush(blk->bs, cb, opaque);
+}
+
+BlockAIOCB *blk_aio_discard(BlockBackend *blk,
+                            int64_t sector_num, int nb_sectors,
+                            BlockCompletionFunc *cb, void *opaque)
+{
+    return bdrv_aio_discard(blk->bs, sector_num, nb_sectors, cb, opaque);
+}
+
+void blk_aio_cancel(BlockAIOCB *acb)
+{
+    bdrv_aio_cancel(acb);
+}
+
+void blk_aio_cancel_async(BlockAIOCB *acb)
+{
+    bdrv_aio_cancel_async(acb);
+}
+
+int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs)
+{
+    return bdrv_aio_multiwrite(blk->bs, reqs, num_reqs);
+}
+
+int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
+{
+    return bdrv_ioctl(blk->bs, req, buf);
+}
+
+BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
+                          BlockCompletionFunc *cb, void *opaque)
+{
+    return bdrv_aio_ioctl(blk->bs, req, buf, cb, opaque);
+}
+
+int blk_flush(BlockBackend *blk)
+{
+    return bdrv_flush(blk->bs);
+}
+
+int blk_flush_all(void)
+{
+    return bdrv_flush_all();
+}
+
+void blk_drain_all(void)
+{
+    bdrv_drain_all();
+}
+
+BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read)
+{
+    return bdrv_get_on_error(blk->bs, is_read);
+}
+
+BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read,
+                                      int error)
+{
+    return bdrv_get_error_action(blk->bs, is_read, error);
+}
+
+void blk_error_action(BlockBackend *blk, BlockErrorAction action,
+                      bool is_read, int error)
+{
+    bdrv_error_action(blk->bs, action, is_read, error);
+}
+
+int blk_is_read_only(BlockBackend *blk)
+{
+    return bdrv_is_read_only(blk->bs);
+}
+
+int blk_is_sg(BlockBackend *blk)
+{
+    return bdrv_is_sg(blk->bs);
+}
+
+int blk_enable_write_cache(BlockBackend *blk)
+{
+    return bdrv_enable_write_cache(blk->bs);
+}
+
+void blk_set_enable_write_cache(BlockBackend *blk, bool wce)
+{
+    bdrv_set_enable_write_cache(blk->bs, wce);
+}
+
+int blk_is_inserted(BlockBackend *blk)
+{
+    return bdrv_is_inserted(blk->bs);
+}
+
+void blk_lock_medium(BlockBackend *blk, bool locked)
+{
+    bdrv_lock_medium(blk->bs, locked);
+}
+
+void blk_eject(BlockBackend *blk, bool eject_flag)
+{
+    bdrv_eject(blk->bs, eject_flag);
+}
+
+int blk_get_flags(BlockBackend *blk)
+{
+    return bdrv_get_flags(blk->bs);
+}
+
+void blk_set_guest_block_size(BlockBackend *blk, int align)
+{
+    bdrv_set_guest_block_size(blk->bs, align);
+}
+
+void *blk_blockalign(BlockBackend *blk, size_t size)
+{
+    return qemu_blockalign(blk ? blk->bs : NULL, size);
+}
+
+bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp)
+{
+    return bdrv_op_is_blocked(blk->bs, op, errp);
+}
+
+void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason)
+{
+    bdrv_op_unblock(blk->bs, op, reason);
+}
+
+void blk_op_block_all(BlockBackend *blk, Error *reason)
+{
+    bdrv_op_block_all(blk->bs, reason);
+}
+
+void blk_op_unblock_all(BlockBackend *blk, Error *reason)
+{
+    bdrv_op_unblock_all(blk->bs, reason);
+}
+
+AioContext *blk_get_aio_context(BlockBackend *blk)
+{
+    return bdrv_get_aio_context(blk->bs);
+}
+
+void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
+{
+    bdrv_set_aio_context(blk->bs, new_context);
+}
+
+void blk_io_plug(BlockBackend *blk)
+{
+    bdrv_io_plug(blk->bs);
+}
+
+void blk_io_unplug(BlockBackend *blk)
+{
+    bdrv_io_unplug(blk->bs);
+}
+
+BlockAcctStats *blk_get_stats(BlockBackend *blk)
+{
+    return bdrv_get_stats(blk->bs);
+}
+
+void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
+                  BlockCompletionFunc *cb, void *opaque)
+{
+    return qemu_aio_get(aiocb_info, blk_bs(blk), cb, opaque);
+}
diff --git a/block/commit.c b/block/commit.c
index 91517d3512..60a2accf04 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -182,7 +182,7 @@ static const BlockJobDriver commit_job_driver = {
 
 void commit_start(BlockDriverState *bs, BlockDriverState *base,
                   BlockDriverState *top, int64_t speed,
-                  BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
+                  BlockdevOnError on_error, BlockCompletionFunc *cb,
                   void *opaque, const char *backing_file_str, Error **errp)
 {
     CommitBlockJob *s;
diff --git a/block/curl.c b/block/curl.c
index 225407c643..b4157cc8b3 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -78,7 +78,7 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
 struct BDRVCURLState;
 
 typedef struct CURLAIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     QEMUBH *bh;
     QEMUIOVector *qiov;
 
@@ -680,9 +680,9 @@ static void curl_readv_bh_cb(void *p)
     curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
 }
 
-static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
+static BlockAIOCB *curl_aio_readv(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     CURLAIOCB *acb;
 
diff --git a/block/iscsi.c b/block/iscsi.c
index 3a01de0edb..233f46285c 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -80,7 +80,7 @@ typedef struct IscsiTask {
 } IscsiTask;
 
 typedef struct IscsiAIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     QEMUIOVector *qiov;
     QEMUBH *bh;
     IscsiLun *iscsilun;
@@ -227,7 +227,7 @@ iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
 }
 
 static void
-iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
+iscsi_aio_cancel(BlockAIOCB *blockacb)
 {
     IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
     IscsiLun *iscsilun = acb->iscsilun;
@@ -663,9 +663,9 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
     iscsi_schedule_bh(acb);
 }
 
-static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
+static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
         unsigned long int req, void *buf,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     IscsiLun *iscsilun = bs->opaque;
     struct iscsi_context *iscsi = iscsilun->iscsi;
@@ -1519,7 +1519,7 @@ static int iscsi_create(const char *filename, QemuOpts *opts, Error **errp)
     IscsiLun *iscsilun = NULL;
     QDict *bs_options;
 
-    bs = bdrv_new("", &error_abort);
+    bs = bdrv_new();
 
     /* Read out options */
     total_size = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
diff --git a/block/linux-aio.c b/block/linux-aio.c
index f4baba2be0..d92513b0f9 100644
--- a/block/linux-aio.c
+++ b/block/linux-aio.c
@@ -28,7 +28,7 @@
 #define MAX_QUEUED_IO  128
 
 struct qemu_laiocb {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     struct qemu_laio_state *ctx;
     struct iocb iocb;
     ssize_t ret;
@@ -146,7 +146,7 @@ static void qemu_laio_completion_cb(EventNotifier *e)
     }
 }
 
-static void laio_cancel(BlockDriverAIOCB *blockacb)
+static void laio_cancel(BlockAIOCB *blockacb)
 {
     struct qemu_laiocb *laiocb = (struct qemu_laiocb *)blockacb;
     struct io_event event;
@@ -243,9 +243,9 @@ int laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug)
     return ret;
 }
 
-BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
+BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque, int type)
+        BlockCompletionFunc *cb, void *opaque, int type)
 {
     struct qemu_laio_state *s = aio_ctx;
     struct qemu_laiocb *laiocb;
diff --git a/block/mirror.c b/block/mirror.c
index 18b18e0353..e8a43eb39e 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -567,7 +567,8 @@ static void mirror_complete(BlockJob *job, Error **errp)
         return;
     }
     if (!s->synced) {
-        error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
+        error_set(errp, QERR_BLOCK_JOB_NOT_READY,
+                  bdrv_get_device_name(job->bs));
         return;
     }
 
@@ -612,7 +613,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
                              int64_t buf_size,
                              BlockdevOnError on_source_error,
                              BlockdevOnError on_target_error,
-                             BlockDriverCompletionFunc *cb,
+                             BlockCompletionFunc *cb,
                              void *opaque, Error **errp,
                              const BlockJobDriver *driver,
                              bool is_none_mode, BlockDriverState *base)
@@ -672,7 +673,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
                   int64_t speed, int64_t granularity, int64_t buf_size,
                   MirrorSyncMode mode, BlockdevOnError on_source_error,
                   BlockdevOnError on_target_error,
-                  BlockDriverCompletionFunc *cb,
+                  BlockCompletionFunc *cb,
                   void *opaque, Error **errp)
 {
     bool is_none_mode;
@@ -689,7 +690,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
 void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
                          int64_t speed,
                          BlockdevOnError on_error,
-                         BlockDriverCompletionFunc *cb,
+                         BlockCompletionFunc *cb,
                          void *opaque, Error **errp)
 {
     int64_t length, base_length;
diff --git a/block/nbd.c b/block/nbd.c
index 89775e1e2b..04cc845076 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -342,30 +342,44 @@ static void nbd_attach_aio_context(BlockDriverState *bs,
 
 static void nbd_refresh_filename(BlockDriverState *bs)
 {
-    BDRVNBDState *s = bs->opaque;
     QDict *opts = qdict_new();
-    const char *path   = qemu_opt_get(s->socket_opts, "path");
-    const char *host   = qemu_opt_get(s->socket_opts, "host");
-    const char *port   = qemu_opt_get(s->socket_opts, "port");
-    const char *export = qemu_opt_get(s->socket_opts, "export");
+    const char *path   = qdict_get_try_str(bs->options, "path");
+    const char *host   = qdict_get_try_str(bs->options, "host");
+    const char *port   = qdict_get_try_str(bs->options, "port");
+    const char *export = qdict_get_try_str(bs->options, "export");
 
     qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd")));
 
-    if (path) {
+    if (path && export) {
         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
-                 "nbd+unix:%s", path);
-        qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(path)));
-    } else if (export) {
+                 "nbd+unix:///%s?socket=%s", export, path);
+    } else if (path && !export) {
         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
-                 "nbd:%s:%s/%s", host, port, export);
-        qdict_put_obj(opts, "host",   QOBJECT(qstring_from_str(host)));
-        qdict_put_obj(opts, "port",   QOBJECT(qstring_from_str(port)));
-        qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export)));
-    } else {
+                 "nbd+unix://?socket=%s", path);
+    } else if (!path && export && port) {
+        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
+                 "nbd://%s:%s/%s", host, port, export);
+    } else if (!path && export && !port) {
+        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
+                 "nbd://%s/%s", host, export);
+    } else if (!path && !export && port) {
+        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
+                 "nbd://%s:%s", host, port);
+    } else if (!path && !export && !port) {
         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
-                 "nbd:%s:%s", host, port);
+                 "nbd://%s", host);
+    }
+
+    if (path) {
+        qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(path)));
+    } else if (port) {
         qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host)));
         qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(port)));
+    } else {
+        qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(host)));
+    }
+    if (export) {
+        qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export)));
     }
 
     bs->full_open_options = opts;
diff --git a/block/null.c b/block/null.c
index 828441999c..ec2bd27a4b 100644
--- a/block/null.c
+++ b/block/null.c
@@ -78,7 +78,7 @@ static coroutine_fn int null_co_flush(BlockDriverState *bs)
 }
 
 typedef struct {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     QEMUBH *bh;
 } NullAIOCB;
 
@@ -94,9 +94,9 @@ static void null_bh_cb(void *opaque)
     qemu_aio_unref(acb);
 }
 
-static inline BlockDriverAIOCB *null_aio_common(BlockDriverState *bs,
-                                                BlockDriverCompletionFunc *cb,
-                                                void *opaque)
+static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
+                                          BlockCompletionFunc *cb,
+                                          void *opaque)
 {
     NullAIOCB *acb;
 
@@ -106,27 +106,27 @@ static inline BlockDriverAIOCB *null_aio_common(BlockDriverState *bs,
     return &acb->common;
 }
 
-static BlockDriverAIOCB *null_aio_readv(BlockDriverState *bs,
-                                        int64_t sector_num, QEMUIOVector *qiov,
-                                        int nb_sectors,
-                                        BlockDriverCompletionFunc *cb,
-                                        void *opaque)
+static BlockAIOCB *null_aio_readv(BlockDriverState *bs,
+                                  int64_t sector_num, QEMUIOVector *qiov,
+                                  int nb_sectors,
+                                  BlockCompletionFunc *cb,
+                                  void *opaque)
 {
     return null_aio_common(bs, cb, opaque);
 }
 
-static BlockDriverAIOCB *null_aio_writev(BlockDriverState *bs,
-                                         int64_t sector_num, QEMUIOVector *qiov,
-                                         int nb_sectors,
-                                         BlockDriverCompletionFunc *cb,
-                                         void *opaque)
+static BlockAIOCB *null_aio_writev(BlockDriverState *bs,
+                                   int64_t sector_num, QEMUIOVector *qiov,
+                                   int nb_sectors,
+                                   BlockCompletionFunc *cb,
+                                   void *opaque)
 {
     return null_aio_common(bs, cb, opaque);
 }
 
-static BlockDriverAIOCB *null_aio_flush(BlockDriverState *bs,
-                                        BlockDriverCompletionFunc *cb,
-                                        void *opaque)
+static BlockAIOCB *null_aio_flush(BlockDriverState *bs,
+                                  BlockCompletionFunc *cb,
+                                  void *opaque)
 {
     return null_aio_common(bs, cb, opaque);
 }
diff --git a/block/qapi.c b/block/qapi.c
index 9733ebd328..1301144bbc 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -28,6 +28,7 @@
 #include "qapi-visit.h"
 #include "qapi/qmp-output-visitor.h"
 #include "qapi/qmp/types.h"
+#include "sysemu/block-backend.h"
 #ifdef __linux__
 #include <linux/fs.h>
 #include <sys/ioctl.h>
@@ -264,22 +265,22 @@ void bdrv_query_image_info(BlockDriverState *bs,
 }
 
 /* @p_info will be set only on success. */
-void bdrv_query_info(BlockDriverState *bs,
-                     BlockInfo **p_info,
-                     Error **errp)
+static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
+                            Error **errp)
 {
     BlockInfo *info = g_malloc0(sizeof(*info));
+    BlockDriverState *bs = blk_bs(blk);
     BlockDriverState *bs0;
     ImageInfo **p_image_info;
     Error *local_err = NULL;
-    info->device = g_strdup(bs->device_name);
+    info->device = g_strdup(blk_name(blk));
     info->type = g_strdup("unknown");
-    info->locked = bdrv_dev_is_medium_locked(bs);
-    info->removable = bdrv_dev_has_removable_media(bs);
+    info->locked = blk_dev_is_medium_locked(blk);
+    info->removable = blk_dev_has_removable_media(blk);
 
-    if (bdrv_dev_has_removable_media(bs)) {
+    if (blk_dev_has_removable_media(blk)) {
         info->has_tray_open = true;
-        info->tray_open = bdrv_dev_is_tray_open(bs);
+        info->tray_open = blk_dev_is_tray_open(blk);
     }
 
     if (bdrv_iostatus_is_enabled(bs)) {
@@ -327,9 +328,9 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs)
 
     s = g_malloc0(sizeof(*s));
 
-    if (bs->device_name[0]) {
+    if (bdrv_get_device_name(bs)[0]) {
         s->has_device = true;
-        s->device = g_strdup(bs->device_name);
+        s->device = g_strdup(bdrv_get_device_name(bs));
     }
 
     s->stats = g_malloc0(sizeof(*s->stats));
@@ -360,12 +361,12 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs)
 BlockInfoList *qmp_query_block(Error **errp)
 {
     BlockInfoList *head = NULL, **p_next = &head;
-    BlockDriverState *bs = NULL;
+    BlockBackend *blk;
     Error *local_err = NULL;
 
-     while ((bs = bdrv_next(bs))) {
+    for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
         BlockInfoList *info = g_malloc0(sizeof(*info));
-        bdrv_query_info(bs, &info->value, &local_err);
+        bdrv_query_info(blk, &info->value, &local_err);
         if (local_err) {
             error_propagate(errp, local_err);
             goto err;
diff --git a/block/qcow.c b/block/qcow.c
index a87bd692f0..ece22697a6 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -124,7 +124,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
         snprintf(version, sizeof(version), "QCOW version %" PRIu32,
                  header.version);
         error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
-                  bs->device_name, "qcow", version);
+                  bdrv_get_device_name(bs), "qcow", version);
         ret = -ENOTSUP;
         goto fail;
     }
@@ -231,7 +231,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
     /* Disable migration when qcow images are used */
     error_set(&s->migration_blocker,
               QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
-              "qcow", bs->device_name, "live migration");
+              "qcow", bdrv_get_device_name(bs), "live migration");
     migrate_add_blocker(s->migration_blocker);
 
     qemu_co_mutex_init(&s->lock);
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 2bcaaf9b98..c31d85a840 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -524,6 +524,7 @@ found:
         QTAILQ_REMOVE(&s->discards, p, next);
         d->offset = MIN(d->offset, p->offset);
         d->bytes += p->bytes;
+        g_free(p);
     }
 }
 
diff --git a/block/qcow2.c b/block/qcow2.c
index fb28493c02..156a219993 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -206,8 +206,8 @@ static void GCC_FMT_ATTR(3, 4) report_unsupported(BlockDriverState *bs,
     vsnprintf(msg, sizeof(msg), fmt, ap);
     va_end(ap);
 
-    error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, bs->device_name, "qcow2",
-              msg);
+    error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+              bdrv_get_device_name(bs), "qcow2", msg);
 }
 
 static void report_unsupported_feature(BlockDriverState *bs,
diff --git a/block/qed-gencb.c b/block/qed-gencb.c
index 7d7ac1ffc8..b817a8bf50 100644
--- a/block/qed-gencb.c
+++ b/block/qed-gencb.c
@@ -13,7 +13,7 @@
 
 #include "qed.h"
 
-void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque)
+void *gencb_alloc(size_t len, BlockCompletionFunc *cb, void *opaque)
 {
     GenericCB *gencb = g_malloc(len);
     gencb->cb = cb;
@@ -24,7 +24,7 @@ void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque)
 void gencb_complete(void *opaque, int ret)
 {
     GenericCB *gencb = opaque;
-    BlockDriverCompletionFunc *cb = gencb->cb;
+    BlockCompletionFunc *cb = gencb->cb;
     void *user_opaque = gencb->opaque;
 
     g_free(gencb);
diff --git a/block/qed-table.c b/block/qed-table.c
index f61107a1cf..513aa872c9 100644
--- a/block/qed-table.c
+++ b/block/qed-table.c
@@ -49,7 +49,7 @@ out:
 }
 
 static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
-                           BlockDriverCompletionFunc *cb, void *opaque)
+                           BlockCompletionFunc *cb, void *opaque)
 {
     QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb),
                                                 cb, opaque);
@@ -119,7 +119,7 @@ out:
  */
 static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
                             unsigned int index, unsigned int n, bool flush,
-                            BlockDriverCompletionFunc *cb, void *opaque)
+                            BlockCompletionFunc *cb, void *opaque)
 {
     QEDWriteTableCB *write_table_cb;
     unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
@@ -180,7 +180,7 @@ int qed_read_l1_table_sync(BDRVQEDState *s)
 }
 
 void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n,
-                        BlockDriverCompletionFunc *cb, void *opaque)
+                        BlockCompletionFunc *cb, void *opaque)
 {
     BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
     qed_write_table(s, s->header.l1_table_offset,
@@ -235,7 +235,7 @@ static void qed_read_l2_table_cb(void *opaque, int ret)
 }
 
 void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
-                       BlockDriverCompletionFunc *cb, void *opaque)
+                       BlockCompletionFunc *cb, void *opaque)
 {
     QEDReadL2TableCB *read_l2_table_cb;
 
@@ -275,7 +275,7 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset
 
 void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
                         unsigned int index, unsigned int n, bool flush,
-                        BlockDriverCompletionFunc *cb, void *opaque)
+                        BlockCompletionFunc *cb, void *opaque)
 {
     BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE);
     qed_write_table(s, request->l2_table->offset,
diff --git a/block/qed.c b/block/qed.c
index e2316d7ff0..80f18d82e2 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -130,7 +130,7 @@ static void qed_write_header_read_cb(void *opaque, int ret)
  * This function only updates known header fields in-place and does not affect
  * extra data after the QED header.
  */
-static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc cb,
+static void qed_write_header(BDRVQEDState *s, BlockCompletionFunc cb,
                              void *opaque)
 {
     /* We must write full sectors for O_DIRECT but cannot necessarily generate
@@ -408,7 +408,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
         snprintf(buf, sizeof(buf), "%" PRIx64,
             s->header.features & ~QED_FEATURE_MASK);
         error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
-            bs->device_name, "QED", buf);
+            bdrv_get_device_name(bs), "QED", buf);
         return -ENOTSUP;
     }
     if (!qed_is_cluster_size_valid(s->header.cluster_size)) {
@@ -759,7 +759,7 @@ static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
 static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
                                   QEMUIOVector *qiov,
                                   QEMUIOVector **backing_qiov,
-                                  BlockDriverCompletionFunc *cb, void *opaque)
+                                  BlockCompletionFunc *cb, void *opaque)
 {
     uint64_t backing_length = 0;
     size_t size;
@@ -851,7 +851,7 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret)
  */
 static void qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos,
                                        uint64_t len, uint64_t offset,
-                                       BlockDriverCompletionFunc *cb,
+                                       BlockCompletionFunc *cb,
                                        void *opaque)
 {
     CopyFromBackingFileCB *copy_cb;
@@ -902,7 +902,7 @@ static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index,
 static void qed_aio_complete_bh(void *opaque)
 {
     QEDAIOCB *acb = opaque;
-    BlockDriverCompletionFunc *cb = acb->common.cb;
+    BlockCompletionFunc *cb = acb->common.cb;
     void *user_opaque = acb->common.opaque;
     int ret = acb->bh_ret;
 
@@ -1064,7 +1064,7 @@ static void qed_aio_write_main(void *opaque, int ret)
     BDRVQEDState *s = acb_to_s(acb);
     uint64_t offset = acb->cur_cluster +
                       qed_offset_into_cluster(s, acb->cur_pos);
-    BlockDriverCompletionFunc *next_fn;
+    BlockCompletionFunc *next_fn;
 
     trace_qed_aio_write_main(s, acb, ret, offset, acb->cur_qiov.size);
 
@@ -1164,7 +1164,7 @@ static void qed_aio_write_zero_cluster(void *opaque, int ret)
 static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
 {
     BDRVQEDState *s = acb_to_s(acb);
-    BlockDriverCompletionFunc *cb;
+    BlockCompletionFunc *cb;
 
     /* Cancel timer when the first allocating request comes in */
     if (QSIMPLEQ_EMPTY(&s->allocating_write_reqs)) {
@@ -1365,11 +1365,11 @@ static void qed_aio_next_io(void *opaque, int ret)
                       io_fn, acb);
 }
 
-static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs,
-                                       int64_t sector_num,
-                                       QEMUIOVector *qiov, int nb_sectors,
-                                       BlockDriverCompletionFunc *cb,
-                                       void *opaque, int flags)
+static BlockAIOCB *qed_aio_setup(BlockDriverState *bs,
+                                 int64_t sector_num,
+                                 QEMUIOVector *qiov, int nb_sectors,
+                                 BlockCompletionFunc *cb,
+                                 void *opaque, int flags)
 {
     QEDAIOCB *acb = qemu_aio_get(&qed_aiocb_info, bs, cb, opaque);
 
@@ -1390,20 +1390,20 @@ static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs,
     return &acb->common;
 }
 
-static BlockDriverAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs,
-                                            int64_t sector_num,
-                                            QEMUIOVector *qiov, int nb_sectors,
-                                            BlockDriverCompletionFunc *cb,
-                                            void *opaque)
+static BlockAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs,
+                                      int64_t sector_num,
+                                      QEMUIOVector *qiov, int nb_sectors,
+                                      BlockCompletionFunc *cb,
+                                      void *opaque)
 {
     return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
 }
 
-static BlockDriverAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs,
-                                             int64_t sector_num,
-                                             QEMUIOVector *qiov, int nb_sectors,
-                                             BlockDriverCompletionFunc *cb,
-                                             void *opaque)
+static BlockAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs,
+                                       int64_t sector_num,
+                                       QEMUIOVector *qiov, int nb_sectors,
+                                       BlockCompletionFunc *cb,
+                                       void *opaque)
 {
     return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb,
                          opaque, QED_AIOCB_WRITE);
@@ -1431,7 +1431,7 @@ static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs,
                                                  int nb_sectors,
                                                  BdrvRequestFlags flags)
 {
-    BlockDriverAIOCB *blockacb;
+    BlockAIOCB *blockacb;
     BDRVQEDState *s = bs->opaque;
     QEDWriteZeroesCB cb = { .done = false };
     QEMUIOVector qiov;
diff --git a/block/qed.h b/block/qed.h
index 2b0e724e05..d3934a05cd 100644
--- a/block/qed.h
+++ b/block/qed.h
@@ -128,7 +128,7 @@ enum {
 };
 
 typedef struct QEDAIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     QEMUBH *bh;
     int bh_ret;                     /* final return status for completion bh */
     QSIMPLEQ_ENTRY(QEDAIOCB) next;  /* next request */
@@ -203,11 +203,11 @@ typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t l
  * Generic callback for chaining async callbacks
  */
 typedef struct {
-    BlockDriverCompletionFunc *cb;
+    BlockCompletionFunc *cb;
     void *opaque;
 } GenericCB;
 
-void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque);
+void *gencb_alloc(size_t len, BlockCompletionFunc *cb, void *opaque);
 void gencb_complete(void *opaque, int ret);
 
 /**
@@ -230,16 +230,16 @@ void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table);
  */
 int qed_read_l1_table_sync(BDRVQEDState *s);
 void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n,
-                        BlockDriverCompletionFunc *cb, void *opaque);
+                        BlockCompletionFunc *cb, void *opaque);
 int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
                             unsigned int n);
 int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
                            uint64_t offset);
 void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
-                       BlockDriverCompletionFunc *cb, void *opaque);
+                       BlockCompletionFunc *cb, void *opaque);
 void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
                         unsigned int index, unsigned int n, bool flush,
-                        BlockDriverCompletionFunc *cb, void *opaque);
+                        BlockCompletionFunc *cb, void *opaque);
 int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
                             unsigned int index, unsigned int n, bool flush);
 
diff --git a/block/quorum.c b/block/quorum.c
index 7687466733..437b12251d 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -92,7 +92,7 @@ typedef struct QuorumAIOCB QuorumAIOCB;
  * $children_count QuorumChildRequest.
  */
 typedef struct QuorumChildRequest {
-    BlockDriverAIOCB *aiocb;
+    BlockAIOCB *aiocb;
     QEMUIOVector qiov;
     uint8_t *buf;
     int ret;
@@ -105,7 +105,7 @@ typedef struct QuorumChildRequest {
  * used to do operations on each children and track overall progress.
  */
 struct QuorumAIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
 
     /* Request metadata */
     uint64_t sector_num;
@@ -130,7 +130,7 @@ struct QuorumAIOCB {
 
 static bool quorum_vote(QuorumAIOCB *acb);
 
-static void quorum_aio_cancel(BlockDriverAIOCB *blockacb)
+static void quorum_aio_cancel(BlockAIOCB *blockacb)
 {
     QuorumAIOCB *acb = container_of(blockacb, QuorumAIOCB, common);
     BDRVQuorumState *s = acb->common.bs->opaque;
@@ -186,7 +186,7 @@ static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
                                    QEMUIOVector *qiov,
                                    uint64_t sector_num,
                                    int nb_sectors,
-                                   BlockDriverCompletionFunc *cb,
+                                   BlockCompletionFunc *cb,
                                    void *opaque)
 {
     QuorumAIOCB *acb = qemu_aio_get(&quorum_aiocb_info, bs, cb, opaque);
@@ -226,8 +226,8 @@ static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret)
 
 static void quorum_report_failure(QuorumAIOCB *acb)
 {
-    const char *reference = acb->common.bs->device_name[0] ?
-                            acb->common.bs->device_name :
+    const char *reference = bdrv_get_device_name(acb->common.bs)[0] ?
+                            bdrv_get_device_name(acb->common.bs) :
                             acb->common.bs->node_name;
 
     qapi_event_send_quorum_failure(reference, acb->sector_num,
@@ -264,7 +264,7 @@ static void quorum_rewrite_aio_cb(void *opaque, int ret)
     quorum_aio_finalize(acb);
 }
 
-static BlockDriverAIOCB *read_fifo_child(QuorumAIOCB *acb);
+static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb);
 
 static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source)
 {
@@ -640,7 +640,7 @@ free_exit:
     return rewrite;
 }
 
-static BlockDriverAIOCB *read_quorum_children(QuorumAIOCB *acb)
+static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb)
 {
     BDRVQuorumState *s = acb->common.bs->opaque;
     int i;
@@ -659,7 +659,7 @@ static BlockDriverAIOCB *read_quorum_children(QuorumAIOCB *acb)
     return &acb->common;
 }
 
-static BlockDriverAIOCB *read_fifo_child(QuorumAIOCB *acb)
+static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb)
 {
     BDRVQuorumState *s = acb->common.bs->opaque;
 
@@ -675,12 +675,12 @@ static BlockDriverAIOCB *read_fifo_child(QuorumAIOCB *acb)
     return &acb->common;
 }
 
-static BlockDriverAIOCB *quorum_aio_readv(BlockDriverState *bs,
-                                          int64_t sector_num,
-                                          QEMUIOVector *qiov,
-                                          int nb_sectors,
-                                          BlockDriverCompletionFunc *cb,
-                                          void *opaque)
+static BlockAIOCB *quorum_aio_readv(BlockDriverState *bs,
+                                    int64_t sector_num,
+                                    QEMUIOVector *qiov,
+                                    int nb_sectors,
+                                    BlockCompletionFunc *cb,
+                                    void *opaque)
 {
     BDRVQuorumState *s = bs->opaque;
     QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num,
@@ -696,12 +696,12 @@ static BlockDriverAIOCB *quorum_aio_readv(BlockDriverState *bs,
     return read_fifo_child(acb);
 }
 
-static BlockDriverAIOCB *quorum_aio_writev(BlockDriverState *bs,
-                                          int64_t sector_num,
-                                          QEMUIOVector *qiov,
-                                          int nb_sectors,
-                                          BlockDriverCompletionFunc *cb,
-                                          void *opaque)
+static BlockAIOCB *quorum_aio_writev(BlockDriverState *bs,
+                                     int64_t sector_num,
+                                     QEMUIOVector *qiov,
+                                     int nb_sectors,
+                                     BlockCompletionFunc *cb,
+                                     void *opaque)
 {
     BDRVQuorumState *s = bs->opaque;
     QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, nb_sectors,
diff --git a/block/raw-aio.h b/block/raw-aio.h
index e18c97509a..80681ce175 100644
--- a/block/raw-aio.h
+++ b/block/raw-aio.h
@@ -35,9 +35,9 @@
 #ifdef CONFIG_LINUX_AIO
 void *laio_init(void);
 void laio_cleanup(void *s);
-BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
+BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque, int type);
+        BlockCompletionFunc *cb, void *opaque, int type);
 void laio_detach_aio_context(void *s, AioContext *old_context);
 void laio_attach_aio_context(void *s, AioContext *new_context);
 void laio_io_plug(BlockDriverState *bs, void *aio_ctx);
@@ -49,10 +49,10 @@ typedef struct QEMUWin32AIOState QEMUWin32AIOState;
 QEMUWin32AIOState *win32_aio_init(void);
 void win32_aio_cleanup(QEMUWin32AIOState *aio);
 int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile);
-BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs,
+BlockAIOCB *win32_aio_submit(BlockDriverState *bs,
         QEMUWin32AIOState *aio, HANDLE hfile,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque, int type);
+        BlockCompletionFunc *cb, void *opaque, int type);
 void win32_aio_detach_aio_context(QEMUWin32AIOState *aio,
                                   AioContext *old_context);
 void win32_aio_attach_aio_context(QEMUWin32AIOState *aio,
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 86ce4f2607..ee4ca3c01e 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -1041,9 +1041,9 @@ static int paio_submit_co(BlockDriverState *bs, int fd,
     return thread_pool_submit_co(pool, aio_worker, acb);
 }
 
-static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
+static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque, int type)
+        BlockCompletionFunc *cb, void *opaque, int type)
 {
     RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
     ThreadPool *pool;
@@ -1066,9 +1066,9 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
     return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
 }
 
-static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
+static BlockAIOCB *raw_aio_submit(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque, int type)
+        BlockCompletionFunc *cb, void *opaque, int type)
 {
     BDRVRawState *s = bs->opaque;
 
@@ -1125,24 +1125,24 @@ static void raw_aio_flush_io_queue(BlockDriverState *bs)
 #endif
 }
 
-static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
+static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     return raw_aio_submit(bs, sector_num, qiov, nb_sectors,
                           cb, opaque, QEMU_AIO_READ);
 }
 
-static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
+static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     return raw_aio_submit(bs, sector_num, qiov, nb_sectors,
                           cb, opaque, QEMU_AIO_WRITE);
 }
 
-static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs,
-        BlockDriverCompletionFunc *cb, void *opaque)
+static BlockAIOCB *raw_aio_flush(BlockDriverState *bs,
+        BlockCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
 
@@ -1482,7 +1482,7 @@ static int64_t try_fiemap(BlockDriverState *bs, off_t start, off_t *data,
 
     f.fm.fm_start = start;
     f.fm.fm_length = (int64_t)nb_sectors * BDRV_SECTOR_SIZE;
-    f.fm.fm_flags = 0;
+    f.fm.fm_flags = FIEMAP_FLAG_SYNC;
     f.fm.fm_extent_count = 1;
     f.fm.fm_reserved = 0;
     if (ioctl(s->fd, FS_IOC_FIEMAP, &f) == -1) {
@@ -1571,9 +1571,9 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
 
     start = sector_num * BDRV_SECTOR_SIZE;
 
-    ret = try_fiemap(bs, start, &data, &hole, nb_sectors, pnum);
+    ret = try_seek_hole(bs, start, &data, &hole, pnum);
     if (ret < 0) {
-        ret = try_seek_hole(bs, start, &data, &hole, pnum);
+        ret = try_fiemap(bs, start, &data, &hole, nb_sectors, pnum);
         if (ret < 0) {
             /* Assume everything is allocated. */
             data = 0;
@@ -1595,9 +1595,9 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
     return ret;
 }
 
-static coroutine_fn BlockDriverAIOCB *raw_aio_discard(BlockDriverState *bs,
+static coroutine_fn BlockAIOCB *raw_aio_discard(BlockDriverState *bs,
     int64_t sector_num, int nb_sectors,
-    BlockDriverCompletionFunc *cb, void *opaque)
+    BlockCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
 
@@ -1935,9 +1935,9 @@ static int hdev_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
     return ioctl(s->fd, req, buf);
 }
 
-static BlockDriverAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
+static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
         unsigned long int req, void *buf,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
     RawPosixAIOData *acb;
@@ -1976,9 +1976,9 @@ static int fd_open(BlockDriverState *bs)
 
 #endif /* !linux && !FreeBSD */
 
-static coroutine_fn BlockDriverAIOCB *hdev_aio_discard(BlockDriverState *bs,
+static coroutine_fn BlockAIOCB *hdev_aio_discard(BlockDriverState *bs,
     int64_t sector_num, int nb_sectors,
-    BlockDriverCompletionFunc *cb, void *opaque)
+    BlockCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
 
diff --git a/block/raw-win32.c b/block/raw-win32.c
index 9bf82252b8..7b588815b9 100644
--- a/block/raw-win32.c
+++ b/block/raw-win32.c
@@ -138,9 +138,9 @@ static int aio_worker(void *arg)
     return ret;
 }
 
-static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
+static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque, int type)
+        BlockCompletionFunc *cb, void *opaque, int type)
 {
     RawWin32AIOData *acb = g_slice_new(RawWin32AIOData);
     ThreadPool *pool;
@@ -369,9 +369,9 @@ fail:
     return ret;
 }
 
-static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
+static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
                          int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-                         BlockDriverCompletionFunc *cb, void *opaque)
+                         BlockCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
     if (s->aio) {
@@ -383,9 +383,9 @@ static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
     }
 }
 
-static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
+static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
                           int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-                          BlockDriverCompletionFunc *cb, void *opaque)
+                          BlockCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
     if (s->aio) {
@@ -397,8 +397,8 @@ static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
     }
 }
 
-static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs,
-                         BlockDriverCompletionFunc *cb, void *opaque)
+static BlockAIOCB *raw_aio_flush(BlockDriverState *bs,
+                         BlockCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
     return paio_submit(bs, s->hfile, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH);
diff --git a/block/raw_bsd.c b/block/raw_bsd.c
index f82f4c25df..401b967e85 100644
--- a/block/raw_bsd.c
+++ b/block/raw_bsd.c
@@ -129,10 +129,10 @@ static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
     return bdrv_ioctl(bs->file, req, buf);
 }
 
-static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs,
-                                       unsigned long int req, void *buf,
-                                       BlockDriverCompletionFunc *cb,
-                                       void *opaque)
+static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs,
+                                 unsigned long int req, void *buf,
+                                 BlockCompletionFunc *cb,
+                                 void *opaque)
 {
     return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque);
 }
diff --git a/block/rbd.c b/block/rbd.c
index 96947e328a..47cab8be94 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -68,7 +68,7 @@ typedef enum {
 } RBDAIOCmd;
 
 typedef struct RBDAIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     QEMUBH *bh;
     int64_t ret;
     QEMUIOVector *qiov;
@@ -589,13 +589,13 @@ static int rbd_aio_flush_wrapper(rbd_image_t image,
 #endif
 }
 
-static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
-                                       int64_t sector_num,
-                                       QEMUIOVector *qiov,
-                                       int nb_sectors,
-                                       BlockDriverCompletionFunc *cb,
-                                       void *opaque,
-                                       RBDAIOCmd cmd)
+static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
+                                 int64_t sector_num,
+                                 QEMUIOVector *qiov,
+                                 int nb_sectors,
+                                 BlockCompletionFunc *cb,
+                                 void *opaque,
+                                 RBDAIOCmd cmd)
 {
     RBDAIOCB *acb;
     RADOSCB *rcb = NULL;
@@ -675,32 +675,32 @@ failed:
     return NULL;
 }
 
-static BlockDriverAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
-                                            int64_t sector_num,
-                                            QEMUIOVector *qiov,
-                                            int nb_sectors,
-                                            BlockDriverCompletionFunc *cb,
-                                            void *opaque)
+static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
+                                      int64_t sector_num,
+                                      QEMUIOVector *qiov,
+                                      int nb_sectors,
+                                      BlockCompletionFunc *cb,
+                                      void *opaque)
 {
     return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
                          RBD_AIO_READ);
 }
 
-static BlockDriverAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
-                                             int64_t sector_num,
-                                             QEMUIOVector *qiov,
-                                             int nb_sectors,
-                                             BlockDriverCompletionFunc *cb,
-                                             void *opaque)
+static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
+                                       int64_t sector_num,
+                                       QEMUIOVector *qiov,
+                                       int nb_sectors,
+                                       BlockCompletionFunc *cb,
+                                       void *opaque)
 {
     return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
                          RBD_AIO_WRITE);
 }
 
 #ifdef LIBRBD_SUPPORTS_AIO_FLUSH
-static BlockDriverAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs,
-                                            BlockDriverCompletionFunc *cb,
-                                            void *opaque)
+static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs,
+                                      BlockCompletionFunc *cb,
+                                      void *opaque)
 {
     return rbd_start_aio(bs, 0, NULL, 0, cb, opaque, RBD_AIO_FLUSH);
 }
@@ -876,11 +876,11 @@ static int qemu_rbd_snap_list(BlockDriverState *bs,
 }
 
 #ifdef LIBRBD_SUPPORTS_DISCARD
-static BlockDriverAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs,
-                                              int64_t sector_num,
-                                              int nb_sectors,
-                                              BlockDriverCompletionFunc *cb,
-                                              void *opaque)
+static BlockAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs,
+                                        int64_t sector_num,
+                                        int nb_sectors,
+                                        BlockCompletionFunc *cb,
+                                        void *opaque)
 {
     return rbd_start_aio(bs, sector_num, NULL, nb_sectors, cb, opaque,
                          RBD_AIO_DISCARD);
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 2d78ef91f7..be3176fcb4 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -301,7 +301,7 @@ enum AIOCBState {
 };
 
 struct SheepdogAIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
 
     QEMUIOVector *qiov;
 
@@ -473,7 +473,7 @@ static bool sd_acb_cancelable(const SheepdogAIOCB *acb)
     return true;
 }
 
-static void sd_aio_cancel(BlockDriverAIOCB *blockacb)
+static void sd_aio_cancel(BlockAIOCB *blockacb)
 {
     SheepdogAIOCB *acb = (SheepdogAIOCB *)blockacb;
     BDRVSheepdogState *s = acb->common.bs->opaque;
diff --git a/block/stream.c b/block/stream.c
index cdea3e8d0c..a1dc8da484 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -220,7 +220,7 @@ static const BlockJobDriver stream_job_driver = {
 void stream_start(BlockDriverState *bs, BlockDriverState *base,
                   const char *backing_file_str, int64_t speed,
                   BlockdevOnError on_error,
-                  BlockDriverCompletionFunc *cb,
+                  BlockCompletionFunc *cb,
                   void *opaque, Error **errp)
 {
     StreamBlockJob *s;
diff --git a/block/vdi.c b/block/vdi.c
index cfa08b0b93..9604721476 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -490,7 +490,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
     /* Disable migration when vdi images are used */
     error_set(&s->migration_blocker,
               QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
-              "vdi", bs->device_name, "live migration");
+              "vdi", bdrv_get_device_name(bs), "live migration");
     migrate_add_blocker(s->migration_blocker);
 
     return 0;
diff --git a/block/vhdx.c b/block/vhdx.c
index b0464487a5..12bfe75e51 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -1004,7 +1004,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
     /* Disable migration when VHDX images are used */
     error_set(&s->migration_blocker,
             QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
-            "vhdx", bs->device_name, "live migration");
+            "vhdx", bdrv_get_device_name(bs), "live migration");
     migrate_add_blocker(s->migration_blocker);
 
     return 0;
diff --git a/block/vmdk.c b/block/vmdk.c
index 4ae6c75310..673d3f5fb4 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -657,7 +657,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
         snprintf(buf, sizeof(buf), "VMDK version %" PRId32,
                  le32_to_cpu(header.version));
         error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
-                  bs->device_name, "vmdk", buf);
+                  bdrv_get_device_name(bs), "vmdk", buf);
         return -ENOTSUP;
     } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) {
         /* VMware KB 2064959 explains that version 3 added support for
@@ -939,7 +939,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
     /* Disable migration when VMDK images are used */
     error_set(&s->migration_blocker,
               QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
-              "vmdk", bs->device_name, "live migration");
+              "vmdk", bdrv_get_device_name(bs), "live migration");
     migrate_add_blocker(s->migration_blocker);
     g_free(buf);
     return 0;
diff --git a/block/vpc.c b/block/vpc.c
index e08144a76e..38c4f02fff 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -320,7 +320,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
     /* Disable migration when VHD images are used */
     error_set(&s->migration_blocker,
               QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
-              "vpc", bs->device_name, "live migration");
+              "vpc", bdrv_get_device_name(bs), "live migration");
     migrate_add_blocker(s->migration_blocker);
 
     return 0;
diff --git a/block/vvfat.c b/block/vvfat.c
index 731e591ec1..cefe3a4f79 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -1182,7 +1182,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
     if (s->qcow) {
         error_set(&s->migration_blocker,
                   QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
-                  "vvfat (rw)", bs->device_name, "live migration");
+                  "vvfat (rw)", bdrv_get_device_name(bs), "live migration");
         migrate_add_blocker(s->migration_blocker);
     }
 
@@ -2939,7 +2939,7 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp)
     unlink(s->qcow_filename);
 #endif
 
-    bdrv_set_backing_hd(s->bs, bdrv_new("", &error_abort));
+    bdrv_set_backing_hd(s->bs, bdrv_new());
     s->bs->backing_hd->drv = &vvfat_write_target;
     s->bs->backing_hd->opaque = g_new(void *, 1);
     *(void**)s->bs->backing_hd->opaque = s;
diff --git a/block/win32-aio.c b/block/win32-aio.c
index 9323c1aed1..64e86827bc 100644
--- a/block/win32-aio.c
+++ b/block/win32-aio.c
@@ -44,7 +44,7 @@ struct QEMUWin32AIOState {
 };
 
 typedef struct QEMUWin32AIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     struct QEMUWin32AIOState *ctx;
     int nbytes;
     OVERLAPPED ov;
@@ -110,10 +110,10 @@ static const AIOCBInfo win32_aiocb_info = {
     .aiocb_size         = sizeof(QEMUWin32AIOCB),
 };
 
-BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs,
+BlockAIOCB *win32_aio_submit(BlockDriverState *bs,
         QEMUWin32AIOState *aio, HANDLE hfile,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque, int type)
+        BlockCompletionFunc *cb, void *opaque, int type)
 {
     struct QEMUWin32AIOCB *waiocb;
     uint64_t offset = sector_num * 512;
diff --git a/blockdev.c b/blockdev.c
index e595910476..eb743a956c 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -30,6 +30,7 @@
  * THE SOFTWARE.
  */
 
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "hw/block/block.h"
 #include "block/blockjob.h"
@@ -46,8 +47,6 @@
 #include "trace.h"
 #include "sysemu/arch_init.h"
 
-static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
-
 static const char *const if_name[IF_COUNT] = {
     [IF_NONE] = "none",
     [IF_IDE] = "ide",
@@ -85,13 +84,15 @@ static int if_max_devs[IF_COUNT] = {
  */
 void override_max_devs(BlockInterfaceType type, int max_devs)
 {
+    BlockBackend *blk;
     DriveInfo *dinfo;
 
     if (max_devs <= 0) {
         return;
     }
 
-    QTAILQ_FOREACH(dinfo, &drives, next) {
+    for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
+        dinfo = blk_legacy_dinfo(blk);
         if (dinfo->type == type) {
             fprintf(stderr, "Cannot override units-per-bus property of"
                     " the %s interface, because a drive of that type has"
@@ -110,28 +111,27 @@ void override_max_devs(BlockInterfaceType type, int max_devs)
  * automatic deletion, and generic qdev code calls blockdev_auto_del()
  * when deletion is actually safe.
  */
-void blockdev_mark_auto_del(BlockDriverState *bs)
+void blockdev_mark_auto_del(BlockBackend *blk)
 {
-    DriveInfo *dinfo = drive_get_by_blockdev(bs);
+    DriveInfo *dinfo = blk_legacy_dinfo(blk);
+    BlockDriverState *bs = blk_bs(blk);
 
-    if (dinfo && !dinfo->enable_auto_del) {
+    if (!dinfo) {
         return;
     }
 
     if (bs->job) {
         block_job_cancel(bs->job);
     }
-    if (dinfo) {
-        dinfo->auto_del = 1;
-    }
+    dinfo->auto_del = 1;
 }
 
-void blockdev_auto_del(BlockDriverState *bs)
+void blockdev_auto_del(BlockBackend *blk)
 {
-    DriveInfo *dinfo = drive_get_by_blockdev(bs);
+    DriveInfo *dinfo = blk_legacy_dinfo(blk);
 
     if (dinfo && dinfo->auto_del) {
-        drive_del(dinfo);
+        blk_unref(blk);
     }
 }
 
@@ -193,15 +193,15 @@ QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
 
 DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit)
 {
+    BlockBackend *blk;
     DriveInfo *dinfo;
 
-    /* seek interface, bus and unit */
-
-    QTAILQ_FOREACH(dinfo, &drives, next) {
-        if (dinfo->type == type &&
-	    dinfo->bus == bus &&
-	    dinfo->unit == unit)
+    for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
+        dinfo = blk_legacy_dinfo(blk);
+        if (dinfo && dinfo->type == type
+            && dinfo->bus == bus && dinfo->unit == unit) {
             return dinfo;
+        }
     }
 
     return NULL;
@@ -209,17 +209,19 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit)
 
 bool drive_check_orphaned(void)
 {
+    BlockBackend *blk;
     DriveInfo *dinfo;
     bool rs = false;
 
-    QTAILQ_FOREACH(dinfo, &drives, next) {
+    for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
+        dinfo = blk_legacy_dinfo(blk);
         /* If dinfo->bdrv->dev is NULL, it has no device attached. */
         /* Unless this is a default drive, this may be an oversight. */
-        if (!dinfo->bdrv->dev && !dinfo->is_default &&
+        if (!blk_get_attached_dev(blk) && !dinfo->is_default &&
             dinfo->type != IF_NONE) {
             fprintf(stderr, "Warning: Orphaned drive without device: "
                     "id=%s,file=%s,if=%s,bus=%d,unit=%d\n",
-                    dinfo->id, dinfo->bdrv->filename, if_name[dinfo->type],
+                    blk_name(blk), blk_bs(blk)->filename, if_name[dinfo->type],
                     dinfo->bus, dinfo->unit);
             rs = true;
         }
@@ -238,13 +240,15 @@ DriveInfo *drive_get_by_index(BlockInterfaceType type, int index)
 int drive_get_max_bus(BlockInterfaceType type)
 {
     int max_bus;
+    BlockBackend *blk;
     DriveInfo *dinfo;
 
     max_bus = -1;
-    QTAILQ_FOREACH(dinfo, &drives, next) {
-        if(dinfo->type == type &&
-           dinfo->bus > max_bus)
+    for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
+        dinfo = blk_legacy_dinfo(blk);
+        if (dinfo && dinfo->type == type && dinfo->bus > max_bus) {
             max_bus = dinfo->bus;
+        }
     }
     return max_bus;
 }
@@ -259,40 +263,11 @@ DriveInfo *drive_get_next(BlockInterfaceType type)
     return drive_get(type, 0, next_block_unit[type]++);
 }
 
-DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
-{
-    DriveInfo *dinfo;
-
-    QTAILQ_FOREACH(dinfo, &drives, next) {
-        if (dinfo->bdrv == bs) {
-            return dinfo;
-        }
-    }
-    return NULL;
-}
-
 static void bdrv_format_print(void *opaque, const char *name)
 {
     error_printf(" %s", name);
 }
 
-void drive_del(DriveInfo *dinfo)
-{
-    bdrv_unref(dinfo->bdrv);
-}
-
-void drive_info_del(DriveInfo *dinfo)
-{
-    if (!dinfo) {
-        return;
-    }
-    qemu_opts_del(dinfo->opts);
-    g_free(dinfo->id);
-    QTAILQ_REMOVE(&drives, dinfo, next);
-    g_free(dinfo->serial);
-    g_free(dinfo);
-}
-
 typedef struct {
     QEMUBH *bh;
     BlockDriverState *bs;
@@ -360,15 +335,15 @@ static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
 typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
 
 /* Takes the ownership of bs_opts */
-static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
-                                Error **errp)
+static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
+                                   Error **errp)
 {
     const char *buf;
     int ro = 0;
     int bdrv_flags = 0;
     int on_read_error, on_write_error;
+    BlockBackend *blk;
     BlockDriverState *bs;
-    DriveInfo *dinfo;
     ThrottleConfig cfg;
     int snapshot = 0;
     bool copy_on_read;
@@ -523,10 +498,11 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
     }
 
     /* init */
-    bs = bdrv_new(qemu_opts_id(opts), errp);
-    if (!bs) {
+    blk = blk_new_with_bs(qemu_opts_id(opts), errp);
+    if (!blk) {
         goto early_err;
     }
+    bs = blk_bs(blk);
     bs->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
     bs->read_only = ro;
     bs->detect_zeroes = detect_zeroes;
@@ -539,18 +515,13 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
         bdrv_set_io_limits(bs, &cfg);
     }
 
-    dinfo = g_malloc0(sizeof(*dinfo));
-    dinfo->id = g_strdup(qemu_opts_id(opts));
-    dinfo->bdrv = bs;
-    QTAILQ_INSERT_TAIL(&drives, dinfo, next);
-
     if (!file || !*file) {
         if (has_driver_specific_opts) {
             file = NULL;
         } else {
             QDECREF(bs_opts);
             qemu_opts_del(opts);
-            return dinfo;
+            return blk;
         }
     }
     if (snapshot) {
@@ -571,11 +542,11 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
 
     QINCREF(bs_opts);
     ret = bdrv_open(&bs, file, NULL, bs_opts, bdrv_flags, drv, &error);
-    assert(bs == dinfo->bdrv);
+    assert(bs == blk_bs(blk));
 
     if (ret < 0) {
         error_setg(errp, "could not open disk image %s: %s",
-                   file ?: dinfo->id, error_get_pretty(error));
+                   file ?: blk_name(blk), error_get_pretty(error));
         error_free(error);
         goto err;
     }
@@ -587,10 +558,10 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
     QDECREF(bs_opts);
     qemu_opts_del(opts);
 
-    return dinfo;
+    return blk;
 
 err:
-    bdrv_unref(bs);
+    blk_unref(blk);
 early_err:
     qemu_opts_del(opts);
 err_no_opts:
@@ -703,6 +674,7 @@ QemuOptsList qemu_legacy_drive_opts = {
 DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
 {
     const char *value;
+    BlockBackend *blk;
     DriveInfo *dinfo = NULL;
     QDict *bs_opts;
     QemuOpts *legacy_opts;
@@ -1000,9 +972,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
     }
 
     /* Actual block device init: Functionality shared with blockdev-add */
-    dinfo = blockdev_init(filename, bs_opts, &local_err);
+    blk = blockdev_init(filename, bs_opts, &local_err);
     bs_opts = NULL;
-    if (dinfo == NULL) {
+    if (!blk) {
         if (local_err) {
             error_report("%s", error_get_pretty(local_err));
             error_free(local_err);
@@ -1012,8 +984,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
         assert(!local_err);
     }
 
-    /* Set legacy DriveInfo fields */
-    dinfo->enable_auto_del = true;
+    /* Create legacy DriveInfo */
+    dinfo = g_malloc0(sizeof(*dinfo));
     dinfo->opts = all_opts;
 
     dinfo->cyls = cyls;
@@ -1025,9 +997,10 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
     dinfo->bus = bus_id;
     dinfo->unit = unit_id;
     dinfo->devaddr = devaddr;
-
     dinfo->serial = g_strdup(serial);
 
+    blk_set_legacy_dinfo(blk, dinfo);
+
     switch(type) {
     case IF_IDE:
     case IF_SCSI:
@@ -1620,19 +1593,21 @@ exit:
 }
 
 
-static void eject_device(BlockDriverState *bs, int force, Error **errp)
+static void eject_device(BlockBackend *blk, int force, Error **errp)
 {
+    BlockDriverState *bs = blk_bs(blk);
+
     if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
         return;
     }
-    if (!bdrv_dev_has_removable_media(bs)) {
+    if (!blk_dev_has_removable_media(blk)) {
         error_setg(errp, "Device '%s' is not removable",
                    bdrv_get_device_name(bs));
         return;
     }
 
-    if (bdrv_dev_is_medium_locked(bs) && !bdrv_dev_is_tray_open(bs)) {
-        bdrv_dev_eject_request(bs, force);
+    if (blk_dev_is_medium_locked(blk) && !blk_dev_is_tray_open(blk)) {
+        blk_dev_eject_request(blk, force);
         if (!force) {
             error_setg(errp, "Device '%s' is locked",
                        bdrv_get_device_name(bs));
@@ -1645,15 +1620,15 @@ static void eject_device(BlockDriverState *bs, int force, Error **errp)
 
 void qmp_eject(const char *device, bool has_force, bool force, Error **errp)
 {
-    BlockDriverState *bs;
+    BlockBackend *blk;
 
-    bs = bdrv_find(device);
-    if (!bs) {
+    blk = blk_by_name(device);
+    if (!blk) {
         error_set(errp, QERR_DEVICE_NOT_FOUND, device);
         return;
     }
 
-    eject_device(bs, force, errp);
+    eject_device(blk, force, errp);
 }
 
 void qmp_block_passwd(bool has_device, const char *device,
@@ -1712,16 +1687,18 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
 void qmp_change_blockdev(const char *device, const char *filename,
                          const char *format, Error **errp)
 {
+    BlockBackend *blk;
     BlockDriverState *bs;
     BlockDriver *drv = NULL;
     int bdrv_flags;
     Error *err = NULL;
 
-    bs = bdrv_find(device);
-    if (!bs) {
+    blk = blk_by_name(device);
+    if (!blk) {
         error_set(errp, QERR_DEVICE_NOT_FOUND, device);
         return;
     }
+    bs = blk_bs(blk);
 
     if (format) {
         drv = bdrv_find_whitelisted_format(format, bs->read_only);
@@ -1731,7 +1708,7 @@ void qmp_change_blockdev(const char *device, const char *filename,
         }
     }
 
-    eject_device(bs, 0, &err);
+    eject_device(blk, 0, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -1829,19 +1806,19 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
 int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
 {
     const char *id = qdict_get_str(qdict, "id");
+    BlockBackend *blk;
     BlockDriverState *bs;
-    DriveInfo *dinfo;
     AioContext *aio_context;
     Error *local_err = NULL;
 
-    bs = bdrv_find(id);
-    if (!bs) {
+    blk = blk_by_name(id);
+    if (!blk) {
         error_report("Device '%s' not found", id);
         return -1;
     }
+    bs = blk_bs(blk);
 
-    dinfo = drive_get_by_blockdev(bs);
-    if (dinfo && !dinfo->enable_auto_del) {
+    if (!blk_legacy_dinfo(blk)) {
         error_report("Deleting device added with blockdev-add"
                      " is not supported");
         return -1;
@@ -1867,14 +1844,13 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
      * can be removed.  If this is a drive with no device backing
      * then we can just get rid of the block driver state right here.
      */
-    if (bdrv_get_attached_dev(bs)) {
-        bdrv_make_anon(bs);
-
+    if (blk_get_attached_dev(blk)) {
+        blk_hide_on_behalf_of_do_drive_del(blk);
         /* Further I/O must not pause the guest */
         bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT,
                           BLOCKDEV_ON_ERROR_REPORT);
     } else {
-        drive_del(dinfo);
+        blk_unref(blk);
     }
 
     aio_context_release(aio_context);
@@ -2566,7 +2542,7 @@ void qmp_change_backing_file(const char *device,
 void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
 {
     QmpOutputVisitor *ov = qmp_output_visitor_new();
-    DriveInfo *dinfo;
+    BlockBackend *blk;
     QObject *obj;
     QDict *qdict;
     Error *local_err = NULL;
@@ -2604,14 +2580,14 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
 
     qdict_flatten(qdict);
 
-    dinfo = blockdev_init(NULL, qdict, &local_err);
+    blk = blockdev_init(NULL, qdict, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         goto fail;
     }
 
-    if (bdrv_key_required(dinfo->bdrv)) {
-        drive_del(dinfo);
+    if (bdrv_key_required(blk_bs(blk))) {
+        blk_unref(blk);
         error_setg(errp, "blockdev-add doesn't support encrypted devices");
         goto fail;
     }
@@ -2620,26 +2596,21 @@ fail:
     qmp_output_visitor_cleanup(ov);
 }
 
-static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
+BlockJobInfoList *qmp_query_block_jobs(Error **errp)
 {
-    BlockJobInfoList **prev = opaque;
-    BlockJob *job = bs->job;
+    BlockJobInfoList *head = NULL, **p_next = &head;
+    BlockDriverState *bs;
 
-    if (job) {
-        BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
-        elem->value = block_job_query(bs->job);
-        (*prev)->next = elem;
-        *prev = elem;
+    for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) {
+        if (bs->job) {
+            BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
+            elem->value = block_job_query(bs->job);
+            *p_next = elem;
+            p_next = &elem->next;
+        }
     }
-}
 
-BlockJobInfoList *qmp_query_block_jobs(Error **errp)
-{
-    /* Dummy is a fake list element for holding the head pointer */
-    BlockJobInfoList dummy = {};
-    BlockJobInfoList *prev = &dummy;
-    bdrv_iterate(do_qmp_query_block_jobs_one, &prev);
-    return dummy.next;
+    return head;
 }
 
 QemuOptsList qemu_common_drive_opts = {
diff --git a/blockjob.c b/blockjob.c
index 0689fdd2b5..ff0908a653 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -36,7 +36,7 @@
 #include "qapi-event.h"
 
 void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
-                       int64_t speed, BlockDriverCompletionFunc *cb,
+                       int64_t speed, BlockCompletionFunc *cb,
                        void *opaque, Error **errp)
 {
     BlockJob *job;
@@ -107,7 +107,8 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
 void block_job_complete(BlockJob *job, Error **errp)
 {
     if (job->paused || job->cancelled || !job->driver->complete) {
-        error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
+        error_set(errp, QERR_BLOCK_JOB_NOT_READY,
+                  bdrv_get_device_name(job->bs));
         return;
     }
 
@@ -154,7 +155,7 @@ void block_job_iostatus_reset(BlockJob *job)
 
 struct BlockCancelData {
     BlockJob *job;
-    BlockDriverCompletionFunc *cb;
+    BlockCompletionFunc *cb;
     void *opaque;
     bool cancelled;
     int ret;
diff --git a/configure b/configure
index 9ac2600230..a9e4d49483 100755
--- a/configure
+++ b/configure
@@ -4211,9 +4211,9 @@ EOF
   fi
 fi
 
-# add pixman flags after all config tests are done
-QEMU_CFLAGS="$QEMU_CFLAGS $pixman_cflags $fdt_cflags"
-libs_softmmu="$libs_softmmu $pixman_libs"
+# prepend pixman and ftd flags after all config tests are done
+QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS"
+libs_softmmu="$pixman_libs $libs_softmmu"
 
 echo "Install prefix    $prefix"
 echo "BIOS directory    `eval echo $qemu_datadir`"
diff --git a/device-hotplug.c b/device-hotplug.c
index e6a1ffb9fa..9e38cc4808 100644
--- a/device-hotplug.c
+++ b/device-hotplug.c
@@ -24,6 +24,7 @@
 
 #include "hw/hw.h"
 #include "hw/boards.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "qemu/config-file.h"
 #include "sysemu/sysemu.h"
@@ -76,6 +77,6 @@ void drive_hot_add(Monitor *mon, const QDict *qdict)
 
 err:
     if (dinfo) {
-        drive_del(dinfo);
+        blk_unref(blk_by_legacy_dinfo(dinfo));
     }
 }
diff --git a/disas/mips.c b/disas/mips.c
index 2106b574cb..7297825138 100644
--- a/disas/mips.c
+++ b/disas/mips.c
@@ -119,6 +119,8 @@ see <http://www.gnu.org/licenses/>.  */
 #define OP_SH_IMMEDIATE		0
 #define OP_MASK_DELTA		0xffff
 #define OP_SH_DELTA		0
+#define OP_MASK_DELTA_R6        0x1ff
+#define OP_SH_DELTA_R6          7
 #define OP_MASK_FUNCT		0x3f
 #define OP_SH_FUNCT		0
 #define OP_MASK_SPEC		0x3f
@@ -405,6 +407,12 @@ struct mips_opcode
    "+3" UDI immediate bits 6-20
    "+4" UDI immediate bits 6-25
 
+   R6 immediates/displacements :
+   (adding suffix to 'o' to avoid adding new characters)
+   "+o"  9 bits immediate/displacement (shift = 7)
+   "+o1" 18 bits immediate/displacement (shift = 0)
+   "+o2" 19 bits immediate/displacement (shift = 0)
+
    Other:
    "()" parens surrounding optional value
    ","  separates operands
@@ -521,6 +529,8 @@ struct mips_opcode
 #define INSN_ISA64                0x00000040
 #define INSN_ISA32R2              0x00000080
 #define INSN_ISA64R2              0x00000100
+#define INSN_ISA32R6              0x00000200
+#define INSN_ISA64R6              0x00000400
 
 /* Masks used for MIPS-defined ASEs.  */
 #define INSN_ASE_MASK		  0x0000f000
@@ -585,6 +595,8 @@ struct mips_opcode
 #define       ISA_MIPS32R2    (ISA_MIPS32 | INSN_ISA32R2)
 #define       ISA_MIPS64R2    (ISA_MIPS64 | INSN_ISA32R2 | INSN_ISA64R2)
 
+#define       ISA_MIPS32R6    (ISA_MIPS32R2 | INSN_ISA32R6)
+#define       ISA_MIPS64R6    (ISA_MIPS64R2 | INSN_ISA32R6 | INSN_ISA64R6)
 
 /* CPU defines, use instead of hardcoding processor number. Keep this
    in sync with bfd/archures.c in order for machine selection to work.  */
@@ -1121,6 +1133,8 @@ extern const int bfd_mips16_num_opcodes;
 #define I64     INSN_ISA64
 #define I33	INSN_ISA32R2
 #define I65	INSN_ISA64R2
+#define I32R6   INSN_ISA32R6
+#define I64R6   INSN_ISA64R6
 
 /* MIPS64 MIPS-3D ASE support.  */
 #define I16     INSN_MIPS16
@@ -1209,6 +1223,146 @@ const struct mips_opcode mips_builtin_opcodes[] =
    them first.  The assemblers uses a hash table based on the
    instruction name anyhow.  */
 /* name,    args,	match,	    mask,	pinfo,          	membership */
+{"lwpc",    "s,+o2",    0xec080000, 0xfc180000, WR_d,                 0, I32R6},
+{"lwupc",   "s,+o2",    0xec100000, 0xfc180000, WR_d,                 0, I64R6},
+{"ldpc",    "s,+o1",    0xec180000, 0xfc1c0000, WR_d,                 0, I64R6},
+{"addiupc", "s,+o2",    0xec000000, 0xfc180000, WR_d,                 0, I32R6},
+{"auipc",   "s,u",      0xec1e0000, 0xfc1f0000, WR_d,                 0, I32R6},
+{"aluipc",  "s,u",      0xec1f0000, 0xfc1f0000, WR_d,                 0, I32R6},
+{"daui",    "s,t,u",    0x74000000, 0xfc000000, RD_s|WR_t,            0, I64R6},
+{"dahi",    "s,u",      0x04060000, 0xfc1f0000, RD_s,                 0, I64R6},
+{"dati",    "s,u",      0x041e0000, 0xfc1f0000, RD_s,                 0, I64R6},
+{"lsa",     "d,s,t",    0x00000005, 0xfc00073f, WR_d|RD_s|RD_t,       0, I32R6},
+{"dlsa",    "d,s,t",    0x00000015, 0xfc00073f, WR_d|RD_s|RD_t,       0, I64R6},
+{"clz",     "U,s",      0x00000050, 0xfc1f07ff, WR_d|RD_s,            0, I32R6},
+{"clo",     "U,s",      0x00000051, 0xfc1f07ff, WR_d|RD_s,            0, I32R6},
+{"dclz",    "U,s",      0x00000052, 0xfc1f07ff, WR_d|RD_s,            0, I64R6},
+{"dclo",    "U,s",      0x00000053, 0xfc1f07ff, WR_d|RD_s,            0, I64R6},
+{"sdbbp",   "B",        0x0000000e, 0xfc00003f, TRAP,                 0, I32R6},
+{"mul",     "d,s,t",    0x00000098, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I32R6},
+{"muh",     "d,s,t",    0x000000d8, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I32R6},
+{"mulu",    "d,s,t",    0x00000099, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I32R6},
+{"muhu",    "d,s,t",    0x000000d9, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I32R6},
+{"div",     "d,s,t",    0x0000009a, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I32R6},
+{"mod",     "d,s,t",    0x000000da, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I32R6},
+{"divu",    "d,s,t",    0x0000009b, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I32R6},
+{"modu",    "d,s,t",    0x000000db, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I32R6},
+{"dmul",    "d,s,t",    0x0000009c, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I64R6},
+{"dmuh",    "d,s,t",    0x000000dc, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I64R6},
+{"dmulu",   "d,s,t",    0x0000009d, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I64R6},
+{"dmuhu",   "d,s,t",    0x000000dd, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I64R6},
+{"ddiv",    "d,s,t",    0x0000009e, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I64R6},
+{"dmod",    "d,s,t",    0x000000de, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I64R6},
+{"ddivu",   "d,s,t",    0x0000009f, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I64R6},
+{"dmodu",   "d,s,t",    0x000000df, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I64R6},
+{"ll",      "t,o(b)",   0x7c000036, 0xfc00007f, LDD|RD_b|WR_t,        0, I32R6},
+{"sc",      "t,o(b)",   0x7c000026, 0xfc00007f, LDD|RD_b|WR_t,        0, I32R6},
+{"lld",     "t,o(b)",   0x7c000037, 0xfc00007f, LDD|RD_b|WR_t,        0, I64R6},
+{"scd",     "t,o(b)",   0x7c000027, 0xfc00007f, LDD|RD_b|WR_t,        0, I64R6},
+{"pref",    "h,o(b)",   0x7c000035, 0xfc00007f, RD_b,                 0, I32R6},
+{"cache",   "k,o(b)",   0x7c000025, 0xfc00007f, RD_b,                 0, I32R6},
+{"seleqz",  "d,v,t",    0x00000035, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I32R6},
+{"selnez",  "d,v,t",    0x00000037, 0xfc0007ff, WR_d|RD_s|RD_t,       0, I32R6},
+{"maddf.s", "D,S,T",    0x46000018, 0xffe0003f, WR_D|RD_S|RD_T|FP_S,  0, I32R6},
+{"maddf.d", "D,S,T",    0x46200018, 0xffe0003f, WR_D|RD_S|RD_T|FP_D,  0, I32R6},
+{"msubf.s", "D,S,T",    0x46000019, 0xffe0003f, WR_D|RD_S|RD_T|FP_S,  0, I32R6},
+{"msubf.d", "D,S,T",    0x46200019, 0xffe0003f, WR_D|RD_S|RD_T|FP_D,  0, I32R6},
+{"max.s",   "D,S,T",    0x4600001e, 0xffe0003f, WR_D|RD_S|RD_T|FP_S,  0, I32R6},
+{"max.d",   "D,S,T",    0x4620001e, 0xffe0003f, WR_D|RD_S|RD_T|FP_D,  0, I32R6},
+{"maxa.s",  "D,S,T",    0x4600001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S,  0, I32R6},
+{"maxa.d",  "D,S,T",    0x4620001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D,  0, I32R6},
+{"rint.s",  "D,S",      0x4600001a, 0xffff003f, WR_D|RD_S|FP_S,       0, I32R6},
+{"rint.d",  "D,S",      0x4620001a, 0xffff003f, WR_D|RD_S|FP_D,       0, I32R6},
+{"class.s", "D,S",      0x4600001b, 0xffff003f, WR_D|RD_S|FP_S,       0, I32R6},
+{"class.d", "D,S",      0x4620001b, 0xffff003f, WR_D|RD_S|FP_D,       0, I32R6},
+{"min.s",   "D,S,T",    0x4600001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S,  0, I32R6},
+{"min.d",   "D,S,T",    0x4620001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D,  0, I32R6},
+{"mina.s",  "D,S,T",    0x4600001d, 0xffe0003f, WR_D|RD_S|RD_T|FP_S,  0, I32R6},
+{"mina.d",  "D,S,T",    0x4620001d, 0xffe0003f, WR_D|RD_S|RD_T|FP_D,  0, I32R6},
+{"sel.s",   "D,S,T",    0x46000010, 0xffe0003f, WR_D|RD_S|RD_T|FP_S,  0, I32R6},
+{"sel.d",   "D,S,T",    0x46200010, 0xffe0003f, WR_D|RD_S|RD_T|FP_D,  0, I32R6},
+{"seleqz.s", "D,S,T",   0x46000014, 0xffe0003f, WR_D|RD_S|RD_T|FP_S,  0, I32R6},
+{"seleqz.d", "D,S,T",   0x46200014, 0xffe0003f, WR_D|RD_S|RD_T|FP_D,  0, I32R6},
+{"selnez.s", "D,S,T",   0x46000017, 0xffe0003f, WR_D|RD_S|RD_T|FP_S,  0, I32R6},
+{"selnez.d", "D,S,T",   0x46200017, 0xffe0003f, WR_D|RD_S|RD_T|FP_D,  0, I32R6},
+{"align",   "d,v,t",    0x7c000220, 0xfc00073f, WR_d|RD_s|RD_t,       0, I32R6},
+{"dalign",  "d,v,t",    0x7c000224, 0xfc00063f, WR_d|RD_s|RD_t,       0, I64R6},
+{"bitswap", "d,w",      0x7c000020, 0xffe007ff, WR_d|RD_t,            0, I32R6},
+{"dbitswap","d,w",      0x7c000024, 0xffe007ff, WR_d|RD_t,            0, I64R6},
+{"balc",    "+p",       0xe8000000, 0xfc000000, UBD|WR_31,            0, I32R6},
+{"bc",      "+p",       0xc8000000, 0xfc000000, UBD|WR_31,            0, I32R6},
+{"jic",     "t,o",      0xd8000000, 0xffe00000, UBD|RD_t,             0, I32R6},
+{"beqzc",   "s,+p",     0xd8000000, 0xfc000000, CBD|RD_s,             0, I32R6},
+{"jialc",   "t,o",      0xf8000000, 0xffe00000, UBD|RD_t,             0, I32R6},
+{"bnezc",   "s,+p",     0xf8000000, 0xfc000000, CBD|RD_s,             0, I32R6},
+{"beqzalc", "s,t,p",    0x20000000, 0xffe00000, CBD|RD_s|RD_t,        0, I32R6},
+{"bovc",    "s,t,p",    0x20000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"beqc",    "s,t,p",    0x20000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"bnezalc", "s,t,p",    0x60000000, 0xffe00000, CBD|RD_s|RD_t,        0, I32R6},
+{"bnvc",    "s,t,p",    0x60000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"bnec",    "s,t,p",    0x60000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"blezc",   "s,t,p",    0x58000000, 0xffe00000, CBD|RD_s|RD_t,        0, I32R6},
+{"bgezc",   "s,t,p",    0x58000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"bgec",    "s,t,p",    0x58000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"bgtzc",   "s,t,p",    0x5c000000, 0xffe00000, CBD|RD_s|RD_t,        0, I32R6},
+{"bltzc",   "s,t,p",    0x5c000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"bltc",    "s,t,p",    0x5c000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"blezalc", "s,t,p",    0x18000000, 0xffe00000, CBD|RD_s|RD_t,        0, I32R6},
+{"bgezalc", "s,t,p",    0x18000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"bgeuc",   "s,t,p",    0x18000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"bgtzalc", "s,t,p",    0x1c000000, 0xffe00000, CBD|RD_s|RD_t,        0, I32R6},
+{"bltzalc", "s,t,p",    0x1c000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"bltuc",   "s,t,p",    0x1c000000, 0xfc000000, CBD|RD_s|RD_t,        0, I32R6},
+{"nal",     "p",        0x04100000, 0xffff0000, WR_31,                0, I32R6},
+{"bal",     "p",        0x04110000, 0xffff0000, UBD|WR_31,            0, I32R6},
+{"bc1eqz",  "T,p",      0x45200000, 0xffe00000, CBD|RD_T|FP_S|FP_D,   0, I32R6},
+{"bc1nez",  "T,p",      0x45a00000, 0xffe00000, CBD|RD_T|FP_S|FP_D,   0, I32R6},
+{"bc2eqz",  "E,p",      0x49200000, 0xffe00000, CBD|RD_C2,            0, I32R6},
+{"bc2nez",  "E,p",      0x49a00000, 0xffe00000, CBD|RD_C2,            0, I32R6},
+{"cmp.af.s",   "D,S,T", 0x46800000, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.un.s",   "D,S,T", 0x46800001, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.eq.s",   "D,S,T", 0x46800002, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.ueq.s",  "D,S,T", 0x46800003, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.lt.s",   "D,S,T", 0x46800004, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.ult.s",  "D,S,T", 0x46800005, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.le.s",   "D,S,T", 0x46800006, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.ule.s",  "D,S,T", 0x46800007, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.saf.s",  "D,S,T", 0x46800008, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.sun.s",  "D,S,T", 0x46800009, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.seq.s",  "D,S,T", 0x4680000a, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.sueq.s", "D,S,T", 0x4680000b, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.slt.s",  "D,S,T", 0x4680000c, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.sult.s", "D,S,T", 0x4680000d, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.sle.s",  "D,S,T", 0x4680000e, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.sule.s", "D,S,T", 0x4680000f, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.or.s",   "D,S,T", 0x46800011, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.une.s",  "D,S,T", 0x46800012, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.ne.s",   "D,S,T", 0x46800013, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.sor.s",  "D,S,T", 0x46800019, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.sune.s", "D,S,T", 0x4680001a, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.sne.s",  "D,S,T", 0x4680001b, 0xffe0003f, RD_S|RD_T|WR_D|FP_S,  0, I32R6},
+{"cmp.af.d",   "D,S,T", 0x46a00000, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.un.d",   "D,S,T", 0x46a00001, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.eq.d",   "D,S,T", 0x46a00002, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.ueq.d",  "D,S,T", 0x46a00003, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.lt.d",   "D,S,T", 0x46a00004, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.ult.d",  "D,S,T", 0x46a00005, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.le.d",   "D,S,T", 0x46a00006, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.ule.d",  "D,S,T", 0x46a00007, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.saf.d",  "D,S,T", 0x46a00008, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.sun.d",  "D,S,T", 0x46a00009, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.seq.d",  "D,S,T", 0x46a0000a, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.sueq.d", "D,S,T", 0x46a0000b, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.slt.d",  "D,S,T", 0x46a0000c, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.sult.d", "D,S,T", 0x46a0000d, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.sle.d",  "D,S,T", 0x46a0000e, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.sule.d", "D,S,T", 0x46a0000f, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.or.d",   "D,S,T", 0x46a00011, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.une.d",  "D,S,T", 0x46a00012, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.ne.d",   "D,S,T", 0x46a00013, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.sor.d",  "D,S,T", 0x46a00019, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.sune.d", "D,S,T", 0x46a0001a, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
+{"cmp.sne.d",  "D,S,T", 0x46a0001b, 0xffe0003f, RD_S|RD_T|WR_D|FP_D,  0, I32R6},
 {"pref",    "k,o(b)",   0xcc000000, 0xfc000000, RD_b,           	0,		I4|I32|G3	},
 {"prefx",   "h,t(b)",	0x4c00000f, 0xfc0007ff, RD_b|RD_t,		0,		I4|I33	},
 {"nop",     "",         0x00000000, 0xffffffff, 0,              	INSN2_ALIAS,	I1      }, /* sll */
@@ -1753,6 +1907,7 @@ const struct mips_opcode mips_builtin_opcodes[] =
 {"lld",	    "t,o(b)",	0xd0000000, 0xfc000000, LDD|RD_b|WR_t,		0,		I3	},
 {"lld",     "t,A(b)",	0,    (int) M_LLD_AB,	INSN_MACRO,		0,		I3	},
 {"lui",     "t,u",	0x3c000000, 0xffe00000,	WR_t,			0,		I1	},
+{"aui",     "s,t,u",    0x3c000000, 0xfc000000, RD_s|WR_t,            0, I32R6},
 {"luxc1",   "D,t(b)",	0x4c000005, 0xfc00f83f, LDD|WR_D|RD_t|RD_b|FP_D, 0,		I5|I33|N55},
 {"lw",      "t,o(b)",	0x8c000000, 0xfc000000,	LDD|RD_b|WR_t,		0,		I1	},
 {"lw",      "t,A(b)",	0,    (int) M_LW_AB,	INSN_MACRO,		0,		I1	},
@@ -3575,6 +3730,42 @@ print_insn_args (const char *d,
 	      (*info->fprintf_func) (info->stream, "0x%x", msbd + 1);
 	      break;
 
+            case 'o':
+                switch (*(d+1)) {
+                case '1':
+                    d++;
+                    delta = l & ((1 << 18) - 1);
+                    if (delta & 0x20000) {
+                        delta |= ~0x1ffff;
+                    }
+                    break;
+                case '2':
+                    d++;
+                    delta = l & ((1 << 19) - 1);
+                    if (delta & 0x40000) {
+                        delta |= ~0x3ffff;
+                    }
+                    break;
+                default:
+                    delta = (l >> OP_SH_DELTA_R6) & OP_MASK_DELTA_R6;
+                    if (delta & 0x8000) {
+                        delta |= ~0xffff;
+                    }
+                }
+
+                (*info->fprintf_func) (info->stream, "%d", delta);
+                break;
+
+            case 'p':
+                /* Sign extend the displacement with 26 bits.  */
+                delta = (l >> OP_SH_DELTA) & OP_MASK_TARGET;
+                if (delta & 0x2000000) {
+                    delta |= ~0x3FFFFFF;
+                }
+                info->target = (delta << 2) + pc + INSNLEN;
+                (*info->print_address_func) (info->target, info);
+                break;
+
 	    case 't': /* Coprocessor 0 reg name */
 	      (*info->fprintf_func) (info->stream, "%s",
 				     mips_cp0_names[(l >> OP_SH_RT) &
@@ -3726,7 +3917,8 @@ print_insn_args (const char *d,
 
 	case 'j': /* Same as i, but sign-extended.  */
 	case 'o':
-	  delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA;
+            delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA;
+
 	  if (delta & 0x8000)
 	    delta |= ~0xffff;
 	  (*info->fprintf_func) (info->stream, "%d",
@@ -4052,6 +4244,23 @@ print_insn_mips (bfd_vma memaddr,
 		  && strcmp (op->name, "jalx"))
 		continue;
 
+              if (strcmp(op->name, "bovc") == 0
+                  || strcmp(op->name, "bnvc") == 0) {
+                  if (((word >> OP_SH_RS) & OP_MASK_RS) <
+                      ((word >> OP_SH_RT) & OP_MASK_RT)) {
+                      continue;
+                  }
+              }
+              if (strcmp(op->name, "bgezc") == 0
+                  || strcmp(op->name, "bltzc") == 0
+                  || strcmp(op->name, "bgezalc") == 0
+                  || strcmp(op->name, "bltzalc") == 0) {
+                  if (((word >> OP_SH_RS) & OP_MASK_RS) !=
+                      ((word >> OP_SH_RT) & OP_MASK_RT)) {
+                      continue;
+                  }
+              }
+
 	      /* Figure out instruction type and branch delay information.  */
 	      if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
 	        {
diff --git a/dma-helpers.c b/dma-helpers.c
index 7f86e18e72..6918572e18 100644
--- a/dma-helpers.c
+++ b/dma-helpers.c
@@ -7,6 +7,7 @@
  * (GNU GPL), version 2 or later.
  */
 
+#include "sysemu/block-backend.h"
 #include "sysemu/dma.h"
 #include "trace.h"
 #include "qemu/range.h"
@@ -67,9 +68,9 @@ void qemu_sglist_destroy(QEMUSGList *qsg)
 }
 
 typedef struct {
-    BlockDriverAIOCB common;
-    BlockDriverState *bs;
-    BlockDriverAIOCB *acb;
+    BlockAIOCB common;
+    BlockBackend *blk;
+    BlockAIOCB *acb;
     QEMUSGList *sg;
     uint64_t sector_num;
     DMADirection dir;
@@ -80,7 +81,7 @@ typedef struct {
     DMAIOFunc *io_func;
 } DMAAIOCB;
 
-static void dma_bdrv_cb(void *opaque, int ret);
+static void dma_blk_cb(void *opaque, int ret);
 
 static void reschedule_dma(void *opaque)
 {
@@ -88,7 +89,7 @@ static void reschedule_dma(void *opaque)
 
     qemu_bh_delete(dbs->bh);
     dbs->bh = NULL;
-    dma_bdrv_cb(dbs, 0);
+    dma_blk_cb(dbs, 0);
 }
 
 static void continue_after_map_failure(void *opaque)
@@ -99,7 +100,7 @@ static void continue_after_map_failure(void *opaque)
     qemu_bh_schedule(dbs->bh);
 }
 
-static void dma_bdrv_unmap(DMAAIOCB *dbs)
+static void dma_blk_unmap(DMAAIOCB *dbs)
 {
     int i;
 
@@ -115,7 +116,7 @@ static void dma_complete(DMAAIOCB *dbs, int ret)
 {
     trace_dma_complete(dbs, ret, dbs->common.cb);
 
-    dma_bdrv_unmap(dbs);
+    dma_blk_unmap(dbs);
     if (dbs->common.cb) {
         dbs->common.cb(dbs->common.opaque, ret);
     }
@@ -127,13 +128,13 @@ static void dma_complete(DMAAIOCB *dbs, int ret)
     qemu_aio_unref(dbs);
 }
 
-static void dma_bdrv_cb(void *opaque, int ret)
+static void dma_blk_cb(void *opaque, int ret)
 {
     DMAAIOCB *dbs = (DMAAIOCB *)opaque;
     dma_addr_t cur_addr, cur_len;
     void *mem;
 
-    trace_dma_bdrv_cb(dbs, ret);
+    trace_dma_blk_cb(dbs, ret);
 
     dbs->acb = NULL;
     dbs->sector_num += dbs->iov.size / 512;
@@ -142,7 +143,7 @@ static void dma_bdrv_cb(void *opaque, int ret)
         dma_complete(dbs, ret);
         return;
     }
-    dma_bdrv_unmap(dbs);
+    dma_blk_unmap(dbs);
 
     while (dbs->sg_cur_index < dbs->sg->nsg) {
         cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte;
@@ -168,19 +169,19 @@ static void dma_bdrv_cb(void *opaque, int ret)
         qemu_iovec_discard_back(&dbs->iov, dbs->iov.size & ~BDRV_SECTOR_MASK);
     }
 
-    dbs->acb = dbs->io_func(dbs->bs, dbs->sector_num, &dbs->iov,
-                            dbs->iov.size / 512, dma_bdrv_cb, dbs);
+    dbs->acb = dbs->io_func(dbs->blk, dbs->sector_num, &dbs->iov,
+                            dbs->iov.size / 512, dma_blk_cb, dbs);
     assert(dbs->acb);
 }
 
-static void dma_aio_cancel(BlockDriverAIOCB *acb)
+static void dma_aio_cancel(BlockAIOCB *acb)
 {
     DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common);
 
     trace_dma_aio_cancel(dbs);
 
     if (dbs->acb) {
-        bdrv_aio_cancel_async(dbs->acb);
+        blk_aio_cancel_async(dbs->acb);
     }
 }
 
@@ -190,17 +191,17 @@ static const AIOCBInfo dma_aiocb_info = {
     .cancel_async       = dma_aio_cancel,
 };
 
-BlockDriverAIOCB *dma_bdrv_io(
-    BlockDriverState *bs, QEMUSGList *sg, uint64_t sector_num,
-    DMAIOFunc *io_func, BlockDriverCompletionFunc *cb,
+BlockAIOCB *dma_blk_io(
+    BlockBackend *blk, QEMUSGList *sg, uint64_t sector_num,
+    DMAIOFunc *io_func, BlockCompletionFunc *cb,
     void *opaque, DMADirection dir)
 {
-    DMAAIOCB *dbs = qemu_aio_get(&dma_aiocb_info, bs, cb, opaque);
+    DMAAIOCB *dbs = blk_aio_get(&dma_aiocb_info, blk, cb, opaque);
 
-    trace_dma_bdrv_io(dbs, bs, sector_num, (dir == DMA_DIRECTION_TO_DEVICE));
+    trace_dma_blk_io(dbs, blk, sector_num, (dir == DMA_DIRECTION_TO_DEVICE));
 
     dbs->acb = NULL;
-    dbs->bs = bs;
+    dbs->blk = blk;
     dbs->sg = sg;
     dbs->sector_num = sector_num;
     dbs->sg_cur_index = 0;
@@ -209,25 +210,25 @@ BlockDriverAIOCB *dma_bdrv_io(
     dbs->io_func = io_func;
     dbs->bh = NULL;
     qemu_iovec_init(&dbs->iov, sg->nsg);
-    dma_bdrv_cb(dbs, 0);
+    dma_blk_cb(dbs, 0);
     return &dbs->common;
 }
 
 
-BlockDriverAIOCB *dma_bdrv_read(BlockDriverState *bs,
-                                QEMUSGList *sg, uint64_t sector,
-                                void (*cb)(void *opaque, int ret), void *opaque)
+BlockAIOCB *dma_blk_read(BlockBackend *blk,
+                         QEMUSGList *sg, uint64_t sector,
+                         void (*cb)(void *opaque, int ret), void *opaque)
 {
-    return dma_bdrv_io(bs, sg, sector, bdrv_aio_readv, cb, opaque,
-                       DMA_DIRECTION_FROM_DEVICE);
+    return dma_blk_io(blk, sg, sector, blk_aio_readv, cb, opaque,
+                      DMA_DIRECTION_FROM_DEVICE);
 }
 
-BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs,
-                                 QEMUSGList *sg, uint64_t sector,
-                                 void (*cb)(void *opaque, int ret), void *opaque)
+BlockAIOCB *dma_blk_write(BlockBackend *blk,
+                          QEMUSGList *sg, uint64_t sector,
+                          void (*cb)(void *opaque, int ret), void *opaque)
 {
-    return dma_bdrv_io(bs, sg, sector, bdrv_aio_writev, cb, opaque,
-                       DMA_DIRECTION_TO_DEVICE);
+    return dma_blk_io(blk, sg, sector, blk_aio_writev, cb, opaque,
+                      DMA_DIRECTION_TO_DEVICE);
 }
 
 
@@ -262,8 +263,8 @@ uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg)
     return dma_buf_rw(ptr, len, sg, DMA_DIRECTION_TO_DEVICE);
 }
 
-void dma_acct_start(BlockDriverState *bs, BlockAcctCookie *cookie,
+void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie,
                     QEMUSGList *sg, enum BlockAcctType type)
 {
-    block_acct_start(bdrv_get_stats(bs), cookie, sg->size, type);
+    block_acct_start(blk_get_stats(blk), cookie, sg->size, type);
 }
diff --git a/docs/blkdebug.txt b/docs/blkdebug.txt
index 5dde07234f..b67a36d5c6 100644
--- a/docs/blkdebug.txt
+++ b/docs/blkdebug.txt
@@ -70,10 +70,10 @@ Rules support the following attributes:
   once - (optional, default "off") only execute this action on the first
          matching request
 
-  immediately - (optional, default "off") return a NULL BlockDriverAIOCB
-                pointer and fail without an errno instead.  This exercises the
-                code path where BlockDriverAIOCB fails and the caller's
-                BlockDriverCompletionFunc is not invoked.
+  immediately - (optional, default "off") return a NULL BlockAIOCB
+                pointer and fail without an errno instead.  This
+                exercises the code path where BlockAIOCB fails and the
+                caller's BlockCompletionFunc is not invoked.
 
 Events
 ------
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 9274ebf101..16b21ebe61 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -7240,13 +7240,17 @@ int float128_compare_quiet( float128 a, float128 b STATUS_PARAM )
  * minnum() and maxnum correspond to the IEEE 754-2008 minNum()
  * and maxNum() operations. min() and max() are the typical min/max
  * semantics provided by many CPUs which predate that specification.
+ *
+ * minnummag() and maxnummag() functions correspond to minNumMag()
+ * and minNumMag() from the IEEE-754 2008.
  */
 #define MINMAX(s)                                                       \
 static inline float ## s float ## s ## _minmax(float ## s a, float ## s b,     \
-                                        int ismin, int isieee STATUS_PARAM) \
+                                               int ismin, int isieee,   \
+                                               int ismag STATUS_PARAM)  \
 {                                                                       \
     flag aSign, bSign;                                                  \
-    uint ## s ## _t av, bv;                                             \
+    uint ## s ## _t av, bv, aav, abv;                                   \
     a = float ## s ## _squash_input_denormal(a STATUS_VAR);             \
     b = float ## s ## _squash_input_denormal(b STATUS_VAR);             \
     if (float ## s ## _is_any_nan(a) ||                                 \
@@ -7266,6 +7270,17 @@ static inline float ## s float ## s ## _minmax(float ## s a, float ## s b,     \
     bSign = extractFloat ## s ## Sign(b);                               \
     av = float ## s ## _val(a);                                         \
     bv = float ## s ## _val(b);                                         \
+    if (ismag) {                                                        \
+        aav = float ## s ## _abs(av);                                   \
+        abv = float ## s ## _abs(bv);                                   \
+        if (aav != abv) {                                               \
+            if (ismin) {                                                \
+                return (aav < abv) ? a : b;                             \
+            } else {                                                    \
+                return (aav < abv) ? b : a;                             \
+            }                                                           \
+        }                                                               \
+    }                                                                   \
     if (aSign != bSign) {                                               \
         if (ismin) {                                                    \
             return aSign ? a : b;                                       \
@@ -7283,22 +7298,32 @@ static inline float ## s float ## s ## _minmax(float ## s a, float ## s b,     \
                                                                         \
 float ## s float ## s ## _min(float ## s a, float ## s b STATUS_PARAM)  \
 {                                                                       \
-    return float ## s ## _minmax(a, b, 1, 0 STATUS_VAR);                \
+    return float ## s ## _minmax(a, b, 1, 0, 0 STATUS_VAR);             \
 }                                                                       \
                                                                         \
 float ## s float ## s ## _max(float ## s a, float ## s b STATUS_PARAM)  \
 {                                                                       \
-    return float ## s ## _minmax(a, b, 0, 0 STATUS_VAR);                \
+    return float ## s ## _minmax(a, b, 0, 0, 0 STATUS_VAR);             \
 }                                                                       \
                                                                         \
 float ## s float ## s ## _minnum(float ## s a, float ## s b STATUS_PARAM) \
 {                                                                       \
-    return float ## s ## _minmax(a, b, 1, 1 STATUS_VAR);                \
+    return float ## s ## _minmax(a, b, 1, 1, 0 STATUS_VAR);             \
 }                                                                       \
                                                                         \
 float ## s float ## s ## _maxnum(float ## s a, float ## s b STATUS_PARAM) \
 {                                                                       \
-    return float ## s ## _minmax(a, b, 0, 1 STATUS_VAR);                \
+    return float ## s ## _minmax(a, b, 0, 1, 0 STATUS_VAR);             \
+}                                                                       \
+                                                                        \
+float ## s float ## s ## _minnummag(float ## s a, float ## s b STATUS_PARAM) \
+{                                                                       \
+    return float ## s ## _minmax(a, b, 1, 1, 1 STATUS_VAR);             \
+}                                                                       \
+                                                                        \
+float ## s float ## s ## _maxnummag(float ## s a, float ## s b STATUS_PARAM) \
+{                                                                       \
+    return float ## s ## _minmax(a, b, 0, 1, 1 STATUS_VAR);             \
 }
 
 MINMAX(32)
diff --git a/hw/arm/collie.c b/hw/arm/collie.c
index ed7851fe06..6c9b82fc54 100644
--- a/hw/arm/collie.c
+++ b/hw/arm/collie.c
@@ -15,7 +15,7 @@
 #include "strongarm.h"
 #include "hw/arm/arm.h"
 #include "hw/block/flash.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 
 static struct arm_boot_info collie_binfo = {
@@ -41,13 +41,13 @@ static void collie_init(MachineState *machine)
 
     dinfo = drive_get(IF_PFLASH, 0, 0);
     pflash_cfi01_register(SA_CS0, NULL, "collie.fl1", 0x02000000,
-                    dinfo ? dinfo->bdrv : NULL, (64 * 1024),
-                    512, 4, 0x00, 0x00, 0x00, 0x00, 0);
+                    dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                    (64 * 1024), 512, 4, 0x00, 0x00, 0x00, 0x00, 0);
 
     dinfo = drive_get(IF_PFLASH, 0, 1);
     pflash_cfi01_register(SA_CS1, NULL, "collie.fl2", 0x02000000,
-                    dinfo ? dinfo->bdrv : NULL, (64 * 1024),
-                    512, 4, 0x00, 0x00, 0x00, 0x00, 0);
+                    dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                    (64 * 1024), 512, 4, 0x00, 0x00, 0x00, 0x00, 0);
 
     sysbus_create_simple("scoop", 0x40800000, NULL);
 
diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c
index 3f8465ebc0..8103278b15 100644
--- a/hw/arm/gumstix.c
+++ b/hw/arm/gumstix.c
@@ -40,7 +40,7 @@
 #include "hw/block/flash.h"
 #include "hw/devices.h"
 #include "hw/boards.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "sysemu/qtest.h"
 
@@ -71,7 +71,7 @@ static void connex_init(MachineState *machine)
     be = 0;
 #endif
     if (!pflash_cfi01_register(0x00000000, NULL, "connext.rom", connex_rom,
-                               dinfo ? dinfo->bdrv : NULL,
+                               dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
                                sector_len, connex_rom / sector_len,
                                2, 0, 0, 0, 0, be)) {
         fprintf(stderr, "qemu: Error registering flash memory.\n");
@@ -109,7 +109,7 @@ static void verdex_init(MachineState *machine)
     be = 0;
 #endif
     if (!pflash_cfi01_register(0x00000000, NULL, "verdex.rom", verdex_rom,
-                               dinfo ? dinfo->bdrv : NULL,
+                               dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
                                sector_len, verdex_rom / sector_len,
                                2, 0, 0, 0, 0, be)) {
         fprintf(stderr, "qemu: Error registering flash memory.\n");
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
index fa61e5fa13..30f744a1bd 100644
--- a/hw/arm/highbank.c
+++ b/hw/arm/highbank.c
@@ -24,7 +24,7 @@
 #include "net/net.h"
 #include "sysemu/sysemu.h"
 #include "hw/boards.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "qemu/error-report.h"
 
diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c
index 2f1d61809d..0da02a67ec 100644
--- a/hw/arm/mainstone.c
+++ b/hw/arm/mainstone.c
@@ -18,7 +18,7 @@
 #include "hw/devices.h"
 #include "hw/boards.h"
 #include "hw/block/flash.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/sysbus.h"
 #include "exec/address-spaces.h"
 #include "sysemu/qtest.h"
@@ -149,9 +149,9 @@ static void mainstone_common_init(MemoryRegion *address_space_mem,
         if (!pflash_cfi01_register(mainstone_flash_base[i], NULL,
                                    i ? "mainstone.flash1" : "mainstone.flash0",
                                    MAINSTONE_FLASH,
-                                   dinfo->bdrv, sector_len,
-                                   MAINSTONE_FLASH / sector_len, 4, 0, 0, 0, 0,
-                                   be)) {
+                                   blk_by_legacy_dinfo(dinfo),
+                                   sector_len, MAINSTONE_FLASH / sector_len,
+                                   4, 0, 0, 0, 0, be)) {
             fprintf(stderr, "qemu: Error registering flash memory.\n");
             exit(1);
         }
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index c541455452..3712de6cba 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -18,11 +18,10 @@
 #include "hw/char/serial.h"
 #include "qemu/timer.h"
 #include "hw/ptimer.h"
-#include "block/block.h"
 #include "hw/block/flash.h"
 #include "ui/console.h"
 #include "hw/i2c/i2c.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "ui/pixel_ops.h"
 
@@ -1632,7 +1631,9 @@ static void musicpal_init(MachineState *machine)
     /* Register flash */
     dinfo = drive_get(IF_PFLASH, 0, 0);
     if (dinfo) {
-        flash_size = bdrv_getlength(dinfo->bdrv);
+        BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
+
+        flash_size = blk_getlength(blk);
         if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 &&
             flash_size != 32*1024*1024) {
             fprintf(stderr, "Invalid flash image size\n");
@@ -1647,16 +1648,14 @@ static void musicpal_init(MachineState *machine)
 #ifdef TARGET_WORDS_BIGENDIAN
         pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL,
                               "musicpal.flash", flash_size,
-                              dinfo->bdrv, 0x10000,
-                              (flash_size + 0xffff) >> 16,
+                              blk, 0x10000, (flash_size + 0xffff) >> 16,
                               MP_FLASH_SIZE_MAX / flash_size,
                               2, 0x00BF, 0x236D, 0x0000, 0x0000,
                               0x5555, 0x2AAA, 1);
 #else
         pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL,
                               "musicpal.flash", flash_size,
-                              dinfo->bdrv, 0x10000,
-                              (flash_size + 0xffff) >> 16,
+                              blk, 0x10000, (flash_size + 0xffff) >> 16,
                               MP_FLASH_SIZE_MAX / flash_size,
                               2, 0x00BF, 0x236D, 0x0000, 0x0000,
                               0x5555, 0x2AAA, 0);
diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c
index 4f092d6446..c7ebaa6abc 100644
--- a/hw/arm/nseries.c
+++ b/hw/arm/nseries.c
@@ -31,7 +31,7 @@
 #include "hw/hw.h"
 #include "hw/bt.h"
 #include "hw/loader.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/sysbus.h"
 #include "exec/address-spaces.h"
 
@@ -172,8 +172,9 @@ static void n8x0_nand_setup(struct n800_s *s)
     qdev_prop_set_uint16(s->nand, "version_id", 0);
     qdev_prop_set_int32(s->nand, "shift", 1);
     dinfo = drive_get(IF_MTD, 0, 0);
-    if (dinfo && dinfo->bdrv) {
-        qdev_prop_set_drive_nofail(s->nand, "drive", dinfo->bdrv);
+    if (dinfo) {
+        qdev_prop_set_drive_nofail(s->nand, "drive",
+                                   blk_by_legacy_dinfo(dinfo));
     }
     qdev_init_nofail(s->nand);
     sysbus_connect_irq(SYS_BUS_DEVICE(s->nand), 0,
diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c
index 306cf17b32..abb183c2d5 100644
--- a/hw/arm/omap1.c
+++ b/hw/arm/omap1.c
@@ -21,6 +21,7 @@
 #include "hw/arm/omap.h"
 #include "sysemu/sysemu.h"
 #include "hw/arm/soc_dma.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "qemu/range.h"
 #include "hw/sysbus.h"
@@ -3978,7 +3979,8 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory,
         fprintf(stderr, "qemu: missing SecureDigital device\n");
         exit(1);
     }
-    s->mmc = omap_mmc_init(0xfffb7800, system_memory, dinfo->bdrv,
+    s->mmc = omap_mmc_init(0xfffb7800, system_memory,
+                           blk_by_legacy_dinfo(dinfo),
                            qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN),
                            &s->drq[OMAP_DMA_MMC_TX],
                     omap_findclk(s, "mmc_ck"));
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index d6e19ae6b2..b083ebebc8 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -18,6 +18,7 @@
  * with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "hw/hw.h"
 #include "hw/arm/arm.h"
@@ -2461,7 +2462,8 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem,
         fprintf(stderr, "qemu: missing SecureDigital device\n");
         exit(1);
     }
-    s->mmc = omap2_mmc_init(omap_l4tao(s->l4, 9), dinfo->bdrv,
+    s->mmc = omap2_mmc_init(omap_l4tao(s->l4, 9),
+                    blk_by_legacy_dinfo(dinfo),
                     qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MMC_IRQ),
                     &s->drq[OMAP24XX_DMA_MMC1_TX],
                     omap_findclk(s, "mmc_fclk"), omap_findclk(s, "mmc_iclk"));
diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c
index 320b39c51b..671e02c4ed 100644
--- a/hw/arm/omap_sx1.c
+++ b/hw/arm/omap_sx1.c
@@ -31,7 +31,7 @@
 #include "hw/boards.h"
 #include "hw/arm/arm.h"
 #include "hw/block/flash.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/qtest.h"
 #include "exec/address-spaces.h"
 
@@ -154,8 +154,8 @@ static void sx1_init(MachineState *machine, const int version)
     if ((dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) {
         if (!pflash_cfi01_register(OMAP_CS0_BASE, NULL,
                                    "omap_sx1.flash0-1", flash_size,
-                                   dinfo->bdrv, sector_size,
-                                   flash_size / sector_size,
+                                   blk_by_legacy_dinfo(dinfo),
+                                   sector_size, flash_size / sector_size,
                                    4, 0, 0, 0, 0, be)) {
             fprintf(stderr, "qemu: Error registering flash memory %d.\n",
                            fl_idx);
@@ -178,8 +178,8 @@ static void sx1_init(MachineState *machine, const int version)
 
         if (!pflash_cfi01_register(OMAP_CS1_BASE, NULL,
                                    "omap_sx1.flash1-1", flash1_size,
-                                   dinfo->bdrv, sector_size,
-                                   flash1_size / sector_size,
+                                   blk_by_legacy_dinfo(dinfo),
+                                   sector_size, flash1_size / sector_size,
                                    4, 0, 0, 0, 0, be)) {
             fprintf(stderr, "qemu: Error registering flash memory %d.\n",
                            fl_idx);
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index 7d306fb1b7..693dfec9f2 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -14,6 +14,7 @@
 #include "hw/i2c/i2c.h"
 #include "hw/ssi.h"
 #include "sysemu/char.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 
 static struct {
@@ -2085,7 +2086,8 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space,
         fprintf(stderr, "qemu: missing SecureDigital device\n");
         exit(1);
     }
-    s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, dinfo->bdrv,
+    s->mmc = pxa2xx_mmci_init(address_space, 0x41100000,
+                    blk_by_legacy_dinfo(dinfo),
                     qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC),
                     qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI),
                     qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI));
@@ -2217,7 +2219,8 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
         fprintf(stderr, "qemu: missing SecureDigital device\n");
         exit(1);
     }
-    s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, dinfo->bdrv,
+    s->mmc = pxa2xx_mmci_init(address_space, 0x41100000,
+                    blk_by_legacy_dinfo(dinfo),
                     qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC),
                     qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI),
                     qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI));
diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index 8bd3ff60e4..af65aa4082 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -16,7 +16,7 @@
 #include "sysemu/sysemu.h"
 #include "hw/boards.h"
 #include "hw/i2c/i2c.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "qemu/error-report.h"
 
diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c
index 9260896424..a16831c2e5 100644
--- a/hw/arm/spitz.c
+++ b/hw/arm/spitz.c
@@ -22,10 +22,9 @@
 #include "hw/devices.h"
 #include "hw/arm/sharpsl.h"
 #include "ui/console.h"
-#include "block/block.h"
 #include "audio/audio.h"
 #include "hw/boards.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/sysbus.h"
 #include "exec/address-spaces.h"
 
@@ -170,7 +169,8 @@ static int sl_nand_init(SysBusDevice *dev)
 
     s->ctl = 0;
     nand = drive_get(IF_MTD, 0, 0);
-    s->nand = nand_init(nand ? nand->bdrv : NULL, s->manf_id, s->chip_id);
+    s->nand = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL,
+                        s->manf_id, s->chip_id);
 
     memory_region_init_io(&s->iomem, OBJECT(s), &sl_ops, s, "sl", 0x40);
     sysbus_init_mmio(dev, &s->iomem);
diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c
index 6c574bc418..73572ebe01 100644
--- a/hw/arm/tosa.c
+++ b/hw/arm/tosa.c
@@ -17,11 +17,10 @@
 #include "hw/devices.h"
 #include "hw/arm/sharpsl.h"
 #include "hw/pcmcia.h"
-#include "block/block.h"
 #include "hw/boards.h"
 #include "hw/i2c/i2c.h"
 #include "hw/ssi.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/sysbus.h"
 #include "exec/address-spaces.h"
 
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
index 7696be182d..e6ef0a2e7e 100644
--- a/hw/arm/versatilepb.c
+++ b/hw/arm/versatilepb.c
@@ -15,7 +15,7 @@
 #include "hw/pci/pci.h"
 #include "hw/i2c/i2c.h"
 #include "hw/boards.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "hw/block/flash.h"
 
@@ -338,7 +338,8 @@ static void versatile_init(MachineState *machine, int board_id)
 
     dinfo = drive_get(IF_PFLASH, 0, 0);
     if (!pflash_cfi01_register(VERSATILE_FLASH_ADDR, NULL, "versatile.flash",
-                          VERSATILE_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
+                          VERSATILE_FLASH_SIZE,
+                          dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
                           VERSATILE_FLASH_SECT_SIZE,
                           VERSATILE_FLASH_SIZE / VERSATILE_FLASH_SECT_SIZE,
                           4, 0x0089, 0x0018, 0x0000, 0x0, 0)) {
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index aed218f01b..7cbd13f182 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -30,7 +30,7 @@
 #include "hw/boards.h"
 #include "hw/loader.h"
 #include "exec/address-spaces.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/block/flash.h"
 #include "sysemu/device_tree.h"
 #include "qemu/error-report.h"
@@ -491,7 +491,8 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name,
 {
     DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
 
-    if (di && qdev_prop_set_drive(dev, "drive", di->bdrv)) {
+    if (di && qdev_prop_set_drive(dev, "drive",
+                                  blk_by_legacy_dinfo(di))) {
         abort();
     }
 
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 8c6b171414..8ea592e92d 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -33,6 +33,7 @@
 #include "hw/arm/primecell.h"
 #include "hw/devices.h"
 #include "net/net.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/device_tree.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/kvm.h"
@@ -450,7 +451,8 @@ static void create_one_flash(const char *name, hwaddr flashbase,
     DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
     const uint64_t sectorlength = 256 * 1024;
 
-    if (dinfo && qdev_prop_set_drive(dev, "drive", dinfo->bdrv)) {
+    if (dinfo && qdev_prop_set_drive(dev, "drive",
+                                     blk_by_legacy_dinfo(dinfo))) {
         abort();
     }
 
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index ad85c7af2e..b59039297a 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -22,7 +22,7 @@
 #include "sysemu/sysemu.h"
 #include "hw/boards.h"
 #include "hw/block/flash.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/loader.h"
 #include "hw/ssi.h"
 #include "qemu/error-report.h"
@@ -164,7 +164,8 @@ static void zynq_init(MachineState *machine)
 
     /* AMD */
     pflash_cfi02_register(0xe2000000, NULL, "zynq.pflash", FLASH_SIZE,
-                          dinfo ? dinfo->bdrv : NULL, FLASH_SECTOR_SIZE,
+                          dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                          FLASH_SECTOR_SIZE,
                           FLASH_SIZE/FLASH_SECTOR_SIZE, 1,
                           1, 0x0066, 0x0022, 0x0000, 0x0000, 0x0555, 0x2aa,
                               0);
diff --git a/hw/arm/z2.c b/hw/arm/z2.c
index 36b3b504f4..17355479a1 100644
--- a/hw/arm/z2.c
+++ b/hw/arm/z2.c
@@ -20,7 +20,7 @@
 #include "hw/boards.h"
 #include "sysemu/sysemu.h"
 #include "hw/block/flash.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "ui/console.h"
 #include "audio/audio.h"
 #include "exec/address-spaces.h"
@@ -336,9 +336,9 @@ static void z2_init(MachineState *machine)
 
     if (!pflash_cfi01_register(Z2_FLASH_BASE,
                                NULL, "z2.flash0", Z2_FLASH_SIZE,
-                               dinfo ? dinfo->bdrv : NULL, sector_len,
-                               Z2_FLASH_SIZE / sector_len, 4, 0, 0, 0, 0,
-                               be)) {
+                               dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                               sector_len, Z2_FLASH_SIZE / sector_len,
+                               4, 0, 0, 0, 0, be)) {
         fprintf(stderr, "qemu: Error registering flash memory.\n");
         exit(1);
     }
diff --git a/hw/block/block.c b/hw/block/block.c
index b6a6dc6baa..a625773d44 100644
--- a/hw/block/block.c
+++ b/hw/block/block.c
@@ -8,6 +8,7 @@
  */
 
 #include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/block/block.h"
 #include "qemu/error-report.h"
 
@@ -17,8 +18,10 @@ void blkconf_serial(BlockConf *conf, char **serial)
 
     if (!*serial) {
         /* try to fall back to value set with legacy -drive serial=... */
-        dinfo = drive_get_by_blockdev(conf->bs);
-        *serial = g_strdup(dinfo->serial);
+        dinfo = blk_legacy_dinfo(conf->blk);
+        if (dinfo) {
+            *serial = g_strdup(dinfo->serial);
+        }
     }
 }
 
@@ -30,16 +33,18 @@ void blkconf_geometry(BlockConf *conf, int *ptrans,
 
     if (!conf->cyls && !conf->heads && !conf->secs) {
         /* try to fall back to value set with legacy -drive cyls=... */
-        dinfo = drive_get_by_blockdev(conf->bs);
-        conf->cyls  = dinfo->cyls;
-        conf->heads = dinfo->heads;
-        conf->secs  = dinfo->secs;
-        if (ptrans) {
-            *ptrans = dinfo->trans;
+        dinfo = blk_legacy_dinfo(conf->blk);
+        if (dinfo) {
+            conf->cyls  = dinfo->cyls;
+            conf->heads = dinfo->heads;
+            conf->secs  = dinfo->secs;
+            if (ptrans) {
+                *ptrans = dinfo->trans;
+            }
         }
     }
     if (!conf->cyls && !conf->heads && !conf->secs) {
-        hd_geometry_guess(conf->bs,
+        hd_geometry_guess(conf->blk,
                           &conf->cyls, &conf->heads, &conf->secs,
                           ptrans);
     } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) {
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index 5458f9d25f..45b1164c3e 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -17,7 +17,7 @@
 #include "qemu/thread.h"
 #include "qemu/error-report.h"
 #include "hw/virtio/dataplane/vring.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "hw/virtio/virtio-blk.h"
 #include "virtio-blk.h"
 #include "block/aio.h"
@@ -30,7 +30,7 @@ struct VirtIOBlockDataPlane {
     bool stopping;
     bool disabled;
 
-    VirtIOBlkConf *blk;
+    VirtIOBlkConf *conf;
 
     VirtIODevice *vdev;
     Vring vring;                    /* virtqueue vring */
@@ -94,7 +94,7 @@ static void handle_notify(EventNotifier *e)
     VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
 
     event_notifier_test_and_clear(&s->host_notifier);
-    bdrv_io_plug(s->blk->conf.bs);
+    blk_io_plug(s->conf->conf.blk);
     for (;;) {
         MultiReqBuffer mrb = {
             .num_writes = 0,
@@ -120,7 +120,7 @@ static void handle_notify(EventNotifier *e)
             virtio_blk_handle_request(req, &mrb);
         }
 
-        virtio_submit_multiwrite(s->blk->conf.bs, &mrb);
+        virtio_submit_multiwrite(s->conf->conf.blk, &mrb);
 
         if (likely(ret == -EAGAIN)) { /* vring emptied */
             /* Re-enable guest->host notifies and stop processing the vring.
@@ -133,11 +133,11 @@ static void handle_notify(EventNotifier *e)
             break;
         }
     }
-    bdrv_io_unplug(s->blk->conf.bs);
+    blk_io_unplug(s->conf->conf.blk);
 }
 
 /* Context: QEMU global mutex held */
-void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
+void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
                                   VirtIOBlockDataPlane **dataplane,
                                   Error **errp)
 {
@@ -148,7 +148,7 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
 
     *dataplane = NULL;
 
-    if (!blk->data_plane && !blk->iothread) {
+    if (!conf->data_plane && !conf->iothread) {
         return;
     }
 
@@ -163,7 +163,8 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
     /* If dataplane is (re-)enabled while the guest is running there could be
      * block jobs that can conflict.
      */
-    if (bdrv_op_is_blocked(blk->conf.bs, BLOCK_OP_TYPE_DATAPLANE, &local_err)) {
+    if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE,
+                          &local_err)) {
         error_setg(errp, "cannot start dataplane thread: %s",
                    error_get_pretty(local_err));
         error_free(local_err);
@@ -172,10 +173,10 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
 
     s = g_new0(VirtIOBlockDataPlane, 1);
     s->vdev = vdev;
-    s->blk = blk;
+    s->conf = conf;
 
-    if (blk->iothread) {
-        s->iothread = blk->iothread;
+    if (conf->iothread) {
+        s->iothread = conf->iothread;
         object_ref(OBJECT(s->iothread));
     } else {
         /* Create per-device IOThread if none specified.  This is for
@@ -192,9 +193,9 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
     s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
 
     error_setg(&s->blocker, "block device is in use by data plane");
-    bdrv_op_block_all(blk->conf.bs, s->blocker);
-    bdrv_op_unblock(blk->conf.bs, BLOCK_OP_TYPE_RESIZE, s->blocker);
-    bdrv_op_unblock(blk->conf.bs, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker);
+    blk_op_block_all(conf->conf.blk, s->blocker);
+    blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker);
+    blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker);
 
     *dataplane = s;
 }
@@ -207,7 +208,7 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
     }
 
     virtio_blk_data_plane_stop(s);
-    bdrv_op_unblock_all(s->blk->conf.bs, s->blocker);
+    blk_op_unblock_all(s->conf->conf.blk, s->blocker);
     error_free(s->blocker);
     object_unref(OBJECT(s->iothread));
     qemu_bh_delete(s->bh);
@@ -262,7 +263,7 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
     s->started = true;
     trace_virtio_blk_data_plane_start(s);
 
-    bdrv_set_aio_context(s->blk->conf.bs, s->ctx);
+    blk_set_aio_context(s->conf->conf.blk, s->ctx);
 
     /* Kick right away to begin processing requests already in vring */
     event_notifier_set(virtio_queue_get_host_notifier(vq));
@@ -308,7 +309,7 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
     aio_set_event_notifier(s->ctx, &s->host_notifier, NULL);
 
     /* Drain and switch bs back to the QEMU main loop */
-    bdrv_set_aio_context(s->blk->conf.bs, qemu_get_aio_context());
+    blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
 
     aio_context_release(s->ctx);
 
diff --git a/hw/block/dataplane/virtio-blk.h b/hw/block/dataplane/virtio-blk.h
index 1750c9905b..c88d40e72c 100644
--- a/hw/block/dataplane/virtio-blk.h
+++ b/hw/block/dataplane/virtio-blk.h
@@ -19,7 +19,7 @@
 
 typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane;
 
-void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
+void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
                                   VirtIOBlockDataPlane **dataplane,
                                   Error **errp);
 void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s);
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index 34c1d8f1c9..739a03ed58 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -33,6 +33,7 @@
 #include "qemu/timer.h"
 #include "hw/isa/isa.h"
 #include "hw/sysbus.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "sysemu/sysemu.h"
 #include "qemu/log.h"
@@ -113,7 +114,7 @@ static const FDFormat fd_formats[] = {
     { FDRIVE_DRV_NONE, -1, -1, 0, 0, },
 };
 
-static void pick_geometry(BlockDriverState *bs, int *nb_heads,
+static void pick_geometry(BlockBackend *blk, int *nb_heads,
                           int *max_track, int *last_sect,
                           FDriveType drive_in, FDriveType *drive,
                           FDriveRate *rate)
@@ -122,7 +123,7 @@ static void pick_geometry(BlockDriverState *bs, int *nb_heads,
     uint64_t nb_sectors, size;
     int i, first_match, match;
 
-    bdrv_get_geometry(bs, &nb_sectors);
+    blk_get_geometry(blk, &nb_sectors);
     match = -1;
     first_match = -1;
     for (i = 0; ; i++) {
@@ -175,7 +176,7 @@ typedef enum FDiskFlags {
 
 typedef struct FDrive {
     FDCtrl *fdctrl;
-    BlockDriverState *bs;
+    BlockBackend *blk;
     /* Drive status */
     FDriveType drive;
     uint8_t perpendicular;    /* 2.88 MB access mode    */
@@ -260,7 +261,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
 #endif
         drv->head = head;
         if (drv->track != track) {
-            if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
+            if (drv->blk != NULL && blk_is_inserted(drv->blk)) {
                 drv->media_changed = 0;
             }
             ret = 1;
@@ -269,7 +270,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
         drv->sect = sect;
     }
 
-    if (drv->bs == NULL || !bdrv_is_inserted(drv->bs)) {
+    if (drv->blk == NULL || !blk_is_inserted(drv->blk)) {
         ret = 2;
     }
 
@@ -291,11 +292,11 @@ static void fd_revalidate(FDrive *drv)
     FDriveRate rate;
 
     FLOPPY_DPRINTF("revalidate\n");
-    if (drv->bs != NULL) {
-        ro = bdrv_is_read_only(drv->bs);
-        pick_geometry(drv->bs, &nb_heads, &max_track,
+    if (drv->blk != NULL) {
+        ro = blk_is_read_only(drv->blk);
+        pick_geometry(drv->blk, &nb_heads, &max_track,
                       &last_sect, drv->drive, &drive, &rate);
-        if (!bdrv_is_inserted(drv->bs)) {
+        if (!blk_is_inserted(drv->blk)) {
             FLOPPY_DPRINTF("No disk in drive\n");
         } else {
             FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads,
@@ -665,7 +666,7 @@ static bool fdrive_media_changed_needed(void *opaque)
 {
     FDrive *drive = opaque;
 
-    return (drive->bs != NULL && drive->media_changed != 1);
+    return (drive->blk != NULL && drive->media_changed != 1);
 }
 
 static const VMStateDescription vmstate_fdrive_media_changed = {
@@ -910,8 +911,9 @@ static void fdctrl_reset(FDCtrl *fdctrl, int do_irq)
     /* Initialise controller */
     fdctrl->sra = 0;
     fdctrl->srb = 0xc0;
-    if (!fdctrl->drives[1].bs)
+    if (!fdctrl->drives[1].blk) {
         fdctrl->sra |= FD_SRA_nDRV2;
+    }
     fdctrl->cur_drv = 0;
     fdctrl->dor = FD_DOR_nRESET;
     fdctrl->dor |= (fdctrl->dma_chann != -1) ? FD_DOR_DMAEN : 0;
@@ -1403,7 +1405,7 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
         status2 = FD_SR2_SNS;
     if (dma_len > fdctrl->data_len)
         dma_len = fdctrl->data_len;
-    if (cur_drv->bs == NULL) {
+    if (cur_drv->blk == NULL) {
         if (fdctrl->data_dir == FD_DIR_WRITE)
             fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
         else
@@ -1424,8 +1426,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
         if (fdctrl->data_dir != FD_DIR_WRITE ||
             len < FD_SECTOR_LEN || rel_pos != 0) {
             /* READ & SCAN commands and realign to a sector for WRITE */
-            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
-                          fdctrl->fifo, 1) < 0) {
+            if (blk_read(cur_drv->blk, fd_sector(cur_drv),
+                         fdctrl->fifo, 1) < 0) {
                 FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
                                fd_sector(cur_drv));
                 /* Sure, image size is too small... */
@@ -1452,8 +1454,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
 
             DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
                              fdctrl->data_pos, len);
-            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
-                           fdctrl->fifo, 1) < 0) {
+            if (blk_write(cur_drv->blk, fd_sector(cur_drv),
+                          fdctrl->fifo, 1) < 0) {
                 FLOPPY_DPRINTF("error writing sector %d\n",
                                fd_sector(cur_drv));
                 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
@@ -1528,7 +1530,8 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
                                    fd_sector(cur_drv));
                     return 0;
                 }
-            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+            if (blk_read(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1)
+                < 0) {
                 FLOPPY_DPRINTF("error getting sector %d\n",
                                fd_sector(cur_drv));
                 /* Sure, image size is too small... */
@@ -1597,8 +1600,8 @@ static void fdctrl_format_sector(FDCtrl *fdctrl)
         break;
     }
     memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
-    if (cur_drv->bs == NULL ||
-        bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+    if (cur_drv->blk == NULL ||
+        blk_write(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
         FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv));
         fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
     } else {
@@ -1988,7 +1991,8 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
         if (pos == FD_SECTOR_LEN - 1 ||
             fdctrl->data_pos == fdctrl->data_len) {
             cur_drv = get_cur_drv(fdctrl);
-            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+            if (blk_write(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1)
+                < 0) {
                 FLOPPY_DPRINTF("error writing sector %d\n",
                                fd_sector(cur_drv));
                 return;
@@ -2076,12 +2080,12 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp)
         drive = &fdctrl->drives[i];
         drive->fdctrl = fdctrl;
 
-        if (drive->bs) {
-            if (bdrv_get_on_error(drive->bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
+        if (drive->blk) {
+            if (blk_get_on_error(drive->blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
                 error_setg(errp, "fdc doesn't support drive option werror");
                 return;
             }
-            if (bdrv_get_on_error(drive->bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {
+            if (blk_get_on_error(drive->blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
                 error_setg(errp, "fdc doesn't support drive option rerror");
                 return;
             }
@@ -2089,8 +2093,8 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp)
 
         fd_init(drive);
         fdctrl_change_cb(drive, 0);
-        if (drive->bs) {
-            bdrv_set_dev_ops(drive->bs, &fdctrl_block_ops, drive);
+        if (drive->blk) {
+            blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive);
         }
     }
 }
@@ -2107,10 +2111,10 @@ ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds)
     dev = DEVICE(isadev);
 
     if (fds[0]) {
-        qdev_prop_set_drive_nofail(dev, "driveA", fds[0]->bdrv);
+        qdev_prop_set_drive_nofail(dev, "driveA", blk_by_legacy_dinfo(fds[0]));
     }
     if (fds[1]) {
-        qdev_prop_set_drive_nofail(dev, "driveB", fds[1]->bdrv);
+        qdev_prop_set_drive_nofail(dev, "driveB", blk_by_legacy_dinfo(fds[1]));
     }
     qdev_init_nofail(dev);
 
@@ -2130,10 +2134,10 @@ void fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
     fdctrl = &sys->state;
     fdctrl->dma_chann = dma_chann; /* FIXME */
     if (fds[0]) {
-        qdev_prop_set_drive_nofail(dev, "driveA", fds[0]->bdrv);
+        qdev_prop_set_drive_nofail(dev, "driveA", blk_by_legacy_dinfo(fds[0]));
     }
     if (fds[1]) {
-        qdev_prop_set_drive_nofail(dev, "driveB", fds[1]->bdrv);
+        qdev_prop_set_drive_nofail(dev, "driveB", blk_by_legacy_dinfo(fds[1]));
     }
     qdev_init_nofail(dev);
     sbd = SYS_BUS_DEVICE(dev);
@@ -2149,7 +2153,7 @@ void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base,
 
     dev = qdev_create(NULL, "SUNW,fdtwo");
     if (fds[0]) {
-        qdev_prop_set_drive_nofail(dev, "drive", fds[0]->bdrv);
+        qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(fds[0]));
     }
     qdev_init_nofail(dev);
     sys = SYSBUS_FDC(dev);
@@ -2286,8 +2290,8 @@ static Property isa_fdc_properties[] = {
     DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0),
     DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6),
     DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2),
-    DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].bs),
-    DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs),
+    DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].blk),
+    DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].blk),
     DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate,
                     0, true),
     DEFINE_PROP_END_OF_LIST(),
@@ -2336,8 +2340,8 @@ static const VMStateDescription vmstate_sysbus_fdc ={
 };
 
 static Property sysbus_fdc_properties[] = {
-    DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].bs),
-    DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].bs),
+    DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].blk),
+    DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].blk),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -2357,7 +2361,7 @@ static const TypeInfo sysbus_fdc_info = {
 };
 
 static Property sun4m_fdc_properties[] = {
-    DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].bs),
+    DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].blk),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c
index 6feb4f8175..6fcf74df44 100644
--- a/hw/block/hd-geometry.c
+++ b/hw/block/hd-geometry.c
@@ -30,7 +30,7 @@
  * THE SOFTWARE.
  */
 
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "hw/block/block.h"
 #include "trace.h"
 
@@ -49,7 +49,7 @@ struct partition {
 
 /* try to guess the disk logical geometry from the MSDOS partition table.
    Return 0 if OK, -1 if could not guess */
-static int guess_disk_lchs(BlockDriverState *bs,
+static int guess_disk_lchs(BlockBackend *blk,
                            int *pcylinders, int *pheads, int *psectors)
 {
     uint8_t buf[BDRV_SECTOR_SIZE];
@@ -58,14 +58,14 @@ static int guess_disk_lchs(BlockDriverState *bs,
     uint32_t nr_sects;
     uint64_t nb_sectors;
 
-    bdrv_get_geometry(bs, &nb_sectors);
+    blk_get_geometry(blk, &nb_sectors);
 
     /**
      * The function will be invoked during startup not only in sync I/O mode,
      * but also in async I/O mode. So the I/O throttling function has to
      * be disabled temporarily here, not permanently.
      */
-    if (bdrv_read_unthrottled(bs, 0, buf, 1) < 0) {
+    if (blk_read_unthrottled(blk, 0, buf, 1) < 0) {
         return -1;
     }
     /* test msdos magic */
@@ -90,20 +90,20 @@ static int guess_disk_lchs(BlockDriverState *bs,
             *pheads = heads;
             *psectors = sectors;
             *pcylinders = cylinders;
-            trace_hd_geometry_lchs_guess(bs, cylinders, heads, sectors);
+            trace_hd_geometry_lchs_guess(blk, cylinders, heads, sectors);
             return 0;
         }
     }
     return -1;
 }
 
-static void guess_chs_for_size(BlockDriverState *bs,
+static void guess_chs_for_size(BlockBackend *blk,
                 uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs)
 {
     uint64_t nb_sectors;
     int cylinders;
 
-    bdrv_get_geometry(bs, &nb_sectors);
+    blk_get_geometry(blk, &nb_sectors);
 
     cylinders = nb_sectors / (16 * 63);
     if (cylinders > 16383) {
@@ -116,21 +116,21 @@ static void guess_chs_for_size(BlockDriverState *bs,
     *psecs = 63;
 }
 
-void hd_geometry_guess(BlockDriverState *bs,
+void hd_geometry_guess(BlockBackend *blk,
                        uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs,
                        int *ptrans)
 {
     int cylinders, heads, secs, translation;
 
-    if (guess_disk_lchs(bs, &cylinders, &heads, &secs) < 0) {
+    if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) {
         /* no LCHS guess: use a standard physical disk geometry  */
-        guess_chs_for_size(bs, pcyls, pheads, psecs);
+        guess_chs_for_size(blk, pcyls, pheads, psecs);
         translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs);
     } else if (heads > 16) {
         /* LCHS guess with heads > 16 means that a BIOS LBA
            translation was active, so a standard physical disk
            geometry is OK */
-        guess_chs_for_size(bs, pcyls, pheads, psecs);
+        guess_chs_for_size(blk, pcyls, pheads, psecs);
         translation = *pcyls * *pheads <= 131072
             ? BIOS_ATA_TRANSLATION_LARGE
             : BIOS_ATA_TRANSLATION_LBA;
@@ -146,7 +146,7 @@ void hd_geometry_guess(BlockDriverState *bs,
     if (ptrans) {
         *ptrans = translation;
     }
-    trace_hd_geometry_guess(bs, *pcyls, *pheads, *psecs, translation);
+    trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation);
 }
 
 int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 5893773f0c..ff1106b6c9 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -22,6 +22,7 @@
  */
 
 #include "hw/hw.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "hw/ssi.h"
 
@@ -245,7 +246,7 @@ typedef struct Flash {
 
     uint32_t r;
 
-    BlockDriverState *bdrv;
+    BlockBackend *blk;
 
     uint8_t *storage;
     uint32_t size;
@@ -279,7 +280,7 @@ typedef struct M25P80Class {
 #define M25P80_GET_CLASS(obj) \
      OBJECT_GET_CLASS(M25P80Class, (obj), TYPE_M25P80)
 
-static void bdrv_sync_complete(void *opaque, int ret)
+static void blk_sync_complete(void *opaque, int ret)
 {
     /* do nothing. Masters do not directly interact with the backing store,
      * only the working copy so no mutexing required.
@@ -288,20 +289,20 @@ static void bdrv_sync_complete(void *opaque, int ret)
 
 static void flash_sync_page(Flash *s, int page)
 {
-    int bdrv_sector, nb_sectors;
+    int blk_sector, nb_sectors;
     QEMUIOVector iov;
 
-    if (!s->bdrv || bdrv_is_read_only(s->bdrv)) {
+    if (!s->blk || blk_is_read_only(s->blk)) {
         return;
     }
 
-    bdrv_sector = (page * s->pi->page_size) / BDRV_SECTOR_SIZE;
+    blk_sector = (page * s->pi->page_size) / BDRV_SECTOR_SIZE;
     nb_sectors = DIV_ROUND_UP(s->pi->page_size, BDRV_SECTOR_SIZE);
     qemu_iovec_init(&iov, 1);
-    qemu_iovec_add(&iov, s->storage + bdrv_sector * BDRV_SECTOR_SIZE,
+    qemu_iovec_add(&iov, s->storage + blk_sector * BDRV_SECTOR_SIZE,
                    nb_sectors * BDRV_SECTOR_SIZE);
-    bdrv_aio_writev(s->bdrv, bdrv_sector, &iov, nb_sectors, bdrv_sync_complete,
-                    NULL);
+    blk_aio_writev(s->blk, blk_sector, &iov, nb_sectors, blk_sync_complete,
+                   NULL);
 }
 
 static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
@@ -309,7 +310,7 @@ static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
     int64_t start, end, nb_sectors;
     QEMUIOVector iov;
 
-    if (!s->bdrv || bdrv_is_read_only(s->bdrv)) {
+    if (!s->blk || blk_is_read_only(s->blk)) {
         return;
     }
 
@@ -320,7 +321,7 @@ static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
     qemu_iovec_init(&iov, 1);
     qemu_iovec_add(&iov, s->storage + (start * BDRV_SECTOR_SIZE),
                                         nb_sectors * BDRV_SECTOR_SIZE);
-    bdrv_aio_writev(s->bdrv, start, &iov, nb_sectors, bdrv_sync_complete, NULL);
+    blk_aio_writev(s->blk, start, &iov, nb_sectors, blk_sync_complete, NULL);
 }
 
 static void flash_erase(Flash *s, int offset, FlashCMD cmd)
@@ -620,17 +621,17 @@ static int m25p80_init(SSISlave *ss)
 
     s->size = s->pi->sector_size * s->pi->n_sectors;
     s->dirty_page = -1;
-    s->storage = qemu_blockalign(s->bdrv, s->size);
+    s->storage = blk_blockalign(s->blk, s->size);
 
     dinfo = drive_get_next(IF_MTD);
 
-    if (dinfo && dinfo->bdrv) {
+    if (dinfo) {
         DB_PRINT_L(0, "Binding to IF_MTD drive\n");
-        s->bdrv = dinfo->bdrv;
+        s->blk = blk_by_legacy_dinfo(dinfo);
 
         /* FIXME: Move to late init */
-        if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size,
-                                                    BDRV_SECTOR_SIZE))) {
+        if (blk_read(s->blk, 0, s->storage,
+                     DIV_ROUND_UP(s->size, BDRV_SECTOR_SIZE))) {
             fprintf(stderr, "Failed to initialize SPI flash!\n");
             return 1;
         }
diff --git a/hw/block/nand.c b/hw/block/nand.c
index 38eefd436d..1882a0cbeb 100644
--- a/hw/block/nand.c
+++ b/hw/block/nand.c
@@ -20,7 +20,7 @@
 
 # include "hw/hw.h"
 # include "hw/block/flash.h"
-# include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/qdev.h"
 #include "qemu/error-report.h"
 
@@ -61,7 +61,7 @@ struct NANDFlashState {
     int size, pages;
     int page_shift, oob_shift, erase_shift, addr_shift;
     uint8_t *storage;
-    BlockDriverState *bdrv;
+    BlockBackend *blk;
     int mem_oob;
 
     uint8_t cle, ale, ce, wp, gnd;
@@ -400,12 +400,12 @@ static void nand_realize(DeviceState *dev, Error **errp)
 
     pagesize = 1 << s->oob_shift;
     s->mem_oob = 1;
-    if (s->bdrv) {
-        if (bdrv_is_read_only(s->bdrv)) {
+    if (s->blk) {
+        if (blk_is_read_only(s->blk)) {
             error_setg(errp, "Can't use a read-only drive");
             return;
         }
-        if (bdrv_getlength(s->bdrv) >=
+        if (blk_getlength(s->blk) >=
                 (s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
             pagesize = 0;
             s->mem_oob = 0;
@@ -424,7 +424,7 @@ static void nand_realize(DeviceState *dev, Error **errp)
 static Property nand_properties[] = {
     DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0),
     DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0),
-    DEFINE_PROP_DRIVE("drive", NANDFlashState, bdrv),
+    DEFINE_PROP_DRIVE("drive", NANDFlashState, blk),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -624,7 +624,7 @@ uint32_t nand_getbuswidth(DeviceState *dev)
     return s->buswidth << 3;
 }
 
-DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id)
+DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id)
 {
     DeviceState *dev;
 
@@ -634,8 +634,8 @@ DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id)
     dev = DEVICE(object_new(TYPE_NAND));
     qdev_prop_set_uint8(dev, "manufacturer_id", manf_id);
     qdev_prop_set_uint8(dev, "chip_id", chip_id);
-    if (bdrv) {
-        qdev_prop_set_drive_nofail(dev, "drive", bdrv);
+    if (blk) {
+        qdev_prop_set_drive_nofail(dev, "drive", blk);
     }
 
     qdev_init_nofail(dev);
@@ -654,14 +654,14 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
     if (PAGE(s->addr) >= s->pages)
         return;
 
-    if (!s->bdrv) {
+    if (!s->blk) {
         mem_and(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) +
                         s->offset, s->io, s->iolen);
     } else if (s->mem_oob) {
         sector = SECTOR(s->addr);
         off = (s->addr & PAGE_MASK) + s->offset;
         soff = SECTOR_OFFSET(s->addr);
-        if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) < 0) {
+        if (blk_read(s->blk, sector, iobuf, PAGE_SECTORS) < 0) {
             printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
             return;
         }
@@ -673,21 +673,21 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
                             MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE));
         }
 
-        if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) < 0) {
+        if (blk_write(s->blk, sector, iobuf, PAGE_SECTORS) < 0) {
             printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
         }
     } else {
         off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset;
         sector = off >> 9;
         soff = off & 0x1ff;
-        if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) < 0) {
+        if (blk_read(s->blk, sector, iobuf, PAGE_SECTORS + 2) < 0) {
             printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
             return;
         }
 
         mem_and(iobuf + soff, s->io, s->iolen);
 
-        if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) < 0) {
+        if (blk_write(s->blk, sector, iobuf, PAGE_SECTORS + 2) < 0) {
             printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
         }
     }
@@ -705,7 +705,7 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
         return;
     }
 
-    if (!s->bdrv) {
+    if (!s->blk) {
         memset(s->storage + PAGE_START(addr),
                         0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift);
     } else if (s->mem_oob) {
@@ -714,17 +714,17 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
         i = SECTOR(addr);
         page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
         for (; i < page; i ++)
-            if (bdrv_write(s->bdrv, i, iobuf, 1) < 0) {
+            if (blk_write(s->blk, i, iobuf, 1) < 0) {
                 printf("%s: write error in sector %" PRIu64 "\n", __func__, i);
             }
     } else {
         addr = PAGE_START(addr);
         page = addr >> 9;
-        if (bdrv_read(s->bdrv, page, iobuf, 1) < 0) {
+        if (blk_read(s->blk, page, iobuf, 1) < 0) {
             printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
         }
         memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
-        if (bdrv_write(s->bdrv, page, iobuf, 1) < 0) {
+        if (blk_write(s->blk, page, iobuf, 1) < 0) {
             printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
         }
 
@@ -732,18 +732,18 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
         i = (addr & ~0x1ff) + 0x200;
         for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200;
                         i < addr; i += 0x200) {
-            if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) < 0) {
+            if (blk_write(s->blk, i >> 9, iobuf, 1) < 0) {
                 printf("%s: write error in sector %" PRIu64 "\n",
                        __func__, i >> 9);
             }
         }
 
         page = i >> 9;
-        if (bdrv_read(s->bdrv, page, iobuf, 1) < 0) {
+        if (blk_read(s->blk, page, iobuf, 1) < 0) {
             printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
         }
         memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);
-        if (bdrv_write(s->bdrv, page, iobuf, 1) < 0) {
+        if (blk_write(s->blk, page, iobuf, 1) < 0) {
             printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
         }
     }
@@ -756,9 +756,9 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
         return;
     }
 
-    if (s->bdrv) {
+    if (s->blk) {
         if (s->mem_oob) {
-            if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) < 0) {
+            if (blk_read(s->blk, SECTOR(addr), s->io, PAGE_SECTORS) < 0) {
                 printf("%s: read error in sector %" PRIu64 "\n",
                                 __func__, SECTOR(addr));
             }
@@ -767,8 +767,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
                             OOB_SIZE);
             s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset;
         } else {
-            if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9,
-                                    s->io, (PAGE_SECTORS + 2)) < 0) {
+            if (blk_read(s->blk, PAGE_START(addr) >> 9,
+                         s->io, (PAGE_SECTORS + 2)) < 0) {
                 printf("%s: read error in sector %" PRIu64 "\n",
                                 __func__, PAGE_START(addr) >> 9);
             }
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index 9a95f7530b..b6263dcabc 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -26,6 +26,7 @@
 #include <hw/pci/pci.h>
 #include "sysemu/sysemu.h"
 #include "qapi/visitor.h"
+#include "sysemu/block-backend.h"
 
 #include "nvme.h"
 
@@ -199,7 +200,7 @@ static void nvme_rw_cb(void *opaque, int ret)
     NvmeCtrl *n = sq->ctrl;
     NvmeCQueue *cq = n->cq[sq->cqid];
 
-    block_acct_done(bdrv_get_stats(n->conf.bs), &req->acct);
+    block_acct_done(blk_get_stats(n->conf.blk), &req->acct);
     if (!ret) {
         req->status = NVME_SUCCESS;
     } else {
@@ -233,11 +234,11 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
     }
     assert((nlb << data_shift) == req->qsg.size);
 
-    dma_acct_start(n->conf.bs, &req->acct, &req->qsg, is_write ?
-        BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
+    dma_acct_start(n->conf.blk, &req->acct, &req->qsg,
+                   is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
     req->aiocb = is_write ?
-        dma_bdrv_write(n->conf.bs, &req->qsg, aio_slba, nvme_rw_cb, req) :
-        dma_bdrv_read(n->conf.bs, &req->qsg, aio_slba, nvme_rw_cb, req);
+        dma_blk_write(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req) :
+        dma_blk_read(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req);
 
     return NVME_NO_COMPLETE;
 }
@@ -290,7 +291,7 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
     while (!QTAILQ_EMPTY(&sq->out_req_list)) {
         req = QTAILQ_FIRST(&sq->out_req_list);
         assert(req->aiocb);
-        bdrv_aio_cancel(req->aiocb);
+        blk_aio_cancel(req->aiocb);
     }
     if (!nvme_check_cqid(n, sq->cqid)) {
         cq = n->cq[sq->cqid];
@@ -565,7 +566,7 @@ static void nvme_clear_ctrl(NvmeCtrl *n)
         }
     }
 
-    bdrv_flush(n->conf.bs);
+    blk_flush(n->conf.blk);
     n->bar.cc = 0;
 }
 
@@ -750,11 +751,11 @@ static int nvme_init(PCIDevice *pci_dev)
     int64_t bs_size;
     uint8_t *pci_conf;
 
-    if (!(n->conf.bs)) {
+    if (!n->conf.blk) {
         return -1;
     }
 
-    bs_size = bdrv_getlength(n->conf.bs);
+    bs_size = blk_getlength(n->conf.blk);
     if (bs_size < 0) {
         return -1;
     }
diff --git a/hw/block/nvme.h b/hw/block/nvme.h
index bd8fc3e4bb..993c51131c 100644
--- a/hw/block/nvme.h
+++ b/hw/block/nvme.h
@@ -636,7 +636,7 @@ typedef struct NvmeAsyncEvent {
 
 typedef struct NvmeRequest {
     struct NvmeSQueue       *sq;
-    BlockDriverAIOCB        *aiocb;
+    BlockAIOCB              *aiocb;
     uint16_t                status;
     NvmeCqe                 cqe;
     BlockAcctCookie         acct;
diff --git a/hw/block/onenand.c b/hw/block/onenand.c
index e48e5859f3..348630d905 100644
--- a/hw/block/onenand.c
+++ b/hw/block/onenand.c
@@ -22,6 +22,7 @@
 #include "hw/hw.h"
 #include "hw/block/flash.h"
 #include "hw/irq.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "exec/memory.h"
 #include "exec/address-spaces.h"
@@ -49,8 +50,8 @@ typedef struct OneNANDState {
     hwaddr base;
     qemu_irq intr;
     qemu_irq rdy;
-    BlockDriverState *bdrv;
-    BlockDriverState *bdrv_cur;
+    BlockBackend *blk;
+    BlockBackend *blk_cur;
     uint8_t *image;
     uint8_t *otp;
     uint8_t *current;
@@ -213,7 +214,7 @@ static void onenand_reset(OneNANDState *s, int cold)
     s->wpstatus = 0x0002;
     s->cycle = 0;
     s->otpmode = 0;
-    s->bdrv_cur = s->bdrv;
+    s->blk_cur = s->blk;
     s->current = s->image;
     s->secs_cur = s->secs;
 
@@ -221,7 +222,7 @@ static void onenand_reset(OneNANDState *s, int cold)
         /* Lock the whole flash */
         memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks);
 
-        if (s->bdrv_cur && bdrv_read(s->bdrv_cur, 0, s->boot[0], 8) < 0) {
+        if (s->blk_cur && blk_read(s->blk_cur, 0, s->boot[0], 8) < 0) {
             hw_error("%s: Loading the BootRAM failed.\n", __func__);
         }
     }
@@ -237,10 +238,11 @@ static void onenand_system_reset(DeviceState *dev)
 static inline int onenand_load_main(OneNANDState *s, int sec, int secn,
                 void *dest)
 {
-    if (s->bdrv_cur)
-        return bdrv_read(s->bdrv_cur, sec, dest, secn) < 0;
-    else if (sec + secn > s->secs_cur)
+    if (s->blk_cur) {
+        return blk_read(s->blk_cur, sec, dest, secn) < 0;
+    } else if (sec + secn > s->secs_cur) {
         return 1;
+    }
 
     memcpy(dest, s->current + (sec << 9), secn << 9);
 
@@ -256,9 +258,9 @@ static inline int onenand_prog_main(OneNANDState *s, int sec, int secn,
         uint32_t size = (uint32_t)secn * 512;
         const uint8_t *sp = (const uint8_t *)src;
         uint8_t *dp = 0;
-        if (s->bdrv_cur) {
+        if (s->blk_cur) {
             dp = g_malloc(size);
-            if (!dp || bdrv_read(s->bdrv_cur, sec, dp, secn) < 0) {
+            if (!dp || blk_read(s->blk_cur, sec, dp, secn) < 0) {
                 result = 1;
             }
         } else {
@@ -273,11 +275,11 @@ static inline int onenand_prog_main(OneNANDState *s, int sec, int secn,
             for (i = 0; i < size; i++) {
                 dp[i] &= sp[i];
             }
-            if (s->bdrv_cur) {
-                result = bdrv_write(s->bdrv_cur, sec, dp, secn) < 0;
+            if (s->blk_cur) {
+                result = blk_write(s->blk_cur, sec, dp, secn) < 0;
             }
         }
-        if (dp && s->bdrv_cur) {
+        if (dp && s->blk_cur) {
             g_free(dp);
         }
     }
@@ -290,14 +292,16 @@ static inline int onenand_load_spare(OneNANDState *s, int sec, int secn,
 {
     uint8_t buf[512];
 
-    if (s->bdrv_cur) {
-        if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0)
+    if (s->blk_cur) {
+        if (blk_read(s->blk_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) {
             return 1;
+        }
         memcpy(dest, buf + ((sec & 31) << 4), secn << 4);
-    } else if (sec + secn > s->secs_cur)
+    } else if (sec + secn > s->secs_cur) {
         return 1;
-    else
+    } else {
         memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4);
+    }
  
     return 0;
 }
@@ -309,11 +313,10 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn,
     if (secn > 0) {
         const uint8_t *sp = (const uint8_t *)src;
         uint8_t *dp = 0, *dpp = 0;
-        if (s->bdrv_cur) {
+        if (s->blk_cur) {
             dp = g_malloc(512);
-            if (!dp || bdrv_read(s->bdrv_cur,
-                                 s->secs_cur + (sec >> 5),
-                                 dp, 1) < 0) {
+            if (!dp
+                || blk_read(s->blk_cur, s->secs_cur + (sec >> 5), dp, 1) < 0) {
                 result = 1;
             } else {
                 dpp = dp + ((sec & 31) << 4);
@@ -330,9 +333,9 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn,
             for (i = 0; i < (secn << 4); i++) {
                 dpp[i] &= sp[i];
             }
-            if (s->bdrv_cur) {
-                result = bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5),
-                                    dp, 1) < 0;
+            if (s->blk_cur) {
+                result = blk_write(s->blk_cur, s->secs_cur + (sec >> 5),
+                                   dp, 1) < 0;
             }
         }
         g_free(dp);
@@ -354,16 +357,16 @@ static inline int onenand_erase(OneNANDState *s, int sec, int num)
     }
     memset(blankbuf, 0xff, 512);
     for (; num > 0; num--, sec++) {
-        if (s->bdrv_cur) {
+        if (s->blk_cur) {
             int erasesec = s->secs_cur + (sec >> 5);
-            if (bdrv_write(s->bdrv_cur, sec, blankbuf, 1) < 0) {
+            if (blk_write(s->blk_cur, sec, blankbuf, 1) < 0) {
                 goto fail;
             }
-            if (bdrv_read(s->bdrv_cur, erasesec, tmpbuf, 1) < 0) {
+            if (blk_read(s->blk_cur, erasesec, tmpbuf, 1) < 0) {
                 goto fail;
             }
             memcpy(tmpbuf + ((sec & 31) << 4), blankbuf, 1 << 4);
-            if (bdrv_write(s->bdrv_cur, erasesec, tmpbuf, 1) < 0) {
+            if (blk_write(s->blk_cur, erasesec, tmpbuf, 1) < 0) {
                 goto fail;
             }
         } else {
@@ -576,7 +579,7 @@ static void onenand_command(OneNANDState *s)
 
     case 0x65:	/* OTP Access */
         s->intstatus |= ONEN_INT;
-        s->bdrv_cur = NULL;
+        s->blk_cur = NULL;
         s->current = s->otp;
         s->secs_cur = 1 << (BLOCK_SHIFT - 9);
         s->addr[ONEN_BUF_BLOCK] = 0;
@@ -776,15 +779,15 @@ static int onenand_initfn(SysBusDevice *sbd)
         ? (1 << (6 + ((s->id.dev >> 4) & 7))) : 0;
     memory_region_init_io(&s->iomem, OBJECT(s), &onenand_ops, s, "onenand",
                           0x10000 << s->shift);
-    if (!s->bdrv) {
+    if (!s->blk) {
         s->image = memset(g_malloc(size + (size >> 5)),
                           0xff, size + (size >> 5));
     } else {
-        if (bdrv_is_read_only(s->bdrv)) {
+        if (blk_is_read_only(s->blk)) {
             error_report("Can't use a read-only drive");
             return -1;
         }
-        s->bdrv_cur = s->bdrv;
+        s->blk_cur = s->blk;
     }
     s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT),
                     0xff, (64 + 2) << PAGE_SHIFT);
@@ -815,7 +818,7 @@ static Property onenand_properties[] = {
     DEFINE_PROP_UINT16("device_id", OneNANDState, id.dev, 0),
     DEFINE_PROP_UINT16("version_id", OneNANDState, id.ver, 0),
     DEFINE_PROP_INT32("shift", OneNANDState, shift, 0),
-    DEFINE_PROP_DRIVE("drive", OneNANDState, bdrv),
+    DEFINE_PROP_DRIVE("drive", OneNANDState, blk),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index 1346541c80..89d380e59d 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -38,7 +38,7 @@
 
 #include "hw/hw.h"
 #include "hw/block/flash.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "qemu/timer.h"
 #include "qemu/bitops.h"
 #include "exec/address-spaces.h"
@@ -69,7 +69,7 @@ struct pflash_t {
     SysBusDevice parent_obj;
     /*< public >*/
 
-    BlockDriverState *bs;
+    BlockBackend *blk;
     uint32_t nb_blocs;
     uint64_t sector_len;
     uint8_t bank_width;
@@ -395,13 +395,13 @@ static void pflash_update(pflash_t *pfl, int offset,
                           int size)
 {
     int offset_end;
-    if (pfl->bs) {
+    if (pfl->blk) {
         offset_end = offset + size;
         /* round to sectors */
         offset = offset >> 9;
         offset_end = (offset_end + 511) >> 9;
-        bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
-                   offset_end - offset);
+        blk_write(pfl->blk, offset, pfl->storage + (offset << 9),
+                  offset_end - offset);
     }
 }
 
@@ -784,9 +784,9 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
     pfl->storage = memory_region_get_ram_ptr(&pfl->mem);
     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
 
-    if (pfl->bs) {
+    if (pfl->blk) {
         /* read the initial flash content */
-        ret = bdrv_read(pfl->bs, 0, pfl->storage, total_len >> 9);
+        ret = blk_read(pfl->blk, 0, pfl->storage, total_len >> 9);
 
         if (ret < 0) {
             vmstate_unregister_ram(&pfl->mem, DEVICE(pfl));
@@ -795,8 +795,8 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
         }
     }
 
-    if (pfl->bs) {
-        pfl->ro = bdrv_is_read_only(pfl->bs);
+    if (pfl->blk) {
+        pfl->ro = blk_is_read_only(pfl->blk);
     } else {
         pfl->ro = 0;
     }
@@ -898,7 +898,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
 }
 
 static Property pflash_cfi01_properties[] = {
-    DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
+    DEFINE_PROP_DRIVE("drive", struct pflash_t, blk),
     /* num-blocks is the number of blocks actually visible to the guest,
      * ie the total size of the device divided by the sector length.
      * If we're emulating flash devices wired in parallel the actual
@@ -962,14 +962,14 @@ type_init(pflash_cfi01_register_types)
 pflash_t *pflash_cfi01_register(hwaddr base,
                                 DeviceState *qdev, const char *name,
                                 hwaddr size,
-                                BlockDriverState *bs,
+                                BlockBackend *blk,
                                 uint32_t sector_len, int nb_blocs,
                                 int bank_width, uint16_t id0, uint16_t id1,
                                 uint16_t id2, uint16_t id3, int be)
 {
     DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01);
 
-    if (bs && qdev_prop_set_drive(dev, "drive", bs)) {
+    if (blk && qdev_prop_set_drive(dev, "drive", blk)) {
         abort();
     }
     qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c
index 01011cf339..8513a17e9f 100644
--- a/hw/block/pflash_cfi02.c
+++ b/hw/block/pflash_cfi02.c
@@ -38,7 +38,7 @@
 #include "hw/hw.h"
 #include "hw/block/flash.h"
 #include "qemu/timer.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "qemu/host-utils.h"
 #include "hw/sysbus.h"
@@ -63,7 +63,7 @@ struct pflash_t {
     SysBusDevice parent_obj;
     /*< public >*/
 
-    BlockDriverState *bs;
+    BlockBackend *blk;
     uint32_t sector_len;
     uint32_t nb_blocs;
     uint32_t chip_len;
@@ -249,13 +249,13 @@ static void pflash_update(pflash_t *pfl, int offset,
                           int size)
 {
     int offset_end;
-    if (pfl->bs) {
+    if (pfl->blk) {
         offset_end = offset + size;
         /* round to sectors */
         offset = offset >> 9;
         offset_end = (offset_end + 511) >> 9;
-        bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
-                   offset_end - offset);
+        blk_write(pfl->blk, offset, pfl->storage + (offset << 9),
+                  offset_end - offset);
     }
 }
 
@@ -618,9 +618,9 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
     vmstate_register_ram(&pfl->orig_mem, DEVICE(pfl));
     pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
     pfl->chip_len = chip_len;
-    if (pfl->bs) {
+    if (pfl->blk) {
         /* read the initial flash content */
-        ret = bdrv_read(pfl->bs, 0, pfl->storage, chip_len >> 9);
+        ret = blk_read(pfl->blk, 0, pfl->storage, chip_len >> 9);
         if (ret < 0) {
             vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl));
             error_setg(errp, "failed to read the initial flash content");
@@ -632,8 +632,8 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
     pfl->rom_mode = 1;
     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
 
-    if (pfl->bs) {
-        pfl->ro = bdrv_is_read_only(pfl->bs);
+    if (pfl->blk) {
+        pfl->ro = blk_is_read_only(pfl->blk);
     } else {
         pfl->ro = 0;
     }
@@ -722,7 +722,7 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
 }
 
 static Property pflash_cfi02_properties[] = {
-    DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
+    DEFINE_PROP_DRIVE("drive", struct pflash_t, blk),
     DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0),
     DEFINE_PROP_UINT32("sector-length", struct pflash_t, sector_len, 0),
     DEFINE_PROP_UINT8("width", struct pflash_t, width, 0),
@@ -763,7 +763,7 @@ type_init(pflash_cfi02_register_types)
 pflash_t *pflash_cfi02_register(hwaddr base,
                                 DeviceState *qdev, const char *name,
                                 hwaddr size,
-                                BlockDriverState *bs, uint32_t sector_len,
+                                BlockBackend *blk, uint32_t sector_len,
                                 int nb_blocs, int nb_mappings, int width,
                                 uint16_t id0, uint16_t id1,
                                 uint16_t id2, uint16_t id3,
@@ -772,7 +772,7 @@ pflash_t *pflash_cfi02_register(hwaddr base,
 {
     DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02);
 
-    if (bs && qdev_prop_set_drive(dev, "drive", bs)) {
+    if (blk && qdev_prop_set_drive(dev, "drive", blk)) {
         abort();
     }
     qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 1fa97709c8..b19b102b42 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -16,6 +16,7 @@
 #include "qemu/error-report.h"
 #include "trace.h"
 #include "hw/block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "hw/virtio/virtio-blk.h"
 #include "dataplane/virtio-blk.h"
@@ -64,7 +65,8 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
 static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
     bool is_read)
 {
-    BlockErrorAction action = bdrv_get_error_action(req->dev->bs, is_read, error);
+    BlockErrorAction action = blk_get_error_action(req->dev->blk,
+                                                   is_read, error);
     VirtIOBlock *s = req->dev;
 
     if (action == BLOCK_ERROR_ACTION_STOP) {
@@ -72,11 +74,11 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
         s->rq = req;
     } else if (action == BLOCK_ERROR_ACTION_REPORT) {
         virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
-        block_acct_done(bdrv_get_stats(s->bs), &req->acct);
+        block_acct_done(blk_get_stats(s->blk), &req->acct);
         virtio_blk_free_request(req);
     }
 
-    bdrv_error_action(s->bs, action, is_read, error);
+    blk_error_action(s->blk, action, is_read, error);
     return action != BLOCK_ERROR_ACTION_IGNORE;
 }
 
@@ -94,7 +96,7 @@ static void virtio_blk_rw_complete(void *opaque, int ret)
     }
 
     virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
-    block_acct_done(bdrv_get_stats(req->dev->bs), &req->acct);
+    block_acct_done(blk_get_stats(req->dev->blk), &req->acct);
     virtio_blk_free_request(req);
 }
 
@@ -109,7 +111,7 @@ static void virtio_blk_flush_complete(void *opaque, int ret)
     }
 
     virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
-    block_acct_done(bdrv_get_stats(req->dev->bs), &req->acct);
+    block_acct_done(blk_get_stats(req->dev->blk), &req->acct);
     virtio_blk_free_request(req);
 }
 
@@ -155,7 +157,7 @@ int virtio_blk_handle_scsi_req(VirtIOBlock *blk,
      */
     scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base;
 
-    if (!blk->blk.scsi) {
+    if (!blk->conf.scsi) {
         status = VIRTIO_BLK_S_UNSUPP;
         goto fail;
     }
@@ -209,7 +211,7 @@ int virtio_blk_handle_scsi_req(VirtIOBlock *blk,
     hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base;
     hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len;
 
-    status = bdrv_ioctl(blk->bs, SG_IO, &hdr);
+    status = blk_ioctl(blk->blk, SG_IO, &hdr);
     if (status) {
         status = VIRTIO_BLK_S_UNSUPP;
         goto fail;
@@ -255,7 +257,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
     virtio_blk_free_request(req);
 }
 
-void virtio_submit_multiwrite(BlockDriverState *bs, MultiReqBuffer *mrb)
+void virtio_submit_multiwrite(BlockBackend *blk, MultiReqBuffer *mrb)
 {
     int i, ret;
 
@@ -263,7 +265,7 @@ void virtio_submit_multiwrite(BlockDriverState *bs, MultiReqBuffer *mrb)
         return;
     }
 
-    ret = bdrv_aio_multiwrite(bs, mrb->blkreq, mrb->num_writes);
+    ret = blk_aio_multiwrite(blk, mrb->blkreq, mrb->num_writes);
     if (ret != 0) {
         for (i = 0; i < mrb->num_writes; i++) {
             if (mrb->blkreq[i].error) {
@@ -277,14 +279,14 @@ void virtio_submit_multiwrite(BlockDriverState *bs, MultiReqBuffer *mrb)
 
 static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb)
 {
-    block_acct_start(bdrv_get_stats(req->dev->bs), &req->acct, 0,
+    block_acct_start(blk_get_stats(req->dev->blk), &req->acct, 0,
                      BLOCK_ACCT_FLUSH);
 
     /*
      * Make sure all outstanding writes are posted to the backing device.
      */
-    virtio_submit_multiwrite(req->dev->bs, mrb);
-    bdrv_aio_flush(req->dev->bs, virtio_blk_flush_complete, req);
+    virtio_submit_multiwrite(req->dev->blk, mrb);
+    blk_aio_flush(req->dev->blk, virtio_blk_flush_complete, req);
 }
 
 static bool virtio_blk_sect_range_ok(VirtIOBlock *dev,
@@ -296,10 +298,10 @@ static bool virtio_blk_sect_range_ok(VirtIOBlock *dev,
     if (sector & dev->sector_mask) {
         return false;
     }
-    if (size % dev->conf->logical_block_size) {
+    if (size % dev->conf.conf.logical_block_size) {
         return false;
     }
-    bdrv_get_geometry(dev->bs, &total_sectors);
+    blk_get_geometry(dev->blk, &total_sectors);
     if (sector > total_sectors || nb_sectors > total_sectors - sector) {
         return false;
     }
@@ -321,11 +323,11 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb)
         return;
     }
 
-    block_acct_start(bdrv_get_stats(req->dev->bs), &req->acct, req->qiov.size,
+    block_acct_start(blk_get_stats(req->dev->blk), &req->acct, req->qiov.size,
                      BLOCK_ACCT_WRITE);
 
     if (mrb->num_writes == 32) {
-        virtio_submit_multiwrite(req->dev->bs, mrb);
+        virtio_submit_multiwrite(req->dev->blk, mrb);
     }
 
     blkreq = &mrb->blkreq[mrb->num_writes];
@@ -353,11 +355,11 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req)
         return;
     }
 
-    block_acct_start(bdrv_get_stats(req->dev->bs), &req->acct, req->qiov.size,
+    block_acct_start(blk_get_stats(req->dev->blk), &req->acct, req->qiov.size,
                      BLOCK_ACCT_READ);
-    bdrv_aio_readv(req->dev->bs, sector, &req->qiov,
-                   req->qiov.size / BDRV_SECTOR_SIZE,
-                   virtio_blk_rw_complete, req);
+    blk_aio_readv(req->dev->blk, sector, &req->qiov,
+                  req->qiov.size / BDRV_SECTOR_SIZE,
+                  virtio_blk_rw_complete, req);
 }
 
 void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
@@ -405,7 +407,7 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
          * NB: per existing s/n string convention the string is
          * terminated by '\0' only when shorter than buffer.
          */
-        const char *serial = s->blk.serial ? s->blk.serial : "";
+        const char *serial = s->conf.serial ? s->conf.serial : "";
         size_t size = MIN(strlen(serial) + 1,
                           MIN(iov_size(in_iov, in_num),
                               VIRTIO_BLK_ID_BYTES));
@@ -445,7 +447,7 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
         virtio_blk_handle_request(req, &mrb);
     }
 
-    virtio_submit_multiwrite(s->bs, &mrb);
+    virtio_submit_multiwrite(s->blk, &mrb);
 
     /*
      * FIXME: Want to check for completions before returning to guest mode,
@@ -473,7 +475,7 @@ static void virtio_blk_dma_restart_bh(void *opaque)
         req = next;
     }
 
-    virtio_submit_multiwrite(s->bs, &mrb);
+    virtio_submit_multiwrite(s->blk, &mrb);
 }
 
 static void virtio_blk_dma_restart_cb(void *opaque, int running,
@@ -486,7 +488,7 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running,
     }
 
     if (!s->bh) {
-        s->bh = aio_bh_new(bdrv_get_aio_context(s->blk.conf.bs),
+        s->bh = aio_bh_new(blk_get_aio_context(s->conf.conf.blk),
                            virtio_blk_dma_restart_bh, s);
         qemu_bh_schedule(s->bh);
     }
@@ -504,8 +506,8 @@ static void virtio_blk_reset(VirtIODevice *vdev)
      * This should cancel pending requests, but can't do nicely until there
      * are per-device request lists.
      */
-    bdrv_drain_all();
-    bdrv_set_enable_write_cache(s->bs, s->original_wce);
+    blk_drain_all();
+    blk_set_enable_write_cache(s->blk, s->original_wce);
 }
 
 /* coalesce internal state, copy to pci i/o region 0
@@ -513,19 +515,20 @@ static void virtio_blk_reset(VirtIODevice *vdev)
 static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
 {
     VirtIOBlock *s = VIRTIO_BLK(vdev);
+    BlockConf *conf = &s->conf.conf;
     struct virtio_blk_config blkcfg;
     uint64_t capacity;
-    int blk_size = s->conf->logical_block_size;
+    int blk_size = conf->logical_block_size;
 
-    bdrv_get_geometry(s->bs, &capacity);
+    blk_get_geometry(s->blk, &capacity);
     memset(&blkcfg, 0, sizeof(blkcfg));
     virtio_stq_p(vdev, &blkcfg.capacity, capacity);
     virtio_stl_p(vdev, &blkcfg.seg_max, 128 - 2);
-    virtio_stw_p(vdev, &blkcfg.cylinders, s->conf->cyls);
+    virtio_stw_p(vdev, &blkcfg.cylinders, conf->cyls);
     virtio_stl_p(vdev, &blkcfg.blk_size, blk_size);
-    virtio_stw_p(vdev, &blkcfg.min_io_size, s->conf->min_io_size / blk_size);
-    virtio_stw_p(vdev, &blkcfg.opt_io_size, s->conf->opt_io_size / blk_size);
-    blkcfg.heads = s->conf->heads;
+    virtio_stw_p(vdev, &blkcfg.min_io_size, conf->min_io_size / blk_size);
+    virtio_stw_p(vdev, &blkcfg.opt_io_size, conf->opt_io_size / blk_size);
+    blkcfg.heads = conf->heads;
     /*
      * We must ensure that the block device capacity is a multiple of
      * the logical block size. If that is not the case, let's use
@@ -537,15 +540,15 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
      * divided by 512 - instead it is the amount of blk_size blocks
      * per track (cylinder).
      */
-    if (bdrv_getlength(s->bs) /  s->conf->heads / s->conf->secs % blk_size) {
-        blkcfg.sectors = s->conf->secs & ~s->sector_mask;
+    if (blk_getlength(s->blk) /  conf->heads / conf->secs % blk_size) {
+        blkcfg.sectors = conf->secs & ~s->sector_mask;
     } else {
-        blkcfg.sectors = s->conf->secs;
+        blkcfg.sectors = conf->secs;
     }
     blkcfg.size_max = 0;
-    blkcfg.physical_block_exp = get_physical_block_exp(s->conf);
+    blkcfg.physical_block_exp = get_physical_block_exp(conf);
     blkcfg.alignment_offset = 0;
-    blkcfg.wce = bdrv_enable_write_cache(s->bs);
+    blkcfg.wce = blk_enable_write_cache(s->blk);
     memcpy(config, &blkcfg, sizeof(struct virtio_blk_config));
 }
 
@@ -556,9 +559,9 @@ static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
 
     memcpy(&blkcfg, config, sizeof(blkcfg));
 
-    aio_context_acquire(bdrv_get_aio_context(s->bs));
-    bdrv_set_enable_write_cache(s->bs, blkcfg.wce != 0);
-    aio_context_release(bdrv_get_aio_context(s->bs));
+    aio_context_acquire(blk_get_aio_context(s->blk));
+    blk_set_enable_write_cache(s->blk, blkcfg.wce != 0);
+    aio_context_release(blk_get_aio_context(s->blk));
 }
 
 static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features)
@@ -571,14 +574,15 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features)
     features |= (1 << VIRTIO_BLK_F_BLK_SIZE);
     features |= (1 << VIRTIO_BLK_F_SCSI);
 
-    if (s->blk.config_wce) {
+    if (s->conf.config_wce) {
         features |= (1 << VIRTIO_BLK_F_CONFIG_WCE);
     }
-    if (bdrv_enable_write_cache(s->bs))
+    if (blk_enable_write_cache(s->blk)) {
         features |= (1 << VIRTIO_BLK_F_WCE);
-
-    if (bdrv_is_read_only(s->bs))
+    }
+    if (blk_is_read_only(s->blk)) {
         features |= 1 << VIRTIO_BLK_F_RO;
+    }
 
     return features;
 }
@@ -612,13 +616,13 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status)
      *     Guest writes 1 to the WCE configuration field (writeback mode)
      *     Guest sets DRIVER_OK bit in status field
      *
-     * s->bs would erroneously be placed in writethrough mode.
+     * s->blk would erroneously be placed in writethrough mode.
      */
     if (!(features & (1 << VIRTIO_BLK_F_CONFIG_WCE))) {
-        aio_context_acquire(bdrv_get_aio_context(s->bs));
-        bdrv_set_enable_write_cache(s->bs,
-                                    !!(features & (1 << VIRTIO_BLK_F_WCE)));
-        aio_context_release(bdrv_get_aio_context(s->bs));
+        aio_context_acquire(blk_get_aio_context(s->blk));
+        blk_set_enable_write_cache(s->blk,
+                                   !!(features & (1 << VIRTIO_BLK_F_WCE)));
+        aio_context_release(blk_get_aio_context(s->blk));
     }
 }
 
@@ -707,8 +711,8 @@ static void virtio_blk_migration_state_changed(Notifier *notifier, void *data)
         if (s->dataplane) {
             return;
         }
-        bdrv_drain_all(); /* complete in-flight non-dataplane requests */
-        virtio_blk_data_plane_create(VIRTIO_DEVICE(s), &s->blk,
+        blk_drain_all(); /* complete in-flight non-dataplane requests */
+        virtio_blk_data_plane_create(VIRTIO_DEVICE(s), &s->conf,
                                      &s->dataplane, &err);
         if (err != NULL) {
             error_report("%s", error_get_pretty(err));
@@ -721,22 +725,22 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
 {
     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
     VirtIOBlock *s = VIRTIO_BLK(dev);
-    VirtIOBlkConf *blk = &(s->blk);
+    VirtIOBlkConf *conf = &s->conf;
     Error *err = NULL;
     static int virtio_blk_id;
 
-    if (!blk->conf.bs) {
+    if (!conf->conf.blk) {
         error_setg(errp, "drive property not set");
         return;
     }
-    if (!bdrv_is_inserted(blk->conf.bs)) {
+    if (!blk_is_inserted(conf->conf.blk)) {
         error_setg(errp, "Device needs media, but drive is empty");
         return;
     }
 
-    blkconf_serial(&blk->conf, &blk->serial);
-    s->original_wce = bdrv_enable_write_cache(blk->conf.bs);
-    blkconf_geometry(&blk->conf, NULL, 65535, 255, 255, &err);
+    blkconf_serial(&conf->conf, &conf->serial);
+    s->original_wce = blk_enable_write_cache(conf->conf.blk);
+    blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -745,14 +749,13 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
     virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
                 sizeof(struct virtio_blk_config));
 
-    s->bs = blk->conf.bs;
-    s->conf = &blk->conf;
+    s->blk = conf->conf.blk;
     s->rq = NULL;
-    s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1;
+    s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1;
 
     s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output);
     s->complete_request = virtio_blk_complete_request;
-    virtio_blk_data_plane_create(vdev, blk, &s->dataplane, &err);
+    virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err);
     if (err != NULL) {
         error_propagate(errp, err);
         virtio_cleanup(vdev);
@@ -764,10 +767,10 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
     s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
     register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
                     virtio_blk_save, virtio_blk_load, s);
-    bdrv_set_dev_ops(s->bs, &virtio_block_ops, s);
-    bdrv_set_guest_block_size(s->bs, s->conf->logical_block_size);
+    blk_set_dev_ops(s->blk, &virtio_block_ops, s);
+    blk_set_guest_block_size(s->blk, s->conf.conf.logical_block_size);
 
-    bdrv_iostatus_enable(s->bs);
+    blk_iostatus_enable(s->blk);
 }
 
 static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp)
@@ -780,7 +783,7 @@ static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp)
     s->dataplane = NULL;
     qemu_del_vm_change_state_handler(s->change);
     unregister_savevm(dev, "virtio-blk", s);
-    blockdev_mark_auto_del(s->bs);
+    blockdev_mark_auto_del(s->blk);
     virtio_cleanup(vdev);
 }
 
@@ -789,23 +792,23 @@ static void virtio_blk_instance_init(Object *obj)
     VirtIOBlock *s = VIRTIO_BLK(obj);
 
     object_property_add_link(obj, "iothread", TYPE_IOTHREAD,
-                             (Object **)&s->blk.iothread,
+                             (Object **)&s->conf.iothread,
                              qdev_prop_allow_set_link_before_realize,
                              OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL);
-    device_add_bootindex_property(obj, &s->blk.conf.bootindex,
+    device_add_bootindex_property(obj, &s->conf.conf.bootindex,
                                   "bootindex", "/disk@0,0",
                                   DEVICE(obj), NULL);
 }
 
 static Property virtio_blk_properties[] = {
-    DEFINE_BLOCK_PROPERTIES(VirtIOBlock, blk.conf),
-    DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, blk.conf),
-    DEFINE_PROP_STRING("serial", VirtIOBlock, blk.serial),
-    DEFINE_PROP_BIT("config-wce", VirtIOBlock, blk.config_wce, 0, true),
+    DEFINE_BLOCK_PROPERTIES(VirtIOBlock, conf.conf),
+    DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, conf.conf),
+    DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial),
+    DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true),
 #ifdef __linux__
-    DEFINE_PROP_BIT("scsi", VirtIOBlock, blk.scsi, 0, true),
+    DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, true),
 #endif
-    DEFINE_PROP_BIT("x-data-plane", VirtIOBlock, blk.data_plane, 0, false),
+    DEFINE_PROP_BIT("x-data-plane", VirtIOBlock, conf.data_plane, 0, false),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
index 0d27ab16a7..231e9a7392 100644
--- a/hw/block/xen_disk.c
+++ b/hw/block/xen_disk.c
@@ -39,6 +39,7 @@
 #include "hw/xen/xen_backend.h"
 #include "xen_blkif.h"
 #include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 
 /* ------------------------------------------------------------- */
 
@@ -122,7 +123,7 @@ struct XenBlkDev {
 
     /* qemu block driver */
     DriveInfo           *dinfo;
-    BlockDriverState    *bs;
+    BlockBackend        *blk;
     QEMUBH              *bh;
 };
 
@@ -479,7 +480,7 @@ static void qemu_aio_complete(void *opaque, int ret)
     if (ioreq->postsync) {
         ioreq->postsync = 0;
         ioreq->aio_inflight++;
-        bdrv_aio_flush(ioreq->blkdev->bs, qemu_aio_complete, ioreq);
+        blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq);
         return;
     }
 
@@ -493,7 +494,7 @@ static void qemu_aio_complete(void *opaque, int ret)
             break;
         }
     case BLKIF_OP_READ:
-        block_acct_done(bdrv_get_stats(ioreq->blkdev->bs), &ioreq->acct);
+        block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
         break;
     case BLKIF_OP_DISCARD:
     default:
@@ -512,18 +513,18 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
 
     ioreq->aio_inflight++;
     if (ioreq->presync) {
-        bdrv_aio_flush(ioreq->blkdev->bs, qemu_aio_complete, ioreq);
+        blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq);
         return 0;
     }
 
     switch (ioreq->req.operation) {
     case BLKIF_OP_READ:
-        block_acct_start(bdrv_get_stats(blkdev->bs), &ioreq->acct,
+        block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
                          ioreq->v.size, BLOCK_ACCT_READ);
         ioreq->aio_inflight++;
-        bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE,
-                       &ioreq->v, ioreq->v.size / BLOCK_SIZE,
-                       qemu_aio_complete, ioreq);
+        blk_aio_readv(blkdev->blk, ioreq->start / BLOCK_SIZE,
+                      &ioreq->v, ioreq->v.size / BLOCK_SIZE,
+                      qemu_aio_complete, ioreq);
         break;
     case BLKIF_OP_WRITE:
     case BLKIF_OP_FLUSH_DISKCACHE:
@@ -531,18 +532,18 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
             break;
         }
 
-        block_acct_start(bdrv_get_stats(blkdev->bs), &ioreq->acct,
+        block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
                          ioreq->v.size, BLOCK_ACCT_WRITE);
         ioreq->aio_inflight++;
-        bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE,
-                        &ioreq->v, ioreq->v.size / BLOCK_SIZE,
-                        qemu_aio_complete, ioreq);
+        blk_aio_writev(blkdev->blk, ioreq->start / BLOCK_SIZE,
+                       &ioreq->v, ioreq->v.size / BLOCK_SIZE,
+                       qemu_aio_complete, ioreq);
         break;
     case BLKIF_OP_DISCARD:
     {
         struct blkif_request_discard *discard_req = (void *)&ioreq->req;
         ioreq->aio_inflight++;
-        bdrv_aio_discard(blkdev->bs,
+        blk_aio_discard(blkdev->blk,
                         discard_req->sector_number, discard_req->nr_sectors,
                         qemu_aio_complete, ioreq);
         break;
@@ -854,44 +855,49 @@ static int blk_connect(struct XenDevice *xendev)
     blkdev->dinfo = drive_get(IF_XEN, 0, index);
     if (!blkdev->dinfo) {
         Error *local_err = NULL;
+        BlockBackend *blk;
         BlockDriver *drv;
+        BlockDriverState *bs;
 
         /* setup via xenbus -> create new block driver instance */
         xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
-        blkdev->bs = bdrv_new(blkdev->dev, NULL);
-        if (!blkdev->bs) {
+        blk = blk_new_with_bs(blkdev->dev, NULL);
+        if (!blk) {
             return -1;
         }
+        blkdev->blk = blk;
 
+        bs = blk_bs(blk);
         drv = bdrv_find_whitelisted_format(blkdev->fileproto, readonly);
-        if (bdrv_open(&blkdev->bs, blkdev->filename, NULL, NULL, qflags,
+        if (bdrv_open(&bs, blkdev->filename, NULL, NULL, qflags,
                       drv, &local_err) != 0) {
             xen_be_printf(&blkdev->xendev, 0, "error: %s\n",
                           error_get_pretty(local_err));
             error_free(local_err);
-            bdrv_unref(blkdev->bs);
-            blkdev->bs = NULL;
+            blk_unref(blk);
+            blkdev->blk = NULL;
             return -1;
         }
+        assert(bs == blk_bs(blk));
     } else {
         /* setup via qemu cmdline -> already setup for us */
         xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
-        blkdev->bs = blkdev->dinfo->bdrv;
-        if (bdrv_is_read_only(blkdev->bs) && !readonly) {
+        blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo);
+        if (blk_is_read_only(blkdev->blk) && !readonly) {
             xen_be_printf(&blkdev->xendev, 0, "Unexpected read-only drive");
-            blkdev->bs = NULL;
+            blkdev->blk = NULL;
             return -1;
         }
-        /* blkdev->bs is not create by us, we get a reference
-         * so we can bdrv_unref() unconditionally */
-        bdrv_ref(blkdev->bs);
+        /* blkdev->blk is not create by us, we get a reference
+         * so we can blk_unref() unconditionally */
+        blk_ref(blkdev->blk);
     }
-    bdrv_attach_dev_nofail(blkdev->bs, blkdev);
-    blkdev->file_size = bdrv_getlength(blkdev->bs);
+    blk_attach_dev_nofail(blkdev->blk, blkdev);
+    blkdev->file_size = blk_getlength(blkdev->blk);
     if (blkdev->file_size < 0) {
-        xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n",
+        xen_be_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n",
                       (int)blkdev->file_size, strerror(-blkdev->file_size),
-                      bdrv_get_format_name(blkdev->bs) ?: "-");
+                      bdrv_get_format_name(blk_bs(blkdev->blk)) ?: "-");
         blkdev->file_size = 0;
     }
 
@@ -982,10 +988,10 @@ static void blk_disconnect(struct XenDevice *xendev)
 {
     struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
 
-    if (blkdev->bs) {
-        bdrv_detach_dev(blkdev->bs, blkdev);
-        bdrv_unref(blkdev->bs);
-        blkdev->bs = NULL;
+    if (blkdev->blk) {
+        blk_detach_dev(blkdev->blk, blkdev);
+        blk_unref(blkdev->blk);
+        blkdev->blk = NULL;
     }
     xen_be_unbind_evtchn(&blkdev->xendev);
 
@@ -1001,7 +1007,7 @@ static int blk_free(struct XenDevice *xendev)
     struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
     struct ioreq *ioreq;
 
-    if (blkdev->bs || blkdev->sring) {
+    if (blkdev->blk || blkdev->sring) {
         blk_disconnect(xendev);
     }
 
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index f2bd954be5..65901ef10e 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -13,6 +13,7 @@
 #include "net/net.h"
 #include "hw/qdev.h"
 #include "qapi/qmp/qerror.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "hw/block/block.h"
 #include "net/hub.h"
@@ -68,16 +69,16 @@ static void set_pointer(Object *obj, Visitor *v, Property *prop,
 
 static int parse_drive(DeviceState *dev, const char *str, void **ptr)
 {
-    BlockDriverState *bs;
+    BlockBackend *blk;
 
-    bs = bdrv_find(str);
-    if (bs == NULL) {
+    blk = blk_by_name(str);
+    if (!blk) {
         return -ENOENT;
     }
-    if (bdrv_attach_dev(bs, dev) < 0) {
+    if (blk_attach_dev(blk, dev) < 0) {
         return -EEXIST;
     }
-    *ptr = bs;
+    *ptr = blk;
     return 0;
 }
 
@@ -85,17 +86,17 @@ static void release_drive(Object *obj, const char *name, void *opaque)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
-    BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop);
+    BlockBackend **ptr = qdev_get_prop_ptr(dev, prop);
 
     if (*ptr) {
-        bdrv_detach_dev(*ptr, dev);
+        blk_detach_dev(*ptr, dev);
         blockdev_auto_del(*ptr);
     }
 }
 
 static char *print_drive(void *ptr)
 {
-    return g_strdup(bdrv_get_device_name(ptr));
+    return g_strdup(blk_name(ptr));
 }
 
 static void get_drive(Object *obj, Visitor *v, void *opaque,
@@ -335,12 +336,11 @@ PropertyInfo qdev_prop_vlan = {
 };
 
 int qdev_prop_set_drive(DeviceState *dev, const char *name,
-                        BlockDriverState *value)
+                        BlockBackend *value)
 {
     Error *err = NULL;
-    const char *bdrv_name = value ? bdrv_get_device_name(value) : "";
-    object_property_set_str(OBJECT(dev), bdrv_name,
-                            name, &err);
+    object_property_set_str(OBJECT(dev),
+                            value ? blk_name(value) : "", name, &err);
     if (err) {
         qerror_report_err(err);
         error_free(err);
@@ -350,7 +350,7 @@ int qdev_prop_set_drive(DeviceState *dev, const char *name,
 }
 
 void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name,
-                                BlockDriverState *value)
+                                BlockBackend *value)
 {
     if (qdev_prop_set_drive(dev, name, value) < 0) {
         exit(1);
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 2ed995f2b7..2e47f70eb4 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -1,7 +1,7 @@
 #include "net/net.h"
 #include "hw/qdev.h"
 #include "qapi/qmp/qerror.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/block/block.h"
 #include "net/hub.h"
 #include "qapi/visitor.h"
diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c
index 2fb3aa81da..047919686d 100644
--- a/hw/cris/axis_dev88.c
+++ b/hw/cris/axis_dev88.c
@@ -30,7 +30,7 @@
 #include "hw/loader.h"
 #include "elf.h"
 #include "boot.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "sysemu/qtest.h"
 
@@ -284,7 +284,7 @@ void axisdev88_init(MachineState *machine)
 
       /* Attach a NAND flash to CS1.  */
     nand = drive_get(IF_MTD, 0, 0);
-    nand_state.nand = nand_init(nand ? nand->bdrv : NULL,
+    nand_state.nand = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL,
                                 NAND_MFR_STMICRO, 0x39);
     memory_region_init_io(&nand_state.iomem, NULL, &nand_ops, &nand_state,
                           "nand", 0x05000000);
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 93b3518b21..b540dd656c 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1591,6 +1591,11 @@ async_common:
             qxl_set_guest_bug(d,
                     "QXL_IO_UPDATE_AREA: invalid area (%ux%u)x(%ux%u)\n",
                     update.left, update.top, update.right, update.bottom);
+            if (update.left == update.right || update.top == update.bottom) {
+                /* old drivers may provide empty area, keep going */
+                qxl_clear_guest_bug(d);
+                goto cancel_async;
+            }
             break;
         }
         if (async == QXL_ASYNC) {
diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c
index c19c055d7c..4306adc959 100644
--- a/hw/display/tc6393xb.c
+++ b/hw/display/tc6393xb.c
@@ -15,6 +15,7 @@
 #include "hw/block/flash.h"
 #include "ui/console.h"
 #include "ui/pixel_ops.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 
 #define IRQ_TC6393_NAND		0
@@ -576,7 +577,8 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq)
     s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS);
 
     nand = drive_get(IF_MTD, 0, 0);
-    s->flash = nand_init(nand ? nand->bdrv : NULL, NAND_MFR_TOSHIBA, 0x76);
+    s->flash = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL,
+                         NAND_MFR_TOSHIBA, 0x76);
 
     memory_region_init_io(&s->iomem, NULL, &tc6393xb_ops, s, "tc6393xb", 0x10000);
     memory_region_add_subregion(sysmem, base, &s->iomem);
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index d045e8b454..61aba9fc54 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -44,7 +44,7 @@
 #include "sysemu/kvm.h"
 #include "kvm_i386.h"
 #include "hw/xen/xen.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/block/block.h"
 #include "ui/qemu-spice.h"
 #include "exec/memory.h"
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 43846339bc..91a20cb8f4 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -41,7 +41,7 @@
 #include "hw/sysbus.h"
 #include "hw/cpu/icc_bus.h"
 #include "sysemu/arch_init.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/i2c/smbus.h"
 #include "hw/xen/xen.h"
 #include "exec/memory.h"
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index bbe367a49f..75913c5b2f 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -23,7 +23,7 @@
  * THE SOFTWARE.
  */
 
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "qemu/error-report.h"
 #include "hw/sysbus.h"
 #include "hw/hw.h"
@@ -103,7 +103,7 @@ static void pc_system_flash_init(MemoryRegion *rom_memory)
 {
     int unit;
     DriveInfo *pflash_drv;
-    BlockDriverState *bdrv;
+    BlockBackend *blk;
     int64_t size;
     char *fatal_errmsg = NULL;
     hwaddr phys_addr = 0x100000000ULL;
@@ -119,8 +119,8 @@ static void pc_system_flash_init(MemoryRegion *rom_memory)
          (unit < FLASH_MAP_UNIT_MAX &&
           (pflash_drv = drive_get(IF_PFLASH, 0, unit)) != NULL);
          ++unit) {
-        bdrv = pflash_drv->bdrv;
-        size = bdrv_getlength(bdrv);
+        blk = blk_by_legacy_dinfo(pflash_drv);
+        size = blk_getlength(blk);
         if (size < 0) {
             fatal_errmsg = g_strdup_printf("failed to get backing file size");
         } else if (size == 0) {
@@ -156,7 +156,7 @@ static void pc_system_flash_init(MemoryRegion *rom_memory)
         /* pflash_cfi01_register() creates a deep copy of the name */
         snprintf(name, sizeof name, "system.flash%d", unit);
         system_flash = pflash_cfi01_register(phys_addr, NULL /* qdev */, name,
-                                             size, bdrv, sector_size,
+                                             size, blk, sector_size,
                                              size >> sector_bits,
                                              1      /* width */,
                                              0x0000 /* id0 */,
diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c
index 8bb18b403a..28b324a6f4 100644
--- a/hw/i386/xen/xen_platform.c
+++ b/hw/i386/xen/xen_platform.c
@@ -34,6 +34,7 @@
 #include "hw/xen/xen_backend.h"
 #include "trace.h"
 #include "exec/address-spaces.h"
+#include "sysemu/block-backend.h"
 
 #include <xenguest.h>
 
@@ -132,8 +133,8 @@ static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t v
            devices, and bit 2 the non-primary-master IDE devices. */
         if (val & UNPLUG_ALL_IDE_DISKS) {
             DPRINTF("unplug disks\n");
-            bdrv_drain_all();
-            bdrv_flush_all();
+            blk_drain_all();
+            blk_flush_all();
             pci_unplug_disks(pci_dev->bus);
         }
         if (val & UNPLUG_ALL_NICS) {
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 063730e8df..70958e3655 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -28,6 +28,7 @@
 #include <hw/sysbus.h>
 
 #include "monitor/monitor.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/dma.h"
 #include "internal.h"
 #include <hw/ide/pci.h>
@@ -84,7 +85,7 @@ static uint32_t  ahci_port_read(AHCIState *s, int port, int offset)
         val = pr->sig;
         break;
     case PORT_SCR_STAT:
-        if (s->dev[port].port.ifs[0].bs) {
+        if (s->dev[port].port.ifs[0].blk) {
             val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP |
                   SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE;
         } else {
@@ -501,7 +502,7 @@ static void ahci_reset_port(AHCIState *s, int port)
     d->init_d2h_sent = false;
 
     ide_state = &s->dev[port].port.ifs[0];
-    if (!ide_state->bs) {
+    if (!ide_state->blk) {
         return;
     }
 
@@ -513,11 +514,11 @@ static void ahci_reset_port(AHCIState *s, int port)
         }
 
         if (ncq_tfs->aiocb) {
-            bdrv_aio_cancel(ncq_tfs->aiocb);
+            blk_aio_cancel(ncq_tfs->aiocb);
             ncq_tfs->aiocb = NULL;
         }
 
-        /* Maybe we just finished the request thanks to bdrv_aio_cancel() */
+        /* Maybe we just finished the request thanks to blk_aio_cancel() */
         if (!ncq_tfs->used) {
             continue;
         }
@@ -527,7 +528,7 @@ static void ahci_reset_port(AHCIState *s, int port)
     }
 
     s->dev[port].port_state = STATE_RUN;
-    if (!ide_state->bs) {
+    if (!ide_state->blk) {
         pr->sig = 0;
         ide_state->status = SEEK_STAT | WRERR_STAT;
     } else if (ide_state->drive_kind == IDE_CD) {
@@ -826,7 +827,7 @@ static void ncq_cb(void *opaque, int ret)
     DPRINTF(ncq_tfs->drive->port_no, "NCQ transfer tag %d finished\n",
             ncq_tfs->tag);
 
-    block_acct_done(bdrv_get_stats(ncq_tfs->drive->port.ifs[0].bs),
+    block_acct_done(blk_get_stats(ncq_tfs->drive->port.ifs[0].blk),
                     &ncq_tfs->acct);
     qemu_sglist_destroy(&ncq_tfs->sglist);
     ncq_tfs->used = 0;
@@ -877,11 +878,11 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
             DPRINTF(port, "tag %d aio read %"PRId64"\n",
                     ncq_tfs->tag, ncq_tfs->lba);
 
-            dma_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
+            dma_acct_start(ncq_tfs->drive->port.ifs[0].blk, &ncq_tfs->acct,
                            &ncq_tfs->sglist, BLOCK_ACCT_READ);
-            ncq_tfs->aiocb = dma_bdrv_read(ncq_tfs->drive->port.ifs[0].bs,
-                                           &ncq_tfs->sglist, ncq_tfs->lba,
-                                           ncq_cb, ncq_tfs);
+            ncq_tfs->aiocb = dma_blk_read(ncq_tfs->drive->port.ifs[0].blk,
+                                          &ncq_tfs->sglist, ncq_tfs->lba,
+                                          ncq_cb, ncq_tfs);
             break;
         case WRITE_FPDMA_QUEUED:
             DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n",
@@ -890,11 +891,11 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
             DPRINTF(port, "tag %d aio write %"PRId64"\n",
                     ncq_tfs->tag, ncq_tfs->lba);
 
-            dma_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
+            dma_acct_start(ncq_tfs->drive->port.ifs[0].blk, &ncq_tfs->acct,
                            &ncq_tfs->sglist, BLOCK_ACCT_WRITE);
-            ncq_tfs->aiocb = dma_bdrv_write(ncq_tfs->drive->port.ifs[0].bs,
-                                            &ncq_tfs->sglist, ncq_tfs->lba,
-                                            ncq_cb, ncq_tfs);
+            ncq_tfs->aiocb = dma_blk_write(ncq_tfs->drive->port.ifs[0].blk,
+                                           &ncq_tfs->sglist, ncq_tfs->lba,
+                                           ncq_cb, ncq_tfs);
             break;
         default:
             DPRINTF(port, "error: tried to process non-NCQ command as NCQ\n");
@@ -943,7 +944,7 @@ static int handle_cmd(AHCIState *s, int port, int slot)
     /* The device we are working for */
     ide_state = &s->dev[port].port.ifs[0];
 
-    if (!ide_state->bs) {
+    if (!ide_state->blk) {
         DPRINTF(port, "error: guest accessed unused port");
         goto out;
     }
@@ -1122,7 +1123,7 @@ out:
 }
 
 static void ahci_start_dma(IDEDMA *dma, IDEState *s,
-                           BlockDriverCompletionFunc *dma_cb)
+                           BlockCompletionFunc *dma_cb)
 {
 #ifdef DEBUG_AHCI
     AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h
index e223258820..e8ea34a1df 100644
--- a/hw/ide/ahci.h
+++ b/hw/ide/ahci.h
@@ -241,7 +241,7 @@ typedef struct AHCIDevice AHCIDevice;
 
 typedef struct NCQTransferState {
     AHCIDevice *drive;
-    BlockDriverAIOCB *aiocb;
+    BlockAIOCB *aiocb;
     QEMUSGList sglist;
     BlockAcctCookie acct;
     uint16_t sector_count;
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 10218dfa3a..c63b7e556e 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -25,6 +25,7 @@
 
 #include "hw/ide/internal.h"
 #include "hw/scsi/scsi.h"
+#include "sysemu/block-backend.h"
 
 static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret);
 
@@ -110,16 +111,16 @@ static int cd_read_sector(IDEState *s, int lba, uint8_t *buf, int sector_size)
 
     switch(sector_size) {
     case 2048:
-        block_acct_start(bdrv_get_stats(s->bs), &s->acct,
+        block_acct_start(blk_get_stats(s->blk), &s->acct,
                          4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
-        ret = bdrv_read(s->bs, (int64_t)lba << 2, buf, 4);
-        block_acct_done(bdrv_get_stats(s->bs), &s->acct);
+        ret = blk_read(s->blk, (int64_t)lba << 2, buf, 4);
+        block_acct_done(blk_get_stats(s->blk), &s->acct);
         break;
     case 2352:
-        block_acct_start(bdrv_get_stats(s->bs), &s->acct,
+        block_acct_start(blk_get_stats(s->blk), &s->acct,
                          4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
-        ret = bdrv_read(s->bs, (int64_t)lba << 2, buf + 16, 4);
-        block_acct_done(bdrv_get_stats(s->bs), &s->acct);
+        ret = blk_read(s->blk, (int64_t)lba << 2, buf + 16, 4);
+        block_acct_done(blk_get_stats(s->blk), &s->acct);
         if (ret < 0)
             return ret;
         cd_data_to_raw(buf, lba);
@@ -254,7 +255,7 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size)
     s->io_buffer_index = 0;
 
     if (s->atapi_dma) {
-        block_acct_start(bdrv_get_stats(s->bs), &s->acct, size,
+        block_acct_start(blk_get_stats(s->blk), &s->acct, size,
                          BLOCK_ACCT_READ);
         s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
         ide_start_dma(s, ide_atapi_cmd_read_dma_cb);
@@ -350,13 +351,13 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
     s->bus->dma->iov.iov_len = n * 4 * 512;
     qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1);
 
-    s->bus->dma->aiocb = bdrv_aio_readv(s->bs, (int64_t)s->lba << 2,
+    s->bus->dma->aiocb = blk_aio_readv(s->blk, (int64_t)s->lba << 2,
                                        &s->bus->dma->qiov, n * 4,
                                        ide_atapi_cmd_read_dma_cb, s);
     return;
 
 eot:
-    block_acct_done(bdrv_get_stats(s->bs), &s->acct);
+    block_acct_done(blk_get_stats(s->blk), &s->acct);
     ide_set_inactive(s, false);
 }
 
@@ -371,7 +372,7 @@ static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors,
     s->io_buffer_size = 0;
     s->cd_sector_size = sector_size;
 
-    block_acct_start(bdrv_get_stats(s->bs), &s->acct, s->packet_transfer_size,
+    block_acct_start(blk_get_stats(s->blk), &s->acct, s->packet_transfer_size,
                      BLOCK_ACCT_READ);
 
     /* XXX: check if BUSY_STAT should be set */
@@ -504,7 +505,7 @@ static unsigned int event_status_media(IDEState *s,
     media_status = 0;
     if (s->tray_open) {
         media_status = MS_TRAY_OPEN;
-    } else if (bdrv_is_inserted(s->bs)) {
+    } else if (blk_is_inserted(s->blk)) {
         media_status = MS_MEDIA_PRESENT;
     }
 
@@ -800,7 +801,7 @@ static void cmd_test_unit_ready(IDEState *s, uint8_t *buf)
 static void cmd_prevent_allow_medium_removal(IDEState *s, uint8_t* buf)
 {
     s->tray_locked = buf[4] & 1;
-    bdrv_lock_medium(s->bs, buf[4] & 1);
+    blk_lock_medium(s->blk, buf[4] & 1);
     ide_atapi_cmd_ok(s);
 }
 
@@ -884,14 +885,14 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf)
 
     if (loej) {
         if (!start && !s->tray_open && s->tray_locked) {
-            sense = bdrv_is_inserted(s->bs)
+            sense = blk_is_inserted(s->blk)
                 ? NOT_READY : ILLEGAL_REQUEST;
             ide_atapi_cmd_error(s, sense, ASC_MEDIA_REMOVAL_PREVENTED);
             return;
         }
 
         if (s->tray_open != !start) {
-            bdrv_eject(s->bs, !start);
+            blk_eject(s->blk, !start);
             s->tray_open = !start;
         }
     }
@@ -1125,7 +1126,7 @@ void ide_atapi_cmd(IDEState *s)
      * states rely on this behavior.
      */
     if (!(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA) &&
-        !s->tray_open && bdrv_is_inserted(s->bs) && s->cdrom_changed) {
+        !s->tray_open && blk_is_inserted(s->blk) && s->cdrom_changed) {
 
         if (s->cdrom_changed == 1) {
             ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT);
@@ -1140,7 +1141,7 @@ void ide_atapi_cmd(IDEState *s)
 
     /* Report a Not Ready condition if appropriate for the command */
     if ((atapi_cmd_table[s->io_buffer[0]].flags & CHECK_READY) &&
-        (!media_present(s) || !bdrv_is_inserted(s->bs)))
+        (!media_present(s) || !blk_is_inserted(s->blk)))
     {
         ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT);
         return;
diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c
index 91048f202f..c8b0322155 100644
--- a/hw/ide/cmd646.c
+++ b/hw/ide/cmd646.c
@@ -26,7 +26,7 @@
 #include <hw/i386/pc.h>
 #include <hw/pci/pci.h>
 #include <hw/isa/isa.h>
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/dma.h"
 
diff --git a/hw/ide/core.c b/hw/ide/core.c
index ae85428864..44e3d502d4 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -31,7 +31,7 @@
 #include "sysemu/sysemu.h"
 #include "sysemu/dma.h"
 #include "hw/block/block.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 
 #include <hw/ide/internal.h>
 
@@ -158,10 +158,11 @@ static void ide_identify(IDEState *s)
         put_le16(p + 84, (1 << 14) | 0);
     }
     /* 14 = NOP supported, 5=WCACHE enabled, 0=SMART feature set enabled */
-    if (bdrv_enable_write_cache(s->bs))
-         put_le16(p + 85, (1 << 14) | (1 << 5) | 1);
-    else
-         put_le16(p + 85, (1 << 14) | 1);
+    if (blk_enable_write_cache(s->blk)) {
+        put_le16(p + 85, (1 << 14) | (1 << 5) | 1);
+    } else {
+        put_le16(p + 85, (1 << 14) | 1);
+    }
     /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
     put_le16(p + 86, (1 << 13) | (1 <<12) | (1 << 10));
     /* 14=set to 1, 8=has WWN, 1=SMART self test, 0=SMART error logging */
@@ -350,7 +351,7 @@ static void ide_set_signature(IDEState *s)
     if (s->drive_kind == IDE_CD) {
         s->lcyl = 0x14;
         s->hcyl = 0xeb;
-    } else if (s->bs) {
+    } else if (s->blk) {
         s->lcyl = 0;
         s->hcyl = 0;
     } else {
@@ -360,15 +361,16 @@ static void ide_set_signature(IDEState *s)
 }
 
 typedef struct TrimAIOCB {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
+    BlockBackend *blk;
     QEMUBH *bh;
     int ret;
     QEMUIOVector *qiov;
-    BlockDriverAIOCB *aiocb;
+    BlockAIOCB *aiocb;
     int i, j;
 } TrimAIOCB;
 
-static void trim_aio_cancel(BlockDriverAIOCB *acb)
+static void trim_aio_cancel(BlockAIOCB *acb)
 {
     TrimAIOCB *iocb = container_of(acb, TrimAIOCB, common);
 
@@ -379,7 +381,7 @@ static void trim_aio_cancel(BlockDriverAIOCB *acb)
     iocb->ret = -ECANCELED;
 
     if (iocb->aiocb) {
-        bdrv_aio_cancel_async(iocb->aiocb);
+        blk_aio_cancel_async(iocb->aiocb);
         iocb->aiocb = NULL;
     }
 }
@@ -420,8 +422,8 @@ static void ide_issue_trim_cb(void *opaque, int ret)
                 }
 
                 /* Got an entry! Submit and exit.  */
-                iocb->aiocb = bdrv_aio_discard(iocb->common.bs, sector, count,
-                                               ide_issue_trim_cb, opaque);
+                iocb->aiocb = blk_aio_discard(iocb->blk, sector, count,
+                                              ide_issue_trim_cb, opaque);
                 return;
             }
 
@@ -438,13 +440,14 @@ static void ide_issue_trim_cb(void *opaque, int ret)
     }
 }
 
-BlockDriverAIOCB *ide_issue_trim(BlockDriverState *bs,
+BlockAIOCB *ide_issue_trim(BlockBackend *blk,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     TrimAIOCB *iocb;
 
-    iocb = qemu_aio_get(&trim_aiocb_info, bs, cb, opaque);
+    iocb = blk_aio_get(&trim_aiocb_info, blk, cb, opaque);
+    iocb->blk = blk;
     iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb);
     iocb->ret = 0;
     iocb->qiov = qiov;
@@ -551,7 +554,7 @@ static bool ide_sect_range_ok(IDEState *s,
 {
     uint64_t total_sectors;
 
-    bdrv_get_geometry(s->bs, &total_sectors);
+    blk_get_geometry(s->blk, &total_sectors);
     if (sector > total_sectors || nb_sectors > total_sectors - sector) {
         return false;
     }
@@ -569,7 +572,7 @@ static void ide_sector_read_cb(void *opaque, int ret)
     if (ret == -ECANCELED) {
         return;
     }
-    block_acct_done(bdrv_get_stats(s->bs), &s->acct);
+    block_acct_done(blk_get_stats(s->blk), &s->acct);
     if (ret != 0) {
         if (ide_handle_rw_error(s, -ret, IDE_RETRY_PIO |
                                 IDE_RETRY_READ)) {
@@ -625,10 +628,10 @@ void ide_sector_read(IDEState *s)
     s->iov.iov_len  = n * BDRV_SECTOR_SIZE;
     qemu_iovec_init_external(&s->qiov, &s->iov, 1);
 
-    block_acct_start(bdrv_get_stats(s->bs), &s->acct,
+    block_acct_start(blk_get_stats(s->blk), &s->acct,
                      n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
-    s->pio_aiocb = bdrv_aio_readv(s->bs, sector_num, &s->qiov, n,
-                                  ide_sector_read_cb, s);
+    s->pio_aiocb = blk_aio_readv(s->blk, sector_num, &s->qiov, n,
+                                 ide_sector_read_cb, s);
 }
 
 static void dma_buf_commit(IDEState *s)
@@ -655,7 +658,7 @@ void ide_dma_error(IDEState *s)
 static int ide_handle_rw_error(IDEState *s, int error, int op)
 {
     bool is_read = (op & IDE_RETRY_READ) != 0;
-    BlockErrorAction action = bdrv_get_error_action(s->bs, is_read, error);
+    BlockErrorAction action = blk_get_error_action(s->blk, is_read, error);
 
     if (action == BLOCK_ERROR_ACTION_STOP) {
         s->bus->dma->ops->set_unit(s->bus->dma, s->unit);
@@ -668,7 +671,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
             ide_rw_error(s);
         }
     }
-    bdrv_error_action(s->bs, action, is_read, error);
+    blk_error_action(s->blk, action, is_read, error);
     return action != BLOCK_ERROR_ACTION_IGNORE;
 }
 
@@ -744,24 +747,24 @@ void ide_dma_cb(void *opaque, int ret)
 
     switch (s->dma_cmd) {
     case IDE_DMA_READ:
-        s->bus->dma->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num,
-                                           ide_dma_cb, s);
+        s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, sector_num,
+                                          ide_dma_cb, s);
         break;
     case IDE_DMA_WRITE:
-        s->bus->dma->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num,
-                                            ide_dma_cb, s);
+        s->bus->dma->aiocb = dma_blk_write(s->blk, &s->sg, sector_num,
+                                           ide_dma_cb, s);
         break;
     case IDE_DMA_TRIM:
-        s->bus->dma->aiocb = dma_bdrv_io(s->bs, &s->sg, sector_num,
-                                         ide_issue_trim, ide_dma_cb, s,
-                                         DMA_DIRECTION_TO_DEVICE);
+        s->bus->dma->aiocb = dma_blk_io(s->blk, &s->sg, sector_num,
+                                        ide_issue_trim, ide_dma_cb, s,
+                                        DMA_DIRECTION_TO_DEVICE);
         break;
     }
     return;
 
 eot:
     if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) {
-        block_acct_done(bdrv_get_stats(s->bs), &s->acct);
+        block_acct_done(blk_get_stats(s->blk), &s->acct);
     }
     ide_set_inactive(s, stay_active);
 }
@@ -775,11 +778,11 @@ static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd)
 
     switch (dma_cmd) {
     case IDE_DMA_READ:
-        block_acct_start(bdrv_get_stats(s->bs), &s->acct,
+        block_acct_start(blk_get_stats(s->blk), &s->acct,
                          s->nsector * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
         break;
     case IDE_DMA_WRITE:
-        block_acct_start(bdrv_get_stats(s->bs), &s->acct,
+        block_acct_start(blk_get_stats(s->blk), &s->acct,
                          s->nsector * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE);
         break;
     default:
@@ -789,7 +792,7 @@ static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd)
     ide_start_dma(s, ide_dma_cb);
 }
 
-void ide_start_dma(IDEState *s, BlockDriverCompletionFunc *cb)
+void ide_start_dma(IDEState *s, BlockCompletionFunc *cb)
 {
     if (s->bus->dma->ops->start_dma) {
         s->bus->dma->ops->start_dma(s->bus->dma, s, cb);
@@ -810,7 +813,7 @@ static void ide_sector_write_cb(void *opaque, int ret)
     if (ret == -ECANCELED) {
         return;
     }
-    block_acct_done(bdrv_get_stats(s->bs), &s->acct);
+    block_acct_done(blk_get_stats(s->blk), &s->acct);
 
     s->pio_aiocb = NULL;
     s->status &= ~BUSY_STAT;
@@ -877,10 +880,10 @@ void ide_sector_write(IDEState *s)
     s->iov.iov_len  = n * BDRV_SECTOR_SIZE;
     qemu_iovec_init_external(&s->qiov, &s->iov, 1);
 
-    block_acct_start(bdrv_get_stats(s->bs), &s->acct,
+    block_acct_start(blk_get_stats(s->blk), &s->acct,
                      n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
-    s->pio_aiocb = bdrv_aio_writev(s->bs, sector_num, &s->qiov, n,
-                                   ide_sector_write_cb, s);
+    s->pio_aiocb = blk_aio_writev(s->blk, sector_num, &s->qiov, n,
+                                  ide_sector_write_cb, s);
 }
 
 static void ide_flush_cb(void *opaque, int ret)
@@ -899,8 +902,8 @@ static void ide_flush_cb(void *opaque, int ret)
         }
     }
 
-    if (s->bs) {
-        block_acct_done(bdrv_get_stats(s->bs), &s->acct);
+    if (s->blk) {
+        block_acct_done(blk_get_stats(s->blk), &s->acct);
     }
     s->status = READY_STAT | SEEK_STAT;
     ide_cmd_done(s);
@@ -909,14 +912,14 @@ static void ide_flush_cb(void *opaque, int ret)
 
 void ide_flush_cache(IDEState *s)
 {
-    if (s->bs == NULL) {
+    if (s->blk == NULL) {
         ide_flush_cb(s, 0);
         return;
     }
 
     s->status |= BUSY_STAT;
-    block_acct_start(bdrv_get_stats(s->bs), &s->acct, 0, BLOCK_ACCT_FLUSH);
-    s->pio_aiocb = bdrv_aio_flush(s->bs, ide_flush_cb, s);
+    block_acct_start(blk_get_stats(s->blk), &s->acct, 0, BLOCK_ACCT_FLUSH);
+    s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s);
 }
 
 static void ide_cfata_metadata_inquiry(IDEState *s)
@@ -979,7 +982,7 @@ static void ide_cd_change_cb(void *opaque, bool load)
     uint64_t nb_sectors;
 
     s->tray_open = !load;
-    bdrv_get_geometry(s->bs, &nb_sectors);
+    blk_get_geometry(s->blk, &nb_sectors);
     s->nb_sectors = nb_sectors;
 
     /*
@@ -1113,7 +1116,7 @@ static bool cmd_data_set_management(IDEState *s, uint8_t cmd)
 {
     switch (s->feature) {
     case DSM_TRIM:
-        if (s->bs) {
+        if (s->blk) {
             ide_sector_start_dma(s, IDE_DMA_TRIM);
             return false;
         }
@@ -1126,7 +1129,7 @@ static bool cmd_data_set_management(IDEState *s, uint8_t cmd)
 
 static bool cmd_identify(IDEState *s, uint8_t cmd)
 {
-    if (s->bs && s->drive_kind != IDE_CD) {
+    if (s->blk && s->drive_kind != IDE_CD) {
         if (s->drive_kind != IDE_CFATA) {
             ide_identify(s);
         } else {
@@ -1176,7 +1179,7 @@ static bool cmd_read_multiple(IDEState *s, uint8_t cmd)
 {
     bool lba48 = (cmd == WIN_MULTREAD_EXT);
 
-    if (!s->bs || !s->mult_sectors) {
+    if (!s->blk || !s->mult_sectors) {
         ide_abort_command(s);
         return true;
     }
@@ -1192,7 +1195,7 @@ static bool cmd_write_multiple(IDEState *s, uint8_t cmd)
     bool lba48 = (cmd == WIN_MULTWRITE_EXT);
     int n;
 
-    if (!s->bs || !s->mult_sectors) {
+    if (!s->blk || !s->mult_sectors) {
         ide_abort_command(s);
         return true;
     }
@@ -1220,7 +1223,7 @@ static bool cmd_read_pio(IDEState *s, uint8_t cmd)
         return true;
     }
 
-    if (!s->bs) {
+    if (!s->blk) {
         ide_abort_command(s);
         return true;
     }
@@ -1236,7 +1239,7 @@ static bool cmd_write_pio(IDEState *s, uint8_t cmd)
 {
     bool lba48 = (cmd == WIN_WRITE_EXT);
 
-    if (!s->bs) {
+    if (!s->blk) {
         ide_abort_command(s);
         return true;
     }
@@ -1256,7 +1259,7 @@ static bool cmd_read_dma(IDEState *s, uint8_t cmd)
 {
     bool lba48 = (cmd == WIN_READDMA_EXT);
 
-    if (!s->bs) {
+    if (!s->blk) {
         ide_abort_command(s);
         return true;
     }
@@ -1271,7 +1274,7 @@ static bool cmd_write_dma(IDEState *s, uint8_t cmd)
 {
     bool lba48 = (cmd == WIN_WRITEDMA_EXT);
 
-    if (!s->bs) {
+    if (!s->blk) {
         ide_abort_command(s);
         return true;
     }
@@ -1322,7 +1325,7 @@ static bool cmd_set_features(IDEState *s, uint8_t cmd)
 {
     uint16_t *identify_data;
 
-    if (!s->bs) {
+    if (!s->blk) {
         ide_abort_command(s);
         return true;
     }
@@ -1330,12 +1333,12 @@ static bool cmd_set_features(IDEState *s, uint8_t cmd)
     /* XXX: valid for CDROM ? */
     switch (s->feature) {
     case 0x02: /* write cache enable */
-        bdrv_set_enable_write_cache(s->bs, true);
+        blk_set_enable_write_cache(s->blk, true);
         identify_data = (uint16_t *)s->identify_data;
         put_le16(identify_data + 85, (1 << 14) | (1 << 5) | 1);
         return true;
     case 0x82: /* write cache disable */
-        bdrv_set_enable_write_cache(s->bs, false);
+        blk_set_enable_write_cache(s->blk, false);
         identify_data = (uint16_t *)s->identify_data;
         put_le16(identify_data + 85, (1 << 14) | 1);
         ide_flush_cache(s);
@@ -1802,8 +1805,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
 #endif
     s = idebus_active_if(bus);
     /* ignore commands to non existent slave */
-    if (s != bus->ifs && !s->bs)
+    if (s != bus->ifs && !s->blk) {
         return;
+    }
 
     /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */
     if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET)
@@ -1848,59 +1852,66 @@ uint32_t ide_ioport_read(void *opaque, uint32_t addr1)
         ret = 0xff;
         break;
     case 1:
-        if ((!bus->ifs[0].bs && !bus->ifs[1].bs) ||
-            (s != bus->ifs && !s->bs))
+        if ((!bus->ifs[0].blk && !bus->ifs[1].blk) ||
+            (s != bus->ifs && !s->blk)) {
             ret = 0;
-        else if (!hob)
+        } else if (!hob) {
             ret = s->error;
-	else
+        } else {
 	    ret = s->hob_feature;
+        }
         break;
     case 2:
-        if (!bus->ifs[0].bs && !bus->ifs[1].bs)
+        if (!bus->ifs[0].blk && !bus->ifs[1].blk) {
             ret = 0;
-        else if (!hob)
+        } else if (!hob) {
             ret = s->nsector & 0xff;
-	else
+        } else {
 	    ret = s->hob_nsector;
+        }
         break;
     case 3:
-        if (!bus->ifs[0].bs && !bus->ifs[1].bs)
+        if (!bus->ifs[0].blk && !bus->ifs[1].blk) {
             ret = 0;
-        else if (!hob)
+        } else if (!hob) {
             ret = s->sector;
-	else
+        } else {
 	    ret = s->hob_sector;
+        }
         break;
     case 4:
-        if (!bus->ifs[0].bs && !bus->ifs[1].bs)
+        if (!bus->ifs[0].blk && !bus->ifs[1].blk) {
             ret = 0;
-        else if (!hob)
+        } else if (!hob) {
             ret = s->lcyl;
-	else
+        } else {
 	    ret = s->hob_lcyl;
+        }
         break;
     case 5:
-        if (!bus->ifs[0].bs && !bus->ifs[1].bs)
+        if (!bus->ifs[0].blk && !bus->ifs[1].blk) {
             ret = 0;
-        else if (!hob)
+        } else if (!hob) {
             ret = s->hcyl;
-	else
+        } else {
 	    ret = s->hob_hcyl;
+        }
         break;
     case 6:
-        if (!bus->ifs[0].bs && !bus->ifs[1].bs)
+        if (!bus->ifs[0].blk && !bus->ifs[1].blk) {
             ret = 0;
-        else
+        } else {
             ret = s->select;
+        }
         break;
     default:
     case 7:
-        if ((!bus->ifs[0].bs && !bus->ifs[1].bs) ||
-            (s != bus->ifs && !s->bs))
+        if ((!bus->ifs[0].blk && !bus->ifs[1].blk) ||
+            (s != bus->ifs && !s->blk)) {
             ret = 0;
-        else
+        } else {
             ret = s->status;
+        }
         qemu_irq_lower(bus->irq);
         break;
     }
@@ -1916,11 +1927,12 @@ uint32_t ide_status_read(void *opaque, uint32_t addr)
     IDEState *s = idebus_active_if(bus);
     int ret;
 
-    if ((!bus->ifs[0].bs && !bus->ifs[1].bs) ||
-        (s != bus->ifs && !s->bs))
+    if ((!bus->ifs[0].blk && !bus->ifs[1].blk) ||
+        (s != bus->ifs && !s->blk)) {
         ret = 0;
-    else
+    } else {
         ret = s->status;
+    }
 #ifdef DEBUG_IDE
     printf("ide: read status addr=0x%x val=%02x\n", addr, ret);
 #endif
@@ -2081,7 +2093,7 @@ static void ide_reset(IDEState *s)
 #endif
 
     if (s->pio_aiocb) {
-        bdrv_aio_cancel(s->pio_aiocb);
+        blk_aio_cancel(s->pio_aiocb);
         s->pio_aiocb = NULL;
     }
 
@@ -2145,7 +2157,7 @@ void ide_bus_reset(IDEBus *bus)
 #ifdef DEBUG_AIO
         printf("aio_cancel\n");
 #endif
-        bdrv_aio_cancel(bus->dma->aiocb);
+        blk_aio_cancel(bus->dma->aiocb);
         bus->dma->aiocb = NULL;
     }
 
@@ -2174,7 +2186,7 @@ static void ide_resize_cb(void *opaque)
         return;
     }
 
-    bdrv_get_geometry(s->bs, &nb_sectors);
+    blk_get_geometry(s->blk, &nb_sectors);
     s->nb_sectors = nb_sectors;
 
     /* Update the identify data buffer. */
@@ -2198,7 +2210,7 @@ static const BlockDevOps ide_hd_block_ops = {
     .resize_cb = ide_resize_cb,
 };
 
-int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind,
+int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind,
                    const char *version, const char *serial, const char *model,
                    uint64_t wwn,
                    uint32_t cylinders, uint32_t heads, uint32_t secs,
@@ -2206,10 +2218,10 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind,
 {
     uint64_t nb_sectors;
 
-    s->bs = bs;
+    s->blk = blk;
     s->drive_kind = kind;
 
-    bdrv_get_geometry(bs, &nb_sectors);
+    blk_get_geometry(blk, &nb_sectors);
     s->cylinders = cylinders;
     s->heads = heads;
     s->sectors = secs;
@@ -2223,18 +2235,18 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind,
     s->smart_errors = 0;
     s->smart_selftest_count = 0;
     if (kind == IDE_CD) {
-        bdrv_set_dev_ops(bs, &ide_cd_block_ops, s);
-        bdrv_set_guest_block_size(bs, 2048);
+        blk_set_dev_ops(blk, &ide_cd_block_ops, s);
+        blk_set_guest_block_size(blk, 2048);
     } else {
-        if (!bdrv_is_inserted(s->bs)) {
+        if (!blk_is_inserted(s->blk)) {
             error_report("Device needs media, but drive is empty");
             return -1;
         }
-        if (bdrv_is_read_only(bs)) {
+        if (blk_is_read_only(blk)) {
             error_report("Can't use a read-only drive");
             return -1;
         }
-        bdrv_set_dev_ops(bs, &ide_hd_block_ops, s);
+        blk_set_dev_ops(blk, &ide_hd_block_ops, s);
     }
     if (serial) {
         pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), serial);
@@ -2265,7 +2277,7 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind,
     }
 
     ide_reset(s);
-    bdrv_iostatus_enable(bs);
+    blk_iostatus_enable(blk);
     return 0;
 }
 
@@ -2282,7 +2294,7 @@ static void ide_init1(IDEBus *bus, int unit)
     s->io_buffer = qemu_memalign(2048, s->io_buffer_total_len);
     memset(s->io_buffer, 0, s->io_buffer_total_len);
 
-    s->smart_selftest_data = qemu_blockalign(s->bs, 512);
+    s->smart_selftest_data = blk_blockalign(s->blk, 512);
     memset(s->smart_selftest_data, 0, 512);
 
     s->sector_write_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
@@ -2377,7 +2389,7 @@ static int ide_drive_post_load(void *opaque, int version_id)
     IDEState *s = opaque;
 
     if (s->identify_set) {
-        bdrv_set_enable_write_cache(s->bs, !!(s->identify_data[85] & (1 << 5)));
+        blk_set_enable_write_cache(s->blk, !!(s->identify_data[85] & (1 << 5)));
     }
     return 0;
 }
diff --git a/hw/ide/ich.c b/hw/ide/ich.c
index 8eb77a1472..fb1d095503 100644
--- a/hw/ide/ich.c
+++ b/hw/ide/ich.c
@@ -65,7 +65,7 @@
 #include <hw/i386/pc.h>
 #include <hw/pci/pci.h>
 #include <hw/isa/isa.h>
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/dma.h"
 
 #include <hw/ide/pci.h>
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 5c19f79437..68ac610d6f 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -319,7 +319,7 @@ typedef enum { IDE_HD, IDE_CD, IDE_CFATA } IDEDriveKind;
 
 typedef void EndTransferFunc(IDEState *);
 
-typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockDriverCompletionFunc *);
+typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockCompletionFunc *);
 typedef void DMAVoidFunc(IDEDMA *);
 typedef int DMAIntFunc(IDEDMA *, int);
 typedef void DMAStopFunc(IDEDMA *, bool);
@@ -373,7 +373,7 @@ struct IDEState {
 
     /* set for lba48 access */
     uint8_t lba48;
-    BlockDriverState *bs;
+    BlockBackend *blk;
     char version[9];
     /* ATAPI specific */
     struct unreported_events events;
@@ -389,7 +389,7 @@ struct IDEState {
     int cd_sector_size;
     int atapi_dma; /* true if dma is requested for the packet cmd */
     BlockAcctCookie acct;
-    BlockDriverAIOCB *pio_aiocb;
+    BlockAIOCB *pio_aiocb;
     struct iovec iov;
     QEMUIOVector qiov;
     /* ATA DMA state */
@@ -442,7 +442,7 @@ struct IDEDMA {
     const struct IDEDMAOps *ops;
     struct iovec iov;
     QEMUIOVector qiov;
-    BlockDriverAIOCB *aiocb;
+    BlockAIOCB *aiocb;
 };
 
 struct IDEBus {
@@ -521,7 +521,7 @@ void ide_bus_reset(IDEBus *bus);
 int64_t ide_get_sector(IDEState *s);
 void ide_set_sector(IDEState *s, int64_t sector_num);
 
-void ide_start_dma(IDEState *s, BlockDriverCompletionFunc *cb);
+void ide_start_dma(IDEState *s, BlockCompletionFunc *cb);
 void ide_dma_error(IDEState *s);
 
 void ide_atapi_cmd_ok(IDEState *s);
@@ -537,7 +537,7 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr);
 void ide_data_writel(void *opaque, uint32_t addr, uint32_t val);
 uint32_t ide_data_readl(void *opaque, uint32_t addr);
 
-int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind,
+int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind,
                    const char *version, const char *serial, const char *model,
                    uint64_t wwn,
                    uint32_t cylinders, uint32_t heads, uint32_t secs,
@@ -555,9 +555,9 @@ void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
                         EndTransferFunc *end_transfer_func);
 void ide_transfer_stop(IDEState *s);
 void ide_set_inactive(IDEState *s, bool more);
-BlockDriverAIOCB *ide_issue_trim(BlockDriverState *bs,
+BlockAIOCB *ide_issue_trim(BlockBackend *blk,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque);
+        BlockCompletionFunc *cb, void *opaque);
 
 /* hw/ide/atapi.c */
 void ide_atapi_cmd(IDEState *s);
diff --git a/hw/ide/isa.c b/hw/ide/isa.c
index 4cbcb1cdf2..b084162ddc 100644
--- a/hw/ide/isa.c
+++ b/hw/ide/isa.c
@@ -25,7 +25,7 @@
 #include <hw/hw.h>
 #include <hw/i386/pc.h>
 #include <hw/isa/isa.h>
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/dma.h"
 
 #include <hw/ide/internal.h>
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index cefc85cf12..9a55407059 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -25,7 +25,7 @@
 #include "hw/hw.h"
 #include "hw/ppc/mac.h"
 #include "hw/ppc/mac_dbdma.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/dma.h"
 
 #include <hw/ide/internal.h>
@@ -134,7 +134,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
         MACIO_DPRINTF("precopying unaligned %d bytes to %#" HWADDR_PRIx "\n",
                       unaligned, io->addr + io->len - unaligned);
 
-        bdrv_read(s->bs, sector_num + nsector, io->remainder, 1);
+        blk_read(s->blk, sector_num + nsector, io->remainder, 1);
         cpu_physical_memory_write(io->addr + io->len - unaligned,
                                   io->remainder, unaligned);
 
@@ -164,14 +164,14 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
                   (s->lba << 2) + (s->io_buffer_index >> 9),
                   s->packet_transfer_size, s->dma_cmd);
 
-    m->aiocb = dma_bdrv_read(s->bs, &s->sg,
-                             (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9),
-                             pmac_ide_atapi_transfer_cb, io);
+    m->aiocb = dma_blk_read(s->blk, &s->sg,
+                            (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9),
+                            pmac_ide_atapi_transfer_cb, io);
     return;
 
 done:
     MACIO_DPRINTF("done DMA\n");
-    block_acct_done(bdrv_get_stats(s->bs), &s->acct);
+    block_acct_done(blk_get_stats(s->blk), &s->acct);
     io->dma_end(opaque);
 }
 
@@ -254,8 +254,8 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
             qemu_iovec_reset(&io->iov);
             qemu_iovec_add(&io->iov, io->remainder, 0x200);
 
-            m->aiocb = bdrv_aio_writev(s->bs, sector_num - 1, &io->iov, 1,
-                                       pmac_ide_transfer_cb, io);
+            m->aiocb = blk_aio_writev(s->blk, sector_num - 1, &io->iov, 1,
+                                      pmac_ide_transfer_cb, io);
         }
     }
 
@@ -294,8 +294,8 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
             qemu_iovec_reset(&io->iov);
             qemu_iovec_add(&io->iov, io->remainder, 0x200);
 
-            m->aiocb = bdrv_aio_readv(s->bs, sector_num + nsector, &io->iov, 1,
-                                      pmac_ide_transfer_cb, io);
+            m->aiocb = blk_aio_readv(s->blk, sector_num + nsector, &io->iov, 1,
+                                     pmac_ide_transfer_cb, io);
             break;
         case IDE_DMA_WRITE:
             /* cache the contents in our io struct */
@@ -333,17 +333,17 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
 
     switch (s->dma_cmd) {
     case IDE_DMA_READ:
-        m->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num,
-                                 pmac_ide_transfer_cb, io);
+        m->aiocb = dma_blk_read(s->blk, &s->sg, sector_num,
+                                pmac_ide_transfer_cb, io);
         break;
     case IDE_DMA_WRITE:
-        m->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num,
-                                  pmac_ide_transfer_cb, io);
+        m->aiocb = dma_blk_write(s->blk, &s->sg, sector_num,
+                                 pmac_ide_transfer_cb, io);
         break;
     case IDE_DMA_TRIM:
-        m->aiocb = dma_bdrv_io(s->bs, &s->sg, sector_num,
-                               ide_issue_trim, pmac_ide_transfer_cb, io,
-                               DMA_DIRECTION_TO_DEVICE);
+        m->aiocb = dma_blk_io(s->blk, &s->sg, sector_num,
+                              ide_issue_trim, pmac_ide_transfer_cb, io,
+                              DMA_DIRECTION_TO_DEVICE);
         break;
     }
 
@@ -352,7 +352,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
 
 done:
     if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) {
-        block_acct_done(bdrv_get_stats(s->bs), &s->acct);
+        block_acct_done(blk_get_stats(s->blk), &s->acct);
     }
     io->dma_end(io);
 }
@@ -370,7 +370,7 @@ static void pmac_ide_transfer(DBDMA_io *io)
         /* Handle non-block ATAPI DMA transfers */
         if (s->lba == -1) {
             s->io_buffer_size = MIN(io->len, s->packet_transfer_size);
-            block_acct_start(bdrv_get_stats(s->bs), &s->acct, s->io_buffer_size,
+            block_acct_start(blk_get_stats(s->blk), &s->acct, s->io_buffer_size,
                              BLOCK_ACCT_READ);
             MACIO_DPRINTF("non-block ATAPI DMA transfer size: %d\n",
                           s->io_buffer_size);
@@ -382,12 +382,12 @@ static void pmac_ide_transfer(DBDMA_io *io)
             m->dma_active = false;
 
             MACIO_DPRINTF("end of non-block ATAPI DMA transfer\n");
-            block_acct_done(bdrv_get_stats(s->bs), &s->acct);
+            block_acct_done(blk_get_stats(s->blk), &s->acct);
             io->dma_end(io);
             return;
         }
 
-        block_acct_start(bdrv_get_stats(s->bs), &s->acct, io->len,
+        block_acct_start(blk_get_stats(s->blk), &s->acct, io->len,
                          BLOCK_ACCT_READ);
         pmac_ide_atapi_transfer_cb(io, 0);
         return;
@@ -395,11 +395,11 @@ static void pmac_ide_transfer(DBDMA_io *io)
 
     switch (s->dma_cmd) {
     case IDE_DMA_READ:
-        block_acct_start(bdrv_get_stats(s->bs), &s->acct, io->len,
+        block_acct_start(blk_get_stats(s->blk), &s->acct, io->len,
                          BLOCK_ACCT_READ);
         break;
     case IDE_DMA_WRITE:
-        block_acct_start(bdrv_get_stats(s->bs), &s->acct, io->len,
+        block_acct_start(blk_get_stats(s->blk), &s->acct, io->len,
                          BLOCK_ACCT_WRITE);
         break;
     default:
@@ -415,7 +415,7 @@ static void pmac_ide_flush(DBDMA_io *io)
     MACIOIDEState *m = io->opaque;
 
     if (m->aiocb) {
-        bdrv_drain_all();
+        blk_drain_all();
     }
 }
 
@@ -558,7 +558,7 @@ static void ide_nop_restart(void *opaque, int x, RunState y)
 }
 
 static void ide_dbdma_start(IDEDMA *dma, IDEState *s,
-                            BlockDriverCompletionFunc *cb)
+                            BlockCompletionFunc *cb)
 {
     MACIOIDEState *m = container_of(dma, MACIOIDEState, dma);
 
diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c
index 15671b8c4d..ed85185a74 100644
--- a/hw/ide/microdrive.c
+++ b/hw/ide/microdrive.c
@@ -25,7 +25,7 @@
 #include <hw/hw.h>
 #include <hw/i386/pc.h>
 #include <hw/pcmcia.h>
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/dma.h"
 
 #include <hw/ide/internal.h>
@@ -247,7 +247,7 @@ static uint16_t md_common_read(PCMCIACardState *card, uint32_t at)
         return ide_ioport_read(&s->bus, 0x1);
     case 0xe:	/* Alternate Status */
         ifs = idebus_active_if(&s->bus);
-        if (ifs->bs) {
+        if (ifs->blk) {
             return ifs->status;
         } else {
             return 0;
diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c
index 334c8ccf08..b6ce62ac5a 100644
--- a/hw/ide/mmio.c
+++ b/hw/ide/mmio.c
@@ -24,7 +24,7 @@
  */
 #include "hw/hw.h"
 #include "hw/sysbus.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/dma.h"
 
 #include <hw/ide/internal.h>
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 2397f355cc..2dad50e8aa 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -26,7 +26,7 @@
 #include <hw/i386/pc.h>
 #include <hw/pci/pci.h>
 #include <hw/isa/isa.h>
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/dma.h"
 
 #include <hw/ide/pci.h>
@@ -38,7 +38,7 @@
         IDE_RETRY_READ | IDE_RETRY_FLUSH)
 
 static void bmdma_start_dma(IDEDMA *dma, IDEState *s,
-                            BlockDriverCompletionFunc *dma_cb)
+                            BlockCompletionFunc *dma_cb)
 {
     BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
 
@@ -302,7 +302,7 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val)
              * aio operation with preadv/pwritev.
              */
             if (bm->bus->dma->aiocb) {
-                bdrv_drain_all();
+                blk_drain_all();
                 assert(bm->bus->dma->aiocb == NULL);
             }
             bm->status &= ~BM_STATUS_DMAING;
diff --git a/hw/ide/pci.h b/hw/ide/pci.h
index 517711f913..2e9314ad87 100644
--- a/hw/ide/pci.h
+++ b/hw/ide/pci.h
@@ -23,7 +23,7 @@ typedef struct BMDMAState {
     uint32_t cur_prd_addr;
     uint32_t cur_prd_len;
     uint8_t unit;
-    BlockDriverCompletionFunc *dma_cb;
+    BlockCompletionFunc *dma_cb;
     int64_t sector_num;
     uint32_t nsector;
     MemoryRegion addr_ioport;
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
index 49e78a738a..574b9c164b 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -27,7 +27,7 @@
 #include <hw/i386/pc.h>
 #include <hw/pci/pci.h>
 #include <hw/isa/isa.h>
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/dma.h"
 
@@ -178,12 +178,13 @@ int pci_piix3_xen_ide_unplug(DeviceState *dev)
     for (; i < 3; i++) {
         di = drive_get_by_index(IF_IDE, i);
         if (di != NULL && !di->media_cd) {
-            DeviceState *ds = bdrv_get_attached_dev(di->bdrv);
+            BlockBackend *blk = blk_by_legacy_dinfo(di);
+            DeviceState *ds = blk_get_attached_dev(blk);
             if (ds) {
-                bdrv_detach_dev(di->bdrv, ds);
+                blk_detach_dev(blk, ds);
             }
-            pci_ide->bus[di->bus].ifs[di->unit].bs = NULL;
-            drive_del(di);
+            pci_ide->bus[di->bus].ifs[di->unit].blk = NULL;
+            blk_unref(blk);
         }
     }
     qdev_reset_all(DEVICE(dev));
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 9814ef0d24..b4f096e12e 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -20,6 +20,7 @@
 #include "sysemu/dma.h"
 #include "qemu/error-report.h"
 #include <hw/ide/internal.h>
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "hw/block/block.h"
 #include "sysemu/sysemu.h"
@@ -72,7 +73,7 @@ static int ide_qdev_init(DeviceState *qdev)
     IDEDeviceClass *dc = IDE_DEVICE_GET_CLASS(dev);
     IDEBus *bus = DO_UPCAST(IDEBus, qbus, qdev->parent_bus);
 
-    if (!dev->conf.bs) {
+    if (!dev->conf.blk) {
         error_report("No drive specified");
         goto err;
     }
@@ -117,7 +118,7 @@ IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive)
 
     dev = qdev_create(&bus->qbus, drive->media_cd ? "ide-cd" : "ide-hd");
     qdev_prop_set_uint32(dev, "unit", unit);
-    qdev_prop_set_drive_nofail(dev, "drive", drive->bdrv);
+    qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(drive));
     qdev_init_nofail(dev);
     return DO_UPCAST(IDEDevice, qdev, dev);
 }
@@ -127,7 +128,7 @@ int ide_get_geometry(BusState *bus, int unit,
 {
     IDEState *s = &DO_UPCAST(IDEBus, qbus, bus)->ifs[unit];
 
-    if (s->drive_kind != IDE_HD || !s->bs) {
+    if (s->drive_kind != IDE_HD || !s->blk) {
         return -1;
     }
 
@@ -172,7 +173,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
         }
     }
 
-    if (ide_init_drive(s, dev->conf.bs, kind,
+    if (ide_init_drive(s, dev->conf.blk, kind,
                        dev->version, dev->serial, dev->model, dev->wwn,
                        dev->conf.cyls, dev->conf.heads, dev->conf.secs,
                        dev->chs_trans) < 0) {
@@ -249,9 +250,9 @@ static int ide_cd_initfn(IDEDevice *dev)
 
 static int ide_drive_initfn(IDEDevice *dev)
 {
-    DriveInfo *dinfo = drive_get_by_blockdev(dev->conf.bs);
+    DriveInfo *dinfo = blk_legacy_dinfo(dev->conf.blk);
 
-    return ide_dev_initfn(dev, dinfo->media_cd ? IDE_CD : IDE_HD);
+    return ide_dev_initfn(dev, dinfo && dinfo->media_cd ? IDE_CD : IDE_HD);
 }
 
 #define DEFINE_IDE_DEV_PROPERTIES()                     \
diff --git a/hw/ide/via.c b/hw/ide/via.c
index 89d27b4c5b..4d8089de75 100644
--- a/hw/ide/via.c
+++ b/hw/ide/via.c
@@ -27,7 +27,7 @@
 #include <hw/i386/pc.h>
 #include <hw/pci/pci.h>
 #include <hw/isa/isa.h>
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/dma.h"
 
diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c
index 9327c53132..40a11060a5 100644
--- a/hw/isa/pc87312.c
+++ b/hw/isa/pc87312.c
@@ -25,6 +25,7 @@
 
 #include "hw/isa/pc87312.h"
 #include "qemu/error-report.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/char.h"
@@ -320,11 +321,13 @@ static void pc87312_realize(DeviceState *dev, Error **errp)
         qdev_prop_set_uint32(d, "irq", 6);
         drive = drive_get(IF_FLOPPY, 0, 0);
         if (drive != NULL) {
-            qdev_prop_set_drive_nofail(d, "driveA", drive->bdrv);
+            qdev_prop_set_drive_nofail(d, "driveA",
+                                       blk_by_legacy_dinfo(drive));
         }
         drive = drive_get(IF_FLOPPY, 0, 1);
         if (drive != NULL) {
-            qdev_prop_set_drive_nofail(d, "driveB", drive->bdrv);
+            qdev_prop_set_drive_nofail(d, "driveB",
+                                       blk_by_legacy_dinfo(drive));
         }
         qdev_init_nofail(d);
         s->fdc.dev = isa;
diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c
index 5ae6cad1ef..af0abdbed9 100644
--- a/hw/lm32/lm32_boards.c
+++ b/hw/lm32/lm32_boards.c
@@ -23,7 +23,7 @@
 #include "hw/devices.h"
 #include "hw/boards.h"
 #include "hw/loader.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "elf.h"
 #include "lm32_hwsetup.h"
 #include "lm32.h"
@@ -119,9 +119,9 @@ static void lm32_evr_init(MachineState *machine)
     dinfo = drive_get(IF_PFLASH, 0, 0);
     /* Spansion S29NS128P */
     pflash_cfi02_register(flash_base, NULL, "lm32_evr.flash", flash_size,
-                          dinfo ? dinfo->bdrv : NULL, flash_sector_size,
-                          flash_size / flash_sector_size, 1, 2,
-                          0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1);
+                          dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                          flash_sector_size, flash_size / flash_sector_size,
+                          1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1);
 
     /* create irq lines */
     cpu_irq = qemu_allocate_irqs(cpu_irq_handler, cpu, 1);
@@ -222,9 +222,9 @@ static void lm32_uclinux_init(MachineState *machine)
     dinfo = drive_get(IF_PFLASH, 0, 0);
     /* Spansion S29NS128P */
     pflash_cfi02_register(flash_base, NULL, "lm32_uclinux.flash", flash_size,
-                          dinfo ? dinfo->bdrv : NULL, flash_sector_size,
-                          flash_size / flash_sector_size, 1, 2,
-                          0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1);
+                          dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                          flash_sector_size, flash_size / flash_sector_size,
+                          1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1);
 
     /* create irq lines */
     cpu_irq = qemu_allocate_irqs(cpu_irq_handler, env, 1);
diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c
index c8832f03b5..256c1029ce 100644
--- a/hw/lm32/milkymist.c
+++ b/hw/lm32/milkymist.c
@@ -26,7 +26,7 @@
 #include "hw/boards.h"
 #include "hw/loader.h"
 #include "elf.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "milkymist-hw.h"
 #include "lm32.h"
 #include "exec/address-spaces.h"
@@ -126,9 +126,9 @@ milkymist_init(MachineState *machine)
     dinfo = drive_get(IF_PFLASH, 0, 0);
     /* Numonyx JS28F256J3F105 */
     pflash_cfi01_register(flash_base, NULL, "milkymist.flash", flash_size,
-                          dinfo ? dinfo->bdrv : NULL, flash_sector_size,
-                          flash_size / flash_sector_size, 2,
-                          0x00, 0x89, 0x00, 0x1d, 1);
+                          dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                          flash_sector_size, flash_size / flash_sector_size,
+                          2, 0x00, 0x89, 0x00, 0x1d, 1);
 
     /* create irq lines */
     cpu_irq = qemu_allocate_irqs(cpu_irq_handler, cpu, 1);
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index fcb51bc070..48c264b9d7 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -32,7 +32,7 @@
 #include "sysemu/sysemu.h"
 #include "hw/devices.h"
 #include "hw/boards.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/char/serial.h"
 #include "exec/address-spaces.h"
 #include "hw/ssi.h"
@@ -113,8 +113,8 @@ petalogix_ml605_init(MachineState *machine)
      * 10th paremeter 0 means little-endian */
     pflash_cfi01_register(FLASH_BASEADDR,
                           NULL, "petalogix_ml605.flash", FLASH_SIZE,
-                          dinfo ? dinfo->bdrv : NULL, (64 * 1024),
-                          FLASH_SIZE >> 16,
+                          dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                          (64 * 1024), FLASH_SIZE >> 16,
                           2, 0x89, 0x18, 0x0000, 0x0, 0);
 
 
diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
index 8e0fca9ebf..84f6e74653 100644
--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
+++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
@@ -30,7 +30,7 @@
 #include "sysemu/sysemu.h"
 #include "hw/devices.h"
 #include "hw/boards.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 
 #include "boot.h"
@@ -94,8 +94,8 @@ petalogix_s3adsp1800_init(MachineState *machine)
     dinfo = drive_get(IF_PFLASH, 0, 0);
     pflash_cfi01_register(FLASH_BASEADDR,
                           NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE,
-                          dinfo ? dinfo->bdrv : NULL, (64 * 1024),
-                          FLASH_SIZE >> 16,
+                          dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                          (64 * 1024), FLASH_SIZE >> 16,
                           1, 0x89, 0x18, 0x0000, 0x0, 1);
 
     dev = qdev_create(NULL, "xlnx.xps-intc");
diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c
index 29cd708e07..6a9ebfa911 100644
--- a/hw/mips/mips_fulong2e.c
+++ b/hw/mips/mips_fulong2e.c
@@ -25,7 +25,7 @@
 #include "net/net.h"
 #include "hw/boards.h"
 #include "hw/i2c/smbus.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "hw/block/flash.h"
 #include "hw/mips/mips.h"
 #include "hw/mips/cpudevs.h"
diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c
index 6c31cb8fe9..3f33093fd9 100644
--- a/hw/mips/mips_jazz.c
+++ b/hw/mips/mips_jazz.c
@@ -39,7 +39,7 @@
 #include "hw/timer/mc146818rtc.h"
 #include "hw/timer/i8254.h"
 #include "hw/audio/pcspk.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "hw/sysbus.h"
 #include "exec/address-spaces.h"
 #include "sysemu/qtest.h"
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index b20807c4e4..5845158a74 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -29,7 +29,7 @@
 #include "net/net.h"
 #include "hw/boards.h"
 #include "hw/i2c/smbus.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "hw/block/flash.h"
 #include "hw/mips/mips.h"
 #include "hw/mips/cpudevs.h"
@@ -44,6 +44,7 @@
 #include "elf.h"
 #include "hw/timer/mc146818rtc.h"
 #include "hw/timer/i8254.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "exec/address-spaces.h"
 #include "hw/sysbus.h"             /* SysBusDevice */
@@ -697,12 +698,12 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base,
     /* Jump to kernel code */
     stl_p(p++, 0x3c1f0000 | ((kernel_entry >> 16) & 0xffff));    /* lui ra, high(kernel_entry) */
     stl_p(p++, 0x37ff0000 | (kernel_entry & 0xffff));            /* ori ra, ra, low(kernel_entry) */
-    stl_p(p++, 0x03e00008);                                      /* jr ra */
+    stl_p(p++, 0x03e00009);                                      /* jalr ra */
     stl_p(p++, 0x00000000);                                      /* nop */
 
     /* YAMON subroutines */
     p = (uint32_t *) (base + 0x800);
-    stl_p(p++, 0x03e00008);                                     /* jr ra */
+    stl_p(p++, 0x03e00009);                                     /* jalr ra */
     stl_p(p++, 0x24020000);                                     /* li v0,0 */
     /* 808 YAMON print */
     stl_p(p++, 0x03e06821);                                     /* move t5,ra */
@@ -716,7 +717,7 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base,
     stl_p(p++, 0x00000000);                                     /* nop */
     stl_p(p++, 0x08000205);                                     /* j 814 */
     stl_p(p++, 0x00000000);                                     /* nop */
-    stl_p(p++, 0x01a00008);                                     /* jr t5 */
+    stl_p(p++, 0x01a00009);                                     /* jalr t5 */
     stl_p(p++, 0x01602021);                                     /* move a0,t3 */
     /* 0x83c YAMON print_count */
     stl_p(p++, 0x03e06821);                                     /* move t5,ra */
@@ -730,7 +731,7 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base,
     stl_p(p++, 0x258cffff);                                     /* addiu t4,t4,-1 */
     stl_p(p++, 0x1580fffa);                                     /* bnez t4,84c */
     stl_p(p++, 0x00000000);                                     /* nop */
-    stl_p(p++, 0x01a00008);                                     /* jr t5 */
+    stl_p(p++, 0x01a00009);                                     /* jalr t5 */
     stl_p(p++, 0x01602021);                                     /* move a0,t3 */
     /* 0x870 */
     stl_p(p++, 0x3c08b800);                                     /* lui t0,0xb400 */
@@ -740,7 +741,7 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base,
     stl_p(p++, 0x31290040);                                     /* andi t1,t1,0x40 */
     stl_p(p++, 0x1120fffc);                                     /* beqz t1,878 <outch+0x8> */
     stl_p(p++, 0x00000000);                                     /* nop */
-    stl_p(p++, 0x03e00008);                                     /* jr ra */
+    stl_p(p++, 0x03e00009);                                     /* jalr ra */
     stl_p(p++, 0xa1040000);                                     /* sb a0,0(t0) */
 
 }
@@ -1032,11 +1033,12 @@ void mips_malta_init(MachineState *machine)
         printf("Register parallel flash %d size " TARGET_FMT_lx " at "
                "addr %08llx '%s' %x\n",
                fl_idx, bios_size, FLASH_ADDRESS,
-               bdrv_get_device_name(dinfo->bdrv), fl_sectors);
+               blk_name(dinfo->bdrv), fl_sectors);
     }
 #endif
     fl = pflash_cfi01_register(FLASH_ADDRESS, NULL, "mips_malta.bios",
-                               BIOS_SIZE, dinfo ? dinfo->bdrv : NULL,
+                               BIOS_SIZE,
+                               dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
                                65536, fl_sectors,
                                4, 0x0000, 0x0000, 0x0000, 0x0000, be);
     bios = pflash_cfi01_get_memory(fl);
diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c
index 93606a490c..a7fe0ceadf 100644
--- a/hw/mips/mips_r4k.c
+++ b/hw/mips/mips_r4k.c
@@ -24,7 +24,7 @@
 #include "elf.h"
 #include "hw/timer/mc146818rtc.h"
 #include "hw/timer/i8254.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "sysemu/qtest.h"
 
@@ -241,8 +241,8 @@ void mips_r4k_init(MachineState *machine)
     } else if ((dinfo = drive_get(IF_PFLASH, 0, 0)) != NULL) {
         uint32_t mips_rom = 0x00400000;
         if (!pflash_cfi01_register(0x1fc00000, NULL, "mips_r4k.bios", mips_rom,
-                                   dinfo->bdrv, sector_len,
-                                   mips_rom / sector_len,
+                                   blk_by_legacy_dinfo(dinfo),
+                                   sector_len, mips_rom / sector_len,
                                    4, 0, 0, 0, 0, be)) {
             fprintf(stderr, "qemu: Error registering flash memory.\n");
 	}
diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c
index 6a72ef4814..10b5b2e86b 100644
--- a/hw/nvram/spapr_nvram.c
+++ b/hw/nvram/spapr_nvram.c
@@ -24,6 +24,7 @@
 
 #include <libfdt.h>
 
+#include "sysemu/block-backend.h"
 #include "sysemu/device_tree.h"
 #include "hw/sysbus.h"
 #include "hw/ppc/spapr.h"
@@ -33,7 +34,7 @@ typedef struct sPAPRNVRAM {
     VIOsPAPRDevice sdev;
     uint32_t size;
     uint8_t *buf;
-    BlockDriverState *drive;
+    BlockBackend *blk;
 } sPAPRNVRAM;
 
 #define TYPE_VIO_SPAPR_NVRAM "spapr-nvram"
@@ -77,8 +78,8 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr,
     }
 
     membuf = cpu_physical_memory_map(buffer, &len, 1);
-    if (nvram->drive) {
-        alen = bdrv_pread(nvram->drive, offset, membuf, len);
+    if (nvram->blk) {
+        alen = blk_pread(nvram->blk, offset, membuf, len);
     } else {
         assert(nvram->buf);
 
@@ -122,8 +123,8 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr,
     }
 
     membuf = cpu_physical_memory_map(buffer, &len, 0);
-    if (nvram->drive) {
-        alen = bdrv_pwrite(nvram->drive, offset, membuf, len);
+    if (nvram->blk) {
+        alen = blk_pwrite(nvram->blk, offset, membuf, len);
     } else {
         assert(nvram->buf);
 
@@ -140,8 +141,8 @@ static int spapr_nvram_init(VIOsPAPRDevice *dev)
 {
     sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(dev);
 
-    if (nvram->drive) {
-        nvram->size = bdrv_getlength(nvram->drive);
+    if (nvram->blk) {
+        nvram->size = blk_getlength(nvram->blk);
     } else {
         nvram->size = DEFAULT_NVRAM_SIZE;
         nvram->buf = g_malloc0(nvram->size);
@@ -168,7 +169,7 @@ static int spapr_nvram_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
 
 static Property spapr_nvram_properties[] = {
     DEFINE_SPAPR_PROPERTIES(sPAPRNVRAM, sdev),
-    DEFINE_PROP_DRIVE("drive", sPAPRNVRAM, drive),
+    DEFINE_PROP_DRIVE("drive", sPAPRNVRAM, blk),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c
index 6ab28b703c..0c09c72ac5 100644
--- a/hw/pci/pci-hotplug-old.c
+++ b/hw/pci/pci-hotplug-old.c
@@ -33,7 +33,7 @@
 #include "hw/scsi/scsi.h"
 #include "hw/virtio/virtio-blk.h"
 #include "qemu/config-file.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "qapi/error.h"
 
 static int pci_read_devaddr(Monitor *mon, const char *addr,
@@ -127,8 +127,10 @@ static int scsi_hot_add(Monitor *mon, DeviceState *adapter,
      */
     dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1);
     dinfo->bus = scsibus->busnr;
-    scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit,
-                                        false, -1, NULL, &local_err);
+    scsidev = scsi_bus_legacy_add_drive(scsibus,
+                                        blk_by_legacy_dinfo(dinfo),
+                                        dinfo->unit, false, -1, NULL,
+                                        &local_err);
     if (!scsidev) {
         error_report("%s", error_get_pretty(local_err));
         error_free(local_err);
@@ -250,7 +252,8 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon,
             return NULL;
         }
         dev = pci_create(bus, devfn, "virtio-blk-pci");
-        if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
+        if (qdev_prop_set_drive(&dev->qdev, "drive",
+                                blk_by_legacy_dinfo(dinfo)) < 0) {
             object_unparent(OBJECT(dev));
             dev = NULL;
             break;
diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h
index aff2b9a566..8bdba30c1e 100644
--- a/hw/ppc/mac.h
+++ b/hw/ppc/mac.h
@@ -131,7 +131,7 @@ typedef struct MACIOIDEState {
 
     MemoryRegion mem;
     IDEBus bus;
-    BlockDriverAIOCB *aiocb;
+    BlockAIOCB *aiocb;
     IDEDMA dma;
     void *dbdma;
     bool dma_active;
diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c
index 4094beb0ed..89aee716d4 100644
--- a/hw/ppc/mac_newworld.c
+++ b/hw/ppc/mac_newworld.c
@@ -65,7 +65,7 @@
 #include "sysemu/kvm.h"
 #include "kvm_ppc.h"
 #include "hw/usb.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "hw/sysbus.h"
 
diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c
index ebd87fb8f5..32c21a4286 100644
--- a/hw/ppc/mac_oldworld.c
+++ b/hw/ppc/mac_oldworld.c
@@ -40,7 +40,7 @@
 #include "elf.h"
 #include "sysemu/kvm.h"
 #include "kvm_ppc.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 
 #define MAX_IDE_BUS 2
diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c
index 18a4ec5e79..1dcea7730e 100644
--- a/hw/ppc/ppc405_boards.c
+++ b/hw/ppc/ppc405_boards.c
@@ -28,11 +28,12 @@
 #include "hw/block/flash.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/qtest.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "hw/boards.h"
 #include "qemu/log.h"
 #include "qemu/error-report.h"
 #include "hw/loader.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "exec/address-spaces.h"
 
@@ -225,17 +226,19 @@ static void ref405ep_init(MachineState *machine)
 #ifdef USE_FLASH_BIOS
     dinfo = drive_get(IF_PFLASH, 0, fl_idx);
     if (dinfo) {
-        bios_size = bdrv_getlength(dinfo->bdrv);
+        BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
+
+        bios_size = blk_getlength(blk);
         fl_sectors = (bios_size + 65535) >> 16;
 #ifdef DEBUG_BOARD_INIT
         printf("Register parallel flash %d size %lx"
                " at addr %lx '%s' %d\n",
                fl_idx, bios_size, -bios_size,
-               bdrv_get_device_name(dinfo->bdrv), fl_sectors);
+               blk_name(blk), fl_sectors);
 #endif
         pflash_cfi02_register((uint32_t)(-bios_size),
                               NULL, "ef405ep.bios", bios_size,
-                              dinfo->bdrv, 65536, fl_sectors, 1,
+                              blk, 65536, fl_sectors, 1,
                               2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
                               1);
         fl_idx++;
@@ -548,7 +551,9 @@ static void taihu_405ep_init(MachineState *machine)
 #if defined(USE_FLASH_BIOS)
     dinfo = drive_get(IF_PFLASH, 0, fl_idx);
     if (dinfo) {
-        bios_size = bdrv_getlength(dinfo->bdrv);
+        BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
+
+        bios_size = blk_getlength(blk);
         /* XXX: should check that size is 2MB */
         //        bios_size = 2 * 1024 * 1024;
         fl_sectors = (bios_size + 65535) >> 16;
@@ -556,11 +561,11 @@ static void taihu_405ep_init(MachineState *machine)
         printf("Register parallel flash %d size %lx"
                " at addr %lx '%s' %d\n",
                fl_idx, bios_size, -bios_size,
-               bdrv_get_device_name(dinfo->bdrv), fl_sectors);
+               blk_name(blk), fl_sectors);
 #endif
         pflash_cfi02_register((uint32_t)(-bios_size),
                               NULL, "taihu_405ep.bios", bios_size,
-                              dinfo->bdrv, 65536, fl_sectors, 1,
+                              blk, 65536, fl_sectors, 1,
                               4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
                               1);
         fl_idx++;
@@ -595,7 +600,9 @@ static void taihu_405ep_init(MachineState *machine)
     /* Register Linux flash */
     dinfo = drive_get(IF_PFLASH, 0, fl_idx);
     if (dinfo) {
-        bios_size = bdrv_getlength(dinfo->bdrv);
+        BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
+
+        bios_size = blk_getlength(blk);
         /* XXX: should check that size is 32MB */
         bios_size = 32 * 1024 * 1024;
         fl_sectors = (bios_size + 65535) >> 16;
@@ -603,10 +610,10 @@ static void taihu_405ep_init(MachineState *machine)
         printf("Register parallel flash %d size %lx"
                " at addr " TARGET_FMT_lx " '%s'\n",
                fl_idx, bios_size, (target_ulong)0xfc000000,
-               bdrv_get_device_name(dinfo->bdrv));
+               blk_name(blk));
 #endif
         pflash_cfi02_register(0xfc000000, NULL, "taihu_405ep.flash", bios_size,
-                              dinfo->bdrv, 65536, fl_sectors, 1,
+                              blk, 65536, fl_sectors, 1,
                               4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
                               1);
         fl_idx++;
diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c
index 9f484cdcb8..dd8433d0c9 100644
--- a/hw/ppc/prep.c
+++ b/hw/ppc/prep.c
@@ -38,7 +38,7 @@
 #include "hw/loader.h"
 #include "hw/timer/mc146818rtc.h"
 #include "hw/isa/pc87312.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/arch_init.h"
 #include "sysemu/qtest.h"
 #include "exec/address-spaces.h"
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 2becc9ff07..45764a7032 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -29,7 +29,7 @@
 #include "hw/fw-path-provider.h"
 #include "elf.h"
 #include "net/net.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/cpus.h"
 #include "sysemu/kvm.h"
 #include "kvm_ppc.h"
@@ -925,7 +925,7 @@ static void spapr_create_nvram(sPAPREnvironment *spapr)
     DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
 
     if (dinfo) {
-        qdev_prop_set_drive_nofail(dev, "drive", dinfo->bdrv);
+        qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(dinfo));
     }
 
     qdev_init_nofail(dev);
diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index 0de51481f3..6ebd5bee8b 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -39,7 +39,7 @@
 #include "hw/ppc/ppc4xx.h"
 #include "ppc405.h"
 
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "qapi/qmp/qerror.h"
 
 #define EPAPR_MAGIC    (0x45504150)
@@ -227,8 +227,8 @@ static void virtex_init(MachineState *machine)
 
     dinfo = drive_get(IF_PFLASH, 0, 0);
     pflash_cfi01_register(PFLASH_BASEADDR, NULL, "virtex.flash", FLASH_SIZE,
-                          dinfo ? dinfo->bdrv : NULL, (64 * 1024),
-                          FLASH_SIZE >> 16,
+                          dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                          (64 * 1024), FLASH_SIZE >> 16,
                           1, 0x89, 0x18, 0x0000, 0x0, 1);
 
     cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT];
diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c
index 29ecdb4194..2059874ae3 100644
--- a/hw/s390x/s390-virtio-bus.c
+++ b/hw/s390x/s390-virtio-bus.c
@@ -18,7 +18,7 @@
  */
 
 #include "hw/hw.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/sysemu.h"
 #include "hw/boards.h"
 #include "monitor/monitor.h"
diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c
index af0004a0ac..c215cd80e3 100644
--- a/hw/s390x/s390-virtio.c
+++ b/hw/s390x/s390-virtio.c
@@ -22,7 +22,7 @@
  */
 
 #include "hw/hw.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "sysemu/sysemu.h"
 #include "net/net.h"
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index f5ec94657f..1c0d91321a 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -10,7 +10,7 @@
  */
 
 #include "hw/hw.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "sysemu/sysemu.h"
 #include "net/net.h"
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index eedc9922a7..36a04f31bb 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -21,6 +21,7 @@
 #include "hw/hw.h"
 #include "hw/pci/pci.h"
 #include "sysemu/dma.h"
+#include "sysemu/block-backend.h"
 #include "hw/pci/msi.h"
 #include "hw/pci/msix.h"
 #include "qemu/iov.h"
@@ -976,7 +977,6 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
 {
     struct mfi_pd_info *info = cmd->iov_buf;
     size_t dcmd_size = sizeof(struct mfi_pd_info);
-    BlockConf *conf = &sdev->conf;
     uint64_t pd_size;
     uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (lun & 0xFF);
     uint8_t cmdbuf[6];
@@ -1037,7 +1037,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
     info->ref.v.device_id = cpu_to_le16(sdev_id);
     info->state.ddf.pd_type = cpu_to_le16(MFI_PD_DDF_TYPE_IN_VD|
                                           MFI_PD_DDF_TYPE_INTF_SAS);
-    bdrv_get_geometry(conf->bs, &pd_size);
+    blk_get_geometry(sdev->conf.blk, &pd_size);
     info->raw_size = cpu_to_le64(pd_size);
     info->non_coerced_size = cpu_to_le64(pd_size);
     info->coerced_size = cpu_to_le64(pd_size);
@@ -1100,13 +1100,12 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
     }
     QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
         SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
-        BlockConf *conf = &sdev->conf;
 
         if (num_ld_disks >= max_ld_disks) {
             break;
         }
         /* Logical device size is in blocks */
-        bdrv_get_geometry(conf->bs, &ld_size);
+        blk_get_geometry(sdev->conf.blk, &ld_size);
         info.ld_list[num_ld_disks].ld.v.target_id = sdev->id;
         info.ld_list[num_ld_disks].ld.v.lun_id = sdev->lun;
         info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL;
@@ -1144,7 +1143,6 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
     uint8_t cdb[6];
     SCSIRequest *req;
     ssize_t len, resid;
-    BlockConf *conf = &sdev->conf;
     uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (lun & 0xFF);
     uint64_t ld_size;
 
@@ -1177,7 +1175,7 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
     info->ld_config.params.num_drives = 1;
     info->ld_config.params.is_consistent = 1;
     /* Logical device size is in blocks */
-    bdrv_get_geometry(conf->bs, &ld_size);
+    blk_get_geometry(sdev->conf.blk, &ld_size);
     info->size = cpu_to_le64(ld_size);
     memset(info->ld_config.span, 0, sizeof(info->ld_config.span));
     info->ld_config.span[0].start_block = 0;
@@ -1261,7 +1259,6 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
 
     QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
         SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
-        BlockConf *conf = &sdev->conf;
         uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF);
         struct mfi_array *array;
         struct mfi_ld_config *ld;
@@ -1269,7 +1266,7 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
         int i;
 
         array = (struct mfi_array *)(data + array_offset);
-        bdrv_get_geometry(conf->bs, &pd_size);
+        blk_get_geometry(sdev->conf.blk, &pd_size);
         array->size = cpu_to_le64(pd_size);
         array->num_drives = 1;
         array->array_ref = cpu_to_le16(sdev_id);
@@ -1340,7 +1337,7 @@ static int megasas_dcmd_get_properties(MegasasState *s, MegasasCmd *cmd)
 
 static int megasas_cache_flush(MegasasState *s, MegasasCmd *cmd)
 {
-    bdrv_drain_all();
+    blk_drain_all();
     return MFI_STAT_OK;
 }
 
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 022a524074..af7707cf27 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -3,6 +3,7 @@
 #include "hw/scsi/scsi.h"
 #include "block/scsi.h"
 #include "hw/qdev.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "trace.h"
 #include "sysemu/dma.h"
@@ -221,7 +222,7 @@ static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp)
 }
 
 /* handle legacy '-drive if=scsi,...' cmd line args */
-SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
+SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk,
                                       int unit, bool removable, int bootindex,
                                       const char *serial, Error **errp)
 {
@@ -229,7 +230,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
     DeviceState *dev;
     Error *err = NULL;
 
-    driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk";
+    driver = blk_is_sg(blk) ? "scsi-generic" : "scsi-disk";
     dev = qdev_create(&bus->qbus, driver);
     qdev_prop_set_uint32(dev, "scsi-id", unit);
     if (bootindex >= 0) {
@@ -242,7 +243,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
     if (serial && object_property_find(OBJECT(dev), "serial", NULL)) {
         qdev_prop_set_string(dev, "serial", serial);
     }
-    if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) {
+    if (qdev_prop_set_drive(dev, "drive", blk) < 0) {
         error_setg(errp, "Setting drive property failed");
         object_unparent(OBJECT(dev));
         return NULL;
@@ -270,8 +271,8 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, Error **errp)
             continue;
         }
         qemu_opts_loc_restore(dinfo->opts);
-        scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false, -1, NULL,
-                                  &err);
+        scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo),
+                                  unit, false, -1, NULL, &err);
         if (err != NULL) {
             error_report("%s", error_get_pretty(err));
             error_propagate(errp, err);
@@ -1753,7 +1754,7 @@ void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier)
     scsi_req_dequeue(req);
     req->io_canceled = true;
     if (req->aiocb) {
-        bdrv_aio_cancel_async(req->aiocb);
+        blk_aio_cancel_async(req->aiocb);
     }
 }
 
@@ -1767,7 +1768,7 @@ void scsi_req_cancel(SCSIRequest *req)
     scsi_req_dequeue(req);
     req->io_canceled = true;
     if (req->aiocb) {
-        bdrv_aio_cancel(req->aiocb);
+        blk_aio_cancel(req->aiocb);
     }
 }
 
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index ae9e08dd13..010eefd443 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -33,6 +33,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
 #include "hw/scsi/scsi.h"
 #include "block/scsi.h"
 #include "sysemu/sysemu.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "hw/block/block.h"
 #include "sysemu/dma.h"
@@ -111,7 +112,7 @@ static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size)
 
     if (!r->iov.iov_base) {
         r->buflen = size;
-        r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
+        r->iov.iov_base = blk_blockalign(s->qdev.conf.blk, r->buflen);
     }
     r->iov.iov_len = MIN(r->sector_count * 512, r->buflen);
     qemu_iovec_init_external(&r->qiov, &r->iov, 1);
@@ -166,7 +167,7 @@ static void scsi_aio_complete(void *opaque, int ret)
 
     assert(r->req.aiocb != NULL);
     r->req.aiocb = NULL;
-    block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
+    block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
     if (r->req.io_canceled) {
         scsi_req_cancel_complete(&r->req);
         goto done;
@@ -220,9 +221,9 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
     }
 
     if (scsi_is_cmd_fua(&r->req.cmd)) {
-        block_acct_start(bdrv_get_stats(s->qdev.conf.bs), &r->acct, 0,
+        block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
                          BLOCK_ACCT_FLUSH);
-        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
+        r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r);
         return;
     }
 
@@ -239,7 +240,7 @@ static void scsi_dma_complete_noio(void *opaque, int ret)
 
     if (r->req.aiocb != NULL) {
         r->req.aiocb = NULL;
-        block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
+        block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
     }
     if (r->req.io_canceled) {
         scsi_req_cancel_complete(&r->req);
@@ -281,7 +282,7 @@ static void scsi_read_complete(void * opaque, int ret)
 
     assert(r->req.aiocb != NULL);
     r->req.aiocb = NULL;
-    block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
+    block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
     if (r->req.io_canceled) {
         scsi_req_cancel_complete(&r->req);
         goto done;
@@ -313,7 +314,7 @@ static void scsi_do_read(void *opaque, int ret)
 
     if (r->req.aiocb != NULL) {
         r->req.aiocb = NULL;
-        block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
+        block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
     }
     if (r->req.io_canceled) {
         scsi_req_cancel_complete(&r->req);
@@ -330,16 +331,16 @@ static void scsi_do_read(void *opaque, int ret)
     scsi_req_ref(&r->req);
 
     if (r->req.sg) {
-        dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BLOCK_ACCT_READ);
+        dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ);
         r->req.resid -= r->req.sg->size;
-        r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
-                                     scsi_dma_complete, r);
+        r->req.aiocb = dma_blk_read(s->qdev.conf.blk, r->req.sg, r->sector,
+                                    scsi_dma_complete, r);
     } else {
         n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
-        block_acct_start(bdrv_get_stats(s->qdev.conf.bs), &r->acct,
+        block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
                          n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
-        r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
-                                      scsi_read_complete, r);
+        r->req.aiocb = blk_aio_readv(s->qdev.conf.blk, r->sector, &r->qiov, n,
+                                     scsi_read_complete, r);
     }
 
 done:
@@ -379,9 +380,9 @@ static void scsi_read_data(SCSIRequest *req)
     first = !r->started;
     r->started = true;
     if (first && scsi_is_cmd_fua(&r->req.cmd)) {
-        block_acct_start(bdrv_get_stats(s->qdev.conf.bs), &r->acct, 0,
+        block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
                          BLOCK_ACCT_FLUSH);
-        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_do_read, r);
+        r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read, r);
     } else {
         scsi_do_read(r, 0);
     }
@@ -398,7 +399,8 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
 {
     bool is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV);
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-    BlockErrorAction action = bdrv_get_error_action(s->qdev.conf.bs, is_read, error);
+    BlockErrorAction action = blk_get_error_action(s->qdev.conf.blk,
+                                                   is_read, error);
 
     if (action == BLOCK_ERROR_ACTION_REPORT) {
         switch (error) {
@@ -419,7 +421,7 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
             break;
         }
     }
-    bdrv_error_action(s->qdev.conf.bs, action, is_read, error);
+    blk_error_action(s->qdev.conf.blk, action, is_read, error);
     if (action == BLOCK_ERROR_ACTION_STOP) {
         scsi_req_retry(&r->req);
     }
@@ -434,7 +436,7 @@ static void scsi_write_complete(void * opaque, int ret)
 
     if (r->req.aiocb != NULL) {
         r->req.aiocb = NULL;
-        block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
+        block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
     }
     if (r->req.io_canceled) {
         scsi_req_cancel_complete(&r->req);
@@ -502,16 +504,16 @@ static void scsi_write_data(SCSIRequest *req)
     }
 
     if (r->req.sg) {
-        dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BLOCK_ACCT_WRITE);
+        dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE);
         r->req.resid -= r->req.sg->size;
-        r->req.aiocb = dma_bdrv_write(s->qdev.conf.bs, r->req.sg, r->sector,
-                                      scsi_dma_complete, r);
+        r->req.aiocb = dma_blk_write(s->qdev.conf.blk, r->req.sg, r->sector,
+                                     scsi_dma_complete, r);
     } else {
         n = r->qiov.size / 512;
-        block_acct_start(bdrv_get_stats(s->qdev.conf.bs), &r->acct,
+        block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
                          n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE);
-        r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n,
-                                       scsi_write_complete, r);
+        r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, r->sector, &r->qiov, n,
+                                      scsi_write_complete, r);
     }
 }
 
@@ -578,7 +580,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
 
         case 0x83: /* Device identification page, mandatory */
         {
-            const char *str = s->serial ?: bdrv_get_device_name(s->qdev.conf.bs);
+            const char *str = s->serial ?: blk_name(s->qdev.conf.blk);
             int max_len = s->serial ? 20 : 255 - 8;
             int id_len = strlen(str);
 
@@ -739,10 +741,10 @@ static inline bool media_is_dvd(SCSIDiskState *s)
     if (s->qdev.type != TYPE_ROM) {
         return false;
     }
-    if (!bdrv_is_inserted(s->qdev.conf.bs)) {
+    if (!blk_is_inserted(s->qdev.conf.blk)) {
         return false;
     }
-    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+    blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
     return nb_sectors > CD_MAX_SECTORS;
 }
 
@@ -752,10 +754,10 @@ static inline bool media_is_cd(SCSIDiskState *s)
     if (s->qdev.type != TYPE_ROM) {
         return false;
     }
-    if (!bdrv_is_inserted(s->qdev.conf.bs)) {
+    if (!blk_is_inserted(s->qdev.conf.blk)) {
         return false;
     }
-    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+    blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
     return nb_sectors <= CD_MAX_SECTORS;
 }
 
@@ -816,7 +818,7 @@ static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r,
     }
 
     if (format != 0xff) {
-        if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
+        if (s->tray_open || !blk_is_inserted(s->qdev.conf.blk)) {
             scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
             return -1;
         }
@@ -838,7 +840,7 @@ static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r,
         if (layer != 0) {
             goto fail;
         }
-        bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+        blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
 
         outbuf[4] = 1;   /* DVD-ROM, part version 1 */
         outbuf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
@@ -893,7 +895,7 @@ static int scsi_event_status_media(SCSIDiskState *s, uint8_t *outbuf)
     media_status = 0;
     if (s->tray_open) {
         media_status = MS_TRAY_OPEN;
-    } else if (bdrv_is_inserted(s->qdev.conf.bs)) {
+    } else if (blk_is_inserted(s->qdev.conf.blk)) {
         media_status = MS_MEDIA_PRESENT;
     }
 
@@ -1091,7 +1093,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
     case MODE_PAGE_CACHING:
         length = 0x12;
         if (page_control == 1 || /* Changeable Values */
-            bdrv_enable_write_cache(s->qdev.conf.bs)) {
+            blk_enable_write_cache(s->qdev.conf.blk)) {
             p[0] = 4; /* WCE */
         }
         break;
@@ -1172,7 +1174,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
 
     if (s->qdev.type == TYPE_DISK) {
         dev_specific_param = s->features & (1 << SCSI_DISK_F_DPOFUA) ? 0x10 : 0;
-        if (bdrv_is_read_only(s->qdev.conf.bs)) {
+        if (blk_is_read_only(s->qdev.conf.blk)) {
             dev_specific_param |= 0x80; /* Readonly.  */
         }
     } else {
@@ -1194,7 +1196,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
         p += 8;
     }
 
-    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+    blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
     if (!dbd && nb_sectors) {
         if (r->req.cmd.buf[0] == MODE_SENSE) {
             outbuf[3] = 8; /* Block descriptor length  */
@@ -1257,7 +1259,7 @@ static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf)
     msf = req->cmd.buf[1] & 2;
     format = req->cmd.buf[2] & 0xf;
     start_track = req->cmd.buf[6];
-    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+    blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
     DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
     nb_sectors /= s->qdev.blocksize / 512;
     switch (format) {
@@ -1297,14 +1299,14 @@ static int scsi_disk_emulate_start_stop(SCSIDiskReq *r)
     if ((s->features & (1 << SCSI_DISK_F_REMOVABLE)) && loej) {
         if (!start && !s->tray_open && s->tray_locked) {
             scsi_check_condition(r,
-                                 bdrv_is_inserted(s->qdev.conf.bs)
+                                 blk_is_inserted(s->qdev.conf.blk)
                                  ? SENSE_CODE(ILLEGAL_REQ_REMOVAL_PREVENTED)
                                  : SENSE_CODE(NOT_READY_REMOVAL_PREVENTED));
             return -1;
         }
 
         if (s->tray_open != !start) {
-            bdrv_eject(s->qdev.conf.bs, !start);
+            blk_eject(s->qdev.conf.blk, !start);
             s->tray_open = !start;
         }
     }
@@ -1371,7 +1373,7 @@ static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, uint8_t *p)
 {
     switch (page) {
     case MODE_PAGE_CACHING:
-        bdrv_set_enable_write_cache(s->qdev.conf.bs, (p[0] & 4) != 0);
+        blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0);
         break;
 
     default:
@@ -1474,12 +1476,12 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
             return;
         }
     }
-    if (!bdrv_enable_write_cache(s->qdev.conf.bs)) {
+    if (!blk_enable_write_cache(s->qdev.conf.blk)) {
         /* The request is used as the AIO opaque value, so add a ref.  */
         scsi_req_ref(&r->req);
-        block_acct_start(bdrv_get_stats(s->qdev.conf.bs), &r->acct, 0,
+        block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
                          BLOCK_ACCT_FLUSH);
-        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
+        r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r);
         return;
     }
 
@@ -1548,10 +1550,10 @@ static void scsi_unmap_complete(void *opaque, int ret)
             goto done;
         }
 
-        r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs,
-                                        sector_num * (s->qdev.blocksize / 512),
-                                        nb_sectors * (s->qdev.blocksize / 512),
-                                        scsi_unmap_complete, data);
+        r->req.aiocb = blk_aio_discard(s->qdev.conf.blk,
+                                       sector_num * (s->qdev.blocksize / 512),
+                                       nb_sectors * (s->qdev.blocksize / 512),
+                                       scsi_unmap_complete, data);
         data->count--;
         data->inbuf += 16;
         return;
@@ -1589,7 +1591,7 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
         goto invalid_param_len;
     }
 
-    if (bdrv_is_read_only(s->qdev.conf.bs)) {
+    if (blk_is_read_only(s->qdev.conf.blk)) {
         scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
         return;
     }
@@ -1628,7 +1630,7 @@ static void scsi_write_same_complete(void *opaque, int ret)
 
     assert(r->req.aiocb != NULL);
     r->req.aiocb = NULL;
-    block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
+    block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
     if (r->req.io_canceled) {
         scsi_req_cancel_complete(&r->req);
         goto done;
@@ -1644,11 +1646,11 @@ static void scsi_write_same_complete(void *opaque, int ret)
     data->sector += data->iov.iov_len / 512;
     data->iov.iov_len = MIN(data->nb_sectors * 512, data->iov.iov_len);
     if (data->iov.iov_len) {
-        block_acct_start(bdrv_get_stats(s->qdev.conf.bs), &r->acct,
+        block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
                          data->iov.iov_len, BLOCK_ACCT_WRITE);
-        r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, data->sector,
-                                       &data->qiov, data->iov.iov_len / 512,
-                                       scsi_write_same_complete, data);
+        r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector,
+                                      &data->qiov, data->iov.iov_len / 512,
+                                      scsi_write_same_complete, data);
         return;
     }
 
@@ -1675,7 +1677,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
         return;
     }
 
-    if (bdrv_is_read_only(s->qdev.conf.bs)) {
+    if (blk_is_read_only(s->qdev.conf.blk)) {
         scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
         return;
     }
@@ -1689,13 +1691,13 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
 
         /* The request is used as the AIO opaque value, so add a ref.  */
         scsi_req_ref(&r->req);
-        block_acct_start(bdrv_get_stats(s->qdev.conf.bs), &r->acct,
+        block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
                          nb_sectors * s->qdev.blocksize,
                         BLOCK_ACCT_WRITE);
-        r->req.aiocb = bdrv_aio_write_zeroes(s->qdev.conf.bs,
-                                             r->req.cmd.lba * (s->qdev.blocksize / 512),
-                                             nb_sectors * (s->qdev.blocksize / 512),
-                                             flags, scsi_aio_complete, r);
+        r->req.aiocb = blk_aio_write_zeroes(s->qdev.conf.blk,
+                                r->req.cmd.lba * (s->qdev.blocksize / 512),
+                                nb_sectors * (s->qdev.blocksize / 512),
+                                flags, scsi_aio_complete, r);
         return;
     }
 
@@ -1704,7 +1706,8 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
     data->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
     data->nb_sectors = nb_sectors * (s->qdev.blocksize / 512);
     data->iov.iov_len = MIN(data->nb_sectors * 512, SCSI_WRITE_SAME_MAX);
-    data->iov.iov_base = buf = qemu_blockalign(s->qdev.conf.bs, data->iov.iov_len);
+    data->iov.iov_base = buf = blk_blockalign(s->qdev.conf.blk,
+                                              data->iov.iov_len);
     qemu_iovec_init_external(&data->qiov, &data->iov, 1);
 
     for (i = 0; i < data->iov.iov_len; i += s->qdev.blocksize) {
@@ -1712,11 +1715,11 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
     }
 
     scsi_req_ref(&r->req);
-    block_acct_start(bdrv_get_stats(s->qdev.conf.bs), &r->acct,
+    block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
                      data->iov.iov_len, BLOCK_ACCT_WRITE);
-    r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, data->sector,
-                                   &data->qiov, data->iov.iov_len / 512,
-                                   scsi_write_same_complete, data);
+    r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector,
+                                  &data->qiov, data->iov.iov_len / 512,
+                                  scsi_write_same_complete, data);
 }
 
 static void scsi_disk_emulate_write_data(SCSIRequest *req)
@@ -1785,7 +1788,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
         break;
 
     default:
-        if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
+        if (s->tray_open || !blk_is_inserted(s->qdev.conf.blk)) {
             scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
             return 0;
         }
@@ -1806,7 +1809,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
     r->buflen = MAX(4096, req->cmd.xfer);
 
     if (!r->iov.iov_base) {
-        r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
+        r->iov.iov_base = blk_blockalign(s->qdev.conf.blk, r->buflen);
     }
 
     buflen = req->cmd.xfer;
@@ -1814,7 +1817,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
     memset(outbuf, 0, r->buflen);
     switch (req->cmd.buf[0]) {
     case TEST_UNIT_READY:
-        assert(!s->tray_open && bdrv_is_inserted(s->qdev.conf.bs));
+        assert(!s->tray_open && blk_is_inserted(s->qdev.conf.blk));
         break;
     case INQUIRY:
         buflen = scsi_disk_emulate_inquiry(req, outbuf);
@@ -1862,12 +1865,12 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
         break;
     case ALLOW_MEDIUM_REMOVAL:
         s->tray_locked = req->cmd.buf[4] & 1;
-        bdrv_lock_medium(s->qdev.conf.bs, req->cmd.buf[4] & 1);
+        blk_lock_medium(s->qdev.conf.blk, req->cmd.buf[4] & 1);
         break;
     case READ_CAPACITY_10:
         /* The normal LEN field for this command is zero.  */
         memset(outbuf, 0, 8);
-        bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+        blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
         if (!nb_sectors) {
             scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
             return 0;
@@ -1936,7 +1939,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
         if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
             DPRINTF("SAI READ CAPACITY(16)\n");
             memset(outbuf, 0, req->cmd.xfer);
-            bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+            blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
             if (!nb_sectors) {
                 scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
                 return 0;
@@ -1977,9 +1980,9 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
     case SYNCHRONIZE_CACHE:
         /* The request is used as the AIO opaque value, so add a ref.  */
         scsi_req_ref(&r->req);
-        block_acct_start(bdrv_get_stats(s->qdev.conf.bs), &r->acct, 0,
+        block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
                          BLOCK_ACCT_FLUSH);
-        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
+        r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r);
         return 0;
     case SEEK_10:
         DPRINTF("Seek(10) (sector %" PRId64 ")\n", r->req.cmd.lba);
@@ -2053,7 +2056,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
 
     command = buf[0];
 
-    if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
+    if (s->tray_open || !blk_is_inserted(s->qdev.conf.blk)) {
         scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
         return 0;
     }
@@ -2081,7 +2084,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
     case WRITE_VERIFY_10:
     case WRITE_VERIFY_12:
     case WRITE_VERIFY_16:
-        if (bdrv_is_read_only(s->qdev.conf.bs)) {
+        if (blk_is_read_only(s->qdev.conf.blk)) {
             scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
             return 0;
         }
@@ -2124,7 +2127,7 @@ static void scsi_disk_reset(DeviceState *dev)
 
     scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET));
 
-    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+    blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
     nb_sectors /= s->qdev.blocksize / 512;
     if (nb_sectors) {
         nb_sectors--;
@@ -2140,7 +2143,7 @@ static void scsi_unrealize(SCSIDevice *dev, Error **errp)
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
 
     scsi_device_purge_requests(&s->qdev, SENSE_CODE(NO_SENSE));
-    blockdev_mark_auto_del(s->qdev.conf.bs);
+    blockdev_mark_auto_del(s->qdev.conf.blk);
 }
 
 static void scsi_disk_resize_cb(void *opaque)
@@ -2223,13 +2226,13 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
     Error *err = NULL;
 
-    if (!s->qdev.conf.bs) {
+    if (!s->qdev.conf.blk) {
         error_setg(errp, "drive property not set");
         return;
     }
 
     if (!(s->features & (1 << SCSI_DISK_F_REMOVABLE)) &&
-        !bdrv_is_inserted(s->qdev.conf.bs)) {
+        !blk_is_inserted(s->qdev.conf.blk)) {
         error_setg(errp, "Device needs media, but drive is empty");
         return;
     }
@@ -2255,20 +2258,20 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
         s->vendor = g_strdup("QEMU");
     }
 
-    if (bdrv_is_sg(s->qdev.conf.bs)) {
+    if (blk_is_sg(s->qdev.conf.blk)) {
         error_setg(errp, "unwanted /dev/sg*");
         return;
     }
 
     if ((s->features & (1 << SCSI_DISK_F_REMOVABLE)) &&
             !(s->features & (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS))) {
-        bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_disk_removable_block_ops, s);
+        blk_set_dev_ops(s->qdev.conf.blk, &scsi_disk_removable_block_ops, s);
     } else {
-        bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_disk_block_ops, s);
+        blk_set_dev_ops(s->qdev.conf.blk, &scsi_disk_block_ops, s);
     }
-    bdrv_set_guest_block_size(s->qdev.conf.bs, s->qdev.blocksize);
+    blk_set_guest_block_size(s->qdev.conf.blk, s->qdev.blocksize);
 
-    bdrv_iostatus_enable(s->qdev.conf.bs);
+    blk_iostatus_enable(s->qdev.conf.blk);
 }
 
 static void scsi_hd_realize(SCSIDevice *dev, Error **errp)
@@ -2299,15 +2302,15 @@ static void scsi_disk_realize(SCSIDevice *dev, Error **errp)
     DriveInfo *dinfo;
     Error *local_err = NULL;
 
-    if (!dev->conf.bs) {
+    if (!dev->conf.blk) {
         scsi_realize(dev, &local_err);
         assert(local_err);
         error_propagate(errp, local_err);
         return;
     }
 
-    dinfo = drive_get_by_blockdev(dev->conf.bs);
-    if (dinfo->media_cd) {
+    dinfo = blk_legacy_dinfo(dev->conf.blk);
+    if (dinfo && dinfo->media_cd) {
         scsi_cd_realize(dev, errp);
     } else {
         scsi_hd_realize(dev, errp);
@@ -2406,7 +2409,6 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
 #ifdef __linux__
 static int get_device_type(SCSIDiskState *s)
 {
-    BlockDriverState *bdrv = s->qdev.conf.bs;
     uint8_t cmd[16];
     uint8_t buf[36];
     uint8_t sensebuf[8];
@@ -2429,7 +2431,7 @@ static int get_device_type(SCSIDiskState *s)
     io_header.sbp = sensebuf;
     io_header.timeout = 6000; /* XXX */
 
-    ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
+    ret = blk_ioctl(s->qdev.conf.blk, SG_IO, &io_header);
     if (ret < 0 || io_header.driver_status || io_header.host_status) {
         return -1;
     }
@@ -2446,13 +2448,13 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
     int sg_version;
     int rc;
 
-    if (!s->qdev.conf.bs) {
+    if (!s->qdev.conf.blk) {
         error_setg(errp, "drive property not set");
         return;
     }
 
     /* check we are using a driver managing SG_IO (version 3 and after) */
-    rc = bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version);
+    rc = blk_ioctl(s->qdev.conf.blk, SG_GET_VERSION_NUM, &sg_version);
     if (rc < 0) {
         error_setg(errp, "cannot get SG_IO version number: %s.  "
                      "Is this a SCSI device?",
@@ -2511,7 +2513,7 @@ static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
          * ones (such as WRITE SAME or EXTENDED COPY, etc.).  So, without
          * O_DIRECT everything must go through SG_IO.
          */
-        if (!(bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE)) {
+        if (!(blk_get_flags(s->qdev.conf.blk) & BDRV_O_NOCACHE)) {
             break;
         }
 
@@ -2660,7 +2662,7 @@ static const TypeInfo scsi_cd_info = {
 
 #ifdef __linux__
 static Property scsi_block_properties[] = {
-    DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.bs),
+    DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 84a1d5bfe3..f2e53afd09 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -14,6 +14,7 @@
 #include "qemu-common.h"
 #include "qemu/error-report.h"
 #include "hw/scsi/scsi.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 
 #ifdef __linux__
@@ -141,9 +142,9 @@ done:
     scsi_req_unref(&r->req);
 }
 
-static int execute_command(BlockDriverState *bdrv,
+static int execute_command(BlockBackend *blk,
                            SCSIGenericReq *r, int direction,
-			   BlockDriverCompletionFunc *complete)
+                           BlockCompletionFunc *complete)
 {
     r->io_header.interface_id = 'S';
     r->io_header.dxfer_direction = direction;
@@ -157,7 +158,7 @@ static int execute_command(BlockDriverState *bdrv,
     r->io_header.usr_ptr = r;
     r->io_header.flags |= SG_FLAG_DIRECT_IO;
 
-    r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
+    r->req.aiocb = blk_aio_ioctl(blk, SG_IO, &r->io_header, complete, r);
     if (r->req.aiocb == NULL) {
         return -EIO;
     }
@@ -193,7 +194,7 @@ static void scsi_read_complete(void * opaque, int ret)
             s->blocksize = ldl_be_p(&r->buf[8]);
             s->max_lba = ldq_be_p(&r->buf[0]);
         }
-        bdrv_set_guest_block_size(s->conf.bs, s->blocksize);
+        blk_set_guest_block_size(s->conf.blk, s->blocksize);
 
         scsi_req_data(&r->req, len);
         scsi_req_unref(&r->req);
@@ -216,7 +217,8 @@ static void scsi_read_data(SCSIRequest *req)
         return;
     }
 
-    ret = execute_command(s->conf.bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
+    ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV,
+                          scsi_read_complete);
     if (ret < 0) {
         scsi_command_complete(r, ret);
     }
@@ -260,7 +262,7 @@ static void scsi_write_data(SCSIRequest *req)
 
     /* The request is used as the AIO opaque value, so add a ref.  */
     scsi_req_ref(&r->req);
-    ret = execute_command(s->conf.bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
+    ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete);
     if (ret < 0) {
         scsi_command_complete(r, ret);
     }
@@ -302,7 +304,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
         r->buf = NULL;
         /* The request is used as the AIO opaque value, so add a ref.  */
         scsi_req_ref(&r->req);
-        ret = execute_command(s->conf.bs, r, SG_DXFER_NONE, scsi_command_complete);
+        ret = execute_command(s->conf.blk, r, SG_DXFER_NONE,
+                              scsi_command_complete);
         if (ret < 0) {
             scsi_command_complete(r, ret);
             return 0;
@@ -327,7 +330,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
     }
 }
 
-static int get_stream_blocksize(BlockDriverState *bdrv)
+static int get_stream_blocksize(BlockBackend *blk)
 {
     uint8_t cmd[6];
     uint8_t buf[12];
@@ -351,7 +354,7 @@ static int get_stream_blocksize(BlockDriverState *bdrv)
     io_header.sbp = sensebuf;
     io_header.timeout = 6000; /* XXX */
 
-    ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
+    ret = blk_ioctl(blk, SG_IO, &io_header);
     if (ret < 0 || io_header.driver_status || io_header.host_status) {
         return -1;
     }
@@ -368,7 +371,7 @@ static void scsi_generic_reset(DeviceState *dev)
 static void scsi_unrealize(SCSIDevice *s, Error **errp)
 {
     scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE));
-    blockdev_mark_auto_del(s->conf.bs);
+    blockdev_mark_auto_del(s->conf.blk);
 }
 
 static void scsi_generic_realize(SCSIDevice *s, Error **errp)
@@ -377,22 +380,22 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp)
     int sg_version;
     struct sg_scsi_id scsiid;
 
-    if (!s->conf.bs) {
+    if (!s->conf.blk) {
         error_setg(errp, "drive property not set");
         return;
     }
 
-    if (bdrv_get_on_error(s->conf.bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
+    if (blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
         error_setg(errp, "Device doesn't support drive option werror");
         return;
     }
-    if (bdrv_get_on_error(s->conf.bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {
+    if (blk_get_on_error(s->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
         error_setg(errp, "Device doesn't support drive option rerror");
         return;
     }
 
     /* check we are using a driver managing SG_IO (version 3 and after */
-    rc = bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version);
+    rc = blk_ioctl(s->conf.blk, SG_GET_VERSION_NUM, &sg_version);
     if (rc < 0) {
         error_setg(errp, "cannot get SG_IO version number: %s.  "
                          "Is this a SCSI device?",
@@ -405,7 +408,7 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp)
     }
 
     /* get LUN of the /dev/sg? */
-    if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) {
+    if (blk_ioctl(s->conf.blk, SG_GET_SCSI_ID, &scsiid)) {
         error_setg(errp, "SG_GET_SCSI_ID ioctl failed");
         return;
     }
@@ -416,7 +419,7 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp)
 
     switch (s->type) {
     case TYPE_TAPE:
-        s->blocksize = get_stream_blocksize(s->conf.bs);
+        s->blocksize = get_stream_blocksize(s->conf.blk);
         if (s->blocksize == -1) {
             s->blocksize = 0;
         }
@@ -459,7 +462,7 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
 }
 
 static Property scsi_generic_properties[] = {
-    DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.bs),
+    DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index b778e051f8..48dcfd2852 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -13,6 +13,7 @@
 
 #include "hw/virtio/virtio-scsi.h"
 #include "qemu/error-report.h"
+#include "sysemu/block-backend.h"
 #include <hw/scsi/scsi.h>
 #include <block/scsi.h>
 #include <hw/virtio/virtio-bus.h>
@@ -205,7 +206,7 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
         aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL);
     }
 
-    bdrv_drain_all(); /* ensure there are no in-flight requests */
+    blk_drain_all(); /* ensure there are no in-flight requests */
 
     aio_context_release(s->ctx);
 
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 8547ea0475..a1725b8f48 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -16,6 +16,7 @@
 #include "hw/virtio/virtio-scsi.h"
 #include "qemu/error-report.h"
 #include "qemu/iov.h"
+#include "sysemu/block-backend.h"
 #include <hw/scsi/scsi.h>
 #include <block/scsi.h>
 #include <hw/virtio/virtio-bus.h>
@@ -236,9 +237,9 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
     int target;
     int ret = 0;
 
-    if (s->dataplane_started && bdrv_get_aio_context(d->conf.bs) != s->ctx) {
+    if (s->dataplane_started && blk_get_aio_context(d->conf.blk) != s->ctx) {
         aio_context_acquire(s->ctx);
-        bdrv_set_aio_context(d->conf.bs, s->ctx);
+        blk_set_aio_context(d->conf.blk, s->ctx);
         aio_context_release(s->ctx);
     }
     /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE".  */
@@ -522,9 +523,9 @@ bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
         virtio_scsi_complete_cmd_req(req);
         return false;
     }
-    if (s->dataplane_started && bdrv_get_aio_context(d->conf.bs) != s->ctx) {
+    if (s->dataplane_started && blk_get_aio_context(d->conf.blk) != s->ctx) {
         aio_context_acquire(s->ctx);
-        bdrv_set_aio_context(d->conf.bs, s->ctx);
+        blk_set_aio_context(d->conf.blk, s->ctx);
         aio_context_release(s->ctx);
     }
     req->sreq = scsi_req_new(d, req->req.cmd.tag,
@@ -539,7 +540,7 @@ bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
         return false;
     }
     scsi_req_ref(req->sreq);
-    bdrv_io_plug(d->conf.bs);
+    blk_io_plug(d->conf.blk);
     return true;
 }
 
@@ -549,7 +550,7 @@ void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
     if (scsi_req_enqueue(sreq)) {
         scsi_req_continue(sreq);
     }
-    bdrv_io_unplug(sreq->dev->conf.bs);
+    blk_io_unplug(sreq->dev->conf.blk);
     scsi_req_unref(sreq);
 }
 
@@ -832,7 +833,7 @@ static void virtio_scsi_migration_state_changed(Notifier *notifier, void *data)
         if (s->dataplane_started) {
             return;
         }
-        bdrv_drain_all(); /* complete in-flight non-dataplane requests */
+        blk_drain_all(); /* complete in-flight non-dataplane requests */
         s->dataplane_disabled = false;
     }
 }
diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c
index 2a40f92732..9661eafac8 100644
--- a/hw/sd/milkymist-memcard.c
+++ b/hw/sd/milkymist-memcard.c
@@ -26,6 +26,7 @@
 #include "sysemu/sysemu.h"
 #include "trace.h"
 #include "qemu/error-report.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "hw/sd.h"
 
@@ -252,14 +253,16 @@ static int milkymist_memcard_init(SysBusDevice *dev)
 {
     MilkymistMemcardState *s = MILKYMIST_MEMCARD(dev);
     DriveInfo *dinfo;
+    BlockBackend *blk;
 
     dinfo = drive_get_next(IF_SD);
-    s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false);
+    blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
+    s->card = sd_init(blk, false);
     if (s->card == NULL) {
         return -1;
     }
 
-    s->enabled = dinfo ? bdrv_is_inserted(dinfo->bdrv) : 0;
+    s->enabled = blk && blk_is_inserted(blk);
 
     memory_region_init_io(&s->regs_region, OBJECT(s), &memcard_mmio_ops, s,
             "milkymist-memcard", R_MAX * 4);
diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c
index 6c92149c04..86c477d720 100644
--- a/hw/sd/omap_mmc.c
+++ b/hw/sd/omap_mmc.c
@@ -574,7 +574,7 @@ static void omap_mmc_cover_cb(void *opaque, int line, int level)
 
 struct omap_mmc_s *omap_mmc_init(hwaddr base,
                 MemoryRegion *sysmem,
-                BlockDriverState *bd,
+                BlockBackend *blk,
                 qemu_irq irq, qemu_irq dma[], omap_clk clk)
 {
     struct omap_mmc_s *s = (struct omap_mmc_s *)
@@ -592,7 +592,7 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base,
     memory_region_add_subregion(sysmem, base, &s->iomem);
 
     /* Instantiate the storage */
-    s->card = sd_init(bd, false);
+    s->card = sd_init(blk, false);
     if (s->card == NULL) {
         exit(1);
     }
@@ -601,7 +601,7 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base,
 }
 
 struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
-                BlockDriverState *bd, qemu_irq irq, qemu_irq dma[],
+                BlockBackend *blk, qemu_irq irq, qemu_irq dma[],
                 omap_clk fclk, omap_clk iclk)
 {
     struct omap_mmc_s *s = (struct omap_mmc_s *)
@@ -620,7 +620,7 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
     omap_l4_attach(ta, 0, &s->iomem);
 
     /* Instantiate the storage */
-    s->card = sd_init(bd, false);
+    s->card = sd_init(blk, false);
     if (s->card == NULL) {
         exit(1);
     }
diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c
index 462558b76d..e704b6e97a 100644
--- a/hw/sd/pl181.c
+++ b/hw/sd/pl181.c
@@ -7,6 +7,7 @@
  * This code is licensed under the GPL.
  */
 
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "hw/sysbus.h"
 #include "hw/sd.h"
@@ -490,7 +491,7 @@ static int pl181_init(SysBusDevice *sbd)
     sysbus_init_irq(sbd, &s->irq[1]);
     qdev_init_gpio_out(dev, s->cardstatus, 2);
     dinfo = drive_get_next(IF_SD);
-    s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false);
+    s->card = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, false);
     if (s->card == NULL) {
         return -1;
     }
diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c
index b9d8b1a3e1..ac3ab39bea 100644
--- a/hw/sd/pxa2xx_mmci.c
+++ b/hw/sd/pxa2xx_mmci.c
@@ -523,7 +523,7 @@ static int pxa2xx_mmci_load(QEMUFile *f, void *opaque, int version_id)
 
 PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
                 hwaddr base,
-                BlockDriverState *bd, qemu_irq irq,
+                BlockBackend *blk, qemu_irq irq,
                 qemu_irq rx_dma, qemu_irq tx_dma)
 {
     PXA2xxMMCIState *s;
@@ -538,7 +538,7 @@ PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
     memory_region_add_subregion(sysmem, base, &s->iomem);
 
     /* Instantiate the actual storage */
-    s->card = sd_init(bd, false);
+    s->card = sd_init(blk, false);
     if (s->card == NULL) {
         exit(1);
     }
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 5efe8c1af7..f955265f74 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -30,7 +30,7 @@
  */
 
 #include "hw/hw.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "hw/sd.h"
 #include "qemu/bitmap.h"
 
@@ -110,7 +110,7 @@ struct SDState {
     uint8_t data[512];
     qemu_irq readonly_cb;
     qemu_irq inserted_cb;
-    BlockDriverState *bdrv;
+    BlockBackend *blk;
     uint8_t *buf;
 
     bool enable;
@@ -389,13 +389,13 @@ static inline uint64_t sd_addr_to_wpnum(uint64_t addr)
     return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
 }
 
-static void sd_reset(SDState *sd, BlockDriverState *bdrv)
+static void sd_reset(SDState *sd, BlockBackend *blk)
 {
     uint64_t size;
     uint64_t sect;
 
-    if (bdrv) {
-        bdrv_get_geometry(bdrv, &sect);
+    if (blk) {
+        blk_get_geometry(blk, &sect);
     } else {
         sect = 0;
     }
@@ -412,11 +412,11 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv)
     sd_set_cardstatus(sd);
     sd_set_sdstatus(sd);
 
-    sd->bdrv = bdrv;
+    sd->blk = blk;
 
     if (sd->wp_groups)
         g_free(sd->wp_groups);
-    sd->wp_switch = bdrv ? bdrv_is_read_only(bdrv) : false;
+    sd->wp_switch = blk ? blk_is_read_only(blk) : false;
     sd->wpgrps_size = sect;
     sd->wp_groups = bitmap_new(sd->wpgrps_size);
     memset(sd->function_group, 0, sizeof(sd->function_group));
@@ -432,9 +432,9 @@ static void sd_cardchange(void *opaque, bool load)
 {
     SDState *sd = opaque;
 
-    qemu_set_irq(sd->inserted_cb, bdrv_is_inserted(sd->bdrv));
-    if (bdrv_is_inserted(sd->bdrv)) {
-        sd_reset(sd, sd->bdrv);
+    qemu_set_irq(sd->inserted_cb, blk_is_inserted(sd->blk));
+    if (blk_is_inserted(sd->blk)) {
+        sd_reset(sd, sd->blk);
         qemu_set_irq(sd->readonly_cb, sd->wp_switch);
     }
 }
@@ -479,23 +479,23 @@ static const VMStateDescription sd_vmstate = {
    whether card should be in SSI or MMC/SD mode.  It is also up to the
    board to ensure that ssi transfers only occur when the chip select
    is asserted.  */
-SDState *sd_init(BlockDriverState *bs, bool is_spi)
+SDState *sd_init(BlockBackend *blk, bool is_spi)
 {
     SDState *sd;
 
-    if (bs && bdrv_is_read_only(bs)) {
+    if (blk && blk_is_read_only(blk)) {
         fprintf(stderr, "sd_init: Cannot use read-only drive\n");
         return NULL;
     }
 
     sd = (SDState *) g_malloc0(sizeof(SDState));
-    sd->buf = qemu_blockalign(bs, 512);
+    sd->buf = blk_blockalign(blk, 512);
     sd->spi = is_spi;
     sd->enable = true;
-    sd_reset(sd, bs);
-    if (sd->bdrv) {
-        bdrv_attach_dev_nofail(sd->bdrv, sd);
-        bdrv_set_dev_ops(sd->bdrv, &sd_block_ops, sd);
+    sd_reset(sd, blk);
+    if (sd->blk) {
+        blk_attach_dev_nofail(sd->blk, sd);
+        blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
     }
     vmstate_register(NULL, -1, &sd_vmstate, sd);
     return sd;
@@ -505,8 +505,8 @@ void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
 {
     sd->readonly_cb = readonly;
     sd->inserted_cb = insert;
-    qemu_set_irq(readonly, sd->bdrv ? bdrv_is_read_only(sd->bdrv) : 0);
-    qemu_set_irq(insert, sd->bdrv ? bdrv_is_inserted(sd->bdrv) : 0);
+    qemu_set_irq(readonly, sd->blk ? blk_is_read_only(sd->blk) : 0);
+    qemu_set_irq(insert, sd->blk ? blk_is_inserted(sd->blk) : 0);
 }
 
 static void sd_erase(SDState *sd)
@@ -680,7 +680,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
 
         default:
             sd->state = sd_idle_state;
-            sd_reset(sd, sd->bdrv);
+            sd_reset(sd, sd->blk);
             return sd->spi ? sd_r1 : sd_r0;
         }
         break;
@@ -1347,7 +1347,7 @@ int sd_do_command(SDState *sd, SDRequest *req,
     sd_rsp_type_t rtype;
     int rsplen;
 
-    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable) {
+    if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) {
         return 0;
     }
 
@@ -1456,7 +1456,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
 
     DPRINTF("sd_blk_read: addr = 0x%08llx, len = %d\n",
             (unsigned long long) addr, len);
-    if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
+    if (!sd->blk || blk_read(sd->blk, addr >> 9, sd->buf, 1) < 0) {
         fprintf(stderr, "sd_blk_read: read error on host side\n");
         return;
     }
@@ -1464,7 +1464,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
     if (end > (addr & ~511) + 512) {
         memcpy(sd->data, sd->buf + (addr & 511), 512 - (addr & 511));
 
-        if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
+        if (blk_read(sd->blk, end >> 9, sd->buf, 1) < 0) {
             fprintf(stderr, "sd_blk_read: read error on host side\n");
             return;
         }
@@ -1478,29 +1478,29 @@ static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
     uint64_t end = addr + len;
 
     if ((addr & 511) || len < 512)
-        if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
+        if (!sd->blk || blk_read(sd->blk, addr >> 9, sd->buf, 1) < 0) {
             fprintf(stderr, "sd_blk_write: read error on host side\n");
             return;
         }
 
     if (end > (addr & ~511) + 512) {
         memcpy(sd->buf + (addr & 511), sd->data, 512 - (addr & 511));
-        if (bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
+        if (blk_write(sd->blk, addr >> 9, sd->buf, 1) < 0) {
             fprintf(stderr, "sd_blk_write: write error on host side\n");
             return;
         }
 
-        if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
+        if (blk_read(sd->blk, end >> 9, sd->buf, 1) < 0) {
             fprintf(stderr, "sd_blk_write: read error on host side\n");
             return;
         }
         memcpy(sd->buf, sd->data + 512 - (addr & 511), end & 511);
-        if (bdrv_write(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
+        if (blk_write(sd->blk, end >> 9, sd->buf, 1) < 0) {
             fprintf(stderr, "sd_blk_write: write error on host side\n");
         }
     } else {
         memcpy(sd->buf + (addr & 511), sd->data, len);
-        if (!sd->bdrv || bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
+        if (!sd->blk || blk_write(sd->blk, addr >> 9, sd->buf, 1) < 0) {
             fprintf(stderr, "sd_blk_write: write error on host side\n");
         }
     }
@@ -1515,7 +1515,7 @@ void sd_write_data(SDState *sd, uint8_t value)
 {
     int i;
 
-    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
+    if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable)
         return;
 
     if (sd->state != sd_receivingdata_state) {
@@ -1641,7 +1641,7 @@ uint8_t sd_read_data(SDState *sd)
     uint8_t ret;
     int io_len;
 
-    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
+    if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable)
         return 0x00;
 
     if (sd->state != sd_sendingdata_state) {
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index f9fe700add..b38005003c 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -23,6 +23,7 @@
  */
 
 #include "hw/hw.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "sysemu/dma.h"
 #include "qemu/timer.h"
@@ -1165,7 +1166,7 @@ static void sdhci_initfn(Object *obj)
     DriveInfo *di;
 
     di = drive_get_next(IF_SD);
-    s->card = sd_init(di ? di->bdrv : NULL, false);
+    s->card = sd_init(di ? blk_by_legacy_dinfo(di) : NULL, false);
     if (s->card == NULL) {
         exit(1);
     }
diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c
index b012e57f64..a71fbca714 100644
--- a/hw/sd/ssi-sd.c
+++ b/hw/sd/ssi-sd.c
@@ -10,6 +10,7 @@
  * GNU GPL, version 2 or (at your option) any later version.
  */
 
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "hw/ssi.h"
 #include "hw/sd.h"
@@ -255,7 +256,7 @@ static int ssi_sd_init(SSISlave *d)
 
     s->mode = SSI_SD_CMD;
     dinfo = drive_get_next(IF_SD);
-    s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, true);
+    s->sd = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, true);
     if (s->sd == NULL) {
         return -1;
     }
diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c
index 321379ece7..12f44d28f0 100644
--- a/hw/sh4/r2d.c
+++ b/hw/sh4/r2d.c
@@ -36,7 +36,7 @@
 #include "hw/loader.h"
 #include "hw/usb.h"
 #include "hw/block/flash.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 
 #define FLASH_BASE 0x00000000
@@ -290,8 +290,8 @@ static void r2d_init(MachineState *machine)
     /* onboard flash memory */
     dinfo = drive_get(IF_PFLASH, 0, 0);
     pflash_cfi02_register(0x0, NULL, "r2d.flash", FLASH_SIZE,
-                          dinfo ? dinfo->bdrv : NULL, (16 * 1024),
-                          FLASH_SIZE >> 16,
+                          dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+                          (16 * 1024), FLASH_SIZE >> 16,
                           1, 4, 0x0000, 0x0000, 0x0000, 0x0000,
                           0x555, 0x2aa, 0);
 
diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c
index 2feab35585..8273199f11 100644
--- a/hw/sparc/sun4m.c
+++ b/hw/sparc/sun4m.c
@@ -40,7 +40,7 @@
 #include "hw/empty_slot.h"
 #include "hw/loader.h"
 #include "elf.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "trace.h"
 
 /*
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index 871a0ea246..f42112c1b2 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -38,7 +38,7 @@
 #include "hw/ide.h"
 #include "hw/loader.h"
 #include "elf.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 
 //#define DEBUG_IRQ
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c
index 82747ee259..c0e7cd738a 100644
--- a/hw/tpm/tpm_tis.c
+++ b/hw/tpm/tpm_tis.c
@@ -21,7 +21,7 @@
 
 #include "sysemu/tpm_backend.h"
 #include "tpm_int.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "hw/hw.h"
 #include "hw/i386/pc.h"
diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c
index 605de86302..eeb4922c4c 100644
--- a/hw/tricore/tricore_testboard.c
+++ b/hw/tricore/tricore_testboard.c
@@ -24,7 +24,7 @@
 #include "sysemu/sysemu.h"
 #include "hw/boards.h"
 #include "hw/loader.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "exec/address-spaces.h"
 #include "hw/block/flash.h"
 #include "elf.h"
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index 467ec86da6..507c9663c5 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -104,6 +104,37 @@ static const USBDescIface desc_iface_mouse = {
     },
 };
 
+static const USBDescIface desc_iface_mouse2 = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 1,
+    .bInterfaceClass               = USB_CLASS_HID,
+    .bInterfaceSubClass            = 0x01, /* boot */
+    .bInterfaceProtocol            = 0x02,
+    .ndesc                         = 1,
+    .descs = (USBDescOther[]) {
+        {
+            /* HID descriptor */
+            .data = (uint8_t[]) {
+                0x09,          /*  u8  bLength */
+                USB_DT_HID,    /*  u8  bDescriptorType */
+                0x01, 0x00,    /*  u16 HID_class */
+                0x00,          /*  u8  country_code */
+                0x01,          /*  u8  num_descriptors */
+                USB_DT_REPORT, /*  u8  type: Report */
+                52, 0,         /*  u16 len */
+            },
+        },
+    },
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | 0x01,
+            .bmAttributes          = USB_ENDPOINT_XFER_INT,
+            .wMaxPacketSize        = 4,
+            .bInterval             = 7, /* 2 ^ (8-1) * 125 usecs = 8 ms */
+        },
+    },
+};
+
 static const USBDescIface desc_iface_tablet = {
     .bInterfaceNumber              = 0,
     .bNumEndpoints                 = 1,
@@ -195,6 +226,37 @@ static const USBDescIface desc_iface_keyboard = {
     },
 };
 
+static const USBDescIface desc_iface_keyboard2 = {
+    .bInterfaceNumber              = 0,
+    .bNumEndpoints                 = 1,
+    .bInterfaceClass               = USB_CLASS_HID,
+    .bInterfaceSubClass            = 0x01, /* boot */
+    .bInterfaceProtocol            = 0x01, /* keyboard */
+    .ndesc                         = 1,
+    .descs = (USBDescOther[]) {
+        {
+            /* HID descriptor */
+            .data = (uint8_t[]) {
+                0x09,          /*  u8  bLength */
+                USB_DT_HID,    /*  u8  bDescriptorType */
+                0x11, 0x01,    /*  u16 HID_class */
+                0x00,          /*  u8  country_code */
+                0x01,          /*  u8  num_descriptors */
+                USB_DT_REPORT, /*  u8  type: Report */
+                0x3f, 0,       /*  u16 len */
+            },
+        },
+    },
+    .eps = (USBDescEndpoint[]) {
+        {
+            .bEndpointAddress      = USB_DIR_IN | 0x01,
+            .bmAttributes          = USB_ENDPOINT_XFER_INT,
+            .wMaxPacketSize        = 8,
+            .bInterval             = 7, /* 2 ^ (8-1) * 125 usecs = 8 ms */
+        },
+    },
+};
+
 static const USBDescDevice desc_device_mouse = {
     .bcdUSB                        = 0x0100,
     .bMaxPacketSize0               = 8,
@@ -212,6 +274,23 @@ static const USBDescDevice desc_device_mouse = {
     },
 };
 
+static const USBDescDevice desc_device_mouse2 = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = STR_CONFIG_MOUSE,
+            .bmAttributes          = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP,
+            .bMaxPower             = 50,
+            .nif = 1,
+            .ifs = &desc_iface_mouse2,
+        },
+    },
+};
+
 static const USBDescDevice desc_device_tablet = {
     .bcdUSB                        = 0x0100,
     .bMaxPacketSize0               = 8,
@@ -263,6 +342,23 @@ static const USBDescDevice desc_device_keyboard = {
     },
 };
 
+static const USBDescDevice desc_device_keyboard2 = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = STR_CONFIG_KEYBOARD,
+            .bmAttributes          = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP,
+            .bMaxPower             = 50,
+            .nif = 1,
+            .ifs = &desc_iface_keyboard2,
+        },
+    },
+};
+
 static const USBDescMSOS desc_msos_suspend = {
     .SelectiveSuspendEnabled = true,
 };
@@ -281,6 +377,21 @@ static const USBDesc desc_mouse = {
     .msos = &desc_msos_suspend,
 };
 
+static const USBDesc desc_mouse2 = {
+    .id = {
+        .idVendor          = 0x0627,
+        .idProduct         = 0x0001,
+        .bcdDevice         = 0,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT_MOUSE,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device_mouse,
+    .high = &desc_device_mouse2,
+    .str  = desc_strings,
+    .msos = &desc_msos_suspend,
+};
+
 static const USBDesc desc_tablet = {
     .id = {
         .idVendor          = 0x0627,
@@ -324,6 +435,21 @@ static const USBDesc desc_keyboard = {
     .msos = &desc_msos_suspend,
 };
 
+static const USBDesc desc_keyboard2 = {
+    .id = {
+        .idVendor          = 0x0627,
+        .idProduct         = 0x0001,
+        .bcdDevice         = 0,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT_KEYBOARD,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device_keyboard,
+    .high = &desc_device_keyboard2,
+    .str  = desc_strings,
+    .msos = &desc_msos_suspend,
+};
+
 static const uint8_t qemu_mouse_hid_report_descriptor[] = {
     0x05, 0x01,		/* Usage Page (Generic Desktop) */
     0x09, 0x02,		/* Usage (Mouse) */
@@ -566,9 +692,26 @@ static void usb_hid_handle_destroy(USBDevice *dev)
     hid_free(&us->hid);
 }
 
-static void usb_hid_initfn(USBDevice *dev, int kind)
+static void usb_hid_initfn(USBDevice *dev, int kind,
+                           const USBDesc *usb1, const USBDesc *usb2,
+                           Error **errp)
 {
     USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+    switch (us->usb_version) {
+    case 1:
+        dev->usb_desc = usb1;
+        break;
+    case 2:
+        dev->usb_desc = usb2;
+        break;
+    default:
+        dev->usb_desc = NULL;
+    }
+    if (!dev->usb_desc) {
+        error_setg(errp, "Invalid usb version %d for usb hid device",
+                   us->usb_version);
+        return;
+    }
 
     if (dev->serial) {
         usb_desc_set_string(dev, STR_SERIALNUMBER, dev->serial);
@@ -583,32 +726,18 @@ static void usb_hid_initfn(USBDevice *dev, int kind)
 
 static void usb_tablet_realize(USBDevice *dev, Error **errp)
 {
-    USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
 
-    switch (us->usb_version) {
-    case 1:
-        dev->usb_desc = &desc_tablet;
-        break;
-    case 2:
-        dev->usb_desc = &desc_tablet2;
-        break;
-    default:
-        error_setg(errp, "Invalid usb version %d for usb-tablet "
-                   "(must be 1 or 2)", us->usb_version);
-        return;
-    }
-
-    usb_hid_initfn(dev, HID_TABLET);
+    usb_hid_initfn(dev, HID_TABLET, &desc_tablet, &desc_tablet2, errp);
 }
 
 static void usb_mouse_realize(USBDevice *dev, Error **errp)
 {
-    usb_hid_initfn(dev, HID_MOUSE);
+    usb_hid_initfn(dev, HID_MOUSE, &desc_mouse, &desc_mouse2, errp);
 }
 
 static void usb_keyboard_realize(USBDevice *dev, Error **errp)
 {
-    usb_hid_initfn(dev, HID_KEYBOARD);
+    usb_hid_initfn(dev, HID_KEYBOARD, &desc_keyboard, &desc_keyboard2, errp);
 }
 
 static int usb_ptr_post_load(void *opaque, int version_id)
@@ -682,6 +811,11 @@ static const TypeInfo usb_tablet_info = {
     .class_init    = usb_tablet_class_initfn,
 };
 
+static Property usb_mouse_properties[] = {
+        DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2),
+        DEFINE_PROP_END_OF_LIST(),
+};
+
 static void usb_mouse_class_initfn(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -690,8 +824,8 @@ static void usb_mouse_class_initfn(ObjectClass *klass, void *data)
     usb_hid_class_initfn(klass, data);
     uc->realize        = usb_mouse_realize;
     uc->product_desc   = "QEMU USB Mouse";
-    uc->usb_desc       = &desc_mouse;
     dc->vmsd = &vmstate_usb_ptr;
+    dc->props = usb_mouse_properties;
     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 }
 
@@ -703,6 +837,7 @@ static const TypeInfo usb_mouse_info = {
 };
 
 static Property usb_keyboard_properties[] = {
+        DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2),
         DEFINE_PROP_STRING("display", USBHIDState, display),
         DEFINE_PROP_END_OF_LIST(),
 };
@@ -715,7 +850,6 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
     usb_hid_class_initfn(klass, data);
     uc->realize        = usb_keyboard_realize;
     uc->product_desc   = "QEMU USB Keyboard";
-    uc->usb_desc       = &desc_keyboard;
     dc->vmsd = &vmstate_usb_kbd;
     dc->props = usb_keyboard_properties;
     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index 5bfc72ca45..4539733e42 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -16,6 +16,7 @@
 #include "ui/console.h"
 #include "monitor/monitor.h"
 #include "sysemu/sysemu.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 #include "qapi/visitor.h"
 
@@ -600,11 +601,11 @@ static const struct SCSIBusInfo usb_msd_scsi_info_bot = {
 static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
 {
     MSDState *s = DO_UPCAST(MSDState, dev, dev);
-    BlockDriverState *bs = s->conf.bs;
+    BlockBackend *blk = s->conf.blk;
     SCSIDevice *scsi_dev;
     Error *err = NULL;
 
-    if (!bs) {
+    if (!blk) {
         error_setg(errp, "drive property not set");
         return;
     }
@@ -620,14 +621,14 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
      *
      * The hack is probably a bad idea.
      */
-    bdrv_detach_dev(bs, &s->dev.qdev);
-    s->conf.bs = NULL;
+    blk_detach_dev(blk, &s->dev.qdev);
+    s->conf.blk = NULL;
 
     usb_desc_create_serial(dev);
     usb_desc_init(dev);
     scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
                  &usb_msd_scsi_info_storage, NULL);
-    scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable,
+    scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable,
                                          s->conf.bootindex, dev->serial,
                                          &err);
     if (!scsi_dev) {
@@ -637,9 +638,10 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
     usb_msd_handle_reset(dev);
     s->scsi_dev = scsi_dev;
 
-    if (bdrv_key_required(bs)) {
+    if (bdrv_key_required(blk_bs(blk))) {
         if (cur_mon) {
-            monitor_read_bdrv_key_start(cur_mon, bs, usb_msd_password_cb, s);
+            monitor_read_bdrv_key_start(cur_mon, blk_bs(blk),
+                                        usb_msd_password_cb, s);
             s->dev.auto_attach = 0;
         } else {
             autostart = 0;
@@ -707,7 +709,8 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
     if (!dev) {
         return NULL;
     }
-    if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
+    if (qdev_prop_set_drive(&dev->qdev, "drive",
+                            blk_by_legacy_dinfo(dinfo)) < 0) {
         object_unparent(OBJECT(dev));
         return NULL;
     }
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index c556367cb1..a27c9d37fa 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1608,12 +1608,6 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
                 "data might be lost\n");
     }
 
-    uint8_t ep = epid>>1;
-
-    if (epid & 1) {
-        ep |= 0x80;
-    }
-
     if (!xhci->slots[slotid-1].uport ||
         !xhci->slots[slotid-1].uport->dev ||
         !xhci->slots[slotid-1].uport->dev->attached) {
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index ae7fef96d5..542fb9e279 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -29,7 +29,7 @@
 #include "hw/pci/msix.h"
 #include "hw/loader.h"
 #include "sysemu/kvm.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "virtio-pci.h"
 #include "qemu/range.h"
 #include "hw/virtio/virtio-bus.h"
diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c
index fa998eff04..e138dbbec9 100644
--- a/hw/xen/xen_devconfig.c
+++ b/hw/xen/xen_devconfig.c
@@ -1,4 +1,5 @@
 #include "hw/xen/xen_backend.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/blockdev.h"
 
 /* ------------------------------------------------------------- */
diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c
index 31500643a2..2e545d2418 100644
--- a/hw/xenpv/xen_machine_pv.c
+++ b/hw/xenpv/xen_machine_pv.c
@@ -26,7 +26,7 @@
 #include "hw/boards.h"
 #include "hw/xen/xen_backend.h"
 #include "xen_domainbuild.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 
 static void xen_init_pv(MachineState *machine)
 {
diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c
index ed06ff7956..0100c3115b 100644
--- a/hw/xtensa/xtfpga.c
+++ b/hw/xtensa/xtfpga.c
@@ -35,7 +35,7 @@
 #include "net/net.h"
 #include "hw/sysbus.h"
 #include "hw/block/flash.h"
-#include "sysemu/blockdev.h"
+#include "sysemu/block-backend.h"
 #include "sysemu/char.h"
 #include "sysemu/device_tree.h"
 #include "qemu/error-report.h"
@@ -230,7 +230,8 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
     if (dinfo) {
         flash = pflash_cfi01_register(board->flash_base,
                 NULL, "lx60.io.flash", board->flash_size,
-                dinfo->bdrv, board->flash_sector_size,
+                blk_by_legacy_dinfo(dinfo),
+                board->flash_sector_size,
                 board->flash_size / board->flash_sector_size,
                 4, 0x0000, 0x0000, 0x0000, 0x0000, be);
         if (flash == NULL) {
diff --git a/include/block/aio.h b/include/block/aio.h
index 156272177f..6bf0e0456a 100644
--- a/include/block/aio.h
+++ b/include/block/aio.h
@@ -22,25 +22,25 @@
 #include "qemu/rfifolock.h"
 #include "qemu/timer.h"
 
-typedef struct BlockDriverAIOCB BlockDriverAIOCB;
-typedef void BlockDriverCompletionFunc(void *opaque, int ret);
+typedef struct BlockAIOCB BlockAIOCB;
+typedef void BlockCompletionFunc(void *opaque, int ret);
 
 typedef struct AIOCBInfo {
-    void (*cancel_async)(BlockDriverAIOCB *acb);
-    AioContext *(*get_aio_context)(BlockDriverAIOCB *acb);
+    void (*cancel_async)(BlockAIOCB *acb);
+    AioContext *(*get_aio_context)(BlockAIOCB *acb);
     size_t aiocb_size;
 } AIOCBInfo;
 
-struct BlockDriverAIOCB {
+struct BlockAIOCB {
     const AIOCBInfo *aiocb_info;
     BlockDriverState *bs;
-    BlockDriverCompletionFunc *cb;
+    BlockCompletionFunc *cb;
     void *opaque;
     int refcnt;
 };
 
 void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs,
-                   BlockDriverCompletionFunc *cb, void *opaque);
+                   BlockCompletionFunc *cb, void *opaque);
 void qemu_aio_unref(void *p);
 void qemu_aio_ref(void *p);
 
diff --git a/include/block/block.h b/include/block/block.h
index 3318f0dfbe..c9ec0ab386 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -48,41 +48,6 @@ typedef struct BlockFragInfo {
     uint64_t compressed_clusters;
 } BlockFragInfo;
 
-/* Callbacks for block device models */
-typedef struct BlockDevOps {
-    /*
-     * Runs when virtual media changed (monitor commands eject, change)
-     * Argument load is true on load and false on eject.
-     * Beware: doesn't run when a host device's physical media
-     * changes.  Sure would be useful if it did.
-     * Device models with removable media must implement this callback.
-     */
-    void (*change_media_cb)(void *opaque, bool load);
-    /*
-     * Runs when an eject request is issued from the monitor, the tray
-     * is closed, and the medium is locked.
-     * Device models that do not implement is_medium_locked will not need
-     * this callback.  Device models that can lock the medium or tray might
-     * want to implement the callback and unlock the tray when "force" is
-     * true, even if they do not support eject requests.
-     */
-    void (*eject_request_cb)(void *opaque, bool force);
-    /*
-     * Is the virtual tray open?
-     * Device models implement this only when the device has a tray.
-     */
-    bool (*is_tray_open)(void *opaque);
-    /*
-     * Is the virtual medium locked into the device?
-     * Device models implement this only when device has such a lock.
-     */
-    bool (*is_medium_locked)(void *opaque);
-    /*
-     * Runs when the size changed (e.g. monitor command block_resize)
-     */
-    void (*resize_cb)(void *opaque);
-} BlockDevOps;
-
 typedef enum {
     BDRV_REQ_COPY_ON_READ = 0x1,
     BDRV_REQ_ZERO_WRITE   = 0x2,
@@ -204,7 +169,8 @@ BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
 int bdrv_create(BlockDriver *drv, const char* filename,
                 QemuOpts *opts, Error **errp);
 int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp);
-BlockDriverState *bdrv_new(const char *device_name, Error **errp);
+BlockDriverState *bdrv_new_root(void);
+BlockDriverState *bdrv_new(void);
 void bdrv_make_anon(BlockDriverState *bs);
 void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old);
 void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
@@ -229,16 +195,6 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state);
 void bdrv_reopen_abort(BDRVReopenState *reopen_state);
 void bdrv_close(BlockDriverState *bs);
 void bdrv_add_close_notifier(BlockDriverState *bs, Notifier *notify);
-int bdrv_attach_dev(BlockDriverState *bs, void *dev);
-void bdrv_attach_dev_nofail(BlockDriverState *bs, void *dev);
-void bdrv_detach_dev(BlockDriverState *bs, void *dev);
-void *bdrv_get_attached_dev(BlockDriverState *bs);
-void bdrv_set_dev_ops(BlockDriverState *bs, const BlockDevOps *ops,
-                      void *opaque);
-void bdrv_dev_eject_request(BlockDriverState *bs, bool force);
-bool bdrv_dev_has_removable_media(BlockDriverState *bs);
-bool bdrv_dev_is_tray_open(BlockDriverState *bs);
-bool bdrv_dev_is_medium_locked(BlockDriverState *bs);
 int bdrv_read(BlockDriverState *bs, int64_t sector_num,
               uint8_t *buf, int nb_sectors);
 int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num,
@@ -247,9 +203,9 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num,
                const uint8_t *buf, int nb_sectors);
 int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num,
                int nb_sectors, BdrvRequestFlags flags);
-BlockDriverAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs, int64_t sector_num,
-                                        int nb_sectors, BdrvRequestFlags flags,
-                                        BlockDriverCompletionFunc *cb, void *opaque);
+BlockAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs, int64_t sector_num,
+                                  int nb_sectors, BdrvRequestFlags flags,
+                                  BlockCompletionFunc *cb, void *opaque);
 int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags);
 int bdrv_pread(BlockDriverState *bs, int64_t offset,
                void *buf, int count);
@@ -325,19 +281,19 @@ BlockDriverState *check_to_replace_node(const char *node_name, Error **errp);
 /* async block I/O */
 typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
                                      int sector_num);
-BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
-                                 QEMUIOVector *iov, int nb_sectors,
-                                 BlockDriverCompletionFunc *cb, void *opaque);
-BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
-                                  QEMUIOVector *iov, int nb_sectors,
-                                  BlockDriverCompletionFunc *cb, void *opaque);
-BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
-                                 BlockDriverCompletionFunc *cb, void *opaque);
-BlockDriverAIOCB *bdrv_aio_discard(BlockDriverState *bs,
-                                   int64_t sector_num, int nb_sectors,
-                                   BlockDriverCompletionFunc *cb, void *opaque);
-void bdrv_aio_cancel(BlockDriverAIOCB *acb);
-void bdrv_aio_cancel_async(BlockDriverAIOCB *acb);
+BlockAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
+                           QEMUIOVector *iov, int nb_sectors,
+                           BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
+                            QEMUIOVector *iov, int nb_sectors,
+                            BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs,
+                           BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *bdrv_aio_discard(BlockDriverState *bs,
+                             int64_t sector_num, int nb_sectors,
+                             BlockCompletionFunc *cb, void *opaque);
+void bdrv_aio_cancel(BlockAIOCB *acb);
+void bdrv_aio_cancel_async(BlockAIOCB *acb);
 
 typedef struct BlockRequest {
     /* Fields to be filled by multiwrite caller */
@@ -345,7 +301,7 @@ typedef struct BlockRequest {
     int nb_sectors;
     int flags;
     QEMUIOVector *qiov;
-    BlockDriverCompletionFunc *cb;
+    BlockCompletionFunc *cb;
     void *opaque;
 
     /* Filled by multiwrite implementation */
@@ -357,9 +313,9 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs,
 
 /* sg packet commands */
 int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf);
-BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
+BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
         unsigned long int req, void *buf,
-        BlockDriverCompletionFunc *cb, void *opaque);
+        BlockCompletionFunc *cb, void *opaque);
 
 /* Invalidate any cached metadata used by image formats */
 void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp);
@@ -410,15 +366,13 @@ BlockDriverState *bdrv_lookup_bs(const char *device,
                                  Error **errp);
 bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base);
 BlockDriverState *bdrv_next(BlockDriverState *bs);
-void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs),
-                  void *opaque);
 int bdrv_is_encrypted(BlockDriverState *bs);
 int bdrv_key_required(BlockDriverState *bs);
 int bdrv_set_key(BlockDriverState *bs, const char *key);
 int bdrv_query_missing_keys(void);
 void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
                          void *opaque);
-const char *bdrv_get_device_name(BlockDriverState *bs);
+const char *bdrv_get_device_name(const BlockDriverState *bs);
 int bdrv_get_flags(BlockDriverState *bs);
 int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
                           const uint8_t *buf, int nb_sectors);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 8d86a6c1eb..8898c6c51b 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -128,17 +128,17 @@ struct BlockDriver {
     void (*bdrv_refresh_filename)(BlockDriverState *bs);
 
     /* aio */
-    BlockDriverAIOCB *(*bdrv_aio_readv)(BlockDriverState *bs,
+    BlockAIOCB *(*bdrv_aio_readv)(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque);
-    BlockDriverAIOCB *(*bdrv_aio_writev)(BlockDriverState *bs,
+        BlockCompletionFunc *cb, void *opaque);
+    BlockAIOCB *(*bdrv_aio_writev)(BlockDriverState *bs,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque);
-    BlockDriverAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs,
-        BlockDriverCompletionFunc *cb, void *opaque);
-    BlockDriverAIOCB *(*bdrv_aio_discard)(BlockDriverState *bs,
+        BlockCompletionFunc *cb, void *opaque);
+    BlockAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs,
+        BlockCompletionFunc *cb, void *opaque);
+    BlockAIOCB *(*bdrv_aio_discard)(BlockDriverState *bs,
         int64_t sector_num, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque);
+        BlockCompletionFunc *cb, void *opaque);
 
     int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs,
         int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
@@ -218,9 +218,9 @@ struct BlockDriver {
 
     /* to control generic scsi devices */
     int (*bdrv_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf);
-    BlockDriverAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs,
+    BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs,
         unsigned long int req, void *buf,
-        BlockDriverCompletionFunc *cb, void *opaque);
+        BlockCompletionFunc *cb, void *opaque);
 
     /* List of options for creating images, terminated by name == NULL */
     QemuOptsList *create_opts;
@@ -324,10 +324,7 @@ struct BlockDriverState {
     BlockDriver *drv; /* NULL means no media */
     void *opaque;
 
-    void *dev;                  /* attached device model, if any */
-    /* TODO change to DeviceState when all users are qdevified */
-    const BlockDevOps *dev_ops;
-    void *dev_opaque;
+    BlockBackend *blk;          /* owning backend, if any */
 
     AioContext *aio_context; /* event loop used for fd handlers, timers, etc */
     /* long-running tasks intended to always use the same AioContext as this
@@ -390,8 +387,6 @@ struct BlockDriverState {
     char node_name[32];
     /* element of the list of named nodes building the graph */
     QTAILQ_ENTRY(BlockDriverState) node_list;
-    /* Device name is the name associated with the "drive" the guest sees */
-    char device_name[32];
     /* element of the list of "drives" the guest sees */
     QTAILQ_ENTRY(BlockDriverState) device_list;
     QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps;
@@ -501,7 +496,7 @@ int is_windows_drive(const char *filename);
  */
 void stream_start(BlockDriverState *bs, BlockDriverState *base,
                   const char *base_id, int64_t speed, BlockdevOnError on_error,
-                  BlockDriverCompletionFunc *cb,
+                  BlockCompletionFunc *cb,
                   void *opaque, Error **errp);
 
 /**
@@ -519,7 +514,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
  */
 void commit_start(BlockDriverState *bs, BlockDriverState *base,
                  BlockDriverState *top, int64_t speed,
-                 BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
+                 BlockdevOnError on_error, BlockCompletionFunc *cb,
                  void *opaque, const char *backing_file_str, Error **errp);
 /**
  * commit_active_start:
@@ -535,7 +530,7 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
 void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
                          int64_t speed,
                          BlockdevOnError on_error,
-                         BlockDriverCompletionFunc *cb,
+                         BlockCompletionFunc *cb,
                          void *opaque, Error **errp);
 /*
  * mirror_start:
@@ -563,7 +558,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
                   int64_t speed, int64_t granularity, int64_t buf_size,
                   MirrorSyncMode mode, BlockdevOnError on_source_error,
                   BlockdevOnError on_target_error,
-                  BlockDriverCompletionFunc *cb,
+                  BlockCompletionFunc *cb,
                   void *opaque, Error **errp);
 
 /*
@@ -584,7 +579,14 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
                   int64_t speed, MirrorSyncMode sync_mode,
                   BlockdevOnError on_source_error,
                   BlockdevOnError on_target_error,
-                  BlockDriverCompletionFunc *cb, void *opaque,
+                  BlockCompletionFunc *cb, void *opaque,
                   Error **errp);
 
+void blk_dev_change_media_cb(BlockBackend *blk, bool load);
+bool blk_dev_has_removable_media(BlockBackend *blk);
+void blk_dev_eject_request(BlockBackend *blk, bool force);
+bool blk_dev_is_tray_open(BlockBackend *blk);
+bool blk_dev_is_medium_locked(BlockBackend *blk);
+void blk_dev_resize_cb(BlockBackend *blk);
+
 #endif /* BLOCK_INT_H */
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index 60aa835ec0..acb399f87d 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -104,7 +104,7 @@ struct BlockJob {
     int64_t speed;
 
     /** The completion function that will be called when the job completes.  */
-    BlockDriverCompletionFunc *cb;
+    BlockCompletionFunc *cb;
 
     /** Block other operations when block job is running */
     Error *blocker;
@@ -132,7 +132,7 @@ struct BlockJob {
  * called from a wrapper that is specific to the job type.
  */
 void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
-                       int64_t speed, BlockDriverCompletionFunc *cb,
+                       int64_t speed, BlockCompletionFunc *cb,
                        void *opaque, Error **errp);
 
 /**
diff --git a/include/block/qapi.h b/include/block/qapi.h
index 03745464d6..168d788521 100644
--- a/include/block/qapi.h
+++ b/include/block/qapi.h
@@ -36,9 +36,6 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs,
 void bdrv_query_image_info(BlockDriverState *bs,
                            ImageInfo **p_info,
                            Error **errp);
-void bdrv_query_info(BlockDriverState *bs,
-                     BlockInfo **p_info,
-                     Error **errp);
 
 void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
                         QEMUSnapshotInfo *sn);
diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h
index 472375227a..42eb5e8421 100644
--- a/include/block/thread-pool.h
+++ b/include/block/thread-pool.h
@@ -27,9 +27,9 @@ typedef struct ThreadPool ThreadPool;
 ThreadPool *thread_pool_new(struct AioContext *ctx);
 void thread_pool_free(ThreadPool *pool);
 
-BlockDriverAIOCB *thread_pool_submit_aio(ThreadPool *pool,
+BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,
         ThreadPoolFunc *func, void *arg,
-        BlockDriverCompletionFunc *cb, void *opaque);
+        BlockCompletionFunc *cb, void *opaque);
 int coroutine_fn thread_pool_submit_co(ThreadPool *pool,
         ThreadPoolFunc *func, void *arg);
 void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg);
diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index 77177c5317..e32e25d547 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -374,6 +374,8 @@ float32 float32_min(float32, float32 STATUS_PARAM);
 float32 float32_max(float32, float32 STATUS_PARAM);
 float32 float32_minnum(float32, float32 STATUS_PARAM);
 float32 float32_maxnum(float32, float32 STATUS_PARAM);
+float32 float32_minnummag(float32, float32 STATUS_PARAM);
+float32 float32_maxnummag(float32, float32 STATUS_PARAM);
 int float32_is_quiet_nan( float32 );
 int float32_is_signaling_nan( float32 );
 float32 float32_maybe_silence_nan( float32 );
@@ -484,6 +486,8 @@ float64 float64_min(float64, float64 STATUS_PARAM);
 float64 float64_max(float64, float64 STATUS_PARAM);
 float64 float64_minnum(float64, float64 STATUS_PARAM);
 float64 float64_maxnum(float64, float64 STATUS_PARAM);
+float64 float64_minnummag(float64, float64 STATUS_PARAM);
+float64 float64_maxnummag(float64, float64 STATUS_PARAM);
 int float64_is_quiet_nan( float64 a );
 int float64_is_signaling_nan( float64 );
 float64 float64_maybe_silence_nan( float64 );
diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h
index b9655ee397..0ad5fb883c 100644
--- a/include/hw/arm/omap.h
+++ b/include/hw/arm/omap.h
@@ -755,10 +755,10 @@ void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip);
 struct omap_mmc_s;
 struct omap_mmc_s *omap_mmc_init(hwaddr base,
                 MemoryRegion *sysmem,
-                BlockDriverState *bd,
+                BlockBackend *blk,
                 qemu_irq irq, qemu_irq dma[], omap_clk clk);
 struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
-                BlockDriverState *bd, qemu_irq irq, qemu_irq dma[],
+                BlockBackend *blk, qemu_irq irq, qemu_irq dma[],
                 omap_clk fclk, omap_clk iclk);
 void omap_mmc_reset(struct omap_mmc_s *s);
 void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover);
diff --git a/include/hw/arm/pxa.h b/include/hw/arm/pxa.h
index c5079067bb..259b852495 100644
--- a/include/hw/arm/pxa.h
+++ b/include/hw/arm/pxa.h
@@ -87,7 +87,7 @@ void pxa2xx_lcdc_oritentation(void *opaque, int angle);
 typedef struct PXA2xxMMCIState PXA2xxMMCIState;
 PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
                 hwaddr base,
-                BlockDriverState *bd, qemu_irq irq,
+                BlockBackend *blk, qemu_irq irq,
                 qemu_irq rx_dma, qemu_irq tx_dma);
 void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly,
                 qemu_irq coverswitch);
diff --git a/include/hw/block/block.h b/include/hw/block/block.h
index 867a226643..0d0ce9a467 100644
--- a/include/hw/block/block.h
+++ b/include/hw/block/block.h
@@ -17,7 +17,7 @@
 /* Configuration */
 
 typedef struct BlockConf {
-    BlockDriverState *bs;
+    BlockBackend *blk;
     uint16_t physical_block_size;
     uint16_t logical_block_size;
     uint16_t min_io_size;
@@ -42,7 +42,7 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
 }
 
 #define DEFINE_BLOCK_PROPERTIES(_state, _conf)                          \
-    DEFINE_PROP_DRIVE("drive", _state, _conf.bs),                       \
+    DEFINE_PROP_DRIVE("drive", _state, _conf.blk),                      \
     DEFINE_PROP_BLOCKSIZE("logical_block_size", _state,                 \
                           _conf.logical_block_size, 512),               \
     DEFINE_PROP_BLOCKSIZE("physical_block_size", _state,                \
@@ -66,7 +66,7 @@ void blkconf_geometry(BlockConf *conf, int *trans,
 
 /* Hard disk geometry */
 
-void hd_geometry_guess(BlockDriverState *bs,
+void hd_geometry_guess(BlockBackend *blk,
                        uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs,
                        int *ptrans);
 int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs);
diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h
index 920d7596e3..50ccbbcf13 100644
--- a/include/hw/block/flash.h
+++ b/include/hw/block/flash.h
@@ -11,7 +11,7 @@ typedef struct pflash_t pflash_t;
 pflash_t *pflash_cfi01_register(hwaddr base,
                                 DeviceState *qdev, const char *name,
                                 hwaddr size,
-                                BlockDriverState *bs,
+                                BlockBackend *blk,
                                 uint32_t sector_len, int nb_blocs, int width,
                                 uint16_t id0, uint16_t id1,
                                 uint16_t id2, uint16_t id3, int be);
@@ -20,7 +20,7 @@ pflash_t *pflash_cfi01_register(hwaddr base,
 pflash_t *pflash_cfi02_register(hwaddr base,
                                 DeviceState *qdev, const char *name,
                                 hwaddr size,
-                                BlockDriverState *bs, uint32_t sector_len,
+                                BlockBackend *blk, uint32_t sector_len,
                                 int nb_blocs, int nb_mappings, int width,
                                 uint16_t id0, uint16_t id1,
                                 uint16_t id2, uint16_t id3,
@@ -30,7 +30,7 @@ pflash_t *pflash_cfi02_register(hwaddr base,
 MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl);
 
 /* nand.c */
-DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id);
+DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id);
 void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale,
                   uint8_t ce, uint8_t wp, uint8_t gnd);
 void nand_getpins(DeviceState *dev, int *rb);
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 1c2602efd7..c4ee520e20 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -314,6 +314,14 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
             .driver   = "secondary-vga",\
             .property = "qemu-extended-regs",\
             .value    = "off",\
+        },{\
+            .driver   = "usb-mouse",\
+            .property = "usb_version",\
+            .value    = stringify(1),\
+        },{\
+            .driver   = "usb-kbd",\
+            .property = "usb_version",\
+            .value    = stringify(1),\
         }
 
 #define PC_COMPAT_2_0 \
diff --git a/include/hw/isa/pc87312.h b/include/hw/isa/pc87312.h
index befc8bdc62..bf74470d40 100644
--- a/include/hw/isa/pc87312.h
+++ b/include/hw/isa/pc87312.h
@@ -47,13 +47,10 @@ typedef struct PC87312State {
 
     struct {
         ISADevice *dev;
-        BlockDriverState *drive[2];
-        uint32_t base;
     } fdc;
 
     struct {
         ISADevice *dev;
-        uint32_t base;
     } ide;
 
     MemoryRegion io;
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index ae56ee59a4..070006c715 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -141,7 +141,7 @@ extern PropertyInfo qdev_prop_arraylen;
 #define DEFINE_PROP_VLAN(_n, _s, _f)             \
     DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NICPeers)
 #define DEFINE_PROP_DRIVE(_n, _s, _f) \
-    DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockDriverState *)
+    DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockBackend *)
 #define DEFINE_PROP_MACADDR(_n, _s, _f)         \
     DEFINE_PROP(_n, _s, _f, qdev_prop_macaddr, MACAddr)
 #define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \
@@ -168,8 +168,10 @@ void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value);
 void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value);
 void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value);
 void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value);
-int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value) QEMU_WARN_UNUSED_RESULT;
-void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverState *value);
+int qdev_prop_set_drive(DeviceState *dev, const char *name,
+                        BlockBackend *value) QEMU_WARN_UNUSED_RESULT;
+void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name,
+                                BlockBackend *value);
 void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value);
 void qdev_prop_set_enum(DeviceState *dev, const char *name, int value);
 /* FIXME: Remove opaque pointer properties.  */
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index caaa3201ce..6bc841ab8b 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -2,7 +2,7 @@
 #define QEMU_HW_SCSI_H
 
 #include "hw/qdev.h"
-#include "block/block.h"
+#include "qemu/typedefs.h"
 #include "hw/block/block.h"
 #include "sysemu/sysemu.h"
 #include "qemu/notify.h"
@@ -68,7 +68,7 @@ struct SCSIRequest {
     bool              io_canceled;
     bool              retry;
     bool              dma_started;
-    BlockDriverAIOCB  *aiocb;
+    BlockAIOCB        *aiocb;
     QEMUSGList        *sg;
     QTAILQ_ENTRY(SCSIRequest) next;
 };
@@ -173,7 +173,7 @@ static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
     return DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus);
 }
 
-SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
+SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk,
                                       int unit, bool removable, int bootindex,
                                       const char *serial, Error **errp);
 void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, Error **errp);
diff --git a/include/hw/sd.h b/include/hw/sd.h
index d9b97e4466..79adb5bb48 100644
--- a/include/hw/sd.h
+++ b/include/hw/sd.h
@@ -68,7 +68,7 @@ typedef struct {
 
 typedef struct SDState SDState;
 
-SDState *sd_init(BlockDriverState *bs, bool is_spi);
+SDState *sd_init(BlockBackend *bs, bool is_spi);
 int sd_do_command(SDState *sd, SDRequest *req,
                   uint8_t *response);
 void sd_write_data(SDState *sd, uint8_t value);
diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h
index f3853f2b75..3979dc41af 100644
--- a/include/hw/virtio/virtio-blk.h
+++ b/include/hw/virtio/virtio-blk.h
@@ -17,8 +17,7 @@
 #include "hw/virtio/virtio.h"
 #include "hw/block/block.h"
 #include "sysemu/iothread.h"
-#include "block/block.h"
-#include "block/accounting.h"
+#include "sysemu/block-backend.h"
 
 #define TYPE_VIRTIO_BLK "virtio-blk-device"
 #define VIRTIO_BLK(obj) \
@@ -121,12 +120,11 @@ struct VirtIOBlockDataPlane;
 struct VirtIOBlockReq;
 typedef struct VirtIOBlock {
     VirtIODevice parent_obj;
-    BlockDriverState *bs;
+    BlockBackend *blk;
     VirtQueue *vq;
     void *rq;
     QEMUBH *bh;
-    BlockConf *conf;
-    VirtIOBlkConf blk;
+    VirtIOBlkConf conf;
     unsigned short sector_mask;
     bool original_wce;
     VMChangeStateEntry *change;
@@ -160,6 +158,6 @@ int virtio_blk_handle_scsi_req(VirtIOBlock *blk,
 
 void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb);
 
-void virtio_submit_multiwrite(BlockDriverState *bs, MultiReqBuffer *mrb);
+void virtio_submit_multiwrite(BlockBackend *blk, MultiReqBuffer *mrb);
 
 #endif
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 78a5fc8197..47606d04ad 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -27,10 +27,10 @@ int monitor_suspend(Monitor *mon);
 void monitor_resume(Monitor *mon);
 
 int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
-                                BlockDriverCompletionFunc *completion_cb,
+                                BlockCompletionFunc *completion_cb,
                                 void *opaque);
 int monitor_read_block_device_key(Monitor *mon, const char *device,
-                                  BlockDriverCompletionFunc *completion_cb,
+                                  BlockCompletionFunc *completion_cb,
                                   void *opaque);
 
 int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp);
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 168a4086d8..34751778a3 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -36,6 +36,7 @@ typedef struct MachineState MachineState;
 typedef struct NICInfo NICInfo;
 typedef struct HCIInfo HCIInfo;
 typedef struct AudioState AudioState;
+typedef struct BlockBackend BlockBackend;
 typedef struct BlockDriverState BlockDriverState;
 typedef struct DriveInfo DriveInfo;
 typedef struct DisplayState DisplayState;
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
new file mode 100644
index 0000000000..52d13c1c0e
--- /dev/null
+++ b/include/sysemu/block-backend.h
@@ -0,0 +1,142 @@
+/*
+ * QEMU Block backends
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * Authors:
+ *  Markus Armbruster <armbru@redhat.com>,
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1
+ * or later.  See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef BLOCK_BACKEND_H
+#define BLOCK_BACKEND_H
+
+#include "qemu/typedefs.h"
+#include "qapi/error.h"
+
+/*
+ * TODO Have to include block/block.h for a bunch of block layer
+ * types.  Unfortunately, this pulls in the whole BlockDriverState
+ * API, which we don't want used by many BlockBackend users.  Some of
+ * the types belong here, and the rest should be split into a common
+ * header and one for the BlockDriverState API.
+ */
+#include "block/block.h"
+
+/* Callbacks for block device models */
+typedef struct BlockDevOps {
+    /*
+     * Runs when virtual media changed (monitor commands eject, change)
+     * Argument load is true on load and false on eject.
+     * Beware: doesn't run when a host device's physical media
+     * changes.  Sure would be useful if it did.
+     * Device models with removable media must implement this callback.
+     */
+    void (*change_media_cb)(void *opaque, bool load);
+    /*
+     * Runs when an eject request is issued from the monitor, the tray
+     * is closed, and the medium is locked.
+     * Device models that do not implement is_medium_locked will not need
+     * this callback.  Device models that can lock the medium or tray might
+     * want to implement the callback and unlock the tray when "force" is
+     * true, even if they do not support eject requests.
+     */
+    void (*eject_request_cb)(void *opaque, bool force);
+    /*
+     * Is the virtual tray open?
+     * Device models implement this only when the device has a tray.
+     */
+    bool (*is_tray_open)(void *opaque);
+    /*
+     * Is the virtual medium locked into the device?
+     * Device models implement this only when device has such a lock.
+     */
+    bool (*is_medium_locked)(void *opaque);
+    /*
+     * Runs when the size changed (e.g. monitor command block_resize)
+     */
+    void (*resize_cb)(void *opaque);
+} BlockDevOps;
+
+BlockBackend *blk_new(const char *name, Error **errp);
+BlockBackend *blk_new_with_bs(const char *name, Error **errp);
+void blk_ref(BlockBackend *blk);
+void blk_unref(BlockBackend *blk);
+const char *blk_name(BlockBackend *blk);
+BlockBackend *blk_by_name(const char *name);
+BlockBackend *blk_next(BlockBackend *blk);
+
+BlockDriverState *blk_bs(BlockBackend *blk);
+
+void blk_hide_on_behalf_of_do_drive_del(BlockBackend *blk);
+
+void blk_iostatus_enable(BlockBackend *blk);
+int blk_attach_dev(BlockBackend *blk, void *dev);
+void blk_attach_dev_nofail(BlockBackend *blk, void *dev);
+void blk_detach_dev(BlockBackend *blk, void *dev);
+void *blk_get_attached_dev(BlockBackend *blk);
+void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque);
+int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf,
+             int nb_sectors);
+int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf,
+                         int nb_sectors);
+int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf,
+              int nb_sectors);
+BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num,
+                                 int nb_sectors, BdrvRequestFlags flags,
+                                 BlockCompletionFunc *cb, void *opaque);
+int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count);
+int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count);
+int64_t blk_getlength(BlockBackend *blk);
+void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr);
+BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num,
+                          QEMUIOVector *iov, int nb_sectors,
+                          BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num,
+                           QEMUIOVector *iov, int nb_sectors,
+                           BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *blk_aio_flush(BlockBackend *blk,
+                          BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *blk_aio_discard(BlockBackend *blk,
+                            int64_t sector_num, int nb_sectors,
+                            BlockCompletionFunc *cb, void *opaque);
+void blk_aio_cancel(BlockAIOCB *acb);
+void blk_aio_cancel_async(BlockAIOCB *acb);
+int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs);
+int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf);
+BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
+                          BlockCompletionFunc *cb, void *opaque);
+int blk_flush(BlockBackend *blk);
+int blk_flush_all(void);
+void blk_drain_all(void);
+BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read);
+BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read,
+                                      int error);
+void blk_error_action(BlockBackend *blk, BlockErrorAction action,
+                      bool is_read, int error);
+int blk_is_read_only(BlockBackend *blk);
+int blk_is_sg(BlockBackend *blk);
+int blk_enable_write_cache(BlockBackend *blk);
+void blk_set_enable_write_cache(BlockBackend *blk, bool wce);
+int blk_is_inserted(BlockBackend *blk);
+void blk_lock_medium(BlockBackend *blk, bool locked);
+void blk_eject(BlockBackend *blk, bool eject_flag);
+int blk_get_flags(BlockBackend *blk);
+void blk_set_guest_block_size(BlockBackend *blk, int align);
+void *blk_blockalign(BlockBackend *blk, size_t size);
+bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp);
+void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason);
+void blk_op_block_all(BlockBackend *blk, Error *reason);
+void blk_op_unblock_all(BlockBackend *blk, Error *reason);
+AioContext *blk_get_aio_context(BlockBackend *blk);
+void blk_set_aio_context(BlockBackend *blk, AioContext *new_context);
+void blk_io_plug(BlockBackend *blk);
+void blk_io_unplug(BlockBackend *blk);
+BlockAcctStats *blk_get_stats(BlockBackend *blk);
+
+void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
+                  BlockCompletionFunc *cb, void *opaque);
+
+#endif
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
index 09716de809..09d1e30b34 100644
--- a/include/sysemu/blockdev.h
+++ b/include/sysemu/blockdev.h
@@ -14,8 +14,8 @@
 #include "qapi/error.h"
 #include "qemu/queue.h"
 
-void blockdev_mark_auto_del(BlockDriverState *bs);
-void blockdev_auto_del(BlockDriverState *bs);
+void blockdev_mark_auto_del(BlockBackend *blk);
+void blockdev_auto_del(BlockBackend *blk);
 
 typedef enum {
     IF_DEFAULT = -1,            /* for use with drive_add() only */
@@ -30,14 +30,11 @@ typedef enum {
 } BlockInterfaceType;
 
 struct DriveInfo {
-    BlockDriverState *bdrv;
-    char *id;
     const char *devaddr;
     BlockInterfaceType type;
     int bus;
     int unit;
     int auto_del;               /* see blockdev_mark_auto_del() */
-    bool enable_auto_del;       /* Only for legacy drive_new() */
     bool is_default;            /* Added by default_drive() ?  */
     int media_cd;
     int cyls, heads, secs, trans;
@@ -46,6 +43,10 @@ struct DriveInfo {
     QTAILQ_ENTRY(DriveInfo) next;
 };
 
+DriveInfo *blk_legacy_dinfo(BlockBackend *blk);
+DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo);
+BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo);
+
 void override_max_devs(BlockInterfaceType type, int max_devs);
 
 DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
@@ -54,14 +55,11 @@ DriveInfo *drive_get_by_index(BlockInterfaceType type, int index);
 int drive_get_max_bus(BlockInterfaceType type);
 int drive_get_max_devs(BlockInterfaceType type);
 DriveInfo *drive_get_next(BlockInterfaceType type);
-DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
 
 QemuOpts *drive_def(const char *optstr);
 QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
                     const char *optstr);
 DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type);
-void drive_del(DriveInfo *dinfo);
-void drive_info_del(DriveInfo *dinfo);
 
 /* device-hotplug */
 
diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h
index 73ff86d4b9..3f2f4c89e3 100644
--- a/include/sysemu/dma.h
+++ b/include/sysemu/dma.h
@@ -197,24 +197,24 @@ void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len);
 void qemu_sglist_destroy(QEMUSGList *qsg);
 #endif
 
-typedef BlockDriverAIOCB *DMAIOFunc(BlockDriverState *bs, int64_t sector_num,
-                                 QEMUIOVector *iov, int nb_sectors,
-                                 BlockDriverCompletionFunc *cb, void *opaque);
-
-BlockDriverAIOCB *dma_bdrv_io(BlockDriverState *bs,
-                              QEMUSGList *sg, uint64_t sector_num,
-                              DMAIOFunc *io_func, BlockDriverCompletionFunc *cb,
-                              void *opaque, DMADirection dir);
-BlockDriverAIOCB *dma_bdrv_read(BlockDriverState *bs,
-                                QEMUSGList *sg, uint64_t sector,
-                                BlockDriverCompletionFunc *cb, void *opaque);
-BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs,
-                                 QEMUSGList *sg, uint64_t sector,
-                                 BlockDriverCompletionFunc *cb, void *opaque);
+typedef BlockAIOCB *DMAIOFunc(BlockBackend *blk, int64_t sector_num,
+                              QEMUIOVector *iov, int nb_sectors,
+                              BlockCompletionFunc *cb, void *opaque);
+
+BlockAIOCB *dma_blk_io(BlockBackend *blk,
+                       QEMUSGList *sg, uint64_t sector_num,
+                       DMAIOFunc *io_func, BlockCompletionFunc *cb,
+                       void *opaque, DMADirection dir);
+BlockAIOCB *dma_blk_read(BlockBackend *blk,
+                         QEMUSGList *sg, uint64_t sector,
+                         BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *dma_blk_write(BlockBackend *blk,
+                          QEMUSGList *sg, uint64_t sector,
+                          BlockCompletionFunc *cb, void *opaque);
 uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg);
 uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg);
 
-void dma_acct_start(BlockDriverState *bs, BlockAcctCookie *cookie,
+void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie,
                     QEMUSGList *sg, enum BlockAcctType type);
 
 #endif
diff --git a/monitor.c b/monitor.c
index 2d14f394d6..fba4ce2fbe 100644
--- a/monitor.c
+++ b/monitor.c
@@ -206,7 +206,7 @@ struct Monitor {
     ReadLineState *rs;
     MonitorControl *mc;
     CPUState *mon_cpu;
-    BlockDriverCompletionFunc *password_completion_cb;
+    BlockCompletionFunc *password_completion_cb;
     void *password_opaque;
     mon_cmd_t *cmd_table;
     QError *error;
@@ -4216,24 +4216,6 @@ static void file_completion(Monitor *mon, const char *input)
     closedir(ffs);
 }
 
-typedef struct MonitorBlockComplete {
-    Monitor *mon;
-    const char *input;
-} MonitorBlockComplete;
-
-static void block_completion_it(void *opaque, BlockDriverState *bs)
-{
-    const char *name = bdrv_get_device_name(bs);
-    MonitorBlockComplete *mbc = opaque;
-    Monitor *mon = mbc->mon;
-    const char *input = mbc->input;
-
-    if (input[0] == '\0' ||
-        !strncmp(name, (char *)input, strlen(input))) {
-        readline_add_completion(mon->rs, name);
-    }
-}
-
 static const char *next_arg_type(const char *typestr)
 {
     const char *p = strchr(typestr, ':');
@@ -4671,9 +4653,9 @@ static void monitor_find_completion_by_table(Monitor *mon,
 {
     const char *cmdname;
     int i;
-    const char *ptype, *str;
+    const char *ptype, *str, *name;
     const mon_cmd_t *cmd;
-    MonitorBlockComplete mbs;
+    BlockDriverState *bs;
 
     if (nb_args <= 1) {
         /* command completion */
@@ -4725,10 +4707,14 @@ static void monitor_find_completion_by_table(Monitor *mon,
             break;
         case 'B':
             /* block device name completion */
-            mbs.mon = mon;
-            mbs.input = str;
             readline_set_completion_index(mon->rs, strlen(str));
-            bdrv_iterate(block_completion_it, &mbs);
+            for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) {
+                name = bdrv_get_device_name(bs);
+                if (str[0] == '\0' ||
+                    !strncmp(name, str, strlen(str))) {
+                    readline_add_completion(mon->rs, name);
+                }
+            }
             break;
         case 's':
         case 'S':
@@ -5388,7 +5374,7 @@ ReadLineState *monitor_get_rs(Monitor *mon)
 }
 
 int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
-                                BlockDriverCompletionFunc *completion_cb,
+                                BlockCompletionFunc *completion_cb,
                                 void *opaque)
 {
     int err;
@@ -5420,7 +5406,7 @@ int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
 }
 
 int monitor_read_block_device_key(Monitor *mon, const char *device,
-                                  BlockDriverCompletionFunc *completion_cb,
+                                  BlockCompletionFunc *completion_cb,
                                   void *opaque)
 {
     BlockDriverState *bs;
diff --git a/qemu-img.c b/qemu-img.c
index 27b85dbda4..09e7e72f5a 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -29,6 +29,7 @@
 #include "qemu/error-report.h"
 #include "qemu/osdep.h"
 #include "sysemu/sysemu.h"
+#include "sysemu/block-backend.h"
 #include "block/block_int.h"
 #include "block/qapi.h"
 #include <getopt.h>
@@ -283,20 +284,19 @@ static int print_block_option_help(const char *filename, const char *fmt)
     return 0;
 }
 
-static BlockDriverState *bdrv_new_open(const char *id,
-                                       const char *filename,
-                                       const char *fmt,
-                                       int flags,
-                                       bool require_io,
-                                       bool quiet)
+static BlockBackend *img_open(const char *id, const char *filename,
+                              const char *fmt, int flags,
+                              bool require_io, bool quiet)
 {
+    BlockBackend *blk;
     BlockDriverState *bs;
     BlockDriver *drv;
     char password[256];
     Error *local_err = NULL;
     int ret;
 
-    bs = bdrv_new(id, &error_abort);
+    blk = blk_new_with_bs(id, &error_abort);
+    bs = blk_bs(blk);
 
     if (fmt) {
         drv = bdrv_find_format(fmt);
@@ -327,9 +327,9 @@ static BlockDriverState *bdrv_new_open(const char *id,
             goto fail;
         }
     }
-    return bs;
+    return blk;
 fail:
-    bdrv_unref(bs);
+    blk_unref(blk);
     return NULL;
 }
 
@@ -575,6 +575,7 @@ static int img_check(int argc, char **argv)
     int c, ret;
     OutputFormat output_format = OFORMAT_HUMAN;
     const char *filename, *fmt, *output, *cache;
+    BlockBackend *blk;
     BlockDriverState *bs;
     int fix = 0;
     int flags = BDRV_O_FLAGS | BDRV_O_CHECK;
@@ -649,10 +650,11 @@ static int img_check(int argc, char **argv)
         return 1;
     }
 
-    bs = bdrv_new_open("image", filename, fmt, flags, true, quiet);
-    if (!bs) {
+    blk = img_open("image", filename, fmt, flags, true, quiet);
+    if (!blk) {
         return 1;
     }
+    bs = blk_bs(blk);
 
     check = g_new0(ImageCheck, 1);
     ret = collect_image_check(bs, check, filename, fmt, fix);
@@ -709,8 +711,7 @@ static int img_check(int argc, char **argv)
 
 fail:
     qapi_free_ImageCheck(check);
-    bdrv_unref(bs);
-
+    blk_unref(blk);
     return ret;
 }
 
@@ -718,6 +719,7 @@ static int img_commit(int argc, char **argv)
 {
     int c, ret, flags;
     const char *filename, *fmt, *cache;
+    BlockBackend *blk;
     BlockDriverState *bs;
     bool quiet = false;
 
@@ -756,10 +758,12 @@ static int img_commit(int argc, char **argv)
         return 1;
     }
 
-    bs = bdrv_new_open("image", filename, fmt, flags, true, quiet);
-    if (!bs) {
+    blk = img_open("image", filename, fmt, flags, true, quiet);
+    if (!blk) {
         return 1;
     }
+    bs = blk_bs(blk);
+
     ret = bdrv_commit(bs);
     switch(ret) {
     case 0:
@@ -779,7 +783,7 @@ static int img_commit(int argc, char **argv)
         break;
     }
 
-    bdrv_unref(bs);
+    blk_unref(blk);
     if (ret) {
         return 1;
     }
@@ -942,6 +946,7 @@ static int check_empty_sectors(BlockDriverState *bs, int64_t sect_num,
 static int img_compare(int argc, char **argv)
 {
     const char *fmt1 = NULL, *fmt2 = NULL, *cache, *filename1, *filename2;
+    BlockBackend *blk1, *blk2;
     BlockDriverState *bs1, *bs2;
     int64_t total_sectors1, total_sectors2;
     uint8_t *buf1 = NULL, *buf2 = NULL;
@@ -1011,19 +1016,21 @@ static int img_compare(int argc, char **argv)
         goto out3;
     }
 
-    bs1 = bdrv_new_open("image_1", filename1, fmt1, flags, true, quiet);
-    if (!bs1) {
+    blk1 = img_open("image_1", filename1, fmt1, flags, true, quiet);
+    if (!blk1) {
         error_report("Can't open file %s", filename1);
         ret = 2;
         goto out3;
     }
+    bs1 = blk_bs(blk1);
 
-    bs2 = bdrv_new_open("image_2", filename2, fmt2, flags, true, quiet);
-    if (!bs2) {
+    blk2 = img_open("image_2", filename2, fmt2, flags, true, quiet);
+    if (!blk2) {
         error_report("Can't open file %s", filename2);
         ret = 2;
         goto out2;
     }
+    bs2 = blk_bs(blk2);
 
     buf1 = qemu_blockalign(bs1, IO_BUF_SIZE);
     buf2 = qemu_blockalign(bs2, IO_BUF_SIZE);
@@ -1183,11 +1190,11 @@ static int img_compare(int argc, char **argv)
     ret = 0;
 
 out:
-    bdrv_unref(bs2);
     qemu_vfree(buf1);
     qemu_vfree(buf2);
+    blk_unref(blk2);
 out2:
-    bdrv_unref(bs1);
+    blk_unref(blk1);
 out3:
     qemu_progress_end();
     return ret;
@@ -1200,6 +1207,7 @@ static int img_convert(int argc, char **argv)
     int progress = 0, flags, src_flags;
     const char *fmt, *out_fmt, *cache, *src_cache, *out_baseimg, *out_filename;
     BlockDriver *drv, *proto_drv;
+    BlockBackend **blk = NULL, *out_blk = NULL;
     BlockDriverState **bs = NULL, *out_bs = NULL;
     int64_t total_sectors, nb_sectors, sector_num, bs_offset;
     int64_t *bs_sectors = NULL;
@@ -1354,6 +1362,7 @@ static int img_convert(int argc, char **argv)
 
     qemu_progress_print(0, 100);
 
+    blk = g_new0(BlockBackend *, bs_n);
     bs = g_new0(BlockDriverState *, bs_n);
     bs_sectors = g_new(int64_t, bs_n);
 
@@ -1361,14 +1370,15 @@ static int img_convert(int argc, char **argv)
     for (bs_i = 0; bs_i < bs_n; bs_i++) {
         char *id = bs_n > 1 ? g_strdup_printf("source_%d", bs_i)
                             : g_strdup("source");
-        bs[bs_i] = bdrv_new_open(id, argv[optind + bs_i], fmt, src_flags,
-                                 true, quiet);
+        blk[bs_i] = img_open(id, argv[optind + bs_i], fmt, src_flags,
+                             true, quiet);
         g_free(id);
-        if (!bs[bs_i]) {
+        if (!blk[bs_i]) {
             error_report("Could not open '%s'", argv[optind + bs_i]);
             ret = -1;
             goto out;
         }
+        bs[bs_i] = blk_bs(blk[bs_i]);
         bs_sectors[bs_i] = bdrv_nb_sectors(bs[bs_i]);
         if (bs_sectors[bs_i] < 0) {
             error_report("Could not get size of %s: %s",
@@ -1486,11 +1496,12 @@ static int img_convert(int argc, char **argv)
         goto out;
     }
 
-    out_bs = bdrv_new_open("target", out_filename, out_fmt, flags, true, quiet);
-    if (!out_bs) {
+    out_blk = img_open("target", out_filename, out_fmt, flags, true, quiet);
+    if (!out_blk) {
         ret = -1;
         goto out;
     }
+    out_bs = blk_bs(out_blk);
 
     bs_i = 0;
     bs_offset = 0;
@@ -1737,16 +1748,13 @@ out:
     qemu_opts_free(create_opts);
     qemu_vfree(buf);
     qemu_opts_del(sn_opts);
-    if (out_bs) {
-        bdrv_unref(out_bs);
-    }
-    if (bs) {
+    blk_unref(out_blk);
+    g_free(bs);
+    if (blk) {
         for (bs_i = 0; bs_i < bs_n; bs_i++) {
-            if (bs[bs_i]) {
-                bdrv_unref(bs[bs_i]);
-            }
+            blk_unref(blk[bs_i]);
         }
-        g_free(bs);
+        g_free(blk);
     }
     g_free(bs_sectors);
 fail_getopt:
@@ -1856,6 +1864,7 @@ static ImageInfoList *collect_image_info_list(const char *filename,
     filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL);
 
     while (filename) {
+        BlockBackend *blk;
         BlockDriverState *bs;
         ImageInfo *info;
         ImageInfoList *elem;
@@ -1867,17 +1876,18 @@ static ImageInfoList *collect_image_info_list(const char *filename,
         }
         g_hash_table_insert(filenames, (gpointer)filename, NULL);
 
-        bs = bdrv_new_open("image", filename, fmt,
-                           BDRV_O_FLAGS | BDRV_O_NO_BACKING, false, false);
-        if (!bs) {
+        blk = img_open("image", filename, fmt,
+                       BDRV_O_FLAGS | BDRV_O_NO_BACKING, false, false);
+        if (!blk) {
             goto err;
         }
+        bs = blk_bs(blk);
 
         bdrv_query_image_info(bs, &info, &err);
         if (err) {
             error_report("%s", error_get_pretty(err));
             error_free(err);
-            bdrv_unref(bs);
+            blk_unref(blk);
             goto err;
         }
 
@@ -1886,7 +1896,7 @@ static ImageInfoList *collect_image_info_list(const char *filename,
         *last = elem;
         last = &elem->next;
 
-        bdrv_unref(bs);
+        blk_unref(blk);
 
         filename = fmt = NULL;
         if (chain) {
@@ -2080,6 +2090,7 @@ static int img_map(int argc, char **argv)
 {
     int c;
     OutputFormat output_format = OFORMAT_HUMAN;
+    BlockBackend *blk;
     BlockDriverState *bs;
     const char *filename, *fmt, *output;
     int64_t length;
@@ -2128,10 +2139,11 @@ static int img_map(int argc, char **argv)
         return 1;
     }
 
-    bs = bdrv_new_open("image", filename, fmt, BDRV_O_FLAGS, true, false);
-    if (!bs) {
+    blk = img_open("image", filename, fmt, BDRV_O_FLAGS, true, false);
+    if (!blk) {
         return 1;
     }
+    bs = blk_bs(blk);
 
     if (output_format == OFORMAT_HUMAN) {
         printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File");
@@ -2172,7 +2184,7 @@ static int img_map(int argc, char **argv)
     dump_map_entry(output_format, &curr, NULL);
 
 out:
-    bdrv_unref(bs);
+    blk_unref(blk);
     return ret < 0;
 }
 
@@ -2183,6 +2195,7 @@ out:
 
 static int img_snapshot(int argc, char **argv)
 {
+    BlockBackend *blk;
     BlockDriverState *bs;
     QEMUSnapshotInfo sn;
     char *filename, *snapshot_name = NULL;
@@ -2248,10 +2261,11 @@ static int img_snapshot(int argc, char **argv)
     filename = argv[optind++];
 
     /* Open the image */
-    bs = bdrv_new_open("image", filename, NULL, bdrv_oflags, true, quiet);
-    if (!bs) {
+    blk = img_open("image", filename, NULL, bdrv_oflags, true, quiet);
+    if (!blk) {
         return 1;
     }
+    bs = blk_bs(blk);
 
     /* Perform the requested action */
     switch(action) {
@@ -2294,7 +2308,7 @@ static int img_snapshot(int argc, char **argv)
     }
 
     /* Cleanup */
-    bdrv_unref(bs);
+    blk_unref(blk);
     if (ret) {
         return 1;
     }
@@ -2303,6 +2317,7 @@ static int img_snapshot(int argc, char **argv)
 
 static int img_rebase(int argc, char **argv)
 {
+    BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL;
     BlockDriverState *bs = NULL, *bs_old_backing = NULL, *bs_new_backing = NULL;
     BlockDriver *old_backing_drv, *new_backing_drv;
     char *filename;
@@ -2391,11 +2406,12 @@ static int img_rebase(int argc, char **argv)
      * Ignore the old backing file for unsafe rebase in case we want to correct
      * the reference to a renamed or moved backing file.
      */
-    bs = bdrv_new_open("image", filename, fmt, flags, true, quiet);
-    if (!bs) {
+    blk = img_open("image", filename, fmt, flags, true, quiet);
+    if (!blk) {
         ret = -1;
         goto out;
     }
+    bs = blk_bs(blk);
 
     /* Find the right drivers for the backing files */
     old_backing_drv = NULL;
@@ -2423,7 +2439,8 @@ static int img_rebase(int argc, char **argv)
     if (!unsafe) {
         char backing_name[1024];
 
-        bs_old_backing = bdrv_new("old_backing", &error_abort);
+        blk_old_backing = blk_new_with_bs("old_backing", &error_abort);
+        bs_old_backing = blk_bs(blk_old_backing);
         bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
         ret = bdrv_open(&bs_old_backing, backing_name, NULL, NULL, src_flags,
                         old_backing_drv, &local_err);
@@ -2434,7 +2451,8 @@ static int img_rebase(int argc, char **argv)
             goto out;
         }
         if (out_baseimg[0]) {
-            bs_new_backing = bdrv_new("new_backing", &error_abort);
+            blk_new_backing = blk_new_with_bs("new_backing", &error_abort);
+            bs_new_backing = blk_bs(blk_new_backing);
             ret = bdrv_open(&bs_new_backing, out_baseimg, NULL, NULL, src_flags,
                             new_backing_drv, &local_err);
             if (ret) {
@@ -2609,15 +2627,11 @@ out:
     qemu_progress_end();
     /* Cleanup */
     if (!unsafe) {
-        if (bs_old_backing != NULL) {
-            bdrv_unref(bs_old_backing);
-        }
-        if (bs_new_backing != NULL) {
-            bdrv_unref(bs_new_backing);
-        }
+        blk_unref(blk_old_backing);
+        blk_unref(blk_new_backing);
     }
 
-    bdrv_unref(bs);
+    blk_unref(blk);
     if (ret) {
         return 1;
     }
@@ -2630,6 +2644,7 @@ static int img_resize(int argc, char **argv)
     const char *filename, *fmt, *size;
     int64_t n, total_size;
     bool quiet = false;
+    BlockBackend *blk = NULL;
     BlockDriverState *bs = NULL;
     QemuOpts *param;
     static QemuOptsList resize_options = {
@@ -2706,12 +2721,13 @@ static int img_resize(int argc, char **argv)
     n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0);
     qemu_opts_del(param);
 
-    bs = bdrv_new_open("image", filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR,
-                       true, quiet);
-    if (!bs) {
+    blk = img_open("image", filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR,
+                   true, quiet);
+    if (!blk) {
         ret = -1;
         goto out;
     }
+    bs = blk_bs(blk);
 
     if (relative) {
         total_size = bdrv_getlength(bs) + n * relative;
@@ -2740,9 +2756,7 @@ static int img_resize(int argc, char **argv)
         break;
     }
 out:
-    if (bs) {
-        bdrv_unref(bs);
-    }
+    blk_unref(blk);
     if (ret) {
         return 1;
     }
@@ -2758,6 +2772,7 @@ static int img_amend(int argc, char **argv)
     const char *fmt = NULL, *filename, *cache;
     int flags;
     bool quiet = false;
+    BlockBackend *blk = NULL;
     BlockDriverState *bs = NULL;
 
     cache = BDRV_DEFAULT_CACHE;
@@ -2821,12 +2836,13 @@ static int img_amend(int argc, char **argv)
         goto out;
     }
 
-    bs = bdrv_new_open("image", filename, fmt, flags, true, quiet);
-    if (!bs) {
+    blk = img_open("image", filename, fmt, flags, true, quiet);
+    if (!blk) {
         error_report("Could not open image '%s'", filename);
         ret = -1;
         goto out;
     }
+    bs = blk_bs(blk);
 
     fmt = bs->drv->format_name;
 
@@ -2851,9 +2867,7 @@ static int img_amend(int argc, char **argv)
     }
 
 out:
-    if (bs) {
-        bdrv_unref(bs);
-    }
+    blk_unref(blk);
     qemu_opts_del(opts);
     qemu_opts_free(create_opts);
     g_free(options);
diff --git a/qemu-io.c b/qemu-io.c
index 66cf3ef4be..60f84dd72a 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -19,6 +19,7 @@
 #include "qemu/option.h"
 #include "qemu/config-file.h"
 #include "qemu/readline.h"
+#include "sysemu/block-backend.h"
 #include "block/block_int.h"
 #include "trace/control.h"
 
@@ -26,6 +27,7 @@
 
 static char *progname;
 
+static BlockBackend *qemuio_blk;
 static BlockDriverState *qemuio_bs;
 
 /* qemu-io commands passed using -c */
@@ -36,8 +38,9 @@ static ReadLineState *readline_state;
 
 static int close_f(BlockDriverState *bs, int argc, char **argv)
 {
-    bdrv_unref(bs);
+    blk_unref(qemuio_blk);
     qemuio_bs = NULL;
+    qemuio_blk = NULL;
     return 0;
 }
 
@@ -58,7 +61,8 @@ static int openfile(char *name, int flags, int growable, QDict *opts)
         return 1;
     }
 
-    qemuio_bs = bdrv_new("hda", &error_abort);
+    qemuio_blk = blk_new_with_bs("hda", &error_abort);
+    qemuio_bs = blk_bs(qemuio_blk);
 
     if (growable) {
         flags |= BDRV_O_PROTOCOL;
@@ -69,8 +73,9 @@ static int openfile(char *name, int flags, int growable, QDict *opts)
                 name ? " device " : "", name ?: "",
                 error_get_pretty(local_err));
         error_free(local_err);
-        bdrv_unref(qemuio_bs);
+        blk_unref(qemuio_blk);
         qemuio_bs = NULL;
+        qemuio_blk = NULL;
         return 1;
     }
 
@@ -481,9 +486,7 @@ int main(int argc, char **argv)
      */
     bdrv_drain_all();
 
-    if (qemuio_bs) {
-        bdrv_unref(qemuio_bs);
-    }
+    blk_unref(qemuio_blk);
     g_free(readline_state);
     return 0;
 }
diff --git a/qemu-nbd.c b/qemu-nbd.c
index b524b3420f..5cd6c6d187 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -17,7 +17,7 @@
  */
 
 #include "qemu-common.h"
-#include "block/block.h"
+#include "sysemu/block-backend.h"
 #include "block/block_int.h"
 #include "block/nbd.h"
 #include "qemu/main-loop.h"
@@ -384,6 +384,7 @@ static void nbd_accept(void *opaque)
 
 int main(int argc, char **argv)
 {
+    BlockBackend *blk;
     BlockDriverState *bs;
     BlockDriver *drv;
     off_t dev_offset = 0;
@@ -691,7 +692,8 @@ int main(int argc, char **argv)
         drv = NULL;
     }
 
-    bs = bdrv_new("hda", &error_abort);
+    blk = blk_new_with_bs("hda", &error_abort);
+    bs = blk_bs(blk);
 
     srcpath = argv[optind];
     ret = bdrv_open(&bs, srcpath, NULL, NULL, flags, drv, &local_err);
@@ -773,7 +775,7 @@ int main(int argc, char **argv)
         }
     } while (state != TERMINATED);
 
-    bdrv_unref(bs);
+    blk_unref(blk);
     if (sockpath) {
         unlink(sockpath);
     }
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index c0b1f6a320..5e347d04bd 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -1,6 +1,5 @@
 stub-obj-y += arch-query-cpu-def.o
 stub-obj-y += bdrv-commit-all.o
-stub-obj-y += blockdev.o
 stub-obj-y += chr-baum-init.o
 stub-obj-y += chr-msmouse.o
 stub-obj-y += chr-testdev.o
diff --git a/stubs/blockdev.c b/stubs/blockdev.c
deleted file mode 100644
index 5d0a79c3a6..0000000000
--- a/stubs/blockdev.c
+++ /dev/null
@@ -1,12 +0,0 @@
-#include <assert.h>
-#include "sysemu/blockdev.h"
-
-DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
-{
-    return NULL;
-}
-
-void drive_info_del(DriveInfo *dinfo)
-{
-    assert(!dinfo);
-}
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 8b9a92ebdc..26e7894eaf 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -431,7 +431,7 @@ struct CPUMIPSState {
     int error_code;
     uint32_t hflags;    /* CPU State */
     /* TMASK defines different execution modes */
-#define MIPS_HFLAG_TMASK  0xC07FF
+#define MIPS_HFLAG_TMASK  0x1807FF
 #define MIPS_HFLAG_MODE   0x00007 /* execution modes                    */
     /* The KSU flags must be the lowest bits in hflags. The flag order
        must be the same as defined for CP0 Status. This allows to use
@@ -450,7 +450,7 @@ struct CPUMIPSState {
        and RSQRT.D.  */
 #define MIPS_HFLAG_COP1X  0x00080 /* COP1X instructions enabled         */
 #define MIPS_HFLAG_RE     0x00100 /* Reversed endianness                */
-#define MIPS_HFLAG_UX     0x00200 /* 64-bit user mode                   */
+#define MIPS_HFLAG_AWRAP  0x00200 /* 32-bit compatibility address wrapping */
 #define MIPS_HFLAG_M16    0x00400 /* MIPS16 mode flag                   */
 #define MIPS_HFLAG_M16_SHIFT 10
     /* If translation is interrupted between the branch instruction and
@@ -463,17 +463,18 @@ struct CPUMIPSState {
 #define MIPS_HFLAG_BL     0x01800 /* Likely branch                      */
 #define MIPS_HFLAG_BR     0x02000 /* branch to register (can't link TB) */
     /* Extra flags about the current pending branch.  */
-#define MIPS_HFLAG_BMASK_EXT 0x3C000
+#define MIPS_HFLAG_BMASK_EXT 0x7C000
 #define MIPS_HFLAG_B16    0x04000 /* branch instruction was 16 bits     */
 #define MIPS_HFLAG_BDS16  0x08000 /* branch requires 16-bit delay slot  */
 #define MIPS_HFLAG_BDS32  0x10000 /* branch requires 32-bit delay slot  */
-#define MIPS_HFLAG_BX     0x20000 /* branch exchanges execution mode    */
+#define MIPS_HFLAG_BDS_STRICT  0x20000 /* Strict delay slot size */
+#define MIPS_HFLAG_BX     0x40000 /* branch exchanges execution mode    */
 #define MIPS_HFLAG_BMASK  (MIPS_HFLAG_BMASK_BASE | MIPS_HFLAG_BMASK_EXT)
     /* MIPS DSP resources access. */
-#define MIPS_HFLAG_DSP   0x40000  /* Enable access to MIPS DSP resources. */
-#define MIPS_HFLAG_DSPR2 0x80000  /* Enable access to MIPS DSPR2 resources. */
+#define MIPS_HFLAG_DSP   0x080000  /* Enable access to MIPS DSP resources. */
+#define MIPS_HFLAG_DSPR2 0x100000  /* Enable access to MIPS DSPR2 resources. */
     /* Extra flag about HWREna register. */
-#define MIPS_HFLAG_HWRENA_ULR 0x100000 /* ULR bit from HWREna is set. */
+#define MIPS_HFLAG_HWRENA_ULR 0x200000 /* ULR bit from HWREna is set. */
     target_ulong btarget;        /* Jump / branch target               */
     target_ulong bcond;          /* Branch condition (if needed)       */
 
@@ -725,7 +726,7 @@ static inline void compute_hflags(CPUMIPSState *env)
 {
     env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 |
                      MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU |
-                     MIPS_HFLAG_UX | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2);
+                     MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2);
     if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
         !(env->CP0_Status & (1 << CP0St_ERL)) &&
         !(env->hflags & MIPS_HFLAG_DM)) {
@@ -737,8 +738,18 @@ static inline void compute_hflags(CPUMIPSState *env)
         (env->CP0_Status & (1 << CP0St_UX))) {
         env->hflags |= MIPS_HFLAG_64;
     }
-    if (env->CP0_Status & (1 << CP0St_UX)) {
-        env->hflags |= MIPS_HFLAG_UX;
+
+    if (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM) &&
+        !(env->CP0_Status & (1 << CP0St_UX))) {
+        env->hflags |= MIPS_HFLAG_AWRAP;
+    } else if (env->insn_flags & ISA_MIPS32R6) {
+        /* Address wrapping for Supervisor and Kernel is specified in R6 */
+        if ((((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_SM) &&
+             !(env->CP0_Status & (1 << CP0St_SX))) ||
+            (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_KM) &&
+             !(env->CP0_Status & (1 << CP0St_KX)))) {
+            env->hflags |= MIPS_HFLAG_AWRAP;
+        }
     }
 #endif
     if ((env->CP0_Status & (1 << CP0St_CU0)) ||
diff --git a/target-mips/dsp_helper.c b/target-mips/dsp_helper.c
index 94083fb424..349f2a0033 100644
--- a/target-mips/dsp_helper.c
+++ b/target-mips/dsp_helper.c
@@ -76,15 +76,6 @@ static inline void set_DSPControl_24(uint32_t flag, int len, CPUMIPSState *env)
   env->active_tc.DSPControl |= (target_ulong)flag << 24;
 }
 
-static inline uint32_t get_DSPControl_24(int len, CPUMIPSState *env)
-{
-  uint32_t filter;
-
-  filter = (0x01 << len) - 1;
-
-  return (env->active_tc.DSPControl >> 24) & filter;
-}
-
 static inline void set_DSPControl_pos(uint32_t pos, CPUMIPSState *env)
 {
     target_ulong dspc;
@@ -283,6 +274,7 @@ static inline int32_t mipsdsp_sat32_acc_q31(int32_t acc, int32_t a,
     return result;
 }
 
+#ifdef TARGET_MIPS64
 /* a[0] is LO, a[1] is HI. */
 static inline void mipsdsp_sat64_acc_add_q63(int64_t *ret,
                                              int32_t ac,
@@ -336,6 +328,7 @@ static inline void mipsdsp_sat64_acc_sub_q63(int64_t *ret,
         set_DSPControl_overflow_flag(1, 16 + ac, env);
     }
 }
+#endif
 
 static inline int32_t mipsdsp_mul_i16_i16(int16_t a, int16_t b,
                                           CPUMIPSState *env)
@@ -357,10 +350,12 @@ static inline int32_t mipsdsp_mul_u16_u16(int32_t a, int32_t b)
     return a * b;
 }
 
+#ifdef TARGET_MIPS64
 static inline int32_t mipsdsp_mul_i32_i32(int32_t a, int32_t b)
 {
     return a * b;
 }
+#endif
 
 static inline int32_t mipsdsp_sat16_mul_i16_i16(int16_t a, int16_t b,
                                                 CPUMIPSState *env)
@@ -417,10 +412,12 @@ static inline int16_t mipsdsp_rashift16(int16_t a, target_ulong mov)
     return a >> mov;
 }
 
+#ifdef TARGET_MIPS64
 static inline int32_t mipsdsp_rashift32(int32_t a, target_ulong mov)
 {
     return a >> mov;
 }
+#endif
 
 static inline int16_t mipsdsp_rshift1_add_q16(int16_t a, int16_t b)
 {
@@ -479,6 +476,7 @@ static inline uint8_t mipsdsp_rrshift1_add_u8(uint8_t a, uint8_t b)
     return (temp >> 1) & 0x00FF;
 }
 
+#ifdef TARGET_MIPS64
 static inline uint8_t mipsdsp_rshift1_sub_u8(uint8_t a, uint8_t b)
 {
     uint16_t temp;
@@ -496,6 +494,7 @@ static inline uint8_t mipsdsp_rrshift1_sub_u8(uint8_t a, uint8_t b)
 
     return (temp >> 1) & 0x00FF;
 }
+#endif
 
 /*  128 bits long. p[0] is LO, p[1] is HI. */
 static inline void mipsdsp_rndrashift_short_acc(int64_t *p,
@@ -511,6 +510,7 @@ static inline void mipsdsp_rndrashift_short_acc(int64_t *p,
     p[1] = (acc >> 63) & 0x01;
 }
 
+#ifdef TARGET_MIPS64
 /* 128 bits long. p[0] is LO, p[1] is HI */
 static inline void mipsdsp_rashift_acc(uint64_t *p,
                                        uint32_t ac,
@@ -558,6 +558,7 @@ static inline void mipsdsp_rndrashift_acc(uint64_t *p,
         }
     }
 }
+#endif
 
 static inline int32_t mipsdsp_mul_q15_q15(int32_t ac, uint16_t a, uint16_t b,
                                           CPUMIPSState *env)
@@ -608,10 +609,12 @@ static inline uint16_t mipsdsp_mul_u8_u16(uint8_t a, uint16_t b,
     return tempI & 0x0000FFFF;
 }
 
+#ifdef TARGET_MIPS64
 static inline uint64_t mipsdsp_mul_u32_u32(uint32_t a, uint32_t b)
 {
     return (uint64_t)a * (uint64_t)b;
 }
+#endif
 
 static inline int16_t mipsdsp_rndq15_mul_q15_q15(uint16_t a, uint16_t b,
                                                  CPUMIPSState *env)
@@ -717,7 +720,7 @@ static inline uint16_t mipsdsp_lshift16(uint16_t a, uint8_t s,
     return a << s;
 }
 
-
+#ifdef TARGET_MIPS64
 static inline uint32_t mipsdsp_lshift32(uint32_t a, uint8_t s,
                                         CPUMIPSState *env)
 {
@@ -734,6 +737,7 @@ static inline uint32_t mipsdsp_lshift32(uint32_t a, uint8_t s,
         return a << s;
     }
 }
+#endif
 
 static inline uint16_t mipsdsp_sat16_lshift(uint16_t a, uint8_t s,
                                             CPUMIPSState *env)
@@ -973,6 +977,7 @@ static inline uint8_t mipsdsp_satu8_sub(uint8_t a, uint8_t b, CPUMIPSState *env)
     return temp & 0x00FF;
 }
 
+#ifdef TARGET_MIPS64
 static inline uint32_t mipsdsp_sub32(int32_t a, int32_t b, CPUMIPSState *env)
 {
     int32_t temp;
@@ -997,6 +1002,7 @@ static inline int32_t mipsdsp_add_i32(int32_t a, int32_t b, CPUMIPSState *env)
 
     return temp;
 }
+#endif
 
 static inline int32_t mipsdsp_cmp_eq(int32_t a, int32_t b)
 {
diff --git a/target-mips/helper.h b/target-mips/helper.h
index 74ef09485f..a127db55c2 100644
--- a/target-mips/helper.h
+++ b/target-mips/helper.h
@@ -39,6 +39,11 @@ DEF_HELPER_3(macchiu, tl, env, tl, tl)
 DEF_HELPER_3(msachi, tl, env, tl, tl)
 DEF_HELPER_3(msachiu, tl, env, tl, tl)
 
+DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl)
+#ifdef TARGET_MIPS64
+DEF_HELPER_FLAGS_1(dbitswap, TCG_CALL_NO_RWG_SE, tl, tl)
+#endif
+
 #ifndef CONFIG_USER_ONLY
 /* CP0 helpers */
 DEF_HELPER_1(mfc0_mvpcontrol, tl, env)
@@ -197,6 +202,25 @@ DEF_HELPER_2(float_cvtw_d, i32, env, i64)
 DEF_HELPER_3(float_addr_ps, i64, env, i64, i64)
 DEF_HELPER_3(float_mulr_ps, i64, env, i64, i64)
 
+DEF_HELPER_FLAGS_1(float_class_s, TCG_CALL_NO_RWG_SE, i32, i32)
+DEF_HELPER_FLAGS_1(float_class_d, TCG_CALL_NO_RWG_SE, i64, i64)
+
+#define FOP_PROTO(op)                                     \
+DEF_HELPER_4(float_ ## op ## _s, i32, env, i32, i32, i32) \
+DEF_HELPER_4(float_ ## op ## _d, i64, env, i64, i64, i64)
+FOP_PROTO(maddf)
+FOP_PROTO(msubf)
+#undef FOP_PROTO
+
+#define FOP_PROTO(op)                                \
+DEF_HELPER_3(float_ ## op ## _s, i32, env, i32, i32) \
+DEF_HELPER_3(float_ ## op ## _d, i64, env, i64, i64)
+FOP_PROTO(max)
+FOP_PROTO(maxa)
+FOP_PROTO(min)
+FOP_PROTO(mina)
+#undef FOP_PROTO
+
 #define FOP_PROTO(op)                            \
 DEF_HELPER_2(float_ ## op ## l_s, i64, env, i32) \
 DEF_HELPER_2(float_ ## op ## l_d, i64, env, i64) \
@@ -214,6 +238,7 @@ DEF_HELPER_2(float_ ## op ## _d, i64, env, i64)
 FOP_PROTO(sqrt)
 FOP_PROTO(rsqrt)
 FOP_PROTO(recip)
+FOP_PROTO(rint)
 #undef FOP_PROTO
 
 #define FOP_PROTO(op)                       \
@@ -279,6 +304,33 @@ FOP_PROTO(le)
 FOP_PROTO(ngt)
 #undef FOP_PROTO
 
+#define FOP_PROTO(op) \
+DEF_HELPER_3(r6_cmp_d_ ## op, i64, env, i64, i64) \
+DEF_HELPER_3(r6_cmp_s_ ## op, i32, env, i32, i32)
+FOP_PROTO(af)
+FOP_PROTO(un)
+FOP_PROTO(eq)
+FOP_PROTO(ueq)
+FOP_PROTO(lt)
+FOP_PROTO(ult)
+FOP_PROTO(le)
+FOP_PROTO(ule)
+FOP_PROTO(saf)
+FOP_PROTO(sun)
+FOP_PROTO(seq)
+FOP_PROTO(sueq)
+FOP_PROTO(slt)
+FOP_PROTO(sult)
+FOP_PROTO(sle)
+FOP_PROTO(sule)
+FOP_PROTO(or)
+FOP_PROTO(une)
+FOP_PROTO(ne)
+FOP_PROTO(sor)
+FOP_PROTO(sune)
+FOP_PROTO(sne)
+#undef FOP_PROTO
+
 /* Special functions */
 #ifndef CONFIG_USER_ONLY
 DEF_HELPER_1(tlbwi, void, env)
diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h
index 9dfa5168da..6cb62b2df0 100644
--- a/target-mips/mips-defs.h
+++ b/target-mips/mips-defs.h
@@ -30,17 +30,21 @@
 #define		ISA_MIPS64	0x00000080
 #define		ISA_MIPS64R2	0x00000100
 #define   ISA_MIPS32R3  0x00000200
-#define   ISA_MIPS32R5  0x00000400
+#define   ISA_MIPS64R3  0x00000400
+#define   ISA_MIPS32R5  0x00000800
+#define   ISA_MIPS64R5  0x00001000
+#define   ISA_MIPS32R6  0x00002000
+#define   ISA_MIPS64R6  0x00004000
 
 /* MIPS ASEs. */
-#define		ASE_MIPS16	0x00001000
-#define		ASE_MIPS3D	0x00002000
-#define		ASE_MDMX	0x00004000
-#define		ASE_DSP		0x00008000
-#define		ASE_DSPR2	0x00010000
-#define		ASE_MT		0x00020000
-#define		ASE_SMARTMIPS	0x00040000
-#define 	ASE_MICROMIPS	0x00080000
+#define   ASE_MIPS16    0x00010000
+#define   ASE_MIPS3D    0x00020000
+#define   ASE_MDMX      0x00040000
+#define   ASE_DSP       0x00080000
+#define   ASE_DSPR2     0x00100000
+#define   ASE_MT        0x00200000
+#define   ASE_SMARTMIPS 0x00400000
+#define   ASE_MICROMIPS 0x00800000
 
 /* Chip specific instructions. */
 #define		INSN_LOONGSON2E  0x20000000
@@ -68,9 +72,15 @@
 
 /* MIPS Technologies "Release 3" */
 #define CPU_MIPS32R3 (CPU_MIPS32R2 | ISA_MIPS32R3)
+#define CPU_MIPS64R3 (CPU_MIPS64R2 | CPU_MIPS32R3 | ISA_MIPS64R3)
 
 /* MIPS Technologies "Release 5" */
 #define CPU_MIPS32R5 (CPU_MIPS32R3 | ISA_MIPS32R5)
+#define CPU_MIPS64R5 (CPU_MIPS64R3 | CPU_MIPS32R5 | ISA_MIPS64R5)
+
+/* MIPS Technologies "Release 6" */
+#define CPU_MIPS32R6 (CPU_MIPS32R5 | ISA_MIPS32R6)
+#define CPU_MIPS64R6 (CPU_MIPS64R5 | CPU_MIPS32R6 | ISA_MIPS64R6)
 
 /* Strictly follow the architecture standard:
    - Disallow "special" instruction handling for PMON/SPIM.
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index df97b35f87..5204ed87ca 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -90,7 +90,6 @@ static inline type do_##name(CPUMIPSState *env, target_ulong addr,      \
     }                                                                   \
 }
 #endif
-HELPER_LD(lbu, ldub, uint8_t)
 HELPER_LD(lw, ldl, int32_t)
 #ifdef TARGET_MIPS64
 HELPER_LD(ld, ldq, int64_t)
@@ -266,6 +265,29 @@ target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1,
                        (uint64_t)(uint32_t)arg2);
 }
 
+static inline target_ulong bitswap(target_ulong v)
+{
+    v = ((v >> 1) & (target_ulong)0x5555555555555555) |
+              ((v & (target_ulong)0x5555555555555555) << 1);
+    v = ((v >> 2) & (target_ulong)0x3333333333333333) |
+              ((v & (target_ulong)0x3333333333333333) << 2);
+    v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0F) |
+              ((v & (target_ulong)0x0F0F0F0F0F0F0F0F) << 4);
+    return v;
+}
+
+#ifdef TARGET_MIPS64
+target_ulong helper_dbitswap(target_ulong rt)
+{
+    return bitswap(rt);
+}
+#endif
+
+target_ulong helper_bitswap(target_ulong rt)
+{
+    return (int32_t)bitswap(rt);
+}
+
 #ifndef CONFIG_USER_ONLY
 
 static inline hwaddr do_translate_address(CPUMIPSState *env,
@@ -2786,6 +2808,110 @@ FLOAT_UNOP(abs)
 FLOAT_UNOP(chs)
 #undef FLOAT_UNOP
 
+#define FLOAT_FMADDSUB(name, bits, muladd_arg)                          \
+uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env,            \
+                                          uint ## bits ## _t fs,        \
+                                          uint ## bits ## _t ft,        \
+                                          uint ## bits ## _t fd)        \
+{                                                                       \
+    uint ## bits ## _t fdret;                                           \
+                                                                        \
+    fdret = float ## bits ## _muladd(fs, ft, fd, muladd_arg,            \
+                                     &env->active_fpu.fp_status);       \
+    update_fcr31(env, GETPC());                                         \
+    return fdret;                                                       \
+}
+
+FLOAT_FMADDSUB(maddf_s, 32, 0)
+FLOAT_FMADDSUB(maddf_d, 64, 0)
+FLOAT_FMADDSUB(msubf_s, 32, float_muladd_negate_product)
+FLOAT_FMADDSUB(msubf_d, 64, float_muladd_negate_product)
+#undef FLOAT_FMADDSUB
+
+#define FLOAT_MINMAX(name, bits, minmaxfunc)                            \
+uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env,            \
+                                          uint ## bits ## _t fs,        \
+                                          uint ## bits ## _t ft)        \
+{                                                                       \
+    uint ## bits ## _t fdret;                                           \
+                                                                        \
+    fdret = float ## bits ## _ ## minmaxfunc(fs, ft,                    \
+                                           &env->active_fpu.fp_status); \
+    update_fcr31(env, GETPC());                                         \
+    return fdret;                                                       \
+}
+
+FLOAT_MINMAX(max_s, 32, maxnum)
+FLOAT_MINMAX(max_d, 64, maxnum)
+FLOAT_MINMAX(maxa_s, 32, maxnummag)
+FLOAT_MINMAX(maxa_d, 64, maxnummag)
+
+FLOAT_MINMAX(min_s, 32, minnum)
+FLOAT_MINMAX(min_d, 64, minnum)
+FLOAT_MINMAX(mina_s, 32, minnummag)
+FLOAT_MINMAX(mina_d, 64, minnummag)
+#undef FLOAT_MINMAX
+
+#define FLOAT_RINT(name, bits)                                              \
+uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env,                \
+                                          uint ## bits ## _t fs)            \
+{                                                                           \
+    uint ## bits ## _t fdret;                                               \
+                                                                            \
+    fdret = float ## bits ## _round_to_int(fs, &env->active_fpu.fp_status); \
+    update_fcr31(env, GETPC());                                             \
+    return fdret;                                                           \
+}
+
+FLOAT_RINT(rint_s, 32)
+FLOAT_RINT(rint_d, 64)
+#undef FLOAT_RINT
+
+#define FLOAT_CLASS_SIGNALING_NAN      0x001
+#define FLOAT_CLASS_QUIET_NAN          0x002
+#define FLOAT_CLASS_NEGATIVE_INFINITY  0x004
+#define FLOAT_CLASS_NEGATIVE_NORMAL    0x008
+#define FLOAT_CLASS_NEGATIVE_SUBNORMAL 0x010
+#define FLOAT_CLASS_NEGATIVE_ZERO      0x020
+#define FLOAT_CLASS_POSITIVE_INFINITY  0x040
+#define FLOAT_CLASS_POSITIVE_NORMAL    0x080
+#define FLOAT_CLASS_POSITIVE_SUBNORMAL 0x100
+#define FLOAT_CLASS_POSITIVE_ZERO      0x200
+
+#define FLOAT_CLASS(name, bits)                                      \
+uint ## bits ## _t helper_float_ ## name (uint ## bits ## _t arg)    \
+{                                                                    \
+    if (float ## bits ## _is_signaling_nan(arg)) {                   \
+        return FLOAT_CLASS_SIGNALING_NAN;                            \
+    } else if (float ## bits ## _is_quiet_nan(arg)) {                \
+        return FLOAT_CLASS_QUIET_NAN;                                \
+    } else if (float ## bits ## _is_neg(arg)) {                      \
+        if (float ## bits ## _is_infinity(arg)) {                    \
+            return FLOAT_CLASS_NEGATIVE_INFINITY;                    \
+        } else if (float ## bits ## _is_zero(arg)) {                 \
+            return FLOAT_CLASS_NEGATIVE_ZERO;                        \
+        } else if (float ## bits ## _is_zero_or_denormal(arg)) {     \
+            return FLOAT_CLASS_NEGATIVE_SUBNORMAL;                   \
+        } else {                                                     \
+            return FLOAT_CLASS_NEGATIVE_NORMAL;                      \
+        }                                                            \
+    } else {                                                         \
+        if (float ## bits ## _is_infinity(arg)) {                    \
+            return FLOAT_CLASS_POSITIVE_INFINITY;                    \
+        } else if (float ## bits ## _is_zero(arg)) {                 \
+            return FLOAT_CLASS_POSITIVE_ZERO;                        \
+        } else if (float ## bits ## _is_zero_or_denormal(arg)) {     \
+            return FLOAT_CLASS_POSITIVE_SUBNORMAL;                   \
+        } else {                                                     \
+            return FLOAT_CLASS_POSITIVE_NORMAL;                      \
+        }                                                            \
+    }                                                                \
+}
+
+FLOAT_CLASS(class_s, 32)
+FLOAT_CLASS(class_d, 64)
+#undef FLOAT_CLASS
+
 /* MIPS specific unary operations */
 uint64_t helper_float_recip_d(CPUMIPSState *env, uint64_t fdt0)
 {
@@ -3261,3 +3387,114 @@ FOP_COND_PS(le,  float32_le(fst0, fst1, &env->active_fpu.fp_status),
                  float32_le(fsth0, fsth1, &env->active_fpu.fp_status))
 FOP_COND_PS(ngt, float32_unordered(fst1, fst0, &env->active_fpu.fp_status)    || float32_le(fst0, fst1, &env->active_fpu.fp_status),
                  float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status)  || float32_le(fsth0, fsth1, &env->active_fpu.fp_status))
+
+/* R6 compare operations */
+#define FOP_CONDN_D(op, cond)                                       \
+uint64_t helper_r6_cmp_d_ ## op(CPUMIPSState * env, uint64_t fdt0,  \
+                         uint64_t fdt1)                             \
+{                                                                   \
+    uint64_t c;                                                     \
+    c = cond;                                                       \
+    update_fcr31(env, GETPC());                                     \
+    if (c) {                                                        \
+        return -1;                                                  \
+    } else {                                                        \
+        return 0;                                                   \
+    }                                                               \
+}
+
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float64_unordered_quiet() is still called. */
+FOP_CONDN_D(af,  (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status), 0))
+FOP_CONDN_D(un,  (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status)))
+FOP_CONDN_D(eq,  (float64_eq_quiet(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(ueq, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status)
+                  || float64_eq_quiet(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(lt,  (float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(ult, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status)
+                  || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(le,  (float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(ule, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status)
+                  || float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status)))
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float64_unordered() is still called. */
+FOP_CONDN_D(saf,  (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status), 0))
+FOP_CONDN_D(sun,  (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status)))
+FOP_CONDN_D(seq,  (float64_eq(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(sueq, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_eq(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(slt,  (float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(sult, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(sle,  (float64_le(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(sule, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_le(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(or,   (float64_le_quiet(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(une,  (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_lt_quiet(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(ne,   (float64_lt_quiet(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(sor,  (float64_le(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_le(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(sune, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_lt(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)))
+FOP_CONDN_D(sne,  (float64_lt(fdt1, fdt0, &env->active_fpu.fp_status)
+                   || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)))
+
+#define FOP_CONDN_S(op, cond)                                       \
+uint32_t helper_r6_cmp_s_ ## op(CPUMIPSState * env, uint32_t fst0,  \
+                         uint32_t fst1)                             \
+{                                                                   \
+    uint64_t c;                                                     \
+    c = cond;                                                       \
+    update_fcr31(env, GETPC());                                     \
+    if (c) {                                                        \
+        return -1;                                                  \
+    } else {                                                        \
+        return 0;                                                   \
+    }                                                               \
+}
+
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float32_unordered_quiet() is still called. */
+FOP_CONDN_S(af,   (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status), 0))
+FOP_CONDN_S(un,   (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)))
+FOP_CONDN_S(eq,   (float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(ueq,  (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(lt,   (float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(ult,  (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(le,   (float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(ule,  (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status)))
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float32_unordered() is still called. */
+FOP_CONDN_S(saf,  (float32_unordered(fst1, fst0, &env->active_fpu.fp_status), 0))
+FOP_CONDN_S(sun,  (float32_unordered(fst1, fst0, &env->active_fpu.fp_status)))
+FOP_CONDN_S(seq,  (float32_eq(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(sueq, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_eq(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(slt,  (float32_lt(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(sult, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_lt(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(sle,  (float32_le(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(sule, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_le(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(or,   (float32_le_quiet(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(une,  (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_lt_quiet(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(ne,   (float32_lt_quiet(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(sor,  (float32_le(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_le(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(sune, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_lt(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_lt(fst0, fst1, &env->active_fpu.fp_status)))
+FOP_CONDN_S(sne,  (float32_lt(fst1, fst0, &env->active_fpu.fp_status)
+                   || float32_lt(fst0, fst1, &env->active_fpu.fp_status)))
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 06db150325..446eb8a20f 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -65,7 +65,6 @@ enum {
     /* Jump and branches */
     OPC_J        = (0x02 << 26),
     OPC_JAL      = (0x03 << 26),
-    OPC_JALS     = OPC_JAL | 0x5,
     OPC_BEQ      = (0x04 << 26),  /* Unconditional if rs = rt = 0 (B) */
     OPC_BEQL     = (0x14 << 26),
     OPC_BNE      = (0x05 << 26),
@@ -74,8 +73,8 @@ enum {
     OPC_BLEZL    = (0x16 << 26),
     OPC_BGTZ     = (0x07 << 26),
     OPC_BGTZL    = (0x17 << 26),
-    OPC_JALX     = (0x1D << 26),  /* MIPS 16 only */
-    OPC_JALXS    = OPC_JALX | 0x5,
+    OPC_JALX     = (0x1D << 26),
+    OPC_DAUI     = (0x1D << 26),
     /* Load and stores */
     OPC_LDL      = (0x1A << 26),
     OPC_LDR      = (0x1B << 26),
@@ -111,13 +110,55 @@ enum {
     OPC_SWC2     = (0x3A << 26),
     OPC_SDC1     = (0x3D << 26),
     OPC_SDC2     = (0x3E << 26),
+    /* Compact Branches */
+    OPC_BLEZALC  = (0x06 << 26),
+    OPC_BGEZALC  = (0x06 << 26),
+    OPC_BGEUC    = (0x06 << 26),
+    OPC_BGTZALC  = (0x07 << 26),
+    OPC_BLTZALC  = (0x07 << 26),
+    OPC_BLTUC    = (0x07 << 26),
+    OPC_BOVC     = (0x08 << 26),
+    OPC_BEQZALC  = (0x08 << 26),
+    OPC_BEQC     = (0x08 << 26),
+    OPC_BLEZC    = (0x16 << 26),
+    OPC_BGEZC    = (0x16 << 26),
+    OPC_BGEC     = (0x16 << 26),
+    OPC_BGTZC    = (0x17 << 26),
+    OPC_BLTZC    = (0x17 << 26),
+    OPC_BLTC     = (0x17 << 26),
+    OPC_BNVC     = (0x18 << 26),
+    OPC_BNEZALC  = (0x18 << 26),
+    OPC_BNEC     = (0x18 << 26),
+    OPC_BC       = (0x32 << 26),
+    OPC_BEQZC    = (0x36 << 26),
+    OPC_JIC      = (0x36 << 26),
+    OPC_BALC     = (0x3A << 26),
+    OPC_BNEZC    = (0x3E << 26),
+    OPC_JIALC    = (0x3E << 26),
     /* MDMX ASE specific */
     OPC_MDMX     = (0x1E << 26),
     /* Cache and prefetch */
     OPC_CACHE    = (0x2F << 26),
     OPC_PREF     = (0x33 << 26),
-    /* Reserved major opcode */
-    OPC_MAJOR3B_RESERVED = (0x3B << 26),
+    /* PC-relative address computation / loads */
+    OPC_PCREL    = (0x3B << 26),
+};
+
+/* PC-relative address computation / loads  */
+#define MASK_OPC_PCREL_TOP2BITS(op)  (MASK_OP_MAJOR(op) | (op & (3 << 19)))
+#define MASK_OPC_PCREL_TOP5BITS(op)  (MASK_OP_MAJOR(op) | (op & (0x1f << 16)))
+enum {
+    /* Instructions determined by bits 19 and 20 */
+    OPC_ADDIUPC = OPC_PCREL | (0 << 19),
+    R6_OPC_LWPC = OPC_PCREL | (1 << 19),
+    OPC_LWUPC   = OPC_PCREL | (2 << 19),
+
+    /* Instructions determined by bits 16 ... 20 */
+    OPC_AUIPC   = OPC_PCREL | (0x1e << 16),
+    OPC_ALUIPC  = OPC_PCREL | (0x1f << 16),
+
+    /* Other */
+    R6_OPC_LDPC = OPC_PCREL | (6 << 18),
 };
 
 /* MIPS special opcodes */
@@ -157,6 +198,7 @@ enum {
     OPC_DMULTU   = 0x1D | OPC_SPECIAL,
     OPC_DDIV     = 0x1E | OPC_SPECIAL,
     OPC_DDIVU    = 0x1F | OPC_SPECIAL,
+
     /* 2 registers arithmetic / logic */
     OPC_ADD      = 0x20 | OPC_SPECIAL,
     OPC_ADDU     = 0x21 | OPC_SPECIAL,
@@ -175,8 +217,6 @@ enum {
     /* Jumps */
     OPC_JR       = 0x08 | OPC_SPECIAL, /* Also JR.HB */
     OPC_JALR     = 0x09 | OPC_SPECIAL, /* Also JALR.HB */
-    OPC_JALRC    = OPC_JALR | (0x5 << 6),
-    OPC_JALRS    = 0x10 | OPC_SPECIAL | (0x5 << 6),
     /* Traps */
     OPC_TGE      = 0x30 | OPC_SPECIAL,
     OPC_TGEU     = 0x31 | OPC_SPECIAL,
@@ -193,6 +233,9 @@ enum {
     OPC_MOVZ     = 0x0A | OPC_SPECIAL,
     OPC_MOVN     = 0x0B | OPC_SPECIAL,
 
+    OPC_SELEQZ   = 0x35 | OPC_SPECIAL,
+    OPC_SELNEZ   = 0x37 | OPC_SPECIAL,
+
     OPC_MOVCI    = 0x01 | OPC_SPECIAL,
 
     /* Special */
@@ -202,15 +245,45 @@ enum {
     OPC_SPIM     = 0x0E | OPC_SPECIAL, /* unofficial */
     OPC_SYNC     = 0x0F | OPC_SPECIAL,
 
-    OPC_SPECIAL15_RESERVED = 0x15 | OPC_SPECIAL,
     OPC_SPECIAL28_RESERVED = 0x28 | OPC_SPECIAL,
     OPC_SPECIAL29_RESERVED = 0x29 | OPC_SPECIAL,
-    OPC_SPECIAL35_RESERVED = 0x35 | OPC_SPECIAL,
-    OPC_SPECIAL37_RESERVED = 0x37 | OPC_SPECIAL,
     OPC_SPECIAL39_RESERVED = 0x39 | OPC_SPECIAL,
     OPC_SPECIAL3D_RESERVED = 0x3D | OPC_SPECIAL,
 };
 
+/* R6 Multiply and Divide instructions have the same Opcode
+   and function field as legacy OPC_MULT[U]/OPC_DIV[U] */
+#define MASK_R6_MULDIV(op)   (MASK_SPECIAL(op) | (op & (0x7ff)))
+
+enum {
+    R6_OPC_MUL   = OPC_MULT  | (2 << 6),
+    R6_OPC_MUH   = OPC_MULT  | (3 << 6),
+    R6_OPC_MULU  = OPC_MULTU | (2 << 6),
+    R6_OPC_MUHU  = OPC_MULTU | (3 << 6),
+    R6_OPC_DIV   = OPC_DIV   | (2 << 6),
+    R6_OPC_MOD   = OPC_DIV   | (3 << 6),
+    R6_OPC_DIVU  = OPC_DIVU  | (2 << 6),
+    R6_OPC_MODU  = OPC_DIVU  | (3 << 6),
+
+    R6_OPC_DMUL   = OPC_DMULT  | (2 << 6),
+    R6_OPC_DMUH   = OPC_DMULT  | (3 << 6),
+    R6_OPC_DMULU  = OPC_DMULTU | (2 << 6),
+    R6_OPC_DMUHU  = OPC_DMULTU | (3 << 6),
+    R6_OPC_DDIV   = OPC_DDIV   | (2 << 6),
+    R6_OPC_DMOD   = OPC_DDIV   | (3 << 6),
+    R6_OPC_DDIVU  = OPC_DDIVU  | (2 << 6),
+    R6_OPC_DMODU  = OPC_DDIVU  | (3 << 6),
+
+    R6_OPC_CLZ      = 0x10 | OPC_SPECIAL,
+    R6_OPC_CLO      = 0x11 | OPC_SPECIAL,
+    R6_OPC_DCLZ     = 0x12 | OPC_SPECIAL,
+    R6_OPC_DCLO     = 0x13 | OPC_SPECIAL,
+    R6_OPC_SDBBP    = 0x0e | OPC_SPECIAL,
+
+    OPC_LSA  = 0x05 | OPC_SPECIAL,
+    OPC_DLSA = 0x15 | OPC_SPECIAL,
+};
+
 /* Multiplication variants of the vr54xx. */
 #define MASK_MUL_VR54XX(op)   MASK_SPECIAL(op) | (op & (0x1F << 6))
 
@@ -240,10 +313,8 @@ enum {
     OPC_BGEZ     = (0x01 << 16) | OPC_REGIMM,
     OPC_BGEZL    = (0x03 << 16) | OPC_REGIMM,
     OPC_BLTZAL   = (0x10 << 16) | OPC_REGIMM,
-    OPC_BLTZALS  = OPC_BLTZAL | 0x5, /* microMIPS */
     OPC_BLTZALL  = (0x12 << 16) | OPC_REGIMM,
     OPC_BGEZAL   = (0x11 << 16) | OPC_REGIMM,
-    OPC_BGEZALS  = OPC_BGEZAL | 0x5, /* microMIPS */
     OPC_BGEZALL  = (0x13 << 16) | OPC_REGIMM,
     OPC_TGEI     = (0x08 << 16) | OPC_REGIMM,
     OPC_TGEIU    = (0x09 << 16) | OPC_REGIMM,
@@ -252,6 +323,9 @@ enum {
     OPC_TEQI     = (0x0C << 16) | OPC_REGIMM,
     OPC_TNEI     = (0x0E << 16) | OPC_REGIMM,
     OPC_SYNCI    = (0x1F << 16) | OPC_REGIMM,
+
+    OPC_DAHI     = (0x06 << 16) | OPC_REGIMM,
+    OPC_DATI     = (0x1e << 16) | OPC_REGIMM,
 };
 
 /* Special2 opcodes */
@@ -346,23 +420,37 @@ enum {
     /* MIPS DSP Accumulator and DSPControl Access Sub-class */
     OPC_EXTR_W_DSP     = 0x38 | OPC_SPECIAL3,
     OPC_DEXTR_W_DSP    = 0x3C | OPC_SPECIAL3,
+
+    /* R6 */
+    R6_OPC_PREF        = 0x35 | OPC_SPECIAL3,
+    R6_OPC_CACHE       = 0x25 | OPC_SPECIAL3,
+    R6_OPC_LL          = 0x36 | OPC_SPECIAL3,
+    R6_OPC_SC          = 0x26 | OPC_SPECIAL3,
+    R6_OPC_LLD         = 0x37 | OPC_SPECIAL3,
+    R6_OPC_SCD         = 0x27 | OPC_SPECIAL3,
 };
 
 /* BSHFL opcodes */
 #define MASK_BSHFL(op)     MASK_SPECIAL3(op) | (op & (0x1F << 6))
 
 enum {
-    OPC_WSBH     = (0x02 << 6) | OPC_BSHFL,
-    OPC_SEB      = (0x10 << 6) | OPC_BSHFL,
-    OPC_SEH      = (0x18 << 6) | OPC_BSHFL,
+    OPC_WSBH      = (0x02 << 6) | OPC_BSHFL,
+    OPC_SEB       = (0x10 << 6) | OPC_BSHFL,
+    OPC_SEH       = (0x18 << 6) | OPC_BSHFL,
+    OPC_ALIGN     = (0x08 << 6) | OPC_BSHFL, /* 010.bp */
+    OPC_ALIGN_END = (0x0B << 6) | OPC_BSHFL, /* 010.00 to 010.11 */
+    OPC_BITSWAP   = (0x00 << 6) | OPC_BSHFL  /* 00000 */
 };
 
 /* DBSHFL opcodes */
 #define MASK_DBSHFL(op)    MASK_SPECIAL3(op) | (op & (0x1F << 6))
 
 enum {
-    OPC_DSBH     = (0x02 << 6) | OPC_DBSHFL,
-    OPC_DSHD     = (0x05 << 6) | OPC_DBSHFL,
+    OPC_DSBH       = (0x02 << 6) | OPC_DBSHFL,
+    OPC_DSHD       = (0x05 << 6) | OPC_DBSHFL,
+    OPC_DALIGN     = (0x08 << 6) | OPC_DBSHFL, /* 01.bp */
+    OPC_DALIGN_END = (0x0F << 6) | OPC_DBSHFL, /* 01.000 to 01.111 */
+    OPC_DBITSWAP   = (0x00 << 6) | OPC_DBSHFL, /* 00000 */
 };
 
 /* MIPS DSP REGIMM opcodes */
@@ -851,6 +939,8 @@ enum {
     OPC_W_FMT    = (FMT_W << 21) | OPC_CP1,
     OPC_L_FMT    = (FMT_L << 21) | OPC_CP1,
     OPC_PS_FMT   = (FMT_PS << 21) | OPC_CP1,
+    OPC_BC1EQZ   = (0x09 << 21) | OPC_CP1,
+    OPC_BC1NEZ   = (0x0D << 21) | OPC_CP1,
 };
 
 #define MASK_CP1_FUNC(op)       MASK_CP1(op) | (op & 0x3F)
@@ -885,6 +975,8 @@ enum {
     OPC_CTC2    = (0x06 << 21) | OPC_CP2,
     OPC_MTHC2   = (0x07 << 21) | OPC_CP2,
     OPC_BC2     = (0x08 << 21) | OPC_CP2,
+    OPC_BC2EQZ  = (0x09 << 21) | OPC_CP2,
+    OPC_BC2NEZ  = (0x0D << 21) | OPC_CP2,
 };
 
 #define MASK_LMI(op)  (MASK_OP_MAJOR(op) | (op & (0x1F << 21)) | (op & 0x1F))
@@ -1012,7 +1104,7 @@ enum {
 /* global register indices */
 static TCGv_ptr cpu_env;
 static TCGv cpu_gpr[32], cpu_PC;
-static TCGv cpu_HI[MIPS_DSP_ACC], cpu_LO[MIPS_DSP_ACC], cpu_ACX[MIPS_DSP_ACC];
+static TCGv cpu_HI[MIPS_DSP_ACC], cpu_LO[MIPS_DSP_ACC];
 static TCGv cpu_dspctrl, btarget, bcond;
 static TCGv_i32 hflags;
 static TCGv_i32 fpu_fcr0, fpu_fcr31;
@@ -1103,10 +1195,6 @@ static const char * const regnames_LO[] = {
     "LO0", "LO1", "LO2", "LO3",
 };
 
-static const char * const regnames_ACX[] = {
-    "ACX0", "ACX1", "ACX2", "ACX3",
-};
-
 static const char * const fregnames[] = {
     "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7",
     "f8",  "f9",  "f10", "f11", "f12", "f13", "f14", "f15",
@@ -1149,17 +1237,6 @@ static inline void gen_store_gpr (TCGv t, int reg)
         tcg_gen_mov_tl(cpu_gpr[reg], t);
 }
 
-/* Moves to/from ACX register.  */
-static inline void gen_load_ACX (TCGv t, int reg)
-{
-    tcg_gen_mov_tl(t, cpu_ACX[reg]);
-}
-
-static inline void gen_store_ACX (TCGv t, int reg)
-{
-    tcg_gen_mov_tl(cpu_ACX[reg], t);
-}
-
 /* Moves to/from shadow registers. */
 static inline void gen_load_srsgpr (int from, int to)
 {
@@ -1343,16 +1420,26 @@ static inline void gen_op_addr_add (DisasContext *ctx, TCGv ret, TCGv arg0, TCGv
     tcg_gen_add_tl(ret, arg0, arg1);
 
 #if defined(TARGET_MIPS64)
-    /* For compatibility with 32-bit code, data reference in user mode
-       with Status_UX = 0 should be casted to 32-bit and sign extended.
-       See the MIPS64 PRA manual, section 4.10. */
-    if (((ctx->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM) &&
-        !(ctx->hflags & MIPS_HFLAG_UX)) {
+    if (ctx->hflags & MIPS_HFLAG_AWRAP) {
         tcg_gen_ext32s_i64(ret, ret);
     }
 #endif
 }
 
+/* Addresses computation (translation time) */
+static target_long addr_add(DisasContext *ctx, target_long base,
+                            target_long offset)
+{
+    target_long sum = base + offset;
+
+#if defined(TARGET_MIPS64)
+    if (ctx->hflags & MIPS_HFLAG_AWRAP) {
+        sum = (int32_t)sum;
+    }
+#endif
+    return sum;
+}
+
 static inline void check_cp0_enabled(DisasContext *ctx)
 {
     if (unlikely(!(ctx->hflags & MIPS_HFLAG_CP0)))
@@ -1436,6 +1523,17 @@ static inline void check_insn(DisasContext *ctx, int flags)
     }
 }
 
+/* This code generates a "reserved instruction" exception if the
+   CPU has corresponding flag set which indicates that the instruction
+   has been removed. */
+static inline void check_insn_opc_removed(DisasContext *ctx, int flags)
+{
+    if (unlikely(ctx->insn_flags & flags)) {
+        generate_exception(ctx, EXCP_RI);
+    }
+}
+
+#ifdef TARGET_MIPS64
 /* This code generates a "reserved instruction" exception if 64-bit
    instructions are not enabled. */
 static inline void check_mips_64(DisasContext *ctx)
@@ -1443,6 +1541,7 @@ static inline void check_mips_64(DisasContext *ctx)
     if (unlikely(!(ctx->hflags & MIPS_HFLAG_64)))
         generate_exception(ctx, EXCP_RI);
 }
+#endif
 
 /* Define small wrappers for gen_load_fpr* so that we have a uniform
    calling interface for 32 and 64-bit FPRs.  No sense in changing
@@ -1504,6 +1603,98 @@ FOP_CONDS(abs, 1, s, FMT_S, 32)
 FOP_CONDS(, 0, ps, FMT_PS, 64)
 FOP_CONDS(abs, 1, ps, FMT_PS, 64)
 #undef FOP_CONDS
+
+#define FOP_CONDNS(fmt, ifmt, bits, STORE)                              \
+static inline void gen_r6_cmp_ ## fmt(DisasContext * ctx, int n,        \
+                                      int ft, int fs, int fd)           \
+{                                                                       \
+    TCGv_i ## bits fp0 = tcg_temp_new_i ## bits();                      \
+    TCGv_i ## bits fp1 = tcg_temp_new_i ## bits();                      \
+    switch (ifmt) {                                                     \
+    case FMT_D:                                                         \
+        check_cp1_registers(ctx, fs | ft | fd);                         \
+        break;                                                          \
+    }                                                                   \
+    gen_ldcmp_fpr ## bits(ctx, fp0, fs);                                \
+    gen_ldcmp_fpr ## bits(ctx, fp1, ft);                                \
+    switch (n) {                                                        \
+    case  0:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _af(fp0, cpu_env, fp0, fp1);       \
+        break;                                                          \
+    case  1:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _un(fp0, cpu_env, fp0, fp1);       \
+        break;                                                          \
+    case  2:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _eq(fp0, cpu_env, fp0, fp1);       \
+        break;                                                          \
+    case  3:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _ueq(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    case  4:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _lt(fp0, cpu_env, fp0, fp1);       \
+        break;                                                          \
+    case  5:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _ult(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    case  6:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _le(fp0, cpu_env, fp0, fp1);       \
+        break;                                                          \
+    case  7:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _ule(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    case  8:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _saf(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    case  9:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _sun(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    case 10:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _seq(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    case 11:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _sueq(fp0, cpu_env, fp0, fp1);     \
+        break;                                                          \
+    case 12:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _slt(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    case 13:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _sult(fp0, cpu_env, fp0, fp1);     \
+        break;                                                          \
+    case 14:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _sle(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    case 15:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _sule(fp0, cpu_env, fp0, fp1);     \
+        break;                                                          \
+    case 17:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _or(fp0, cpu_env, fp0, fp1);       \
+        break;                                                          \
+    case 18:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _une(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    case 19:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _ne(fp0, cpu_env, fp0, fp1);       \
+        break;                                                          \
+    case 25:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _sor(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    case 26:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _sune(fp0, cpu_env, fp0, fp1);     \
+        break;                                                          \
+    case 27:                                                            \
+        gen_helper_r6_cmp_ ## fmt ## _sne(fp0, cpu_env, fp0, fp1);      \
+        break;                                                          \
+    default:                                                            \
+        abort();                                                        \
+    }                                                                   \
+    STORE;                                                              \
+    tcg_temp_free_i ## bits (fp0);                                      \
+    tcg_temp_free_i ## bits (fp1);                                      \
+}
+
+FOP_CONDNS(d, FMT_D, 64, gen_store_fpr64(ctx, fp0, fd))
+FOP_CONDNS(s, FMT_S, 32, gen_store_fpr32(fp0, fd))
+#undef FOP_CONDNS
 #undef gen_ldcmp_fpr32
 #undef gen_ldcmp_fpr64
 
@@ -1630,6 +1821,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
         opn = "ld";
         break;
     case OPC_LLD:
+    case R6_OPC_LLD:
         save_cpu_state(ctx, 1);
         op_ld_lld(t0, t0, ctx);
         gen_store_gpr(t0, rt);
@@ -1764,6 +1956,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
         opn = "lwr";
         break;
     case OPC_LL:
+    case R6_OPC_LL:
         save_cpu_state(ctx, 1);
         op_ld_ll(t0, t0, ctx);
         gen_store_gpr(t0, rt);
@@ -1851,12 +2044,14 @@ static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt,
     switch (opc) {
 #if defined(TARGET_MIPS64)
     case OPC_SCD:
+    case R6_OPC_SCD:
         save_cpu_state(ctx, 1);
         op_st_scd(t1, t0, rt, ctx);
         opn = "scd";
         break;
 #endif
     case OPC_SC:
+    case R6_OPC_SC:
         save_cpu_state(ctx, 1);
         op_st_sc(t1, t0, rt, ctx);
         opn = "sc";
@@ -2063,8 +2258,15 @@ static void gen_logic_imm(DisasContext *ctx, uint32_t opc,
                    regnames[rs], uimm);
         break;
     case OPC_LUI:
-        tcg_gen_movi_tl(cpu_gpr[rt], imm << 16);
-        MIPS_DEBUG("lui %s, " TARGET_FMT_lx, regnames[rt], uimm);
+        if (rs != 0 && (ctx->insn_flags & ISA_MIPS32R6)) {
+            /* OPC_AUI */
+            tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], imm << 16);
+            tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]);
+            MIPS_DEBUG("aui %s, %s, %04x", regnames[rt], regnames[rs], imm);
+        } else {
+            tcg_gen_movi_tl(cpu_gpr[rt], imm << 16);
+            MIPS_DEBUG("lui %s, " TARGET_FMT_lx, regnames[rt], uimm);
+        }
         break;
 
     default:
@@ -2402,6 +2604,14 @@ static void gen_cond_move(DisasContext *ctx, uint32_t opc,
         tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr[rd], t0, t1, t2, cpu_gpr[rd]);
         opn = "movz";
         break;
+    case OPC_SELNEZ:
+        tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rd], t0, t1, t2, t1);
+        opn = "selnez";
+        break;
+    case OPC_SELEQZ:
+        tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr[rd], t0, t1, t2, t1);
+        opn = "seleqz";
+        break;
     }
     tcg_temp_free(t2);
     tcg_temp_free(t1);
@@ -2660,6 +2870,309 @@ static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg)
     MIPS_DEBUG("%s %s", opn, regnames[reg]);
 }
 
+static inline void gen_r6_ld(target_long addr, int reg, int memidx,
+                             TCGMemOp memop)
+{
+    TCGv t0 = tcg_const_tl(addr);
+    tcg_gen_qemu_ld_tl(t0, t0, memidx, memop);
+    gen_store_gpr(t0, reg);
+    tcg_temp_free(t0);
+}
+
+static inline void gen_pcrel(DisasContext *ctx, int rs, int16_t imm)
+{
+    target_long offset;
+    target_long addr;
+
+    switch (MASK_OPC_PCREL_TOP2BITS(ctx->opcode)) {
+    case OPC_ADDIUPC:
+        if (rs != 0) {
+            offset = sextract32(ctx->opcode << 2, 0, 21);
+            addr = addr_add(ctx, ctx->pc, offset);
+            tcg_gen_movi_tl(cpu_gpr[rs], addr);
+        }
+        break;
+    case R6_OPC_LWPC:
+        offset = sextract32(ctx->opcode << 2, 0, 21);
+        addr = addr_add(ctx, ctx->pc, offset);
+        gen_r6_ld(addr, rs, ctx->mem_idx, MO_TESL);
+        break;
+#if defined(TARGET_MIPS64)
+    case OPC_LWUPC:
+        check_mips_64(ctx);
+        offset = sextract32(ctx->opcode << 2, 0, 21);
+        addr = addr_add(ctx, ctx->pc, offset);
+        gen_r6_ld(addr, rs, ctx->mem_idx, MO_TEUL);
+        break;
+#endif
+    default:
+        switch (MASK_OPC_PCREL_TOP5BITS(ctx->opcode)) {
+        case OPC_AUIPC:
+            if (rs != 0) {
+                offset = imm << 16;
+                addr = addr_add(ctx, ctx->pc, offset);
+                tcg_gen_movi_tl(cpu_gpr[rs], addr);
+            }
+            break;
+        case OPC_ALUIPC:
+            if (rs != 0) {
+                offset = imm << 16;
+                addr = ~0xFFFF & addr_add(ctx, ctx->pc, offset);
+                tcg_gen_movi_tl(cpu_gpr[rs], addr);
+            }
+            break;
+#if defined(TARGET_MIPS64)
+        case R6_OPC_LDPC: /* bits 16 and 17 are part of immediate */
+        case R6_OPC_LDPC + (1 << 16):
+        case R6_OPC_LDPC + (2 << 16):
+        case R6_OPC_LDPC + (3 << 16):
+            check_mips_64(ctx);
+            offset = sextract32(ctx->opcode << 3, 0, 21);
+            addr = addr_add(ctx, (ctx->pc & ~0x7), offset);
+            gen_r6_ld(addr, rs, ctx->mem_idx, MO_TEQ);
+            break;
+#endif
+        default:
+            MIPS_INVAL("OPC_PCREL");
+            generate_exception(ctx, EXCP_RI);
+            break;
+        }
+        break;
+    }
+}
+
+static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt)
+{
+    const char *opn = "r6 mul/div";
+    TCGv t0, t1;
+
+    if (rd == 0) {
+        /* Treat as NOP. */
+        MIPS_DEBUG("NOP");
+        return;
+    }
+
+    t0 = tcg_temp_new();
+    t1 = tcg_temp_new();
+
+    gen_load_gpr(t0, rs);
+    gen_load_gpr(t1, rt);
+
+    switch (opc) {
+    case R6_OPC_DIV:
+        {
+            TCGv t2 = tcg_temp_new();
+            TCGv t3 = tcg_temp_new();
+            tcg_gen_ext32s_tl(t0, t0);
+            tcg_gen_ext32s_tl(t1, t1);
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN);
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1);
+            tcg_gen_and_tl(t2, t2, t3);
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0);
+            tcg_gen_or_tl(t2, t2, t3);
+            tcg_gen_movi_tl(t3, 0);
+            tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1);
+            tcg_gen_div_tl(cpu_gpr[rd], t0, t1);
+            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+            tcg_temp_free(t3);
+            tcg_temp_free(t2);
+        }
+        opn = "div";
+        break;
+    case R6_OPC_MOD:
+        {
+            TCGv t2 = tcg_temp_new();
+            TCGv t3 = tcg_temp_new();
+            tcg_gen_ext32s_tl(t0, t0);
+            tcg_gen_ext32s_tl(t1, t1);
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN);
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1);
+            tcg_gen_and_tl(t2, t2, t3);
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0);
+            tcg_gen_or_tl(t2, t2, t3);
+            tcg_gen_movi_tl(t3, 0);
+            tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1);
+            tcg_gen_rem_tl(cpu_gpr[rd], t0, t1);
+            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+            tcg_temp_free(t3);
+            tcg_temp_free(t2);
+        }
+        opn = "mod";
+        break;
+    case R6_OPC_DIVU:
+        {
+            TCGv t2 = tcg_const_tl(0);
+            TCGv t3 = tcg_const_tl(1);
+            tcg_gen_ext32u_tl(t0, t0);
+            tcg_gen_ext32u_tl(t1, t1);
+            tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1);
+            tcg_gen_divu_tl(cpu_gpr[rd], t0, t1);
+            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+            tcg_temp_free(t3);
+            tcg_temp_free(t2);
+        }
+        opn = "divu";
+        break;
+    case R6_OPC_MODU:
+        {
+            TCGv t2 = tcg_const_tl(0);
+            TCGv t3 = tcg_const_tl(1);
+            tcg_gen_ext32u_tl(t0, t0);
+            tcg_gen_ext32u_tl(t1, t1);
+            tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1);
+            tcg_gen_remu_tl(cpu_gpr[rd], t0, t1);
+            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+            tcg_temp_free(t3);
+            tcg_temp_free(t2);
+        }
+        opn = "modu";
+        break;
+    case R6_OPC_MUL:
+        {
+            TCGv_i32 t2 = tcg_temp_new_i32();
+            TCGv_i32 t3 = tcg_temp_new_i32();
+            tcg_gen_trunc_tl_i32(t2, t0);
+            tcg_gen_trunc_tl_i32(t3, t1);
+            tcg_gen_mul_i32(t2, t2, t3);
+            tcg_gen_ext_i32_tl(cpu_gpr[rd], t2);
+            tcg_temp_free_i32(t2);
+            tcg_temp_free_i32(t3);
+        }
+        opn = "mul";
+        break;
+    case R6_OPC_MUH:
+        {
+            TCGv_i32 t2 = tcg_temp_new_i32();
+            TCGv_i32 t3 = tcg_temp_new_i32();
+            tcg_gen_trunc_tl_i32(t2, t0);
+            tcg_gen_trunc_tl_i32(t3, t1);
+            tcg_gen_muls2_i32(t2, t3, t2, t3);
+            tcg_gen_ext_i32_tl(cpu_gpr[rd], t3);
+            tcg_temp_free_i32(t2);
+            tcg_temp_free_i32(t3);
+        }
+        opn = "muh";
+        break;
+    case R6_OPC_MULU:
+        {
+            TCGv_i32 t2 = tcg_temp_new_i32();
+            TCGv_i32 t3 = tcg_temp_new_i32();
+            tcg_gen_trunc_tl_i32(t2, t0);
+            tcg_gen_trunc_tl_i32(t3, t1);
+            tcg_gen_mul_i32(t2, t2, t3);
+            tcg_gen_ext_i32_tl(cpu_gpr[rd], t2);
+            tcg_temp_free_i32(t2);
+            tcg_temp_free_i32(t3);
+        }
+        opn = "mulu";
+        break;
+    case R6_OPC_MUHU:
+        {
+            TCGv_i32 t2 = tcg_temp_new_i32();
+            TCGv_i32 t3 = tcg_temp_new_i32();
+            tcg_gen_trunc_tl_i32(t2, t0);
+            tcg_gen_trunc_tl_i32(t3, t1);
+            tcg_gen_mulu2_i32(t2, t3, t2, t3);
+            tcg_gen_ext_i32_tl(cpu_gpr[rd], t3);
+            tcg_temp_free_i32(t2);
+            tcg_temp_free_i32(t3);
+        }
+        opn = "muhu";
+        break;
+#if defined(TARGET_MIPS64)
+    case R6_OPC_DDIV:
+        {
+            TCGv t2 = tcg_temp_new();
+            TCGv t3 = tcg_temp_new();
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, -1LL << 63);
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1LL);
+            tcg_gen_and_tl(t2, t2, t3);
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0);
+            tcg_gen_or_tl(t2, t2, t3);
+            tcg_gen_movi_tl(t3, 0);
+            tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1);
+            tcg_gen_div_tl(cpu_gpr[rd], t0, t1);
+            tcg_temp_free(t3);
+            tcg_temp_free(t2);
+        }
+        opn = "ddiv";
+        break;
+    case R6_OPC_DMOD:
+        {
+            TCGv t2 = tcg_temp_new();
+            TCGv t3 = tcg_temp_new();
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, -1LL << 63);
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1LL);
+            tcg_gen_and_tl(t2, t2, t3);
+            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0);
+            tcg_gen_or_tl(t2, t2, t3);
+            tcg_gen_movi_tl(t3, 0);
+            tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1);
+            tcg_gen_rem_tl(cpu_gpr[rd], t0, t1);
+            tcg_temp_free(t3);
+            tcg_temp_free(t2);
+        }
+        opn = "dmod";
+        break;
+    case R6_OPC_DDIVU:
+        {
+            TCGv t2 = tcg_const_tl(0);
+            TCGv t3 = tcg_const_tl(1);
+            tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1);
+            tcg_gen_divu_i64(cpu_gpr[rd], t0, t1);
+            tcg_temp_free(t3);
+            tcg_temp_free(t2);
+        }
+        opn = "ddivu";
+        break;
+    case R6_OPC_DMODU:
+        {
+            TCGv t2 = tcg_const_tl(0);
+            TCGv t3 = tcg_const_tl(1);
+            tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1);
+            tcg_gen_remu_i64(cpu_gpr[rd], t0, t1);
+            tcg_temp_free(t3);
+            tcg_temp_free(t2);
+        }
+        opn = "dmodu";
+        break;
+    case R6_OPC_DMUL:
+        tcg_gen_mul_i64(cpu_gpr[rd], t0, t1);
+        opn = "dmul";
+        break;
+    case R6_OPC_DMUH:
+        {
+            TCGv t2 = tcg_temp_new();
+            tcg_gen_muls2_i64(t2, cpu_gpr[rd], t0, t1);
+            tcg_temp_free(t2);
+        }
+        opn = "dmuh";
+        break;
+    case R6_OPC_DMULU:
+        tcg_gen_mul_i64(cpu_gpr[rd], t0, t1);
+        opn = "dmulu";
+        break;
+    case R6_OPC_DMUHU:
+        {
+            TCGv t2 = tcg_temp_new();
+            tcg_gen_mulu2_i64(t2, cpu_gpr[rd], t0, t1);
+            tcg_temp_free(t2);
+        }
+        opn = "dmuhu";
+        break;
+#endif
+    default:
+        MIPS_INVAL(opn);
+        generate_exception(ctx, EXCP_RI);
+        goto out;
+    }
+    (void)opn; /* avoid a compiler warning */
+    MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]);
+ out:
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+}
+
 static void gen_muldiv(DisasContext *ctx, uint32_t opc,
                        int acc, int rs, int rt)
 {
@@ -2975,19 +3488,23 @@ static void gen_cl (DisasContext *ctx, uint32_t opc,
     gen_load_gpr(t0, rs);
     switch (opc) {
     case OPC_CLO:
+    case R6_OPC_CLO:
         gen_helper_clo(cpu_gpr[rd], t0);
         opn = "clo";
         break;
     case OPC_CLZ:
+    case R6_OPC_CLZ:
         gen_helper_clz(cpu_gpr[rd], t0);
         opn = "clz";
         break;
 #if defined(TARGET_MIPS64)
     case OPC_DCLO:
+    case R6_OPC_DCLO:
         gen_helper_dclo(cpu_gpr[rd], t0);
         opn = "dclo";
         break;
     case OPC_DCLZ:
+    case R6_OPC_DCLZ:
         gen_helper_dclz(cpu_gpr[rd], t0);
         opn = "dclz";
         break;
@@ -3601,7 +4118,8 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
 /* Branches (before delay slot) */
 static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
                                 int insn_bytes,
-                                int rs, int rt, int32_t offset)
+                                int rs, int rt, int32_t offset,
+                                int delayslot_size)
 {
     target_ulong btgt = -1;
     int blink = 0;
@@ -3633,7 +4151,6 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
         break;
     case OPC_BGEZ:
     case OPC_BGEZAL:
-    case OPC_BGEZALS:
     case OPC_BGEZALL:
     case OPC_BGEZL:
     case OPC_BGTZ:
@@ -3642,7 +4159,6 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
     case OPC_BLEZL:
     case OPC_BLTZ:
     case OPC_BLTZAL:
-    case OPC_BLTZALS:
     case OPC_BLTZALL:
     case OPC_BLTZL:
         /* Compare to zero */
@@ -3665,15 +4181,11 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
     case OPC_J:
     case OPC_JAL:
     case OPC_JALX:
-    case OPC_JALS:
-    case OPC_JALXS:
         /* Jump to immediate */
         btgt = ((ctx->pc + insn_bytes) & (int32_t)0xF0000000) | (uint32_t)offset;
         break;
     case OPC_JR:
     case OPC_JALR:
-    case OPC_JALRC:
-    case OPC_JALRS:
         /* Jump to register */
         if (offset != 0 && offset != 16) {
             /* Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the
@@ -3702,12 +4214,8 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
             ctx->hflags |= MIPS_HFLAG_B;
             MIPS_DEBUG("balways");
             break;
-        case OPC_BGEZALS:
         case OPC_BGEZAL:  /* 0 >= 0          */
         case OPC_BGEZALL: /* 0 >= 0 likely   */
-            ctx->hflags |= (opc == OPC_BGEZALS
-                            ? MIPS_HFLAG_BDS16
-                            : MIPS_HFLAG_BDS32);
             /* Always take and link */
             blink = 31;
             ctx->hflags |= MIPS_HFLAG_B;
@@ -3719,15 +4227,11 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
             /* Treat as NOP. */
             MIPS_DEBUG("bnever (NOP)");
             goto out;
-        case OPC_BLTZALS:
         case OPC_BLTZAL:  /* 0 < 0           */
-            ctx->hflags |= (opc == OPC_BLTZALS
-                            ? MIPS_HFLAG_BDS16
-                            : MIPS_HFLAG_BDS32);
             /* Handle as an unconditional branch to get correct delay
                slot checking.  */
             blink = 31;
-            btgt = ctx->pc + (opc == OPC_BLTZALS ? 6 : 8);
+            btgt = ctx->pc + insn_bytes + delayslot_size;
             ctx->hflags |= MIPS_HFLAG_B;
             MIPS_DEBUG("bnever and link");
             break;
@@ -3748,33 +4252,21 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
             ctx->hflags |= MIPS_HFLAG_B;
             MIPS_DEBUG("j " TARGET_FMT_lx, btgt);
             break;
-        case OPC_JALXS:
         case OPC_JALX:
             ctx->hflags |= MIPS_HFLAG_BX;
             /* Fallthrough */
-        case OPC_JALS:
         case OPC_JAL:
             blink = 31;
             ctx->hflags |= MIPS_HFLAG_B;
-            ctx->hflags |= ((opc == OPC_JALS || opc == OPC_JALXS)
-                            ? MIPS_HFLAG_BDS16
-                            : MIPS_HFLAG_BDS32);
             MIPS_DEBUG("jal " TARGET_FMT_lx, btgt);
             break;
         case OPC_JR:
             ctx->hflags |= MIPS_HFLAG_BR;
-            if (insn_bytes == 4)
-                ctx->hflags |= MIPS_HFLAG_BDS32;
             MIPS_DEBUG("jr %s", regnames[rs]);
             break;
-        case OPC_JALRS:
         case OPC_JALR:
-        case OPC_JALRC:
             blink = rt;
             ctx->hflags |= MIPS_HFLAG_BR;
-            ctx->hflags |= (opc == OPC_JALRS
-                            ? MIPS_HFLAG_BDS16
-                            : MIPS_HFLAG_BDS32);
             MIPS_DEBUG("jalr %s, %s", regnames[rt], regnames[rs]);
             break;
         default:
@@ -3812,11 +4304,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
             tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
             MIPS_DEBUG("bgezl %s, " TARGET_FMT_lx, regnames[rs], btgt);
             goto likely;
-        case OPC_BGEZALS:
         case OPC_BGEZAL:
-            ctx->hflags |= (opc == OPC_BGEZALS
-                            ? MIPS_HFLAG_BDS16
-                            : MIPS_HFLAG_BDS32);
             tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
             MIPS_DEBUG("bgezal %s, " TARGET_FMT_lx, regnames[rs], btgt);
             blink = 31;
@@ -3860,11 +4348,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
             MIPS_DEBUG("bposge64 " TARGET_FMT_lx, btgt);
             goto not_likely;
 #endif
-        case OPC_BLTZALS:
         case OPC_BLTZAL:
-            ctx->hflags |= (opc == OPC_BLTZALS
-                            ? MIPS_HFLAG_BDS16
-                            : MIPS_HFLAG_BDS32);
             tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0);
             blink = 31;
             MIPS_DEBUG("bltzal %s, " TARGET_FMT_lx, regnames[rs], btgt);
@@ -3888,13 +4372,20 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
                blink, ctx->hflags, btgt);
 
     ctx->btarget = btgt;
+
+    switch (delayslot_size) {
+    case 2:
+        ctx->hflags |= MIPS_HFLAG_BDS16;
+        break;
+    case 4:
+        ctx->hflags |= MIPS_HFLAG_BDS32;
+        break;
+    }
+
     if (blink > 0) {
-        int post_delay = insn_bytes;
+        int post_delay = insn_bytes + delayslot_size;
         int lowbit = !!(ctx->hflags & MIPS_HFLAG_M16);
 
-        if (opc != OPC_JALRC)
-            post_delay += ((ctx->hflags & MIPS_HFLAG_BDS16) ? 2 : 4);
-
         tcg_gen_movi_tl(cpu_gpr[blink], ctx->pc + post_delay + lowbit);
     }
 
@@ -7128,11 +7619,60 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op,
     MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn,
                ctx->hflags, btarget);
     ctx->btarget = btarget;
-
+    ctx->hflags |= MIPS_HFLAG_BDS32;
  out:
     tcg_temp_free_i32(t0);
 }
 
+/* R6 CP1 Branches */
+static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op,
+                                   int32_t ft, int32_t offset)
+{
+    target_ulong btarget;
+    const char *opn = "cp1 cond branch";
+    TCGv_i64 t0 = tcg_temp_new_i64();
+
+    if (ctx->hflags & MIPS_HFLAG_BMASK) {
+#ifdef MIPS_DEBUG_DISAS
+        LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc);
+#endif
+        generate_exception(ctx, EXCP_RI);
+        goto out;
+    }
+
+    gen_load_fpr64(ctx, t0, ft);
+    tcg_gen_andi_i64(t0, t0, 1);
+
+    btarget = addr_add(ctx, ctx->pc + 4, offset);
+
+    switch (op) {
+    case OPC_BC1EQZ:
+        tcg_gen_xori_i64(t0, t0, 1);
+        opn = "bc1eqz";
+        ctx->hflags |= MIPS_HFLAG_BC;
+        break;
+    case OPC_BC1NEZ:
+        /* t0 already set */
+        opn = "bc1nez";
+        ctx->hflags |= MIPS_HFLAG_BC;
+        break;
+    default:
+        MIPS_INVAL(opn);
+        generate_exception(ctx, EXCP_RI);
+        goto out;
+    }
+
+    tcg_gen_trunc_i64_tl(bcond, t0);
+
+    (void)opn; /* avoid a compiler warning */
+    MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn,
+               ctx->hflags, btarget);
+    ctx->btarget = btarget;
+
+out:
+    tcg_temp_free_i64(t0);
+}
+
 /* Coprocessor 1 (FPU) */
 
 #define FOP(func, fmt) (((fmt) << 21) | (func))
@@ -7154,14 +7694,25 @@ enum fopcode {
     OPC_TRUNC_W_S = FOP(13, FMT_S),
     OPC_CEIL_W_S = FOP(14, FMT_S),
     OPC_FLOOR_W_S = FOP(15, FMT_S),
+    OPC_SEL_S = FOP(16, FMT_S),
     OPC_MOVCF_S = FOP(17, FMT_S),
     OPC_MOVZ_S = FOP(18, FMT_S),
     OPC_MOVN_S = FOP(19, FMT_S),
+    OPC_SELEQZ_S = FOP(20, FMT_S),
     OPC_RECIP_S = FOP(21, FMT_S),
     OPC_RSQRT_S = FOP(22, FMT_S),
+    OPC_SELNEZ_S = FOP(23, FMT_S),
+    OPC_MADDF_S = FOP(24, FMT_S),
+    OPC_MSUBF_S = FOP(25, FMT_S),
+    OPC_RINT_S = FOP(26, FMT_S),
+    OPC_CLASS_S = FOP(27, FMT_S),
+    OPC_MIN_S = FOP(28, FMT_S),
     OPC_RECIP2_S = FOP(28, FMT_S),
+    OPC_MINA_S = FOP(29, FMT_S),
     OPC_RECIP1_S = FOP(29, FMT_S),
+    OPC_MAX_S = FOP(30, FMT_S),
     OPC_RSQRT1_S = FOP(30, FMT_S),
+    OPC_MAXA_S = FOP(31, FMT_S),
     OPC_RSQRT2_S = FOP(31, FMT_S),
     OPC_CVT_D_S = FOP(33, FMT_S),
     OPC_CVT_W_S = FOP(36, FMT_S),
@@ -7200,14 +7751,25 @@ enum fopcode {
     OPC_TRUNC_W_D = FOP(13, FMT_D),
     OPC_CEIL_W_D = FOP(14, FMT_D),
     OPC_FLOOR_W_D = FOP(15, FMT_D),
+    OPC_SEL_D = FOP(16, FMT_D),
     OPC_MOVCF_D = FOP(17, FMT_D),
     OPC_MOVZ_D = FOP(18, FMT_D),
     OPC_MOVN_D = FOP(19, FMT_D),
+    OPC_SELEQZ_D = FOP(20, FMT_D),
     OPC_RECIP_D = FOP(21, FMT_D),
     OPC_RSQRT_D = FOP(22, FMT_D),
+    OPC_SELNEZ_D = FOP(23, FMT_D),
+    OPC_MADDF_D = FOP(24, FMT_D),
+    OPC_MSUBF_D = FOP(25, FMT_D),
+    OPC_RINT_D = FOP(26, FMT_D),
+    OPC_CLASS_D = FOP(27, FMT_D),
+    OPC_MIN_D = FOP(28, FMT_D),
     OPC_RECIP2_D = FOP(28, FMT_D),
+    OPC_MINA_D = FOP(29, FMT_D),
     OPC_RECIP1_D = FOP(29, FMT_D),
+    OPC_MAX_D = FOP(30, FMT_D),
     OPC_RSQRT1_D = FOP(30, FMT_D),
+    OPC_MAXA_D = FOP(31, FMT_D),
     OPC_RSQRT2_D = FOP(31, FMT_D),
     OPC_CVT_S_D = FOP(32, FMT_D),
     OPC_CVT_W_D = FOP(36, FMT_D),
@@ -7277,6 +7839,53 @@ enum fopcode {
     OPC_CMP_NGT_PS = FOP (63, FMT_PS),
 };
 
+enum r6_f_cmp_op {
+    R6_OPC_CMP_AF_S   = FOP(0, FMT_W),
+    R6_OPC_CMP_UN_S   = FOP(1, FMT_W),
+    R6_OPC_CMP_EQ_S   = FOP(2, FMT_W),
+    R6_OPC_CMP_UEQ_S  = FOP(3, FMT_W),
+    R6_OPC_CMP_LT_S   = FOP(4, FMT_W),
+    R6_OPC_CMP_ULT_S  = FOP(5, FMT_W),
+    R6_OPC_CMP_LE_S   = FOP(6, FMT_W),
+    R6_OPC_CMP_ULE_S  = FOP(7, FMT_W),
+    R6_OPC_CMP_SAF_S  = FOP(8, FMT_W),
+    R6_OPC_CMP_SUN_S  = FOP(9, FMT_W),
+    R6_OPC_CMP_SEQ_S  = FOP(10, FMT_W),
+    R6_OPC_CMP_SEUQ_S = FOP(11, FMT_W),
+    R6_OPC_CMP_SLT_S  = FOP(12, FMT_W),
+    R6_OPC_CMP_SULT_S = FOP(13, FMT_W),
+    R6_OPC_CMP_SLE_S  = FOP(14, FMT_W),
+    R6_OPC_CMP_SULE_S = FOP(15, FMT_W),
+    R6_OPC_CMP_OR_S   = FOP(17, FMT_W),
+    R6_OPC_CMP_UNE_S  = FOP(18, FMT_W),
+    R6_OPC_CMP_NE_S   = FOP(19, FMT_W),
+    R6_OPC_CMP_SOR_S  = FOP(25, FMT_W),
+    R6_OPC_CMP_SUNE_S = FOP(26, FMT_W),
+    R6_OPC_CMP_SNE_S  = FOP(27, FMT_W),
+
+    R6_OPC_CMP_AF_D   = FOP(0, FMT_L),
+    R6_OPC_CMP_UN_D   = FOP(1, FMT_L),
+    R6_OPC_CMP_EQ_D   = FOP(2, FMT_L),
+    R6_OPC_CMP_UEQ_D  = FOP(3, FMT_L),
+    R6_OPC_CMP_LT_D   = FOP(4, FMT_L),
+    R6_OPC_CMP_ULT_D  = FOP(5, FMT_L),
+    R6_OPC_CMP_LE_D   = FOP(6, FMT_L),
+    R6_OPC_CMP_ULE_D  = FOP(7, FMT_L),
+    R6_OPC_CMP_SAF_D  = FOP(8, FMT_L),
+    R6_OPC_CMP_SUN_D  = FOP(9, FMT_L),
+    R6_OPC_CMP_SEQ_D  = FOP(10, FMT_L),
+    R6_OPC_CMP_SEUQ_D = FOP(11, FMT_L),
+    R6_OPC_CMP_SLT_D  = FOP(12, FMT_L),
+    R6_OPC_CMP_SULT_D = FOP(13, FMT_L),
+    R6_OPC_CMP_SLE_D  = FOP(14, FMT_L),
+    R6_OPC_CMP_SULE_D = FOP(15, FMT_L),
+    R6_OPC_CMP_OR_D   = FOP(17, FMT_L),
+    R6_OPC_CMP_UNE_D  = FOP(18, FMT_L),
+    R6_OPC_CMP_NE_D   = FOP(19, FMT_L),
+    R6_OPC_CMP_SOR_D  = FOP(25, FMT_L),
+    R6_OPC_CMP_SUNE_D = FOP(26, FMT_L),
+    R6_OPC_CMP_SNE_D  = FOP(27, FMT_L),
+};
 static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs)
 {
     const char *opn = "cp1 move";
@@ -7463,6 +8072,79 @@ static inline void gen_movcf_ps(DisasContext *ctx, int fs, int fd,
     gen_set_label(l2);
 }
 
+static void gen_sel_s(DisasContext *ctx, enum fopcode op1, int fd, int ft,
+                      int fs)
+{
+    TCGv_i32 t1 = tcg_const_i32(0);
+    TCGv_i32 fp0 = tcg_temp_new_i32();
+    TCGv_i32 fp1 = tcg_temp_new_i32();
+    TCGv_i32 fp2 = tcg_temp_new_i32();
+    gen_load_fpr32(fp0, fd);
+    gen_load_fpr32(fp1, ft);
+    gen_load_fpr32(fp2, fs);
+
+    switch (op1) {
+    case OPC_SEL_S:
+        tcg_gen_andi_i32(fp0, fp0, 1);
+        tcg_gen_movcond_i32(TCG_COND_NE, fp0, fp0, t1, fp1, fp2);
+        break;
+    case OPC_SELEQZ_S:
+        tcg_gen_andi_i32(fp1, fp1, 1);
+        tcg_gen_movcond_i32(TCG_COND_EQ, fp0, fp1, t1, fp2, t1);
+        break;
+    case OPC_SELNEZ_S:
+        tcg_gen_andi_i32(fp1, fp1, 1);
+        tcg_gen_movcond_i32(TCG_COND_NE, fp0, fp1, t1, fp2, t1);
+        break;
+    default:
+        MIPS_INVAL("gen_sel_s");
+        generate_exception (ctx, EXCP_RI);
+        break;
+    }
+
+    gen_store_fpr32(fp0, fd);
+    tcg_temp_free_i32(fp2);
+    tcg_temp_free_i32(fp1);
+    tcg_temp_free_i32(fp0);
+    tcg_temp_free_i32(t1);
+}
+
+static void gen_sel_d(DisasContext *ctx, enum fopcode op1, int fd, int ft,
+                      int fs)
+{
+    TCGv_i64 t1 = tcg_const_i64(0);
+    TCGv_i64 fp0 = tcg_temp_new_i64();
+    TCGv_i64 fp1 = tcg_temp_new_i64();
+    TCGv_i64 fp2 = tcg_temp_new_i64();
+    gen_load_fpr64(ctx, fp0, fd);
+    gen_load_fpr64(ctx, fp1, ft);
+    gen_load_fpr64(ctx, fp2, fs);
+
+    switch (op1) {
+    case OPC_SEL_D:
+        tcg_gen_andi_i64(fp0, fp0, 1);
+        tcg_gen_movcond_i64(TCG_COND_NE, fp0, fp0, t1, fp1, fp2);
+        break;
+    case OPC_SELEQZ_D:
+        tcg_gen_andi_i64(fp1, fp1, 1);
+        tcg_gen_movcond_i64(TCG_COND_EQ, fp0, fp1, t1, fp2, t1);
+        break;
+    case OPC_SELNEZ_D:
+        tcg_gen_andi_i64(fp1, fp1, 1);
+        tcg_gen_movcond_i64(TCG_COND_NE, fp0, fp1, t1, fp2, t1);
+        break;
+    default:
+        MIPS_INVAL("gen_sel_d");
+        generate_exception (ctx, EXCP_RI);
+        break;
+    }
+
+    gen_store_fpr64(ctx, fp0, fd);
+    tcg_temp_free_i64(fp2);
+    tcg_temp_free_i64(fp1);
+    tcg_temp_free_i64(fp0);
+    tcg_temp_free_i64(t1);
+}
 
 static void gen_farith (DisasContext *ctx, enum fopcode op1,
                         int ft, int fs, int fd, int cc)
@@ -7711,11 +8393,28 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
         }
         opn = "floor.w.s";
         break;
+    case OPC_SEL_S:
+        check_insn(ctx, ISA_MIPS32R6);
+        gen_sel_s(ctx, op1, fd, ft, fs);
+        opn = "sel.s";
+        break;
+    case OPC_SELEQZ_S:
+        check_insn(ctx, ISA_MIPS32R6);
+        gen_sel_s(ctx, op1, fd, ft, fs);
+        opn = "seleqz.s";
+        break;
+    case OPC_SELNEZ_S:
+        check_insn(ctx, ISA_MIPS32R6);
+        gen_sel_s(ctx, op1, fd, ft, fs);
+        opn = "selnez.s";
+        break;
     case OPC_MOVCF_S:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         gen_movcf_s(fs, fd, (ft >> 2) & 0x7, ft & 0x1);
         opn = "movcf.s";
         break;
     case OPC_MOVZ_S:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         {
             int l1 = gen_new_label();
             TCGv_i32 fp0;
@@ -7732,6 +8431,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
         opn = "movz.s";
         break;
     case OPC_MOVN_S:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         {
             int l1 = gen_new_label();
             TCGv_i32 fp0;
@@ -7771,59 +8471,175 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
         }
         opn = "rsqrt.s";
         break;
-    case OPC_RECIP2_S:
-        check_cp1_64bitmode(ctx);
+    case OPC_MADDF_S:
+        check_insn(ctx, ISA_MIPS32R6);
         {
             TCGv_i32 fp0 = tcg_temp_new_i32();
             TCGv_i32 fp1 = tcg_temp_new_i32();
-
+            TCGv_i32 fp2 = tcg_temp_new_i32();
             gen_load_fpr32(fp0, fs);
             gen_load_fpr32(fp1, ft);
-            gen_helper_float_recip2_s(fp0, cpu_env, fp0, fp1);
+            gen_load_fpr32(fp2, fd);
+            gen_helper_float_maddf_s(fp2, cpu_env, fp0, fp1, fp2);
+            gen_store_fpr32(fp2, fd);
+            tcg_temp_free_i32(fp2);
             tcg_temp_free_i32(fp1);
-            gen_store_fpr32(fp0, fd);
             tcg_temp_free_i32(fp0);
+            opn = "maddf.s";
         }
-        opn = "recip2.s";
         break;
-    case OPC_RECIP1_S:
-        check_cp1_64bitmode(ctx);
+    case OPC_MSUBF_S:
+        check_insn(ctx, ISA_MIPS32R6);
         {
             TCGv_i32 fp0 = tcg_temp_new_i32();
-
+            TCGv_i32 fp1 = tcg_temp_new_i32();
+            TCGv_i32 fp2 = tcg_temp_new_i32();
             gen_load_fpr32(fp0, fs);
-            gen_helper_float_recip1_s(fp0, cpu_env, fp0);
-            gen_store_fpr32(fp0, fd);
+            gen_load_fpr32(fp1, ft);
+            gen_load_fpr32(fp2, fd);
+            gen_helper_float_msubf_s(fp2, cpu_env, fp0, fp1, fp2);
+            gen_store_fpr32(fp2, fd);
+            tcg_temp_free_i32(fp2);
+            tcg_temp_free_i32(fp1);
             tcg_temp_free_i32(fp0);
+            opn = "msubf.s";
         }
-        opn = "recip1.s";
         break;
-    case OPC_RSQRT1_S:
-        check_cp1_64bitmode(ctx);
+    case OPC_RINT_S:
+        check_insn(ctx, ISA_MIPS32R6);
         {
             TCGv_i32 fp0 = tcg_temp_new_i32();
-
             gen_load_fpr32(fp0, fs);
-            gen_helper_float_rsqrt1_s(fp0, cpu_env, fp0);
+            gen_helper_float_rint_s(fp0, cpu_env, fp0);
             gen_store_fpr32(fp0, fd);
             tcg_temp_free_i32(fp0);
+            opn = "rint.s";
         }
-        opn = "rsqrt1.s";
         break;
-    case OPC_RSQRT2_S:
-        check_cp1_64bitmode(ctx);
+    case OPC_CLASS_S:
+        check_insn(ctx, ISA_MIPS32R6);
         {
             TCGv_i32 fp0 = tcg_temp_new_i32();
+            gen_load_fpr32(fp0, fs);
+            gen_helper_float_class_s(fp0, fp0);
+            gen_store_fpr32(fp0, fd);
+            tcg_temp_free_i32(fp0);
+            opn = "class.s";
+        }
+        break;
+    case OPC_MIN_S: /* OPC_RECIP2_S */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_MIN_S */
+            TCGv_i32 fp0 = tcg_temp_new_i32();
             TCGv_i32 fp1 = tcg_temp_new_i32();
+            TCGv_i32 fp2 = tcg_temp_new_i32();
+            gen_load_fpr32(fp0, fs);
+            gen_load_fpr32(fp1, ft);
+            gen_helper_float_min_s(fp2, cpu_env, fp0, fp1);
+            gen_store_fpr32(fp2, fd);
+            tcg_temp_free_i32(fp2);
+            tcg_temp_free_i32(fp1);
+            tcg_temp_free_i32(fp0);
+            opn = "min.s";
+        } else {
+            /* OPC_RECIP2_S */
+            check_cp1_64bitmode(ctx);
+            {
+                TCGv_i32 fp0 = tcg_temp_new_i32();
+                TCGv_i32 fp1 = tcg_temp_new_i32();
 
+                gen_load_fpr32(fp0, fs);
+                gen_load_fpr32(fp1, ft);
+                gen_helper_float_recip2_s(fp0, cpu_env, fp0, fp1);
+                tcg_temp_free_i32(fp1);
+                gen_store_fpr32(fp0, fd);
+                tcg_temp_free_i32(fp0);
+            }
+            opn = "recip2.s";
+        }
+        break;
+    case OPC_MINA_S: /* OPC_RECIP1_S */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_MINA_S */
+            TCGv_i32 fp0 = tcg_temp_new_i32();
+            TCGv_i32 fp1 = tcg_temp_new_i32();
+            TCGv_i32 fp2 = tcg_temp_new_i32();
             gen_load_fpr32(fp0, fs);
             gen_load_fpr32(fp1, ft);
-            gen_helper_float_rsqrt2_s(fp0, cpu_env, fp0, fp1);
+            gen_helper_float_mina_s(fp2, cpu_env, fp0, fp1);
+            gen_store_fpr32(fp2, fd);
+            tcg_temp_free_i32(fp2);
             tcg_temp_free_i32(fp1);
-            gen_store_fpr32(fp0, fd);
             tcg_temp_free_i32(fp0);
+            opn = "mina.s";
+        } else {
+            /* OPC_RECIP1_S */
+            check_cp1_64bitmode(ctx);
+            {
+                TCGv_i32 fp0 = tcg_temp_new_i32();
+
+                gen_load_fpr32(fp0, fs);
+                gen_helper_float_recip1_s(fp0, cpu_env, fp0);
+                gen_store_fpr32(fp0, fd);
+                tcg_temp_free_i32(fp0);
+            }
+            opn = "recip1.s";
+        }
+        break;
+    case OPC_MAX_S: /* OPC_RSQRT1_S */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_MAX_S */
+            TCGv_i32 fp0 = tcg_temp_new_i32();
+            TCGv_i32 fp1 = tcg_temp_new_i32();
+            gen_load_fpr32(fp0, fs);
+            gen_load_fpr32(fp1, ft);
+            gen_helper_float_max_s(fp1, cpu_env, fp0, fp1);
+            gen_store_fpr32(fp1, fd);
+            tcg_temp_free_i32(fp1);
+            tcg_temp_free_i32(fp0);
+            opn = "max.s";
+        } else {
+            /* OPC_RSQRT1_S */
+            check_cp1_64bitmode(ctx);
+            {
+                TCGv_i32 fp0 = tcg_temp_new_i32();
+
+                gen_load_fpr32(fp0, fs);
+                gen_helper_float_rsqrt1_s(fp0, cpu_env, fp0);
+                gen_store_fpr32(fp0, fd);
+                tcg_temp_free_i32(fp0);
+            }
+            opn = "rsqrt1.s";
+        }
+        break;
+    case OPC_MAXA_S: /* OPC_RSQRT2_S */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_MAXA_S */
+            TCGv_i32 fp0 = tcg_temp_new_i32();
+            TCGv_i32 fp1 = tcg_temp_new_i32();
+            gen_load_fpr32(fp0, fs);
+            gen_load_fpr32(fp1, ft);
+            gen_helper_float_maxa_s(fp1, cpu_env, fp0, fp1);
+            gen_store_fpr32(fp1, fd);
+            tcg_temp_free_i32(fp1);
+            tcg_temp_free_i32(fp0);
+            opn = "maxa.s";
+        } else {
+            /* OPC_RSQRT2_S */
+            check_cp1_64bitmode(ctx);
+            {
+                TCGv_i32 fp0 = tcg_temp_new_i32();
+                TCGv_i32 fp1 = tcg_temp_new_i32();
+
+                gen_load_fpr32(fp0, fs);
+                gen_load_fpr32(fp1, ft);
+                gen_helper_float_rsqrt2_s(fp0, cpu_env, fp0, fp1);
+                tcg_temp_free_i32(fp1);
+                gen_store_fpr32(fp0, fd);
+                tcg_temp_free_i32(fp0);
+            }
+            opn = "rsqrt2.s";
         }
-        opn = "rsqrt2.s";
         break;
     case OPC_CVT_D_S:
         check_cp1_registers(ctx, fd);
@@ -7865,6 +8681,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
         opn = "cvt.l.s";
         break;
     case OPC_CVT_PS_S:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         check_cp1_64bitmode(ctx);
         {
             TCGv_i64 fp64 = tcg_temp_new_i64();
@@ -7897,6 +8714,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
     case OPC_CMP_NGE_S:
     case OPC_CMP_LE_S:
     case OPC_CMP_NGT_S:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         if (ctx->opcode & (1 << 6)) {
             gen_cmpabs_s(ctx, func-48, ft, fs, cc);
             opn = condnames_abs[func-48];
@@ -8120,11 +8938,28 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
         }
         opn = "floor.w.d";
         break;
+    case OPC_SEL_D:
+        check_insn(ctx, ISA_MIPS32R6);
+        gen_sel_d(ctx, op1, fd, ft, fs);
+        opn = "sel.d";
+        break;
+    case OPC_SELEQZ_D:
+        check_insn(ctx, ISA_MIPS32R6);
+        gen_sel_d(ctx, op1, fd, ft, fs);
+        opn = "seleqz.d";
+        break;
+    case OPC_SELNEZ_D:
+        check_insn(ctx, ISA_MIPS32R6);
+        gen_sel_d(ctx, op1, fd, ft, fs);
+        opn = "selnez.d";
+        break;
     case OPC_MOVCF_D:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         gen_movcf_d(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1);
         opn = "movcf.d";
         break;
     case OPC_MOVZ_D:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         {
             int l1 = gen_new_label();
             TCGv_i64 fp0;
@@ -8141,6 +8976,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
         opn = "movz.d";
         break;
     case OPC_MOVN_D:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         {
             int l1 = gen_new_label();
             TCGv_i64 fp0;
@@ -8180,59 +9016,171 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
         }
         opn = "rsqrt.d";
         break;
-    case OPC_RECIP2_D:
-        check_cp1_64bitmode(ctx);
+    case OPC_MADDF_D:
+        check_insn(ctx, ISA_MIPS32R6);
         {
             TCGv_i64 fp0 = tcg_temp_new_i64();
             TCGv_i64 fp1 = tcg_temp_new_i64();
-
+            TCGv_i64 fp2 = tcg_temp_new_i64();
             gen_load_fpr64(ctx, fp0, fs);
             gen_load_fpr64(ctx, fp1, ft);
-            gen_helper_float_recip2_d(fp0, cpu_env, fp0, fp1);
+            gen_load_fpr64(ctx, fp2, fd);
+            gen_helper_float_maddf_d(fp2, cpu_env, fp0, fp1, fp2);
+            gen_store_fpr64(ctx, fp2, fd);
+            tcg_temp_free_i64(fp2);
             tcg_temp_free_i64(fp1);
-            gen_store_fpr64(ctx, fp0, fd);
             tcg_temp_free_i64(fp0);
+            opn = "maddf.d";
         }
-        opn = "recip2.d";
         break;
-    case OPC_RECIP1_D:
-        check_cp1_64bitmode(ctx);
+    case OPC_MSUBF_D:
+        check_insn(ctx, ISA_MIPS32R6);
         {
             TCGv_i64 fp0 = tcg_temp_new_i64();
-
+            TCGv_i64 fp1 = tcg_temp_new_i64();
+            TCGv_i64 fp2 = tcg_temp_new_i64();
             gen_load_fpr64(ctx, fp0, fs);
-            gen_helper_float_recip1_d(fp0, cpu_env, fp0);
-            gen_store_fpr64(ctx, fp0, fd);
+            gen_load_fpr64(ctx, fp1, ft);
+            gen_load_fpr64(ctx, fp2, fd);
+            gen_helper_float_msubf_d(fp2, cpu_env, fp0, fp1, fp2);
+            gen_store_fpr64(ctx, fp2, fd);
+            tcg_temp_free_i64(fp2);
+            tcg_temp_free_i64(fp1);
             tcg_temp_free_i64(fp0);
+            opn = "msubf.d";
         }
-        opn = "recip1.d";
         break;
-    case OPC_RSQRT1_D:
-        check_cp1_64bitmode(ctx);
+    case OPC_RINT_D:
+        check_insn(ctx, ISA_MIPS32R6);
         {
             TCGv_i64 fp0 = tcg_temp_new_i64();
-
             gen_load_fpr64(ctx, fp0, fs);
-            gen_helper_float_rsqrt1_d(fp0, cpu_env, fp0);
+            gen_helper_float_rint_d(fp0, cpu_env, fp0);
             gen_store_fpr64(ctx, fp0, fd);
             tcg_temp_free_i64(fp0);
+            opn = "rint.d";
         }
-        opn = "rsqrt1.d";
         break;
-    case OPC_RSQRT2_D:
-        check_cp1_64bitmode(ctx);
+    case OPC_CLASS_D:
+        check_insn(ctx, ISA_MIPS32R6);
         {
             TCGv_i64 fp0 = tcg_temp_new_i64();
+            gen_load_fpr64(ctx, fp0, fs);
+            gen_helper_float_class_d(fp0, fp0);
+            gen_store_fpr64(ctx, fp0, fd);
+            tcg_temp_free_i64(fp0);
+            opn = "class.d";
+        }
+        break;
+    case OPC_MIN_D: /* OPC_RECIP2_D */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_MIN_D */
+            TCGv_i64 fp0 = tcg_temp_new_i64();
             TCGv_i64 fp1 = tcg_temp_new_i64();
+            gen_load_fpr64(ctx, fp0, fs);
+            gen_load_fpr64(ctx, fp1, ft);
+            gen_helper_float_min_d(fp1, cpu_env, fp0, fp1);
+            gen_store_fpr64(ctx, fp1, fd);
+            tcg_temp_free_i64(fp1);
+            tcg_temp_free_i64(fp0);
+            opn = "min.d";
+        } else {
+            /* OPC_RECIP2_D */
+            check_cp1_64bitmode(ctx);
+            {
+                TCGv_i64 fp0 = tcg_temp_new_i64();
+                TCGv_i64 fp1 = tcg_temp_new_i64();
 
+                gen_load_fpr64(ctx, fp0, fs);
+                gen_load_fpr64(ctx, fp1, ft);
+                gen_helper_float_recip2_d(fp0, cpu_env, fp0, fp1);
+                tcg_temp_free_i64(fp1);
+                gen_store_fpr64(ctx, fp0, fd);
+                tcg_temp_free_i64(fp0);
+            }
+            opn = "recip2.d";
+        }
+        break;
+    case OPC_MINA_D: /* OPC_RECIP1_D */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_MINA_D */
+            TCGv_i64 fp0 = tcg_temp_new_i64();
+            TCGv_i64 fp1 = tcg_temp_new_i64();
             gen_load_fpr64(ctx, fp0, fs);
             gen_load_fpr64(ctx, fp1, ft);
-            gen_helper_float_rsqrt2_d(fp0, cpu_env, fp0, fp1);
+            gen_helper_float_mina_d(fp1, cpu_env, fp0, fp1);
+            gen_store_fpr64(ctx, fp1, fd);
+            tcg_temp_free_i64(fp1);
+            tcg_temp_free_i64(fp0);
+            opn = "mina.d";
+        } else {
+            /* OPC_RECIP1_D */
+            check_cp1_64bitmode(ctx);
+            {
+                TCGv_i64 fp0 = tcg_temp_new_i64();
+
+                gen_load_fpr64(ctx, fp0, fs);
+                gen_helper_float_recip1_d(fp0, cpu_env, fp0);
+                gen_store_fpr64(ctx, fp0, fd);
+                tcg_temp_free_i64(fp0);
+            }
+            opn = "recip1.d";
+        }
+        break;
+    case OPC_MAX_D: /*  OPC_RSQRT1_D */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_MAX_D */
+            TCGv_i64 fp0 = tcg_temp_new_i64();
+            TCGv_i64 fp1 = tcg_temp_new_i64();
+            gen_load_fpr64(ctx, fp0, fs);
+            gen_load_fpr64(ctx, fp1, ft);
+            gen_helper_float_max_d(fp1, cpu_env, fp0, fp1);
+            gen_store_fpr64(ctx, fp1, fd);
+            tcg_temp_free_i64(fp1);
+            tcg_temp_free_i64(fp0);
+            opn = "max.d";
+        } else {
+            /* OPC_RSQRT1_D */
+            check_cp1_64bitmode(ctx);
+            {
+                TCGv_i64 fp0 = tcg_temp_new_i64();
+
+                gen_load_fpr64(ctx, fp0, fs);
+                gen_helper_float_rsqrt1_d(fp0, cpu_env, fp0);
+                gen_store_fpr64(ctx, fp0, fd);
+                tcg_temp_free_i64(fp0);
+            }
+            opn = "rsqrt1.d";
+        }
+        break;
+    case OPC_MAXA_D: /* OPC_RSQRT2_D */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_MAXA_D */
+            TCGv_i64 fp0 = tcg_temp_new_i64();
+            TCGv_i64 fp1 = tcg_temp_new_i64();
+            gen_load_fpr64(ctx, fp0, fs);
+            gen_load_fpr64(ctx, fp1, ft);
+            gen_helper_float_maxa_d(fp1, cpu_env, fp0, fp1);
+            gen_store_fpr64(ctx, fp1, fd);
             tcg_temp_free_i64(fp1);
-            gen_store_fpr64(ctx, fp0, fd);
             tcg_temp_free_i64(fp0);
+            opn = "maxa.d";
+        } else {
+            /* OPC_RSQRT2_D */
+            check_cp1_64bitmode(ctx);
+            {
+                TCGv_i64 fp0 = tcg_temp_new_i64();
+                TCGv_i64 fp1 = tcg_temp_new_i64();
+
+                gen_load_fpr64(ctx, fp0, fs);
+                gen_load_fpr64(ctx, fp1, ft);
+                gen_helper_float_rsqrt2_d(fp0, cpu_env, fp0, fp1);
+                tcg_temp_free_i64(fp1);
+                gen_store_fpr64(ctx, fp0, fd);
+                tcg_temp_free_i64(fp0);
+            }
+            opn = "rsqrt2.d";
         }
-        opn = "rsqrt2.d";
         break;
     case OPC_CMP_F_D:
     case OPC_CMP_UN_D:
@@ -8250,6 +9198,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
     case OPC_CMP_NGE_D:
     case OPC_CMP_LE_D:
     case OPC_CMP_NGT_D:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         if (ctx->opcode & (1 << 6)) {
             gen_cmpabs_d(ctx, func-48, ft, fs, cc);
             opn = condnames_abs[func-48];
@@ -8350,6 +9299,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
         opn = "cvt.d.l";
         break;
     case OPC_CVT_PS_PW:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         check_cp1_64bitmode(ctx);
         {
             TCGv_i64 fp0 = tcg_temp_new_i64();
@@ -9128,7 +10078,7 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd)
     tcg_temp_free(t0);
 }
 
-static void handle_delay_slot(DisasContext *ctx, int insn_bytes)
+static void gen_branch(DisasContext *ctx, int insn_bytes)
 {
     if (ctx->hflags & MIPS_HFLAG_BMASK) {
         int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK;
@@ -9671,15 +10621,15 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx)
         gen_addiupc(ctx, rx, imm, 0, 1);
         break;
     case M16_OPC_B:
-        gen_compute_branch(ctx, OPC_BEQ, 4, 0, 0, offset << 1);
+        gen_compute_branch(ctx, OPC_BEQ, 4, 0, 0, offset << 1, 0);
         /* No delay slot, so just process as a normal instruction */
         break;
     case M16_OPC_BEQZ:
-        gen_compute_branch(ctx, OPC_BEQ, 4, rx, 0, offset << 1);
+        gen_compute_branch(ctx, OPC_BEQ, 4, rx, 0, offset << 1, 0);
         /* No delay slot, so just process as a normal instruction */
         break;
     case M16_OPC_BNEQZ:
-        gen_compute_branch(ctx, OPC_BNE, 4, rx, 0, offset << 1);
+        gen_compute_branch(ctx, OPC_BNE, 4, rx, 0, offset << 1, 0);
         /* No delay slot, so just process as a normal instruction */
         break;
     case M16_OPC_SHIFT:
@@ -9737,10 +10687,10 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx)
     case M16_OPC_I8:
         switch (funct) {
         case I8_BTEQZ:
-            gen_compute_branch(ctx, OPC_BEQ, 4, 24, 0, offset << 1);
+            gen_compute_branch(ctx, OPC_BEQ, 4, 24, 0, offset << 1, 0);
             break;
         case I8_BTNEZ:
-            gen_compute_branch(ctx, OPC_BNE, 4, 24, 0, offset << 1);
+            gen_compute_branch(ctx, OPC_BNE, 4, 24, 0, offset << 1, 0);
             break;
         case I8_SWRASP:
             gen_st(ctx, OPC_SW, 31, 29, imm);
@@ -9868,7 +10818,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx)
     case M16_OPC_B:
         offset = (ctx->opcode & 0x7ff) << 1;
         offset = (int16_t)(offset << 4) >> 4;
-        gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, offset);
+        gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, offset, 0);
         /* No delay slot, so just process as a normal instruction */
         break;
     case M16_OPC_JAL:
@@ -9876,16 +10826,18 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx)
         offset = (((ctx->opcode & 0x1f) << 21)
                   | ((ctx->opcode >> 5) & 0x1f) << 16
                   | offset) << 2;
-        op = ((ctx->opcode >> 10) & 0x1) ? OPC_JALXS : OPC_JALS;
-        gen_compute_branch(ctx, op, 4, rx, ry, offset);
+        op = ((ctx->opcode >> 10) & 0x1) ? OPC_JALX : OPC_JAL;
+        gen_compute_branch(ctx, op, 4, rx, ry, offset, 2);
         n_bytes = 4;
         break;
     case M16_OPC_BEQZ:
-        gen_compute_branch(ctx, OPC_BEQ, 2, rx, 0, ((int8_t)ctx->opcode) << 1);
+        gen_compute_branch(ctx, OPC_BEQ, 2, rx, 0,
+                           ((int8_t)ctx->opcode) << 1, 0);
         /* No delay slot, so just process as a normal instruction */
         break;
     case M16_OPC_BNEQZ:
-        gen_compute_branch(ctx, OPC_BNE, 2, rx, 0, ((int8_t)ctx->opcode) << 1);
+        gen_compute_branch(ctx, OPC_BNE, 2, rx, 0,
+                           ((int8_t)ctx->opcode) << 1, 0);
         /* No delay slot, so just process as a normal instruction */
         break;
     case M16_OPC_SHIFT:
@@ -9958,11 +10910,11 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx)
             switch (funct) {
             case I8_BTEQZ:
                 gen_compute_branch(ctx, OPC_BEQ, 2, 24, 0,
-                                   ((int8_t)ctx->opcode) << 1);
+                                   ((int8_t)ctx->opcode) << 1, 0);
                 break;
             case I8_BTNEZ:
                 gen_compute_branch(ctx, OPC_BNE, 2, 24, 0,
-                                   ((int8_t)ctx->opcode) << 1);
+                                   ((int8_t)ctx->opcode) << 1, 0);
                 break;
             case I8_SWRASP:
                 gen_st(ctx, OPC_SW, 31, 29, (ctx->opcode & 0xff) << 2);
@@ -10111,12 +11063,13 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx)
                 int ra = (ctx->opcode >> 5) & 0x1;
 
                 if (link) {
-                    op = nd ? OPC_JALRC : OPC_JALRS;
+                    op = OPC_JALR;
                 } else {
                     op = OPC_JR;
                 }
 
-                gen_compute_branch(ctx, op, 2, ra ? 31 : rx, 31, 0);
+                gen_compute_branch(ctx, op, 2, ra ? 31 : rx, 31, 0,
+                                   (nd ? 0 : 2));
             }
             break;
         case RR_SDBBP:
@@ -10874,7 +11827,6 @@ static void gen_pool16c_insn(DisasContext *ctx)
 {
     int rd = mmreg((ctx->opcode >> 3) & 0x7);
     int rs = mmreg(ctx->opcode & 0x7);
-    int opc;
 
     switch (((ctx->opcode) >> 4) & 0x3f) {
     case NOT16 + 0:
@@ -10930,32 +11882,27 @@ static void gen_pool16c_insn(DisasContext *ctx)
         {
             int reg = ctx->opcode & 0x1f;
 
-            gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0);
+            gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0, 4);
         }
         break;
     case JRC16 + 0:
     case JRC16 + 1:
         {
             int reg = ctx->opcode & 0x1f;
-
-            gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0);
+            gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0, 0);
             /* Let normal delay slot handling in our caller take us
                to the branch target.  */
         }
         break;
     case JALR16 + 0:
     case JALR16 + 1:
-        opc = OPC_JALR;
-        goto do_jalr;
+        gen_compute_branch(ctx, OPC_JALR, 2, ctx->opcode & 0x1f, 31, 0, 4);
+        ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
+        break;
     case JALR16S + 0:
     case JALR16S + 1:
-        opc = OPC_JALRS;
-    do_jalr:
-        {
-            int reg = ctx->opcode & 0x1f;
-
-            gen_compute_branch(ctx, opc, 2, reg, 31, 0);
-        }
+        gen_compute_branch(ctx, OPC_JALR, 2, ctx->opcode & 0x1f, 31, 0, 2);
+        ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
         break;
     case MFHI16 + 0:
     case MFHI16 + 1:
@@ -10983,8 +11930,7 @@ static void gen_pool16c_insn(DisasContext *ctx)
     case JRADDIUSP + 1:
         {
             int imm = ZIMM(ctx->opcode, 0, 5);
-
-            gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0);
+            gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0, 0);
             gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm << 2);
             /* Let normal delay slot handling in our caller take us
                to the branch target.  */
@@ -11241,11 +12187,13 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs)
         switch (minor) {
         case JALR:
         case JALR_HB:
-            gen_compute_branch (ctx, OPC_JALR, 4, rs, rt, 0);
+            gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 4);
+            ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
             break;
         case JALRS:
         case JALRS_HB:
-            gen_compute_branch (ctx, OPC_JALRS, 4, rs, rt, 0);
+            gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 2);
+            ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
             break;
         default:
             goto pool32axf_invalid;
@@ -12135,30 +13083,32 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx,
         minor = (ctx->opcode >> 21) & 0x1f;
         switch (minor) {
         case BLTZ:
-            mips32_op = OPC_BLTZ;
-            goto do_branch;
+            gen_compute_branch(ctx, OPC_BLTZ, 4, rs, -1, imm << 1, 4);
+            break;
         case BLTZAL:
-            mips32_op = OPC_BLTZAL;
-            goto do_branch;
+            gen_compute_branch(ctx, OPC_BLTZAL, 4, rs, -1, imm << 1, 4);
+            ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
+            break;
         case BLTZALS:
-            mips32_op = OPC_BLTZALS;
-            goto do_branch;
+            gen_compute_branch(ctx, OPC_BLTZAL, 4, rs, -1, imm << 1, 2);
+            ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
+            break;
         case BGEZ:
-            mips32_op = OPC_BGEZ;
-            goto do_branch;
+            gen_compute_branch(ctx, OPC_BGEZ, 4, rs, -1, imm << 1, 4);
+            break;
         case BGEZAL:
-            mips32_op = OPC_BGEZAL;
-            goto do_branch;
+            gen_compute_branch(ctx, OPC_BGEZAL, 4, rs, -1, imm << 1, 4);
+            ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
+            break;
         case BGEZALS:
-            mips32_op = OPC_BGEZALS;
-            goto do_branch;
+            gen_compute_branch(ctx, OPC_BGEZAL, 4, rs, -1, imm << 1, 2);
+            ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
+            break;
         case BLEZ:
-            mips32_op = OPC_BLEZ;
-            goto do_branch;
+            gen_compute_branch(ctx, OPC_BLEZ, 4, rs, -1, imm << 1, 4);
+            break;
         case BGTZ:
-            mips32_op = OPC_BGTZ;
-        do_branch:
-            gen_compute_branch(ctx, mips32_op, 4, rs, -1, imm << 1);
+            gen_compute_branch(ctx, OPC_BGTZ, 4, rs, -1, imm << 1, 4);
             break;
 
             /* Traps */
@@ -12186,7 +13136,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx,
         case BNEZC:
         case BEQZC:
             gen_compute_branch(ctx, minor == BNEZC ? OPC_BNE : OPC_BEQ,
-                               4, rs, 0, imm << 1);
+                               4, rs, 0, imm << 1, 0);
             /* Compact branches don't have a delay slot, so just let
                the normal delay slot handling take us to the branch
                target. */
@@ -12195,6 +13145,9 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx,
             gen_logic_imm(ctx, OPC_LUI, rs, -1, imm);
             break;
         case SYNCI:
+            /* Break the TB to be able to sync copied instructions
+               immediately */
+            ctx->bstate = BS_STOP;
             break;
         case BC2F:
         case BC2T:
@@ -12324,25 +13277,28 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx,
         break;
     case JALX32:
         offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2;
-        gen_compute_branch(ctx, OPC_JALX, 4, rt, rs, offset);
+        gen_compute_branch(ctx, OPC_JALX, 4, rt, rs, offset, 4);
+        ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
         break;
     case JALS32:
         offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 1;
-        gen_compute_branch(ctx, OPC_JALS, 4, rt, rs, offset);
+        gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, offset, 2);
+        ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
         break;
     case BEQ32:
-        gen_compute_branch(ctx, OPC_BEQ, 4, rt, rs, imm << 1);
+        gen_compute_branch(ctx, OPC_BEQ, 4, rt, rs, imm << 1, 4);
         break;
     case BNE32:
-        gen_compute_branch(ctx, OPC_BNE, 4, rt, rs, imm << 1);
+        gen_compute_branch(ctx, OPC_BNE, 4, rt, rs, imm << 1, 4);
         break;
     case J32:
         gen_compute_branch(ctx, OPC_J, 4, rt, rs,
-                           (int32_t)(ctx->opcode & 0x3FFFFFF) << 1);
+                           (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4);
         break;
     case JAL32:
         gen_compute_branch(ctx, OPC_JAL, 4, rt, rs,
-                           (int32_t)(ctx->opcode & 0x3FFFFFF) << 1);
+                           (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4);
+        ctx->hflags |= MIPS_HFLAG_BDS_STRICT;
         break;
         /* Floating point (COP1) */
     case LWC132:
@@ -12426,84 +13382,41 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx)
 
     op = (ctx->opcode >> 10) & 0x3f;
     /* Enforce properly-sized instructions in a delay slot */
-    if (ctx->hflags & MIPS_HFLAG_BMASK) {
-        int bits = ctx->hflags & MIPS_HFLAG_BMASK_EXT;
-
-        switch (op) {
-        case POOL32A:
-        case POOL32B:
-        case POOL32I:
-        case POOL32C:
-        case ADDI32:
-        case ADDIU32:
-        case ORI32:
-        case XORI32:
-        case SLTI32:
-        case SLTIU32:
-        case ANDI32:
-        case JALX32:
-        case LBU32:
-        case LHU32:
-        case POOL32F:
-        case JALS32:
-        case BEQ32:
-        case BNE32:
-        case J32:
-        case JAL32:
-        case SB32:
-        case SH32:
-        case POOL32S:
-        case ADDIUPC:
-        case SWC132:
-        case SDC132:
-        case SD32:
-        case SW32:
-        case LB32:
-        case LH32:
-        case DADDIU32:
-        case LWC132:
-        case LDC132:
-        case LD32:
-        case LW32:
-            if (bits & MIPS_HFLAG_BDS16) {
+    if (ctx->hflags & MIPS_HFLAG_BDS_STRICT) {
+        switch (op & 0x7) { /* MSB-3..MSB-5 */
+        case 0:
+        /* POOL32A, POOL32B, POOL32I, POOL32C */
+        case 4:
+        /* ADDI32, ADDIU32, ORI32, XORI32, SLTI32, SLTIU32, ANDI32, JALX32 */
+        case 5:
+        /* LBU32, LHU32, POOL32F, JALS32, BEQ32, BNE32, J32, JAL32 */
+        case 6:
+        /* SB32, SH32, ADDIUPC, SWC132, SDC132, SW32 */
+        case 7:
+        /* LB32, LH32, LWC132, LDC132, LW32 */
+            if (ctx->hflags & MIPS_HFLAG_BDS16) {
                 generate_exception(ctx, EXCP_RI);
                 /* Just stop translation; the user is confused.  */
                 ctx->bstate = BS_STOP;
                 return 2;
             }
             break;
-        case POOL16A:
-        case POOL16B:
-        case POOL16C:
-        case LWGP16:
-        case POOL16F:
-        case LBU16:
-        case LHU16:
-        case LWSP16:
-        case LW16:
-        case SB16:
-        case SH16:
-        case SWSP16:
-        case SW16:
-        case MOVE16:
-        case ANDI16:
-        case POOL16D:
-        case POOL16E:
-        case BEQZ16:
-        case BNEZ16:
-        case B16:
-        case LI16:
-            if (bits & MIPS_HFLAG_BDS32) {
+        case 1:
+        /* POOL16A, POOL16B, POOL16C, LWGP16, POOL16F */
+        case 2:
+        /* LBU16, LHU16, LWSP16, LW16, SB16, SH16, SWSP16, SW16 */
+        case 3:
+        /* MOVE16, ANDI16, POOL16D, POOL16E, BEQZ16, BNEZ16, B16, LI16 */
+            if (ctx->hflags & MIPS_HFLAG_BDS32) {
                 generate_exception(ctx, EXCP_RI);
                 /* Just stop translation; the user is confused.  */
                 ctx->bstate = BS_STOP;
                 return 2;
             }
             break;
-        default:
-            break;
         }
     }
+
     switch (op) {
     case POOL16A:
         {
@@ -12684,13 +13597,13 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx)
         break;
     case B16:
         gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0,
-                           SIMM(ctx->opcode, 0, 10) << 1);
+                           SIMM(ctx->opcode, 0, 10) << 1, 4);
         break;
     case BNEZ16:
     case BEQZ16:
         gen_compute_branch(ctx, op == BNEZ16 ? OPC_BNE : OPC_BEQ, 2,
                            mmreg(uMIPS_RD(ctx->opcode)),
-                           0, SIMM(ctx->opcode, 0, 7) << 1);
+                           0, SIMM(ctx->opcode, 0, 7) << 1, 4);
         break;
     case LI16:
         {
@@ -14447,905 +15360,1521 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2,
 
 /* End MIPSDSP functions. */
 
-static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
+/* Compact Branches */
+static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc,
+                                       int rs, int rt, int32_t offset)
 {
-    int32_t offset;
-    int rs, rt, rd, sa;
-    uint32_t op, op1, op2;
-    int16_t imm;
+    int bcond_compute = 0;
+    TCGv t0 = tcg_temp_new();
+    TCGv t1 = tcg_temp_new();
 
-    /* make sure instructions are on a word boundary */
-    if (ctx->pc & 0x3) {
-        env->CP0_BadVAddr = ctx->pc;
-        generate_exception(ctx, EXCP_AdEL);
-        return;
+    if (ctx->hflags & MIPS_HFLAG_BMASK) {
+#ifdef MIPS_DEBUG_DISAS
+        LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc);
+#endif
+        generate_exception(ctx, EXCP_RI);
+        goto out;
     }
 
-    /* Handle blikely not taken case */
-    if ((ctx->hflags & MIPS_HFLAG_BMASK_BASE) == MIPS_HFLAG_BL) {
-        int l1 = gen_new_label();
-
-        MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4);
-        tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1);
-        tcg_gen_movi_i32(hflags, ctx->hflags & ~MIPS_HFLAG_BMASK);
-        gen_goto_tb(ctx, 1, ctx->pc + 4);
-        gen_set_label(l1);
-    }
+    /* Load needed operands and calculate btarget */
+    switch (opc) {
+    /* compact branch */
+    case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */
+    case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */
+        gen_load_gpr(t0, rs);
+        gen_load_gpr(t1, rt);
+        bcond_compute = 1;
+        ctx->btarget = addr_add(ctx, ctx->pc + 4, offset);
+        if (rs <= rt && rs == 0) {
+            /* OPC_BEQZALC, OPC_BNEZALC */
+            tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4);
+        }
+        break;
+    case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */
+    case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */
+        gen_load_gpr(t0, rs);
+        gen_load_gpr(t1, rt);
+        bcond_compute = 1;
+        ctx->btarget = addr_add(ctx, ctx->pc + 4, offset);
+        break;
+    case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */
+    case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */
+        if (rs == 0 || rs == rt) {
+            /* OPC_BLEZALC, OPC_BGEZALC */
+            /* OPC_BGTZALC, OPC_BLTZALC */
+            tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4);
+        }
+        gen_load_gpr(t0, rs);
+        gen_load_gpr(t1, rt);
+        bcond_compute = 1;
+        ctx->btarget = addr_add(ctx, ctx->pc + 4, offset);
+        break;
+    case OPC_BC:
+    case OPC_BALC:
+        ctx->btarget = addr_add(ctx, ctx->pc + 4, offset);
+        break;
+    case OPC_BEQZC:
+    case OPC_BNEZC:
+        if (rs != 0) {
+            /* OPC_BEQZC, OPC_BNEZC */
+            gen_load_gpr(t0, rs);
+            bcond_compute = 1;
+            ctx->btarget = addr_add(ctx, ctx->pc + 4, offset);
+        } else {
+            /* OPC_JIC, OPC_JIALC */
+            TCGv tbase = tcg_temp_new();
+            TCGv toffset = tcg_temp_new();
 
-    if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) {
-        tcg_gen_debug_insn_start(ctx->pc);
+            gen_load_gpr(tbase, rt);
+            tcg_gen_movi_tl(toffset, offset);
+            gen_op_addr_add(ctx, btarget, tbase, toffset);
+            tcg_temp_free(tbase);
+            tcg_temp_free(toffset);
+        }
+        break;
+    default:
+        MIPS_INVAL("Compact branch/jump");
+        generate_exception(ctx, EXCP_RI);
+        goto out;
     }
 
-    op = MASK_OP_MAJOR(ctx->opcode);
-    rs = (ctx->opcode >> 21) & 0x1f;
-    rt = (ctx->opcode >> 16) & 0x1f;
-    rd = (ctx->opcode >> 11) & 0x1f;
-    sa = (ctx->opcode >> 6) & 0x1f;
-    imm = (int16_t)ctx->opcode;
-    switch (op) {
-    case OPC_SPECIAL:
-        op1 = MASK_SPECIAL(ctx->opcode);
-        switch (op1) {
-        case OPC_SLL:          /* Shift with immediate */
-        case OPC_SRA:
-            gen_shift_imm(ctx, op1, rd, rt, sa);
+    if (bcond_compute == 0) {
+        /* Uncoditional compact branch */
+        switch (opc) {
+        case OPC_JIALC:
+            tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4);
+            /* Fallthrough */
+        case OPC_JIC:
+            ctx->hflags |= MIPS_HFLAG_BR;
             break;
-        case OPC_SRL:
-            switch ((ctx->opcode >> 21) & 0x1f) {
-            case 1:
-                /* rotr is decoded as srl on non-R2 CPUs */
-                if (ctx->insn_flags & ISA_MIPS32R2) {
-                    op1 = OPC_ROTR;
-                }
-                /* Fallthrough */
-            case 0:
-                gen_shift_imm(ctx, op1, rd, rt, sa);
-                break;
-            default:
-                generate_exception(ctx, EXCP_RI);
-                break;
-            }
+        case OPC_BALC:
+            tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4);
+            /* Fallthrough */
+        case OPC_BC:
+            ctx->hflags |= MIPS_HFLAG_B;
             break;
-        case OPC_MOVN:         /* Conditional move */
-        case OPC_MOVZ:
-            check_insn(ctx, ISA_MIPS4 | ISA_MIPS32 |
-                                 INSN_LOONGSON2E | INSN_LOONGSON2F);
-            gen_cond_move(ctx, op1, rd, rs, rt);
+        default:
+            MIPS_INVAL("Compact branch/jump");
+            generate_exception(ctx, EXCP_RI);
+            goto out;
+        }
+
+        /* Generating branch here as compact branches don't have delay slot */
+        gen_branch(ctx, 4);
+    } else {
+        /* Conditional compact branch */
+        int l1 = gen_new_label();
+        save_cpu_state(ctx, 0);
+
+        switch (opc) {
+        case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */
+            if (rs == 0 && rt != 0) {
+                /* OPC_BLEZALC */
+                tcg_gen_brcondi_tl(TCG_COND_LE, t1, 0, l1);
+            } else if (rs != 0 && rt != 0 && rs == rt) {
+                /* OPC_BGEZALC */
+                tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
+            } else {
+                /* OPC_BGEUC */
+                tcg_gen_brcond_tl(TCG_COND_GEU, t0, t1, l1);
+            }
             break;
-        case OPC_ADD ... OPC_SUBU:
-            gen_arith(ctx, op1, rd, rs, rt);
+        case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */
+            if (rs == 0 && rt != 0) {
+                /* OPC_BGTZALC */
+                tcg_gen_brcondi_tl(TCG_COND_GT, t1, 0, l1);
+            } else if (rs != 0 && rt != 0 && rs == rt) {
+                /* OPC_BLTZALC */
+                tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l1);
+            } else {
+                /* OPC_BLTUC */
+                tcg_gen_brcond_tl(TCG_COND_LTU, t0, t1, l1);
+            }
             break;
-        case OPC_SLLV:         /* Shifts */
-        case OPC_SRAV:
-            gen_shift(ctx, op1, rd, rs, rt);
+        case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */
+            if (rs == 0 && rt != 0) {
+                /* OPC_BLEZC */
+                tcg_gen_brcondi_tl(TCG_COND_LE, t1, 0, l1);
+            } else if (rs != 0 && rt != 0 && rs == rt) {
+                /* OPC_BGEZC */
+                tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
+            } else {
+                /* OPC_BGEC */
+                tcg_gen_brcond_tl(TCG_COND_GE, t0, t1, l1);
+            }
             break;
-        case OPC_SRLV:
-            switch ((ctx->opcode >> 6) & 0x1f) {
-            case 1:
-                /* rotrv is decoded as srlv on non-R2 CPUs */
-                if (ctx->insn_flags & ISA_MIPS32R2) {
-                    op1 = OPC_ROTRV;
-                }
-                /* Fallthrough */
-            case 0:
-                gen_shift(ctx, op1, rd, rs, rt);
-                break;
-            default:
-                generate_exception(ctx, EXCP_RI);
-                break;
+        case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */
+            if (rs == 0 && rt != 0) {
+                /* OPC_BGTZC */
+                tcg_gen_brcondi_tl(TCG_COND_GT, t1, 0, l1);
+            } else if (rs != 0 && rt != 0 && rs == rt) {
+                /* OPC_BLTZC */
+                tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l1);
+            } else {
+                /* OPC_BLTC */
+                tcg_gen_brcond_tl(TCG_COND_LT, t0, t1, l1);
             }
             break;
-        case OPC_SLT:          /* Set on less than */
-        case OPC_SLTU:
-            gen_slt(ctx, op1, rd, rs, rt);
-            break;
-        case OPC_AND:          /* Logic*/
-        case OPC_OR:
-        case OPC_NOR:
-        case OPC_XOR:
-            gen_logic(ctx, op1, rd, rs, rt);
-            break;
-        case OPC_MULT:
-        case OPC_MULTU:
-            if (sa) {
-                check_insn(ctx, INSN_VR54XX);
-                op1 = MASK_MUL_VR54XX(ctx->opcode);
-                gen_mul_vr54xx(ctx, op1, rd, rs, rt);
+        case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */
+        case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */
+            if (rs >= rt) {
+                /* OPC_BOVC, OPC_BNVC */
+                TCGv t2 = tcg_temp_new();
+                TCGv t3 = tcg_temp_new();
+                TCGv t4 = tcg_temp_new();
+                TCGv input_overflow = tcg_temp_new();
+
+                gen_load_gpr(t0, rs);
+                gen_load_gpr(t1, rt);
+                tcg_gen_ext32s_tl(t2, t0);
+                tcg_gen_setcond_tl(TCG_COND_NE, input_overflow, t2, t0);
+                tcg_gen_ext32s_tl(t3, t1);
+                tcg_gen_setcond_tl(TCG_COND_NE, t4, t3, t1);
+                tcg_gen_or_tl(input_overflow, input_overflow, t4);
+
+                tcg_gen_add_tl(t4, t2, t3);
+                tcg_gen_ext32s_tl(t4, t4);
+                tcg_gen_xor_tl(t2, t2, t3);
+                tcg_gen_xor_tl(t3, t4, t3);
+                tcg_gen_andc_tl(t2, t3, t2);
+                tcg_gen_setcondi_tl(TCG_COND_LT, t4, t2, 0);
+                tcg_gen_or_tl(t4, t4, input_overflow);
+                if (opc == OPC_BOVC) {
+                    /* OPC_BOVC */
+                    tcg_gen_brcondi_tl(TCG_COND_NE, t4, 0, l1);
+                } else {
+                    /* OPC_BNVC */
+                    tcg_gen_brcondi_tl(TCG_COND_EQ, t4, 0, l1);
+                }
+                tcg_temp_free(input_overflow);
+                tcg_temp_free(t4);
+                tcg_temp_free(t3);
+                tcg_temp_free(t2);
+            } else if (rs < rt && rs == 0) {
+                /* OPC_BEQZALC, OPC_BNEZALC */
+                if (opc == OPC_BEQZALC) {
+                    /* OPC_BEQZALC */
+                    tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
+                } else {
+                    /* OPC_BNEZALC */
+                    tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
+                }
             } else {
-                gen_muldiv(ctx, op1, rd & 3, rs, rt);
+                /* OPC_BEQC, OPC_BNEC */
+                if (opc == OPC_BEQC) {
+                    /* OPC_BEQC */
+                    tcg_gen_brcond_tl(TCG_COND_EQ, t0, t1, l1);
+                } else {
+                    /* OPC_BNEC */
+                    tcg_gen_brcond_tl(TCG_COND_NE, t0, t1, l1);
+                }
             }
             break;
-        case OPC_DIV:
-        case OPC_DIVU:
-            gen_muldiv(ctx, op1, 0, rs, rt);
+        case OPC_BEQZC:
+            tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);
             break;
-        case OPC_JR ... OPC_JALR:
-            gen_compute_branch(ctx, op1, 4, rs, rd, sa);
+        case OPC_BNEZC:
+            tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0, l1);
             break;
-        case OPC_TGE ... OPC_TEQ: /* Traps */
-        case OPC_TNE:
-            gen_trap(ctx, op1, rs, rt, -1);
+        default:
+            MIPS_INVAL("Compact conditional branch/jump");
+            generate_exception(ctx, EXCP_RI);
+            goto out;
+        }
+
+        /* Generating branch here as compact branches don't have delay slot */
+        /* TODO: implement forbidden slot */
+        gen_goto_tb(ctx, 1, ctx->pc + 4);
+        gen_set_label(l1);
+        gen_goto_tb(ctx, 0, ctx->btarget);
+        MIPS_DEBUG("Compact conditional branch");
+        ctx->bstate = BS_BRANCH;
+    }
+
+out:
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+}
+
+static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx)
+{
+    int rs, rt, rd, sa;
+    uint32_t op1, op2;
+
+    rs = (ctx->opcode >> 21) & 0x1f;
+    rt = (ctx->opcode >> 16) & 0x1f;
+    rd = (ctx->opcode >> 11) & 0x1f;
+    sa = (ctx->opcode >> 6) & 0x1f;
+
+    op1 = MASK_SPECIAL(ctx->opcode);
+    switch (op1) {
+    case OPC_LSA:
+        if (rd != 0) {
+            int imm2 = extract32(ctx->opcode, 6, 3);
+            TCGv t0 = tcg_temp_new();
+            TCGv t1 = tcg_temp_new();
+            gen_load_gpr(t0, rs);
+            gen_load_gpr(t1, rt);
+            tcg_gen_shli_tl(t0, t0, imm2 + 1);
+            tcg_gen_add_tl(t0, t0, t1);
+            tcg_gen_ext32s_tl(cpu_gpr[rd], t0);
+            tcg_temp_free(t1);
+            tcg_temp_free(t0);
+        }
+        break;
+    case OPC_MULT ... OPC_DIVU:
+        op2 = MASK_R6_MULDIV(ctx->opcode);
+        switch (op2) {
+        case R6_OPC_MUL:
+        case R6_OPC_MUH:
+        case R6_OPC_MULU:
+        case R6_OPC_MUHU:
+        case R6_OPC_DIV:
+        case R6_OPC_MOD:
+        case R6_OPC_DIVU:
+        case R6_OPC_MODU:
+            gen_r6_muldiv(ctx, op2, rd, rs, rt);
             break;
-        case OPC_MFHI:          /* Move from HI/LO */
-        case OPC_MFLO:
-            gen_HILO(ctx, op1, rs & 3, rd);
+        default:
+            MIPS_INVAL("special_r6 muldiv");
+            generate_exception(ctx, EXCP_RI);
             break;
-        case OPC_MTHI:
-        case OPC_MTLO:          /* Move to HI/LO */
-            gen_HILO(ctx, op1, rd & 3, rs);
+        }
+        break;
+    case OPC_SELEQZ:
+    case OPC_SELNEZ:
+        gen_cond_move(ctx, op1, rd, rs, rt);
+        break;
+    case R6_OPC_CLO:
+    case R6_OPC_CLZ:
+        if (rt == 0 && sa == 1) {
+            /* Major opcode and function field is shared with preR6 MFHI/MTHI.
+               We need additionally to check other fields */
+            gen_cl(ctx, op1, rd, rs);
+        } else {
+            generate_exception(ctx, EXCP_RI);
+        }
+        break;
+    case R6_OPC_SDBBP:
+        generate_exception(ctx, EXCP_DBp);
+        break;
+#if defined(TARGET_MIPS64)
+    case OPC_DLSA:
+        check_mips_64(ctx);
+        if (rd != 0) {
+            int imm2 = extract32(ctx->opcode, 6, 3);
+            TCGv t0 = tcg_temp_new();
+            TCGv t1 = tcg_temp_new();
+            gen_load_gpr(t0, rs);
+            gen_load_gpr(t1, rt);
+            tcg_gen_shli_tl(t0, t0, imm2 + 1);
+            tcg_gen_add_tl(cpu_gpr[rd], t0, t1);
+            tcg_temp_free(t1);
+            tcg_temp_free(t0);
+        }
+        break;
+    case R6_OPC_DCLO:
+    case R6_OPC_DCLZ:
+        if (rt == 0 && sa == 1) {
+            /* Major opcode and function field is shared with preR6 MFHI/MTHI.
+               We need additionally to check other fields */
+            check_mips_64(ctx);
+            gen_cl(ctx, op1, rd, rs);
+        } else {
+            generate_exception(ctx, EXCP_RI);
+        }
+        break;
+    case OPC_DMULT ... OPC_DDIVU:
+        op2 = MASK_R6_MULDIV(ctx->opcode);
+        switch (op2) {
+        case R6_OPC_DMUL:
+        case R6_OPC_DMUH:
+        case R6_OPC_DMULU:
+        case R6_OPC_DMUHU:
+        case R6_OPC_DDIV:
+        case R6_OPC_DMOD:
+        case R6_OPC_DDIVU:
+        case R6_OPC_DMODU:
+            check_mips_64(ctx);
+            gen_r6_muldiv(ctx, op2, rd, rs, rt);
             break;
-        case OPC_PMON:          /* Pmon entry point, also R4010 selsl */
-#ifdef MIPS_STRICT_STANDARD
-            MIPS_INVAL("PMON / selsl");
+        default:
+            MIPS_INVAL("special_r6 muldiv");
             generate_exception(ctx, EXCP_RI);
+            break;
+        }
+        break;
+#endif
+    default:            /* Invalid */
+        MIPS_INVAL("special_r6");
+        generate_exception(ctx, EXCP_RI);
+        break;
+    }
+}
+
+static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx)
+{
+    int rs, rt, rd, sa;
+    uint32_t op1;
+
+    rs = (ctx->opcode >> 21) & 0x1f;
+    rt = (ctx->opcode >> 16) & 0x1f;
+    rd = (ctx->opcode >> 11) & 0x1f;
+    sa = (ctx->opcode >> 6) & 0x1f;
+
+    op1 = MASK_SPECIAL(ctx->opcode);
+    switch (op1) {
+    case OPC_MOVN:         /* Conditional move */
+    case OPC_MOVZ:
+        check_insn(ctx, ISA_MIPS4 | ISA_MIPS32 |
+                   INSN_LOONGSON2E | INSN_LOONGSON2F);
+        gen_cond_move(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_MFHI:          /* Move from HI/LO */
+    case OPC_MFLO:
+        gen_HILO(ctx, op1, rs & 3, rd);
+        break;
+    case OPC_MTHI:
+    case OPC_MTLO:          /* Move to HI/LO */
+        gen_HILO(ctx, op1, rd & 3, rs);
+        break;
+    case OPC_MOVCI:
+        check_insn(ctx, ISA_MIPS4 | ISA_MIPS32);
+        if (env->CP0_Config1 & (1 << CP0C1_FP)) {
+            check_cp1_enabled(ctx);
+            gen_movci(ctx, rd, rs, (ctx->opcode >> 18) & 0x7,
+                      (ctx->opcode >> 16) & 1);
+        } else {
+            generate_exception_err(ctx, EXCP_CpU, 1);
+        }
+        break;
+    case OPC_MULT:
+    case OPC_MULTU:
+        if (sa) {
+            check_insn(ctx, INSN_VR54XX);
+            op1 = MASK_MUL_VR54XX(ctx->opcode);
+            gen_mul_vr54xx(ctx, op1, rd, rs, rt);
+        } else {
+            gen_muldiv(ctx, op1, rd & 3, rs, rt);
+        }
+        break;
+    case OPC_DIV:
+    case OPC_DIVU:
+        gen_muldiv(ctx, op1, 0, rs, rt);
+        break;
+#if defined(TARGET_MIPS64)
+    case OPC_DMULT ... OPC_DDIVU:
+        check_insn(ctx, ISA_MIPS3);
+        check_mips_64(ctx);
+        gen_muldiv(ctx, op1, 0, rs, rt);
+        break;
+#endif
+    case OPC_JR:
+        gen_compute_branch(ctx, op1, 4, rs, rd, sa, 4);
+        break;
+    case OPC_SPIM:
+#ifdef MIPS_STRICT_STANDARD
+        MIPS_INVAL("SPIM");
+        generate_exception(ctx, EXCP_RI);
 #else
-            gen_helper_0e0i(pmon, sa);
+        /* Implemented as RI exception for now. */
+        MIPS_INVAL("spim (unofficial)");
+        generate_exception(ctx, EXCP_RI);
 #endif
+        break;
+    default:            /* Invalid */
+        MIPS_INVAL("special_legacy");
+        generate_exception(ctx, EXCP_RI);
+        break;
+    }
+}
+
+static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx)
+{
+    int rs, rt, rd, sa;
+    uint32_t op1;
+
+    rs = (ctx->opcode >> 21) & 0x1f;
+    rt = (ctx->opcode >> 16) & 0x1f;
+    rd = (ctx->opcode >> 11) & 0x1f;
+    sa = (ctx->opcode >> 6) & 0x1f;
+
+    op1 = MASK_SPECIAL(ctx->opcode);
+    switch (op1) {
+    case OPC_SLL:          /* Shift with immediate */
+    case OPC_SRA:
+        gen_shift_imm(ctx, op1, rd, rt, sa);
+        break;
+    case OPC_SRL:
+        switch ((ctx->opcode >> 21) & 0x1f) {
+        case 1:
+            /* rotr is decoded as srl on non-R2 CPUs */
+            if (ctx->insn_flags & ISA_MIPS32R2) {
+                op1 = OPC_ROTR;
+            }
+            /* Fallthrough */
+        case 0:
+            gen_shift_imm(ctx, op1, rd, rt, sa);
             break;
-        case OPC_SYSCALL:
-            generate_exception(ctx, EXCP_SYSCALL);
-            ctx->bstate = BS_STOP;
+        default:
+            generate_exception(ctx, EXCP_RI);
             break;
-        case OPC_BREAK:
-            generate_exception(ctx, EXCP_BREAK);
+        }
+        break;
+    case OPC_ADD ... OPC_SUBU:
+        gen_arith(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_SLLV:         /* Shifts */
+    case OPC_SRAV:
+        gen_shift(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_SRLV:
+        switch ((ctx->opcode >> 6) & 0x1f) {
+        case 1:
+            /* rotrv is decoded as srlv on non-R2 CPUs */
+            if (ctx->insn_flags & ISA_MIPS32R2) {
+                op1 = OPC_ROTRV;
+            }
+            /* Fallthrough */
+        case 0:
+            gen_shift(ctx, op1, rd, rs, rt);
+            break;
+        default:
+            generate_exception(ctx, EXCP_RI);
             break;
-        case OPC_SPIM:
+        }
+        break;
+    case OPC_SLT:          /* Set on less than */
+    case OPC_SLTU:
+        gen_slt(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_AND:          /* Logic*/
+    case OPC_OR:
+    case OPC_NOR:
+    case OPC_XOR:
+        gen_logic(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_JALR:
+        gen_compute_branch(ctx, op1, 4, rs, rd, sa, 4);
+        break;
+    case OPC_TGE ... OPC_TEQ: /* Traps */
+    case OPC_TNE:
+        gen_trap(ctx, op1, rs, rt, -1);
+        break;
+    case OPC_LSA: /* OPC_PMON */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            decode_opc_special_r6(env, ctx);
+        } else {
+            /* Pmon entry point, also R4010 selsl */
 #ifdef MIPS_STRICT_STANDARD
-            MIPS_INVAL("SPIM");
+            MIPS_INVAL("PMON / selsl");
             generate_exception(ctx, EXCP_RI);
 #else
-           /* Implemented as RI exception for now. */
-            MIPS_INVAL("spim (unofficial)");
-            generate_exception(ctx, EXCP_RI);
+            gen_helper_0e0i(pmon, sa);
 #endif
-            break;
-        case OPC_SYNC:
-            /* Treat as NOP. */
-            break;
-
-        case OPC_MOVCI:
-            check_insn(ctx, ISA_MIPS4 | ISA_MIPS32);
-            if (ctx->CP0_Config1 & (1 << CP0C1_FP)) {
-                check_cp1_enabled(ctx);
-                gen_movci(ctx, rd, rs, (ctx->opcode >> 18) & 0x7,
-                          (ctx->opcode >> 16) & 1);
-            } else {
-                generate_exception_err(ctx, EXCP_CpU, 1);
-            }
-            break;
+        }
+        break;
+    case OPC_SYSCALL:
+        generate_exception(ctx, EXCP_SYSCALL);
+        ctx->bstate = BS_STOP;
+        break;
+    case OPC_BREAK:
+        generate_exception(ctx, EXCP_BREAK);
+        break;
+    case OPC_SYNC:
+        /* Treat as NOP. */
+        break;
 
 #if defined(TARGET_MIPS64)
-       /* MIPS64 specific opcodes */
-        case OPC_DSLL:
-        case OPC_DSRA:
-        case OPC_DSLL32:
-        case OPC_DSRA32:
+        /* MIPS64 specific opcodes */
+    case OPC_DSLL:
+    case OPC_DSRA:
+    case OPC_DSLL32:
+    case OPC_DSRA32:
+        check_insn(ctx, ISA_MIPS3);
+        check_mips_64(ctx);
+        gen_shift_imm(ctx, op1, rd, rt, sa);
+        break;
+    case OPC_DSRL:
+        switch ((ctx->opcode >> 21) & 0x1f) {
+        case 1:
+            /* drotr is decoded as dsrl on non-R2 CPUs */
+            if (ctx->insn_flags & ISA_MIPS32R2) {
+                op1 = OPC_DROTR;
+            }
+            /* Fallthrough */
+        case 0:
             check_insn(ctx, ISA_MIPS3);
             check_mips_64(ctx);
             gen_shift_imm(ctx, op1, rd, rt, sa);
             break;
-        case OPC_DSRL:
-            switch ((ctx->opcode >> 21) & 0x1f) {
-            case 1:
-                /* drotr is decoded as dsrl on non-R2 CPUs */
-                if (ctx->insn_flags & ISA_MIPS32R2) {
-                    op1 = OPC_DROTR;
-                }
-                /* Fallthrough */
-            case 0:
-                check_insn(ctx, ISA_MIPS3);
-                check_mips_64(ctx);
-                gen_shift_imm(ctx, op1, rd, rt, sa);
-                break;
-            default:
-                generate_exception(ctx, EXCP_RI);
-                break;
-            }
+        default:
+            generate_exception(ctx, EXCP_RI);
             break;
-        case OPC_DSRL32:
-            switch ((ctx->opcode >> 21) & 0x1f) {
-            case 1:
-                /* drotr32 is decoded as dsrl32 on non-R2 CPUs */
-                if (ctx->insn_flags & ISA_MIPS32R2) {
-                    op1 = OPC_DROTR32;
-                }
-                /* Fallthrough */
-            case 0:
-                check_insn(ctx, ISA_MIPS3);
-                check_mips_64(ctx);
-                gen_shift_imm(ctx, op1, rd, rt, sa);
-                break;
-            default:
-                generate_exception(ctx, EXCP_RI);
-                break;
+        }
+        break;
+    case OPC_DSRL32:
+        switch ((ctx->opcode >> 21) & 0x1f) {
+        case 1:
+            /* drotr32 is decoded as dsrl32 on non-R2 CPUs */
+            if (ctx->insn_flags & ISA_MIPS32R2) {
+                op1 = OPC_DROTR32;
             }
-            break;
-        case OPC_DADD ... OPC_DSUBU:
+            /* Fallthrough */
+        case 0:
             check_insn(ctx, ISA_MIPS3);
             check_mips_64(ctx);
-            gen_arith(ctx, op1, rd, rs, rt);
+            gen_shift_imm(ctx, op1, rd, rt, sa);
             break;
-        case OPC_DSLLV:
-        case OPC_DSRAV:
-            check_insn(ctx, ISA_MIPS3);
-            check_mips_64(ctx);
-            gen_shift(ctx, op1, rd, rs, rt);
+        default:
+            generate_exception(ctx, EXCP_RI);
             break;
-        case OPC_DSRLV:
-            switch ((ctx->opcode >> 6) & 0x1f) {
-            case 1:
-                /* drotrv is decoded as dsrlv on non-R2 CPUs */
-                if (ctx->insn_flags & ISA_MIPS32R2) {
-                    op1 = OPC_DROTRV;
-                }
-                /* Fallthrough */
-            case 0:
-                check_insn(ctx, ISA_MIPS3);
-                check_mips_64(ctx);
-                gen_shift(ctx, op1, rd, rs, rt);
-                break;
-            default:
-                generate_exception(ctx, EXCP_RI);
-                break;
+        }
+        break;
+    case OPC_DADD ... OPC_DSUBU:
+        check_insn(ctx, ISA_MIPS3);
+        check_mips_64(ctx);
+        gen_arith(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_DSLLV:
+    case OPC_DSRAV:
+        check_insn(ctx, ISA_MIPS3);
+        check_mips_64(ctx);
+        gen_shift(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_DSRLV:
+        switch ((ctx->opcode >> 6) & 0x1f) {
+        case 1:
+            /* drotrv is decoded as dsrlv on non-R2 CPUs */
+            if (ctx->insn_flags & ISA_MIPS32R2) {
+                op1 = OPC_DROTRV;
             }
-            break;
-        case OPC_DMULT ... OPC_DDIVU:
+            /* Fallthrough */
+        case 0:
             check_insn(ctx, ISA_MIPS3);
             check_mips_64(ctx);
-            gen_muldiv(ctx, op1, 0, rs, rt);
+            gen_shift(ctx, op1, rd, rs, rt);
             break;
-#endif
-        default:            /* Invalid */
-            MIPS_INVAL("special");
+        default:
             generate_exception(ctx, EXCP_RI);
             break;
         }
         break;
-    case OPC_SPECIAL2:
-        op1 = MASK_SPECIAL2(ctx->opcode);
-        switch (op1) {
-        case OPC_MADD ... OPC_MADDU: /* Multiply and add/sub */
-        case OPC_MSUB ... OPC_MSUBU:
-            check_insn(ctx, ISA_MIPS32);
-            gen_muldiv(ctx, op1, rd & 3, rs, rt);
-            break;
-        case OPC_MUL:
-            gen_arith(ctx, op1, rd, rs, rt);
-            break;
-        case OPC_CLO:
-        case OPC_CLZ:
-            check_insn(ctx, ISA_MIPS32);
-            gen_cl(ctx, op1, rd, rs);
-            break;
-        case OPC_SDBBP:
-            /* XXX: not clear which exception should be raised
-             *      when in debug mode...
-             */
-            check_insn(ctx, ISA_MIPS32);
-            if (!(ctx->hflags & MIPS_HFLAG_DM)) {
-                generate_exception(ctx, EXCP_DBp);
-            } else {
-                generate_exception(ctx, EXCP_DBp);
-            }
-            /* Treat as NOP. */
-            break;
-        case OPC_DIV_G_2F:
-        case OPC_DIVU_G_2F:
-        case OPC_MULT_G_2F:
-        case OPC_MULTU_G_2F:
-        case OPC_MOD_G_2F:
-        case OPC_MODU_G_2F:
-            check_insn(ctx, INSN_LOONGSON2F);
-            gen_loongson_integer(ctx, op1, rd, rs, rt);
-            break;
+#endif
+    default:
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            decode_opc_special_r6(env, ctx);
+        } else {
+            decode_opc_special_legacy(env, ctx);
+        }
+    }
+}
+
+static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx)
+{
+    int rs, rt, rd;
+    uint32_t op1;
+
+    check_insn_opc_removed(ctx, ISA_MIPS32R6);
+
+    rs = (ctx->opcode >> 21) & 0x1f;
+    rt = (ctx->opcode >> 16) & 0x1f;
+    rd = (ctx->opcode >> 11) & 0x1f;
+
+    op1 = MASK_SPECIAL2(ctx->opcode);
+    switch (op1) {
+    case OPC_MADD ... OPC_MADDU: /* Multiply and add/sub */
+    case OPC_MSUB ... OPC_MSUBU:
+        check_insn(ctx, ISA_MIPS32);
+        gen_muldiv(ctx, op1, rd & 3, rs, rt);
+        break;
+    case OPC_MUL:
+        gen_arith(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_DIV_G_2F:
+    case OPC_DIVU_G_2F:
+    case OPC_MULT_G_2F:
+    case OPC_MULTU_G_2F:
+    case OPC_MOD_G_2F:
+    case OPC_MODU_G_2F:
+        check_insn(ctx, INSN_LOONGSON2F);
+        gen_loongson_integer(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_CLO:
+    case OPC_CLZ:
+        check_insn(ctx, ISA_MIPS32);
+        gen_cl(ctx, op1, rd, rs);
+        break;
+    case OPC_SDBBP:
+        /* XXX: not clear which exception should be raised
+         *      when in debug mode...
+         */
+        check_insn(ctx, ISA_MIPS32);
+        if (!(ctx->hflags & MIPS_HFLAG_DM)) {
+            generate_exception(ctx, EXCP_DBp);
+        } else {
+            generate_exception(ctx, EXCP_DBp);
+        }
+        /* Treat as NOP. */
+        break;
 #if defined(TARGET_MIPS64)
-        case OPC_DCLO:
-        case OPC_DCLZ:
-            check_insn(ctx, ISA_MIPS64);
-            check_mips_64(ctx);
-            gen_cl(ctx, op1, rd, rs);
-            break;
-        case OPC_DMULT_G_2F:
-        case OPC_DMULTU_G_2F:
-        case OPC_DDIV_G_2F:
-        case OPC_DDIVU_G_2F:
-        case OPC_DMOD_G_2F:
-        case OPC_DMODU_G_2F:
-            check_insn(ctx, INSN_LOONGSON2F);
-            gen_loongson_integer(ctx, op1, rd, rs, rt);
-            break;
+    case OPC_DCLO:
+    case OPC_DCLZ:
+        check_insn(ctx, ISA_MIPS64);
+        check_mips_64(ctx);
+        gen_cl(ctx, op1, rd, rs);
+        break;
+    case OPC_DMULT_G_2F:
+    case OPC_DMULTU_G_2F:
+    case OPC_DDIV_G_2F:
+    case OPC_DDIVU_G_2F:
+    case OPC_DMOD_G_2F:
+    case OPC_DMODU_G_2F:
+        check_insn(ctx, INSN_LOONGSON2F);
+        gen_loongson_integer(ctx, op1, rd, rs, rt);
+        break;
 #endif
-        default:            /* Invalid */
-            MIPS_INVAL("special2");
+    default:            /* Invalid */
+        MIPS_INVAL("special2_legacy");
+        generate_exception(ctx, EXCP_RI);
+        break;
+    }
+}
+
+static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx)
+{
+    int rs, rt, rd, sa;
+    uint32_t op1, op2;
+    int16_t imm;
+
+    rs = (ctx->opcode >> 21) & 0x1f;
+    rt = (ctx->opcode >> 16) & 0x1f;
+    rd = (ctx->opcode >> 11) & 0x1f;
+    sa = (ctx->opcode >> 6) & 0x1f;
+    imm = (int16_t)ctx->opcode >> 7;
+
+    op1 = MASK_SPECIAL3(ctx->opcode);
+    switch (op1) {
+    case R6_OPC_PREF:
+        if (rt >= 24) {
+            /* hint codes 24-31 are reserved and signal RI */
             generate_exception(ctx, EXCP_RI);
-            break;
         }
+        /* Treat as NOP. */
         break;
-    case OPC_SPECIAL3:
-        op1 = MASK_SPECIAL3(ctx->opcode);
-        switch (op1) {
-        case OPC_EXT:
-        case OPC_INS:
-            check_insn(ctx, ISA_MIPS32R2);
-            gen_bitops(ctx, op1, rt, rs, sa, rd);
-            break;
-        case OPC_BSHFL:
-            check_insn(ctx, ISA_MIPS32R2);
-            op2 = MASK_BSHFL(ctx->opcode);
-            gen_bshfl(ctx, op2, rt, rd);
-            break;
-        case OPC_RDHWR:
-            gen_rdhwr(ctx, rt, rd);
-            break;
-        case OPC_FORK:
-            check_insn(ctx, ASE_MT);
-            {
-                TCGv t0 = tcg_temp_new();
-                TCGv t1 = tcg_temp_new();
-
-                gen_load_gpr(t0, rt);
-                gen_load_gpr(t1, rs);
-                gen_helper_fork(t0, t1);
-                tcg_temp_free(t0);
-                tcg_temp_free(t1);
+    case R6_OPC_CACHE:
+        /* Treat as NOP. */
+        break;
+    case R6_OPC_SC:
+        gen_st_cond(ctx, op1, rt, rs, imm);
+        break;
+    case R6_OPC_LL:
+        gen_ld(ctx, op1, rt, rs, imm);
+        break;
+    case OPC_BSHFL:
+        {
+            if (rd == 0) {
+                /* Treat as NOP. */
+                break;
             }
-            break;
-        case OPC_YIELD:
-            check_insn(ctx, ASE_MT);
-            {
-                TCGv t0 = tcg_temp_new();
+            TCGv t0 = tcg_temp_new();
+            gen_load_gpr(t0, rt);
 
-                save_cpu_state(ctx, 1);
-                gen_load_gpr(t0, rs);
-                gen_helper_yield(t0, cpu_env, t0);
-                gen_store_gpr(t0, rd);
-                tcg_temp_free(t0);
-            }
-            break;
-        case OPC_DIV_G_2E ... OPC_DIVU_G_2E:
-        case OPC_MOD_G_2E ... OPC_MODU_G_2E:
-        case OPC_MULT_G_2E ... OPC_MULTU_G_2E:
-        /* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have
-         * the same mask and op1. */
-            if ((ctx->insn_flags & ASE_DSPR2) && (op1 == OPC_MULT_G_2E)) {
-                op2 = MASK_ADDUH_QB(ctx->opcode);
-                switch (op2) {
-                case OPC_ADDUH_QB:
-                case OPC_ADDUH_R_QB:
-                case OPC_ADDQH_PH:
-                case OPC_ADDQH_R_PH:
-                case OPC_ADDQH_W:
-                case OPC_ADDQH_R_W:
-                case OPC_SUBUH_QB:
-                case OPC_SUBUH_R_QB:
-                case OPC_SUBQH_PH:
-                case OPC_SUBQH_R_PH:
-                case OPC_SUBQH_W:
-                case OPC_SUBQH_R_W:
-                    gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
-                    break;
-                case OPC_MUL_PH:
-                case OPC_MUL_S_PH:
-                case OPC_MULQ_S_W:
-                case OPC_MULQ_RS_W:
-                    gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1);
-                    break;
-                default:
-                    MIPS_INVAL("MASK ADDUH.QB");
-                    generate_exception(ctx, EXCP_RI);
-                    break;
-                }
-            } else if (ctx->insn_flags & INSN_LOONGSON2E) {
-                gen_loongson_integer(ctx, op1, rd, rs, rt);
-            } else {
-                generate_exception(ctx, EXCP_RI);
-            }
-            break;
-        case OPC_LX_DSP:
-            op2 = MASK_LX(ctx->opcode);
+            op2 = MASK_BSHFL(ctx->opcode);
             switch (op2) {
+            case OPC_ALIGN ... OPC_ALIGN_END:
+                sa &= 3;
+                if (sa == 0) {
+                    tcg_gen_mov_tl(cpu_gpr[rd], t0);
+                } else {
+                    TCGv t1 = tcg_temp_new();
+                    TCGv_i64 t2 = tcg_temp_new_i64();
+                    gen_load_gpr(t1, rs);
+                    tcg_gen_concat_tl_i64(t2, t1, t0);
+                    tcg_gen_shri_i64(t2, t2, 8 * (4 - sa));
 #if defined(TARGET_MIPS64)
-            case OPC_LDX:
+                    tcg_gen_ext32s_i64(cpu_gpr[rd], t2);
+#else
+                    tcg_gen_trunc_i64_i32(cpu_gpr[rd], t2);
 #endif
-            case OPC_LBUX:
-            case OPC_LHX:
-            case OPC_LWX:
-                gen_mipsdsp_ld(ctx, op2, rd, rs, rt);
+                    tcg_temp_free_i64(t2);
+                    tcg_temp_free(t1);
+                }
                 break;
-            default:            /* Invalid */
-                MIPS_INVAL("MASK LX");
-                generate_exception(ctx, EXCP_RI);
+            case OPC_BITSWAP:
+                gen_helper_bitswap(cpu_gpr[rd], t0);
                 break;
             }
-            break;
-        case OPC_ABSQ_S_PH_DSP:
-            op2 = MASK_ABSQ_S_PH(ctx->opcode);
-            switch (op2) {
-            case OPC_ABSQ_S_QB:
-            case OPC_ABSQ_S_PH:
-            case OPC_ABSQ_S_W:
-            case OPC_PRECEQ_W_PHL:
-            case OPC_PRECEQ_W_PHR:
-            case OPC_PRECEQU_PH_QBL:
-            case OPC_PRECEQU_PH_QBR:
-            case OPC_PRECEQU_PH_QBLA:
-            case OPC_PRECEQU_PH_QBRA:
-            case OPC_PRECEU_PH_QBL:
-            case OPC_PRECEU_PH_QBR:
-            case OPC_PRECEU_PH_QBLA:
-            case OPC_PRECEU_PH_QBRA:
-                gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
-                break;
-            case OPC_BITREV:
-            case OPC_REPL_QB:
-            case OPC_REPLV_QB:
-            case OPC_REPL_PH:
-            case OPC_REPLV_PH:
-                gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt);
-                break;
-            default:
-                MIPS_INVAL("MASK ABSQ_S.PH");
-                generate_exception(ctx, EXCP_RI);
+            tcg_temp_free(t0);
+        }
+        break;
+#if defined(TARGET_MIPS64)
+    case R6_OPC_SCD:
+        gen_st_cond(ctx, op1, rt, rs, imm);
+        break;
+    case R6_OPC_LLD:
+        gen_ld(ctx, op1, rt, rs, imm);
+        break;
+    case OPC_DBSHFL:
+        check_mips_64(ctx);
+        {
+            if (rd == 0) {
+                /* Treat as NOP. */
                 break;
             }
-            break;
-        case OPC_ADDU_QB_DSP:
-            op2 = MASK_ADDU_QB(ctx->opcode);
+            TCGv t0 = tcg_temp_new();
+            gen_load_gpr(t0, rt);
+
+            op2 = MASK_DBSHFL(ctx->opcode);
             switch (op2) {
-            case OPC_ADDQ_PH:
-            case OPC_ADDQ_S_PH:
-            case OPC_ADDQ_S_W:
-            case OPC_ADDU_QB:
-            case OPC_ADDU_S_QB:
-            case OPC_ADDU_PH:
-            case OPC_ADDU_S_PH:
-            case OPC_SUBQ_PH:
-            case OPC_SUBQ_S_PH:
-            case OPC_SUBQ_S_W:
-            case OPC_SUBU_QB:
-            case OPC_SUBU_S_QB:
-            case OPC_SUBU_PH:
-            case OPC_SUBU_S_PH:
-            case OPC_ADDSC:
-            case OPC_ADDWC:
-            case OPC_MODSUB:
-            case OPC_RADDU_W_QB:
-                gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
-                break;
-            case OPC_MULEU_S_PH_QBL:
-            case OPC_MULEU_S_PH_QBR:
-            case OPC_MULQ_RS_PH:
-            case OPC_MULEQ_S_W_PHL:
-            case OPC_MULEQ_S_W_PHR:
-            case OPC_MULQ_S_PH:
-                gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1);
+            case OPC_DALIGN ... OPC_DALIGN_END:
+                sa &= 7;
+                if (sa == 0) {
+                    tcg_gen_mov_tl(cpu_gpr[rd], t0);
+                } else {
+                    TCGv t1 = tcg_temp_new();
+                    gen_load_gpr(t1, rs);
+                    tcg_gen_shli_tl(t0, t0, 8 * sa);
+                    tcg_gen_shri_tl(t1, t1, 8 * (8 - sa));
+                    tcg_gen_or_tl(cpu_gpr[rd], t1, t0);
+                    tcg_temp_free(t1);
+                }
                 break;
-            default:            /* Invalid */
-                MIPS_INVAL("MASK ADDU.QB");
-                generate_exception(ctx, EXCP_RI);
+            case OPC_DBITSWAP:
+                gen_helper_dbitswap(cpu_gpr[rd], t0);
                 break;
-
             }
-            break;
-        case OPC_CMPU_EQ_QB_DSP:
-            op2 = MASK_CMPU_EQ_QB(ctx->opcode);
+            tcg_temp_free(t0);
+        }
+        break;
+#endif
+    default:            /* Invalid */
+        MIPS_INVAL("special3_r6");
+        generate_exception(ctx, EXCP_RI);
+        break;
+    }
+}
+
+static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx)
+{
+    int rs, rt, rd;
+    uint32_t op1, op2;
+
+    rs = (ctx->opcode >> 21) & 0x1f;
+    rt = (ctx->opcode >> 16) & 0x1f;
+    rd = (ctx->opcode >> 11) & 0x1f;
+
+    op1 = MASK_SPECIAL3(ctx->opcode);
+    switch (op1) {
+    case OPC_DIV_G_2E ... OPC_DIVU_G_2E:
+    case OPC_MOD_G_2E ... OPC_MODU_G_2E:
+    case OPC_MULT_G_2E ... OPC_MULTU_G_2E:
+        /* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have
+         * the same mask and op1. */
+        if ((ctx->insn_flags & ASE_DSPR2) && (op1 == OPC_MULT_G_2E)) {
+            op2 = MASK_ADDUH_QB(ctx->opcode);
             switch (op2) {
-            case OPC_PRECR_SRA_PH_W:
-            case OPC_PRECR_SRA_R_PH_W:
-                gen_mipsdsp_arith(ctx, op1, op2, rt, rs, rd);
-                break;
-            case OPC_PRECR_QB_PH:
-            case OPC_PRECRQ_QB_PH:
-            case OPC_PRECRQ_PH_W:
-            case OPC_PRECRQ_RS_PH_W:
-            case OPC_PRECRQU_S_QB_PH:
+            case OPC_ADDUH_QB:
+            case OPC_ADDUH_R_QB:
+            case OPC_ADDQH_PH:
+            case OPC_ADDQH_R_PH:
+            case OPC_ADDQH_W:
+            case OPC_ADDQH_R_W:
+            case OPC_SUBUH_QB:
+            case OPC_SUBUH_R_QB:
+            case OPC_SUBQH_PH:
+            case OPC_SUBQH_R_PH:
+            case OPC_SUBQH_W:
+            case OPC_SUBQH_R_W:
                 gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
                 break;
-            case OPC_CMPU_EQ_QB:
-            case OPC_CMPU_LT_QB:
-            case OPC_CMPU_LE_QB:
-            case OPC_CMP_EQ_PH:
-            case OPC_CMP_LT_PH:
-            case OPC_CMP_LE_PH:
-                gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 0);
-                break;
-            case OPC_CMPGU_EQ_QB:
-            case OPC_CMPGU_LT_QB:
-            case OPC_CMPGU_LE_QB:
-            case OPC_CMPGDU_EQ_QB:
-            case OPC_CMPGDU_LT_QB:
-            case OPC_CMPGDU_LE_QB:
-            case OPC_PICK_QB:
-            case OPC_PICK_PH:
-            case OPC_PACKRL_PH:
-                gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 1);
+            case OPC_MUL_PH:
+            case OPC_MUL_S_PH:
+            case OPC_MULQ_S_W:
+            case OPC_MULQ_RS_W:
+                gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1);
                 break;
-            default:            /* Invalid */
-                MIPS_INVAL("MASK CMPU.EQ.QB");
+            default:
+                MIPS_INVAL("MASK ADDUH.QB");
                 generate_exception(ctx, EXCP_RI);
                 break;
             }
+        } else if (ctx->insn_flags & INSN_LOONGSON2E) {
+            gen_loongson_integer(ctx, op1, rd, rs, rt);
+        } else {
+            generate_exception(ctx, EXCP_RI);
+        }
+        break;
+    case OPC_LX_DSP:
+        op2 = MASK_LX(ctx->opcode);
+        switch (op2) {
+#if defined(TARGET_MIPS64)
+        case OPC_LDX:
+#endif
+        case OPC_LBUX:
+        case OPC_LHX:
+        case OPC_LWX:
+            gen_mipsdsp_ld(ctx, op2, rd, rs, rt);
             break;
-        case OPC_SHLL_QB_DSP:
-            gen_mipsdsp_shift(ctx, op1, rd, rs, rt);
+        default:            /* Invalid */
+            MIPS_INVAL("MASK LX");
+            generate_exception(ctx, EXCP_RI);
             break;
-        case OPC_DPA_W_PH_DSP:
-            op2 = MASK_DPA_W_PH(ctx->opcode);
-            switch (op2) {
-            case OPC_DPAU_H_QBL:
-            case OPC_DPAU_H_QBR:
-            case OPC_DPSU_H_QBL:
-            case OPC_DPSU_H_QBR:
-            case OPC_DPA_W_PH:
-            case OPC_DPAX_W_PH:
-            case OPC_DPAQ_S_W_PH:
-            case OPC_DPAQX_S_W_PH:
-            case OPC_DPAQX_SA_W_PH:
-            case OPC_DPS_W_PH:
-            case OPC_DPSX_W_PH:
-            case OPC_DPSQ_S_W_PH:
-            case OPC_DPSQX_S_W_PH:
-            case OPC_DPSQX_SA_W_PH:
-            case OPC_MULSAQ_S_W_PH:
-            case OPC_DPAQ_SA_L_W:
-            case OPC_DPSQ_SA_L_W:
-            case OPC_MAQ_S_W_PHL:
-            case OPC_MAQ_S_W_PHR:
-            case OPC_MAQ_SA_W_PHL:
-            case OPC_MAQ_SA_W_PHR:
-            case OPC_MULSA_W_PH:
-                gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0);
-                break;
-            default:            /* Invalid */
-                MIPS_INVAL("MASK DPAW.PH");
-                generate_exception(ctx, EXCP_RI);
-                break;
-            }
+        }
+        break;
+    case OPC_ABSQ_S_PH_DSP:
+        op2 = MASK_ABSQ_S_PH(ctx->opcode);
+        switch (op2) {
+        case OPC_ABSQ_S_QB:
+        case OPC_ABSQ_S_PH:
+        case OPC_ABSQ_S_W:
+        case OPC_PRECEQ_W_PHL:
+        case OPC_PRECEQ_W_PHR:
+        case OPC_PRECEQU_PH_QBL:
+        case OPC_PRECEQU_PH_QBR:
+        case OPC_PRECEQU_PH_QBLA:
+        case OPC_PRECEQU_PH_QBRA:
+        case OPC_PRECEU_PH_QBL:
+        case OPC_PRECEU_PH_QBR:
+        case OPC_PRECEU_PH_QBLA:
+        case OPC_PRECEU_PH_QBRA:
+            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
+            break;
+        case OPC_BITREV:
+        case OPC_REPL_QB:
+        case OPC_REPLV_QB:
+        case OPC_REPL_PH:
+        case OPC_REPLV_PH:
+            gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt);
+            break;
+        default:
+            MIPS_INVAL("MASK ABSQ_S.PH");
+            generate_exception(ctx, EXCP_RI);
+            break;
+        }
+        break;
+    case OPC_ADDU_QB_DSP:
+        op2 = MASK_ADDU_QB(ctx->opcode);
+        switch (op2) {
+        case OPC_ADDQ_PH:
+        case OPC_ADDQ_S_PH:
+        case OPC_ADDQ_S_W:
+        case OPC_ADDU_QB:
+        case OPC_ADDU_S_QB:
+        case OPC_ADDU_PH:
+        case OPC_ADDU_S_PH:
+        case OPC_SUBQ_PH:
+        case OPC_SUBQ_S_PH:
+        case OPC_SUBQ_S_W:
+        case OPC_SUBU_QB:
+        case OPC_SUBU_S_QB:
+        case OPC_SUBU_PH:
+        case OPC_SUBU_S_PH:
+        case OPC_ADDSC:
+        case OPC_ADDWC:
+        case OPC_MODSUB:
+        case OPC_RADDU_W_QB:
+            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
+            break;
+        case OPC_MULEU_S_PH_QBL:
+        case OPC_MULEU_S_PH_QBR:
+        case OPC_MULQ_RS_PH:
+        case OPC_MULEQ_S_W_PHL:
+        case OPC_MULEQ_S_W_PHR:
+        case OPC_MULQ_S_PH:
+            gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1);
+            break;
+        default:            /* Invalid */
+            MIPS_INVAL("MASK ADDU.QB");
+            generate_exception(ctx, EXCP_RI);
             break;
-        case OPC_INSV_DSP:
-            op2 = MASK_INSV(ctx->opcode);
-            switch (op2) {
-            case OPC_INSV:
-                check_dsp(ctx);
-                {
-                    TCGv t0, t1;
 
-                    if (rt == 0) {
-                        MIPS_DEBUG("NOP");
-                        break;
-                    }
+        }
+        break;
+    case OPC_CMPU_EQ_QB_DSP:
+        op2 = MASK_CMPU_EQ_QB(ctx->opcode);
+        switch (op2) {
+        case OPC_PRECR_SRA_PH_W:
+        case OPC_PRECR_SRA_R_PH_W:
+            gen_mipsdsp_arith(ctx, op1, op2, rt, rs, rd);
+            break;
+        case OPC_PRECR_QB_PH:
+        case OPC_PRECRQ_QB_PH:
+        case OPC_PRECRQ_PH_W:
+        case OPC_PRECRQ_RS_PH_W:
+        case OPC_PRECRQU_S_QB_PH:
+            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
+            break;
+        case OPC_CMPU_EQ_QB:
+        case OPC_CMPU_LT_QB:
+        case OPC_CMPU_LE_QB:
+        case OPC_CMP_EQ_PH:
+        case OPC_CMP_LT_PH:
+        case OPC_CMP_LE_PH:
+            gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 0);
+            break;
+        case OPC_CMPGU_EQ_QB:
+        case OPC_CMPGU_LT_QB:
+        case OPC_CMPGU_LE_QB:
+        case OPC_CMPGDU_EQ_QB:
+        case OPC_CMPGDU_LT_QB:
+        case OPC_CMPGDU_LE_QB:
+        case OPC_PICK_QB:
+        case OPC_PICK_PH:
+        case OPC_PACKRL_PH:
+            gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 1);
+            break;
+        default:            /* Invalid */
+            MIPS_INVAL("MASK CMPU.EQ.QB");
+            generate_exception(ctx, EXCP_RI);
+            break;
+        }
+        break;
+    case OPC_SHLL_QB_DSP:
+        gen_mipsdsp_shift(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_DPA_W_PH_DSP:
+        op2 = MASK_DPA_W_PH(ctx->opcode);
+        switch (op2) {
+        case OPC_DPAU_H_QBL:
+        case OPC_DPAU_H_QBR:
+        case OPC_DPSU_H_QBL:
+        case OPC_DPSU_H_QBR:
+        case OPC_DPA_W_PH:
+        case OPC_DPAX_W_PH:
+        case OPC_DPAQ_S_W_PH:
+        case OPC_DPAQX_S_W_PH:
+        case OPC_DPAQX_SA_W_PH:
+        case OPC_DPS_W_PH:
+        case OPC_DPSX_W_PH:
+        case OPC_DPSQ_S_W_PH:
+        case OPC_DPSQX_S_W_PH:
+        case OPC_DPSQX_SA_W_PH:
+        case OPC_MULSAQ_S_W_PH:
+        case OPC_DPAQ_SA_L_W:
+        case OPC_DPSQ_SA_L_W:
+        case OPC_MAQ_S_W_PHL:
+        case OPC_MAQ_S_W_PHR:
+        case OPC_MAQ_SA_W_PHL:
+        case OPC_MAQ_SA_W_PHR:
+        case OPC_MULSA_W_PH:
+            gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0);
+            break;
+        default:            /* Invalid */
+            MIPS_INVAL("MASK DPAW.PH");
+            generate_exception(ctx, EXCP_RI);
+            break;
+        }
+        break;
+    case OPC_INSV_DSP:
+        op2 = MASK_INSV(ctx->opcode);
+        switch (op2) {
+        case OPC_INSV:
+            check_dsp(ctx);
+            {
+                TCGv t0, t1;
 
-                    t0 = tcg_temp_new();
-                    t1 = tcg_temp_new();
+                if (rt == 0) {
+                    MIPS_DEBUG("NOP");
+                    break;
+                }
 
-                    gen_load_gpr(t0, rt);
-                    gen_load_gpr(t1, rs);
+                t0 = tcg_temp_new();
+                t1 = tcg_temp_new();
 
-                    gen_helper_insv(cpu_gpr[rt], cpu_env, t1, t0);
+                gen_load_gpr(t0, rt);
+                gen_load_gpr(t1, rs);
 
-                    tcg_temp_free(t0);
-                    tcg_temp_free(t1);
-                    break;
-                }
-            default:            /* Invalid */
-                MIPS_INVAL("MASK INSV");
-                generate_exception(ctx, EXCP_RI);
+                gen_helper_insv(cpu_gpr[rt], cpu_env, t1, t0);
+
+                tcg_temp_free(t0);
+                tcg_temp_free(t1);
                 break;
             }
+        default:            /* Invalid */
+            MIPS_INVAL("MASK INSV");
+            generate_exception(ctx, EXCP_RI);
             break;
-        case OPC_APPEND_DSP:
-            gen_mipsdsp_append(env, ctx, op1, rt, rs, rd);
+        }
+        break;
+    case OPC_APPEND_DSP:
+        gen_mipsdsp_append(env, ctx, op1, rt, rs, rd);
+        break;
+    case OPC_EXTR_W_DSP:
+        op2 = MASK_EXTR_W(ctx->opcode);
+        switch (op2) {
+        case OPC_EXTR_W:
+        case OPC_EXTR_R_W:
+        case OPC_EXTR_RS_W:
+        case OPC_EXTR_S_H:
+        case OPC_EXTRV_S_H:
+        case OPC_EXTRV_W:
+        case OPC_EXTRV_R_W:
+        case OPC_EXTRV_RS_W:
+        case OPC_EXTP:
+        case OPC_EXTPV:
+        case OPC_EXTPDP:
+        case OPC_EXTPDPV:
+            gen_mipsdsp_accinsn(ctx, op1, op2, rt, rs, rd, 1);
             break;
-        case OPC_EXTR_W_DSP:
-            op2 = MASK_EXTR_W(ctx->opcode);
-            switch (op2) {
-            case OPC_EXTR_W:
-            case OPC_EXTR_R_W:
-            case OPC_EXTR_RS_W:
-            case OPC_EXTR_S_H:
-            case OPC_EXTRV_S_H:
-            case OPC_EXTRV_W:
-            case OPC_EXTRV_R_W:
-            case OPC_EXTRV_RS_W:
-            case OPC_EXTP:
-            case OPC_EXTPV:
-            case OPC_EXTPDP:
-            case OPC_EXTPDPV:
-                gen_mipsdsp_accinsn(ctx, op1, op2, rt, rs, rd, 1);
-                break;
-            case OPC_RDDSP:
-                gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 1);
-                break;
-            case OPC_SHILO:
-            case OPC_SHILOV:
-            case OPC_MTHLIP:
-            case OPC_WRDSP:
-                gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 0);
-                break;
-            default:            /* Invalid */
-                MIPS_INVAL("MASK EXTR.W");
-                generate_exception(ctx, EXCP_RI);
-                break;
-            }
+        case OPC_RDDSP:
+            gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 1);
             break;
+        case OPC_SHILO:
+        case OPC_SHILOV:
+        case OPC_MTHLIP:
+        case OPC_WRDSP:
+            gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 0);
+            break;
+        default:            /* Invalid */
+            MIPS_INVAL("MASK EXTR.W");
+            generate_exception(ctx, EXCP_RI);
+            break;
+        }
+        break;
 #if defined(TARGET_MIPS64)
-        case OPC_DEXTM ... OPC_DEXT:
-        case OPC_DINSM ... OPC_DINS:
-            check_insn(ctx, ISA_MIPS64R2);
-            check_mips_64(ctx);
-            gen_bitops(ctx, op1, rt, rs, sa, rd);
+    case OPC_DDIV_G_2E ... OPC_DDIVU_G_2E:
+    case OPC_DMULT_G_2E ... OPC_DMULTU_G_2E:
+    case OPC_DMOD_G_2E ... OPC_DMODU_G_2E:
+        check_insn(ctx, INSN_LOONGSON2E);
+        gen_loongson_integer(ctx, op1, rd, rs, rt);
+        break;
+    case OPC_ABSQ_S_QH_DSP:
+        op2 = MASK_ABSQ_S_QH(ctx->opcode);
+        switch (op2) {
+        case OPC_PRECEQ_L_PWL:
+        case OPC_PRECEQ_L_PWR:
+        case OPC_PRECEQ_PW_QHL:
+        case OPC_PRECEQ_PW_QHR:
+        case OPC_PRECEQ_PW_QHLA:
+        case OPC_PRECEQ_PW_QHRA:
+        case OPC_PRECEQU_QH_OBL:
+        case OPC_PRECEQU_QH_OBR:
+        case OPC_PRECEQU_QH_OBLA:
+        case OPC_PRECEQU_QH_OBRA:
+        case OPC_PRECEU_QH_OBL:
+        case OPC_PRECEU_QH_OBR:
+        case OPC_PRECEU_QH_OBLA:
+        case OPC_PRECEU_QH_OBRA:
+        case OPC_ABSQ_S_OB:
+        case OPC_ABSQ_S_PW:
+        case OPC_ABSQ_S_QH:
+            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
             break;
-        case OPC_DBSHFL:
-            check_insn(ctx, ISA_MIPS64R2);
-            check_mips_64(ctx);
-            op2 = MASK_DBSHFL(ctx->opcode);
-            gen_bshfl(ctx, op2, rt, rd);
+        case OPC_REPL_OB:
+        case OPC_REPL_PW:
+        case OPC_REPL_QH:
+        case OPC_REPLV_OB:
+        case OPC_REPLV_PW:
+        case OPC_REPLV_QH:
+            gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt);
             break;
-        case OPC_DDIV_G_2E ... OPC_DDIVU_G_2E:
-        case OPC_DMULT_G_2E ... OPC_DMULTU_G_2E:
-        case OPC_DMOD_G_2E ... OPC_DMODU_G_2E:
-            check_insn(ctx, INSN_LOONGSON2E);
-            gen_loongson_integer(ctx, op1, rd, rs, rt);
+        default:            /* Invalid */
+            MIPS_INVAL("MASK ABSQ_S.QH");
+            generate_exception(ctx, EXCP_RI);
             break;
-        case OPC_ABSQ_S_QH_DSP:
-            op2 = MASK_ABSQ_S_QH(ctx->opcode);
-            switch (op2) {
-            case OPC_PRECEQ_L_PWL:
-            case OPC_PRECEQ_L_PWR:
-            case OPC_PRECEQ_PW_QHL:
-            case OPC_PRECEQ_PW_QHR:
-            case OPC_PRECEQ_PW_QHLA:
-            case OPC_PRECEQ_PW_QHRA:
-            case OPC_PRECEQU_QH_OBL:
-            case OPC_PRECEQU_QH_OBR:
-            case OPC_PRECEQU_QH_OBLA:
-            case OPC_PRECEQU_QH_OBRA:
-            case OPC_PRECEU_QH_OBL:
-            case OPC_PRECEU_QH_OBR:
-            case OPC_PRECEU_QH_OBLA:
-            case OPC_PRECEU_QH_OBRA:
-            case OPC_ABSQ_S_OB:
-            case OPC_ABSQ_S_PW:
-            case OPC_ABSQ_S_QH:
-                gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
-                break;
-            case OPC_REPL_OB:
-            case OPC_REPL_PW:
-            case OPC_REPL_QH:
-            case OPC_REPLV_OB:
-            case OPC_REPLV_PW:
-            case OPC_REPLV_QH:
-                gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt);
-                break;
-            default:            /* Invalid */
-                MIPS_INVAL("MASK ABSQ_S.QH");
-                generate_exception(ctx, EXCP_RI);
-                break;
-            }
+        }
+        break;
+    case OPC_ADDU_OB_DSP:
+        op2 = MASK_ADDU_OB(ctx->opcode);
+        switch (op2) {
+        case OPC_RADDU_L_OB:
+        case OPC_SUBQ_PW:
+        case OPC_SUBQ_S_PW:
+        case OPC_SUBQ_QH:
+        case OPC_SUBQ_S_QH:
+        case OPC_SUBU_OB:
+        case OPC_SUBU_S_OB:
+        case OPC_SUBU_QH:
+        case OPC_SUBU_S_QH:
+        case OPC_SUBUH_OB:
+        case OPC_SUBUH_R_OB:
+        case OPC_ADDQ_PW:
+        case OPC_ADDQ_S_PW:
+        case OPC_ADDQ_QH:
+        case OPC_ADDQ_S_QH:
+        case OPC_ADDU_OB:
+        case OPC_ADDU_S_OB:
+        case OPC_ADDU_QH:
+        case OPC_ADDU_S_QH:
+        case OPC_ADDUH_OB:
+        case OPC_ADDUH_R_OB:
+            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
             break;
-        case OPC_ADDU_OB_DSP:
-            op2 = MASK_ADDU_OB(ctx->opcode);
-            switch (op2) {
-            case OPC_RADDU_L_OB:
-            case OPC_SUBQ_PW:
-            case OPC_SUBQ_S_PW:
-            case OPC_SUBQ_QH:
-            case OPC_SUBQ_S_QH:
-            case OPC_SUBU_OB:
-            case OPC_SUBU_S_OB:
-            case OPC_SUBU_QH:
-            case OPC_SUBU_S_QH:
-            case OPC_SUBUH_OB:
-            case OPC_SUBUH_R_OB:
-            case OPC_ADDQ_PW:
-            case OPC_ADDQ_S_PW:
-            case OPC_ADDQ_QH:
-            case OPC_ADDQ_S_QH:
-            case OPC_ADDU_OB:
-            case OPC_ADDU_S_OB:
-            case OPC_ADDU_QH:
-            case OPC_ADDU_S_QH:
-            case OPC_ADDUH_OB:
-            case OPC_ADDUH_R_OB:
-                gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
-                break;
-            case OPC_MULEQ_S_PW_QHL:
-            case OPC_MULEQ_S_PW_QHR:
-            case OPC_MULEU_S_QH_OBL:
-            case OPC_MULEU_S_QH_OBR:
-            case OPC_MULQ_RS_QH:
-                gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1);
-                break;
-            default:            /* Invalid */
-                MIPS_INVAL("MASK ADDU.OB");
-                generate_exception(ctx, EXCP_RI);
-                break;
-            }
+        case OPC_MULEQ_S_PW_QHL:
+        case OPC_MULEQ_S_PW_QHR:
+        case OPC_MULEU_S_QH_OBL:
+        case OPC_MULEU_S_QH_OBR:
+        case OPC_MULQ_RS_QH:
+            gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1);
             break;
-        case OPC_CMPU_EQ_OB_DSP:
-            op2 = MASK_CMPU_EQ_OB(ctx->opcode);
-            switch (op2) {
-            case OPC_PRECR_SRA_QH_PW:
-            case OPC_PRECR_SRA_R_QH_PW:
-                /* Return value is rt. */
-                gen_mipsdsp_arith(ctx, op1, op2, rt, rs, rd);
-                break;
-            case OPC_PRECR_OB_QH:
-            case OPC_PRECRQ_OB_QH:
-            case OPC_PRECRQ_PW_L:
-            case OPC_PRECRQ_QH_PW:
-            case OPC_PRECRQ_RS_QH_PW:
-            case OPC_PRECRQU_S_OB_QH:
-                gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
-                break;
-            case OPC_CMPU_EQ_OB:
-            case OPC_CMPU_LT_OB:
-            case OPC_CMPU_LE_OB:
-            case OPC_CMP_EQ_QH:
-            case OPC_CMP_LT_QH:
-            case OPC_CMP_LE_QH:
-            case OPC_CMP_EQ_PW:
-            case OPC_CMP_LT_PW:
-            case OPC_CMP_LE_PW:
-                gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 0);
-                break;
-            case OPC_CMPGDU_EQ_OB:
-            case OPC_CMPGDU_LT_OB:
-            case OPC_CMPGDU_LE_OB:
-            case OPC_CMPGU_EQ_OB:
-            case OPC_CMPGU_LT_OB:
-            case OPC_CMPGU_LE_OB:
-            case OPC_PACKRL_PW:
-            case OPC_PICK_OB:
-            case OPC_PICK_PW:
-            case OPC_PICK_QH:
-                gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 1);
-                break;
-            default:            /* Invalid */
-                MIPS_INVAL("MASK CMPU_EQ.OB");
-                generate_exception(ctx, EXCP_RI);
-                break;
-            }
+        default:            /* Invalid */
+            MIPS_INVAL("MASK ADDU.OB");
+            generate_exception(ctx, EXCP_RI);
             break;
-        case OPC_DAPPEND_DSP:
-            gen_mipsdsp_append(env, ctx, op1, rt, rs, rd);
+        }
+        break;
+    case OPC_CMPU_EQ_OB_DSP:
+        op2 = MASK_CMPU_EQ_OB(ctx->opcode);
+        switch (op2) {
+        case OPC_PRECR_SRA_QH_PW:
+        case OPC_PRECR_SRA_R_QH_PW:
+            /* Return value is rt. */
+            gen_mipsdsp_arith(ctx, op1, op2, rt, rs, rd);
             break;
-        case OPC_DEXTR_W_DSP:
-            op2 = MASK_DEXTR_W(ctx->opcode);
-            switch (op2) {
-            case OPC_DEXTP:
-            case OPC_DEXTPDP:
-            case OPC_DEXTPDPV:
-            case OPC_DEXTPV:
-            case OPC_DEXTR_L:
-            case OPC_DEXTR_R_L:
-            case OPC_DEXTR_RS_L:
-            case OPC_DEXTR_W:
-            case OPC_DEXTR_R_W:
-            case OPC_DEXTR_RS_W:
-            case OPC_DEXTR_S_H:
-            case OPC_DEXTRV_L:
-            case OPC_DEXTRV_R_L:
-            case OPC_DEXTRV_RS_L:
-            case OPC_DEXTRV_S_H:
-            case OPC_DEXTRV_W:
-            case OPC_DEXTRV_R_W:
-            case OPC_DEXTRV_RS_W:
-                gen_mipsdsp_accinsn(ctx, op1, op2, rt, rs, rd, 1);
-                break;
-            case OPC_DMTHLIP:
-            case OPC_DSHILO:
-            case OPC_DSHILOV:
-                gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 0);
-                break;
-            default:            /* Invalid */
-                MIPS_INVAL("MASK EXTR.W");
-                generate_exception(ctx, EXCP_RI);
-                break;
-            }
+        case OPC_PRECR_OB_QH:
+        case OPC_PRECRQ_OB_QH:
+        case OPC_PRECRQ_PW_L:
+        case OPC_PRECRQ_QH_PW:
+        case OPC_PRECRQ_RS_QH_PW:
+        case OPC_PRECRQU_S_OB_QH:
+            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt);
             break;
-        case OPC_DPAQ_W_QH_DSP:
-            op2 = MASK_DPAQ_W_QH(ctx->opcode);
-            switch (op2) {
-            case OPC_DPAU_H_OBL:
-            case OPC_DPAU_H_OBR:
-            case OPC_DPSU_H_OBL:
-            case OPC_DPSU_H_OBR:
-            case OPC_DPA_W_QH:
-            case OPC_DPAQ_S_W_QH:
-            case OPC_DPS_W_QH:
-            case OPC_DPSQ_S_W_QH:
-            case OPC_MULSAQ_S_W_QH:
-            case OPC_DPAQ_SA_L_PW:
-            case OPC_DPSQ_SA_L_PW:
-            case OPC_MULSAQ_S_L_PW:
-                gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0);
-                break;
-            case OPC_MAQ_S_W_QHLL:
-            case OPC_MAQ_S_W_QHLR:
-            case OPC_MAQ_S_W_QHRL:
-            case OPC_MAQ_S_W_QHRR:
-            case OPC_MAQ_SA_W_QHLL:
-            case OPC_MAQ_SA_W_QHLR:
-            case OPC_MAQ_SA_W_QHRL:
-            case OPC_MAQ_SA_W_QHRR:
-            case OPC_MAQ_S_L_PWL:
-            case OPC_MAQ_S_L_PWR:
-            case OPC_DMADD:
-            case OPC_DMADDU:
-            case OPC_DMSUB:
-            case OPC_DMSUBU:
-                gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0);
-                break;
-            default:            /* Invalid */
-                MIPS_INVAL("MASK DPAQ.W.QH");
-                generate_exception(ctx, EXCP_RI);
-                break;
-            }
+        case OPC_CMPU_EQ_OB:
+        case OPC_CMPU_LT_OB:
+        case OPC_CMPU_LE_OB:
+        case OPC_CMP_EQ_QH:
+        case OPC_CMP_LT_QH:
+        case OPC_CMP_LE_QH:
+        case OPC_CMP_EQ_PW:
+        case OPC_CMP_LT_PW:
+        case OPC_CMP_LE_PW:
+            gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 0);
             break;
-        case OPC_DINSV_DSP:
-            op2 = MASK_INSV(ctx->opcode);
-            switch (op2) {
-            case OPC_DINSV:
-                {
-                    TCGv t0, t1;
+        case OPC_CMPGDU_EQ_OB:
+        case OPC_CMPGDU_LT_OB:
+        case OPC_CMPGDU_LE_OB:
+        case OPC_CMPGU_EQ_OB:
+        case OPC_CMPGU_LT_OB:
+        case OPC_CMPGU_LE_OB:
+        case OPC_PACKRL_PW:
+        case OPC_PICK_OB:
+        case OPC_PICK_PW:
+        case OPC_PICK_QH:
+            gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 1);
+            break;
+        default:            /* Invalid */
+            MIPS_INVAL("MASK CMPU_EQ.OB");
+            generate_exception(ctx, EXCP_RI);
+            break;
+        }
+        break;
+    case OPC_DAPPEND_DSP:
+        gen_mipsdsp_append(env, ctx, op1, rt, rs, rd);
+        break;
+    case OPC_DEXTR_W_DSP:
+        op2 = MASK_DEXTR_W(ctx->opcode);
+        switch (op2) {
+        case OPC_DEXTP:
+        case OPC_DEXTPDP:
+        case OPC_DEXTPDPV:
+        case OPC_DEXTPV:
+        case OPC_DEXTR_L:
+        case OPC_DEXTR_R_L:
+        case OPC_DEXTR_RS_L:
+        case OPC_DEXTR_W:
+        case OPC_DEXTR_R_W:
+        case OPC_DEXTR_RS_W:
+        case OPC_DEXTR_S_H:
+        case OPC_DEXTRV_L:
+        case OPC_DEXTRV_R_L:
+        case OPC_DEXTRV_RS_L:
+        case OPC_DEXTRV_S_H:
+        case OPC_DEXTRV_W:
+        case OPC_DEXTRV_R_W:
+        case OPC_DEXTRV_RS_W:
+            gen_mipsdsp_accinsn(ctx, op1, op2, rt, rs, rd, 1);
+            break;
+        case OPC_DMTHLIP:
+        case OPC_DSHILO:
+        case OPC_DSHILOV:
+            gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 0);
+            break;
+        default:            /* Invalid */
+            MIPS_INVAL("MASK EXTR.W");
+            generate_exception(ctx, EXCP_RI);
+            break;
+        }
+        break;
+    case OPC_DPAQ_W_QH_DSP:
+        op2 = MASK_DPAQ_W_QH(ctx->opcode);
+        switch (op2) {
+        case OPC_DPAU_H_OBL:
+        case OPC_DPAU_H_OBR:
+        case OPC_DPSU_H_OBL:
+        case OPC_DPSU_H_OBR:
+        case OPC_DPA_W_QH:
+        case OPC_DPAQ_S_W_QH:
+        case OPC_DPS_W_QH:
+        case OPC_DPSQ_S_W_QH:
+        case OPC_MULSAQ_S_W_QH:
+        case OPC_DPAQ_SA_L_PW:
+        case OPC_DPSQ_SA_L_PW:
+        case OPC_MULSAQ_S_L_PW:
+            gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0);
+            break;
+        case OPC_MAQ_S_W_QHLL:
+        case OPC_MAQ_S_W_QHLR:
+        case OPC_MAQ_S_W_QHRL:
+        case OPC_MAQ_S_W_QHRR:
+        case OPC_MAQ_SA_W_QHLL:
+        case OPC_MAQ_SA_W_QHLR:
+        case OPC_MAQ_SA_W_QHRL:
+        case OPC_MAQ_SA_W_QHRR:
+        case OPC_MAQ_S_L_PWL:
+        case OPC_MAQ_S_L_PWR:
+        case OPC_DMADD:
+        case OPC_DMADDU:
+        case OPC_DMSUB:
+        case OPC_DMSUBU:
+            gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0);
+            break;
+        default:            /* Invalid */
+            MIPS_INVAL("MASK DPAQ.W.QH");
+            generate_exception(ctx, EXCP_RI);
+            break;
+        }
+        break;
+    case OPC_DINSV_DSP:
+        op2 = MASK_INSV(ctx->opcode);
+        switch (op2) {
+        case OPC_DINSV:
+        {
+            TCGv t0, t1;
 
-                    if (rt == 0) {
-                        MIPS_DEBUG("NOP");
-                        break;
-                    }
-                    check_dsp(ctx);
+            if (rt == 0) {
+                MIPS_DEBUG("NOP");
+                break;
+            }
+            check_dsp(ctx);
 
-                    t0 = tcg_temp_new();
-                    t1 = tcg_temp_new();
+            t0 = tcg_temp_new();
+            t1 = tcg_temp_new();
 
-                    gen_load_gpr(t0, rt);
-                    gen_load_gpr(t1, rs);
+            gen_load_gpr(t0, rt);
+            gen_load_gpr(t1, rs);
 
-                    gen_helper_dinsv(cpu_gpr[rt], cpu_env, t1, t0);
+            gen_helper_dinsv(cpu_gpr[rt], cpu_env, t1, t0);
 
-                    tcg_temp_free(t0);
-                    tcg_temp_free(t1);
-                    break;
-                }
-            default:            /* Invalid */
-                MIPS_INVAL("MASK DINSV");
-                generate_exception(ctx, EXCP_RI);
-                break;
-            }
-            break;
-        case OPC_SHLL_OB_DSP:
-            gen_mipsdsp_shift(ctx, op1, rd, rs, rt);
+            tcg_temp_free(t0);
+            tcg_temp_free(t1);
             break;
-#endif
+        }
         default:            /* Invalid */
-            MIPS_INVAL("special3");
+            MIPS_INVAL("MASK DINSV");
             generate_exception(ctx, EXCP_RI);
             break;
         }
         break;
+    case OPC_SHLL_OB_DSP:
+        gen_mipsdsp_shift(ctx, op1, rd, rs, rt);
+        break;
+#endif
+    default:            /* Invalid */
+        MIPS_INVAL("special3_legacy");
+        generate_exception(ctx, EXCP_RI);
+        break;
+    }
+}
+
+static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx)
+{
+    int rs, rt, rd, sa;
+    uint32_t op1, op2;
+
+    rs = (ctx->opcode >> 21) & 0x1f;
+    rt = (ctx->opcode >> 16) & 0x1f;
+    rd = (ctx->opcode >> 11) & 0x1f;
+    sa = (ctx->opcode >> 6) & 0x1f;
+
+    op1 = MASK_SPECIAL3(ctx->opcode);
+    switch (op1) {
+    case OPC_EXT:
+    case OPC_INS:
+        check_insn(ctx, ISA_MIPS32R2);
+        gen_bitops(ctx, op1, rt, rs, sa, rd);
+        break;
+    case OPC_BSHFL:
+        op2 = MASK_BSHFL(ctx->opcode);
+        switch (op2) {
+        case OPC_ALIGN ... OPC_ALIGN_END:
+        case OPC_BITSWAP:
+            check_insn(ctx, ISA_MIPS32R6);
+            decode_opc_special3_r6(env, ctx);
+            break;
+        default:
+            check_insn(ctx, ISA_MIPS32R2);
+            gen_bshfl(ctx, op2, rt, rd);
+            break;
+        }
+        break;
+#if defined(TARGET_MIPS64)
+    case OPC_DEXTM ... OPC_DEXT:
+    case OPC_DINSM ... OPC_DINS:
+        check_insn(ctx, ISA_MIPS64R2);
+        check_mips_64(ctx);
+        gen_bitops(ctx, op1, rt, rs, sa, rd);
+        break;
+    case OPC_DBSHFL:
+        op2 = MASK_DBSHFL(ctx->opcode);
+        switch (op2) {
+        case OPC_DALIGN ... OPC_DALIGN_END:
+        case OPC_DBITSWAP:
+            check_insn(ctx, ISA_MIPS32R6);
+            decode_opc_special3_r6(env, ctx);
+            break;
+        default:
+            check_insn(ctx, ISA_MIPS64R2);
+            check_mips_64(ctx);
+            op2 = MASK_DBSHFL(ctx->opcode);
+            gen_bshfl(ctx, op2, rt, rd);
+            break;
+        }
+        break;
+#endif
+    case OPC_RDHWR:
+        gen_rdhwr(ctx, rt, rd);
+        break;
+    case OPC_FORK:
+        check_insn(ctx, ASE_MT);
+        {
+            TCGv t0 = tcg_temp_new();
+            TCGv t1 = tcg_temp_new();
+
+            gen_load_gpr(t0, rt);
+            gen_load_gpr(t1, rs);
+            gen_helper_fork(t0, t1);
+            tcg_temp_free(t0);
+            tcg_temp_free(t1);
+        }
+        break;
+    case OPC_YIELD:
+        check_insn(ctx, ASE_MT);
+        {
+            TCGv t0 = tcg_temp_new();
+
+            save_cpu_state(ctx, 1);
+            gen_load_gpr(t0, rs);
+            gen_helper_yield(t0, cpu_env, t0);
+            gen_store_gpr(t0, rd);
+            tcg_temp_free(t0);
+        }
+        break;
+    default:
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            decode_opc_special3_r6(env, ctx);
+        } else {
+            decode_opc_special3_legacy(env, ctx);
+        }
+    }
+}
+
+static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
+{
+    int32_t offset;
+    int rs, rt, rd, sa;
+    uint32_t op, op1;
+    int16_t imm;
+
+    /* make sure instructions are on a word boundary */
+    if (ctx->pc & 0x3) {
+        env->CP0_BadVAddr = ctx->pc;
+        generate_exception(ctx, EXCP_AdEL);
+        return;
+    }
+
+    /* Handle blikely not taken case */
+    if ((ctx->hflags & MIPS_HFLAG_BMASK_BASE) == MIPS_HFLAG_BL) {
+        int l1 = gen_new_label();
+
+        MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4);
+        tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1);
+        tcg_gen_movi_i32(hflags, ctx->hflags & ~MIPS_HFLAG_BMASK);
+        gen_goto_tb(ctx, 1, ctx->pc + 4);
+        gen_set_label(l1);
+    }
+
+    if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) {
+        tcg_gen_debug_insn_start(ctx->pc);
+    }
+
+    op = MASK_OP_MAJOR(ctx->opcode);
+    rs = (ctx->opcode >> 21) & 0x1f;
+    rt = (ctx->opcode >> 16) & 0x1f;
+    rd = (ctx->opcode >> 11) & 0x1f;
+    sa = (ctx->opcode >> 6) & 0x1f;
+    imm = (int16_t)ctx->opcode;
+    switch (op) {
+    case OPC_SPECIAL:
+        decode_opc_special(env, ctx);
+        break;
+    case OPC_SPECIAL2:
+        decode_opc_special2_legacy(env, ctx);
+        break;
+    case OPC_SPECIAL3:
+        decode_opc_special3(env, ctx);
+        break;
     case OPC_REGIMM:
         op1 = MASK_REGIMM(ctx->opcode);
         switch (op1) {
-        case OPC_BLTZ ... OPC_BGEZL: /* REGIMM branches */
-        case OPC_BLTZAL ... OPC_BGEZALL:
-            gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2);
+        case OPC_BLTZL: /* REGIMM branches */
+        case OPC_BGEZL:
+        case OPC_BLTZALL:
+        case OPC_BGEZALL:
+            check_insn_opc_removed(ctx, ISA_MIPS32R6);
+        case OPC_BLTZ:
+        case OPC_BGEZ:
+            gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2, 4);
+            break;
+        case OPC_BLTZAL:
+        case OPC_BGEZAL:
+            if (ctx->insn_flags & ISA_MIPS32R6) {
+                if (rs == 0) {
+                    /* OPC_NAL, OPC_BAL */
+                    gen_compute_branch(ctx, op1, 4, 0, -1, imm << 2, 4);
+                } else {
+                    generate_exception(ctx, EXCP_RI);
+                }
+            } else {
+                gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2, 4);
+            }
             break;
         case OPC_TGEI ... OPC_TEQI: /* REGIMM traps */
         case OPC_TNEI:
+            check_insn_opc_removed(ctx, ISA_MIPS32R6);
             gen_trap(ctx, op1, rs, -1, imm);
             break;
         case OPC_SYNCI:
             check_insn(ctx, ISA_MIPS32R2);
-            /* Treat as NOP. */
+            /* Break the TB to be able to sync copied instructions
+               immediately */
+            ctx->bstate = BS_STOP;
             break;
         case OPC_BPOSGE32:    /* MIPS DSP branch */
 #if defined(TARGET_MIPS64)
         case OPC_BPOSGE64:
 #endif
             check_dsp(ctx);
-            gen_compute_branch(ctx, op1, 4, -1, -2, (int32_t)imm << 2);
+            gen_compute_branch(ctx, op1, 4, -1, -2, (int32_t)imm << 2, 4);
+            break;
+#if defined(TARGET_MIPS64)
+        case OPC_DAHI:
+            check_insn(ctx, ISA_MIPS32R6);
+            check_mips_64(ctx);
+            if (rs != 0) {
+                tcg_gen_addi_tl(cpu_gpr[rs], cpu_gpr[rs], (int64_t)imm << 32);
+            }
+            MIPS_DEBUG("dahi %s, %04x", regnames[rs], imm);
             break;
+        case OPC_DATI:
+            check_insn(ctx, ISA_MIPS32R6);
+            check_mips_64(ctx);
+            if (rs != 0) {
+                tcg_gen_addi_tl(cpu_gpr[rs], cpu_gpr[rs], (int64_t)imm << 48);
+            }
+            MIPS_DEBUG("dati %s, %04x", regnames[rs], imm);
+            break;
+#endif
         default:            /* Invalid */
             MIPS_INVAL("regimm");
             generate_exception(ctx, EXCP_RI);
@@ -15376,6 +16905,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
         case OPC_MFMC0:
 #ifndef CONFIG_USER_ONLY
             {
+                uint32_t op2;
                 TCGv t0 = tcg_temp_new();
 
                 op2 = MASK_MFMC0(ctx->opcode);
@@ -15439,7 +16969,16 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
             break;
         }
         break;
-    case OPC_ADDI: /* Arithmetic with immediate opcode */
+    case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC, OPC_ADDI */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_BOVC, OPC_BEQZALC, OPC_BEQC */
+            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2);
+        } else {
+            /* OPC_ADDI */
+            /* Arithmetic with immediate opcode */
+            gen_arith_imm(ctx, op, rt, rs, imm);
+        }
+        break;
     case OPC_ADDIU:
          gen_arith_imm(ctx, op, rt, rs, imm);
          break;
@@ -15448,36 +16987,96 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
          gen_slt_imm(ctx, op, rt, rs, imm);
          break;
     case OPC_ANDI: /* Arithmetic with immediate opcode */
-    case OPC_LUI:
+    case OPC_LUI: /* OPC_AUI */
     case OPC_ORI:
     case OPC_XORI:
          gen_logic_imm(ctx, op, rt, rs, imm);
          break;
     case OPC_J ... OPC_JAL: /* Jump */
          offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2;
-         gen_compute_branch(ctx, op, 4, rs, rt, offset);
+         gen_compute_branch(ctx, op, 4, rs, rt, offset, 4);
          break;
-    case OPC_BEQ ... OPC_BGTZ: /* Branch */
-    case OPC_BEQL ... OPC_BGTZL:
-         gen_compute_branch(ctx, op, 4, rs, rt, imm << 2);
+    /* Branch */
+    case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC, OPC_BLEZL */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            if (rt == 0) {
+                generate_exception(ctx, EXCP_RI);
+                break;
+            }
+            /* OPC_BLEZC, OPC_BGEZC, OPC_BGEC */
+            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2);
+        } else {
+            /* OPC_BLEZL */
+            gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4);
+        }
+        break;
+    case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC, OPC_BGTZL */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            if (rt == 0) {
+                generate_exception(ctx, EXCP_RI);
+                break;
+            }
+            /* OPC_BGTZC, OPC_BLTZC, OPC_BLTC */
+            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2);
+        } else {
+            /* OPC_BGTZL */
+            gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4);
+        }
+        break;
+    case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC, OPC_BLEZ */
+        if (rt == 0) {
+            /* OPC_BLEZ */
+            gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4);
+        } else {
+            check_insn(ctx, ISA_MIPS32R6);
+            /* OPC_BLEZALC, OPC_BGEZALC, OPC_BGEUC */
+            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2);
+        }
+        break;
+    case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC, OPC_BGTZ */
+        if (rt == 0) {
+            /* OPC_BGTZ */
+            gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4);
+        } else {
+            check_insn(ctx, ISA_MIPS32R6);
+            /* OPC_BGTZALC, OPC_BLTZALC, OPC_BLTUC */
+            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2);
+        }
+        break;
+    case OPC_BEQL:
+    case OPC_BNEL:
+         check_insn_opc_removed(ctx, ISA_MIPS32R6);
+    case OPC_BEQ:
+    case OPC_BNE:
+         gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4);
          break;
-    case OPC_LB ... OPC_LWR: /* Load and stores */
+    case OPC_LWL: /* Load and stores */
+    case OPC_LWR:
     case OPC_LL:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
+    case OPC_LB ... OPC_LH:
+    case OPC_LW ... OPC_LHU:
          gen_ld(ctx, op, rt, rs, imm);
          break;
-    case OPC_SB ... OPC_SW:
+    case OPC_SWL:
     case OPC_SWR:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
+    case OPC_SB ... OPC_SH:
+    case OPC_SW:
          gen_st(ctx, op, rt, rs, imm);
          break;
     case OPC_SC:
+         check_insn_opc_removed(ctx, ISA_MIPS32R6);
          gen_st_cond(ctx, op, rt, rs, imm);
          break;
     case OPC_CACHE:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         check_cp0_enabled(ctx);
         check_insn(ctx, ISA_MIPS3 | ISA_MIPS32);
         /* Treat as NOP. */
         break;
     case OPC_PREF:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         check_insn(ctx, ISA_MIPS4 | ISA_MIPS32);
         /* Treat as NOP. */
         break;
@@ -15511,23 +17110,106 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
                 gen_cp1(ctx, op1, rt, rd);
                 break;
 #endif
-            case OPC_BC1ANY2:
+            case OPC_BC1EQZ: /* OPC_BC1ANY2 */
+                if (ctx->insn_flags & ISA_MIPS32R6) {
+                    /* OPC_BC1EQZ */
+                    gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode),
+                                    rt, imm << 2);
+                } else {
+                    /* OPC_BC1ANY2 */
+                    check_cop1x(ctx);
+                    check_insn(ctx, ASE_MIPS3D);
+                    gen_compute_branch1(ctx, MASK_BC1(ctx->opcode),
+                                    (rt >> 2) & 0x7, imm << 2);
+                }
+                break;
+            case OPC_BC1NEZ:
+                check_insn(ctx, ISA_MIPS32R6);
+                gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode),
+                                rt, imm << 2);
+                break;
             case OPC_BC1ANY4:
+                check_insn_opc_removed(ctx, ISA_MIPS32R6);
                 check_cop1x(ctx);
                 check_insn(ctx, ASE_MIPS3D);
                 /* fall through */
             case OPC_BC1:
+                check_insn_opc_removed(ctx, ISA_MIPS32R6);
                 gen_compute_branch1(ctx, MASK_BC1(ctx->opcode),
                                     (rt >> 2) & 0x7, imm << 2);
                 break;
+            case OPC_PS_FMT:
+                check_insn_opc_removed(ctx, ISA_MIPS32R6);
             case OPC_S_FMT:
             case OPC_D_FMT:
-            case OPC_W_FMT:
-            case OPC_L_FMT:
-            case OPC_PS_FMT:
                 gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa,
                            (imm >> 8) & 0x7);
                 break;
+            case OPC_W_FMT:
+            case OPC_L_FMT:
+            {
+                int r6_op = ctx->opcode & FOP(0x3f, 0x1f);
+                if (ctx->insn_flags & ISA_MIPS32R6) {
+                    switch (r6_op) {
+                    case R6_OPC_CMP_AF_S:
+                    case R6_OPC_CMP_UN_S:
+                    case R6_OPC_CMP_EQ_S:
+                    case R6_OPC_CMP_UEQ_S:
+                    case R6_OPC_CMP_LT_S:
+                    case R6_OPC_CMP_ULT_S:
+                    case R6_OPC_CMP_LE_S:
+                    case R6_OPC_CMP_ULE_S:
+                    case R6_OPC_CMP_SAF_S:
+                    case R6_OPC_CMP_SUN_S:
+                    case R6_OPC_CMP_SEQ_S:
+                    case R6_OPC_CMP_SEUQ_S:
+                    case R6_OPC_CMP_SLT_S:
+                    case R6_OPC_CMP_SULT_S:
+                    case R6_OPC_CMP_SLE_S:
+                    case R6_OPC_CMP_SULE_S:
+                    case R6_OPC_CMP_OR_S:
+                    case R6_OPC_CMP_UNE_S:
+                    case R6_OPC_CMP_NE_S:
+                    case R6_OPC_CMP_SOR_S:
+                    case R6_OPC_CMP_SUNE_S:
+                    case R6_OPC_CMP_SNE_S:
+                        gen_r6_cmp_s(ctx, ctx->opcode & 0x1f, rt, rd, sa);
+                        break;
+                    case R6_OPC_CMP_AF_D:
+                    case R6_OPC_CMP_UN_D:
+                    case R6_OPC_CMP_EQ_D:
+                    case R6_OPC_CMP_UEQ_D:
+                    case R6_OPC_CMP_LT_D:
+                    case R6_OPC_CMP_ULT_D:
+                    case R6_OPC_CMP_LE_D:
+                    case R6_OPC_CMP_ULE_D:
+                    case R6_OPC_CMP_SAF_D:
+                    case R6_OPC_CMP_SUN_D:
+                    case R6_OPC_CMP_SEQ_D:
+                    case R6_OPC_CMP_SEUQ_D:
+                    case R6_OPC_CMP_SLT_D:
+                    case R6_OPC_CMP_SULT_D:
+                    case R6_OPC_CMP_SLE_D:
+                    case R6_OPC_CMP_SULE_D:
+                    case R6_OPC_CMP_OR_D:
+                    case R6_OPC_CMP_UNE_D:
+                    case R6_OPC_CMP_NE_D:
+                    case R6_OPC_CMP_SOR_D:
+                    case R6_OPC_CMP_SUNE_D:
+                    case R6_OPC_CMP_SNE_D:
+                        gen_r6_cmp_d(ctx, ctx->opcode & 0x1f, rt, rd, sa);
+                        break;
+                    default:
+                        gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa,
+                                                       (imm >> 8) & 0x7);
+                        break;
+                    }
+                } else {
+                    gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa,
+                               (imm >> 8) & 0x7);
+                }
+                break;
+            }
             default:
                 MIPS_INVAL("cp1");
                 generate_exception (ctx, EXCP_RI);
@@ -15538,13 +17220,35 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
         }
         break;
 
-    /* COP2.  */
-    case OPC_LWC2:
-    case OPC_LDC2:
-    case OPC_SWC2:
-    case OPC_SDC2:
-        /* COP2: Not implemented. */
-        generate_exception_err(ctx, EXCP_CpU, 2);
+    /* Compact branches [R6] and COP2 [non-R6] */
+    case OPC_BC: /* OPC_LWC2 */
+    case OPC_BALC: /* OPC_SWC2 */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_BC, OPC_BALC */
+            gen_compute_compact_branch(ctx, op, 0, 0,
+                                       sextract32(ctx->opcode << 2, 0, 28));
+        } else {
+            /* OPC_LWC2, OPC_SWC2 */
+            /* COP2: Not implemented. */
+            generate_exception_err(ctx, EXCP_CpU, 2);
+        }
+        break;
+    case OPC_BEQZC: /* OPC_JIC, OPC_LDC2 */
+    case OPC_BNEZC: /* OPC_JIALC, OPC_SDC2 */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            if (rs != 0) {
+                /* OPC_BEQZC, OPC_BNEZC */
+                gen_compute_compact_branch(ctx, op, rs, 0,
+                                           sextract32(ctx->opcode << 2, 0, 23));
+            } else {
+                /* OPC_JIC, OPC_JIALC */
+                gen_compute_compact_branch(ctx, op, 0, rt, imm);
+            }
+        } else {
+            /* OPC_LWC2, OPC_SWC2 */
+            /* COP2: Not implemented. */
+            generate_exception_err(ctx, EXCP_CpU, 2);
+        }
         break;
     case OPC_CP2:
         check_insn(ctx, INSN_LOONGSON2F);
@@ -15553,6 +17257,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
         break;
 
     case OPC_CP3:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         if (ctx->CP0_Config1 & (1 << CP0C1_FP)) {
             check_cp1_enabled(ctx);
             op1 = MASK_CP3(ctx->opcode);
@@ -15595,40 +17300,85 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
 
 #if defined(TARGET_MIPS64)
     /* MIPS64 opcodes */
-    case OPC_LWU:
     case OPC_LDL ... OPC_LDR:
     case OPC_LLD:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
+    case OPC_LWU:
     case OPC_LD:
         check_insn(ctx, ISA_MIPS3);
         check_mips_64(ctx);
         gen_ld(ctx, op, rt, rs, imm);
         break;
     case OPC_SDL ... OPC_SDR:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
     case OPC_SD:
         check_insn(ctx, ISA_MIPS3);
         check_mips_64(ctx);
         gen_st(ctx, op, rt, rs, imm);
         break;
     case OPC_SCD:
+        check_insn_opc_removed(ctx, ISA_MIPS32R6);
         check_insn(ctx, ISA_MIPS3);
         check_mips_64(ctx);
         gen_st_cond(ctx, op, rt, rs, imm);
         break;
-    case OPC_DADDI:
+    case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC, OPC_DADDI */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            /* OPC_BNVC, OPC_BNEZALC, OPC_BNEC */
+            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2);
+        } else {
+            /* OPC_DADDI */
+            check_insn(ctx, ISA_MIPS3);
+            check_mips_64(ctx);
+            gen_arith_imm(ctx, op, rt, rs, imm);
+        }
+        break;
     case OPC_DADDIU:
         check_insn(ctx, ISA_MIPS3);
         check_mips_64(ctx);
         gen_arith_imm(ctx, op, rt, rs, imm);
         break;
+#else
+    case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2);
+        } else {
+            MIPS_INVAL("major opcode");
+            generate_exception(ctx, EXCP_RI);
+        }
+        break;
 #endif
-    case OPC_JALX:
-        check_insn(ctx, ASE_MIPS16 | ASE_MICROMIPS);
-        offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2;
-        gen_compute_branch(ctx, op, 4, rs, rt, offset);
+    case OPC_DAUI: /* OPC_JALX */
+        if (ctx->insn_flags & ISA_MIPS32R6) {
+#if defined(TARGET_MIPS64)
+            /* OPC_DAUI */
+            check_mips_64(ctx);
+            if (rt != 0) {
+                TCGv t0 = tcg_temp_new();
+                gen_load_gpr(t0, rs);
+                tcg_gen_addi_tl(cpu_gpr[rt], t0, imm << 16);
+                tcg_temp_free(t0);
+            }
+            MIPS_DEBUG("daui %s, %s, %04x", regnames[rt], regnames[rs], imm);
+#else
+            generate_exception(ctx, EXCP_RI);
+            MIPS_INVAL("major opcode");
+#endif
+        } else {
+            /* OPC_JALX */
+            check_insn(ctx, ASE_MIPS16 | ASE_MICROMIPS);
+            offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2;
+            gen_compute_branch(ctx, op, 4, rs, rt, offset, 4);
+        }
         break;
     case OPC_MDMX:
         check_insn(ctx, ASE_MDMX);
         /* MDMX: Not implemented. */
+        break;
+    case OPC_PCREL:
+        check_insn(ctx, ISA_MIPS32R6);
+        gen_pcrel(ctx, rs, imm);
+        break;
     default:            /* Invalid */
         MIPS_INVAL("major opcode");
         generate_exception(ctx, EXCP_RI);
@@ -15726,8 +17476,15 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
             ctx.bstate = BS_STOP;
             break;
         }
+
+        if (ctx.hflags & MIPS_HFLAG_BMASK) {
+            if (!(ctx.hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32))) {
+                is_delay = 1;
+                /* force to generate branch as no delay slot is required */
+            }
+        }
         if (is_delay) {
-            handle_delay_slot(&ctx, insn_bytes);
+            gen_branch(&ctx, insn_bytes);
         }
         ctx.pc += insn_bytes;
 
@@ -15944,9 +17701,6 @@ void mips_tcg_init(void)
         cpu_LO[i] = tcg_global_mem_new(TCG_AREG0,
                                        offsetof(CPUMIPSState, active_tc.LO[i]),
                                        regnames_LO[i]);
-        cpu_ACX[i] = tcg_global_mem_new(TCG_AREG0,
-                                        offsetof(CPUMIPSState, active_tc.ACX[i]),
-                                        regnames_ACX[i]);
     }
     cpu_dspctrl = tcg_global_mem_new(TCG_AREG0,
                                      offsetof(CPUMIPSState, active_tc.DSPControl),
@@ -16134,6 +17888,12 @@ void cpu_state_reset(CPUMIPSState *env)
         }
     }
 #endif
+    if ((env->insn_flags & ISA_MIPS32R6) &&
+        (env->active_fpu.fcr0 & (1 << FCR0_F64))) {
+        /* Status.FR = 0 mode in 64-bit FPU not allowed in R6 */
+        env->CP0_Status |= (1 << CP0St_FR);
+    }
+
     compute_hflags(env);
     cs->exception_index = EXCP_NONE;
 }
diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c
index 29dc2ef738..67b7837750 100644
--- a/target-mips/translate_init.c
+++ b/target-mips/translate_init.c
@@ -516,6 +516,36 @@ static const mips_def_t mips_defs[] =
         .mmu_type = MMU_TYPE_R4000,
     },
     {
+        /* A generic CPU supporting MIPS64 Release 6 ISA.
+           FIXME: It does not support all the MIPS64R6 features yet.
+                  Eventually this should be replaced by a real CPU model. */
+        .name = "MIPS64R6-generic",
+        .CP0_PRid = 0x00010000,
+        .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AR) | (0x2 << CP0C0_AT) |
+                       (MMU_TYPE_R4000 << CP0C0_MT),
+        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (63 << CP0C1_MMU) |
+                       (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) |
+                       (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) |
+                       (0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP),
+        .CP0_Config2 = MIPS_CONFIG2,
+        .CP0_Config3 = MIPS_CONFIG3,
+        .CP0_LLAddr_rw_bitmask = 0,
+        .CP0_LLAddr_shift = 0,
+        .SYNCI_Step = 32,
+        .CCRes = 2,
+        .CP0_Status_rw_bitmask = 0x30D8FFFF,
+        .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
+                    (1 << FCR0_D) | (1 << FCR0_S) | (0x00 << FCR0_PRID) |
+                    (0x0 << FCR0_REV),
+        .SEGBITS = 42,
+        /* The architectural limit is 59, but we have hardcoded 36 bit
+           in some places...
+        .PABITS = 59, */ /* the architectural limit */
+        .PABITS = 36,
+        .insn_flags = CPU_MIPS64R6,
+        .mmu_type = MMU_TYPE_R4000,
+    },
+    {
         .name = "Loongson-2E",
         .CP0_PRid = 0x6302,
         /*64KB I-cache and d-cache. 4 way with 32 bit cache line size*/
diff --git a/target-tricore/helper.h b/target-tricore/helper.h
index 7b7d74b32c..b3fc33ceae 100644
--- a/target-tricore/helper.h
+++ b/target-tricore/helper.h
@@ -23,3 +23,10 @@ DEF_HELPER_2(call, void, env, i32)
 DEF_HELPER_1(ret, void, env)
 DEF_HELPER_2(bisr, void, env, i32)
 DEF_HELPER_1(rfe, void, env)
+DEF_HELPER_2(ldlcx, void, env, i32)
+DEF_HELPER_2(lducx, void, env, i32)
+DEF_HELPER_2(stlcx, void, env, i32)
+DEF_HELPER_2(stucx, void, env, i32)
+/* Address mode helper */
+DEF_HELPER_1(br_update, i32, i32)
+DEF_HELPER_2(circ_update, i32, i32, i32)
diff --git a/target-tricore/op_helper.c b/target-tricore/op_helper.c
index 6376f074e5..a36988aa1b 100644
--- a/target-tricore/op_helper.c
+++ b/target-tricore/op_helper.c
@@ -20,6 +20,42 @@
 #include "exec/helper-proto.h"
 #include "exec/cpu_ldst.h"
 
+/* Addressing mode helper */
+
+static uint16_t reverse16(uint16_t val)
+{
+    uint8_t high = (uint8_t)(val >> 8);
+    uint8_t low  = (uint8_t)(val & 0xff);
+
+    uint16_t rh, rl;
+
+    rl = (uint16_t)((high * 0x0202020202ULL & 0x010884422010ULL) % 1023);
+    rh = (uint16_t)((low * 0x0202020202ULL & 0x010884422010ULL) % 1023);
+
+    return (rh << 8) | rl;
+}
+
+uint32_t helper_br_update(uint32_t reg)
+{
+    uint32_t index = reg & 0xffff;
+    uint32_t incr  = reg >> 16;
+    uint32_t new_index = reverse16(reverse16(index) + reverse16(incr));
+    return reg - index + new_index;
+}
+
+uint32_t helper_circ_update(uint32_t reg, uint32_t off)
+{
+    uint32_t index = reg & 0xffff;
+    uint32_t length = reg >> 16;
+    int32_t new_index = index + off;
+    if (new_index < 0) {
+        new_index += length;
+    } else {
+        new_index %= length;
+    }
+    return reg - index + new_index;
+}
+
 #define SSOV(env, ret, arg, len) do {               \
     int64_t max_pos = INT##len ##_MAX;              \
     int64_t max_neg = INT##len ##_MIN;              \
@@ -114,10 +150,8 @@ static bool cdc_zero(target_ulong *psw)
     return count == 0;
 }
 
-static void save_context_upper(CPUTriCoreState *env, int ea,
-                               target_ulong *new_FCX)
+static void save_context_upper(CPUTriCoreState *env, int ea)
 {
-    *new_FCX = cpu_ldl_data(env, ea);
     cpu_stl_data(env, ea, env->PCXI);
     cpu_stl_data(env, ea+4, env->PSW);
     cpu_stl_data(env, ea+8, env->gpr_a[10]);
@@ -134,15 +168,12 @@ static void save_context_upper(CPUTriCoreState *env, int ea,
     cpu_stl_data(env, ea+52, env->gpr_d[13]);
     cpu_stl_data(env, ea+56, env->gpr_d[14]);
     cpu_stl_data(env, ea+60, env->gpr_d[15]);
-
 }
 
-static void save_context_lower(CPUTriCoreState *env, int ea,
-                               target_ulong *new_FCX)
+static void save_context_lower(CPUTriCoreState *env, int ea)
 {
-    *new_FCX = cpu_ldl_data(env, ea);
     cpu_stl_data(env, ea, env->PCXI);
-    cpu_stl_data(env, ea+4, env->PSW);
+    cpu_stl_data(env, ea+4, env->gpr_a[11]);
     cpu_stl_data(env, ea+8, env->gpr_a[2]);
     cpu_stl_data(env, ea+12, env->gpr_a[3]);
     cpu_stl_data(env, ea+16, env->gpr_d[0]);
@@ -178,7 +209,27 @@ static void restore_context_upper(CPUTriCoreState *env, int ea,
     env->gpr_d[13] = cpu_ldl_data(env, ea+52);
     env->gpr_d[14] = cpu_ldl_data(env, ea+56);
     env->gpr_d[15] = cpu_ldl_data(env, ea+60);
-    cpu_stl_data(env, ea, env->FCX);
+}
+
+static void restore_context_lower(CPUTriCoreState *env, int ea,
+                                  target_ulong *ra, target_ulong *pcxi)
+{
+    *pcxi = cpu_ldl_data(env, ea);
+    *ra = cpu_ldl_data(env, ea+4);
+    env->gpr_a[2] = cpu_ldl_data(env, ea+8);
+    env->gpr_a[3] = cpu_ldl_data(env, ea+12);
+    env->gpr_d[0] = cpu_ldl_data(env, ea+16);
+    env->gpr_d[1] = cpu_ldl_data(env, ea+20);
+    env->gpr_d[2] = cpu_ldl_data(env, ea+24);
+    env->gpr_d[3] = cpu_ldl_data(env, ea+28);
+    env->gpr_a[4] = cpu_ldl_data(env, ea+32);
+    env->gpr_a[5] = cpu_ldl_data(env, ea+36);
+    env->gpr_a[6] = cpu_ldl_data(env, ea+40);
+    env->gpr_a[7] = cpu_ldl_data(env, ea+44);
+    env->gpr_d[4] = cpu_ldl_data(env, ea+48);
+    env->gpr_d[5] = cpu_ldl_data(env, ea+52);
+    env->gpr_d[6] = cpu_ldl_data(env, ea+56);
+    env->gpr_d[7] = cpu_ldl_data(env, ea+60);
 }
 
 void helper_call(CPUTriCoreState *env, uint32_t next_pc)
@@ -206,11 +257,12 @@ void helper_call(CPUTriCoreState *env, uint32_t next_pc)
     /* EA = {FCX.FCXS, 6'b0, FCX.FCXO, 6'b0}; */
     ea = ((env->FCX & MASK_FCX_FCXS) << 12) +
          ((env->FCX & MASK_FCX_FCXO) << 6);
-    /* new_FCX = M(EA, word);
-       M(EA, 16 * word) = {PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11],
-                          A[12], A[13], A[14], A[15], D[12], D[13], D[14],
-                          D[15]}; */
-    save_context_upper(env, ea, &new_FCX);
+    /* new_FCX = M(EA, word); */
+    new_FCX = cpu_ldl_data(env, ea);
+    /* M(EA, 16 * word) = {PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11],
+                           A[12], A[13], A[14], A[15], D[12], D[13], D[14],
+                           D[15]}; */
+    save_context_upper(env, ea);
 
     /* PCXI.PCPN = ICR.CCPN; */
     env->PCXI = (env->PCXI & 0xffffff) +
@@ -263,9 +315,10 @@ void helper_ret(CPUTriCoreState *env)
     ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) +
          ((env->PCXI & MASK_PCXI_PCXO) << 6);
     /* {new_PCXI, new_PSW, A[10], A[11], D[8], D[9], D[10], D[11], A[12],
-        A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word);
-        M(EA, word) = FCX; */
+        A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */
     restore_context_upper(env, ea, &new_PCXI, &new_PSW);
+    /* M(EA, word) = FCX; */
+    cpu_stl_data(env, ea, env->FCX);
     /* FCX[19: 0] = PCXI[19: 0]; */
     env->FCX = (env->FCX & 0xfff00000) + (env->PCXI & 0x000fffff);
     /* PCXI = new_PCXI; */
@@ -293,7 +346,12 @@ void helper_bisr(CPUTriCoreState *env, uint32_t const9)
     tmp_FCX = env->FCX;
     ea = ((env->FCX & 0xf0000) << 12) + ((env->FCX & 0xffff) << 6);
 
-    save_context_lower(env, ea, &new_FCX);
+    /* new_FCX = M(EA, word); */
+    new_FCX = cpu_ldl_data(env, ea);
+    /* M(EA, 16 * word) = {PCXI, A[11], A[2], A[3], D[0], D[1], D[2], D[3], A[4]
+                           , A[5], A[6], A[7], D[4], D[5], D[6], D[7]}; */
+    save_context_lower(env, ea);
+
 
     /* PCXI.PCPN = ICR.CCPN */
     env->PCXI = (env->PCXI & 0xffffff) +
@@ -343,9 +401,10 @@ void helper_rfe(CPUTriCoreState *env)
     ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) +
          ((env->PCXI & MASK_PCXI_PCXO) << 6);
     /*{new_PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11], A[12],
-      A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word);
-      M(EA, word) = FCX;*/
+      A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */
     restore_context_upper(env, ea, &new_PCXI, &new_PSW);
+    /* M(EA, word) = FCX;*/
+    cpu_stl_data(env, ea, env->FCX);
     /* FCX[19: 0] = PCXI[19: 0]; */
     env->FCX = (env->FCX & 0xfff00000) + (env->PCXI & 0x000fffff);
     /* PCXI = new_PCXI; */
@@ -354,6 +413,30 @@ void helper_rfe(CPUTriCoreState *env)
     psw_write(env, new_PSW);
 }
 
+void helper_ldlcx(CPUTriCoreState *env, uint32_t ea)
+{
+    uint32_t dummy;
+    /* insn doesn't load PCXI and RA */
+    restore_context_lower(env, ea, &dummy, &dummy);
+}
+
+void helper_lducx(CPUTriCoreState *env, uint32_t ea)
+{
+    uint32_t dummy;
+    /* insn doesn't load PCXI and PSW */
+    restore_context_upper(env, ea, &dummy, &dummy);
+}
+
+void helper_stlcx(CPUTriCoreState *env, uint32_t ea)
+{
+    save_context_lower(env, ea);
+}
+
+void helper_stucx(CPUTriCoreState *env, uint32_t ea)
+{
+    save_context_upper(env, ea);
+}
+
 static inline void QEMU_NORETURN do_raise_exception_err(CPUTriCoreState *env,
                                                         uint32_t exception,
                                                         int error_code,
@@ -371,13 +454,6 @@ static inline void QEMU_NORETURN do_raise_exception_err(CPUTriCoreState *env,
     cpu_loop_exit(cs);
 }
 
-static inline void QEMU_NORETURN do_raise_exception(CPUTriCoreState *env,
-                                                    uint32_t exception,
-                                                    uintptr_t pc)
-{
-    do_raise_exception_err(env, exception, 0, pc);
-}
-
 void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
               uintptr_t retaddr)
 {
diff --git a/target-tricore/translate.c b/target-tricore/translate.c
index 4f654deb65..d5a95969bb 100644
--- a/target-tricore/translate.c
+++ b/target-tricore/translate.c
@@ -115,6 +115,10 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f,
     tcg_temp_free_i32(helper_tmp);                                \
     } while (0)
 
+#define EA_ABS_FORMAT(con) (((con & 0x3C000) << 14) + (con & 0x3FFF))
+#define EA_B_ABSOLUT(con) (((offset & 0xf00000) << 8) | \
+                           ((offset & 0x0fffff) << 1))
+
 /* Functions for load/save to/from memory */
 
 static inline void gen_offset_ld(DisasContext *ctx, TCGv r1, TCGv r2,
@@ -135,6 +139,100 @@ static inline void gen_offset_st(DisasContext *ctx, TCGv r1, TCGv r2,
     tcg_temp_free(temp);
 }
 
+static void gen_st_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx)
+{
+    TCGv_i64 temp = tcg_temp_new_i64();
+
+    tcg_gen_concat_i32_i64(temp, rl, rh);
+    tcg_gen_qemu_st_i64(temp, address, ctx->mem_idx, MO_LEQ);
+
+    tcg_temp_free_i64(temp);
+}
+
+static void gen_offset_st_2regs(TCGv rh, TCGv rl, TCGv base, int16_t con,
+                                DisasContext *ctx)
+{
+    TCGv temp = tcg_temp_new();
+    tcg_gen_addi_tl(temp, base, con);
+    gen_st_2regs_64(rh, rl, temp, ctx);
+    tcg_temp_free(temp);
+}
+
+static void gen_ld_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx)
+{
+    TCGv_i64 temp = tcg_temp_new_i64();
+
+    tcg_gen_qemu_ld_i64(temp, address, ctx->mem_idx, MO_LEQ);
+    /* write back to two 32 bit regs */
+    tcg_gen_extr_i64_i32(rl, rh, temp);
+
+    tcg_temp_free_i64(temp);
+}
+
+static void gen_offset_ld_2regs(TCGv rh, TCGv rl, TCGv base, int16_t con,
+                                DisasContext *ctx)
+{
+    TCGv temp = tcg_temp_new();
+    tcg_gen_addi_tl(temp, base, con);
+    gen_ld_2regs_64(rh, rl, temp, ctx);
+    tcg_temp_free(temp);
+}
+
+static void gen_st_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off,
+                           TCGMemOp mop)
+{
+    TCGv temp = tcg_temp_new();
+    tcg_gen_addi_tl(temp, r2, off);
+    tcg_gen_qemu_st_tl(r1, temp, ctx->mem_idx, mop);
+    tcg_gen_mov_tl(r2, temp);
+    tcg_temp_free(temp);
+}
+
+static void gen_ld_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off,
+                           TCGMemOp mop)
+{
+    TCGv temp = tcg_temp_new();
+    tcg_gen_addi_tl(temp, r2, off);
+    tcg_gen_qemu_ld_tl(r1, temp, ctx->mem_idx, mop);
+    tcg_gen_mov_tl(r2, temp);
+    tcg_temp_free(temp);
+}
+
+/* M(EA, word) = (M(EA, word) & ~E[a][63:32]) | (E[a][31:0] & E[a][63:32]); */
+static void gen_ldmst(DisasContext *ctx, int ereg, TCGv ea)
+{
+    TCGv temp = tcg_temp_new();
+    TCGv temp2 = tcg_temp_new();
+
+    /* temp = (M(EA, word) */
+    tcg_gen_qemu_ld_tl(temp, ea, ctx->mem_idx, MO_LEUL);
+    /* temp = temp & ~E[a][63:32]) */
+    tcg_gen_andc_tl(temp, temp, cpu_gpr_d[ereg+1]);
+    /* temp2 = (E[a][31:0] & E[a][63:32]); */
+    tcg_gen_and_tl(temp2, cpu_gpr_d[ereg], cpu_gpr_d[ereg+1]);
+    /* temp = temp | temp2; */
+    tcg_gen_or_tl(temp, temp, temp2);
+    /* M(EA, word) = temp; */
+    tcg_gen_qemu_st_tl(temp, ea, ctx->mem_idx, MO_LEUL);
+
+    tcg_temp_free(temp);
+    tcg_temp_free(temp2);
+}
+
+/* tmp = M(EA, word);
+   M(EA, word) = D[a];
+   D[a] = tmp[31:0];*/
+static void gen_swap(DisasContext *ctx, int reg, TCGv ea)
+{
+    TCGv temp = tcg_temp_new();
+
+    tcg_gen_qemu_ld_tl(temp, ea, ctx->mem_idx, MO_LEUL);
+    tcg_gen_qemu_st_tl(cpu_gpr_d[reg], ea, ctx->mem_idx, MO_LEUL);
+    tcg_gen_mov_tl(cpu_gpr_d[reg], temp);
+
+    tcg_temp_free(temp);
+}
+
 /* Functions for arithmetic instructions  */
 
 static inline void gen_add_d(TCGv ret, TCGv r1, TCGv r2)
@@ -365,6 +463,49 @@ static inline void gen_subs(TCGv ret, TCGv r1, TCGv r2)
     gen_helper_sub_ssov(ret, cpu_env, r1, r2);
 }
 
+static inline void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2,
+                               int pos1, int pos2,
+                               void(*op1)(TCGv, TCGv, TCGv),
+                               void(*op2)(TCGv, TCGv, TCGv))
+{
+    TCGv temp1, temp2;
+
+    temp1 = tcg_temp_new();
+    temp2 = tcg_temp_new();
+
+    tcg_gen_shri_tl(temp2, r2, pos2);
+    tcg_gen_shri_tl(temp1, r1, pos1);
+
+    (*op1)(temp1, temp1, temp2);
+    (*op2)(temp1 , ret, temp1);
+
+    tcg_gen_deposit_tl(ret, ret, temp1, 0, 1);
+
+    tcg_temp_free(temp1);
+    tcg_temp_free(temp2);
+}
+
+/* ret = r1[pos1] op1 r2[pos2]; */
+static inline void gen_bit_1op(TCGv ret, TCGv r1, TCGv r2,
+                               int pos1, int pos2,
+                               void(*op1)(TCGv, TCGv, TCGv))
+{
+    TCGv temp1, temp2;
+
+    temp1 = tcg_temp_new();
+    temp2 = tcg_temp_new();
+
+    tcg_gen_shri_tl(temp2, r2, pos2);
+    tcg_gen_shri_tl(temp1, r1, pos1);
+
+    (*op1)(ret, temp1, temp2);
+
+    tcg_gen_andi_tl(ret, ret, 0x1);
+
+    tcg_temp_free(temp1);
+    tcg_temp_free(temp2);
+}
+
 /* helpers for generating program flow micro-ops */
 
 static inline void gen_save_pc(target_ulong pc)
@@ -434,6 +575,7 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1,
     case OPC1_32_B_J:
         gen_goto_tb(ctx, 0, ctx->pc + offset * 2);
         break;
+    case OPC1_32_B_CALL:
     case OPC1_16_SB_CALL:
         gen_helper_1arg(call, ctx->next_pc);
         gen_goto_tb(ctx, 0, ctx->pc + offset * 2);
@@ -509,6 +651,20 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1,
         gen_helper_ret(cpu_env);
         tcg_gen_exit_tb(0);
         break;
+/* B-format */
+    case OPC1_32_B_CALLA:
+        gen_helper_1arg(call, ctx->next_pc);
+        gen_goto_tb(ctx, 0, EA_B_ABSOLUT(offset));
+        break;
+    case OPC1_32_B_JLA:
+        tcg_gen_movi_tl(cpu_gpr_a[11], ctx->next_pc);
+    case OPC1_32_B_JA:
+        gen_goto_tb(ctx, 0, EA_B_ABSOLUT(offset));
+        break;
+    case OPC1_32_B_JL:
+        tcg_gen_movi_tl(cpu_gpr_a[11], ctx->next_pc);
+        gen_goto_tb(ctx, 0, ctx->pc + offset * 2);
+        break;
     default:
         printf("Branch Error at %x\n", ctx->pc);
     }
@@ -1099,8 +1255,1157 @@ static void decode_16Bit_opc(CPUTriCoreState *env, DisasContext *ctx)
     }
 }
 
+/*
+ * 32 bit instructions
+ */
+
+/* ABS-format */
+static void decode_abs_ldw(CPUTriCoreState *env, DisasContext *ctx)
+{
+    int32_t op2;
+    int32_t r1;
+    uint32_t address;
+    TCGv temp;
+
+    r1 = MASK_OP_ABS_S1D(ctx->opcode);
+    address = MASK_OP_ABS_OFF18(ctx->opcode);
+    op2 = MASK_OP_ABS_OP2(ctx->opcode);
+
+    temp = tcg_const_i32(EA_ABS_FORMAT(address));
+
+    switch (op2) {
+    case OPC2_32_ABS_LD_A:
+        tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL);
+        break;
+    case OPC2_32_ABS_LD_D:
+        gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx);
+        break;
+    case OPC2_32_ABS_LD_DA:
+        gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx);
+        break;
+    case OPC2_32_ABS_LD_W:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL);
+        break;
+    }
+
+    tcg_temp_free(temp);
+}
+
+static void decode_abs_ldb(CPUTriCoreState *env, DisasContext *ctx)
+{
+    int32_t op2;
+    int32_t r1;
+    uint32_t address;
+    TCGv temp;
+
+    r1 = MASK_OP_ABS_S1D(ctx->opcode);
+    address = MASK_OP_ABS_OFF18(ctx->opcode);
+    op2 = MASK_OP_ABS_OP2(ctx->opcode);
+
+    temp = tcg_const_i32(EA_ABS_FORMAT(address));
+
+    switch (op2) {
+    case OPC2_32_ABS_LD_B:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_SB);
+        break;
+    case OPC2_32_ABS_LD_BU:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_UB);
+        break;
+    case OPC2_32_ABS_LD_H:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESW);
+        break;
+    case OPC2_32_ABS_LD_HU:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW);
+        break;
+    }
+
+    tcg_temp_free(temp);
+}
+
+static void decode_abs_ldst_swap(CPUTriCoreState *env, DisasContext *ctx)
+{
+    int32_t op2;
+    int32_t r1;
+    uint32_t address;
+    TCGv temp;
+
+    r1 = MASK_OP_ABS_S1D(ctx->opcode);
+    address = MASK_OP_ABS_OFF18(ctx->opcode);
+    op2 = MASK_OP_ABS_OP2(ctx->opcode);
+
+    temp = tcg_const_i32(EA_ABS_FORMAT(address));
+
+    switch (op2) {
+    case OPC2_32_ABS_LDMST:
+        gen_ldmst(ctx, r1, temp);
+        break;
+    case OPC2_32_ABS_SWAP_W:
+        gen_swap(ctx, r1, temp);
+        break;
+    }
+
+    tcg_temp_free(temp);
+}
+
+static void decode_abs_ldst_context(CPUTriCoreState *env, DisasContext *ctx)
+{
+    uint32_t op2;
+    int32_t off18;
+
+    off18 = MASK_OP_ABS_OFF18(ctx->opcode);
+    op2   = MASK_OP_ABS_OP2(ctx->opcode);
+
+    switch (op2) {
+    case OPC2_32_ABS_LDLCX:
+        gen_helper_1arg(ldlcx, EA_ABS_FORMAT(off18));
+        break;
+    case OPC2_32_ABS_LDUCX:
+        gen_helper_1arg(lducx, EA_ABS_FORMAT(off18));
+        break;
+    case OPC2_32_ABS_STLCX:
+        gen_helper_1arg(stlcx, EA_ABS_FORMAT(off18));
+        break;
+    case OPC2_32_ABS_STUCX:
+        gen_helper_1arg(stucx, EA_ABS_FORMAT(off18));
+        break;
+    }
+}
+
+static void decode_abs_store(CPUTriCoreState *env, DisasContext *ctx)
+{
+    int32_t op2;
+    int32_t r1;
+    uint32_t address;
+    TCGv temp;
+
+    r1 = MASK_OP_ABS_S1D(ctx->opcode);
+    address = MASK_OP_ABS_OFF18(ctx->opcode);
+    op2 = MASK_OP_ABS_OP2(ctx->opcode);
+
+    temp = tcg_const_i32(EA_ABS_FORMAT(address));
+
+    switch (op2) {
+    case OPC2_32_ABS_ST_A:
+        tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL);
+        break;
+    case OPC2_32_ABS_ST_D:
+        gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx);
+        break;
+    case OPC2_32_ABS_ST_DA:
+        gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx);
+        break;
+    case OPC2_32_ABS_ST_W:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL);
+        break;
+
+    }
+    tcg_temp_free(temp);
+}
+
+static void decode_abs_storeb_h(CPUTriCoreState *env, DisasContext *ctx)
+{
+    int32_t op2;
+    int32_t r1;
+    uint32_t address;
+    TCGv temp;
+
+    r1 = MASK_OP_ABS_S1D(ctx->opcode);
+    address = MASK_OP_ABS_OFF18(ctx->opcode);
+    op2 = MASK_OP_ABS_OP2(ctx->opcode);
+
+    temp = tcg_const_i32(EA_ABS_FORMAT(address));
+
+    switch (op2) {
+    case OPC2_32_ABS_ST_B:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_UB);
+        break;
+    case OPC2_32_ABS_ST_H:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW);
+        break;
+    }
+    tcg_temp_free(temp);
+}
+
+/* Bit-format */
+
+static void decode_bit_andacc(CPUTriCoreState *env, DisasContext *ctx)
+{
+    uint32_t op2;
+    int r1, r2, r3;
+    int pos1, pos2;
+
+    r1 = MASK_OP_BIT_S1(ctx->opcode);
+    r2 = MASK_OP_BIT_S2(ctx->opcode);
+    r3 = MASK_OP_BIT_D(ctx->opcode);
+    pos1 = MASK_OP_BIT_POS1(ctx->opcode);
+    pos2 = MASK_OP_BIT_POS2(ctx->opcode);
+    op2 = MASK_OP_BIT_OP2(ctx->opcode);
+
+
+    switch (op2) {
+    case OPC2_32_BIT_AND_AND_T:
+        gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_and_tl, &tcg_gen_and_tl);
+        break;
+    case OPC2_32_BIT_AND_ANDN_T:
+        gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_and_tl);
+        break;
+    case OPC2_32_BIT_AND_NOR_T:
+        if (TCG_TARGET_HAS_andc_i32) {
+            gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                        pos1, pos2, &tcg_gen_or_tl, &tcg_gen_andc_tl);
+        } else {
+            gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                        pos1, pos2, &tcg_gen_nor_tl, &tcg_gen_and_tl);
+        }
+        break;
+    case OPC2_32_BIT_AND_OR_T:
+        gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_or_tl, &tcg_gen_and_tl);
+        break;
+    }
+}
+
+static void decode_bit_logical_t(CPUTriCoreState *env, DisasContext *ctx)
+{
+    uint32_t op2;
+    int r1, r2, r3;
+    int pos1, pos2;
+    r1 = MASK_OP_BIT_S1(ctx->opcode);
+    r2 = MASK_OP_BIT_S2(ctx->opcode);
+    r3 = MASK_OP_BIT_D(ctx->opcode);
+    pos1 = MASK_OP_BIT_POS1(ctx->opcode);
+    pos2 = MASK_OP_BIT_POS2(ctx->opcode);
+    op2 = MASK_OP_BIT_OP2(ctx->opcode);
+
+    switch (op2) {
+    case OPC2_32_BIT_AND_T:
+        gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_and_tl);
+        break;
+    case OPC2_32_BIT_ANDN_T:
+        gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_andc_tl);
+        break;
+    case OPC2_32_BIT_NOR_T:
+        gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_nor_tl);
+        break;
+    case OPC2_32_BIT_OR_T:
+        gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_or_tl);
+        break;
+    }
+}
+
+static void decode_bit_insert(CPUTriCoreState *env, DisasContext *ctx)
+{
+    uint32_t op2;
+    int r1, r2, r3;
+    int pos1, pos2;
+    TCGv temp;
+    op2 = MASK_OP_BIT_OP2(ctx->opcode);
+    r1 = MASK_OP_BIT_S1(ctx->opcode);
+    r2 = MASK_OP_BIT_S2(ctx->opcode);
+    r3 = MASK_OP_BIT_D(ctx->opcode);
+    pos1 = MASK_OP_BIT_POS1(ctx->opcode);
+    pos2 = MASK_OP_BIT_POS2(ctx->opcode);
+
+    temp = tcg_temp_new();
+
+    tcg_gen_shri_tl(temp, cpu_gpr_d[r2], pos2);
+    if (op2 == OPC2_32_BIT_INSN_T) {
+        tcg_gen_not_tl(temp, temp);
+    }
+    tcg_gen_deposit_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], temp, pos1, 1);
+    tcg_temp_free(temp);
+}
+
+static void decode_bit_logical_t2(CPUTriCoreState *env, DisasContext *ctx)
+{
+    uint32_t op2;
+
+    int r1, r2, r3;
+    int pos1, pos2;
+
+    op2 = MASK_OP_BIT_OP2(ctx->opcode);
+    r1 = MASK_OP_BIT_S1(ctx->opcode);
+    r2 = MASK_OP_BIT_S2(ctx->opcode);
+    r3 = MASK_OP_BIT_D(ctx->opcode);
+    pos1 = MASK_OP_BIT_POS1(ctx->opcode);
+    pos2 = MASK_OP_BIT_POS2(ctx->opcode);
+
+    switch (op2) {
+    case OPC2_32_BIT_NAND_T:
+        gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_nand_tl);
+        break;
+    case OPC2_32_BIT_ORN_T:
+        gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_orc_tl);
+        break;
+    case OPC2_32_BIT_XNOR_T:
+        gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_eqv_tl);
+        break;
+    case OPC2_32_BIT_XOR_T:
+        gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_xor_tl);
+        break;
+    }
+}
+
+static void decode_bit_orand(CPUTriCoreState *env, DisasContext *ctx)
+{
+    uint32_t op2;
+
+    int r1, r2, r3;
+    int pos1, pos2;
+
+    op2 = MASK_OP_BIT_OP2(ctx->opcode);
+    r1 = MASK_OP_BIT_S1(ctx->opcode);
+    r2 = MASK_OP_BIT_S2(ctx->opcode);
+    r3 = MASK_OP_BIT_D(ctx->opcode);
+    pos1 = MASK_OP_BIT_POS1(ctx->opcode);
+    pos2 = MASK_OP_BIT_POS2(ctx->opcode);
+
+    switch (op2) {
+    case OPC2_32_BIT_OR_AND_T:
+        gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_and_tl, &tcg_gen_or_tl);
+        break;
+    case OPC2_32_BIT_OR_ANDN_T:
+        gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_or_tl);
+        break;
+    case OPC2_32_BIT_OR_NOR_T:
+        if (TCG_TARGET_HAS_orc_i32) {
+            gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                        pos1, pos2, &tcg_gen_or_tl, &tcg_gen_orc_tl);
+        } else {
+            gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                        pos1, pos2, &tcg_gen_nor_tl, &tcg_gen_or_tl);
+        }
+        break;
+    case OPC2_32_BIT_OR_OR_T:
+        gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_or_tl, &tcg_gen_or_tl);
+        break;
+    }
+}
+
+static void decode_bit_sh_logic1(CPUTriCoreState *env, DisasContext *ctx)
+{
+    uint32_t op2;
+    int r1, r2, r3;
+    int pos1, pos2;
+    TCGv temp;
+
+    op2 = MASK_OP_BIT_OP2(ctx->opcode);
+    r1 = MASK_OP_BIT_S1(ctx->opcode);
+    r2 = MASK_OP_BIT_S2(ctx->opcode);
+    r3 = MASK_OP_BIT_D(ctx->opcode);
+    pos1 = MASK_OP_BIT_POS1(ctx->opcode);
+    pos2 = MASK_OP_BIT_POS2(ctx->opcode);
+
+    temp = tcg_temp_new();
+
+    switch (op2) {
+    case OPC2_32_BIT_SH_AND_T:
+        gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_and_tl);
+        break;
+    case OPC2_32_BIT_SH_ANDN_T:
+        gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_andc_tl);
+        break;
+    case OPC2_32_BIT_SH_NOR_T:
+        gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_nor_tl);
+        break;
+    case OPC2_32_BIT_SH_OR_T:
+        gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_or_tl);
+        break;
+    }
+    tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1);
+    tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp);
+    tcg_temp_free(temp);
+}
+
+static void decode_bit_sh_logic2(CPUTriCoreState *env, DisasContext *ctx)
+{
+    uint32_t op2;
+    int r1, r2, r3;
+    int pos1, pos2;
+    TCGv temp;
+
+    op2 = MASK_OP_BIT_OP2(ctx->opcode);
+    r1 = MASK_OP_BIT_S1(ctx->opcode);
+    r2 = MASK_OP_BIT_S2(ctx->opcode);
+    r3 = MASK_OP_BIT_D(ctx->opcode);
+    pos1 = MASK_OP_BIT_POS1(ctx->opcode);
+    pos2 = MASK_OP_BIT_POS2(ctx->opcode);
+
+    temp = tcg_temp_new();
+
+    switch (op2) {
+    case OPC2_32_BIT_SH_NAND_T:
+        gen_bit_1op(temp, cpu_gpr_d[r1] , cpu_gpr_d[r2] ,
+                    pos1, pos2, &tcg_gen_nand_tl);
+        break;
+    case OPC2_32_BIT_SH_ORN_T:
+        gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_orc_tl);
+        break;
+    case OPC2_32_BIT_SH_XNOR_T:
+        gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_eqv_tl);
+        break;
+    case OPC2_32_BIT_SH_XOR_T:
+        gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2],
+                    pos1, pos2, &tcg_gen_xor_tl);
+        break;
+    }
+    tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1);
+    tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp);
+    tcg_temp_free(temp);
+}
+
+/* BO-format */
+
+
+static void decode_bo_addrmode_post_pre_base(CPUTriCoreState *env,
+                                             DisasContext *ctx)
+{
+    uint32_t op2;
+    uint32_t off10;
+    int32_t r1, r2;
+    TCGv temp;
+
+    r1 = MASK_OP_BO_S1D(ctx->opcode);
+    r2  = MASK_OP_BO_S2(ctx->opcode);
+    off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode);
+    op2 = MASK_OP_BO_OP2(ctx->opcode);
+
+    switch (op2) {
+    case OPC2_32_BO_CACHEA_WI_SHORTOFF:
+    case OPC2_32_BO_CACHEA_W_SHORTOFF:
+    case OPC2_32_BO_CACHEA_I_SHORTOFF:
+        /* instruction to access the cache */
+        break;
+    case OPC2_32_BO_CACHEA_WI_POSTINC:
+    case OPC2_32_BO_CACHEA_W_POSTINC:
+    case OPC2_32_BO_CACHEA_I_POSTINC:
+        /* instruction to access the cache, but we still need to handle
+           the addressing mode */
+        tcg_gen_addi_tl(cpu_gpr_d[r2], cpu_gpr_d[r2], off10);
+        break;
+    case OPC2_32_BO_CACHEA_WI_PREINC:
+    case OPC2_32_BO_CACHEA_W_PREINC:
+    case OPC2_32_BO_CACHEA_I_PREINC:
+        /* instruction to access the cache, but we still need to handle
+           the addressing mode */
+        tcg_gen_addi_tl(cpu_gpr_d[r2], cpu_gpr_d[r2], off10);
+        break;
+    case OPC2_32_BO_CACHEI_WI_SHORTOFF:
+    case OPC2_32_BO_CACHEI_W_SHORTOFF:
+        /* TODO: Raise illegal opcode trap,
+                 if tricore_feature(TRICORE_FEATURE_13) */
+        break;
+    case OPC2_32_BO_CACHEI_W_POSTINC:
+    case OPC2_32_BO_CACHEI_WI_POSTINC:
+        if (!tricore_feature(env, TRICORE_FEATURE_13)) {
+            tcg_gen_addi_tl(cpu_gpr_d[r2], cpu_gpr_d[r2], off10);
+        } /* TODO: else raise illegal opcode trap */
+        break;
+    case OPC2_32_BO_CACHEI_W_PREINC:
+    case OPC2_32_BO_CACHEI_WI_PREINC:
+        if (!tricore_feature(env, TRICORE_FEATURE_13)) {
+            tcg_gen_addi_tl(cpu_gpr_d[r2], cpu_gpr_d[r2], off10);
+        } /* TODO: else raise illegal opcode trap */
+        break;
+    case OPC2_32_BO_ST_A_SHORTOFF:
+        gen_offset_st(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LESL);
+        break;
+    case OPC2_32_BO_ST_A_POSTINC:
+        tcg_gen_qemu_st_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_LESL);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_ST_A_PREINC:
+        gen_st_preincr(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LESL);
+        break;
+    case OPC2_32_BO_ST_B_SHORTOFF:
+        gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB);
+        break;
+    case OPC2_32_BO_ST_B_POSTINC:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_UB);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_ST_B_PREINC:
+        gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB);
+        break;
+    case OPC2_32_BO_ST_D_SHORTOFF:
+        gen_offset_st_2regs(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2],
+                            off10, ctx);
+        break;
+    case OPC2_32_BO_ST_D_POSTINC:
+        gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_ST_D_PREINC:
+        temp = tcg_temp_new();
+        tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10);
+        gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx);
+        tcg_gen_mov_tl(cpu_gpr_a[r2], temp);
+        tcg_temp_free(temp);
+        break;
+    case OPC2_32_BO_ST_DA_SHORTOFF:
+        gen_offset_st_2regs(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2],
+                            off10, ctx);
+        break;
+    case OPC2_32_BO_ST_DA_POSTINC:
+        gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_ST_DA_PREINC:
+        temp = tcg_temp_new();
+        tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10);
+        gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx);
+        tcg_gen_mov_tl(cpu_gpr_a[r2], temp);
+        tcg_temp_free(temp);
+        break;
+    case OPC2_32_BO_ST_H_SHORTOFF:
+        gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW);
+        break;
+    case OPC2_32_BO_ST_H_POSTINC:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_LEUW);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_ST_H_PREINC:
+        gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW);
+        break;
+    case OPC2_32_BO_ST_Q_SHORTOFF:
+        temp = tcg_temp_new();
+        tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16);
+        gen_offset_st(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW);
+        tcg_temp_free(temp);
+        break;
+    case OPC2_32_BO_ST_Q_POSTINC:
+        temp = tcg_temp_new();
+        tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16);
+        tcg_gen_qemu_st_tl(temp, cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_LEUW);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        tcg_temp_free(temp);
+        break;
+    case OPC2_32_BO_ST_Q_PREINC:
+        temp = tcg_temp_new();
+        tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16);
+        gen_st_preincr(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW);
+        tcg_temp_free(temp);
+        break;
+    case OPC2_32_BO_ST_W_SHORTOFF:
+        gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL);
+        break;
+    case OPC2_32_BO_ST_W_POSTINC:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_LEUL);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_ST_W_PREINC:
+        gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL);
+        break;
+    }
+}
+
+static void decode_bo_addrmode_bitreverse_circular(CPUTriCoreState *env,
+                                                   DisasContext *ctx)
+{
+    uint32_t op2;
+    uint32_t off10;
+    int32_t r1, r2;
+    TCGv temp, temp2, temp3;
+
+    r1 = MASK_OP_BO_S1D(ctx->opcode);
+    r2  = MASK_OP_BO_S2(ctx->opcode);
+    off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode);
+    op2 = MASK_OP_BO_OP2(ctx->opcode);
+
+    temp = tcg_temp_new();
+    temp2 = tcg_temp_new();
+    temp3 = tcg_const_i32(off10);
+
+    tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]);
+    tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp);
+
+    switch (op2) {
+    case OPC2_32_BO_CACHEA_WI_BR:
+    case OPC2_32_BO_CACHEA_W_BR:
+    case OPC2_32_BO_CACHEA_I_BR:
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_CACHEA_WI_CIRC:
+    case OPC2_32_BO_CACHEA_W_CIRC:
+    case OPC2_32_BO_CACHEA_I_CIRC:
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_ST_A_BR:
+        tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_ST_A_CIRC:
+        tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_ST_B_BR:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_ST_B_CIRC:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_ST_D_BR:
+        gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp2, ctx);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_ST_D_CIRC:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL);
+        tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16);
+        tcg_gen_addi_tl(temp, temp, 4);
+        tcg_gen_rem_tl(temp, temp, temp2);
+        tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp);
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1+1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_ST_DA_BR:
+        gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp2, ctx);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_ST_DA_CIRC:
+        tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL);
+        tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16);
+        tcg_gen_addi_tl(temp, temp, 4);
+        tcg_gen_rem_tl(temp, temp, temp2);
+        tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp);
+        tcg_gen_qemu_st_tl(cpu_gpr_a[r1+1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_ST_H_BR:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_ST_H_CIRC:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_ST_Q_BR:
+        tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16);
+        tcg_gen_qemu_st_tl(temp, temp2, ctx->mem_idx, MO_LEUW);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_ST_Q_CIRC:
+        tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16);
+        tcg_gen_qemu_st_tl(temp, temp2, ctx->mem_idx, MO_LEUW);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_ST_W_BR:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_ST_W_CIRC:
+        tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    }
+    tcg_temp_free(temp);
+    tcg_temp_free(temp2);
+    tcg_temp_free(temp3);
+}
+
+static void decode_bo_addrmode_ld_post_pre_base(CPUTriCoreState *env,
+                                                DisasContext *ctx)
+{
+    uint32_t op2;
+    uint32_t off10;
+    int32_t r1, r2;
+    TCGv temp;
+
+    r1 = MASK_OP_BO_S1D(ctx->opcode);
+    r2  = MASK_OP_BO_S2(ctx->opcode);
+    off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode);
+    op2 = MASK_OP_BO_OP2(ctx->opcode);
+
+    switch (op2) {
+    case OPC2_32_BO_LD_A_SHORTOFF:
+        gen_offset_ld(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LEUL);
+        break;
+    case OPC2_32_BO_LD_A_POSTINC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_LEUL);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_LD_A_PREINC:
+        gen_ld_preincr(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LEUL);
+        break;
+    case OPC2_32_BO_LD_B_SHORTOFF:
+        gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB);
+        break;
+    case OPC2_32_BO_LD_B_POSTINC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_SB);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_LD_B_PREINC:
+        gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB);
+        break;
+    case OPC2_32_BO_LD_BU_SHORTOFF:
+        gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB);
+        break;
+    case OPC2_32_BO_LD_BU_POSTINC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_UB);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_LD_BU_PREINC:
+        gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB);
+        break;
+    case OPC2_32_BO_LD_D_SHORTOFF:
+        gen_offset_ld_2regs(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2],
+                            off10, ctx);
+        break;
+    case OPC2_32_BO_LD_D_POSTINC:
+        gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_LD_D_PREINC:
+        temp = tcg_temp_new();
+        tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10);
+        gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx);
+        tcg_gen_mov_tl(cpu_gpr_a[r2], temp);
+        tcg_temp_free(temp);
+        break;
+    case OPC2_32_BO_LD_DA_SHORTOFF:
+        gen_offset_ld_2regs(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2],
+                            off10, ctx);
+        break;
+    case OPC2_32_BO_LD_DA_POSTINC:
+        gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_LD_DA_PREINC:
+        temp = tcg_temp_new();
+        tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10);
+        gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx);
+        tcg_gen_mov_tl(cpu_gpr_a[r2], temp);
+        tcg_temp_free(temp);
+        break;
+    case OPC2_32_BO_LD_H_SHORTOFF:
+        gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LESW);
+        break;
+    case OPC2_32_BO_LD_H_POSTINC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_LESW);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_LD_H_PREINC:
+        gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LESW);
+        break;
+    case OPC2_32_BO_LD_HU_SHORTOFF:
+        gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW);
+        break;
+    case OPC2_32_BO_LD_HU_POSTINC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_LEUW);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_LD_HU_PREINC:
+        gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW);
+        break;
+    case OPC2_32_BO_LD_Q_SHORTOFF:
+        gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW);
+        tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16);
+        break;
+    case OPC2_32_BO_LD_Q_POSTINC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_LEUW);
+        tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_LD_Q_PREINC:
+        gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW);
+        tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16);
+        break;
+    case OPC2_32_BO_LD_W_SHORTOFF:
+        gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL);
+        break;
+    case OPC2_32_BO_LD_W_POSTINC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx,
+                           MO_LEUL);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_LD_W_PREINC:
+        gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL);
+        break;
+    }
+}
+
+static void decode_bo_addrmode_ld_bitreverse_circular(CPUTriCoreState *env,
+                                                DisasContext *ctx)
+{
+    uint32_t op2;
+    uint32_t off10;
+    int r1, r2;
+
+    TCGv temp, temp2, temp3;
+
+    r1 = MASK_OP_BO_S1D(ctx->opcode);
+    r2 = MASK_OP_BO_S2(ctx->opcode);
+    off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode);
+    op2 = MASK_OP_BO_OP2(ctx->opcode);
+
+    temp = tcg_temp_new();
+    temp2 = tcg_temp_new();
+    temp3 = tcg_const_i32(off10);
+
+    tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]);
+    tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp);
+
+
+    switch (op2) {
+    case OPC2_32_BO_LD_A_BR:
+        tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_LD_A_CIRC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_LD_B_BR:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_LD_B_CIRC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_LD_BU_BR:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_LD_BU_CIRC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_LD_D_BR:
+        gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp2, ctx);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_LD_D_CIRC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL);
+        tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16);
+        tcg_gen_addi_tl(temp, temp, 4);
+        tcg_gen_rem_tl(temp, temp, temp2);
+        tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp);
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1+1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_LD_DA_BR:
+        gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp2, ctx);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_LD_DA_CIRC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL);
+        tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16);
+        tcg_gen_addi_tl(temp, temp, 4);
+        tcg_gen_rem_tl(temp, temp, temp2);
+        tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp);
+        tcg_gen_qemu_ld_tl(cpu_gpr_a[r1+1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_LD_H_BR:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_LD_H_CIRC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_LD_HU_BR:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_LD_HU_CIRC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_LD_Q_BR:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW);
+        tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_LD_Q_CIRC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW);
+        tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_LD_W_BR:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_LD_W_CIRC:
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    }
+    tcg_temp_free(temp);
+    tcg_temp_free(temp2);
+    tcg_temp_free(temp3);
+}
+
+static void decode_bo_addrmode_stctx_post_pre_base(CPUTriCoreState *env,
+                                                   DisasContext *ctx)
+{
+    uint32_t op2;
+    uint32_t off10;
+    int r1, r2;
+
+    TCGv temp, temp2;
+
+    r1 = MASK_OP_BO_S1D(ctx->opcode);
+    r2 = MASK_OP_BO_S2(ctx->opcode);
+    off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode);
+    op2 = MASK_OP_BO_OP2(ctx->opcode);
+
+
+    temp = tcg_temp_new();
+    temp2 = tcg_temp_new();
+
+    switch (op2) {
+    case OPC2_32_BO_LDLCX_SHORTOFF:
+        tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10);
+        gen_helper_ldlcx(cpu_env, temp);
+        break;
+    case OPC2_32_BO_LDMST_SHORTOFF:
+        tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10);
+        gen_ldmst(ctx, r1, temp);
+        break;
+    case OPC2_32_BO_LDMST_POSTINC:
+        gen_ldmst(ctx, r1, cpu_gpr_a[r2]);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_LDMST_PREINC:
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        gen_ldmst(ctx, r1, cpu_gpr_a[r2]);
+        break;
+    case OPC2_32_BO_LDUCX_SHORTOFF:
+        tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10);
+        gen_helper_lducx(cpu_env, temp);
+        break;
+    case OPC2_32_BO_LEA_SHORTOFF:
+        tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_STLCX_SHORTOFF:
+        tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10);
+        gen_helper_stlcx(cpu_env, temp);
+        break;
+    case OPC2_32_BO_STUCX_SHORTOFF:
+        tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10);
+        gen_helper_stucx(cpu_env, temp);
+        break;
+    case OPC2_32_BO_SWAP_W_SHORTOFF:
+        tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10);
+        gen_swap(ctx, r1, temp);
+        break;
+    case OPC2_32_BO_SWAP_W_POSTINC:
+        gen_swap(ctx, r1, cpu_gpr_a[r2]);
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        break;
+    case OPC2_32_BO_SWAP_W_PREINC:
+        tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10);
+        gen_swap(ctx, r1, cpu_gpr_a[r2]);
+        break;
+    }
+    tcg_temp_free(temp);
+    tcg_temp_free(temp2);
+}
+
+static void decode_bo_addrmode_ldmst_bitreverse_circular(CPUTriCoreState *env,
+                                                         DisasContext *ctx)
+{
+    uint32_t op2;
+    uint32_t off10;
+    int r1, r2;
+
+    TCGv temp, temp2, temp3;
+
+    r1 = MASK_OP_BO_S1D(ctx->opcode);
+    r2 = MASK_OP_BO_S2(ctx->opcode);
+    off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode);
+    op2 = MASK_OP_BO_OP2(ctx->opcode);
+
+    temp = tcg_temp_new();
+    temp2 = tcg_temp_new();
+    temp3 = tcg_const_i32(off10);
+
+    tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]);
+    tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp);
+
+    switch (op2) {
+    case OPC2_32_BO_LDMST_BR:
+        gen_ldmst(ctx, r1, temp2);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_LDMST_CIRC:
+        gen_ldmst(ctx, r1, temp2);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    case OPC2_32_BO_SWAP_W_BR:
+        gen_swap(ctx, r1, temp2);
+        gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]);
+        break;
+    case OPC2_32_BO_SWAP_W_CIRC:
+        gen_swap(ctx, r1, temp2);
+        gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3);
+        break;
+    }
+    tcg_temp_free(temp);
+    tcg_temp_free(temp2);
+    tcg_temp_free(temp3);
+}
+
 static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx)
 {
+    int op1;
+    int32_t r1;
+    int32_t address;
+    int8_t b;
+    int32_t bpos;
+    TCGv temp, temp2;
+
+    op1 = MASK_OP_MAJOR(ctx->opcode);
+
+    switch (op1) {
+/* ABS-format */
+    case OPCM_32_ABS_LDW:
+        decode_abs_ldw(env, ctx);
+        break;
+    case OPCM_32_ABS_LDB:
+        decode_abs_ldb(env, ctx);
+        break;
+    case OPCM_32_ABS_LDMST_SWAP:
+        decode_abs_ldst_swap(env, ctx);
+        break;
+    case OPCM_32_ABS_LDST_CONTEXT:
+        decode_abs_ldst_context(env, ctx);
+        break;
+    case OPCM_32_ABS_STORE:
+        decode_abs_store(env, ctx);
+        break;
+    case OPCM_32_ABS_STOREB_H:
+        decode_abs_storeb_h(env, ctx);
+        break;
+    case OPC1_32_ABS_STOREQ:
+        address = MASK_OP_ABS_OFF18(ctx->opcode);
+        r1 = MASK_OP_ABS_S1D(ctx->opcode);
+        temp = tcg_const_i32(EA_ABS_FORMAT(address));
+        temp2 = tcg_temp_new();
+
+        tcg_gen_shri_tl(temp2, cpu_gpr_d[r1], 16);
+        tcg_gen_qemu_st_tl(temp2, temp, ctx->mem_idx, MO_LEUW);
+
+        tcg_temp_free(temp2);
+        tcg_temp_free(temp);
+        break;
+    case OPC1_32_ABS_LD_Q:
+        address = MASK_OP_ABS_OFF18(ctx->opcode);
+        r1 = MASK_OP_ABS_S1D(ctx->opcode);
+        temp = tcg_const_i32(EA_ABS_FORMAT(address));
+
+        tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW);
+        tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16);
+
+        tcg_temp_free(temp);
+        break;
+    case OPC1_32_ABS_LEA:
+        address = MASK_OP_ABS_OFF18(ctx->opcode);
+        r1 = MASK_OP_ABS_S1D(ctx->opcode);
+        tcg_gen_movi_tl(cpu_gpr_a[r1], EA_ABS_FORMAT(address));
+        break;
+/* ABSB-format */
+    case OPC1_32_ABSB_ST_T:
+        address = MASK_OP_ABS_OFF18(ctx->opcode);
+        b = MASK_OP_ABSB_B(ctx->opcode);
+        bpos = MASK_OP_ABSB_BPOS(ctx->opcode);
+
+        temp = tcg_const_i32(EA_ABS_FORMAT(address));
+        temp2 = tcg_temp_new();
+
+        tcg_gen_qemu_ld_tl(temp2, temp, ctx->mem_idx, MO_UB);
+        tcg_gen_andi_tl(temp2, temp2, ~(0x1u << bpos));
+        tcg_gen_ori_tl(temp2, temp2, (b << bpos));
+        tcg_gen_qemu_st_tl(temp2, temp, ctx->mem_idx, MO_UB);
+
+        tcg_temp_free(temp);
+        tcg_temp_free(temp2);
+        break;
+/* B-format */
+    case OPC1_32_B_CALL:
+    case OPC1_32_B_CALLA:
+    case OPC1_32_B_J:
+    case OPC1_32_B_JA:
+    case OPC1_32_B_JL:
+    case OPC1_32_B_JLA:
+        address = MASK_OP_B_DISP24(ctx->opcode);
+        gen_compute_branch(ctx, op1, 0, 0, 0, address);
+        break;
+/* Bit-format */
+    case OPCM_32_BIT_ANDACC:
+        decode_bit_andacc(env, ctx);
+        break;
+    case OPCM_32_BIT_LOGICAL_T1:
+        decode_bit_logical_t(env, ctx);
+        break;
+    case OPCM_32_BIT_INSERT:
+        decode_bit_insert(env, ctx);
+        break;
+    case OPCM_32_BIT_LOGICAL_T2:
+        decode_bit_logical_t2(env, ctx);
+        break;
+    case OPCM_32_BIT_ORAND:
+        decode_bit_orand(env, ctx);
+        break;
+    case OPCM_32_BIT_SH_LOGIC1:
+        decode_bit_sh_logic1(env, ctx);
+        break;
+    case OPCM_32_BIT_SH_LOGIC2:
+        decode_bit_sh_logic2(env, ctx);
+        break;
+    /* BO Format */
+    case OPCM_32_BO_ADDRMODE_POST_PRE_BASE:
+        decode_bo_addrmode_post_pre_base(env, ctx);
+        break;
+    case OPCM_32_BO_ADDRMODE_BITREVERSE_CIRCULAR:
+        decode_bo_addrmode_bitreverse_circular(env, ctx);
+        break;
+    case OPCM_32_BO_ADDRMODE_LD_POST_PRE_BASE:
+        decode_bo_addrmode_ld_post_pre_base(env, ctx);
+        break;
+    case OPCM_32_BO_ADDRMODE_LD_BITREVERSE_CIRCULAR:
+        decode_bo_addrmode_ld_bitreverse_circular(env, ctx);
+        break;
+    case OPCM_32_BO_ADDRMODE_STCTX_POST_PRE_BASE:
+        decode_bo_addrmode_stctx_post_pre_base(env, ctx);
+        break;
+    case OPCM_32_BO_ADDRMODE_LDMST_BITREVERSE_CIRCULAR:
+        decode_bo_addrmode_ldmst_bitreverse_circular(env, ctx);
+        break;
+    }
 }
 
 static void decode_opc(CPUTriCoreState *env, DisasContext *ctx, int *is_branch)
diff --git a/target-tricore/tricore-opcodes.h b/target-tricore/tricore-opcodes.h
index 9c6ec013ac..7e6f33bd62 100644
--- a/target-tricore/tricore-opcodes.h
+++ b/target-tricore/tricore-opcodes.h
@@ -89,7 +89,7 @@
 #define MASK_OP_ABSB_OFF18(op) MASK_OP_ABS_OFF18(op)
 #define MASK_OP_ABSB_OP2(op)   MASK_BITS_SHIFT(op, 26, 27)
 #define MASK_OP_ABSB_B(op)     MASK_BITS_SHIFT(op, 11, 11)
-#define MASK_OP_ABSB_BPOS(op)  MASK_BITS_SHIFT(op, 7, 10)
+#define MASK_OP_ABSB_BPOS(op)  MASK_BITS_SHIFT(op, 8, 10)
 
 /* B Format   */
 #define MASK_OP_B_DISP24(op)   (MASK_BITS_SHIFT(op, 16, 31) + \
@@ -105,6 +105,8 @@
 /* BO Format */
 #define MASK_OP_BO_OFF10(op)   (MASK_BITS_SHIFT(op, 16, 21) + \
                                (MASK_BITS_SHIFT(op, 28, 31) << 6))
+#define MASK_OP_BO_OFF10_SEXT(op)   (MASK_BITS_SHIFT_SEXT(op, 16, 21) + \
+                                    (MASK_BITS_SHIFT_SEXT(op, 28, 31) << 6))
 #define MASK_OP_BO_OP2(op)     MASK_BITS_SHIFT(op, 22, 27)
 #define MASK_OP_BO_S2(op)      MASK_BITS_SHIFT(op, 12, 15)
 #define MASK_OP_BO_S1D(op)     MASK_BITS_SHIFT(op, 8, 11)
diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c
index 8c4d68b345..6a0b9813f5 100644
--- a/tests/test-thread-pool.c
+++ b/tests/test-thread-pool.c
@@ -11,7 +11,7 @@ static ThreadPool *pool;
 static int active;
 
 typedef struct {
-    BlockDriverAIOCB *aiocb;
+    BlockAIOCB *aiocb;
     int n;
     int ret;
 } WorkerTestData;
diff --git a/thread-pool.c b/thread-pool.c
index 86fa4a8f89..e2cac8e4ff 100644
--- a/thread-pool.c
+++ b/thread-pool.c
@@ -34,7 +34,7 @@ enum ThreadState {
 };
 
 struct ThreadPoolElement {
-    BlockDriverAIOCB common;
+    BlockAIOCB common;
     ThreadPool *pool;
     ThreadPoolFunc *func;
     void *arg;
@@ -195,7 +195,7 @@ restart:
     }
 }
 
-static void thread_pool_cancel(BlockDriverAIOCB *acb)
+static void thread_pool_cancel(BlockAIOCB *acb)
 {
     ThreadPoolElement *elem = (ThreadPoolElement *)acb;
     ThreadPool *pool = elem->pool;
@@ -220,7 +220,7 @@ static void thread_pool_cancel(BlockDriverAIOCB *acb)
     qemu_mutex_unlock(&pool->lock);
 }
 
-static AioContext *thread_pool_get_aio_context(BlockDriverAIOCB *acb)
+static AioContext *thread_pool_get_aio_context(BlockAIOCB *acb)
 {
     ThreadPoolElement *elem = (ThreadPoolElement *)acb;
     ThreadPool *pool = elem->pool;
@@ -233,9 +233,9 @@ static const AIOCBInfo thread_pool_aiocb_info = {
     .get_aio_context    = thread_pool_get_aio_context,
 };
 
-BlockDriverAIOCB *thread_pool_submit_aio(ThreadPool *pool,
+BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,
         ThreadPoolFunc *func, void *arg,
-        BlockDriverCompletionFunc *cb, void *opaque)
+        BlockCompletionFunc *cb, void *opaque)
 {
     ThreadPoolElement *req;
 
diff --git a/trace-events b/trace-events
index 529080640a..6c3a4003c1 100644
--- a/trace-events
+++ b/trace-events
@@ -195,8 +195,8 @@ fw_cfg_add_file_dupe(void *s, char *name) "%p %s"
 fw_cfg_add_file(void *s, int index, char *name, size_t len) "%p #%d: %s (%zd bytes)"
 
 # hw/block/hd-geometry.c
-hd_geometry_lchs_guess(void *bs, int cyls, int heads, int secs) "bs %p LCHS %d %d %d"
-hd_geometry_guess(void *bs, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "bs %p CHS %u %u %u trans %d"
+hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
+hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d"
 
 # hw/display/jazz_led.c
 jazz_led_read(uint64_t addr, uint8_t val) "read addr=0x%"PRIx64": 0x%x"
@@ -1078,10 +1078,10 @@ win_helper_done(uint32_t tl) "tl=%d"
 win_helper_retry(uint32_t tl) "tl=%d"
 
 # dma-helpers.c
-dma_bdrv_io(void *dbs, void *bs, int64_t sector_num, bool to_dev) "dbs=%p bs=%p sector_num=%" PRId64 " to_dev=%d"
+dma_blk_io(void *dbs, void *bs, int64_t sector_num, bool to_dev) "dbs=%p bs=%p sector_num=%" PRId64 " to_dev=%d"
 dma_aio_cancel(void *dbs) "dbs=%p"
 dma_complete(void *dbs, int ret, void *cb) "dbs=%p ret=%d cb=%p"
-dma_bdrv_cb(void *dbs, int ret) "dbs=%p ret=%d"
+dma_blk_cb(void *dbs, int ret) "dbs=%p ret=%d"
 dma_map_wait(void *dbs) "dbs=%p"
 
 # ui/console.c
diff --git a/ui/gtk.c b/ui/gtk.c
index 2345d7e3a3..8e055da0dc 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -931,6 +931,12 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
     int qemu_keycode;
     int i;
 
+    if (key->keyval == GDK_KEY_Pause) {
+        qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
+                                        key->type == GDK_KEY_PRESS);
+        return TRUE;
+    }
+
     qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget),
                                   gdk_keycode);
 
@@ -1810,6 +1816,13 @@ static void gd_set_keycode_type(GtkDisplayState *s)
             fprintf(stderr, "unknown keycodes `%s', please report to "
                     "qemu-devel@nongnu.org\n", keycodes);
         }
+
+        if (desc) {
+            XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
+        }
+        if (keycodes) {
+            XFree(keycodes);
+        }
     }
 #endif
 }