summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml73
-rw-r--r--.gitmodules3
-rw-r--r--.travis.yml16
-rw-r--r--MAINTAINERS109
-rw-r--r--Makefile16
-rw-r--r--Makefile.objs8
-rw-r--r--Makefile.target2
-rw-r--r--audio/audio.c73
-rw-r--r--audio/sdlaudio.c188
-rw-r--r--authz/Makefile.objs7
-rw-r--r--authz/base.c82
-rw-r--r--authz/list.c271
-rw-r--r--authz/listfile.c283
-rw-r--r--authz/pamacct.c148
-rw-r--r--authz/simple.c115
-rw-r--r--authz/trace-events18
-rw-r--r--backends/Makefile.objs5
-rw-r--r--block.c597
-rw-r--r--block/backup.c5
-rw-r--r--block/blkdebug.c70
-rw-r--r--block/blklogwrites.c33
-rw-r--r--block/blkverify.c29
-rw-r--r--block/block-backend.c17
-rw-r--r--block/commit.c14
-rw-r--r--block/crypto.c8
-rw-r--r--block/curl.c55
-rw-r--r--block/gluster.c19
-rw-r--r--block/io.c89
-rw-r--r--block/iscsi.c18
-rw-r--r--block/mirror.c3
-rw-r--r--block/nbd-client.c36
-rw-r--r--block/nbd-client.h1
-rw-r--r--block/nbd.c46
-rw-r--r--block/nfs.c54
-rw-r--r--block/null.c32
-rw-r--r--block/nvme.c29
-rw-r--r--block/parallels.c13
-rw-r--r--block/qapi.c16
-rw-r--r--block/qcow.c35
-rw-r--r--block/qcow2-cluster.c3
-rw-r--r--block/qcow2-snapshot.c5
-rw-r--r--block/qcow2.c101
-rw-r--r--block/qed-table.c16
-rw-r--r--block/qed.c38
-rw-r--r--block/quorum.c71
-rw-r--r--block/raw-format.c11
-rw-r--r--block/rbd.c14
-rw-r--r--block/replication.c10
-rw-r--r--block/sheepdog.c12
-rw-r--r--block/snapshot.c25
-rw-r--r--block/ssh.c12
-rw-r--r--block/stream.c7
-rw-r--r--block/throttle.c7
-rw-r--r--block/vhdx-log.c1
-rw-r--r--block/vmdk.c53
-rw-r--r--block/vpc.c7
-rw-r--r--block/vvfat.c12
-rw-r--r--block/vxhs.c11
-rw-r--r--blockdev.c8
-rw-r--r--chardev/spice.c60
-rw-r--r--chardev/trace-events1
-rwxr-xr-xconfigure171
-rw-r--r--cpus.c1
-rw-r--r--crypto/tlssession.c35
-rw-r--r--crypto/trace-events2
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--default-configs/virtio.mak4
-rw-r--r--disas/nanomips.cpp96
-rw-r--r--disas/nanomips.h2
-rw-r--r--docs/vfio-ap.txt61
-rw-r--r--fpu/softfloat.c94
-rw-r--r--hmp-commands.hx32
-rw-r--r--hw/acpi/piix4.c7
-rw-r--r--hw/arm/armsse.c87
-rw-r--r--hw/arm/aspeed.c2
-rw-r--r--hw/arm/pxa2xx.c2
-rw-r--r--hw/arm/stellaris.c2
-rw-r--r--hw/arm/tosa.c4
-rw-r--r--hw/arm/z2.c2
-rw-r--r--hw/audio/wm8750.c2
-rw-r--r--hw/block/dataplane/xen-block.c5
-rw-r--r--hw/block/virtio-blk.c249
-rw-r--r--hw/block/xen-block.c40
-rw-r--r--hw/core/machine.c2
-rw-r--r--hw/display/qxl.c14
-rw-r--r--hw/display/sii9022.c2
-rw-r--r--hw/display/ssd0303.c4
-rw-r--r--hw/display/trace-events1
-rw-r--r--hw/display/virtio-gpu-3d.c24
-rw-r--r--hw/display/virtio-gpu.c74
-rw-r--r--hw/gpio/max7310.c2
-rw-r--r--hw/i2c/Makefile.objs2
-rw-r--r--hw/i2c/aspeed_i2c.c9
-rw-r--r--hw/i2c/core.c32
-rw-r--r--hw/i2c/exynos4210_i2c.c8
-rw-r--r--hw/i2c/i2c-ddc.c2
-rw-r--r--hw/i2c/imx_i2c.c12
-rw-r--r--hw/i2c/pm_smbus.c119
-rw-r--r--hw/i2c/smbus.c379
-rw-r--r--hw/i2c/smbus_eeprom.c136
-rw-r--r--hw/i2c/smbus_ich9.c12
-rw-r--r--hw/i2c/smbus_master.c165
-rw-r--r--hw/i2c/smbus_slave.c236
-rw-r--r--hw/i386/pc_piix.c3
-rw-r--r--hw/i386/pc_q35.c3
-rw-r--r--hw/ide/atapi.c14
-rw-r--r--hw/ide/core.c19
-rw-r--r--hw/input/lm832x.c2
-rw-r--r--hw/intc/spapr_xive.c20
-rw-r--r--hw/intc/xics.c7
-rw-r--r--hw/intc/xics_kvm.c74
-rw-r--r--hw/intc/xics_spapr.c2
-rw-r--r--hw/intc/xive.c4
-rw-r--r--hw/isa/vt82c686.c1
-rw-r--r--hw/m68k/mcf5208.c30
-rw-r--r--hw/mem/pc-dimm.c4
-rw-r--r--hw/mips/mips_fulong2e.c2
-rw-r--r--hw/mips/mips_malta.c2
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/armsse-mhu.c198
-rw-r--r--hw/misc/iotkit-sysctl.c294
-rw-r--r--hw/misc/pca9552.c2
-rw-r--r--hw/misc/tmp105.c2
-rw-r--r--hw/misc/tmp421.c2
-rw-r--r--hw/misc/trace-events4
-rw-r--r--hw/net/Makefile.objs4
-rw-r--r--hw/net/vhost_net-stub.c92
-rw-r--r--hw/net/vhost_net.c85
-rw-r--r--hw/net/virtio-net.c31
-rw-r--r--hw/nvram/eeprom_at24c.c4
-rw-r--r--hw/pci/pcie.c13
-rw-r--r--hw/ppc/pnv.c22
-rw-r--r--hw/ppc/pnv_psi.c4
-rw-r--r--hw/ppc/ppc.c44
-rw-r--r--hw/ppc/sam460ex.c2
-rw-r--r--hw/ppc/spapr.c406
-rw-r--r--hw/ppc/spapr_drc.c51
-rw-r--r--hw/ppc/spapr_events.c3
-rw-r--r--hw/ppc/spapr_hcall.c68
-rw-r--r--hw/ppc/spapr_irq.c42
-rw-r--r--hw/ppc/spapr_ovec.c6
-rw-r--r--hw/ppc/spapr_pci.c135
-rw-r--r--hw/ppc/spapr_rtas.c6
-rw-r--r--hw/s390x/ap-bridge.c12
-rw-r--r--hw/smbios/smbios.c1
-rw-r--r--hw/timer/ds1338.c2
-rw-r--r--hw/timer/m41t80.c2
-rw-r--r--hw/timer/twl92230.c2
-rw-r--r--hw/usb/dev-mtp.c281
-rw-r--r--hw/usb/trace-events2
-rw-r--r--hw/vfio/ap.c2
-rw-r--r--hw/vfio/pci-quirks.c2
-rw-r--r--hw/virtio/Makefile.objs8
-rw-r--r--hw/virtio/vhost-backend.c12
-rw-r--r--hw/virtio/vhost-user.c13
-rw-r--r--hw/virtio/vhost.c2
-rw-r--r--hw/virtio/virtio-balloon.c102
-rw-r--r--hw/virtio/virtio.c15
-rw-r--r--include/authz/base.h112
-rw-r--r--include/authz/list.h106
-rw-r--r--include/authz/listfile.h111
-rw-r--r--include/authz/pamacct.h100
-rw-r--r--include/authz/simple.h84
-rw-r--r--include/block/block.h16
-rw-r--r--include/block/block_int.h53
-rw-r--r--include/block/nbd.h3
-rw-r--r--include/block/snapshot.h3
-rw-r--r--include/chardev/spice.h27
-rw-r--r--include/exec/poison.h1
-rw-r--r--include/fpu/softfloat.h15
-rw-r--r--include/hw/arm/armsse.h3
-rw-r--r--include/hw/boards.h1
-rw-r--r--include/hw/firmware/smbios.h1
-rw-r--r--include/hw/i2c/i2c.h7
-rw-r--r--include/hw/i2c/pm_smbus.h17
-rw-r--r--include/hw/i2c/smbus_eeprom.h (renamed from include/qemu/acl.h)51
-rw-r--r--include/hw/i2c/smbus_master.h (renamed from include/hw/i2c/smbus.h)54
-rw-r--r--include/hw/i2c/smbus_slave.h100
-rw-r--r--include/hw/ide/internal.h3
-rw-r--r--include/hw/misc/armsse-mhu.h44
-rw-r--r--include/hw/misc/iotkit-sysctl.h25
-rw-r--r--include/hw/pci-host/spapr.h11
-rw-r--r--include/hw/pci/pci_ids.h2
-rw-r--r--include/hw/ppc/ppc.h2
-rw-r--r--include/hw/ppc/spapr.h13
-rw-r--r--include/hw/ppc/spapr_drc.h17
-rw-r--r--include/hw/ppc/spapr_irq.h2
-rw-r--r--include/hw/ppc/spapr_xive.h3
-rw-r--r--include/hw/ppc/xics.h1
-rw-r--r--include/hw/ppc/xics_spapr.h2
-rw-r--r--include/hw/virtio/virtio-balloon.h3
-rw-r--r--include/hw/virtio/virtio-blk.h6
-rw-r--r--include/hw/virtio/virtio-gpu.h10
-rw-r--r--include/hw/virtio/virtio.h15
-rw-r--r--include/io/channel.h9
-rw-r--r--include/migration/vmstate.h3
-rw-r--r--include/qemu/filemonitor.h128
-rw-r--r--include/qemu/iov.h64
-rw-r--r--include/sysemu/block-backend.h2
-rw-r--r--include/ui/spice-display.h4
-rw-r--r--io/channel.c22
-rw-r--r--linux-user/elfload.c2
-rw-r--r--migration/block.c10
-rw-r--r--monitor.c179
-rw-r--r--nbd/client.c52
-rw-r--r--nbd/nbd-internal.h19
-rw-r--r--net/Makefile.objs4
-rw-r--r--net/net.c2
-rw-r--r--net/vhost-user-stub.c23
-rw-r--r--net/vhost-user.c13
-rw-r--r--qapi/Makefile.objs2
-rw-r--r--qapi/authz.json58
-rw-r--r--qapi/qapi-schema.json1
-rw-r--r--qapi/ui.json32
-rw-r--r--qemu-doc.texi46
-rw-r--r--qemu-img.c74
-rw-r--r--qemu-options.hx110
-rw-r--r--qom/object.c12
-rw-r--r--qom/object_interfaces.c16
-rw-r--r--roms/Makefile13
m---------roms/edk20
-rw-r--r--scripts/qemu.py5
-rw-r--r--target/arm/arm-powerctl.c56
-rw-r--r--target/arm/arm-powerctl.h16
-rw-r--r--target/arm/cpu.c32
-rw-r--r--target/arm/cpu.h76
-rw-r--r--target/arm/cpu64.c2
-rw-r--r--target/arm/helper.c27
-rw-r--r--target/arm/helper.h9
-rw-r--r--target/arm/kvm32.c23
-rw-r--r--target/arm/kvm64.c2
-rw-r--r--target/arm/machine.c2
-rw-r--r--target/arm/translate-a64.c49
-rw-r--r--target/arm/translate.c180
-rw-r--r--target/arm/vec_helper.c148
-rw-r--r--target/mips/translate.c43
-rw-r--r--target/ppc/cpu-qom.h5
-rw-r--r--target/ppc/cpu.h33
-rw-r--r--target/ppc/excp_helper.c164
-rw-r--r--target/ppc/helper.h1
-rw-r--r--target/ppc/helper_regs.h27
-rw-r--r--target/ppc/misc_helper.c15
-rw-r--r--target/ppc/mmu-book3s-v3.c29
-rw-r--r--target/ppc/mmu-book3s-v3.h78
-rw-r--r--target/ppc/mmu-hash32.c6
-rw-r--r--target/ppc/mmu-hash64.c39
-rw-r--r--target/ppc/mmu-hash64.h22
-rw-r--r--target/ppc/mmu-radix64.c95
-rw-r--r--target/ppc/mmu-radix64.h4
-rw-r--r--target/ppc/mmu_helper.c9
-rw-r--r--target/ppc/translate.c28
-rw-r--r--target/ppc/translate_init.inc.c41
-rw-r--r--target/s390x/cc_helper.c8
-rw-r--r--target/s390x/excp_helper.c46
-rw-r--r--target/s390x/fpu_helper.c541
-rw-r--r--target/s390x/gen-features.c5
-rw-r--r--target/s390x/helper.c40
-rw-r--r--target/s390x/helper.h9
-rw-r--r--target/s390x/insn-data.def16
-rw-r--r--target/s390x/insn-format.def2
-rw-r--r--target/s390x/internal.h14
-rw-r--r--target/s390x/translate.c409
-rw-r--r--target/xtensa/core-test_mmuhifi_c3/xtensa-modules.inc.c1322
-rw-r--r--target/xtensa/cpu.h40
-rw-r--r--target/xtensa/helper.c94
-rw-r--r--target/xtensa/helper.h5
-rwxr-xr-xtarget/xtensa/import_core.sh2
-rw-r--r--target/xtensa/overlay_tool.h1
-rw-r--r--target/xtensa/translate.c2114
-rw-r--r--target/xtensa/win_helper.c22
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.include50
-rw-r--r--tests/cdrom-test.c10
-rw-r--r--tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2bin0 -> 11776 bytes
-rw-r--r--tests/data/uefi-boot-images/bios-tables-test.arm.iso.qcow2bin0 -> 11776 bytes
-rw-r--r--tests/data/uefi-boot-images/bios-tables-test.i386.iso.qcow2bin0 -> 12800 bytes
-rw-r--r--tests/data/uefi-boot-images/bios-tables-test.x86_64.iso.qcow2bin0 -> 13312 bytes
-rw-r--r--tests/device-plug-test.c178
-rw-r--r--tests/docker/dockerfiles/debian-amd64.docker1
-rw-r--r--tests/docker/dockerfiles/debian9.docker4
-rw-r--r--tests/fp/fp-test.c46
-rw-r--r--tests/fp/wrap.inc.c1
-rwxr-xr-xtests/qemu-iotests/0452
-rw-r--r--tests/qemu-iotests/051.out8
-rw-r--r--tests/qemu-iotests/051.pc.out8
-rwxr-xr-xtests/qemu-iotests/11029
-rw-r--r--tests/qemu-iotests/110.out9
-rwxr-xr-xtests/qemu-iotests/1788
-rw-r--r--tests/qemu-iotests/178.out.qcow224
-rw-r--r--tests/qemu-iotests/206.out56
-rwxr-xr-xtests/qemu-iotests/20710
-rw-r--r--tests/qemu-iotests/207.out18
-rwxr-xr-xtests/qemu-iotests/2105
-rw-r--r--tests/qemu-iotests/210.out28
-rwxr-xr-xtests/qemu-iotests/2119
-rw-r--r--tests/qemu-iotests/211.out26
-rwxr-xr-xtests/qemu-iotests/2125
-rw-r--r--tests/qemu-iotests/212.out44
-rwxr-xr-xtests/qemu-iotests/2135
-rw-r--r--tests/qemu-iotests/213.out46
-rwxr-xr-xtests/qemu-iotests/224139
-rw-r--r--tests/qemu-iotests/224.out18
-rwxr-xr-xtests/qemu-iotests/228239
-rw-r--r--tests/qemu-iotests/228.out84
-rwxr-xr-xtests/qemu-iotests/2325
-rwxr-xr-xtests/qemu-iotests/2333
-rwxr-xr-xtests/qemu-iotests/2377
-rw-r--r--tests/qemu-iotests/237.out54
-rwxr-xr-xtests/qemu-iotests/2428
-rw-r--r--tests/qemu-iotests/common.rc1
-rw-r--r--tests/qemu-iotests/common.tls48
-rw-r--r--tests/qemu-iotests/group2
-rw-r--r--tests/qemu-iotests/iotests.py36
-rw-r--r--tests/tcg/mips/include/wrappers_msa.h30
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_w.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_w.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_w.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_w.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_w.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_w.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_w.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_w.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_w.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_w.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_b.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_d.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_h.c153
-rw-r--r--tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_w.c153
-rw-r--r--tests/tcg/xtensa/Makefile13
-rw-r--r--tests/tcg/xtensa/linker.ld.S67
-rw-r--r--tests/tcg/xtensa/macros.inc41
-rw-r--r--tests/tcg/xtensa/test_b.S40
-rw-r--r--tests/tcg/xtensa/test_boolean.S4
-rw-r--r--tests/tcg/xtensa/test_break.S27
-rw-r--r--tests/tcg/xtensa/test_cache.S62
-rw-r--r--tests/tcg/xtensa/test_clamps.S4
-rw-r--r--tests/tcg/xtensa/test_flix.S60
-rw-r--r--tests/tcg/xtensa/test_fp0_arith.S173
-rw-r--r--tests/tcg/xtensa/test_fp0_conv.S304
-rw-r--r--tests/tcg/xtensa/test_fp1.S141
-rw-r--r--tests/tcg/xtensa/test_fp_cpenable.S27
-rw-r--r--tests/tcg/xtensa/test_interrupt.S88
-rw-r--r--tests/tcg/xtensa/test_loop.S4
-rw-r--r--tests/tcg/xtensa/test_lsc.S122
-rw-r--r--tests/tcg/xtensa/test_mac16.S4
-rw-r--r--tests/tcg/xtensa/test_max.S4
-rw-r--r--tests/tcg/xtensa/test_min.S4
-rw-r--r--tests/tcg/xtensa/test_mmu.S4
-rw-r--r--tests/tcg/xtensa/test_mul16.S4
-rw-r--r--tests/tcg/xtensa/test_mul32.S4
-rw-r--r--tests/tcg/xtensa/test_nsa.S4
-rw-r--r--tests/tcg/xtensa/test_phys_mem.S4
-rw-r--r--tests/tcg/xtensa/test_quo.S4
-rw-r--r--tests/tcg/xtensa/test_rem.S4
-rw-r--r--tests/tcg/xtensa/test_rst0.S8
-rw-r--r--tests/tcg/xtensa/test_s32c1i.S12
-rw-r--r--tests/tcg/xtensa/test_sext.S4
-rw-r--r--tests/tcg/xtensa/test_sr.S153
-rw-r--r--tests/tcg/xtensa/test_timer.S48
-rw-r--r--tests/tcg/xtensa/test_windowed.S32
-rw-r--r--tests/tcg/xtensa/vectors.S14
-rw-r--r--tests/test-authz-list.c159
-rw-r--r--tests/test-authz-listfile.c195
-rw-r--r--tests/test-authz-pam.c124
-rw-r--r--tests/test-authz-simple.c50
-rw-r--r--tests/test-bdrv-drain.c61
-rw-r--r--tests/test-bdrv-graph-mod.c198
-rw-r--r--tests/test-crypto-tlssession.c15
-rw-r--r--tests/test-io-channel-tls.c16
-rw-r--r--tests/test-util-filemonitor.c685
-rw-r--r--tests/uefi-test-tools/.gitignore3
-rw-r--r--tests/uefi-test-tools/LICENSE25
-rw-r--r--tests/uefi-test-tools/Makefile106
-rw-r--r--tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.c130
-rw-r--r--tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.inf41
-rw-r--r--tests/uefi-test-tools/UefiTestToolsPkg/Include/Guid/BiosTablesTest.h67
-rw-r--r--tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dec27
-rw-r--r--tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc69
-rwxr-xr-xtests/uefi-test-tools/build.sh145
-rw-r--r--tests/vhost-user-test.c160
-rw-r--r--tests/virtio-blk-test.c127
-rw-r--r--ui/Makefile.objs5
-rw-r--r--ui/gtk.c30
-rw-r--r--ui/kbd-state.c16
-rw-r--r--ui/sdl2-input.c3
-rw-r--r--ui/spice-app.c202
-rw-r--r--ui/spice-core.c64
-rw-r--r--ui/spice-display.c11
-rw-r--r--ui/vnc-auth-sasl.c23
-rw-r--r--ui/vnc-auth-sasl.h5
-rw-r--r--ui/vnc-auth-vencrypt.c2
-rw-r--r--ui/vnc-ws.c2
-rw-r--r--ui/vnc.c37
-rw-r--r--ui/vnc.h4
-rw-r--r--util/Makefile.objs4
-rw-r--r--util/acl.c179
-rw-r--r--util/aio-posix.c3
-rw-r--r--util/filemonitor-inotify.c339
-rw-r--r--util/filemonitor-stub.c59
-rw-r--r--util/trace-events9
430 files changed, 22687 insertions, 5541 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000..79d02cf740
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,73 @@
+before_script:
+ - apt-get update -qq
+ - apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage
+
+build-system1:
+ script:
+ - apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev
+      libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev
+ - ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
+      cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu
+      mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu"
+ - make -j2
+ - make -j2 check
+
+build-system2:
+ script:
+ - apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev
+      libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev
+ - ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu
+      microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu
+      sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu"
+ - make -j2
+ - make -j2 check
+
+build-disabled:
+ script:
+ - ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl
+      --disable-capstone --disable-live-block-migration --disable-glusterfs
+      --disable-replication --disable-coroutine-pool --disable-smartcard
+      --disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm
+      --disable-qom-cast-debug --disable-spice --disable-vhost-vsock
+      --disable-vhost-net --disable-vhost-crypto --disable-vhost-user
+      --target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user"
+ - make -j2
+ - make -j2 check-qtest SPEED=slow
+
+build-tcg-disabled:
+ script:
+ - apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev
+ - ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
+ - make -j2
+ - make check-unit
+ - make check-qapi-schema
+ - cd tests/qemu-iotests/
+ - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
+            052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160
+            163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236
+ - ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019
+            020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038
+            039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058
+            060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082
+            085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108
+            110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138
+            139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158
+            161 165 170 172 174 176 177 179 184 186 187 190 192 194 195 196
+            197 200 202 203 205 208 209 214 215 216 217 218 222 226 227 229 234
+
+build-user:
+ script:
+ - ./configure --enable-werror --disable-system --disable-guest-agent
+               --disable-capstone --disable-slirp --disable-fdt
+ - make -j2
+ - make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user
+
+build-clang:
+ script:
+ - apt-get install -y -qq clang libsdl2-dev
+      xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev
+ - ./configure --cc=clang --cxx=clang++ --enable-werror
+      --target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
+                     ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
+ - make -j2
+ - make -j2 check
diff --git a/.gitmodules b/.gitmodules
index 6b91176098..ceafb0ee29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -49,3 +49,6 @@
 [submodule "tests/fp/berkeley-softfloat-3"]
 	path = tests/fp/berkeley-softfloat-3
 	url = https://github.com/cota/berkeley-softfloat-3
+[submodule "roms/edk2"]
+	path = roms/edk2
+	url = https://github.com/tianocore/edk2.git
diff --git a/.travis.yml b/.travis.yml
index baa06b976a..cca57f4314 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -86,11 +86,16 @@ matrix:
 
 
     - env:
-        - CONFIG="--enable-debug --enable-debug-tcg"
+        - CONFIG="--enable-debug --enable-debug-tcg --disable-user"
 
 
+    # TCG debug can be run just on it's own and is mostly agnostic to user/softmmu distinctions
     - env:
-        - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user"
+        - CONFIG="--enable-debug-tcg --disable-system"
+
+
+    - env:
+        - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-user --disable-replication"
 
 
     - env:
@@ -174,13 +179,6 @@ matrix:
       compiler: clang
 
 
-    - env:
-        - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
-      os: osx
-      osx_image: xcode10
-      compiler: clang
-
-
     # Python builds
     - env:
         - CONFIG="--target-list=x86_64-softmmu"
diff --git a/MAINTAINERS b/MAINTAINERS
index 6ae55ff732..5040d9dfb1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -425,15 +425,20 @@ Hosts:
 ------
 
 LINUX
-L: qemu-devel@nongnu.org
+M: Michael S. Tsirkin <mst@redhat.com>
+M: Cornelia Huck <cohuck@redhat.com>
+M: Paolo Bonzini <pbonzini@redhat.com>
 S: Maintained
-F: linux-*
 F: linux-headers/
+F: scripts/update-linux-headers.sh
 
 POSIX
-L: qemu-devel@nongnu.org
+M: Paolo Bonzini <pbonzini@redhat.com>
 S: Maintained
-F: *posix*
+F: os-posix.c
+F: include/sysemu/os-posix.h
+F: util/*posix*.c
+F: include/qemu/*posix*.h
 
 NETBSD
 L: qemu-devel@nongnu.org
@@ -633,6 +638,8 @@ F: hw/misc/iotkit-sysinfo.c
 F: include/hw/misc/iotkit-sysinfo.h
 F: hw/misc/armsse-cpuid.c
 F: include/hw/misc/armsse-cpuid.h
+F: hw/misc/armsse-mhu.c
+F: include/hw/misc/armsse-mhu.h
 
 Musca
 M: Peter Maydell <peter.maydell@linaro.org>
@@ -851,6 +858,15 @@ S: Maintained
 F: hw/cris/axis_dev88.c
 F: hw/*/etraxfs_*.c
 
+HP-PARISC Machines
+------------------
+Dino
+M: Richard Henderson <rth@twiddle.net>
+R: Helge Deller <deller@gmx.de>
+S: Odd Fixes
+F: hw/hppa/
+F: pc-bios/hppa-firmware.img
+
 LM32 Machines
 -------------
 EVR32 and uclinux BSP
@@ -1089,20 +1105,27 @@ M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
 S: Maintained
 F: hw/sparc/sun4m.c
 F: hw/sparc/sun4m_iommu.c
+F: hw/display/cg3.c
+F: hw/display/tcx.c
 F: hw/dma/sparc32_dma.c
 F: hw/misc/eccmemctl.c
-F: hw/misc/slavio_misc.c
+F: hw/*/slavio_*.c
+F: include/hw/nvram/sun_nvram.h
 F: include/hw/sparc/sparc32_dma.h
-F: pc-bios/openbios-sparc32
 F: include/hw/sparc/sun4m_iommu.h
+F: pc-bios/openbios-sparc32
 
 Sun4u
 M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
 S: Maintained
 F: hw/sparc64/sun4u.c
-F: pc-bios/openbios-sparc64
+F: hw/sparc64/sun4u_iommu.c
+F: include/hw/sparc/sun4u_iommu.h
 F: hw/pci-host/sabre.c
 F: include/hw/pci-host/sabre.h
+F: hw/pci-bridge/simba.c
+F: include/hw/pci-bridge/simba.h
+F: pc-bios/openbios-sparc64
 
 Sun4v
 M: Artyom Tarasenko <atar4qemu@gmail.com>
@@ -1181,6 +1204,10 @@ F: hw/acpi/ich9.c
 F: include/hw/acpi/ich9.h
 F: include/hw/acpi/piix4.h
 F: hw/misc/sga.c
+F: hw/isa/apm.c
+F: include/hw/isa/apm.h
+F: tests/test-x86-cpuid.c
+F: tests/test-x86-cpuid-compat.c
 
 PC Chipset
 M: Michael S. Tsirkin <mst@redhat.com>
@@ -1698,7 +1725,7 @@ F: include/scsi/*
 F: scsi/*
 
 Block Jobs
-M: Jeff Cody <jcody@redhat.com>
+M: John Snow <jsnow@redhat.com>
 L: qemu-block@nongnu.org
 S: Supported
 F: blockjob.c
@@ -1711,7 +1738,7 @@ F: block/commit.c
 F: block/stream.c
 F: block/mirror.c
 F: qapi/job.json
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
+T: git https://github.com/jnsnow/qemu.git jobs
 
 Block QAPI, monitor, command line
 M: Markus Armbruster <armbru@redhat.com>
@@ -1795,8 +1822,7 @@ F: util/error.c
 F: util/qemu-error.c
 
 GDB stub
-L: qemu-devel@nongnu.org
-S: Odd Fixes
+S: Orphan
 F: gdbstub*
 F: gdb-xml/
 
@@ -1932,10 +1958,14 @@ F: include/qapi/qmp/
 X: include/qapi/qmp/dispatch.h
 F: scripts/coccinelle/qobject.cocci
 F: tests/check-qdict.c
-F: tests/check-qnum.c
 F: tests/check-qjson.c
 F: tests/check-qlist.c
+F: tests/check-qlit.c
+F: tests/check-qnull.c
+F: tests/check-qnum.c
+F: tests/check-qobject.c
 F: tests/check-qstring.c
+F: qdict-test-data.txt
 T: git https://repo.or.cz/qemu/armbru.git qapi-next
 
 QEMU Guest Agent
@@ -2057,6 +2087,8 @@ F: crypto/
 F: include/crypto/
 F: tests/test-crypto-*
 F: tests/benchmark-crypto-*
+F: tests/crypto-tls-*
+F: tests/pkix_asn1_tab.c
 F: qemu.sasl
 
 Coroutines
@@ -2079,6 +2111,14 @@ F: io/
 F: include/io/
 F: tests/test-io-*
 
+User authorization
+M: Daniel P. Berrange <berrange@redhat.com>
+S: Maintained
+F: authz/
+F: qapi/authz.json
+F: include/authz/
+F: tests/test-authz-*
+
 Sockets
 M: Daniel P. Berrange <berrange@redhat.com>
 M: Gerd Hoffmann <kraxel@redhat.com>
@@ -2087,6 +2127,13 @@ F: include/qemu/sockets.h
 F: util/qemu-sockets.c
 F: qapi/sockets.json
 
+File monitor
+M: Daniel P. Berrange <berrange@redhat.com>
+S: Odd fixes
+F: util/filemonitor*.c
+F: include/qemu/filemonitor.h
+F: tests/test-util-filemonitor.c
+
 Throttling infrastructure
 M: Alberto Garcia <berto@igalia.com>
 S: Supported
@@ -2145,6 +2192,18 @@ M: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
 S: Maintained
 F: contrib/elf2dmp/
 
+I2C and SMBus
+M: Corey Minyard <cminyard@mvista.com>
+S: Maintained
+F: hw/i2c/core.c
+F: hw/i2c/smbus_slave.c
+F: hw/i2c/smbus_master.c
+F: hw/i2c/smbus_eeprom.c
+F: include/hw/i2c/i2c.h
+F: include/hw/i2c/smbus_master.h
+F: include/hw/i2c/smbus_slave.h
+F: include/hw/i2c/smbus_eeprom.h
+
 Usermode Emulation
 ------------------
 Overall
@@ -2190,7 +2249,7 @@ F: tcg/arm/
 F: disas/arm.c
 
 i386 target
-L: qemu-devel@nongnu.org
+M: Richard Henderson <rth@twiddle.net>
 S: Maintained
 F: tcg/i386/
 F: disas/i386.c
@@ -2209,7 +2268,6 @@ F: tcg/ppc/
 F: disas/ppc.c
 
 RISC-V
-M: Michael Clark <mjc@sifive.com>
 M: Palmer Dabbelt <palmer@sifive.com>
 M: Alistair Francis <Alistair.Francis@wdc.com>
 L: qemu-riscv@nongnu.org
@@ -2246,26 +2304,22 @@ F: block/vmdk.c
 
 RBD
 M: Josh Durgin <jdurgin@redhat.com>
-M: Jeff Cody <jcody@redhat.com>
 L: qemu-block@nongnu.org
 S: Supported
 F: block/rbd.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
 
 Sheepdog
 M: Liu Yuan <namei.unix@gmail.com>
-M: Jeff Cody <jcody@redhat.com>
 L: qemu-block@nongnu.org
-S: Supported
+L: sheepdog@lists.wpkg.org
+S: Odd Fixes
 F: block/sheepdog.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
 
 VHDX
-M: Jeff Cody <jcody@redhat.com>
+M: Jeff Cody <codyprime@gmail.com>
 L: qemu-block@nongnu.org
 S: Supported
 F: block/vhdx*
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
 
 VDI
 M: Stefan Weil <sw@weilnetz.de>
@@ -2295,34 +2349,26 @@ F: docs/interop/nbd.txt
 T: git https://repo.or.cz/qemu/ericb.git nbd
 
 NFS
-M: Jeff Cody <jcody@redhat.com>
 M: Peter Lieven <pl@kamp.de>
 L: qemu-block@nongnu.org
 S: Maintained
 F: block/nfs.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
 
 SSH
 M: Richard W.M. Jones <rjones@redhat.com>
-M: Jeff Cody <jcody@redhat.com>
 L: qemu-block@nongnu.org
 S: Supported
 F: block/ssh.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
 
 CURL
-M: Jeff Cody <jcody@redhat.com>
 L: qemu-block@nongnu.org
 S: Supported
 F: block/curl.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
 
 GLUSTER
-M: Jeff Cody <jcody@redhat.com>
 L: qemu-block@nongnu.org
 S: Supported
 F: block/gluster.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
 
 Null Block Driver
 M: Fam Zheng <fam@euphon.net>
@@ -2486,6 +2532,11 @@ S: Maintained
 F: .cirrus.yml
 W: https://cirrus-ci.com/github/qemu/qemu
 
+GitLab Continuous Integration
+M: Thomas Huth <thuth@redhat.com>
+S: Maintained
+F: .gitlab-ci.yml
+
 Guest Test Compilation Support
 M: Alex Bennée <alex.bennee@linaro.org>
 R: Philippe Mathieu-Daudé <f4bug@amsat.org>
diff --git a/Makefile b/Makefile
index a6de28677f..2208bde419 100644
--- a/Makefile
+++ b/Makefile
@@ -359,6 +359,7 @@ endif
 
 dummy := $(call unnest-vars,, \
                 stub-obj-y \
+                authz-obj-y \
                 chardev-obj-y \
                 util-obj-y \
                 qga-obj-y \
@@ -423,6 +424,7 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
 SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
 SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
 
+$(SOFTMMU_SUBDIR_RULES): $(authz-obj-y)
 $(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
 $(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
 $(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
@@ -485,9 +487,9 @@ COMMON_LDADDS = libqemuutil.a
 
 qemu-img.o: qemu-img-cmds.h
 
-qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
-qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
-qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
 
 qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
 
@@ -498,7 +500,7 @@ qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS)
 fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
 fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
 
-scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
 ifdef CONFIG_MPATH
 scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist
 endif
@@ -607,7 +609,11 @@ clean:
 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
 	rm -f qemu-options.def
 	rm -f *.msi
-	find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} +
+	find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f \
+		! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-aarch64.a \
+		! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \
+		! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \
+		-exec rm {} +
 	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
 	rm -f fsdev/*.pod scsi/*.pod
 	rm -f qemu-img-cmds.h
diff --git a/Makefile.objs b/Makefile.objs
index 5fb022d7ad..6e91ee5674 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -1,12 +1,17 @@
 #######################################################################
 # Common libraries for tools and emulators
-stub-obj-y = stubs/ crypto/
+stub-obj-y = stubs/ util/ crypto/
 util-obj-y = util/ qobject/ qapi/
 
 chardev-obj-y = chardev/
 slirp-obj-$(CONFIG_SLIRP) = slirp/
 
 #######################################################################
+# authz-obj-y is code used by both qemu system emulation and qemu-img
+
+authz-obj-y = authz/
+
+#######################################################################
 # block-obj-y is code used by both qemu system emulation and qemu-img
 
 block-obj-y += nbd/
@@ -125,6 +130,7 @@ trace-events-subdirs =
 trace-events-subdirs += accel/kvm
 trace-events-subdirs += accel/tcg
 trace-events-subdirs += audio
+trace-events-subdirs += authz
 trace-events-subdirs += block
 trace-events-subdirs += chardev
 trace-events-subdirs += crypto
diff --git a/Makefile.target b/Makefile.target
index d6ce549388..3b79e7074c 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -179,6 +179,7 @@ include $(SRC_PATH)/Makefile.objs
 dummy := $(call unnest-vars,,target-obj-y)
 target-obj-y-save := $(target-obj-y)
 dummy := $(call unnest-vars,.., \
+               authz-obj-y \
                block-obj-y \
                block-obj-m \
                chardev-obj-y \
@@ -193,6 +194,7 @@ target-obj-y := $(target-obj-y-save)
 all-obj-y += $(common-obj-y)
 all-obj-y += $(target-obj-y)
 all-obj-y += $(qom-obj-y)
+all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
 all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
 all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
 all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
diff --git a/audio/audio.c b/audio/audio.c
index d163ffbc88..909c817103 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -454,9 +454,7 @@ static void audio_print_options (const char *prefix,
 static void audio_process_options (const char *prefix,
                                    struct audio_option *opt)
 {
-    char *optname;
-    const char qemu_prefix[] = "QEMU_";
-    size_t preflen, optlen;
+    gchar *prefix_upper;
 
     if (audio_bug(__func__, !prefix)) {
         dolog ("prefix = NULL\n");
@@ -468,10 +466,10 @@ static void audio_process_options (const char *prefix,
         return;
     }
 
-    preflen = strlen (prefix);
+    prefix_upper = g_utf8_strup(prefix, -1);
 
     for (; opt->name; opt++) {
-        size_t len, i;
+        char *optname;
         int def;
 
         if (!opt->valp) {
@@ -480,21 +478,7 @@ static void audio_process_options (const char *prefix,
             continue;
         }
 
-        len = strlen (opt->name);
-        /* len of opt->name + len of prefix + size of qemu_prefix
-         * (includes trailing zero) + zero + underscore (on behalf of
-         * sizeof) */
-        optlen = len + preflen + sizeof (qemu_prefix) + 1;
-        optname = g_malloc (optlen);
-
-        pstrcpy (optname, optlen, qemu_prefix);
-
-        /* copy while upper-casing, including trailing zero */
-        for (i = 0; i <= preflen; ++i) {
-            optname[i + sizeof (qemu_prefix) - 1] = qemu_toupper(prefix[i]);
-        }
-        pstrcat (optname, optlen, "_");
-        pstrcat (optname, optlen, opt->name);
+        optname = g_strdup_printf("QEMU_%s_%s", prefix_upper, opt->name);
 
         def = 1;
         switch (opt->tag) {
@@ -532,6 +516,7 @@ static void audio_process_options (const char *prefix,
         *opt->overriddenp = !def;
         g_free (optname);
     }
+    g_free(prefix_upper);
 }
 
 static void audio_print_settings (struct audsettings *as)
@@ -826,12 +811,7 @@ static int audio_attach_capture (HWVoiceOut *hw)
         SWVoiceOut *sw;
         HWVoiceOut *hw_cap = &cap->hw;
 
-        sc = audio_calloc(__func__, 1, sizeof(*sc));
-        if (!sc) {
-            dolog ("Could not allocate soft capture voice (%zu bytes)\n",
-                   sizeof (*sc));
-            return -1;
-        }
+        sc = g_malloc0(sizeof(*sc));
 
         sc->cap = cap;
         sw = &sc->sw;
@@ -1975,15 +1955,10 @@ CaptureVoiceOut *AUD_add_capture (
     if (audio_validate_settings (as)) {
         dolog ("Invalid settings were passed when trying to add capture\n");
         audio_print_settings (as);
-        goto err0;
+        return NULL;
     }
 
-    cb = audio_calloc(__func__, 1, sizeof(*cb));
-    if (!cb) {
-        dolog ("Could not allocate capture callback information, size %zu\n",
-               sizeof (*cb));
-        goto err0;
-    }
+    cb = g_malloc0(sizeof(*cb));
     cb->ops = *ops;
     cb->opaque = cb_opaque;
 
@@ -1996,12 +1971,7 @@ CaptureVoiceOut *AUD_add_capture (
         HWVoiceOut *hw;
         CaptureVoiceOut *cap;
 
-        cap = audio_calloc(__func__, 1, sizeof(*cap));
-        if (!cap) {
-            dolog ("Could not allocate capture voice, size %zu\n",
-                   sizeof (*cap));
-            goto err1;
-        }
+        cap = g_malloc0(sizeof(*cap));
 
         hw = &cap->hw;
         QLIST_INIT (&hw->sw_head);
@@ -2009,23 +1979,11 @@ CaptureVoiceOut *AUD_add_capture (
 
         /* XXX find a more elegant way */
         hw->samples = 4096 * 4;
-        hw->mix_buf = audio_calloc(__func__, hw->samples,
-                                   sizeof(struct st_sample));
-        if (!hw->mix_buf) {
-            dolog ("Could not allocate capture mix buffer (%d samples)\n",
-                   hw->samples);
-            goto err2;
-        }
+        hw->mix_buf = g_new0(struct st_sample, hw->samples);
 
         audio_pcm_init_info (&hw->info, as);
 
-        cap->buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
-        if (!cap->buf) {
-            dolog ("Could not allocate capture buffer "
-                   "(%d samples, each %d bytes)\n",
-                   hw->samples, 1 << hw->info.shift);
-            goto err3;
-        }
+        cap->buf = g_malloc0_n(hw->samples, 1 << hw->info.shift);
 
         hw->clip = mixeng_clip
             [hw->info.nchannels == 2]
@@ -2040,15 +1998,6 @@ CaptureVoiceOut *AUD_add_capture (
             audio_attach_capture (hw);
         }
         return cap;
-
-    err3:
-        g_free (cap->hw.mix_buf);
-    err2:
-        g_free (cap);
-    err1:
-        g_free (cb);
-    err0:
-        return NULL;
     }
 }
 
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index 9db5ac92bc..f7ee70b153 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -38,14 +38,9 @@
 #define AUDIO_CAP "sdl"
 #include "audio_int.h"
 
-#define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2)
-
 typedef struct SDLVoiceOut {
     HWVoiceOut hw;
     int live;
-#if USE_SEMAPHORE
-    int rpos;
-#endif
     int decr;
 } SDLVoiceOut;
 
@@ -57,10 +52,6 @@ static struct {
 
 static struct SDLAudioState {
     int exit;
-#if USE_SEMAPHORE
-    SDL_mutex *mutex;
-    SDL_sem *sem;
-#endif
     int initialized;
     bool driver_created;
 } glob_sdl;
@@ -77,66 +68,6 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
     AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
 }
 
-static int sdl_lock (SDLAudioState *s, const char *forfn)
-{
-#if USE_SEMAPHORE
-    if (SDL_LockMutex (s->mutex)) {
-        sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
-        return -1;
-    }
-#else
-    SDL_LockAudio();
-#endif
-
-    return 0;
-}
-
-static int sdl_unlock (SDLAudioState *s, const char *forfn)
-{
-#if USE_SEMAPHORE
-    if (SDL_UnlockMutex (s->mutex)) {
-        sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
-        return -1;
-    }
-#else
-    SDL_UnlockAudio();
-#endif
-
-    return 0;
-}
-
-static int sdl_post (SDLAudioState *s, const char *forfn)
-{
-#if USE_SEMAPHORE
-    if (SDL_SemPost (s->sem)) {
-        sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
-        return -1;
-    }
-#endif
-
-    return 0;
-}
-
-#if USE_SEMAPHORE
-static int sdl_wait (SDLAudioState *s, const char *forfn)
-{
-    if (SDL_SemWait (s->sem)) {
-        sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
-        return -1;
-    }
-    return 0;
-}
-#endif
-
-static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
-{
-    if (sdl_unlock (s, forfn)) {
-        return -1;
-    }
-
-    return sdl_post (s, forfn);
-}
-
 static int aud_to_sdlfmt (audfmt_e fmt)
 {
     switch (fmt) {
@@ -243,9 +174,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
 static void sdl_close (SDLAudioState *s)
 {
     if (s->initialized) {
-        sdl_lock (s, "sdl_close");
+        SDL_LockAudio();
         s->exit = 1;
-        sdl_unlock_and_post (s, "sdl_close");
+        SDL_UnlockAudio();
         SDL_PauseAudio (1);
         SDL_CloseAudio ();
         s->initialized = 0;
@@ -258,76 +189,36 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
     SDLAudioState *s = &glob_sdl;
     HWVoiceOut *hw = &sdl->hw;
     int samples = len >> hw->info.shift;
+    int to_mix, decr;
 
-    if (s->exit) {
+    if (s->exit || !sdl->live) {
         return;
     }
 
-    while (samples) {
-        int to_mix, decr;
-
-        /* dolog ("in callback samples=%d\n", samples); */
-#if USE_SEMAPHORE
-        sdl_wait (s, "sdl_callback");
-        if (s->exit) {
-            return;
-        }
-
-        if (sdl_lock (s, "sdl_callback")) {
-            return;
-        }
-
-        if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) {
-            dolog ("sdl->live=%d hw->samples=%d\n",
-                   sdl->live, hw->samples);
-            return;
-        }
-
-        if (!sdl->live) {
-            goto again;
-        }
-#else
-        if (s->exit || !sdl->live) {
-            break;
-        }
-#endif
+    /* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */
 
-        /* dolog ("in callback live=%d\n", live); */
-        to_mix = audio_MIN (samples, sdl->live);
-        decr = to_mix;
-        while (to_mix) {
-            int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
-            struct st_sample *src = hw->mix_buf + hw->rpos;
-
-            /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
-            hw->clip (buf, src, chunk);
-#if USE_SEMAPHORE
-            sdl->rpos = (sdl->rpos + chunk) % hw->samples;
-#else
-            hw->rpos = (hw->rpos + chunk) % hw->samples;
-#endif
-            to_mix -= chunk;
-            buf += chunk << hw->info.shift;
-        }
-        samples -= decr;
-        sdl->live -= decr;
-        sdl->decr += decr;
-
-#if USE_SEMAPHORE
-    again:
-        if (sdl_unlock (s, "sdl_callback")) {
-            return;
-        }
-#endif
+    to_mix = audio_MIN(samples, sdl->live);
+    decr = to_mix;
+    while (to_mix) {
+        int chunk = audio_MIN(to_mix, hw->samples - hw->rpos);
+        struct st_sample *src = hw->mix_buf + hw->rpos;
+
+        /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
+        hw->clip(buf, src, chunk);
+        hw->rpos = (hw->rpos + chunk) % hw->samples;
+        to_mix -= chunk;
+        buf += chunk << hw->info.shift;
     }
+    samples -= decr;
+    sdl->live -= decr;
+    sdl->decr += decr;
+
     /* dolog ("done len=%d\n", len); */
 
-#if (SDL_MAJOR_VERSION >= 2)
     /* SDL2 does not clear the remaining buffer for us, so do it on our own */
     if (samples) {
         memset(buf, 0, samples << hw->info.shift);
     }
-#endif
 }
 
 static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
@@ -339,11 +230,8 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
 {
     int decr;
     SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
-    SDLAudioState *s = &glob_sdl;
 
-    if (sdl_lock (s, "sdl_run_out")) {
-        return 0;
-    }
+    SDL_LockAudio();
 
     if (sdl->decr > live) {
         ldebug ("sdl->decr %d live %d sdl->live %d\n",
@@ -355,19 +243,10 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
     decr = audio_MIN (sdl->decr, live);
     sdl->decr -= decr;
 
-#if USE_SEMAPHORE
-    sdl->live = live - decr;
-    hw->rpos = sdl->rpos;
-#else
     sdl->live = live;
-#endif
 
-    if (sdl->live > 0) {
-        sdl_unlock_and_post (s, "sdl_run_out");
-    }
-    else {
-        sdl_unlock (s, "sdl_run_out");
-    }
+    SDL_UnlockAudio();
+
     return decr;
 }
 
@@ -449,23 +328,6 @@ static void *sdl_audio_init (void)
         return NULL;
     }
 
-#if USE_SEMAPHORE
-    s->mutex = SDL_CreateMutex ();
-    if (!s->mutex) {
-        sdl_logerr ("Failed to create SDL mutex\n");
-        SDL_QuitSubSystem (SDL_INIT_AUDIO);
-        return NULL;
-    }
-
-    s->sem = SDL_CreateSemaphore (0);
-    if (!s->sem) {
-        sdl_logerr ("Failed to create SDL semaphore\n");
-        SDL_DestroyMutex (s->mutex);
-        SDL_QuitSubSystem (SDL_INIT_AUDIO);
-        return NULL;
-    }
-#endif
-
     s->driver_created = true;
     return s;
 }
@@ -474,10 +336,6 @@ static void sdl_audio_fini (void *opaque)
 {
     SDLAudioState *s = opaque;
     sdl_close (s);
-#if USE_SEMAPHORE
-    SDL_DestroySemaphore (s->sem);
-    SDL_DestroyMutex (s->mutex);
-#endif
     SDL_QuitSubSystem (SDL_INIT_AUDIO);
     s->driver_created = false;
 }
diff --git a/authz/Makefile.objs b/authz/Makefile.objs
new file mode 100644
index 0000000000..ed7b273596
--- /dev/null
+++ b/authz/Makefile.objs
@@ -0,0 +1,7 @@
+authz-obj-y += base.o
+authz-obj-y += simple.o
+authz-obj-y += list.o
+authz-obj-y += listfile.o
+authz-obj-$(CONFIG_AUTH_PAM) += pamacct.o
+
+pamacct.o-libs = -lpam
diff --git a/authz/base.c b/authz/base.c
new file mode 100644
index 0000000000..110dfa4195
--- /dev/null
+++ b/authz/base.c
@@ -0,0 +1,82 @@
+/*
+ * QEMU authorization framework base class
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/base.h"
+#include "authz/trace.h"
+
+bool qauthz_is_allowed(QAuthZ *authz,
+                       const char *identity,
+                       Error **errp)
+{
+    QAuthZClass *cls = QAUTHZ_GET_CLASS(authz);
+    bool allowed;
+
+    allowed = cls->is_allowed(authz, identity, errp);
+    trace_qauthz_is_allowed(authz, identity, allowed);
+
+    return allowed;
+}
+
+
+bool qauthz_is_allowed_by_id(const char *authzid,
+                             const char *identity,
+                             Error **errp)
+{
+    QAuthZ *authz;
+    Object *obj;
+    Object *container;
+
+    container = object_get_objects_root();
+    obj = object_resolve_path_component(container,
+                                        authzid);
+    if (!obj) {
+        error_setg(errp, "Cannot find QAuthZ object ID %s",
+                   authzid);
+        return false;
+    }
+
+    if (!object_dynamic_cast(obj, TYPE_QAUTHZ)) {
+        error_setg(errp, "Object '%s' is not a QAuthZ subclass",
+                   authzid);
+        return false;
+    }
+
+    authz = QAUTHZ(obj);
+
+    return qauthz_is_allowed(authz, identity, errp);
+}
+
+
+static const TypeInfo authz_info = {
+    .parent = TYPE_OBJECT,
+    .name = TYPE_QAUTHZ,
+    .instance_size = sizeof(QAuthZ),
+    .class_size = sizeof(QAuthZClass),
+    .abstract = true,
+};
+
+static void qauthz_register_types(void)
+{
+    type_register_static(&authz_info);
+}
+
+type_init(qauthz_register_types)
+
diff --git a/authz/list.c b/authz/list.c
new file mode 100644
index 0000000000..dc6b0fec13
--- /dev/null
+++ b/authz/list.c
@@ -0,0 +1,271 @@
+/*
+ * QEMU access control list authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/list.h"
+#include "authz/trace.h"
+#include "qom/object_interfaces.h"
+#include "qapi/qapi-visit-authz.h"
+
+static bool qauthz_list_is_allowed(QAuthZ *authz,
+                                   const char *identity,
+                                   Error **errp)
+{
+    QAuthZList *lauthz = QAUTHZ_LIST(authz);
+    QAuthZListRuleList *rules = lauthz->rules;
+
+    while (rules) {
+        QAuthZListRule *rule = rules->value;
+        QAuthZListFormat format = rule->has_format ? rule->format :
+            QAUTHZ_LIST_FORMAT_EXACT;
+
+        trace_qauthz_list_check_rule(authz, rule->match, identity,
+                                     format, rule->policy);
+        switch (format) {
+        case QAUTHZ_LIST_FORMAT_EXACT:
+            if (g_str_equal(rule->match, identity)) {
+                return rule->policy == QAUTHZ_LIST_POLICY_ALLOW;
+            }
+            break;
+        case QAUTHZ_LIST_FORMAT_GLOB:
+            if (g_pattern_match_simple(rule->match, identity)) {
+                return rule->policy == QAUTHZ_LIST_POLICY_ALLOW;
+            }
+            break;
+        default:
+            g_warn_if_reached();
+            return false;
+        }
+        rules = rules->next;
+    }
+
+    trace_qauthz_list_default_policy(authz, identity, lauthz->policy);
+    return lauthz->policy == QAUTHZ_LIST_POLICY_ALLOW;
+}
+
+
+static void
+qauthz_list_prop_set_policy(Object *obj,
+                            int value,
+                            Error **errp G_GNUC_UNUSED)
+{
+    QAuthZList *lauthz = QAUTHZ_LIST(obj);
+
+    lauthz->policy = value;
+}
+
+
+static int
+qauthz_list_prop_get_policy(Object *obj,
+                            Error **errp G_GNUC_UNUSED)
+{
+    QAuthZList *lauthz = QAUTHZ_LIST(obj);
+
+    return lauthz->policy;
+}
+
+
+static void
+qauthz_list_prop_get_rules(Object *obj, Visitor *v, const char *name,
+                           void *opaque, Error **errp)
+{
+    QAuthZList *lauthz = QAUTHZ_LIST(obj);
+
+    visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp);
+}
+
+static void
+qauthz_list_prop_set_rules(Object *obj, Visitor *v, const char *name,
+                           void *opaque, Error **errp)
+{
+    QAuthZList *lauthz = QAUTHZ_LIST(obj);
+    QAuthZListRuleList *oldrules;
+
+    oldrules = lauthz->rules;
+    visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp);
+
+    qapi_free_QAuthZListRuleList(oldrules);
+}
+
+
+static void
+qauthz_list_finalize(Object *obj)
+{
+    QAuthZList *lauthz = QAUTHZ_LIST(obj);
+
+    qapi_free_QAuthZListRuleList(lauthz->rules);
+}
+
+
+static void
+qauthz_list_class_init(ObjectClass *oc, void *data)
+{
+    QAuthZClass *authz = QAUTHZ_CLASS(oc);
+
+    object_class_property_add_enum(oc, "policy",
+                                   "QAuthZListPolicy",
+                                   &QAuthZListPolicy_lookup,
+                                   qauthz_list_prop_get_policy,
+                                   qauthz_list_prop_set_policy,
+                                   NULL);
+
+    object_class_property_add(oc, "rules", "QAuthZListRule",
+                              qauthz_list_prop_get_rules,
+                              qauthz_list_prop_set_rules,
+                              NULL, NULL, NULL);
+
+    authz->is_allowed = qauthz_list_is_allowed;
+}
+
+
+QAuthZList *qauthz_list_new(const char *id,
+                            QAuthZListPolicy policy,
+                            Error **errp)
+{
+    return QAUTHZ_LIST(
+        object_new_with_props(TYPE_QAUTHZ_LIST,
+                              object_get_objects_root(),
+                              id, errp,
+                              "policy", QAuthZListPolicy_str(policy),
+                              NULL));
+}
+
+ssize_t qauthz_list_append_rule(QAuthZList *auth,
+                                const char *match,
+                                QAuthZListPolicy policy,
+                                QAuthZListFormat format,
+                                Error **errp)
+{
+    QAuthZListRule *rule;
+    QAuthZListRuleList *rules, *tmp;
+    size_t i = 0;
+
+    rule = g_new0(QAuthZListRule, 1);
+    rule->policy = policy;
+    rule->match = g_strdup(match);
+    rule->format = format;
+    rule->has_format = true;
+
+    tmp = g_new0(QAuthZListRuleList, 1);
+    tmp->value = rule;
+
+    rules = auth->rules;
+    if (rules) {
+        while (rules->next) {
+            i++;
+            rules = rules->next;
+        }
+        rules->next = tmp;
+        return i + 1;
+    } else {
+        auth->rules = tmp;
+        return 0;
+    }
+}
+
+
+ssize_t qauthz_list_insert_rule(QAuthZList *auth,
+                                const char *match,
+                                QAuthZListPolicy policy,
+                                QAuthZListFormat format,
+                                size_t index,
+                                Error **errp)
+{
+    QAuthZListRule *rule;
+    QAuthZListRuleList *rules, *tmp;
+    size_t i = 0;
+
+    rule = g_new0(QAuthZListRule, 1);
+    rule->policy = policy;
+    rule->match = g_strdup(match);
+    rule->format = format;
+    rule->has_format = true;
+
+    tmp = g_new0(QAuthZListRuleList, 1);
+    tmp->value = rule;
+
+    rules = auth->rules;
+    if (rules && index > 0) {
+        while (rules->next && i < (index - 1)) {
+            i++;
+            rules = rules->next;
+        }
+        tmp->next = rules->next;
+        rules->next = tmp;
+        return i + 1;
+    } else {
+        tmp->next = auth->rules;
+        auth->rules = tmp;
+        return 0;
+    }
+}
+
+
+ssize_t qauthz_list_delete_rule(QAuthZList *auth, const char *match)
+{
+    QAuthZListRule *rule;
+    QAuthZListRuleList *rules, *prev;
+    size_t i = 0;
+
+    prev = NULL;
+    rules = auth->rules;
+    while (rules) {
+        rule = rules->value;
+        if (g_str_equal(rule->match, match)) {
+            if (prev) {
+                prev->next = rules->next;
+            } else {
+                auth->rules = rules->next;
+            }
+            rules->next = NULL;
+            qapi_free_QAuthZListRuleList(rules);
+            return i;
+        }
+        prev = rules;
+        rules = rules->next;
+        i++;
+    }
+
+    return -1;
+}
+
+
+static const TypeInfo qauthz_list_info = {
+    .parent = TYPE_QAUTHZ,
+    .name = TYPE_QAUTHZ_LIST,
+    .instance_size = sizeof(QAuthZList),
+    .instance_finalize = qauthz_list_finalize,
+    .class_size = sizeof(QAuthZListClass),
+    .class_init = qauthz_list_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
+
+static void
+qauthz_list_register_types(void)
+{
+    type_register_static(&qauthz_list_info);
+}
+
+
+type_init(qauthz_list_register_types);
diff --git a/authz/listfile.c b/authz/listfile.c
new file mode 100644
index 0000000000..d4579767e7
--- /dev/null
+++ b/authz/listfile.c
@@ -0,0 +1,283 @@
+/*
+ * QEMU access control list file authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/listfile.h"
+#include "authz/trace.h"
+#include "qemu/error-report.h"
+#include "qemu/main-loop.h"
+#include "qemu/sockets.h"
+#include "qemu/filemonitor.h"
+#include "qom/object_interfaces.h"
+#include "qapi/qapi-visit-authz.h"
+#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/qobject-input-visitor.h"
+
+
+static bool
+qauthz_list_file_is_allowed(QAuthZ *authz,
+                            const char *identity,
+                            Error **errp)
+{
+    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(authz);
+    if (fauthz->list) {
+        return qauthz_is_allowed(fauthz->list, identity, errp);
+    }
+
+    return false;
+}
+
+
+static QAuthZ *
+qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp)
+{
+    GError *err = NULL;
+    gchar *content = NULL;
+    gsize len;
+    QObject *obj = NULL;
+    QDict *pdict;
+    Visitor *v = NULL;
+    QAuthZ *ret = NULL;
+
+    trace_qauthz_list_file_load(fauthz, fauthz->filename);
+    if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) {
+        error_setg(errp, "Unable to read '%s': %s",
+                   fauthz->filename, err->message);
+        goto cleanup;
+    }
+
+    obj = qobject_from_json(content, errp);
+    if (!obj) {
+        goto cleanup;
+    }
+
+    pdict = qobject_to(QDict, obj);
+    if (!pdict) {
+        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "obj", "dict");
+        goto cleanup;
+    }
+
+    v = qobject_input_visitor_new(obj);
+
+    ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST,
+                                            NULL, pdict, v, errp);
+
+ cleanup:
+    visit_free(v);
+    qobject_unref(obj);
+    if (err) {
+        g_error_free(err);
+    }
+    g_free(content);
+    return ret;
+}
+
+
+static void
+qauthz_list_file_event(int wd G_GNUC_UNUSED,
+                       QFileMonitorEvent ev G_GNUC_UNUSED,
+                       const char *name G_GNUC_UNUSED,
+                       void *opaque)
+{
+    QAuthZListFile *fauthz = opaque;
+    Error *err = NULL;
+
+    if (ev != QFILE_MONITOR_EVENT_MODIFIED &&
+        ev != QFILE_MONITOR_EVENT_CREATED) {
+        return;
+    }
+
+    object_unref(OBJECT(fauthz->list));
+    fauthz->list = qauthz_list_file_load(fauthz, &err);
+    trace_qauthz_list_file_refresh(fauthz,
+                                   fauthz->filename, fauthz->list ? 1 : 0);
+    if (!fauthz->list) {
+        error_report_err(err);
+    }
+}
+
+static void
+qauthz_list_file_complete(UserCreatable *uc, Error **errp)
+{
+    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(uc);
+    gchar *dir = NULL, *file = NULL;
+
+    fauthz->list = qauthz_list_file_load(fauthz, errp);
+
+    if (!fauthz->refresh) {
+        return;
+    }
+
+    fauthz->file_monitor = qemu_file_monitor_new(errp);
+    if (!fauthz->file_monitor) {
+        return;
+    }
+
+    dir = g_path_get_dirname(fauthz->filename);
+    if (g_str_equal(dir, ".")) {
+        error_setg(errp, "Filename must be an absolute path");
+        goto cleanup;
+    }
+    file = g_path_get_basename(fauthz->filename);
+    if (g_str_equal(file, ".")) {
+        error_setg(errp, "Path has no trailing filename component");
+        goto cleanup;
+    }
+
+    fauthz->file_watch = qemu_file_monitor_add_watch(
+        fauthz->file_monitor, dir, file,
+        qauthz_list_file_event, fauthz, errp);
+    if (fauthz->file_watch < 0) {
+        goto cleanup;
+    }
+
+ cleanup:
+    g_free(file);
+    g_free(dir);
+}
+
+
+static void
+qauthz_list_file_prop_set_filename(Object *obj,
+                                   const char *value,
+                                   Error **errp G_GNUC_UNUSED)
+{
+    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
+
+    g_free(fauthz->filename);
+    fauthz->filename = g_strdup(value);
+}
+
+
+static char *
+qauthz_list_file_prop_get_filename(Object *obj,
+                                   Error **errp G_GNUC_UNUSED)
+{
+    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
+
+    return g_strdup(fauthz->filename);
+}
+
+
+static void
+qauthz_list_file_prop_set_refresh(Object *obj,
+                                  bool value,
+                                  Error **errp G_GNUC_UNUSED)
+{
+    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
+
+    fauthz->refresh = value;
+}
+
+
+static bool
+qauthz_list_file_prop_get_refresh(Object *obj,
+                                  Error **errp G_GNUC_UNUSED)
+{
+    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
+
+    return fauthz->refresh;
+}
+
+
+static void
+qauthz_list_file_finalize(Object *obj)
+{
+    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
+
+    object_unref(OBJECT(fauthz->list));
+    g_free(fauthz->filename);
+    qemu_file_monitor_free(fauthz->file_monitor);
+}
+
+
+static void
+qauthz_list_file_class_init(ObjectClass *oc, void *data)
+{
+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+    QAuthZClass *authz = QAUTHZ_CLASS(oc);
+
+    ucc->complete = qauthz_list_file_complete;
+
+    object_class_property_add_str(oc, "filename",
+                                  qauthz_list_file_prop_get_filename,
+                                  qauthz_list_file_prop_set_filename,
+                                  NULL);
+    object_class_property_add_bool(oc, "refresh",
+                                   qauthz_list_file_prop_get_refresh,
+                                   qauthz_list_file_prop_set_refresh,
+                                   NULL);
+
+    authz->is_allowed = qauthz_list_file_is_allowed;
+}
+
+
+static void
+qauthz_list_file_init(Object *obj)
+{
+    QAuthZListFile *authz = QAUTHZ_LIST_FILE(obj);
+
+    authz->file_watch = -1;
+#ifdef CONFIG_INOTIFY1
+    authz->refresh = TRUE;
+#endif
+}
+
+
+QAuthZListFile *qauthz_list_file_new(const char *id,
+                                     const char *filename,
+                                     bool refresh,
+                                     Error **errp)
+{
+    return QAUTHZ_LIST_FILE(
+        object_new_with_props(TYPE_QAUTHZ_LIST_FILE,
+                              object_get_objects_root(),
+                              id, errp,
+                              "filename", filename,
+                              "refresh", refresh ? "yes" : "no",
+                              NULL));
+}
+
+
+static const TypeInfo qauthz_list_file_info = {
+    .parent = TYPE_QAUTHZ,
+    .name = TYPE_QAUTHZ_LIST_FILE,
+    .instance_init = qauthz_list_file_init,
+    .instance_size = sizeof(QAuthZListFile),
+    .instance_finalize = qauthz_list_file_finalize,
+    .class_size = sizeof(QAuthZListFileClass),
+    .class_init = qauthz_list_file_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
+
+static void
+qauthz_list_file_register_types(void)
+{
+    type_register_static(&qauthz_list_file_info);
+}
+
+
+type_init(qauthz_list_file_register_types);
diff --git a/authz/pamacct.c b/authz/pamacct.c
new file mode 100644
index 0000000000..5038358cdc
--- /dev/null
+++ b/authz/pamacct.c
@@ -0,0 +1,148 @@
+/*
+ * QEMU PAM authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/pamacct.h"
+#include "authz/trace.h"
+#include "qom/object_interfaces.h"
+
+#include <security/pam_appl.h>
+
+
+static bool qauthz_pam_is_allowed(QAuthZ *authz,
+                                  const char *identity,
+                                  Error **errp)
+{
+    QAuthZPAM *pauthz = QAUTHZ_PAM(authz);
+    const struct pam_conv pam_conversation = { 0 };
+    pam_handle_t *pamh = NULL;
+    int ret;
+
+    trace_qauthz_pam_check(authz, identity, pauthz->service);
+    ret = pam_start(pauthz->service,
+                    identity,
+                    &pam_conversation,
+                    &pamh);
+    if (ret != PAM_SUCCESS) {
+        error_setg(errp, "Unable to start PAM transaction: %s",
+                   pam_strerror(NULL, ret));
+        return false;
+    }
+
+    ret = pam_acct_mgmt(pamh, PAM_SILENT);
+    pam_end(pamh, ret);
+    if (ret != PAM_SUCCESS) {
+        error_setg(errp, "Unable to authorize user '%s': %s",
+                   identity, pam_strerror(pamh, ret));
+        return false;
+    }
+
+    return true;
+}
+
+
+static void
+qauthz_pam_prop_set_service(Object *obj,
+                            const char *service,
+                            Error **errp G_GNUC_UNUSED)
+{
+    QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
+
+    g_free(pauthz->service);
+    pauthz->service = g_strdup(service);
+}
+
+
+static char *
+qauthz_pam_prop_get_service(Object *obj,
+                            Error **errp G_GNUC_UNUSED)
+{
+    QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
+
+    return g_strdup(pauthz->service);
+}
+
+
+static void
+qauthz_pam_complete(UserCreatable *uc, Error **errp)
+{
+}
+
+
+static void
+qauthz_pam_finalize(Object *obj)
+{
+    QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
+
+    g_free(pauthz->service);
+}
+
+
+static void
+qauthz_pam_class_init(ObjectClass *oc, void *data)
+{
+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+    QAuthZClass *authz = QAUTHZ_CLASS(oc);
+
+    ucc->complete = qauthz_pam_complete;
+    authz->is_allowed = qauthz_pam_is_allowed;
+
+    object_class_property_add_str(oc, "service",
+                                  qauthz_pam_prop_get_service,
+                                  qauthz_pam_prop_set_service,
+                                  NULL);
+}
+
+
+QAuthZPAM *qauthz_pam_new(const char *id,
+                          const char *service,
+                          Error **errp)
+{
+    return QAUTHZ_PAM(
+        object_new_with_props(TYPE_QAUTHZ_PAM,
+                              object_get_objects_root(),
+                              id, errp,
+                              "service", service,
+                              NULL));
+}
+
+
+static const TypeInfo qauthz_pam_info = {
+    .parent = TYPE_QAUTHZ,
+    .name = TYPE_QAUTHZ_PAM,
+    .instance_size = sizeof(QAuthZPAM),
+    .instance_finalize = qauthz_pam_finalize,
+    .class_size = sizeof(QAuthZPAMClass),
+    .class_init = qauthz_pam_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
+
+static void
+qauthz_pam_register_types(void)
+{
+    type_register_static(&qauthz_pam_info);
+}
+
+
+type_init(qauthz_pam_register_types);
diff --git a/authz/simple.c b/authz/simple.c
new file mode 100644
index 0000000000..8ab718803e
--- /dev/null
+++ b/authz/simple.c
@@ -0,0 +1,115 @@
+/*
+ * QEMU simple authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/simple.h"
+#include "authz/trace.h"
+#include "qom/object_interfaces.h"
+
+static bool qauthz_simple_is_allowed(QAuthZ *authz,
+                                     const char *identity,
+                                     Error **errp)
+{
+    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(authz);
+
+    trace_qauthz_simple_is_allowed(authz, sauthz->identity, identity);
+    return g_str_equal(identity, sauthz->identity);
+}
+
+static void
+qauthz_simple_prop_set_identity(Object *obj,
+                                const char *value,
+                                Error **errp G_GNUC_UNUSED)
+{
+    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+
+    g_free(sauthz->identity);
+    sauthz->identity = g_strdup(value);
+}
+
+
+static char *
+qauthz_simple_prop_get_identity(Object *obj,
+                                Error **errp G_GNUC_UNUSED)
+{
+    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+
+    return g_strdup(sauthz->identity);
+}
+
+
+static void
+qauthz_simple_finalize(Object *obj)
+{
+    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+
+    g_free(sauthz->identity);
+}
+
+
+static void
+qauthz_simple_class_init(ObjectClass *oc, void *data)
+{
+    QAuthZClass *authz = QAUTHZ_CLASS(oc);
+
+    authz->is_allowed = qauthz_simple_is_allowed;
+
+    object_class_property_add_str(oc, "identity",
+                                  qauthz_simple_prop_get_identity,
+                                  qauthz_simple_prop_set_identity,
+                                  NULL);
+}
+
+
+QAuthZSimple *qauthz_simple_new(const char *id,
+                                const char *identity,
+                                Error **errp)
+{
+    return QAUTHZ_SIMPLE(
+        object_new_with_props(TYPE_QAUTHZ_SIMPLE,
+                              object_get_objects_root(),
+                              id, errp,
+                              "identity", identity,
+                              NULL));
+}
+
+
+static const TypeInfo qauthz_simple_info = {
+    .parent = TYPE_QAUTHZ,
+    .name = TYPE_QAUTHZ_SIMPLE,
+    .instance_size = sizeof(QAuthZSimple),
+    .instance_finalize = qauthz_simple_finalize,
+    .class_size = sizeof(QAuthZSimpleClass),
+    .class_init = qauthz_simple_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
+
+static void
+qauthz_simple_register_types(void)
+{
+    type_register_static(&qauthz_simple_info);
+}
+
+
+type_init(qauthz_simple_register_types);
diff --git a/authz/trace-events b/authz/trace-events
new file mode 100644
index 0000000000..72c411927d
--- /dev/null
+++ b/authz/trace-events
@@ -0,0 +1,18 @@
+# See docs/devel/tracing.txt for syntax documentation.
+
+# authz/base.c
+qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d"
+
+# auth/simple.c
+qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char *gotidentity) "AuthZ simple %p check want identity=%s got identity=%s"
+
+# auth/list.c
+qauthz_list_check_rule(void *authz, const char *identity, const char *rule, int format, int policy) "AuthZ list %p check rule=%s identity=%s format=%d policy=%d"
+qauthz_list_default_policy(void *authz, const char *identity, int policy) "AuthZ list %p default identity=%s policy=%d"
+
+# auth/listfile.c
+qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p load filename=%s"
+qauthz_list_file_refresh(void *authz, const char *filename, int success) "AuthZ file %p load filename=%s success=%d"
+
+# auth/pam.c
+qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s"
diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index 717fcbdae4..ff619d31b4 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -9,10 +9,9 @@ common-obj-$(CONFIG_POSIX) += hostmem-file.o
 common-obj-y += cryptodev.o
 common-obj-y += cryptodev-builtin.o
 
-ifeq ($(CONFIG_VIRTIO),y)
+ifeq ($(CONFIG_VIRTIO_CRYPTO),y)
 common-obj-y += cryptodev-vhost.o
-common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \
-    cryptodev-vhost-user.o
+common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o
 endif
 
 common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
diff --git a/block.c b/block.c
index 4ad0e90d7e..35e78e2172 100644
--- a/block.c
+++ b/block.c
@@ -152,53 +152,53 @@ int path_is_absolute(const char *path)
 #endif
 }
 
-/* if filename is absolute, just copy it to dest. Otherwise, build a
+/* if filename is absolute, just return its duplicate. Otherwise, build a
    path to it by considering it is relative to base_path. URL are
    supported. */
-void path_combine(char *dest, int dest_size,
-                  const char *base_path,
-                  const char *filename)
+char *path_combine(const char *base_path, const char *filename)
 {
+    const char *protocol_stripped = NULL;
     const char *p, *p1;
+    char *result;
     int len;
 
-    if (dest_size <= 0)
-        return;
     if (path_is_absolute(filename)) {
-        pstrcpy(dest, dest_size, filename);
-    } else {
-        const char *protocol_stripped = NULL;
+        return g_strdup(filename);
+    }
 
-        if (path_has_protocol(base_path)) {
-            protocol_stripped = strchr(base_path, ':');
-            if (protocol_stripped) {
-                protocol_stripped++;
-            }
+    if (path_has_protocol(base_path)) {
+        protocol_stripped = strchr(base_path, ':');
+        if (protocol_stripped) {
+            protocol_stripped++;
         }
-        p = protocol_stripped ?: base_path;
+    }
+    p = protocol_stripped ?: base_path;
 
-        p1 = strrchr(base_path, '/');
+    p1 = strrchr(base_path, '/');
 #ifdef _WIN32
-        {
-            const char *p2;
-            p2 = strrchr(base_path, '\\');
-            if (!p1 || p2 > p1)
-                p1 = p2;
+    {
+        const char *p2;
+        p2 = strrchr(base_path, '\\');
+        if (!p1 || p2 > p1) {
+            p1 = p2;
         }
+    }
 #endif
-        if (p1)
-            p1++;
-        else
-            p1 = base_path;
-        if (p1 > p)
-            p = p1;
-        len = p - base_path;
-        if (len > dest_size - 1)
-            len = dest_size - 1;
-        memcpy(dest, base_path, len);
-        dest[len] = '\0';
-        pstrcat(dest, dest_size, filename);
+    if (p1) {
+        p1++;
+    } else {
+        p1 = base_path;
     }
+    if (p1 > p) {
+        p = p1;
+    }
+    len = p - base_path;
+
+    result = g_malloc(len + strlen(filename) + 1);
+    memcpy(result, base_path, len);
+    strcpy(result + len, filename);
+
+    return result;
 }
 
 /*
@@ -303,30 +303,61 @@ fail:
     return -EACCES;
 }
 
-void bdrv_get_full_backing_filename_from_filename(const char *backed,
-                                                  const char *backing,
-                                                  char *dest, size_t sz,
-                                                  Error **errp)
+/*
+ * If @backing is empty, this function returns NULL without setting
+ * @errp.  In all other cases, NULL will only be returned with @errp
+ * set.
+ *
+ * Therefore, a return value of NULL without @errp set means that
+ * there is no backing file; if @errp is set, there is one but its
+ * absolute filename cannot be generated.
+ */
+char *bdrv_get_full_backing_filename_from_filename(const char *backed,
+                                                   const char *backing,
+                                                   Error **errp)
 {
-    if (backing[0] == '\0' || path_has_protocol(backing) ||
-        path_is_absolute(backing))
-    {
-        pstrcpy(dest, sz, backing);
+    if (backing[0] == '\0') {
+        return NULL;
+    } else if (path_has_protocol(backing) || path_is_absolute(backing)) {
+        return g_strdup(backing);
     } else if (backed[0] == '\0' || strstart(backed, "json:", NULL)) {
         error_setg(errp, "Cannot use relative backing file names for '%s'",
                    backed);
+        return NULL;
     } else {
-        path_combine(dest, sz, backed, backing);
+        return path_combine(backed, backing);
     }
 }
 
-void bdrv_get_full_backing_filename(BlockDriverState *bs, char *dest, size_t sz,
-                                    Error **errp)
+/*
+ * If @filename is empty or NULL, this function returns NULL without
+ * setting @errp.  In all other cases, NULL will only be returned with
+ * @errp set.
+ */
+static char *bdrv_make_absolute_filename(BlockDriverState *relative_to,
+                                         const char *filename, Error **errp)
 {
-    char *backed = bs->exact_filename[0] ? bs->exact_filename : bs->filename;
+    char *dir, *full_name;
 
-    bdrv_get_full_backing_filename_from_filename(backed, bs->backing_file,
-                                                 dest, sz, errp);
+    if (!filename || filename[0] == '\0') {
+        return NULL;
+    } else if (path_has_protocol(filename) || path_is_absolute(filename)) {
+        return g_strdup(filename);
+    }
+
+    dir = bdrv_dirname(relative_to, errp);
+    if (!dir) {
+        return NULL;
+    }
+
+    full_name = g_strconcat(dir, filename, NULL);
+    g_free(dir);
+    return full_name;
+}
+
+char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp)
+{
+    return bdrv_make_absolute_filename(bs, bs->backing_file, errp);
 }
 
 void bdrv_register(BlockDriver *bdrv)
@@ -1004,6 +1035,8 @@ static void bdrv_backing_attach(BdrvChild *c)
                "node is used as backing hd of '%s'",
                bdrv_get_device_or_node_name(parent));
 
+    bdrv_refresh_filename(backing_hd);
+
     parent->open_flags &= ~BDRV_O_NO_BACKING;
     pstrcpy(parent->backing_file, sizeof(parent->backing_file),
             backing_hd->filename);
@@ -1413,6 +1446,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
     }
 
     if (file != NULL) {
+        bdrv_refresh_filename(blk_bs(file));
         filename = blk_bs(file)->filename;
     } else {
         /*
@@ -1954,13 +1988,32 @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
     ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp);
     g_slist_free(ignore_children);
 
-    return ret;
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (!c->has_backup_perm) {
+        c->has_backup_perm = true;
+        c->backup_perm = c->perm;
+        c->backup_shared_perm = c->shared_perm;
+    }
+    /*
+     * Note: it's OK if c->has_backup_perm was already set, as we can find the
+     * same child twice during check_perm procedure
+     */
+
+    c->perm = perm;
+    c->shared_perm = shared;
+
+    return 0;
 }
 
 static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared)
 {
     uint64_t cumulative_perms, cumulative_shared_perms;
 
+    c->has_backup_perm = false;
+
     c->perm = perm;
     c->shared_perm = shared;
 
@@ -1971,6 +2024,12 @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared)
 
 static void bdrv_child_abort_perm_update(BdrvChild *c)
 {
+    if (c->has_backup_perm) {
+        c->perm = c->backup_perm;
+        c->shared_perm = c->backup_shared_perm;
+        c->has_backup_perm = false;
+    }
+
     bdrv_abort_perm_update(c->bs);
 }
 
@@ -2309,8 +2368,6 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
         bdrv_unref(backing_hd);
     }
 
-    bdrv_refresh_filename(bs);
-
 out:
     bdrv_refresh_limits(bs, NULL);
 }
@@ -2328,10 +2385,11 @@ out:
 int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
                            const char *bdref_key, Error **errp)
 {
-    char *backing_filename = g_malloc0(PATH_MAX);
+    char *backing_filename = NULL;
     char *bdref_key_dot;
     const char *reference = NULL;
     int ret = 0;
+    bool implicit_backing = false;
     BlockDriverState *backing_hd;
     QDict *options;
     QDict *tmp_parent_options = NULL;
@@ -2362,13 +2420,22 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
      */
     reference = qdict_get_try_str(parent_options, bdref_key);
     if (reference || qdict_haskey(options, "file.filename")) {
-        backing_filename[0] = '\0';
+        /* keep backing_filename NULL */
     } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) {
         qobject_unref(options);
         goto free_exit;
     } else {
-        bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX,
-                                       &local_err);
+        if (qdict_size(options) == 0) {
+            /* If the user specifies options that do not modify the
+             * backing file's behavior, we might still consider it the
+             * implicit backing file.  But it's easier this way, and
+             * just specifying some of the backing BDS's options is
+             * only possible with -drive anyway (otherwise the QAPI
+             * schema forces the user to specify everything). */
+            implicit_backing = !strcmp(bs->auto_backing_file, bs->backing_file);
+        }
+
+        backing_filename = bdrv_get_full_backing_filename(bs, &local_err);
         if (local_err) {
             ret = -EINVAL;
             error_propagate(errp, local_err);
@@ -2389,9 +2456,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
         qdict_put_str(options, "driver", bs->backing_format);
     }
 
-    backing_hd = bdrv_open_inherit(*backing_filename ? backing_filename : NULL,
-                                   reference, options, 0, bs, &child_backing,
-                                   errp);
+    backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs,
+                                   &child_backing, errp);
     if (!backing_hd) {
         bs->open_flags |= BDRV_O_NO_BACKING;
         error_prepend(errp, "Could not open backing file: ");
@@ -2400,6 +2466,12 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
     }
     bdrv_set_aio_context(backing_hd, bdrv_get_aio_context(bs));
 
+    if (implicit_backing) {
+        bdrv_refresh_filename(backing_hd);
+        pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file),
+                backing_hd->filename);
+    }
+
     /* Hook up the backing file link; drop our reference, bs owns the
      * backing_hd reference now */
     bdrv_set_backing_hd(bs, backing_hd, &local_err);
@@ -2839,8 +2911,6 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
         g_free(child_key_dot);
     }
 
-    bdrv_refresh_filename(bs);
-
     /* Check if any unknown options were used */
     if (qdict_size(options) != 0) {
         const QDictEntry *entry = qdict_first(options);
@@ -3285,6 +3355,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
             if (local_err != NULL) {
                 error_propagate(errp, local_err);
             } else {
+                bdrv_refresh_filename(reopen_state->bs);
                 error_setg(errp, "failed while preparing to reopen image '%s'",
                            reopen_state->bs->filename);
             }
@@ -3542,7 +3613,9 @@ void bdrv_close_all(void)
 
 static bool should_update_child(BdrvChild *c, BlockDriverState *to)
 {
-    BdrvChild *to_c;
+    GQueue *queue;
+    GHashTable *found;
+    bool ret;
 
     if (c->role->stay_at_node) {
         return false;
@@ -3578,14 +3651,43 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
      * if A is a child of B, that means we cannot replace A by B there
      * because that would create a loop.  Silently detaching A from B
      * is also not really an option.  So overall just leaving A in
-     * place there is the most sensible choice. */
-    QLIST_FOREACH(to_c, &to->children, next) {
-        if (to_c == c) {
-            return false;
+     * place there is the most sensible choice.
+     *
+     * We would also create a loop in any cases where @c is only
+     * indirectly referenced by @to. Prevent this by returning false
+     * if @c is found (by breadth-first search) anywhere in the whole
+     * subtree of @to.
+     */
+
+    ret = true;
+    found = g_hash_table_new(NULL, NULL);
+    g_hash_table_add(found, to);
+    queue = g_queue_new();
+    g_queue_push_tail(queue, to);
+
+    while (!g_queue_is_empty(queue)) {
+        BlockDriverState *v = g_queue_pop_head(queue);
+        BdrvChild *c2;
+
+        QLIST_FOREACH(c2, &v->children, next) {
+            if (c2 == c) {
+                ret = false;
+                break;
+            }
+
+            if (g_hash_table_contains(found, c2->bs)) {
+                continue;
+            }
+
+            g_queue_push_tail(queue, c2->bs);
+            g_hash_table_add(found, c2->bs);
         }
     }
 
-    return true;
+    g_queue_free(queue);
+    g_hash_table_destroy(found);
+
+    return ret;
 }
 
 void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
@@ -3789,6 +3891,8 @@ int bdrv_change_backing_file(BlockDriverState *bs,
     if (ret == 0) {
         pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
         pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");
+        pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file),
+                backing_file ?: "");
     }
     return ret;
 }
@@ -3881,7 +3985,10 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
     /* success - we can delete the intermediate states, and link top->base */
     /* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once
      * we've figured out how they should work. */
-    backing_file_str = backing_file_str ? backing_file_str : base->filename;
+    if (!backing_file_str) {
+        bdrv_refresh_filename(base);
+        backing_file_str = base->filename;
+    }
 
     QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) {
         /* Check whether we are allowed to switch c from top to base */
@@ -4429,16 +4536,6 @@ bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs)
     return bs->supported_zero_flags & BDRV_REQ_MAY_UNMAP;
 }
 
-const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
-{
-    if (bs->backing && bs->backing->bs->encrypted)
-        return bs->backing_file;
-    else if (bs->encrypted)
-        return bs->filename;
-    else
-        return NULL;
-}
-
 void bdrv_get_backing_filename(BlockDriverState *bs,
                                char *filename, int filename_size)
 {
@@ -4547,7 +4644,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
     int is_protocol = 0;
     BlockDriverState *curr_bs = NULL;
     BlockDriverState *retval = NULL;
-    Error *local_error = NULL;
 
     if (!bs || !bs->drv || !backing_file) {
         return NULL;
@@ -4555,7 +4651,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
 
     filename_full     = g_malloc(PATH_MAX);
     backing_file_full = g_malloc(PATH_MAX);
-    filename_tmp      = g_malloc(PATH_MAX);
 
     is_protocol = path_has_protocol(backing_file);
 
@@ -4564,41 +4659,43 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
         /* If either of the filename paths is actually a protocol, then
          * compare unmodified paths; otherwise make paths relative */
         if (is_protocol || path_has_protocol(curr_bs->backing_file)) {
+            char *backing_file_full_ret;
+
             if (strcmp(backing_file, curr_bs->backing_file) == 0) {
                 retval = curr_bs->backing->bs;
                 break;
             }
             /* Also check against the full backing filename for the image */
-            bdrv_get_full_backing_filename(curr_bs, backing_file_full, PATH_MAX,
-                                           &local_error);
-            if (local_error == NULL) {
-                if (strcmp(backing_file, backing_file_full) == 0) {
+            backing_file_full_ret = bdrv_get_full_backing_filename(curr_bs,
+                                                                   NULL);
+            if (backing_file_full_ret) {
+                bool equal = strcmp(backing_file, backing_file_full_ret) == 0;
+                g_free(backing_file_full_ret);
+                if (equal) {
                     retval = curr_bs->backing->bs;
                     break;
                 }
-            } else {
-                error_free(local_error);
-                local_error = NULL;
             }
         } else {
             /* If not an absolute filename path, make it relative to the current
              * image's filename path */
-            path_combine(filename_tmp, PATH_MAX, curr_bs->filename,
-                         backing_file);
-
-            /* We are going to compare absolute pathnames */
-            if (!realpath(filename_tmp, filename_full)) {
+            filename_tmp = bdrv_make_absolute_filename(curr_bs, backing_file,
+                                                       NULL);
+            /* We are going to compare canonicalized absolute pathnames */
+            if (!filename_tmp || !realpath(filename_tmp, filename_full)) {
+                g_free(filename_tmp);
                 continue;
             }
+            g_free(filename_tmp);
 
             /* We need to make sure the backing filename we are comparing against
              * is relative to the current image filename (or absolute) */
-            path_combine(filename_tmp, PATH_MAX, curr_bs->filename,
-                         curr_bs->backing_file);
-
-            if (!realpath(filename_tmp, backing_file_full)) {
+            filename_tmp = bdrv_get_full_backing_filename(curr_bs, NULL);
+            if (!filename_tmp || !realpath(filename_tmp, backing_file_full)) {
+                g_free(filename_tmp);
                 continue;
             }
+            g_free(filename_tmp);
 
             if (strcmp(backing_file_full, filename_full) == 0) {
                 retval = curr_bs->backing->bs;
@@ -4609,7 +4706,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
 
     g_free(filename_full);
     g_free(backing_file_full);
-    g_free(filename_tmp);
     return retval;
 }
 
@@ -5096,17 +5192,17 @@ void bdrv_img_create(const char *filename, const char *fmt,
     size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, img_size);
     if (backing_file && !(flags & BDRV_O_NO_BACKING)) {
         BlockDriverState *bs;
-        char *full_backing = g_new0(char, PATH_MAX);
+        char *full_backing;
         int back_flags;
         QDict *backing_options = NULL;
 
-        bdrv_get_full_backing_filename_from_filename(filename, backing_file,
-                                                     full_backing, PATH_MAX,
-                                                     &local_err);
+        full_backing =
+            bdrv_get_full_backing_filename_from_filename(filename, backing_file,
+                                                         &local_err);
         if (local_err) {
-            g_free(full_backing);
             goto out;
         }
+        assert(full_backing);
 
         /* backing files always opened read-only */
         back_flags = flags;
@@ -5227,6 +5323,9 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
         bdrv_detach_aio_context(child->bs);
     }
 
+    if (bs->quiesce_counter) {
+        aio_enable_external(bs->aio_context);
+    }
     bs->aio_context = NULL;
 }
 
@@ -5240,6 +5339,10 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
         return;
     }
 
+    if (bs->quiesce_counter) {
+        aio_disable_external(new_context);
+    }
+
     bs->aio_context = new_context;
 
     QLIST_FOREACH(child, &bs->children, next) {
@@ -5261,18 +5364,16 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
     bs->walking_aio_notifiers = false;
 }
 
+/* The caller must own the AioContext lock for the old AioContext of bs, but it
+ * must not own the AioContext lock for new_context (unless new_context is
+ * the same as the current context of bs). */
 void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
 {
-    AioContext *ctx = bdrv_get_aio_context(bs);
-
-    aio_disable_external(ctx);
-    bdrv_parent_drained_begin(bs, NULL, false);
-    bdrv_drain(bs); /* ensure there are no in-flight requests */
-
-    while (aio_poll(ctx, false)) {
-        /* wait for all bottom halves to execute */
+    if (bdrv_get_aio_context(bs) == new_context) {
+        return;
     }
 
+    bdrv_drained_begin(bs);
     bdrv_detach_aio_context(bs);
 
     /* This function executes in the old AioContext so acquire the new one in
@@ -5280,8 +5381,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
      */
     aio_context_acquire(new_context);
     bdrv_attach_aio_context(bs, new_context);
-    bdrv_parent_drained_end(bs, NULL, false);
-    aio_enable_external(ctx);
+    bdrv_drained_end(bs);
     aio_context_release(new_context);
 }
 
@@ -5435,33 +5535,113 @@ out:
     return to_replace_bs;
 }
 
-static bool append_open_options(QDict *d, BlockDriverState *bs)
+/**
+ * Iterates through the list of runtime option keys that are said to
+ * be "strong" for a BDS.  An option is called "strong" if it changes
+ * a BDS's data.  For example, the null block driver's "size" and
+ * "read-zeroes" options are strong, but its "latency-ns" option is
+ * not.
+ *
+ * If a key returned by this function ends with a dot, all options
+ * starting with that prefix are strong.
+ */
+static const char *const *strong_options(BlockDriverState *bs,
+                                         const char *const *curopt)
+{
+    static const char *const global_options[] = {
+        "driver", "filename", NULL
+    };
+
+    if (!curopt) {
+        return &global_options[0];
+    }
+
+    curopt++;
+    if (curopt == &global_options[ARRAY_SIZE(global_options) - 1] && bs->drv) {
+        curopt = bs->drv->strong_runtime_opts;
+    }
+
+    return (curopt && *curopt) ? curopt : NULL;
+}
+
+/**
+ * Copies all strong runtime options from bs->options to the given
+ * QDict.  The set of strong option keys is determined by invoking
+ * strong_options().
+ *
+ * Returns true iff any strong option was present in bs->options (and
+ * thus copied to the target QDict) with the exception of "filename"
+ * and "driver".  The caller is expected to use this value to decide
+ * whether the existence of strong options prevents the generation of
+ * a plain filename.
+ */
+static bool append_strong_runtime_options(QDict *d, BlockDriverState *bs)
 {
-    const QDictEntry *entry;
-    QemuOptDesc *desc;
     bool found_any = false;
+    const char *const *option_name = NULL;
 
-    for (entry = qdict_first(bs->options); entry;
-         entry = qdict_next(bs->options, entry))
-    {
-        /* Exclude all non-driver-specific options */
-        for (desc = bdrv_runtime_opts.desc; desc->name; desc++) {
-            if (!strcmp(qdict_entry_key(entry), desc->name)) {
-                break;
+    if (!bs->drv) {
+        return false;
+    }
+
+    while ((option_name = strong_options(bs, option_name))) {
+        bool option_given = false;
+
+        assert(strlen(*option_name) > 0);
+        if ((*option_name)[strlen(*option_name) - 1] != '.') {
+            QObject *entry = qdict_get(bs->options, *option_name);
+            if (!entry) {
+                continue;
+            }
+
+            qdict_put_obj(d, *option_name, qobject_ref(entry));
+            option_given = true;
+        } else {
+            const QDictEntry *entry;
+            for (entry = qdict_first(bs->options); entry;
+                 entry = qdict_next(bs->options, entry))
+            {
+                if (strstart(qdict_entry_key(entry), *option_name, NULL)) {
+                    qdict_put_obj(d, qdict_entry_key(entry),
+                                  qobject_ref(qdict_entry_value(entry)));
+                    option_given = true;
+                }
             }
         }
-        if (desc->name) {
-            continue;
+
+        /* While "driver" and "filename" need to be included in a JSON filename,
+         * their existence does not prohibit generation of a plain filename. */
+        if (!found_any && option_given &&
+            strcmp(*option_name, "driver") && strcmp(*option_name, "filename"))
+        {
+            found_any = true;
         }
+    }
 
-        qdict_put_obj(d, qdict_entry_key(entry),
-                      qobject_ref(qdict_entry_value(entry)));
-        found_any = true;
+    if (!qdict_haskey(d, "driver")) {
+        /* Drivers created with bdrv_new_open_driver() may not have a
+         * @driver option.  Add it here. */
+        qdict_put_str(d, "driver", bs->drv->format_name);
     }
 
     return found_any;
 }
 
+/* Note: This function may return false positives; it may return true
+ * even if opening the backing file specified by bs's image header
+ * would result in exactly bs->backing. */
+static bool bdrv_backing_overridden(BlockDriverState *bs)
+{
+    if (bs->backing) {
+        return strcmp(bs->auto_backing_file,
+                      bs->backing->bs->filename);
+    } else {
+        /* No backing BDS, so if the image header reports any backing
+         * file, it must have been suppressed */
+        return bs->auto_backing_file[0] != '\0';
+    }
+}
+
 /* Updates the following BDS fields:
  *  - exact_filename: A filename which may be used for opening a block device
  *                    which (mostly) equals the given BDS (even without any
@@ -5477,92 +5657,108 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
 void bdrv_refresh_filename(BlockDriverState *bs)
 {
     BlockDriver *drv = bs->drv;
+    BdrvChild *child;
     QDict *opts;
+    bool backing_overridden;
+    bool generate_json_filename; /* Whether our default implementation should
+                                    fill exact_filename (false) or not (true) */
 
     if (!drv) {
         return;
     }
 
-    /* This BDS's file name will most probably depend on its file's name, so
-     * refresh that first */
-    if (bs->file) {
-        bdrv_refresh_filename(bs->file->bs);
+    /* This BDS's file name may depend on any of its children's file names, so
+     * refresh those first */
+    QLIST_FOREACH(child, &bs->children, next) {
+        bdrv_refresh_filename(child->bs);
+    }
+
+    if (bs->implicit) {
+        /* For implicit nodes, just copy everything from the single child */
+        child = QLIST_FIRST(&bs->children);
+        assert(QLIST_NEXT(child, next) == NULL);
+
+        pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
+                child->bs->exact_filename);
+        pstrcpy(bs->filename, sizeof(bs->filename), child->bs->filename);
+
+        bs->full_open_options = qobject_ref(child->bs->full_open_options);
+
+        return;
+    }
+
+    backing_overridden = bdrv_backing_overridden(bs);
+
+    if (bs->open_flags & BDRV_O_NO_IO) {
+        /* Without I/O, the backing file does not change anything.
+         * Therefore, in such a case (primarily qemu-img), we can
+         * pretend the backing file has not been overridden even if
+         * it technically has been. */
+        backing_overridden = false;
     }
 
+    /* Gather the options QDict */
+    opts = qdict_new();
+    generate_json_filename = append_strong_runtime_options(opts, bs);
+    generate_json_filename |= backing_overridden;
+
+    if (drv->bdrv_gather_child_options) {
+        /* Some block drivers may not want to present all of their children's
+         * options, or name them differently from BdrvChild.name */
+        drv->bdrv_gather_child_options(bs, opts, backing_overridden);
+    } else {
+        QLIST_FOREACH(child, &bs->children, next) {
+            if (child->role == &child_backing && !backing_overridden) {
+                /* We can skip the backing BDS if it has not been overridden */
+                continue;
+            }
+
+            qdict_put(opts, child->name,
+                      qobject_ref(child->bs->full_open_options));
+        }
+
+        if (backing_overridden && !bs->backing) {
+            /* Force no backing file */
+            qdict_put_null(opts, "backing");
+        }
+    }
+
+    qobject_unref(bs->full_open_options);
+    bs->full_open_options = opts;
+
     if (drv->bdrv_refresh_filename) {
         /* Obsolete information is of no use here, so drop the old file name
          * information before refreshing it */
         bs->exact_filename[0] = '\0';
-        if (bs->full_open_options) {
-            qobject_unref(bs->full_open_options);
-            bs->full_open_options = NULL;
-        }
 
-        opts = qdict_new();
-        append_open_options(opts, bs);
-        drv->bdrv_refresh_filename(bs, opts);
-        qobject_unref(opts);
+        drv->bdrv_refresh_filename(bs);
     } else if (bs->file) {
         /* Try to reconstruct valid information from the underlying file */
-        bool has_open_options;
 
         bs->exact_filename[0] = '\0';
-        if (bs->full_open_options) {
-            qobject_unref(bs->full_open_options);
-            bs->full_open_options = NULL;
-        }
-
-        opts = qdict_new();
-        has_open_options = append_open_options(opts, bs);
 
-        /* If no specific options have been given for this BDS, the filename of
-         * the underlying file should suffice for this one as well */
-        if (bs->file->bs->exact_filename[0] && !has_open_options) {
+        /*
+         * We can use the underlying file's filename if:
+         * - it has a filename,
+         * - the file is a protocol BDS, and
+         * - opening that file (as this BDS's format) will automatically create
+         *   the BDS tree we have right now, that is:
+         *   - the user did not significantly change this BDS's behavior with
+         *     some explicit (strong) options
+         *   - no non-file child of this BDS has been overridden by the user
+         *   Both of these conditions are represented by generate_json_filename.
+         */
+        if (bs->file->bs->exact_filename[0] &&
+            bs->file->bs->drv->bdrv_file_open &&
+            !generate_json_filename)
+        {
             strcpy(bs->exact_filename, bs->file->bs->exact_filename);
         }
-        /* Reconstructing the full options QDict is simple for most format block
-         * drivers, as long as the full options are known for the underlying
-         * file BDS. The full options QDict of that file BDS should somehow
-         * contain a representation of the filename, therefore the following
-         * suffices without querying the (exact_)filename of this BDS. */
-        if (bs->file->bs->full_open_options) {
-            qdict_put_str(opts, "driver", drv->format_name);
-            qdict_put(opts, "file",
-                      qobject_ref(bs->file->bs->full_open_options));
-
-            bs->full_open_options = opts;
-        } else {
-            qobject_unref(opts);
-        }
-    } else if (!bs->full_open_options && qdict_size(bs->options)) {
-        /* There is no underlying file BDS (at least referenced by BDS.file),
-         * so the full options QDict should be equal to the options given
-         * specifically for this block device when it was opened (plus the
-         * driver specification).
-         * Because those options don't change, there is no need to update
-         * full_open_options when it's already set. */
-
-        opts = qdict_new();
-        append_open_options(opts, bs);
-        qdict_put_str(opts, "driver", drv->format_name);
-
-        if (bs->exact_filename[0]) {
-            /* This may not work for all block protocol drivers (some may
-             * require this filename to be parsed), but we have to find some
-             * default solution here, so just include it. If some block driver
-             * does not support pure options without any filename at all or
-             * needs some special format of the options QDict, it needs to
-             * implement the driver-specific bdrv_refresh_filename() function.
-             */
-            qdict_put_str(opts, "filename", bs->exact_filename);
-        }
-
-        bs->full_open_options = opts;
     }
 
     if (bs->exact_filename[0]) {
         pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename);
-    } else if (bs->full_open_options) {
+    } else {
         QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
         snprintf(bs->filename, sizeof(bs->filename), "json:%s",
                  qstring_get_str(json));
@@ -5570,6 +5766,33 @@ void bdrv_refresh_filename(BlockDriverState *bs)
     }
 }
 
+char *bdrv_dirname(BlockDriverState *bs, Error **errp)
+{
+    BlockDriver *drv = bs->drv;
+
+    if (!drv) {
+        error_setg(errp, "Node '%s' is ejected", bs->node_name);
+        return NULL;
+    }
+
+    if (drv->bdrv_dirname) {
+        return drv->bdrv_dirname(bs, errp);
+    }
+
+    if (bs->file) {
+        return bdrv_dirname(bs->file->bs, errp);
+    }
+
+    bdrv_refresh_filename(bs);
+    if (bs->exact_filename[0] != '\0') {
+        return path_combine(bs->exact_filename, "");
+    }
+
+    error_setg(errp, "Cannot generate a base directory for %s nodes",
+               drv->format_name);
+    return NULL;
+}
+
 /*
  * Hot add/remove a BDS's child. So the user can take a child offline when
  * it is broken and take a new child online
diff --git a/block/backup.c b/block/backup.c
index 435414e964..9988753249 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -107,7 +107,6 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
                                                       void **bounce_buffer)
 {
     int ret;
-    struct iovec iov;
     QEMUIOVector qiov;
     BlockBackend *blk = job->common.blk;
     int nbytes;
@@ -119,9 +118,7 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
     if (!*bounce_buffer) {
         *bounce_buffer = blk_blockalign(blk, job->cluster_size);
     }
-    iov.iov_base = *bounce_buffer;
-    iov.iov_len = nbytes;
-    qemu_iovec_init_external(&qiov, &iov, 1);
+    qemu_iovec_init_buf(&qiov, *bounce_buffer, nbytes);
 
     ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags);
     if (ret < 0) {
diff --git a/block/blkdebug.c b/block/blkdebug.c
index 0759452925..1ea835c2b9 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -811,51 +811,37 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
     return bdrv_getlength(bs->file->bs);
 }
 
-static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
+static void blkdebug_refresh_filename(BlockDriverState *bs)
 {
     BDRVBlkdebugState *s = bs->opaque;
-    QDict *opts;
     const QDictEntry *e;
-    bool force_json = false;
-
-    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
-        if (strcmp(qdict_entry_key(e), "config") &&
-            strcmp(qdict_entry_key(e), "x-image"))
-        {
-            force_json = true;
-            break;
-        }
-    }
+    int ret;
 
-    if (force_json && !bs->file->bs->full_open_options) {
-        /* The config file cannot be recreated, so creating a plain filename
-         * is impossible */
+    if (!bs->file->bs->exact_filename[0]) {
         return;
     }
 
-    if (!force_json && bs->file->bs->exact_filename[0]) {
-        int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
-                           "blkdebug:%s:%s", s->config_file ?: "",
-                           bs->file->bs->exact_filename);
-        if (ret >= sizeof(bs->exact_filename)) {
-            /* An overflow makes the filename unusable, so do not report any */
-            bs->exact_filename[0] = 0;
+    for (e = qdict_first(bs->full_open_options); e;
+         e = qdict_next(bs->full_open_options, e))
+    {
+        /* Real child options are under "image", but "x-image" may
+         * contain a filename */
+        if (strcmp(qdict_entry_key(e), "config") &&
+            strcmp(qdict_entry_key(e), "image") &&
+            strcmp(qdict_entry_key(e), "x-image") &&
+            strcmp(qdict_entry_key(e), "driver"))
+        {
+            return;
         }
     }
 
-    opts = qdict_new();
-    qdict_put_str(opts, "driver", "blkdebug");
-
-    qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options));
-
-    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
-        if (strcmp(qdict_entry_key(e), "x-image")) {
-            qdict_put_obj(opts, qdict_entry_key(e),
-                          qobject_ref(qdict_entry_value(e)));
-        }
+    ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
+                   "blkdebug:%s:%s",
+                   s->config_file ?: "", bs->file->bs->exact_filename);
+    if (ret >= sizeof(bs->exact_filename)) {
+        /* An overflow makes the filename unusable, so do not report any */
+        bs->exact_filename[0] = 0;
     }
-
-    bs->full_open_options = opts;
 }
 
 static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
@@ -888,6 +874,20 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
     return 0;
 }
 
+static const char *const blkdebug_strong_runtime_opts[] = {
+    "config",
+    "inject-error.",
+    "set-state.",
+    "align",
+    "max-transfer",
+    "opt-write-zero",
+    "max-write-zero",
+    "opt-discard",
+    "max-discard",
+
+    NULL
+};
+
 static BlockDriver bdrv_blkdebug = {
     .format_name            = "blkdebug",
     .protocol_name          = "blkdebug",
@@ -917,6 +917,8 @@ static BlockDriver bdrv_blkdebug = {
                                 = blkdebug_debug_remove_breakpoint,
     .bdrv_debug_resume          = blkdebug_debug_resume,
     .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
+
+    .strong_runtime_opts        = blkdebug_strong_runtime_opts,
 };
 
 static void bdrv_blkdebug_init(void)
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
index d2e01bdb1d..eb2b4901a5 100644
--- a/block/blklogwrites.c
+++ b/block/blklogwrites.c
@@ -280,30 +280,6 @@ static int64_t blk_log_writes_getlength(BlockDriverState *bs)
     return bdrv_getlength(bs->file->bs);
 }
 
-static void blk_log_writes_refresh_filename(BlockDriverState *bs,
-                                            QDict *options)
-{
-    BDRVBlkLogWritesState *s = bs->opaque;
-
-    /* bs->file->bs has already been refreshed */
-    bdrv_refresh_filename(s->log_file->bs);
-
-    if (bs->file->bs->full_open_options
-        && s->log_file->bs->full_open_options)
-    {
-        QDict *opts = qdict_new();
-        qdict_put_str(opts, "driver", "blklogwrites");
-
-        qobject_ref(bs->file->bs->full_open_options);
-        qdict_put(opts, "file", bs->file->bs->full_open_options);
-        qobject_ref(s->log_file->bs->full_open_options);
-        qdict_put(opts, "log", s->log_file->bs->full_open_options);
-        qdict_put_int(opts, "log-sector-size", s->sectorsize);
-
-        bs->full_open_options = opts;
-    }
-}
-
 static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
                                       const BdrvChildRole *role,
                                       BlockReopenQueue *ro_q,
@@ -520,6 +496,13 @@ blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
                                  LOG_DISCARD_FLAG, false);
 }
 
+static const char *const blk_log_writes_strong_runtime_opts[] = {
+    "log-append",
+    "log-sector-size",
+
+    NULL
+};
+
 static BlockDriver bdrv_blk_log_writes = {
     .format_name            = "blklogwrites",
     .instance_size          = sizeof(BDRVBlkLogWritesState),
@@ -527,7 +510,6 @@ static BlockDriver bdrv_blk_log_writes = {
     .bdrv_open              = blk_log_writes_open,
     .bdrv_close             = blk_log_writes_close,
     .bdrv_getlength         = blk_log_writes_getlength,
-    .bdrv_refresh_filename  = blk_log_writes_refresh_filename,
     .bdrv_child_perm        = blk_log_writes_child_perm,
     .bdrv_refresh_limits    = blk_log_writes_refresh_limits,
 
@@ -539,6 +521,7 @@ static BlockDriver bdrv_blk_log_writes = {
     .bdrv_co_block_status   = bdrv_co_block_status_from_file,
 
     .is_filter              = true,
+    .strong_runtime_opts    = blk_log_writes_strong_runtime_opts,
 };
 
 static void bdrv_blk_log_writes_init(void)
diff --git a/block/blkverify.c b/block/blkverify.c
index 89bf4386e3..3ff77ff49a 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -281,27 +281,10 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
     return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
 }
 
-static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
+static void blkverify_refresh_filename(BlockDriverState *bs)
 {
     BDRVBlkverifyState *s = bs->opaque;
 
-    /* bs->file->bs has already been refreshed */
-    bdrv_refresh_filename(s->test_file->bs);
-
-    if (bs->file->bs->full_open_options
-        && s->test_file->bs->full_open_options)
-    {
-        QDict *opts = qdict_new();
-        qdict_put_str(opts, "driver", "blkverify");
-
-        qdict_put(opts, "raw",
-                  qobject_ref(bs->file->bs->full_open_options));
-        qdict_put(opts, "test",
-                  qobject_ref(s->test_file->bs->full_open_options));
-
-        bs->full_open_options = opts;
-    }
-
     if (bs->file->bs->exact_filename[0]
         && s->test_file->bs->exact_filename[0])
     {
@@ -316,6 +299,15 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
     }
 }
 
+static char *blkverify_dirname(BlockDriverState *bs, Error **errp)
+{
+    /* In general, there are two BDSs with different dirnames below this one;
+     * so there is no unique dirname we could return (unless both are equal by
+     * chance). Therefore, to be consistent, just always return NULL. */
+    error_setg(errp, "Cannot generate a base directory for blkverify nodes");
+    return NULL;
+}
+
 static BlockDriver bdrv_blkverify = {
     .format_name                      = "blkverify",
     .protocol_name                    = "blkverify",
@@ -327,6 +319,7 @@ static BlockDriver bdrv_blkverify = {
     .bdrv_child_perm                  = bdrv_filter_default_perms,
     .bdrv_getlength                   = blkverify_getlength,
     .bdrv_refresh_filename            = blkverify_refresh_filename,
+    .bdrv_dirname                     = blkverify_dirname,
 
     .bdrv_co_preadv                   = blkverify_co_preadv,
     .bdrv_co_pwritev                  = blkverify_co_pwritev,
diff --git a/block/block-backend.c b/block/block-backend.c
index f6ea824308..edad02a0f2 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1204,17 +1204,8 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
                    int64_t bytes, CoroutineEntry co_entry,
                    BdrvRequestFlags flags)
 {
-    QEMUIOVector qiov;
-    struct iovec iov;
-    BlkRwCo rwco;
-
-    iov = (struct iovec) {
-        .iov_base = buf,
-        .iov_len = bytes,
-    };
-    qemu_iovec_init_external(&qiov, &iov, 1);
-
-    rwco = (BlkRwCo) {
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
+    BlkRwCo rwco = {
         .blk    = blk,
         .offset = offset,
         .iobuf  = &qiov,
@@ -1262,12 +1253,12 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags)
     return bdrv_make_zero(blk->root, flags);
 }
 
-static void blk_inc_in_flight(BlockBackend *blk)
+void blk_inc_in_flight(BlockBackend *blk)
 {
     atomic_inc(&blk->in_flight);
 }
 
-static void blk_dec_in_flight(BlockBackend *blk)
+void blk_dec_in_flight(BlockBackend *blk)
 {
     atomic_dec(&blk->in_flight);
     aio_wait_kick();
diff --git a/block/commit.c b/block/commit.c
index 53148e610b..3b46ca7f97 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -47,14 +47,9 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
                                         void *buf)
 {
     int ret = 0;
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base = buf,
-        .iov_len = bytes,
-    };
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
 
     assert(bytes < SIZE_MAX);
-    qemu_iovec_init_external(&qiov, &iov, 1);
 
     ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0);
     if (ret < 0) {
@@ -230,9 +225,8 @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
     return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
 }
 
-static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts)
+static void bdrv_commit_top_refresh_filename(BlockDriverState *bs)
 {
-    bdrv_refresh_filename(bs->backing->bs);
     pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
             bs->backing->bs->filename);
 }
@@ -374,10 +368,12 @@ fail:
     if (s->top) {
         blk_unref(s->top);
     }
+    job_early_fail(&s->common.job);
+    /* commit_top_bs has to be replaced after deleting the block job,
+     * otherwise this would fail because of lack of permissions. */
     if (commit_top_bs) {
         bdrv_replace_node(commit_top_bs, top, &error_abort);
     }
-    job_early_fail(&s->common.job);
 }
 
 
diff --git a/block/crypto.c b/block/crypto.c
index d5b1da66a1..fd8c7cfac6 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -619,6 +619,12 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp)
     return spec_info;
 }
 
+static const char *const block_crypto_strong_runtime_opts[] = {
+    BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
+
+    NULL
+};
+
 BlockDriver bdrv_crypto_luks = {
     .format_name        = "luks",
     .instance_size      = sizeof(BlockCrypto),
@@ -640,6 +646,8 @@ BlockDriver bdrv_crypto_luks = {
     .bdrv_getlength     = block_crypto_getlength,
     .bdrv_get_info      = block_crypto_get_info_luks,
     .bdrv_get_specific_info = block_crypto_get_specific_info_luks,
+
+    .strong_runtime_opts = block_crypto_strong_runtime_opts,
 };
 
 static void block_crypto_init(void)
diff --git a/block/curl.c b/block/curl.c
index b7ac265d3a..606709fea4 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -61,8 +61,6 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
 
 #define CURL_NUM_STATES 8
 #define CURL_NUM_ACB    8
-#define READ_AHEAD_DEFAULT (256 * 1024)
-#define CURL_TIMEOUT_DEFAULT 5
 #define CURL_TIMEOUT_MAX 10000
 
 #define CURL_BLOCK_OPT_URL       "url"
@@ -76,6 +74,10 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
 #define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
 #define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret"
 
+#define CURL_BLOCK_OPT_READAHEAD_DEFAULT (256 * 1024)
+#define CURL_BLOCK_OPT_SSLVERIFY_DEFAULT true
+#define CURL_BLOCK_OPT_TIMEOUT_DEFAULT 5
+
 struct BDRVCURLState;
 
 static bool libcurl_initialized;
@@ -696,7 +698,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
     }
 
     s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD,
-                                          READ_AHEAD_DEFAULT);
+                                          CURL_BLOCK_OPT_READAHEAD_DEFAULT);
     if ((s->readahead_size & 0x1ff) != 0) {
         error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
                    s->readahead_size);
@@ -704,13 +706,14 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
     }
 
     s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT,
-                                     CURL_TIMEOUT_DEFAULT);
+                                     CURL_BLOCK_OPT_TIMEOUT_DEFAULT);
     if (s->timeout > CURL_TIMEOUT_MAX) {
         error_setg(errp, "timeout parameter is too large or negative");
         goto out_noclean;
     }
 
-    s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
+    s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY,
+                                     CURL_BLOCK_OPT_SSLVERIFY_DEFAULT);
 
     cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
     cookie_secret = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE_SECRET);
@@ -947,6 +950,36 @@ static int64_t curl_getlength(BlockDriverState *bs)
     return s->len;
 }
 
+static void curl_refresh_filename(BlockDriverState *bs)
+{
+    BDRVCURLState *s = bs->opaque;
+
+    /* "readahead" and "timeout" do not change the guest-visible data,
+     * so ignore them */
+    if (s->sslverify != CURL_BLOCK_OPT_SSLVERIFY_DEFAULT ||
+        s->cookie || s->username || s->password || s->proxyusername ||
+        s->proxypassword)
+    {
+        return;
+    }
+
+    pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), s->url);
+}
+
+
+static const char *const curl_strong_runtime_opts[] = {
+    CURL_BLOCK_OPT_URL,
+    CURL_BLOCK_OPT_SSLVERIFY,
+    CURL_BLOCK_OPT_COOKIE,
+    CURL_BLOCK_OPT_COOKIE_SECRET,
+    CURL_BLOCK_OPT_USERNAME,
+    CURL_BLOCK_OPT_PASSWORD_SECRET,
+    CURL_BLOCK_OPT_PROXY_USERNAME,
+    CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET,
+
+    NULL
+};
+
 static BlockDriver bdrv_http = {
     .format_name                = "http",
     .protocol_name              = "http",
@@ -961,6 +994,9 @@ static BlockDriver bdrv_http = {
 
     .bdrv_detach_aio_context    = curl_detach_aio_context,
     .bdrv_attach_aio_context    = curl_attach_aio_context,
+
+    .bdrv_refresh_filename      = curl_refresh_filename,
+    .strong_runtime_opts        = curl_strong_runtime_opts,
 };
 
 static BlockDriver bdrv_https = {
@@ -977,6 +1013,9 @@ static BlockDriver bdrv_https = {
 
     .bdrv_detach_aio_context    = curl_detach_aio_context,
     .bdrv_attach_aio_context    = curl_attach_aio_context,
+
+    .bdrv_refresh_filename      = curl_refresh_filename,
+    .strong_runtime_opts        = curl_strong_runtime_opts,
 };
 
 static BlockDriver bdrv_ftp = {
@@ -993,6 +1032,9 @@ static BlockDriver bdrv_ftp = {
 
     .bdrv_detach_aio_context    = curl_detach_aio_context,
     .bdrv_attach_aio_context    = curl_attach_aio_context,
+
+    .bdrv_refresh_filename      = curl_refresh_filename,
+    .strong_runtime_opts        = curl_strong_runtime_opts,
 };
 
 static BlockDriver bdrv_ftps = {
@@ -1009,6 +1051,9 @@ static BlockDriver bdrv_ftps = {
 
     .bdrv_detach_aio_context    = curl_detach_aio_context,
     .bdrv_attach_aio_context    = curl_attach_aio_context,
+
+    .bdrv_refresh_filename      = curl_refresh_filename,
+    .strong_runtime_opts        = curl_strong_runtime_opts,
 };
 
 static void curl_block_init(void)
diff --git a/block/gluster.c b/block/gluster.c
index 72891060e3..af64330211 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -1495,6 +1495,21 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
 }
 
 
+static const char *const gluster_strong_open_opts[] = {
+    GLUSTER_OPT_VOLUME,
+    GLUSTER_OPT_PATH,
+    GLUSTER_OPT_TYPE,
+    GLUSTER_OPT_SERVER_PATTERN,
+    GLUSTER_OPT_HOST,
+    GLUSTER_OPT_PORT,
+    GLUSTER_OPT_TO,
+    GLUSTER_OPT_IPV4,
+    GLUSTER_OPT_IPV6,
+    GLUSTER_OPT_SOCKET,
+
+    NULL
+};
+
 static BlockDriver bdrv_gluster = {
     .format_name                  = "gluster",
     .protocol_name                = "gluster",
@@ -1522,6 +1537,7 @@ static BlockDriver bdrv_gluster = {
 #endif
     .bdrv_co_block_status         = qemu_gluster_co_block_status,
     .create_opts                  = &qemu_gluster_create_opts,
+    .strong_runtime_opts          = gluster_strong_open_opts,
 };
 
 static BlockDriver bdrv_gluster_tcp = {
@@ -1551,6 +1567,7 @@ static BlockDriver bdrv_gluster_tcp = {
 #endif
     .bdrv_co_block_status         = qemu_gluster_co_block_status,
     .create_opts                  = &qemu_gluster_create_opts,
+    .strong_runtime_opts          = gluster_strong_open_opts,
 };
 
 static BlockDriver bdrv_gluster_unix = {
@@ -1580,6 +1597,7 @@ static BlockDriver bdrv_gluster_unix = {
 #endif
     .bdrv_co_block_status         = qemu_gluster_co_block_status,
     .create_opts                  = &qemu_gluster_create_opts,
+    .strong_runtime_opts          = gluster_strong_open_opts,
 };
 
 /* rdma is deprecated (actually never supported for volfile fetch).
@@ -1615,6 +1633,7 @@ static BlockDriver bdrv_gluster_rdma = {
 #endif
     .bdrv_co_block_status         = qemu_gluster_co_block_status,
     .create_opts                  = &qemu_gluster_create_opts,
+    .strong_runtime_opts          = gluster_strong_open_opts,
 };
 
 static void bdrv_gluster_init(void)
diff --git a/block/io.c b/block/io.c
index 213ca03d8d..2ba603c7bc 100644
--- a/block/io.c
+++ b/block/io.c
@@ -843,17 +843,13 @@ static int bdrv_prwv_co(BdrvChild *child, int64_t offset,
 static int bdrv_rw_co(BdrvChild *child, int64_t sector_num, uint8_t *buf,
                       int nb_sectors, bool is_write, BdrvRequestFlags flags)
 {
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base = (void *)buf,
-        .iov_len = nb_sectors * BDRV_SECTOR_SIZE,
-    };
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf,
+                                            nb_sectors * BDRV_SECTOR_SIZE);
 
     if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
         return -EINVAL;
     }
 
-    qemu_iovec_init_external(&qiov, &iov, 1);
     return bdrv_prwv_co(child, sector_num << BDRV_SECTOR_BITS,
                         &qiov, is_write, flags);
 }
@@ -880,13 +876,8 @@ int bdrv_write(BdrvChild *child, int64_t sector_num,
 int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
                        int bytes, BdrvRequestFlags flags)
 {
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base = NULL,
-        .iov_len = bytes,
-    };
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, bytes);
 
-    qemu_iovec_init_external(&qiov, &iov, 1);
     return bdrv_prwv_co(child, offset, &qiov, true,
                         BDRV_REQ_ZERO_WRITE | flags);
 }
@@ -950,17 +941,12 @@ int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
 
 int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes)
 {
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base = (void *)buf,
-        .iov_len = bytes,
-    };
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
 
     if (bytes < 0) {
         return -EINVAL;
     }
 
-    qemu_iovec_init_external(&qiov, &iov, 1);
     return bdrv_preadv(child, offset, &qiov);
 }
 
@@ -978,17 +964,12 @@ int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
 
 int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes)
 {
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base   = (void *) buf,
-        .iov_len    = bytes,
-    };
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
 
     if (bytes < 0) {
         return -EINVAL;
     }
 
-    qemu_iovec_init_external(&qiov, &iov, 1);
     return bdrv_pwritev(child, offset, &qiov);
 }
 
@@ -1165,7 +1146,6 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
     void *bounce_buffer;
 
     BlockDriver *drv = bs->drv;
-    struct iovec iov;
     QEMUIOVector local_qiov;
     int64_t cluster_offset;
     int64_t cluster_bytes;
@@ -1230,9 +1210,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
 
         if (ret <= 0) {
             /* Must copy-on-read; use the bounce buffer */
-            iov.iov_base = bounce_buffer;
-            iov.iov_len = pnum = MIN(pnum, MAX_BOUNCE_BUFFER);
-            qemu_iovec_init_external(&local_qiov, &iov, 1);
+            pnum = MIN(pnum, MAX_BOUNCE_BUFFER);
+            qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum);
 
             ret = bdrv_driver_preadv(bs, cluster_offset, pnum,
                                      &local_qiov, 0);
@@ -1477,7 +1456,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
 {
     BlockDriver *drv = bs->drv;
     QEMUIOVector qiov;
-    struct iovec iov = {0};
+    void *buf = NULL;
     int ret = 0;
     bool need_flush = false;
     int head = 0;
@@ -1547,16 +1526,14 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
                 need_flush = true;
             }
             num = MIN(num, max_transfer);
-            iov.iov_len = num;
-            if (iov.iov_base == NULL) {
-                iov.iov_base = qemu_try_blockalign(bs, num);
-                if (iov.iov_base == NULL) {
+            if (buf == NULL) {
+                buf = qemu_try_blockalign0(bs, num);
+                if (buf == NULL) {
                     ret = -ENOMEM;
                     goto fail;
                 }
-                memset(iov.iov_base, 0, num);
             }
-            qemu_iovec_init_external(&qiov, &iov, 1);
+            qemu_iovec_init_buf(&qiov, buf, num);
 
             ret = bdrv_driver_pwritev(bs, offset, num, &qiov, write_flags);
 
@@ -1564,8 +1541,8 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
              * all future requests.
              */
             if (num < max_transfer) {
-                qemu_vfree(iov.iov_base);
-                iov.iov_base = NULL;
+                qemu_vfree(buf);
+                buf = NULL;
             }
         }
 
@@ -1577,7 +1554,7 @@ fail:
     if (ret == 0 && need_flush) {
         ret = bdrv_co_flush(bs);
     }
-    qemu_vfree(iov.iov_base);
+    qemu_vfree(buf);
     return ret;
 }
 
@@ -1763,7 +1740,6 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
     BlockDriverState *bs = child->bs;
     uint8_t *buf = NULL;
     QEMUIOVector local_qiov;
-    struct iovec iov;
     uint64_t align = bs->bl.request_alignment;
     unsigned int head_padding_bytes, tail_padding_bytes;
     int ret = 0;
@@ -1775,11 +1751,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
     assert(flags & BDRV_REQ_ZERO_WRITE);
     if (head_padding_bytes || tail_padding_bytes) {
         buf = qemu_blockalign(bs, align);
-        iov = (struct iovec) {
-            .iov_base   = buf,
-            .iov_len    = align,
-        };
-        qemu_iovec_init_external(&local_qiov, &iov, 1);
+        qemu_iovec_init_buf(&local_qiov, buf, align);
     }
     if (head_padding_bytes) {
         uint64_t zero_bytes = MIN(bytes, align - head_padding_bytes);
@@ -1885,17 +1857,12 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
 
     if (offset & (align - 1)) {
         QEMUIOVector head_qiov;
-        struct iovec head_iov;
 
         mark_request_serialising(&req, align);
         wait_serialising_requests(&req);
 
         head_buf = qemu_blockalign(bs, align);
-        head_iov = (struct iovec) {
-            .iov_base   = head_buf,
-            .iov_len    = align,
-        };
-        qemu_iovec_init_external(&head_qiov, &head_iov, 1);
+        qemu_iovec_init_buf(&head_qiov, head_buf, align);
 
         bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
         ret = bdrv_aligned_preadv(child, &req, offset & ~(align - 1), align,
@@ -1924,7 +1891,6 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
 
     if ((offset + bytes) & (align - 1)) {
         QEMUIOVector tail_qiov;
-        struct iovec tail_iov;
         size_t tail_bytes;
         bool waited;
 
@@ -1933,11 +1899,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
         assert(!waited || !use_local_qiov);
 
         tail_buf = qemu_blockalign(bs, align);
-        tail_iov = (struct iovec) {
-            .iov_base   = tail_buf,
-            .iov_len    = align,
-        };
-        qemu_iovec_init_external(&tail_qiov, &tail_iov, 1);
+        qemu_iovec_init_buf(&tail_qiov, tail_buf, align);
 
         bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
         ret = bdrv_aligned_preadv(child, &req, (offset + bytes) & ~(align - 1),
@@ -2468,15 +2430,9 @@ bdrv_rw_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos,
 int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
                       int64_t pos, int size)
 {
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base   = (void *) buf,
-        .iov_len    = size,
-    };
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size);
     int ret;
 
-    qemu_iovec_init_external(&qiov, &iov, 1);
-
     ret = bdrv_writev_vmstate(bs, &qiov, pos);
     if (ret < 0) {
         return ret;
@@ -2493,14 +2449,9 @@ int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
 int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
                       int64_t pos, int size)
 {
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base   = buf,
-        .iov_len    = size,
-    };
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size);
     int ret;
 
-    qemu_iovec_init_external(&qiov, &iov, 1);
     ret = bdrv_readv_vmstate(bs, &qiov, pos);
     if (ret < 0) {
         return ret;
diff --git a/block/iscsi.c b/block/iscsi.c
index ff473206e6..a0c0084837 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -2448,6 +2448,20 @@ static QemuOptsList iscsi_create_opts = {
     }
 };
 
+static const char *const iscsi_strong_runtime_opts[] = {
+    "transport",
+    "portal",
+    "target",
+    "user",
+    "password",
+    "password-secret",
+    "lun",
+    "initiator-name",
+    "header-digest",
+
+    NULL
+};
+
 static BlockDriver bdrv_iscsi = {
     .format_name     = "iscsi",
     .protocol_name   = "iscsi",
@@ -2482,6 +2496,8 @@ static BlockDriver bdrv_iscsi = {
 
     .bdrv_detach_aio_context = iscsi_detach_aio_context,
     .bdrv_attach_aio_context = iscsi_attach_aio_context,
+
+    .strong_runtime_opts = iscsi_strong_runtime_opts,
 };
 
 #if LIBISCSI_API_VERSION >= (20160603)
@@ -2519,6 +2535,8 @@ static BlockDriver bdrv_iser = {
 
     .bdrv_detach_aio_context = iscsi_detach_aio_context,
     .bdrv_attach_aio_context = iscsi_attach_aio_context,
+
+    .strong_runtime_opts = iscsi_strong_runtime_opts,
 };
 #endif
 
diff --git a/block/mirror.c b/block/mirror.c
index b67b0120f8..726d3c27fb 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -1431,14 +1431,13 @@ static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs,
                                     NULL, 0);
 }
 
-static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs, QDict *opts)
+static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs)
 {
     if (bs->backing == NULL) {
         /* we can be here after failed bdrv_attach_child in
          * bdrv_set_backing_hd */
         return;
     }
-    bdrv_refresh_filename(bs->backing->bs);
     pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
             bs->backing->bs->filename);
 }
diff --git a/block/nbd-client.c b/block/nbd-client.c
index f0ad54ce21..bfbaf7ebe9 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -76,8 +76,18 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
     Error *local_err = NULL;
 
     while (!s->quit) {
+        /*
+         * The NBD client can only really be considered idle when it has
+         * yielded from qio_channel_readv_all_eof(), waiting for data. This is
+         * the point where the additional scheduled coroutine entry happens
+         * after nbd_client_attach_aio_context().
+         *
+         * Therefore we keep an additional in_flight reference all the time and
+         * only drop it temporarily here.
+         */
         assert(s->reply.handle == 0);
-        ret = nbd_receive_reply(s->ioc, &s->reply, &local_err);
+        ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, &local_err);
+
         if (local_err) {
             trace_nbd_read_reply_entry_fail(ret, error_get_pretty(local_err));
             error_free(local_err);
@@ -116,6 +126,8 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
 
     s->quit = true;
     nbd_recv_coroutines_wake_all(s);
+    bdrv_dec_in_flight(s->bs);
+
     s->connection_co = NULL;
     aio_wait_kick();
 }
@@ -965,12 +977,30 @@ void nbd_client_detach_aio_context(BlockDriverState *bs)
     qio_channel_detach_aio_context(QIO_CHANNEL(client->ioc));
 }
 
+static void nbd_client_attach_aio_context_bh(void *opaque)
+{
+    BlockDriverState *bs = opaque;
+    NBDClientSession *client = nbd_get_client_session(bs);
+
+    /* The node is still drained, so we know the coroutine has yielded in
+     * nbd_read_eof(), the only place where bs->in_flight can reach 0, or it is
+     * entered for the first time. Both places are safe for entering the
+     * coroutine.*/
+    qemu_aio_coroutine_enter(bs->aio_context, client->connection_co);
+    bdrv_dec_in_flight(bs);
+}
+
 void nbd_client_attach_aio_context(BlockDriverState *bs,
                                    AioContext *new_context)
 {
     NBDClientSession *client = nbd_get_client_session(bs);
     qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context);
-    aio_co_schedule(new_context, client->connection_co);
+
+    bdrv_inc_in_flight(bs);
+
+    /* Need to wait here for the BH to run because the BH must run while the
+     * node is still drained. */
+    aio_wait_bh_oneshot(new_context, nbd_client_attach_aio_context_bh, bs);
 }
 
 void nbd_client_close(BlockDriverState *bs)
@@ -1076,6 +1106,7 @@ static int nbd_client_connect(BlockDriverState *bs,
      * kick the reply mechanism.  */
     qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
     client->connection_co = qemu_coroutine_create(nbd_connection_entry, client);
+    bdrv_inc_in_flight(bs);
     nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs));
 
     logout("Established connection with NBD server\n");
@@ -1108,6 +1139,7 @@ int nbd_client_init(BlockDriverState *bs,
 {
     NBDClientSession *client = nbd_get_client_session(bs);
 
+    client->bs = bs;
     qemu_co_mutex_init(&client->send_mutex);
     qemu_co_queue_init(&client->free_sema);
 
diff --git a/block/nbd-client.h b/block/nbd-client.h
index d990207a5c..09e03013d2 100644
--- a/block/nbd-client.h
+++ b/block/nbd-client.h
@@ -35,6 +35,7 @@ typedef struct NBDClientSession {
 
     NBDClientRequest requests[MAX_NBD_REQUESTS];
     NBDReply reply;
+    BlockDriverState *bs;
     bool quit;
 } NBDClientSession;
 
diff --git a/block/nbd.c b/block/nbd.c
index 9db5eded89..2e72df528a 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -477,12 +477,9 @@ static void nbd_attach_aio_context(BlockDriverState *bs,
     nbd_client_attach_aio_context(bs, new_context);
 }
 
-static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
+static void nbd_refresh_filename(BlockDriverState *bs)
 {
     BDRVNBDState *s = bs->opaque;
-    QDict *opts = qdict_new();
-    QObject *saddr_qdict;
-    Visitor *ov;
     const char *host = NULL, *port = NULL, *path = NULL;
 
     if (s->saddr->type == SOCKET_ADDRESS_TYPE_INET) {
@@ -495,8 +492,6 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
         path = s->saddr->u.q_unix.path;
     } /* else can't represent as pseudo-filename */
 
-    qdict_put_str(opts, "driver", "nbd");
-
     if (path && s->export) {
         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
                  "nbd+unix:///%s?socket=%s", s->export, path);
@@ -510,23 +505,28 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
                  "nbd://%s:%s", host, port);
     }
+}
 
-    ov = qobject_output_visitor_new(&saddr_qdict);
-    visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort);
-    visit_complete(ov, &saddr_qdict);
-    visit_free(ov);
-    qdict_put_obj(opts, "server", saddr_qdict);
+static char *nbd_dirname(BlockDriverState *bs, Error **errp)
+{
+    /* The generic bdrv_dirname() implementation is able to work out some
+     * directory name for NBD nodes, but that would be wrong. So far there is no
+     * specification for how "export paths" would work, so NBD does not have
+     * directory names. */
+    error_setg(errp, "Cannot generate a base directory for NBD nodes");
+    return NULL;
+}
 
-    if (s->export) {
-        qdict_put_str(opts, "export", s->export);
-    }
-    if (s->tlscredsid) {
-        qdict_put_str(opts, "tls-creds", s->tlscredsid);
-    }
+static const char *const nbd_strong_runtime_opts[] = {
+    "path",
+    "host",
+    "port",
+    "export",
+    "tls-creds",
+    "server.",
 
-    qdict_flatten(opts);
-    bs->full_open_options = opts;
-}
+    NULL
+};
 
 static BlockDriver bdrv_nbd = {
     .format_name                = "nbd",
@@ -546,6 +546,8 @@ static BlockDriver bdrv_nbd = {
     .bdrv_attach_aio_context    = nbd_attach_aio_context,
     .bdrv_refresh_filename      = nbd_refresh_filename,
     .bdrv_co_block_status       = nbd_client_co_block_status,
+    .bdrv_dirname               = nbd_dirname,
+    .strong_runtime_opts        = nbd_strong_runtime_opts,
 };
 
 static BlockDriver bdrv_nbd_tcp = {
@@ -566,6 +568,8 @@ static BlockDriver bdrv_nbd_tcp = {
     .bdrv_attach_aio_context    = nbd_attach_aio_context,
     .bdrv_refresh_filename      = nbd_refresh_filename,
     .bdrv_co_block_status       = nbd_client_co_block_status,
+    .bdrv_dirname               = nbd_dirname,
+    .strong_runtime_opts        = nbd_strong_runtime_opts,
 };
 
 static BlockDriver bdrv_nbd_unix = {
@@ -586,6 +590,8 @@ static BlockDriver bdrv_nbd_unix = {
     .bdrv_attach_aio_context    = nbd_attach_aio_context,
     .bdrv_refresh_filename      = nbd_refresh_filename,
     .bdrv_co_block_status       = nbd_client_co_block_status,
+    .bdrv_dirname               = nbd_dirname,
+    .strong_runtime_opts        = nbd_strong_runtime_opts,
 };
 
 static void bdrv_nbd_init(void)
diff --git a/block/nfs.c b/block/nfs.c
index eab1a2c408..531903610b 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -799,14 +799,9 @@ static int nfs_reopen_prepare(BDRVReopenState *state,
     return 0;
 }
 
-static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
+static void nfs_refresh_filename(BlockDriverState *bs)
 {
     NFSClient *client = bs->opaque;
-    QDict *opts = qdict_new();
-    QObject *server_qdict;
-    Visitor *ov;
-
-    qdict_put_str(opts, "driver", "nfs");
 
     if (client->uid && !client->gid) {
         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
@@ -824,35 +819,20 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
                  "nfs://%s%s", client->server->host, client->path);
     }
+}
 
-    ov = qobject_output_visitor_new(&server_qdict);
-    visit_type_NFSServer(ov, NULL, &client->server, &error_abort);
-    visit_complete(ov, &server_qdict);
-    qdict_put_obj(opts, "server", server_qdict);
-    qdict_put_str(opts, "path", client->path);
+static char *nfs_dirname(BlockDriverState *bs, Error **errp)
+{
+    NFSClient *client = bs->opaque;
 
-    if (client->uid) {
-        qdict_put_int(opts, "user", client->uid);
-    }
-    if (client->gid) {
-        qdict_put_int(opts, "group", client->gid);
-    }
-    if (client->tcp_syncnt) {
-        qdict_put_int(opts, "tcp-syn-cnt", client->tcp_syncnt);
-    }
-    if (client->readahead) {
-        qdict_put_int(opts, "readahead-size", client->readahead);
-    }
-    if (client->pagecache) {
-        qdict_put_int(opts, "page-cache-size", client->pagecache);
-    }
-    if (client->debug) {
-        qdict_put_int(opts, "debug", client->debug);
+    if (client->uid || client->gid) {
+        bdrv_refresh_filename(bs);
+        error_setg(errp, "Cannot generate a base directory for NFS node '%s'",
+                   bs->filename);
+        return NULL;
     }
 
-    visit_free(ov);
-    qdict_flatten(opts);
-    bs->full_open_options = opts;
+    return g_strdup_printf("nfs://%s%s/", client->server->host, client->path);
 }
 
 #ifdef LIBNFS_FEATURE_PAGECACHE
@@ -864,6 +844,15 @@ static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
 }
 #endif
 
+static const char *nfs_strong_runtime_opts[] = {
+    "path",
+    "user",
+    "group",
+    "server.",
+
+    NULL
+};
+
 static BlockDriver bdrv_nfs = {
     .format_name                    = "nfs",
     .protocol_name                  = "nfs",
@@ -889,6 +878,9 @@ static BlockDriver bdrv_nfs = {
     .bdrv_detach_aio_context        = nfs_detach_aio_context,
     .bdrv_attach_aio_context        = nfs_attach_aio_context,
     .bdrv_refresh_filename          = nfs_refresh_filename,
+    .bdrv_dirname                   = nfs_dirname,
+
+    .strong_runtime_opts            = nfs_strong_runtime_opts,
 
 #ifdef LIBNFS_FEATURE_PAGECACHE
     .bdrv_co_invalidate_cache       = nfs_co_invalidate_cache,
diff --git a/block/null.c b/block/null.c
index d442d3e901..a322929478 100644
--- a/block/null.c
+++ b/block/null.c
@@ -239,19 +239,33 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs,
     return ret;
 }
 
-static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
+static void null_refresh_filename(BlockDriverState *bs)
 {
-    qdict_del(opts, "filename");
-
-    if (!qdict_size(opts)) {
-        snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
-                 bs->drv->format_name);
+    const QDictEntry *e;
+
+    for (e = qdict_first(bs->full_open_options); e;
+         e = qdict_next(bs->full_open_options, e))
+    {
+        /* These options can be ignored */
+        if (strcmp(qdict_entry_key(e), "filename") &&
+            strcmp(qdict_entry_key(e), "driver") &&
+            strcmp(qdict_entry_key(e), NULL_OPT_LATENCY))
+        {
+            return;
+        }
     }
 
-    qdict_put_str(opts, "driver", bs->drv->format_name);
-    bs->full_open_options = qobject_ref(opts);
+    snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
+             bs->drv->format_name);
 }
 
+static const char *const null_strong_runtime_opts[] = {
+    BLOCK_OPT_SIZE,
+    NULL_OPT_ZEROES,
+
+    NULL
+};
+
 static BlockDriver bdrv_null_co = {
     .format_name            = "null-co",
     .protocol_name          = "null-co",
@@ -269,6 +283,7 @@ static BlockDriver bdrv_null_co = {
     .bdrv_co_block_status   = null_co_block_status,
 
     .bdrv_refresh_filename  = null_refresh_filename,
+    .strong_runtime_opts    = null_strong_runtime_opts,
 };
 
 static BlockDriver bdrv_null_aio = {
@@ -288,6 +303,7 @@ static BlockDriver bdrv_null_aio = {
     .bdrv_co_block_status   = null_co_block_status,
 
     .bdrv_refresh_filename  = null_refresh_filename,
+    .strong_runtime_opts    = null_strong_runtime_opts,
 };
 
 static void bdrv_null_init(void)
diff --git a/block/nvme.c b/block/nvme.c
index b5952c9b08..0684bbd077 100644
--- a/block/nvme.c
+++ b/block/nvme.c
@@ -82,7 +82,7 @@ typedef volatile struct {
     uint8_t  reserved1[0xec0];
     uint8_t  cmd_set_specfic[0x100];
     uint32_t doorbells[];
-} QEMU_PACKED NVMeRegs;
+} NVMeRegs;
 
 QEMU_BUILD_BUG_ON(offsetof(NVMeRegs, doorbells) != 0x1000);
 
@@ -111,6 +111,9 @@ typedef struct {
 
     /* Total size of mapped qiov, accessed under dma_map_lock */
     int dma_map_count;
+
+    /* PCI address (required for nvme_refresh_filename()) */
+    char *device;
 } BDRVNVMeState;
 
 #define NVME_BLOCK_OPT_DEVICE "device"
@@ -557,6 +560,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
 
     qemu_co_mutex_init(&s->dma_map_lock);
     qemu_co_queue_init(&s->dma_flush_queue);
+    s->device = g_strdup(device);
     s->nsid = namespace;
     s->aio_context = bdrv_get_aio_context(bs);
     ret = event_notifier_init(&s->irq_notifier, 0);
@@ -729,6 +733,8 @@ static void nvme_close(BlockDriverState *bs)
     event_notifier_cleanup(&s->irq_notifier);
     qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE);
     qemu_vfio_close(s->vfio);
+
+    g_free(s->device);
 }
 
 static int nvme_file_open(BlockDriverState *bs, QDict *options, int flags,
@@ -1053,17 +1059,12 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
     return 0;
 }
 
-static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
+static void nvme_refresh_filename(BlockDriverState *bs)
 {
-    qdict_del(opts, "filename");
-
-    if (!qdict_size(opts)) {
-        snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
-                 bs->drv->format_name);
-    }
+    BDRVNVMeState *s = bs->opaque;
 
-    qdict_put_str(opts, "driver", bs->drv->format_name);
-    bs->full_open_options = qobject_ref(opts);
+    snprintf(bs->exact_filename, sizeof(bs->exact_filename), "nvme://%s/%i",
+             s->device, s->nsid);
 }
 
 static void nvme_refresh_limits(BlockDriverState *bs, Error **errp)
@@ -1136,6 +1137,13 @@ static void nvme_unregister_buf(BlockDriverState *bs, void *host)
     qemu_vfio_dma_unmap(s->vfio, host);
 }
 
+static const char *const nvme_strong_runtime_opts[] = {
+    NVME_BLOCK_OPT_DEVICE,
+    NVME_BLOCK_OPT_NAMESPACE,
+
+    NULL
+};
+
 static BlockDriver bdrv_nvme = {
     .format_name              = "nvme",
     .protocol_name            = "nvme",
@@ -1153,6 +1161,7 @@ static BlockDriver bdrv_nvme = {
 
     .bdrv_refresh_filename    = nvme_refresh_filename,
     .bdrv_refresh_limits      = nvme_refresh_limits,
+    .strong_runtime_opts      = nvme_strong_runtime_opts,
 
     .bdrv_detach_aio_context  = nvme_detach_aio_context,
     .bdrv_attach_aio_context  = nvme_attach_aio_context,
diff --git a/block/parallels.c b/block/parallels.c
index cc9445879d..15bc97b759 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -220,23 +220,20 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
     if (bs->backing) {
         int64_t nb_cow_sectors = to_allocate * s->tracks;
         int64_t nb_cow_bytes = nb_cow_sectors << BDRV_SECTOR_BITS;
-        QEMUIOVector qiov;
-        struct iovec iov = {
-            .iov_len = nb_cow_bytes,
-            .iov_base = qemu_blockalign(bs, nb_cow_bytes)
-        };
-        qemu_iovec_init_external(&qiov, &iov, 1);
+        QEMUIOVector qiov =
+            QEMU_IOVEC_INIT_BUF(qiov, qemu_blockalign(bs, nb_cow_bytes),
+                                nb_cow_bytes);
 
         ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
                              nb_cow_bytes, &qiov, 0);
         if (ret < 0) {
-            qemu_vfree(iov.iov_base);
+            qemu_vfree(qemu_iovec_buf(&qiov));
             return ret;
         }
 
         ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE,
                               nb_cow_bytes, &qiov, 0);
-        qemu_vfree(iov.iov_base);
+        qemu_vfree(qemu_iovec_buf(&qiov));
         if (ret < 0) {
             return ret;
         }
diff --git a/block/qapi.c b/block/qapi.c
index 00291f9105..6002a768f8 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -51,6 +51,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
         return NULL;
     }
 
+    bdrv_refresh_filename(bs);
+
     info = g_malloc0(sizeof(*info));
     info->file                   = g_strdup(bs->filename);
     info->ro                     = bs->read_only;
@@ -264,6 +266,8 @@ void bdrv_query_image_info(BlockDriverState *bs,
         goto out;
     }
 
+    bdrv_refresh_filename(bs);
+
     info = g_new0(ImageInfo, 1);
     info->filename        = g_strdup(bs->filename);
     info->format          = g_strdup(bdrv_get_format_name(bs));
@@ -292,18 +296,10 @@ void bdrv_query_image_info(BlockDriverState *bs,
 
     backing_filename = bs->backing_file;
     if (backing_filename[0] != '\0') {
-        char *backing_filename2 = g_malloc0(PATH_MAX);
+        char *backing_filename2;
         info->backing_filename = g_strdup(backing_filename);
         info->has_backing_filename = true;
-        bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err);
-        if (err) {
-            /* Can't reconstruct the full backing filename, so we must omit
-             * this field and apply a Best Effort to this query. */
-            g_free(backing_filename2);
-            backing_filename2 = NULL;
-            error_free(err);
-            err = NULL;
-        }
+        backing_filename2 = bdrv_get_full_backing_filename(bs, NULL);
 
         /* Always report the full_backing_filename if present, even if it's the
          * same as backing_filename. That they are same is useful info. */
diff --git a/block/qcow.c b/block/qcow.c
index 0a235bf393..10d2cf14b3 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -31,6 +31,7 @@
 #include "qemu/module.h"
 #include "qemu/option.h"
 #include "qemu/bswap.h"
+#include "qemu/cutils.h"
 #include <zlib.h>
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qstring.h"
@@ -295,11 +296,13 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
             goto fail;
         }
         ret = bdrv_pread(bs->file, header.backing_file_offset,
-                   bs->backing_file, len);
+                   bs->auto_backing_file, len);
         if (ret < 0) {
             goto fail;
         }
-        bs->backing_file[len] = '\0';
+        bs->auto_backing_file[len] = '\0';
+        pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+                bs->auto_backing_file);
     }
 
     /* Disable migration when qcow images are used */
@@ -628,7 +631,6 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
     int offset_in_cluster;
     int ret = 0, n;
     uint64_t cluster_offset;
-    struct iovec hd_iov;
     QEMUIOVector hd_qiov;
     uint8_t *buf;
     void *orig_buf;
@@ -661,9 +663,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
         if (!cluster_offset) {
             if (bs->backing) {
                 /* read from the base image */
-                hd_iov.iov_base = (void *)buf;
-                hd_iov.iov_len = n;
-                qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
+                qemu_iovec_init_buf(&hd_qiov, buf, n);
                 qemu_co_mutex_unlock(&s->lock);
                 /* qcow2 emits this on bs->file instead of bs->backing */
                 BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
@@ -688,9 +688,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
                 ret = -EIO;
                 break;
             }
-            hd_iov.iov_base = (void *)buf;
-            hd_iov.iov_len = n;
-            qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
+            qemu_iovec_init_buf(&hd_qiov, buf, n);
             qemu_co_mutex_unlock(&s->lock);
             BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
             ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster,
@@ -733,7 +731,6 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
     int offset_in_cluster;
     uint64_t cluster_offset;
     int ret = 0, n;
-    struct iovec hd_iov;
     QEMUIOVector hd_qiov;
     uint8_t *buf;
     void *orig_buf;
@@ -779,9 +776,7 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
             }
         }
 
-        hd_iov.iov_base = (void *)buf;
-        hd_iov.iov_len = n;
-        qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
+        qemu_iovec_init_buf(&hd_qiov, buf, n);
         qemu_co_mutex_unlock(&s->lock);
         BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
         ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
@@ -1062,7 +1057,6 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
 {
     BDRVQcowState *s = bs->opaque;
     QEMUIOVector hd_qiov;
-    struct iovec iov;
     z_stream strm;
     int ret, out_len;
     uint8_t *buf, *out_buf;
@@ -1128,11 +1122,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
     }
     cluster_offset &= s->cluster_offset_mask;
 
-    iov = (struct iovec) {
-        .iov_base   = out_buf,
-        .iov_len    = out_len,
-    };
-    qemu_iovec_init_external(&hd_qiov, &iov, 1);
+    qemu_iovec_init_buf(&hd_qiov, out_buf, out_len);
     BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
     ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0);
     if (ret < 0) {
@@ -1183,6 +1173,12 @@ static QemuOptsList qcow_create_opts = {
     }
 };
 
+static const char *const qcow_strong_runtime_opts[] = {
+    "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET,
+
+    NULL
+};
+
 static BlockDriver bdrv_qcow = {
     .format_name	= "qcow",
     .instance_size	= sizeof(BDRVQcowState),
@@ -1206,6 +1202,7 @@ static BlockDriver bdrv_qcow = {
     .bdrv_get_info          = qcow_get_info,
 
     .create_opts            = &qcow_create_opts,
+    .strong_runtime_opts    = qcow_strong_runtime_opts,
 };
 
 static void bdrv_qcow_init(void)
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 30eca26c47..179aa2c728 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -285,6 +285,9 @@ static int l2_allocate(BlockDriverState *bs, int l1_index)
         goto fail;
     }
 
+    /* The offset must fit in the offset field of the L1 table entry */
+    assert((l2_offset & L1E_OFFSET_MASK) == l2_offset);
+
     /* If we're allocating the table at offset 0 then something is wrong */
     if (l2_offset == 0) {
         qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid "
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index bb6a5b7516..20e8472191 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -358,11 +358,6 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
     /* Generate an ID */
     find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str));
 
-    /* Check that the ID is unique */
-    if (find_snapshot_by_id_and_name(bs, sn_info->id_str, NULL) >= 0) {
-        return -EEXIST;
-    }
-
     /* Populate sn with passed data */
     sn->id_str = g_strdup(sn_info->id_str);
     sn->name = g_strdup(sn_info->name);
diff --git a/block/qcow2.c b/block/qcow2.c
index 65a54c9ac6..7fb2730f09 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1474,13 +1474,15 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
             goto fail;
         }
         ret = bdrv_pread(bs->file, header.backing_file_offset,
-                         bs->backing_file, len);
+                         bs->auto_backing_file, len);
         if (ret < 0) {
             error_setg_errno(errp, -ret, "Could not read backing file name");
             goto fail;
         }
-        bs->backing_file[len] = '\0';
-        s->image_backing_file = g_strdup(bs->backing_file);
+        bs->auto_backing_file[len] = '\0';
+        pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+                bs->auto_backing_file);
+        s->image_backing_file = g_strdup(bs->auto_backing_file);
     }
 
     /* Internal snapshots */
@@ -2518,6 +2520,8 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
         return -EINVAL;
     }
 
+    pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file),
+            backing_file ?: "");
     pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
     pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");
 
@@ -3894,7 +3898,6 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
 {
     BDRVQcow2State *s = bs->opaque;
     QEMUIOVector hd_qiov;
-    struct iovec iov;
     int ret;
     size_t out_len;
     uint8_t *buf, *out_buf;
@@ -3960,11 +3963,7 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
         goto fail;
     }
 
-    iov = (struct iovec) {
-        .iov_base   = out_buf,
-        .iov_len    = out_len,
-    };
-    qemu_iovec_init_external(&hd_qiov, &iov, 1);
+    qemu_iovec_init_buf(&hd_qiov, out_buf, out_len);
 
     BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
     ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0);
@@ -3990,7 +3989,6 @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
     int ret = 0, csize, nb_csectors;
     uint64_t coffset;
     uint8_t *buf, *out_buf;
-    struct iovec iov;
     QEMUIOVector local_qiov;
     int offset_in_cluster = offset_into_cluster(s, offset);
 
@@ -4002,9 +4000,7 @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
     if (!buf) {
         return -ENOMEM;
     }
-    iov.iov_base = buf;
-    iov.iov_len = csize;
-    qemu_iovec_init_external(&local_qiov, &iov, 1);
+    qemu_iovec_init_buf(&local_qiov, buf, csize);
 
     out_buf = qemu_blockalign(bs, s->cluster_size);
 
@@ -4232,6 +4228,60 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
     return ret;
 }
 
+static ssize_t qcow2_measure_crypto_hdr_init_func(QCryptoBlock *block,
+        size_t headerlen, void *opaque, Error **errp)
+{
+    size_t *headerlenp = opaque;
+
+    /* Stash away the payload size */
+    *headerlenp = headerlen;
+    return 0;
+}
+
+static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block,
+        size_t offset, const uint8_t *buf, size_t buflen,
+        void *opaque, Error **errp)
+{
+    /* Discard the bytes, we're not actually writing to an image */
+    return buflen;
+}
+
+/* Determine the number of bytes for the LUKS payload */
+static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len,
+                                         Error **errp)
+{
+    QDict *opts_qdict;
+    QDict *cryptoopts_qdict;
+    QCryptoBlockCreateOptions *cryptoopts;
+    QCryptoBlock *crypto;
+
+    /* Extract "encrypt." options into a qdict */
+    opts_qdict = qemu_opts_to_qdict(opts, NULL);
+    qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
+    qobject_unref(opts_qdict);
+
+    /* Build QCryptoBlockCreateOptions object from qdict */
+    qdict_put_str(cryptoopts_qdict, "format", "luks");
+    cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp);
+    qobject_unref(cryptoopts_qdict);
+    if (!cryptoopts) {
+        return false;
+    }
+
+    /* Fake LUKS creation in order to determine the payload size */
+    crypto = qcrypto_block_create(cryptoopts, "encrypt.",
+                                  qcow2_measure_crypto_hdr_init_func,
+                                  qcow2_measure_crypto_hdr_write_func,
+                                  len, errp);
+    qapi_free_QCryptoBlockCreateOptions(cryptoopts);
+    if (!crypto) {
+        return false;
+    }
+
+    qcrypto_block_free(crypto);
+    return true;
+}
+
 static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
                                        Error **errp)
 {
@@ -4241,11 +4291,13 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
     uint64_t virtual_size; /* disk size as seen by guest */
     uint64_t refcount_bits;
     uint64_t l2_tables;
+    uint64_t luks_payload_size = 0;
     size_t cluster_size;
     int version;
     char *optstr;
     PreallocMode prealloc;
     bool has_backing_file;
+    bool has_luks;
 
     /* Parse image creation options */
     cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err);
@@ -4275,6 +4327,20 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
     has_backing_file = !!optstr;
     g_free(optstr);
 
+    optstr = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
+    has_luks = optstr && strcmp(optstr, "luks") == 0;
+    g_free(optstr);
+
+    if (has_luks) {
+        size_t headerlen;
+
+        if (!qcow2_measure_luks_headerlen(opts, &headerlen, &local_err)) {
+            goto err;
+        }
+
+        luks_payload_size = ROUND_UP(headerlen, cluster_size);
+    }
+
     virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
     virtual_size = ROUND_UP(virtual_size, cluster_size);
 
@@ -4345,7 +4411,7 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
     info = g_new(BlockMeasureInfo, 1);
     info->fully_allocated =
         qcow2_calc_prealloc_size(virtual_size, cluster_size,
-                                 ctz32(refcount_bits));
+                                 ctz32(refcount_bits)) + luks_payload_size;
 
     /* Remove data clusters that are not required.  This overestimates the
      * required size because metadata needed for the fully allocated file is
@@ -4932,6 +4998,12 @@ static QemuOptsList qcow2_create_opts = {
     }
 };
 
+static const char *const qcow2_strong_runtime_opts[] = {
+    "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET,
+
+    NULL
+};
+
 BlockDriver bdrv_qcow2 = {
     .format_name        = "qcow2",
     .instance_size      = sizeof(BDRVQcow2State),
@@ -4980,6 +5052,7 @@ BlockDriver bdrv_qcow2 = {
     .bdrv_inactivate            = qcow2_inactivate,
 
     .create_opts         = &qcow2_create_opts,
+    .strong_runtime_opts = qcow2_strong_runtime_opts,
     .bdrv_co_check       = qcow2_co_check,
     .bdrv_amend_options  = qcow2_amend_options,
 
diff --git a/block/qed-table.c b/block/qed-table.c
index 7df5680adb..c497bd4aec 100644
--- a/block/qed-table.c
+++ b/block/qed-table.c
@@ -21,16 +21,11 @@
 /* Called with table_lock held.  */
 static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
 {
-    QEMUIOVector qiov;
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(
+        qiov, table->offsets, s->header.cluster_size * s->header.table_size);
     int noffsets;
     int i, ret;
 
-    struct iovec iov = {
-        .iov_base = table->offsets,
-        .iov_len = s->header.cluster_size * s->header.table_size,
-    };
-    qemu_iovec_init_external(&qiov, &iov, 1);
-
     trace_qed_read_table(s, offset, table);
 
     qemu_co_mutex_unlock(&s->table_lock);
@@ -71,7 +66,6 @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
     unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
     unsigned int start, end, i;
     QEDTable *new_table;
-    struct iovec iov;
     QEMUIOVector qiov;
     size_t len_bytes;
     int ret;
@@ -85,11 +79,7 @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
     len_bytes = (end - start) * sizeof(uint64_t);
 
     new_table = qemu_blockalign(s->bs, len_bytes);
-    iov = (struct iovec) {
-        .iov_base = new_table->offsets,
-        .iov_len = len_bytes,
-    };
-    qemu_iovec_init_external(&qiov, &iov, 1);
+    qemu_iovec_init_buf(&qiov, new_table->offsets, len_bytes);
 
     /* Byteswap table */
     for (i = start; i < end; i++) {
diff --git a/block/qed.c b/block/qed.c
index 1280870024..89af05d524 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -113,18 +113,13 @@ static int coroutine_fn qed_write_header(BDRVQEDState *s)
     int nsectors = DIV_ROUND_UP(sizeof(QEDHeader), BDRV_SECTOR_SIZE);
     size_t len = nsectors * BDRV_SECTOR_SIZE;
     uint8_t *buf;
-    struct iovec iov;
     QEMUIOVector qiov;
     int ret;
 
     assert(s->allocating_acb || s->allocating_write_reqs_plugged);
 
     buf = qemu_blockalign(s->bs, len);
-    iov = (struct iovec) {
-        .iov_base = buf,
-        .iov_len = len,
-    };
-    qemu_iovec_init_external(&qiov, &iov, 1);
+    qemu_iovec_init_buf(&qiov, buf, len);
 
     ret = bdrv_co_preadv(s->bs->file, 0, qiov.size, &qiov, 0);
     if (ret < 0) {
@@ -454,11 +449,14 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
         }
 
         ret = qed_read_string(bs->file, s->header.backing_filename_offset,
-                              s->header.backing_filename_size, bs->backing_file,
-                              sizeof(bs->backing_file));
+                              s->header.backing_filename_size,
+                              bs->auto_backing_file,
+                              sizeof(bs->auto_backing_file));
         if (ret < 0) {
             return ret;
         }
+        pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+                bs->auto_backing_file);
 
         if (s->header.features & QED_F_BACKING_FORMAT_NO_PROBE) {
             pstrcpy(bs->backing_format, sizeof(bs->backing_format), "raw");
@@ -913,7 +911,6 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s,
 {
     QEMUIOVector qiov;
     QEMUIOVector *backing_qiov = NULL;
-    struct iovec iov;
     int ret;
 
     /* Skip copy entirely if there is no work to do */
@@ -921,11 +918,7 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s,
         return 0;
     }
 
-    iov = (struct iovec) {
-        .iov_base = qemu_blockalign(s->bs, len),
-        .iov_len = len,
-    };
-    qemu_iovec_init_external(&qiov, &iov, 1);
+    qemu_iovec_init_buf(&qiov, qemu_blockalign(s->bs, len), len);
 
     ret = qed_read_backing_file(s, pos, &qiov, &backing_qiov);
 
@@ -946,7 +939,7 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s,
     }
     ret = 0;
 out:
-    qemu_vfree(iov.iov_base);
+    qemu_vfree(qemu_iovec_buf(&qiov));
     return ret;
 }
 
@@ -1447,8 +1440,12 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
                                                   BdrvRequestFlags flags)
 {
     BDRVQEDState *s = bs->opaque;
-    QEMUIOVector qiov;
-    struct iovec iov;
+
+    /*
+     * Zero writes start without an I/O buffer.  If a buffer becomes necessary
+     * then it will be allocated during request processing.
+     */
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, bytes);
 
     /* Fall back if the request is not aligned */
     if (qed_offset_into_cluster(s, offset) ||
@@ -1456,13 +1453,6 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
         return -ENOTSUP;
     }
 
-    /* Zero writes start without an I/O buffer.  If a buffer becomes necessary
-     * then it will be allocated during request processing.
-     */
-    iov.iov_base = NULL;
-    iov.iov_len = bytes;
-
-    qemu_iovec_init_external(&qiov, &iov, 1);
     return qed_co_request(bs, offset >> BDRV_SECTOR_BITS, &qiov,
                           bytes >> BDRV_SECTOR_BITS,
                           QED_AIOCB_WRITE | QED_AIOCB_ZERO);
diff --git a/block/quorum.c b/block/quorum.c
index 16b3c8067c..352f729136 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -1065,36 +1065,64 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child,
     bdrv_drained_end(bs);
 }
 
-static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
+static void quorum_gather_child_options(BlockDriverState *bs, QDict *target,
+                                        bool backing_overridden)
 {
     BDRVQuorumState *s = bs->opaque;
-    QDict *opts;
-    QList *children;
+    QList *children_list;
     int i;
 
-    for (i = 0; i < s->num_children; i++) {
-        bdrv_refresh_filename(s->children[i]->bs);
-        if (!s->children[i]->bs->full_open_options) {
-            return;
-        }
-    }
+    /*
+     * The generic implementation for gathering child options in
+     * bdrv_refresh_filename() would use the names of the children
+     * as specified for bdrv_open_child() or bdrv_attach_child(),
+     * which is "children.%u" with %u being a value
+     * (s->next_child_index) that is incremented each time a new child
+     * is added (and never decremented).  Since children can be
+     * deleted at runtime, there may be gaps in that enumeration.
+     * When creating a new quorum BDS and specifying the children for
+     * it through runtime options, the enumeration used there may not
+     * have any gaps, though.
+     *
+     * Therefore, we have to create a new gap-less enumeration here
+     * (which we can achieve by simply putting all of the children's
+     * full_open_options into a QList).
+     *
+     * XXX: Note that there are issues with the current child option
+     *      structure quorum uses (such as the fact that children do
+     *      not really have unique permanent names).  Therefore, this
+     *      is going to have to change in the future and ideally we
+     *      want quorum to be covered by the generic implementation.
+     */
+
+    children_list = qlist_new();
+    qdict_put(target, "children", children_list);
 
-    children = qlist_new();
     for (i = 0; i < s->num_children; i++) {
-        qlist_append(children,
+        qlist_append(children_list,
                      qobject_ref(s->children[i]->bs->full_open_options));
     }
+}
 
-    opts = qdict_new();
-    qdict_put_str(opts, "driver", "quorum");
-    qdict_put_int(opts, QUORUM_OPT_VOTE_THRESHOLD, s->threshold);
-    qdict_put_bool(opts, QUORUM_OPT_BLKVERIFY, s->is_blkverify);
-    qdict_put_bool(opts, QUORUM_OPT_REWRITE, s->rewrite_corrupted);
-    qdict_put(opts, "children", children);
-
-    bs->full_open_options = opts;
+static char *quorum_dirname(BlockDriverState *bs, Error **errp)
+{
+    /* In general, there are multiple BDSs with different dirnames below this
+     * one; so there is no unique dirname we could return (unless all are equal
+     * by chance, or there is only one). Therefore, to be consistent, just
+     * always return NULL. */
+    error_setg(errp, "Cannot generate a base directory for quorum nodes");
+    return NULL;
 }
 
+static const char *const quorum_strong_runtime_opts[] = {
+    QUORUM_OPT_VOTE_THRESHOLD,
+    QUORUM_OPT_BLKVERIFY,
+    QUORUM_OPT_REWRITE,
+    QUORUM_OPT_READ_PATTERN,
+
+    NULL
+};
+
 static BlockDriver bdrv_quorum = {
     .format_name                        = "quorum",
 
@@ -1102,7 +1130,8 @@ static BlockDriver bdrv_quorum = {
 
     .bdrv_open                          = quorum_open,
     .bdrv_close                         = quorum_close,
-    .bdrv_refresh_filename              = quorum_refresh_filename,
+    .bdrv_gather_child_options          = quorum_gather_child_options,
+    .bdrv_dirname                       = quorum_dirname,
 
     .bdrv_co_flush_to_disk              = quorum_co_flush,
 
@@ -1118,6 +1147,8 @@ static BlockDriver bdrv_quorum = {
 
     .is_filter                          = true,
     .bdrv_recurse_is_first_non_filter   = quorum_recurse_is_first_non_filter,
+
+    .strong_runtime_opts                = quorum_strong_runtime_opts,
 };
 
 static void bdrv_quorum_init(void)
diff --git a/block/raw-format.c b/block/raw-format.c
index 6f6dc99b2c..e3e5ba2c8a 100644
--- a/block/raw-format.c
+++ b/block/raw-format.c
@@ -436,6 +436,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
             bs->file->bs->supported_zero_flags);
 
     if (bs->probed && !bdrv_is_read_only(bs)) {
+        bdrv_refresh_filename(bs->file->bs);
         fprintf(stderr,
                 "WARNING: Image format was not specified for '%s' and probing "
                 "guessed raw.\n"
@@ -531,6 +532,13 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs,
                                  read_flags, write_flags);
 }
 
+static const char *const raw_strong_runtime_opts[] = {
+    "offset",
+    "size",
+
+    NULL
+};
+
 BlockDriver bdrv_raw = {
     .format_name          = "raw",
     .instance_size        = sizeof(BDRVRawState),
@@ -560,7 +568,8 @@ BlockDriver bdrv_raw = {
     .bdrv_lock_medium     = &raw_lock_medium,
     .bdrv_co_ioctl        = &raw_co_ioctl,
     .create_opts          = &raw_create_opts,
-    .bdrv_has_zero_init   = &raw_has_zero_init
+    .bdrv_has_zero_init   = &raw_has_zero_init,
+    .strong_runtime_opts  = raw_strong_runtime_opts,
 };
 
 static void bdrv_raw_init(void)
diff --git a/block/rbd.c b/block/rbd.c
index 8a1a9f4b6e..0c549c9935 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -1228,6 +1228,18 @@ static QemuOptsList qemu_rbd_create_opts = {
     }
 };
 
+static const char *const qemu_rbd_strong_runtime_opts[] = {
+    "pool",
+    "image",
+    "conf",
+    "snapshot",
+    "user",
+    "server.",
+    "password-secret",
+
+    NULL
+};
+
 static BlockDriver bdrv_rbd = {
     .format_name            = "rbd",
     .instance_size          = sizeof(BDRVRBDState),
@@ -1265,6 +1277,8 @@ static BlockDriver bdrv_rbd = {
 #ifdef LIBRBD_SUPPORTS_INVALIDATE
     .bdrv_co_invalidate_cache = qemu_rbd_co_invalidate_cache,
 #endif
+
+    .strong_runtime_opts    = qemu_rbd_strong_runtime_opts,
 };
 
 static void bdrv_rbd_init(void)
diff --git a/block/replication.c b/block/replication.c
index e70dd95001..4c80b54daf 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -616,8 +616,6 @@ static void replication_done(void *opaque, int ret)
     if (ret == 0) {
         s->stage = BLOCK_REPLICATION_DONE;
 
-        /* refresh top bs's filename */
-        bdrv_refresh_filename(bs);
         s->active_disk = NULL;
         s->secondary_disk = NULL;
         s->hidden_disk = NULL;
@@ -678,6 +676,13 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
     aio_context_release(aio_context);
 }
 
+static const char *const replication_strong_runtime_opts[] = {
+    REPLICATION_MODE,
+    REPLICATION_TOP_ID,
+
+    NULL
+};
+
 BlockDriver bdrv_replication = {
     .format_name                = "replication",
     .instance_size              = sizeof(BDRVReplicationState),
@@ -694,6 +699,7 @@ BlockDriver bdrv_replication = {
     .bdrv_recurse_is_first_non_filter = replication_recurse_is_first_non_filter,
 
     .has_variable_length        = true,
+    .strong_runtime_opts        = replication_strong_runtime_opts,
 };
 
 static void bdrv_replication_init(void)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index b916ba07bf..cbdfe9ab6e 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -3203,6 +3203,15 @@ static QemuOptsList sd_create_opts = {
     }
 };
 
+static const char *const sd_strong_runtime_opts[] = {
+    "vdi",
+    "snap-id",
+    "tag",
+    "server.",
+
+    NULL
+};
+
 static BlockDriver bdrv_sheepdog = {
     .format_name                  = "sheepdog",
     .protocol_name                = "sheepdog",
@@ -3238,6 +3247,7 @@ static BlockDriver bdrv_sheepdog = {
     .bdrv_attach_aio_context      = sd_attach_aio_context,
 
     .create_opts                  = &sd_create_opts,
+    .strong_runtime_opts          = sd_strong_runtime_opts,
 };
 
 static BlockDriver bdrv_sheepdog_tcp = {
@@ -3275,6 +3285,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
     .bdrv_attach_aio_context      = sd_attach_aio_context,
 
     .create_opts                  = &sd_create_opts,
+    .strong_runtime_opts          = sd_strong_runtime_opts,
 };
 
 static BlockDriver bdrv_sheepdog_unix = {
@@ -3312,6 +3323,7 @@ static BlockDriver bdrv_sheepdog_unix = {
     .bdrv_attach_aio_context      = sd_attach_aio_context,
 
     .create_opts                  = &sd_create_opts,
+    .strong_runtime_opts          = sd_strong_runtime_opts,
 };
 
 static void bdrv_sheepdog_init(void)
diff --git a/block/snapshot.c b/block/snapshot.c
index 3218a542df..f2f48f926a 100644
--- a/block/snapshot.c
+++ b/block/snapshot.c
@@ -63,7 +63,7 @@ int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
     }
     for (i = 0; i < nb_sns; i++) {
         sn = &sn_tab[i];
-        if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
+        if (!strcmp(sn->name, name)) {
             *sn_info = *sn;
             ret = 0;
             break;
@@ -301,26 +301,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
     return ret;
 }
 
-int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
-                                       const char *id_or_name,
-                                       Error **errp)
-{
-    int ret;
-    Error *local_err = NULL;
-
-    ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err);
-    if (ret == -ENOENT || ret == -EINVAL) {
-        error_free(local_err);
-        local_err = NULL;
-        ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err);
-    }
-
-    if (ret < 0) {
-        error_propagate(errp, local_err);
-    }
-    return ret;
-}
-
 int bdrv_snapshot_list(BlockDriverState *bs,
                        QEMUSnapshotInfo **psn_info)
 {
@@ -448,7 +428,8 @@ int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs,
         aio_context_acquire(ctx);
         if (bdrv_can_snapshot(bs) &&
                 bdrv_snapshot_find(bs, snapshot, name) >= 0) {
-            ret = bdrv_snapshot_delete_by_id_or_name(bs, name, err);
+            ret = bdrv_snapshot_delete(bs, snapshot->id_str,
+                                       snapshot->name, err);
         }
         aio_context_release(ctx);
         if (ret < 0) {
diff --git a/block/ssh.c b/block/ssh.c
index bbc513e095..190ef95300 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -1254,6 +1254,17 @@ static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
     return ssh_grow_file(s, offset, errp);
 }
 
+static const char *const ssh_strong_runtime_opts[] = {
+    "host",
+    "port",
+    "path",
+    "user",
+    "host_key_check",
+    "server.",
+
+    NULL
+};
+
 static BlockDriver bdrv_ssh = {
     .format_name                  = "ssh",
     .protocol_name                = "ssh",
@@ -1270,6 +1281,7 @@ static BlockDriver bdrv_ssh = {
     .bdrv_co_truncate             = ssh_co_truncate,
     .bdrv_co_flush_to_disk        = ssh_co_flush,
     .create_opts                  = &ssh_create_opts,
+    .strong_runtime_opts          = ssh_strong_runtime_opts,
 };
 
 static void bdrv_ssh_init(void)
diff --git a/block/stream.c b/block/stream.c
index 7a49ac0992..e14579ff80 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -41,14 +41,9 @@ static int coroutine_fn stream_populate(BlockBackend *blk,
                                         int64_t offset, uint64_t bytes,
                                         void *buf)
 {
-    struct iovec iov = {
-        .iov_base = buf,
-        .iov_len  = bytes,
-    };
-    QEMUIOVector qiov;
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
 
     assert(bytes < SIZE_MAX);
-    qemu_iovec_init_external(&qiov, &iov, 1);
 
     /* Copy-on-read the unallocated clusters */
     return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
diff --git a/block/throttle.c b/block/throttle.c
index 636c9764aa..f64dcc27b9 100644
--- a/block/throttle.c
+++ b/block/throttle.c
@@ -227,6 +227,12 @@ static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs)
     atomic_dec(&tgm->io_limits_disabled);
 }
 
+static const char *const throttle_strong_runtime_opts[] = {
+    QEMU_OPT_THROTTLE_GROUP_NAME,
+
+    NULL
+};
+
 static BlockDriver bdrv_throttle = {
     .format_name                        =   "throttle",
     .instance_size                      =   sizeof(ThrottleGroupMember),
@@ -259,6 +265,7 @@ static BlockDriver bdrv_throttle = {
     .bdrv_co_drain_end                  =   throttle_co_drain_end,
 
     .is_filter                          =   true,
+    .strong_runtime_opts                =   throttle_strong_runtime_opts,
 };
 
 static void bdrv_throttle_init(void)
diff --git a/block/vhdx-log.c b/block/vhdx-log.c
index ecd64266c5..3149ff08d8 100644
--- a/block/vhdx-log.c
+++ b/block/vhdx-log.c
@@ -803,6 +803,7 @@ int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
 
     if (logs.valid) {
         if (bs->read_only) {
+            bdrv_refresh_filename(bs);
             ret = -EPERM;
             error_setg(errp,
                        "VHDX image file '%s' opened read-only, but "
diff --git a/block/vmdk.c b/block/vmdk.c
index 096e8eb662..d8c0c50390 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -27,6 +27,7 @@
 #include "qapi/error.h"
 #include "block/block_int.h"
 #include "sysemu/block-backend.h"
+#include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qerror.h"
 #include "qemu/error-report.h"
 #include "qemu/module.h"
@@ -386,12 +387,14 @@ static int vmdk_parent_open(BlockDriverState *bs)
             ret = -EINVAL;
             goto out;
         }
-        if ((end_name - p_name) > sizeof(bs->backing_file) - 1) {
+        if ((end_name - p_name) > sizeof(bs->auto_backing_file) - 1) {
             ret = -EINVAL;
             goto out;
         }
 
-        pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
+        pstrcpy(bs->auto_backing_file, end_name - p_name + 1, p_name);
+        pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+                bs->auto_backing_file);
     }
 
 out:
@@ -479,6 +482,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
                      extent->l1_table,
                      l1_size);
     if (ret < 0) {
+        bdrv_refresh_filename(extent->file->bs);
         error_setg_errno(errp, -ret,
                          "Could not read l1 table from extent '%s'",
                          extent->file->bs->filename);
@@ -499,6 +503,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
                          extent->l1_backup_table,
                          l1_size);
         if (ret < 0) {
+            bdrv_refresh_filename(extent->file->bs);
             error_setg_errno(errp, -ret,
                              "Could not read l1 backup table from extent '%s'",
                              extent->file->bs->filename);
@@ -530,6 +535,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
 
     ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
     if (ret < 0) {
+        bdrv_refresh_filename(file->bs);
         error_setg_errno(errp, -ret,
                          "Could not read header from file '%s'",
                          file->bs->filename);
@@ -607,6 +613,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
 
     ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
     if (ret < 0) {
+        bdrv_refresh_filename(file->bs);
         error_setg_errno(errp, -ret,
                          "Could not read header from file '%s'",
                          file->bs->filename);
@@ -861,13 +868,13 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
         if (!path_is_absolute(fname) && !path_has_protocol(fname) &&
             !desc_file_path[0])
         {
+            bdrv_refresh_filename(bs->file->bs);
             error_setg(errp, "Cannot use relative extent paths with VMDK "
                        "descriptor file '%s'", bs->file->bs->filename);
             return -EINVAL;
         }
 
-        extent_path = g_malloc0(PATH_MAX);
-        path_combine(extent_path, PATH_MAX, desc_file_path, fname);
+        extent_path = path_combine(desc_file_path, fname);
 
         ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents);
         assert(ret < 32);
@@ -1371,7 +1378,6 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
     VmdkGrainMarker *data = NULL;
     uLongf buf_len;
     QEMUIOVector local_qiov;
-    struct iovec iov;
     int64_t write_offset;
     int64_t write_end_sector;
 
@@ -1399,11 +1405,7 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
         data->size = cpu_to_le32(buf_len);
 
         n_bytes = buf_len + sizeof(VmdkGrainMarker);
-        iov = (struct iovec) {
-            .iov_base   = data,
-            .iov_len    = n_bytes,
-        };
-        qemu_iovec_init_external(&local_qiov, &iov, 1);
+        qemu_iovec_init_buf(&local_qiov, data, n_bytes);
 
         BLKDBG_EVENT(extent->file, BLKDBG_WRITE_COMPRESSED);
     } else {
@@ -2072,16 +2074,16 @@ static int coroutine_fn vmdk_co_do_create(int64_t size,
 
     if (backing_file) {
         BlockBackend *backing;
-        char *full_backing = g_new0(char, PATH_MAX);
-        bdrv_get_full_backing_filename_from_filename(blk_bs(blk)->filename, backing_file,
-                                                     full_backing, PATH_MAX,
-                                                     &local_err);
+        char *full_backing =
+            bdrv_get_full_backing_filename_from_filename(blk_bs(blk)->filename,
+                                                         backing_file,
+                                                         &local_err);
         if (local_err) {
-            g_free(full_backing);
             error_propagate(errp, local_err);
             ret = -ENOENT;
             goto exit;
         }
+        assert(full_backing);
 
         backing = blk_new_open(full_backing, NULL, NULL,
                                BDRV_O_NO_BACKING, errp);
@@ -2260,7 +2262,7 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
     compat6 = qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false);
     if (strcmp(hw_version, "undefined") == 0) {
         g_free(hw_version);
-        hw_version = g_strdup("4");
+        hw_version = NULL;
     }
     fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
     zeroed_grain = qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false);
@@ -2470,6 +2472,7 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
 {
     ImageInfo *info = g_new0(ImageInfo, 1);
 
+    bdrv_refresh_filename(extent->file->bs);
     *info = (ImageInfo){
         .filename         = g_strdup(extent->file->bs->filename),
         .format           = g_strdup(extent->type),
@@ -2601,6 +2604,23 @@ static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
     return 0;
 }
 
+static void vmdk_gather_child_options(BlockDriverState *bs, QDict *target,
+                                      bool backing_overridden)
+{
+    /* No children but file and backing can be explicitly specified (TODO) */
+    qdict_put(target, "file",
+              qobject_ref(bs->file->bs->full_open_options));
+
+    if (backing_overridden) {
+        if (bs->backing) {
+            qdict_put(target, "backing",
+                      qobject_ref(bs->backing->bs->full_open_options));
+        } else {
+            qdict_put_null(target, "backing");
+        }
+    }
+}
+
 static QemuOptsList vmdk_create_opts = {
     .name = "vmdk-create-opts",
     .head = QTAILQ_HEAD_INITIALIZER(vmdk_create_opts.head),
@@ -2672,6 +2692,7 @@ static BlockDriver bdrv_vmdk = {
     .bdrv_get_specific_info       = vmdk_get_specific_info,
     .bdrv_refresh_limits          = vmdk_refresh_limits,
     .bdrv_get_info                = vmdk_get_info,
+    .bdrv_gather_child_options    = vmdk_gather_child_options,
 
     .supports_backing             = true,
     .create_opts                  = &vmdk_create_opts,
diff --git a/block/vpc.c b/block/vpc.c
index 52ab717642..a902a4c54d 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -1218,6 +1218,12 @@ static QemuOptsList vpc_create_opts = {
     }
 };
 
+static const char *const vpc_strong_runtime_opts[] = {
+    VPC_OPT_SIZE_CALC,
+
+    NULL
+};
+
 static BlockDriver bdrv_vpc = {
     .format_name    = "vpc",
     .instance_size  = sizeof(BDRVVPCState),
@@ -1238,6 +1244,7 @@ static BlockDriver bdrv_vpc = {
 
     .create_opts            = &vpc_create_opts,
     .bdrv_has_zero_init     = vpc_has_zero_init,
+    .strong_runtime_opts    = vpc_strong_runtime_opts,
 };
 
 static void bdrv_vpc_init(void)
diff --git a/block/vvfat.c b/block/vvfat.c
index b7b61ea8b7..5f66787890 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -3253,6 +3253,16 @@ static void vvfat_close(BlockDriverState *bs)
     }
 }
 
+static const char *const vvfat_strong_runtime_opts[] = {
+    "dir",
+    "fat-type",
+    "floppy",
+    "label",
+    "rw",
+
+    NULL
+};
+
 static BlockDriver bdrv_vvfat = {
     .format_name            = "vvfat",
     .protocol_name          = "fat",
@@ -3267,6 +3277,8 @@ static BlockDriver bdrv_vvfat = {
     .bdrv_co_preadv         = vvfat_co_preadv,
     .bdrv_co_pwritev        = vvfat_co_pwritev,
     .bdrv_co_block_status   = vvfat_co_block_status,
+
+    .strong_runtime_opts    = vvfat_strong_runtime_opts,
 };
 
 static void bdrv_vvfat_init(void)
diff --git a/block/vxhs.c b/block/vxhs.c
index 0cb0a007e9..2e18229ba4 100644
--- a/block/vxhs.c
+++ b/block/vxhs.c
@@ -556,6 +556,16 @@ static int64_t vxhs_getlength(BlockDriverState *bs)
     return vdisk_size;
 }
 
+static const char *const vxhs_strong_runtime_opts[] = {
+    VXHS_OPT_VDISK_ID,
+    "tls-creds",
+    VXHS_OPT_HOST,
+    VXHS_OPT_PORT,
+    VXHS_OPT_SERVER".",
+
+    NULL
+};
+
 static BlockDriver bdrv_vxhs = {
     .format_name                  = "vxhs",
     .protocol_name                = "vxhs",
@@ -567,6 +577,7 @@ static BlockDriver bdrv_vxhs = {
     .bdrv_getlength               = vxhs_getlength,
     .bdrv_aio_preadv              = vxhs_aio_preadv,
     .bdrv_aio_pwritev             = vxhs_aio_pwritev,
+    .strong_runtime_opts          = vxhs_strong_runtime_opts,
 };
 
 static void bdrv_vxhs_init(void)
diff --git a/blockdev.c b/blockdev.c
index 8714ad2702..7e6bf9955c 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1627,6 +1627,7 @@ static void external_snapshot_prepare(BlkActionState *common,
                 error_setg_errno(errp, -size, "bdrv_getlength failed");
                 goto out;
             }
+            bdrv_refresh_filename(state->old_bs);
             bdrv_img_create(new_image_file, format,
                             state->old_bs->filename,
                             state->old_bs->drv->format_name,
@@ -3230,6 +3231,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
             goto out;
         }
         assert(bdrv_get_aio_context(base_bs) == aio_context);
+        bdrv_refresh_filename(base_bs);
         base_name = base_bs->filename;
     }
 
@@ -3349,6 +3351,10 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
             goto out;
         }
     } else if (has_top && top) {
+        /* This strcmp() is just a shortcut, there is no need to
+         * refresh @bs's filename.  If it mismatches,
+         * bdrv_find_backing_image() will do the refresh and may still
+         * return @bs. */
         if (strcmp(bs->filename, top) != 0) {
             top_bs = bdrv_find_backing_image(bs, top);
         }
@@ -3509,6 +3515,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
     if (backup->mode != NEW_IMAGE_MODE_EXISTING) {
         assert(backup->format);
         if (source) {
+            bdrv_refresh_filename(source);
             bdrv_img_create(backup->target, backup->format, source->filename,
                             source->drv->format_name, NULL,
                             size, flags, false, &local_err);
@@ -3889,6 +3896,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
             break;
         case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
             /* create new image with backing file */
+            bdrv_refresh_filename(source);
             bdrv_img_create(arg->target, format,
                             source->filename,
                             source->drv->format_name,
diff --git a/chardev/spice.c b/chardev/spice.c
index 173c257949..22c30ae833 100644
--- a/chardev/spice.c
+++ b/chardev/spice.c
@@ -2,30 +2,12 @@
 #include "trace.h"
 #include "ui/qemu-spice.h"
 #include "chardev/char.h"
+#include "chardev/spice.h"
 #include "qapi/error.h"
 #include "qemu/error-report.h"
 #include "qemu/option.h"
-#include <spice.h>
 #include <spice/protocol.h>
 
-
-typedef struct SpiceChardev {
-    Chardev               parent;
-
-    SpiceCharDeviceInstance sin;
-    bool                  active;
-    bool                  blocked;
-    const uint8_t         *datapos;
-    int                   datalen;
-    QLIST_ENTRY(SpiceChardev) next;
-} SpiceChardev;
-
-#define TYPE_CHARDEV_SPICE "chardev-spice"
-#define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc"
-#define TYPE_CHARDEV_SPICEPORT "chardev-spiceport"
-
-#define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), TYPE_CHARDEV_SPICE)
-
 typedef struct SpiceCharSource {
     GSource               source;
     SpiceChardev       *scd;
@@ -148,15 +130,25 @@ static void vmc_unregister_interface(SpiceChardev *scd)
 static gboolean spice_char_source_prepare(GSource *source, gint *timeout)
 {
     SpiceCharSource *src = (SpiceCharSource *)source;
+    Chardev *chr = CHARDEV(src->scd);
 
     *timeout = -1;
 
+    if (!chr->be_open) {
+        return true;
+    }
+
     return !src->scd->blocked;
 }
 
 static gboolean spice_char_source_check(GSource *source)
 {
     SpiceCharSource *src = (SpiceCharSource *)source;
+    Chardev *chr = CHARDEV(src->scd);
+
+    if (!chr->be_open) {
+        return true;
+    }
 
     return !src->scd->blocked;
 }
@@ -164,9 +156,12 @@ static gboolean spice_char_source_check(GSource *source)
 static gboolean spice_char_source_dispatch(GSource *source,
     GSourceFunc callback, gpointer user_data)
 {
+    SpiceCharSource *src = (SpiceCharSource *)source;
+    Chardev *chr = CHARDEV(src->scd);
     GIOFunc func = (GIOFunc)callback;
+    GIOCondition cond = chr->be_open ? G_IO_OUT : G_IO_HUP;
 
-    return func(NULL, G_IO_OUT, user_data);
+    return func(NULL, cond, user_data);
 }
 
 static GSourceFuncs SpiceCharSourceFuncs = {
@@ -195,6 +190,12 @@ static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
     int read_bytes;
 
     assert(s->datalen == 0);
+
+    if (!chr->be_open) {
+        trace_spice_chr_discard_write(len);
+        return len;
+    }
+
     s->datapos = buf;
     s->datalen = len;
     spice_server_char_device_wakeup(&s->sin);
@@ -287,13 +288,19 @@ static void qemu_chr_open_spice_vmc(Chardev *chr,
     }
 
     *be_opened = false;
+#if SPICE_SERVER_VERSION < 0x000e02
+    /* Spice < 0.14.2 doesn't explicitly open smartcard chardev */
+    if (strcmp(type, "smartcard") == 0) {
+        *be_opened = true;
+    }
+#endif
     chr_open(chr, type);
 }
 
-static void qemu_chr_open_spice_port(Chardev *chr,
-                                     ChardevBackend *backend,
-                                     bool *be_opened,
-                                     Error **errp)
+void qemu_chr_open_spice_port(Chardev *chr,
+                              ChardevBackend *backend,
+                              bool *be_opened,
+                              Error **errp)
 {
     ChardevSpicePort *spiceport = backend->u.spiceport.data;
     const char *name = spiceport->fqdn;
@@ -309,6 +316,11 @@ static void qemu_chr_open_spice_port(Chardev *chr,
     *be_opened = false;
     s = SPICE_CHARDEV(chr);
     s->sin.portname = g_strdup(name);
+
+    if (using_spice) {
+        /* spice server already created */
+        vmc_register_interface(s);
+    }
 }
 
 void qemu_spice_register_ports(void)
diff --git a/chardev/trace-events b/chardev/trace-events
index d0e5f3bbc1..b8a7596344 100644
--- a/chardev/trace-events
+++ b/chardev/trace-events
@@ -10,6 +10,7 @@ wct_cmd_other(const char *cmd) "%s"
 wct_speed(int speed) "%d"
 
 # chardev/spice.c
+spice_chr_discard_write(int len) "spice chr write discarded %d"
 spice_vmc_write(ssize_t out, int len) "spice wrote %zd of requested %d"
 spice_vmc_read(int bytes, int len) "spice read %d of requested %d"
 spice_vmc_register_interface(void *scd) "spice vmc registered interface %p"
diff --git a/configure b/configure
index a61682c3c7..cefeb8fcce 100755
--- a/configure
+++ b/configure
@@ -368,10 +368,10 @@ libattr=""
 xfs=""
 tcg="yes"
 membarrier=""
-vhost_net="no"
-vhost_crypto="no"
-vhost_scsi="no"
-vhost_vsock="no"
+vhost_net=""
+vhost_crypto=""
+vhost_scsi=""
+vhost_vsock=""
 vhost_user=""
 kvm="no"
 hax="no"
@@ -463,6 +463,7 @@ gnutls=""
 nettle=""
 gcrypt=""
 gcrypt_hmac="no"
+auth_pam=""
 vte=""
 virglrenderer=""
 tpm="yes"
@@ -782,6 +783,7 @@ case $targetos in
 MINGW32*)
   mingw32="yes"
   hax="yes"
+  vhost_user="no"
   audio_possible_drivers="dsound sdl"
   if check_include dsound.h; then
     audio_drv_list="dsound"
@@ -878,15 +880,11 @@ Haiku)
   LIBS="-lposix_error_mapper -lnetwork $LIBS"
 ;;
 Linux)
-  audio_drv_list="try-pa try-alsa try-sdl oss"
+  audio_drv_list="try-pa oss"
   audio_possible_drivers="oss alsa sdl pa"
   linux="yes"
   linux_user="yes"
   kvm="yes"
-  vhost_net="yes"
-  vhost_crypto="yes"
-  vhost_scsi="yes"
-  vhost_vsock="yes"
   QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$PWD/linux-headers $QEMU_INCLUDES"
   supported_os="yes"
   libudev="yes"
@@ -1262,11 +1260,7 @@ for opt do
   ;;
   --disable-vhost-crypto) vhost_crypto="no"
   ;;
-  --enable-vhost-crypto)
-      vhost_crypto="yes"
-      if test "$mingw32" = "yes"; then
-          error_exit "vhost-crypto isn't available on win32"
-      fi
+  --enable-vhost-crypto) vhost_crypto="yes"
   ;;
   --disable-vhost-scsi) vhost_scsi="no"
   ;;
@@ -1381,6 +1375,10 @@ for opt do
   ;;
   --enable-gcrypt) gcrypt="yes"
   ;;
+  --disable-auth-pam) auth_pam="no"
+  ;;
+  --enable-auth-pam) auth_pam="yes"
+  ;;
   --enable-rdma) rdma="yes"
   ;;
   --disable-rdma) rdma="no"
@@ -1471,11 +1469,11 @@ for opt do
   ;;
   --disable-vhost-user) vhost_user="no"
   ;;
-  --enable-vhost-user)
-      vhost_user="yes"
-      if test "$mingw32" = "yes"; then
-          error_exit "vhost-user isn't available on win32"
-      fi
+  --enable-vhost-user) vhost_user="yes"
+  ;;
+  --disable-vhost-kernel) vhost_kernel="no"
+  ;;
+  --enable-vhost-kernel) vhost_kernel="yes"
   ;;
   --disable-capstone) capstone="no"
   ;;
@@ -1507,14 +1505,6 @@ for opt do
   esac
 done
 
-if test "$vhost_user" = ""; then
-    if test "$mingw32" = "yes"; then
-        vhost_user="no"
-    else
-        vhost_user="yes"
-    fi
-fi
-
 case "$cpu" in
     ppc)
            CPU_CFLAGS="-m32"
@@ -1707,6 +1697,7 @@ disabled with --disable-FEATURE, default is enabled if available:
   gnutls          GNUTLS cryptography support
   nettle          nettle cryptography support
   gcrypt          libgcrypt cryptography support
+  auth-pam        PAM access control
   sdl             SDL UI
   sdl_image       SDL Image support for icons
   gtk             gtk UI
@@ -1737,8 +1728,12 @@ disabled with --disable-FEATURE, default is enabled if available:
   linux-aio       Linux AIO support
   cap-ng          libcap-ng support
   attr            attr and xattr support
-  vhost-net       vhost-net acceleration support
-  vhost-crypto    vhost-crypto acceleration support
+  vhost-net       vhost-net kernel acceleration support
+  vhost-vsock     virtio sockets device support
+  vhost-scsi      vhost-scsi kernel target support
+  vhost-crypto    vhost-user-crypto backend support
+  vhost-kernel    vhost kernel backend support
+  vhost-user      vhost-user backend support
   spice           spice
   rbd             rados block device (rbd)
   libiscsi        iscsi support
@@ -1764,7 +1759,6 @@ disabled with --disable-FEATURE, default is enabled if available:
   jemalloc        jemalloc support
   avx2            AVX2 optimization support
   replication     replication support
-  vhost-vsock     virtio sockets device support
   opengl          opengl support
   virglrenderer   virgl rendering support
   xfsctl          xfsctl support
@@ -1781,7 +1775,6 @@ disabled with --disable-FEATURE, default is enabled if available:
   parallels       parallels image format support
   sheepdog        sheepdog block driver support
   crypto-afalg    Linux AF_ALG crypto backend driver
-  vhost-user      vhost-user support
   capstone        capstone disassembler support
   debug-mutex     mutex debugging support
   libpmem         libpmem support
@@ -2171,6 +2164,45 @@ else
   l2tpv3=no
 fi
 
+#########################################
+# vhost interdependencies and host support
+
+# vhost backends
+test "$vhost_user" = "" && vhost_user=yes
+if test "$vhost_user" = "yes" && test "$mingw32" = "yes"; then
+  error_exit "vhost-user isn't available on win32"
+fi
+test "$vhost_kernel" = "" && vhost_kernel=$linux
+if test "$vhost_kernel" = "yes" && test "$linux" != "yes"; then
+  error_exit "vhost-kernel is only available on Linux"
+fi
+
+# vhost-kernel devices
+test "$vhost_scsi" = "" && vhost_scsi=$vhost_kernel
+if test "$vhost_scsi" = "yes" && test "$vhost_kernel" != "yes"; then
+  error_exit "--enable-vhost-scsi requires --enable-vhost-kernel"
+fi
+test "$vhost_vsock" = "" && vhost_vsock=$vhost_kernel
+if test "$vhost_vsock" = "yes" && test "$vhost_kernel" != "yes"; then
+  error_exit "--enable-vhost-vsock requires --enable-vhost-kernel"
+fi
+
+# vhost-user backends
+test "$vhost_net_user" = "" && vhost_net_user=$vhost_user
+if test "$vhost_net_user" = "yes" && test "$vhost_user" = "no"; then
+  error_exit "--enable-vhost-net-user requires --enable-vhost-user"
+fi
+test "$vhost_crypto" = "" && vhost_crypto=$vhost_user
+if test "$vhost_crypto" = "yes" && test "$vhost_user" = "no"; then
+  error_exit "--enable-vhost-crypto requires --enable-vhost-user"
+fi
+
+# OR the vhost-kernel and vhost-user values for simplicity
+if test "$vhost_net" = ""; then
+  test "$vhost_net_user" = "yes" && vhost_net=yes
+  test "$vhost_kernel" = "yes" && vhost_net=yes
+fi
+
 ##########################################
 # MinGW / Mingw-w64 localtime_r/gmtime_r check
 
@@ -2865,6 +2897,33 @@ fi
 
 
 ##########################################
+# PAM probe
+
+if test "$auth_pam" != "no"; then
+    cat > $TMPC <<EOF
+#include <security/pam_appl.h>
+#include <stdio.h>
+int main(void) {
+   const char *service_name = "qemu";
+   const char *user = "frank";
+   const struct pam_conv *pam_conv = NULL;
+   pam_handle_t *pamh = NULL;
+   pam_start(service_name, user, pam_conv, &pamh);
+   return 0;
+}
+EOF
+    if compile_prog "" "-lpam" ; then
+        auth_pam=yes
+    else
+        if test "$auth_pam" = "yes"; then
+            feature_not_found "PAM" "Install PAM development package"
+        else
+            auth_pam=no
+        fi
+    fi
+fi
+
+##########################################
 # getifaddrs (for tests/test-io-channel-socket )
 
 have_ifaddrs_h=yes
@@ -3172,20 +3231,6 @@ if test "$xkbcommon" != "no" ; then
   fi
 fi
 
-##########################################
-# fnmatch() probe, used for ACL routines
-fnmatch="no"
-cat > $TMPC << EOF
-#include <fnmatch.h>
-int main(void)
-{
-    fnmatch("foo", "foo", 0);
-    return 0;
-}
-EOF
-if compile_prog "" "" ; then
-   fnmatch="yes"
-fi
 
 ##########################################
 # xfsctl() probe, used for file-posix.c
@@ -3503,6 +3548,14 @@ for i in $glib_modules; do
     fi
 done
 
+if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then
+    gio=yes
+    gio_cflags=$($pkg_config --cflags gio-2.0)
+    gio_libs=$($pkg_config --libs gio-2.0)
+else
+    gio=no
+fi
+
 # Sanity check that the current size_t matches the
 # size that glib thinks it should be. This catches
 # problems on multi-arch where people try to build
@@ -6083,6 +6136,7 @@ echo "GNUTLS support    $gnutls"
 echo "libgcrypt         $gcrypt"
 echo "nettle            $nettle $(echo_version $nettle $nettle_version)"
 echo "libtasn1          $tasn1"
+echo "PAM               $auth_pam"
 echo "curses support    $curses"
 echo "virgl support     $virglrenderer $(echo_version $virglrenderer $virgl_version)"
 echo "curl support      $curl"
@@ -6374,9 +6428,6 @@ if test "$xkbcommon" = "yes" ; then
   echo "XKBCOMMON_CFLAGS=$xkbcommon_cflags" >> $config_host_mak
   echo "XKBCOMMON_LIBS=$xkbcommon_libs" >> $config_host_mak
 fi
-if test "$fnmatch" = "yes" ; then
-  echo "CONFIG_FNMATCH=y" >> $config_host_mak
-fi
 if test "$xfs" = "yes" ; then
   echo "CONFIG_XFS=y" >> $config_host_mak
 fi
@@ -6520,6 +6571,11 @@ if test "$gtk" = "yes" ; then
     echo "CONFIG_GTK_GL=y" >> $config_host_mak
   fi
 fi
+if test "$gio" = "yes" ; then
+    echo "CONFIG_GIO=y" >> $config_host_mak
+    echo "GIO_CFLAGS=$gio_cflags" >> $config_host_mak
+    echo "GIO_LIBS=$gio_libs" >> $config_host_mak
+fi
 echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak
 if test "$gnutls" = "yes" ; then
   echo "CONFIG_GNUTLS=y" >> $config_host_mak
@@ -6537,6 +6593,9 @@ fi
 if test "$tasn1" = "yes" ; then
   echo "CONFIG_TASN1=y" >> $config_host_mak
 fi
+if test "$auth_pam" = "yes" ; then
+    echo "CONFIG_AUTH_PAM=y" >> $config_host_mak
+fi
 if test "$have_ifaddrs_h" = "yes" ; then
     echo "HAVE_IFADDRS_H=y" >> $config_host_mak
 fi
@@ -6589,8 +6648,11 @@ fi
 if test "$vhost_scsi" = "yes" ; then
   echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak
 fi
-if test "$vhost_net" = "yes" && test "$vhost_user" = "yes"; then
-  echo "CONFIG_VHOST_NET_USED=y" >> $config_host_mak
+if test "$vhost_net" = "yes" ; then
+  echo "CONFIG_VHOST_NET=y" >> $config_host_mak
+fi
+if test "$vhost_net_user" = "yes" ; then
+  echo "CONFIG_VHOST_NET_USER=y" >> $config_host_mak
 fi
 if test "$vhost_crypto" = "yes" ; then
   echo "CONFIG_VHOST_CRYPTO=y" >> $config_host_mak
@@ -6598,6 +6660,9 @@ fi
 if test "$vhost_vsock" = "yes" ; then
   echo "CONFIG_VHOST_VSOCK=y" >> $config_host_mak
 fi
+if test "$vhost_kernel" = "yes" ; then
+  echo "CONFIG_VHOST_KERNEL=y" >> $config_host_mak
+fi
 if test "$vhost_user" = "yes" ; then
   echo "CONFIG_VHOST_USER=y" >> $config_host_mak
 fi
@@ -7368,12 +7433,6 @@ if supported_xen_target $target; then
 fi
 if supported_kvm_target $target; then
     echo "CONFIG_KVM=y" >> $config_target_mak
-    if test "$vhost_net" = "yes" ; then
-        echo "CONFIG_VHOST_NET=y" >> $config_target_mak
-        if test "$vhost_user" = "yes" ; then
-            echo "CONFIG_VHOST_USER_NET_TEST_$target_name=y" >> $config_host_mak
-        fi
-    fi
 fi
 if supported_hax_target $target; then
     echo "CONFIG_HAX=y" >> $config_target_mak
diff --git a/cpus.c b/cpus.c
index 154daf57dc..e83f72b48b 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1333,6 +1333,7 @@ static void *qemu_dummy_cpu_thread_fn(void *arg)
         qemu_wait_io_event(cpu);
     } while (!cpu->unplug);
 
+    qemu_mutex_unlock_iothread();
     rcu_unregister_thread();
     return NULL;
 #endif
diff --git a/crypto/tlssession.c b/crypto/tlssession.c
index 0dedd4af52..c3a920dfe8 100644
--- a/crypto/tlssession.c
+++ b/crypto/tlssession.c
@@ -24,7 +24,7 @@
 #include "crypto/tlscredspsk.h"
 #include "crypto/tlscredsx509.h"
 #include "qapi/error.h"
-#include "qemu/acl.h"
+#include "authz/base.h"
 #include "trace.h"
 
 #ifdef CONFIG_GNUTLS
@@ -37,7 +37,7 @@ struct QCryptoTLSSession {
     QCryptoTLSCreds *creds;
     gnutls_session_t handle;
     char *hostname;
-    char *aclname;
+    char *authzid;
     bool handshakeComplete;
     QCryptoTLSSessionWriteFunc writeFunc;
     QCryptoTLSSessionReadFunc readFunc;
@@ -56,7 +56,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session)
     gnutls_deinit(session->handle);
     g_free(session->hostname);
     g_free(session->peername);
-    g_free(session->aclname);
+    g_free(session->authzid);
     object_unref(OBJECT(session->creds));
     g_free(session);
 }
@@ -95,7 +95,7 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len)
 QCryptoTLSSession *
 qcrypto_tls_session_new(QCryptoTLSCreds *creds,
                         const char *hostname,
-                        const char *aclname,
+                        const char *authzid,
                         QCryptoTLSCredsEndpoint endpoint,
                         Error **errp)
 {
@@ -105,13 +105,13 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds,
     session = g_new0(QCryptoTLSSession, 1);
     trace_qcrypto_tls_session_new(
         session, creds, hostname ? hostname : "<none>",
-        aclname ? aclname : "<none>", endpoint);
+        authzid ? authzid : "<none>", endpoint);
 
     if (hostname) {
         session->hostname = g_strdup(hostname);
     }
-    if (aclname) {
-        session->aclname = g_strdup(aclname);
+    if (authzid) {
+        session->authzid = g_strdup(authzid);
     }
     session->creds = creds;
     object_ref(OBJECT(creds));
@@ -262,6 +262,7 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSession *session,
     unsigned int nCerts, i;
     time_t now;
     gnutls_x509_crt_t cert = NULL;
+    Error *err = NULL;
 
     now = time(NULL);
     if (now == ((time_t)-1)) {
@@ -349,19 +350,17 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSession *session,
                            gnutls_strerror(ret));
                 goto error;
             }
-            if (session->aclname) {
-                qemu_acl *acl = qemu_acl_find(session->aclname);
-                int allow;
-                if (!acl) {
-                    error_setg(errp, "Cannot find ACL %s",
-                               session->aclname);
+            if (session->authzid) {
+                bool allow;
+
+                allow = qauthz_is_allowed_by_id(session->authzid,
+                                                session->peername, &err);
+                if (err) {
+                    error_propagate(errp, err);
                     goto error;
                 }
-
-                allow = qemu_acl_party_is_allowed(acl, session->peername);
-
                 if (!allow) {
-                    error_setg(errp, "TLS x509 ACL check for %s is denied",
+                    error_setg(errp, "TLS x509 authz check for %s is denied",
                                session->peername);
                     goto error;
                 }
@@ -555,7 +554,7 @@ qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session)
 QCryptoTLSSession *
 qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED,
                         const char *hostname G_GNUC_UNUSED,
-                        const char *aclname G_GNUC_UNUSED,
+                        const char *authzid G_GNUC_UNUSED,
                         QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED,
                         Error **errp)
 {
diff --git a/crypto/trace-events b/crypto/trace-events
index 597389b73c..a38ad7b787 100644
--- a/crypto/trace-events
+++ b/crypto/trace-events
@@ -19,5 +19,5 @@ qcrypto_tls_creds_x509_load_cert(void *creds, int isServer, const char *file) "T
 qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds x509 load cert list creds=%p file=%s"
 
 # crypto/tlssession.c
-qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *aclname, int endpoint) "TLS session new session=%p creds=%p hostname=%s aclname=%s endpoint=%d"
+qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *authzid, int endpoint) "TLS session new session=%p creds=%p hostname=%s authzid=%s endpoint=%d"
 qcrypto_tls_session_check_creds(void *session, const char *status) "TLS session check creds session=%p status=%s"
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 87ad267494..bd6943b691 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -119,6 +119,7 @@ CONFIG_IOTKIT_SECCTL=y
 CONFIG_IOTKIT_SYSCTL=y
 CONFIG_IOTKIT_SYSINFO=y
 CONFIG_ARMSSE_CPUID=y
+CONFIG_ARMSSE_MHU=y
 
 CONFIG_VERSATILE=y
 CONFIG_VERSATILE_PCI=y
diff --git a/default-configs/virtio.mak b/default-configs/virtio.mak
index ecb4420e74..b653aa06b1 100644
--- a/default-configs/virtio.mak
+++ b/default-configs/virtio.mak
@@ -1,5 +1,5 @@
-CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX))
-CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX))
+CONFIG_VHOST_USER_SCSI=$(CONFIG_VHOST_USER)
+CONFIG_VHOST_USER_BLK=$(CONFIG_VHOST_USER)
 CONFIG_VIRTIO=y
 CONFIG_VIRTIO_9P=$(CONFIG_VIRTFS)
 CONFIG_VIRTIO_BALLOON=y
diff --git a/disas/nanomips.cpp b/disas/nanomips.cpp
index f90f1a958b..f090f61c76 100644
--- a/disas/nanomips.cpp
+++ b/disas/nanomips.cpp
@@ -1058,7 +1058,7 @@ uint64 NMD::extract_stripe_6(uint64 instruction)
 }
 
 
-uint64 NMD::extract_ac_13_12(uint64 instruction)
+uint64 NMD::extract_ac_15_14(uint64 instruction)
 {
     uint64 value = 0;
     value |= extract_bits(instruction, 14, 2);
@@ -6375,7 +6375,7 @@ std::string NMD::DPA_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6399,7 +6399,7 @@ std::string NMD::DPAQ_SA_L_W(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6423,7 +6423,7 @@ std::string NMD::DPAQ_S_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6447,7 +6447,7 @@ std::string NMD::DPAQX_SA_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6471,7 +6471,7 @@ std::string NMD::DPAQX_S_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6495,7 +6495,7 @@ std::string NMD::DPAU_H_QBL(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6519,7 +6519,7 @@ std::string NMD::DPAU_H_QBR(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6543,7 +6543,7 @@ std::string NMD::DPAX_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6567,7 +6567,7 @@ std::string NMD::DPS_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6591,7 +6591,7 @@ std::string NMD::DPSQ_SA_L_W(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6615,7 +6615,7 @@ std::string NMD::DPSQ_S_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6639,7 +6639,7 @@ std::string NMD::DPSQX_SA_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6663,7 +6663,7 @@ std::string NMD::DPSQX_S_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6687,7 +6687,7 @@ std::string NMD::DPSU_H_QBL(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6711,7 +6711,7 @@ std::string NMD::DPSU_H_QBR(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -6735,7 +6735,7 @@ std::string NMD::DPSX_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -7373,7 +7373,7 @@ std::string NMD::EXTPDP(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 size_value = extract_size_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7397,7 +7397,7 @@ std::string NMD::EXTPDPV(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7421,7 +7421,7 @@ std::string NMD::EXTP(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 size_value = extract_size_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7445,7 +7445,7 @@ std::string NMD::EXTPV(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7469,7 +7469,7 @@ std::string NMD::EXTR_RS_W(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 shift_value = extract_shift_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7493,7 +7493,7 @@ std::string NMD::EXTR_R_W(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 shift_value = extract_shift_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7517,7 +7517,7 @@ std::string NMD::EXTR_S_H(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 shift_value = extract_shift_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7541,7 +7541,7 @@ std::string NMD::EXTR_W(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 shift_value = extract_shift_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7565,7 +7565,7 @@ std::string NMD::EXTRV_RS_W(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7589,7 +7589,7 @@ std::string NMD::EXTRV_R_W(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7613,7 +7613,7 @@ std::string NMD::EXTRV_S_H(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -7637,7 +7637,7 @@ std::string NMD::EXTRV_W(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -9719,7 +9719,7 @@ std::string NMD::MADD_DSP_(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -9792,7 +9792,7 @@ std::string NMD::MADDU_DSP_(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -9817,7 +9817,7 @@ std::string NMD::MAQ_S_W_PHL(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -9842,7 +9842,7 @@ std::string NMD::MAQ_S_W_PHR(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -9867,7 +9867,7 @@ std::string NMD::MAQ_SA_W_PHL(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -9892,7 +9892,7 @@ std::string NMD::MAQ_SA_W_PHR(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -10195,7 +10195,7 @@ std::string NMD::MFHGC0(uint64 instruction)
 std::string NMD::MFHI_DSP_(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -10243,7 +10243,7 @@ std::string NMD::MFHTR(uint64 instruction)
 std::string NMD::MFLO_DSP_(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rt = GPR(copy(rt_value));
     std::string ac = AC(copy(ac_value));
@@ -10652,7 +10652,7 @@ std::string NMD::MSUB_DSP_(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -10724,7 +10724,7 @@ std::string NMD::MSUBU_DSP_(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -10931,7 +10931,7 @@ std::string NMD::MTHGC0(uint64 instruction)
 std::string NMD::MTHI_DSP_(uint64 instruction)
 {
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rs = GPR(copy(rs_value));
     std::string ac = AC(copy(ac_value));
@@ -10953,7 +10953,7 @@ std::string NMD::MTHI_DSP_(uint64 instruction)
 std::string NMD::MTHLIP(uint64 instruction)
 {
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rs = GPR(copy(rs_value));
     std::string ac = AC(copy(ac_value));
@@ -11001,7 +11001,7 @@ std::string NMD::MTHTR(uint64 instruction)
 std::string NMD::MTLO_DSP_(uint64 instruction)
 {
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rs = GPR(copy(rs_value));
     std::string ac = AC(copy(ac_value));
@@ -11432,7 +11432,7 @@ std::string NMD::MULSA_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -11456,7 +11456,7 @@ std::string NMD::MULSAQ_S_W_PH(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -11480,7 +11480,7 @@ std::string NMD::MULT_DSP_(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -11504,7 +11504,7 @@ std::string NMD::MULTU_DSP_(uint64 instruction)
 {
     uint64 rt_value = extract_rt_25_24_23_22_21(instruction);
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string ac = AC(copy(ac_value));
     std::string rs = GPR(copy(rs_value));
@@ -13932,7 +13932,7 @@ std::string NMD::SHE(uint64 instruction)
 std::string NMD::SHILO(uint64 instruction)
 {
     int64 shift_value = extract_shift__se5_21_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string shift = IMMEDIATE(copy(shift_value));
     std::string ac = AC(copy(ac_value));
@@ -13954,7 +13954,7 @@ std::string NMD::SHILO(uint64 instruction)
 std::string NMD::SHILOV(uint64 instruction)
 {
     uint64 rs_value = extract_rs_20_19_18_17_16(instruction);
-    uint64 ac_value = extract_ac_13_12(instruction);
+    uint64 ac_value = extract_ac_15_14(instruction);
 
     std::string rs = GPR(copy(rs_value));
     std::string ac = AC(copy(ac_value));
diff --git a/disas/nanomips.h b/disas/nanomips.h
index 6482edafe3..243c3e38d2 100644
--- a/disas/nanomips.h
+++ b/disas/nanomips.h
@@ -159,7 +159,7 @@ private:
     int64 extract_s__se31_0_11_to_2_20_to_12_s12(uint64 instruction);
     int64 extract_shift__se5_21_20_19_18_17_16(uint64 instruction);
 
-    uint64 extract_ac_13_12(uint64 instruction);
+    uint64 extract_ac_15_14(uint64 instruction);
     uint64 extract_bit_16_15_14_13_12_11(uint64 instruction);
     uint64 extract_bit_23_22_21(uint64 instruction);
     uint64 extract_c0s_20_19_18_17_16(uint64 instruction);
diff --git a/docs/vfio-ap.txt b/docs/vfio-ap.txt
index 12339684cd..8cd060a01e 100644
--- a/docs/vfio-ap.txt
+++ b/docs/vfio-ap.txt
@@ -440,8 +440,7 @@ unassign_control_domain
    'unassign_domain' file. This may be done multiple times to unassign more than
    one control domain.
 
-Notes: Hot plug/unplug is not currently supported for mediated AP matrix
-devices, so no changes to the AP matrix will be allowed while a guest using
+Notes: No changes to the AP matrix will be allowed while a guest using
 the mediated matrix device is running. Attempts to assign an adapter,
 domain or control domain will be rejected and an error (EBUSY) returned.
 
@@ -562,6 +561,54 @@ facilities:
                      for guest usage, no AP devices can be made accessible to a
                      guest started without APFT installed.
 
+Hot plug a vfio-ap device into a running guest:
+==============================================
+Only one vfio-ap device can be attached to the virtual machine's ap-bus, so a
+vfio-ap device can be hot plugged if and only if no vfio-ap device is attached
+to the bus already, whether via the QEMU command line or a prior hot plug
+action.
+
+To hot plug a vfio-ap device, use the QEMU device_add command:
+
+    (qemu) device_add vfio-ap,sysfsdev="$path-to-mdev"
+
+    Where the '$path-to-mdev' value specifies the absolute path to a mediated
+    device to which AP resources to be used by the guest have been assigned.
+
+Note that on Linux guests, the AP devices will be created in the
+/sys/bus/ap/devices directory when the AP bus subsequently performs its periodic
+scan, so there may be a short delay before the AP devices are accessible on the
+guest.
+
+The command will fail if:
+
+* A vfio-ap device has already been attached to the virtual machine's ap-bus.
+
+* The CPU model features for controlling guest access to AP facilities are not
+  enabled (see 'CPU model features' subsection in the previous section).
+
+Hot unplug a vfio-ap device from a running guest:
+================================================
+A vfio-ap device can be unplugged from a running KVM guest if a vfio-ap device
+has been attached to the virtual machine's ap-bus via the QEMU command line
+or a prior hot plug action.
+
+To hot unplug a vfio-ap device, use the QEMU device_del command:
+
+    (qemu) device_del vfio-ap,sysfsdev="$path-to-mdev"
+
+    Where $path-to-mdev is the same as the path specified when the vfio-ap
+    device was attached to the virtual machine's ap-bus.
+
+On a Linux guest, the AP devices will be removed from the /sys/bus/ap/devices
+directory on the guest when the AP bus subsequently performs its periodic scan,
+so there may be a short delay before the AP devices are no longer accessible by
+the guest.
+
+The command will fail if the $path-to-mdev specified on the device_del command
+does not match the value specified when the vfio-ap device was attached to
+the virtual machine's ap-bus.
+
 Example: Configure AP Matrixes for Three Linux Guests:
 =====================================================
 Let's now provide an example to illustrate how KVM guests may be given
@@ -819,7 +866,11 @@ Limitations
   assigned lest the host be given access to the private data of the AP queue
   device, such as a private key configured specifically for the guest.
 
-* Dynamically modifying the AP matrix for a running guest (which would amount to
-  hot(un)plug of AP devices for the guest) is currently not supported
+* Dynamically assigning AP resources to or unassigning AP resources from a
+  mediated matrix device - see 'Configuring an AP matrix for a linux guest'
+  section above - while a running guest is using it is currently not supported.
 
-* Live guest migration is not supported for guests using AP devices.
+* Live guest migration is not supported for guests using AP devices. If a guest
+  is using AP devices, the vfio-ap device configured for the guest must be
+  unplugged before migrating the guest (see 'Hot unplug a vfio-ap device from a
+  running guest' section above.
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 9132d7a0b0..4610738ab1 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -696,6 +696,7 @@ static FloatParts sf_canonicalize(FloatParts part, const FloatFmt *parm,
 static FloatParts round_canonical(FloatParts p, float_status *s,
                                   const FloatFmt *parm)
 {
+    const uint64_t frac_lsb = parm->frac_lsb;
     const uint64_t frac_lsbm1 = parm->frac_lsbm1;
     const uint64_t round_mask = parm->round_mask;
     const uint64_t roundeven_mask = parm->roundeven_mask;
@@ -731,6 +732,10 @@ static FloatParts round_canonical(FloatParts p, float_status *s,
             inc = p.sign ? round_mask : 0;
             overflow_norm = !p.sign;
             break;
+        case float_round_to_odd:
+            overflow_norm = true;
+            inc = frac & frac_lsb ? 0 : round_mask;
+            break;
         default:
             g_assert_not_reached();
         }
@@ -778,9 +783,14 @@ static FloatParts round_canonical(FloatParts p, float_status *s,
             shift64RightJamming(frac, 1 - exp, &frac);
             if (frac & round_mask) {
                 /* Need to recompute round-to-even.  */
-                if (s->float_rounding_mode == float_round_nearest_even) {
+                switch (s->float_rounding_mode) {
+                case float_round_nearest_even:
                     inc = ((frac & roundeven_mask) != frac_lsbm1
                            ? frac_lsbm1 : 0);
+                    break;
+                case float_round_to_odd:
+                    inc = frac & frac_lsb ? 0 : round_mask;
+                    break;
                 }
                 flags |= float_flag_inexact;
                 frac += inc;
@@ -1988,6 +1998,9 @@ static FloatParts round_to_int(FloatParts a, int rmode,
             case float_round_down:
                 one = a.sign;
                 break;
+            case float_round_to_odd:
+                one = true;
+                break;
             default:
                 g_assert_not_reached();
             }
@@ -2021,6 +2034,9 @@ static FloatParts round_to_int(FloatParts a, int rmode,
             case float_round_down:
                 inc = a.sign ? rnd_mask : 0;
                 break;
+            case float_round_to_odd:
+                inc = a.frac & frac_lsb ? 0 : rnd_mask;
+                break;
             default:
                 g_assert_not_reached();
             }
@@ -3314,6 +3330,9 @@ static int32_t roundAndPackInt32(flag zSign, uint64_t absZ, float_status *status
     case float_round_down:
         roundIncrement = zSign ? 0x7f : 0;
         break;
+    case float_round_to_odd:
+        roundIncrement = absZ & 0x80 ? 0 : 0x7f;
+        break;
     default:
         abort();
     }
@@ -3368,6 +3387,9 @@ static int64_t roundAndPackInt64(flag zSign, uint64_t absZ0, uint64_t absZ1,
     case float_round_down:
         increment = zSign && absZ1;
         break;
+    case float_round_to_odd:
+        increment = !(absZ0 & 1) && absZ1;
+        break;
     default:
         abort();
     }
@@ -3424,6 +3446,9 @@ static int64_t roundAndPackUint64(flag zSign, uint64_t absZ0,
     case float_round_down:
         increment = zSign && absZ1;
         break;
+    case float_round_to_odd:
+        increment = !(absZ0 & 1) && absZ1;
+        break;
     default:
         abort();
     }
@@ -3526,6 +3551,9 @@ static float32 roundAndPackFloat32(flag zSign, int zExp, uint32_t zSig,
     case float_round_down:
         roundIncrement = zSign ? 0x7f : 0;
         break;
+    case float_round_to_odd:
+        roundIncrement = zSig & 0x80 ? 0 : 0x7f;
+        break;
     default:
         abort();
         break;
@@ -3536,8 +3564,10 @@ static float32 roundAndPackFloat32(flag zSign, int zExp, uint32_t zSig,
              || (    ( zExp == 0xFD )
                   && ( (int32_t) ( zSig + roundIncrement ) < 0 ) )
            ) {
+            bool overflow_to_inf = roundingMode != float_round_to_odd &&
+                                   roundIncrement != 0;
             float_raise(float_flag_overflow | float_flag_inexact, status);
-            return packFloat32( zSign, 0xFF, - ( roundIncrement == 0 ));
+            return packFloat32(zSign, 0xFF, -!overflow_to_inf);
         }
         if ( zExp < 0 ) {
             if (status->flush_to_zero) {
@@ -3555,6 +3585,13 @@ static float32 roundAndPackFloat32(flag zSign, int zExp, uint32_t zSig,
             if (isTiny && roundBits) {
                 float_raise(float_flag_underflow, status);
             }
+            if (roundingMode == float_round_to_odd) {
+                /*
+                 * For round-to-odd case, the roundIncrement depends on
+                 * zSig which just changed.
+                 */
+                roundIncrement = zSig & 0x80 ? 0 : 0x7f;
+            }
         }
     }
     if (roundBits) {
@@ -6793,6 +6830,35 @@ uint32_t float128_to_uint32_round_to_zero(float128 a, float_status *status)
 }
 
 /*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point value
+| `a' to the 32-bit unsigned integer format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode.  If `a' is a NaN, the largest
+| positive integer is returned.  If the conversion overflows, the
+| largest unsigned integer is returned.  If 'a' is negative, the value is
+| rounded and zero is returned; negative values that do not round to zero
+| will raise the inexact exception.
+*----------------------------------------------------------------------------*/
+
+uint32_t float128_to_uint32(float128 a, float_status *status)
+{
+    uint64_t v;
+    uint32_t res;
+    int old_exc_flags = get_float_exception_flags(status);
+
+    v = float128_to_uint64(a, status);
+    if (v > 0xffffffff) {
+        res = 0xffffffff;
+    } else {
+        return v;
+    }
+    set_float_exception_flags(old_exc_flags, status);
+    float_raise(float_flag_invalid, status);
+    return res;
+}
+
+/*----------------------------------------------------------------------------
 | Returns the result of converting the quadruple-precision floating-point
 | value `a' to the single-precision floating-point format.  The conversion
 | is performed according to the IEC/IEEE Standard for Binary Floating-Point
@@ -6958,6 +7024,15 @@ float128 float128_round_to_int(float128 a, float_status *status)
                 add128(z.high, z.low, 0, roundBitsMask, &z.high, &z.low);
             }
             break;
+        case float_round_to_odd:
+            /*
+             * Note that if lastBitMask == 0, the last bit is the lsb
+             * of high, and roundBitsMask == -1.
+             */
+            if ((lastBitMask ? z.low & lastBitMask : z.high & 1) == 0) {
+                add128(z.high, z.low, 0, roundBitsMask, &z.high, &z.low);
+            }
+            break;
         default:
             abort();
         }
@@ -6969,7 +7044,7 @@ float128 float128_round_to_int(float128 a, float_status *status)
             status->float_exception_flags |= float_flag_inexact;
             aSign = extractFloat128Sign( a );
             switch (status->float_rounding_mode) {
-             case float_round_nearest_even:
+            case float_round_nearest_even:
                 if (    ( aExp == 0x3FFE )
                      && (   extractFloat128Frac0( a )
                           | extractFloat128Frac1( a ) )
@@ -6982,14 +7057,17 @@ float128 float128_round_to_int(float128 a, float_status *status)
                     return packFloat128(aSign, 0x3FFF, 0, 0);
                 }
                 break;
-             case float_round_down:
+            case float_round_down:
                 return
                       aSign ? packFloat128( 1, 0x3FFF, 0, 0 )
                     : packFloat128( 0, 0, 0, 0 );
-             case float_round_up:
+            case float_round_up:
                 return
                       aSign ? packFloat128( 1, 0, 0, 0 )
                     : packFloat128( 0, 0x3FFF, 0, 0 );
+
+            case float_round_to_odd:
+                return packFloat128(aSign, 0x3FFF, 0, 0);
             }
             return packFloat128( aSign, 0, 0, 0 );
         }
@@ -7022,6 +7100,12 @@ float128 float128_round_to_int(float128 a, float_status *status)
                 z.high += roundBitsMask;
             }
             break;
+        case float_round_to_odd:
+            if ((z.high & lastBitMask) == 0) {
+                z.high |= (a.low != 0);
+                z.high += roundBitsMask;
+            }
+            break;
         default:
             abort();
         }
diff --git a/hmp-commands.hx b/hmp-commands.hx
index ba71558c25..e5fbc2ca59 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -350,49 +350,57 @@ ETEXI
     {
         .name       = "savevm",
         .args_type  = "name:s?",
-        .params     = "[tag|id]",
-        .help       = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
+        .params     = "tag",
+        .help       = "save a VM snapshot. If no tag is provided, a new snapshot is created",
         .cmd        = hmp_savevm,
     },
 
 STEXI
-@item savevm [@var{tag}|@var{id}]
+@item savevm @var{tag}
 @findex savevm
 Create a snapshot of the whole virtual machine. If @var{tag} is
 provided, it is used as human readable identifier. If there is already
-a snapshot with the same tag or ID, it is replaced. More info at
+a snapshot with the same tag, it is replaced. More info at
 @ref{vm_snapshots}.
+
+Since 4.0, savevm stopped allowing the snapshot id to be set, accepting
+only @var{tag} as parameter.
 ETEXI
 
     {
         .name       = "loadvm",
         .args_type  = "name:s",
-        .params     = "tag|id",
-        .help       = "restore a VM snapshot from its tag or id",
+        .params     = "tag",
+        .help       = "restore a VM snapshot from its tag",
         .cmd        = hmp_loadvm,
         .command_completion = loadvm_completion,
     },
 
 STEXI
-@item loadvm @var{tag}|@var{id}
+@item loadvm @var{tag}
 @findex loadvm
 Set the whole virtual machine to the snapshot identified by the tag
-@var{tag} or the unique snapshot ID @var{id}.
+@var{tag}.
+
+Since 4.0, loadvm stopped accepting snapshot id as parameter.
 ETEXI
 
     {
         .name       = "delvm",
         .args_type  = "name:s",
-        .params     = "tag|id",
-        .help       = "delete a VM snapshot from its tag or id",
+        .params     = "tag",
+        .help       = "delete a VM snapshot from its tag",
         .cmd        = hmp_delvm,
         .command_completion = delvm_completion,
     },
 
 STEXI
-@item delvm @var{tag}|@var{id}
+@item delvm @var{tag}
 @findex delvm
-Delete the snapshot identified by @var{tag} or @var{id}.
+Delete the snapshot identified by @var{tag}.
+
+Since 4.0, delvm stopped deleting snapshots by snapshot id, accepting
+only @var{tag} as parameter.
 ETEXI
 
     {
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index df8c0db909..8fd25a5926 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -302,6 +302,11 @@ static const VMStateDescription vmstate_cpuhp_state = {
     }
 };
 
+static bool piix4_vmstate_need_smbus(void *opaque, int version_id)
+{
+    return pm_smbus_vmstate_needed();
+}
+
 /* qemu-kvm 1.2 uses version 3 but advertised as 2
  * To support incoming qemu-kvm 1.2 migration, change version_id
  * and minimum_version_id to 2 below (which breaks migration from
@@ -321,6 +326,8 @@ static const VMStateDescription vmstate_acpi = {
         VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState),
         VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState),
         VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState),
+        VMSTATE_STRUCT_TEST(smb, PIIX4PMState, piix4_vmstate_need_smbus, 3,
+                            pmsmb_vmstate, PMSMBus),
         VMSTATE_TIMER_PTR(ar.tmr.timer, PIIX4PMState),
         VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState),
         VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),
diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c
index 129e7ea7fe..76cc690579 100644
--- a/hw/arm/armsse.c
+++ b/hw/arm/armsse.c
@@ -11,6 +11,7 @@
 
 #include "qemu/osdep.h"
 #include "qemu/log.h"
+#include "qemu/bitops.h"
 #include "qapi/error.h"
 #include "trace.h"
 #include "hw/sysbus.h"
@@ -29,6 +30,7 @@ struct ARMSSEInfo {
     int sram_banks;
     int num_cpus;
     uint32_t sys_version;
+    uint32_t cpuwait_rst;
     SysConfigFormat sys_config_format;
     bool has_mhus;
     bool has_ppus;
@@ -43,6 +45,7 @@ static const ARMSSEInfo armsse_variants[] = {
         .sram_banks = 1,
         .num_cpus = 1,
         .sys_version = 0x41743,
+        .cpuwait_rst = 0,
         .sys_config_format = IoTKitFormat,
         .has_mhus = false,
         .has_ppus = false,
@@ -55,6 +58,7 @@ static const ARMSSEInfo armsse_variants[] = {
         .sram_banks = 4,
         .num_cpus = 2,
         .sys_version = 0x22041743,
+        .cpuwait_rst = 2,
         .sys_config_format = SSE200Format,
         .has_mhus = true,
         .has_ppus = true,
@@ -282,9 +286,9 @@ static void armsse_init(Object *obj)
                           sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO);
     if (info->has_mhus) {
         sysbus_init_child_obj(obj, "mhu0", &s->mhu[0], sizeof(s->mhu[0]),
-                              TYPE_UNIMPLEMENTED_DEVICE);
+                              TYPE_ARMSSE_MHU);
         sysbus_init_child_obj(obj, "mhu1", &s->mhu[1], sizeof(s->mhu[1]),
-                              TYPE_UNIMPLEMENTED_DEVICE);
+                              TYPE_ARMSSE_MHU);
     }
     if (info->has_ppus) {
         for (i = 0; i < info->num_cpus; i++) {
@@ -495,30 +499,33 @@ static void armsse_realize(DeviceState *dev, Error **errp)
 
         qdev_prop_set_uint32(cpudev, "num-irq", s->exp_numirq + 32);
         /*
-         * In real hardware the initial Secure VTOR is set from the INITSVTOR0
-         * register in the IoT Kit System Control Register block, and the
-         * initial value of that is in turn specifiable by the FPGA that
-         * instantiates the IoT Kit. In QEMU we don't implement this wrinkle,
-         * and simply set the CPU's init-svtor to the IoT Kit default value.
-         * In SSE-200 the situation is similar, except that the default value
-         * is a reset-time signal input. Typically a board using the SSE-200
-         * will have a system control processor whose boot firmware initializes
-         * the INITSVTOR* registers before powering up the CPUs in any case,
-         * so the hardware's default value doesn't matter. QEMU doesn't emulate
+         * In real hardware the initial Secure VTOR is set from the INITSVTOR*
+         * registers in the IoT Kit System Control Register block. In QEMU
+         * we set the initial value here, and also the reset value of the
+         * sysctl register, from this object's QOM init-svtor property.
+         * If the guest changes the INITSVTOR* registers at runtime then the
+         * code in iotkit-sysctl.c will update the CPU init-svtor property
+         * (which will then take effect on the next CPU warm-reset).
+         *
+         * Note that typically a board using the SSE-200 will have a system
+         * control processor whose boot firmware initializes the INITSVTOR*
+         * registers before powering up the CPUs. QEMU doesn't emulate
          * the control processor, so instead we behave in the way that the
-         * firmware does. The initial value is configurable by the board code
-         * to match whatever its firmware does.
+         * firmware does: the initial value should be set by the board code
+         * (using the init-svtor property on the ARMSSE object) to match
+         * whatever its firmware does.
          */
         qdev_prop_set_uint32(cpudev, "init-svtor", s->init_svtor);
         /*
-         * Start all CPUs except CPU0 powered down. In real hardware it is
-         * a configurable property of the SSE-200 which CPUs start powered up
-         * (via the CPUWAIT0_RST and CPUWAIT1_RST parameters), but since all
-         * the boards we care about start CPU0 and leave CPU1 powered off,
-         * we hard-code that for now. We can add QOM properties for this
+         * CPUs start powered down if the corresponding bit in the CPUWAIT
+         * register is 1. In real hardware the CPUWAIT register reset value is
+         * a configurable property of the SSE-200 (via the CPUWAIT0_RST and
+         * CPUWAIT1_RST parameters), but since all the boards we care about
+         * start CPU0 and leave CPU1 powered off, we hard-code that in
+         * info->cpuwait_rst for now. We can add QOM properties for this
          * later if necessary.
          */
-        if (i > 0) {
+        if (extract32(info->cpuwait_rst, i, 1)) {
             object_property_set_bool(cpuobj, true, "start-powered-off", &err);
             if (err) {
                 error_propagate(errp, err);
@@ -766,22 +773,28 @@ static void armsse_realize(DeviceState *dev, Error **errp)
     }
 
     if (info->has_mhus) {
+        /*
+         * An SSE-200 with only one CPU should have only one MHU created,
+         * with the region where the second MHU usually is being RAZ/WI.
+         * We don't implement that SSE-200 config; if we want to support
+         * it then this code needs to be enhanced to handle creating the
+         * RAZ/WI region instead of the second MHU.
+         */
+        assert(info->num_cpus == ARRAY_SIZE(s->mhu));
+
         for (i = 0; i < ARRAY_SIZE(s->mhu); i++) {
-            char *name;
             char *port;
+            int cpunum;
+            SysBusDevice *mhu_sbd = SYS_BUS_DEVICE(&s->mhu[i]);
 
-            name = g_strdup_printf("MHU%d", i);
-            qdev_prop_set_string(DEVICE(&s->mhu[i]), "name", name);
-            qdev_prop_set_uint64(DEVICE(&s->mhu[i]), "size", 0x1000);
             object_property_set_bool(OBJECT(&s->mhu[i]), true,
                                      "realized", &err);
-            g_free(name);
             if (err) {
                 error_propagate(errp, err);
                 return;
             }
             port = g_strdup_printf("port[%d]", i + 3);
-            mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mhu[i]), 0);
+            mr = sysbus_mmio_get_region(mhu_sbd, 0);
             object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr),
                                      port, &err);
             g_free(port);
@@ -789,6 +802,20 @@ static void armsse_realize(DeviceState *dev, Error **errp)
                 error_propagate(errp, err);
                 return;
             }
+
+            /*
+             * Each MHU has an irq line for each CPU:
+             *  MHU 0 irq line 0 -> CPU 0 IRQ 6
+             *  MHU 0 irq line 1 -> CPU 1 IRQ 6
+             *  MHU 1 irq line 0 -> CPU 0 IRQ 7
+             *  MHU 1 irq line 1 -> CPU 1 IRQ 7
+             */
+            for (cpunum = 0; cpunum < info->num_cpus; cpunum++) {
+                DeviceState *cpudev = DEVICE(&s->armv7m[cpunum]);
+
+                sysbus_connect_irq(mhu_sbd, cpunum,
+                                   qdev_get_gpio_in(cpudev, 6 + i));
+            }
         }
     }
 
@@ -977,6 +1004,14 @@ static void armsse_realize(DeviceState *dev, Error **errp)
     /* System information registers */
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000);
     /* System control registers */
+    object_property_set_int(OBJECT(&s->sysctl), info->sys_version,
+                            "SYS_VERSION", &err);
+    object_property_set_int(OBJECT(&s->sysctl), info->cpuwait_rst,
+                            "CPUWAIT_RST", &err);
+    object_property_set_int(OBJECT(&s->sysctl), s->init_svtor,
+                            "INITSVTOR0_RST", &err);
+    object_property_set_int(OBJECT(&s->sysctl), s->init_svtor,
+                            "INITSVTOR1_RST", &err);
     object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err);
     if (err) {
         error_propagate(errp, err);
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 5158985482..996812498d 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -18,7 +18,7 @@
 #include "hw/arm/aspeed.h"
 #include "hw/arm/aspeed_soc.h"
 #include "hw/boards.h"
-#include "hw/i2c/smbus.h"
+#include "hw/i2c/smbus_eeprom.h"
 #include "qemu/log.h"
 #include "sysemu/block-backend.h"
 #include "hw/loader.h"
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index f598a1c053..3d7c88910e 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1286,7 +1286,7 @@ static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
     return 0;
 }
 
-static int pxa2xx_i2c_rx(I2CSlave *i2c)
+static uint8_t pxa2xx_i2c_rx(I2CSlave *i2c)
 {
     PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c);
     PXA2xxI2CState *s = slave->host;
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index 442529cc65..7b45fe3ccf 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -811,7 +811,7 @@ static void stellaris_i2c_write(void *opaque, hwaddr offset,
             /* TODO: Handle errors.  */
             if (s->msa & 1) {
                 /* Recv */
-                s->mdr = i2c_recv(s->bus) & 0xff;
+                s->mdr = i2c_recv(s->bus);
             } else {
                 /* Send */
                 i2c_send(s->bus, s->mdr);
diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c
index 7a925fa5e6..eef9d427e7 100644
--- a/hw/arm/tosa.c
+++ b/hw/arm/tosa.c
@@ -197,10 +197,10 @@ static int tosa_dac_event(I2CSlave *i2c, enum i2c_event event)
     return 0;
 }
 
-static int tosa_dac_recv(I2CSlave *s)
+static uint8_t tosa_dac_recv(I2CSlave *s)
 {
     printf("%s: recv not supported!!!\n", __func__);
-    return -1;
+    return 0xff;
 }
 
 static void tosa_tg_init(PXA2xxState *cpu)
diff --git a/hw/arm/z2.c b/hw/arm/z2.c
index 697a822f1e..6f18d924df 100644
--- a/hw/arm/z2.c
+++ b/hw/arm/z2.c
@@ -243,7 +243,7 @@ static int aer915_event(I2CSlave *i2c, enum i2c_event event)
     return 0;
 }
 
-static int aer915_recv(I2CSlave *slave)
+static uint8_t aer915_recv(I2CSlave *slave)
 {
     AER915State *s = AER915(slave);
     int retval = 0x00;
diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c
index f4aa838f62..169b006ade 100644
--- a/hw/audio/wm8750.c
+++ b/hw/audio/wm8750.c
@@ -561,7 +561,7 @@ static int wm8750_tx(I2CSlave *i2c, uint8_t data)
     return 0;
 }
 
-static int wm8750_rx(I2CSlave *i2c)
+static uint8_t wm8750_rx(I2CSlave *i2c)
 {
     return 0x00;
 }
diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
index c6a15da024..f1523c5b45 100644
--- a/hw/block/dataplane/xen-block.c
+++ b/hw/block/dataplane/xen-block.c
@@ -281,10 +281,6 @@ static void xen_block_complete_aio(void *opaque, int ret)
         break;
     case BLKIF_OP_WRITE:
     case BLKIF_OP_FLUSH_DISKCACHE:
-        if (!request->req.nr_segments) {
-            break;
-        }
-        break;
     default:
         break;
     }
@@ -298,6 +294,7 @@ static void xen_block_complete_aio(void *opaque, int ret)
         if (!request->req.nr_segments) {
             break;
         }
+        /* fall through */
     case BLKIF_OP_READ:
         if (request->status == BLKIF_RSP_OKAY) {
             block_acct_done(blk_get_stats(dataplane->blk), &request->acct);
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index cf7f47eaba..0cc3c590b9 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -28,9 +28,28 @@
 #include "hw/virtio/virtio-bus.h"
 #include "hw/virtio/virtio-access.h"
 
-/* We don't support discard yet, hide associated config fields. */
+/* Config size before the discard support (hide associated config fields) */
 #define VIRTIO_BLK_CFG_SIZE offsetof(struct virtio_blk_config, \
                                      max_discard_sectors)
+/*
+ * Starting from the discard feature, we can use this array to properly
+ * set the config size depending on the features enabled.
+ */
+static VirtIOFeature feature_sizes[] = {
+    {.flags = 1ULL << VIRTIO_BLK_F_DISCARD,
+     .end = virtio_endof(struct virtio_blk_config, discard_sector_alignment)},
+    {.flags = 1ULL << VIRTIO_BLK_F_WRITE_ZEROES,
+     .end = virtio_endof(struct virtio_blk_config, write_zeroes_may_unmap)},
+    {}
+};
+
+static void virtio_blk_set_config_size(VirtIOBlock *s, uint64_t host_features)
+{
+    s->config_size = MAX(VIRTIO_BLK_CFG_SIZE,
+        virtio_feature_get_config_size(feature_sizes, host_features));
+
+    assert(s->config_size <= sizeof(struct virtio_blk_config));
+}
 
 static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq,
                                     VirtIOBlockReq *req)
@@ -65,7 +84,7 @@ 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)
+    bool is_read, bool acct_failed)
 {
     VirtIOBlock *s = req->dev;
     BlockErrorAction action = blk_get_error_action(s->blk, is_read, error);
@@ -78,7 +97,9 @@ 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_failed(blk_get_stats(s->blk), &req->acct);
+        if (acct_failed) {
+            block_acct_failed(blk_get_stats(s->blk), &req->acct);
+        }
         virtio_blk_free_request(req);
     }
 
@@ -116,7 +137,7 @@ static void virtio_blk_rw_complete(void *opaque, int ret)
              * the memory until the request is completed (which will
              * happen on the other side of the migration).
              */
-            if (virtio_blk_handle_rw_error(req, -ret, is_read)) {
+            if (virtio_blk_handle_rw_error(req, -ret, is_read, true)) {
                 continue;
             }
         }
@@ -135,7 +156,7 @@ static void virtio_blk_flush_complete(void *opaque, int ret)
 
     aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
     if (ret) {
-        if (virtio_blk_handle_rw_error(req, -ret, 0)) {
+        if (virtio_blk_handle_rw_error(req, -ret, 0, true)) {
             goto out;
         }
     }
@@ -148,6 +169,30 @@ out:
     aio_context_release(blk_get_aio_context(s->conf.conf.blk));
 }
 
+static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret)
+{
+    VirtIOBlockReq *req = opaque;
+    VirtIOBlock *s = req->dev;
+    bool is_write_zeroes = (virtio_ldl_p(VIRTIO_DEVICE(s), &req->out.type) &
+                            ~VIRTIO_BLK_T_BARRIER) == VIRTIO_BLK_T_WRITE_ZEROES;
+
+    aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
+    if (ret) {
+        if (virtio_blk_handle_rw_error(req, -ret, false, is_write_zeroes)) {
+            goto out;
+        }
+    }
+
+    virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
+    if (is_write_zeroes) {
+        block_acct_done(blk_get_stats(s->blk), &req->acct);
+    }
+    virtio_blk_free_request(req);
+
+out:
+    aio_context_release(blk_get_aio_context(s->conf.conf.blk));
+}
+
 #ifdef __linux__
 
 typedef struct {
@@ -243,7 +288,7 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req)
      */
     scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base;
 
-    if (!blk->conf.scsi) {
+    if (!virtio_has_feature(blk->host_features, VIRTIO_BLK_F_SCSI)) {
         status = VIRTIO_BLK_S_UNSUPP;
         goto fail;
     }
@@ -481,6 +526,84 @@ static bool virtio_blk_sect_range_ok(VirtIOBlock *dev,
     return true;
 }
 
+static uint8_t virtio_blk_handle_discard_write_zeroes(VirtIOBlockReq *req,
+    struct virtio_blk_discard_write_zeroes *dwz_hdr, bool is_write_zeroes)
+{
+    VirtIOBlock *s = req->dev;
+    VirtIODevice *vdev = VIRTIO_DEVICE(s);
+    uint64_t sector;
+    uint32_t num_sectors, flags, max_sectors;
+    uint8_t err_status;
+    int bytes;
+
+    sector = virtio_ldq_p(vdev, &dwz_hdr->sector);
+    num_sectors = virtio_ldl_p(vdev, &dwz_hdr->num_sectors);
+    flags = virtio_ldl_p(vdev, &dwz_hdr->flags);
+    max_sectors = is_write_zeroes ? s->conf.max_write_zeroes_sectors :
+                  s->conf.max_discard_sectors;
+
+    /*
+     * max_sectors is at most BDRV_REQUEST_MAX_SECTORS, this check
+     * make us sure that "num_sectors << BDRV_SECTOR_BITS" can fit in
+     * the integer variable.
+     */
+    if (unlikely(num_sectors > max_sectors)) {
+        err_status = VIRTIO_BLK_S_IOERR;
+        goto err;
+    }
+
+    bytes = num_sectors << BDRV_SECTOR_BITS;
+
+    if (unlikely(!virtio_blk_sect_range_ok(s, sector, bytes))) {
+        err_status = VIRTIO_BLK_S_IOERR;
+        goto err;
+    }
+
+    /*
+     * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard
+     * and write zeroes commands if any unknown flag is set.
+     */
+    if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
+        err_status = VIRTIO_BLK_S_UNSUPP;
+        goto err;
+    }
+
+    if (is_write_zeroes) { /* VIRTIO_BLK_T_WRITE_ZEROES */
+        int blk_aio_flags = 0;
+
+        if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) {
+            blk_aio_flags |= BDRV_REQ_MAY_UNMAP;
+        }
+
+        block_acct_start(blk_get_stats(s->blk), &req->acct, bytes,
+                         BLOCK_ACCT_WRITE);
+
+        blk_aio_pwrite_zeroes(s->blk, sector << BDRV_SECTOR_BITS,
+                              bytes, blk_aio_flags,
+                              virtio_blk_discard_write_zeroes_complete, req);
+    } else { /* VIRTIO_BLK_T_DISCARD */
+        /*
+         * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for
+         * discard commands if the unmap flag is set.
+         */
+        if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
+            err_status = VIRTIO_BLK_S_UNSUPP;
+            goto err;
+        }
+
+        blk_aio_pdiscard(s->blk, sector << BDRV_SECTOR_BITS, bytes,
+                         virtio_blk_discard_write_zeroes_complete, req);
+    }
+
+    return VIRTIO_BLK_S_OK;
+
+err:
+    if (is_write_zeroes) {
+        block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_WRITE);
+    }
+    return err_status;
+}
+
 static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
 {
     uint32_t type;
@@ -582,6 +705,47 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
         virtio_blk_free_request(req);
         break;
     }
+    /*
+     * VIRTIO_BLK_T_DISCARD and VIRTIO_BLK_T_WRITE_ZEROES are defined with
+     * VIRTIO_BLK_T_OUT flag set. We masked this flag in the switch statement,
+     * so we must mask it for these requests, then we will check if it is set.
+     */
+    case VIRTIO_BLK_T_DISCARD & ~VIRTIO_BLK_T_OUT:
+    case VIRTIO_BLK_T_WRITE_ZEROES & ~VIRTIO_BLK_T_OUT:
+    {
+        struct virtio_blk_discard_write_zeroes dwz_hdr;
+        size_t out_len = iov_size(out_iov, out_num);
+        bool is_write_zeroes = (type & ~VIRTIO_BLK_T_BARRIER) ==
+                               VIRTIO_BLK_T_WRITE_ZEROES;
+        uint8_t err_status;
+
+        /*
+         * Unsupported if VIRTIO_BLK_T_OUT is not set or the request contains
+         * more than one segment.
+         */
+        if (unlikely(!(type & VIRTIO_BLK_T_OUT) ||
+                     out_len > sizeof(dwz_hdr))) {
+            virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
+            virtio_blk_free_request(req);
+            return 0;
+        }
+
+        if (unlikely(iov_to_buf(out_iov, out_num, 0, &dwz_hdr,
+                                sizeof(dwz_hdr)) != sizeof(dwz_hdr))) {
+            virtio_error(vdev, "virtio-blk discard/write_zeroes header"
+                         " too short");
+            return -1;
+        }
+
+        err_status = virtio_blk_handle_discard_write_zeroes(req, &dwz_hdr,
+                                                            is_write_zeroes);
+        if (err_status != VIRTIO_BLK_S_OK) {
+            virtio_blk_req_complete(req, err_status);
+            virtio_blk_free_request(req);
+        }
+
+        break;
+    }
     default:
         virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
         virtio_blk_free_request(req);
@@ -675,6 +839,7 @@ static void virtio_blk_dma_restart_bh(void *opaque)
     if (mrb.num_reqs) {
         virtio_blk_submit_multireq(s->blk, &mrb);
     }
+    blk_dec_in_flight(s->conf.conf.blk);
     aio_context_release(blk_get_aio_context(s->conf.conf.blk));
 }
 
@@ -688,8 +853,11 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running,
     }
 
     if (!s->bh) {
+        /* FIXME The data plane is not started yet, so these requests are
+         * processed in the main thread. */
         s->bh = aio_bh_new(blk_get_aio_context(s->conf.conf.blk),
                            virtio_blk_dma_restart_bh, s);
+        blk_inc_in_flight(s->conf.conf.blk);
         qemu_bh_schedule(s->bh);
     }
 }
@@ -761,8 +929,25 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
     blkcfg.alignment_offset = 0;
     blkcfg.wce = blk_enable_write_cache(s->blk);
     virtio_stw_p(vdev, &blkcfg.num_queues, s->conf.num_queues);
-    memcpy(config, &blkcfg, VIRTIO_BLK_CFG_SIZE);
-    QEMU_BUILD_BUG_ON(VIRTIO_BLK_CFG_SIZE > sizeof(blkcfg));
+    if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD)) {
+        virtio_stl_p(vdev, &blkcfg.max_discard_sectors,
+                     s->conf.max_discard_sectors);
+        virtio_stl_p(vdev, &blkcfg.discard_sector_alignment,
+                     blk_size >> BDRV_SECTOR_BITS);
+        /*
+         * We support only one segment per request since multiple segments
+         * are not widely used and there are no userspace APIs that allow
+         * applications to submit multiple segments in a single call.
+         */
+        virtio_stl_p(vdev, &blkcfg.max_discard_seg, 1);
+    }
+    if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_WRITE_ZEROES)) {
+        virtio_stl_p(vdev, &blkcfg.max_write_zeroes_sectors,
+                     s->conf.max_write_zeroes_sectors);
+        blkcfg.write_zeroes_may_unmap = 1;
+        virtio_stl_p(vdev, &blkcfg.max_write_zeroes_seg, 1);
+    }
+    memcpy(config, &blkcfg, s->config_size);
 }
 
 static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
@@ -770,8 +955,7 @@ static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
     VirtIOBlock *s = VIRTIO_BLK(vdev);
     struct virtio_blk_config blkcfg;
 
-    memcpy(&blkcfg, config, VIRTIO_BLK_CFG_SIZE);
-    QEMU_BUILD_BUG_ON(VIRTIO_BLK_CFG_SIZE > sizeof(blkcfg));
+    memcpy(&blkcfg, config, s->config_size);
 
     aio_context_acquire(blk_get_aio_context(s->blk));
     blk_set_enable_write_cache(s->blk, blkcfg.wce != 0);
@@ -783,12 +967,15 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features,
 {
     VirtIOBlock *s = VIRTIO_BLK(vdev);
 
+    /* Firstly sync all virtio-blk possible supported features */
+    features |= s->host_features;
+
     virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX);
     virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY);
     virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY);
     virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE);
     if (virtio_has_feature(features, VIRTIO_F_VERSION_1)) {
-        if (s->conf.scsi) {
+        if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_SCSI)) {
             error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0");
             return 0;
         }
@@ -797,9 +984,6 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features,
         virtio_add_feature(&features, VIRTIO_BLK_F_SCSI);
     }
 
-    if (s->conf.config_wce) {
-        virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE);
-    }
     if (blk_enable_write_cache(s->blk)) {
         virtio_add_feature(&features, VIRTIO_BLK_F_WCE);
     }
@@ -954,7 +1138,28 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
         return;
     }
 
-    virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, VIRTIO_BLK_CFG_SIZE);
+    if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD) &&
+        (!conf->max_discard_sectors ||
+         conf->max_discard_sectors > BDRV_REQUEST_MAX_SECTORS)) {
+        error_setg(errp, "invalid max-discard-sectors property (%" PRIu32 ")"
+                   ", must be between 1 and %d",
+                   conf->max_discard_sectors, (int)BDRV_REQUEST_MAX_SECTORS);
+        return;
+    }
+
+    if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_WRITE_ZEROES) &&
+        (!conf->max_write_zeroes_sectors ||
+         conf->max_write_zeroes_sectors > BDRV_REQUEST_MAX_SECTORS)) {
+        error_setg(errp, "invalid max-write-zeroes-sectors property (%" PRIu32
+                   "), must be between 1 and %d",
+                   conf->max_write_zeroes_sectors,
+                   (int)BDRV_REQUEST_MAX_SECTORS);
+        return;
+    }
+
+    virtio_blk_set_config_size(s, s->host_features);
+
+    virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, s->config_size);
 
     s->blk = conf->conf.blk;
     s->rq = NULL;
@@ -1013,9 +1218,11 @@ static Property virtio_blk_properties[] = {
     DEFINE_BLOCK_ERROR_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),
+    DEFINE_PROP_BIT64("config-wce", VirtIOBlock, host_features,
+                      VIRTIO_BLK_F_CONFIG_WCE, true),
 #ifdef __linux__
-    DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, false),
+    DEFINE_PROP_BIT64("scsi", VirtIOBlock, host_features,
+                      VIRTIO_BLK_F_SCSI, false),
 #endif
     DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
                     true),
@@ -1023,6 +1230,14 @@ static Property virtio_blk_properties[] = {
     DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 128),
     DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD,
                      IOThread *),
+    DEFINE_PROP_BIT64("discard", VirtIOBlock, host_features,
+                      VIRTIO_BLK_F_DISCARD, true),
+    DEFINE_PROP_BIT64("write-zeroes", VirtIOBlock, host_features,
+                      VIRTIO_BLK_F_WRITE_ZEROES, true),
+    DEFINE_PROP_UINT32("max-discard-sectors", VirtIOBlock,
+                       conf.max_discard_sectors, BDRV_REQUEST_MAX_SECTORS),
+    DEFINE_PROP_UINT32("max-write-zeroes-sectors", VirtIOBlock,
+                       conf.max_write_zeroes_sectors, BDRV_REQUEST_MAX_SECTORS),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c
index 5012af9cb6..70fc2455e8 100644
--- a/hw/block/xen-block.c
+++ b/hw/block/xen-block.c
@@ -351,21 +351,28 @@ static void xen_block_get_vdev(Object *obj, Visitor *v, const char *name,
     g_free(str);
 }
 
-static unsigned int vbd_name_to_disk(const char *name, const char **endp)
+static int vbd_name_to_disk(const char *name, const char **endp,
+                            unsigned long *disk)
 {
-    unsigned int disk = 0;
+    unsigned int n = 0;
 
     while (*name != '\0') {
         if (!g_ascii_isalpha(*name) || !g_ascii_islower(*name)) {
             break;
         }
 
-        disk *= 26;
-        disk += *name++ - 'a' + 1;
+        n *= 26;
+        n += *name++ - 'a' + 1;
     }
     *endp = name;
 
-    return disk - 1;
+    if (!n) {
+        return -1;
+    }
+
+    *disk = n - 1;
+
+    return 0;
 }
 
 static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name,
@@ -413,13 +420,14 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name,
         }
 
         if (*end == 'p') {
-            p = (char *) ++end;
-            if (*end == '\0') {
+            if (*(++end) == '\0') {
                 goto invalid;
             }
         }
     } else {
-        vdev->disk = vbd_name_to_disk(p, &end);
+        if (vbd_name_to_disk(p, &end, &vdev->disk)) {
+            goto invalid;
+        }
     }
 
     if (*end != '\0') {
@@ -735,12 +743,12 @@ static XenBlockDrive *xen_block_drive_create(const char *id,
         }
 
         g_strfreev(v);
-    }
-
-    if (!filename) {
-        error_setg(errp, "no filename");
+    } else {
+        error_setg(errp, "no params");
         goto done;
     }
+
+    assert(filename);
     assert(driver);
 
     drive = g_new0(XenBlockDrive, 1);
@@ -750,6 +758,7 @@ static XenBlockDrive *xen_block_drive_create(const char *id,
 
     qdict_put_str(file_layer, "driver", "file");
     qdict_put_str(file_layer, "filename", filename);
+    g_free(filename);
 
     if (mode && *mode != 'w') {
         qdict_put_bool(file_layer, "read-only", true);
@@ -785,16 +794,17 @@ static XenBlockDrive *xen_block_drive_create(const char *id,
     driver_layer = qdict_new();
 
     qdict_put_str(driver_layer, "driver", driver);
+    g_free(driver);
+
     qdict_put_obj(driver_layer, "file", QOBJECT(file_layer));
 
     g_assert(!drive->node_name);
     drive->node_name = xen_block_blockdev_add(drive->id, driver_layer,
                                               &local_err);
 
-done:
-    g_free(driver);
-    g_free(filename);
+    qobject_unref(driver_layer);
 
+done:
     if (local_err) {
         error_propagate(errp, local_err);
         xen_block_drive_destroy(drive, NULL);
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 077fbd182a..766ca5899d 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -33,6 +33,8 @@ GlobalProperty hw_compat_3_1[] = {
     { "usb-kbd", "serial", "42" },
     { "usb-mouse", "serial", "42" },
     { "usb-kbd", "serial", "42" },
+    { "virtio-blk-device", "discard", "false" },
+    { "virtio-blk-device", "write-zeroes", "false" },
 };
 const size_t hw_compat_3_1_len = G_N_ELEMENTS(hw_compat_3_1);
 
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index da8fd5a40a..c8ce5781e0 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -276,7 +276,8 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay)
                     QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
                     0));
     } else {
-#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
+/* >= release 0.12.6, < release 0.14.2 */
+#if SPICE_SERVER_VERSION >= 0x000c06 && SPICE_SERVER_VERSION < 0x000e02
         if (qxl->max_outputs) {
             spice_qxl_set_max_monitors(&qxl->ssd.qxl, qxl->max_outputs);
         }
@@ -2188,6 +2189,17 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp)
                    SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR);
         return;
     }
+
+#if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */
+    char device_address[256] = "";
+    if (qemu_spice_fill_device_address(qxl->vga.con, device_address, 256)) {
+        spice_qxl_set_device_info(&qxl->ssd.qxl,
+                                  device_address,
+                                  0,
+                                  qxl->max_outputs);
+    }
+#endif
+
     qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);
 
     qxl->update_irq = qemu_bh_new(qxl_update_irq_bh, qxl);
diff --git a/hw/display/sii9022.c b/hw/display/sii9022.c
index eaf11a6e7b..9994385c35 100644
--- a/hw/display/sii9022.c
+++ b/hw/display/sii9022.c
@@ -79,7 +79,7 @@ static int sii9022_event(I2CSlave *i2c, enum i2c_event event)
     return 0;
 }
 
-static int sii9022_rx(I2CSlave *i2c)
+static uint8_t sii9022_rx(I2CSlave *i2c)
 {
     sii9022_state *s = SII9022(i2c);
     uint8_t res = 0x00;
diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c
index eb90ba26be..8edf34986c 100644
--- a/hw/display/ssd0303.c
+++ b/hw/display/ssd0303.c
@@ -62,10 +62,10 @@ typedef struct {
     uint8_t framebuffer[132*8];
 } ssd0303_state;
 
-static int ssd0303_recv(I2CSlave *i2c)
+static uint8_t ssd0303_recv(I2CSlave *i2c)
 {
     BADF("Reads not implemented\n");
-    return -1;
+    return 0xff;
 }
 
 static int ssd0303_send(I2CSlave *i2c, uint8_t data)
diff --git a/hw/display/trace-events b/hw/display/trace-events
index 387c6b8931..37d3264bb2 100644
--- a/hw/display/trace-events
+++ b/hw/display/trace-events
@@ -35,6 +35,7 @@ vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp"
 # hw/display/virtio-gpu.c
 virtio_gpu_features(bool virgl) "virgl %d"
 virtio_gpu_cmd_get_display_info(void) ""
+virtio_gpu_cmd_get_edid(uint32_t scanout) "scanout %d"
 virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d"
 virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h) "res 0x%x, fmt 0x%x, w %d, h %d"
 virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d"
diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c
index bc6e99c943..2d302526ab 100644
--- a/hw/display/virtio-gpu-3d.c
+++ b/hw/display/virtio-gpu-3d.c
@@ -404,11 +404,6 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
 {
     VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
 
-    cmd->waiting = g->renderer_blocked;
-    if (cmd->waiting) {
-        return;
-    }
-
     virgl_renderer_force_ctx_0();
     switch (cmd->cmd_hdr.type) {
     case VIRTIO_GPU_CMD_CTX_CREATE:
@@ -468,6 +463,9 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
     case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
         virtio_gpu_get_display_info(g, cmd);
         break;
+    case VIRTIO_GPU_CMD_GET_EDID:
+        virtio_gpu_get_edid(g, cmd);
+        break;
     default:
         cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
         break;
@@ -604,22 +602,6 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g)
     }
 }
 
-void virtio_gpu_gl_block(void *opaque, bool block)
-{
-    VirtIOGPU *g = opaque;
-
-    if (block) {
-        g->renderer_blocked++;
-    } else {
-        g->renderer_blocked--;
-    }
-    assert(g->renderer_blocked >= 0);
-
-    if (g->renderer_blocked == 0) {
-        virtio_gpu_process_cmdq(g);
-    }
-}
-
 int virtio_gpu_virgl_init(VirtIOGPU *g)
 {
     int ret;
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index c6fab56f9b..a3627f58a9 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -21,6 +21,7 @@
 #include "hw/virtio/virtio.h"
 #include "hw/virtio/virtio-gpu.h"
 #include "hw/virtio/virtio-bus.h"
+#include "hw/display/edid.h"
 #include "migration/blocker.h"
 #include "qemu/log.h"
 #include "qapi/error.h"
@@ -207,6 +208,9 @@ static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features,
     if (virtio_gpu_virgl_enabled(g->conf)) {
         features |= (1 << VIRTIO_GPU_F_VIRGL);
     }
+    if (virtio_gpu_edid_enabled(g->conf)) {
+        features |= (1 << VIRTIO_GPU_F_EDID);
+    }
     return features;
 }
 
@@ -301,6 +305,40 @@ void virtio_gpu_get_display_info(VirtIOGPU *g,
                              sizeof(display_info));
 }
 
+static void
+virtio_gpu_generate_edid(VirtIOGPU *g, int scanout,
+                         struct virtio_gpu_resp_edid *edid)
+{
+    qemu_edid_info info = {
+        .prefx = g->req_state[scanout].width,
+        .prefy = g->req_state[scanout].height,
+    };
+
+    edid->size = cpu_to_le32(sizeof(edid->edid));
+    qemu_edid_generate(edid->edid, sizeof(edid->edid), &info);
+}
+
+void virtio_gpu_get_edid(VirtIOGPU *g,
+                         struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_resp_edid edid;
+    struct virtio_gpu_cmd_get_edid get_edid;
+
+    VIRTIO_GPU_FILL_CMD(get_edid);
+    virtio_gpu_bswap_32(&get_edid, sizeof(get_edid));
+
+    if (get_edid.scanout >= g->conf.max_outputs) {
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+        return;
+    }
+
+    trace_virtio_gpu_cmd_get_edid(get_edid.scanout);
+    memset(&edid, 0, sizeof(edid));
+    edid.hdr.type = VIRTIO_GPU_RESP_OK_EDID;
+    virtio_gpu_generate_edid(g, get_edid.scanout, &edid);
+    virtio_gpu_ctrl_response(g, cmd, &edid.hdr, sizeof(edid));
+}
+
 static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format)
 {
     switch (virtio_gpu_format) {
@@ -839,6 +877,9 @@ static void virtio_gpu_simple_process_cmd(VirtIOGPU *g,
     case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
         virtio_gpu_get_display_info(g, cmd);
         break;
+    case VIRTIO_GPU_CMD_GET_EDID:
+        virtio_gpu_get_edid(g, cmd);
+        break;
     case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
         virtio_gpu_resource_create_2d(g, cmd);
         break;
@@ -889,12 +930,14 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
     while (!QTAILQ_EMPTY(&g->cmdq)) {
         cmd = QTAILQ_FIRST(&g->cmdq);
 
+        if (g->renderer_blocked) {
+            break;
+        }
+
         /* process command */
         VIRGL(g, virtio_gpu_virgl_process_cmd, virtio_gpu_simple_process_cmd,
               g, cmd);
-        if (cmd->waiting) {
-            break;
-        }
+
         QTAILQ_REMOVE(&g->cmdq, cmd, next);
         if (virtio_gpu_stats_enabled(g->conf)) {
             g->stats.requests++;
@@ -936,7 +979,6 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
         cmd->vq = vq;
         cmd->error = 0;
         cmd->finished = false;
-        cmd->waiting = false;
         QTAILQ_INSERT_TAIL(&g->cmdq, cmd, next);
         cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command));
     }
@@ -1030,14 +1072,28 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
     return 0;
 }
 
+static void virtio_gpu_gl_block(void *opaque, bool block)
+{
+    VirtIOGPU *g = opaque;
+
+    if (block) {
+        g->renderer_blocked++;
+    } else {
+        g->renderer_blocked--;
+    }
+    assert(g->renderer_blocked >= 0);
+
+    if (g->renderer_blocked == 0) {
+        virtio_gpu_process_cmdq(g);
+    }
+}
+
 const GraphicHwOps virtio_gpu_ops = {
     .invalidate = virtio_gpu_invalidate_display,
     .gfx_update = virtio_gpu_update_display,
     .text_update = virtio_gpu_text_update,
     .ui_info = virtio_gpu_ui_info,
-#ifdef CONFIG_VIRGL
     .gl_block = virtio_gpu_gl_block,
-#endif
 };
 
 static const VMStateDescription vmstate_virtio_gpu_scanout = {
@@ -1238,10 +1294,9 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
         }
     }
 
-    g->config_size = sizeof(struct virtio_gpu_config);
     g->virtio_config.num_scanouts = cpu_to_le32(g->conf.max_outputs);
     virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
-                g->config_size);
+                sizeof(struct virtio_gpu_config));
 
     g->req_state[0].width = g->conf.xres;
     g->req_state[0].height = g->conf.yres;
@@ -1268,7 +1323,6 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
     QTAILQ_INIT(&g->fenceq);
 
     g->enabled_output_bitmask = 1;
-    g->qdev = qdev;
 
     for (i = 0; i < g->conf.max_outputs; i++) {
         g->scanout[i].con =
@@ -1356,6 +1410,8 @@ static Property virtio_gpu_properties[] = {
     DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags,
                     VIRTIO_GPU_FLAG_STATS_ENABLED, false),
 #endif
+    DEFINE_PROP_BIT("edid", VirtIOGPU, conf.flags,
+                    VIRTIO_GPU_FLAG_EDID_ENABLED, false),
     DEFINE_PROP_UINT32("xres", VirtIOGPU, conf.xres, 1024),
     DEFINE_PROP_UINT32("yres", VirtIOGPU, conf.yres, 768),
     DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c
index 1a2478b5a9..c6f686c3eb 100644
--- a/hw/gpio/max7310.c
+++ b/hw/gpio/max7310.c
@@ -39,7 +39,7 @@ static void max7310_reset(DeviceState *dev)
     s->command = 0x00;
 }
 
-static int max7310_rx(I2CSlave *i2c)
+static uint8_t max7310_rx(I2CSlave *i2c)
 {
     MAX7310State *s = MAX7310(i2c);
 
diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs
index cecee486f7..9205cbee16 100644
--- a/hw/i2c/Makefile.objs
+++ b/hw/i2c/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-$(CONFIG_I2C) += core.o smbus.o
+common-obj-$(CONFIG_I2C) += core.o smbus_slave.o smbus_master.o
 common-obj-$(CONFIG_SMBUS_EEPROM) += smbus_eeprom.o
 common-obj-$(CONFIG_DDC) += i2c-ddc.o
 common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o
diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c
index a2dfa82760..a085510cfd 100644
--- a/hw/i2c/aspeed_i2c.c
+++ b/hw/i2c/aspeed_i2c.c
@@ -189,16 +189,11 @@ static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus)
 
 static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus)
 {
-    int ret;
+    uint8_t ret;
 
     aspeed_i2c_set_state(bus, I2CD_MRXD);
     ret = i2c_recv(bus->bus);
-    if (ret < 0) {
-        qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__);
-        ret = 0xff;
-    } else {
-        bus->intr_status |= I2CD_INTR_RX_DONE;
-    }
+    bus->intr_status |= I2CD_INTR_RX_DONE;
     bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT;
     if (bus->cmd & I2CD_M_S_RX_CMD_LAST) {
         i2c_nack(bus->bus);
diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index b54725985a..15237ad073 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -191,23 +191,17 @@ int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send)
         }
         return ret ? -1 : 0;
     } else {
-        if ((QLIST_EMPTY(&bus->current_devs)) || (bus->broadcast)) {
-            return -1;
-        }
-
-        sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt);
-        if (sc->recv) {
-            s = QLIST_FIRST(&bus->current_devs)->elt;
-            ret = sc->recv(s);
-            trace_i2c_recv(s->address, ret);
-            if (ret < 0) {
-                return ret;
-            } else {
-                *data = ret;
-                return 0;
+        ret = 0xff;
+        if (!QLIST_EMPTY(&bus->current_devs) && !bus->broadcast) {
+            sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt);
+            if (sc->recv) {
+                s = QLIST_FIRST(&bus->current_devs)->elt;
+                ret = sc->recv(s);
+                trace_i2c_recv(s->address, ret);
             }
         }
-        return -1;
+        *data = ret;
+        return 0;
     }
 }
 
@@ -216,12 +210,12 @@ int i2c_send(I2CBus *bus, uint8_t data)
     return i2c_send_recv(bus, &data, true);
 }
 
-int i2c_recv(I2CBus *bus)
+uint8_t i2c_recv(I2CBus *bus)
 {
-    uint8_t data;
-    int ret = i2c_send_recv(bus, &data, false);
+    uint8_t data = 0xff;
 
-    return ret < 0 ? ret : data;
+    i2c_send_recv(bus, &data, false);
+    return data;
 }
 
 void i2c_nack(I2CBus *bus)
diff --git a/hw/i2c/exynos4210_i2c.c b/hw/i2c/exynos4210_i2c.c
index c96fa7d7be..d154b05739 100644
--- a/hw/i2c/exynos4210_i2c.c
+++ b/hw/i2c/exynos4210_i2c.c
@@ -106,16 +106,10 @@ static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s)
 static void exynos4210_i2c_data_receive(void *opaque)
 {
     Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
-    int ret;
 
     s->i2cstat &= ~I2CSTAT_LAST_BIT;
     s->scl_free = false;
-    ret = i2c_recv(s->bus);
-    if (ret < 0 && (s->i2ccon & I2CCON_ACK_GEN)) {
-        s->i2cstat |= I2CSTAT_LAST_BIT;  /* Data is not acknowledged */
-    } else {
-        s->i2cds = ret;
-    }
+    s->i2cds = i2c_recv(s->bus);
     exynos4210_i2c_raise_interrupt(s);
 }
 
diff --git a/hw/i2c/i2c-ddc.c b/hw/i2c/i2c-ddc.c
index 0a0367ff38..7aa8727771 100644
--- a/hw/i2c/i2c-ddc.c
+++ b/hw/i2c/i2c-ddc.c
@@ -51,7 +51,7 @@ static int i2c_ddc_event(I2CSlave *i2c, enum i2c_event event)
     return 0;
 }
 
-static int i2c_ddc_rx(I2CSlave *i2c)
+static uint8_t i2c_ddc_rx(I2CSlave *i2c)
 {
     I2CDDCState *s = I2CDDC(i2c);
 
diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c
index 6c81b98ebd..6da5224e2e 100644
--- a/hw/i2c/imx_i2c.c
+++ b/hw/i2c/imx_i2c.c
@@ -120,7 +120,7 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset,
         value = s->i2dr_read;
 
         if (imx_i2c_is_master(s)) {
-            int ret = 0xff;
+            uint8_t ret = 0xff;
 
             if (s->address == ADDR_RESET) {
                 /* something is wrong as the address is not set */
@@ -133,15 +133,7 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset,
             } else {
                 /* get the next byte */
                 ret = i2c_recv(s->bus);
-
-                if (ret >= 0) {
-                    imx_i2c_raise_interrupt(s);
-                } else {
-                    qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed "
-                                  "for device 0x%02x\n", TYPE_IMX_I2C,
-                                  __func__, s->address);
-                    ret = 0xff;
-                }
+                imx_i2c_raise_interrupt(s);
             }
 
             s->i2dr_read = ret;
diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c
index 03062740cc..e48544f909 100644
--- a/hw/i2c/pm_smbus.c
+++ b/hw/i2c/pm_smbus.c
@@ -19,8 +19,9 @@
  */
 #include "qemu/osdep.h"
 #include "hw/hw.h"
+#include "hw/boards.h"
 #include "hw/i2c/pm_smbus.h"
-#include "hw/i2c/smbus.h"
+#include "hw/i2c/smbus_master.h"
 
 #define SMBHSTSTS       0x00
 #define SMBHSTCNT       0x02
@@ -118,19 +119,30 @@ static void smb_transaction(PMSMBus *s)
         }
         break;
     case PROT_I2C_BLOCK_READ:
-        if (read) {
-            int xfersize = s->smb_data0;
-            if (xfersize > sizeof(s->smb_data)) {
-                xfersize = sizeof(s->smb_data);
-            }
-            ret = smbus_read_block(bus, addr, s->smb_data1, s->smb_data,
-                                   xfersize, false, true);
-            goto data8;
-        } else {
-            /* The manual says the behavior is undefined, just set DEV_ERR. */
+        /* According to the Linux i2c-i801 driver:
+         *   NB: page 240 of ICH5 datasheet shows that the R/#W
+         *   bit should be cleared here, even when reading.
+         *   However if SPD Write Disable is set (Lynx Point and later),
+         *   the read will fail if we don't set the R/#W bit.
+         * So at least Linux may or may not set the read bit here.
+         * So just ignore the read bit for this command.
+         */
+        if (i2c_start_transfer(bus, addr, 0)) {
             goto error;
         }
-        break;
+        ret = i2c_send(bus, s->smb_data1);
+        if (ret) {
+            goto error;
+        }
+        if (i2c_start_transfer(bus, addr, 1)) {
+            goto error;
+        }
+        s->in_i2c_block_read = true;
+        s->smb_blkdata = i2c_recv(s->smbus);
+        s->op_done = false;
+        s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
+        goto out;
+
     case PROT_BLOCK_DATA:
         if (read) {
             ret = smbus_read_block(bus, addr, cmd, s->smb_data,
@@ -208,6 +220,7 @@ static void smb_transaction_start(PMSMBus *s)
 {
     if (s->smb_ctl & CTL_INTREN) {
         smb_transaction(s);
+        s->start_transaction_on_status_read = false;
     } else {
         /* Do not execute immediately the command; it will be
          * executed when guest will read SMB_STAT register.  This
@@ -217,6 +230,7 @@ static void smb_transaction_start(PMSMBus *s)
          * checking for status.  If STS_HOST_BUSY doesn't get
          * set, it gets stuck. */
         s->smb_stat |= STS_HOST_BUSY;
+        s->start_transaction_on_status_read = true;
     }
 }
 
@@ -226,19 +240,38 @@ smb_irq_value(PMSMBus *s)
     return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN);
 }
 
+static bool
+smb_byte_by_byte(PMSMBus *s)
+{
+    if (s->op_done) {
+        return false;
+    }
+    if (s->in_i2c_block_read) {
+        return true;
+    }
+    return !(s->smb_auxctl & AUX_BLK);
+}
+
 static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
                               unsigned width)
 {
     PMSMBus *s = opaque;
+    uint8_t clear_byte_done;
 
     SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx
                   " val=0x%02" PRIx64 "\n", addr, val);
     switch(addr) {
     case SMBHSTSTS:
+        clear_byte_done = s->smb_stat & val & STS_BYTE_DONE;
         s->smb_stat &= ~(val & ~STS_HOST_BUSY);
-        if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) {
+        if (clear_byte_done && smb_byte_by_byte(s)) {
             uint8_t read = s->smb_addr & 0x01;
 
+            if (s->in_i2c_block_read) {
+                /* See comment below PROT_I2C_BLOCK_READ above. */
+                read = 1;
+            }
+
             s->smb_index++;
             if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
                 s->smb_index = 0;
@@ -268,12 +301,23 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
                 s->smb_stat |= STS_BYTE_DONE;
             } else if (s->smb_ctl & CTL_LAST_BYTE) {
                 s->op_done = true;
-                s->smb_blkdata = s->smb_data[s->smb_index];
+                if (s->in_i2c_block_read) {
+                    s->in_i2c_block_read = false;
+                    s->smb_blkdata = i2c_recv(s->smbus);
+                    i2c_nack(s->smbus);
+                    i2c_end_transfer(s->smbus);
+                } else {
+                    s->smb_blkdata = s->smb_data[s->smb_index];
+                }
                 s->smb_index = 0;
                 s->smb_stat |= STS_INTR;
                 s->smb_stat &= ~STS_HOST_BUSY;
             } else {
-                s->smb_blkdata = s->smb_data[s->smb_index];
+                if (s->in_i2c_block_read) {
+                    s->smb_blkdata = i2c_recv(s->smbus);
+                } else {
+                    s->smb_blkdata = s->smb_data[s->smb_index];
+                }
                 s->smb_stat |= STS_BYTE_DONE;
             }
         }
@@ -284,6 +328,10 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
             if (!s->op_done) {
                 s->smb_index = 0;
                 s->op_done = true;
+                if (s->in_i2c_block_read) {
+                    s->in_i2c_block_read = false;
+                    i2c_end_transfer(s->smbus);
+                }
             }
             smb_transaction_start(s);
         }
@@ -337,8 +385,9 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
     switch(addr) {
     case SMBHSTSTS:
         val = s->smb_stat;
-        if (s->smb_stat & STS_HOST_BUSY) {
+        if (s->start_transaction_on_status_read) {
             /* execute command now */
+            s->start_transaction_on_status_read = false;
             s->smb_stat &= ~STS_HOST_BUSY;
             smb_transaction(s);
         }
@@ -359,10 +408,10 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
         val = s->smb_data1;
         break;
     case SMBBLKDAT:
-        if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
-            s->smb_index = 0;
-        }
-        if (s->smb_auxctl & AUX_BLK) {
+        if (s->smb_auxctl & AUX_BLK && !s->in_i2c_block_read) {
+            if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
+                s->smb_index = 0;
+            }
             val = s->smb_data[s->smb_index++];
             if (!s->op_done && s->smb_index == s->smb_data0) {
                 s->op_done = true;
@@ -405,6 +454,36 @@ static const MemoryRegionOps pm_smbus_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
+bool pm_smbus_vmstate_needed(void)
+{
+    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+
+    return !mc->smbus_no_migration_support;
+}
+
+const VMStateDescription pmsmb_vmstate = {
+    .name = "pmsmb",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(smb_stat, PMSMBus),
+        VMSTATE_UINT8(smb_ctl, PMSMBus),
+        VMSTATE_UINT8(smb_cmd, PMSMBus),
+        VMSTATE_UINT8(smb_addr, PMSMBus),
+        VMSTATE_UINT8(smb_data0, PMSMBus),
+        VMSTATE_UINT8(smb_data1, PMSMBus),
+        VMSTATE_UINT32(smb_index, PMSMBus),
+        VMSTATE_UINT8_ARRAY(smb_data, PMSMBus, PM_SMBUS_MAX_MSG_SIZE),
+        VMSTATE_UINT8(smb_auxctl, PMSMBus),
+        VMSTATE_UINT8(smb_blkdata, PMSMBus),
+        VMSTATE_BOOL(i2c_enable, PMSMBus),
+        VMSTATE_BOOL(op_done, PMSMBus),
+        VMSTATE_BOOL(in_i2c_block_read, PMSMBus),
+        VMSTATE_BOOL(start_transaction_on_status_read, PMSMBus),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk)
 {
     smb->op_done = true;
diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c
deleted file mode 100644
index 30028bfcc2..0000000000
--- a/hw/i2c/smbus.c
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * QEMU SMBus device emulation.
- *
- * Copyright (c) 2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the LGPL.
- */
-
-/* TODO: Implement PEC.  */
-
-#include "qemu/osdep.h"
-#include "hw/hw.h"
-#include "hw/i2c/i2c.h"
-#include "hw/i2c/smbus.h"
-
-//#define DEBUG_SMBUS 1
-
-#ifdef DEBUG_SMBUS
-#define DPRINTF(fmt, ...) \
-do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0)
-#endif
-
-enum {
-    SMBUS_IDLE,
-    SMBUS_WRITE_DATA,
-    SMBUS_RECV_BYTE,
-    SMBUS_READ_DATA,
-    SMBUS_DONE,
-    SMBUS_CONFUSED = -1
-};
-
-static void smbus_do_quick_cmd(SMBusDevice *dev, int recv)
-{
-    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
-
-    DPRINTF("Quick Command %d\n", recv);
-    if (sc->quick_cmd) {
-        sc->quick_cmd(dev, recv);
-    }
-}
-
-static void smbus_do_write(SMBusDevice *dev)
-{
-    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
-
-    if (dev->data_len == 0) {
-        smbus_do_quick_cmd(dev, 0);
-    } else if (dev->data_len == 1) {
-        DPRINTF("Send Byte\n");
-        if (sc->send_byte) {
-            sc->send_byte(dev, dev->data_buf[0]);
-        }
-    } else {
-        dev->command = dev->data_buf[0];
-        DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1);
-        if (sc->write_data) {
-            sc->write_data(dev, dev->command, dev->data_buf + 1,
-                           dev->data_len - 1);
-        }
-    }
-}
-
-static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
-{
-    SMBusDevice *dev = SMBUS_DEVICE(s);
-
-    switch (event) {
-    case I2C_START_SEND:
-        switch (dev->mode) {
-        case SMBUS_IDLE:
-            DPRINTF("Incoming data\n");
-            dev->mode = SMBUS_WRITE_DATA;
-            break;
-        default:
-            BADF("Unexpected send start condition in state %d\n", dev->mode);
-            dev->mode = SMBUS_CONFUSED;
-            break;
-        }
-        break;
-
-    case I2C_START_RECV:
-        switch (dev->mode) {
-        case SMBUS_IDLE:
-            DPRINTF("Read mode\n");
-            dev->mode = SMBUS_RECV_BYTE;
-            break;
-        case SMBUS_WRITE_DATA:
-            if (dev->data_len == 0) {
-                BADF("Read after write with no data\n");
-                dev->mode = SMBUS_CONFUSED;
-            } else {
-                if (dev->data_len > 1) {
-                    smbus_do_write(dev);
-                } else {
-                    dev->command = dev->data_buf[0];
-                    DPRINTF("%02x: Command %d\n", dev->i2c.address,
-                            dev->command);
-                }
-                DPRINTF("Read mode\n");
-                dev->data_len = 0;
-                dev->mode = SMBUS_READ_DATA;
-            }
-            break;
-        default:
-            BADF("Unexpected recv start condition in state %d\n", dev->mode);
-            dev->mode = SMBUS_CONFUSED;
-            break;
-        }
-        break;
-
-    case I2C_FINISH:
-        switch (dev->mode) {
-        case SMBUS_WRITE_DATA:
-            smbus_do_write(dev);
-            break;
-        case SMBUS_RECV_BYTE:
-            smbus_do_quick_cmd(dev, 1);
-            break;
-        case SMBUS_READ_DATA:
-            BADF("Unexpected stop during receive\n");
-            break;
-        default:
-            /* Nothing to do.  */
-            break;
-        }
-        dev->mode = SMBUS_IDLE;
-        dev->data_len = 0;
-        break;
-
-    case I2C_NACK:
-        switch (dev->mode) {
-        case SMBUS_DONE:
-            /* Nothing to do.  */
-            break;
-        case SMBUS_READ_DATA:
-            dev->mode = SMBUS_DONE;
-            break;
-        default:
-            BADF("Unexpected NACK in state %d\n", dev->mode);
-            dev->mode = SMBUS_CONFUSED;
-            break;
-        }
-    }
-
-    return 0;
-}
-
-static int smbus_i2c_recv(I2CSlave *s)
-{
-    SMBusDevice *dev = SMBUS_DEVICE(s);
-    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
-    int ret;
-
-    switch (dev->mode) {
-    case SMBUS_RECV_BYTE:
-        if (sc->receive_byte) {
-            ret = sc->receive_byte(dev);
-        } else {
-            ret = 0;
-        }
-        DPRINTF("Receive Byte %02x\n", ret);
-        dev->mode = SMBUS_DONE;
-        break;
-    case SMBUS_READ_DATA:
-        if (sc->read_data) {
-            ret = sc->read_data(dev, dev->command, dev->data_len);
-            dev->data_len++;
-        } else {
-            ret = 0;
-        }
-        DPRINTF("Read data %02x\n", ret);
-        break;
-    default:
-        BADF("Unexpected read in state %d\n", dev->mode);
-        dev->mode = SMBUS_CONFUSED;
-        ret = 0;
-        break;
-    }
-    return ret;
-}
-
-static int smbus_i2c_send(I2CSlave *s, uint8_t data)
-{
-    SMBusDevice *dev = SMBUS_DEVICE(s);
-
-    switch (dev->mode) {
-    case SMBUS_WRITE_DATA:
-        DPRINTF("Write data %02x\n", data);
-        if (dev->data_len >= sizeof(dev->data_buf)) {
-            BADF("Too many bytes sent\n");
-        } else {
-            dev->data_buf[dev->data_len++] = data;
-        }
-        break;
-    default:
-        BADF("Unexpected write in state %d\n", dev->mode);
-        break;
-    }
-    return 0;
-}
-
-/* Master device commands.  */
-int smbus_quick_command(I2CBus *bus, uint8_t addr, int read)
-{
-    if (i2c_start_transfer(bus, addr, read)) {
-        return -1;
-    }
-    i2c_end_transfer(bus);
-    return 0;
-}
-
-int smbus_receive_byte(I2CBus *bus, uint8_t addr)
-{
-    uint8_t data;
-
-    if (i2c_start_transfer(bus, addr, 1)) {
-        return -1;
-    }
-    data = i2c_recv(bus);
-    i2c_nack(bus);
-    i2c_end_transfer(bus);
-    return data;
-}
-
-int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data)
-{
-    if (i2c_start_transfer(bus, addr, 0)) {
-        return -1;
-    }
-    i2c_send(bus, data);
-    i2c_end_transfer(bus);
-    return 0;
-}
-
-int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command)
-{
-    uint8_t data;
-    if (i2c_start_transfer(bus, addr, 0)) {
-        return -1;
-    }
-    i2c_send(bus, command);
-    if (i2c_start_transfer(bus, addr, 1)) {
-        i2c_end_transfer(bus);
-        return -1;
-    }
-    data = i2c_recv(bus);
-    i2c_nack(bus);
-    i2c_end_transfer(bus);
-    return data;
-}
-
-int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data)
-{
-    if (i2c_start_transfer(bus, addr, 0)) {
-        return -1;
-    }
-    i2c_send(bus, command);
-    i2c_send(bus, data);
-    i2c_end_transfer(bus);
-    return 0;
-}
-
-int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command)
-{
-    uint16_t data;
-    if (i2c_start_transfer(bus, addr, 0)) {
-        return -1;
-    }
-    i2c_send(bus, command);
-    if (i2c_start_transfer(bus, addr, 1)) {
-        i2c_end_transfer(bus);
-        return -1;
-    }
-    data = i2c_recv(bus);
-    data |= i2c_recv(bus) << 8;
-    i2c_nack(bus);
-    i2c_end_transfer(bus);
-    return data;
-}
-
-int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data)
-{
-    if (i2c_start_transfer(bus, addr, 0)) {
-        return -1;
-    }
-    i2c_send(bus, command);
-    i2c_send(bus, data & 0xff);
-    i2c_send(bus, data >> 8);
-    i2c_end_transfer(bus);
-    return 0;
-}
-
-int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
-                     int len, bool recv_len, bool send_cmd)
-{
-    int rlen;
-    int i;
-
-    if (send_cmd) {
-        if (i2c_start_transfer(bus, addr, 0)) {
-            return -1;
-        }
-        i2c_send(bus, command);
-    }
-    if (i2c_start_transfer(bus, addr, 1)) {
-        if (send_cmd) {
-            i2c_end_transfer(bus);
-        }
-        return -1;
-    }
-    if (recv_len) {
-        rlen = i2c_recv(bus);
-    } else {
-        rlen = len;
-    }
-    if (rlen > len) {
-        rlen = 0;
-    }
-    for (i = 0; i < rlen; i++) {
-        data[i] = i2c_recv(bus);
-    }
-    i2c_nack(bus);
-    i2c_end_transfer(bus);
-    return rlen;
-}
-
-int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
-                      int len, bool send_len)
-{
-    int i;
-
-    if (len > 32)
-        len = 32;
-
-    if (i2c_start_transfer(bus, addr, 0)) {
-        return -1;
-    }
-    i2c_send(bus, command);
-    if (send_len) {
-        i2c_send(bus, len);
-    }
-    for (i = 0; i < len; i++) {
-        i2c_send(bus, data[i]);
-    }
-    i2c_end_transfer(bus);
-    return 0;
-}
-
-static void smbus_device_class_init(ObjectClass *klass, void *data)
-{
-    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
-
-    sc->event = smbus_i2c_event;
-    sc->recv = smbus_i2c_recv;
-    sc->send = smbus_i2c_send;
-}
-
-static const TypeInfo smbus_device_type_info = {
-    .name = TYPE_SMBUS_DEVICE,
-    .parent = TYPE_I2C_SLAVE,
-    .instance_size = sizeof(SMBusDevice),
-    .abstract = true,
-    .class_size = sizeof(SMBusDeviceClass),
-    .class_init = smbus_device_class_init,
-};
-
-static void smbus_device_register_types(void)
-{
-    type_register_static(&smbus_device_type_info);
-}
-
-type_init(smbus_device_register_types)
diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c
index 01b9439014..37167e7244 100644
--- a/hw/i2c/smbus_eeprom.c
+++ b/hw/i2c/smbus_eeprom.c
@@ -26,39 +26,35 @@
 #include "qemu/units.h"
 #include "qapi/error.h"
 #include "hw/hw.h"
+#include "hw/boards.h"
 #include "hw/i2c/i2c.h"
-#include "hw/i2c/smbus.h"
+#include "hw/i2c/smbus_slave.h"
+#include "hw/i2c/smbus_eeprom.h"
 
 //#define DEBUG
 
+#define TYPE_SMBUS_EEPROM "smbus-eeprom"
+
+#define SMBUS_EEPROM(obj) \
+    OBJECT_CHECK(SMBusEEPROMDevice, (obj), TYPE_SMBUS_EEPROM)
+
+#define SMBUS_EEPROM_SIZE 256
+
 typedef struct SMBusEEPROMDevice {
     SMBusDevice smbusdev;
-    void *data;
+    uint8_t data[SMBUS_EEPROM_SIZE];
+    void *init_data;
     uint8_t offset;
+    bool accessed;
 } SMBusEEPROMDevice;
 
-static void eeprom_quick_cmd(SMBusDevice *dev, uint8_t read)
-{
-#ifdef DEBUG
-    printf("eeprom_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read);
-#endif
-}
-
-static void eeprom_send_byte(SMBusDevice *dev, uint8_t val)
-{
-    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
-#ifdef DEBUG
-    printf("eeprom_send_byte: addr=0x%02x val=0x%02x\n",
-           dev->i2c.address, val);
-#endif
-    eeprom->offset = val;
-}
-
 static uint8_t eeprom_receive_byte(SMBusDevice *dev)
 {
-    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
+    SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev);
     uint8_t *data = eeprom->data;
     uint8_t val = data[eeprom->offset++];
+
+    eeprom->accessed = true;
 #ifdef DEBUG
     printf("eeprom_receive_byte: addr=0x%02x val=0x%02x\n",
            dev->i2c.address, val);
@@ -66,48 +62,77 @@ static uint8_t eeprom_receive_byte(SMBusDevice *dev)
     return val;
 }
 
-static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len)
+static int eeprom_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len)
 {
-    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
-    int n;
+    SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev);
+    uint8_t *data = eeprom->data;
+
+    eeprom->accessed = true;
 #ifdef DEBUG
     printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n",
-           dev->i2c.address, cmd, buf[0]);
+           dev->i2c.address, buf[0], buf[1]);
 #endif
-    /* A page write operation is not a valid SMBus command.
-       It is a block write without a length byte.  Fortunately we
-       get the full block anyway.  */
-    /* TODO: Should this set the current location?  */
-    if (cmd + len > 256)
-        n = 256 - cmd;
-    else
-        n = len;
-    memcpy(eeprom->data + cmd, buf, n);
-    len -= n;
-    if (len)
-        memcpy(eeprom->data, buf + n, len);
+    /* len is guaranteed to be > 0 */
+    eeprom->offset = buf[0];
+    buf++;
+    len--;
+
+    for (; len > 0; len--) {
+        data[eeprom->offset] = *buf++;
+        eeprom->offset = (eeprom->offset + 1) % SMBUS_EEPROM_SIZE;
+    }
+
+    return 0;
 }
 
-static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n)
+static bool smbus_eeprom_vmstate_needed(void *opaque)
 {
-    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
-    /* If this is the first byte then set the current position.  */
-    if (n == 0)
-        eeprom->offset = cmd;
-    /* As with writes, we implement block reads without the
-       SMBus length byte.  */
-    return eeprom_receive_byte(dev);
+    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+    SMBusEEPROMDevice *eeprom = opaque;
+
+    return (eeprom->accessed || smbus_vmstate_needed(&eeprom->smbusdev)) &&
+        !mc->smbus_no_migration_support;
 }
 
-static void smbus_eeprom_realize(DeviceState *dev, Error **errp)
+static const VMStateDescription vmstate_smbus_eeprom = {
+    .name = "smbus-eeprom",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = smbus_eeprom_vmstate_needed,
+    .fields      = (VMStateField[]) {
+        VMSTATE_SMBUS_DEVICE(smbusdev, SMBusEEPROMDevice),
+        VMSTATE_UINT8_ARRAY(data, SMBusEEPROMDevice, SMBUS_EEPROM_SIZE),
+        VMSTATE_UINT8(offset, SMBusEEPROMDevice),
+        VMSTATE_BOOL(accessed, SMBusEEPROMDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/*
+ * Reset the EEPROM contents to the initial state on a reset.  This
+ * isn't really how an EEPROM works, of course, but the general
+ * principle of QEMU is to restore function on reset to what it would
+ * be if QEMU was stopped and started.
+ *
+ * The proper thing to do would be to have a backing blockdev to hold
+ * the contents and restore that on startup, and not do this on reset.
+ * But until that time, act as if we had been stopped and restarted.
+ */
+static void smbus_eeprom_reset(DeviceState *dev)
 {
-    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev;
+    SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev);
 
+    memcpy(eeprom->data, eeprom->init_data, SMBUS_EEPROM_SIZE);
     eeprom->offset = 0;
 }
 
+static void smbus_eeprom_realize(DeviceState *dev, Error **errp)
+{
+    smbus_eeprom_reset(dev);
+}
+
 static Property smbus_eeprom_properties[] = {
-    DEFINE_PROP_PTR("data", SMBusEEPROMDevice, data),
+    DEFINE_PROP_PTR("data", SMBusEEPROMDevice, init_data),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -117,18 +142,17 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data)
     SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass);
 
     dc->realize = smbus_eeprom_realize;
-    sc->quick_cmd = eeprom_quick_cmd;
-    sc->send_byte = eeprom_send_byte;
+    dc->reset = smbus_eeprom_reset;
     sc->receive_byte = eeprom_receive_byte;
     sc->write_data = eeprom_write_data;
-    sc->read_data = eeprom_read_data;
     dc->props = smbus_eeprom_properties;
+    dc->vmsd = &vmstate_smbus_eeprom;
     /* Reason: pointer property "data" */
     dc->user_creatable = false;
 }
 
 static const TypeInfo smbus_eeprom_info = {
-    .name          = "smbus-eeprom",
+    .name          = TYPE_SMBUS_EEPROM,
     .parent        = TYPE_SMBUS_DEVICE,
     .instance_size = sizeof(SMBusEEPROMDevice),
     .class_init    = smbus_eeprom_class_initfn,
@@ -145,7 +169,7 @@ void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf)
 {
     DeviceState *dev;
 
-    dev = qdev_create((BusState *) smbus, "smbus-eeprom");
+    dev = qdev_create((BusState *) smbus, TYPE_SMBUS_EEPROM);
     qdev_prop_set_uint8(dev, "address", address);
     qdev_prop_set_ptr(dev, "data", eeprom_buf);
     qdev_init_nofail(dev);
@@ -155,13 +179,17 @@ void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom,
                        const uint8_t *eeprom_spd, int eeprom_spd_size)
 {
     int i;
-    uint8_t *eeprom_buf = g_malloc0(8 * 256); /* XXX: make this persistent */
+     /* XXX: make this persistent */
+
+    assert(nb_eeprom <= 8);
+    uint8_t *eeprom_buf = g_malloc0(8 * SMBUS_EEPROM_SIZE);
     if (eeprom_spd_size > 0) {
         memcpy(eeprom_buf, eeprom_spd, eeprom_spd_size);
     }
 
     for (i = 0; i < nb_eeprom; i++) {
-        smbus_eeprom_init_one(smbus, 0x50 + i, eeprom_buf + (i * 256));
+        smbus_eeprom_init_one(smbus, 0x50 + i,
+                              eeprom_buf + (i * SMBUS_EEPROM_SIZE));
     }
 }
 
diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c
index 2a8b49e02f..7b24be8256 100644
--- a/hw/i2c/smbus_ich9.c
+++ b/hw/i2c/smbus_ich9.c
@@ -29,8 +29,6 @@
 #include "hw/i2c/pm_smbus.h"
 #include "hw/pci/pci.h"
 #include "sysemu/sysemu.h"
-#include "hw/i2c/i2c.h"
-#include "hw/i2c/smbus.h"
 
 #include "hw/i386/ich9.h"
 
@@ -45,12 +43,20 @@ typedef struct ICH9SMBState {
     PMSMBus smb;
 } ICH9SMBState;
 
+static bool ich9_vmstate_need_smbus(void *opaque, int version_id)
+{
+    return pm_smbus_vmstate_needed();
+}
+
 static const VMStateDescription vmstate_ich9_smbus = {
     .name = "ich9_smb",
     .version_id = 1,
     .minimum_version_id = 1,
     .fields = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(dev, struct ICH9SMBState),
+        VMSTATE_PCI_DEVICE(dev, ICH9SMBState),
+        VMSTATE_BOOL_TEST(irq_enabled, ICH9SMBState, ich9_vmstate_need_smbus),
+        VMSTATE_STRUCT_TEST(smb, ICH9SMBState, ich9_vmstate_need_smbus, 1,
+                            pmsmb_vmstate, PMSMBus),
         VMSTATE_END_OF_LIST()
     }
 };
diff --git a/hw/i2c/smbus_master.c b/hw/i2c/smbus_master.c
new file mode 100644
index 0000000000..0a6223744c
--- /dev/null
+++ b/hw/i2c/smbus_master.c
@@ -0,0 +1,165 @@
+/*
+ * QEMU SMBus host (master) emulation.
+ *
+ * This code emulates SMBus transactions from the master point of view,
+ * it runs the individual I2C transaction to do the SMBus protocol
+ * over I2C.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "hw/i2c/smbus_master.h"
+
+/* Master device commands.  */
+int smbus_quick_command(I2CBus *bus, uint8_t addr, int read)
+{
+    if (i2c_start_transfer(bus, addr, read)) {
+        return -1;
+    }
+    i2c_end_transfer(bus);
+    return 0;
+}
+
+int smbus_receive_byte(I2CBus *bus, uint8_t addr)
+{
+    uint8_t data;
+
+    if (i2c_start_transfer(bus, addr, 1)) {
+        return -1;
+    }
+    data = i2c_recv(bus);
+    i2c_nack(bus);
+    i2c_end_transfer(bus);
+    return data;
+}
+
+int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data)
+{
+    if (i2c_start_transfer(bus, addr, 0)) {
+        return -1;
+    }
+    i2c_send(bus, data);
+    i2c_end_transfer(bus);
+    return 0;
+}
+
+int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command)
+{
+    uint8_t data;
+    if (i2c_start_transfer(bus, addr, 0)) {
+        return -1;
+    }
+    i2c_send(bus, command);
+    if (i2c_start_transfer(bus, addr, 1)) {
+        i2c_end_transfer(bus);
+        return -1;
+    }
+    data = i2c_recv(bus);
+    i2c_nack(bus);
+    i2c_end_transfer(bus);
+    return data;
+}
+
+int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data)
+{
+    if (i2c_start_transfer(bus, addr, 0)) {
+        return -1;
+    }
+    i2c_send(bus, command);
+    i2c_send(bus, data);
+    i2c_end_transfer(bus);
+    return 0;
+}
+
+int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command)
+{
+    uint16_t data;
+    if (i2c_start_transfer(bus, addr, 0)) {
+        return -1;
+    }
+    i2c_send(bus, command);
+    if (i2c_start_transfer(bus, addr, 1)) {
+        i2c_end_transfer(bus);
+        return -1;
+    }
+    data = i2c_recv(bus);
+    data |= i2c_recv(bus) << 8;
+    i2c_nack(bus);
+    i2c_end_transfer(bus);
+    return data;
+}
+
+int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data)
+{
+    if (i2c_start_transfer(bus, addr, 0)) {
+        return -1;
+    }
+    i2c_send(bus, command);
+    i2c_send(bus, data & 0xff);
+    i2c_send(bus, data >> 8);
+    i2c_end_transfer(bus);
+    return 0;
+}
+
+int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
+                     int len, bool recv_len, bool send_cmd)
+{
+    int rlen;
+    int i;
+
+    if (send_cmd) {
+        if (i2c_start_transfer(bus, addr, 0)) {
+            return -1;
+        }
+        i2c_send(bus, command);
+    }
+    if (i2c_start_transfer(bus, addr, 1)) {
+        if (send_cmd) {
+            i2c_end_transfer(bus);
+        }
+        return -1;
+    }
+    if (recv_len) {
+        rlen = i2c_recv(bus);
+    } else {
+        rlen = len;
+    }
+    if (rlen > len) {
+        rlen = 0;
+    }
+    for (i = 0; i < rlen; i++) {
+        data[i] = i2c_recv(bus);
+    }
+    i2c_nack(bus);
+    i2c_end_transfer(bus);
+    return rlen;
+}
+
+int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
+                      int len, bool send_len)
+{
+    int i;
+
+    if (len > 32) {
+        len = 32;
+    }
+
+    if (i2c_start_transfer(bus, addr, 0)) {
+        return -1;
+    }
+    i2c_send(bus, command);
+    if (send_len) {
+        i2c_send(bus, len);
+    }
+    for (i = 0; i < len; i++) {
+        i2c_send(bus, data[i]);
+    }
+    i2c_end_transfer(bus);
+    return 0;
+}
diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c
new file mode 100644
index 0000000000..9a2d314d1a
--- /dev/null
+++ b/hw/i2c/smbus_slave.c
@@ -0,0 +1,236 @@
+/*
+ * QEMU SMBus device emulation.
+ *
+ * This code is a helper for SMBus device emulation.  It implements an
+ * I2C device inteface and runs the SMBus protocol from the device
+ * point of view and maps those to simple calls to emulate.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the LGPL.
+ */
+
+/* TODO: Implement PEC.  */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "hw/i2c/smbus_slave.h"
+
+//#define DEBUG_SMBUS 1
+
+#ifdef DEBUG_SMBUS
+#define DPRINTF(fmt, ...) \
+do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+enum {
+    SMBUS_IDLE,
+    SMBUS_WRITE_DATA,
+    SMBUS_READ_DATA,
+    SMBUS_DONE,
+    SMBUS_CONFUSED = -1
+};
+
+static void smbus_do_quick_cmd(SMBusDevice *dev, int recv)
+{
+    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
+
+    DPRINTF("Quick Command %d\n", recv);
+    if (sc->quick_cmd) {
+        sc->quick_cmd(dev, recv);
+    }
+}
+
+static void smbus_do_write(SMBusDevice *dev)
+{
+    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
+
+    DPRINTF("Command %d len %d\n", dev->data_buf[0], dev->data_len);
+    if (sc->write_data) {
+        sc->write_data(dev, dev->data_buf, dev->data_len);
+    }
+}
+
+static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
+{
+    SMBusDevice *dev = SMBUS_DEVICE(s);
+
+    switch (event) {
+    case I2C_START_SEND:
+        switch (dev->mode) {
+        case SMBUS_IDLE:
+            DPRINTF("Incoming data\n");
+            dev->mode = SMBUS_WRITE_DATA;
+            break;
+
+        default:
+            BADF("Unexpected send start condition in state %d\n", dev->mode);
+            dev->mode = SMBUS_CONFUSED;
+            break;
+        }
+        break;
+
+    case I2C_START_RECV:
+        switch (dev->mode) {
+        case SMBUS_IDLE:
+            DPRINTF("Read mode\n");
+            dev->mode = SMBUS_READ_DATA;
+            break;
+
+        case SMBUS_WRITE_DATA:
+            if (dev->data_len == 0) {
+                BADF("Read after write with no data\n");
+                dev->mode = SMBUS_CONFUSED;
+            } else {
+                smbus_do_write(dev);
+                DPRINTF("Read mode\n");
+                dev->mode = SMBUS_READ_DATA;
+            }
+            break;
+
+        default:
+            BADF("Unexpected recv start condition in state %d\n", dev->mode);
+            dev->mode = SMBUS_CONFUSED;
+            break;
+        }
+        break;
+
+    case I2C_FINISH:
+        if (dev->data_len == 0) {
+            if (dev->mode == SMBUS_WRITE_DATA || dev->mode == SMBUS_READ_DATA) {
+                smbus_do_quick_cmd(dev, dev->mode == SMBUS_READ_DATA);
+            }
+        } else {
+            switch (dev->mode) {
+            case SMBUS_WRITE_DATA:
+                smbus_do_write(dev);
+                break;
+
+            case SMBUS_READ_DATA:
+                BADF("Unexpected stop during receive\n");
+                break;
+
+            default:
+                /* Nothing to do.  */
+                break;
+            }
+        }
+        dev->mode = SMBUS_IDLE;
+        dev->data_len = 0;
+        break;
+
+    case I2C_NACK:
+        switch (dev->mode) {
+        case SMBUS_DONE:
+            /* Nothing to do.  */
+            break;
+
+        case SMBUS_READ_DATA:
+            dev->mode = SMBUS_DONE;
+            break;
+
+        default:
+            BADF("Unexpected NACK in state %d\n", dev->mode);
+            dev->mode = SMBUS_CONFUSED;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static uint8_t smbus_i2c_recv(I2CSlave *s)
+{
+    SMBusDevice *dev = SMBUS_DEVICE(s);
+    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
+    uint8_t ret = 0xff;
+
+    switch (dev->mode) {
+    case SMBUS_READ_DATA:
+        if (sc->receive_byte) {
+            ret = sc->receive_byte(dev);
+        }
+        DPRINTF("Read data %02x\n", ret);
+        break;
+
+    default:
+        BADF("Unexpected read in state %d\n", dev->mode);
+        dev->mode = SMBUS_CONFUSED;
+        break;
+    }
+
+    return ret;
+}
+
+static int smbus_i2c_send(I2CSlave *s, uint8_t data)
+{
+    SMBusDevice *dev = SMBUS_DEVICE(s);
+
+    switch (dev->mode) {
+    case SMBUS_WRITE_DATA:
+        DPRINTF("Write data %02x\n", data);
+        if (dev->data_len >= sizeof(dev->data_buf)) {
+            BADF("Too many bytes sent\n");
+        } else {
+            dev->data_buf[dev->data_len++] = data;
+        }
+        break;
+
+    default:
+        BADF("Unexpected write in state %d\n", dev->mode);
+        break;
+    }
+
+    return 0;
+}
+
+static void smbus_device_class_init(ObjectClass *klass, void *data)
+{
+    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+
+    sc->event = smbus_i2c_event;
+    sc->recv = smbus_i2c_recv;
+    sc->send = smbus_i2c_send;
+}
+
+bool smbus_vmstate_needed(SMBusDevice *dev)
+{
+    return dev->mode != SMBUS_IDLE;
+}
+
+const VMStateDescription vmstate_smbus_device = {
+    .name = TYPE_SMBUS_DEVICE,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_I2C_SLAVE(i2c, SMBusDevice),
+        VMSTATE_INT32(mode, SMBusDevice),
+        VMSTATE_INT32(data_len, SMBusDevice),
+        VMSTATE_UINT8_ARRAY(data_buf, SMBusDevice, SMBUS_DATA_MAX_LEN),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const TypeInfo smbus_device_type_info = {
+    .name = TYPE_SMBUS_DEVICE,
+    .parent = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(SMBusDevice),
+    .abstract = true,
+    .class_size = sizeof(SMBusDeviceClass),
+    .class_init = smbus_device_class_init,
+};
+
+static void smbus_device_register_types(void)
+{
+    type_register_static(&smbus_device_type_info);
+}
+
+type_init(smbus_device_register_types)
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index fd0f2c268f..8770ecada9 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -42,7 +42,7 @@
 #include "sysemu/sysemu.h"
 #include "hw/sysbus.h"
 #include "sysemu/arch_init.h"
-#include "hw/i2c/smbus.h"
+#include "hw/i2c/smbus_eeprom.h"
 #include "hw/xen/xen.h"
 #include "exec/memory.h"
 #include "exec/address-spaces.h"
@@ -444,6 +444,7 @@ static void pc_i440fx_3_1_machine_options(MachineClass *m)
 
     pc_i440fx_4_0_machine_options(m);
     m->is_default = 0;
+    m->smbus_no_migration_support = true;
     m->alias = NULL;
     pcmc->pvh_enabled = false;
     compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len);
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 4a175ea50e..cfb9043e12 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -33,7 +33,7 @@
 #include "hw/hw.h"
 #include "hw/loader.h"
 #include "sysemu/arch_init.h"
-#include "hw/i2c/smbus.h"
+#include "hw/i2c/smbus_eeprom.h"
 #include "hw/boards.h"
 #include "hw/timer/mc146818rtc.h"
 #include "hw/xen/xen.h"
@@ -380,6 +380,7 @@ static void pc_q35_3_1_machine_options(MachineClass *m)
 
     pc_q35_4_0_machine_options(m);
     m->default_kernel_irqchip_split = false;
+    m->smbus_no_migration_support = true;
     m->alias = NULL;
     pcmc->pvh_enabled = false;
     compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len);
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 39e473f9c2..1b0f66cc08 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -174,16 +174,15 @@ static void cd_read_sector_cb(void *opaque, int ret)
 
 static int cd_read_sector(IDEState *s)
 {
+    void *buf;
+
     if (s->cd_sector_size != 2048 && s->cd_sector_size != 2352) {
         block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
         return -EINVAL;
     }
 
-    s->iov.iov_base = (s->cd_sector_size == 2352) ?
-                      s->io_buffer + 16 : s->io_buffer;
-
-    s->iov.iov_len = ATAPI_SECTOR_SIZE;
-    qemu_iovec_init_external(&s->qiov, &s->iov, 1);
+    buf = (s->cd_sector_size == 2352) ? s->io_buffer + 16 : s->io_buffer;
+    qemu_iovec_init_buf(&s->qiov, buf, ATAPI_SECTOR_SIZE);
 
     trace_cd_read_sector(s->lba);
 
@@ -421,9 +420,8 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
         data_offset = 0;
     }
     trace_ide_atapi_cmd_read_dma_cb_aio(s, s->lba, n);
-    s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset);
-    s->bus->dma->iov.iov_len = n * ATAPI_SECTOR_SIZE;
-    qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1);
+    qemu_iovec_init_buf(&s->bus->dma->qiov, s->io_buffer + data_offset,
+                        n * ATAPI_SECTOR_SIZE);
 
     s->bus->dma->aiocb = ide_buffered_readv(s, (int64_t)s->lba << 2,
                                             &s->bus->dma->qiov, n * 4,
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 84832008b8..6afadf894f 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -629,13 +629,15 @@ static void ide_buffered_readv_cb(void *opaque, int ret)
     IDEBufferedRequest *req = opaque;
     if (!req->orphaned) {
         if (!ret) {
-            qemu_iovec_from_buf(req->original_qiov, 0, req->iov.iov_base,
+            assert(req->qiov.size == req->original_qiov->size);
+            qemu_iovec_from_buf(req->original_qiov, 0,
+                                req->qiov.local_iov.iov_base,
                                 req->original_qiov->size);
         }
         req->original_cb(req->original_opaque, ret);
     }
     QLIST_REMOVE(req, list);
-    qemu_vfree(req->iov.iov_base);
+    qemu_vfree(qemu_iovec_buf(&req->qiov));
     g_free(req);
 }
 
@@ -660,9 +662,8 @@ BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num,
     req->original_qiov = iov;
     req->original_cb = cb;
     req->original_opaque = opaque;
-    req->iov.iov_base = qemu_blockalign(blk_bs(s->blk), iov->size);
-    req->iov.iov_len = iov->size;
-    qemu_iovec_init_external(&req->qiov, &req->iov, 1);
+    qemu_iovec_init_buf(&req->qiov, blk_blockalign(s->blk, iov->size),
+                        iov->size);
 
     aioreq = blk_aio_preadv(s->blk, sector_num << BDRV_SECTOR_BITS,
                             &req->qiov, 0, ide_buffered_readv_cb, req);
@@ -774,9 +775,7 @@ static void ide_sector_read(IDEState *s)
         return;
     }
 
-    s->iov.iov_base = s->io_buffer;
-    s->iov.iov_len  = n * BDRV_SECTOR_SIZE;
-    qemu_iovec_init_external(&s->qiov, &s->iov, 1);
+    qemu_iovec_init_buf(&s->qiov, s->io_buffer, n * BDRV_SECTOR_SIZE);
 
     block_acct_start(blk_get_stats(s->blk), &s->acct,
                      n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
@@ -1045,9 +1044,7 @@ static void ide_sector_write(IDEState *s)
         return;
     }
 
-    s->iov.iov_base = s->io_buffer;
-    s->iov.iov_len  = n * BDRV_SECTOR_SIZE;
-    qemu_iovec_init_external(&s->qiov, &s->iov, 1);
+    qemu_iovec_init_buf(&s->qiov, s->io_buffer, n * BDRV_SECTOR_SIZE);
 
     block_acct_start(blk_get_stats(s->blk), &s->acct,
                      n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE);
diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c
index cffbf586d4..1fc7b86f19 100644
--- a/hw/input/lm832x.c
+++ b/hw/input/lm832x.c
@@ -401,7 +401,7 @@ static int lm_i2c_event(I2CSlave *i2c, enum i2c_event event)
     return 0;
 }
 
-static int lm_i2c_rx(I2CSlave *i2c)
+static uint8_t lm_i2c_rx(I2CSlave *i2c)
 {
     LM823KbdState *s = LM8323(i2c);
 
diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c
index 290a290e43..e0e5cb5d8e 100644
--- a/hw/intc/spapr_xive.c
+++ b/hw/intc/spapr_xive.c
@@ -244,13 +244,12 @@ static void spapr_xive_instance_init(Object *obj)
 {
     sPAPRXive *xive = SPAPR_XIVE(obj);
 
-    object_initialize(&xive->source, sizeof(xive->source), TYPE_XIVE_SOURCE);
-    object_property_add_child(obj, "source", OBJECT(&xive->source), NULL);
+    object_initialize_child(obj, "source", &xive->source, sizeof(xive->source),
+                            TYPE_XIVE_SOURCE, &error_abort, NULL);
 
-    object_initialize(&xive->end_source, sizeof(xive->end_source),
-                      TYPE_XIVE_END_SOURCE);
-    object_property_add_child(obj, "end_source", OBJECT(&xive->end_source),
-                              NULL);
+    object_initialize_child(obj, "end_source", &xive->end_source,
+                            sizeof(xive->end_source), TYPE_XIVE_END_SOURCE,
+                            &error_abort, NULL);
 }
 
 static void spapr_xive_realize(DeviceState *dev, Error **errp)
@@ -317,6 +316,9 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
     /* Map all regions */
     spapr_xive_map_mmio(xive);
 
+    xive->nodename = g_strdup_printf("interrupt-controller@%" PRIx64,
+                           xive->tm_base + XIVE_TM_USER_PAGE * (1 << TM_SHIFT));
+
     qemu_register_reset(spapr_xive_reset, dev);
 }
 
@@ -1448,7 +1450,6 @@ void spapr_dt_xive(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
         cpu_to_be32(7),    /* start */
         cpu_to_be32(0xf8), /* count */
     };
-    gchar *nodename;
 
     /* Thread Interrupt Management Area : User (ring 3) and OS (ring 2) */
     timas[0] = cpu_to_be64(xive->tm_base +
@@ -1458,10 +1459,7 @@ void spapr_dt_xive(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
                            XIVE_TM_OS_PAGE * (1ull << TM_SHIFT));
     timas[3] = cpu_to_be64(1ull << TM_SHIFT);
 
-    nodename = g_strdup_printf("interrupt-controller@%" PRIx64,
-                           xive->tm_base + XIVE_TM_USER_PAGE * (1 << TM_SHIFT));
-    _FDT(node = fdt_add_subnode(fdt, 0, nodename));
-    g_free(nodename);
+    _FDT(node = fdt_add_subnode(fdt, 0, xive->nodename));
 
     _FDT(fdt_setprop_string(fdt, node, "device_type", "power-ivpe"));
     _FDT(fdt_setprop(fdt, node, "reg", timas, sizeof(timas)));
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 3009fa7472..af7dc709ab 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -338,6 +338,9 @@ static void icp_realize(DeviceState *dev, Error **errp)
     case PPC_FLAGS_INPUT_POWER7:
         icp->output = env->irq_inputs[POWER7_INPUT_INT];
         break;
+    case PPC_FLAGS_INPUT_POWER9: /* For SPAPR xics emulation */
+        icp->output = env->irq_inputs[POWER9_INPUT_INT];
+        break;
 
     case PPC_FLAGS_INPUT_970:
         icp->output = env->irq_inputs[PPC970_INPUT_INT];
@@ -755,6 +758,10 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
 
     ics->irqs[srcno].flags |=
         lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
+
+    if (kvm_irqchip_in_kernel()) {
+        ics_set_kvm_state_one(ics, srcno);
+    }
 }
 
 static void xics_register_types(void)
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
index a00d0a7962..c6e1b630a4 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -213,45 +213,57 @@ void ics_synchronize_state(ICSState *ics)
     ics_get_kvm_state(ics);
 }
 
-int ics_set_kvm_state(ICSState *ics)
+int ics_set_kvm_state_one(ICSState *ics, int srcno)
 {
     uint64_t state;
-    int i;
     Error *local_err = NULL;
+    ICSIRQState *irq = &ics->irqs[srcno];
+    int ret;
 
-    for (i = 0; i < ics->nr_irqs; i++) {
-        ICSIRQState *irq = &ics->irqs[i];
-        int ret;
-
-        state = irq->server;
-        state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK)
-            << KVM_XICS_PRIORITY_SHIFT;
-        if (irq->priority != irq->saved_priority) {
-            assert(irq->priority == 0xff);
-            state |= KVM_XICS_MASKED;
-        }
+    state = irq->server;
+    state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK)
+        << KVM_XICS_PRIORITY_SHIFT;
+    if (irq->priority != irq->saved_priority) {
+        assert(irq->priority == 0xff);
+        state |= KVM_XICS_MASKED;
+    }
 
-        if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) {
-            state |= KVM_XICS_LEVEL_SENSITIVE;
-            if (irq->status & XICS_STATUS_ASSERTED) {
-                state |= KVM_XICS_PENDING;
-            }
-        } else {
-            if (irq->status & XICS_STATUS_MASKED_PENDING) {
-                state |= KVM_XICS_PENDING;
-            }
+    if (irq->flags & XICS_FLAGS_IRQ_LSI) {
+        state |= KVM_XICS_LEVEL_SENSITIVE;
+        if (irq->status & XICS_STATUS_ASSERTED) {
+            state |= KVM_XICS_PENDING;
         }
-        if (irq->status & XICS_STATUS_PRESENTED) {
-                state |= KVM_XICS_PRESENTED;
-        }
-        if (irq->status & XICS_STATUS_QUEUED) {
-                state |= KVM_XICS_QUEUED;
+    } else {
+        if (irq->status & XICS_STATUS_MASKED_PENDING) {
+            state |= KVM_XICS_PENDING;
         }
+    }
+    if (irq->status & XICS_STATUS_PRESENTED) {
+        state |= KVM_XICS_PRESENTED;
+    }
+    if (irq->status & XICS_STATUS_QUEUED) {
+        state |= KVM_XICS_QUEUED;
+    }
+
+    ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES,
+                            srcno + ics->offset, &state, true, &local_err);
+    if (local_err) {
+        error_report_err(local_err);
+        return ret;
+    }
+
+    return 0;
+}
+
+int ics_set_kvm_state(ICSState *ics)
+{
+    int i;
+
+    for (i = 0; i < ics->nr_irqs; i++) {
+        int ret;
 
-        ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES,
-                                i + ics->offset, &state, true, &local_err);
-        if (local_err) {
-            error_report_err(local_err);
+        ret = ics_set_kvm_state_one(ics, i);
+        if (ret) {
             return ret;
         }
     }
diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
index e2d8b38183..53bda6661b 100644
--- a/hw/intc/xics_spapr.c
+++ b/hw/intc/xics_spapr.c
@@ -254,7 +254,7 @@ void spapr_dt_xics(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
     };
     int node;
 
-    _FDT(node = fdt_add_subnode(fdt, 0, "interrupt-controller"));
+    _FDT(node = fdt_add_subnode(fdt, 0, XICS_NODENAME));
 
     _FDT(fdt_setprop_string(fdt, node, "device_type",
                             "PowerPC-External-Interrupt-Presentation"));
diff --git a/hw/intc/xive.c b/hw/intc/xive.c
index 2e9b8efd43..daa7badc84 100644
--- a/hw/intc/xive.c
+++ b/hw/intc/xive.c
@@ -481,8 +481,8 @@ static void xive_tctx_realize(DeviceState *dev, Error **errp)
 
     env = &cpu->env;
     switch (PPC_INPUT(env)) {
-    case PPC_FLAGS_INPUT_POWER7:
-        tctx->output = env->irq_inputs[POWER7_INPUT_INT];
+    case PPC_FLAGS_INPUT_POWER9:
+        tctx->output = env->irq_inputs[POWER9_INPUT_INT];
         break;
 
     default:
diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c
index 7302f6d74b..85d0532dd5 100644
--- a/hw/isa/vt82c686.c
+++ b/hw/isa/vt82c686.c
@@ -14,7 +14,6 @@
 #include "hw/hw.h"
 #include "hw/isa/vt82c686.h"
 #include "hw/i2c/i2c.h"
-#include "hw/i2c/smbus.h"
 #include "hw/pci/pci.h"
 #include "hw/isa/isa.h"
 #include "hw/isa/superio.h"
diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c
index 8531e07e5b..6f6efae9fc 100644
--- a/hw/m68k/mcf5208.c
+++ b/hw/m68k/mcf5208.c
@@ -27,6 +27,8 @@
 
 #define SYS_FREQ 166666666
 
+#define ROM_SIZE 0x200000
+
 #define PCSR_EN         0x0001
 #define PCSR_RLD        0x0002
 #define PCSR_PIF        0x0004
@@ -227,6 +229,7 @@ static void mcf5208evb_init(MachineState *machine)
     hwaddr entry;
     qemu_irq *pic;
     MemoryRegion *address_space_mem = get_system_memory();
+    MemoryRegion *rom = g_new(MemoryRegion, 1);
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     MemoryRegion *sram = g_new(MemoryRegion, 1);
 
@@ -237,6 +240,10 @@ static void mcf5208evb_init(MachineState *machine)
     env->vbr = 0;
     /* TODO: Configure BARs.  */
 
+    /* ROM at 0x00000000 */
+    memory_region_init_rom(rom, NULL, "mcf5208.rom", ROM_SIZE, &error_fatal);
+    memory_region_add_subregion(address_space_mem, 0x00000000, rom);
+
     /* DRAM at 0x40000000 */
     memory_region_allocate_system_memory(ram, NULL, "mcf5208.ram", ram_size);
     memory_region_add_subregion(address_space_mem, 0x40000000, ram);
@@ -285,9 +292,30 @@ static void mcf5208evb_init(MachineState *machine)
     /*  0xfc0a4000 GPIO.  */
     /* 0xfc0a8000 SDRAM controller.  */
 
+    /* Load firmware */
+    if (bios_name) {
+        char *fn;
+        uint8_t *ptr;
+
+        fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+        if (!fn) {
+            error_report("Could not find ROM image '%s'", bios_name);
+            exit(1);
+        }
+        if (load_image_targphys(fn, 0x0, ROM_SIZE) < 8) {
+            error_report("Could not load ROM image '%s'", bios_name);
+            exit(1);
+        }
+        g_free(fn);
+        /* Initial PC is always at offset 4 in firmware binaries */
+        ptr = rom_ptr(0x4, 4);
+        assert(ptr != NULL);
+        env->pc = ldl_p(ptr);
+    }
+
     /* Load kernel.  */
     if (!kernel_filename) {
-        if (qtest_enabled()) {
+        if (qtest_enabled() || bios_name) {
             return;
         }
         error_report("Kernel image must be specified");
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index 0c9b9e8292..152400b1fc 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -204,9 +204,7 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp)
 
 static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md)
 {
-    const PCDIMMDevice *dimm = PC_DIMM(md);
-
-    return dimm->addr;
+    return object_property_get_uint(OBJECT(md), PC_DIMM_ADDR_PROP, &error_abort);
 }
 
 static void pc_dimm_md_set_addr(MemoryDeviceState *md, uint64_t addr,
diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c
index eec6fd02c8..fbbc543eed 100644
--- a/hw/mips/mips_fulong2e.c
+++ b/hw/mips/mips_fulong2e.c
@@ -28,7 +28,7 @@
 #include "hw/isa/superio.h"
 #include "net/net.h"
 #include "hw/boards.h"
-#include "hw/i2c/smbus.h"
+#include "hw/i2c/smbus_eeprom.h"
 #include "hw/block/flash.h"
 #include "hw/mips/mips.h"
 #include "hw/mips/cpudevs.h"
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index 7a403ef1ce..39aef4b6db 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -33,7 +33,7 @@
 #include "hw/char/serial.h"
 #include "net/net.h"
 #include "hw/boards.h"
-#include "hw/i2c/smbus.h"
+#include "hw/i2c/smbus_eeprom.h"
 #include "hw/block/flash.h"
 #include "hw/mips/mips.h"
 #include "hw/mips/cpudevs.h"
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 74c91d250c..c71e07ae35 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -70,6 +70,7 @@ obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o
 obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o
 obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o
 obj-$(CONFIG_ARMSSE_CPUID) += armsse-cpuid.o
+obj-$(CONFIG_ARMSSE_MHU) += armsse-mhu.o
 
 obj-$(CONFIG_PVPANIC) += pvpanic.o
 obj-$(CONFIG_AUX) += auxbus.o
diff --git a/hw/misc/armsse-mhu.c b/hw/misc/armsse-mhu.c
new file mode 100644
index 0000000000..9ebca32e9a
--- /dev/null
+++ b/hw/misc/armsse-mhu.c
@@ -0,0 +1,198 @@
+/*
+ * ARM SSE-200 Message Handling Unit (MHU)
+ *
+ * Copyright (c) 2019 Linaro Limited
+ * Written by Peter Maydell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 or
+ *  (at your option) any later version.
+ */
+
+/*
+ * This is a model of the Message Handling Unit (MHU) which is part of the
+ * Arm SSE-200 and documented in
+ * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "qapi/error.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/armsse-mhu.h"
+
+REG32(CPU0INTR_STAT, 0x0)
+REG32(CPU0INTR_SET, 0x4)
+REG32(CPU0INTR_CLR, 0x8)
+REG32(CPU1INTR_STAT, 0x10)
+REG32(CPU1INTR_SET, 0x14)
+REG32(CPU1INTR_CLR, 0x18)
+REG32(PID4, 0xfd0)
+REG32(PID5, 0xfd4)
+REG32(PID6, 0xfd8)
+REG32(PID7, 0xfdc)
+REG32(PID0, 0xfe0)
+REG32(PID1, 0xfe4)
+REG32(PID2, 0xfe8)
+REG32(PID3, 0xfec)
+REG32(CID0, 0xff0)
+REG32(CID1, 0xff4)
+REG32(CID2, 0xff8)
+REG32(CID3, 0xffc)
+
+/* Valid bits in the interrupt registers. If any are set the IRQ is raised */
+#define INTR_MASK 0xf
+
+/* PID/CID values */
+static const int armsse_mhu_id[] = {
+    0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
+    0x56, 0xb8, 0x0b, 0x00, /* PID0..PID3 */
+    0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
+};
+
+static void armsse_mhu_update(ARMSSEMHU *s)
+{
+    qemu_set_irq(s->cpu0irq, s->cpu0intr != 0);
+    qemu_set_irq(s->cpu1irq, s->cpu1intr != 0);
+}
+
+static uint64_t armsse_mhu_read(void *opaque, hwaddr offset, unsigned size)
+{
+    ARMSSEMHU *s = ARMSSE_MHU(opaque);
+    uint64_t r;
+
+    switch (offset) {
+    case A_CPU0INTR_STAT:
+        r = s->cpu0intr;
+        break;
+
+    case A_CPU1INTR_STAT:
+        r = s->cpu1intr;
+        break;
+
+    case A_PID4 ... A_CID3:
+        r = armsse_mhu_id[(offset - A_PID4) / 4];
+        break;
+
+    case A_CPU0INTR_SET:
+    case A_CPU0INTR_CLR:
+    case A_CPU1INTR_SET:
+    case A_CPU1INTR_CLR:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "SSE MHU: read of write-only register at offset 0x%x\n",
+                      (int)offset);
+        r = 0;
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "SSE MHU read: bad offset 0x%x\n", (int)offset);
+        r = 0;
+        break;
+    }
+    trace_armsse_mhu_read(offset, r, size);
+    return r;
+}
+
+static void armsse_mhu_write(void *opaque, hwaddr offset,
+                             uint64_t value, unsigned size)
+{
+    ARMSSEMHU *s = ARMSSE_MHU(opaque);
+
+    trace_armsse_mhu_write(offset, value, size);
+
+    switch (offset) {
+    case A_CPU0INTR_SET:
+        s->cpu0intr |= (value & INTR_MASK);
+        break;
+    case A_CPU0INTR_CLR:
+        s->cpu0intr &= ~(value & INTR_MASK);
+        break;
+    case A_CPU1INTR_SET:
+        s->cpu1intr |= (value & INTR_MASK);
+        break;
+    case A_CPU1INTR_CLR:
+        s->cpu1intr &= ~(value & INTR_MASK);
+        break;
+
+    case A_CPU0INTR_STAT:
+    case A_CPU1INTR_STAT:
+    case A_PID4 ... A_CID3:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "SSE MHU: write to read-only register at offset 0x%x\n",
+                      (int)offset);
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "SSE MHU write: bad offset 0x%x\n", (int)offset);
+        break;
+    }
+
+    armsse_mhu_update(s);
+}
+
+static const MemoryRegionOps armsse_mhu_ops = {
+    .read = armsse_mhu_read,
+    .write = armsse_mhu_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+};
+
+static void armsse_mhu_reset(DeviceState *dev)
+{
+    ARMSSEMHU *s = ARMSSE_MHU(dev);
+
+    s->cpu0intr = 0;
+    s->cpu1intr = 0;
+}
+
+static const VMStateDescription armsse_mhu_vmstate = {
+    .name = "armsse-mhu",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(cpu0intr, ARMSSEMHU),
+        VMSTATE_UINT32(cpu1intr, ARMSSEMHU),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void armsse_mhu_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    ARMSSEMHU *s = ARMSSE_MHU(obj);
+
+    memory_region_init_io(&s->iomem, obj, &armsse_mhu_ops,
+                          s, "armsse-mhu", 0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->cpu0irq);
+    sysbus_init_irq(sbd, &s->cpu1irq);
+}
+
+static void armsse_mhu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = armsse_mhu_reset;
+    dc->vmsd = &armsse_mhu_vmstate;
+}
+
+static const TypeInfo armsse_mhu_info = {
+    .name = TYPE_ARMSSE_MHU,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ARMSSEMHU),
+    .instance_init = armsse_mhu_init,
+    .class_init = armsse_mhu_class_init,
+};
+
+static void armsse_mhu_register_types(void)
+{
+    type_register_static(&armsse_mhu_info);
+}
+
+type_init(armsse_mhu_register_types);
diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c
index a21d8bd678..54064a31ef 100644
--- a/hw/misc/iotkit-sysctl.c
+++ b/hw/misc/iotkit-sysctl.c
@@ -17,6 +17,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/bitops.h"
 #include "qemu/log.h"
 #include "trace.h"
 #include "qapi/error.h"
@@ -24,19 +25,32 @@
 #include "hw/sysbus.h"
 #include "hw/registerfields.h"
 #include "hw/misc/iotkit-sysctl.h"
+#include "target/arm/arm-powerctl.h"
+#include "target/arm/cpu.h"
 
 REG32(SECDBGSTAT, 0x0)
 REG32(SECDBGSET, 0x4)
 REG32(SECDBGCLR, 0x8)
+REG32(SCSECCTRL, 0xc)
+REG32(FCLK_DIV, 0x10)
+REG32(SYSCLK_DIV, 0x14)
+REG32(CLOCK_FORCE, 0x18)
 REG32(RESET_SYNDROME, 0x100)
 REG32(RESET_MASK, 0x104)
 REG32(SWRESET, 0x108)
     FIELD(SWRESET, SWRESETREQ, 9, 1)
 REG32(GRETREG, 0x10c)
-REG32(INITSVRTOR0, 0x110)
+REG32(INITSVTOR0, 0x110)
+REG32(INITSVTOR1, 0x114)
 REG32(CPUWAIT, 0x118)
-REG32(BUSWAIT, 0x11c)
+REG32(NMI_ENABLE, 0x11c) /* BUSWAIT in IoTKit */
 REG32(WICCTRL, 0x120)
+REG32(EWCTRL, 0x124)
+REG32(PDCM_PD_SYS_SENSE, 0x200)
+REG32(PDCM_PD_SRAM0_SENSE, 0x20c)
+REG32(PDCM_PD_SRAM1_SENSE, 0x210)
+REG32(PDCM_PD_SRAM2_SENSE, 0x214)
+REG32(PDCM_PD_SRAM3_SENSE, 0x218)
 REG32(PID4, 0xfd0)
 REG32(PID5, 0xfd4)
 REG32(PID6, 0xfd8)
@@ -57,6 +71,21 @@ static const int sysctl_id[] = {
     0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
 };
 
+/*
+ * Set the initial secure vector table offset address for the core.
+ * This will take effect when the CPU next resets.
+ */
+static void set_init_vtor(uint64_t cpuid, uint32_t vtor)
+{
+    Object *cpuobj = OBJECT(arm_get_cpu_by_id(cpuid));
+
+    if (cpuobj) {
+        if (object_property_find(cpuobj, "init-svtor", NULL)) {
+            object_property_set_uint(cpuobj, vtor, "init-svtor", &error_abort);
+        }
+    }
+}
+
 static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset,
                                     unsigned size)
 {
@@ -67,6 +96,30 @@ static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset,
     case A_SECDBGSTAT:
         r = s->secure_debug;
         break;
+    case A_SCSECCTRL:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->scsecctrl;
+        break;
+    case A_FCLK_DIV:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->fclk_div;
+        break;
+    case A_SYSCLK_DIV:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->sysclk_div;
+        break;
+    case A_CLOCK_FORCE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->clock_force;
+        break;
     case A_RESET_SYNDROME:
         r = s->reset_syndrome;
         break;
@@ -76,19 +129,65 @@ static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset,
     case A_GRETREG:
         r = s->gretreg;
         break;
-    case A_INITSVRTOR0:
-        r = s->initsvrtor0;
+    case A_INITSVTOR0:
+        r = s->initsvtor0;
+        break;
+    case A_INITSVTOR1:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->initsvtor1;
         break;
     case A_CPUWAIT:
         r = s->cpuwait;
         break;
-    case A_BUSWAIT:
-        /* In IoTKit BUSWAIT is reserved, R/O, zero */
-        r = 0;
+    case A_NMI_ENABLE:
+        /* In IoTKit this is named BUSWAIT but is marked reserved, R/O, zero */
+        if (!s->is_sse200) {
+            r = 0;
+            break;
+        }
+        r = s->nmi_enable;
         break;
     case A_WICCTRL:
         r = s->wicctrl;
         break;
+    case A_EWCTRL:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->ewctrl;
+        break;
+    case A_PDCM_PD_SYS_SENSE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->pdcm_pd_sys_sense;
+        break;
+    case A_PDCM_PD_SRAM0_SENSE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->pdcm_pd_sram0_sense;
+        break;
+    case A_PDCM_PD_SRAM1_SENSE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->pdcm_pd_sram1_sense;
+        break;
+    case A_PDCM_PD_SRAM2_SENSE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->pdcm_pd_sram2_sense;
+        break;
+    case A_PDCM_PD_SRAM3_SENSE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        r = s->pdcm_pd_sram3_sense;
+        break;
     case A_PID4 ... A_CID3:
         r = sysctl_id[(offset - A_PID4) / 4];
         break;
@@ -101,6 +200,7 @@ static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset,
         r = 0;
         break;
     default:
+    bad_offset:
         qemu_log_mask(LOG_GUEST_ERROR,
                       "IoTKit SysCtl read: bad offset %x\n", (int)offset);
         r = 0;
@@ -145,12 +245,19 @@ static void iotkit_sysctl_write(void *opaque, hwaddr offset,
          */
         s->gretreg = value;
         break;
-    case A_INITSVRTOR0:
-        qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl INITSVRTOR0 unimplemented\n");
-        s->initsvrtor0 = value;
+    case A_INITSVTOR0:
+        s->initsvtor0 = value;
+        set_init_vtor(0, s->initsvtor0);
         break;
     case A_CPUWAIT:
-        qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CPUWAIT unimplemented\n");
+        if ((s->cpuwait & 1) && !(value & 1)) {
+            /* Powering up CPU 0 */
+            arm_set_cpu_on_and_reset(0);
+        }
+        if ((s->cpuwait & 2) && !(value & 2)) {
+            /* Powering up CPU 1 */
+            arm_set_cpu_on_and_reset(1);
+        }
         s->cpuwait = value;
         break;
     case A_WICCTRL:
@@ -172,14 +279,105 @@ static void iotkit_sysctl_write(void *opaque, hwaddr offset,
             qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
         }
         break;
-    case A_BUSWAIT:        /* In IoTKit BUSWAIT is reserved, R/O, zero */
+    case A_SCSECCTRL:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SCSECCTRL unimplemented\n");
+        s->scsecctrl = value;
+        break;
+    case A_FCLK_DIV:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl FCLK_DIV unimplemented\n");
+        s->fclk_div = value;
+        break;
+    case A_SYSCLK_DIV:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SYSCLK_DIV unimplemented\n");
+        s->sysclk_div = value;
+        break;
+    case A_CLOCK_FORCE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CLOCK_FORCE unimplemented\n");
+        s->clock_force = value;
+        break;
+    case A_INITSVTOR1:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        s->initsvtor1 = value;
+        set_init_vtor(1, s->initsvtor1);
+        break;
+    case A_EWCTRL:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl EWCTRL unimplemented\n");
+        s->ewctrl = value;
+        break;
+    case A_PDCM_PD_SYS_SENSE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        qemu_log_mask(LOG_UNIMP,
+                      "IoTKit SysCtl PDCM_PD_SYS_SENSE unimplemented\n");
+        s->pdcm_pd_sys_sense = value;
+        break;
+    case A_PDCM_PD_SRAM0_SENSE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        qemu_log_mask(LOG_UNIMP,
+                      "IoTKit SysCtl PDCM_PD_SRAM0_SENSE unimplemented\n");
+        s->pdcm_pd_sram0_sense = value;
+        break;
+    case A_PDCM_PD_SRAM1_SENSE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        qemu_log_mask(LOG_UNIMP,
+                      "IoTKit SysCtl PDCM_PD_SRAM1_SENSE unimplemented\n");
+        s->pdcm_pd_sram1_sense = value;
+        break;
+    case A_PDCM_PD_SRAM2_SENSE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        qemu_log_mask(LOG_UNIMP,
+                      "IoTKit SysCtl PDCM_PD_SRAM2_SENSE unimplemented\n");
+        s->pdcm_pd_sram2_sense = value;
+        break;
+    case A_PDCM_PD_SRAM3_SENSE:
+        if (!s->is_sse200) {
+            goto bad_offset;
+        }
+        qemu_log_mask(LOG_UNIMP,
+                      "IoTKit SysCtl PDCM_PD_SRAM3_SENSE unimplemented\n");
+        s->pdcm_pd_sram3_sense = value;
+        break;
+    case A_NMI_ENABLE:
+        /* In IoTKit this is BUSWAIT: reserved, R/O, zero */
+        if (!s->is_sse200) {
+            goto ro_offset;
+        }
+        qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl NMI_ENABLE unimplemented\n");
+        s->nmi_enable = value;
+        break;
     case A_SECDBGSTAT:
     case A_PID4 ... A_CID3:
+    ro_offset:
         qemu_log_mask(LOG_GUEST_ERROR,
                       "IoTKit SysCtl write: write of RO offset %x\n",
                       (int)offset);
         break;
     default:
+    bad_offset:
         qemu_log_mask(LOG_GUEST_ERROR,
                       "IoTKit SysCtl write: bad offset %x\n", (int)offset);
         break;
@@ -206,9 +404,21 @@ static void iotkit_sysctl_reset(DeviceState *dev)
     s->reset_syndrome = 1;
     s->reset_mask = 0;
     s->gretreg = 0;
-    s->initsvrtor0 = 0x10000000;
-    s->cpuwait = 0;
+    s->initsvtor0 = s->initsvtor0_rst;
+    s->initsvtor1 = s->initsvtor1_rst;
+    s->cpuwait = s->cpuwait_rst;
     s->wicctrl = 0;
+    s->scsecctrl = 0;
+    s->fclk_div = 0;
+    s->sysclk_div = 0;
+    s->clock_force = 0;
+    s->nmi_enable = 0;
+    s->ewctrl = 0;
+    s->pdcm_pd_sys_sense = 0x7f;
+    s->pdcm_pd_sram0_sense = 0;
+    s->pdcm_pd_sram1_sense = 0;
+    s->pdcm_pd_sram2_sense = 0;
+    s->pdcm_pd_sram3_sense = 0;
 }
 
 static void iotkit_sysctl_init(Object *obj)
@@ -221,6 +431,44 @@ static void iotkit_sysctl_init(Object *obj)
     sysbus_init_mmio(sbd, &s->iomem);
 }
 
+static void iotkit_sysctl_realize(DeviceState *dev, Error **errp)
+{
+    IoTKitSysCtl *s = IOTKIT_SYSCTL(dev);
+
+    /* The top 4 bits of the SYS_VERSION register tell us if we're an SSE-200 */
+    if (extract32(s->sys_version, 28, 4) == 2) {
+        s->is_sse200 = true;
+    }
+}
+
+static bool sse200_needed(void *opaque)
+{
+    IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque);
+
+    return s->is_sse200;
+}
+
+static const VMStateDescription iotkit_sysctl_sse200_vmstate = {
+    .name = "iotkit-sysctl/sse-200",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = sse200_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(scsecctrl, IoTKitSysCtl),
+        VMSTATE_UINT32(fclk_div, IoTKitSysCtl),
+        VMSTATE_UINT32(sysclk_div, IoTKitSysCtl),
+        VMSTATE_UINT32(clock_force, IoTKitSysCtl),
+        VMSTATE_UINT32(initsvtor1, IoTKitSysCtl),
+        VMSTATE_UINT32(nmi_enable, IoTKitSysCtl),
+        VMSTATE_UINT32(pdcm_pd_sys_sense, IoTKitSysCtl),
+        VMSTATE_UINT32(pdcm_pd_sram0_sense, IoTKitSysCtl),
+        VMSTATE_UINT32(pdcm_pd_sram1_sense, IoTKitSysCtl),
+        VMSTATE_UINT32(pdcm_pd_sram2_sense, IoTKitSysCtl),
+        VMSTATE_UINT32(pdcm_pd_sram3_sense, IoTKitSysCtl),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription iotkit_sysctl_vmstate = {
     .name = "iotkit-sysctl",
     .version_id = 1,
@@ -230,19 +478,35 @@ static const VMStateDescription iotkit_sysctl_vmstate = {
         VMSTATE_UINT32(reset_syndrome, IoTKitSysCtl),
         VMSTATE_UINT32(reset_mask, IoTKitSysCtl),
         VMSTATE_UINT32(gretreg, IoTKitSysCtl),
-        VMSTATE_UINT32(initsvrtor0, IoTKitSysCtl),
+        VMSTATE_UINT32(initsvtor0, IoTKitSysCtl),
         VMSTATE_UINT32(cpuwait, IoTKitSysCtl),
         VMSTATE_UINT32(wicctrl, IoTKitSysCtl),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription*[]) {
+        &iotkit_sysctl_sse200_vmstate,
+        NULL
     }
 };
 
+static Property iotkit_sysctl_props[] = {
+    DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysCtl, sys_version, 0),
+    DEFINE_PROP_UINT32("CPUWAIT_RST", IoTKitSysCtl, cpuwait_rst, 0),
+    DEFINE_PROP_UINT32("INITSVTOR0_RST", IoTKitSysCtl, initsvtor0_rst,
+                       0x10000000),
+    DEFINE_PROP_UINT32("INITSVTOR1_RST", IoTKitSysCtl, initsvtor1_rst,
+                       0x10000000),
+    DEFINE_PROP_END_OF_LIST()
+};
+
 static void iotkit_sysctl_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->vmsd = &iotkit_sysctl_vmstate;
     dc->reset = iotkit_sysctl_reset;
+    dc->props = iotkit_sysctl_props;
+    dc->realize = iotkit_sysctl_realize;
 }
 
 static const TypeInfo iotkit_sysctl_info = {
diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index 9775d5274a..7325d3f287 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -115,7 +115,7 @@ static void pca9552_autoinc(PCA9552State *s)
     }
 }
 
-static int pca9552_recv(I2CSlave *i2c)
+static uint8_t pca9552_recv(I2CSlave *i2c)
 {
     PCA9552State *s = PCA9552(i2c);
     uint8_t ret;
diff --git a/hw/misc/tmp105.c b/hw/misc/tmp105.c
index f6d7163273..0c32f6f8b6 100644
--- a/hw/misc/tmp105.c
+++ b/hw/misc/tmp105.c
@@ -147,7 +147,7 @@ static void tmp105_write(TMP105State *s)
     }
 }
 
-static int tmp105_rx(I2CSlave *i2c)
+static uint8_t tmp105_rx(I2CSlave *i2c)
 {
     TMP105State *s = TMP105(i2c);
 
diff --git a/hw/misc/tmp421.c b/hw/misc/tmp421.c
index eeb11000f0..ce6d40ac9c 100644
--- a/hw/misc/tmp421.c
+++ b/hw/misc/tmp421.c
@@ -249,7 +249,7 @@ static void tmp421_write(TMP421State *s)
     }
 }
 
-static int tmp421_rx(I2CSlave *i2c)
+static uint8_t tmp421_rx(I2CSlave *i2c)
 {
     TMP421State *s = TMP421(i2c);
 
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index b0701bddd3..c1795bb54b 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -136,3 +136,7 @@ iotkit_sysctl_reset(void) "IoTKit SysCtl: reset"
 # hw/misc/armsse-cpuid.c
 armsse_cpuid_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
 armsse_cpuid_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+
+# hw/misc/armsse-mhu.c
+armsse_mhu_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+armsse_mhu_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index a43351aa04..ea63715780 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -37,7 +37,9 @@ obj-$(CONFIG_PSERIES) += spapr_llan.o
 obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o
 
 obj-$(CONFIG_VIRTIO_NET) += virtio-net.o
-obj-y += vhost_net.o
+common-obj-$(call land,$(CONFIG_VIRTIO_NET),$(CONFIG_VHOST_NET)) += vhost_net.o
+common-obj-$(call lnot,$(call land,$(CONFIG_VIRTIO_NET),$(CONFIG_VHOST_NET))) += vhost_net-stub.o
+common-obj-$(CONFIG_ALL) += vhost_net-stub.o
 
 obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \
 			fsl_etsec/rings.o fsl_etsec/miim.o
diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c
new file mode 100644
index 0000000000..aac0e98228
--- /dev/null
+++ b/hw/net/vhost_net-stub.c
@@ -0,0 +1,92 @@
+/*
+ * vhost-net support
+ *
+ * Copyright Red Hat, Inc. 2010
+ *
+ * Authors:
+ *  Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "net/net.h"
+#include "net/tap.h"
+#include "net/vhost-user.h"
+
+#include "hw/virtio/virtio-net.h"
+#include "net/vhost_net.h"
+#include "qemu/error-report.h"
+
+
+uint64_t vhost_net_get_max_queues(VHostNetState *net)
+{
+    return 1;
+}
+
+struct vhost_net *vhost_net_init(VhostNetOptions *options)
+{
+    error_report("vhost-net support is not compiled in");
+    return NULL;
+}
+
+int vhost_net_start(VirtIODevice *dev,
+                    NetClientState *ncs,
+                    int total_queues)
+{
+    return -ENOSYS;
+}
+void vhost_net_stop(VirtIODevice *dev,
+                    NetClientState *ncs,
+                    int total_queues)
+{
+}
+
+void vhost_net_cleanup(struct vhost_net *net)
+{
+}
+
+uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features)
+{
+    return features;
+}
+
+void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
+{
+}
+
+uint64_t vhost_net_get_acked_features(VHostNetState *net)
+{
+    return 0;
+}
+
+bool vhost_net_virtqueue_pending(VHostNetState *net, int idx)
+{
+    return false;
+}
+
+void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev,
+                              int idx, bool mask)
+{
+}
+
+int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr)
+{
+    return -1;
+}
+
+VHostNetState *get_vhost_net(NetClientState *nc)
+{
+    return 0;
+}
+
+int vhost_set_vring_enable(NetClientState *nc, int enable)
+{
+    return 0;
+}
+
+int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu)
+{
+    return 0;
+}
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index e037db63a3..be3cc88370 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -18,17 +18,13 @@
 #include "net/tap.h"
 #include "net/vhost-user.h"
 
+#include "standard-headers/linux/vhost_types.h"
 #include "hw/virtio/virtio-net.h"
 #include "net/vhost_net.h"
 #include "qemu/error-report.h"
 
 
-#ifdef CONFIG_VHOST_NET
-#include <linux/vhost.h>
 #include <sys/socket.h>
-#include <linux/kvm.h>
-#include <netpacket/packet.h>
-#include <net/ethernet.h>
 #include <net/if.h>
 #include <netinet/in.h>
 
@@ -136,7 +132,7 @@ static int vhost_net_get_fd(NetClientState *backend)
         return tap_get_fd(backend);
     default:
         fprintf(stderr, "vhost-net requires tap backend\n");
-        return -EBADFD;
+        return -ENOSYS;
     }
 }
 
@@ -194,6 +190,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
     }
 
     /* Set sane init value. Override when guest acks. */
+#ifdef CONFIG_VHOST_NET_USER
     if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
         features = vhost_user_get_acked_features(net->nc);
         if (~net->dev.features & features) {
@@ -203,6 +200,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
             goto fail;
         }
     }
+#endif
 
     vhost_net_ack_features(net, features);
 
@@ -414,10 +412,12 @@ VHostNetState *get_vhost_net(NetClientState *nc)
     case NET_CLIENT_DRIVER_TAP:
         vhost_net = tap_get_vhost_net(nc);
         break;
+#ifdef CONFIG_VHOST_NET_USER
     case NET_CLIENT_DRIVER_VHOST_USER:
         vhost_net = vhost_user_get_vhost_net(nc);
         assert(vhost_net);
         break;
+#endif
     default:
         break;
     }
@@ -449,76 +449,3 @@ int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu)
 
     return vhost_ops->vhost_net_set_mtu(&net->dev, mtu);
 }
-
-#else
-uint64_t vhost_net_get_max_queues(VHostNetState *net)
-{
-    return 1;
-}
-
-struct vhost_net *vhost_net_init(VhostNetOptions *options)
-{
-    error_report("vhost-net support is not compiled in");
-    return NULL;
-}
-
-int vhost_net_start(VirtIODevice *dev,
-                    NetClientState *ncs,
-                    int total_queues)
-{
-    return -ENOSYS;
-}
-void vhost_net_stop(VirtIODevice *dev,
-                    NetClientState *ncs,
-                    int total_queues)
-{
-}
-
-void vhost_net_cleanup(struct vhost_net *net)
-{
-}
-
-uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features)
-{
-    return features;
-}
-
-void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
-{
-}
-
-uint64_t vhost_net_get_acked_features(VHostNetState *net)
-{
-    return 0;
-}
-
-bool vhost_net_virtqueue_pending(VHostNetState *net, int idx)
-{
-    return false;
-}
-
-void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev,
-                              int idx, bool mask)
-{
-}
-
-int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr)
-{
-    return -1;
-}
-
-VHostNetState *get_vhost_net(NetClientState *nc)
-{
-    return 0;
-}
-
-int vhost_set_vring_enable(NetClientState *nc, int enable)
-{
-    return 0;
-}
-
-int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu)
-{
-    return 0;
-}
-#endif
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 3f319ef723..6e6b146022 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -82,29 +82,17 @@ static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
 
 #endif
 
-/*
- * Calculate the number of bytes up to and including the given 'field' of
- * 'container'.
- */
-#define endof(container, field) \
-    (offsetof(container, field) + sizeof_field(container, field))
-
-typedef struct VirtIOFeature {
-    uint64_t flags;
-    size_t end;
-} VirtIOFeature;
-
 static VirtIOFeature feature_sizes[] = {
     {.flags = 1ULL << VIRTIO_NET_F_MAC,
-     .end = endof(struct virtio_net_config, mac)},
+     .end = virtio_endof(struct virtio_net_config, mac)},
     {.flags = 1ULL << VIRTIO_NET_F_STATUS,
-     .end = endof(struct virtio_net_config, status)},
+     .end = virtio_endof(struct virtio_net_config, status)},
     {.flags = 1ULL << VIRTIO_NET_F_MQ,
-     .end = endof(struct virtio_net_config, max_virtqueue_pairs)},
+     .end = virtio_endof(struct virtio_net_config, max_virtqueue_pairs)},
     {.flags = 1ULL << VIRTIO_NET_F_MTU,
-     .end = endof(struct virtio_net_config, mtu)},
+     .end = virtio_endof(struct virtio_net_config, mtu)},
     {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
-     .end = endof(struct virtio_net_config, duplex)},
+     .end = virtio_endof(struct virtio_net_config, duplex)},
     {}
 };
 
@@ -2580,15 +2568,10 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
 
 static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features)
 {
-    int i, config_size = 0;
     virtio_add_feature(&host_features, VIRTIO_NET_F_MAC);
 
-    for (i = 0; feature_sizes[i].flags != 0; i++) {
-        if (host_features & feature_sizes[i].flags) {
-            config_size = MAX(feature_sizes[i].end, config_size);
-        }
-    }
-    n->config_size = config_size;
+    n->config_size = virtio_feature_get_config_size(feature_sizes,
+                                                    host_features);
 }
 
 void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c
index 27cd01e615..d1456dafbd 100644
--- a/hw/nvram/eeprom_at24c.c
+++ b/hw/nvram/eeprom_at24c.c
@@ -74,10 +74,10 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event)
 }
 
 static
-int at24c_eeprom_recv(I2CSlave *s)
+uint8_t at24c_eeprom_recv(I2CSlave *s)
 {
     EEPROMState *ee = AT24C_EE(s);
-    int ret;
+    uint8_t ret;
 
     ret = ee->mem[ee->cur];
 
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 3f7c366093..3618d6ab2e 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -834,9 +834,12 @@ void pcie_add_capability(PCIDevice *dev,
 /*
  * Sync the PCIe Link Status negotiated speed and width of a bridge with the
  * downstream device.  If downstream device is not present, re-write with the
- * Link Capability fields.  Limit width and speed to bridge capabilities for
- * compatibility.  Use config_read to access the downstream device since it
- * could be an assigned device with volatile link information.
+ * Link Capability fields.  If downstream device reports invalid width or
+ * speed, replace with minimum values (LnkSta fields are RsvdZ on VFs but such
+ * values interfere with PCIe native hotplug detecting new devices).  Limit
+ * width and speed to bridge capabilities for compatibility.  Use config_read
+ * to access the downstream device since it could be an assigned device with
+ * volatile link information.
  */
 void pcie_sync_bridge_lnk(PCIDevice *bridge_dev)
 {
@@ -856,11 +859,15 @@ void pcie_sync_bridge_lnk(PCIDevice *bridge_dev)
         if ((lnksta & PCI_EXP_LNKSTA_NLW) > (lnkcap & PCI_EXP_LNKCAP_MLW)) {
             lnksta &= ~PCI_EXP_LNKSTA_NLW;
             lnksta |= lnkcap & PCI_EXP_LNKCAP_MLW;
+        } else if (!(lnksta & PCI_EXP_LNKSTA_NLW)) {
+            lnksta |= QEMU_PCI_EXP_LNKSTA_NLW(QEMU_PCI_EXP_LNK_X1);
         }
 
         if ((lnksta & PCI_EXP_LNKSTA_CLS) > (lnkcap & PCI_EXP_LNKCAP_SLS)) {
             lnksta &= ~PCI_EXP_LNKSTA_CLS;
             lnksta |= lnkcap & PCI_EXP_LNKCAP_SLS;
+        } else if (!(lnksta & PCI_EXP_LNKSTA_CLS)) {
+            lnksta |= QEMU_PCI_EXP_LNKSTA_CLS(QEMU_PCI_EXP_LNK_2_5GT);
         }
     }
 
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index da540860a2..3d5dfef220 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -47,14 +47,16 @@
 
 #include <libfdt.h>
 
-#define FDT_MAX_SIZE            0x00100000
+#define FDT_MAX_SIZE            (1 * MiB)
 
 #define FW_FILE_NAME            "skiboot.lid"
 #define FW_LOAD_ADDR            0x0
-#define FW_MAX_SIZE             0x00400000
+#define FW_MAX_SIZE             (4 * MiB)
 
 #define KERNEL_LOAD_ADDR        0x20000000
+#define KERNEL_MAX_SIZE         (256 * MiB)
 #define INITRD_LOAD_ADDR        0x60000000
+#define INITRD_MAX_SIZE         (256 * MiB)
 
 static const char *pnv_chip_core_typename(const PnvChip *o)
 {
@@ -588,7 +590,7 @@ static void pnv_init(MachineState *machine)
         long kernel_size;
 
         kernel_size = load_image_targphys(machine->kernel_filename,
-                                          KERNEL_LOAD_ADDR, 0x2000000);
+                                          KERNEL_LOAD_ADDR, KERNEL_MAX_SIZE);
         if (kernel_size < 0) {
             error_report("Could not load kernel '%s'",
                          machine->kernel_filename);
@@ -600,7 +602,7 @@ static void pnv_init(MachineState *machine)
     if (machine->initrd_filename) {
         pnv->initrd_base = INITRD_LOAD_ADDR;
         pnv->initrd_size = load_image_targphys(machine->initrd_filename,
-                                  pnv->initrd_base, 0x10000000); /* 128MB max */
+                                  pnv->initrd_base, INITRD_MAX_SIZE);
         if (pnv->initrd_size < 0) {
             error_report("Could not load initial ram disk '%s'",
                          machine->initrd_filename);
@@ -736,18 +738,18 @@ static void pnv_chip_power8_instance_init(Object *obj)
 {
     Pnv8Chip *chip8 = PNV8_CHIP(obj);
 
-    object_initialize(&chip8->psi, sizeof(chip8->psi), TYPE_PNV_PSI);
-    object_property_add_child(obj, "psi", OBJECT(&chip8->psi), NULL);
+    object_initialize_child(obj, "psi",  &chip8->psi, sizeof(chip8->psi),
+                            TYPE_PNV_PSI, &error_abort, NULL);
     object_property_add_const_link(OBJECT(&chip8->psi), "xics",
                                    OBJECT(qdev_get_machine()), &error_abort);
 
-    object_initialize(&chip8->lpc, sizeof(chip8->lpc), TYPE_PNV_LPC);
-    object_property_add_child(obj, "lpc", OBJECT(&chip8->lpc), NULL);
+    object_initialize_child(obj, "lpc",  &chip8->lpc, sizeof(chip8->lpc),
+                            TYPE_PNV_LPC, &error_abort, NULL);
     object_property_add_const_link(OBJECT(&chip8->lpc), "psi",
                                    OBJECT(&chip8->psi), &error_abort);
 
-    object_initialize(&chip8->occ, sizeof(chip8->occ), TYPE_PNV_OCC);
-    object_property_add_child(obj, "occ", OBJECT(&chip8->occ), NULL);
+    object_initialize_child(obj, "occ",  &chip8->occ, sizeof(chip8->occ),
+                            TYPE_PNV_OCC, &error_abort, NULL);
     object_property_add_const_link(OBJECT(&chip8->occ), "psi",
                                    OBJECT(&chip8->psi), &error_abort);
 }
diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c
index 8ced095063..44bc0cbf58 100644
--- a/hw/ppc/pnv_psi.c
+++ b/hw/ppc/pnv_psi.c
@@ -444,8 +444,8 @@ static void pnv_psi_init(Object *obj)
 {
     PnvPsi *psi = PNV_PSI(obj);
 
-    object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE);
-    object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL);
+    object_initialize_child(obj, "ics-psi",  &psi->ics, sizeof(psi->ics),
+                            TYPE_ICS_SIMPLE, &error_abort, NULL);
 }
 
 static const uint8_t irq_to_xivr[] = {
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index cffdc3914a..d1e3d4cd20 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -306,6 +306,48 @@ void ppcPOWER7_irq_init(PowerPCCPU *cpu)
     env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu,
                                                   POWER7_INPUT_NB);
 }
+
+/* POWER9 internal IRQ controller */
+static void power9_set_irq(void *opaque, int pin, int level)
+{
+    PowerPCCPU *cpu = opaque;
+    CPUPPCState *env = &cpu->env;
+
+    LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
+                env, pin, level);
+
+    switch (pin) {
+    case POWER9_INPUT_INT:
+        /* Level sensitive - active high */
+        LOG_IRQ("%s: set the external IRQ state to %d\n",
+                __func__, level);
+        ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
+        break;
+    case POWER9_INPUT_HINT:
+        /* Level sensitive - active high */
+        LOG_IRQ("%s: set the external IRQ state to %d\n",
+                __func__, level);
+        ppc_set_irq(cpu, PPC_INTERRUPT_HVIRT, level);
+        break;
+    default:
+        /* Unknown pin - do nothing */
+        LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
+        return;
+    }
+    if (level) {
+        env->irq_input_state |= 1 << pin;
+    } else {
+        env->irq_input_state &= ~(1 << pin);
+    }
+}
+
+void ppcPOWER9_irq_init(PowerPCCPU *cpu)
+{
+    CPUPPCState *env = &cpu->env;
+
+    env->irq_inputs = (void **)qemu_allocate_irqs(&power9_set_irq, cpu,
+                                                  POWER9_INPUT_NB);
+}
 #endif /* defined(TARGET_PPC64) */
 
 void ppc40x_core_reset(PowerPCCPU *cpu)
@@ -776,7 +818,7 @@ static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu)
      * interrupts in a PM state. Not only they don't cause a
      * wakeup but they also get effectively discarded.
      */
-    if (!env->in_pm_state) {
+    if (!env->resume_as_sreset) {
         ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
     }
 }
diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c
index 75250d49e4..d455c4bd07 100644
--- a/hw/ppc/sam460ex.c
+++ b/hw/ppc/sam460ex.c
@@ -34,7 +34,7 @@
 #include "hw/sysbus.h"
 #include "hw/char/serial.h"
 #include "hw/i2c/ppc4xx_i2c.h"
-#include "hw/i2c/smbus.h"
+#include "hw/i2c/smbus_eeprom.h"
 #include "hw/usb/hcd-ehci.h"
 #include "hw/ppc/fdt.h"
 
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index abf9ebce59..b6a571b6f1 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1247,13 +1247,30 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr)
      * Add info to guest to indentify which host is it being run on
      * and what is the uuid of the guest
      */
-    if (kvmppc_get_host_model(&buf)) {
-        _FDT(fdt_setprop_string(fdt, 0, "host-model", buf));
-        g_free(buf);
+    if (spapr->host_model && !g_str_equal(spapr->host_model, "none")) {
+        if (g_str_equal(spapr->host_model, "passthrough")) {
+            /* -M host-model=passthrough */
+            if (kvmppc_get_host_model(&buf)) {
+                _FDT(fdt_setprop_string(fdt, 0, "host-model", buf));
+                g_free(buf);
+            }
+        } else {
+            /* -M host-model=<user-string> */
+            _FDT(fdt_setprop_string(fdt, 0, "host-model", spapr->host_model));
+        }
     }
-    if (kvmppc_get_host_serial(&buf)) {
-        _FDT(fdt_setprop_string(fdt, 0, "host-serial", buf));
-        g_free(buf);
+
+    if (spapr->host_serial && !g_str_equal(spapr->host_serial, "none")) {
+        if (g_str_equal(spapr->host_serial, "passthrough")) {
+            /* -M host-serial=passthrough */
+            if (kvmppc_get_host_serial(&buf)) {
+                _FDT(fdt_setprop_string(fdt, 0, "host-serial", buf));
+                g_free(buf);
+            }
+        } else {
+            /* -M host-serial=<user-string> */
+            _FDT(fdt_setprop_string(fdt, 0, "host-serial", spapr->host_serial));
+        }
     }
 
     buf = qemu_uuid_unparse_strdup(&qemu_uuid);
@@ -1295,7 +1312,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr)
 
     QLIST_FOREACH(phb, &spapr->phbs, list) {
         ret = spapr_populate_pci_dt(phb, PHANDLE_INTC, fdt,
-                                    spapr->irq->nr_msis);
+                                    spapr->irq->nr_msis, NULL);
         if (ret < 0) {
             error_report("couldn't setup PCI devices in fdt");
             exit(1);
@@ -1348,6 +1365,14 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr)
         exit(1);
     }
 
+    if (smc->dr_phb_enabled) {
+        ret = spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB);
+        if (ret < 0) {
+            error_report("Couldn't set up PHB DR device tree properties");
+            exit(1);
+        }
+    }
+
     return fdt;
 }
 
@@ -1372,11 +1397,44 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
     }
 }
 
-static uint64_t spapr_get_patbe(PPCVirtualHypervisor *vhyp)
+struct LPCRSyncState {
+    target_ulong value;
+    target_ulong mask;
+};
+
+static void do_lpcr_sync(CPUState *cs, run_on_cpu_data arg)
+{
+    struct LPCRSyncState *s = arg.host_ptr;
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    CPUPPCState *env = &cpu->env;
+    target_ulong lpcr;
+
+    cpu_synchronize_state(cs);
+    lpcr = env->spr[SPR_LPCR];
+    lpcr &= ~s->mask;
+    lpcr |= s->value;
+    ppc_store_lpcr(cpu, lpcr);
+}
+
+void spapr_set_all_lpcrs(target_ulong value, target_ulong mask)
+{
+    CPUState *cs;
+    struct LPCRSyncState s = {
+        .value = value,
+        .mask = mask
+    };
+    CPU_FOREACH(cs) {
+        run_on_cpu(cs, do_lpcr_sync, RUN_ON_CPU_HOST_PTR(&s));
+    }
+}
+
+static void spapr_get_pate(PPCVirtualHypervisor *vhyp, ppc_v3_pate_t *entry)
 {
     sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp);
 
-    return spapr->patb_entry;
+    /* Copy PATE1:GR into PATE0:HR */
+    entry->dw0 = spapr->patb_entry & PATE0_HR;
+    entry->dw1 = spapr->patb_entry;
 }
 
 #define HPTE(_table, _i)   (void *)(((uint64_t *)(_table)) + ((_i) * 2))
@@ -1476,8 +1534,25 @@ static void spapr_store_hpte(PPCVirtualHypervisor *vhyp, hwaddr ptex,
     if (!spapr->htab) {
         kvmppc_write_hpte(ptex, pte0, pte1);
     } else {
-        stq_p(spapr->htab + offset, pte0);
-        stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1);
+        if (pte0 & HPTE64_V_VALID) {
+            stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1);
+            /*
+             * When setting valid, we write PTE1 first. This ensures
+             * proper synchronization with the reading code in
+             * ppc_hash64_pteg_search()
+             */
+            smp_wmb();
+            stq_p(spapr->htab + offset, pte0);
+        } else {
+            stq_p(spapr->htab + offset, pte0);
+            /*
+             * When clearing it we set PTE0 first. This ensures proper
+             * synchronization with the reading code in
+             * ppc_hash64_pteg_search()
+             */
+            smp_wmb();
+            stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1);
+        }
     }
 }
 
@@ -1548,7 +1623,7 @@ void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift,
         }
     }
     /* We're setting up a hash table, so that means we're not radix */
-    spapr->patb_entry = 0;
+    spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT);
 }
 
 void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr)
@@ -1602,16 +1677,21 @@ static void spapr_machine_reset(void)
     if (kvm_enabled() && kvmppc_has_cap_mmu_radix() &&
         ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0,
                               spapr->max_compat_pvr)) {
-        /* If using KVM with radix mode available, VCPUs can be started
+        /*
+         * If using KVM with radix mode available, VCPUs can be started
          * without a HPT because KVM will start them in radix mode.
-         * Set the GR bit in PATB so that we know there is no HPT. */
-        spapr->patb_entry = PATBE1_GR;
+         * Set the GR bit in PATE so that we know there is no HPT.
+         */
+        spapr->patb_entry = PATE1_GR;
+        spapr_set_all_lpcrs(LPCR_HR | LPCR_UPRT, LPCR_HR | LPCR_UPRT);
     } else {
         spapr_setup_hpt_and_vrma(spapr);
     }
 
-    /* if this reset wasn't generated by CAS, we should reset our
-     * negotiated options and start from scratch */
+    /*
+     * If this reset wasn't generated by CAS, we should reset our
+     * negotiated options and start from scratch
+     */
     if (!spapr->cas_reboot) {
         spapr_ovec_cleanup(spapr->ov5_cas);
         spapr->ov5_cas = spapr_ovec_new();
@@ -1696,9 +1776,9 @@ static void spapr_create_nvram(sPAPRMachineState *spapr)
 
 static void spapr_rtc_create(sPAPRMachineState *spapr)
 {
-    object_initialize(&spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC);
-    object_property_add_child(OBJECT(spapr), "rtc", OBJECT(&spapr->rtc),
-                              &error_fatal);
+    object_initialize_child(OBJECT(spapr), "rtc",
+                            &spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC,
+                            &error_fatal, NULL);
     object_property_set_bool(OBJECT(&spapr->rtc), true, "realized",
                               &error_fatal);
     object_property_add_alias(OBJECT(spapr), "rtc-time", OBJECT(&spapr->rtc),
@@ -1761,9 +1841,16 @@ static int spapr_post_load(void *opaque, int version_id)
 
     if (kvm_enabled() && spapr->patb_entry) {
         PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
-        bool radix = !!(spapr->patb_entry & PATBE1_GR);
+        bool radix = !!(spapr->patb_entry & PATE1_GR);
         bool gtse = !!(cpu->env.spr[SPR_LPCR] & LPCR_GTSE);
 
+        /*
+         * Update LPCR:HR and UPRT as they may not be set properly in
+         * the stream
+         */
+        spapr_set_all_lpcrs(radix ? (LPCR_HR | LPCR_UPRT) : 0,
+                            LPCR_HR | LPCR_UPRT);
+
         err = kvmppc_configure_v3_mmu(cpu, radix, gtse, spapr->patb_entry);
         if (err) {
             error_report("Process table config unsupported by the host");
@@ -2796,6 +2883,19 @@ static void spapr_machine_init(MachineState *machine)
     /* We always have at least the nvram device on VIO */
     spapr_create_nvram(spapr);
 
+    /*
+     * Setup hotplug / dynamic-reconfiguration connectors. top-level
+     * connectors (described in root DT node's "ibm,drc-types" property)
+     * are pre-initialized here. additional child connectors (such as
+     * connectors for a PHBs PCI slots) are added as needed during their
+     * parent's realization.
+     */
+    if (smc->dr_phb_enabled) {
+        for (i = 0; i < SPAPR_MAX_PHBS; i++) {
+            spapr_dr_connector_new(OBJECT(machine), TYPE_SPAPR_DRC_PHB, i);
+        }
+    }
+
     /* Set up PCI */
     spapr_pci_rtas_init();
 
@@ -2909,6 +3009,9 @@ static void spapr_machine_init(MachineState *machine)
     register_savevm_live(NULL, "spapr/htab", -1, 1,
                          &savevm_htab_handlers, spapr);
 
+    qbus_set_hotplug_handler(sysbus_get_default(), OBJECT(machine),
+                             &error_fatal);
+
     qemu_register_boot_set(spapr_boot_set, spapr);
 
     if (kvm_enabled()) {
@@ -3144,6 +3247,36 @@ static void spapr_set_ic_mode(Object *obj, const char *value, Error **errp)
     }
 }
 
+static char *spapr_get_host_model(Object *obj, Error **errp)
+{
+    sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+
+    return g_strdup(spapr->host_model);
+}
+
+static void spapr_set_host_model(Object *obj, const char *value, Error **errp)
+{
+    sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+
+    g_free(spapr->host_model);
+    spapr->host_model = g_strdup(value);
+}
+
+static char *spapr_get_host_serial(Object *obj, Error **errp)
+{
+    sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+
+    return g_strdup(spapr->host_serial);
+}
+
+static void spapr_set_host_serial(Object *obj, const char *value, Error **errp)
+{
+    sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+
+    g_free(spapr->host_serial);
+    spapr->host_serial = g_strdup(value);
+}
+
 static void spapr_instance_init(Object *obj)
 {
     sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
@@ -3189,6 +3322,17 @@ static void spapr_instance_init(Object *obj)
     object_property_set_description(obj, "ic-mode",
                  "Specifies the interrupt controller mode (xics, xive, dual)",
                  NULL);
+
+    object_property_add_str(obj, "host-model",
+        spapr_get_host_model, spapr_set_host_model,
+        &error_abort);
+    object_property_set_description(obj, "host-model",
+        "Set host's model-id to use - none|passthrough|string", &error_abort);
+    object_property_add_str(obj, "host-serial",
+        spapr_get_host_serial, spapr_set_host_serial,
+        &error_abort);
+    object_property_set_description(obj, "host-serial",
+        "Set host's system-id to use - none|passthrough|string", &error_abort);
 }
 
 static void spapr_machine_finalizefn(Object *obj)
@@ -3213,14 +3357,26 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp)
     }
 }
 
+int spapr_lmb_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
+                          void *fdt, int *fdt_start_offset, Error **errp)
+{
+    uint64_t addr;
+    uint32_t node;
+
+    addr = spapr_drc_index(drc) * SPAPR_MEMORY_BLOCK_SIZE;
+    node = object_property_get_uint(OBJECT(drc->dev), PC_DIMM_NODE_PROP,
+                                    &error_abort);
+    *fdt_start_offset = spapr_populate_memory_node(fdt, node, addr,
+                                                   SPAPR_MEMORY_BLOCK_SIZE);
+    return 0;
+}
+
 static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
-                           uint32_t node, bool dedicated_hp_event_source,
-                           Error **errp)
+                           bool dedicated_hp_event_source, Error **errp)
 {
     sPAPRDRConnector *drc;
     uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE;
-    int i, fdt_offset, fdt_size;
-    void *fdt;
+    int i;
     uint64_t addr = addr_start;
     bool hotplugged = spapr_drc_hotplugged(dev);
     Error *local_err = NULL;
@@ -3230,11 +3386,7 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
                               addr / SPAPR_MEMORY_BLOCK_SIZE);
         g_assert(drc);
 
-        fdt = create_device_tree(&fdt_size);
-        fdt_offset = spapr_populate_memory_node(fdt, node, addr,
-                                                SPAPR_MEMORY_BLOCK_SIZE);
-
-        spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err);
+        spapr_drc_attach(drc, dev, &local_err);
         if (local_err) {
             while (addr > addr_start) {
                 addr -= SPAPR_MEMORY_BLOCK_SIZE;
@@ -3242,7 +3394,6 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
                                       addr / SPAPR_MEMORY_BLOCK_SIZE);
                 spapr_drc_detach(drc);
             }
-            g_free(fdt);
             error_propagate(errp, local_err);
             return;
         }
@@ -3275,7 +3426,6 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
     sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev);
     PCDIMMDevice *dimm = PC_DIMM(dev);
     uint64_t size, addr;
-    uint32_t node;
 
     size = memory_device_get_region_size(MEMORY_DEVICE(dev), &error_abort);
 
@@ -3290,10 +3440,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
         goto out_unplug;
     }
 
-    node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP,
-                                    &error_abort);
-    spapr_add_lmbs(dev, addr, size, node,
-                   spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT),
+    spapr_add_lmbs(dev, addr, size, spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT),
                    &local_err);
     if (local_err) {
         goto out_unplug;
@@ -3513,27 +3660,6 @@ out:
     error_propagate(errp, local_err);
 }
 
-static void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset,
-                                           sPAPRMachineState *spapr)
-{
-    PowerPCCPU *cpu = POWERPC_CPU(cs);
-    DeviceClass *dc = DEVICE_GET_CLASS(cs);
-    int id = spapr_get_vcpu_id(cpu);
-    void *fdt;
-    int offset, fdt_size;
-    char *nodename;
-
-    fdt = create_device_tree(&fdt_size);
-    nodename = g_strdup_printf("%s@%x", dc->fw_name, id);
-    offset = fdt_add_subnode(fdt, 0, nodename);
-
-    spapr_populate_cpu_dt(cs, fdt, offset, spapr);
-    g_free(nodename);
-
-    *fdt_offset = offset;
-    return fdt;
-}
-
 /* Callback to be called during DRC release. */
 void spapr_core_release(DeviceState *dev)
 {
@@ -3594,6 +3720,27 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev,
     spapr_hotplug_req_remove_by_index(drc);
 }
 
+int spapr_core_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
+                           void *fdt, int *fdt_start_offset, Error **errp)
+{
+    sPAPRCPUCore *core = SPAPR_CPU_CORE(drc->dev);
+    CPUState *cs = CPU(core->threads[0]);
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    DeviceClass *dc = DEVICE_GET_CLASS(cs);
+    int id = spapr_get_vcpu_id(cpu);
+    char *nodename;
+    int offset;
+
+    nodename = g_strdup_printf("%s@%x", dc->fw_name, id);
+    offset = fdt_add_subnode(fdt, 0, nodename);
+    g_free(nodename);
+
+    spapr_populate_cpu_dt(cs, fdt, offset, spapr);
+
+    *fdt_start_offset = offset;
+    return 0;
+}
+
 static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
                             Error **errp)
 {
@@ -3602,7 +3749,7 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
     sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
     sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev));
     CPUCore *cc = CPU_CORE(dev);
-    CPUState *cs = CPU(core->threads[0]);
+    CPUState *cs;
     sPAPRDRConnector *drc;
     Error *local_err = NULL;
     CPUArchId *core_slot;
@@ -3621,14 +3768,8 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
     g_assert(drc || !mc->has_hotpluggable_cpus);
 
     if (drc) {
-        void *fdt;
-        int fdt_offset;
-
-        fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr);
-
-        spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err);
+        spapr_drc_attach(drc, dev, &local_err);
         if (local_err) {
-            g_free(fdt);
             error_propagate(errp, local_err);
             return;
         }
@@ -3712,6 +3853,115 @@ out:
     error_propagate(errp, local_err);
 }
 
+int spapr_phb_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
+                          void *fdt, int *fdt_start_offset, Error **errp)
+{
+    sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(drc->dev);
+    int intc_phandle;
+
+    intc_phandle = spapr_irq_get_phandle(spapr, spapr->fdt_blob, errp);
+    if (intc_phandle <= 0) {
+        return -1;
+    }
+
+    if (spapr_populate_pci_dt(sphb, intc_phandle, fdt, spapr->irq->nr_msis,
+                              fdt_start_offset)) {
+        error_setg(errp, "unable to create FDT node for PHB %d", sphb->index);
+        return -1;
+    }
+
+    /* generally SLOF creates these, for hotplug it's up to QEMU */
+    _FDT(fdt_setprop_string(fdt, *fdt_start_offset, "name", "pci"));
+
+    return 0;
+}
+
+static void spapr_phb_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+                               Error **errp)
+{
+    sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev));
+    sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(dev);
+    sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
+    const unsigned windows_supported = spapr_phb_windows_supported(sphb);
+
+    if (dev->hotplugged && !smc->dr_phb_enabled) {
+        error_setg(errp, "PHB hotplug not supported for this machine");
+        return;
+    }
+
+    if (sphb->index == (uint32_t)-1) {
+        error_setg(errp, "\"index\" for PAPR PHB is mandatory");
+        return;
+    }
+
+    /*
+     * This will check that sphb->index doesn't exceed the maximum number of
+     * PHBs for the current machine type.
+     */
+    smc->phb_placement(spapr, sphb->index,
+                       &sphb->buid, &sphb->io_win_addr,
+                       &sphb->mem_win_addr, &sphb->mem64_win_addr,
+                       windows_supported, sphb->dma_liobn, errp);
+}
+
+static void spapr_phb_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+                           Error **errp)
+{
+    sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev));
+    sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
+    sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(dev);
+    sPAPRDRConnector *drc;
+    bool hotplugged = spapr_drc_hotplugged(dev);
+    Error *local_err = NULL;
+
+    if (!smc->dr_phb_enabled) {
+        return;
+    }
+
+    drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, sphb->index);
+    /* hotplug hooks should check it's enabled before getting this far */
+    assert(drc);
+
+    spapr_drc_attach(drc, DEVICE(dev), &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    if (hotplugged) {
+        spapr_hotplug_req_add_by_index(drc);
+    } else {
+        spapr_drc_reset(drc);
+    }
+}
+
+void spapr_phb_release(DeviceState *dev)
+{
+    HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev);
+
+    hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort);
+}
+
+static void spapr_phb_unplug(HotplugHandler *hotplug_dev, DeviceState *dev)
+{
+    object_unparent(OBJECT(dev));
+}
+
+static void spapr_phb_unplug_request(HotplugHandler *hotplug_dev,
+                                     DeviceState *dev, Error **errp)
+{
+    sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(dev);
+    sPAPRDRConnector *drc;
+
+    drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, sphb->index);
+    assert(drc);
+
+    if (!spapr_drc_unplug_requested(drc)) {
+        spapr_drc_detach(drc);
+        spapr_hotplug_req_remove_by_index(drc);
+    }
+}
+
 static void spapr_machine_device_plug(HotplugHandler *hotplug_dev,
                                       DeviceState *dev, Error **errp)
 {
@@ -3719,6 +3969,8 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev,
         spapr_memory_plug(hotplug_dev, dev, errp);
     } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
         spapr_core_plug(hotplug_dev, dev, errp);
+    } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
+        spapr_phb_plug(hotplug_dev, dev, errp);
     }
 }
 
@@ -3729,6 +3981,8 @@ static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev,
         spapr_memory_unplug(hotplug_dev, dev);
     } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
         spapr_core_unplug(hotplug_dev, dev);
+    } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
+        spapr_phb_unplug(hotplug_dev, dev);
     }
 }
 
@@ -3737,6 +3991,7 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev,
 {
     sPAPRMachineState *sms = SPAPR_MACHINE(OBJECT(hotplug_dev));
     MachineClass *mc = MACHINE_GET_CLASS(sms);
+    sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
 
     if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
         if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) {
@@ -3756,6 +4011,12 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev,
             return;
         }
         spapr_core_unplug_request(hotplug_dev, dev, errp);
+    } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
+        if (!smc->dr_phb_enabled) {
+            error_setg(errp, "PHB hot unplug not supported on this machine");
+            return;
+        }
+        spapr_phb_unplug_request(hotplug_dev, dev, errp);
     }
 }
 
@@ -3766,6 +4027,8 @@ static void spapr_machine_device_pre_plug(HotplugHandler *hotplug_dev,
         spapr_memory_pre_plug(hotplug_dev, dev, errp);
     } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
         spapr_core_pre_plug(hotplug_dev, dev, errp);
+    } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
+        spapr_phb_pre_plug(hotplug_dev, dev, errp);
     }
 }
 
@@ -3773,7 +4036,8 @@ static HotplugHandler *spapr_get_hotplug_handler(MachineState *machine,
                                                  DeviceState *dev)
 {
     if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
-        object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
+        object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE) ||
+        object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
         return HOTPLUG_HANDLER(machine);
     }
     return NULL;
@@ -4004,7 +4268,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
     vhc->map_hptes = spapr_map_hptes;
     vhc->unmap_hptes = spapr_unmap_hptes;
     vhc->store_hpte = spapr_store_hpte;
-    vhc->get_patbe = spapr_get_patbe;
+    vhc->get_pate = spapr_get_pate;
     vhc->encode_hpt_for_kvm_pr = spapr_encode_hpt_for_kvm_pr;
     xic->ics_get = spapr_ics_get;
     xic->ics_resend = spapr_ics_resend;
@@ -4026,6 +4290,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
     smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF;
     spapr_caps_add_properties(smc, &error_abort);
     smc->irq = &spapr_irq_xics;
+    smc->dr_phb_enabled = true;
 }
 
 static const TypeInfo spapr_machine_info = {
@@ -4086,11 +4351,18 @@ DEFINE_SPAPR_MACHINE(4_0, "4.0", true);
 static void spapr_machine_3_1_class_options(MachineClass *mc)
 {
     sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
+    static GlobalProperty compat[] = {
+        { TYPE_SPAPR_MACHINE, "host-model", "passthrough" },
+        { TYPE_SPAPR_MACHINE, "host-serial", "passthrough" },
+    };
 
     spapr_machine_4_0_class_options(mc);
     compat_props_add(mc->compat_props, hw_compat_3_1, hw_compat_3_1_len);
+    compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat));
+
     mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0");
     smc->update_dt_enabled = false;
+    smc->dr_phb_enabled = false;
 }
 
 DEFINE_SPAPR_MACHINE(3_1, "3.1", false);
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 2edb7d1e9c..2943cf47d4 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -22,6 +22,7 @@
 #include "qemu/error-report.h"
 #include "hw/ppc/spapr.h" /* for RTAS return codes */
 #include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */
+#include "sysemu/device_tree.h"
 #include "trace.h"
 
 #define DRC_CONTAINER_PATH "/dr-connector"
@@ -373,8 +374,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
     } while (fdt_depth != 0);
 }
 
-void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
-                      int fdt_start_offset, Error **errp)
+void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, Error **errp)
 {
     trace_spapr_drc_attach(spapr_drc_index(drc));
 
@@ -384,11 +384,8 @@ void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
     }
     g_assert((drc->state == SPAPR_DRC_STATE_LOGICAL_UNUSABLE)
              || (drc->state == SPAPR_DRC_STATE_PHYSICAL_POWERON));
-    g_assert(fdt);
 
     drc->dev = d;
-    drc->fdt = fdt;
-    drc->fdt_start_offset = fdt_start_offset;
 
     object_property_add_link(OBJECT(drc), "device",
                              object_get_typename(OBJECT(drc->dev)),
@@ -674,6 +671,7 @@ static void spapr_drc_cpu_class_init(ObjectClass *k, void *data)
     drck->typename = "CPU";
     drck->drc_name_prefix = "CPU ";
     drck->release = spapr_core_release;
+    drck->dt_populate = spapr_core_dt_populate;
 }
 
 static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
@@ -684,6 +682,7 @@ static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
     drck->typename = "28";
     drck->drc_name_prefix = "C";
     drck->release = spapr_phb_remove_pci_device_cb;
+    drck->dt_populate = spapr_pci_dt_populate;
 }
 
 static void spapr_drc_lmb_class_init(ObjectClass *k, void *data)
@@ -694,6 +693,18 @@ static void spapr_drc_lmb_class_init(ObjectClass *k, void *data)
     drck->typename = "MEM";
     drck->drc_name_prefix = "LMB ";
     drck->release = spapr_lmb_release;
+    drck->dt_populate = spapr_lmb_dt_populate;
+}
+
+static void spapr_drc_phb_class_init(ObjectClass *k, void *data)
+{
+    sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
+
+    drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB;
+    drck->typename = "PHB";
+    drck->drc_name_prefix = "PHB ";
+    drck->release = spapr_phb_release;
+    drck->dt_populate = spapr_phb_dt_populate;
 }
 
 static const TypeInfo spapr_dr_connector_info = {
@@ -739,6 +750,13 @@ static const TypeInfo spapr_drc_lmb_info = {
     .class_init    = spapr_drc_lmb_class_init,
 };
 
+static const TypeInfo spapr_drc_phb_info = {
+    .name          = TYPE_SPAPR_DRC_PHB,
+    .parent        = TYPE_SPAPR_DRC_LOGICAL,
+    .instance_size = sizeof(sPAPRDRConnector),
+    .class_init    = spapr_drc_phb_class_init,
+};
+
 /* helper functions for external users */
 
 sPAPRDRConnector *spapr_drc_by_index(uint32_t index)
@@ -1102,10 +1120,28 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
         goto out;
     }
 
-    g_assert(drc->fdt);
-
     drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
 
+    if (!drc->fdt) {
+        Error *local_err = NULL;
+        void *fdt;
+        int fdt_size;
+
+        fdt = create_device_tree(&fdt_size);
+
+        if (drck->dt_populate(drc, spapr, fdt, &drc->fdt_start_offset,
+                              &local_err)) {
+            g_free(fdt);
+            error_free(local_err);
+            rc = SPAPR_DR_CC_RESPONSE_ERROR;
+            goto out;
+        }
+
+        drc->fdt = fdt;
+        drc->ccs_offset = drc->fdt_start_offset;
+        drc->ccs_depth = 0;
+    }
+
     do {
         uint32_t tag;
         const char *name;
@@ -1189,6 +1225,7 @@ static void spapr_drc_register_types(void)
     type_register_static(&spapr_drc_cpu_info);
     type_register_static(&spapr_drc_pci_info);
     type_register_static(&spapr_drc_lmb_info);
+    type_register_static(&spapr_drc_phb_info);
 
     spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator",
                         rtas_set_indicator);
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index b9c7ecb9e9..ab9a1f0063 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -526,6 +526,9 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
     case SPAPR_DR_CONNECTOR_TYPE_CPU:
         hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_CPU;
         break;
+    case SPAPR_DR_CONNECTOR_TYPE_PHB:
+        hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PHB;
+        break;
     default:
         /* we shouldn't be signaling hotplug events for resources
          * that don't support them
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 17bcaa3822..476bad6271 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -17,37 +17,6 @@
 #include "mmu-book3s-v3.h"
 #include "hw/mem/memory-device.h"
 
-struct LPCRSyncState {
-    target_ulong value;
-    target_ulong mask;
-};
-
-static void do_lpcr_sync(CPUState *cs, run_on_cpu_data arg)
-{
-    struct LPCRSyncState *s = arg.host_ptr;
-    PowerPCCPU *cpu = POWERPC_CPU(cs);
-    CPUPPCState *env = &cpu->env;
-    target_ulong lpcr;
-
-    cpu_synchronize_state(cs);
-    lpcr = env->spr[SPR_LPCR];
-    lpcr &= ~s->mask;
-    lpcr |= s->value;
-    ppc_store_lpcr(cpu, lpcr);
-}
-
-static void set_all_lpcrs(target_ulong value, target_ulong mask)
-{
-    CPUState *cs;
-    struct LPCRSyncState s = {
-        .value = value,
-        .mask = mask
-    };
-    CPU_FOREACH(cs) {
-        run_on_cpu(cs, do_lpcr_sync, RUN_ON_CPU_HOST_PTR(&s));
-    }
-}
-
 static bool has_spr(PowerPCCPU *cpu, int spr)
 {
     /* We can test whether the SPR is defined by checking for a valid name */
@@ -1255,12 +1224,12 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu,
 
     switch (mflags) {
     case H_SET_MODE_ENDIAN_BIG:
-        set_all_lpcrs(0, LPCR_ILE);
+        spapr_set_all_lpcrs(0, LPCR_ILE);
         spapr_pci_switch_vga(true);
         return H_SUCCESS;
 
     case H_SET_MODE_ENDIAN_LITTLE:
-        set_all_lpcrs(LPCR_ILE, LPCR_ILE);
+        spapr_set_all_lpcrs(LPCR_ILE, LPCR_ILE);
         spapr_pci_switch_vga(false);
         return H_SUCCESS;
     }
@@ -1289,7 +1258,7 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu,
         return H_UNSUPPORTED_FLAG;
     }
 
-    set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL);
+    spapr_set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL);
 
     return H_SUCCESS;
 }
@@ -1342,12 +1311,12 @@ static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr,
      *       later and so assumed radix and now it's called H_REG_PROC_TBL
      */
 
-    if ((patbe_old & PATBE1_GR) == (patbe_new & PATBE1_GR)) {
+    if ((patbe_old & PATE1_GR) == (patbe_new & PATE1_GR)) {
         /* We assume RADIX, so this catches all the "Do Nothing" cases */
-    } else if (!(patbe_old & PATBE1_GR)) {
+    } else if (!(patbe_old & PATE1_GR)) {
         /* HASH->RADIX : Free HPT */
         spapr_free_hpt(spapr);
-    } else if (!(patbe_new & PATBE1_GR)) {
+    } else if (!(patbe_new & PATE1_GR)) {
         /* RADIX->HASH || NOTHING->HASH : Allocate HPT */
         spapr_setup_hpt_and_vrma(spapr);
     }
@@ -1385,7 +1354,7 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu,
                 } else if (table_size > 24) {
                     return H_P4;
                 }
-                cproc = PATBE1_GR | proc_tbl | table_size;
+                cproc = PATE1_GR | proc_tbl | table_size;
             } else { /* Register new HPT process table */
                 if (flags & FLAG_HASH_PROC_TBL) { /* Hash with Segment Tables */
                     /* TODO - Not Supported */
@@ -1404,13 +1373,15 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu,
             }
 
         } else { /* Deregister current process table */
-            /* Set to benign value: (current GR) | 0. This allows
-             * deregistration in KVM to succeed even if the radix bit in flags
-             * doesn't match the radix bit in the old PATB. */
-            cproc = spapr->patb_entry & PATBE1_GR;
+            /*
+             * Set to benign value: (current GR) | 0. This allows
+             * deregistration in KVM to succeed even if the radix bit
+             * in flags doesn't match the radix bit in the old PATE.
+             */
+            cproc = spapr->patb_entry & PATE1_GR;
         }
     } else { /* Maintain current registration */
-        if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATBE1_GR)) {
+        if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATE1_GR)) {
             /* Technically caused by flag bits => H_PARAMETER */
             return H_PARAMETER; /* Existing Process Table Mismatch */
         }
@@ -1422,10 +1393,11 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu,
 
     spapr->patb_entry = cproc; /* Save new process table */
 
-    /* Update the UPRT and GTSE bits in the LPCR for all cpus */
-    set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) |
-                  ((flags & FLAG_GTSE) ? LPCR_GTSE : 0),
-                  LPCR_UPRT | LPCR_GTSE);
+    /* Update the UPRT, HR and GTSE bits in the LPCR for all cpus */
+    spapr_set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ?
+                         (LPCR_UPRT | LPCR_HR) : 0) |
+                        ((flags & FLAG_GTSE) ? LPCR_GTSE : 0),
+                        LPCR_UPRT | LPCR_HR | LPCR_GTSE);
 
     if (kvm_enabled()) {
         return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX,
@@ -1646,7 +1618,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
     if (!spapr->cas_reboot) {
         /* If spapr_machine_reset() did not set up a HPT but one is necessary
          * (because the guest isn't going to use radix) then set it up here. */
-        if ((spapr->patb_entry & PATBE1_GR) && !guest_radix) {
+        if ((spapr->patb_entry & PATE1_GR) && !guest_radix) {
             /* legacy hash or new hash: */
             spapr_setup_hpt_and_vrma(spapr);
         }
diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c
index 4297eed600..4145079d7f 100644
--- a/hw/ppc/spapr_irq.c
+++ b/hw/ppc/spapr_irq.c
@@ -230,6 +230,11 @@ static void spapr_irq_reset_xics(sPAPRMachineState *spapr, Error **errp)
     /* TODO: create the KVM XICS device */
 }
 
+static const char *spapr_irq_get_nodename_xics(sPAPRMachineState *spapr)
+{
+    return XICS_NODENAME;
+}
+
 #define SPAPR_IRQ_XICS_NR_IRQS     0x1000
 #define SPAPR_IRQ_XICS_NR_MSIS     \
     (XICS_IRQ_BASE + SPAPR_IRQ_XICS_NR_IRQS - SPAPR_IRQ_MSI)
@@ -249,6 +254,7 @@ sPAPRIrq spapr_irq_xics = {
     .post_load   = spapr_irq_post_load_xics,
     .reset       = spapr_irq_reset_xics,
     .set_irq     = spapr_irq_set_irq_xics,
+    .get_nodename = spapr_irq_get_nodename_xics,
 };
 
 /*
@@ -384,6 +390,11 @@ static void spapr_irq_set_irq_xive(void *opaque, int srcno, int val)
     xive_source_set_irq(&spapr->xive->source, srcno, val);
 }
 
+static const char *spapr_irq_get_nodename_xive(sPAPRMachineState *spapr)
+{
+    return spapr->xive->nodename;
+}
+
 /*
  * XIVE uses the full IRQ number space. Set it to 8K to be compatible
  * with XICS.
@@ -407,6 +418,7 @@ sPAPRIrq spapr_irq_xive = {
     .post_load   = spapr_irq_post_load_xive,
     .reset       = spapr_irq_reset_xive,
     .set_irq     = spapr_irq_set_irq_xive,
+    .get_nodename = spapr_irq_get_nodename_xive,
 };
 
 /*
@@ -541,6 +553,11 @@ static void spapr_irq_set_irq_dual(void *opaque, int srcno, int val)
     spapr_irq_current(spapr)->set_irq(spapr, srcno, val);
 }
 
+static const char *spapr_irq_get_nodename_dual(sPAPRMachineState *spapr)
+{
+    return spapr_irq_current(spapr)->get_nodename(spapr);
+}
+
 /*
  * Define values in sync with the XIVE and XICS backend
  */
@@ -561,7 +578,8 @@ sPAPRIrq spapr_irq_dual = {
     .cpu_intc_create = spapr_irq_cpu_intc_create_dual,
     .post_load   = spapr_irq_post_load_dual,
     .reset       = spapr_irq_reset_dual,
-    .set_irq     = spapr_irq_set_irq_dual
+    .set_irq     = spapr_irq_set_irq_dual,
+    .get_nodename = spapr_irq_get_nodename_dual,
 };
 
 /*
@@ -620,6 +638,27 @@ void spapr_irq_reset(sPAPRMachineState *spapr, Error **errp)
     }
 }
 
+int spapr_irq_get_phandle(sPAPRMachineState *spapr, void *fdt, Error **errp)
+{
+    const char *nodename = spapr->irq->get_nodename(spapr);
+    int offset, phandle;
+
+    offset = fdt_subnode_offset(fdt, 0, nodename);
+    if (offset < 0) {
+        error_setg(errp, "Can't find node \"%s\": %s", nodename,
+                   fdt_strerror(offset));
+        return -1;
+    }
+
+    phandle = fdt_get_phandle(fdt, offset);
+    if (!phandle) {
+        error_setg(errp, "Can't get phandle of node \"%s\"", nodename);
+        return -1;
+    }
+
+    return phandle;
+}
+
 /*
  * XICS legacy routines - to deprecate one day
  */
@@ -691,4 +730,5 @@ sPAPRIrq spapr_irq_xics_legacy = {
     .cpu_intc_create = spapr_irq_cpu_intc_create_xics,
     .post_load   = spapr_irq_post_load_xics,
     .set_irq     = spapr_irq_set_irq_xics,
+    .get_nodename = spapr_irq_get_nodename_xics,
 };
diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c
index 318bf33de4..12510b236a 100644
--- a/hw/ppc/spapr_ovec.c
+++ b/hw/ppc/spapr_ovec.c
@@ -16,6 +16,7 @@
 #include "qemu/bitmap.h"
 #include "exec/address-spaces.h"
 #include "qemu/error-report.h"
+#include "sysemu/qtest.h"
 #include "trace.h"
 #include <libfdt.h>
 
@@ -131,6 +132,11 @@ bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr)
     g_assert(ov);
     g_assert(bitnr < OV_MAXBITS);
 
+    /* support memory unplug for qtest */
+    if (qtest_enabled() && bitnr == OV5_HP_EVT) {
+        return true;
+    }
+
     return test_bit(bitnr, ov->bitmap) ? true : false;
 }
 
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 60777b2355..06a5ffd281 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -1408,6 +1408,17 @@ static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb,
     return spapr_drc_index(drc);
 }
 
+int spapr_pci_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
+                          void *fdt, int *fdt_start_offset, Error **errp)
+{
+    HotplugHandler *plug_handler = qdev_get_hotplug_handler(drc->dev);
+    sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(plug_handler);
+    PCIDevice *pdev = PCI_DEVICE(drc->dev);
+
+    *fdt_start_offset = spapr_create_pci_child_dt(sphb, pdev, fdt, 0);
+    return 0;
+}
+
 static void spapr_pci_plug(HotplugHandler *plug_handler,
                            DeviceState *plugged_dev, Error **errp)
 {
@@ -1417,8 +1428,6 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
     Error *local_err = NULL;
     PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)));
     uint32_t slotnr = PCI_SLOT(pdev->devfn);
-    void *fdt = NULL;
-    int fdt_start_offset, fdt_size;
 
     /* if DR is disabled we don't need to do anything in the case of
      * hotplug or coldplug callbacks
@@ -1448,10 +1457,7 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
         goto out;
     }
 
-    fdt = create_device_tree(&fdt_size);
-    fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
-
-    spapr_drc_attach(drc, DEVICE(pdev), fdt, fdt_start_offset, &local_err);
+    spapr_drc_attach(drc, DEVICE(pdev), &local_err);
     if (local_err) {
         goto out;
     }
@@ -1483,7 +1489,6 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
 out:
     if (local_err) {
         error_propagate(errp, local_err);
-        g_free(fdt);
     }
 }
 
@@ -1565,6 +1570,75 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler,
     }
 }
 
+static void spapr_phb_finalizefn(Object *obj)
+{
+    sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(obj);
+
+    g_free(sphb->dtbusname);
+    sphb->dtbusname = NULL;
+}
+
+static void spapr_phb_unrealize(DeviceState *dev, Error **errp)
+{
+    sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+    SysBusDevice *s = SYS_BUS_DEVICE(dev);
+    PCIHostState *phb = PCI_HOST_BRIDGE(s);
+    sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(phb);
+    sPAPRTCETable *tcet;
+    int i;
+    const unsigned windows_supported = spapr_phb_windows_supported(sphb);
+
+    if (sphb->msi) {
+        g_hash_table_unref(sphb->msi);
+        sphb->msi = NULL;
+    }
+
+    /*
+     * Remove IO/MMIO subregions and aliases, rest should get cleaned
+     * via PHB's unrealize->object_finalize
+     */
+    for (i = windows_supported - 1; i >= 0; i--) {
+        tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[i]);
+        if (tcet) {
+            memory_region_del_subregion(&sphb->iommu_root,
+                                        spapr_tce_get_iommu(tcet));
+        }
+    }
+
+    if (sphb->dr_enabled) {
+        for (i = PCI_SLOT_MAX * 8 - 1; i >= 0; i--) {
+            sPAPRDRConnector *drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PCI,
+                                                    (sphb->index << 16) | i);
+
+            if (drc) {
+                object_unparent(OBJECT(drc));
+            }
+        }
+    }
+
+    for (i = PCI_NUM_PINS - 1; i >= 0; i--) {
+        if (sphb->lsi_table[i].irq) {
+            spapr_irq_free(spapr, sphb->lsi_table[i].irq, 1);
+            sphb->lsi_table[i].irq = 0;
+        }
+    }
+
+    QLIST_REMOVE(sphb, list);
+
+    memory_region_del_subregion(&sphb->iommu_root, &sphb->msiwindow);
+
+    address_space_destroy(&sphb->iommu_as);
+
+    qbus_set_hotplug_handler(BUS(phb->bus), NULL, &error_abort);
+    pci_unregister_root_bus(phb->bus);
+
+    memory_region_del_subregion(get_system_memory(), &sphb->iowindow);
+    if (sphb->mem64_win_pciaddr != (hwaddr)-1) {
+        memory_region_del_subregion(get_system_memory(), &sphb->mem64window);
+    }
+    memory_region_del_subregion(get_system_memory(), &sphb->mem32window);
+}
+
 static void spapr_phb_realize(DeviceState *dev, Error **errp)
 {
     /* We don't use SPAPR_MACHINE() in order to exit gracefully if the user
@@ -1582,29 +1656,14 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
     PCIBus *bus;
     uint64_t msi_window_size = 4096;
     sPAPRTCETable *tcet;
-    const unsigned windows_supported =
-        sphb->ddw_enabled ? SPAPR_PCI_DMA_MAX_WINDOWS : 1;
+    const unsigned windows_supported = spapr_phb_windows_supported(sphb);
 
     if (!spapr) {
         error_setg(errp, TYPE_SPAPR_PCI_HOST_BRIDGE " needs a pseries machine");
         return;
     }
 
-    if (sphb->index != (uint32_t)-1) {
-        Error *local_err = NULL;
-
-        smc->phb_placement(spapr, sphb->index,
-                           &sphb->buid, &sphb->io_win_addr,
-                           &sphb->mem_win_addr, &sphb->mem64_win_addr,
-                           windows_supported, sphb->dma_liobn, &local_err);
-        if (local_err) {
-            error_propagate(errp, local_err);
-            return;
-        }
-    } else {
-        error_setg(errp, "\"index\" for PAPR PHB is mandatory");
-        return;
-    }
+    assert(sphb->index != (uint32_t)-1); /* checked in spapr_phb_pre_plug() */
 
     if (sphb->mem64_win_size != 0) {
         if (sphb->mem_win_size > SPAPR_PCI_MEM32_WIN_SIZE) {
@@ -1740,6 +1799,10 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
             if (local_err) {
                 error_propagate_prepend(errp, local_err,
                                         "can't allocate LSIs: ");
+                /*
+                 * Older machines will never support PHB hotplug, ie, this is an
+                 * init only path and QEMU will terminate. No need to rollback.
+                 */
                 return;
             }
         }
@@ -1747,7 +1810,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
         spapr_irq_claim(spapr, irq, true, &local_err);
         if (local_err) {
             error_propagate_prepend(errp, local_err, "can't allocate LSIs: ");
-            return;
+            goto unrealize;
         }
 
         sphb->lsi_table[i].irq = irq;
@@ -1767,13 +1830,17 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
         if (!tcet) {
             error_setg(errp, "Creating window#%d failed for %s",
                        i, sphb->dtbusname);
-            return;
+            goto unrealize;
         }
         memory_region_add_subregion(&sphb->iommu_root, 0,
                                     spapr_tce_get_iommu(tcet));
     }
 
     sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
+    return;
+
+unrealize:
+    spapr_phb_unrealize(dev, NULL);
 }
 
 static int spapr_phb_children_reset(Object *child, void *opaque)
@@ -1972,6 +2039,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
 
     hc->root_bus_path = spapr_phb_root_bus_path;
     dc->realize = spapr_phb_realize;
+    dc->unrealize = spapr_phb_unrealize;
     dc->props = spapr_phb_properties;
     dc->reset = spapr_phb_reset;
     dc->vmsd = &vmstate_spapr_pci;
@@ -1987,6 +2055,7 @@ static const TypeInfo spapr_phb_info = {
     .name          = TYPE_SPAPR_PCI_HOST_BRIDGE,
     .parent        = TYPE_PCI_HOST_BRIDGE,
     .instance_size = sizeof(sPAPRPHBState),
+    .instance_finalize = spapr_phb_finalizefn,
     .class_init    = spapr_phb_class_init,
     .interfaces    = (InterfaceInfo[]) {
         { TYPE_HOTPLUG_HANDLER },
@@ -2070,7 +2139,7 @@ static void spapr_phb_pci_enumerate(sPAPRPHBState *phb)
 }
 
 int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt,
-                          uint32_t nr_msis)
+                          uint32_t nr_msis, int *node_offset)
 {
     int bus_off, i, j, ret;
     gchar *nodename;
@@ -2120,11 +2189,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt,
     sPAPRTCETable *tcet;
     PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus;
     sPAPRFDT s_fdt;
+    sPAPRDRConnector *drc;
 
     /* Start populating the FDT */
     nodename = g_strdup_printf("pci@%" PRIx64, phb->buid);
     _FDT(bus_off = fdt_add_subnode(fdt, 0, nodename));
     g_free(nodename);
+    if (node_offset) {
+        *node_offset = bus_off;
+    }
 
     /* Write PHB properties */
     _FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci"));
@@ -2183,6 +2256,14 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt,
                  tcet->liobn, tcet->bus_offset,
                  tcet->nb_table << tcet->page_shift);
 
+    drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, phb->index);
+    if (drc) {
+        uint32_t drc_index = cpu_to_be32(spapr_drc_index(drc));
+
+        _FDT(fdt_setprop(fdt, bus_off, "ibm,my-drc-index", &drc_index,
+                         sizeof(drc_index)));
+    }
+
     /* Walk the bridges and program the bus numbers*/
     spapr_phb_pci_enumerate(phb);
     _FDT(fdt_setprop_cell(fdt, bus_off, "qemu,phb-enumerated", 0x1));
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index d6a0952154..7a2cb786a3 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -172,10 +172,10 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr,
          * New cpus are expected to start in the same radix/hash mode
          * as the existing CPUs
          */
-        if (ppc64_radix_guest(callcpu)) {
-            lpcr |= LPCR_UPRT | LPCR_GTSE;
+        if (ppc64_v3_radix(callcpu)) {
+            lpcr |= LPCR_UPRT | LPCR_GTSE | LPCR_HR;
         } else {
-            lpcr &= ~(LPCR_UPRT | LPCR_GTSE);
+            lpcr &= ~(LPCR_UPRT | LPCR_GTSE | LPCR_HR);
         }
     }
     ppc_store_lpcr(newcpu, lpcr);
diff --git a/hw/s390x/ap-bridge.c b/hw/s390x/ap-bridge.c
index 3795d30dd7..75ad0a66b9 100644
--- a/hw/s390x/ap-bridge.c
+++ b/hw/s390x/ap-bridge.c
@@ -39,6 +39,7 @@ static const TypeInfo ap_bus_info = {
 void s390_init_ap(void)
 {
     DeviceState *dev;
+    BusState *bus;
 
     /* If no AP instructions then no need for AP bridge */
     if (!s390_has_feat(S390_FEAT_AP)) {
@@ -52,13 +53,18 @@ void s390_init_ap(void)
     qdev_init_nofail(dev);
 
     /* Create bus on bridge device */
-    qbus_create(TYPE_AP_BUS, dev, TYPE_AP_BUS);
+    bus = qbus_create(TYPE_AP_BUS, dev, TYPE_AP_BUS);
+
+    /* Enable hotplugging */
+    qbus_set_hotplug_handler(bus, OBJECT(dev), &error_abort);
  }
 
 static void ap_bridge_class_init(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
+    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
 
+    hc->unplug = qdev_simple_device_unplug_cb;
     set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
 }
 
@@ -67,6 +73,10 @@ static const TypeInfo ap_bridge_info = {
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = 0,
     .class_init    = ap_bridge_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_HOTPLUG_HANDLER },
+        { }
+    }
 };
 
 static void ap_register(void)
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 818be8a838..47be9071fa 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -563,6 +563,7 @@ static void smbios_build_type_3_table(void)
     t->height = 0;
     t->number_of_power_cords = 0;
     t->contained_element_count = 0;
+    t->contained_element_record_length = 0;
     SMBIOS_TABLE_SET_STR(3, sku_number_str, type3.sku);
 
     SMBIOS_BUILD_TABLE_POST;
diff --git a/hw/timer/ds1338.c b/hw/timer/ds1338.c
index 3849b74a68..03da75486b 100644
--- a/hw/timer/ds1338.c
+++ b/hw/timer/ds1338.c
@@ -117,7 +117,7 @@ static int ds1338_event(I2CSlave *i2c, enum i2c_event event)
     return 0;
 }
 
-static int ds1338_recv(I2CSlave *i2c)
+static uint8_t ds1338_recv(I2CSlave *i2c)
 {
     DS1338State *s = DS1338(i2c);
     uint8_t res;
diff --git a/hw/timer/m41t80.c b/hw/timer/m41t80.c
index 734d7d95fc..c45b9297d8 100644
--- a/hw/timer/m41t80.c
+++ b/hw/timer/m41t80.c
@@ -40,7 +40,7 @@ static int m41t80_send(I2CSlave *i2c, uint8_t data)
     return 0;
 }
 
-static int m41t80_recv(I2CSlave *i2c)
+static uint8_t m41t80_recv(I2CSlave *i2c)
 {
     M41t80State *s = M41T80(i2c);
     struct tm now;
diff --git a/hw/timer/twl92230.c b/hw/timer/twl92230.c
index 51ec355f3f..c83d803dd8 100644
--- a/hw/timer/twl92230.c
+++ b/hw/timer/twl92230.c
@@ -737,7 +737,7 @@ static int menelaus_tx(I2CSlave *i2c, uint8_t data)
     return 0;
 }
 
-static int menelaus_rx(I2CSlave *i2c)
+static uint8_t menelaus_rx(I2CSlave *i2c)
 {
     MenelausState *s = TWL92230(i2c);
 
diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index f1d20fa1b9..4ee4fc5a89 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -11,17 +11,16 @@
 
 #include "qemu/osdep.h"
 #include "qapi/error.h"
+#include "qemu/error-report.h"
 #include <wchar.h>
 #include <dirent.h>
 
 #include <sys/statvfs.h>
-#ifdef CONFIG_INOTIFY1
-#include <sys/inotify.h>
-#include "qemu/main-loop.h"
-#endif
+
 
 #include "qemu-common.h"
 #include "qemu/iov.h"
+#include "qemu/filemonitor.h"
 #include "trace.h"
 #include "hw/usb.h"
 #include "desc.h"
@@ -132,7 +131,6 @@ enum {
     EP_EVENT,
 };
 
-#ifdef CONFIG_INOTIFY1
 typedef struct MTPMonEntry MTPMonEntry;
 
 struct MTPMonEntry {
@@ -141,7 +139,6 @@ struct MTPMonEntry {
 
     QTAILQ_ENTRY(MTPMonEntry) next;
 };
-#endif
 
 struct MTPControl {
     uint16_t     code;
@@ -172,10 +169,8 @@ struct MTPObject {
     char         *name;
     char         *path;
     struct stat  stat;
-#ifdef CONFIG_INOTIFY1
-    /* inotify watch cookie */
-    int          watchfd;
-#endif
+    /* file monitor watch id */
+    int          watchid;
     MTPObject    *parent;
     uint32_t     nchildren;
     QLIST_HEAD(, MTPObject) children;
@@ -198,11 +193,8 @@ struct MTPState {
     bool         readonly;
 
     QTAILQ_HEAD(, MTPObject) objects;
-#ifdef CONFIG_INOTIFY1
-    /* inotify descriptor */
-    int          inotifyfd;
+    QFileMonitor *file_monitor;
     QTAILQ_HEAD(, MTPMonEntry) events;
-#endif
     /* Responder is expecting a write operation */
     bool write_pending;
     struct {
@@ -383,7 +375,7 @@ static const USBDesc desc = {
 /* ----------------------------------------------------------------------- */
 
 static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle,
-                                       MTPObject *parent, char *name)
+                                       MTPObject *parent, const char *name)
 {
     MTPObject *o = g_new0(MTPObject, 1);
 
@@ -391,6 +383,7 @@ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle,
         goto ignore;
     }
 
+    o->watchid = -1;
     o->handle = handle;
     o->parent = parent;
     o->name = g_strdup(name);
@@ -437,6 +430,10 @@ static void usb_mtp_object_free(MTPState *s, MTPObject *o)
 
     trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path);
 
+    if (o->watchid != -1 && s->file_monitor) {
+        qemu_file_monitor_remove_watch(s->file_monitor, o->path, o->watchid);
+    }
+
     QTAILQ_REMOVE(&s->objects, o, next);
     if (o->parent) {
         QLIST_REMOVE(o, list);
@@ -465,7 +462,7 @@ static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle)
 }
 
 static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
-                                    char *name)
+                                    const char *name)
 {
     MTPObject *child =
         usb_mtp_object_alloc(s, s->next_handle++, o, name);
@@ -484,10 +481,14 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
 }
 
 static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
-                                             char *name, int len)
+                                             const char *name, int len)
 {
     MTPObject *iter;
 
+    if (len == -1) {
+        len = strlen(name);
+    }
+
     QLIST_FOREACH(iter, &parent->children, list) {
         if (strncmp(iter->name, name, len) == 0) {
             return iter;
@@ -497,13 +498,12 @@ static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
     return NULL;
 }
 
-#ifdef CONFIG_INOTIFY1
-static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
+static MTPObject *usb_mtp_object_lookup_id(MTPState *s, int id)
 {
     MTPObject *iter;
 
     QTAILQ_FOREACH(iter, &s->objects, next) {
-        if (iter->watchfd == wd) {
+        if (iter->watchid == id) {
             return iter;
         }
     }
@@ -511,160 +511,103 @@ static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
     return NULL;
 }
 
-static void inotify_watchfn(void *arg)
+static void file_monitor_event(int id,
+                               QFileMonitorEvent ev,
+                               const char *name,
+                               void *opaque)
 {
-    MTPState *s = arg;
-    ssize_t bytes;
-    /* From the man page: atleast one event can be read */
-    int pos;
-    char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
-
-    for (;;) {
-        bytes = read(s->inotifyfd, buf, sizeof(buf));
-        pos = 0;
-
-        if (bytes <= 0) {
-            /* Better luck next time */
+    MTPState *s = opaque;
+    MTPObject *parent = usb_mtp_object_lookup_id(s, id);
+    MTPMonEntry *entry = NULL;
+    MTPObject *o;
+
+    if (!parent) {
+        return;
+    }
+
+    switch (ev) {
+    case QFILE_MONITOR_EVENT_CREATED:
+        if (usb_mtp_object_lookup_name(parent, name, -1)) {
+            /* Duplicate create event */
             return;
         }
+        entry = g_new0(MTPMonEntry, 1);
+        entry->handle = s->next_handle;
+        entry->event = EVT_OBJ_ADDED;
+        o = usb_mtp_add_child(s, parent, name);
+        if (!o) {
+            g_free(entry);
+            return;
+        }
+        trace_usb_mtp_file_monitor_event(s->dev.addr, name, "Obj Added");
+        break;
 
+    case QFILE_MONITOR_EVENT_DELETED:
         /*
-         * TODO: Ignore initiator initiated events.
-         * For now we are good because the store is RO
+         * The kernel issues a IN_IGNORED event
+         * when a dir containing a watchpoint is
+         * deleted, so we don't have to delete the
+         * watchpoint
          */
-        while (bytes > 0) {
-            char *p = buf + pos;
-            struct inotify_event *event = (struct inotify_event *)p;
-            int watchfd = 0;
-            uint32_t mask = event->mask & (IN_CREATE | IN_DELETE |
-                                           IN_MODIFY | IN_IGNORED);
-            MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd);
-            MTPMonEntry *entry = NULL;
-            MTPObject *o;
-
-            pos = pos + sizeof(struct inotify_event) + event->len;
-            bytes = bytes - pos;
-
-            if (!parent) {
-                continue;
-            }
-
-            switch (mask) {
-            case IN_CREATE:
-                if (usb_mtp_object_lookup_name
-                    (parent, event->name, event->len)) {
-                    /* Duplicate create event */
-                    continue;
-                }
-                entry = g_new0(MTPMonEntry, 1);
-                entry->handle = s->next_handle;
-                entry->event = EVT_OBJ_ADDED;
-                o = usb_mtp_add_child(s, parent, event->name);
-                if (!o) {
-                    g_free(entry);
-                    continue;
-                }
-                o->watchfd = watchfd;
-                trace_usb_mtp_inotify_event(s->dev.addr, event->name,
-                                            event->mask, "Obj Added");
-                break;
-
-            case IN_DELETE:
-                /*
-                 * The kernel issues a IN_IGNORED event
-                 * when a dir containing a watchpoint is
-                 * deleted, so we don't have to delete the
-                 * watchpoint
-                 */
-                o = usb_mtp_object_lookup_name(parent, event->name, event->len);
-                if (!o) {
-                    continue;
-                }
-                entry = g_new0(MTPMonEntry, 1);
-                entry->handle = o->handle;
-                entry->event = EVT_OBJ_REMOVED;
-                trace_usb_mtp_inotify_event(s->dev.addr, o->path,
-                                      event->mask, "Obj Deleted");
-                usb_mtp_object_free(s, o);
-                break;
-
-            case IN_MODIFY:
-                o = usb_mtp_object_lookup_name(parent, event->name, event->len);
-                if (!o) {
-                    continue;
-                }
-                entry = g_new0(MTPMonEntry, 1);
-                entry->handle = o->handle;
-                entry->event = EVT_OBJ_INFO_CHANGED;
-                trace_usb_mtp_inotify_event(s->dev.addr, o->path,
-                                      event->mask, "Obj Modified");
-                break;
-
-            case IN_IGNORED:
-                trace_usb_mtp_inotify_event(s->dev.addr, parent->path,
-                                      event->mask, "Obj parent dir ignored");
-                break;
-
-            default:
-                fprintf(stderr, "usb-mtp: failed to parse inotify event\n");
-                continue;
-            }
-
-            if (entry) {
-                QTAILQ_INSERT_HEAD(&s->events, entry, next);
-            }
+        o = usb_mtp_object_lookup_name(parent, name, -1);
+        if (!o) {
+            return;
         }
-    }
-}
+        entry = g_new0(MTPMonEntry, 1);
+        entry->handle = o->handle;
+        entry->event = EVT_OBJ_REMOVED;
+        trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Deleted");
+        usb_mtp_object_free(s, o);
+        break;
 
-static int usb_mtp_inotify_init(MTPState *s)
-{
-    int fd;
+    case QFILE_MONITOR_EVENT_MODIFIED:
+        o = usb_mtp_object_lookup_name(parent, name, -1);
+        if (!o) {
+            return;
+        }
+        entry = g_new0(MTPMonEntry, 1);
+        entry->handle = o->handle;
+        entry->event = EVT_OBJ_INFO_CHANGED;
+        trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Modified");
+        break;
 
-    fd = inotify_init1(IN_NONBLOCK);
-    if (fd == -1) {
-        return 1;
-    }
+    case QFILE_MONITOR_EVENT_IGNORED:
+        trace_usb_mtp_file_monitor_event(s->dev.addr, parent->path,
+                                    "Obj parent dir ignored");
+        break;
 
-    QTAILQ_INIT(&s->events);
-    s->inotifyfd = fd;
+    case QFILE_MONITOR_EVENT_ATTRIBUTES:
+        break;
 
-    qemu_set_fd_handler(fd, inotify_watchfn, NULL, s);
+    default:
+        g_assert_not_reached();
+    }
 
-    return 0;
+    if (entry) {
+        QTAILQ_INSERT_HEAD(&s->events, entry, next);
+    }
 }
 
-static void usb_mtp_inotify_cleanup(MTPState *s)
+static void usb_mtp_file_monitor_cleanup(MTPState *s)
 {
     MTPMonEntry *e, *p;
 
-    if (!s->inotifyfd) {
-        return;
-    }
-
-    qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s);
-    close(s->inotifyfd);
-
     QTAILQ_FOREACH_SAFE(e, &s->events, next, p) {
         QTAILQ_REMOVE(&s->events, e, next);
         g_free(e);
     }
-}
 
-static int usb_mtp_add_watch(int inotifyfd, char *path)
-{
-    uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY |
-        IN_ISDIR;
-
-    return inotify_add_watch(inotifyfd, path, mask);
+    qemu_file_monitor_free(s->file_monitor);
+    s->file_monitor = NULL;
 }
-#endif
+
 
 static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
 {
     struct dirent *entry;
     DIR *dir;
     int fd;
+    Error *err = NULL;
 
     if (o->have_children) {
         return;
@@ -680,16 +623,21 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
         close(fd);
         return;
     }
-#ifdef CONFIG_INOTIFY1
-    int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path);
-    if (watchfd == -1) {
-        fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path);
-    } else {
-        trace_usb_mtp_inotify_event(s->dev.addr, o->path,
-                                    0, "Watch Added");
-        o->watchfd = watchfd;
+
+    if (s->file_monitor) {
+        int id = qemu_file_monitor_add_watch(s->file_monitor, o->path, NULL,
+                                             file_monitor_event, s, &err);
+        if (id == -1) {
+            error_report("usb-mtp: failed to add watch for %s: %s", o->path,
+                         error_get_pretty(err));
+            error_free(err);
+        } else {
+            trace_usb_mtp_file_monitor_event(s->dev.addr, o->path,
+                                             "Watch Added");
+            o->watchid = id;
+        }
     }
-#endif
+
     while ((entry = readdir(dir)) != NULL) {
         usb_mtp_add_child(s, o, entry->d_name);
     }
@@ -1197,13 +1145,11 @@ enum {
 /* Assumes that children, if any, have been already freed */
 static void usb_mtp_object_free_one(MTPState *s, MTPObject *o)
 {
-#ifndef CONFIG_INOTIFY1
     assert(o->nchildren == 0);
     QTAILQ_REMOVE(&s->objects, o, next);
     g_free(o->name);
     g_free(o->path);
     g_free(o);
-#endif
 }
 
 static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans)
@@ -1302,6 +1248,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
     MTPData *data_in = NULL;
     MTPObject *o = NULL;
     uint32_t nres = 0, res0 = 0;
+    Error *err = NULL;
 
     /* sanity checks */
     if (c->code >= CMD_CLOSE_SESSION && s->session == 0) {
@@ -1329,19 +1276,21 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
         trace_usb_mtp_op_open_session(s->dev.addr);
         s->session = c->argv[0];
         usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root);
-#ifdef CONFIG_INOTIFY1
-        if (usb_mtp_inotify_init(s)) {
-            fprintf(stderr, "usb-mtp: file monitoring init failed\n");
+
+        s->file_monitor = qemu_file_monitor_new(&err);
+        if (err) {
+            error_report("usb-mtp: file monitoring init failed: %s",
+                         error_get_pretty(err));
+            error_free(err);
+        } else {
+            QTAILQ_INIT(&s->events);
         }
-#endif
         break;
     case CMD_CLOSE_SESSION:
         trace_usb_mtp_op_close_session(s->dev.addr);
         s->session = 0;
         s->next_handle = 0;
-#ifdef CONFIG_INOTIFY1
-        usb_mtp_inotify_cleanup(s);
-#endif
+        usb_mtp_file_monitor_cleanup(s);
         usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
         assert(QTAILQ_EMPTY(&s->objects));
         break;
@@ -1554,9 +1503,7 @@ static void usb_mtp_handle_reset(USBDevice *dev)
 
     trace_usb_mtp_reset(s->dev.addr);
 
-#ifdef CONFIG_INOTIFY1
-    usb_mtp_inotify_cleanup(s);
-#endif
+    usb_mtp_file_monitor_cleanup(s);
     usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
     s->session = 0;
     usb_mtp_data_free(s->data_in);
@@ -2027,7 +1974,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
         }
         break;
     case EP_EVENT:
-#ifdef CONFIG_INOTIFY1
         if (!QTAILQ_EMPTY(&s->events)) {
             struct MTPMonEntry *e = QTAILQ_LAST(&s->events);
             uint32_t handle;
@@ -2051,7 +1997,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
             g_free(e);
             return;
         }
-#endif
         p->status = USB_RET_NAK;
         return;
     default:
diff --git a/hw/usb/trace-events b/hw/usb/trace-events
index 2c18770ca5..99b1e8b8ce 100644
--- a/hw/usb/trace-events
+++ b/hw/usb/trace-events
@@ -237,7 +237,7 @@ usb_mtp_op_unknown(int dev, uint32_t code) "dev %d, command code 0x%x"
 usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
 usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
 usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
-usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char *s) "dev %d, path %s mask 0x%x event %s"
+usb_mtp_file_monitor_event(int dev, const char *path, const char *s) "dev %d, path %s event %s"
 
 # hw/usb/host-libusb.c
 usb_host_open_started(int bus, int addr) "dev %d:%d"
diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c
index 6166ccd47a..d8b79ebe53 100644
--- a/hw/vfio/ap.c
+++ b/hw/vfio/ap.c
@@ -169,7 +169,7 @@ static void vfio_ap_class_init(ObjectClass *klass, void *data)
     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
     dc->realize = vfio_ap_realize;
     dc->unrealize = vfio_ap_unrealize;
-    dc->hotpluggable = false;
+    dc->hotpluggable = true;
     dc->reset = vfio_ap_reset;
     dc->bus_type = TYPE_AP_BUS;
 }
diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
index eae31c74d6..40a12001f5 100644
--- a/hw/vfio/pci-quirks.c
+++ b/hw/vfio/pci-quirks.c
@@ -526,8 +526,6 @@ static void vfio_probe_ati_bar2_quirk(VFIOPCIDevice *vdev, int nr)
  * note it for future reference.
  */
 
-#define PCI_VENDOR_ID_NVIDIA                    0x10de
-
 /*
  * Nvidia has several different methods to get to config space, the
  * nouveu project has several of these documented here:
diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs
index d335dd0a6a..a3eb8ed866 100644
--- a/hw/virtio/Makefile.objs
+++ b/hw/virtio/Makefile.objs
@@ -2,15 +2,18 @@ ifeq ($(CONFIG_VIRTIO),y)
 common-obj-y += virtio-bus.o
 obj-y += virtio.o
 
+obj-$(call lor,$(CONFIG_VHOST_USER),$(CONFIG_VHOST_KERNEL)) += vhost.o vhost-backend.o
+common-obj-$(call lnot,$(call lor,$(CONFIG_VHOST_USER),$(CONFIG_VHOST_KERNEL))) += vhost-stub.o
+obj-$(CONFIG_VHOST_USER) += vhost-user.o
+
 common-obj-$(CONFIG_VIRTIO_RNG) += virtio-rng.o
 common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
 common-obj-$(CONFIG_VIRTIO_MMIO) += virtio-mmio.o
 obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon.o
 obj-$(CONFIG_VIRTIO_CRYPTO) += virtio-crypto.o
 obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-pci.o
-
-obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o
 obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o
+
 ifeq ($(CONFIG_VIRTIO_PCI),y)
 obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock-pci.o
 obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk-pci.o
@@ -28,5 +31,4 @@ obj-$(CONFIG_VIRTIO_SERIAL) += virtio-serial-pci.o
 endif
 endif
 
-common-obj-$(call lnot,$(call land,$(CONFIG_VIRTIO),$(CONFIG_LINUX))) += vhost-stub.o
 common-obj-$(CONFIG_ALL) += vhost-stub.o
diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c
index 7f09efab8b..96b8d3c95d 100644
--- a/hw/virtio/vhost-backend.c
+++ b/hw/virtio/vhost-backend.c
@@ -9,11 +9,14 @@
  */
 
 #include "qemu/osdep.h"
-#include <linux/vhost.h>
-#include <sys/ioctl.h>
 #include "hw/virtio/vhost.h"
 #include "hw/virtio/vhost-backend.h"
 #include "qemu/error-report.h"
+#include "standard-headers/linux/vhost_types.h"
+
+#ifdef CONFIG_VHOST_KERNEL
+#include <linux/vhost.h>
+#include <sys/ioctl.h>
 
 static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request,
                              void *arg)
@@ -265,18 +268,23 @@ static const VhostOps kernel_ops = {
         .vhost_set_iotlb_callback = vhost_kernel_set_iotlb_callback,
         .vhost_send_device_iotlb_msg = vhost_kernel_send_device_iotlb_msg,
 };
+#endif
 
 int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type)
 {
     int r = 0;
 
     switch (backend_type) {
+#ifdef CONFIG_VHOST_KERNEL
     case VHOST_BACKEND_TYPE_KERNEL:
         dev->vhost_ops = &kernel_ops;
         break;
+#endif
+#ifdef CONFIG_VHOST_USER
     case VHOST_BACKEND_TYPE_USER:
         dev->vhost_ops = &user_ops;
         break;
+#endif
     default:
         error_report("Unknown vhost backend type");
         r = -1;
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 564a31d12c..0d6c64e5ca 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -27,8 +27,12 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/un.h>
-#include <linux/vhost.h>
+
+#include "standard-headers/linux/vhost_types.h"
+
+#ifdef CONFIG_LINUX
 #include <linux/userfaultfd.h>
+#endif
 
 #define VHOST_MEMORY_MAX_NREGIONS    8
 #define VHOST_USER_F_PROTOCOL_FEATURES 30
@@ -1110,6 +1114,7 @@ out:
     return ret;
 }
 
+#ifdef CONFIG_LINUX
 /*
  * Called back from the postcopy fault thread when a fault is received on our
  * ufd.
@@ -1177,6 +1182,7 @@ static int vhost_user_postcopy_waker(struct PostCopyFD *pcfd, RAMBlock *rb,
     trace_vhost_user_postcopy_waker_nomatch(qemu_ram_get_idstr(rb), offset);
     return 0;
 }
+#endif
 
 /*
  * Called at the start of an inbound postcopy on reception of the
@@ -1184,6 +1190,7 @@ static int vhost_user_postcopy_waker(struct PostCopyFD *pcfd, RAMBlock *rb,
  */
 static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp)
 {
+#ifdef CONFIG_LINUX
     struct vhost_user *u = dev->opaque;
     CharBackend *chr = u->user->chr;
     int ufd;
@@ -1227,6 +1234,10 @@ static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp)
     u->postcopy_fd.idstr = "vhost-user"; /* Need to find unique name */
     postcopy_register_shared_ufd(&u->postcopy_fd);
     return 0;
+#else
+    error_setg(errp, "Postcopy not supported on non-Linux systems");
+    return -1;
+#endif
 }
 
 /*
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 569c4053ea..311432f190 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -21,7 +21,7 @@
 #include "qemu/range.h"
 #include "qemu/error-report.h"
 #include "qemu/memfd.h"
-#include <linux/vhost.h>
+#include "standard-headers/linux/vhost_types.h"
 #include "exec/address-spaces.h"
 #include "hw/virtio/virtio-bus.h"
 #include "hw/virtio/virtio-access.h"
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index a12677d4d5..d3f2913a85 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -33,11 +33,81 @@
 
 #define BALLOON_PAGE_SIZE  (1 << VIRTIO_BALLOON_PFN_SHIFT)
 
-static void balloon_page(void *addr, int deflate)
+struct PartiallyBalloonedPage {
+    RAMBlock *rb;
+    ram_addr_t base;
+    unsigned long bitmap[];
+};
+
+static void balloon_inflate_page(VirtIOBalloon *balloon,
+                                 MemoryRegion *mr, hwaddr offset)
 {
-    if (!qemu_balloon_is_inhibited()) {
-        qemu_madvise(addr, BALLOON_PAGE_SIZE,
-                deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED);
+    void *addr = memory_region_get_ram_ptr(mr) + offset;
+    RAMBlock *rb;
+    size_t rb_page_size;
+    int subpages;
+    ram_addr_t ram_offset, host_page_base;
+
+    /* XXX is there a better way to get to the RAMBlock than via a
+     * host address? */
+    rb = qemu_ram_block_from_host(addr, false, &ram_offset);
+    rb_page_size = qemu_ram_pagesize(rb);
+    host_page_base = ram_offset & ~(rb_page_size - 1);
+
+    if (rb_page_size == BALLOON_PAGE_SIZE) {
+        /* Easy case */
+
+        ram_block_discard_range(rb, ram_offset, rb_page_size);
+        /* We ignore errors from ram_block_discard_range(), because it
+         * has already reported them, and failing to discard a balloon
+         * page is not fatal */
+        return;
+    }
+
+    /* Hard case
+     *
+     * We've put a piece of a larger host page into the balloon - we
+     * need to keep track until we have a whole host page to
+     * discard
+     */
+    warn_report_once(
+"Balloon used with backing page size > 4kiB, this may not be reliable");
+
+    subpages = rb_page_size / BALLOON_PAGE_SIZE;
+
+    if (balloon->pbp
+        && (rb != balloon->pbp->rb
+            || host_page_base != balloon->pbp->base)) {
+        /* We've partially ballooned part of a host page, but now
+         * we're trying to balloon part of a different one.  Too hard,
+         * give up on the old partial page */
+        free(balloon->pbp);
+        balloon->pbp = NULL;
+    }
+
+    if (!balloon->pbp) {
+        /* Starting on a new host page */
+        size_t bitlen = BITS_TO_LONGS(subpages) * sizeof(unsigned long);
+        balloon->pbp = g_malloc0(sizeof(PartiallyBalloonedPage) + bitlen);
+        balloon->pbp->rb = rb;
+        balloon->pbp->base = host_page_base;
+    }
+
+    bitmap_set(balloon->pbp->bitmap,
+               (ram_offset - balloon->pbp->base) / BALLOON_PAGE_SIZE,
+               subpages);
+
+    if (bitmap_full(balloon->pbp->bitmap, subpages)) {
+        /* We've accumulated a full host page, we can actually discard
+         * it now */
+
+        ram_block_discard_range(rb, balloon->pbp->base, rb_page_size);
+        /* We ignore errors from ram_block_discard_range(), because it
+         * has already reported them, and failing to discard a balloon
+         * page is not fatal */
+
+        free(balloon->pbp);
+        balloon->pbp = NULL;
     }
 }
 
@@ -222,17 +292,19 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
         }
 
         while (iov_to_buf(elem->out_sg, elem->out_num, offset, &pfn, 4) == 4) {
-            ram_addr_t pa;
-            ram_addr_t addr;
+            hwaddr pa;
             int p = virtio_ldl_p(vdev, &pfn);
 
-            pa = (ram_addr_t) p << VIRTIO_BALLOON_PFN_SHIFT;
+            pa = (hwaddr) p << VIRTIO_BALLOON_PFN_SHIFT;
             offset += 4;
 
-            /* FIXME: remove get_system_memory(), but how? */
-            section = memory_region_find(get_system_memory(), pa, 1);
-            if (!int128_nz(section.size) ||
-                !memory_region_is_ram(section.mr) ||
+            section = memory_region_find(get_system_memory(), pa,
+                                         BALLOON_PAGE_SIZE);
+            if (!section.mr) {
+                trace_virtio_balloon_bad_addr(pa);
+                continue;
+            }
+            if (!memory_region_is_ram(section.mr) ||
                 memory_region_is_rom(section.mr) ||
                 memory_region_is_romd(section.mr)) {
                 trace_virtio_balloon_bad_addr(pa);
@@ -242,11 +314,9 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
 
             trace_virtio_balloon_handle_output(memory_region_name(section.mr),
                                                pa);
-            /* Using memory_region_get_ram_ptr is bending the rules a bit, but
-               should be OK because we only want a single page.  */
-            addr = section.offset_within_region;
-            balloon_page(memory_region_get_ram_ptr(section.mr) + addr,
-                         !!(vq == s->dvq));
+            if (!qemu_balloon_is_inhibited() && vq != s->dvq) {
+                balloon_inflate_page(s, section.mr, section.offset_within_region);
+            }
             memory_region_unref(section.mr);
         }
 
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index a1ff647a66..2626a895cb 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -2036,6 +2036,21 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val)
     return ret;
 }
 
+size_t virtio_feature_get_config_size(VirtIOFeature *feature_sizes,
+                                      uint64_t host_features)
+{
+    size_t config_size = 0;
+    int i;
+
+    for (i = 0; feature_sizes[i].flags != 0; i++) {
+        if (host_features & feature_sizes[i].flags) {
+            config_size = MAX(feature_sizes[i].end, config_size);
+        }
+    }
+
+    return config_size;
+}
+
 int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
 {
     int i, ret;
diff --git a/include/authz/base.h b/include/authz/base.h
new file mode 100644
index 0000000000..77dcd54c4c
--- /dev/null
+++ b/include/authz/base.h
@@ -0,0 +1,112 @@
+/*
+ * QEMU authorization framework base class
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_BASE_H__
+#define QAUTHZ_BASE_H__
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qom/object.h"
+
+
+#define TYPE_QAUTHZ "authz"
+
+#define QAUTHZ_CLASS(klass) \
+     OBJECT_CLASS_CHECK(QAuthZClass, (klass), \
+                        TYPE_QAUTHZ)
+#define QAUTHZ_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(QAuthZClass, (obj), \
+                      TYPE_QAUTHZ)
+#define QAUTHZ(obj) \
+     INTERFACE_CHECK(QAuthZ, (obj), \
+                     TYPE_QAUTHZ)
+
+typedef struct QAuthZ QAuthZ;
+typedef struct QAuthZClass QAuthZClass;
+
+/**
+ * QAuthZ:
+ *
+ * The QAuthZ class defines an API contract to be used
+ * for providing an authorization driver for services
+ * with user identities.
+ */
+
+struct QAuthZ {
+    Object parent_obj;
+};
+
+
+struct QAuthZClass {
+    ObjectClass parent_class;
+
+    bool (*is_allowed)(QAuthZ *authz,
+                       const char *identity,
+                       Error **errp);
+};
+
+
+/**
+ * qauthz_is_allowed:
+ * @authz: the authorization object
+ * @identity: the user identity to authorize
+ * @errp: pointer to a NULL initialized error object
+ *
+ * Check if a user @identity is authorized. If an error
+ * occurs this method will return false to indicate
+ * denial, as well as setting @errp to contain the details.
+ * Callers are recommended to treat the denial and error
+ * scenarios identically. Specifically the error info in
+ * @errp should never be fed back to the user being
+ * authorized, it is merely for benefit of administrator
+ * debugging.
+ *
+ * Returns: true if @identity is authorized, false if denied or if
+ * an error occurred.
+ */
+bool qauthz_is_allowed(QAuthZ *authz,
+                       const char *identity,
+                       Error **errp);
+
+
+/**
+ * qauthz_is_allowed_by_id:
+ * @authzid: ID of the authorization object
+ * @identity: the user identity to authorize
+ * @errp: pointer to a NULL initialized error object
+ *
+ * Check if a user @identity is authorized. If an error
+ * occurs this method will return false to indicate
+ * denial, as well as setting @errp to contain the details.
+ * Callers are recommended to treat the denial and error
+ * scenarios identically. Specifically the error info in
+ * @errp should never be fed back to the user being
+ * authorized, it is merely for benefit of administrator
+ * debugging.
+ *
+ * Returns: true if @identity is authorized, false if denied or if
+ * an error occurred.
+ */
+bool qauthz_is_allowed_by_id(const char *authzid,
+                             const char *identity,
+                             Error **errp);
+
+#endif /* QAUTHZ_BASE_H__ */
+
diff --git a/include/authz/list.h b/include/authz/list.h
new file mode 100644
index 0000000000..a7225a747c
--- /dev/null
+++ b/include/authz/list.h
@@ -0,0 +1,106 @@
+/*
+ * QEMU list authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_LIST_H__
+#define QAUTHZ_LIST_H__
+
+#include "authz/base.h"
+#include "qapi/qapi-types-authz.h"
+
+#define TYPE_QAUTHZ_LIST "authz-list"
+
+#define QAUTHZ_LIST_CLASS(klass)                        \
+    OBJECT_CLASS_CHECK(QAuthZListClass, (klass),        \
+                       TYPE_QAUTHZ_LIST)
+#define QAUTHZ_LIST_GET_CLASS(obj)              \
+    OBJECT_GET_CLASS(QAuthZListClass, (obj),    \
+                      TYPE_QAUTHZ_LIST)
+#define QAUTHZ_LIST(obj) \
+    INTERFACE_CHECK(QAuthZList, (obj),          \
+                    TYPE_QAUTHZ_LIST)
+
+typedef struct QAuthZList QAuthZList;
+typedef struct QAuthZListClass QAuthZListClass;
+
+
+/**
+ * QAuthZList:
+ *
+ * This authorization driver provides a list mechanism
+ * for granting access by matching user names against a
+ * list of globs. Each match rule has an associated policy
+ * and a catch all policy applies if no rule matches
+ *
+ * To create an instance of this class via QMP:
+ *
+ *  {
+ *    "execute": "object-add",
+ *    "arguments": {
+ *      "qom-type": "authz-list",
+ *      "id": "authz0",
+ *      "props": {
+ *        "rules": [
+ *           { "match": "fred", "policy": "allow", "format": "exact" },
+ *           { "match": "bob", "policy": "allow", "format": "exact" },
+ *           { "match": "danb", "policy": "deny", "format": "exact" },
+ *           { "match": "dan*", "policy": "allow", "format": "glob" }
+ *        ],
+ *        "policy": "deny"
+ *      }
+ *    }
+ *  }
+ *
+ */
+struct QAuthZList {
+    QAuthZ parent_obj;
+
+    QAuthZListPolicy policy;
+    QAuthZListRuleList *rules;
+};
+
+
+struct QAuthZListClass {
+    QAuthZClass parent_class;
+};
+
+
+QAuthZList *qauthz_list_new(const char *id,
+                            QAuthZListPolicy policy,
+                            Error **errp);
+
+ssize_t qauthz_list_append_rule(QAuthZList *auth,
+                                const char *match,
+                                QAuthZListPolicy policy,
+                                QAuthZListFormat format,
+                                Error **errp);
+
+ssize_t qauthz_list_insert_rule(QAuthZList *auth,
+                                const char *match,
+                                QAuthZListPolicy policy,
+                                QAuthZListFormat format,
+                                size_t index,
+                                Error **errp);
+
+ssize_t qauthz_list_delete_rule(QAuthZList *auth,
+                                const char *match);
+
+
+#endif /* QAUTHZ_LIST_H__ */
+
diff --git a/include/authz/listfile.h b/include/authz/listfile.h
new file mode 100644
index 0000000000..bcc8d80743
--- /dev/null
+++ b/include/authz/listfile.h
@@ -0,0 +1,111 @@
+/*
+ * QEMU list file authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_LIST_FILE_H__
+#define QAUTHZ_LIST_FILE_H__
+
+#include "authz/list.h"
+#include "qapi/qapi-types-authz.h"
+#include "qemu/filemonitor.h"
+
+#define TYPE_QAUTHZ_LIST_FILE "authz-list-file"
+
+#define QAUTHZ_LIST_FILE_CLASS(klass)                        \
+    OBJECT_CLASS_CHECK(QAuthZListFileClass, (klass),        \
+                       TYPE_QAUTHZ_LIST_FILE)
+#define QAUTHZ_LIST_FILE_GET_CLASS(obj)              \
+    OBJECT_GET_CLASS(QAuthZListFileClass, (obj),    \
+                      TYPE_QAUTHZ_LIST_FILE)
+#define QAUTHZ_LIST_FILE(obj) \
+    INTERFACE_CHECK(QAuthZListFile, (obj),          \
+                    TYPE_QAUTHZ_LIST_FILE)
+
+typedef struct QAuthZListFile QAuthZListFile;
+typedef struct QAuthZListFileClass QAuthZListFileClass;
+
+
+/**
+ * QAuthZListFile:
+ *
+ * This authorization driver provides a file mechanism
+ * for granting access by matching user names against a
+ * file of globs. Each match rule has an associated policy
+ * and a catch all policy applies if no rule matches
+ *
+ * To create an instance of this class via QMP:
+ *
+ *  {
+ *    "execute": "object-add",
+ *    "arguments": {
+ *      "qom-type": "authz-list-file",
+ *      "id": "authz0",
+ *      "props": {
+ *        "filename": "/etc/qemu/myvm-vnc.acl",
+ *        "refresh": true
+ *      }
+ *    }
+ *  }
+ *
+ * If 'refresh' is 'yes', inotify is used to monitor for changes
+ * to the file and auto-reload the rules.
+ *
+ * The myvm-vnc.acl file should contain the parameters for
+ * the QAuthZList object in JSON format:
+ *
+ *      {
+ *        "rules": [
+ *           { "match": "fred", "policy": "allow", "format": "exact" },
+ *           { "match": "bob", "policy": "allow", "format": "exact" },
+ *           { "match": "danb", "policy": "deny", "format": "exact" },
+ *           { "match": "dan*", "policy": "allow", "format": "glob" }
+ *        ],
+ *        "policy": "deny"
+ *      }
+ *
+ * The object can be created on the command line using
+ *
+ *   -object authz-list-file,id=authz0,\
+ *           filename=/etc/qemu/myvm-vnc.acl,refresh=yes
+ *
+ */
+struct QAuthZListFile {
+    QAuthZ parent_obj;
+
+    QAuthZ *list;
+    char *filename;
+    bool refresh;
+    QFileMonitor *file_monitor;
+    int file_watch;
+};
+
+
+struct QAuthZListFileClass {
+    QAuthZClass parent_class;
+};
+
+
+QAuthZListFile *qauthz_list_file_new(const char *id,
+                                     const char *filename,
+                                     bool refresh,
+                                     Error **errp);
+
+
+#endif /* QAUTHZ_LIST_FILE_H__ */
+
diff --git a/include/authz/pamacct.h b/include/authz/pamacct.h
new file mode 100644
index 0000000000..6e3046e528
--- /dev/null
+++ b/include/authz/pamacct.h
@@ -0,0 +1,100 @@
+/*
+ * QEMU PAM authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_PAM_H__
+#define QAUTHZ_PAM_H__
+
+#include "authz/base.h"
+
+
+#define TYPE_QAUTHZ_PAM "authz-pam"
+
+#define QAUTHZ_PAM_CLASS(klass) \
+     OBJECT_CLASS_CHECK(QAuthZPAMClass, (klass), \
+                        TYPE_QAUTHZ_PAM)
+#define QAUTHZ_PAM_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(QAuthZPAMClass, (obj), \
+                      TYPE_QAUTHZ_PAM)
+#define QAUTHZ_PAM(obj) \
+     INTERFACE_CHECK(QAuthZPAM, (obj), \
+                     TYPE_QAUTHZ_PAM)
+
+typedef struct QAuthZPAM QAuthZPAM;
+typedef struct QAuthZPAMClass QAuthZPAMClass;
+
+
+/**
+ * QAuthZPAM:
+ *
+ * This authorization driver provides a PAM mechanism
+ * for granting access by matching user names against a
+ * list of globs. Each match rule has an associated policy
+ * and a catch all policy applies if no rule matches
+ *
+ * To create an instance of this class via QMP:
+ *
+ *  {
+ *    "execute": "object-add",
+ *    "arguments": {
+ *      "qom-type": "authz-pam",
+ *      "id": "authz0",
+ *      "parameters": {
+ *        "service": "qemu-vnc-tls"
+ *      }
+ *    }
+ *  }
+ *
+ * The driver only uses the PAM "account" verification
+ * subsystem. The above config would require a config
+ * file /etc/pam.d/qemu-vnc-tls. For a simple file
+ * lookup it would contain
+ *
+ *   account requisite  pam_listfile.so item=user sense=allow \
+ *           file=/etc/qemu/vnc.allow
+ *
+ * The external file would then contain a list of usernames.
+ * If x509 cert was being used as the username, a suitable
+ * entry would match the distinguish name:
+ *
+ *  CN=laptop.berrange.com,O=Berrange Home,L=London,ST=London,C=GB
+ *
+ * On the command line it can be created using
+ *
+ *   -object authz-pam,id=authz0,service=qemu-vnc-tls
+ *
+ */
+struct QAuthZPAM {
+    QAuthZ parent_obj;
+
+    char *service;
+};
+
+
+struct QAuthZPAMClass {
+    QAuthZClass parent_class;
+};
+
+
+QAuthZPAM *qauthz_pam_new(const char *id,
+                          const char *service,
+                          Error **errp);
+
+
+#endif /* QAUTHZ_PAM_H__ */
diff --git a/include/authz/simple.h b/include/authz/simple.h
new file mode 100644
index 0000000000..ef13958269
--- /dev/null
+++ b/include/authz/simple.h
@@ -0,0 +1,84 @@
+/*
+ * QEMU simple authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_SIMPLE_H__
+#define QAUTHZ_SIMPLE_H__
+
+#include "authz/base.h"
+
+#define TYPE_QAUTHZ_SIMPLE "authz-simple"
+
+#define QAUTHZ_SIMPLE_CLASS(klass)                        \
+    OBJECT_CLASS_CHECK(QAuthZSimpleClass, (klass),        \
+                       TYPE_QAUTHZ_SIMPLE)
+#define QAUTHZ_SIMPLE_GET_CLASS(obj)              \
+    OBJECT_GET_CLASS(QAuthZSimpleClass, (obj),    \
+                      TYPE_QAUTHZ_SIMPLE)
+#define QAUTHZ_SIMPLE(obj) \
+    INTERFACE_CHECK(QAuthZSimple, (obj),          \
+                    TYPE_QAUTHZ_SIMPLE)
+
+typedef struct QAuthZSimple QAuthZSimple;
+typedef struct QAuthZSimpleClass QAuthZSimpleClass;
+
+
+/**
+ * QAuthZSimple:
+ *
+ * This authorization driver provides a simple mechanism
+ * for granting access based on an exact matched username.
+ *
+ * To create an instance of this class via QMP:
+ *
+ *  {
+ *    "execute": "object-add",
+ *    "arguments": {
+ *      "qom-type": "authz-simple",
+ *      "id": "authz0",
+ *      "props": {
+ *        "identity": "fred"
+ *      }
+ *    }
+ *  }
+ *
+ * Or via the command line
+ *
+ *   -object authz-simple,id=authz0,identity=fred
+ *
+ */
+struct QAuthZSimple {
+    QAuthZ parent_obj;
+
+    char *identity;
+};
+
+
+struct QAuthZSimpleClass {
+    QAuthZClass parent_class;
+};
+
+
+QAuthZSimple *qauthz_simple_new(const char *id,
+                                const char *identity,
+                                Error **errp);
+
+
+#endif /* QAUTHZ_SIMPLE_H__ */
+
diff --git a/include/block/block.h b/include/block/block.h
index 73357c6c25..5b5cf868df 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -485,21 +485,17 @@ void bdrv_round_to_clusters(BlockDriverState *bs,
                             int64_t *cluster_offset,
                             int64_t *cluster_bytes);
 
-const char *bdrv_get_encrypted_filename(BlockDriverState *bs);
 void bdrv_get_backing_filename(BlockDriverState *bs,
                                char *filename, int filename_size);
-void bdrv_get_full_backing_filename(BlockDriverState *bs,
-                                    char *dest, size_t sz, Error **errp);
-void bdrv_get_full_backing_filename_from_filename(const char *backed,
-                                                  const char *backing,
-                                                  char *dest, size_t sz,
-                                                  Error **errp);
+char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp);
+char *bdrv_get_full_backing_filename_from_filename(const char *backed,
+                                                   const char *backing,
+                                                   Error **errp);
+char *bdrv_dirname(BlockDriverState *bs, Error **errp);
 
 int path_has_protocol(const char *path);
 int path_is_absolute(const char *path);
-void path_combine(char *dest, int dest_size,
-                  const char *base_path,
-                  const char *filename);
+char *path_combine(const char *base_path, const char *filename);
 
 int bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
 int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 0075bafd10..836d67c1ae 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -139,7 +139,42 @@ struct BlockDriver {
                                             Error **errp);
     int (*bdrv_make_empty)(BlockDriverState *bs);
 
-    void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options);
+    /*
+     * Refreshes the bs->exact_filename field. If that is impossible,
+     * bs->exact_filename has to be left empty.
+     */
+    void (*bdrv_refresh_filename)(BlockDriverState *bs);
+
+    /*
+     * Gathers the open options for all children into @target.
+     * A simple format driver (without backing file support) might
+     * implement this function like this:
+     *
+     *     QINCREF(bs->file->bs->full_open_options);
+     *     qdict_put(target, "file", bs->file->bs->full_open_options);
+     *
+     * If not specified, the generic implementation will simply put
+     * all children's options under their respective name.
+     *
+     * @backing_overridden is true when bs->backing seems not to be
+     * the child that would result from opening bs->backing_file.
+     * Therefore, if it is true, the backing child's options should be
+     * gathered; otherwise, there is no need since the backing child
+     * is the one implied by the image header.
+     *
+     * Note that ideally this function would not be needed.  Every
+     * block driver which implements it is probably doing something
+     * shady regarding its runtime option structure.
+     */
+    void (*bdrv_gather_child_options)(BlockDriverState *bs, QDict *target,
+                                      bool backing_overridden);
+
+    /*
+     * Returns an allocated string which is the directory name of this BDS: It
+     * will be used to make relative filenames absolute by prepending this
+     * function's return value to them.
+     */
+    char *(*bdrv_dirname)(BlockDriverState *bs, Error **errp);
 
     /* aio */
     BlockAIOCB *(*bdrv_aio_preadv)(BlockDriverState *bs,
@@ -510,6 +545,13 @@ struct BlockDriver {
     void (*bdrv_register_buf)(BlockDriverState *bs, void *host, size_t size);
     void (*bdrv_unregister_buf)(BlockDriverState *bs, void *host);
     QLIST_ENTRY(BlockDriver) list;
+
+    /* Pointer to a NULL-terminated array of names of strong options
+     * that can be specified for bdrv_open(). A strong option is one
+     * that changes the data of a BDS.
+     * If this pointer is NULL, the array is considered empty.
+     * "filename" and "driver" are always considered strong. */
+    const char *const *strong_runtime_opts;
 };
 
 typedef struct BlockLimits {
@@ -662,6 +704,11 @@ struct BdrvChild {
      */
     uint64_t shared_perm;
 
+    /* backup of permissions during permission update procedure */
+    bool has_backup_perm;
+    uint64_t backup_perm;
+    uint64_t backup_shared_perm;
+
     QLIST_ENTRY(BdrvChild) next;
     QLIST_ENTRY(BdrvChild) next_parent;
 };
@@ -697,6 +744,10 @@ struct BlockDriverState {
     char filename[PATH_MAX];
     char backing_file[PATH_MAX]; /* if non zero, the image is a diff of
                                     this file image */
+    /* The backing filename indicated by the image header; if we ever
+     * open this file, then this is replaced by the resulting BDS's
+     * filename (i.e. after a bdrv_refresh_filename() run). */
+    char auto_backing_file[PATH_MAX];
     char backing_format[16]; /* if non-zero and backing_file exists */
 
     QDict *full_open_options;
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 96cfb1d7d5..c6ef1ef42e 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -300,7 +300,8 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
 int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info,
              Error **errp);
 int nbd_send_request(QIOChannel *ioc, NBDRequest *request);
-int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp);
+int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc,
+                                   NBDReply *reply, Error **errp);
 int nbd_client(int fd);
 int nbd_disconnect(int fd);
 int nbd_errno_to_system_errno(int err);
diff --git a/include/block/snapshot.h b/include/block/snapshot.h
index f73d1094af..b5d5084a12 100644
--- a/include/block/snapshot.h
+++ b/include/block/snapshot.h
@@ -61,9 +61,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
                          const char *snapshot_id,
                          const char *name,
                          Error **errp);
-int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
-                                       const char *id_or_name,
-                                       Error **errp);
 int bdrv_snapshot_list(BlockDriverState *bs,
                        QEMUSnapshotInfo **psn_info);
 int bdrv_snapshot_load_tmp(BlockDriverState *bs,
diff --git a/include/chardev/spice.h b/include/chardev/spice.h
new file mode 100644
index 0000000000..6431da3205
--- /dev/null
+++ b/include/chardev/spice.h
@@ -0,0 +1,27 @@
+#ifndef CHARDEV_SPICE_H_
+#define CHARDEV_SPICE_H_
+
+#include <spice.h>
+#include "chardev/char-fe.h"
+
+typedef struct SpiceChardev {
+    Chardev               parent;
+
+    SpiceCharDeviceInstance sin;
+    bool                  active;
+    bool                  blocked;
+    const uint8_t         *datapos;
+    int                   datalen;
+    QLIST_ENTRY(SpiceChardev) next;
+} SpiceChardev;
+
+#define TYPE_CHARDEV_SPICE "chardev-spice"
+#define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc"
+#define TYPE_CHARDEV_SPICEPORT "chardev-spiceport"
+
+#define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), TYPE_CHARDEV_SPICE)
+
+void qemu_chr_open_spice_port(Chardev *chr, ChardevBackend *backend,
+                              bool *be_opened, Error **errp);
+
+#endif
diff --git a/include/exec/poison.h b/include/exec/poison.h
index ecdc83c147..1a7a57baae 100644
--- a/include/exec/poison.h
+++ b/include/exec/poison.h
@@ -86,7 +86,6 @@
 #pragma GCC poison CONFIG_XTENSA_DIS
 
 #pragma GCC poison CONFIG_LINUX_USER
-#pragma GCC poison CONFIG_VHOST_NET
 #pragma GCC poison CONFIG_KVM
 #pragma GCC poison CONFIG_SOFTMMU
 
diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index 38a5e99cf3..3ff3fa5224 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -466,7 +466,7 @@ static inline int float32_is_zero_or_denormal(float32 a)
 
 static inline bool float32_is_normal(float32 a)
 {
-    return ((float32_val(a) + 0x00800000) & 0x7fffffff) >= 0x01000000;
+    return (((float32_val(a) >> 23) + 1) & 0xff) >= 2;
 }
 
 static inline bool float32_is_denormal(float32 a)
@@ -622,7 +622,7 @@ static inline int float64_is_zero_or_denormal(float64 a)
 
 static inline bool float64_is_normal(float64 a)
 {
-    return ((float64_val(a) + (1ULL << 52)) & -1ULL >> 1) >= 1ULL << 53;
+    return (((float64_val(a) >> 52) + 1) & 0x7ff) >= 2;
 }
 
 static inline bool float64_is_denormal(float64 a)
@@ -878,6 +878,7 @@ int64_t float128_to_int64(float128, float_status *status);
 int64_t float128_to_int64_round_to_zero(float128, float_status *status);
 uint64_t float128_to_uint64(float128, float_status *status);
 uint64_t float128_to_uint64_round_to_zero(float128, float_status *status);
+uint32_t float128_to_uint32(float128, float_status *status);
 uint32_t float128_to_uint32_round_to_zero(float128, float_status *status);
 float32 float128_to_float32(float128, float_status *status);
 float64 float128_to_float64(float128, float_status *status);
@@ -940,6 +941,16 @@ static inline int float128_is_zero_or_denormal(float128 a)
     return (a.high & 0x7fff000000000000LL) == 0;
 }
 
+static inline bool float128_is_normal(float128 a)
+{
+    return (((a.high >> 48) + 1) & 0x7fff) >= 2;
+}
+
+static inline bool float128_is_denormal(float128 a)
+{
+    return float128_is_zero_or_denormal(a) && !float128_is_zero(a);
+}
+
 static inline int float128_is_any_nan(float128 a)
 {
     return ((a.high >> 48) & 0x7fff) == 0x7fff &&
diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h
index 7ef871c7df..81e082cccf 100644
--- a/include/hw/arm/armsse.h
+++ b/include/hw/arm/armsse.h
@@ -95,6 +95,7 @@
 #include "hw/misc/iotkit-sysctl.h"
 #include "hw/misc/iotkit-sysinfo.h"
 #include "hw/misc/armsse-cpuid.h"
+#include "hw/misc/armsse-mhu.h"
 #include "hw/misc/unimp.h"
 #include "hw/or-irq.h"
 #include "hw/core/split-irq.h"
@@ -166,7 +167,7 @@ typedef struct ARMSSE {
     IoTKitSysCtl sysctl;
     IoTKitSysCtl sysinfo;
 
-    UnimplementedDeviceState mhu[2];
+    ARMSSEMHU mhu[2];
     UnimplementedDeviceState ppu[NUM_PPUS];
     UnimplementedDeviceState cachectrl[SSE_MAX_CPUS];
     UnimplementedDeviceState cpusecctrl[SSE_MAX_CPUS];
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 05f9f45c3d..21212f0859 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -206,6 +206,7 @@ struct MachineClass {
     void (*numa_auto_assign_ram)(MachineClass *mc, NodeInfo *nodes,
                                  int nb_nodes, ram_addr_t size);
     bool ignore_boot_device_suffixes;
+    bool smbus_no_migration_support;
 
     HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
                                            DeviceState *dev);
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index eeb5a4d7b6..6fef32a3c9 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -162,6 +162,7 @@ struct smbios_type_3 {
     uint8_t height;
     uint8_t number_of_power_cords;
     uint8_t contained_element_count;
+    uint8_t contained_element_record_length;
     uint8_t sku_number_str;
     /* contained elements follow */
 } QEMU_PACKED;
diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h
index cf4c45a98f..8e236f7bb4 100644
--- a/include/hw/i2c/i2c.h
+++ b/include/hw/i2c/i2c.h
@@ -33,10 +33,9 @@ typedef struct I2CSlaveClass {
 
     /*
      * Slave to master.  This cannot fail, the device should always
-     * return something here.  Negative values probably result in 0xff
-     * and a possible log from the driver, and shouldn't be used.
+     * return something here.
      */
-    int (*recv)(I2CSlave *s);
+    uint8_t (*recv)(I2CSlave *s);
 
     /*
      * Notify the slave of a bus state change.  For start event,
@@ -78,7 +77,7 @@ void i2c_end_transfer(I2CBus *bus);
 void i2c_nack(I2CBus *bus);
 int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send);
 int i2c_send(I2CBus *bus, uint8_t data);
-int i2c_recv(I2CBus *bus);
+uint8_t i2c_recv(I2CBus *bus);
 
 DeviceState *i2c_create_slave(I2CBus *bus, const char *name, uint8_t addr);
 
diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h
index 060d3c6ac0..fb55c44444 100644
--- a/include/hw/i2c/pm_smbus.h
+++ b/include/hw/i2c/pm_smbus.h
@@ -1,6 +1,8 @@
 #ifndef PM_SMBUS_H
 #define PM_SMBUS_H
 
+#include "hw/i2c/smbus_master.h"
+
 #define PM_SMBUS_MAX_MSG_SIZE 32
 
 typedef struct PMSMBus {
@@ -31,8 +33,23 @@ typedef struct PMSMBus {
     /* Set on block transfers after the last byte has been read, so the
        INTR bit can be set at the right time. */
     bool op_done;
+
+    /* Set during an I2C block read, so we know how to handle data. */
+    bool in_i2c_block_read;
+
+    /* Used to work around a bug in AMIBIOS, see smb_transaction_start() */
+    bool start_transaction_on_status_read;
 } PMSMBus;
 
 void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk);
 
+/*
+ * For backwards compatibility on migration, older versions don't have
+ * working migration for pm_smbus, this lets us ignore the migrations
+ * for older machine versions.
+ */
+bool pm_smbus_vmstate_needed(void);
+
+extern const VMStateDescription pmsmb_vmstate;
+
 #endif /* PM_SMBUS_H */
diff --git a/include/qemu/acl.h b/include/hw/i2c/smbus_eeprom.h
index 73d2a71c8d..0f96836bab 100644
--- a/include/qemu/acl.h
+++ b/include/hw/i2c/smbus_eeprom.h
@@ -1,7 +1,5 @@
 /*
- * QEMU access control list management
- *
- * Copyright (C) 2009 Red Hat, Inc
+ * QEMU SMBus EEPROM API
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -22,45 +20,16 @@
  * THE SOFTWARE.
  */
 
-#ifndef QEMU_ACL_H
-#define QEMU_ACL_H
-
-#include "qemu/queue.h"
-
-typedef struct qemu_acl_entry qemu_acl_entry;
-typedef struct qemu_acl qemu_acl;
-
-struct qemu_acl_entry {
-    char *match;
-    int deny;
-
-    QTAILQ_ENTRY(qemu_acl_entry) next;
-};
-
-struct qemu_acl {
-    char *aclname;
-    unsigned int nentries;
-    QTAILQ_HEAD(,qemu_acl_entry) entries;
-    int defaultDeny;
-};
-
-qemu_acl *qemu_acl_init(const char *aclname);
-
-qemu_acl *qemu_acl_find(const char *aclname);
+#ifndef HW_SMBUS_EEPROM_H
+#define HW_SMBUS_EEPROM_H
 
-int qemu_acl_party_is_allowed(qemu_acl *acl,
-                              const char *party);
+#include "hw/i2c/i2c.h"
 
-void qemu_acl_reset(qemu_acl *acl);
+void smbus_eeprom_init_one(I2CBus *bus, uint8_t address, uint8_t *eeprom_buf);
+void smbus_eeprom_init(I2CBus *bus, int nb_eeprom,
+                       const uint8_t *eeprom_spd, int size);
 
-int qemu_acl_append(qemu_acl *acl,
-                    int deny,
-                    const char *match);
-int qemu_acl_insert(qemu_acl *acl,
-                    int deny,
-                    const char *match,
-                    int index);
-int qemu_acl_remove(qemu_acl *acl,
-                    const char *match);
+enum sdram_type { SDR = 0x4, DDR = 0x7, DDR2 = 0x8 };
+uint8_t *spd_data_generate(enum sdram_type type, ram_addr_t size, Error **errp);
 
-#endif /* QEMU_ACL_H */
+#endif
diff --git a/include/hw/i2c/smbus.h b/include/hw/i2c/smbus_master.h
index 89dfea1a08..bb13bc423c 100644
--- a/include/hw/i2c/smbus.h
+++ b/include/hw/i2c/smbus_master.h
@@ -1,8 +1,5 @@
-#ifndef QEMU_SMBUS_H
-#define QEMU_SMBUS_H
-
 /*
- * QEMU SMBus API
+ * QEMU SMBus host (master) API
  *
  * Copyright (c) 2007 Arastra, Inc.
  *
@@ -25,46 +22,10 @@
  * THE SOFTWARE.
  */
 
-#include "hw/i2c/i2c.h"
-
-#define TYPE_SMBUS_DEVICE "smbus-device"
-#define SMBUS_DEVICE(obj) \
-     OBJECT_CHECK(SMBusDevice, (obj), TYPE_SMBUS_DEVICE)
-#define SMBUS_DEVICE_CLASS(klass) \
-     OBJECT_CLASS_CHECK(SMBusDeviceClass, (klass), TYPE_SMBUS_DEVICE)
-#define SMBUS_DEVICE_GET_CLASS(obj) \
-     OBJECT_GET_CLASS(SMBusDeviceClass, (obj), TYPE_SMBUS_DEVICE)
-
-typedef struct SMBusDevice SMBusDevice;
-
-typedef struct SMBusDeviceClass
-{
-    I2CSlaveClass parent_class;
-    void (*quick_cmd)(SMBusDevice *dev, uint8_t read);
-    void (*send_byte)(SMBusDevice *dev, uint8_t val);
-    uint8_t (*receive_byte)(SMBusDevice *dev);
-    /* We can't distinguish between a word write and a block write with
-       length 1, so pass the whole data block including the length byte
-       (if present).  The device is responsible figuring out what type of
-       command  this is.  */
-    void (*write_data)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len);
-    /* Likewise we can't distinguish between different reads, or even know
-       the length of the read until the read is complete, so read data a
-       byte at a time.  The device is responsible for adding the length
-       byte on block reads.  */
-    uint8_t (*read_data)(SMBusDevice *dev, uint8_t cmd, int n);
-} SMBusDeviceClass;
+#ifndef HW_SMBUS_MASTER_H
+#define HW_SMBUS_MASTER_H
 
-struct SMBusDevice {
-    /* The SMBus protocol is implemented on top of I2C.  */
-    I2CSlave i2c;
-
-    /* Remaining fields for internal use only.  */
-    int mode;
-    int data_len;
-    uint8_t data_buf[34]; /* command + len + 32 bytes of data.  */
-    uint8_t command;
-};
+#include "hw/i2c/i2c.h"
 
 /* Master device commands.  */
 int smbus_quick_command(I2CBus *bus, uint8_t addr, int read);
@@ -91,11 +52,4 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
 int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
                       int len, bool send_len);
 
-void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf);
-void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom,
-                       const uint8_t *eeprom_spd, int size);
-
-enum sdram_type { SDR = 0x4, DDR = 0x7, DDR2 = 0x8 };
-uint8_t *spd_data_generate(enum sdram_type type, ram_addr_t size, Error **errp);
-
 #endif
diff --git a/include/hw/i2c/smbus_slave.h b/include/hw/i2c/smbus_slave.h
new file mode 100644
index 0000000000..ebe068304e
--- /dev/null
+++ b/include/hw/i2c/smbus_slave.h
@@ -0,0 +1,100 @@
+/*
+ * QEMU SMBus device (slave) API
+ *
+ * Copyright (c) 2007 Arastra, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_SMBUS_SLAVE_H
+#define HW_SMBUS_SLAVE_H
+
+#include "hw/i2c/i2c.h"
+
+#define TYPE_SMBUS_DEVICE "smbus-device"
+#define SMBUS_DEVICE(obj) \
+     OBJECT_CHECK(SMBusDevice, (obj), TYPE_SMBUS_DEVICE)
+#define SMBUS_DEVICE_CLASS(klass) \
+     OBJECT_CLASS_CHECK(SMBusDeviceClass, (klass), TYPE_SMBUS_DEVICE)
+#define SMBUS_DEVICE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(SMBusDeviceClass, (obj), TYPE_SMBUS_DEVICE)
+
+typedef struct SMBusDevice SMBusDevice;
+
+typedef struct SMBusDeviceClass
+{
+    I2CSlaveClass parent_class;
+
+    /*
+     * An operation with no data, special in SMBus.
+     * This may be NULL, quick commands are ignore in that case.
+     */
+    void (*quick_cmd)(SMBusDevice *dev, uint8_t read);
+
+    /*
+     * We can't distinguish between a word write and a block write with
+     * length 1, so pass the whole data block including the length byte
+     * (if present).  The device is responsible figuring out what type of
+     * command this is.
+     * This may be NULL if no data is written to the device.  Writes
+     * will be ignore in that case.
+     */
+    int (*write_data)(SMBusDevice *dev, uint8_t *buf, uint8_t len);
+
+    /*
+     * Likewise we can't distinguish between different reads, or even know
+     * the length of the read until the read is complete, so read data a
+     * byte at a time.  The device is responsible for adding the length
+     * byte on block reads.  This call cannot fail, it should return
+     * something, preferably 0xff if nothing is available.
+     * This may be NULL if no data is read from the device.  Reads will
+     * return 0xff in that case.
+     */
+    uint8_t (*receive_byte)(SMBusDevice *dev);
+} SMBusDeviceClass;
+
+#define SMBUS_DATA_MAX_LEN 34  /* command + len + 32 bytes of data.  */
+
+struct SMBusDevice {
+    /* The SMBus protocol is implemented on top of I2C.  */
+    I2CSlave i2c;
+
+    /* Remaining fields for internal use only.  */
+    int32_t mode;
+    int32_t data_len;
+    uint8_t data_buf[SMBUS_DATA_MAX_LEN];
+};
+
+extern const VMStateDescription vmstate_smbus_device;
+
+#define VMSTATE_SMBUS_DEVICE(_field, _state) {                       \
+    .name       = (stringify(_field)),                               \
+    .size       = sizeof(SMBusDevice),                               \
+    .vmsd       = &vmstate_smbus_device,                             \
+    .flags      = VMS_STRUCT,                                        \
+    .offset     = vmstate_offset_value(_state, _field, SMBusDevice), \
+}
+
+/*
+ * Users should call this in their .needed functions to know if the
+ * SMBus slave data needs to be transferred.
+ */
+bool smbus_vmstate_needed(SMBusDevice *dev);
+
+#endif
diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h
index 880413ddc7..8efd03132b 100644
--- a/include/hw/ide/internal.h
+++ b/include/hw/ide/internal.h
@@ -346,7 +346,6 @@ extern const char *IDE_DMA_CMD_lookup[IDE_DMA__COUNT];
 
 typedef struct IDEBufferedRequest {
     QLIST_ENTRY(IDEBufferedRequest) list;
-    struct iovec iov;
     QEMUIOVector qiov;
     QEMUIOVector *original_qiov;
     BlockCompletionFunc *original_cb;
@@ -405,7 +404,6 @@ struct IDEState {
     int atapi_dma; /* true if dma is requested for the packet cmd */
     BlockAcctCookie acct;
     BlockAIOCB *pio_aiocb;
-    struct iovec iov;
     QEMUIOVector qiov;
     QLIST_HEAD(, IDEBufferedRequest) buffered_requests;
     /* ATA DMA state */
@@ -457,7 +455,6 @@ struct IDEDMAOps {
 
 struct IDEDMA {
     const struct IDEDMAOps *ops;
-    struct iovec iov;
     QEMUIOVector qiov;
     BlockAIOCB *aiocb;
 };
diff --git a/include/hw/misc/armsse-mhu.h b/include/hw/misc/armsse-mhu.h
new file mode 100644
index 0000000000..e57eafc252
--- /dev/null
+++ b/include/hw/misc/armsse-mhu.h
@@ -0,0 +1,44 @@
+/*
+ * ARM SSE-200 Message Handling Unit (MHU)
+ *
+ * Copyright (c) 2019 Linaro Limited
+ * Written by Peter Maydell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 or
+ *  (at your option) any later version.
+ */
+
+/*
+ * This is a model of the Message Handling Unit (MHU) which is part of the
+ * Arm SSE-200 and documented in
+ * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf
+ *
+ * QEMU interface:
+ *  + sysbus MMIO region 0: the system information register bank
+ *  + sysbus IRQ 0: interrupt for CPU 0
+ *  + sysbus IRQ 1: interrupt for CPU 1
+ */
+
+#ifndef HW_MISC_SSE_MHU_H
+#define HW_MISC_SSE_MHU_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_ARMSSE_MHU "armsse-mhu"
+#define ARMSSE_MHU(obj) OBJECT_CHECK(ARMSSEMHU, (obj), TYPE_ARMSSE_MHU)
+
+typedef struct ARMSSEMHU {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    qemu_irq cpu0irq;
+    qemu_irq cpu1irq;
+
+    uint32_t cpu0intr;
+    uint32_t cpu1intr;
+} ARMSSEMHU;
+
+#endif
diff --git a/include/hw/misc/iotkit-sysctl.h b/include/hw/misc/iotkit-sysctl.h
index e36613cb5e..601c8ecc0d 100644
--- a/include/hw/misc/iotkit-sysctl.h
+++ b/include/hw/misc/iotkit-sysctl.h
@@ -17,6 +17,9 @@
  * "system control register" blocks.
  *
  * QEMU interface:
+ *  + QOM property "SYS_VERSION": value of the SYS_VERSION register of the
+ *    system information block of the SSE
+ *    (used to identify whether to provide SSE-200-only registers)
  *  + sysbus MMIO region 0: the system information register bank
  *  + sysbus MMIO region 1: the system control register bank
  */
@@ -41,9 +44,29 @@ typedef struct IoTKitSysCtl {
     uint32_t reset_syndrome;
     uint32_t reset_mask;
     uint32_t gretreg;
-    uint32_t initsvrtor0;
+    uint32_t initsvtor0;
     uint32_t cpuwait;
     uint32_t wicctrl;
+    uint32_t scsecctrl;
+    uint32_t fclk_div;
+    uint32_t sysclk_div;
+    uint32_t clock_force;
+    uint32_t initsvtor1;
+    uint32_t nmi_enable;
+    uint32_t ewctrl;
+    uint32_t pdcm_pd_sys_sense;
+    uint32_t pdcm_pd_sram0_sense;
+    uint32_t pdcm_pd_sram1_sense;
+    uint32_t pdcm_pd_sram2_sense;
+    uint32_t pdcm_pd_sram3_sense;
+
+    /* Properties */
+    uint32_t sys_version;
+    uint32_t cpuwait_rst;
+    uint32_t initsvtor0_rst;
+    uint32_t initsvtor1_rst;
+
+    bool is_sse200;
 } IoTKitSysCtl;
 
 #endif
diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h
index 51d81c4b7c..ab0e3a0a6f 100644
--- a/include/hw/pci-host/spapr.h
+++ b/include/hw/pci-host/spapr.h
@@ -113,7 +113,7 @@ static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin)
 }
 
 int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt,
-                          uint32_t nr_msis);
+                          uint32_t nr_msis, int *node_offset);
 
 void spapr_pci_rtas_init(void);
 
@@ -121,8 +121,10 @@ sPAPRPHBState *spapr_pci_find_phb(sPAPRMachineState *spapr, uint64_t buid);
 PCIDevice *spapr_pci_find_dev(sPAPRMachineState *spapr, uint64_t buid,
                               uint32_t config_addr);
 
-/* PCI release callback. */
+/* DRC callbacks */
 void spapr_phb_remove_pci_device_cb(DeviceState *dev);
+int spapr_pci_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
+                          void *fdt, int *fdt_start_offset, Error **errp);
 
 /* VFIO EEH hooks */
 #ifdef CONFIG_LINUX
@@ -163,4 +165,9 @@ static inline void spapr_phb_vfio_reset(DeviceState *qdev)
 
 void spapr_phb_dma_reset(sPAPRPHBState *sphb);
 
+static inline unsigned spapr_phb_windows_supported(sPAPRPHBState *sphb)
+{
+    return sphb->ddw_enabled ? SPAPR_PCI_DMA_MAX_WINDOWS : 1;
+}
+
 #endif /* PCI_HOST_SPAPR_H */
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index eeb33018ad..0abe27a53a 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -271,4 +271,6 @@
 
 #define PCI_VENDOR_ID_SYNOPSYS           0x16C3
 
+#define PCI_VENDOR_ID_NVIDIA             0x10de
+
 #endif
diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h
index 298ec354a8..746170f635 100644
--- a/include/hw/ppc/ppc.h
+++ b/include/hw/ppc/ppc.h
@@ -73,6 +73,7 @@ static inline void ppc40x_irq_init(PowerPCCPU *cpu) {}
 static inline void ppc6xx_irq_init(PowerPCCPU *cpu) {}
 static inline void ppc970_irq_init(PowerPCCPU *cpu) {}
 static inline void ppcPOWER7_irq_init(PowerPCCPU *cpu) {}
+static inline void ppcPOWER9_irq_init(PowerPCCPU *cpu) {}
 static inline void ppce500_irq_init(PowerPCCPU *cpu) {}
 #else
 void ppc40x_irq_init(PowerPCCPU *cpu);
@@ -80,6 +81,7 @@ void ppce500_irq_init(PowerPCCPU *cpu);
 void ppc6xx_irq_init(PowerPCCPU *cpu);
 void ppc970_irq_init(PowerPCCPU *cpu);
 void ppcPOWER7_irq_init(PowerPCCPU *cpu);
+void ppcPOWER9_irq_init(PowerPCCPU *cpu);
 #endif
 
 /* PPC machines for OpenBIOS */
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 631fc5103b..59073a7579 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -104,6 +104,7 @@ struct sPAPRMachineClass {
 
     /*< public >*/
     bool dr_lmb_enabled;       /* enable dynamic-reconfig/hotplug of LMBs */
+    bool dr_phb_enabled;       /* enable dynamic-reconfig/hotplug of PHBs */
     bool update_dt_enabled;    /* enable KVMPPC_H_UPDATE_DT */
     bool use_ohci_by_default;  /* use USB-OHCI instead of XHCI */
     bool pre_2_10_has_unused_icps;
@@ -177,6 +178,8 @@ struct sPAPRMachineState {
 
     /*< public >*/
     char *kvm_type;
+    char *host_model;
+    char *host_serial;
 
     int32_t irq_map_nr;
     unsigned long *irq_map;
@@ -762,9 +765,16 @@ void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift,
 void spapr_clear_pending_events(sPAPRMachineState *spapr);
 int spapr_max_server_number(sPAPRMachineState *spapr);
 
-/* CPU and LMB DRC release callbacks. */
+/* DRC callbacks. */
 void spapr_core_release(DeviceState *dev);
+int spapr_core_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
+                           void *fdt, int *fdt_start_offset, Error **errp);
 void spapr_lmb_release(DeviceState *dev);
+int spapr_lmb_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
+                          void *fdt, int *fdt_start_offset, Error **errp);
+void spapr_phb_release(DeviceState *dev);
+int spapr_phb_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
+                          void *fdt, int *fdt_start_offset, Error **errp);
 
 void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns);
 int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset);
@@ -839,4 +849,5 @@ void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize,
 #define SPAPR_OV5_XIVE_EXPLOIT  0x40
 #define SPAPR_OV5_XIVE_BOTH     0x80 /* Only to advertise on the platform */
 
+void spapr_set_all_lpcrs(target_ulong value, target_ulong mask);
 #endif /* HW_SPAPR_H */
diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h
index f6ff32e7e2..46b0f6216d 100644
--- a/include/hw/ppc/spapr_drc.h
+++ b/include/hw/ppc/spapr_drc.h
@@ -18,6 +18,7 @@
 #include "qom/object.h"
 #include "sysemu/sysemu.h"
 #include "hw/qdev.h"
+#include "qapi/error.h"
 
 #define TYPE_SPAPR_DR_CONNECTOR "spapr-dr-connector"
 #define SPAPR_DR_CONNECTOR_GET_CLASS(obj) \
@@ -70,6 +71,14 @@
 #define SPAPR_DRC_LMB(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \
                                         TYPE_SPAPR_DRC_LMB)
 
+#define TYPE_SPAPR_DRC_PHB "spapr-drc-phb"
+#define SPAPR_DRC_PHB_GET_CLASS(obj) \
+        OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DRC_PHB)
+#define SPAPR_DRC_PHB_CLASS(klass) \
+        OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, TYPE_SPAPR_DRC_PHB)
+#define SPAPR_DRC_PHB(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \
+                                        TYPE_SPAPR_DRC_PHB)
+
 /*
  * Various hotplug types managed by sPAPRDRConnector
  *
@@ -213,6 +222,8 @@ typedef struct sPAPRDRConnector {
     int fdt_start_offset;
 } sPAPRDRConnector;
 
+struct sPAPRMachineState;
+
 typedef struct sPAPRDRConnectorClass {
     /*< private >*/
     DeviceClass parent;
@@ -228,6 +239,9 @@ typedef struct sPAPRDRConnectorClass {
     uint32_t (*isolate)(sPAPRDRConnector *drc);
     uint32_t (*unisolate)(sPAPRDRConnector *drc);
     void (*release)(DeviceState *dev);
+
+    int (*dt_populate)(sPAPRDRConnector *drc, struct sPAPRMachineState *spapr,
+                       void *fdt, int *fdt_start_offset, Error **errp);
 } sPAPRDRConnectorClass;
 
 typedef struct sPAPRDRCPhysical {
@@ -255,8 +269,7 @@ sPAPRDRConnector *spapr_drc_by_id(const char *type, uint32_t id);
 int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
                           uint32_t drc_type_mask);
 
-void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
-                      int fdt_start_offset, Error **errp);
+void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, Error **errp);
 void spapr_drc_detach(sPAPRDRConnector *drc);
 bool spapr_drc_needed(void *opaque);
 
diff --git a/include/hw/ppc/spapr_irq.h b/include/hw/ppc/spapr_irq.h
index 488511c3d8..ec1ee64fa6 100644
--- a/include/hw/ppc/spapr_irq.h
+++ b/include/hw/ppc/spapr_irq.h
@@ -47,6 +47,7 @@ typedef struct sPAPRIrq {
     int (*post_load)(sPAPRMachineState *spapr, int version_id);
     void (*reset)(sPAPRMachineState *spapr, Error **errp);
     void (*set_irq)(void *opaque, int srcno, int val);
+    const char *(*get_nodename)(sPAPRMachineState *spapr);
 } sPAPRIrq;
 
 extern sPAPRIrq spapr_irq_xics;
@@ -60,6 +61,7 @@ void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num);
 qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq);
 int spapr_irq_post_load(sPAPRMachineState *spapr, int version_id);
 void spapr_irq_reset(sPAPRMachineState *spapr, Error **errp);
+int spapr_irq_get_phandle(sPAPRMachineState *spapr, void *fdt, Error **errp);
 
 /*
  * XICS legacy routines
diff --git a/include/hw/ppc/spapr_xive.h b/include/hw/ppc/spapr_xive.h
index 9bec9192e4..2d31f24e3b 100644
--- a/include/hw/ppc/spapr_xive.h
+++ b/include/hw/ppc/spapr_xive.h
@@ -26,6 +26,9 @@ typedef struct sPAPRXive {
     XiveENDSource end_source;
     hwaddr        end_base;
 
+    /* DT */
+    gchar *nodename;
+
     /* Routing table */
     XiveEAS       *eat;
     uint32_t      nr_irqs;
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index d36bbe11ee..eb65ad7e43 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -195,6 +195,7 @@ void icp_synchronize_state(ICPState *icp);
 void icp_kvm_realize(DeviceState *dev, Error **errp);
 
 void ics_get_kvm_state(ICSState *ics);
+int ics_set_kvm_state_one(ICSState *ics, int srcno);
 int ics_set_kvm_state(ICSState *ics);
 void ics_synchronize_state(ICSState *ics);
 void ics_kvm_set_irq(ICSState *ics, int srcno, int val);
diff --git a/include/hw/ppc/xics_spapr.h b/include/hw/ppc/xics_spapr.h
index b1ab27d022..b8d924baf4 100644
--- a/include/hw/ppc/xics_spapr.h
+++ b/include/hw/ppc/xics_spapr.h
@@ -29,6 +29,8 @@
 
 #include "hw/ppc/spapr.h"
 
+#define XICS_NODENAME "interrupt-controller"
+
 void spapr_dt_xics(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
                    uint32_t phandle);
 int xics_kvm_init(sPAPRMachineState *spapr, Error **errp);
diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h
index e0df3528c8..99dcd6d105 100644
--- a/include/hw/virtio/virtio-balloon.h
+++ b/include/hw/virtio/virtio-balloon.h
@@ -30,6 +30,8 @@ typedef struct virtio_balloon_stat_modern {
        uint64_t val;
 } VirtIOBalloonStatModern;
 
+typedef struct PartiallyBalloonedPage PartiallyBalloonedPage;
+
 typedef struct VirtIOBalloon {
     VirtIODevice parent_obj;
     VirtQueue *ivq, *dvq, *svq;
@@ -42,6 +44,7 @@ typedef struct VirtIOBalloon {
     int64_t stats_last_update;
     int64_t stats_poll_interval;
     uint32_t host_features;
+    PartiallyBalloonedPage *pbp;
 } VirtIOBalloon;
 
 #endif
diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h
index 5117431d96..cddcfbebe9 100644
--- a/include/hw/virtio/virtio-blk.h
+++ b/include/hw/virtio/virtio-blk.h
@@ -35,11 +35,11 @@ struct VirtIOBlkConf
     BlockConf conf;
     IOThread *iothread;
     char *serial;
-    uint32_t scsi;
-    uint32_t config_wce;
     uint32_t request_merging;
     uint16_t num_queues;
     uint16_t queue_size;
+    uint32_t max_discard_sectors;
+    uint32_t max_write_zeroes_sectors;
 };
 
 struct VirtIOBlockDataPlane;
@@ -57,6 +57,8 @@ typedef struct VirtIOBlock {
     bool dataplane_disabled;
     bool dataplane_started;
     struct VirtIOBlockDataPlane *dataplane;
+    uint64_t host_features;
+    size_t config_size;
 } VirtIOBlock;
 
 typedef struct VirtIOBlockReq {
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index c8c599f1b9..98504f9075 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -61,12 +61,15 @@ struct virtio_gpu_requested_state {
 enum virtio_gpu_conf_flags {
     VIRTIO_GPU_FLAG_VIRGL_ENABLED = 1,
     VIRTIO_GPU_FLAG_STATS_ENABLED,
+    VIRTIO_GPU_FLAG_EDID_ENABLED,
 };
 
 #define virtio_gpu_virgl_enabled(_cfg) \
     (_cfg.flags & (1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED))
 #define virtio_gpu_stats_enabled(_cfg) \
     (_cfg.flags & (1 << VIRTIO_GPU_FLAG_STATS_ENABLED))
+#define virtio_gpu_edid_enabled(_cfg) \
+    (_cfg.flags & (1 << VIRTIO_GPU_FLAG_EDID_ENABLED))
 
 struct virtio_gpu_conf {
     uint64_t max_hostmem;
@@ -81,7 +84,6 @@ struct virtio_gpu_ctrl_command {
     VirtQueue *vq;
     struct virtio_gpu_ctrl_hdr cmd_hdr;
     uint32_t error;
-    bool waiting;
     bool finished;
     QTAILQ_ENTRY(virtio_gpu_ctrl_command) next;
 };
@@ -96,9 +98,6 @@ typedef struct VirtIOGPU {
 
     int enable;
 
-    int config_size;
-    DeviceState *qdev;
-
     QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist;
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq;
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq;
@@ -159,6 +158,8 @@ void virtio_gpu_ctrl_response_nodata(VirtIOGPU *g,
                                      enum virtio_gpu_ctrl_type type);
 void virtio_gpu_get_display_info(VirtIOGPU *g,
                                  struct virtio_gpu_ctrl_command *cmd);
+void virtio_gpu_get_edid(VirtIOGPU *g,
+                         struct virtio_gpu_ctrl_command *cmd);
 int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
                                   struct virtio_gpu_resource_attach_backing *ab,
                                   struct virtio_gpu_ctrl_command *cmd,
@@ -172,7 +173,6 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
                                   struct virtio_gpu_ctrl_command *cmd);
 void virtio_gpu_virgl_fence_poll(VirtIOGPU *g);
 void virtio_gpu_virgl_reset(VirtIOGPU *g);
-void virtio_gpu_gl_block(void *opaque, bool block);
 int virtio_gpu_virgl_init(VirtIOGPU *g);
 int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g);
 #endif
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index 9c1fa07d6d..ce9516236a 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -37,6 +37,21 @@ static inline hwaddr vring_align(hwaddr addr,
     return QEMU_ALIGN_UP(addr, align);
 }
 
+/*
+ * Calculate the number of bytes up to and including the given 'field' of
+ * 'container'.
+ */
+#define virtio_endof(container, field) \
+    (offsetof(container, field) + sizeof_field(container, field))
+
+typedef struct VirtIOFeature {
+    uint64_t flags;
+    size_t end;
+} VirtIOFeature;
+
+size_t virtio_feature_get_config_size(VirtIOFeature *features,
+                                      uint64_t host_features);
+
 typedef struct VirtQueue VirtQueue;
 
 #define VIRTQUEUE_MAX_SIZE 1024
diff --git a/include/io/channel.h b/include/io/channel.h
index da2f138200..59460cb1ec 100644
--- a/include/io/channel.h
+++ b/include/io/channel.h
@@ -739,10 +739,13 @@ void qio_channel_detach_aio_context(QIOChannel *ioc);
  * addition, no two coroutine can be waiting on the same condition
  * and channel at the same time.
  *
- * This must only be called from coroutine context
+ * This must only be called from coroutine context. It is safe to
+ * reenter the coroutine externally while it is waiting; in this
+ * case the function will return even if @condition is not yet
+ * available.
  */
-void qio_channel_yield(QIOChannel *ioc,
-                       GIOCondition condition);
+void coroutine_fn qio_channel_yield(QIOChannel *ioc,
+                                    GIOCondition condition);
 
 /**
  * qio_channel_wait:
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 067b126cf1..a668ec75b8 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -851,6 +851,9 @@ extern const VMStateInfo vmstate_info_qtailq;
 #define VMSTATE_INT32_POSITIVE_LE(_f, _s)                             \
     VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t)
 
+#define VMSTATE_BOOL_TEST(_f, _s, _t)                               \
+    VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_bool, bool)
+
 #define VMSTATE_INT8_TEST(_f, _s, _t)                               \
     VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int8, int8_t)
 
diff --git a/include/qemu/filemonitor.h b/include/qemu/filemonitor.h
new file mode 100644
index 0000000000..cd031832ed
--- /dev/null
+++ b/include/qemu/filemonitor.h
@@ -0,0 +1,128 @@
+/*
+ * QEMU file monitor helper
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QEMU_FILE_MONITOR_H
+#define QEMU_FILE_MONITOR_H
+
+#include "qemu-common.h"
+
+
+typedef struct QFileMonitor QFileMonitor;
+
+typedef enum {
+    /* File has been created in a dir */
+    QFILE_MONITOR_EVENT_CREATED,
+    /* File has been modified in a dir */
+    QFILE_MONITOR_EVENT_MODIFIED,
+    /* File has been deleted in a dir */
+    QFILE_MONITOR_EVENT_DELETED,
+    /* File has attributes changed */
+    QFILE_MONITOR_EVENT_ATTRIBUTES,
+    /* Dir is no longer being monitored (due to deletion) */
+    QFILE_MONITOR_EVENT_IGNORED,
+} QFileMonitorEvent;
+
+
+/**
+ * QFileMonitorHandler:
+ * @id: id from qemu_file_monitor_add_watch()
+ * @event: the file change that occurred
+ * @filename: the name of the file affected
+ * @opaque: opaque data provided to qemu_file_monitor_add_watch()
+ *
+ * Invoked whenever a file changes. If @event is
+ * QFILE_MONITOR_EVENT_IGNORED, @filename will be
+ * empty.
+ *
+ */
+typedef void (*QFileMonitorHandler)(int id,
+                                    QFileMonitorEvent event,
+                                    const char *filename,
+                                    void *opaque);
+
+/**
+ * qemu_file_monitor_new:
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * Create a handle for a file monitoring object.
+ *
+ * This object does locking internally to enable it to be
+ * safe to use from multiple threads
+ *
+ * If the platform does not support file monitoring, an
+ * error will be reported. Likewise if file monitoring
+ * is supported, but cannot be initialized
+ *
+ * Currently this is implemented on Linux platforms with
+ * the inotify subsystem.
+ *
+ * Returns: the new monitoring object, or NULL on error
+ */
+QFileMonitor *qemu_file_monitor_new(Error **errp);
+
+/**
+ * qemu_file_monitor_free:
+ * @mon: the file monitor context
+ *
+ * Free resources associated with the file monitor,
+ * including any currently registered watches.
+ */
+void qemu_file_monitor_free(QFileMonitor *mon);
+
+/**
+ * qemu_file_monitor_add_watch:
+ * @mon: the file monitor context
+ * @dirpath: the directory whose contents to watch
+ * @filename: optional filename to filter on
+ * @cb: the function to invoke when @dirpath has changes
+ * @opaque: data to pass to @cb
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * Register to receive notifications of changes
+ * in the directory @dirpath. All files in the
+ * directory will be monitored. If the caller is
+ * only interested in one specific file, @filename
+ * can be used to filter events.
+ *
+ * Returns: a positive integer watch ID, or -1 on error
+ */
+int qemu_file_monitor_add_watch(QFileMonitor *mon,
+                                const char *dirpath,
+                                const char *filename,
+                                QFileMonitorHandler cb,
+                                void *opaque,
+                                Error **errp);
+
+/**
+ * qemu_file_monitor_remove_watch:
+ * @mon: the file monitor context
+ * @dirpath: the directory whose contents to unwatch
+ * @id: id of the watch to remove
+ *
+ * Removes the file monitoring watch @id, associated
+ * with the directory @dirpath. This must never be
+ * called from a QFileMonitorHandler callback, or a
+ * deadlock will result.
+ */
+void qemu_file_monitor_remove_watch(QFileMonitor *mon,
+                                    const char *dirpath,
+                                    int id);
+
+#endif /* QEMU_FILE_MONITOR_H */
diff --git a/include/qemu/iov.h b/include/qemu/iov.h
index 5f433c7768..48b45987b7 100644
--- a/include/qemu/iov.h
+++ b/include/qemu/iov.h
@@ -133,10 +133,70 @@ size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
 typedef struct QEMUIOVector {
     struct iovec *iov;
     int niov;
-    int nalloc;
-    size_t size;
+
+    /*
+     * For external @iov (qemu_iovec_init_external()) or allocated @iov
+     * (qemu_iovec_init()), @size is the cumulative size of iovecs and
+     * @local_iov is invalid and unused.
+     *
+     * For embedded @iov (QEMU_IOVEC_INIT_BUF() or qemu_iovec_init_buf()),
+     * @iov is equal to &@local_iov, and @size is valid, as it has same
+     * offset and type as @local_iov.iov_len, which is guaranteed by
+     * static assertion below.
+     *
+     * @nalloc is always valid and is -1 both for embedded and external
+     * cases. It is included in the union only to ensure the padding prior
+     * to the @size field will not result in a 0-length array.
+     */
+    union {
+        struct {
+            int nalloc;
+            struct iovec local_iov;
+        };
+        struct {
+            char __pad[sizeof(int) + offsetof(struct iovec, iov_len)];
+            size_t size;
+        };
+    };
 } QEMUIOVector;
 
+QEMU_BUILD_BUG_ON(offsetof(QEMUIOVector, size) !=
+                  offsetof(QEMUIOVector, local_iov.iov_len));
+
+#define QEMU_IOVEC_INIT_BUF(self, buf, len)              \
+{                                                        \
+    .iov = &(self).local_iov,                            \
+    .niov = 1,                                           \
+    .nalloc = -1,                                        \
+    .local_iov = {                                       \
+        .iov_base = (void *)(buf), /* cast away const */ \
+        .iov_len = (len),                                \
+    },                                                   \
+}
+
+/*
+ * qemu_iovec_init_buf
+ *
+ * Initialize embedded QEMUIOVector.
+ *
+ * Note: "const" is used over @buf pointer to make it simple to pass
+ * const pointers, appearing in read functions. Then this "const" is
+ * cast away by QEMU_IOVEC_INIT_BUF().
+ */
+static inline void qemu_iovec_init_buf(QEMUIOVector *qiov,
+                                       const void *buf, size_t len)
+{
+    *qiov = (QEMUIOVector) QEMU_IOVEC_INIT_BUF(*qiov, buf, len);
+}
+
+static inline void *qemu_iovec_buf(QEMUIOVector *qiov)
+{
+    /* Only supports embedded iov */
+    assert(qiov->nalloc == -1 && qiov->iov == &qiov->local_iov);
+
+    return qiov->local_iov.iov_base;
+}
+
 void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint);
 void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov);
 void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 832a4bf168..e2066eb06b 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -156,6 +156,8 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes);
 int blk_co_flush(BlockBackend *blk);
 int blk_flush(BlockBackend *blk);
 int blk_commit_all(void);
+void blk_inc_in_flight(BlockBackend *blk);
+void blk_dec_in_flight(BlockBackend *blk);
 void blk_drain(BlockBackend *blk);
 void blk_drain_all(void);
 void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error,
diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index 87a84a59d4..53c3612c32 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -179,3 +179,7 @@ void qemu_spice_wakeup(SimpleSpiceDisplay *ssd);
 void qemu_spice_display_start(void);
 void qemu_spice_display_stop(void);
 int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd);
+
+bool qemu_spice_fill_device_address(QemuConsole *con,
+                                    char *device_address,
+                                    size_t size);
diff --git a/io/channel.c b/io/channel.c
index 8dd0684f5d..2a26c2a2c0 100644
--- a/io/channel.c
+++ b/io/channel.c
@@ -400,15 +400,14 @@ off_t qio_channel_io_seek(QIOChannel *ioc,
 }
 
 
-static void qio_channel_set_aio_fd_handlers(QIOChannel *ioc);
-
 static void qio_channel_restart_read(void *opaque)
 {
     QIOChannel *ioc = opaque;
     Coroutine *co = ioc->read_coroutine;
 
-    ioc->read_coroutine = NULL;
-    qio_channel_set_aio_fd_handlers(ioc);
+    /* Assert that aio_co_wake() reenters the coroutine directly */
+    assert(qemu_get_current_aio_context() ==
+           qemu_coroutine_get_aio_context(co));
     aio_co_wake(co);
 }
 
@@ -417,8 +416,9 @@ static void qio_channel_restart_write(void *opaque)
     QIOChannel *ioc = opaque;
     Coroutine *co = ioc->write_coroutine;
 
-    ioc->write_coroutine = NULL;
-    qio_channel_set_aio_fd_handlers(ioc);
+    /* Assert that aio_co_wake() reenters the coroutine directly */
+    assert(qemu_get_current_aio_context() ==
+           qemu_coroutine_get_aio_context(co));
     aio_co_wake(co);
 }
 
@@ -469,6 +469,16 @@ void coroutine_fn qio_channel_yield(QIOChannel *ioc,
     }
     qio_channel_set_aio_fd_handlers(ioc);
     qemu_coroutine_yield();
+
+    /* Allow interrupting the operation by reentering the coroutine other than
+     * through the aio_fd_handlers. */
+    if (condition == G_IO_IN && ioc->read_coroutine) {
+        ioc->read_coroutine = NULL;
+        qio_channel_set_aio_fd_handlers(ioc);
+    } else if (condition == G_IO_OUT && ioc->write_coroutine) {
+        ioc->write_coroutine = NULL;
+        qio_channel_set_aio_fd_handlers(ioc);
+    }
 }
 
 
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 3a50d587ff..b9f7cbbdc1 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -602,6 +602,8 @@ static uint32_t get_elf_hwcap(void)
     GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA);
     GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE);
     GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG);
+    GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM);
+    GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT);
 
 #undef GET_FEATURE_ID
 
diff --git a/migration/block.c b/migration/block.c
index 0e24e18d13..83c633fb3f 100644
--- a/migration/block.c
+++ b/migration/block.c
@@ -83,7 +83,6 @@ typedef struct BlkMigBlock {
     BlkMigDevState *bmds;
     int64_t sector;
     int nr_sectors;
-    struct iovec iov;
     QEMUIOVector qiov;
     BlockAIOCB *aiocb;
 
@@ -314,9 +313,7 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
     blk->sector = cur_sector;
     blk->nr_sectors = nr_sectors;
 
-    blk->iov.iov_base = blk->buf;
-    blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
-    qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
+    qemu_iovec_init_buf(&blk->qiov, blk->buf, nr_sectors * BDRV_SECTOR_SIZE);
 
     blk_mig_lock();
     block_mig_state.submitted++;
@@ -556,9 +553,8 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
             blk->nr_sectors = nr_sectors;
 
             if (is_async) {
-                blk->iov.iov_base = blk->buf;
-                blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
-                qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
+                qemu_iovec_init_buf(&blk->qiov, blk->buf,
+                                    nr_sectors * BDRV_SECTOR_SIZE);
 
                 blk->aiocb = blk_aio_preadv(bmds->blk,
                                             sector * BDRV_SECTOR_SIZE,
diff --git a/monitor.c b/monitor.c
index 33ccbf3957..defa129319 100644
--- a/monitor.c
+++ b/monitor.c
@@ -51,7 +51,8 @@
 #include "sysemu/balloon.h"
 #include "qemu/timer.h"
 #include "sysemu/hw_accel.h"
-#include "qemu/acl.h"
+#include "authz/list.h"
+#include "qapi/util.h"
 #include "sysemu/tpm.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qerror.h"
@@ -2016,93 +2017,148 @@ static void hmp_wavcapture(Monitor *mon, const QDict *qdict)
     QLIST_INSERT_HEAD (&capture_head, s, entries);
 }
 
-static qemu_acl *find_acl(Monitor *mon, const char *name)
+static QAuthZList *find_auth(Monitor *mon, const char *name)
 {
-    qemu_acl *acl = qemu_acl_find(name);
+    Object *obj;
+    Object *container;
 
-    if (!acl) {
+    container = object_get_objects_root();
+    obj = object_resolve_path_component(container, name);
+    if (!obj) {
         monitor_printf(mon, "acl: unknown list '%s'\n", name);
+        return NULL;
     }
-    return acl;
+
+    return QAUTHZ_LIST(obj);
 }
 
 static void hmp_acl_show(Monitor *mon, const QDict *qdict)
 {
     const char *aclname = qdict_get_str(qdict, "aclname");
-    qemu_acl *acl = find_acl(mon, aclname);
-    qemu_acl_entry *entry;
-    int i = 0;
-
-    if (acl) {
-        monitor_printf(mon, "policy: %s\n",
-                       acl->defaultDeny ? "deny" : "allow");
-        QTAILQ_FOREACH(entry, &acl->entries, next) {
-            i++;
-            monitor_printf(mon, "%d: %s %s\n", i,
-                           entry->deny ? "deny" : "allow", entry->match);
-        }
+    QAuthZList *auth = find_auth(mon, aclname);
+    QAuthZListRuleList *rules;
+    size_t i = 0;
+
+    if (!auth) {
+        return;
+    }
+
+    monitor_printf(mon, "policy: %s\n",
+                   QAuthZListPolicy_str(auth->policy));
+
+    rules = auth->rules;
+    while (rules) {
+        QAuthZListRule *rule = rules->value;
+        i++;
+        monitor_printf(mon, "%zu: %s %s\n", i,
+                       QAuthZListPolicy_str(rule->policy),
+                       rule->match);
+        rules = rules->next;
     }
 }
 
 static void hmp_acl_reset(Monitor *mon, const QDict *qdict)
 {
     const char *aclname = qdict_get_str(qdict, "aclname");
-    qemu_acl *acl = find_acl(mon, aclname);
+    QAuthZList *auth = find_auth(mon, aclname);
 
-    if (acl) {
-        qemu_acl_reset(acl);
-        monitor_printf(mon, "acl: removed all rules\n");
+    if (!auth) {
+        return;
     }
+
+    auth->policy = QAUTHZ_LIST_POLICY_DENY;
+    qapi_free_QAuthZListRuleList(auth->rules);
+    auth->rules = NULL;
+    monitor_printf(mon, "acl: removed all rules\n");
 }
 
 static void hmp_acl_policy(Monitor *mon, const QDict *qdict)
 {
     const char *aclname = qdict_get_str(qdict, "aclname");
     const char *policy = qdict_get_str(qdict, "policy");
-    qemu_acl *acl = find_acl(mon, aclname);
+    QAuthZList *auth = find_auth(mon, aclname);
+    int val;
+    Error *err = NULL;
 
-    if (acl) {
-        if (strcmp(policy, "allow") == 0) {
-            acl->defaultDeny = 0;
+    if (!auth) {
+        return;
+    }
+
+    val = qapi_enum_parse(&QAuthZListPolicy_lookup,
+                          policy,
+                          QAUTHZ_LIST_POLICY_DENY,
+                          &err);
+    if (err) {
+        error_free(err);
+        monitor_printf(mon, "acl: unknown policy '%s', "
+                       "expected 'deny' or 'allow'\n", policy);
+    } else {
+        auth->policy = val;
+        if (auth->policy == QAUTHZ_LIST_POLICY_ALLOW) {
             monitor_printf(mon, "acl: policy set to 'allow'\n");
-        } else if (strcmp(policy, "deny") == 0) {
-            acl->defaultDeny = 1;
-            monitor_printf(mon, "acl: policy set to 'deny'\n");
         } else {
-            monitor_printf(mon, "acl: unknown policy '%s', "
-                           "expected 'deny' or 'allow'\n", policy);
+            monitor_printf(mon, "acl: policy set to 'deny'\n");
         }
     }
 }
 
+static QAuthZListFormat hmp_acl_get_format(const char *match)
+{
+    if (strchr(match, '*')) {
+        return QAUTHZ_LIST_FORMAT_GLOB;
+    } else {
+        return QAUTHZ_LIST_FORMAT_EXACT;
+    }
+}
+
 static void hmp_acl_add(Monitor *mon, const QDict *qdict)
 {
     const char *aclname = qdict_get_str(qdict, "aclname");
     const char *match = qdict_get_str(qdict, "match");
-    const char *policy = qdict_get_str(qdict, "policy");
+    const char *policystr = qdict_get_str(qdict, "policy");
     int has_index = qdict_haskey(qdict, "index");
     int index = qdict_get_try_int(qdict, "index", -1);
-    qemu_acl *acl = find_acl(mon, aclname);
-    int deny, ret;
-
-    if (acl) {
-        if (strcmp(policy, "allow") == 0) {
-            deny = 0;
-        } else if (strcmp(policy, "deny") == 0) {
-            deny = 1;
-        } else {
-            monitor_printf(mon, "acl: unknown policy '%s', "
-                           "expected 'deny' or 'allow'\n", policy);
-            return;
-        }
-        if (has_index)
-            ret = qemu_acl_insert(acl, deny, match, index);
-        else
-            ret = qemu_acl_append(acl, deny, match);
-        if (ret < 0)
-            monitor_printf(mon, "acl: unable to add acl entry\n");
-        else
-            monitor_printf(mon, "acl: added rule at position %d\n", ret);
+    QAuthZList *auth = find_auth(mon, aclname);
+    Error *err = NULL;
+    QAuthZListPolicy policy;
+    QAuthZListFormat format;
+    size_t i = 0;
+
+    if (!auth) {
+        return;
+    }
+
+    policy = qapi_enum_parse(&QAuthZListPolicy_lookup,
+                             policystr,
+                             QAUTHZ_LIST_POLICY_DENY,
+                             &err);
+    if (err) {
+        error_free(err);
+        monitor_printf(mon, "acl: unknown policy '%s', "
+                       "expected 'deny' or 'allow'\n", policystr);
+        return;
+    }
+
+    format = hmp_acl_get_format(match);
+
+    if (has_index && index == 0) {
+        monitor_printf(mon, "acl: unable to add acl entry\n");
+        return;
+    }
+
+    if (has_index) {
+        i = qauthz_list_insert_rule(auth, match, policy,
+                                    format, index - 1, &err);
+    } else {
+        i = qauthz_list_append_rule(auth, match, policy,
+                                    format, &err);
+    }
+    if (err) {
+        monitor_printf(mon, "acl: unable to add rule: %s",
+                       error_get_pretty(err));
+        error_free(err);
+    } else {
+        monitor_printf(mon, "acl: added rule at position %zu\n", i + 1);
     }
 }
 
@@ -2110,15 +2166,18 @@ static void hmp_acl_remove(Monitor *mon, const QDict *qdict)
 {
     const char *aclname = qdict_get_str(qdict, "aclname");
     const char *match = qdict_get_str(qdict, "match");
-    qemu_acl *acl = find_acl(mon, aclname);
-    int ret;
+    QAuthZList *auth = find_auth(mon, aclname);
+    ssize_t i = 0;
 
-    if (acl) {
-        ret = qemu_acl_remove(acl, match);
-        if (ret < 0)
-            monitor_printf(mon, "acl: no matching acl entry\n");
-        else
-            monitor_printf(mon, "acl: removed rule at position %d\n", ret);
+    if (!auth) {
+        return;
+    }
+
+    i = qauthz_list_delete_rule(auth, match);
+    if (i >= 0) {
+        monitor_printf(mon, "acl: removed rule at position %zu\n", i + 1);
+    } else {
+        monitor_printf(mon, "acl: no matching acl entry\n");
     }
 }
 
diff --git a/nbd/client.c b/nbd/client.c
index 10a52ad7d0..de7da48246 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -1387,17 +1387,65 @@ static int nbd_receive_structured_reply_chunk(QIOChannel *ioc,
     return 0;
 }
 
+/* nbd_read_eof
+ * Tries to read @size bytes from @ioc.
+ * Returns 1 on success
+ *         0 on eof, when no data was read (errp is not set)
+ *         negative errno on failure (errp is set)
+ */
+static inline int coroutine_fn
+nbd_read_eof(BlockDriverState *bs, QIOChannel *ioc, void *buffer, size_t size,
+             Error **errp)
+{
+    bool partial = false;
+
+    assert(size);
+    while (size > 0) {
+        struct iovec iov = { .iov_base = buffer, .iov_len = size };
+        ssize_t len;
+
+        len = qio_channel_readv(ioc, &iov, 1, errp);
+        if (len == QIO_CHANNEL_ERR_BLOCK) {
+            bdrv_dec_in_flight(bs);
+            qio_channel_yield(ioc, G_IO_IN);
+            bdrv_inc_in_flight(bs);
+            continue;
+        } else if (len < 0) {
+            return -EIO;
+        } else if (len == 0) {
+            if (partial) {
+                error_setg(errp,
+                           "Unexpected end-of-file before all bytes were read");
+                return -EIO;
+            } else {
+                return 0;
+            }
+        }
+
+        partial = true;
+        size -= len;
+        buffer = (uint8_t*) buffer + len;
+    }
+    return 1;
+}
+
 /* nbd_receive_reply
+ *
+ * Decreases bs->in_flight while waiting for a new reply. This yield is where
+ * we wait indefinitely and the coroutine must be able to be safely reentered
+ * for nbd_client_attach_aio_context().
+ *
  * Returns 1 on success
  *         0 on eof, when no data was read (errp is not set)
  *         negative errno on failure (errp is set)
  */
-int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp)
+int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc,
+                                   NBDReply *reply, Error **errp)
 {
     int ret;
     const char *type;
 
-    ret = nbd_read_eof(ioc, &reply->magic, sizeof(reply->magic), errp);
+    ret = nbd_read_eof(bs, ioc, &reply->magic, sizeof(reply->magic), errp);
     if (ret <= 0) {
         return ret;
     }
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index 82aa221227..049f83df77 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -64,25 +64,6 @@
 #define NBD_SET_TIMEOUT             _IO(0xab, 9)
 #define NBD_SET_FLAGS               _IO(0xab, 10)
 
-/* nbd_read_eof
- * Tries to read @size bytes from @ioc.
- * Returns 1 on success
- *         0 on eof, when no data was read (errp is not set)
- *         negative errno on failure (errp is set)
- */
-static inline int nbd_read_eof(QIOChannel *ioc, void *buffer, size_t size,
-                               Error **errp)
-{
-    int ret;
-
-    assert(size);
-    ret = qio_channel_read_all_eof(ioc, buffer, size, errp);
-    if (ret < 0) {
-        ret = -EIO;
-    }
-    return ret;
-}
-
 /* nbd_write
  * Writes @size bytes to @ioc. Returns 0 on success.
  */
diff --git a/net/Makefile.objs b/net/Makefile.objs
index b2bf88a0ef..df2b409066 100644
--- a/net/Makefile.objs
+++ b/net/Makefile.objs
@@ -3,7 +3,9 @@ common-obj-y += socket.o
 common-obj-y += dump.o
 common-obj-y += eth.o
 common-obj-$(CONFIG_L2TPV3) += l2tpv3.o
-common-obj-$(CONFIG_POSIX) += vhost-user.o
+common-obj-$(call land,$(CONFIG_VIRTIO_NET),$(CONFIG_VHOST_NET_USER)) += vhost-user.o
+common-obj-$(call land,$(call lnot,$(CONFIG_VIRTIO_NET)),$(CONFIG_VHOST_NET_USER)) += vhost-user-stub.o
+common-obj-$(CONFIG_ALL) += vhost-user-stub.o
 common-obj-$(CONFIG_SLIRP) += slirp.o
 common-obj-$(CONFIG_VDE) += vde.o
 common-obj-$(CONFIG_NETMAP) += netmap.o
diff --git a/net/net.c b/net/net.c
index 5dcff7fe2a..f3a3c5444c 100644
--- a/net/net.c
+++ b/net/net.c
@@ -961,7 +961,7 @@ static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])(
         [NET_CLIENT_DRIVER_BRIDGE]    = net_init_bridge,
 #endif
         [NET_CLIENT_DRIVER_HUBPORT]   = net_init_hubport,
-#ifdef CONFIG_VHOST_NET_USED
+#ifdef CONFIG_VHOST_NET_USER
         [NET_CLIENT_DRIVER_VHOST_USER] = net_init_vhost_user,
 #endif
 #ifdef CONFIG_L2TPV3
diff --git a/net/vhost-user-stub.c b/net/vhost-user-stub.c
new file mode 100644
index 0000000000..52ab4e13f1
--- /dev/null
+++ b/net/vhost-user-stub.c
@@ -0,0 +1,23 @@
+/*
+ * vhost-user-stub.c
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "clients.h"
+#include "net/vhost_net.h"
+#include "net/vhost-user.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+
+int net_init_vhost_user(const Netdev *netdev, const char *name,
+                        NetClientState *peer, Error **errp)
+{
+    error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
+    return -1;
+}
diff --git a/net/vhost-user.c b/net/vhost-user.c
index a39f9c9974..cd9659df87 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -172,6 +172,17 @@ static void net_vhost_user_cleanup(NetClientState *nc)
     qemu_purge_queued_packets(nc);
 }
 
+static int vhost_user_set_vnet_endianness(NetClientState *nc,
+                                          bool enable)
+{
+    /* Nothing to do.  If the server supports
+     * VHOST_USER_PROTOCOL_F_CROSS_ENDIAN, it will get the
+     * vnet header endianness from there.  If it doesn't, negotiation
+     * fails.
+     */
+    return 0;
+}
+
 static bool vhost_user_has_vnet_hdr(NetClientState *nc)
 {
     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
@@ -193,6 +204,8 @@ static NetClientInfo net_vhost_user_info = {
         .cleanup = net_vhost_user_cleanup,
         .has_vnet_hdr = vhost_user_has_vnet_hdr,
         .has_ufo = vhost_user_has_ufo,
+        .set_vnet_be = vhost_user_set_vnet_endianness,
+        .set_vnet_le = vhost_user_set_vnet_endianness,
 };
 
 static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond,
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 87e4df1660..77acca0209 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -5,7 +5,7 @@ util-obj-y += opts-visitor.o qapi-clone-visitor.o
 util-obj-y += qmp-event.o
 util-obj-y += qapi-util.o
 
-QAPI_COMMON_MODULES = block-core block char common crypto introspect
+QAPI_COMMON_MODULES = authz block-core block char common crypto introspect
 QAPI_COMMON_MODULES += job migration misc net rdma rocker run-state
 QAPI_COMMON_MODULES += sockets tpm trace transaction ui
 QAPI_TARGET_MODULES = target
diff --git a/qapi/authz.json b/qapi/authz.json
new file mode 100644
index 0000000000..1c836a3abd
--- /dev/null
+++ b/qapi/authz.json
@@ -0,0 +1,58 @@
+# -*- Mode: Python -*-
+#
+# QAPI authz definitions
+
+##
+# @QAuthZListPolicy:
+#
+# The authorization policy result
+#
+# @deny: deny access
+# @allow: allow access
+#
+# Since: 4.0
+##
+{ 'enum': 'QAuthZListPolicy',
+  'prefix': 'QAUTHZ_LIST_POLICY',
+  'data': ['deny', 'allow']}
+
+##
+# @QAuthZListFormat:
+#
+# The authorization policy match format
+#
+# @exact: an exact string match
+# @glob: string with ? and * shell wildcard support
+#
+# Since: 4.0
+##
+{ 'enum': 'QAuthZListFormat',
+  'prefix': 'QAUTHZ_LIST_FORMAT',
+  'data': ['exact', 'glob']}
+
+##
+# @QAuthZListRule:
+#
+# A single authorization rule.
+#
+# @match: a string or glob to match against a user identity
+# @policy: the result to return if @match evaluates to true
+# @format: the format of the @match rule (default 'exact')
+#
+# Since: 4.0
+##
+{ 'struct': 'QAuthZListRule',
+  'data': {'match': 'str',
+           'policy': 'QAuthZListPolicy',
+           '*format': 'QAuthZListFormat'}}
+
+##
+# @QAuthZListRuleListHack:
+#
+# Not exposed via QMP; hack to generate QAuthZListRuleList
+# for use internally by the code.
+#
+# Since: 4.0
+##
+{ 'struct': 'QAuthZListRuleListHack',
+  'data': { 'unused': ['QAuthZListRule'] } }
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index db61bfd688..a34899c626 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -92,6 +92,7 @@
 { 'include': 'rocker.json' }
 { 'include': 'tpm.json' }
 { 'include': 'ui.json' }
+{ 'include': 'authz.json' }
 { 'include': 'migration.json' }
 { 'include': 'transaction.json' }
 { 'include': 'trace.json' }
diff --git a/qapi/ui.json b/qapi/ui.json
index 7d9c4bddaf..c5d1d7f099 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1085,12 +1085,42 @@
 #
 # Display (user interface) type.
 #
+# @default: The default user interface, selecting from the first available
+#           of gtk, sdl, cocoa, and vnc.
+#
+# @none: No user interface or video output display. The guest will
+#        still see an emulated graphics card, but its output will not
+#        be displayed to the QEMU user.
+#
+# @gtk: The GTK user interface.
+#
+# @sdl: The SDL user interface.
+#
+# @egl-headless: No user interface, offload GL operations to a local
+#                DRI device. Graphical display need to be paired with
+#                VNC or Spice. (Since 3.1)
+#
+# @curses: Display video output via curses.  For graphics device
+#          models which support a text mode, QEMU can display this
+#          output using a curses/ncurses interface. Nothing is
+#          displayed when the graphics device is in graphical mode or
+#          if the graphics device does not support a text
+#          mode. Generally only the VGA device models support text
+#          mode.
+#
+# @cocoa: The Cocoa user interface.
+#
+# @spice-app: Set up a Spice server and run the default associated
+#             application to connect to it. The server will redirect
+#             the serial console and QEMU monitors. (Since 4.0)
+#
 # Since: 2.12
 #
 ##
 { 'enum'    : 'DisplayType',
   'data'    : [ 'default', 'none', 'gtk', 'sdl',
-                'egl-headless', 'curses', 'cocoa' ] }
+                'egl-headless', 'curses', 'cocoa',
+                'spice-app'] }
 
 ##
 # @DisplayOptions:
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 83be010a0a..ae3c3f9632 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -2049,6 +2049,15 @@ Malta FPGA serial device
 Cirrus (default) or any other PCI VGA graphics card
 @end itemize
 
+The Boston board emulation supports the following devices:
+
+@itemize @minus
+@item
+Xilinx FPGA, which includes a PCIe root port and an UART
+@item
+Intel EG20T PCH connects the I/O peripherals, but only the SATA bus is emulated
+@end itemize
+
 The ACER Pica emulation supports:
 
 @itemize @minus
@@ -2062,32 +2071,45 @@ PC Keyboard
 IDE controller
 @end itemize
 
-The mipssim pseudo board emulation provides an environment similar
-to what the proprietary MIPS emulator uses for running Linux.
-It supports:
+The MIPS Magnum R4000 emulation supports:
 
 @itemize @minus
 @item
-A range of MIPS CPUs, default is the 24Kf
+MIPS R4000 CPU
 @item
-PC style serial port
+PC-style IRQ controller
 @item
-MIPSnet network emulation
+PC Keyboard
+@item
+SCSI controller
+@item
+G364 framebuffer
 @end itemize
 
-The MIPS Magnum R4000 emulation supports:
+The Fulong 2E emulation supports:
 
 @itemize @minus
 @item
-MIPS R4000 CPU
+Loongson 2E CPU
 @item
-PC-style IRQ controller
+Bonito64 system controller as North Bridge
 @item
-PC Keyboard
+VT82C686 chipset as South Bridge
 @item
-SCSI controller
+RTL8139D as a network card chipset
+@end itemize
+
+The mipssim pseudo board emulation provides an environment similar
+to what the proprietary MIPS emulator uses for running Linux.
+It supports:
+
+@itemize @minus
 @item
-G364 framebuffer
+A range of MIPS CPUs, default is the 24Kf
+@item
+PC style serial port
+@item
+MIPSnet network emulation
 @end itemize
 
 @node nanoMIPS System emulator
diff --git a/qemu-img.c b/qemu-img.c
index 25288c4d18..660c01898e 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -503,7 +503,7 @@ static int img_create(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         goto fail;
     }
 
@@ -753,7 +753,7 @@ static int img_check(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         return 1;
     }
 
@@ -966,7 +966,7 @@ static int img_commit(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         return 1;
     }
 
@@ -1349,7 +1349,7 @@ static int img_compare(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         ret = 2;
         goto out4;
     }
@@ -1670,7 +1670,6 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
 {
     int n, ret;
     QEMUIOVector qiov;
-    struct iovec iov;
 
     assert(nb_sectors <= s->buf_sectors);
     while (nb_sectors > 0) {
@@ -1686,9 +1685,7 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
         bs_sectors = s->src_sectors[src_cur];
 
         n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset));
-        iov.iov_base = buf;
-        iov.iov_len = n << BDRV_SECTOR_BITS;
-        qemu_iovec_init_external(&qiov, &iov, 1);
+        qemu_iovec_init_buf(&qiov, buf, n << BDRV_SECTOR_BITS);
 
         ret = blk_co_preadv(
                 blk, (sector_num - src_cur_offset) << BDRV_SECTOR_BITS,
@@ -1712,7 +1709,6 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num,
 {
     int ret;
     QEMUIOVector qiov;
-    struct iovec iov;
 
     while (nb_sectors > 0) {
         int n = nb_sectors;
@@ -1740,9 +1736,7 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num,
                 (s->compressed &&
                  !buffer_is_zero(buf, n * BDRV_SECTOR_SIZE)))
             {
-                iov.iov_base = buf;
-                iov.iov_len = n << BDRV_SECTOR_BITS;
-                qemu_iovec_init_external(&qiov, &iov, 1);
+                qemu_iovec_init_buf(&qiov, buf, n << BDRV_SECTOR_BITS);
 
                 ret = blk_co_pwritev(s->target, sector_num << BDRV_SECTOR_BITS,
                                      n << BDRV_SECTOR_BITS, &qiov, flags);
@@ -2159,7 +2153,7 @@ static int img_convert(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         goto fail_getopt;
     }
 
@@ -2713,7 +2707,7 @@ static int img_info(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         return 1;
     }
 
@@ -2790,6 +2784,7 @@ static int get_block_status(BlockDriverState *bs, int64_t offset,
     BlockDriverState *file;
     bool has_offset;
     int64_t map;
+    char *filename = NULL;
 
     /* As an optimization, we could cache the current range of unallocated
      * clusters in each file of the chain, and avoid querying the same
@@ -2817,6 +2812,11 @@ static int get_block_status(BlockDriverState *bs, int64_t offset,
 
     has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID);
 
+    if (file && has_offset) {
+        bdrv_refresh_filename(file);
+        filename = file->filename;
+    }
+
     *e = (MapEntry) {
         .start = offset,
         .length = bytes,
@@ -2825,8 +2825,8 @@ static int get_block_status(BlockDriverState *bs, int64_t offset,
         .offset = map,
         .has_offset = has_offset,
         .depth = depth,
-        .has_filename = file && has_offset,
-        .filename = file && has_offset ? file->filename : NULL,
+        .has_filename = filename,
+        .filename = filename,
     };
 
     return 0;
@@ -2932,7 +2932,7 @@ static int img_map(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         return 1;
     }
 
@@ -3081,7 +3081,7 @@ static int img_snapshot(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         return 1;
     }
 
@@ -3123,11 +3123,18 @@ static int img_snapshot(int argc, char **argv)
         break;
 
     case SNAPSHOT_DELETE:
-        bdrv_snapshot_delete_by_id_or_name(bs, snapshot_name, &err);
-        if (err) {
-            error_reportf_err(err, "Could not delete snapshot '%s': ",
-                              snapshot_name);
+        ret = bdrv_snapshot_find(bs, &sn, snapshot_name);
+        if (ret < 0) {
+            error_report("Could not delete snapshot '%s': snapshot not "
+                         "found", snapshot_name);
             ret = 1;
+        } else {
+            ret = bdrv_snapshot_delete(bs, sn.id_str, sn.name, &err);
+            if (ret < 0) {
+                error_reportf_err(err, "Could not delete snapshot '%s': ",
+                                  snapshot_name);
+                ret = 1;
+            }
         }
         break;
     }
@@ -3241,7 +3248,7 @@ static int img_rebase(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         return 1;
     }
 
@@ -3327,20 +3334,17 @@ static int img_rebase(int argc, char **argv)
                 qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
             }
 
+            bdrv_refresh_filename(bs);
             overlay_filename = bs->exact_filename[0] ? bs->exact_filename
                                                      : bs->filename;
-            out_real_path = g_malloc(PATH_MAX);
-
-            bdrv_get_full_backing_filename_from_filename(overlay_filename,
-                                                         out_baseimg,
-                                                         out_real_path,
-                                                         PATH_MAX,
-                                                         &local_err);
+            out_real_path =
+                bdrv_get_full_backing_filename_from_filename(overlay_filename,
+                                                             out_baseimg,
+                                                             &local_err);
             if (local_err) {
                 error_reportf_err(local_err,
                                   "Could not resolve backing filename: ");
                 ret = -1;
-                g_free(out_real_path);
                 goto out;
             }
 
@@ -3621,7 +3625,7 @@ static int img_resize(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         return 1;
     }
 
@@ -3865,7 +3869,7 @@ static int img_amend(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         ret = -1;
         goto out_no_progress;
     }
@@ -4509,7 +4513,7 @@ static int img_dd(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         ret = -1;
         goto out;
     }
@@ -4786,7 +4790,7 @@ static int img_measure(int argc, char **argv)
 
     if (qemu_opts_foreach(&qemu_object_opts,
                           user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
+                          NULL, &error_fatal)) {
         goto out;
     }
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 77bd98e20b..1cf9aac1fe 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1211,6 +1211,7 @@ STEXI
 ETEXI
 
 DEF("display", HAS_ARG, QEMU_OPTION_display,
+    "-display spice-app[,gl=on|off]\n"
     "-display sdl[,frame=on|off][,alt_grab=on|off][,ctrl_grab=on|off]\n"
     "            [,window_close=on|off][,gl=on|core|es|off]\n"
     "-display gtk[,grab_on_hover=on|off][,gl=on|off]|\n"
@@ -1262,6 +1263,10 @@ Start a VNC server on display <arg>
 @item egl-headless
 Offload all OpenGL operations to a local DRI device. For any graphical display,
 this display needs to be paired with either VNC or SPICE displays.
+@item spice-app
+Start QEMU as a Spice server and launch the default Spice client
+application. The Spice server will redirect the serial consoles and
+QEMU monitors. (Since 4.0)
 @end table
 ETEXI
 
@@ -4360,6 +4365,111 @@ e.g to launch a SEV guest
      .....
 
 @end example
+
+
+@item -object authz-simple,id=@var{id},identity=@var{string}
+
+Create an authorization object that will control access to network services.
+
+The @option{identity} parameter is identifies the user and its format
+depends on the network service that authorization object is associated
+with. For authorizing based on TLS x509 certificates, the identity must
+be the x509 distinguished name. Note that care must be taken to escape
+any commas in the distinguished name.
+
+An example authorization object to validate a x509 distinguished name
+would look like:
+@example
+ # $QEMU \
+     ...
+     -object 'authz-simple,id=auth0,identity=CN=laptop.example.com,,O=Example Org,,L=London,,ST=London,,C=GB' \
+     ...
+@end example
+
+Note the use of quotes due to the x509 distinguished name containing
+whitespace, and escaping of ','.
+
+@item -object authz-listfile,id=@var{id},filename=@var{path},refresh=@var{yes|no}
+
+Create an authorization object that will control access to network services.
+
+The @option{filename} parameter is the fully qualified path to a file
+containing the access control list rules in JSON format.
+
+An example set of rules that match against SASL usernames might look
+like:
+
+@example
+  @{
+    "rules": [
+       @{ "match": "fred", "policy": "allow", "format": "exact" @},
+       @{ "match": "bob", "policy": "allow", "format": "exact" @},
+       @{ "match": "danb", "policy": "deny", "format": "glob" @},
+       @{ "match": "dan*", "policy": "allow", "format": "exact" @},
+    ],
+    "policy": "deny"
+  @}
+@end example
+
+When checking access the object will iterate over all the rules and
+the first rule to match will have its @option{policy} value returned
+as the result. If no rules match, then the default @option{policy}
+value is returned.
+
+The rules can either be an exact string match, or they can use the
+simple UNIX glob pattern matching to allow wildcards to be used.
+
+If @option{refresh} is set to true the file will be monitored
+and automatically reloaded whenever its content changes.
+
+As with the @code{authz-simple} object, the format of the identity
+strings being matched depends on the network service, but is usually
+a TLS x509 distinguished name, or a SASL username.
+
+An example authorization object to validate a SASL username
+would look like:
+@example
+ # $QEMU \
+     ...
+     -object authz-simple,id=auth0,filename=/etc/qemu/vnc-sasl.acl,refresh=yes
+     ...
+@end example
+
+@item -object authz-pam,id=@var{id},service=@var{string}
+
+Create an authorization object that will control access to network services.
+
+The @option{service} parameter provides the name of a PAM service to use
+for authorization. It requires that a file @code{/etc/pam.d/@var{service}}
+exist to provide the configuration for the @code{account} subsystem.
+
+An example authorization object to validate a TLS x509 distinguished
+name would look like:
+
+@example
+ # $QEMU \
+     ...
+     -object authz-pam,id=auth0,service=qemu-vnc
+     ...
+@end example
+
+There would then be a corresponding config file for PAM at
+@code{/etc/pam.d/qemu-vnc} that contains:
+
+@example
+account requisite  pam_listfile.so item=user sense=allow \
+           file=/etc/qemu/vnc.allow
+@end example
+
+Finally the @code{/etc/qemu/vnc.allow} file would contain
+the list of x509 distingished names that are permitted
+access
+
+@example
+CN=laptop.example.com,O=Example Home,L=London,ST=London,C=GB
+@end example
+
+
 @end table
 
 ETEXI
diff --git a/qom/object.c b/qom/object.c
index b8c732063b..05a8567041 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -646,16 +646,20 @@ Object *object_new_with_propv(const char *typename,
         goto error;
     }
 
-    object_property_add_child(parent, id, obj, &local_err);
-    if (local_err) {
-        goto error;
+    if (id != NULL) {
+        object_property_add_child(parent, id, obj, &local_err);
+        if (local_err) {
+            goto error;
+        }
     }
 
     uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE);
     if (uc) {
         user_creatable_complete(uc, &local_err);
         if (local_err) {
-            object_unparent(obj);
+            if (id != NULL) {
+                object_unparent(obj);
+            }
             goto error;
         }
     }
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index db85d1eb75..cb5809934a 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -75,16 +75,20 @@ Object *user_creatable_add_type(const char *type, const char *id,
         goto out;
     }
 
-    object_property_add_child(object_get_objects_root(),
-                              id, obj, &local_err);
-    if (local_err) {
-        goto out;
+    if (id != NULL) {
+        object_property_add_child(object_get_objects_root(),
+                                  id, obj, &local_err);
+        if (local_err) {
+            goto out;
+        }
     }
 
     user_creatable_complete(USER_CREATABLE(obj), &local_err);
     if (local_err) {
-        object_property_del(object_get_objects_root(),
-                            id, &error_abort);
+        if (id != NULL) {
+            object_property_del(object_get_objects_root(),
+                                id, &error_abort);
+        }
         goto out;
     }
 out:
diff --git a/roms/Makefile b/roms/Makefile
index a6043eff37..78d5dd18c3 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -47,10 +47,7 @@ SEABIOS_EXTRAVERSION="-prebuilt.qemu.org"
 # We need that to combine multiple images (legacy bios,
 # efi ia32, efi x64) into a single rom binary.
 #
-# We try to find it in the path.  You can also pass the location on
-# the command line, i.e. "make EFIROM=/path/to/EfiRom efirom"
-#
-EFIROM ?= $(shell which EfiRom 2>/dev/null)
+EFIROM = edk2/BaseTools/Source/C/bin/EfiRom
 
 default:
 	@echo "nothing is build by default"
@@ -59,8 +56,7 @@ default:
 	@echo "  vgabios        -- update vgabios binaries (seabios)"
 	@echo "  sgabios        -- update sgabios binaries"
 	@echo "  pxerom         -- update nic roms (bios only)"
-	@echo "  efirom         -- update nic roms (bios+efi, this needs"
-	@echo "                    the EfiRom utility from edk2 / tianocore)"
+	@echo "  efirom         -- update nic roms (bios+efi)"
 	@echo "  slof           -- update slof.bin"
 	@echo "  skiboot        -- update skiboot.lid"
 	@echo "  u-boot.e500    -- update u-boot.e500"
@@ -106,7 +102,7 @@ pxe-rom-%: build-pxe-roms
 
 efirom: $(patsubst %,efi-rom-%,$(pxerom_variants))
 
-efi-rom-%: build-pxe-roms build-efi-roms
+efi-rom-%: build-pxe-roms build-efi-roms $(EFIROM)
 	$(EFIROM) -f "0x$(VID)" -i "0x$(DID)" -l 0x02 \
 		-b ipxe/src/bin/$(VID)$(DID).rom \
 		-ec ipxe/src/bin-i386-efi/$(VID)$(DID).efidrv \
@@ -124,6 +120,8 @@ build-efi-roms: build-pxe-roms
 		$(patsubst %,bin-i386-efi/%.efidrv,$(pxerom_targets)) \
 		$(patsubst %,bin-x86_64-efi/%.efidrv,$(pxerom_targets))
 
+$(EFIROM):
+	$(MAKE) -C edk2/BaseTools
 
 slof:
 	$(MAKE) -C SLOF CROSS=$(powerpc64_cross_prefix) qemu
@@ -150,6 +148,7 @@ clean:
 	$(MAKE) -C sgabios clean
 	rm -f sgabios/.depend
 	$(MAKE) -C ipxe/src veryclean
+	$(MAKE) -C edk2/BaseTools clean
 	$(MAKE) -C SLOF clean
 	rm -rf u-boot/build.e500
 	$(MAKE) -C u-boot-sam460ex distclean
diff --git a/roms/edk2 b/roms/edk2
new file mode 160000
+Subproject 85588389222a3636baf0f9ed8227f2434af4c3f
diff --git a/scripts/qemu.py b/scripts/qemu.py
index 32b00af5cc..f7269eefbb 100644
--- a/scripts/qemu.py
+++ b/scripts/qemu.py
@@ -144,10 +144,9 @@ class QEMUMachine(object):
         return False
 
     # This can be used to add an unused monitor instance.
-    def add_monitor_telnet(self, ip, port):
-        args = 'tcp:%s:%d,server,nowait,telnet' % (ip, port)
+    def add_monitor_null(self):
         self._args.append('-monitor')
-        self._args.append(args)
+        self._args.append('null')
 
     def add_fd(self, fd, fdset, opaque, opts=''):
         """
diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c
index f9de5164e5..f77a950db6 100644
--- a/target/arm/arm-powerctl.c
+++ b/target/arm/arm-powerctl.c
@@ -228,6 +228,62 @@ int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
     return QEMU_ARM_POWERCTL_RET_SUCCESS;
 }
 
+static void arm_set_cpu_on_and_reset_async_work(CPUState *target_cpu_state,
+                                                run_on_cpu_data data)
+{
+    ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
+
+    /* Initialize the cpu we are turning on */
+    cpu_reset(target_cpu_state);
+    target_cpu_state->halted = 0;
+
+    /* Finally set the power status */
+    assert(qemu_mutex_iothread_locked());
+    target_cpu->power_state = PSCI_ON;
+}
+
+int arm_set_cpu_on_and_reset(uint64_t cpuid)
+{
+    CPUState *target_cpu_state;
+    ARMCPU *target_cpu;
+
+    assert(qemu_mutex_iothread_locked());
+
+    /* Retrieve the cpu we are powering up */
+    target_cpu_state = arm_get_cpu_by_id(cpuid);
+    if (!target_cpu_state) {
+        /* The cpu was not found */
+        return QEMU_ARM_POWERCTL_INVALID_PARAM;
+    }
+
+    target_cpu = ARM_CPU(target_cpu_state);
+    if (target_cpu->power_state == PSCI_ON) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "[ARM]%s: CPU %" PRId64 " is already on\n",
+                      __func__, cpuid);
+        return QEMU_ARM_POWERCTL_ALREADY_ON;
+    }
+
+    /*
+     * If another CPU has powered the target on we are in the state
+     * ON_PENDING and additional attempts to power on the CPU should
+     * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI
+     * spec)
+     */
+    if (target_cpu->power_state == PSCI_ON_PENDING) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "[ARM]%s: CPU %" PRId64 " is already powering on\n",
+                      __func__, cpuid);
+        return QEMU_ARM_POWERCTL_ON_PENDING;
+    }
+
+    async_run_on_cpu(target_cpu_state, arm_set_cpu_on_and_reset_async_work,
+                     RUN_ON_CPU_NULL);
+
+    /* We are good to go */
+    return QEMU_ARM_POWERCTL_RET_SUCCESS;
+}
+
 static void arm_set_cpu_off_async_work(CPUState *target_cpu_state,
                                        run_on_cpu_data data)
 {
diff --git a/target/arm/arm-powerctl.h b/target/arm/arm-powerctl.h
index 04353923c0..37c8a04f0a 100644
--- a/target/arm/arm-powerctl.h
+++ b/target/arm/arm-powerctl.h
@@ -74,4 +74,20 @@ int arm_set_cpu_off(uint64_t cpuid);
  */
 int arm_reset_cpu(uint64_t cpuid);
 
+/*
+ * arm_set_cpu_on_and_reset:
+ * @cpuid: the id of the CPU we want to star
+ *
+ * Start the cpu designated by @cpuid and put it through its normal
+ * CPU reset process. The CPU will start in the way it is architected
+ * to start after a power-on reset.
+ *
+ * Returns: QEMU_ARM_POWERCTL_RET_SUCCESS on success.
+ * QEMU_ARM_POWERCTL_INVALID_PARAM if there is no CPU with that ID.
+ * QEMU_ARM_POWERCTL_ALREADY_ON if the CPU is already on.
+ * QEMU_ARM_POWERCTL_ON_PENDING if the CPU is already partway through
+ * powering on.
+ */
+int arm_set_cpu_on_and_reset(uint64_t cpuid);
+
 #endif
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 8ea6569088..54b61f917b 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -22,6 +22,7 @@
 #include "target/arm/idau.h"
 #include "qemu/error-report.h"
 #include "qapi/error.h"
+#include "qapi/visitor.h"
 #include "cpu.h"
 #include "internals.h"
 #include "qemu-common.h"
@@ -771,9 +772,21 @@ static Property arm_cpu_pmsav7_dregion_property =
                                            pmsav7_dregion,
                                            qdev_prop_uint32, uint32_t);
 
-/* M profile: initial value of the Secure VTOR */
-static Property arm_cpu_initsvtor_property =
-            DEFINE_PROP_UINT32("init-svtor", ARMCPU, init_svtor, 0);
+static void arm_get_init_svtor(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
+{
+    ARMCPU *cpu = ARM_CPU(obj);
+
+    visit_type_uint32(v, name, &cpu->init_svtor, errp);
+}
+
+static void arm_set_init_svtor(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
+{
+    ARMCPU *cpu = ARM_CPU(obj);
+
+    visit_type_uint32(v, name, &cpu->init_svtor, errp);
+}
 
 void arm_cpu_post_init(Object *obj)
 {
@@ -845,8 +858,14 @@ void arm_cpu_post_init(Object *obj)
                                  qdev_prop_allow_set_link_before_realize,
                                  OBJ_PROP_LINK_STRONG,
                                  &error_abort);
-        qdev_property_add_static(DEVICE(obj), &arm_cpu_initsvtor_property,
-                                 &error_abort);
+        /*
+         * M profile: initial value of the Secure VTOR. We can't just use
+         * a simple DEFINE_PROP_UINT32 for this because we want to permit
+         * the property to be set after realize.
+         */
+        object_property_add(obj, "init-svtor", "uint32",
+                            arm_get_init_svtor, arm_set_init_svtor,
+                            NULL, NULL, &error_abort);
     }
 
     qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property,
@@ -995,7 +1014,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
     }
     if (arm_feature(env, ARM_FEATURE_VFP4)) {
         set_feature(env, ARM_FEATURE_VFP3);
-        set_feature(env, ARM_FEATURE_VFP_FP16);
     }
     if (arm_feature(env, ARM_FEATURE_VFP3)) {
         set_feature(env, ARM_FEATURE_VFP);
@@ -1656,7 +1674,6 @@ static void cortex_a9_initfn(Object *obj)
     cpu->dtb_compatible = "arm,cortex-a9";
     set_feature(&cpu->env, ARM_FEATURE_V7);
     set_feature(&cpu->env, ARM_FEATURE_VFP3);
-    set_feature(&cpu->env, ARM_FEATURE_VFP_FP16);
     set_feature(&cpu->env, ARM_FEATURE_NEON);
     set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
     set_feature(&cpu->env, ARM_FEATURE_EL3);
@@ -2003,6 +2020,7 @@ static void arm_max_initfn(Object *obj)
             t = cpu->isar.id_isar6;
             t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1);
             t = FIELD_DP32(t, ID_ISAR6, DP, 1);
+            t = FIELD_DP32(t, ID_ISAR6, FHM, 1);
             cpu->isar.id_isar6 = t;
 
             t = cpu->id_mmfr4;
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 1eea1a408b..36cd365efa 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -1730,6 +1730,27 @@ FIELD(ID_DFR0, MPROFDBG, 20, 4)
 FIELD(ID_DFR0, PERFMON, 24, 4)
 FIELD(ID_DFR0, TRACEFILT, 28, 4)
 
+FIELD(MVFR0, SIMDREG, 0, 4)
+FIELD(MVFR0, FPSP, 4, 4)
+FIELD(MVFR0, FPDP, 8, 4)
+FIELD(MVFR0, FPTRAP, 12, 4)
+FIELD(MVFR0, FPDIVIDE, 16, 4)
+FIELD(MVFR0, FPSQRT, 20, 4)
+FIELD(MVFR0, FPSHVEC, 24, 4)
+FIELD(MVFR0, FPROUND, 28, 4)
+
+FIELD(MVFR1, FPFTZ, 0, 4)
+FIELD(MVFR1, FPDNAN, 4, 4)
+FIELD(MVFR1, SIMDLS, 8, 4)
+FIELD(MVFR1, SIMDINT, 12, 4)
+FIELD(MVFR1, SIMDSP, 16, 4)
+FIELD(MVFR1, SIMDHP, 20, 4)
+FIELD(MVFR1, FPHP, 24, 4)
+FIELD(MVFR1, SIMDFMAC, 28, 4)
+
+FIELD(MVFR2, SIMDMISC, 0, 4)
+FIELD(MVFR2, FPMISC, 4, 4)
+
 QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK);
 
 /* If adding a feature bit which corresponds to a Linux ELF
@@ -1747,7 +1768,6 @@ enum arm_features {
     ARM_FEATURE_THUMB2,
     ARM_FEATURE_PMSA,   /* no MMU; may have Memory Protection Unit */
     ARM_FEATURE_VFP3,
-    ARM_FEATURE_VFP_FP16,
     ARM_FEATURE_NEON,
     ARM_FEATURE_M, /* Microcontroller profile.  */
     ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling.  */
@@ -2538,25 +2558,18 @@ bool write_list_to_cpustate(ARMCPU *cpu);
 /**
  * write_cpustate_to_list:
  * @cpu: ARMCPU
- * @kvm_sync: true if this is for syncing back to KVM
  *
  * For each register listed in the ARMCPU cpreg_indexes list, write
  * its value from the ARMCPUState structure into the cpreg_values list.
  * This is used to copy info from TCG's working data structures into
  * KVM or for outbound migration.
  *
- * @kvm_sync is true if we are doing this in order to sync the
- * register state back to KVM. In this case we will only update
- * values in the list if the previous list->cpustate sync actually
- * successfully wrote the CPU state. Otherwise we will keep the value
- * that is in the list.
- *
  * Returns: true if all register values were read correctly,
  * false if some register was unknown or could not be read.
  * Note that we do not stop early on failure -- we will attempt
  * reading all registers in the list.
  */
-bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync);
+bool write_cpustate_to_list(ARMCPU *cpu);
 
 #define ARM_CPUID_TI915T      0x54029152
 #define ARM_CPUID_TI925T      0x54029252
@@ -3283,6 +3296,11 @@ static inline bool isar_feature_aa32_dp(const ARMISARegisters *id)
     return FIELD_EX32(id->id_isar6, ID_ISAR6, DP) != 0;
 }
 
+static inline bool isar_feature_aa32_fhm(const ARMISARegisters *id)
+{
+    return FIELD_EX32(id->id_isar6, ID_ISAR6, FHM) != 0;
+}
+
 static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id)
 {
     /*
@@ -3294,6 +3312,41 @@ static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id)
 }
 
 /*
+ * We always set the FP and SIMD FP16 fields to indicate identical
+ * levels of support (assuming SIMD is implemented at all), so
+ * we only need one set of accessors.
+ */
+static inline bool isar_feature_aa32_fp16_spconv(const ARMISARegisters *id)
+{
+    return FIELD_EX64(id->mvfr1, MVFR1, FPHP) > 0;
+}
+
+static inline bool isar_feature_aa32_fp16_dpconv(const ARMISARegisters *id)
+{
+    return FIELD_EX64(id->mvfr1, MVFR1, FPHP) > 1;
+}
+
+static inline bool isar_feature_aa32_vsel(const ARMISARegisters *id)
+{
+    return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 1;
+}
+
+static inline bool isar_feature_aa32_vcvt_dr(const ARMISARegisters *id)
+{
+    return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 2;
+}
+
+static inline bool isar_feature_aa32_vrint(const ARMISARegisters *id)
+{
+    return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 3;
+}
+
+static inline bool isar_feature_aa32_vminmaxnm(const ARMISARegisters *id)
+{
+    return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 4;
+}
+
+/*
  * 64-bit feature tests via id registers.
  */
 static inline bool isar_feature_aa64_aes(const ARMISARegisters *id)
@@ -3356,6 +3409,11 @@ static inline bool isar_feature_aa64_dp(const ARMISARegisters *id)
     return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, DP) != 0;
 }
 
+static inline bool isar_feature_aa64_fhm(const ARMISARegisters *id)
+{
+    return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, FHM) != 0;
+}
+
 static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id)
 {
     return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0;
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 69e4134f79..1b0c427277 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -308,6 +308,7 @@ static void aarch64_max_initfn(Object *obj)
         t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1);
         t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 1);
         t = FIELD_DP64(t, ID_AA64ISAR0, DP, 1);
+        t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 1);
         cpu->isar.id_aa64isar0 = t;
 
         t = cpu->isar.id_aa64isar1;
@@ -347,6 +348,7 @@ static void aarch64_max_initfn(Object *obj)
         u = cpu->isar.id_isar6;
         u = FIELD_DP32(u, ID_ISAR6, JSCVT, 1);
         u = FIELD_DP32(u, ID_ISAR6, DP, 1);
+        u = FIELD_DP32(u, ID_ISAR6, FHM, 1);
         cpu->isar.id_isar6 = u;
 
         /*
diff --git a/target/arm/helper.c b/target/arm/helper.c
index fbaa801cea..1fa282a7fc 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -265,7 +265,7 @@ static bool raw_accessors_invalid(const ARMCPRegInfo *ri)
     return true;
 }
 
-bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync)
+bool write_cpustate_to_list(ARMCPU *cpu)
 {
     /* Write the coprocessor state from cpu->env to the (index,value) list. */
     int i;
@@ -274,7 +274,6 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync)
     for (i = 0; i < cpu->cpreg_array_len; i++) {
         uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]);
         const ARMCPRegInfo *ri;
-        uint64_t newval;
 
         ri = get_arm_cp_reginfo(cpu->cp_regs, regidx);
         if (!ri) {
@@ -284,29 +283,7 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync)
         if (ri->type & ARM_CP_NO_RAW) {
             continue;
         }
-
-        newval = read_raw_cp_reg(&cpu->env, ri);
-        if (kvm_sync) {
-            /*
-             * Only sync if the previous list->cpustate sync succeeded.
-             * Rather than tracking the success/failure state for every
-             * item in the list, we just recheck "does the raw write we must
-             * have made in write_list_to_cpustate() read back OK" here.
-             */
-            uint64_t oldval = cpu->cpreg_values[i];
-
-            if (oldval == newval) {
-                continue;
-            }
-
-            write_raw_cp_reg(&cpu->env, ri, oldval);
-            if (read_raw_cp_reg(&cpu->env, ri) != oldval) {
-                continue;
-            }
-
-            write_raw_cp_reg(&cpu->env, ri, newval);
-        }
-        cpu->cpreg_values[i] = newval;
+        cpu->cpreg_values[i] = read_raw_cp_reg(&cpu->env, ri);
     }
     return ok;
 }
diff --git a/target/arm/helper.h b/target/arm/helper.h
index 747cb64d29..d363904278 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -677,6 +677,15 @@ DEF_HELPER_FLAGS_5(gvec_sqsub_s, TCG_CALL_NO_RWG,
 DEF_HELPER_FLAGS_5(gvec_sqsub_d, TCG_CALL_NO_RWG,
                    void, ptr, ptr, ptr, ptr, i32)
 
+DEF_HELPER_FLAGS_5(gvec_fmlal_a32, TCG_CALL_NO_RWG,
+                   void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmlal_a64, TCG_CALL_NO_RWG,
+                   void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a32, TCG_CALL_NO_RWG,
+                   void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a64, TCG_CALL_NO_RWG,
+                   void, ptr, ptr, ptr, ptr, i32)
+
 #ifdef TARGET_AARCH64
 #include "helper-a64.h"
 #include "helper-sve.h"
diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c
index a75e04cc8f..50327989dc 100644
--- a/target/arm/kvm32.c
+++ b/target/arm/kvm32.c
@@ -125,9 +125,6 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
     if (extract32(id_pfr0, 12, 4) == 1) {
         set_feature(&features, ARM_FEATURE_THUMB2EE);
     }
-    if (extract32(ahcf->isar.mvfr1, 20, 4) == 1) {
-        set_feature(&features, ARM_FEATURE_VFP_FP16);
-    }
     if (extract32(ahcf->isar.mvfr1, 12, 4) == 1) {
         set_feature(&features, ARM_FEATURE_NEON);
     }
@@ -387,8 +384,24 @@ int kvm_arch_put_registers(CPUState *cs, int level)
         return ret;
     }
 
-    write_cpustate_to_list(cpu, true);
-
+    /* Note that we do not call write_cpustate_to_list()
+     * here, so we are only writing the tuple list back to
+     * KVM. This is safe because nothing can change the
+     * CPUARMState cp15 fields (in particular gdb accesses cannot)
+     * and so there are no changes to sync. In fact syncing would
+     * be wrong at this point: for a constant register where TCG and
+     * KVM disagree about its value, the preceding write_list_to_cpustate()
+     * would not have had any effect on the CPUARMState value (since the
+     * register is read-only), and a write_cpustate_to_list() here would
+     * then try to write the TCG value back into KVM -- this would either
+     * fail or incorrectly change the value the guest sees.
+     *
+     * If we ever want to allow the user to modify cp15 registers via
+     * the gdb stub, we would need to be more clever here (for instance
+     * tracking the set of registers kvm_arch_get_registers() successfully
+     * managed to update the CPUARMState with, and only allowing those
+     * to be written back up into the kernel).
+     */
     if (!write_list_to_kvmstate(cpu, level)) {
         return EINVAL;
     }
diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c
index e3ba149248..089af9c5f0 100644
--- a/target/arm/kvm64.c
+++ b/target/arm/kvm64.c
@@ -838,8 +838,6 @@ int kvm_arch_put_registers(CPUState *cs, int level)
         return ret;
     }
 
-    write_cpustate_to_list(cpu, true);
-
     if (!write_list_to_kvmstate(cpu, level)) {
         return EINVAL;
     }
diff --git a/target/arm/machine.c b/target/arm/machine.c
index 124192bfc2..b292549614 100644
--- a/target/arm/machine.c
+++ b/target/arm/machine.c
@@ -630,7 +630,7 @@ static int cpu_pre_save(void *opaque)
             abort();
         }
     } else {
-        if (!write_cpustate_to_list(cpu, false)) {
+        if (!write_cpustate_to_list(cpu)) {
             /* This should never fail. */
             abort();
         }
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index c56e878787..d3c8eaf089 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -10917,9 +10917,29 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn)
         if (!fp_access_check(s)) {
             return;
         }
-
         handle_3same_float(s, size, elements, fpopcode, rd, rn, rm);
         return;
+
+    case 0x1d: /* FMLAL  */
+    case 0x3d: /* FMLSL  */
+    case 0x59: /* FMLAL2 */
+    case 0x79: /* FMLSL2 */
+        if (size & 1 || !dc_isar_feature(aa64_fhm, s)) {
+            unallocated_encoding(s);
+            return;
+        }
+        if (fp_access_check(s)) {
+            int is_s = extract32(insn, 23, 1);
+            int is_2 = extract32(insn, 29, 1);
+            int data = (is_2 << 1) | is_s;
+            tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd),
+                               vec_full_reg_offset(s, rn),
+                               vec_full_reg_offset(s, rm), cpu_env,
+                               is_q ? 16 : 8, vec_full_reg_size(s),
+                               data, gen_helper_gvec_fmlal_a64);
+        }
+        return;
+
     default:
         unallocated_encoding(s);
         return;
@@ -12739,6 +12759,17 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
         }
         is_fp = 2;
         break;
+    case 0x00: /* FMLAL */
+    case 0x04: /* FMLSL */
+    case 0x18: /* FMLAL2 */
+    case 0x1c: /* FMLSL2 */
+        if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_fhm, s)) {
+            unallocated_encoding(s);
+            return;
+        }
+        size = MO_16;
+        /* is_fp, but we pass cpu_env not fp_status.  */
+        break;
     default:
         unallocated_encoding(s);
         return;
@@ -12849,6 +12880,22 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
             tcg_temp_free_ptr(fpst);
         }
         return;
+
+    case 0x00: /* FMLAL */
+    case 0x04: /* FMLSL */
+    case 0x18: /* FMLAL2 */
+    case 0x1c: /* FMLSL2 */
+        {
+            int is_s = extract32(opcode, 2, 1);
+            int is_2 = u;
+            int data = (index << 2) | (is_2 << 1) | is_s;
+            tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd),
+                               vec_full_reg_offset(s, rn),
+                               vec_full_reg_offset(s, rm), cpu_env,
+                               is_q ? 16 : 8, vec_full_reg_size(s),
+                               data, gen_helper_gvec_fmlal_idx_a64);
+        }
+        return;
     }
 
     if (size == 3) {
diff --git a/target/arm/translate.c b/target/arm/translate.c
index c1175798ac..8f7f5b95aa 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -3357,14 +3357,10 @@ static const uint8_t fp_decode_rm[] = {
     FPROUNDING_NEGINF,
 };
 
-static int disas_vfp_v8_insn(DisasContext *s, uint32_t insn)
+static int disas_vfp_misc_insn(DisasContext *s, uint32_t insn)
 {
     uint32_t rd, rn, rm, dp = extract32(insn, 8, 1);
 
-    if (!arm_dc_feature(s, ARM_FEATURE_V8)) {
-        return 1;
-    }
-
     if (dp) {
         VFP_DREG_D(rd, insn);
         VFP_DREG_N(rn, insn);
@@ -3375,15 +3371,18 @@ static int disas_vfp_v8_insn(DisasContext *s, uint32_t insn)
         rm = VFP_SREG_M(insn);
     }
 
-    if ((insn & 0x0f800e50) == 0x0e000a00) {
+    if ((insn & 0x0f800e50) == 0x0e000a00 && dc_isar_feature(aa32_vsel, s)) {
         return handle_vsel(insn, rd, rn, rm, dp);
-    } else if ((insn & 0x0fb00e10) == 0x0e800a00) {
+    } else if ((insn & 0x0fb00e10) == 0x0e800a00 &&
+               dc_isar_feature(aa32_vminmaxnm, s)) {
         return handle_vminmaxnm(insn, rd, rn, rm, dp);
-    } else if ((insn & 0x0fbc0ed0) == 0x0eb80a40) {
+    } else if ((insn & 0x0fbc0ed0) == 0x0eb80a40 &&
+               dc_isar_feature(aa32_vrint, s)) {
         /* VRINTA, VRINTN, VRINTP, VRINTM */
         int rounding = fp_decode_rm[extract32(insn, 16, 2)];
         return handle_vrint(insn, rd, rm, dp, rounding);
-    } else if ((insn & 0x0fbc0e50) == 0x0ebc0a40) {
+    } else if ((insn & 0x0fbc0e50) == 0x0ebc0a40 &&
+               dc_isar_feature(aa32_vcvt_dr, s)) {
         /* VCVTA, VCVTN, VCVTP, VCVTM */
         int rounding = fp_decode_rm[extract32(insn, 16, 2)];
         return handle_vcvt(insn, rd, rm, dp, rounding);
@@ -3427,10 +3426,12 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
     }
 
     if (extract32(insn, 28, 4) == 0xf) {
-        /* Encodings with T=1 (Thumb) or unconditional (ARM):
-         * only used in v8 and above.
+        /*
+         * Encodings with T=1 (Thumb) or unconditional (ARM):
+         * only used for the "miscellaneous VFP features" added in v8A
+         * and v7M (and gated on the MVFR2.FPMisc field).
          */
-        return disas_vfp_v8_insn(s, insn);
+        return disas_vfp_misc_insn(s, insn);
     }
 
     dp = ((insn & 0xf00) == 0xb00);
@@ -3663,17 +3664,27 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
                      * UNPREDICTABLE if bit 8 is set prior to ARMv8
                      * (we choose to UNDEF)
                      */
-                    if ((dp && !arm_dc_feature(s, ARM_FEATURE_V8)) ||
-                        !arm_dc_feature(s, ARM_FEATURE_VFP_FP16)) {
-                        return 1;
+                    if (dp) {
+                        if (!dc_isar_feature(aa32_fp16_dpconv, s)) {
+                            return 1;
+                        }
+                    } else {
+                        if (!dc_isar_feature(aa32_fp16_spconv, s)) {
+                            return 1;
+                        }
                     }
                     rm_is_dp = false;
                     break;
                 case 0x06: /* vcvtb.f16.f32, vcvtb.f16.f64 */
                 case 0x07: /* vcvtt.f16.f32, vcvtt.f16.f64 */
-                    if ((dp && !arm_dc_feature(s, ARM_FEATURE_V8)) ||
-                        !arm_dc_feature(s, ARM_FEATURE_VFP_FP16)) {
-                        return 1;
+                    if (dp) {
+                        if (!dc_isar_feature(aa32_fp16_dpconv, s)) {
+                            return 1;
+                        }
+                    } else {
+                        if (!dc_isar_feature(aa32_fp16_spconv, s)) {
+                            return 1;
+                        }
                     }
                     rd_is_dp = false;
                     break;
@@ -7876,7 +7887,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
                     TCGv_ptr fpst;
                     TCGv_i32 ahp;
 
-                    if (!arm_dc_feature(s, ARM_FEATURE_VFP_FP16) ||
+                    if (!dc_isar_feature(aa32_fp16_spconv, s) ||
                         q || (rm & 1)) {
                         return 1;
                     }
@@ -7908,7 +7919,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
                 {
                     TCGv_ptr fpst;
                     TCGv_i32 ahp;
-                    if (!arm_dc_feature(s, ARM_FEATURE_VFP_FP16) ||
+                    if (!dc_isar_feature(aa32_fp16_spconv, s) ||
                         q || (rd & 1)) {
                         return 1;
                     }
@@ -8372,15 +8383,9 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn)
     gen_helper_gvec_3_ptr *fn_gvec_ptr = NULL;
     int rd, rn, rm, opr_sz;
     int data = 0;
-    bool q;
-
-    q = extract32(insn, 6, 1);
-    VFP_DREG_D(rd, insn);
-    VFP_DREG_N(rn, insn);
-    VFP_DREG_M(rm, insn);
-    if ((rd | rn | rm) & q) {
-        return 1;
-    }
+    int off_rn, off_rm;
+    bool is_long = false, q = extract32(insn, 6, 1);
+    bool ptr_is_env = false;
 
     if ((insn & 0xfe200f10) == 0xfc200800) {
         /* VCMLA -- 1111 110R R.1S .... .... 1000 ...0 .... */
@@ -8407,10 +8412,39 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn)
             return 1;
         }
         fn_gvec = u ? gen_helper_gvec_udot_b : gen_helper_gvec_sdot_b;
+    } else if ((insn & 0xff300f10) == 0xfc200810) {
+        /* VFM[AS]L -- 1111 1100 S.10 .... .... 1000 .Q.1 .... */
+        int is_s = extract32(insn, 23, 1);
+        if (!dc_isar_feature(aa32_fhm, s)) {
+            return 1;
+        }
+        is_long = true;
+        data = is_s; /* is_2 == 0 */
+        fn_gvec_ptr = gen_helper_gvec_fmlal_a32;
+        ptr_is_env = true;
     } else {
         return 1;
     }
 
+    VFP_DREG_D(rd, insn);
+    if (rd & q) {
+        return 1;
+    }
+    if (q || !is_long) {
+        VFP_DREG_N(rn, insn);
+        VFP_DREG_M(rm, insn);
+        if ((rn | rm) & q & !is_long) {
+            return 1;
+        }
+        off_rn = vfp_reg_offset(1, rn);
+        off_rm = vfp_reg_offset(1, rm);
+    } else {
+        rn = VFP_SREG_N(insn);
+        rm = VFP_SREG_M(insn);
+        off_rn = vfp_reg_offset(0, rn);
+        off_rm = vfp_reg_offset(0, rm);
+    }
+
     if (s->fp_excp_el) {
         gen_exception_insn(s, 4, EXCP_UDEF,
                            syn_simd_access_trap(1, 0xe, false), s->fp_excp_el);
@@ -8422,16 +8456,19 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn)
 
     opr_sz = (1 + q) * 8;
     if (fn_gvec_ptr) {
-        TCGv_ptr fpst = get_fpstatus_ptr(1);
-        tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd),
-                           vfp_reg_offset(1, rn),
-                           vfp_reg_offset(1, rm), fpst,
+        TCGv_ptr ptr;
+        if (ptr_is_env) {
+            ptr = cpu_env;
+        } else {
+            ptr = get_fpstatus_ptr(1);
+        }
+        tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), off_rn, off_rm, ptr,
                            opr_sz, opr_sz, data, fn_gvec_ptr);
-        tcg_temp_free_ptr(fpst);
+        if (!ptr_is_env) {
+            tcg_temp_free_ptr(ptr);
+        }
     } else {
-        tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd),
-                           vfp_reg_offset(1, rn),
-                           vfp_reg_offset(1, rm),
+        tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd), off_rn, off_rm,
                            opr_sz, opr_sz, data, fn_gvec);
     }
     return 0;
@@ -8450,14 +8487,9 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn)
     gen_helper_gvec_3 *fn_gvec = NULL;
     gen_helper_gvec_3_ptr *fn_gvec_ptr = NULL;
     int rd, rn, rm, opr_sz, data;
-    bool q;
-
-    q = extract32(insn, 6, 1);
-    VFP_DREG_D(rd, insn);
-    VFP_DREG_N(rn, insn);
-    if ((rd | rn) & q) {
-        return 1;
-    }
+    int off_rn, off_rm;
+    bool is_long = false, q = extract32(insn, 6, 1);
+    bool ptr_is_env = false;
 
     if ((insn & 0xff000f10) == 0xfe000800) {
         /* VCMLA (indexed) -- 1111 1110 S.RR .... .... 1000 ...0 .... */
@@ -8486,6 +8518,7 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn)
     } else if ((insn & 0xffb00f00) == 0xfe200d00) {
         /* V[US]DOT -- 1111 1110 0.10 .... .... 1101 .Q.U .... */
         int u = extract32(insn, 4, 1);
+
         if (!dc_isar_feature(aa32_dp, s)) {
             return 1;
         }
@@ -8493,10 +8526,48 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn)
         /* rm is just Vm, and index is M.  */
         data = extract32(insn, 5, 1); /* index */
         rm = extract32(insn, 0, 4);
+    } else if ((insn & 0xffa00f10) == 0xfe000810) {
+        /* VFM[AS]L -- 1111 1110 0.0S .... .... 1000 .Q.1 .... */
+        int is_s = extract32(insn, 20, 1);
+        int vm20 = extract32(insn, 0, 3);
+        int vm3 = extract32(insn, 3, 1);
+        int m = extract32(insn, 5, 1);
+        int index;
+
+        if (!dc_isar_feature(aa32_fhm, s)) {
+            return 1;
+        }
+        if (q) {
+            rm = vm20;
+            index = m * 2 + vm3;
+        } else {
+            rm = vm20 * 2 + m;
+            index = vm3;
+        }
+        is_long = true;
+        data = (index << 2) | is_s; /* is_2 == 0 */
+        fn_gvec_ptr = gen_helper_gvec_fmlal_idx_a32;
+        ptr_is_env = true;
     } else {
         return 1;
     }
 
+    VFP_DREG_D(rd, insn);
+    if (rd & q) {
+        return 1;
+    }
+    if (q || !is_long) {
+        VFP_DREG_N(rn, insn);
+        if (rn & q & !is_long) {
+            return 1;
+        }
+        off_rn = vfp_reg_offset(1, rn);
+        off_rm = vfp_reg_offset(1, rm);
+    } else {
+        rn = VFP_SREG_N(insn);
+        off_rn = vfp_reg_offset(0, rn);
+        off_rm = vfp_reg_offset(0, rm);
+    }
     if (s->fp_excp_el) {
         gen_exception_insn(s, 4, EXCP_UDEF,
                            syn_simd_access_trap(1, 0xe, false), s->fp_excp_el);
@@ -8508,16 +8579,19 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn)
 
     opr_sz = (1 + q) * 8;
     if (fn_gvec_ptr) {
-        TCGv_ptr fpst = get_fpstatus_ptr(1);
-        tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd),
-                           vfp_reg_offset(1, rn),
-                           vfp_reg_offset(1, rm), fpst,
+        TCGv_ptr ptr;
+        if (ptr_is_env) {
+            ptr = cpu_env;
+        } else {
+            ptr = get_fpstatus_ptr(1);
+        }
+        tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), off_rn, off_rm, ptr,
                            opr_sz, opr_sz, data, fn_gvec_ptr);
-        tcg_temp_free_ptr(fpst);
+        if (!ptr_is_env) {
+            tcg_temp_free_ptr(ptr);
+        }
     } else {
-        tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd),
-                           vfp_reg_offset(1, rn),
-                           vfp_reg_offset(1, rm),
+        tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd), off_rn, off_rm,
                            opr_sz, opr_sz, data, fn_gvec);
     }
     return 0;
diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c
index dfc635cf9a..dedef62403 100644
--- a/target/arm/vec_helper.c
+++ b/target/arm/vec_helper.c
@@ -898,3 +898,151 @@ void HELPER(gvec_sqsub_d)(void *vd, void *vq, void *vn,
     }
     clear_tail(d, oprsz, simd_maxsz(desc));
 }
+
+/*
+ * Convert float16 to float32, raising no exceptions and
+ * preserving exceptional values, including SNaN.
+ * This is effectively an unpack+repack operation.
+ */
+static float32 float16_to_float32_by_bits(uint32_t f16, bool fz16)
+{
+    const int f16_bias = 15;
+    const int f32_bias = 127;
+    uint32_t sign = extract32(f16, 15, 1);
+    uint32_t exp = extract32(f16, 10, 5);
+    uint32_t frac = extract32(f16, 0, 10);
+
+    if (exp == 0x1f) {
+        /* Inf or NaN */
+        exp = 0xff;
+    } else if (exp == 0) {
+        /* Zero or denormal.  */
+        if (frac != 0) {
+            if (fz16) {
+                frac = 0;
+            } else {
+                /*
+                 * Denormal; these are all normal float32.
+                 * Shift the fraction so that the msb is at bit 11,
+                 * then remove bit 11 as the implicit bit of the
+                 * normalized float32.  Note that we still go through
+                 * the shift for normal numbers below, to put the
+                 * float32 fraction at the right place.
+                 */
+                int shift = clz32(frac) - 21;
+                frac = (frac << shift) & 0x3ff;
+                exp = f32_bias - f16_bias - shift + 1;
+            }
+        }
+    } else {
+        /* Normal number; adjust the bias.  */
+        exp += f32_bias - f16_bias;
+    }
+    sign <<= 31;
+    exp <<= 23;
+    frac <<= 23 - 10;
+
+    return sign | exp | frac;
+}
+
+static uint64_t load4_f16(uint64_t *ptr, int is_q, int is_2)
+{
+    /*
+     * Branchless load of u32[0], u64[0], u32[1], or u64[1].
+     * Load the 2nd qword iff is_q & is_2.
+     * Shift to the 2nd dword iff !is_q & is_2.
+     * For !is_q & !is_2, the upper bits of the result are garbage.
+     */
+    return ptr[is_q & is_2] >> ((is_2 & ~is_q) << 5);
+}
+
+/*
+ * Note that FMLAL requires oprsz == 8 or oprsz == 16,
+ * as there is not yet SVE versions that might use blocking.
+ */
+
+static void do_fmlal(float32 *d, void *vn, void *vm, float_status *fpst,
+                     uint32_t desc, bool fz16)
+{
+    intptr_t i, oprsz = simd_oprsz(desc);
+    int is_s = extract32(desc, SIMD_DATA_SHIFT, 1);
+    int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1);
+    int is_q = oprsz == 16;
+    uint64_t n_4, m_4;
+
+    /* Pre-load all of the f16 data, avoiding overlap issues.  */
+    n_4 = load4_f16(vn, is_q, is_2);
+    m_4 = load4_f16(vm, is_q, is_2);
+
+    /* Negate all inputs for FMLSL at once.  */
+    if (is_s) {
+        n_4 ^= 0x8000800080008000ull;
+    }
+
+    for (i = 0; i < oprsz / 4; i++) {
+        float32 n_1 = float16_to_float32_by_bits(n_4 >> (i * 16), fz16);
+        float32 m_1 = float16_to_float32_by_bits(m_4 >> (i * 16), fz16);
+        d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], 0, fpst);
+    }
+    clear_tail(d, oprsz, simd_maxsz(desc));
+}
+
+void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm,
+                            void *venv, uint32_t desc)
+{
+    CPUARMState *env = venv;
+    do_fmlal(vd, vn, vm, &env->vfp.standard_fp_status, desc,
+             get_flush_inputs_to_zero(&env->vfp.fp_status_f16));
+}
+
+void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm,
+                            void *venv, uint32_t desc)
+{
+    CPUARMState *env = venv;
+    do_fmlal(vd, vn, vm, &env->vfp.fp_status, desc,
+             get_flush_inputs_to_zero(&env->vfp.fp_status_f16));
+}
+
+static void do_fmlal_idx(float32 *d, void *vn, void *vm, float_status *fpst,
+                         uint32_t desc, bool fz16)
+{
+    intptr_t i, oprsz = simd_oprsz(desc);
+    int is_s = extract32(desc, SIMD_DATA_SHIFT, 1);
+    int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1);
+    int index = extract32(desc, SIMD_DATA_SHIFT + 2, 3);
+    int is_q = oprsz == 16;
+    uint64_t n_4;
+    float32 m_1;
+
+    /* Pre-load all of the f16 data, avoiding overlap issues.  */
+    n_4 = load4_f16(vn, is_q, is_2);
+
+    /* Negate all inputs for FMLSL at once.  */
+    if (is_s) {
+        n_4 ^= 0x8000800080008000ull;
+    }
+
+    m_1 = float16_to_float32_by_bits(((float16 *)vm)[H2(index)], fz16);
+
+    for (i = 0; i < oprsz / 4; i++) {
+        float32 n_1 = float16_to_float32_by_bits(n_4 >> (i * 16), fz16);
+        d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], 0, fpst);
+    }
+    clear_tail(d, oprsz, simd_maxsz(desc));
+}
+
+void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm,
+                                void *venv, uint32_t desc)
+{
+    CPUARMState *env = venv;
+    do_fmlal_idx(vd, vn, vm, &env->vfp.standard_fp_status, desc,
+                 get_flush_inputs_to_zero(&env->vfp.fp_status_f16));
+}
+
+void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm,
+                                void *venv, uint32_t desc)
+{
+    CPUARMState *env = venv;
+    do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status, desc,
+                 get_flush_inputs_to_zero(&env->vfp.fp_status_f16));
+}
diff --git a/target/mips/translate.c b/target/mips/translate.c
index 3b170208c3..364bd6dc4f 100644
--- a/target/mips/translate.c
+++ b/target/mips/translate.c
@@ -4362,6 +4362,7 @@ static void gen_shift(DisasContext *ctx, uint32_t opc,
     tcg_temp_free(t1);
 }
 
+#if defined(TARGET_MIPS64)
 /* Copy GPR to and from TX79 HI1/LO1 register. */
 static void gen_HILO1_tx79(DisasContext *ctx, uint32_t opc, int reg)
 {
@@ -4397,6 +4398,7 @@ static void gen_HILO1_tx79(DisasContext *ctx, uint32_t opc, int reg)
         break;
     }
 }
+#endif
 
 /* Arithmetic on HI/LO registers */
 static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg)
@@ -4746,6 +4748,7 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt)
     tcg_temp_free(t1);
 }
 
+#if defined(TARGET_MIPS64)
 static void gen_div1_tx79(DisasContext *ctx, uint32_t opc, int rs, int rt)
 {
     TCGv t0, t1;
@@ -4802,6 +4805,7 @@ static void gen_div1_tx79(DisasContext *ctx, uint32_t opc, int rs, int rt)
     tcg_temp_free(t0);
     tcg_temp_free(t1);
 }
+#endif
 
 static void gen_muldiv(DisasContext *ctx, uint32_t opc,
                        int acc, int rs, int rt)
@@ -24324,6 +24328,29 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx)
 }
 
 
+#if defined(TARGET_MIPS64)
+
+/*
+ *
+ *           MMI (MultiMedia Interface) ASE instructions
+ *           ===========================================
+ */
+
+/*
+ *          MMI instructions category: data communication
+ *          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *   PCPYH    PEXCH    PEXTLB   PINTH    PPACB    PEXT5    PREVH
+ *   PCPYLD   PEXCW    PEXTLH   PINTEH   PPACH    PPAC5    PROT3W
+ *   PCPYUD   PEXEH    PEXTLW            PPACW
+ *            PEXEW    PEXTUB
+ *                     PEXTUH
+ *                     PEXTUW
+ */
+
+#endif
+
+
 #if !defined(TARGET_MIPS64)
 
 /* MXU accumulate add/subtract 1-bit pattern 'aptn1' */
@@ -27247,6 +27274,9 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx)
     }
 }
 
+
+#if defined(TARGET_MIPS64)
+
 static void decode_mmi0(CPUMIPSState *env, DisasContext *ctx)
 {
     uint32_t opc = MASK_MMI0(ctx->opcode);
@@ -27491,6 +27521,8 @@ static void decode_mmi_sq(CPUMIPSState *env, DisasContext *ctx)
     gen_mmi_sq(ctx, base, rt, offset);
 }
 
+#endif
+
 static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx)
 {
     int rs, rt, rd, sa;
@@ -28796,10 +28828,11 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
         decode_opc_special(env, ctx);
         break;
     case OPC_SPECIAL2:
+#if defined(TARGET_MIPS64)
         if ((ctx->insn_flags & INSN_R5900) && (ctx->insn_flags & ASE_MMI)) {
             decode_mmi(env, ctx);
-#if !defined(TARGET_MIPS64)
-        } else if (ctx->insn_flags & ASE_MXU) {
+#else
+        if (ctx->insn_flags & ASE_MXU) {
             decode_opc_mxu(env, ctx);
 #endif
         } else {
@@ -28807,11 +28840,15 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
         }
         break;
     case OPC_SPECIAL3:
+#if defined(TARGET_MIPS64)
         if (ctx->insn_flags & INSN_R5900) {
             decode_mmi_sq(env, ctx);    /* MMI_OPC_SQ */
         } else {
             decode_opc_special3(env, ctx);
         }
+#else
+        decode_opc_special3(env, ctx);
+#endif
         break;
     case OPC_REGIMM:
         op1 = MASK_REGIMM(ctx->opcode);
@@ -29483,7 +29520,9 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
         break;
     case OPC_MSA: /* OPC_MDMX */
         if (ctx->insn_flags & INSN_R5900) {
+#if defined(TARGET_MIPS64)
             gen_mmi_lq(env, ctx);    /* MMI_OPC_LQ */
+#endif
         } else {
             /* MDMX: Not implemented. */
             gen_msa(env, ctx);
diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h
index 3130802304..ae51fe754e 100644
--- a/target/ppc/cpu-qom.h
+++ b/target/ppc/cpu-qom.h
@@ -113,6 +113,8 @@ enum powerpc_excp_t {
     POWERPC_EXCP_POWER7,
     /* POWER8 exception model           */
     POWERPC_EXCP_POWER8,
+    /* POWER9 exception model           */
+    POWERPC_EXCP_POWER9,
 };
 
 /*****************************************************************************/
@@ -122,6 +124,7 @@ typedef enum {
     PPC_PM_NAP,
     PPC_PM_SLEEP,
     PPC_PM_RVWINKLE,
+    PPC_PM_STOP,
 } powerpc_pm_insn_t;
 
 /*****************************************************************************/
@@ -139,6 +142,8 @@ enum powerpc_input_t {
     PPC_FLAGS_INPUT_970,
     /* PowerPC POWER7 bus               */
     PPC_FLAGS_INPUT_POWER7,
+    /* PowerPC POWER9 bus               */
+    PPC_FLAGS_INPUT_POWER9,
     /* PowerPC 401 bus                  */
     PPC_FLAGS_INPUT_401,
     /* Freescale RCPU bus               */
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 325ebbeb98..26604ddf98 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -160,8 +160,10 @@ enum {
     /* Server doorbell variants */
     POWERPC_EXCP_SDOOR    = 99,
     POWERPC_EXCP_SDOOR_HV = 100,
+    /* ISA 3.00 additions */
+    POWERPC_EXCP_HVIRT    = 101,
     /* EOL                                                                   */
-    POWERPC_EXCP_NB       = 101,
+    POWERPC_EXCP_NB       = 102,
     /* QEMU exceptions: used internally during code translation              */
     POWERPC_EXCP_STOP         = 0x200, /* stop translation                   */
     POWERPC_EXCP_BRANCH       = 0x201, /* branch instruction                 */
@@ -318,6 +320,10 @@ struct ppc_slb_t {
 #define SEGMENT_SHIFT_1T        40
 #define SEGMENT_MASK_1T         (~((1ULL << SEGMENT_SHIFT_1T) - 1))
 
+typedef struct ppc_v3_pate_t {
+    uint64_t dw0;
+    uint64_t dw1;
+} ppc_v3_pate_t;
 
 /*****************************************************************************/
 /* Machine state register bits definition                                    */
@@ -386,6 +392,7 @@ struct ppc_slb_t {
 #define LPCR_AIL          (3ull << LPCR_AIL_SHIFT)
 #define LPCR_UPRT         PPC_BIT(41) /* Use Process Table */
 #define LPCR_EVIRT        PPC_BIT(42) /* Enhanced Virtualisation */
+#define LPCR_HR           PPC_BIT(43) /* Host Radix */
 #define LPCR_ONL          PPC_BIT(45)
 #define LPCR_LD           PPC_BIT(46) /* Large Decrementer */
 #define LPCR_P7_PECE0     PPC_BIT(49)
@@ -414,6 +421,10 @@ struct ppc_slb_t {
 #define LPCR_HVICE        PPC_BIT(62) /* HV Virtualisation Int Enable */
 #define LPCR_HDICE        PPC_BIT(63)
 
+/* PSSCR bits */
+#define PSSCR_ESL         PPC_BIT(42) /* Enable State Loss */
+#define PSSCR_EC          PPC_BIT(43) /* Exit Criterion */
+
 #define msr_sf   ((env->msr >> MSR_SF)   & 1)
 #define msr_isf  ((env->msr >> MSR_ISF)  & 1)
 #define msr_shv  ((env->msr >> MSR_SHV)  & 1)
@@ -1110,11 +1121,13 @@ struct CPUPPCState {
      * instructions and SPRs are diallowed if MSR:HV is 0
      */
     bool has_hv_mode;
-    /* On P7/P8, set when in PM state, we need to handle resume
-     * in a special way (such as routing some resume causes to
-     * 0x100), so flag this here.
+
+    /*
+     * On P7/P8/P9, set when in PM state, we need to handle resume in
+     * a special way (such as routing some resume causes to 0x100, ie,
+     * sreset), so flag this here.
      */
-    bool in_pm_state;
+    bool resume_as_sreset;
 #endif
 
     /* Those resources are used only during code translation */
@@ -1239,7 +1252,7 @@ struct PPCVirtualHypervisorClass {
                         hwaddr ptex, int n);
     void (*store_hpte)(PPCVirtualHypervisor *vhyp, hwaddr ptex,
                        uint64_t pte0, uint64_t pte1);
-    uint64_t (*get_patbe)(PPCVirtualHypervisor *vhyp);
+    void (*get_pate)(PPCVirtualHypervisor *vhyp, ppc_v3_pate_t *entry);
     target_ulong (*encode_hpt_for_kvm_pr)(PPCVirtualHypervisor *vhyp);
 };
 
@@ -2319,6 +2332,13 @@ enum {
      * them */
     POWER7_INPUT_NB,
 };
+
+enum {
+    /* POWER9 input pins */
+    POWER9_INPUT_INT        = 0,
+    POWER9_INPUT_HINT       = 1,
+    POWER9_INPUT_NB,
+};
 #endif
 
 /* Hardware exceptions definitions */
@@ -2343,6 +2363,7 @@ enum {
     PPC_INTERRUPT_PERFM,          /* Performance monitor interrupt        */
     PPC_INTERRUPT_HMI,            /* Hypervisor Maintainance interrupt    */
     PPC_INTERRUPT_HDOORBELL,      /* Hypervisor Doorbell interrupt        */
+    PPC_INTERRUPT_HVIRT,          /* Hypervisor virtualization interrupt  */
 };
 
 /* Processor Compatibility mask (PCR) */
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 751d759fcc..39bedbb11d 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -65,6 +65,49 @@ static inline void dump_syscall(CPUPPCState *env)
                   ppc_dump_gpr(env, 6), env->nip);
 }
 
+static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
+                                target_ulong *msr)
+{
+    /* We no longer are in a PM state */
+    env->resume_as_sreset = false;
+
+    /* Pretend to be returning from doze always as we don't lose state */
+    *msr |= (0x1ull << (63 - 47));
+
+    /* Machine checks are sent normally */
+    if (excp == POWERPC_EXCP_MCHECK) {
+        return excp;
+    }
+    switch (excp) {
+    case POWERPC_EXCP_RESET:
+        *msr |= 0x4ull << (63 - 45);
+        break;
+    case POWERPC_EXCP_EXTERNAL:
+        *msr |= 0x8ull << (63 - 45);
+        break;
+    case POWERPC_EXCP_DECR:
+        *msr |= 0x6ull << (63 - 45);
+        break;
+    case POWERPC_EXCP_SDOOR:
+        *msr |= 0x5ull << (63 - 45);
+        break;
+    case POWERPC_EXCP_SDOOR_HV:
+        *msr |= 0x3ull << (63 - 45);
+        break;
+    case POWERPC_EXCP_HV_MAINT:
+        *msr |= 0xaull << (63 - 45);
+        break;
+    case POWERPC_EXCP_HVIRT:
+        *msr |= 0x9ull << (63 - 45);
+        break;
+    default:
+        cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
+                  excp);
+    }
+    return POWERPC_EXCP_RESET;
+}
+
+
 /* Note that this function should be greatly optimized
  * when called with a constant excp, from ppc_hw_interrupt
  */
@@ -97,47 +140,17 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     asrr0 = -1;
     asrr1 = -1;
 
-    /* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */
-    if (env->in_pm_state) {
-        env->in_pm_state = false;
-
-        /* Pretend to be returning from doze always as we don't lose state */
-        msr |= (0x1ull << (63 - 47));
-
-        /* Non-machine check are routed to 0x100 with a wakeup cause
-         * encoded in SRR1
-         */
-        if (excp != POWERPC_EXCP_MCHECK) {
-            switch (excp) {
-            case POWERPC_EXCP_RESET:
-                msr |= 0x4ull << (63 - 45);
-                break;
-            case POWERPC_EXCP_EXTERNAL:
-                msr |= 0x8ull << (63 - 45);
-                break;
-            case POWERPC_EXCP_DECR:
-                msr |= 0x6ull << (63 - 45);
-                break;
-            case POWERPC_EXCP_SDOOR:
-                msr |= 0x5ull << (63 - 45);
-                break;
-            case POWERPC_EXCP_SDOOR_HV:
-                msr |= 0x3ull << (63 - 45);
-                break;
-            case POWERPC_EXCP_HV_MAINT:
-                msr |= 0xaull << (63 - 45);
-                break;
-            default:
-                cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
-                          excp);
-            }
-            excp = POWERPC_EXCP_RESET;
-        }
+    /*
+     * check for special resume at 0x100 from doze/nap/sleep/winkle on
+     * P7/P8/P9
+     */
+    if (env->resume_as_sreset) {
+        excp = powerpc_reset_wakeup(cs, env, excp, &msr);
     }
 
     /* Exception targetting modifiers
      *
-     * LPES0 is supported on POWER7/8
+     * LPES0 is supported on POWER7/8/9
      * LPES1 is not supported (old iSeries mode)
      *
      * On anything else, we behave as if LPES0 is 1
@@ -148,9 +161,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
      */
 #if defined(TARGET_PPC64)
     if (excp_model == POWERPC_EXCP_POWER7 ||
-        excp_model == POWERPC_EXCP_POWER8) {
+        excp_model == POWERPC_EXCP_POWER8 ||
+        excp_model == POWERPC_EXCP_POWER9) {
         lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
-        if (excp_model == POWERPC_EXCP_POWER8) {
+        if (excp_model != POWERPC_EXCP_POWER7) {
             ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
         } else {
             ail = 0;
@@ -416,6 +430,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     case POWERPC_EXCP_HISEG:     /* Hypervisor instruction segment exception */
     case POWERPC_EXCP_SDOOR_HV:  /* Hypervisor Doorbell interrupt            */
     case POWERPC_EXCP_HV_EMU:
+    case POWERPC_EXCP_HVIRT:     /* Hypervisor virtualization                */
         srr0 = SPR_HSRR0;
         srr1 = SPR_HSRR1;
         new_msr |= (target_ulong)MSR_HVB;
@@ -652,7 +667,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         }
     } else if (excp_model == POWERPC_EXCP_POWER8) {
         if (new_msr & MSR_HVB) {
-            if (env->spr[SPR_HID0] & (HID0_HILE | HID0_POWER9_HILE)) {
+            if (env->spr[SPR_HID0] & HID0_HILE) {
+                new_msr |= (target_ulong)1 << MSR_LE;
+            }
+        } else if (env->spr[SPR_LPCR] & LPCR_ILE) {
+            new_msr |= (target_ulong)1 << MSR_LE;
+        }
+    } else if (excp_model == POWERPC_EXCP_POWER9) {
+        if (new_msr & MSR_HVB) {
+            if (env->spr[SPR_HID0] & HID0_POWER9_HILE) {
                 new_msr |= (target_ulong)1 << MSR_LE;
             }
         } else if (env->spr[SPR_LPCR] & LPCR_ILE) {
@@ -748,6 +771,7 @@ void ppc_cpu_do_interrupt(CPUState *cs)
 static void ppc_hw_interrupt(CPUPPCState *env)
 {
     PowerPCCPU *cpu = ppc_env_get_cpu(env);
+    bool async_deliver;
 
     /* External reset */
     if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
@@ -769,21 +793,44 @@ static void ppc_hw_interrupt(CPUPPCState *env)
         return;
     }
 #endif
+
+    /*
+     * For interrupts that gate on MSR:EE, we need to do something a
+     * bit more subtle, as we need to let them through even when EE is
+     * clear when coming out of some power management states (in order
+     * for them to become a 0x100).
+     */
+    async_deliver = (msr_ee != 0) || env->resume_as_sreset;
+
     /* Hypervisor decrementer exception */
     if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
         /* LPCR will be clear when not supported so this will work */
         bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
-        if ((msr_ee != 0 || msr_hv == 0) && hdice) {
+        if ((async_deliver || msr_hv == 0) && hdice) {
             /* HDEC clears on delivery */
             env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
             powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR);
             return;
         }
     }
-    /* Extermal interrupt can ignore MSR:EE under some circumstances */
+
+    /* Hypervisor virtualization interrupt */
+    if (env->pending_interrupts & (1 << PPC_INTERRUPT_HVIRT)) {
+        /* LPCR will be clear when not supported so this will work */
+        bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE);
+        if ((async_deliver || msr_hv == 0) && hvice) {
+            powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HVIRT);
+            return;
+        }
+    }
+
+    /* External interrupt can ignore MSR:EE under some circumstances */
     if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) {
         bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
-        if (msr_ee != 0 || (env->has_hv_mode && msr_hv == 0 && !lpes0)) {
+        bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
+        /* HEIC blocks delivery to the hypervisor */
+        if ((async_deliver && !(heic && msr_hv && !msr_pr)) ||
+            (env->has_hv_mode && msr_hv == 0 && !lpes0)) {
             powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL);
             return;
         }
@@ -795,7 +842,7 @@ static void ppc_hw_interrupt(CPUPPCState *env)
             return;
         }
     }
-    if (msr_ee != 0) {
+    if (async_deliver != 0) {
         /* Watchdog timer on embedded PowerPC */
         if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) {
             env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT);
@@ -849,6 +896,22 @@ static void ppc_hw_interrupt(CPUPPCState *env)
             return;
         }
     }
+
+    if (env->resume_as_sreset) {
+        /*
+         * This is a bug ! It means that has_work took us out of halt without
+         * anything to deliver while in a PM state that requires getting
+         * out via a 0x100
+         *
+         * This means we will incorrectly execute past the power management
+         * instruction instead of triggering a reset.
+         *
+         * It generally means a discrepancy between the wakup conditions in the
+         * processor has_work implementation and the logic in this function.
+         */
+        cpu_abort(CPU(ppc_env_get_cpu(env)),
+                  "Wakeup from PM state but interrupt Undelivered");
+    }
 }
 
 void ppc_cpu_do_system_reset(CPUState *cs)
@@ -943,22 +1006,15 @@ void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
 
     cs = CPU(ppc_env_get_cpu(env));
     cs->halted = 1;
-    env->in_pm_state = true;
 
     /* The architecture specifies that HDEC interrupts are
      * discarded in PM states
      */
     env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
 
-    /* Technically, nap doesn't set EE, but if we don't set it
-     * then ppc_hw_interrupt() won't deliver. We could add some
-     * other tests there based on LPCR but it's simpler to just
-     * whack EE in. It will be cleared by the 0x100 at wakeup
-     * anyway. It will still be observable by the guest in SRR1
-     * but this doesn't seem to be a problem.
-     */
-    env->msr |= (1ull << MSR_EE);
-    raise_exception(env, EXCP_HLT);
+    /* Condition for waking up at 0x100 */
+    env->resume_as_sreset = (insn != PPC_PM_STOP) ||
+        (env->spr[SPR_PSSCR] & PSSCR_EC);
 }
 #endif /* defined(TARGET_PPC64) */
 
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index 18910d18a4..638a6e99c4 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -689,6 +689,7 @@ DEF_HELPER_2(store_ptcr, void, env, tl)
 #endif
 DEF_HELPER_2(store_sdr1, void, env, tl)
 DEF_HELPER_2(store_pidr, void, env, tl)
+DEF_HELPER_2(store_lpidr, void, env, tl)
 DEF_HELPER_FLAGS_2(store_tbl, TCG_CALL_NO_RWG, void, env, tl)
 DEF_HELPER_FLAGS_2(store_tbu, TCG_CALL_NO_RWG, void, env, tl)
 DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl)
diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h
index 5efd18049e..a2205e1044 100644
--- a/target/ppc/helper_regs.h
+++ b/target/ppc/helper_regs.h
@@ -174,26 +174,19 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
 static inline void check_tlb_flush(CPUPPCState *env, bool global)
 {
     CPUState *cs = CPU(ppc_env_get_cpu(env));
-    if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) {
-        tlb_flush(cs);
-        env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
-    }
 
-    /* Propagate TLB invalidations to other CPUs when the guest uses broadcast
-     * TLB invalidation instructions.
-     */
+    /* Handle global flushes first */
     if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) {
-        CPUState *other_cs;
-        CPU_FOREACH(other_cs) {
-            if (other_cs != cs) {
-                PowerPCCPU *cpu = POWERPC_CPU(other_cs);
-                CPUPPCState *other_env = &cpu->env;
-
-                other_env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
-                tlb_flush(other_cs);
-            }
-        }
         env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH;
+        env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
+        tlb_flush_all_cpus_synced(cs);
+        return;
+    }
+
+    /* Then handle local ones */
+    if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) {
+        env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
+        tlb_flush(cs);
     }
 }
 #else
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index b884930096..c65d1ade15 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -117,6 +117,21 @@ void helper_store_pidr(CPUPPCState *env, target_ulong val)
     tlb_flush(CPU(cpu));
 }
 
+void helper_store_lpidr(CPUPPCState *env, target_ulong val)
+{
+    PowerPCCPU *cpu = ppc_env_get_cpu(env);
+
+    env->spr[SPR_LPIDR] = val;
+
+    /*
+     * We need to flush the TLB on LPID changes as we only tag HV vs
+     * guest in TCG TLB. Also the quadrants means the HV will
+     * potentially access and cache entries for the current LPID as
+     * well.
+     */
+    tlb_flush(CPU(cpu));
+}
+
 void helper_store_hid0_601(CPUPPCState *env, target_ulong val)
 {
     target_ulong hid0;
diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c
index b60df4408f..32b8c166b5 100644
--- a/target/ppc/mmu-book3s-v3.c
+++ b/target/ppc/mmu-book3s-v3.c
@@ -26,9 +26,36 @@
 int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
                               int mmu_idx)
 {
-    if (ppc64_radix_guest(cpu)) { /* Guest uses radix */
+    if (ppc64_v3_radix(cpu)) { /* Guest uses radix */
         return ppc_radix64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
     } else { /* Guest uses hash */
         return ppc_hash64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
     }
 }
+
+hwaddr ppc64_v3_get_phys_page_debug(PowerPCCPU *cpu, vaddr eaddr)
+{
+    if (ppc64_v3_radix(cpu)) {
+        return ppc_radix64_get_phys_page_debug(cpu, eaddr);
+    } else {
+        return ppc_hash64_get_phys_page_debug(cpu, eaddr);
+    }
+}
+
+bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry)
+{
+    uint64_t patb = cpu->env.spr[SPR_PTCR] & PTCR_PATB;
+    uint64_t pats = cpu->env.spr[SPR_PTCR] & PTCR_PATS;
+
+    /* Calculate number of entries */
+    pats = 1ull << (pats + 12 - 4);
+    if (pats <= lpid) {
+        return false;
+    }
+
+    /* Grab entry */
+    patb += 16 * lpid;
+    entry->dw0 = ldq_phys(CPU(cpu)->as, patb);
+    entry->dw1 = ldq_phys(CPU(cpu)->as, patb + 8);
+    return true;
+}
diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h
index fdf80987d7..ee8288e32d 100644
--- a/target/ppc/mmu-book3s-v3.h
+++ b/target/ppc/mmu-book3s-v3.h
@@ -17,8 +17,10 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef MMU_H
-#define MMU_H
+#ifndef MMU_BOOOK3S_V3_H
+#define MMU_BOOOK3S_V3_H
+
+#include "mmu-hash64.h"
 
 #ifndef CONFIG_USER_ONLY
 
@@ -29,7 +31,16 @@
 #define PTCR_PATS               0x000000000000001FULL /* Partition Table Size */
 
 /* Partition Table Entry Fields */
-#define PATBE1_GR 0x8000000000000000
+#define PATE0_HR 0x8000000000000000
+
+/*
+ * WARNING: This field doesn't actually exist in the final version of
+ * the architecture and is unused by hardware. However, qemu uses it
+ * as an indication of a radix guest in the pseudo-PATB entry that it
+ * maintains for SPAPR guests and in the migration stream, so we need
+ * to keep it around
+ */
+#define PATE1_GR 0x8000000000000000
 
 /* Process Table Entry */
 struct prtb_entry {
@@ -43,19 +54,68 @@ static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu)
     return !!(cpu->env.spr[SPR_LPCR] & LPCR_UPRT);
 }
 
-static inline bool ppc64_radix_guest(PowerPCCPU *cpu)
-{
-    PPCVirtualHypervisorClass *vhc =
-        PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid,
+                       ppc_v3_pate_t *entry);
 
-    return !!(vhc->get_patbe(cpu->vhyp) & PATBE1_GR);
+/*
+ * The LPCR:HR bit is a shortcut that avoids having to
+ * dig out the partition table in the fast path. This is
+ * also how the HW uses it.
+ */
+static inline bool ppc64_v3_radix(PowerPCCPU *cpu)
+{
+    return !!(cpu->env.spr[SPR_LPCR] & LPCR_HR);
 }
 
+hwaddr ppc64_v3_get_phys_page_debug(PowerPCCPU *cpu, vaddr eaddr);
+
 int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
                               int mmu_idx);
 
+static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu)
+{
+    uint64_t base;
+
+    if (cpu->vhyp) {
+        return 0;
+    }
+    if (cpu->env.mmu_model == POWERPC_MMU_3_00) {
+        ppc_v3_pate_t pate;
+
+        if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) {
+            return 0;
+        }
+        base = pate.dw0;
+    } else {
+        base = cpu->env.spr[SPR_SDR1];
+    }
+    return base & SDR_64_HTABORG;
+}
+
+static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu)
+{
+    uint64_t base;
+
+    if (cpu->vhyp) {
+        PPCVirtualHypervisorClass *vhc =
+            PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+        return vhc->hpt_mask(cpu->vhyp);
+    }
+    if (cpu->env.mmu_model == POWERPC_MMU_3_00) {
+        ppc_v3_pate_t pate;
+
+        if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) {
+            return 0;
+        }
+        base = pate.dw0;
+    } else {
+        base = cpu->env.spr[SPR_SDR1];
+    }
+    return (1ULL << ((base & SDR_64_HTABSIZE) + 18 - 7)) - 1;
+}
+
 #endif /* TARGET_PPC64 */
 
 #endif /* CONFIG_USER_ONLY */
 
-#endif /* MMU_H */
+#endif /* MMU_BOOOK3S_V3_H */
diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c
index 03ae3c1279..e8562a7c87 100644
--- a/target/ppc/mmu-hash32.c
+++ b/target/ppc/mmu-hash32.c
@@ -319,6 +319,12 @@ static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off,
 
     for (i = 0; i < HPTES_PER_GROUP; i++) {
         pte0 = ppc_hash32_load_hpte0(cpu, pte_offset);
+        /*
+         * pte0 contains the valid bit and must be read before pte1,
+         * otherwise we might see an old pte1 with a new valid bit and
+         * thus an inconsistent hpte value
+         */
+        smp_rmb();
         pte1 = ppc_hash32_load_hpte1(cpu, pte_offset);
 
         if ((pte0 & HPTE32_V_VALID)
diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c
index 276d9015e7..c431303eff 100644
--- a/target/ppc/mmu-hash64.c
+++ b/target/ppc/mmu-hash64.c
@@ -417,7 +417,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu,
                                              hwaddr ptex, int n)
 {
     hwaddr pte_offset = ptex * HASH_PTE_SIZE_64;
-    hwaddr base = ppc_hash64_hpt_base(cpu);
+    hwaddr base;
     hwaddr plen = n * HASH_PTE_SIZE_64;
     const ppc_hash_pte64_t *hptes;
 
@@ -426,6 +426,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu,
             PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
         return vhc->map_hptes(cpu->vhyp, ptex, n);
     }
+    base = ppc_hash64_hpt_base(cpu);
 
     if (!base) {
         return NULL;
@@ -490,6 +491,18 @@ static unsigned hpte_page_shift(const PPCHash64SegmentPageSizes *sps,
     return 0; /* Bad page size encoding */
 }
 
+static void ppc64_v3_new_to_old_hpte(target_ulong *pte0, target_ulong *pte1)
+{
+    /* Insert B into pte0 */
+    *pte0 = (*pte0 & HPTE64_V_COMMON_BITS) |
+            ((*pte1 & HPTE64_R_3_0_SSIZE_MASK) <<
+             (HPTE64_V_SSIZE_SHIFT - HPTE64_R_3_0_SSIZE_SHIFT));
+
+    /* Remove B from pte1 */
+    *pte1 = *pte1 & ~HPTE64_R_3_0_SSIZE_MASK;
+}
+
+
 static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash,
                                      const PPCHash64SegmentPageSizes *sps,
                                      target_ulong ptem,
@@ -507,8 +520,19 @@ static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash,
     }
     for (i = 0; i < HPTES_PER_GROUP; i++) {
         pte0 = ppc_hash64_hpte0(cpu, pteg, i);
+        /*
+         * pte0 contains the valid bit and must be read before pte1,
+         * otherwise we might see an old pte1 with a new valid bit and
+         * thus an inconsistent hpte value
+         */
+        smp_rmb();
         pte1 = ppc_hash64_hpte1(cpu, pteg, i);
 
+        /* Convert format if necessary */
+        if (cpu->env.mmu_model == POWERPC_MMU_3_00 && !cpu->vhyp) {
+            ppc64_v3_new_to_old_hpte(&pte0, &pte1);
+        }
+
         /* This compares V, B, H (secondary) and the AVPN */
         if (HPTE64_V_COMPARE(pte0, ptem)) {
             *pshift = hpte_page_shift(sps, pte0, pte1);
@@ -918,7 +942,7 @@ hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr)
 void ppc_hash64_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
                            uint64_t pte0, uint64_t pte1)
 {
-    hwaddr base = ppc_hash64_hpt_base(cpu);
+    hwaddr base;
     hwaddr offset = ptex * HASH_PTE_SIZE_64;
 
     if (cpu->vhyp) {
@@ -927,6 +951,7 @@ void ppc_hash64_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
         vhc->store_hpte(cpu->vhyp, ptex, pte0, pte1);
         return;
     }
+    base = ppc_hash64_hpt_base(cpu);
 
     stq_phys(CPU(cpu)->as, base + offset, pte0);
     stq_phys(CPU(cpu)->as, base + offset + HASH_PTE_SIZE_64 / 2, pte1);
@@ -1084,10 +1109,18 @@ void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val)
     case POWERPC_MMU_3_00: /* P9 */
         lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD |
                       (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL |
-                      LPCR_UPRT | LPCR_EVIRT | LPCR_ONL |
+                      LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR |
                       (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE |
                       LPCR_DEE | LPCR_OEE)) | LPCR_MER | LPCR_GTSE | LPCR_TC |
                       LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE);
+        /*
+         * If we have a virtual hypervisor, we need to bring back RMLS. It
+         * doesn't exist on an actual P9 but that's all we know how to
+         * configure with softmmu at the moment
+         */
+        if (cpu->vhyp) {
+            lpcr |= (val & LPCR_RMLS);
+        }
         break;
     default:
         ;
diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h
index f11efc9cbc..6b555b7220 100644
--- a/target/ppc/mmu-hash64.h
+++ b/target/ppc/mmu-hash64.h
@@ -63,6 +63,7 @@ void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu,
 #define SDR_64_HTABORG         0x0FFFFFFFFFFC0000ULL
 #define SDR_64_HTABSIZE        0x000000000000001FULL
 
+#define PATE0_HTABORG           0x0FFFFFFFFFFC0000ULL
 #define HPTES_PER_GROUP         8
 #define HASH_PTE_SIZE_64        16
 #define HASH_PTEG_SIZE_64       (HASH_PTE_SIZE_64 * HPTES_PER_GROUP)
@@ -102,23 +103,10 @@ void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu,
 #define HPTE64_V_1TB_SEG        0x4000000000000000ULL
 #define HPTE64_V_VRMA_MASK      0x4001ffffff000000ULL
 
-static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu)
-{
-    if (cpu->vhyp) {
-        return 0;
-    }
-    return cpu->env.spr[SPR_SDR1] & SDR_64_HTABORG;
-}
-
-static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu)
-{
-    if (cpu->vhyp) {
-        PPCVirtualHypervisorClass *vhc =
-            PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
-        return vhc->hpt_mask(cpu->vhyp);
-    }
-    return (1ULL << ((cpu->env.spr[SPR_SDR1] & SDR_64_HTABSIZE) + 18 - 7)) - 1;
-}
+/* Format changes for ARCH v3 */
+#define HPTE64_V_COMMON_BITS    0x000fffffffffffffULL
+#define HPTE64_R_3_0_SSIZE_SHIFT 58
+#define HPTE64_R_3_0_SSIZE_MASK (3ULL << HPTE64_R_3_0_SSIZE_SHIFT)
 
 struct ppc_hash_pte64 {
     uint64_t pte0, pte1;
diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c
index ab76cbc835..ca1fb2673f 100644
--- a/target/ppc/mmu-radix64.c
+++ b/target/ppc/mmu-radix64.c
@@ -31,10 +31,26 @@
 static bool ppc_radix64_get_fully_qualified_addr(CPUPPCState *env, vaddr eaddr,
                                                  uint64_t *lpid, uint64_t *pid)
 {
-    /* We don't have HV support yet and shouldn't get here with it set anyway */
-    assert(!msr_hv);
-
-    if (!msr_hv) { /* !MSR[HV] -> Guest */
+    if (msr_hv) { /* MSR[HV] -> Hypervisor/bare metal */
+        switch (eaddr & R_EADDR_QUADRANT) {
+        case R_EADDR_QUADRANT0:
+            *lpid = 0;
+            *pid = env->spr[SPR_BOOKS_PID];
+            break;
+        case R_EADDR_QUADRANT1:
+            *lpid = env->spr[SPR_LPIDR];
+            *pid = env->spr[SPR_BOOKS_PID];
+            break;
+        case R_EADDR_QUADRANT2:
+            *lpid = env->spr[SPR_LPIDR];
+            *pid = 0;
+            break;
+        case R_EADDR_QUADRANT3:
+            *lpid = 0;
+            *pid = 0;
+            break;
+        }
+    } else {  /* !MSR[HV] -> Guest */
         switch (eaddr & R_EADDR_QUADRANT) {
         case R_EADDR_QUADRANT0: /* Guest application */
             *lpid = env->spr[SPR_LPIDR];
@@ -186,20 +202,32 @@ static uint64_t ppc_radix64_walk_tree(PowerPCCPU *cpu, vaddr eaddr,
                                  raddr, psize, fault_cause, pte_addr);
 }
 
+static bool validate_pate(PowerPCCPU *cpu, uint64_t lpid, ppc_v3_pate_t *pate)
+{
+    CPUPPCState *env = &cpu->env;
+
+    if (!(pate->dw0 & PATE0_HR)) {
+        return false;
+    }
+    if (lpid == 0 && !msr_hv) {
+        return false;
+    }
+    /* More checks ... */
+    return true;
+}
+
 int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
                                  int mmu_idx)
 {
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
-    PPCVirtualHypervisorClass *vhc =
-        PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+    PPCVirtualHypervisorClass *vhc;
     hwaddr raddr, pte_addr;
-    uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte;
+    uint64_t lpid = 0, pid = 0, offset, size, prtbe0, pte;
     int page_size, prot, fault_cause = 0;
+    ppc_v3_pate_t pate;
 
     assert((rwx == 0) || (rwx == 1) || (rwx == 2));
-    assert(!msr_hv); /* For now there is no Radix PowerNV Support */
-    assert(cpu->vhyp);
     assert(ppc64_use_proc_tbl(cpu));
 
     /* Real Mode Access */
@@ -220,17 +248,33 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
     }
 
     /* Get Process Table */
-    patbe = vhc->get_patbe(cpu->vhyp);
+    if (cpu->vhyp) {
+        vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+        vhc->get_pate(cpu->vhyp, &pate);
+    } else {
+        if (!ppc64_v3_get_pate(cpu, lpid, &pate)) {
+            ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
+            return 1;
+        }
+        if (!validate_pate(cpu, lpid, &pate)) {
+            ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_R_BADCONFIG);
+        }
+        /* We don't support guest mode yet */
+        if (lpid != 0) {
+            error_report("PowerNV guest support Unimplemented");
+            exit(1);
+       }
+    }
 
     /* Index Process Table by PID to Find Corresponding Process Table Entry */
     offset = pid * sizeof(struct prtb_entry);
-    size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
+    size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12);
     if (offset >= size) {
         /* offset exceeds size of the process table */
         ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
         return 1;
     }
-    prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
+    prtbe0 = ldq_phys(cs->as, (pate.dw1 & PATE1_R_PRTB) + offset);
 
     /* Walk Radix Tree from Process Table Entry to Convert EA to RA */
     page_size = PRTBE_R_GET_RTS(prtbe0);
@@ -255,11 +299,11 @@ hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr)
 {
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
-    PPCVirtualHypervisorClass *vhc =
-        PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+    PPCVirtualHypervisorClass *vhc;
     hwaddr raddr, pte_addr;
-    uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte;
+    uint64_t lpid = 0, pid = 0, offset, size, prtbe0, pte;
     int page_size, fault_cause = 0;
+    ppc_v3_pate_t pate;
 
     /* Handle Real Mode */
     if (msr_dr == 0) {
@@ -273,16 +317,31 @@ hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr)
     }
 
     /* Get Process Table */
-    patbe = vhc->get_patbe(cpu->vhyp);
+    if (cpu->vhyp) {
+        vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+        vhc->get_pate(cpu->vhyp, &pate);
+    } else {
+        if (!ppc64_v3_get_pate(cpu, lpid, &pate)) {
+            return -1;
+        }
+        if (!validate_pate(cpu, lpid, &pate)) {
+            return -1;
+        }
+        /* We don't support guest mode yet */
+        if (lpid != 0) {
+            error_report("PowerNV guest support Unimplemented");
+            exit(1);
+       }
+    }
 
     /* Index Process Table by PID to Find Corresponding Process Table Entry */
     offset = pid * sizeof(struct prtb_entry);
-    size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
+    size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12);
     if (offset >= size) {
         /* offset exceeds size of the process table */
         return -1;
     }
-    prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
+    prtbe0 = ldq_phys(cs->as, (pate.dw1 & PATE1_R_PRTB) + offset);
 
     /* Walk Radix Tree from Process Table Entry to Convert EA to RA */
     page_size = PRTBE_R_GET_RTS(prtbe0);
diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h
index 0ecf063a17..96228546aa 100644
--- a/target/ppc/mmu-radix64.h
+++ b/target/ppc/mmu-radix64.h
@@ -12,8 +12,8 @@
 #define R_EADDR_QUADRANT3       0xC000000000000000
 
 /* Radix Partition Table Entry Fields */
-#define PATBE1_R_PRTB           0x0FFFFFFFFFFFF000
-#define PATBE1_R_PRTS           0x000000000000001F
+#define PATE1_R_PRTB           0x0FFFFFFFFFFFF000
+#define PATE1_R_PRTS           0x000000000000001F
 
 /* Radix Process Table Entry Fields */
 #define PRTBE_R_GET_RTS(rts) \
diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c
index bcf19da61d..4a6be4d63b 100644
--- a/target/ppc/mmu_helper.c
+++ b/target/ppc/mmu_helper.c
@@ -1342,7 +1342,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
         dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env));
         break;
     case POWERPC_MMU_3_00:
-        if (ppc64_radix_guest(ppc_env_get_cpu(env))) {
+        if (ppc64_v3_radix(ppc_env_get_cpu(env))) {
             /* TODO - Unsupported */
         } else {
             dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env));
@@ -1489,12 +1489,7 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
     case POWERPC_MMU_2_07:
         return ppc_hash64_get_phys_page_debug(cpu, addr);
     case POWERPC_MMU_3_00:
-        if (ppc64_radix_guest(ppc_env_get_cpu(env))) {
-            return ppc_radix64_get_phys_page_debug(cpu, addr);
-        } else {
-            return ppc_hash64_get_phys_page_debug(cpu, addr);
-        }
-        break;
+        return ppc64_v3_get_phys_page_debug(cpu, addr);
 #endif
 
     case POWERPC_MMU_32B:
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index f4d70e725a..819221f246 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -3566,7 +3566,8 @@ static void gen_doze(DisasContext *ctx)
     t = tcg_const_i32(PPC_PM_DOZE);
     gen_helper_pminsn(cpu_env, t);
     tcg_temp_free_i32(t);
-    gen_stop_exception(ctx);
+    /* Stop translation, as the CPU is supposed to sleep from now */
+    gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next);
 #endif /* defined(CONFIG_USER_ONLY) */
 }
 
@@ -3581,13 +3582,25 @@ static void gen_nap(DisasContext *ctx)
     t = tcg_const_i32(PPC_PM_NAP);
     gen_helper_pminsn(cpu_env, t);
     tcg_temp_free_i32(t);
-    gen_stop_exception(ctx);
+    /* Stop translation, as the CPU is supposed to sleep from now */
+    gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next);
 #endif /* defined(CONFIG_USER_ONLY) */
 }
 
 static void gen_stop(DisasContext *ctx)
 {
-    gen_nap(ctx);
+#if defined(CONFIG_USER_ONLY)
+    GEN_PRIV;
+#else
+    TCGv_i32 t;
+
+    CHK_HV;
+    t = tcg_const_i32(PPC_PM_STOP);
+    gen_helper_pminsn(cpu_env, t);
+    tcg_temp_free_i32(t);
+    /* Stop translation, as the CPU is supposed to sleep from now */
+    gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next);
+#endif /* defined(CONFIG_USER_ONLY) */
 }
 
 static void gen_sleep(DisasContext *ctx)
@@ -3601,7 +3614,8 @@ static void gen_sleep(DisasContext *ctx)
     t = tcg_const_i32(PPC_PM_SLEEP);
     gen_helper_pminsn(cpu_env, t);
     tcg_temp_free_i32(t);
-    gen_stop_exception(ctx);
+    /* Stop translation, as the CPU is supposed to sleep from now */
+    gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next);
 #endif /* defined(CONFIG_USER_ONLY) */
 }
 
@@ -3616,7 +3630,8 @@ static void gen_rvwinkle(DisasContext *ctx)
     t = tcg_const_i32(PPC_PM_RVWINKLE);
     gen_helper_pminsn(cpu_env, t);
     tcg_temp_free_i32(t);
-    gen_stop_exception(ctx);
+    /* Stop translation, as the CPU is supposed to sleep from now */
+    gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next);
 #endif /* defined(CONFIG_USER_ONLY) */
 }
 #endif /* #if defined(TARGET_PPC64) */
@@ -7466,7 +7481,8 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
 
 #if defined(TARGET_PPC64)
     if (env->excp_model == POWERPC_EXCP_POWER7 ||
-        env->excp_model == POWERPC_EXCP_POWER8) {
+        env->excp_model == POWERPC_EXCP_POWER8 ||
+        env->excp_model == POWERPC_EXCP_POWER9)  {
         cpu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n",
                     env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
     }
diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c
index d884906004..58542c0fe0 100644
--- a/target/ppc/translate_init.inc.c
+++ b/target/ppc/translate_init.inc.c
@@ -408,6 +408,11 @@ static void spr_write_pidr(DisasContext *ctx, int sprn, int gprn)
     gen_helper_store_pidr(cpu_env, cpu_gpr[gprn]);
 }
 
+static void spr_write_lpidr(DisasContext *ctx, int sprn, int gprn)
+{
+    gen_helper_store_lpidr(cpu_env, cpu_gpr[gprn]);
+}
+
 static void spr_read_hior(DisasContext *ctx, int gprn, int sprn)
 {
     tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, excp_prefix));
@@ -3313,6 +3318,15 @@ static void init_excp_POWER8(CPUPPCState *env)
 #endif
 }
 
+static void init_excp_POWER9(CPUPPCState *env)
+{
+    init_excp_POWER8(env);
+
+#if !defined(CONFIG_USER_ONLY)
+    env->excp_vectors[POWERPC_EXCP_HVIRT]    = 0x00000EA0;
+#endif
+}
+
 #endif
 
 /*****************************************************************************/
@@ -7876,7 +7890,7 @@ static void gen_spr_book3s_ids(CPUPPCState *env)
     spr_register_hv(env, SPR_LPIDR, "LPIDR",
                  SPR_NOACCESS, SPR_NOACCESS,
                  SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
+                 &spr_read_generic, &spr_write_lpidr,
                  0x00000000);
     spr_register_hv(env, SPR_HFSCR, "HFSCR",
                  SPR_NOACCESS, SPR_NOACCESS,
@@ -8783,8 +8797,8 @@ static void init_proc_POWER9(CPUPPCState *env)
     env->icache_line_size = 128;
 
     /* Allocate hardware IRQ controller */
-    init_excp_POWER8(env);
-    ppcPOWER7_irq_init(ppc_env_get_cpu(env));
+    init_excp_POWER9(env);
+    ppcPOWER9_irq_init(ppc_env_get_cpu(env));
 }
 
 static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr)
@@ -8801,13 +8815,23 @@ static bool cpu_has_work_POWER9(CPUState *cs)
     CPUPPCState *env = &cpu->env;
 
     if (cs->halted) {
+        uint64_t psscr = env->spr[SPR_PSSCR];
+
         if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
             return false;
         }
+
+        /* If EC is clear, just return true on any pending interrupt */
+        if (!(psscr & PSSCR_EC)) {
+            return true;
+        }
         /* External Exception */
         if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
             (env->spr[SPR_LPCR] & LPCR_EEE)) {
-            return true;
+            bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
+            if (heic == 0 || !msr_hv || msr_pr) {
+                return true;
+            }
         }
         /* Decrementer Exception */
         if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
@@ -8829,6 +8853,11 @@ static bool cpu_has_work_POWER9(CPUState *cs)
             (env->spr[SPR_LPCR] & LPCR_HDEE)) {
             return true;
         }
+        /* Hypervisor virtualization exception */
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HVIRT)) &&
+            (env->spr[SPR_LPCR] & LPCR_HVEE)) {
+            return true;
+        }
         if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
             return true;
         }
@@ -8898,8 +8927,8 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
     pcc->hash64_opts = &ppc_hash64_opts_POWER7;
     pcc->radix_page_info = &POWER9_radix_page_info;
 #endif
-    pcc->excp_model = POWERPC_EXCP_POWER8;
-    pcc->bus_model = PPC_FLAGS_INPUT_POWER7;
+    pcc->excp_model = POWERPC_EXCP_POWER9;
+    pcc->bus_model = PPC_FLAGS_INPUT_POWER9;
     pcc->bfd_mach = bfd_mach_ppc64;
     pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
                  POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
diff --git a/target/s390x/cc_helper.c b/target/s390x/cc_helper.c
index 307ad61aee..0e467bf2b6 100644
--- a/target/s390x/cc_helper.c
+++ b/target/s390x/cc_helper.c
@@ -397,6 +397,11 @@ static uint32_t cc_calc_flogr(uint64_t dst)
     return dst ? 2 : 0;
 }
 
+static uint32_t cc_calc_lcbb(uint64_t dst)
+{
+    return dst == 16 ? 0 : 3;
+}
+
 static uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op,
                                   uint64_t src, uint64_t dst, uint64_t vr)
 {
@@ -506,6 +511,9 @@ static uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op,
     case CC_OP_FLOGR:
         r = cc_calc_flogr(dst);
         break;
+    case CC_OP_LCBB:
+        r = cc_calc_lcbb(dst);
+        break;
 
     case CC_OP_NZ_F32:
         r = set_cc_nz_f32(dst);
diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c
index a758649f47..f84bfb1284 100644
--- a/target/s390x/excp_helper.c
+++ b/target/s390x/excp_helper.c
@@ -347,10 +347,41 @@ static void do_io_interrupt(CPUS390XState *env)
     load_psw(env, mask, addr);
 }
 
+typedef struct MchkExtSaveArea {
+    uint64_t    vregs[32][2];                     /* 0x0000 */
+    uint8_t     pad_0x0200[0x0400 - 0x0200];      /* 0x0200 */
+} MchkExtSaveArea;
+QEMU_BUILD_BUG_ON(sizeof(MchkExtSaveArea) != 1024);
+
+static int mchk_store_vregs(CPUS390XState *env, uint64_t mcesao)
+{
+    hwaddr len = sizeof(MchkExtSaveArea);
+    MchkExtSaveArea *sa;
+    int i;
+
+    sa = cpu_physical_memory_map(mcesao, &len, 1);
+    if (!sa) {
+        return -EFAULT;
+    }
+    if (len != sizeof(MchkExtSaveArea)) {
+        cpu_physical_memory_unmap(sa, len, 1, 0);
+        return -EFAULT;
+    }
+
+    for (i = 0; i < 32; i++) {
+        sa->vregs[i][0] = cpu_to_be64(env->vregs[i][0].ll);
+        sa->vregs[i][1] = cpu_to_be64(env->vregs[i][1].ll);
+    }
+
+    cpu_physical_memory_unmap(sa, len, 1, len);
+    return 0;
+}
+
 static void do_mchk_interrupt(CPUS390XState *env)
 {
     QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic());
-    uint64_t mask, addr;
+    uint64_t mcic = s390_build_validity_mcic() | MCIC_SC_CP;
+    uint64_t mask, addr, mcesao = 0;
     LowCore *lowcore;
     int i;
 
@@ -362,6 +393,17 @@ static void do_mchk_interrupt(CPUS390XState *env)
 
     lowcore = cpu_map_lowcore(env);
 
+    /* extended save area */
+    if (mcic & MCIC_VB_VR) {
+        /* length and alignment is 1024 bytes */
+        mcesao = be64_to_cpu(lowcore->mcesad) & ~0x3ffull;
+    }
+
+    /* try to store vector registers */
+    if (!mcesao || mchk_store_vregs(env, mcesao)) {
+        mcic &= ~MCIC_VB_VR;
+    }
+
     /* we are always in z/Architecture mode */
     lowcore->ar_access_id = 1;
 
@@ -377,7 +419,7 @@ static void do_mchk_interrupt(CPUS390XState *env)
     lowcore->cpu_timer_save_area = cpu_to_be64(env->cputm);
     lowcore->clock_comp_save_area = cpu_to_be64(env->ckc >> 8);
 
-    lowcore->mcic = cpu_to_be64(s390_build_validity_mcic() | MCIC_SC_CP);
+    lowcore->mcic = cpu_to_be64(mcic);
     lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env));
     lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr);
     mask = be64_to_cpu(lowcore->mcck_new_psw.mask);
diff --git a/target/s390x/fpu_helper.c b/target/s390x/fpu_helper.c
index e921172bc4..1be68bafea 100644
--- a/target/s390x/fpu_helper.c
+++ b/target/s390x/fpu_helper.c
@@ -36,13 +36,21 @@
 
 #define RET128(F) (env->retxl = F.low, F.high)
 
-#define convert_bit(mask, from, to) \
-    (to < from                      \
-     ? (mask / (from / to)) & to    \
-     : (mask & from) * (to / from))
+uint8_t s390_softfloat_exc_to_ieee(unsigned int exc)
+{
+    uint8_t s390_exc = 0;
+
+    s390_exc |= (exc & float_flag_invalid) ? S390_IEEE_MASK_INVALID : 0;
+    s390_exc |= (exc & float_flag_divbyzero) ? S390_IEEE_MASK_DIVBYZERO : 0;
+    s390_exc |= (exc & float_flag_overflow) ? S390_IEEE_MASK_OVERFLOW : 0;
+    s390_exc |= (exc & float_flag_underflow) ? S390_IEEE_MASK_UNDERFLOW : 0;
+    s390_exc |= (exc & float_flag_inexact) ? S390_IEEE_MASK_INEXACT : 0;
+
+    return s390_exc;
+}
 
 /* Should be called after any operation that may raise IEEE exceptions.  */
-static void handle_exceptions(CPUS390XState *env, uintptr_t retaddr)
+static void handle_exceptions(CPUS390XState *env, bool XxC, uintptr_t retaddr)
 {
     unsigned s390_exc, qemu_exc;
 
@@ -53,22 +61,54 @@ static void handle_exceptions(CPUS390XState *env, uintptr_t retaddr)
         return;
     }
     env->fpu_status.float_exception_flags = 0;
+    s390_exc = s390_softfloat_exc_to_ieee(qemu_exc);
+
+    /*
+     * IEEE-Underflow exception recognition exists if a tininess condition
+     * (underflow) exists and
+     * - The mask bit in the FPC is zero and the result is inexact
+     * - The mask bit in the FPC is one
+     * So tininess conditions that are not inexact don't trigger any
+     * underflow action in case the mask bit is not one.
+     */
+    if (!(s390_exc & S390_IEEE_MASK_INEXACT) &&
+        !((env->fpc >> 24) & S390_IEEE_MASK_UNDERFLOW)) {
+        s390_exc &= ~S390_IEEE_MASK_UNDERFLOW;
+    }
 
-    /* Convert softfloat exception bits to s390 exception bits.  */
-    s390_exc = 0;
-    s390_exc |= convert_bit(qemu_exc, float_flag_invalid, 0x80);
-    s390_exc |= convert_bit(qemu_exc, float_flag_divbyzero, 0x40);
-    s390_exc |= convert_bit(qemu_exc, float_flag_overflow, 0x20);
-    s390_exc |= convert_bit(qemu_exc, float_flag_underflow, 0x10);
-    s390_exc |= convert_bit(qemu_exc, float_flag_inexact, 0x08);
-
-    /* Install the exceptions that we raised.  */
-    env->fpc |= s390_exc << 16;
+    /*
+     * FIXME:
+     * 1. Right now, all inexact conditions are inidicated as
+     *    "truncated" (0) and never as "incremented" (1) in the DXC.
+     * 2. Only traps due to invalid/divbyzero are suppressing. Other traps
+     *    are completing, meaning the target register has to be written!
+     *    This, however will mean that we have to write the register before
+     *    triggering the trap - impossible right now.
+     */
+
+    /*
+     * invalid/divbyzero cannot coexist with other conditions.
+     * overflow/underflow however can coexist with inexact, we have to
+     * handle it separatly.
+     */
+    if (s390_exc & ~S390_IEEE_MASK_INEXACT) {
+        if (s390_exc & ~S390_IEEE_MASK_INEXACT & env->fpc >> 24) {
+            /* trap condition - inexact reported along */
+            tcg_s390_data_exception(env, s390_exc, retaddr);
+        }
+        /* nontrap condition - inexact handled differently */
+        env->fpc |= (s390_exc & ~S390_IEEE_MASK_INEXACT) << 16;
+    }
 
-    /* Send signals for enabled exceptions.  */
-    s390_exc &= env->fpc >> 24;
-    if (s390_exc) {
-        tcg_s390_data_exception(env, s390_exc, retaddr);
+    /* inexact handling */
+    if (s390_exc & S390_IEEE_MASK_INEXACT && !XxC) {
+        /* trap condition - overflow/underflow _not_ reported along */
+        if (s390_exc & S390_IEEE_MASK_INEXACT & env->fpc >> 24) {
+            tcg_s390_data_exception(env, s390_exc & S390_IEEE_MASK_INEXACT,
+                                    retaddr);
+        }
+        /* nontrap condition */
+        env->fpc |= (s390_exc & S390_IEEE_MASK_INEXACT) << 16;
     }
 }
 
@@ -130,11 +170,22 @@ uint32_t set_cc_nz_f128(float128 v)
     }
 }
 
+static inline uint8_t round_from_m34(uint32_t m34)
+{
+    return extract32(m34, 0, 4);
+}
+
+static inline bool xxc_from_m34(uint32_t m34)
+{
+    /* XxC is bit 1 of m4 */
+    return extract32(m34, 4 + 3 - 1, 1);
+}
+
 /* 32-bit FP addition */
 uint64_t HELPER(aeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     float32 ret = float32_add(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -142,7 +193,7 @@ uint64_t HELPER(aeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 uint64_t HELPER(adb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     float64 ret = float64_add(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -153,7 +204,7 @@ uint64_t HELPER(axb)(CPUS390XState *env, uint64_t ah, uint64_t al,
     float128 ret = float128_add(make_float128(ah, al),
                                 make_float128(bh, bl),
                                 &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return RET128(ret);
 }
 
@@ -161,7 +212,7 @@ uint64_t HELPER(axb)(CPUS390XState *env, uint64_t ah, uint64_t al,
 uint64_t HELPER(seb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     float32 ret = float32_sub(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -169,7 +220,7 @@ uint64_t HELPER(seb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 uint64_t HELPER(sdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     float64 ret = float64_sub(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -180,7 +231,7 @@ uint64_t HELPER(sxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
     float128 ret = float128_sub(make_float128(ah, al),
                                 make_float128(bh, bl),
                                 &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return RET128(ret);
 }
 
@@ -188,7 +239,7 @@ uint64_t HELPER(sxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
 uint64_t HELPER(deb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     float32 ret = float32_div(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -196,7 +247,7 @@ uint64_t HELPER(deb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 uint64_t HELPER(ddb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     float64 ret = float64_div(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -207,7 +258,7 @@ uint64_t HELPER(dxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
     float128 ret = float128_div(make_float128(ah, al),
                                 make_float128(bh, bl),
                                 &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return RET128(ret);
 }
 
@@ -215,7 +266,7 @@ uint64_t HELPER(dxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
 uint64_t HELPER(meeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     float32 ret = float32_mul(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -223,7 +274,7 @@ uint64_t HELPER(meeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 uint64_t HELPER(mdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     float64 ret = float64_mul(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -232,7 +283,7 @@ uint64_t HELPER(mdeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     float64 ret = float32_to_float64(f2, &env->fpu_status);
     ret = float64_mul(f1, ret, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -243,7 +294,7 @@ uint64_t HELPER(mxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
     float128 ret = float128_mul(make_float128(ah, al),
                                 make_float128(bh, bl),
                                 &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return RET128(ret);
 }
 
@@ -253,7 +304,7 @@ uint64_t HELPER(mxdb)(CPUS390XState *env, uint64_t ah, uint64_t al,
 {
     float128 ret = float64_to_float128(f2, &env->fpu_status);
     ret = float128_mul(make_float128(ah, al), ret, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return RET128(ret);
 }
 
@@ -261,15 +312,19 @@ uint64_t HELPER(mxdb)(CPUS390XState *env, uint64_t ah, uint64_t al,
 uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2)
 {
     float64 ret = float32_to_float64(f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
 /* convert 128-bit float to 64-bit float */
-uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al)
+uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
+                      uint32_t m34)
 {
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
@@ -277,7 +332,7 @@ uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al)
 uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2)
 {
     float128 ret = float64_to_float128(f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return RET128(ret);
 }
 
@@ -285,23 +340,30 @@ uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2)
 uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2)
 {
     float128 ret = float32_to_float128(f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return RET128(ret);
 }
 
 /* convert 64-bit float to 32-bit float */
-uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2)
+uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2, uint32_t m34)
 {
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float32 ret = float64_to_float32(f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 128-bit float to 32-bit float */
-uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al)
+uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al,
+                      uint32_t m34)
 {
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
@@ -309,7 +371,7 @@ uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al)
 uint32_t HELPER(ceb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     int cmp = float32_compare_quiet(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return float_comp_to_cc(env, cmp);
 }
 
@@ -317,7 +379,7 @@ uint32_t HELPER(ceb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 uint32_t HELPER(cdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     int cmp = float64_compare_quiet(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return float_comp_to_cc(env, cmp);
 }
 
@@ -328,21 +390,28 @@ uint32_t HELPER(cxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
     int cmp = float128_compare_quiet(make_float128(ah, al),
                                      make_float128(bh, bl),
                                      &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return float_comp_to_cc(env, cmp);
 }
 
-static int swap_round_mode(CPUS390XState *env, int m3)
+int s390_swap_bfp_rounding_mode(CPUS390XState *env, int m3)
 {
     int ret = env->fpu_status.float_rounding_mode;
+
     switch (m3) {
     case 0:
         /* current mode */
         break;
     case 1:
-        /* biased round no nearest */
+        /* round to nearest with ties away from 0 */
+        set_float_rounding_mode(float_round_ties_away, &env->fpu_status);
+        break;
+    case 3:
+        /* round to prepare for shorter precision */
+        set_float_rounding_mode(float_round_to_odd, &env->fpu_status);
+        break;
     case 4:
-        /* round to nearest */
+        /* round to nearest with ties to even */
         set_float_rounding_mode(float_round_nearest_even, &env->fpu_status);
         break;
     case 5:
@@ -357,226 +426,251 @@ static int swap_round_mode(CPUS390XState *env, int m3)
         /* round to -inf */
         set_float_rounding_mode(float_round_down, &env->fpu_status);
         break;
+    default:
+        g_assert_not_reached();
     }
     return ret;
 }
 
+void s390_restore_bfp_rounding_mode(CPUS390XState *env, int old_mode)
+{
+    set_float_rounding_mode(old_mode, &env->fpu_status);
+}
+
 /* convert 64-bit int to 32-bit float */
-uint64_t HELPER(cegb)(CPUS390XState *env, int64_t v2, uint32_t m3)
+uint64_t HELPER(cegb)(CPUS390XState *env, int64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float32 ret = int64_to_float32(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 64-bit int to 64-bit float */
-uint64_t HELPER(cdgb)(CPUS390XState *env, int64_t v2, uint32_t m3)
+uint64_t HELPER(cdgb)(CPUS390XState *env, int64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float64 ret = int64_to_float64(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 64-bit int to 128-bit float */
-uint64_t HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m3)
+uint64_t HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float128 ret = int64_to_float128(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return RET128(ret);
 }
 
 /* convert 64-bit uint to 32-bit float */
-uint64_t HELPER(celgb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(celgb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float32 ret = uint64_to_float32(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 64-bit uint to 64-bit float */
-uint64_t HELPER(cdlgb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(cdlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float64 ret = uint64_to_float64(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 64-bit uint to 128-bit float */
-uint64_t HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float128 ret = uint64_to_float128(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return RET128(ret);
 }
 
 /* convert 32-bit float to 64-bit int */
-uint64_t HELPER(cgeb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(cgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     int64_t ret = float32_to_int64(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 64-bit float to 64-bit int */
-uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     int64_t ret = float64_to_int64(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 128-bit float to 64-bit int */
-uint64_t HELPER(cgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3)
+uint64_t HELPER(cgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float128 v2 = make_float128(h, l);
     int64_t ret = float128_to_int64(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 32-bit float to 32-bit int */
-uint64_t HELPER(cfeb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(cfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     int32_t ret = float32_to_int32(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 64-bit float to 32-bit int */
-uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     int32_t ret = float64_to_int32(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 128-bit float to 32-bit int */
-uint64_t HELPER(cfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3)
+uint64_t HELPER(cfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float128 v2 = make_float128(h, l);
     int32_t ret = float128_to_int32(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 32-bit float to 64-bit uint */
-uint64_t HELPER(clgeb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(clgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     uint64_t ret;
+
     v2 = float32_to_float64(v2, &env->fpu_status);
     ret = float64_to_uint64(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 64-bit float to 64-bit uint */
-uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     uint64_t ret = float64_to_uint64(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 128-bit float to 64-bit uint */
-uint64_t HELPER(clgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3)
+uint64_t HELPER(clgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
-    float128 v2 = make_float128(h, l);
-    /* ??? Not 100% correct.  */
-    uint64_t ret = float128_to_int64(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
+    uint64_t ret = float128_to_uint64(make_float128(h, l), &env->fpu_status);
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 32-bit float to 32-bit uint */
-uint64_t HELPER(clfeb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(clfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     uint32_t ret = float32_to_uint32(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 64-bit float to 32-bit uint */
-uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
+uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     uint32_t ret = float64_to_uint32(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* convert 128-bit float to 32-bit uint */
-uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3)
+uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
-    float128 v2 = make_float128(h, l);
-    /* Not 100% correct.  */
-    uint32_t ret = float128_to_int64(v2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
+    uint32_t ret = float128_to_uint32(make_float128(h, l), &env->fpu_status);
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* round to integer 32-bit */
-uint64_t HELPER(fieb)(CPUS390XState *env, uint64_t f2, uint32_t m3)
+uint64_t HELPER(fieb)(CPUS390XState *env, uint64_t f2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float32 ret = float32_round_to_int(f2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* round to integer 64-bit */
-uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m3)
+uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float64 ret = float64_round_to_int(f2, &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return ret;
 }
 
 /* round to integer 128-bit */
-uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint32_t m3)
+uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al,
+                      uint32_t m34)
 {
-    int hold = swap_round_mode(env, m3);
+    int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
     float128 ret = float128_round_to_int(make_float128(ah, al),
                                          &env->fpu_status);
-    set_float_rounding_mode(hold, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+
+    s390_restore_bfp_rounding_mode(env, old_mode);
+    handle_exceptions(env, xxc_from_m34(m34), GETPC());
     return RET128(ret);
 }
 
@@ -584,7 +678,7 @@ uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint32_t m3)
 uint32_t HELPER(keb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     int cmp = float32_compare(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return float_comp_to_cc(env, cmp);
 }
 
@@ -592,7 +686,7 @@ uint32_t HELPER(keb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 uint32_t HELPER(kdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
 {
     int cmp = float64_compare(f1, f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return float_comp_to_cc(env, cmp);
 }
 
@@ -603,7 +697,7 @@ uint32_t HELPER(kxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
     int cmp = float128_compare(make_float128(ah, al),
                                make_float128(bh, bl),
                                &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return float_comp_to_cc(env, cmp);
 }
 
@@ -612,7 +706,7 @@ uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1,
                       uint64_t f2, uint64_t f3)
 {
     float32 ret = float32_muladd(f2, f3, f1, 0, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -621,7 +715,7 @@ uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1,
                       uint64_t f2, uint64_t f3)
 {
     float64 ret = float64_muladd(f2, f3, f1, 0, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -631,7 +725,7 @@ uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1,
 {
     float32 ret = float32_muladd(f2, f3, f1, float_muladd_negate_c,
                                  &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -641,78 +735,63 @@ uint64_t HELPER(msdb)(CPUS390XState *env, uint64_t f1,
 {
     float64 ret = float64_muladd(f2, f3, f1, float_muladd_negate_c,
                                  &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
+/* The rightmost bit has the number 11. */
+static inline uint16_t dcmask(int bit, bool neg)
+{
+    return 1 << (11 - bit - neg);
+}
+
+#define DEF_FLOAT_DCMASK(_TYPE) \
+static uint16_t _TYPE##_dcmask(CPUS390XState *env, _TYPE f1)       \
+{                                                                  \
+    const bool neg = _TYPE##_is_neg(f1);                           \
+                                                                   \
+    /* Sorted by most common cases - only one class is possible */ \
+    if (_TYPE##_is_normal(f1)) {                                   \
+        return dcmask(2, neg);                                     \
+    } else if (_TYPE##_is_zero(f1)) {                              \
+        return dcmask(0, neg);                                     \
+    } else if (_TYPE##_is_denormal(f1)) {                          \
+        return dcmask(4, neg);                                     \
+    } else if (_TYPE##_is_infinity(f1)) {                          \
+        return dcmask(6, neg);                                     \
+    } else if (_TYPE##_is_quiet_nan(f1, &env->fpu_status)) {       \
+        return dcmask(8, neg);                                     \
+    }                                                              \
+    /* signaling nan, as last remaining case */                    \
+    return dcmask(10, neg);                                        \
+}
+DEF_FLOAT_DCMASK(float32)
+DEF_FLOAT_DCMASK(float64)
+DEF_FLOAT_DCMASK(float128)
+
 /* test data class 32-bit */
 uint32_t HELPER(tceb)(CPUS390XState *env, uint64_t f1, uint64_t m2)
 {
-    float32 v1 = f1;
-    int neg = float32_is_neg(v1);
-    uint32_t cc = 0;
-
-    if ((float32_is_zero(v1) && (m2 & (1 << (11-neg)))) ||
-        (float32_is_infinity(v1) && (m2 & (1 << (5-neg)))) ||
-        (float32_is_any_nan(v1) && (m2 & (1 << (3-neg)))) ||
-        (float32_is_signaling_nan(v1, &env->fpu_status) &&
-         (m2 & (1 << (1-neg))))) {
-        cc = 1;
-    } else if (m2 & (1 << (9-neg))) {
-        /* assume normalized number */
-        cc = 1;
-    }
-    /* FIXME: denormalized? */
-    return cc;
+    return (m2 & float32_dcmask(env, f1)) != 0;
 }
 
 /* test data class 64-bit */
 uint32_t HELPER(tcdb)(CPUS390XState *env, uint64_t v1, uint64_t m2)
 {
-    int neg = float64_is_neg(v1);
-    uint32_t cc = 0;
-
-    if ((float64_is_zero(v1) && (m2 & (1 << (11-neg)))) ||
-        (float64_is_infinity(v1) && (m2 & (1 << (5-neg)))) ||
-        (float64_is_any_nan(v1) && (m2 & (1 << (3-neg)))) ||
-        (float64_is_signaling_nan(v1, &env->fpu_status) &&
-         (m2 & (1 << (1-neg))))) {
-        cc = 1;
-    } else if (m2 & (1 << (9-neg))) {
-        /* assume normalized number */
-        cc = 1;
-    }
-    /* FIXME: denormalized? */
-    return cc;
+    return (m2 & float64_dcmask(env, v1)) != 0;
 }
 
 /* test data class 128-bit */
-uint32_t HELPER(tcxb)(CPUS390XState *env, uint64_t ah,
-                      uint64_t al, uint64_t m2)
-{
-    float128 v1 = make_float128(ah, al);
-    int neg = float128_is_neg(v1);
-    uint32_t cc = 0;
-
-    if ((float128_is_zero(v1) && (m2 & (1 << (11-neg)))) ||
-        (float128_is_infinity(v1) && (m2 & (1 << (5-neg)))) ||
-        (float128_is_any_nan(v1) && (m2 & (1 << (3-neg)))) ||
-        (float128_is_signaling_nan(v1, &env->fpu_status) &&
-         (m2 & (1 << (1-neg))))) {
-        cc = 1;
-    } else if (m2 & (1 << (9-neg))) {
-        /* assume normalized number */
-        cc = 1;
-    }
-    /* FIXME: denormalized? */
-    return cc;
+uint32_t HELPER(tcxb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t m2)
+{
+    return (m2 & float128_dcmask(env, make_float128(ah, al))) != 0;
 }
 
 /* square root 32-bit */
 uint64_t HELPER(sqeb)(CPUS390XState *env, uint64_t f2)
 {
     float32 ret = float32_sqrt(f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -720,7 +799,7 @@ uint64_t HELPER(sqeb)(CPUS390XState *env, uint64_t f2)
 uint64_t HELPER(sqdb)(CPUS390XState *env, uint64_t f2)
 {
     float64 ret = float64_sqrt(f2, &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return ret;
 }
 
@@ -728,44 +807,84 @@ uint64_t HELPER(sqdb)(CPUS390XState *env, uint64_t f2)
 uint64_t HELPER(sqxb)(CPUS390XState *env, uint64_t ah, uint64_t al)
 {
     float128 ret = float128_sqrt(make_float128(ah, al), &env->fpu_status);
-    handle_exceptions(env, GETPC());
+    handle_exceptions(env, false, GETPC());
     return RET128(ret);
 }
 
-static const int fpc_to_rnd[4] = {
+static const int fpc_to_rnd[8] = {
     float_round_nearest_even,
     float_round_to_zero,
     float_round_up,
-    float_round_down
+    float_round_down,
+    -1,
+    -1,
+    -1,
+    float_round_to_odd,
 };
 
 /* set fpc */
 void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
 {
+    if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u ||
+        (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
+        s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+    }
+
     /* Install everything in the main FPC.  */
     env->fpc = fpc;
 
     /* Install the rounding mode in the shadow fpu_status.  */
-    set_float_rounding_mode(fpc_to_rnd[fpc & 3], &env->fpu_status);
+    set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
 }
 
 /* set fpc and signal */
-void HELPER(sfas)(CPUS390XState *env, uint64_t val)
+void HELPER(sfas)(CPUS390XState *env, uint64_t fpc)
 {
     uint32_t signalling = env->fpc;
-    uint32_t source = val;
     uint32_t s390_exc;
 
-    /* The contents of the source operand are placed in the FPC register;
-       then the flags in the FPC register are set to the logical OR of the
-       signalling flags and the source flags.  */
-    env->fpc = source | (signalling & 0x00ff0000);
-    set_float_rounding_mode(fpc_to_rnd[source & 3], &env->fpu_status);
+    if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u ||
+        (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
+        s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+    }
 
-    /* If any signalling flag is 1 and the corresponding source mask
-       is also 1, a simulated-iee-exception trap occurs.  */
-    s390_exc = (signalling >> 16) & (source >> 24);
+    /*
+     * FPC is set to the FPC operand with a bitwise OR of the signalling
+     * flags.
+     */
+    env->fpc = fpc | (signalling & 0x00ff0000);
+    set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
+
+    /*
+     * If any signaling flag is enabled in the new FPC mask, a
+     * simulated-iee-exception exception occurs.
+     */
+    s390_exc = (signalling >> 16) & (fpc >> 24);
     if (s390_exc) {
+        if (s390_exc & S390_IEEE_MASK_INVALID) {
+            s390_exc = S390_IEEE_MASK_INVALID;
+        } else if (s390_exc & S390_IEEE_MASK_DIVBYZERO) {
+            s390_exc = S390_IEEE_MASK_DIVBYZERO;
+        } else if (s390_exc & S390_IEEE_MASK_OVERFLOW) {
+            s390_exc &= (S390_IEEE_MASK_OVERFLOW | S390_IEEE_MASK_INEXACT);
+        } else if (s390_exc & S390_IEEE_MASK_UNDERFLOW) {
+            s390_exc &= (S390_IEEE_MASK_UNDERFLOW | S390_IEEE_MASK_INEXACT);
+        } else if (s390_exc & S390_IEEE_MASK_INEXACT) {
+            s390_exc = S390_IEEE_MASK_INEXACT;
+        } else if (s390_exc & S390_IEEE_MASK_QUANTUM) {
+            s390_exc = S390_IEEE_MASK_QUANTUM;
+        }
         tcg_s390_data_exception(env, s390_exc | 3, GETPC());
     }
 }
+
+/* set bfp rounding mode */
+void HELPER(srnm)(CPUS390XState *env, uint64_t rnd)
+{
+    if (rnd > 0x7 || fpc_to_rnd[rnd & 0x7] == -1) {
+        s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+    }
+
+    env->fpc = deposit32(env->fpc, 0, 3, rnd);
+    set_float_rounding_mode(fpc_to_rnd[rnd & 0x7], &env->fpu_status);
+}
diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
index 44eca45474..e4739a6b9f 100644
--- a/target/s390x/gen-features.c
+++ b/target/s390x/gen-features.c
@@ -601,6 +601,11 @@ static uint16_t qemu_V3_1[] = {
 };
 
 static uint16_t qemu_LATEST[] = {
+    /*
+     * Only BFP bits are implemented (HFP, DFP, PFPO and DIVIDE TO INTEGER not
+     * implemented yet).
+     */
+    S390_FEAT_FLOATING_POINT_EXT,
     S390_FEAT_ZPCI,
 };
 
diff --git a/target/s390x/helper.c b/target/s390x/helper.c
index 3d74836a83..8e9573221c 100644
--- a/target/s390x/helper.c
+++ b/target/s390x/helper.c
@@ -211,7 +211,7 @@ void s390_cpu_recompute_watchpoints(CPUState *cs)
     }
 }
 
-struct sigp_save_area {
+typedef struct SigpSaveArea {
     uint64_t    fprs[16];                       /* 0x0000 */
     uint64_t    grs[16];                        /* 0x0080 */
     PSW         psw;                            /* 0x0100 */
@@ -225,13 +225,13 @@ struct sigp_save_area {
     uint8_t     pad_0x0138[0x0140 - 0x0138];    /* 0x0138 */
     uint32_t    ars[16];                        /* 0x0140 */
     uint64_t    crs[16];                        /* 0x0384 */
-};
-QEMU_BUILD_BUG_ON(sizeof(struct sigp_save_area) != 512);
+} SigpSaveArea;
+QEMU_BUILD_BUG_ON(sizeof(SigpSaveArea) != 512);
 
 int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch)
 {
     static const uint8_t ar_id = 1;
-    struct sigp_save_area *sa;
+    SigpSaveArea *sa;
     hwaddr len = sizeof(*sa);
     int i;
 
@@ -272,32 +272,43 @@ int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch)
     return 0;
 }
 
-#define ADTL_GS_OFFSET   1024 /* offset of GS data in adtl save area */
+typedef struct SigpAdtlSaveArea {
+    uint64_t    vregs[32][2];                     /* 0x0000 */
+    uint8_t     pad_0x0200[0x0400 - 0x0200];      /* 0x0200 */
+    uint64_t    gscb[4];                          /* 0x0400 */
+    uint8_t     pad_0x0420[0x1000 - 0x0420];      /* 0x0420 */
+} SigpAdtlSaveArea;
+QEMU_BUILD_BUG_ON(sizeof(SigpAdtlSaveArea) != 4096);
+
 #define ADTL_GS_MIN_SIZE 2048 /* minimal size of adtl save area for GS */
 int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len)
 {
+    SigpAdtlSaveArea *sa;
     hwaddr save = len;
-    void *mem;
+    int i;
 
-    mem = cpu_physical_memory_map(addr, &save, 1);
-    if (!mem) {
+    sa = cpu_physical_memory_map(addr, &save, 1);
+    if (!sa) {
         return -EFAULT;
     }
     if (save != len) {
-        cpu_physical_memory_unmap(mem, len, 1, 0);
+        cpu_physical_memory_unmap(sa, len, 1, 0);
         return -EFAULT;
     }
 
-    /* FIXME: as soon as TCG supports these features, convert cpu->be */
     if (s390_has_feat(S390_FEAT_VECTOR)) {
-        memcpy(mem, &cpu->env.vregs, 512);
+        for (i = 0; i < 32; i++) {
+            sa->vregs[i][0] = cpu_to_be64(cpu->env.vregs[i][0].ll);
+            sa->vregs[i][1] = cpu_to_be64(cpu->env.vregs[i][1].ll);
+        }
     }
     if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) && len >= ADTL_GS_MIN_SIZE) {
-        memcpy(mem + ADTL_GS_OFFSET, &cpu->env.gscb, 32);
+        for (i = 0; i < 4; i++) {
+            sa->gscb[i] = cpu_to_be64(cpu->env.gscb[i]);
+        }
     }
 
-    cpu_physical_memory_unmap(mem, len, 1, len);
-
+    cpu_physical_memory_unmap(sa, len, 1, len);
     return 0;
 }
 #endif /* CONFIG_USER_ONLY */
@@ -406,6 +417,7 @@ const char *cc_name(enum cc_op cc_op)
         [CC_OP_SLA_32]    = "CC_OP_SLA_32",
         [CC_OP_SLA_64]    = "CC_OP_SLA_64",
         [CC_OP_FLOGR]     = "CC_OP_FLOGR",
+        [CC_OP_LCBB]      = "CC_OP_LCBB",
     };
 
     return cc_names[cc_op];
diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index 6260b50496..bb659257f6 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -53,11 +53,11 @@ DEF_HELPER_FLAGS_3(mdb, TCG_CALL_NO_WG, i64, env, i64, i64)
 DEF_HELPER_FLAGS_5(mxb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64)
 DEF_HELPER_FLAGS_4(mxdb, TCG_CALL_NO_WG, i64, env, i64, i64, i64)
 DEF_HELPER_FLAGS_2(ldeb, TCG_CALL_NO_WG, i64, env, i64)
-DEF_HELPER_FLAGS_3(ldxb, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_4(ldxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32)
 DEF_HELPER_FLAGS_2(lxdb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(lxeb, TCG_CALL_NO_WG, i64, env, i64)
-DEF_HELPER_FLAGS_2(ledb, TCG_CALL_NO_WG, i64, env, i64)
-DEF_HELPER_FLAGS_3(lexb, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(ledb, TCG_CALL_NO_WG, i64, env, i64, i32)
+DEF_HELPER_FLAGS_4(lexb, TCG_CALL_NO_WG, i64, env, i64, i64, i32)
 DEF_HELPER_FLAGS_3(ceb, TCG_CALL_NO_WG_SE, i32, env, i64, i64)
 DEF_HELPER_FLAGS_3(cdb, TCG_CALL_NO_WG_SE, i32, env, i64, i64)
 DEF_HELPER_FLAGS_5(cxb, TCG_CALL_NO_WG_SE, i32, env, i64, i64, i64, i64)
@@ -104,8 +104,9 @@ DEF_HELPER_4(trtr, i32, env, i32, i64, i64)
 DEF_HELPER_5(trXX, i32, env, i32, i32, i32, i32)
 DEF_HELPER_4(cksm, i64, env, i64, i64, i64)
 DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE, i32, env, i32, i64, i64, i64)
-DEF_HELPER_FLAGS_2(sfpc, TCG_CALL_NO_RWG, void, env, i64)
+DEF_HELPER_FLAGS_2(sfpc, TCG_CALL_NO_WG, void, env, i64)
 DEF_HELPER_FLAGS_2(sfas, TCG_CALL_NO_WG, void, env, i64)
+DEF_HELPER_FLAGS_2(srnm, TCG_CALL_NO_WG, void, env, i64)
 DEF_HELPER_FLAGS_1(popcnt, TCG_CALL_NO_RWG_SE, i64, i64)
 DEF_HELPER_2(stfle, i32, env, i64)
 DEF_HELPER_FLAGS_2(lpq, TCG_CALL_NO_WG, i64, env, i64)
diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def
index 61582372ab..61b750a855 100644
--- a/target/s390x/insn-data.def
+++ b/target/s390x/insn-data.def
@@ -479,6 +479,8 @@
     F(0xb313, LCDBR,   RRE,   Z,   0, f2, new, f1, negf64, f64, IF_BFP)
     F(0xb343, LCXBR,   RRE,   Z,   x2h, x2l, new_P, x1, negf128, f128, IF_BFP)
     F(0xb373, LCDFR,   RRE,   FPSSH, 0, f2, new, f1, negf64, 0, IF_AFP1 | IF_AFP2)
+/* LOAD COUNT TO BLOCK BOUNDARY */
+    C(0xe727, LCBB,    RXE,   V,   la2, 0, r1, 0, lcbb, 0)
 /* LOAD HALFWORD */
     C(0xb927, LHR,     RRE,   EI,  0, r2_16s, 0, r1_32, mov2, 0)
     C(0xb907, LGHR,    RRE,   EI,  0, r2_16s, 0, r1, mov2, 0)
@@ -598,10 +600,12 @@
     F(0xed04, LDEB,    RXE,   Z,   0, m2_32u, new, f1, ldeb, 0, IF_BFP)
     F(0xed05, LXDB,    RXE,   Z,   0, m2_64, new_P, x1, lxdb, 0, IF_BFP)
     F(0xed06, LXEB,    RXE,   Z,   0, m2_32u, new_P, x1, lxeb, 0, IF_BFP)
+    F(0xb324, LDER,    RXE,   Z,   0, e2, new, f1, lde, 0, IF_AFP1)
+    F(0xed24, LDE,     RXE,   Z,   0, m2_32u, new, f1, lde, 0, IF_AFP1)
 /* LOAD ROUNDED */
-    F(0xb344, LEDBR,   RRE,   Z,   0, f2, new, e1, ledb, 0, IF_BFP)
-    F(0xb345, LDXBR,   RRE,   Z,   x2h, x2l, new, f1, ldxb, 0, IF_BFP)
-    F(0xb346, LEXBR,   RRE,   Z,   x2h, x2l, new, e1, lexb, 0, IF_BFP)
+    F(0xb344, LEDBR,   RRF_e, Z,   0, f2, new, e1, ledb, 0, IF_BFP)
+    F(0xb345, LDXBR,   RRF_e, Z,   x2h, x2l, new, f1, ldxb, 0, IF_BFP)
+    F(0xb346, LEXBR,   RRF_e, Z,   x2h, x2l, new, e1, lexb, 0, IF_BFP)
 
 /* LOAD MULTIPLE */
     C(0x9800, LM,      RS_a,  Z,   0, a2, 0, 0, lm32, 0)
@@ -759,10 +763,10 @@
 /* SET FPC AND SIGNAL */
     F(0xb385, SFASR,   RRE,   IEEEE_SIM, 0, r1_o, 0, 0, sfas, 0, IF_DFP)
 /* SET BFP ROUNDING MODE */
-    F(0xb299, SRNM,    S,     Z,   0, 0, 0, 0, srnm, 0, IF_BFP)
-    F(0xb2b8, SRNMB,   S,     FPE, 0, 0, 0, 0, srnm, 0, IF_BFP)
+    F(0xb299, SRNM,    S,     Z,   la2, 0, 0, 0, srnm, 0, IF_BFP)
+    F(0xb2b8, SRNMB,   S,     FPE, la2, 0, 0, 0, srnmb, 0, IF_BFP)
 /* SET DFP ROUNDING MODE */
-    F(0xb2b9, SRNMT,   S,     DFPR, 0, 0, 0, 0, srnm, 0, IF_DFP)
+    F(0xb2b9, SRNMT,   S,     DFPR, la2, 0, 0, 0, srnmt, 0, IF_DFP)
 /* SET PROGRAM MASK */
     C(0x0400, SPM,     RR_a,  Z,   r1, 0, 0, 0, spm, 0)
 
diff --git a/target/s390x/insn-format.def b/target/s390x/insn-format.def
index a412d90fb7..4297ff4165 100644
--- a/target/s390x/insn-format.def
+++ b/target/s390x/insn-format.def
@@ -36,7 +36,7 @@ F3(RSY_a, R(1, 8),     BDL(2),      R(3,12))
 F3(RSY_b, R(1, 8),     BDL(2),      M(3,12))
 F2(RX_a,  R(1, 8),     BXD(2))
 F2(RX_b,  M(1, 8),     BXD(2))
-F2(RXE,   R(1, 8),     BXD(2))
+F3(RXE,   R(1, 8),     BXD(2),      M(3,32))
 F3(RXF,   R(1,32),     BXD(2),      R(3, 8))
 F2(RXY_a, R(1, 8),     BXDL(2))
 F2(RXY_b, M(1, 8),     BXDL(2))
diff --git a/target/s390x/internal.h b/target/s390x/internal.h
index f2a771e2b4..7baf0e2404 100644
--- a/target/s390x/internal.h
+++ b/target/s390x/internal.h
@@ -101,7 +101,9 @@ typedef struct LowCore {
     /* whether the kernel died with panic() or not */
     uint32_t        panic_magic;              /* 0xe00 */
 
-    uint8_t         pad13[0x11b8 - 0xe04];    /* 0xe04 */
+    uint8_t         pad13[0x11b0 - 0xe04];    /* 0xe04 */
+
+    uint64_t        mcesad;                    /* 0x11B0 */
 
     /* 64 bit extparam used for pfault, diag 250 etc  */
     uint64_t        ext_params2;               /* 0x11B8 */
@@ -234,6 +236,7 @@ enum cc_op {
     CC_OP_SLA_32,               /* Calculate shift left signed (32bit) */
     CC_OP_SLA_64,               /* Calculate shift left signed (64bit) */
     CC_OP_FLOGR,                /* find leftmost one */
+    CC_OP_LCBB,                 /* load count to block boundary */
     CC_OP_MAX
 };
 
@@ -308,6 +311,15 @@ void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
 uint32_t set_cc_nz_f32(float32 v);
 uint32_t set_cc_nz_f64(float64 v);
 uint32_t set_cc_nz_f128(float128 v);
+#define S390_IEEE_MASK_INVALID   0x80
+#define S390_IEEE_MASK_DIVBYZERO 0x40
+#define S390_IEEE_MASK_OVERFLOW  0x20
+#define S390_IEEE_MASK_UNDERFLOW 0x10
+#define S390_IEEE_MASK_INEXACT   0x08
+#define S390_IEEE_MASK_QUANTUM   0x04
+uint8_t s390_softfloat_exc_to_ieee(unsigned int exc);
+int s390_swap_bfp_rounding_mode(CPUS390XState *env, int m3);
+void s390_restore_bfp_rounding_mode(CPUS390XState *env, int old_mode);
 
 
 /* gdbstub.c */
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index 19072efec6..41fb466bb4 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -145,12 +145,18 @@ void s390x_translate_init(void)
     }
 }
 
-static inline int vec_reg_offset(uint8_t reg, uint8_t enr, TCGMemOp size)
+static inline int vec_full_reg_offset(uint8_t reg)
 {
-    const uint8_t es = 1 << size;
-    int offs = enr * es;
-
     g_assert(reg < 32);
+    return offsetof(CPUS390XState, vregs[reg][0].d);
+}
+
+static inline int vec_reg_offset(uint8_t reg, uint8_t enr, TCGMemOp es)
+{
+    /* Convert element size (es) - e.g. MO_8 - to bytes */
+    const uint8_t bytes = 1 << es;
+    int offs = enr * bytes;
+
     /*
      * vregs[n][0] is the lowest 8 byte and vregs[n][1] the highest 8 byte
      * of the 16 byte vector, on both, little and big endian systems.
@@ -173,11 +179,11 @@ static inline int vec_reg_offset(uint8_t reg, uint8_t enr, TCGMemOp size)
      * the two 8 byte elements have to be loaded separately. Let's force all
      * 16 byte operations to handle it in a special way.
      */
-    g_assert(size <= MO_64);
+    g_assert(es <= MO_64);
 #ifndef HOST_WORDS_BIGENDIAN
-    offs ^= (8 - es);
+    offs ^= (8 - bytes);
 #endif
-    return offs + offsetof(CPUS390XState, vregs[reg][0].d);
+    return offs + vec_full_reg_offset(reg);
 }
 
 static inline int freg64_offset(uint8_t reg)
@@ -376,32 +382,43 @@ static inline void gen_trap(DisasContext *s)
     gen_data_exception(0xff);
 }
 
+static void gen_addi_and_wrap_i64(DisasContext *s, TCGv_i64 dst, TCGv_i64 src,
+                                  int64_t imm)
+{
+    tcg_gen_addi_i64(dst, src, imm);
+    if (!(s->base.tb->flags & FLAG_MASK_64)) {
+        if (s->base.tb->flags & FLAG_MASK_32) {
+            tcg_gen_andi_i64(dst, dst, 0x7fffffff);
+        } else {
+            tcg_gen_andi_i64(dst, dst, 0x00ffffff);
+        }
+    }
+}
+
 static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2)
 {
     TCGv_i64 tmp = tcg_temp_new_i64();
-    bool need_31 = !(s->base.tb->flags & FLAG_MASK_64);
-
-    /* Note that d2 is limited to 20 bits, signed.  If we crop negative
-       displacements early we create larger immedate addends.  */
 
-    /* Note that addi optimizes the imm==0 case.  */
+    /*
+     * Note that d2 is limited to 20 bits, signed.  If we crop negative
+     * displacements early we create larger immedate addends.
+     */
     if (b2 && x2) {
         tcg_gen_add_i64(tmp, regs[b2], regs[x2]);
-        tcg_gen_addi_i64(tmp, tmp, d2);
+        gen_addi_and_wrap_i64(s, tmp, tmp, d2);
     } else if (b2) {
-        tcg_gen_addi_i64(tmp, regs[b2], d2);
+        gen_addi_and_wrap_i64(s, tmp, regs[b2], d2);
     } else if (x2) {
-        tcg_gen_addi_i64(tmp, regs[x2], d2);
-    } else {
-        if (need_31) {
-            d2 &= 0x7fffffff;
-            need_31 = false;
+        gen_addi_and_wrap_i64(s, tmp, regs[x2], d2);
+    } else if (!(s->base.tb->flags & FLAG_MASK_64)) {
+        if (s->base.tb->flags & FLAG_MASK_32) {
+            tcg_gen_movi_i64(tmp, d2 & 0x7fffffff);
+        } else {
+            tcg_gen_movi_i64(tmp, d2 & 0x00ffffff);
         }
+    } else {
         tcg_gen_movi_i64(tmp, d2);
     }
-    if (need_31) {
-        tcg_gen_andi_i64(tmp, tmp, 0x7fffffff);
-    }
 
     return tmp;
 }
@@ -540,6 +557,7 @@ static void gen_op_calc_cc(DisasContext *s)
     case CC_OP_NZ_F32:
     case CC_OP_NZ_F64:
     case CC_OP_FLOGR:
+    case CC_OP_LCBB:
         /* 1 argument */
         gen_helper_calc_cc(cc_op, cpu_env, local_cc_op, dummy, cc_dst, dummy);
         break;
@@ -1758,160 +1776,257 @@ static DisasJumpType op_cxb(DisasContext *s, DisasOps *o)
     return DISAS_NEXT;
 }
 
+static TCGv_i32 fpinst_extract_m34(DisasContext *s, bool m3_with_fpe,
+                                   bool m4_with_fpe)
+{
+    const bool fpe = s390_has_feat(S390_FEAT_FLOATING_POINT_EXT);
+    uint8_t m3 = get_field(s->fields, m3);
+    uint8_t m4 = get_field(s->fields, m4);
+
+    /* m3 field was introduced with FPE */
+    if (!fpe && m3_with_fpe) {
+        m3 = 0;
+    }
+    /* m4 field was introduced with FPE */
+    if (!fpe && m4_with_fpe) {
+        m4 = 0;
+    }
+
+    /* Check for valid rounding modes. Mode 3 was introduced later. */
+    if (m3 == 2 || m3 > 7 || (!fpe && m3 == 3)) {
+        gen_program_exception(s, PGM_SPECIFICATION);
+        return NULL;
+    }
+
+    return tcg_const_i32(deposit32(m3, 4, 4, m4));
+}
+
 static DisasJumpType op_cfeb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cfeb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cfeb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f32(s, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_cfdb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cfdb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cfdb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f64(s, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_cfxb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cfxb(o->out, cpu_env, o->in1, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cfxb(o->out, cpu_env, o->in1, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f128(s, o->in1, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_cgeb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cgeb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cgeb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f32(s, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_cgdb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cgdb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cgdb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f64(s, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_cgxb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cgxb(o->out, cpu_env, o->in1, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cgxb(o->out, cpu_env, o->in1, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f128(s, o->in1, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_clfeb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_clfeb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, false);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_clfeb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f32(s, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_clfdb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_clfdb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, false);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_clfdb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f64(s, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_clfxb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_clfxb(o->out, cpu_env, o->in1, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, false);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_clfxb(o->out, cpu_env, o->in1, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f128(s, o->in1, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_clgeb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_clgeb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, false);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_clgeb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f32(s, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_clgdb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_clgdb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, false);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_clgdb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f64(s, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_clgxb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_clgxb(o->out, cpu_env, o->in1, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, false);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_clgxb(o->out, cpu_env, o->in1, o->in2, m34);
+    tcg_temp_free_i32(m34);
     gen_set_cc_nz_f128(s, o->in1, o->in2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_cegb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cegb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, true, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cegb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_cdgb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cdgb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, true, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cdgb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_cxgb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cxgb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, true, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cxgb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return_low128(o->out2);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_celgb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_celgb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, false);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_celgb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_cdlgb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cdlgb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, false);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cdlgb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_cxlgb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_cxlgb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, false);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_cxlgb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return_low128(o->out2);
     return DISAS_NEXT;
 }
@@ -2390,26 +2505,38 @@ static DisasJumpType op_ex(DisasContext *s, DisasOps *o)
 
 static DisasJumpType op_fieb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_fieb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_fieb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_fidb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_fidb(o->out, cpu_env, o->in2, m3);
-    tcg_temp_free_i32(m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_fidb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_fixb(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
-    gen_helper_fixb(o->out, cpu_env, o->in1, o->in2, m3);
+    TCGv_i32 m34 = fpinst_extract_m34(s, false, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_fixb(o->out, cpu_env, o->in1, o->in2, m34);
     return_low128(o->out2);
-    tcg_temp_free_i32(m3);
+    tcg_temp_free_i32(m34);
     return DISAS_NEXT;
 }
 
@@ -2678,19 +2805,37 @@ static DisasJumpType op_ldeb(DisasContext *s, DisasOps *o)
 
 static DisasJumpType op_ledb(DisasContext *s, DisasOps *o)
 {
-    gen_helper_ledb(o->out, cpu_env, o->in2);
+    TCGv_i32 m34 = fpinst_extract_m34(s, true, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_ledb(o->out, cpu_env, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_ldxb(DisasContext *s, DisasOps *o)
 {
-    gen_helper_ldxb(o->out, cpu_env, o->in1, o->in2);
+    TCGv_i32 m34 = fpinst_extract_m34(s, true, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_ldxb(o->out, cpu_env, o->in1, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return DISAS_NEXT;
 }
 
 static DisasJumpType op_lexb(DisasContext *s, DisasOps *o)
 {
-    gen_helper_lexb(o->out, cpu_env, o->in1, o->in2);
+    TCGv_i32 m34 = fpinst_extract_m34(s, true, true);
+
+    if (!m34) {
+        return DISAS_NORETURN;
+    }
+    gen_helper_lexb(o->out, cpu_env, o->in1, o->in2, m34);
+    tcg_temp_free_i32(m34);
     return DISAS_NEXT;
 }
 
@@ -2708,6 +2853,12 @@ static DisasJumpType op_lxeb(DisasContext *s, DisasOps *o)
     return DISAS_NEXT;
 }
 
+static DisasJumpType op_lde(DisasContext *s, DisasOps *o)
+{
+    tcg_gen_shli_i64(o->out, o->in2, 32);
+    return DISAS_NEXT;
+}
+
 static DisasJumpType op_llgt(DisasContext *s, DisasOps *o)
 {
     tcg_gen_andi_i64(o->out, o->in2, 0x7fffffff);
@@ -3119,6 +3270,23 @@ static DisasJumpType op_lzrb(DisasContext *s, DisasOps *o)
     return DISAS_NEXT;
 }
 
+static DisasJumpType op_lcbb(DisasContext *s, DisasOps *o)
+{
+    const int64_t block_size = (1ull << (get_field(s->fields, m3) + 6));
+
+    if (get_field(s->fields, m3) > 6) {
+        gen_program_exception(s, PGM_SPECIFICATION);
+        return DISAS_NORETURN;
+    }
+
+    tcg_gen_ori_i64(o->addr1, o->addr1, -block_size);
+    tcg_gen_neg_i64(o->addr1, o->addr1);
+    tcg_gen_movi_i64(o->out, 16);
+    tcg_gen_umin_i64(o->out, o->out, o->addr1);
+    gen_op_update1_cc_i64(s, CC_OP_LCBB, o->out);
+    return DISAS_NEXT;
+}
+
 static DisasJumpType op_mov2(DisasContext *s, DisasOps *o)
 {
     o->out = o->in2;
@@ -3955,41 +4123,33 @@ static DisasJumpType op_sfas(DisasContext *s, DisasOps *o)
 
 static DisasJumpType op_srnm(DisasContext *s, DisasOps *o)
 {
-    int b2 = get_field(s->fields, b2);
-    int d2 = get_field(s->fields, d2);
-    TCGv_i64 t1 = tcg_temp_new_i64();
-    TCGv_i64 t2 = tcg_temp_new_i64();
-    int mask, pos, len;
+    /* Bits other than 62 and 63 are ignored. Bit 29 is set to zero. */
+    tcg_gen_andi_i64(o->addr1, o->addr1, 0x3ull);
+    gen_helper_srnm(cpu_env, o->addr1);
+    return DISAS_NEXT;
+}
 
-    switch (s->fields->op2) {
-    case 0x99: /* SRNM */
-        pos = 0, len = 2;
-        break;
-    case 0xb8: /* SRNMB */
-        pos = 0, len = 3;
-        break;
-    case 0xb9: /* SRNMT */
-        pos = 4, len = 3;
-        break;
-    default:
-        tcg_abort();
-    }
-    mask = (1 << len) - 1;
+static DisasJumpType op_srnmb(DisasContext *s, DisasOps *o)
+{
+    /* Bits 0-55 are are ignored. */
+    tcg_gen_andi_i64(o->addr1, o->addr1, 0xffull);
+    gen_helper_srnm(cpu_env, o->addr1);
+    return DISAS_NEXT;
+}
 
-    /* Insert the value into the appropriate field of the FPC.  */
-    if (b2 == 0) {
-        tcg_gen_movi_i64(t1, d2 & mask);
-    } else {
-        tcg_gen_addi_i64(t1, regs[b2], d2);
-        tcg_gen_andi_i64(t1, t1, mask);
-    }
-    tcg_gen_ld32u_i64(t2, cpu_env, offsetof(CPUS390XState, fpc));
-    tcg_gen_deposit_i64(t2, t2, t1, pos, len);
-    tcg_temp_free_i64(t1);
+static DisasJumpType op_srnmt(DisasContext *s, DisasOps *o)
+{
+    TCGv_i64 tmp = tcg_temp_new_i64();
 
-    /* Then install the new FPC to set the rounding mode in fpu_status.  */
-    gen_helper_sfpc(cpu_env, t2);
-    tcg_temp_free_i64(t2);
+    /* Bits other than 61-63 are ignored. */
+    tcg_gen_andi_i64(o->addr1, o->addr1, 0x7ull);
+
+    /* No need to call a helper, we don't implement dfp */
+    tcg_gen_ld32u_i64(tmp, cpu_env, offsetof(CPUS390XState, fpc));
+    tcg_gen_deposit_i64(tmp, tmp, o->addr1, 4, 3);
+    tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUS390XState, fpc));
+
+    tcg_temp_free_i64(tmp);
     return DISAS_NEXT;
 }
 
@@ -5908,6 +6068,7 @@ enum DisasInsnEnum {
 #define FAC_ECT         S390_FEAT_EXTRACT_CPU_TIME
 #define FAC_PCI         S390_FEAT_ZPCI /* z/PCI facility */
 #define FAC_AIS         S390_FEAT_ADAPTER_INT_SUPPRESSION
+#define FAC_V           S390_FEAT_VECTOR /* vector facility */
 
 static const DisasInsn insn_info[] = {
 #include "insn-data.def"
@@ -6091,7 +6252,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s)
     const DisasInsn *insn;
     DisasJumpType ret = DISAS_NEXT;
     DisasFields f;
-    DisasOps o;
+    DisasOps o = {};
 
     /* Search for the insn in the table.  */
     insn = extract_insn(env, s, &f);
@@ -6161,12 +6322,6 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s)
     /* Set up the strutures we use to communicate with the helpers. */
     s->insn = insn;
     s->fields = &f;
-    o.g_out = o.g_out2 = o.g_in1 = o.g_in2 = false;
-    o.out = NULL;
-    o.out2 = NULL;
-    o.in1 = NULL;
-    o.in2 = NULL;
-    o.addr1 = NULL;
 
     /* Implement the instruction.  */
     if (insn->help_in1) {
diff --git a/target/xtensa/core-test_mmuhifi_c3/xtensa-modules.inc.c b/target/xtensa/core-test_mmuhifi_c3/xtensa-modules.inc.c
index ef70f80f1d..687631b8fb 100644
--- a/target/xtensa/core-test_mmuhifi_c3/xtensa-modules.inc.c
+++ b/target/xtensa/core-test_mmuhifi_c3/xtensa-modules.inc.c
@@ -24159,2627 +24159,2627 @@ Opcode_ae_sbf_Slot_inst_encode (xtensa_insnbuf slotbuf)
   slotbuf[0] = 0xe7d014;
 }
 
-xtensa_opcode_encode_fn Opcode_excw_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_excw_encode_fns[] = {
   Opcode_excw_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rfe_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rfe_encode_fns[] = {
   Opcode_rfe_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rfde_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rfde_encode_fns[] = {
   Opcode_rfde_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_syscall_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_syscall_encode_fns[] = {
   Opcode_syscall_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_simcall_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_simcall_encode_fns[] = {
   Opcode_simcall_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_call12_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_call12_encode_fns[] = {
   Opcode_call12_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_call8_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_call8_encode_fns[] = {
   Opcode_call8_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_call4_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_call4_encode_fns[] = {
   Opcode_call4_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_callx12_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_callx12_encode_fns[] = {
   Opcode_callx12_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_callx8_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_callx8_encode_fns[] = {
   Opcode_callx8_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_callx4_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_callx4_encode_fns[] = {
   Opcode_callx4_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_entry_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_entry_encode_fns[] = {
   Opcode_entry_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_movsp_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_movsp_encode_fns[] = {
   Opcode_movsp_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rotw_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rotw_encode_fns[] = {
   Opcode_rotw_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_retw_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_retw_encode_fns[] = {
   Opcode_retw_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_retw_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_retw_n_encode_fns[] = {
   0, 0, Opcode_retw_n_Slot_inst16b_encode, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rfwo_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rfwo_encode_fns[] = {
   Opcode_rfwo_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rfwu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rfwu_encode_fns[] = {
   Opcode_rfwu_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_l32e_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_l32e_encode_fns[] = {
   Opcode_l32e_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_s32e_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_s32e_encode_fns[] = {
   Opcode_s32e_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_windowbase_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_windowbase_encode_fns[] = {
   Opcode_rsr_windowbase_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_windowbase_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_windowbase_encode_fns[] = {
   Opcode_wsr_windowbase_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_windowbase_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_windowbase_encode_fns[] = {
   Opcode_xsr_windowbase_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_windowstart_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_windowstart_encode_fns[] = {
   Opcode_rsr_windowstart_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_windowstart_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_windowstart_encode_fns[] = {
   Opcode_wsr_windowstart_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_windowstart_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_windowstart_encode_fns[] = {
   Opcode_xsr_windowstart_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_add_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_add_n_encode_fns[] = {
   0, Opcode_add_n_Slot_inst16a_encode, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_addi_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_addi_n_encode_fns[] = {
   0, Opcode_addi_n_Slot_inst16a_encode, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_beqz_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_beqz_n_encode_fns[] = {
   0, 0, Opcode_beqz_n_Slot_inst16b_encode, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_bnez_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bnez_n_encode_fns[] = {
   0, 0, Opcode_bnez_n_Slot_inst16b_encode, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ill_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ill_n_encode_fns[] = {
   0, 0, Opcode_ill_n_Slot_inst16b_encode, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_l32i_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_l32i_n_encode_fns[] = {
   0, Opcode_l32i_n_Slot_inst16a_encode, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_mov_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_mov_n_encode_fns[] = {
   0, 0, Opcode_mov_n_Slot_inst16b_encode, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_movi_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_movi_n_encode_fns[] = {
   0, 0, Opcode_movi_n_Slot_inst16b_encode, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_nop_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_nop_n_encode_fns[] = {
   0, 0, Opcode_nop_n_Slot_inst16b_encode, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ret_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ret_n_encode_fns[] = {
   0, 0, Opcode_ret_n_Slot_inst16b_encode, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_s32i_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_s32i_n_encode_fns[] = {
   0, Opcode_s32i_n_Slot_inst16a_encode, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_threadptr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_threadptr_encode_fns[] = {
   Opcode_rur_threadptr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_threadptr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_threadptr_encode_fns[] = {
   Opcode_wur_threadptr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_addi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_addi_encode_fns[] = {
   Opcode_addi_Slot_inst_encode, 0, 0, 0, Opcode_addi_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_addmi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_addmi_encode_fns[] = {
   Opcode_addmi_Slot_inst_encode, 0, 0, 0, Opcode_addmi_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_add_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_add_encode_fns[] = {
   Opcode_add_Slot_inst_encode, 0, 0, 0, Opcode_add_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_sub_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_sub_encode_fns[] = {
   Opcode_sub_Slot_inst_encode, 0, 0, 0, Opcode_sub_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_addx2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_addx2_encode_fns[] = {
   Opcode_addx2_Slot_inst_encode, 0, 0, 0, Opcode_addx2_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_addx4_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_addx4_encode_fns[] = {
   Opcode_addx4_Slot_inst_encode, 0, 0, 0, Opcode_addx4_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_addx8_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_addx8_encode_fns[] = {
   Opcode_addx8_Slot_inst_encode, 0, 0, 0, Opcode_addx8_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_subx2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_subx2_encode_fns[] = {
   Opcode_subx2_Slot_inst_encode, 0, 0, 0, Opcode_subx2_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_subx4_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_subx4_encode_fns[] = {
   Opcode_subx4_Slot_inst_encode, 0, 0, 0, Opcode_subx4_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_subx8_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_subx8_encode_fns[] = {
   Opcode_subx8_Slot_inst_encode, 0, 0, 0, Opcode_subx8_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_and_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_and_encode_fns[] = {
   Opcode_and_Slot_inst_encode, 0, 0, 0, Opcode_and_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_or_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_or_encode_fns[] = {
   Opcode_or_Slot_inst_encode, 0, 0, 0, Opcode_or_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_xor_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xor_encode_fns[] = {
   Opcode_xor_Slot_inst_encode, 0, 0, 0, Opcode_xor_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_beqi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_beqi_encode_fns[] = {
   Opcode_beqi_Slot_inst_encode, 0, 0, 0, Opcode_beqi_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bnei_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bnei_encode_fns[] = {
   Opcode_bnei_Slot_inst_encode, 0, 0, 0, Opcode_bnei_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bgei_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bgei_encode_fns[] = {
   Opcode_bgei_Slot_inst_encode, 0, 0, 0, Opcode_bgei_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_blti_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_blti_encode_fns[] = {
   Opcode_blti_Slot_inst_encode, 0, 0, 0, Opcode_blti_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bbci_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bbci_encode_fns[] = {
   Opcode_bbci_Slot_inst_encode, 0, 0, 0, Opcode_bbci_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bbsi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bbsi_encode_fns[] = {
   Opcode_bbsi_Slot_inst_encode, 0, 0, 0, Opcode_bbsi_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bgeui_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bgeui_encode_fns[] = {
   Opcode_bgeui_Slot_inst_encode, 0, 0, 0, Opcode_bgeui_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bltui_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bltui_encode_fns[] = {
   Opcode_bltui_Slot_inst_encode, 0, 0, 0, Opcode_bltui_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_beq_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_beq_encode_fns[] = {
   Opcode_beq_Slot_inst_encode, 0, 0, 0, Opcode_beq_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bne_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bne_encode_fns[] = {
   Opcode_bne_Slot_inst_encode, 0, 0, 0, Opcode_bne_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bge_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bge_encode_fns[] = {
   Opcode_bge_Slot_inst_encode, 0, 0, 0, Opcode_bge_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_blt_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_blt_encode_fns[] = {
   Opcode_blt_Slot_inst_encode, 0, 0, 0, Opcode_blt_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bgeu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bgeu_encode_fns[] = {
   Opcode_bgeu_Slot_inst_encode, 0, 0, 0, Opcode_bgeu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bltu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bltu_encode_fns[] = {
   Opcode_bltu_Slot_inst_encode, 0, 0, 0, Opcode_bltu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bany_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bany_encode_fns[] = {
   Opcode_bany_Slot_inst_encode, 0, 0, 0, Opcode_bany_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bnone_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bnone_encode_fns[] = {
   Opcode_bnone_Slot_inst_encode, 0, 0, 0, Opcode_bnone_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ball_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ball_encode_fns[] = {
   Opcode_ball_Slot_inst_encode, 0, 0, 0, Opcode_ball_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bnall_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bnall_encode_fns[] = {
   Opcode_bnall_Slot_inst_encode, 0, 0, 0, Opcode_bnall_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bbc_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bbc_encode_fns[] = {
   Opcode_bbc_Slot_inst_encode, 0, 0, 0, Opcode_bbc_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bbs_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bbs_encode_fns[] = {
   Opcode_bbs_Slot_inst_encode, 0, 0, 0, Opcode_bbs_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_beqz_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_beqz_encode_fns[] = {
   Opcode_beqz_Slot_inst_encode, 0, 0, 0, Opcode_beqz_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bnez_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bnez_encode_fns[] = {
   Opcode_bnez_Slot_inst_encode, 0, 0, 0, Opcode_bnez_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bgez_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bgez_encode_fns[] = {
   Opcode_bgez_Slot_inst_encode, 0, 0, 0, Opcode_bgez_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bltz_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bltz_encode_fns[] = {
   Opcode_bltz_Slot_inst_encode, 0, 0, 0, Opcode_bltz_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_call0_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_call0_encode_fns[] = {
   Opcode_call0_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_callx0_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_callx0_encode_fns[] = {
   Opcode_callx0_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_extui_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_extui_encode_fns[] = {
   Opcode_extui_Slot_inst_encode, 0, 0, 0, Opcode_extui_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ill_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ill_encode_fns[] = {
   Opcode_ill_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_j_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_j_encode_fns[] = {
   Opcode_j_Slot_inst_encode, 0, 0, 0, Opcode_j_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_jx_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_jx_encode_fns[] = {
   Opcode_jx_Slot_inst_encode, 0, 0, 0, Opcode_jx_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_l16ui_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_l16ui_encode_fns[] = {
   Opcode_l16ui_Slot_inst_encode, 0, 0, 0, Opcode_l16ui_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_l16si_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_l16si_encode_fns[] = {
   Opcode_l16si_Slot_inst_encode, 0, 0, 0, Opcode_l16si_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_l32i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_l32i_encode_fns[] = {
   Opcode_l32i_Slot_inst_encode, 0, 0, 0, Opcode_l32i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_l32r_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_l32r_encode_fns[] = {
   Opcode_l32r_Slot_inst_encode, 0, 0, 0, Opcode_l32r_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_l8ui_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_l8ui_encode_fns[] = {
   Opcode_l8ui_Slot_inst_encode, 0, 0, 0, Opcode_l8ui_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_loop_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_loop_encode_fns[] = {
   Opcode_loop_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_loopnez_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_loopnez_encode_fns[] = {
   Opcode_loopnez_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_loopgtz_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_loopgtz_encode_fns[] = {
   Opcode_loopgtz_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_movi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_movi_encode_fns[] = {
   Opcode_movi_Slot_inst_encode, 0, 0, 0, Opcode_movi_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_moveqz_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_moveqz_encode_fns[] = {
   Opcode_moveqz_Slot_inst_encode, 0, 0, 0, Opcode_moveqz_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_movnez_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_movnez_encode_fns[] = {
   Opcode_movnez_Slot_inst_encode, 0, 0, 0, Opcode_movnez_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_movltz_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_movltz_encode_fns[] = {
   Opcode_movltz_Slot_inst_encode, 0, 0, 0, Opcode_movltz_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_movgez_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_movgez_encode_fns[] = {
   Opcode_movgez_Slot_inst_encode, 0, 0, 0, Opcode_movgez_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_neg_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_neg_encode_fns[] = {
   Opcode_neg_Slot_inst_encode, 0, 0, 0, Opcode_neg_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_abs_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_abs_encode_fns[] = {
   Opcode_abs_Slot_inst_encode, 0, 0, 0, Opcode_abs_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_nop_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_nop_encode_fns[] = {
   Opcode_nop_Slot_inst_encode, 0, 0, Opcode_nop_Slot_ae_slot1_encode, Opcode_nop_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ret_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ret_encode_fns[] = {
   Opcode_ret_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_s16i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_s16i_encode_fns[] = {
   Opcode_s16i_Slot_inst_encode, 0, 0, 0, Opcode_s16i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_s32i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_s32i_encode_fns[] = {
   Opcode_s32i_Slot_inst_encode, 0, 0, 0, Opcode_s32i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_s8i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_s8i_encode_fns[] = {
   Opcode_s8i_Slot_inst_encode, 0, 0, 0, Opcode_s8i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ssr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ssr_encode_fns[] = {
   Opcode_ssr_Slot_inst_encode, 0, 0, 0, Opcode_ssr_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ssl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ssl_encode_fns[] = {
   Opcode_ssl_Slot_inst_encode, 0, 0, 0, Opcode_ssl_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ssa8l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ssa8l_encode_fns[] = {
   Opcode_ssa8l_Slot_inst_encode, 0, 0, 0, Opcode_ssa8l_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ssa8b_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ssa8b_encode_fns[] = {
   Opcode_ssa8b_Slot_inst_encode, 0, 0, 0, Opcode_ssa8b_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ssai_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ssai_encode_fns[] = {
   Opcode_ssai_Slot_inst_encode, 0, 0, 0, Opcode_ssai_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_sll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_sll_encode_fns[] = {
   Opcode_sll_Slot_inst_encode, 0, 0, 0, Opcode_sll_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_src_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_src_encode_fns[] = {
   Opcode_src_Slot_inst_encode, 0, 0, 0, Opcode_src_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_srl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_srl_encode_fns[] = {
   Opcode_srl_Slot_inst_encode, 0, 0, 0, Opcode_srl_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_sra_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_sra_encode_fns[] = {
   Opcode_sra_Slot_inst_encode, 0, 0, 0, Opcode_sra_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_slli_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_slli_encode_fns[] = {
   Opcode_slli_Slot_inst_encode, 0, 0, 0, Opcode_slli_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_srai_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_srai_encode_fns[] = {
   Opcode_srai_Slot_inst_encode, 0, 0, 0, Opcode_srai_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_srli_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_srli_encode_fns[] = {
   Opcode_srli_Slot_inst_encode, 0, 0, 0, Opcode_srli_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_memw_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_memw_encode_fns[] = {
   Opcode_memw_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_extw_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_extw_encode_fns[] = {
   Opcode_extw_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_isync_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_isync_encode_fns[] = {
   Opcode_isync_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsync_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsync_encode_fns[] = {
   Opcode_rsync_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_esync_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_esync_encode_fns[] = {
   Opcode_esync_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_dsync_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_dsync_encode_fns[] = {
   Opcode_dsync_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsil_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsil_encode_fns[] = {
   Opcode_rsil_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_lend_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_lend_encode_fns[] = {
   Opcode_rsr_lend_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_lend_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_lend_encode_fns[] = {
   Opcode_wsr_lend_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_lend_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_lend_encode_fns[] = {
   Opcode_xsr_lend_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_lcount_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_lcount_encode_fns[] = {
   Opcode_rsr_lcount_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_lcount_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_lcount_encode_fns[] = {
   Opcode_wsr_lcount_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_lcount_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_lcount_encode_fns[] = {
   Opcode_xsr_lcount_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_lbeg_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_lbeg_encode_fns[] = {
   Opcode_rsr_lbeg_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_lbeg_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_lbeg_encode_fns[] = {
   Opcode_wsr_lbeg_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_lbeg_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_lbeg_encode_fns[] = {
   Opcode_xsr_lbeg_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_sar_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_sar_encode_fns[] = {
   Opcode_rsr_sar_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_sar_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_sar_encode_fns[] = {
   Opcode_wsr_sar_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_sar_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_sar_encode_fns[] = {
   Opcode_xsr_sar_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_litbase_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_litbase_encode_fns[] = {
   Opcode_rsr_litbase_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_litbase_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_litbase_encode_fns[] = {
   Opcode_wsr_litbase_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_litbase_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_litbase_encode_fns[] = {
   Opcode_xsr_litbase_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_176_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_176_encode_fns[] = {
   Opcode_rsr_176_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_176_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_176_encode_fns[] = {
   Opcode_wsr_176_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_208_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_208_encode_fns[] = {
   Opcode_rsr_208_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_ps_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_ps_encode_fns[] = {
   Opcode_rsr_ps_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_ps_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_ps_encode_fns[] = {
   Opcode_wsr_ps_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_ps_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_ps_encode_fns[] = {
   Opcode_xsr_ps_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_epc1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_epc1_encode_fns[] = {
   Opcode_rsr_epc1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_epc1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_epc1_encode_fns[] = {
   Opcode_wsr_epc1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_epc1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_epc1_encode_fns[] = {
   Opcode_xsr_epc1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_excsave1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_excsave1_encode_fns[] = {
   Opcode_rsr_excsave1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_excsave1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_excsave1_encode_fns[] = {
   Opcode_wsr_excsave1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_excsave1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_excsave1_encode_fns[] = {
   Opcode_xsr_excsave1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_epc2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_epc2_encode_fns[] = {
   Opcode_rsr_epc2_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_epc2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_epc2_encode_fns[] = {
   Opcode_wsr_epc2_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_epc2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_epc2_encode_fns[] = {
   Opcode_xsr_epc2_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_excsave2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_excsave2_encode_fns[] = {
   Opcode_rsr_excsave2_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_excsave2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_excsave2_encode_fns[] = {
   Opcode_wsr_excsave2_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_excsave2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_excsave2_encode_fns[] = {
   Opcode_xsr_excsave2_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_eps2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_eps2_encode_fns[] = {
   Opcode_rsr_eps2_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_eps2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_eps2_encode_fns[] = {
   Opcode_wsr_eps2_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_eps2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_eps2_encode_fns[] = {
   Opcode_xsr_eps2_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_excvaddr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_excvaddr_encode_fns[] = {
   Opcode_rsr_excvaddr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_excvaddr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_excvaddr_encode_fns[] = {
   Opcode_wsr_excvaddr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_excvaddr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_excvaddr_encode_fns[] = {
   Opcode_xsr_excvaddr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_depc_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_depc_encode_fns[] = {
   Opcode_rsr_depc_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_depc_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_depc_encode_fns[] = {
   Opcode_wsr_depc_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_depc_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_depc_encode_fns[] = {
   Opcode_xsr_depc_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_exccause_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_exccause_encode_fns[] = {
   Opcode_rsr_exccause_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_exccause_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_exccause_encode_fns[] = {
   Opcode_wsr_exccause_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_exccause_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_exccause_encode_fns[] = {
   Opcode_xsr_exccause_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_misc0_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_misc0_encode_fns[] = {
   Opcode_rsr_misc0_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_misc0_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_misc0_encode_fns[] = {
   Opcode_wsr_misc0_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_misc0_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_misc0_encode_fns[] = {
   Opcode_xsr_misc0_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_misc1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_misc1_encode_fns[] = {
   Opcode_rsr_misc1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_misc1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_misc1_encode_fns[] = {
   Opcode_wsr_misc1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_misc1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_misc1_encode_fns[] = {
   Opcode_xsr_misc1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_prid_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_prid_encode_fns[] = {
   Opcode_rsr_prid_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_vecbase_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_vecbase_encode_fns[] = {
   Opcode_rsr_vecbase_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_vecbase_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_vecbase_encode_fns[] = {
   Opcode_wsr_vecbase_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_vecbase_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_vecbase_encode_fns[] = {
   Opcode_xsr_vecbase_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_mul16u_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_mul16u_encode_fns[] = {
   Opcode_mul16u_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_mul16s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_mul16s_encode_fns[] = {
   Opcode_mul16s_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_mull_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_mull_encode_fns[] = {
   Opcode_mull_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rfi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rfi_encode_fns[] = {
   Opcode_rfi_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_waiti_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_waiti_encode_fns[] = {
   Opcode_waiti_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_interrupt_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_interrupt_encode_fns[] = {
   Opcode_rsr_interrupt_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_intset_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_intset_encode_fns[] = {
   Opcode_wsr_intset_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_intclear_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_intclear_encode_fns[] = {
   Opcode_wsr_intclear_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_intenable_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_intenable_encode_fns[] = {
   Opcode_rsr_intenable_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_intenable_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_intenable_encode_fns[] = {
   Opcode_wsr_intenable_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_intenable_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_intenable_encode_fns[] = {
   Opcode_xsr_intenable_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_break_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_break_encode_fns[] = {
   Opcode_break_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_break_n_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_break_n_encode_fns[] = {
   0, 0, Opcode_break_n_Slot_inst16b_encode, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_debugcause_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_debugcause_encode_fns[] = {
   Opcode_rsr_debugcause_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_debugcause_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_debugcause_encode_fns[] = {
   Opcode_wsr_debugcause_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_debugcause_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_debugcause_encode_fns[] = {
   Opcode_xsr_debugcause_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_icount_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_icount_encode_fns[] = {
   Opcode_rsr_icount_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_icount_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_icount_encode_fns[] = {
   Opcode_wsr_icount_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_icount_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_icount_encode_fns[] = {
   Opcode_xsr_icount_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_icountlevel_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_icountlevel_encode_fns[] = {
   Opcode_rsr_icountlevel_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_icountlevel_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_icountlevel_encode_fns[] = {
   Opcode_wsr_icountlevel_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_icountlevel_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_icountlevel_encode_fns[] = {
   Opcode_xsr_icountlevel_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_ddr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_ddr_encode_fns[] = {
   Opcode_rsr_ddr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_ddr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_ddr_encode_fns[] = {
   Opcode_wsr_ddr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_ddr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_ddr_encode_fns[] = {
   Opcode_xsr_ddr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rfdo_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rfdo_encode_fns[] = {
   Opcode_rfdo_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rfdd_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rfdd_encode_fns[] = {
   Opcode_rfdd_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_andb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_andb_encode_fns[] = {
   Opcode_andb_Slot_inst_encode, 0, 0, 0, Opcode_andb_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_andbc_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_andbc_encode_fns[] = {
   Opcode_andbc_Slot_inst_encode, 0, 0, 0, Opcode_andbc_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_orb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_orb_encode_fns[] = {
   Opcode_orb_Slot_inst_encode, 0, 0, 0, Opcode_orb_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_orbc_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_orbc_encode_fns[] = {
   Opcode_orbc_Slot_inst_encode, 0, 0, 0, Opcode_orbc_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_xorb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xorb_encode_fns[] = {
   Opcode_xorb_Slot_inst_encode, 0, 0, 0, Opcode_xorb_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_any4_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_any4_encode_fns[] = {
   Opcode_any4_Slot_inst_encode, 0, 0, 0, Opcode_any4_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_all4_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_all4_encode_fns[] = {
   Opcode_all4_Slot_inst_encode, 0, 0, 0, Opcode_all4_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_any8_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_any8_encode_fns[] = {
   Opcode_any8_Slot_inst_encode, 0, 0, 0, Opcode_any8_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_all8_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_all8_encode_fns[] = {
   Opcode_all8_Slot_inst_encode, 0, 0, 0, Opcode_all8_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bf_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bf_encode_fns[] = {
   Opcode_bf_Slot_inst_encode, 0, 0, 0, Opcode_bf_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_bt_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_bt_encode_fns[] = {
   Opcode_bt_Slot_inst_encode, 0, 0, 0, Opcode_bt_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_movf_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_movf_encode_fns[] = {
   Opcode_movf_Slot_inst_encode, 0, 0, 0, Opcode_movf_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_movt_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_movt_encode_fns[] = {
   Opcode_movt_Slot_inst_encode, 0, 0, 0, Opcode_movt_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_br_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_br_encode_fns[] = {
   Opcode_rsr_br_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_br_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_br_encode_fns[] = {
   Opcode_wsr_br_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_br_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_br_encode_fns[] = {
   Opcode_xsr_br_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_ccount_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_ccount_encode_fns[] = {
   Opcode_rsr_ccount_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_ccount_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_ccount_encode_fns[] = {
   Opcode_wsr_ccount_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_ccount_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_ccount_encode_fns[] = {
   Opcode_xsr_ccount_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_ccompare0_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_ccompare0_encode_fns[] = {
   Opcode_rsr_ccompare0_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_ccompare0_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_ccompare0_encode_fns[] = {
   Opcode_wsr_ccompare0_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_ccompare0_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_ccompare0_encode_fns[] = {
   Opcode_xsr_ccompare0_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_ccompare1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_ccompare1_encode_fns[] = {
   Opcode_rsr_ccompare1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_ccompare1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_ccompare1_encode_fns[] = {
   Opcode_wsr_ccompare1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_ccompare1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_ccompare1_encode_fns[] = {
   Opcode_xsr_ccompare1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ipf_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ipf_encode_fns[] = {
   Opcode_ipf_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ihi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ihi_encode_fns[] = {
   Opcode_ihi_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_iii_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_iii_encode_fns[] = {
   Opcode_iii_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_lict_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_lict_encode_fns[] = {
   Opcode_lict_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_licw_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_licw_encode_fns[] = {
   Opcode_licw_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_sict_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_sict_encode_fns[] = {
   Opcode_sict_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_sicw_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_sicw_encode_fns[] = {
   Opcode_sicw_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_dhwb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_dhwb_encode_fns[] = {
   Opcode_dhwb_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_dhwbi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_dhwbi_encode_fns[] = {
   Opcode_dhwbi_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_diwb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_diwb_encode_fns[] = {
   Opcode_diwb_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_diwbi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_diwbi_encode_fns[] = {
   Opcode_diwbi_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_dhi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_dhi_encode_fns[] = {
   Opcode_dhi_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_dii_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_dii_encode_fns[] = {
   Opcode_dii_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_dpfr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_dpfr_encode_fns[] = {
   Opcode_dpfr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_dpfw_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_dpfw_encode_fns[] = {
   Opcode_dpfw_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_dpfro_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_dpfro_encode_fns[] = {
   Opcode_dpfro_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_dpfwo_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_dpfwo_encode_fns[] = {
   Opcode_dpfwo_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_sdct_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_sdct_encode_fns[] = {
   Opcode_sdct_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ldct_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ldct_encode_fns[] = {
   Opcode_ldct_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_ptevaddr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_ptevaddr_encode_fns[] = {
   Opcode_wsr_ptevaddr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_ptevaddr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_ptevaddr_encode_fns[] = {
   Opcode_rsr_ptevaddr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_ptevaddr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_ptevaddr_encode_fns[] = {
   Opcode_xsr_ptevaddr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_rasid_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_rasid_encode_fns[] = {
   Opcode_rsr_rasid_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_rasid_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_rasid_encode_fns[] = {
   Opcode_wsr_rasid_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_rasid_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_rasid_encode_fns[] = {
   Opcode_xsr_rasid_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_itlbcfg_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_itlbcfg_encode_fns[] = {
   Opcode_rsr_itlbcfg_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_itlbcfg_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_itlbcfg_encode_fns[] = {
   Opcode_wsr_itlbcfg_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_itlbcfg_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_itlbcfg_encode_fns[] = {
   Opcode_xsr_itlbcfg_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_dtlbcfg_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_dtlbcfg_encode_fns[] = {
   Opcode_rsr_dtlbcfg_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_dtlbcfg_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_dtlbcfg_encode_fns[] = {
   Opcode_wsr_dtlbcfg_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_dtlbcfg_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_dtlbcfg_encode_fns[] = {
   Opcode_xsr_dtlbcfg_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_idtlb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_idtlb_encode_fns[] = {
   Opcode_idtlb_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_pdtlb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_pdtlb_encode_fns[] = {
   Opcode_pdtlb_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rdtlb0_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rdtlb0_encode_fns[] = {
   Opcode_rdtlb0_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rdtlb1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rdtlb1_encode_fns[] = {
   Opcode_rdtlb1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wdtlb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wdtlb_encode_fns[] = {
   Opcode_wdtlb_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_iitlb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_iitlb_encode_fns[] = {
   Opcode_iitlb_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_pitlb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_pitlb_encode_fns[] = {
   Opcode_pitlb_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ritlb0_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ritlb0_encode_fns[] = {
   Opcode_ritlb0_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ritlb1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ritlb1_encode_fns[] = {
   Opcode_ritlb1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_witlb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_witlb_encode_fns[] = {
   Opcode_witlb_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ldpte_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ldpte_encode_fns[] = {
   Opcode_ldpte_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_hwwitlba_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_hwwitlba_encode_fns[] = {
   Opcode_hwwitlba_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_hwwdtlba_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_hwwdtlba_encode_fns[] = {
   Opcode_hwwdtlba_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_cpenable_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_cpenable_encode_fns[] = {
   Opcode_rsr_cpenable_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_cpenable_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_cpenable_encode_fns[] = {
   Opcode_wsr_cpenable_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_cpenable_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_cpenable_encode_fns[] = {
   Opcode_xsr_cpenable_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_clamps_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_clamps_encode_fns[] = {
   Opcode_clamps_Slot_inst_encode, 0, 0, 0, Opcode_clamps_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_min_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_min_encode_fns[] = {
   Opcode_min_Slot_inst_encode, 0, 0, 0, Opcode_min_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_max_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_max_encode_fns[] = {
   Opcode_max_Slot_inst_encode, 0, 0, 0, Opcode_max_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_minu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_minu_encode_fns[] = {
   Opcode_minu_Slot_inst_encode, 0, 0, 0, Opcode_minu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_maxu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_maxu_encode_fns[] = {
   Opcode_maxu_Slot_inst_encode, 0, 0, 0, Opcode_maxu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_nsa_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_nsa_encode_fns[] = {
   Opcode_nsa_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_nsau_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_nsau_encode_fns[] = {
   Opcode_nsau_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_sext_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_sext_encode_fns[] = {
   Opcode_sext_Slot_inst_encode, 0, 0, 0, Opcode_sext_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_l32ai_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_l32ai_encode_fns[] = {
   Opcode_l32ai_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_s32ri_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_s32ri_encode_fns[] = {
   Opcode_s32ri_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_s32c1i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_s32c1i_encode_fns[] = {
   Opcode_s32c1i_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_scompare1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_scompare1_encode_fns[] = {
   Opcode_rsr_scompare1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_scompare1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_scompare1_encode_fns[] = {
   Opcode_wsr_scompare1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_scompare1_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_scompare1_encode_fns[] = {
   Opcode_xsr_scompare1_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rsr_atomctl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rsr_atomctl_encode_fns[] = {
   Opcode_rsr_atomctl_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wsr_atomctl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wsr_atomctl_encode_fns[] = {
   Opcode_wsr_atomctl_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_xsr_atomctl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_xsr_atomctl_encode_fns[] = {
   Opcode_xsr_atomctl_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rer_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rer_encode_fns[] = {
   Opcode_rer_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wer_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wer_encode_fns[] = {
   Opcode_wer_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_ovf_sar_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_ovf_sar_encode_fns[] = {
   Opcode_rur_ae_ovf_sar_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_ovf_sar_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_ovf_sar_encode_fns[] = {
   Opcode_wur_ae_ovf_sar_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_bithead_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_bithead_encode_fns[] = {
   Opcode_rur_ae_bithead_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_bithead_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_bithead_encode_fns[] = {
   Opcode_wur_ae_bithead_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_ts_fts_bu_bp_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_ts_fts_bu_bp_encode_fns[] = {
   Opcode_rur_ae_ts_fts_bu_bp_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_ts_fts_bu_bp_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_ts_fts_bu_bp_encode_fns[] = {
   Opcode_wur_ae_ts_fts_bu_bp_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_sd_no_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_sd_no_encode_fns[] = {
   Opcode_rur_ae_sd_no_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_sd_no_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_sd_no_encode_fns[] = {
   Opcode_wur_ae_sd_no_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_overflow_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_overflow_encode_fns[] = {
   Opcode_rur_ae_overflow_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_overflow_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_overflow_encode_fns[] = {
   Opcode_wur_ae_overflow_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_sar_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_sar_encode_fns[] = {
   Opcode_rur_ae_sar_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_sar_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_sar_encode_fns[] = {
   Opcode_wur_ae_sar_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_bitptr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_bitptr_encode_fns[] = {
   Opcode_rur_ae_bitptr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_bitptr_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_bitptr_encode_fns[] = {
   Opcode_wur_ae_bitptr_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_bitsused_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_bitsused_encode_fns[] = {
   Opcode_rur_ae_bitsused_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_bitsused_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_bitsused_encode_fns[] = {
   Opcode_wur_ae_bitsused_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_tablesize_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_tablesize_encode_fns[] = {
   Opcode_rur_ae_tablesize_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_tablesize_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_tablesize_encode_fns[] = {
   Opcode_wur_ae_tablesize_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_first_ts_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_first_ts_encode_fns[] = {
   Opcode_rur_ae_first_ts_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_first_ts_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_first_ts_encode_fns[] = {
   Opcode_wur_ae_first_ts_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_nextoffset_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_nextoffset_encode_fns[] = {
   Opcode_rur_ae_nextoffset_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_nextoffset_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_nextoffset_encode_fns[] = {
   Opcode_wur_ae_nextoffset_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_rur_ae_searchdone_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_rur_ae_searchdone_encode_fns[] = {
   Opcode_rur_ae_searchdone_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_wur_ae_searchdone_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_wur_ae_searchdone_encode_fns[] = {
   Opcode_wur_ae_searchdone_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp16f_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp16f_i_encode_fns[] = {
   Opcode_ae_lp16f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp16f_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp16f_iu_encode_fns[] = {
   Opcode_ae_lp16f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp16f_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp16f_x_encode_fns[] = {
   Opcode_ae_lp16f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp16f_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp16f_xu_encode_fns[] = {
   Opcode_ae_lp16f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24_i_encode_fns[] = {
   Opcode_ae_lp24_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24_iu_encode_fns[] = {
   Opcode_ae_lp24_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24_x_encode_fns[] = {
   Opcode_ae_lp24_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24_xu_encode_fns[] = {
   Opcode_ae_lp24_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24f_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24f_i_encode_fns[] = {
   Opcode_ae_lp24f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24f_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24f_iu_encode_fns[] = {
   Opcode_ae_lp24f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24f_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24f_x_encode_fns[] = {
   Opcode_ae_lp24f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24f_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24f_xu_encode_fns[] = {
   Opcode_ae_lp24f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp16x2f_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_i_encode_fns[] = {
   Opcode_ae_lp16x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp16x2f_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_iu_encode_fns[] = {
   Opcode_ae_lp16x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp16x2f_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_x_encode_fns[] = {
   Opcode_ae_lp16x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp16x2f_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_xu_encode_fns[] = {
   Opcode_ae_lp16x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24x2f_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_i_encode_fns[] = {
   Opcode_ae_lp24x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24x2f_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_iu_encode_fns[] = {
   Opcode_ae_lp24x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24x2f_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_x_encode_fns[] = {
   Opcode_ae_lp24x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24x2f_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_xu_encode_fns[] = {
   Opcode_ae_lp24x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24x2_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24x2_i_encode_fns[] = {
   Opcode_ae_lp24x2_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24x2_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24x2_iu_encode_fns[] = {
   Opcode_ae_lp24x2_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24x2_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24x2_x_encode_fns[] = {
   Opcode_ae_lp24x2_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lp24x2_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lp24x2_xu_encode_fns[] = {
   Opcode_ae_lp24x2_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp16x2f_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_i_encode_fns[] = {
   Opcode_ae_sp16x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp16x2f_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_iu_encode_fns[] = {
   Opcode_ae_sp16x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp16x2f_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_x_encode_fns[] = {
   Opcode_ae_sp16x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp16x2f_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_xu_encode_fns[] = {
   Opcode_ae_sp16x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24x2s_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_i_encode_fns[] = {
   Opcode_ae_sp24x2s_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24x2s_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_iu_encode_fns[] = {
   Opcode_ae_sp24x2s_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24x2s_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_x_encode_fns[] = {
   Opcode_ae_sp24x2s_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24x2s_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_xu_encode_fns[] = {
   Opcode_ae_sp24x2s_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24x2f_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_i_encode_fns[] = {
   Opcode_ae_sp24x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24x2f_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_iu_encode_fns[] = {
   Opcode_ae_sp24x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24x2f_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_x_encode_fns[] = {
   Opcode_ae_sp24x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24x2f_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_xu_encode_fns[] = {
   Opcode_ae_sp24x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp16f_l_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_i_encode_fns[] = {
   Opcode_ae_sp16f_l_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp16f_l_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_iu_encode_fns[] = {
   Opcode_ae_sp16f_l_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp16f_l_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_x_encode_fns[] = {
   Opcode_ae_sp16f_l_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp16f_l_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_xu_encode_fns[] = {
   Opcode_ae_sp16f_l_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24s_l_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_i_encode_fns[] = {
   Opcode_ae_sp24s_l_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24s_l_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_iu_encode_fns[] = {
   Opcode_ae_sp24s_l_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24s_l_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_x_encode_fns[] = {
   Opcode_ae_sp24s_l_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24s_l_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_xu_encode_fns[] = {
   Opcode_ae_sp24s_l_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24f_l_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_i_encode_fns[] = {
   Opcode_ae_sp24f_l_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24f_l_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_iu_encode_fns[] = {
   Opcode_ae_sp24f_l_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24f_l_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_x_encode_fns[] = {
   Opcode_ae_sp24f_l_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sp24f_l_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_xu_encode_fns[] = {
   Opcode_ae_sp24f_l_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lq56_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lq56_i_encode_fns[] = {
   Opcode_ae_lq56_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lq56_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lq56_iu_encode_fns[] = {
   Opcode_ae_lq56_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lq56_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lq56_x_encode_fns[] = {
   Opcode_ae_lq56_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lq56_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lq56_xu_encode_fns[] = {
   Opcode_ae_lq56_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lq32f_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lq32f_i_encode_fns[] = {
   Opcode_ae_lq32f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lq32f_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lq32f_iu_encode_fns[] = {
   Opcode_ae_lq32f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lq32f_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lq32f_x_encode_fns[] = {
   Opcode_ae_lq32f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lq32f_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lq32f_xu_encode_fns[] = {
   Opcode_ae_lq32f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sq56s_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sq56s_i_encode_fns[] = {
   Opcode_ae_sq56s_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sq56s_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sq56s_iu_encode_fns[] = {
   Opcode_ae_sq56s_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sq56s_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sq56s_x_encode_fns[] = {
   Opcode_ae_sq56s_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sq56s_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sq56s_xu_encode_fns[] = {
   Opcode_ae_sq56s_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sq32f_i_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sq32f_i_encode_fns[] = {
   Opcode_ae_sq32f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_i_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sq32f_iu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sq32f_iu_encode_fns[] = {
   Opcode_ae_sq32f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_iu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sq32f_x_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sq32f_x_encode_fns[] = {
   Opcode_ae_sq32f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_x_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sq32f_xu_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sq32f_xu_encode_fns[] = {
   Opcode_ae_sq32f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_xu_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_zerop48_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_zerop48_encode_fns[] = {
   0, 0, 0, Opcode_ae_zerop48_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movp48_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movp48_encode_fns[] = {
   Opcode_ae_movp48_Slot_inst_encode, 0, 0, Opcode_ae_movp48_Slot_ae_slot1_encode, Opcode_ae_movp48_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_selp24_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_selp24_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_selp24_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_selp24_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_selp24_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_selp24_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_selp24_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_selp24_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_selp24_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_selp24_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_selp24_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_selp24_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movtp24x2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movtp24x2_encode_fns[] = {
   0, 0, 0, Opcode_ae_movtp24x2_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movfp24x2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movfp24x2_encode_fns[] = {
   0, 0, 0, Opcode_ae_movfp24x2_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movtp48_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movtp48_encode_fns[] = {
   0, 0, 0, Opcode_ae_movtp48_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movfp48_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movfp48_encode_fns[] = {
   0, 0, 0, Opcode_ae_movfp48_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movpa24x2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movpa24x2_encode_fns[] = {
   Opcode_ae_movpa24x2_Slot_inst_encode, 0, 0, 0, Opcode_ae_movpa24x2_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_truncp24a32x2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_truncp24a32x2_encode_fns[] = {
   Opcode_ae_truncp24a32x2_Slot_inst_encode, 0, 0, 0, Opcode_ae_truncp24a32x2_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_cvta32p24_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_cvta32p24_l_encode_fns[] = {
   Opcode_ae_cvta32p24_l_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvta32p24_l_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_cvta32p24_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_cvta32p24_h_encode_fns[] = {
   Opcode_ae_cvta32p24_h_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvta32p24_h_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_ll_encode_fns[] = {
   Opcode_ae_cvtp24a16x2_ll_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_ll_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_lh_encode_fns[] = {
   Opcode_ae_cvtp24a16x2_lh_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_lh_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_hl_encode_fns[] = {
   Opcode_ae_cvtp24a16x2_hl_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_hl_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_hh_encode_fns[] = {
   Opcode_ae_cvtp24a16x2_hh_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_hh_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_truncp24q48x2_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_truncp24q48x2_encode_fns[] = {
   0, 0, 0, Opcode_ae_truncp24q48x2_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_truncp16_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_truncp16_encode_fns[] = {
   0, 0, 0, Opcode_ae_truncp16_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_roundsp24q48sym_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_roundsp24q48sym_encode_fns[] = {
   0, 0, 0, Opcode_ae_roundsp24q48sym_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_roundsp24q48asym_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_roundsp24q48asym_encode_fns[] = {
   0, 0, 0, Opcode_ae_roundsp24q48asym_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_roundsp16q48sym_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_roundsp16q48sym_encode_fns[] = {
   0, 0, 0, Opcode_ae_roundsp16q48sym_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_roundsp16q48asym_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_roundsp16q48asym_encode_fns[] = {
   0, 0, 0, Opcode_ae_roundsp16q48asym_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_roundsp16sym_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_roundsp16sym_encode_fns[] = {
   0, 0, 0, Opcode_ae_roundsp16sym_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_roundsp16asym_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_roundsp16asym_encode_fns[] = {
   0, 0, 0, Opcode_ae_roundsp16asym_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_zeroq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_zeroq56_encode_fns[] = {
   0, 0, 0, Opcode_ae_zeroq56_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movq56_encode_fns[] = {
   Opcode_ae_movq56_Slot_inst_encode, 0, 0, Opcode_ae_movq56_Slot_ae_slot1_encode, Opcode_ae_movq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movtq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movtq56_encode_fns[] = {
   Opcode_ae_movtq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_movtq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movfq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movfq56_encode_fns[] = {
   Opcode_ae_movfq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_movfq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_cvtq48a32s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_cvtq48a32s_encode_fns[] = {
   Opcode_ae_cvtq48a32s_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtq48a32s_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_cvtq48p24s_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_cvtq48p24s_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_cvtq48p24s_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_cvtq48p24s_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_cvtq48p24s_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_cvtq48p24s_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_satq48s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_satq48s_encode_fns[] = {
   0, 0, 0, Opcode_ae_satq48s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_truncq32_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_truncq32_encode_fns[] = {
   0, 0, 0, Opcode_ae_truncq32_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_roundsq32sym_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_roundsq32sym_encode_fns[] = {
   0, 0, 0, Opcode_ae_roundsq32sym_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_roundsq32asym_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_roundsq32asym_encode_fns[] = {
   0, 0, 0, Opcode_ae_roundsq32asym_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_trunca32q48_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_trunca32q48_encode_fns[] = {
   Opcode_ae_trunca32q48_Slot_inst_encode, 0, 0, 0, Opcode_ae_trunca32q48_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movap24s_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movap24s_l_encode_fns[] = {
   Opcode_ae_movap24s_l_Slot_inst_encode, 0, 0, 0, Opcode_ae_movap24s_l_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_movap24s_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_movap24s_h_encode_fns[] = {
   Opcode_ae_movap24s_h_Slot_inst_encode, 0, 0, 0, Opcode_ae_movap24s_h_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_trunca16p24s_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_trunca16p24s_l_encode_fns[] = {
   Opcode_ae_trunca16p24s_l_Slot_inst_encode, 0, 0, 0, Opcode_ae_trunca16p24s_l_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_trunca16p24s_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_trunca16p24s_h_encode_fns[] = {
   Opcode_ae_trunca16p24s_h_Slot_inst_encode, 0, 0, 0, Opcode_ae_trunca16p24s_h_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_addp24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_addp24_encode_fns[] = {
   0, 0, 0, Opcode_ae_addp24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_subp24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_subp24_encode_fns[] = {
   0, 0, 0, Opcode_ae_subp24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_negp24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_negp24_encode_fns[] = {
   0, 0, 0, Opcode_ae_negp24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_absp24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_absp24_encode_fns[] = {
   0, 0, 0, Opcode_ae_absp24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_maxp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_maxp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_maxp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_minp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_minp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_minp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_maxbp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_maxbp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_maxbp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_minbp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_minbp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_minbp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_addsp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_addsp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_addsp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_subsp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_subsp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_subsp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_negsp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_negsp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_negsp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_abssp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_abssp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_abssp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_andp48_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_andp48_encode_fns[] = {
   0, 0, 0, Opcode_ae_andp48_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_nandp48_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_nandp48_encode_fns[] = {
   0, 0, 0, Opcode_ae_nandp48_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_orp48_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_orp48_encode_fns[] = {
   0, 0, 0, Opcode_ae_orp48_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_xorp48_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_xorp48_encode_fns[] = {
   0, 0, 0, Opcode_ae_xorp48_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_ltp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_ltp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_ltp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lep24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lep24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_lep24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_eqp24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_eqp24_encode_fns[] = {
   0, 0, 0, Opcode_ae_eqp24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_addq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_addq56_encode_fns[] = {
   0, 0, 0, Opcode_ae_addq56_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_subq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_subq56_encode_fns[] = {
   0, 0, 0, Opcode_ae_subq56_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_negq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_negq56_encode_fns[] = {
   0, 0, 0, Opcode_ae_negq56_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_absq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_absq56_encode_fns[] = {
   0, 0, 0, Opcode_ae_absq56_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_maxq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_maxq56s_encode_fns[] = {
   0, 0, 0, Opcode_ae_maxq56s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_minq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_minq56s_encode_fns[] = {
   0, 0, 0, Opcode_ae_minq56s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_maxbq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_maxbq56s_encode_fns[] = {
   0, 0, 0, Opcode_ae_maxbq56s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_minbq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_minbq56s_encode_fns[] = {
   0, 0, 0, Opcode_ae_minbq56s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_addsq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_addsq56s_encode_fns[] = {
   0, 0, 0, Opcode_ae_addsq56s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_subsq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_subsq56s_encode_fns[] = {
   0, 0, 0, Opcode_ae_subsq56s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_negsq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_negsq56s_encode_fns[] = {
   0, 0, 0, Opcode_ae_negsq56s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_abssq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_abssq56s_encode_fns[] = {
   0, 0, 0, Opcode_ae_abssq56s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_andq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_andq56_encode_fns[] = {
   0, 0, 0, Opcode_ae_andq56_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_nandq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_nandq56_encode_fns[] = {
   0, 0, 0, Opcode_ae_nandq56_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_orq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_orq56_encode_fns[] = {
   0, 0, 0, Opcode_ae_orq56_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_xorq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_xorq56_encode_fns[] = {
   0, 0, 0, Opcode_ae_xorq56_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sllip24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sllip24_encode_fns[] = {
   0, 0, 0, Opcode_ae_sllip24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_srlip24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_srlip24_encode_fns[] = {
   0, 0, 0, Opcode_ae_srlip24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sraip24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sraip24_encode_fns[] = {
   0, 0, 0, Opcode_ae_sraip24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sllsp24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sllsp24_encode_fns[] = {
   0, 0, 0, Opcode_ae_sllsp24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_srlsp24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_srlsp24_encode_fns[] = {
   0, 0, 0, Opcode_ae_srlsp24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_srasp24_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_srasp24_encode_fns[] = {
   0, 0, 0, Opcode_ae_srasp24_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sllisp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sllisp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_sllisp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sllssp24s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sllssp24s_encode_fns[] = {
   0, 0, 0, Opcode_ae_sllssp24s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_slliq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_slliq56_encode_fns[] = {
   Opcode_ae_slliq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_slliq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_srliq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_srliq56_encode_fns[] = {
   Opcode_ae_srliq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srliq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sraiq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sraiq56_encode_fns[] = {
   Opcode_ae_sraiq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sraiq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sllsq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sllsq56_encode_fns[] = {
   Opcode_ae_sllsq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllsq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_srlsq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_srlsq56_encode_fns[] = {
   Opcode_ae_srlsq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srlsq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_srasq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_srasq56_encode_fns[] = {
   Opcode_ae_srasq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srasq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sllaq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sllaq56_encode_fns[] = {
   Opcode_ae_sllaq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllaq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_srlaq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_srlaq56_encode_fns[] = {
   Opcode_ae_srlaq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srlaq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sraaq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sraaq56_encode_fns[] = {
   Opcode_ae_sraaq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sraaq56_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sllisq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sllisq56s_encode_fns[] = {
   Opcode_ae_sllisq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllisq56s_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sllssq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sllssq56s_encode_fns[] = {
   Opcode_ae_sllssq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllssq56s_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sllasq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sllasq56s_encode_fns[] = {
   Opcode_ae_sllasq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllasq56s_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_ltq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_ltq56s_encode_fns[] = {
   0, 0, 0, Opcode_ae_ltq56s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_leq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_leq56s_encode_fns[] = {
   0, 0, 0, Opcode_ae_leq56s_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_eqq56_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_eqq56_encode_fns[] = {
   0, 0, 0, Opcode_ae_eqq56_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_nsaq56s_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_nsaq56s_encode_fns[] = {
   Opcode_ae_nsaq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_nsaq56s_Slot_ae_slot0_encode
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfs32p16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfp24s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfp24s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulp24s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulp24s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulp24s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfs32p16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfp24s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfp24s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulp24s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulp24s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulp24s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfs32p16s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfp24s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfp24s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulp24s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulp24s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulp24s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfs32p16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfp24s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfp24s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulp24s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulp24s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulp24s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafs32p16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafp24s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafp24s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulap24s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulap24s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulap24s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafs32p16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafp24s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafp24s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulap24s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulap24s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulap24s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafs32p16s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafp24s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafp24s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulap24s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulap24s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulap24s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafs32p16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafp24s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafp24s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulap24s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulap24s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulap24s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfs32p16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfp24s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsp24s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsp24s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfs32p16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfp24s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsp24s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsp24s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfs32p16s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfp24s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsp24s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsp24s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfs32p16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfp24s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsp24s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsp24s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafs56p24s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulas56p24s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafs56p24s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulas56p24s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafs56p24s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulas56p24s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafs56p24s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulas56p24s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfs56p24s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulss56p24s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfs56p24s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulss56p24s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfs56p24s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_hl_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_hl_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulss56p24s_hl_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfs56p24s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulss56p24s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16s_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16s_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfq32sp16s_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16s_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16s_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfq32sp16s_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16u_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16u_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfq32sp16u_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16u_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16u_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulfq32sp16u_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulq32sp16s_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16s_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulq32sp16s_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulq32sp16s_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16s_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulq32sp16s_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulq32sp16u_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16u_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulq32sp16u_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulq32sp16u_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16u_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulq32sp16u_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16s_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16s_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafq32sp16s_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16s_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16s_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafq32sp16s_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16u_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16u_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafq32sp16u_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16u_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16u_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulafq32sp16u_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16s_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16s_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulaq32sp16s_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16s_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16s_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulaq32sp16s_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16u_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16u_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulaq32sp16u_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16u_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16u_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulaq32sp16u_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16s_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16s_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfq32sp16s_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16s_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16s_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfq32sp16s_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16u_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16u_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfq32sp16u_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16u_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16u_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsfq32sp16u_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16s_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16s_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsq32sp16s_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16s_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16s_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsq32sp16s_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16u_l_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16u_l_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsq32sp16u_l_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16u_h_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16u_h_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsq32sp16u_h_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaaq32sp16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaafq32sp16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaaq32sp16u_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaafq32sp16u_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaaq32sp16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaafq32sp16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaaq32sp16u_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaafq32sp16u_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaaq32sp16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaafq32sp16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaaq32sp16u_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaafq32sp16u_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasq32sp16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasfq32sp16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasq32sp16u_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasfq32sp16u_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasq32sp16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasfq32sp16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasq32sp16u_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasfq32sp16u_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasq32sp16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasfq32sp16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasq32sp16u_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasfq32sp16u_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsaq32sp16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsafq32sp16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsaq32sp16u_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsafq32sp16u_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsaq32sp16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsafq32sp16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsaq32sp16u_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsafq32sp16u_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsaq32sp16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsafq32sp16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsaq32sp16u_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsafq32sp16u_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssq32sp16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssfq32sp16s_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssq32sp16u_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssfq32sp16u_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssq32sp16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssfq32sp16s_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssq32sp16u_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_hh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_hh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssfq32sp16u_hh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssq32sp16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssfq32sp16s_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssq32sp16u_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssfq32sp16u_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaafp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaafp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaafp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaap24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaap24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaap24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaafp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaafp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaafp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzaap24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzaap24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzaap24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasfp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasfp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasfp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasfp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasfp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasfp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzasp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzasp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzasp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsafp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsafp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsafp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsap24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsap24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsap24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsafp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsafp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsafp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzsap24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzsap24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzsap24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssfp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssfp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssfp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssfp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssfp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssfp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulzssp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulzssp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulzssp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulaafp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulaafp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulaafp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulaap24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulaap24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulaap24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulaafp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulaafp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulaafp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulaap24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulaap24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulaap24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulasfp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulasfp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulasfp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulasp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulasp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulasp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulasfp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulasfp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulasfp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulasp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulasp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulasp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsafp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsafp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsafp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsap24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsap24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsap24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsafp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsafp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsafp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulsap24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulsap24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulsap24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulssfp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulssfp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulssfp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulssp24s_hh_ll_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulssp24s_hh_ll_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulssp24s_hh_ll_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulssfp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulssfp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulssfp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_mulssp24s_hl_lh_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_mulssp24s_hl_lh_encode_fns[] = {
   0, 0, 0, Opcode_ae_mulssp24s_hl_lh_Slot_ae_slot1_encode, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sha32_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sha32_encode_fns[] = {
   Opcode_ae_sha32_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_vldl32t_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_vldl32t_encode_fns[] = {
   Opcode_ae_vldl32t_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_vldl16t_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_vldl16t_encode_fns[] = {
   Opcode_ae_vldl16t_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_vldl16c_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_vldl16c_encode_fns[] = {
   Opcode_ae_vldl16c_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_vldsht_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_vldsht_encode_fns[] = {
   Opcode_ae_vldsht_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lb_encode_fns[] = {
   Opcode_ae_lb_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lbi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lbi_encode_fns[] = {
   Opcode_ae_lbi_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lbk_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lbk_encode_fns[] = {
   Opcode_ae_lbk_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_lbki_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_lbki_encode_fns[] = {
   Opcode_ae_lbki_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_db_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_db_encode_fns[] = {
   Opcode_ae_db_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_dbi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_dbi_encode_fns[] = {
   Opcode_ae_dbi_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_vlel32t_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_vlel32t_encode_fns[] = {
   Opcode_ae_vlel32t_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_vlel16t_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_vlel16t_encode_fns[] = {
   Opcode_ae_vlel16t_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sb_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sb_encode_fns[] = {
   Opcode_ae_sb_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sbi_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sbi_encode_fns[] = {
   Opcode_ae_sbi_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_vles16c_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_vles16c_encode_fns[] = {
   Opcode_ae_vles16c_Slot_inst_encode, 0, 0, 0, 0
 };
 
-xtensa_opcode_encode_fn Opcode_ae_sbf_encode_fns[] = {
+static xtensa_opcode_encode_fn Opcode_ae_sbf_encode_fns[] = {
   Opcode_ae_sbf_Slot_inst_encode, 0, 0, 0, 0
 };
 
@@ -30818,7 +30818,7 @@ Slot_inst_decode (const xtensa_insnbuf insn)
 	}
       break;
     }
-  return 0;
+  return XTENSA_UNDEFINED;
 }
 
 static int
@@ -30869,7 +30869,7 @@ Slot_inst16b_decode (const xtensa_insnbuf insn)
 	}
       break;
     }
-  return 0;
+  return XTENSA_UNDEFINED;
 }
 
 static int
@@ -30886,7 +30886,7 @@ Slot_inst16a_decode (const xtensa_insnbuf insn)
     case 11:
       return OPCODE_ADDI_N;
     }
-  return 0;
+  return XTENSA_UNDEFINED;
 }
 
 static int
@@ -31561,7 +31561,7 @@ Slot_ae_slot0_decode (const xtensa_insnbuf insn)
 	return OPCODE_BF;
       break;
     }
-  return 0;
+  return XTENSA_UNDEFINED;
 }
 
 static int
@@ -32279,7 +32279,7 @@ Slot_ae_slot1_decode (const xtensa_insnbuf insn)
 	return OPCODE_AE_MULZSSQ32SP16U_LL;
       break;
     }
-  return 0;
+  return XTENSA_UNDEFINED;
 }
 
 
diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h
index b665bfc006..4d8152682f 100644
--- a/target/xtensa/cpu.h
+++ b/target/xtensa/cpu.h
@@ -131,6 +131,7 @@ enum {
     ACCLO = 16,
     ACCHI = 17,
     MR = 32,
+    PREFCTL = 40,
     WINDOW_BASE = 72,
     WINDOW_START = 73,
     PTEVADDR = 83,
@@ -345,14 +346,21 @@ typedef struct XtensaMemory {
     } location[MAX_NMEMORY];
 } XtensaMemory;
 
+typedef struct opcode_arg {
+    uint32_t imm;
+    uint32_t raw_imm;
+    void *in;
+    void *out;
+} OpcodeArg;
+
 typedef struct DisasContext DisasContext;
-typedef void (*XtensaOpcodeOp)(DisasContext *dc, const uint32_t arg[],
+typedef void (*XtensaOpcodeOp)(DisasContext *dc, const OpcodeArg arg[],
                                const uint32_t par[]);
 typedef bool (*XtensaOpcodeBoolTest)(DisasContext *dc,
-                                     const uint32_t arg[],
+                                     const OpcodeArg arg[],
                                      const uint32_t par[]);
 typedef uint32_t (*XtensaOpcodeUintTest)(DisasContext *dc,
-                                         const uint32_t arg[],
+                                         const OpcodeArg arg[],
                                          const uint32_t par[]);
 
 enum {
@@ -368,19 +376,34 @@ enum {
 
     XTENSA_OP_DIVIDE_BY_ZERO = 0x100,
 
+    /* Postprocessing flags */
     XTENSA_OP_CHECK_INTERRUPTS = 0x200,
     XTENSA_OP_EXIT_TB_M1 = 0x400,
     XTENSA_OP_EXIT_TB_0 = 0x800,
+    XTENSA_OP_SYNC_REGISTER_WINDOW = 0x1000,
+
+    XTENSA_OP_POSTPROCESS =
+        XTENSA_OP_CHECK_INTERRUPTS |
+        XTENSA_OP_EXIT_TB_M1 |
+        XTENSA_OP_EXIT_TB_0 |
+        XTENSA_OP_SYNC_REGISTER_WINDOW,
+
+    XTENSA_OP_NAME_ARRAY = 0x8000,
+
+    XTENSA_OP_CONTROL_FLOW = 0x10000,
+    XTENSA_OP_STORE = 0x20000,
+    XTENSA_OP_LOAD = 0x40000,
+    XTENSA_OP_LOAD_STORE =
+        XTENSA_OP_LOAD | XTENSA_OP_STORE,
 };
 
 typedef struct XtensaOpcodeOps {
-    const char *name;
+    const void *name;
     XtensaOpcodeOp translate;
     XtensaOpcodeBoolTest test_ill;
     XtensaOpcodeUintTest test_overflow;
     const uint32_t *par;
     uint32_t op_flags;
-    uint32_t windowed_register_op;
     uint32_t coprocessor;
 } XtensaOpcodeOps;
 
@@ -438,6 +461,8 @@ struct XtensaConfig {
     xtensa_isa isa;
     XtensaOpcodeOps **opcode_ops;
     const XtensaOpcodeTranslators **opcode_translators;
+    xtensa_regfile a_regfile;
+    void ***regfile;
 
     uint32_t clock_freq_khz;
 
@@ -474,6 +499,7 @@ typedef struct CPUXtensaState {
         float64 f64;
     } fregs[16];
     float_status fp_status;
+    uint32_t windowbase_next;
 
 #ifndef CONFIG_USER_ONLY
     xtensa_tlb_entry itlb[7][MAX_TLB_WAY_SIZE];
@@ -565,8 +591,8 @@ void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
     XTENSA_CPU_TYPE_NAME(XTENSA_DEFAULT_CPU_NOMMU_MODEL)
 
 void xtensa_translate_init(void);
+void **xtensa_get_regfile_by_name(const char *name);
 void xtensa_breakpoint_handler(CPUState *cs);
-void xtensa_finalize_config(XtensaConfig *config);
 void xtensa_register_core(XtensaConfigList *node);
 void xtensa_sim_open_console(Chardev *chr);
 void check_interrupts(CPUXtensaState *s);
@@ -588,8 +614,6 @@ static inline void xtensa_select_static_vectors(CPUXtensaState *env,
     env->static_vectors = n;
 }
 void xtensa_runstall(CPUXtensaState *env, bool runstall);
-XtensaOpcodeOps *xtensa_find_opcode_ops(const XtensaOpcodeTranslators *t,
-                                        const char *opcode);
 
 #define XTENSA_OPTION_BIT(opt) (((uint64_t)1) << (opt))
 #define XTENSA_OPTION_ALL (~(uint64_t)0)
diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c
index bcf2f20d48..f4867a9b56 100644
--- a/target/xtensa/helper.c
+++ b/target/xtensa/helper.c
@@ -30,24 +30,60 @@
 #include "exec/exec-all.h"
 #include "exec/gdbstub.h"
 #include "exec/helper-proto.h"
+#include "qemu/error-report.h"
 #include "qemu/host-utils.h"
 
 static struct XtensaConfigList *xtensa_cores;
 
-static void xtensa_core_class_init(ObjectClass *oc, void *data)
+static void add_translator_to_hash(GHashTable *translator,
+                                   const char *name,
+                                   const XtensaOpcodeOps *opcode)
 {
-    CPUClass *cc = CPU_CLASS(oc);
-    XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc);
-    const XtensaConfig *config = data;
+    if (!g_hash_table_insert(translator, (void *)name, (void *)opcode)) {
+        error_report("Multiple definitions of '%s' opcode in a single table",
+                     name);
+    }
+}
 
-    xcc->config = config;
+static GHashTable *hash_opcode_translators(const XtensaOpcodeTranslators *t)
+{
+    unsigned i, j;
+    GHashTable *translator = g_hash_table_new(g_str_hash, g_str_equal);
 
-    /* Use num_core_regs to see only non-privileged registers in an unmodified
-     * gdb. Use num_regs to see all registers. gdb modification is required
-     * for that: reset bit 0 in the 'flags' field of the registers definitions
-     * in the gdb/xtensa-config.c inside gdb source tree or inside gdb overlay.
-     */
-    cc->gdb_num_core_regs = config->gdb_regmap.num_regs;
+    for (i = 0; i < t->num_opcodes; ++i) {
+        if (t->opcode[i].op_flags & XTENSA_OP_NAME_ARRAY) {
+            const char * const *name = t->opcode[i].name;
+
+            for (j = 0; name[j]; ++j) {
+                add_translator_to_hash(translator,
+                                       (void *)name[j],
+                                       (void *)(t->opcode + i));
+            }
+        } else {
+            add_translator_to_hash(translator,
+                                   (void *)t->opcode[i].name,
+                                   (void *)(t->opcode + i));
+        }
+    }
+    return translator;
+}
+
+static XtensaOpcodeOps *
+xtensa_find_opcode_ops(const XtensaOpcodeTranslators *t,
+                       const char *name)
+{
+    static GHashTable *translators;
+    GHashTable *translator;
+
+    if (translators == NULL) {
+        translators = g_hash_table_new(g_direct_hash, g_direct_equal);
+    }
+    translator = g_hash_table_lookup(translators, t);
+    if (translator == NULL) {
+        translator = hash_opcode_translators(t);
+        g_hash_table_insert(translators, (void *)t, translator);
+    }
+    return g_hash_table_lookup(translator, name);
 }
 
 static void init_libisa(XtensaConfig *config)
@@ -55,11 +91,13 @@ static void init_libisa(XtensaConfig *config)
     unsigned i, j;
     unsigned opcodes;
     unsigned formats;
+    unsigned regfiles;
 
     config->isa = xtensa_isa_init(config->isa_internal, NULL, NULL);
     assert(xtensa_isa_maxlength(config->isa) <= MAX_INSN_LENGTH);
     opcodes = xtensa_isa_num_opcodes(config->isa);
     formats = xtensa_isa_num_formats(config->isa);
+    regfiles = xtensa_isa_num_regfiles(config->isa);
     config->opcode_ops = g_new(XtensaOpcodeOps *, opcodes);
 
     for (i = 0; i < formats; ++i) {
@@ -88,9 +126,23 @@ static void init_libisa(XtensaConfig *config)
 #endif
         config->opcode_ops[i] = ops;
     }
+    config->a_regfile = xtensa_regfile_lookup(config->isa, "AR");
+
+    config->regfile = g_new(void **, regfiles);
+    for (i = 0; i < regfiles; ++i) {
+        const char *name = xtensa_regfile_name(config->isa, i);
+
+        config->regfile[i] = xtensa_get_regfile_by_name(name);
+#ifdef DEBUG
+        if (config->regfile[i] == NULL) {
+            fprintf(stderr, "regfile '%s' not found for %s\n",
+                    name, config->name);
+        }
+#endif
+    }
 }
 
-void xtensa_finalize_config(XtensaConfig *config)
+static void xtensa_finalize_config(XtensaConfig *config)
 {
     if (config->isa_internal) {
         init_libisa(config);
@@ -111,6 +163,24 @@ void xtensa_finalize_config(XtensaConfig *config)
     }
 }
 
+static void xtensa_core_class_init(ObjectClass *oc, void *data)
+{
+    CPUClass *cc = CPU_CLASS(oc);
+    XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc);
+    XtensaConfig *config = data;
+
+    xtensa_finalize_config(config);
+    xcc->config = config;
+
+    /*
+     * Use num_core_regs to see only non-privileged registers in an unmodified
+     * gdb. Use num_regs to see all registers. gdb modification is required
+     * for that: reset bit 0 in the 'flags' field of the registers definitions
+     * in the gdb/xtensa-config.c inside gdb source tree or inside gdb overlay.
+     */
+    cc->gdb_num_core_regs = config->gdb_regmap.num_regs;
+}
+
 void xtensa_register_core(XtensaConfigList *node)
 {
     TypeInfo type = {
diff --git a/target/xtensa/helper.h b/target/xtensa/helper.h
index 2a7db35874..0b9ec670c8 100644
--- a/target/xtensa/helper.h
+++ b/target/xtensa/helper.h
@@ -3,12 +3,11 @@ DEF_HELPER_3(exception_cause, noreturn, env, i32, i32)
 DEF_HELPER_4(exception_cause_vaddr, noreturn, env, i32, i32, i32)
 DEF_HELPER_3(debug_exception, noreturn, env, i32, i32)
 
-DEF_HELPER_2(wsr_windowbase, void, env, i32)
+DEF_HELPER_1(sync_windowbase, void, env)
 DEF_HELPER_4(entry, void, env, i32, i32, i32)
 DEF_HELPER_2(test_ill_retw, void, env, i32)
 DEF_HELPER_2(test_underflow_retw, void, env, i32)
-DEF_HELPER_2(retw, i32, env, i32)
-DEF_HELPER_2(rotw, void, env, i32)
+DEF_HELPER_2(retw, void, env, i32)
 DEF_HELPER_3(window_check, noreturn, env, i32, i32)
 DEF_HELPER_1(restore_owb, void, env)
 DEF_HELPER_2(movsp, void, env, i32)
diff --git a/target/xtensa/import_core.sh b/target/xtensa/import_core.sh
index 039406bf28..e4a2e39f63 100755
--- a/target/xtensa/import_core.sh
+++ b/target/xtensa/import_core.sh
@@ -27,7 +27,7 @@ tar -xf "$OVERLAY" -O gdb/xtensa-config.c | \
 # Fix up known issues in the xtensa-modules.c
 #
 tar -xf "$OVERLAY" -O binutils/xtensa-modules.c | \
-    sed -e 's/\(xtensa_opcode_encode_fn.*\[\] =\)/static \1/' \
+    sed -e 's/^\(xtensa_opcode_encode_fn.*\[\] =\)/static \1/' \
         -e '/^int num_bypass_groups()/,/}/d' \
         -e '/^int num_bypass_group_chunks()/,/}/d' \
         -e '/^uint32 \*bypass_entry(int i)/,/}/d' \
diff --git a/target/xtensa/overlay_tool.h b/target/xtensa/overlay_tool.h
index 12609a0d0c..ea07576bc9 100644
--- a/target/xtensa/overlay_tool.h
+++ b/target/xtensa/overlay_tool.h
@@ -377,7 +377,6 @@
         static XtensaConfigList node = { \
             .config = &core, \
         }; \
-        xtensa_finalize_config(&core); \
         xtensa_register_core(&node); \
     }
 #else
diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c
index d1e9f59b31..77bc04d6b0 100644
--- a/target/xtensa/translate.c
+++ b/target/xtensa/translate.c
@@ -71,7 +71,7 @@ struct DisasContext {
 
     unsigned cpenable;
 
-    uint32_t *raw_arg;
+    uint32_t op_flags;
     xtensa_insnbuf insnbuf;
     xtensa_insnbuf slotbuf;
 };
@@ -79,8 +79,15 @@ struct DisasContext {
 static TCGv_i32 cpu_pc;
 static TCGv_i32 cpu_R[16];
 static TCGv_i32 cpu_FR[16];
+static TCGv_i32 cpu_MR[4];
+static TCGv_i32 cpu_BR[16];
+static TCGv_i32 cpu_BR4[4];
+static TCGv_i32 cpu_BR8[2];
 static TCGv_i32 cpu_SR[256];
 static TCGv_i32 cpu_UR[256];
+static TCGv_i32 cpu_windowbase_next;
+
+static GHashTable *xtensa_regfile_table;
 
 #include "exec/gen-icount.h"
 
@@ -127,6 +134,7 @@ static const XtensaReg sregnames[256] = {
     [MR + 1] = XTENSA_REG("MR1", XTENSA_OPTION_MAC16),
     [MR + 2] = XTENSA_REG("MR2", XTENSA_OPTION_MAC16),
     [MR + 3] = XTENSA_REG("MR3", XTENSA_OPTION_MAC16),
+    [PREFCTL] = XTENSA_REG_BITS("PREFCTL", XTENSA_OPTION_ALL),
     [WINDOW_BASE] = XTENSA_REG("WINDOW_BASE", XTENSA_OPTION_WINDOWED_REGISTER),
     [WINDOW_START] = XTENSA_REG("WINDOW_START",
             XTENSA_OPTION_WINDOWED_REGISTER),
@@ -220,6 +228,15 @@ void xtensa_translate_init(void)
         "f8", "f9", "f10", "f11",
         "f12", "f13", "f14", "f15",
     };
+    static const char * const mregnames[] = {
+        "m0", "m1", "m2", "m3",
+    };
+    static const char * const bregnames[] = {
+        "b0", "b1", "b2", "b3",
+        "b4", "b5", "b6", "b7",
+        "b8", "b9", "b10", "b11",
+        "b12", "b13", "b14", "b15",
+    };
     int i;
 
     cpu_pc = tcg_global_mem_new_i32(cpu_env,
@@ -227,14 +244,41 @@ void xtensa_translate_init(void)
 
     for (i = 0; i < 16; i++) {
         cpu_R[i] = tcg_global_mem_new_i32(cpu_env,
-                offsetof(CPUXtensaState, regs[i]),
-                regnames[i]);
+                                          offsetof(CPUXtensaState, regs[i]),
+                                          regnames[i]);
     }
 
     for (i = 0; i < 16; i++) {
         cpu_FR[i] = tcg_global_mem_new_i32(cpu_env,
-                offsetof(CPUXtensaState, fregs[i].f32[FP_F32_LOW]),
-                fregnames[i]);
+                                           offsetof(CPUXtensaState,
+                                                    fregs[i].f32[FP_F32_LOW]),
+                                           fregnames[i]);
+    }
+
+    for (i = 0; i < 4; i++) {
+        cpu_MR[i] = tcg_global_mem_new_i32(cpu_env,
+                                           offsetof(CPUXtensaState,
+                                                    sregs[MR + i]),
+                                           mregnames[i]);
+    }
+
+    for (i = 0; i < 16; i++) {
+        cpu_BR[i] = tcg_global_mem_new_i32(cpu_env,
+                                           offsetof(CPUXtensaState,
+                                                    sregs[BR]),
+                                           bregnames[i]);
+        if (i % 4 == 0) {
+            cpu_BR4[i / 4] = tcg_global_mem_new_i32(cpu_env,
+                                                    offsetof(CPUXtensaState,
+                                                             sregs[BR]),
+                                                    bregnames[i]);
+        }
+        if (i % 8 == 0) {
+            cpu_BR8[i / 8] = tcg_global_mem_new_i32(cpu_env,
+                                                    offsetof(CPUXtensaState,
+                                                             sregs[BR]),
+                                                    bregnames[i]);
+        }
     }
 
     for (i = 0; i < 256; ++i) {
@@ -252,6 +296,31 @@ void xtensa_translate_init(void)
                     uregnames[i].name);
         }
     }
+
+    cpu_windowbase_next =
+        tcg_global_mem_new_i32(cpu_env,
+                               offsetof(CPUXtensaState, windowbase_next),
+                               "windowbase_next");
+}
+
+void **xtensa_get_regfile_by_name(const char *name)
+{
+    if (xtensa_regfile_table == NULL) {
+        xtensa_regfile_table = g_hash_table_new(g_str_hash, g_str_equal);
+        g_hash_table_insert(xtensa_regfile_table,
+                            (void *)"AR", (void *)cpu_R);
+        g_hash_table_insert(xtensa_regfile_table,
+                            (void *)"MR", (void *)cpu_MR);
+        g_hash_table_insert(xtensa_regfile_table,
+                            (void *)"FR", (void *)cpu_FR);
+        g_hash_table_insert(xtensa_regfile_table,
+                            (void *)"BR", (void *)cpu_BR);
+        g_hash_table_insert(xtensa_regfile_table,
+                            (void *)"BR4", (void *)cpu_BR4);
+        g_hash_table_insert(xtensa_regfile_table,
+                            (void *)"BR8", (void *)cpu_BR8);
+    }
+    return (void **)g_hash_table_lookup(xtensa_regfile_table, (void *)name);
 }
 
 static inline bool option_enabled(DisasContext *dc, int opt)
@@ -363,6 +432,8 @@ static bool gen_check_cpenable(DisasContext *dc, uint32_t cp_mask)
     return true;
 }
 
+static int gen_postprocess(DisasContext *dc, int slot);
+
 static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot)
 {
     tcg_gen_mov_i32(cpu_pc, dest);
@@ -372,6 +443,9 @@ static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot)
     if (dc->base.singlestep_enabled) {
         gen_exception(dc, EXCP_DEBUG);
     } else {
+        if (dc->op_flags & XTENSA_OP_POSTPROCESS) {
+            slot = gen_postprocess(dc, slot);
+        }
         if (slot >= 0) {
             tcg_gen_goto_tb(slot);
             tcg_gen_exit_tb(dc->base.tb, slot);
@@ -387,13 +461,19 @@ static void gen_jump(DisasContext *dc, TCGv dest)
     gen_jump_slot(dc, dest, -1);
 }
 
-static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot)
+static int adjust_jump_slot(DisasContext *dc, uint32_t dest, int slot)
 {
-    TCGv_i32 tmp = tcg_const_i32(dest);
     if (((dc->base.pc_first ^ dest) & TARGET_PAGE_MASK) != 0) {
-        slot = -1;
+        return -1;
+    } else {
+        return slot;
     }
-    gen_jump_slot(dc, tmp, slot);
+}
+
+static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot)
+{
+    TCGv_i32 tmp = tcg_const_i32(dest);
+    gen_jump_slot(dc, tmp, adjust_jump_slot(dc, dest, slot));
     tcg_temp_free(tmp);
 }
 
@@ -410,21 +490,6 @@ static void gen_callw_slot(DisasContext *dc, int callinc, TCGv_i32 dest,
     gen_jump_slot(dc, dest, slot);
 }
 
-static void gen_callw(DisasContext *dc, int callinc, TCGv_i32 dest)
-{
-    gen_callw_slot(dc, callinc, dest, -1);
-}
-
-static void gen_callwi(DisasContext *dc, int callinc, uint32_t dest, int slot)
-{
-    TCGv_i32 tmp = tcg_const_i32(dest);
-    if (((dc->base.pc_first ^ dest) & TARGET_PAGE_MASK) != 0) {
-        slot = -1;
-    }
-    gen_callw_slot(dc, callinc, tmp, slot);
-    tcg_temp_free(tmp);
-}
-
 static bool gen_check_loop_end(DisasContext *dc, int slot)
 {
     if (dc->base.pc_next == dc->lend) {
@@ -560,7 +625,7 @@ static void gen_wsr_acchi(DisasContext *dc, uint32_t sr, TCGv_i32 s)
 #ifndef CONFIG_USER_ONLY
 static void gen_wsr_windowbase(DisasContext *dc, uint32_t sr, TCGv_i32 v)
 {
-    gen_helper_wsr_windowbase(cpu_env, v);
+    tcg_gen_mov_i32(cpu_windowbase_next, v);
 }
 
 static void gen_wsr_windowstart(DisasContext *dc, uint32_t sr, TCGv_i32 v)
@@ -841,11 +906,11 @@ static TCGv_i32 gen_mac16_m(TCGv_i32 v, bool hi, bool is_unsigned)
     return m;
 }
 
-static void gen_zero_check(DisasContext *dc, const uint32_t arg[])
+static void gen_zero_check(DisasContext *dc, const OpcodeArg arg[])
 {
     TCGLabel *label = gen_new_label();
 
-    tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[arg[2]], 0, label);
+    tcg_gen_brcondi_i32(TCG_COND_NE, arg[2].in, 0, label);
     gen_exception_cause(dc, INTEGER_DIVIDE_BY_ZERO_CAUSE);
     gen_set_label(label);
 }
@@ -855,6 +920,270 @@ static inline unsigned xtensa_op0_insn_len(DisasContext *dc, uint8_t op0)
     return xtensa_isa_length_from_chars(dc->config->isa, &op0);
 }
 
+static int gen_postprocess(DisasContext *dc, int slot)
+{
+    uint32_t op_flags = dc->op_flags;
+
+    if (op_flags & XTENSA_OP_CHECK_INTERRUPTS) {
+        gen_check_interrupts(dc);
+    }
+    if (op_flags & XTENSA_OP_SYNC_REGISTER_WINDOW) {
+        gen_helper_sync_windowbase(cpu_env);
+    }
+    if (op_flags & XTENSA_OP_EXIT_TB_M1) {
+        slot = -1;
+    }
+    return slot;
+}
+
+struct opcode_arg_copy {
+    uint32_t resource;
+    void *temp;
+    OpcodeArg *arg;
+};
+
+struct opcode_arg_info {
+    uint32_t resource;
+    int index;
+};
+
+struct slot_prop {
+    XtensaOpcodeOps *ops;
+    OpcodeArg arg[MAX_OPCODE_ARGS];
+    struct opcode_arg_info in[MAX_OPCODE_ARGS];
+    struct opcode_arg_info out[MAX_OPCODE_ARGS];
+    unsigned n_in;
+    unsigned n_out;
+    uint32_t op_flags;
+};
+
+enum resource_type {
+    RES_REGFILE,
+    RES_STATE,
+    RES_MAX,
+};
+
+static uint32_t encode_resource(enum resource_type r, unsigned g, unsigned n)
+{
+    assert(r < RES_MAX && g < 256 && n < 65536);
+    return (r << 24) | (g << 16) | n;
+}
+
+static enum resource_type get_resource_type(uint32_t resource)
+{
+    return resource >> 24;
+}
+
+/*
+ * a depends on b if b must be executed before a,
+ * because a's side effects will destroy b's inputs.
+ */
+static bool op_depends_on(const struct slot_prop *a,
+                          const struct slot_prop *b)
+{
+    unsigned i = 0;
+    unsigned j = 0;
+
+    if (a->op_flags & XTENSA_OP_CONTROL_FLOW) {
+        return true;
+    }
+    if ((a->op_flags & XTENSA_OP_LOAD_STORE) <
+        (b->op_flags & XTENSA_OP_LOAD_STORE)) {
+        return true;
+    }
+    while (i < a->n_out && j < b->n_in) {
+        if (a->out[i].resource < b->in[j].resource) {
+            ++i;
+        } else if (a->out[i].resource > b->in[j].resource) {
+            ++j;
+        } else {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Try to break a dependency on b, append temporary register copy records
+ * to the end of copy and update n_copy in case of success.
+ * This is not always possible: e.g. control flow must always be the last,
+ * load/store must be first and state dependencies are not supported yet.
+ */
+static bool break_dependency(struct slot_prop *a,
+                             struct slot_prop *b,
+                             struct opcode_arg_copy *copy,
+                             unsigned *n_copy)
+{
+    unsigned i = 0;
+    unsigned j = 0;
+    unsigned n = *n_copy;
+    bool rv = false;
+
+    if (a->op_flags & XTENSA_OP_CONTROL_FLOW) {
+        return false;
+    }
+    if ((a->op_flags & XTENSA_OP_LOAD_STORE) <
+        (b->op_flags & XTENSA_OP_LOAD_STORE)) {
+        return false;
+    }
+    while (i < a->n_out && j < b->n_in) {
+        if (a->out[i].resource < b->in[j].resource) {
+            ++i;
+        } else if (a->out[i].resource > b->in[j].resource) {
+            ++j;
+        } else {
+            int index = b->in[j].index;
+
+            if (get_resource_type(a->out[i].resource) != RES_REGFILE ||
+                index < 0) {
+                return false;
+            }
+            copy[n].resource = b->in[j].resource;
+            copy[n].arg = b->arg + index;
+            ++n;
+            ++i;
+            ++j;
+            rv = true;
+        }
+    }
+    *n_copy = n;
+    return rv;
+}
+
+/*
+ * Calculate evaluation order for slot opcodes.
+ * Build opcode order graph and output its nodes in topological sort order.
+ * An edge a -> b in the graph means that opcode a must be followed by
+ * opcode b.
+ */
+static bool tsort(struct slot_prop *slot,
+                  struct slot_prop *sorted[],
+                  unsigned n,
+                  struct opcode_arg_copy *copy,
+                  unsigned *n_copy)
+{
+    struct tsnode {
+        unsigned n_in_edge;
+        unsigned n_out_edge;
+        unsigned out_edge[MAX_INSN_SLOTS];
+    } node[MAX_INSN_SLOTS];
+
+    unsigned in[MAX_INSN_SLOTS];
+    unsigned i, j;
+    unsigned n_in = 0;
+    unsigned n_out = 0;
+    unsigned n_edge = 0;
+    unsigned in_idx = 0;
+    unsigned node_idx = 0;
+
+    for (i = 0; i < n; ++i) {
+        node[i].n_in_edge = 0;
+        node[i].n_out_edge = 0;
+    }
+
+    for (i = 0; i < n; ++i) {
+        unsigned n_out_edge = 0;
+
+        for (j = 0; j < n; ++j) {
+            if (i != j && op_depends_on(slot + j, slot + i)) {
+                node[i].out_edge[n_out_edge] = j;
+                ++node[j].n_in_edge;
+                ++n_out_edge;
+                ++n_edge;
+            }
+        }
+        node[i].n_out_edge = n_out_edge;
+    }
+
+    for (i = 0; i < n; ++i) {
+        if (!node[i].n_in_edge) {
+            in[n_in] = i;
+            ++n_in;
+        }
+    }
+
+again:
+    for (; in_idx < n_in; ++in_idx) {
+        i = in[in_idx];
+        sorted[n_out] = slot + i;
+        ++n_out;
+        for (j = 0; j < node[i].n_out_edge; ++j) {
+            --n_edge;
+            if (--node[node[i].out_edge[j]].n_in_edge == 0) {
+                in[n_in] = node[i].out_edge[j];
+                ++n_in;
+            }
+        }
+    }
+    if (n_edge) {
+        for (; node_idx < n; ++node_idx) {
+            struct tsnode *cnode = node + node_idx;
+
+            if (cnode->n_in_edge) {
+                for (j = 0; j < cnode->n_out_edge; ++j) {
+                    unsigned k = cnode->out_edge[j];
+
+                    if (break_dependency(slot + k, slot + node_idx,
+                                         copy, n_copy) &&
+                        --node[k].n_in_edge == 0) {
+                        in[n_in] = k;
+                        ++n_in;
+                        --n_edge;
+                        cnode->out_edge[j] =
+                            cnode->out_edge[cnode->n_out_edge - 1];
+                        --cnode->n_out_edge;
+                        goto again;
+                    }
+                }
+            }
+        }
+    }
+    return n_edge == 0;
+}
+
+static void opcode_add_resource(struct slot_prop *op,
+                                uint32_t resource, char direction,
+                                int index)
+{
+    switch (direction) {
+    case 'm':
+    case 'i':
+        assert(op->n_in < ARRAY_SIZE(op->in));
+        op->in[op->n_in].resource = resource;
+        op->in[op->n_in].index = index;
+        ++op->n_in;
+        /* fall through */
+    case 'o':
+        if (direction == 'm' || direction == 'o') {
+            assert(op->n_out < ARRAY_SIZE(op->out));
+            op->out[op->n_out].resource = resource;
+            op->out[op->n_out].index = index;
+            ++op->n_out;
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static int resource_compare(const void *a, const void *b)
+{
+    const struct opcode_arg_info *pa = a;
+    const struct opcode_arg_info *pb = b;
+
+    return pa->resource < pb->resource ?
+        -1 : (pa->resource > pb->resource ? 1 : 0);
+}
+
+static int arg_copy_compare(const void *a, const void *b)
+{
+    const struct opcode_arg_copy *pa = a;
+    const struct opcode_arg_copy *pb = b;
+
+    return pa->resource < pb->resource ?
+        -1 : (pa->resource > pb->resource ? 1 : 0);
+}
+
 static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
 {
     xtensa_isa isa = dc->config->isa;
@@ -864,11 +1193,10 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
     int slot, slots;
     unsigned i;
     uint32_t op_flags = 0;
-    struct {
-        XtensaOpcodeOps *ops;
-        uint32_t arg[MAX_OPCODE_ARGS];
-        uint32_t raw_arg[MAX_OPCODE_ARGS];
-    } slot_prop[MAX_INSN_SLOTS];
+    struct slot_prop slot_prop[MAX_INSN_SLOTS];
+    struct slot_prop *ordered[MAX_INSN_SLOTS];
+    struct opcode_arg_copy arg_copy[MAX_INSN_SLOTS * MAX_OPCODE_ARGS];
+    unsigned n_arg_copy = 0;
     uint32_t debug_cause = 0;
     uint32_t windowed_register = 0;
     uint32_t coprocessor = 0;
@@ -898,12 +1226,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
     for (slot = 0; slot < slots; ++slot) {
         xtensa_opcode opc;
         int opnd, vopnd, opnds;
-        uint32_t *raw_arg = slot_prop[slot].raw_arg;
-        uint32_t *arg = slot_prop[slot].arg;
+        OpcodeArg *arg = slot_prop[slot].arg;
         XtensaOpcodeOps *ops;
 
-        dc->raw_arg = raw_arg;
-
         xtensa_format_get_slot(isa, fmt, slot, dc->insnbuf, dc->slotbuf);
         opc = xtensa_opcode_decode(isa, fmt, slot, dc->slotbuf);
         if (opc == XTENSA_UNDEFINED) {
@@ -916,17 +1241,37 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
         opnds = xtensa_opcode_num_operands(isa, opc);
 
         for (opnd = vopnd = 0; opnd < opnds; ++opnd) {
+            void **register_file = NULL;
+
+            if (xtensa_operand_is_register(isa, opc, opnd)) {
+                xtensa_regfile rf = xtensa_operand_regfile(isa, opc, opnd);
+
+                register_file = dc->config->regfile[rf];
+
+                if (rf == dc->config->a_regfile) {
+                    uint32_t v;
+
+                    xtensa_operand_get_field(isa, opc, opnd, fmt, slot,
+                                             dc->slotbuf, &v);
+                    xtensa_operand_decode(isa, opc, opnd, &v);
+                    windowed_register |= 1u << v;
+                }
+            }
             if (xtensa_operand_is_visible(isa, opc, opnd)) {
                 uint32_t v;
 
                 xtensa_operand_get_field(isa, opc, opnd, fmt, slot,
                                          dc->slotbuf, &v);
                 xtensa_operand_decode(isa, opc, opnd, &v);
-                raw_arg[vopnd] = v;
+                arg[vopnd].raw_imm = v;
                 if (xtensa_operand_is_PCrelative(isa, opc, opnd)) {
                     xtensa_operand_undo_reloc(isa, opc, opnd, &v, dc->pc);
                 }
-                arg[vopnd] = v;
+                arg[vopnd].imm = v;
+                if (register_file) {
+                    arg[vopnd].in = register_file[v];
+                    arg[vopnd].out = register_file[v];
+                }
                 ++vopnd;
             }
         }
@@ -952,17 +1297,69 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
         if (ops->test_overflow) {
             windowed_register |= ops->test_overflow(dc, arg, ops->par);
         }
-        if (ops->windowed_register_op) {
-            uint32_t reg_opnd = ops->windowed_register_op;
+        coprocessor |= ops->coprocessor;
+
+        if (slots > 1) {
+            slot_prop[slot].n_in = 0;
+            slot_prop[slot].n_out = 0;
+            slot_prop[slot].op_flags = ops->op_flags & XTENSA_OP_LOAD_STORE;
 
-            while (reg_opnd) {
-                unsigned i = ctz32(reg_opnd);
+            opnds = xtensa_opcode_num_operands(isa, opc);
 
-                windowed_register |= 1 << arg[i];
-                reg_opnd ^= 1 << i;
+            for (opnd = vopnd = 0; opnd < opnds; ++opnd) {
+                bool visible = xtensa_operand_is_visible(isa, opc, opnd);
+
+                if (xtensa_operand_is_register(isa, opc, opnd)) {
+                    xtensa_regfile rf = xtensa_operand_regfile(isa, opc, opnd);
+                    uint32_t v = 0;
+
+                    xtensa_operand_get_field(isa, opc, opnd, fmt, slot,
+                                             dc->slotbuf, &v);
+                    xtensa_operand_decode(isa, opc, opnd, &v);
+                    opcode_add_resource(slot_prop + slot,
+                                        encode_resource(RES_REGFILE, rf, v),
+                                        xtensa_operand_inout(isa, opc, opnd),
+                                        visible ? vopnd : -1);
+                }
+                if (visible) {
+                    ++vopnd;
+                }
+            }
+
+            opnds = xtensa_opcode_num_stateOperands(isa, opc);
+
+            for (opnd = 0; opnd < opnds; ++opnd) {
+                xtensa_state state = xtensa_stateOperand_state(isa, opc, opnd);
+
+                opcode_add_resource(slot_prop + slot,
+                                    encode_resource(RES_STATE, 0, state),
+                                    xtensa_stateOperand_inout(isa, opc, opnd),
+                                    -1);
+            }
+            if (xtensa_opcode_is_branch(isa, opc) ||
+                xtensa_opcode_is_jump(isa, opc) ||
+                xtensa_opcode_is_loop(isa, opc) ||
+                xtensa_opcode_is_call(isa, opc)) {
+                slot_prop[slot].op_flags |= XTENSA_OP_CONTROL_FLOW;
             }
+
+            qsort(slot_prop[slot].in, slot_prop[slot].n_in,
+                  sizeof(slot_prop[slot].in[0]), resource_compare);
+            qsort(slot_prop[slot].out, slot_prop[slot].n_out,
+                  sizeof(slot_prop[slot].out[0]), resource_compare);
         }
-        coprocessor |= ops->coprocessor;
+    }
+
+    if (slots > 1) {
+        if (!tsort(slot_prop, ordered, slots, arg_copy, &n_arg_copy)) {
+            qemu_log_mask(LOG_UNIMP,
+                          "Circular resource dependencies (pc = %08x)\n",
+                          dc->pc);
+            gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE);
+            return;
+        }
+    } else {
+        ordered[0] = slot_prop + 0;
     }
 
     if ((op_flags & XTENSA_OP_PRIVILEGED) &&
@@ -1002,6 +1399,29 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
         return;
     }
 
+    if (n_arg_copy) {
+        uint32_t resource;
+        void *temp;
+        unsigned j;
+
+        qsort(arg_copy, n_arg_copy, sizeof(*arg_copy), arg_copy_compare);
+        for (i = j = 0; i < n_arg_copy; ++i) {
+            if (i == 0 || arg_copy[i].resource != resource) {
+                resource = arg_copy[i].resource;
+                temp = tcg_temp_local_new();
+                tcg_gen_mov_i32(temp, arg_copy[i].arg->in);
+                arg_copy[i].temp = temp;
+
+                if (i != j) {
+                    arg_copy[j] = arg_copy[i];
+                }
+                ++j;
+            }
+            arg_copy[i].arg->in = temp;
+        }
+        n_arg_copy = j;
+    }
+
     if (op_flags & XTENSA_OP_DIVIDE_BY_ZERO) {
         for (slot = 0; slot < slots; ++slot) {
             if (slot_prop[slot].ops->op_flags & XTENSA_OP_DIVIDE_BY_ZERO) {
@@ -1010,29 +1430,31 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
         }
     }
 
+    dc->op_flags = op_flags;
+
     for (slot = 0; slot < slots; ++slot) {
-        XtensaOpcodeOps *ops = slot_prop[slot].ops;
+        struct slot_prop *pslot = ordered[slot];
+        XtensaOpcodeOps *ops = pslot->ops;
 
-        dc->raw_arg = slot_prop[slot].raw_arg;
-        ops->translate(dc, slot_prop[slot].arg, ops->par);
+        ops->translate(dc, pslot->arg, ops->par);
     }
 
-    if (dc->base.is_jmp == DISAS_NEXT) {
-        if (op_flags & XTENSA_OP_CHECK_INTERRUPTS) {
-            gen_check_interrupts(dc);
-        }
+    for (i = 0; i < n_arg_copy; ++i) {
+        tcg_temp_free(arg_copy[i].temp);
+    }
 
+    if (dc->base.is_jmp == DISAS_NEXT) {
+        gen_postprocess(dc, 0);
+        dc->op_flags = 0;
         if (op_flags & XTENSA_OP_EXIT_TB_M1) {
             /* Change in mmu index, memory mapping or tb->flags; exit tb */
             gen_jumpi_check_loop_end(dc, -1);
         } else if (op_flags & XTENSA_OP_EXIT_TB_0) {
             gen_jumpi_check_loop_end(dc, 0);
+        } else {
+            gen_check_loop_end(dc, 0);
         }
     }
-
-    if (dc->base.is_jmp == DISAS_NEXT) {
-        gen_check_loop_end(dc, 0);
-    }
     dc->pc = dc->base.pc_next;
 }
 
@@ -1283,105 +1705,91 @@ void restore_state_to_opc(CPUXtensaState *env, TranslationBlock *tb,
     env->pc = data[0];
 }
 
-static int compare_opcode_ops(const void *a, const void *b)
-{
-    return strcmp((const char *)a,
-                  ((const XtensaOpcodeOps *)b)->name);
-}
-
-XtensaOpcodeOps *
-xtensa_find_opcode_ops(const XtensaOpcodeTranslators *t,
-                       const char *name)
-{
-    return bsearch(name, t->opcode, t->num_opcodes,
-                   sizeof(XtensaOpcodeOps), compare_opcode_ops);
-}
-
-static void translate_abs(DisasContext *dc, const uint32_t arg[],
+static void translate_abs(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     TCGv_i32 zero = tcg_const_i32(0);
     TCGv_i32 neg = tcg_temp_new_i32();
 
-    tcg_gen_neg_i32(neg, cpu_R[arg[1]]);
-    tcg_gen_movcond_i32(TCG_COND_GE, cpu_R[arg[0]],
-                        cpu_R[arg[1]], zero, cpu_R[arg[1]], neg);
+    tcg_gen_neg_i32(neg, arg[1].in);
+    tcg_gen_movcond_i32(TCG_COND_GE, arg[0].out,
+                        arg[1].in, zero, arg[1].in, neg);
     tcg_temp_free(neg);
     tcg_temp_free(zero);
 }
 
-static void translate_add(DisasContext *dc, const uint32_t arg[],
+static void translate_add(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    tcg_gen_add_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_add_i32(arg[0].out, arg[1].in, arg[2].in);
 }
 
-static void translate_addi(DisasContext *dc, const uint32_t arg[],
+static void translate_addi(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_addi_i32(cpu_R[arg[0]], cpu_R[arg[1]], arg[2]);
+    tcg_gen_addi_i32(arg[0].out, arg[1].in, arg[2].imm);
 }
 
-static void translate_addx(DisasContext *dc, const uint32_t arg[],
+static void translate_addx(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
-    tcg_gen_shli_i32(tmp, cpu_R[arg[1]], par[0]);
-    tcg_gen_add_i32(cpu_R[arg[0]], tmp, cpu_R[arg[2]]);
+    tcg_gen_shli_i32(tmp, arg[1].in, par[0]);
+    tcg_gen_add_i32(arg[0].out, tmp, arg[2].in);
     tcg_temp_free(tmp);
 }
 
-static void translate_all(DisasContext *dc, const uint32_t arg[],
+static void translate_all(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     uint32_t shift = par[1];
-    TCGv_i32 mask = tcg_const_i32(((1 << shift) - 1) << arg[1]);
+    TCGv_i32 mask = tcg_const_i32(((1 << shift) - 1) << arg[1].imm);
     TCGv_i32 tmp = tcg_temp_new_i32();
 
-    tcg_gen_and_i32(tmp, cpu_SR[BR], mask);
+    tcg_gen_and_i32(tmp, arg[1].in, mask);
     if (par[0]) {
-        tcg_gen_addi_i32(tmp, tmp, 1 << arg[1]);
+        tcg_gen_addi_i32(tmp, tmp, 1 << arg[1].imm);
     } else {
         tcg_gen_add_i32(tmp, tmp, mask);
     }
-    tcg_gen_shri_i32(tmp, tmp, arg[1] + shift);
-    tcg_gen_deposit_i32(cpu_SR[BR], cpu_SR[BR],
-                        tmp, arg[0], 1);
+    tcg_gen_shri_i32(tmp, tmp, arg[1].imm + shift);
+    tcg_gen_deposit_i32(arg[0].out, arg[0].out,
+                        tmp, arg[0].imm, 1);
     tcg_temp_free(mask);
     tcg_temp_free(tmp);
 }
 
-static void translate_and(DisasContext *dc, const uint32_t arg[],
+static void translate_and(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    tcg_gen_and_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_and_i32(arg[0].out, arg[1].in, arg[2].in);
 }
 
-static void translate_ball(DisasContext *dc, const uint32_t arg[],
+static void translate_ball(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
-    tcg_gen_and_i32(tmp, cpu_R[arg[0]], cpu_R[arg[1]]);
-    gen_brcond(dc, par[0], tmp, cpu_R[arg[1]], arg[2]);
+    tcg_gen_and_i32(tmp, arg[0].in, arg[1].in);
+    gen_brcond(dc, par[0], tmp, arg[1].in, arg[2].imm);
     tcg_temp_free(tmp);
 }
 
-static void translate_bany(DisasContext *dc, const uint32_t arg[],
+static void translate_bany(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
-    tcg_gen_and_i32(tmp, cpu_R[arg[0]], cpu_R[arg[1]]);
-    gen_brcondi(dc, par[0], tmp, 0, arg[2]);
+    tcg_gen_and_i32(tmp, arg[0].in, arg[1].in);
+    gen_brcondi(dc, par[0], tmp, 0, arg[2].imm);
     tcg_temp_free(tmp);
 }
 
-static void translate_b(DisasContext *dc, const uint32_t arg[],
+static void translate_b(DisasContext *dc, const OpcodeArg arg[],
                         const uint32_t par[])
 {
-    gen_brcond(dc, par[0], cpu_R[arg[0]], cpu_R[arg[1]], arg[2]);
+    gen_brcond(dc, par[0], arg[0].in, arg[1].in, arg[2].imm);
 }
 
-static void translate_bb(DisasContext *dc, const uint32_t arg[],
+static void translate_bb(DisasContext *dc, const OpcodeArg arg[],
                          const uint32_t par[])
 {
 #ifdef TARGET_WORDS_BIGENDIAN
@@ -1390,41 +1798,41 @@ static void translate_bb(DisasContext *dc, const uint32_t arg[],
     TCGv_i32 bit = tcg_const_i32(0x00000001u);
 #endif
     TCGv_i32 tmp = tcg_temp_new_i32();
-    tcg_gen_andi_i32(tmp, cpu_R[arg[1]], 0x1f);
+    tcg_gen_andi_i32(tmp, arg[1].in, 0x1f);
 #ifdef TARGET_WORDS_BIGENDIAN
     tcg_gen_shr_i32(bit, bit, tmp);
 #else
     tcg_gen_shl_i32(bit, bit, tmp);
 #endif
-    tcg_gen_and_i32(tmp, cpu_R[arg[0]], bit);
-    gen_brcondi(dc, par[0], tmp, 0, arg[2]);
+    tcg_gen_and_i32(tmp, arg[0].in, bit);
+    gen_brcondi(dc, par[0], tmp, 0, arg[2].imm);
     tcg_temp_free(tmp);
     tcg_temp_free(bit);
 }
 
-static void translate_bbi(DisasContext *dc, const uint32_t arg[],
+static void translate_bbi(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
 #ifdef TARGET_WORDS_BIGENDIAN
-    tcg_gen_andi_i32(tmp, cpu_R[arg[0]], 0x80000000u >> arg[1]);
+    tcg_gen_andi_i32(tmp, arg[0].in, 0x80000000u >> arg[1].imm);
 #else
-    tcg_gen_andi_i32(tmp, cpu_R[arg[0]], 0x00000001u << arg[1]);
+    tcg_gen_andi_i32(tmp, arg[0].in, 0x00000001u << arg[1].imm);
 #endif
-    gen_brcondi(dc, par[0], tmp, 0, arg[2]);
+    gen_brcondi(dc, par[0], tmp, 0, arg[2].imm);
     tcg_temp_free(tmp);
 }
 
-static void translate_bi(DisasContext *dc, const uint32_t arg[],
+static void translate_bi(DisasContext *dc, const OpcodeArg arg[],
                          const uint32_t par[])
 {
-    gen_brcondi(dc, par[0], cpu_R[arg[0]], arg[1], arg[2]);
+    gen_brcondi(dc, par[0], arg[0].in, arg[1].imm, arg[2].imm);
 }
 
-static void translate_bz(DisasContext *dc, const uint32_t arg[],
+static void translate_bz(DisasContext *dc, const OpcodeArg arg[],
                          const uint32_t par[])
 {
-    gen_brcondi(dc, par[0], cpu_R[arg[0]], 0, arg[1]);
+    gen_brcondi(dc, par[0], arg[0].in, 0, arg[1].imm);
 }
 
 enum {
@@ -1435,7 +1843,7 @@ enum {
     BOOLEAN_XOR,
 };
 
-static void translate_boolean(DisasContext *dc, const uint32_t arg[],
+static void translate_boolean(DisasContext *dc, const OpcodeArg arg[],
                               const uint32_t par[])
 {
     static void (* const op[])(TCGv_i32, TCGv_i32, TCGv_i32) = {
@@ -1449,114 +1857,110 @@ static void translate_boolean(DisasContext *dc, const uint32_t arg[],
     TCGv_i32 tmp1 = tcg_temp_new_i32();
     TCGv_i32 tmp2 = tcg_temp_new_i32();
 
-    tcg_gen_shri_i32(tmp1, cpu_SR[BR], arg[1]);
-    tcg_gen_shri_i32(tmp2, cpu_SR[BR], arg[2]);
+    tcg_gen_shri_i32(tmp1, arg[1].in, arg[1].imm);
+    tcg_gen_shri_i32(tmp2, arg[2].in, arg[2].imm);
     op[par[0]](tmp1, tmp1, tmp2);
-    tcg_gen_deposit_i32(cpu_SR[BR], cpu_SR[BR], tmp1, arg[0], 1);
+    tcg_gen_deposit_i32(arg[0].out, arg[0].out, tmp1, arg[0].imm, 1);
     tcg_temp_free(tmp1);
     tcg_temp_free(tmp2);
 }
 
-static void translate_bp(DisasContext *dc, const uint32_t arg[],
+static void translate_bp(DisasContext *dc, const OpcodeArg arg[],
                          const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
 
-    tcg_gen_andi_i32(tmp, cpu_SR[BR], 1 << arg[0]);
-    gen_brcondi(dc, par[0], tmp, 0, arg[1]);
+    tcg_gen_andi_i32(tmp, arg[0].in, 1 << arg[0].imm);
+    gen_brcondi(dc, par[0], tmp, 0, arg[1].imm);
     tcg_temp_free(tmp);
 }
 
-static void translate_call0(DisasContext *dc, const uint32_t arg[],
+static void translate_call0(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
     tcg_gen_movi_i32(cpu_R[0], dc->base.pc_next);
-    gen_jumpi(dc, arg[0], 0);
-}
-
-static uint32_t test_overflow_callw(DisasContext *dc, const uint32_t arg[],
-                                    const uint32_t par[])
-{
-    return 1 << (par[0] * 4);
+    gen_jumpi(dc, arg[0].imm, 0);
 }
 
-static void translate_callw(DisasContext *dc, const uint32_t arg[],
+static void translate_callw(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    gen_callwi(dc, par[0], arg[0], 0);
+    TCGv_i32 tmp = tcg_const_i32(arg[0].imm);
+    gen_callw_slot(dc, par[0], tmp, adjust_jump_slot(dc, arg[0].imm, 0));
+    tcg_temp_free(tmp);
 }
 
-static void translate_callx0(DisasContext *dc, const uint32_t arg[],
+static void translate_callx0(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
-    tcg_gen_mov_i32(tmp, cpu_R[arg[0]]);
+    tcg_gen_mov_i32(tmp, arg[0].in);
     tcg_gen_movi_i32(cpu_R[0], dc->base.pc_next);
     gen_jump(dc, tmp);
     tcg_temp_free(tmp);
 }
 
-static void translate_callxw(DisasContext *dc, const uint32_t arg[],
+static void translate_callxw(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
 
-    tcg_gen_mov_i32(tmp, cpu_R[arg[0]]);
-    gen_callw(dc, par[0], tmp);
+    tcg_gen_mov_i32(tmp, arg[0].in);
+    gen_callw_slot(dc, par[0], tmp, -1);
     tcg_temp_free(tmp);
 }
 
-static void translate_clamps(DisasContext *dc, const uint32_t arg[],
+static void translate_clamps(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
-    TCGv_i32 tmp1 = tcg_const_i32(-1u << arg[2]);
-    TCGv_i32 tmp2 = tcg_const_i32((1 << arg[2]) - 1);
+    TCGv_i32 tmp1 = tcg_const_i32(-1u << arg[2].imm);
+    TCGv_i32 tmp2 = tcg_const_i32((1 << arg[2].imm) - 1);
 
-    tcg_gen_smax_i32(tmp1, tmp1, cpu_R[arg[1]]);
-    tcg_gen_smin_i32(cpu_R[arg[0]], tmp1, tmp2);
+    tcg_gen_smax_i32(tmp1, tmp1, arg[1].in);
+    tcg_gen_smin_i32(arg[0].out, tmp1, tmp2);
     tcg_temp_free(tmp1);
     tcg_temp_free(tmp2);
 }
 
-static void translate_clrb_expstate(DisasContext *dc, const uint32_t arg[],
+static void translate_clrb_expstate(DisasContext *dc, const OpcodeArg arg[],
                                     const uint32_t par[])
 {
     /* TODO: GPIO32 may be a part of coprocessor */
-    tcg_gen_andi_i32(cpu_UR[EXPSTATE], cpu_UR[EXPSTATE], ~(1u << arg[0]));
+    tcg_gen_andi_i32(cpu_UR[EXPSTATE], cpu_UR[EXPSTATE], ~(1u << arg[0].imm));
 }
 
-static void translate_const16(DisasContext *dc, const uint32_t arg[],
+static void translate_const16(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
-    TCGv_i32 c = tcg_const_i32(arg[1]);
+    TCGv_i32 c = tcg_const_i32(arg[1].imm);
 
-    tcg_gen_deposit_i32(cpu_R[arg[0]], c, cpu_R[arg[0]], 16, 16);
+    tcg_gen_deposit_i32(arg[0].out, c, arg[0].in, 16, 16);
     tcg_temp_free(c);
 }
 
-static void translate_dcache(DisasContext *dc, const uint32_t arg[],
+static void translate_dcache(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
     TCGv_i32 addr = tcg_temp_new_i32();
     TCGv_i32 res = tcg_temp_new_i32();
 
-    tcg_gen_addi_i32(addr, cpu_R[arg[0]], arg[1]);
+    tcg_gen_addi_i32(addr, arg[0].in, arg[1].imm);
     tcg_gen_qemu_ld8u(res, addr, dc->cring);
     tcg_temp_free(addr);
     tcg_temp_free(res);
 }
 
-static void translate_depbits(DisasContext *dc, const uint32_t arg[],
+static void translate_depbits(DisasContext *dc, const OpcodeArg arg[],
                               const uint32_t par[])
 {
-    tcg_gen_deposit_i32(cpu_R[arg[1]], cpu_R[arg[1]], cpu_R[arg[0]],
-                        arg[2], arg[3]);
+    tcg_gen_deposit_i32(arg[1].out, arg[1].in, arg[0].in,
+                        arg[2].imm, arg[3].imm);
 }
 
-static bool test_ill_entry(DisasContext *dc, const uint32_t arg[],
+static bool test_ill_entry(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    if (arg[0] > 3 || !dc->cwoe) {
+    if (arg[0].imm > 3 || !dc->cwoe) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "Illegal entry instruction(pc = %08x)\n", dc->pc);
         return true;
@@ -1565,88 +1969,88 @@ static bool test_ill_entry(DisasContext *dc, const uint32_t arg[],
     }
 }
 
-static uint32_t test_overflow_entry(DisasContext *dc, const uint32_t arg[],
+static uint32_t test_overflow_entry(DisasContext *dc, const OpcodeArg arg[],
                                     const uint32_t par[])
 {
     return 1 << (dc->callinc * 4);
 }
 
-static void translate_entry(DisasContext *dc, const uint32_t arg[],
+static void translate_entry(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
     TCGv_i32 pc = tcg_const_i32(dc->pc);
-    TCGv_i32 s = tcg_const_i32(arg[0]);
-    TCGv_i32 imm = tcg_const_i32(arg[1]);
+    TCGv_i32 s = tcg_const_i32(arg[0].imm);
+    TCGv_i32 imm = tcg_const_i32(arg[1].imm);
     gen_helper_entry(cpu_env, pc, s, imm);
     tcg_temp_free(imm);
     tcg_temp_free(s);
     tcg_temp_free(pc);
 }
 
-static void translate_extui(DisasContext *dc, const uint32_t arg[],
+static void translate_extui(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    int maskimm = (1 << arg[3]) - 1;
+    int maskimm = (1 << arg[3].imm) - 1;
 
     TCGv_i32 tmp = tcg_temp_new_i32();
-    tcg_gen_shri_i32(tmp, cpu_R[arg[1]], arg[2]);
-    tcg_gen_andi_i32(cpu_R[arg[0]], tmp, maskimm);
+    tcg_gen_shri_i32(tmp, arg[1].in, arg[2].imm);
+    tcg_gen_andi_i32(arg[0].out, tmp, maskimm);
     tcg_temp_free(tmp);
 }
 
-static void translate_icache(DisasContext *dc, const uint32_t arg[],
+static void translate_icache(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
 #ifndef CONFIG_USER_ONLY
     TCGv_i32 addr = tcg_temp_new_i32();
 
     tcg_gen_movi_i32(cpu_pc, dc->pc);
-    tcg_gen_addi_i32(addr, cpu_R[arg[0]], arg[1]);
+    tcg_gen_addi_i32(addr, arg[0].in, arg[1].imm);
     gen_helper_itlb_hit_test(cpu_env, addr);
     tcg_temp_free(addr);
 #endif
 }
 
-static void translate_itlb(DisasContext *dc, const uint32_t arg[],
+static void translate_itlb(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
 #ifndef CONFIG_USER_ONLY
     TCGv_i32 dtlb = tcg_const_i32(par[0]);
 
-    gen_helper_itlb(cpu_env, cpu_R[arg[0]], dtlb);
+    gen_helper_itlb(cpu_env, arg[0].in, dtlb);
     tcg_temp_free(dtlb);
 #endif
 }
 
-static void translate_j(DisasContext *dc, const uint32_t arg[],
+static void translate_j(DisasContext *dc, const OpcodeArg arg[],
                         const uint32_t par[])
 {
-    gen_jumpi(dc, arg[0], 0);
+    gen_jumpi(dc, arg[0].imm, 0);
 }
 
-static void translate_jx(DisasContext *dc, const uint32_t arg[],
+static void translate_jx(DisasContext *dc, const OpcodeArg arg[],
                          const uint32_t par[])
 {
-    gen_jump(dc, cpu_R[arg[0]]);
+    gen_jump(dc, arg[0].in);
 }
 
-static void translate_l32e(DisasContext *dc, const uint32_t arg[],
+static void translate_l32e(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGv_i32 addr = tcg_temp_new_i32();
 
-    tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]);
+    tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm);
     gen_load_store_alignment(dc, 2, addr, false);
-    tcg_gen_qemu_ld_tl(cpu_R[arg[0]], addr, dc->ring, MO_TEUL);
+    tcg_gen_qemu_ld_tl(arg[0].out, addr, dc->ring, MO_TEUL);
     tcg_temp_free(addr);
 }
 
-static void translate_ldst(DisasContext *dc, const uint32_t arg[],
+static void translate_ldst(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGv_i32 addr = tcg_temp_new_i32();
 
-    tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]);
+    tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm);
     if (par[0] & MO_SIZE) {
         gen_load_store_alignment(dc, par[0] & MO_SIZE, addr, par[1]);
     }
@@ -1654,9 +2058,9 @@ static void translate_ldst(DisasContext *dc, const uint32_t arg[],
         if (par[1]) {
             tcg_gen_mb(TCG_BAR_STRL | TCG_MO_ALL);
         }
-        tcg_gen_qemu_st_tl(cpu_R[arg[0]], addr, dc->cring, par[0]);
+        tcg_gen_qemu_st_tl(arg[0].in, addr, dc->cring, par[0]);
     } else {
-        tcg_gen_qemu_ld_tl(cpu_R[arg[0]], addr, dc->cring, par[0]);
+        tcg_gen_qemu_ld_tl(arg[0].out, addr, dc->cring, par[0]);
         if (par[1]) {
             tcg_gen_mb(TCG_BAR_LDAQ | TCG_MO_ALL);
         }
@@ -1664,33 +2068,33 @@ static void translate_ldst(DisasContext *dc, const uint32_t arg[],
     tcg_temp_free(addr);
 }
 
-static void translate_l32r(DisasContext *dc, const uint32_t arg[],
+static void translate_l32r(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGv_i32 tmp;
 
     if (dc->base.tb->flags & XTENSA_TBFLAG_LITBASE) {
-        tmp = tcg_const_i32(dc->raw_arg[1] - 1);
+        tmp = tcg_const_i32(arg[1].raw_imm - 1);
         tcg_gen_add_i32(tmp, cpu_SR[LITBASE], tmp);
     } else {
-        tmp = tcg_const_i32(arg[1]);
+        tmp = tcg_const_i32(arg[1].imm);
     }
-    tcg_gen_qemu_ld32u(cpu_R[arg[0]], tmp, dc->cring);
+    tcg_gen_qemu_ld32u(arg[0].out, tmp, dc->cring);
     tcg_temp_free(tmp);
 }
 
-static void translate_loop(DisasContext *dc, const uint32_t arg[],
+static void translate_loop(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    uint32_t lend = arg[1];
+    uint32_t lend = arg[1].imm;
 
-    tcg_gen_subi_i32(cpu_SR[LCOUNT], cpu_R[arg[0]], 1);
+    tcg_gen_subi_i32(cpu_SR[LCOUNT], arg[0].in, 1);
     tcg_gen_movi_i32(cpu_SR[LBEG], dc->base.pc_next);
     tcg_gen_movi_i32(cpu_SR[LEND], lend);
 
     if (par[0] != TCG_COND_NEVER) {
         TCGLabel *label = gen_new_label();
-        tcg_gen_brcondi_i32(par[0], cpu_R[arg[0]], 0, label);
+        tcg_gen_brcondi_i32(par[0], arg[0].in, 0, label);
         gen_jumpi(dc, lend, 1);
         gen_set_label(label);
     }
@@ -1716,41 +2120,25 @@ enum {
     MAC16_XH = 0x2,
 };
 
-enum {
-    MAC16_AA,
-    MAC16_AD,
-    MAC16_DA,
-    MAC16_DD,
-
-    MAC16_XD = 0x1,
-    MAC16_DX = 0x2,
-};
-
-static void translate_mac16(DisasContext *dc, const uint32_t arg[],
+static void translate_mac16(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
     int op = par[0];
-    bool is_m1_sr = par[1] & MAC16_DX;
-    bool is_m2_sr = par[1] & MAC16_XD;
-    unsigned half = par[2];
-    uint32_t ld_offset = par[3];
+    unsigned half = par[1];
+    uint32_t ld_offset = par[2];
     unsigned off = ld_offset ? 2 : 0;
     TCGv_i32 vaddr = tcg_temp_new_i32();
     TCGv_i32 mem32 = tcg_temp_new_i32();
 
     if (ld_offset) {
-        tcg_gen_addi_i32(vaddr, cpu_R[arg[1]], ld_offset);
+        tcg_gen_addi_i32(vaddr, arg[1].in, ld_offset);
         gen_load_store_alignment(dc, 2, vaddr, false);
         tcg_gen_qemu_ld32u(mem32, vaddr, dc->cring);
     }
     if (op != MAC16_NONE) {
-        TCGv_i32 m1 = gen_mac16_m(is_m1_sr ?
-                                  cpu_SR[MR + arg[off]] :
-                                  cpu_R[arg[off]],
+        TCGv_i32 m1 = gen_mac16_m(arg[off].in,
                                   half & MAC16_HX, op == MAC16_UMUL);
-        TCGv_i32 m2 = gen_mac16_m(is_m2_sr ?
-                                  cpu_SR[MR + arg[off + 1]] :
-                                  cpu_R[arg[off + 1]],
+        TCGv_i32 m2 = gen_mac16_m(arg[off + 1].in,
                                   half & MAC16_XH, op == MAC16_UMUL);
 
         if (op == MAC16_MUL || op == MAC16_UMUL) {
@@ -1784,221 +2172,221 @@ static void translate_mac16(DisasContext *dc, const uint32_t arg[],
         tcg_temp_free(m2);
     }
     if (ld_offset) {
-        tcg_gen_mov_i32(cpu_R[arg[1]], vaddr);
-        tcg_gen_mov_i32(cpu_SR[MR + arg[0]], mem32);
+        tcg_gen_mov_i32(arg[1].out, vaddr);
+        tcg_gen_mov_i32(cpu_SR[MR + arg[0].imm], mem32);
     }
     tcg_temp_free(vaddr);
     tcg_temp_free(mem32);
 }
 
-static void translate_memw(DisasContext *dc, const uint32_t arg[],
+static void translate_memw(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL);
 }
 
-static void translate_smin(DisasContext *dc, const uint32_t arg[],
+static void translate_smin(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_smin_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_smin_i32(arg[0].out, arg[1].in, arg[2].in);
 }
 
-static void translate_umin(DisasContext *dc, const uint32_t arg[],
+static void translate_umin(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_umin_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_umin_i32(arg[0].out, arg[1].in, arg[2].in);
 }
 
-static void translate_smax(DisasContext *dc, const uint32_t arg[],
+static void translate_smax(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_smax_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_smax_i32(arg[0].out, arg[1].in, arg[2].in);
 }
 
-static void translate_umax(DisasContext *dc, const uint32_t arg[],
+static void translate_umax(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_umax_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_umax_i32(arg[0].out, arg[1].in, arg[2].in);
 }
 
-static void translate_mov(DisasContext *dc, const uint32_t arg[],
+static void translate_mov(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    tcg_gen_mov_i32(cpu_R[arg[0]], cpu_R[arg[1]]);
+    tcg_gen_mov_i32(arg[0].out, arg[1].in);
 }
 
-static void translate_movcond(DisasContext *dc, const uint32_t arg[],
+static void translate_movcond(DisasContext *dc, const OpcodeArg arg[],
                               const uint32_t par[])
 {
     TCGv_i32 zero = tcg_const_i32(0);
 
-    tcg_gen_movcond_i32(par[0], cpu_R[arg[0]],
-                        cpu_R[arg[2]], zero, cpu_R[arg[1]], cpu_R[arg[0]]);
+    tcg_gen_movcond_i32(par[0], arg[0].out,
+                        arg[2].in, zero, arg[1].in, arg[0].in);
     tcg_temp_free(zero);
 }
 
-static void translate_movi(DisasContext *dc, const uint32_t arg[],
+static void translate_movi(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_movi_i32(cpu_R[arg[0]], arg[1]);
+    tcg_gen_movi_i32(arg[0].out, arg[1].imm);
 }
 
-static void translate_movp(DisasContext *dc, const uint32_t arg[],
+static void translate_movp(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGv_i32 zero = tcg_const_i32(0);
     TCGv_i32 tmp = tcg_temp_new_i32();
 
-    tcg_gen_andi_i32(tmp, cpu_SR[BR], 1 << arg[2]);
+    tcg_gen_andi_i32(tmp, arg[2].in, 1 << arg[2].imm);
     tcg_gen_movcond_i32(par[0],
-                        cpu_R[arg[0]], tmp, zero,
-                        cpu_R[arg[1]], cpu_R[arg[0]]);
+                        arg[0].out, tmp, zero,
+                        arg[1].in, arg[0].in);
     tcg_temp_free(tmp);
     tcg_temp_free(zero);
 }
 
-static void translate_movsp(DisasContext *dc, const uint32_t arg[],
+static void translate_movsp(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    tcg_gen_mov_i32(cpu_R[arg[0]], cpu_R[arg[1]]);
+    tcg_gen_mov_i32(arg[0].out, arg[1].in);
 }
 
-static void translate_mul16(DisasContext *dc, const uint32_t arg[],
+static void translate_mul16(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
     TCGv_i32 v1 = tcg_temp_new_i32();
     TCGv_i32 v2 = tcg_temp_new_i32();
 
     if (par[0]) {
-        tcg_gen_ext16s_i32(v1, cpu_R[arg[1]]);
-        tcg_gen_ext16s_i32(v2, cpu_R[arg[2]]);
+        tcg_gen_ext16s_i32(v1, arg[1].in);
+        tcg_gen_ext16s_i32(v2, arg[2].in);
     } else {
-        tcg_gen_ext16u_i32(v1, cpu_R[arg[1]]);
-        tcg_gen_ext16u_i32(v2, cpu_R[arg[2]]);
+        tcg_gen_ext16u_i32(v1, arg[1].in);
+        tcg_gen_ext16u_i32(v2, arg[2].in);
     }
-    tcg_gen_mul_i32(cpu_R[arg[0]], v1, v2);
+    tcg_gen_mul_i32(arg[0].out, v1, v2);
     tcg_temp_free(v2);
     tcg_temp_free(v1);
 }
 
-static void translate_mull(DisasContext *dc, const uint32_t arg[],
+static void translate_mull(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_mul_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_mul_i32(arg[0].out, arg[1].in, arg[2].in);
 }
 
-static void translate_mulh(DisasContext *dc, const uint32_t arg[],
+static void translate_mulh(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGv_i32 lo = tcg_temp_new();
 
     if (par[0]) {
-        tcg_gen_muls2_i32(lo, cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+        tcg_gen_muls2_i32(lo, arg[0].out, arg[1].in, arg[2].in);
     } else {
-        tcg_gen_mulu2_i32(lo, cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+        tcg_gen_mulu2_i32(lo, arg[0].out, arg[1].in, arg[2].in);
     }
     tcg_temp_free(lo);
 }
 
-static void translate_neg(DisasContext *dc, const uint32_t arg[],
+static void translate_neg(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    tcg_gen_neg_i32(cpu_R[arg[0]], cpu_R[arg[1]]);
+    tcg_gen_neg_i32(arg[0].out, arg[1].in);
 }
 
-static void translate_nop(DisasContext *dc, const uint32_t arg[],
+static void translate_nop(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
 }
 
-static void translate_nsa(DisasContext *dc, const uint32_t arg[],
+static void translate_nsa(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    tcg_gen_clrsb_i32(cpu_R[arg[0]], cpu_R[arg[1]]);
+    tcg_gen_clrsb_i32(arg[0].out, arg[1].in);
 }
 
-static void translate_nsau(DisasContext *dc, const uint32_t arg[],
+static void translate_nsau(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_clzi_i32(cpu_R[arg[0]], cpu_R[arg[1]], 32);
+    tcg_gen_clzi_i32(arg[0].out, arg[1].in, 32);
 }
 
-static void translate_or(DisasContext *dc, const uint32_t arg[],
+static void translate_or(DisasContext *dc, const OpcodeArg arg[],
                          const uint32_t par[])
 {
-    tcg_gen_or_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_or_i32(arg[0].out, arg[1].in, arg[2].in);
 }
 
-static void translate_ptlb(DisasContext *dc, const uint32_t arg[],
+static void translate_ptlb(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
 #ifndef CONFIG_USER_ONLY
     TCGv_i32 dtlb = tcg_const_i32(par[0]);
 
     tcg_gen_movi_i32(cpu_pc, dc->pc);
-    gen_helper_ptlb(cpu_R[arg[0]], cpu_env, cpu_R[arg[1]], dtlb);
+    gen_helper_ptlb(arg[0].out, cpu_env, arg[1].in, dtlb);
     tcg_temp_free(dtlb);
 #endif
 }
 
-static void translate_quos(DisasContext *dc, const uint32_t arg[],
+static void translate_quos(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGLabel *label1 = gen_new_label();
     TCGLabel *label2 = gen_new_label();
 
-    tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[arg[1]], 0x80000000,
+    tcg_gen_brcondi_i32(TCG_COND_NE, arg[1].in, 0x80000000,
                         label1);
-    tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[arg[2]], 0xffffffff,
+    tcg_gen_brcondi_i32(TCG_COND_NE, arg[2].in, 0xffffffff,
                         label1);
-    tcg_gen_movi_i32(cpu_R[arg[0]],
+    tcg_gen_movi_i32(arg[0].out,
                      par[0] ? 0x80000000 : 0);
     tcg_gen_br(label2);
     gen_set_label(label1);
     if (par[0]) {
-        tcg_gen_div_i32(cpu_R[arg[0]],
-                        cpu_R[arg[1]], cpu_R[arg[2]]);
+        tcg_gen_div_i32(arg[0].out,
+                        arg[1].in, arg[2].in);
     } else {
-        tcg_gen_rem_i32(cpu_R[arg[0]],
-                        cpu_R[arg[1]], cpu_R[arg[2]]);
+        tcg_gen_rem_i32(arg[0].out,
+                        arg[1].in, arg[2].in);
     }
     gen_set_label(label2);
 }
 
-static void translate_quou(DisasContext *dc, const uint32_t arg[],
+static void translate_quou(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_divu_i32(cpu_R[arg[0]],
-                     cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_divu_i32(arg[0].out,
+                     arg[1].in, arg[2].in);
 }
 
-static void translate_read_impwire(DisasContext *dc, const uint32_t arg[],
+static void translate_read_impwire(DisasContext *dc, const OpcodeArg arg[],
                                    const uint32_t par[])
 {
     /* TODO: GPIO32 may be a part of coprocessor */
-    tcg_gen_movi_i32(cpu_R[arg[0]], 0);
+    tcg_gen_movi_i32(arg[0].out, 0);
 }
 
-static void translate_remu(DisasContext *dc, const uint32_t arg[],
+static void translate_remu(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_remu_i32(cpu_R[arg[0]],
-                     cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_remu_i32(arg[0].out,
+                     arg[1].in, arg[2].in);
 }
 
-static void translate_rer(DisasContext *dc, const uint32_t arg[],
+static void translate_rer(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    gen_helper_rer(cpu_R[arg[0]], cpu_env, cpu_R[arg[1]]);
+    gen_helper_rer(arg[0].out, cpu_env, arg[1].in);
 }
 
-static void translate_ret(DisasContext *dc, const uint32_t arg[],
+static void translate_ret(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     gen_jump(dc, cpu_R[0]);
 }
 
-static bool test_ill_retw(DisasContext *dc, const uint32_t arg[],
+static bool test_ill_retw(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     if (!dc->cwoe) {
@@ -2014,36 +2402,41 @@ static bool test_ill_retw(DisasContext *dc, const uint32_t arg[],
     }
 }
 
-static void translate_retw(DisasContext *dc, const uint32_t arg[],
+static void translate_retw(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    TCGv_i32 tmp = tcg_const_i32(dc->pc);
-    gen_helper_retw(tmp, cpu_env, tmp);
+    TCGv_i32 tmp = tcg_const_i32(1);
+    tcg_gen_shl_i32(tmp, tmp, cpu_SR[WINDOW_BASE]);
+    tcg_gen_andc_i32(cpu_SR[WINDOW_START],
+                     cpu_SR[WINDOW_START], tmp);
+    tcg_gen_movi_i32(tmp, dc->pc);
+    tcg_gen_deposit_i32(tmp, tmp, cpu_R[0], 0, 30);
+    gen_helper_retw(cpu_env, cpu_R[0]);
     gen_jump(dc, tmp);
     tcg_temp_free(tmp);
 }
 
-static void translate_rfde(DisasContext *dc, const uint32_t arg[],
+static void translate_rfde(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     gen_jump(dc, cpu_SR[dc->config->ndepc ? DEPC : EPC1]);
 }
 
-static void translate_rfe(DisasContext *dc, const uint32_t arg[],
+static void translate_rfe(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM);
     gen_jump(dc, cpu_SR[EPC1]);
 }
 
-static void translate_rfi(DisasContext *dc, const uint32_t arg[],
+static void translate_rfi(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    tcg_gen_mov_i32(cpu_SR[PS], cpu_SR[EPS2 + arg[0] - 2]);
-    gen_jump(dc, cpu_SR[EPC1 + arg[0] - 1]);
+    tcg_gen_mov_i32(cpu_SR[PS], cpu_SR[EPS2 + arg[0].imm - 2]);
+    gen_jump(dc, cpu_SR[EPC1 + arg[0].imm - 1]);
 }
 
-static void translate_rfw(DisasContext *dc, const uint32_t arg[],
+static void translate_rfw(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_const_i32(1);
@@ -2064,35 +2457,33 @@ static void translate_rfw(DisasContext *dc, const uint32_t arg[],
     gen_jump(dc, cpu_SR[EPC1]);
 }
 
-static void translate_rotw(DisasContext *dc, const uint32_t arg[],
+static void translate_rotw(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    TCGv_i32 tmp = tcg_const_i32(arg[0]);
-    gen_helper_rotw(cpu_env, tmp);
-    tcg_temp_free(tmp);
+    tcg_gen_addi_i32(cpu_windowbase_next, cpu_SR[WINDOW_BASE], arg[0].imm);
 }
 
-static void translate_rsil(DisasContext *dc, const uint32_t arg[],
+static void translate_rsil(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_mov_i32(cpu_R[arg[0]], cpu_SR[PS]);
+    tcg_gen_mov_i32(arg[0].out, cpu_SR[PS]);
     tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_INTLEVEL);
-    tcg_gen_ori_i32(cpu_SR[PS], cpu_SR[PS], arg[1]);
+    tcg_gen_ori_i32(cpu_SR[PS], cpu_SR[PS], arg[1].imm);
 }
 
-static bool test_ill_rsr(DisasContext *dc, const uint32_t arg[],
+static bool test_ill_rsr(DisasContext *dc, const OpcodeArg arg[],
                          const uint32_t par[])
 {
     return !check_sr(dc, par[0], SR_R);
 }
 
-static void translate_rsr(DisasContext *dc, const uint32_t arg[],
+static void translate_rsr(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    gen_rsr(dc, cpu_R[arg[0]], par[0]);
+    gen_rsr(dc, arg[0].out, par[0]);
 }
 
-static void translate_rtlb(DisasContext *dc, const uint32_t arg[],
+static void translate_rtlb(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
 #ifndef CONFIG_USER_ONLY
@@ -2103,26 +2494,26 @@ static void translate_rtlb(DisasContext *dc, const uint32_t arg[],
     };
     TCGv_i32 dtlb = tcg_const_i32(par[0]);
 
-    helper[par[1]](cpu_R[arg[0]], cpu_env, cpu_R[arg[1]], dtlb);
+    helper[par[1]](arg[0].out, cpu_env, arg[1].in, dtlb);
     tcg_temp_free(dtlb);
 #endif
 }
 
-static void translate_rur(DisasContext *dc, const uint32_t arg[],
+static void translate_rur(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     if (uregnames[par[0]].name) {
-        tcg_gen_mov_i32(cpu_R[arg[0]], cpu_UR[par[0]]);
+        tcg_gen_mov_i32(arg[0].out, cpu_UR[par[0]]);
     } else {
         qemu_log_mask(LOG_UNIMP, "RUR %d not implemented\n", par[0]);
     }
 }
 
-static void translate_setb_expstate(DisasContext *dc, const uint32_t arg[],
+static void translate_setb_expstate(DisasContext *dc, const OpcodeArg arg[],
                                     const uint32_t par[])
 {
     /* TODO: GPIO32 may be a part of coprocessor */
-    tcg_gen_ori_i32(cpu_UR[EXPSTATE], cpu_UR[EXPSTATE], 1u << arg[0]);
+    tcg_gen_ori_i32(cpu_UR[EXPSTATE], cpu_UR[EXPSTATE], 1u << arg[0].imm);
 }
 
 #ifdef CONFIG_USER_ONLY
@@ -2139,59 +2530,59 @@ static void gen_check_atomctl(DisasContext *dc, TCGv_i32 addr)
 }
 #endif
 
-static void translate_s32c1i(DisasContext *dc, const uint32_t arg[],
+static void translate_s32c1i(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_local_new_i32();
     TCGv_i32 addr = tcg_temp_local_new_i32();
 
-    tcg_gen_mov_i32(tmp, cpu_R[arg[0]]);
-    tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]);
+    tcg_gen_mov_i32(tmp, arg[0].in);
+    tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm);
     gen_load_store_alignment(dc, 2, addr, true);
     gen_check_atomctl(dc, addr);
-    tcg_gen_atomic_cmpxchg_i32(cpu_R[arg[0]], addr, cpu_SR[SCOMPARE1],
+    tcg_gen_atomic_cmpxchg_i32(arg[0].out, addr, cpu_SR[SCOMPARE1],
                                tmp, dc->cring, MO_TEUL);
     tcg_temp_free(addr);
     tcg_temp_free(tmp);
 }
 
-static void translate_s32e(DisasContext *dc, const uint32_t arg[],
+static void translate_s32e(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGv_i32 addr = tcg_temp_new_i32();
 
-    tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]);
+    tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm);
     gen_load_store_alignment(dc, 2, addr, false);
-    tcg_gen_qemu_st_tl(cpu_R[arg[0]], addr, dc->ring, MO_TEUL);
+    tcg_gen_qemu_st_tl(arg[0].in, addr, dc->ring, MO_TEUL);
     tcg_temp_free(addr);
 }
 
-static void translate_salt(DisasContext *dc, const uint32_t arg[],
+static void translate_salt(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     tcg_gen_setcond_i32(par[0],
-                        cpu_R[arg[0]],
-                        cpu_R[arg[1]], cpu_R[arg[2]]);
+                        arg[0].out,
+                        arg[1].in, arg[2].in);
 }
 
-static void translate_sext(DisasContext *dc, const uint32_t arg[],
+static void translate_sext(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    int shift = 31 - arg[2];
+    int shift = 31 - arg[2].imm;
 
     if (shift == 24) {
-        tcg_gen_ext8s_i32(cpu_R[arg[0]], cpu_R[arg[1]]);
+        tcg_gen_ext8s_i32(arg[0].out, arg[1].in);
     } else if (shift == 16) {
-        tcg_gen_ext16s_i32(cpu_R[arg[0]], cpu_R[arg[1]]);
+        tcg_gen_ext16s_i32(arg[0].out, arg[1].in);
     } else {
         TCGv_i32 tmp = tcg_temp_new_i32();
-        tcg_gen_shli_i32(tmp, cpu_R[arg[1]], shift);
-        tcg_gen_sari_i32(cpu_R[arg[0]], tmp, shift);
+        tcg_gen_shli_i32(tmp, arg[1].in, shift);
+        tcg_gen_sari_i32(arg[0].out, tmp, shift);
         tcg_temp_free(tmp);
     }
 }
 
-static bool test_ill_simcall(DisasContext *dc, const uint32_t arg[],
+static bool test_ill_simcall(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
 #ifdef CONFIG_USER_ONLY
@@ -2205,7 +2596,7 @@ static bool test_ill_simcall(DisasContext *dc, const uint32_t arg[],
     return ill;
 }
 
-static void translate_simcall(DisasContext *dc, const uint32_t arg[],
+static void translate_simcall(DisasContext *dc, const OpcodeArg arg[],
                               const uint32_t par[])
 {
 #ifndef CONFIG_USER_ONLY
@@ -2221,73 +2612,73 @@ static void translate_simcall(DisasContext *dc, const uint32_t arg[],
                     TCGv_i64 tmp = tcg_temp_new_i64(); \
                     tcg_gen_extu_i32_i64(tmp, reg); \
                     tcg_gen_##cmd##_i64(v, v, tmp); \
-                    tcg_gen_extrl_i64_i32(cpu_R[arg[0]], v); \
+                    tcg_gen_extrl_i64_i32(arg[0].out, v); \
                     tcg_temp_free_i64(v); \
                     tcg_temp_free_i64(tmp); \
                 } while (0)
 
 #define gen_shift(cmd) gen_shift_reg(cmd, cpu_SR[SAR])
 
-static void translate_sll(DisasContext *dc, const uint32_t arg[],
+static void translate_sll(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     if (dc->sar_m32_5bit) {
-        tcg_gen_shl_i32(cpu_R[arg[0]], cpu_R[arg[1]], dc->sar_m32);
+        tcg_gen_shl_i32(arg[0].out, arg[1].in, dc->sar_m32);
     } else {
         TCGv_i64 v = tcg_temp_new_i64();
         TCGv_i32 s = tcg_const_i32(32);
         tcg_gen_sub_i32(s, s, cpu_SR[SAR]);
         tcg_gen_andi_i32(s, s, 0x3f);
-        tcg_gen_extu_i32_i64(v, cpu_R[arg[1]]);
+        tcg_gen_extu_i32_i64(v, arg[1].in);
         gen_shift_reg(shl, s);
         tcg_temp_free(s);
     }
 }
 
-static void translate_slli(DisasContext *dc, const uint32_t arg[],
+static void translate_slli(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    if (arg[2] == 32) {
+    if (arg[2].imm == 32) {
         qemu_log_mask(LOG_GUEST_ERROR, "slli a%d, a%d, 32 is undefined\n",
-                      arg[0], arg[1]);
+                      arg[0].imm, arg[1].imm);
     }
-    tcg_gen_shli_i32(cpu_R[arg[0]], cpu_R[arg[1]], arg[2] & 0x1f);
+    tcg_gen_shli_i32(arg[0].out, arg[1].in, arg[2].imm & 0x1f);
 }
 
-static void translate_sra(DisasContext *dc, const uint32_t arg[],
+static void translate_sra(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     if (dc->sar_m32_5bit) {
-        tcg_gen_sar_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_SR[SAR]);
+        tcg_gen_sar_i32(arg[0].out, arg[1].in, cpu_SR[SAR]);
     } else {
         TCGv_i64 v = tcg_temp_new_i64();
-        tcg_gen_ext_i32_i64(v, cpu_R[arg[1]]);
+        tcg_gen_ext_i32_i64(v, arg[1].in);
         gen_shift(sar);
     }
 }
 
-static void translate_srai(DisasContext *dc, const uint32_t arg[],
+static void translate_srai(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_sari_i32(cpu_R[arg[0]], cpu_R[arg[1]], arg[2]);
+    tcg_gen_sari_i32(arg[0].out, arg[1].in, arg[2].imm);
 }
 
-static void translate_src(DisasContext *dc, const uint32_t arg[],
+static void translate_src(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     TCGv_i64 v = tcg_temp_new_i64();
-    tcg_gen_concat_i32_i64(v, cpu_R[arg[2]], cpu_R[arg[1]]);
+    tcg_gen_concat_i32_i64(v, arg[2].in, arg[1].in);
     gen_shift(shr);
 }
 
-static void translate_srl(DisasContext *dc, const uint32_t arg[],
+static void translate_srl(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     if (dc->sar_m32_5bit) {
-        tcg_gen_shr_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_SR[SAR]);
+        tcg_gen_shr_i32(arg[0].out, arg[1].in, cpu_SR[SAR]);
     } else {
         TCGv_i64 v = tcg_temp_new_i64();
-        tcg_gen_extu_i32_i64(v, cpu_R[arg[1]]);
+        tcg_gen_extu_i32_i64(v, arg[1].in);
         gen_shift(shr);
     }
 }
@@ -2295,138 +2686,138 @@ static void translate_srl(DisasContext *dc, const uint32_t arg[],
 #undef gen_shift
 #undef gen_shift_reg
 
-static void translate_srli(DisasContext *dc, const uint32_t arg[],
+static void translate_srli(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    tcg_gen_shri_i32(cpu_R[arg[0]], cpu_R[arg[1]], arg[2]);
+    tcg_gen_shri_i32(arg[0].out, arg[1].in, arg[2].imm);
 }
 
-static void translate_ssa8b(DisasContext *dc, const uint32_t arg[],
+static void translate_ssa8b(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
-    tcg_gen_shli_i32(tmp, cpu_R[arg[0]], 3);
+    tcg_gen_shli_i32(tmp, arg[0].in, 3);
     gen_left_shift_sar(dc, tmp);
     tcg_temp_free(tmp);
 }
 
-static void translate_ssa8l(DisasContext *dc, const uint32_t arg[],
+static void translate_ssa8l(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
-    tcg_gen_shli_i32(tmp, cpu_R[arg[0]], 3);
+    tcg_gen_shli_i32(tmp, arg[0].in, 3);
     gen_right_shift_sar(dc, tmp);
     tcg_temp_free(tmp);
 }
 
-static void translate_ssai(DisasContext *dc, const uint32_t arg[],
+static void translate_ssai(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
-    TCGv_i32 tmp = tcg_const_i32(arg[0]);
+    TCGv_i32 tmp = tcg_const_i32(arg[0].imm);
     gen_right_shift_sar(dc, tmp);
     tcg_temp_free(tmp);
 }
 
-static void translate_ssl(DisasContext *dc, const uint32_t arg[],
+static void translate_ssl(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    gen_left_shift_sar(dc, cpu_R[arg[0]]);
+    gen_left_shift_sar(dc, arg[0].in);
 }
 
-static void translate_ssr(DisasContext *dc, const uint32_t arg[],
+static void translate_ssr(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    gen_right_shift_sar(dc, cpu_R[arg[0]]);
+    gen_right_shift_sar(dc, arg[0].in);
 }
 
-static void translate_sub(DisasContext *dc, const uint32_t arg[],
+static void translate_sub(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    tcg_gen_sub_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_sub_i32(arg[0].out, arg[1].in, arg[2].in);
 }
 
-static void translate_subx(DisasContext *dc, const uint32_t arg[],
+static void translate_subx(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
-    tcg_gen_shli_i32(tmp, cpu_R[arg[1]], par[0]);
-    tcg_gen_sub_i32(cpu_R[arg[0]], tmp, cpu_R[arg[2]]);
+    tcg_gen_shli_i32(tmp, arg[1].in, par[0]);
+    tcg_gen_sub_i32(arg[0].out, tmp, arg[2].in);
     tcg_temp_free(tmp);
 }
 
-static void translate_waiti(DisasContext *dc, const uint32_t arg[],
+static void translate_waiti(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
 #ifndef CONFIG_USER_ONLY
-    gen_waiti(dc, arg[0]);
+    gen_waiti(dc, arg[0].imm);
 #endif
 }
 
-static void translate_wtlb(DisasContext *dc, const uint32_t arg[],
+static void translate_wtlb(DisasContext *dc, const OpcodeArg arg[],
                            const uint32_t par[])
 {
 #ifndef CONFIG_USER_ONLY
     TCGv_i32 dtlb = tcg_const_i32(par[0]);
 
-    gen_helper_wtlb(cpu_env, cpu_R[arg[0]], cpu_R[arg[1]], dtlb);
+    gen_helper_wtlb(cpu_env, arg[0].in, arg[1].in, dtlb);
     tcg_temp_free(dtlb);
 #endif
 }
 
-static void translate_wer(DisasContext *dc, const uint32_t arg[],
+static void translate_wer(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    gen_helper_wer(cpu_env, cpu_R[arg[0]], cpu_R[arg[1]]);
+    gen_helper_wer(cpu_env, arg[0].in, arg[1].in);
 }
 
-static void translate_wrmsk_expstate(DisasContext *dc, const uint32_t arg[],
+static void translate_wrmsk_expstate(DisasContext *dc, const OpcodeArg arg[],
                                      const uint32_t par[])
 {
     /* TODO: GPIO32 may be a part of coprocessor */
-    tcg_gen_and_i32(cpu_UR[EXPSTATE], cpu_R[arg[0]], cpu_R[arg[1]]);
+    tcg_gen_and_i32(cpu_UR[EXPSTATE], arg[0].in, arg[1].in);
 }
 
-static bool test_ill_wsr(DisasContext *dc, const uint32_t arg[],
+static bool test_ill_wsr(DisasContext *dc, const OpcodeArg arg[],
                          const uint32_t par[])
 {
     return !check_sr(dc, par[0], SR_W);
 }
 
-static void translate_wsr(DisasContext *dc, const uint32_t arg[],
+static void translate_wsr(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    gen_wsr(dc, par[0], cpu_R[arg[0]]);
+    gen_wsr(dc, par[0], arg[0].in);
 }
 
-static void translate_wur(DisasContext *dc, const uint32_t arg[],
+static void translate_wur(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     if (uregnames[par[0]].name) {
-        gen_wur(par[0], cpu_R[arg[0]]);
+        gen_wur(par[0], arg[0].in);
     } else {
         qemu_log_mask(LOG_UNIMP, "WUR %d not implemented\n", par[0]);
     }
 }
 
-static void translate_xor(DisasContext *dc, const uint32_t arg[],
+static void translate_xor(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
-    tcg_gen_xor_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_xor_i32(arg[0].out, arg[1].in, arg[2].in);
 }
 
-static bool test_ill_xsr(DisasContext *dc, const uint32_t arg[],
+static bool test_ill_xsr(DisasContext *dc, const OpcodeArg arg[],
                          const uint32_t par[])
 {
     return !check_sr(dc, par[0], SR_X);
 }
 
-static void translate_xsr(DisasContext *dc, const uint32_t arg[],
+static void translate_xsr(DisasContext *dc, const OpcodeArg arg[],
                           const uint32_t par[])
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
 
-    tcg_gen_mov_i32(tmp, cpu_R[arg[0]]);
-    gen_rsr(dc, cpu_R[arg[0]], par[0]);
+    tcg_gen_mov_i32(tmp, arg[0].in);
+    gen_rsr(dc, arg[0].out, par[0]);
     gen_wsr(dc, par[0], tmp);
     tcg_temp_free(tmp);
 }
@@ -2435,42 +2826,33 @@ static const XtensaOpcodeOps core_ops[] = {
     {
         .name = "abs",
         .translate = translate_abs,
-        .windowed_register_op = 0x3,
     }, {
-        .name = "add",
+        .name = (const char * const[]) {
+            "add", "add.n", NULL,
+        },
         .translate = translate_add,
-        .windowed_register_op = 0x7,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "add.n",
-        .translate = translate_add,
-        .windowed_register_op = 0x7,
-    }, {
-        .name = "addi",
-        .translate = translate_addi,
-        .windowed_register_op = 0x3,
-    }, {
-        .name = "addi.n",
+        .name = (const char * const[]) {
+            "addi", "addi.n", NULL,
+        },
         .translate = translate_addi,
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
         .name = "addmi",
         .translate = translate_addi,
-        .windowed_register_op = 0x3,
     }, {
         .name = "addx2",
         .translate = translate_addx,
         .par = (const uint32_t[]){1},
-        .windowed_register_op = 0x7,
     }, {
         .name = "addx4",
         .translate = translate_addx,
         .par = (const uint32_t[]){2},
-        .windowed_register_op = 0x7,
     }, {
         .name = "addx8",
         .translate = translate_addx,
         .par = (const uint32_t[]){3},
-        .windowed_register_op = 0x7,
     }, {
         .name = "all4",
         .translate = translate_all,
@@ -2482,7 +2864,6 @@ static const XtensaOpcodeOps core_ops[] = {
     }, {
         .name = "and",
         .translate = translate_and,
-        .windowed_register_op = 0x7,
     }, {
         .name = "andb",
         .translate = translate_boolean,
@@ -2500,139 +2881,177 @@ static const XtensaOpcodeOps core_ops[] = {
         .translate = translate_all,
         .par = (const uint32_t[]){false, 8},
     }, {
-        .name = "ball",
+        .name = (const char * const[]) {
+            "ball", "ball.w15", "ball.w18", NULL,
+        },
         .translate = translate_ball,
         .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bany",
+        .name = (const char * const[]) {
+            "bany", "bany.w15", "bany.w18", NULL,
+        },
         .translate = translate_bany,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bbc",
+        .name = (const char * const[]) {
+            "bbc", "bbc.w15", "bbc.w18", NULL,
+        },
         .translate = translate_bb,
         .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bbci",
+        .name = (const char * const[]) {
+            "bbci", "bbci.w15", "bbci.w18", NULL,
+        },
         .translate = translate_bbi,
         .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bbs",
+        .name = (const char * const[]) {
+            "bbs", "bbs.w15", "bbs.w18", NULL,
+        },
         .translate = translate_bb,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bbsi",
+        .name = (const char * const[]) {
+            "bbsi", "bbsi.w15", "bbsi.w18", NULL,
+        },
         .translate = translate_bbi,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "beq",
+        .name = (const char * const[]) {
+            "beq", "beq.w15", "beq.w18", NULL,
+        },
         .translate = translate_b,
         .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "beqi",
+        .name = (const char * const[]) {
+            "beqi", "beqi.w15", "beqi.w18", NULL,
+        },
         .translate = translate_bi,
         .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x1,
-    }, {
-        .name = "beqz",
-        .translate = translate_bz,
-        .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "beqz.n",
+        .name = (const char * const[]) {
+            "beqz", "beqz.n", "beqz.w15", "beqz.w18", NULL,
+        },
         .translate = translate_bz,
         .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
         .name = "bf",
         .translate = translate_bp,
         .par = (const uint32_t[]){TCG_COND_EQ},
     }, {
-        .name = "bge",
+        .name = (const char * const[]) {
+            "bge", "bge.w15", "bge.w18", NULL,
+        },
         .translate = translate_b,
         .par = (const uint32_t[]){TCG_COND_GE},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bgei",
+        .name = (const char * const[]) {
+            "bgei", "bgei.w15", "bgei.w18", NULL,
+        },
         .translate = translate_bi,
         .par = (const uint32_t[]){TCG_COND_GE},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bgeu",
+        .name = (const char * const[]) {
+            "bgeu", "bgeu.w15", "bgeu.w18", NULL,
+        },
         .translate = translate_b,
         .par = (const uint32_t[]){TCG_COND_GEU},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bgeui",
+        .name = (const char * const[]) {
+            "bgeui", "bgeui.w15", "bgeui.w18", NULL,
+        },
         .translate = translate_bi,
         .par = (const uint32_t[]){TCG_COND_GEU},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bgez",
+        .name = (const char * const[]) {
+            "bgez", "bgez.w15", "bgez.w18", NULL,
+        },
         .translate = translate_bz,
         .par = (const uint32_t[]){TCG_COND_GE},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "blt",
+        .name = (const char * const[]) {
+            "blt", "blt.w15", "blt.w18", NULL,
+        },
         .translate = translate_b,
         .par = (const uint32_t[]){TCG_COND_LT},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "blti",
+        .name = (const char * const[]) {
+            "blti", "blti.w15", "blti.w18", NULL,
+        },
         .translate = translate_bi,
         .par = (const uint32_t[]){TCG_COND_LT},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bltu",
+        .name = (const char * const[]) {
+            "bltu", "bltu.w15", "bltu.w18", NULL,
+        },
         .translate = translate_b,
         .par = (const uint32_t[]){TCG_COND_LTU},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bltui",
+        .name = (const char * const[]) {
+            "bltui", "bltui.w15", "bltui.w18", NULL,
+        },
         .translate = translate_bi,
         .par = (const uint32_t[]){TCG_COND_LTU},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bltz",
+        .name = (const char * const[]) {
+            "bltz", "bltz.w15", "bltz.w18", NULL,
+        },
         .translate = translate_bz,
         .par = (const uint32_t[]){TCG_COND_LT},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bnall",
+        .name = (const char * const[]) {
+            "bnall", "bnall.w15", "bnall.w18", NULL,
+        },
         .translate = translate_ball,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bne",
+        .name = (const char * const[]) {
+            "bne", "bne.w15", "bne.w18", NULL,
+        },
         .translate = translate_b,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bnei",
+        .name = (const char * const[]) {
+            "bnei", "bnei.w15", "bnei.w18", NULL,
+        },
         .translate = translate_bi,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bnez",
+        .name = (const char * const[]) {
+            "bnez", "bnez.n", "bnez.w15", "bnez.w18", NULL,
+        },
         .translate = translate_bz,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "bnez.n",
-        .translate = translate_bz,
-        .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x1,
-    }, {
-        .name = "bnone",
+        .name = (const char * const[]) {
+            "bnone", "bnone.w15", "bnone.w18", NULL,
+        },
         .translate = translate_bany,
         .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
         .name = "break",
         .translate = translate_nop,
@@ -2653,114 +3072,88 @@ static const XtensaOpcodeOps core_ops[] = {
     }, {
         .name = "call12",
         .translate = translate_callw,
-        .test_overflow = test_overflow_callw,
         .par = (const uint32_t[]){3},
     }, {
         .name = "call4",
         .translate = translate_callw,
-        .test_overflow = test_overflow_callw,
         .par = (const uint32_t[]){1},
     }, {
         .name = "call8",
         .translate = translate_callw,
-        .test_overflow = test_overflow_callw,
         .par = (const uint32_t[]){2},
     }, {
         .name = "callx0",
         .translate = translate_callx0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "callx12",
         .translate = translate_callxw,
-        .test_overflow = test_overflow_callw,
         .par = (const uint32_t[]){3},
-        .windowed_register_op = 0x1,
     }, {
         .name = "callx4",
         .translate = translate_callxw,
-        .test_overflow = test_overflow_callw,
         .par = (const uint32_t[]){1},
-        .windowed_register_op = 0x1,
     }, {
         .name = "callx8",
         .translate = translate_callxw,
-        .test_overflow = test_overflow_callw,
         .par = (const uint32_t[]){2},
-        .windowed_register_op = 0x1,
     }, {
         .name = "clamps",
         .translate = translate_clamps,
-        .windowed_register_op = 0x3,
     }, {
         .name = "clrb_expstate",
         .translate = translate_clrb_expstate,
     }, {
         .name = "const16",
         .translate = translate_const16,
-        .windowed_register_op = 0x1,
     }, {
         .name = "depbits",
         .translate = translate_depbits,
-        .windowed_register_op = 0x3,
     }, {
         .name = "dhi",
         .translate = translate_dcache,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "dhu",
         .translate = translate_dcache,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "dhwb",
         .translate = translate_dcache,
-        .windowed_register_op = 0x1,
     }, {
         .name = "dhwbi",
         .translate = translate_dcache,
-        .windowed_register_op = 0x1,
     }, {
         .name = "dii",
         .translate = translate_nop,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "diu",
         .translate = translate_nop,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "diwb",
         .translate = translate_nop,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "diwbi",
         .translate = translate_nop,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "dpfl",
         .translate = translate_dcache,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "dpfr",
         .translate = translate_nop,
-        .windowed_register_op = 0x1,
     }, {
         .name = "dpfro",
         .translate = translate_nop,
-        .windowed_register_op = 0x1,
     }, {
         .name = "dpfw",
         .translate = translate_nop,
-        .windowed_register_op = 0x1,
     }, {
         .name = "dpfwo",
         .translate = translate_nop,
-        .windowed_register_op = 0x1,
     }, {
         .name = "dsync",
         .translate = translate_nop,
@@ -2769,7 +3162,8 @@ static const XtensaOpcodeOps core_ops[] = {
         .translate = translate_entry,
         .test_ill = test_ill_entry,
         .test_overflow = test_overflow_entry,
-        .op_flags = XTENSA_OP_EXIT_TB_M1,
+        .op_flags = XTENSA_OP_EXIT_TB_M1 |
+            XTENSA_OP_SYNC_REGISTER_WINDOW,
     }, {
         .name = "esync",
         .translate = translate_nop,
@@ -2779,7 +3173,6 @@ static const XtensaOpcodeOps core_ops[] = {
     }, {
         .name = "extui",
         .translate = translate_extui,
-        .windowed_register_op = 0x3,
     }, {
         .name = "extw",
         .translate = translate_memw,
@@ -2794,47 +3187,38 @@ static const XtensaOpcodeOps core_ops[] = {
         .translate = translate_itlb,
         .par = (const uint32_t[]){true},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "ihi",
         .translate = translate_icache,
-        .windowed_register_op = 0x1,
     }, {
         .name = "ihu",
         .translate = translate_icache,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "iii",
         .translate = translate_nop,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "iitlb",
         .translate = translate_itlb,
         .par = (const uint32_t[]){false},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "iiu",
         .translate = translate_nop,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
-        .name = "ill",
-        .op_flags = XTENSA_OP_ILL,
-    }, {
-        .name = "ill.n",
-        .op_flags = XTENSA_OP_ILL,
+        .name = (const char * const[]) {
+            "ill", "ill.n", NULL,
+        },
+        .op_flags = XTENSA_OP_ILL | XTENSA_OP_NAME_ARRAY,
     }, {
         .name = "ipf",
         .translate = translate_nop,
-        .windowed_register_op = 0x1,
     }, {
         .name = "ipfl",
         .translate = translate_icache,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "isync",
         .translate = translate_nop,
@@ -2844,498 +3228,423 @@ static const XtensaOpcodeOps core_ops[] = {
     }, {
         .name = "jx",
         .translate = translate_jx,
-        .windowed_register_op = 0x1,
     }, {
         .name = "l16si",
         .translate = translate_ldst,
         .par = (const uint32_t[]){MO_TESW, false, false},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_LOAD,
     }, {
         .name = "l16ui",
         .translate = translate_ldst,
         .par = (const uint32_t[]){MO_TEUW, false, false},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_LOAD,
     }, {
         .name = "l32ai",
         .translate = translate_ldst,
         .par = (const uint32_t[]){MO_TEUL, true, false},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_LOAD,
     }, {
         .name = "l32e",
         .translate = translate_l32e,
-        .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x3,
-    }, {
-        .name = "l32i",
-        .translate = translate_ldst,
-        .par = (const uint32_t[]){MO_TEUL, false, false},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_LOAD,
     }, {
-        .name = "l32i.n",
+        .name = (const char * const[]) {
+            "l32i", "l32i.n", NULL,
+        },
         .translate = translate_ldst,
         .par = (const uint32_t[]){MO_TEUL, false, false},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY | XTENSA_OP_LOAD,
     }, {
         .name = "l32r",
         .translate = translate_l32r,
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_LOAD,
     }, {
         .name = "l8ui",
         .translate = translate_ldst,
         .par = (const uint32_t[]){MO_UB, false, false},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_LOAD,
     }, {
         .name = "lddec",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_NONE, 0, 0, -4},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_NONE, 0, -4},
+        .op_flags = XTENSA_OP_LOAD,
     }, {
         .name = "ldinc",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_NONE, 0, 0, 4},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_NONE, 0, 4},
+        .op_flags = XTENSA_OP_LOAD,
     }, {
         .name = "ldpte",
         .op_flags = XTENSA_OP_ILL,
     }, {
-        .name = "loop",
+        .name = (const char * const[]) {
+            "loop", "loop.w15", NULL,
+        },
         .translate = translate_loop,
         .par = (const uint32_t[]){TCG_COND_NEVER},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "loopgtz",
+        .name = (const char * const[]) {
+            "loopgtz", "loopgtz.w15", NULL,
+        },
         .translate = translate_loop,
         .par = (const uint32_t[]){TCG_COND_GT},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "loopnez",
+        .name = (const char * const[]) {
+            "loopnez", "loopnez.w15", NULL,
+        },
         .translate = translate_loop,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
         .name = "max",
         .translate = translate_smax,
-        .windowed_register_op = 0x7,
     }, {
         .name = "maxu",
         .translate = translate_umax,
-        .windowed_register_op = 0x7,
     }, {
         .name = "memw",
         .translate = translate_memw,
     }, {
         .name = "min",
         .translate = translate_smin,
-        .windowed_register_op = 0x7,
     }, {
         .name = "minu",
         .translate = translate_umin,
-        .windowed_register_op = 0x7,
     }, {
-        .name = "mov",
+        .name = (const char * const[]) {
+            "mov", "mov.n", NULL,
+        },
         .translate = translate_mov,
-        .windowed_register_op = 0x3,
-    }, {
-        .name = "mov.n",
-        .translate = translate_mov,
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
         .name = "moveqz",
         .translate = translate_movcond,
         .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x7,
     }, {
         .name = "movf",
         .translate = translate_movp,
         .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x3,
     }, {
         .name = "movgez",
         .translate = translate_movcond,
         .par = (const uint32_t[]){TCG_COND_GE},
-        .windowed_register_op = 0x7,
     }, {
         .name = "movi",
         .translate = translate_movi,
-        .windowed_register_op = 0x1,
     }, {
         .name = "movi.n",
         .translate = translate_movi,
-        .windowed_register_op = 0x1,
     }, {
         .name = "movltz",
         .translate = translate_movcond,
         .par = (const uint32_t[]){TCG_COND_LT},
-        .windowed_register_op = 0x7,
     }, {
         .name = "movnez",
         .translate = translate_movcond,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x7,
     }, {
         .name = "movsp",
         .translate = translate_movsp,
-        .windowed_register_op = 0x3,
         .op_flags = XTENSA_OP_ALLOCA,
     }, {
         .name = "movt",
         .translate = translate_movp,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x3,
     }, {
         .name = "mul.aa.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_AA, MAC16_HH, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_HH, 0},
     }, {
         .name = "mul.aa.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_AA, MAC16_HL, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_HL, 0},
     }, {
         .name = "mul.aa.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_AA, MAC16_LH, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_LH, 0},
     }, {
         .name = "mul.aa.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_AA, MAC16_LL, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_LL, 0},
     }, {
         .name = "mul.ad.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_AD, MAC16_HH, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_HH, 0},
     }, {
         .name = "mul.ad.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_AD, MAC16_HL, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_HL, 0},
     }, {
         .name = "mul.ad.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_AD, MAC16_LH, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_LH, 0},
     }, {
         .name = "mul.ad.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_AD, MAC16_LL, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_LL, 0},
     }, {
         .name = "mul.da.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_DA, MAC16_HH, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_HH, 0},
     }, {
         .name = "mul.da.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_DA, MAC16_HL, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_HL, 0},
     }, {
         .name = "mul.da.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_DA, MAC16_LH, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_LH, 0},
     }, {
         .name = "mul.da.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_DA, MAC16_LL, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_LL, 0},
     }, {
         .name = "mul.dd.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_DD, MAC16_HH, 0},
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_HH, 0},
     }, {
         .name = "mul.dd.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_DD, MAC16_HL, 0},
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_HL, 0},
     }, {
         .name = "mul.dd.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_DD, MAC16_LH, 0},
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_LH, 0},
     }, {
         .name = "mul.dd.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MUL, MAC16_DD, MAC16_LL, 0},
+        .par = (const uint32_t[]){MAC16_MUL, MAC16_LL, 0},
     }, {
         .name = "mul16s",
         .translate = translate_mul16,
         .par = (const uint32_t[]){true},
-        .windowed_register_op = 0x7,
     }, {
         .name = "mul16u",
         .translate = translate_mul16,
         .par = (const uint32_t[]){false},
-        .windowed_register_op = 0x7,
     }, {
         .name = "mula.aa.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_AA, MAC16_HH, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 0},
     }, {
         .name = "mula.aa.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_AA, MAC16_HL, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 0},
     }, {
         .name = "mula.aa.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_AA, MAC16_LH, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 0},
     }, {
         .name = "mula.aa.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_AA, MAC16_LL, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 0},
     }, {
         .name = "mula.ad.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_AD, MAC16_HH, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 0},
     }, {
         .name = "mula.ad.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_AD, MAC16_HL, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 0},
     }, {
         .name = "mula.ad.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_AD, MAC16_LH, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 0},
     }, {
         .name = "mula.ad.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_AD, MAC16_LL, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 0},
     }, {
         .name = "mula.da.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HH, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 0},
     }, {
         .name = "mula.da.hh.lddec",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HH, -4},
-        .windowed_register_op = 0xa,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, -4},
     }, {
         .name = "mula.da.hh.ldinc",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HH, 4},
-        .windowed_register_op = 0xa,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 4},
     }, {
         .name = "mula.da.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HL, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 0},
     }, {
         .name = "mula.da.hl.lddec",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HL, -4},
-        .windowed_register_op = 0xa,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, -4},
     }, {
         .name = "mula.da.hl.ldinc",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HL, 4},
-        .windowed_register_op = 0xa,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 4},
     }, {
         .name = "mula.da.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LH, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 0},
     }, {
         .name = "mula.da.lh.lddec",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LH, -4},
-        .windowed_register_op = 0xa,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, -4},
     }, {
         .name = "mula.da.lh.ldinc",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LH, 4},
-        .windowed_register_op = 0xa,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 4},
     }, {
         .name = "mula.da.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LL, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 0},
     }, {
         .name = "mula.da.ll.lddec",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LL, -4},
-        .windowed_register_op = 0xa,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, -4},
     }, {
         .name = "mula.da.ll.ldinc",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LL, 4},
-        .windowed_register_op = 0xa,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 4},
     }, {
         .name = "mula.dd.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HH, 0},
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 0},
     }, {
         .name = "mula.dd.hh.lddec",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HH, -4},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, -4},
     }, {
         .name = "mula.dd.hh.ldinc",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HH, 4},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 4},
     }, {
         .name = "mula.dd.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HL, 0},
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 0},
     }, {
         .name = "mula.dd.hl.lddec",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HL, -4},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, -4},
     }, {
         .name = "mula.dd.hl.ldinc",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HL, 4},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 4},
     }, {
         .name = "mula.dd.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LH, 0},
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 0},
     }, {
         .name = "mula.dd.lh.lddec",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LH, -4},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, -4},
     }, {
         .name = "mula.dd.lh.ldinc",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LH, 4},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 4},
     }, {
         .name = "mula.dd.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LL, 0},
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 0},
     }, {
         .name = "mula.dd.ll.lddec",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LL, -4},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, -4},
     }, {
         .name = "mula.dd.ll.ldinc",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LL, 4},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 4},
     }, {
         .name = "mull",
         .translate = translate_mull,
-        .windowed_register_op = 0x7,
     }, {
         .name = "muls.aa.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_AA, MAC16_HH, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_HH, 0},
     }, {
         .name = "muls.aa.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_AA, MAC16_HL, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_HL, 0},
     }, {
         .name = "muls.aa.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_AA, MAC16_LH, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_LH, 0},
     }, {
         .name = "muls.aa.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_AA, MAC16_LL, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_LL, 0},
     }, {
         .name = "muls.ad.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_AD, MAC16_HH, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_HH, 0},
     }, {
         .name = "muls.ad.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_AD, MAC16_HL, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_HL, 0},
     }, {
         .name = "muls.ad.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_AD, MAC16_LH, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_LH, 0},
     }, {
         .name = "muls.ad.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_AD, MAC16_LL, 0},
-        .windowed_register_op = 0x1,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_LL, 0},
     }, {
         .name = "muls.da.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_DA, MAC16_HH, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_HH, 0},
     }, {
         .name = "muls.da.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_DA, MAC16_HL, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_HL, 0},
     }, {
         .name = "muls.da.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_DA, MAC16_LH, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_LH, 0},
     }, {
         .name = "muls.da.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_DA, MAC16_LL, 0},
-        .windowed_register_op = 0x2,
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_LL, 0},
     }, {
         .name = "muls.dd.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_DD, MAC16_HH, 0},
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_HH, 0},
     }, {
         .name = "muls.dd.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_DD, MAC16_HL, 0},
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_HL, 0},
     }, {
         .name = "muls.dd.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_DD, MAC16_LH, 0},
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_LH, 0},
     }, {
         .name = "muls.dd.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_MULS, MAC16_DD, MAC16_LL, 0},
+        .par = (const uint32_t[]){MAC16_MULS, MAC16_LL, 0},
     }, {
         .name = "mulsh",
         .translate = translate_mulh,
         .par = (const uint32_t[]){true},
-        .windowed_register_op = 0x7,
     }, {
         .name = "muluh",
         .translate = translate_mulh,
         .par = (const uint32_t[]){false},
-        .windowed_register_op = 0x7,
     }, {
         .name = "neg",
         .translate = translate_neg,
-        .windowed_register_op = 0x3,
     }, {
-        .name = "nop",
-        .translate = translate_nop,
-    }, {
-        .name = "nop.n",
+        .name = (const char * const[]) {
+            "nop", "nop.n", NULL,
+        },
         .translate = translate_nop,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
         .name = "nsa",
         .translate = translate_nsa,
-        .windowed_register_op = 0x3,
     }, {
         .name = "nsau",
         .translate = translate_nsau,
-        .windowed_register_op = 0x3,
     }, {
         .name = "or",
         .translate = translate_or,
-        .windowed_register_op = 0x7,
     }, {
         .name = "orb",
         .translate = translate_boolean,
@@ -3349,72 +3658,59 @@ static const XtensaOpcodeOps core_ops[] = {
         .translate = translate_ptlb,
         .par = (const uint32_t[]){true},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x3,
     }, {
         .name = "pitlb",
         .translate = translate_ptlb,
         .par = (const uint32_t[]){false},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x3,
     }, {
         .name = "quos",
         .translate = translate_quos,
         .par = (const uint32_t[]){true},
         .op_flags = XTENSA_OP_DIVIDE_BY_ZERO,
-        .windowed_register_op = 0x7,
     }, {
         .name = "quou",
         .translate = translate_quou,
         .op_flags = XTENSA_OP_DIVIDE_BY_ZERO,
-        .windowed_register_op = 0x7,
     }, {
         .name = "rdtlb0",
         .translate = translate_rtlb,
         .par = (const uint32_t[]){true, 0},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x3,
     }, {
         .name = "rdtlb1",
         .translate = translate_rtlb,
         .par = (const uint32_t[]){true, 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x3,
     }, {
         .name = "read_impwire",
         .translate = translate_read_impwire,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rems",
         .translate = translate_quos,
         .par = (const uint32_t[]){false},
         .op_flags = XTENSA_OP_DIVIDE_BY_ZERO,
-        .windowed_register_op = 0x7,
     }, {
         .name = "remu",
         .translate = translate_remu,
         .op_flags = XTENSA_OP_DIVIDE_BY_ZERO,
-        .windowed_register_op = 0x7,
     }, {
         .name = "rer",
         .translate = translate_rer,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x3,
-    }, {
-        .name = "ret",
-        .translate = translate_ret,
     }, {
-        .name = "ret.n",
+        .name = (const char * const[]) {
+            "ret", "ret.n", NULL,
+        },
         .translate = translate_ret,
+        .op_flags = XTENSA_OP_NAME_ARRAY,
     }, {
-        .name = "retw",
+        .name = (const char * const[]) {
+            "retw", "retw.n", NULL,
+        },
         .translate = translate_retw,
         .test_ill = test_ill_retw,
-        .op_flags = XTENSA_OP_UNDERFLOW,
-    }, {
-        .name = "retw.n",
-        .translate = translate_retw,
-        .test_ill = test_ill_retw,
-        .op_flags = XTENSA_OP_UNDERFLOW,
+        .op_flags = XTENSA_OP_UNDERFLOW | XTENSA_OP_NAME_ARRAY,
     }, {
         .name = "rfdd",
         .op_flags = XTENSA_OP_ILL,
@@ -3448,17 +3744,17 @@ static const XtensaOpcodeOps core_ops[] = {
         .translate = translate_rtlb,
         .par = (const uint32_t[]){false, 0},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x3,
     }, {
         .name = "ritlb1",
         .translate = translate_rtlb,
         .par = (const uint32_t[]){false, 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x3,
     }, {
         .name = "rotw",
         .translate = translate_rotw,
-        .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
+        .op_flags = XTENSA_OP_PRIVILEGED |
+            XTENSA_OP_EXIT_TB_M1 |
+            XTENSA_OP_SYNC_REGISTER_WINDOW,
     }, {
         .name = "rsil",
         .translate = translate_rsil,
@@ -3466,526 +3762,454 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_0 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.176",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){176},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.208",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){208},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.acchi",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){ACCHI},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.acclo",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){ACCLO},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.atomctl",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){ATOMCTL},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.br",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){BR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.cacheattr",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){CACHEATTR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.ccompare0",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){CCOMPARE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.ccompare1",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){CCOMPARE + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.ccompare2",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){CCOMPARE + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.ccount",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){CCOUNT},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.configid0",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){CONFIGID0},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.configid1",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){CONFIGID1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.cpenable",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){CPENABLE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.dbreaka0",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){DBREAKA},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.dbreaka1",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){DBREAKA + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.dbreakc0",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){DBREAKC},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.dbreakc1",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){DBREAKC + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.ddr",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){DDR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.debugcause",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){DEBUGCAUSE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.depc",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){DEPC},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.dtlbcfg",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){DTLBCFG},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.epc1",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPC1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.epc2",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPC1 + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.epc3",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPC1 + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.epc4",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPC1 + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.epc5",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPC1 + 4},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.epc6",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPC1 + 5},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.epc7",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPC1 + 6},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.eps2",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPS2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.eps3",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPS2 + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.eps4",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPS2 + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.eps5",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPS2 + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.eps6",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPS2 + 4},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.eps7",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EPS2 + 5},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.exccause",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EXCCAUSE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.excsave1",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EXCSAVE1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.excsave2",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EXCSAVE1 + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.excsave3",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EXCSAVE1 + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.excsave4",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EXCSAVE1 + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.excsave5",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EXCSAVE1 + 4},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.excsave6",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EXCSAVE1 + 5},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.excsave7",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EXCSAVE1 + 6},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.excvaddr",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){EXCVADDR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.ibreaka0",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){IBREAKA},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.ibreaka1",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){IBREAKA + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.ibreakenable",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){IBREAKENABLE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.icount",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){ICOUNT},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.icountlevel",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){ICOUNTLEVEL},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.intclear",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){INTCLEAR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.intenable",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){INTENABLE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.interrupt",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){INTSET},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.intset",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){INTSET},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.itlbcfg",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){ITLBCFG},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.lbeg",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){LBEG},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.lcount",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){LCOUNT},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.lend",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){LEND},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.litbase",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){LITBASE},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.m0",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){MR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.m1",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){MR + 1},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.m2",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){MR + 2},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.m3",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){MR + 3},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.memctl",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){MEMCTL},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.misc0",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){MISC},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.misc1",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){MISC + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.misc2",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){MISC + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.misc3",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){MISC + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
+    }, {
+        .name = "rsr.prefctl",
+        .translate = translate_rsr,
+        .test_ill = test_ill_rsr,
+        .par = (const uint32_t[]){PREFCTL},
     }, {
         .name = "rsr.prid",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){PRID},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.ps",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){PS},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.ptevaddr",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){PTEVADDR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.rasid",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){RASID},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.sar",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){SAR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.scompare1",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){SCOMPARE1},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.vecbase",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){VECBASE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.windowbase",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){WINDOW_BASE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsr.windowstart",
         .translate = translate_rsr,
         .test_ill = test_ill_rsr,
         .par = (const uint32_t[]){WINDOW_START},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "rsync",
         .translate = translate_nop,
@@ -3993,80 +4217,64 @@ static const XtensaOpcodeOps core_ops[] = {
         .name = "rur.expstate",
         .translate = translate_rur,
         .par = (const uint32_t[]){EXPSTATE},
-        .windowed_register_op = 0x1,
     }, {
         .name = "rur.fcr",
         .translate = translate_rur,
         .par = (const uint32_t[]){FCR},
-        .windowed_register_op = 0x1,
         .coprocessor = 0x1,
     }, {
         .name = "rur.fsr",
         .translate = translate_rur,
         .par = (const uint32_t[]){FSR},
-        .windowed_register_op = 0x1,
         .coprocessor = 0x1,
     }, {
         .name = "rur.threadptr",
         .translate = translate_rur,
         .par = (const uint32_t[]){THREADPTR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "s16i",
         .translate = translate_ldst,
         .par = (const uint32_t[]){MO_TEUW, false, true},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_STORE,
     }, {
         .name = "s32c1i",
         .translate = translate_s32c1i,
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_LOAD | XTENSA_OP_STORE,
     }, {
         .name = "s32e",
         .translate = translate_s32e,
-        .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x3,
-    }, {
-        .name = "s32i",
-        .translate = translate_ldst,
-        .par = (const uint32_t[]){MO_TEUL, false, true},
-        .windowed_register_op = 0x3,
-    }, {
-        .name = "s32i.n",
-        .translate = translate_ldst,
-        .par = (const uint32_t[]){MO_TEUL, false, true},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_STORE,
     }, {
-        .name = "s32nb",
+        .name = (const char * const[]) {
+            "s32i", "s32i.n", "s32nb", NULL,
+        },
         .translate = translate_ldst,
         .par = (const uint32_t[]){MO_TEUL, false, true},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_NAME_ARRAY | XTENSA_OP_STORE,
     }, {
         .name = "s32ri",
         .translate = translate_ldst,
         .par = (const uint32_t[]){MO_TEUL, true, true},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_STORE,
     }, {
         .name = "s8i",
         .translate = translate_ldst,
         .par = (const uint32_t[]){MO_UB, false, true},
-        .windowed_register_op = 0x3,
+        .op_flags = XTENSA_OP_STORE,
     }, {
         .name = "salt",
         .translate = translate_salt,
         .par = (const uint32_t[]){TCG_COND_LT},
-        .windowed_register_op = 0x7,
     }, {
         .name = "saltu",
         .translate = translate_salt,
         .par = (const uint32_t[]){TCG_COND_LTU},
-        .windowed_register_op = 0x7,
     }, {
         .name = "setb_expstate",
         .translate = translate_setb_expstate,
     }, {
         .name = "sext",
         .translate = translate_sext,
-        .windowed_register_op = 0x3,
     }, {
         .name = "simcall",
         .translate = translate_simcall,
@@ -4075,92 +4283,73 @@ static const XtensaOpcodeOps core_ops[] = {
     }, {
         .name = "sll",
         .translate = translate_sll,
-        .windowed_register_op = 0x3,
     }, {
         .name = "slli",
         .translate = translate_slli,
-        .windowed_register_op = 0x3,
     }, {
         .name = "sra",
         .translate = translate_sra,
-        .windowed_register_op = 0x3,
     }, {
         .name = "srai",
         .translate = translate_srai,
-        .windowed_register_op = 0x3,
     }, {
         .name = "src",
         .translate = translate_src,
-        .windowed_register_op = 0x7,
     }, {
         .name = "srl",
         .translate = translate_srl,
-        .windowed_register_op = 0x3,
     }, {
         .name = "srli",
         .translate = translate_srli,
-        .windowed_register_op = 0x3,
     }, {
         .name = "ssa8b",
         .translate = translate_ssa8b,
-        .windowed_register_op = 0x1,
     }, {
         .name = "ssa8l",
         .translate = translate_ssa8l,
-        .windowed_register_op = 0x1,
     }, {
         .name = "ssai",
         .translate = translate_ssai,
     }, {
         .name = "ssl",
         .translate = translate_ssl,
-        .windowed_register_op = 0x1,
     }, {
         .name = "ssr",
         .translate = translate_ssr,
-        .windowed_register_op = 0x1,
     }, {
         .name = "sub",
         .translate = translate_sub,
-        .windowed_register_op = 0x7,
     }, {
         .name = "subx2",
         .translate = translate_subx,
         .par = (const uint32_t[]){1},
-        .windowed_register_op = 0x7,
     }, {
         .name = "subx4",
         .translate = translate_subx,
         .par = (const uint32_t[]){2},
-        .windowed_register_op = 0x7,
     }, {
         .name = "subx8",
         .translate = translate_subx,
         .par = (const uint32_t[]){3},
-        .windowed_register_op = 0x7,
     }, {
         .name = "syscall",
         .op_flags = XTENSA_OP_SYSCALL,
     }, {
         .name = "umul.aa.hh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_UMUL, MAC16_AA, MAC16_HH, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_UMUL, MAC16_HH, 0},
     }, {
         .name = "umul.aa.hl",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_UMUL, MAC16_AA, MAC16_HL, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_UMUL, MAC16_HL, 0},
     }, {
         .name = "umul.aa.lh",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_UMUL, MAC16_AA, MAC16_LH, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_UMUL, MAC16_LH, 0},
     }, {
         .name = "umul.aa.ll",
         .translate = translate_mac16,
-        .par = (const uint32_t[]){MAC16_UMUL, MAC16_AA, MAC16_LL, 0},
-        .windowed_register_op = 0x3,
+        .par = (const uint32_t[]){MAC16_UMUL, MAC16_LL, 0},
     }, {
         .name = "waiti",
         .translate = translate_waiti,
@@ -4170,362 +4359,309 @@ static const XtensaOpcodeOps core_ops[] = {
         .translate = translate_wtlb,
         .par = (const uint32_t[]){true},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x3,
     }, {
         .name = "wer",
         .translate = translate_wer,
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x3,
     }, {
         .name = "witlb",
         .translate = translate_wtlb,
         .par = (const uint32_t[]){false},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x3,
     }, {
         .name = "wrmsk_expstate",
         .translate = translate_wrmsk_expstate,
-        .windowed_register_op = 0x3,
     }, {
         .name = "wsr.176",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){176},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.208",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){208},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.acchi",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){ACCHI},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.acclo",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){ACCLO},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.atomctl",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){ATOMCTL},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.br",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){BR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.cacheattr",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){CACHEATTR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.ccompare0",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){CCOMPARE},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.ccompare1",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){CCOMPARE + 1},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.ccompare2",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){CCOMPARE + 2},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.ccount",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){CCOUNT},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.configid0",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){CONFIGID0},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.configid1",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){CONFIGID1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.cpenable",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){CPENABLE},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.dbreaka0",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){DBREAKA},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.dbreaka1",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){DBREAKA + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.dbreakc0",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){DBREAKC},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.dbreakc1",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){DBREAKC + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.ddr",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){DDR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.debugcause",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){DEBUGCAUSE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.depc",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){DEPC},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.dtlbcfg",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){DTLBCFG},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.epc1",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPC1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.epc2",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPC1 + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.epc3",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPC1 + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.epc4",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPC1 + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.epc5",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPC1 + 4},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.epc6",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPC1 + 5},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.epc7",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPC1 + 6},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.eps2",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPS2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.eps3",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPS2 + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.eps4",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPS2 + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.eps5",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPS2 + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.eps6",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPS2 + 4},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.eps7",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EPS2 + 5},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.exccause",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EXCCAUSE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.excsave1",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EXCSAVE1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.excsave2",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EXCSAVE1 + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.excsave3",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EXCSAVE1 + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.excsave4",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EXCSAVE1 + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.excsave5",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EXCSAVE1 + 4},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.excsave6",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EXCSAVE1 + 5},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.excsave7",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EXCSAVE1 + 6},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.excvaddr",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){EXCVADDR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.ibreaka0",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){IBREAKA},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.ibreaka1",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){IBREAKA + 1},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.ibreakenable",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){IBREAKENABLE},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.icount",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){ICOUNT},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.icountlevel",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){ICOUNTLEVEL},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.intclear",
         .translate = translate_wsr,
@@ -4535,7 +4671,6 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_0 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.intenable",
         .translate = translate_wsr,
@@ -4545,7 +4680,6 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_0 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.interrupt",
         .translate = translate_wsr,
@@ -4555,7 +4689,6 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_0 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.intset",
         .translate = translate_wsr,
@@ -4565,114 +4698,102 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_0 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.itlbcfg",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){ITLBCFG},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.lbeg",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){LBEG},
         .op_flags = XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.lcount",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){LCOUNT},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.lend",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){LEND},
         .op_flags = XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.litbase",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){LITBASE},
         .op_flags = XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.m0",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){MR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.m1",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){MR + 1},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.m2",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){MR + 2},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.m3",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){MR + 3},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.memctl",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){MEMCTL},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.misc0",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){MISC},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.misc1",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){MISC + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.misc2",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){MISC + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.misc3",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){MISC + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.mmid",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){MMID},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
+    }, {
+        .name = "wsr.prefctl",
+        .translate = translate_wsr,
+        .test_ill = test_ill_wsr,
+        .par = (const uint32_t[]){PREFCTL},
     }, {
         .name = "wsr.prid",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){PRID},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.ps",
         .translate = translate_wsr,
@@ -4682,80 +4803,69 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_M1 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.ptevaddr",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){PTEVADDR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.rasid",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){RASID},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.sar",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){SAR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.scompare1",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){SCOMPARE1},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.vecbase",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){VECBASE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wsr.windowbase",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){WINDOW_BASE},
-        .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_PRIVILEGED |
+            XTENSA_OP_EXIT_TB_M1 |
+            XTENSA_OP_SYNC_REGISTER_WINDOW,
     }, {
         .name = "wsr.windowstart",
         .translate = translate_wsr,
         .test_ill = test_ill_wsr,
         .par = (const uint32_t[]){WINDOW_START},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "wur.expstate",
         .translate = translate_wur,
         .par = (const uint32_t[]){EXPSTATE},
-        .windowed_register_op = 0x1,
     }, {
         .name = "wur.fcr",
         .translate = translate_wur,
         .par = (const uint32_t[]){FCR},
-        .windowed_register_op = 0x1,
         .coprocessor = 0x1,
     }, {
         .name = "wur.fsr",
         .translate = translate_wur,
         .par = (const uint32_t[]){FSR},
-        .windowed_register_op = 0x1,
         .coprocessor = 0x1,
     }, {
         .name = "wur.threadptr",
         .translate = translate_wur,
         .par = (const uint32_t[]){THREADPTR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xor",
         .translate = translate_xor,
-        .windowed_register_op = 0x7,
     }, {
         .name = "xorb",
         .translate = translate_boolean,
@@ -4766,340 +4876,291 @@ static const XtensaOpcodeOps core_ops[] = {
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){176},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.208",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){208},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.acchi",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){ACCHI},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.acclo",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){ACCLO},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.atomctl",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){ATOMCTL},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.br",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){BR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.cacheattr",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){CACHEATTR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.ccompare0",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){CCOMPARE},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.ccompare1",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){CCOMPARE + 1},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.ccompare2",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){CCOMPARE + 2},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.ccount",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){CCOUNT},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.configid0",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){CONFIGID0},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.configid1",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){CONFIGID1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.cpenable",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){CPENABLE},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.dbreaka0",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){DBREAKA},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.dbreaka1",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){DBREAKA + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.dbreakc0",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){DBREAKC},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.dbreakc1",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){DBREAKC + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.ddr",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){DDR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.debugcause",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){DEBUGCAUSE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.depc",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){DEPC},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.dtlbcfg",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){DTLBCFG},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.epc1",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPC1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.epc2",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPC1 + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.epc3",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPC1 + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.epc4",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPC1 + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.epc5",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPC1 + 4},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.epc6",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPC1 + 5},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.epc7",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPC1 + 6},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.eps2",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPS2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.eps3",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPS2 + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.eps4",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPS2 + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.eps5",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPS2 + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.eps6",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPS2 + 4},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.eps7",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EPS2 + 5},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.exccause",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EXCCAUSE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.excsave1",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EXCSAVE1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.excsave2",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EXCSAVE1 + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.excsave3",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EXCSAVE1 + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.excsave4",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EXCSAVE1 + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.excsave5",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EXCSAVE1 + 4},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.excsave6",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EXCSAVE1 + 5},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.excsave7",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EXCSAVE1 + 6},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.excvaddr",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){EXCVADDR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.ibreaka0",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){IBREAKA},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.ibreaka1",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){IBREAKA + 1},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.ibreakenable",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){IBREAKENABLE},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.icount",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){ICOUNT},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.icountlevel",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){ICOUNTLEVEL},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.intclear",
         .translate = translate_xsr,
@@ -5109,7 +5170,6 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_0 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.intenable",
         .translate = translate_xsr,
@@ -5119,7 +5179,6 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_0 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.interrupt",
         .translate = translate_xsr,
@@ -5129,7 +5188,6 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_0 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.intset",
         .translate = translate_xsr,
@@ -5139,107 +5197,96 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_0 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.itlbcfg",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){ITLBCFG},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.lbeg",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){LBEG},
         .op_flags = XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.lcount",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){LCOUNT},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.lend",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){LEND},
         .op_flags = XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.litbase",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){LITBASE},
         .op_flags = XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.m0",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){MR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.m1",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){MR + 1},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.m2",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){MR + 2},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.m3",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){MR + 3},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.memctl",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){MEMCTL},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.misc0",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){MISC},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.misc1",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){MISC + 1},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.misc2",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){MISC + 2},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.misc3",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){MISC + 3},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
+    }, {
+        .name = "xsr.prefctl",
+        .translate = translate_xsr,
+        .test_ill = test_ill_xsr,
+        .par = (const uint32_t[]){PREFCTL},
     }, {
         .name = "xsr.prid",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){PRID},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.ps",
         .translate = translate_xsr,
@@ -5249,54 +5296,48 @@ static const XtensaOpcodeOps core_ops[] = {
             XTENSA_OP_PRIVILEGED |
             XTENSA_OP_EXIT_TB_M1 |
             XTENSA_OP_CHECK_INTERRUPTS,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.ptevaddr",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){PTEVADDR},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.rasid",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){RASID},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.sar",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){SAR},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.scompare1",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){SCOMPARE1},
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.vecbase",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){VECBASE},
         .op_flags = XTENSA_OP_PRIVILEGED,
-        .windowed_register_op = 0x1,
     }, {
         .name = "xsr.windowbase",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){WINDOW_BASE},
-        .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
+        .op_flags = XTENSA_OP_PRIVILEGED |
+            XTENSA_OP_EXIT_TB_M1 |
+            XTENSA_OP_SYNC_REGISTER_WINDOW,
     }, {
         .name = "xsr.windowstart",
         .translate = translate_xsr,
         .test_ill = test_ill_xsr,
         .par = (const uint32_t[]){WINDOW_START},
         .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1,
-        .windowed_register_op = 0x1,
     },
 };
 
@@ -5306,17 +5347,17 @@ const XtensaOpcodeTranslators xtensa_core_opcodes = {
 };
 
 
-static void translate_abs_s(DisasContext *dc, const uint32_t arg[],
+static void translate_abs_s(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    gen_helper_abs_s(cpu_FR[arg[0]], cpu_FR[arg[1]]);
+    gen_helper_abs_s(arg[0].out, arg[1].in);
 }
 
-static void translate_add_s(DisasContext *dc, const uint32_t arg[],
+static void translate_add_s(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    gen_helper_add_s(cpu_FR[arg[0]], cpu_env,
-                     cpu_FR[arg[1]], cpu_FR[arg[2]]);
+    gen_helper_add_s(arg[0].out, cpu_env,
+                     arg[1].in, arg[2].in);
 }
 
 enum {
@@ -5329,7 +5370,7 @@ enum {
     COMPARE_ULE,
 };
 
-static void translate_compare_s(DisasContext *dc, const uint32_t arg[],
+static void translate_compare_s(DisasContext *dc, const OpcodeArg arg[],
                                 const uint32_t par[])
 {
     static void (* const helper[])(TCGv_env env, TCGv_i32 bit,
@@ -5342,153 +5383,153 @@ static void translate_compare_s(DisasContext *dc, const uint32_t arg[],
         [COMPARE_OLE] = gen_helper_ole_s,
         [COMPARE_ULE] = gen_helper_ule_s,
     };
-    TCGv_i32 bit = tcg_const_i32(1 << arg[0]);
+    TCGv_i32 bit = tcg_const_i32(1 << arg[0].imm);
 
-    helper[par[0]](cpu_env, bit, cpu_FR[arg[1]], cpu_FR[arg[2]]);
+    helper[par[0]](cpu_env, bit, arg[1].in, arg[2].in);
     tcg_temp_free(bit);
 }
 
-static void translate_float_s(DisasContext *dc, const uint32_t arg[],
+static void translate_float_s(DisasContext *dc, const OpcodeArg arg[],
                               const uint32_t par[])
 {
-    TCGv_i32 scale = tcg_const_i32(-arg[2]);
+    TCGv_i32 scale = tcg_const_i32(-arg[2].imm);
 
     if (par[0]) {
-        gen_helper_uitof(cpu_FR[arg[0]], cpu_env, cpu_R[arg[1]], scale);
+        gen_helper_uitof(arg[0].out, cpu_env, arg[1].in, scale);
     } else {
-        gen_helper_itof(cpu_FR[arg[0]], cpu_env, cpu_R[arg[1]], scale);
+        gen_helper_itof(arg[0].out, cpu_env, arg[1].in, scale);
     }
     tcg_temp_free(scale);
 }
 
-static void translate_ftoi_s(DisasContext *dc, const uint32_t arg[],
+static void translate_ftoi_s(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
     TCGv_i32 rounding_mode = tcg_const_i32(par[0]);
-    TCGv_i32 scale = tcg_const_i32(arg[2]);
+    TCGv_i32 scale = tcg_const_i32(arg[2].imm);
 
     if (par[1]) {
-        gen_helper_ftoui(cpu_R[arg[0]], cpu_FR[arg[1]],
+        gen_helper_ftoui(arg[0].out, arg[1].in,
                          rounding_mode, scale);
     } else {
-        gen_helper_ftoi(cpu_R[arg[0]], cpu_FR[arg[1]],
+        gen_helper_ftoi(arg[0].out, arg[1].in,
                         rounding_mode, scale);
     }
     tcg_temp_free(rounding_mode);
     tcg_temp_free(scale);
 }
 
-static void translate_ldsti(DisasContext *dc, const uint32_t arg[],
+static void translate_ldsti(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
     TCGv_i32 addr = tcg_temp_new_i32();
 
-    tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]);
+    tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm);
     gen_load_store_alignment(dc, 2, addr, false);
     if (par[0]) {
-        tcg_gen_qemu_st32(cpu_FR[arg[0]], addr, dc->cring);
+        tcg_gen_qemu_st32(arg[0].in, addr, dc->cring);
     } else {
-        tcg_gen_qemu_ld32u(cpu_FR[arg[0]], addr, dc->cring);
+        tcg_gen_qemu_ld32u(arg[0].out, addr, dc->cring);
     }
     if (par[1]) {
-        tcg_gen_mov_i32(cpu_R[arg[1]], addr);
+        tcg_gen_mov_i32(arg[1].out, addr);
     }
     tcg_temp_free(addr);
 }
 
-static void translate_ldstx(DisasContext *dc, const uint32_t arg[],
+static void translate_ldstx(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
     TCGv_i32 addr = tcg_temp_new_i32();
 
-    tcg_gen_add_i32(addr, cpu_R[arg[1]], cpu_R[arg[2]]);
+    tcg_gen_add_i32(addr, arg[1].in, arg[2].in);
     gen_load_store_alignment(dc, 2, addr, false);
     if (par[0]) {
-        tcg_gen_qemu_st32(cpu_FR[arg[0]], addr, dc->cring);
+        tcg_gen_qemu_st32(arg[0].in, addr, dc->cring);
     } else {
-        tcg_gen_qemu_ld32u(cpu_FR[arg[0]], addr, dc->cring);
+        tcg_gen_qemu_ld32u(arg[0].out, addr, dc->cring);
     }
     if (par[1]) {
-        tcg_gen_mov_i32(cpu_R[arg[1]], addr);
+        tcg_gen_mov_i32(arg[1].out, addr);
     }
     tcg_temp_free(addr);
 }
 
-static void translate_madd_s(DisasContext *dc, const uint32_t arg[],
+static void translate_madd_s(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
-    gen_helper_madd_s(cpu_FR[arg[0]], cpu_env,
-                      cpu_FR[arg[0]], cpu_FR[arg[1]], cpu_FR[arg[2]]);
+    gen_helper_madd_s(arg[0].out, cpu_env,
+                      arg[0].in, arg[1].in, arg[2].in);
 }
 
-static void translate_mov_s(DisasContext *dc, const uint32_t arg[],
+static void translate_mov_s(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    tcg_gen_mov_i32(cpu_FR[arg[0]], cpu_FR[arg[1]]);
+    tcg_gen_mov_i32(arg[0].out, arg[1].in);
 }
 
-static void translate_movcond_s(DisasContext *dc, const uint32_t arg[],
+static void translate_movcond_s(DisasContext *dc, const OpcodeArg arg[],
                                 const uint32_t par[])
 {
     TCGv_i32 zero = tcg_const_i32(0);
 
-    tcg_gen_movcond_i32(par[0], cpu_FR[arg[0]],
-                        cpu_R[arg[2]], zero,
-                        cpu_FR[arg[1]], cpu_FR[arg[0]]);
+    tcg_gen_movcond_i32(par[0], arg[0].out,
+                        arg[2].in, zero,
+                        arg[1].in, arg[0].in);
     tcg_temp_free(zero);
 }
 
-static void translate_movp_s(DisasContext *dc, const uint32_t arg[],
+static void translate_movp_s(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
     TCGv_i32 zero = tcg_const_i32(0);
     TCGv_i32 tmp = tcg_temp_new_i32();
 
-    tcg_gen_andi_i32(tmp, cpu_SR[BR], 1 << arg[2]);
+    tcg_gen_andi_i32(tmp, arg[2].in, 1 << arg[2].imm);
     tcg_gen_movcond_i32(par[0],
-                        cpu_FR[arg[0]], tmp, zero,
-                        cpu_FR[arg[1]], cpu_FR[arg[0]]);
+                        arg[0].out, tmp, zero,
+                        arg[1].in, arg[0].in);
     tcg_temp_free(tmp);
     tcg_temp_free(zero);
 }
 
-static void translate_mul_s(DisasContext *dc, const uint32_t arg[],
+static void translate_mul_s(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    gen_helper_mul_s(cpu_FR[arg[0]], cpu_env,
-                     cpu_FR[arg[1]], cpu_FR[arg[2]]);
+    gen_helper_mul_s(arg[0].out, cpu_env,
+                     arg[1].in, arg[2].in);
 }
 
-static void translate_msub_s(DisasContext *dc, const uint32_t arg[],
+static void translate_msub_s(DisasContext *dc, const OpcodeArg arg[],
                              const uint32_t par[])
 {
-    gen_helper_msub_s(cpu_FR[arg[0]], cpu_env,
-                      cpu_FR[arg[0]], cpu_FR[arg[1]], cpu_FR[arg[2]]);
+    gen_helper_msub_s(arg[0].out, cpu_env,
+                      arg[0].in, arg[1].in, arg[2].in);
 }
 
-static void translate_neg_s(DisasContext *dc, const uint32_t arg[],
+static void translate_neg_s(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    gen_helper_neg_s(cpu_FR[arg[0]], cpu_FR[arg[1]]);
+    gen_helper_neg_s(arg[0].out, arg[1].in);
 }
 
-static void translate_rfr_s(DisasContext *dc, const uint32_t arg[],
+static void translate_rfr_s(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    tcg_gen_mov_i32(cpu_R[arg[0]], cpu_FR[arg[1]]);
+    tcg_gen_mov_i32(arg[0].out, arg[1].in);
 }
 
-static void translate_sub_s(DisasContext *dc, const uint32_t arg[],
+static void translate_sub_s(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    gen_helper_sub_s(cpu_FR[arg[0]], cpu_env,
-                     cpu_FR[arg[1]], cpu_FR[arg[2]]);
+    gen_helper_sub_s(arg[0].out, cpu_env,
+                     arg[1].in, arg[2].in);
 }
 
-static void translate_wfr_s(DisasContext *dc, const uint32_t arg[],
+static void translate_wfr_s(DisasContext *dc, const OpcodeArg arg[],
                             const uint32_t par[])
 {
-    tcg_gen_mov_i32(cpu_FR[arg[0]], cpu_R[arg[1]]);
+    tcg_gen_mov_i32(arg[0].out, arg[1].in);
 }
 
 static const XtensaOpcodeOps fpu2000_ops[] = {
@@ -5504,43 +5545,40 @@ static const XtensaOpcodeOps fpu2000_ops[] = {
         .name = "ceil.s",
         .translate = translate_ftoi_s,
         .par = (const uint32_t[]){float_round_up, false},
-        .windowed_register_op = 0x1,
         .coprocessor = 0x1,
     }, {
         .name = "float.s",
         .translate = translate_float_s,
         .par = (const uint32_t[]){false},
-        .windowed_register_op = 0x2,
         .coprocessor = 0x1,
     }, {
         .name = "floor.s",
         .translate = translate_ftoi_s,
         .par = (const uint32_t[]){float_round_down, false},
-        .windowed_register_op = 0x1,
         .coprocessor = 0x1,
     }, {
         .name = "lsi",
         .translate = translate_ldsti,
         .par = (const uint32_t[]){false, false},
-        .windowed_register_op = 0x2,
+        .op_flags = XTENSA_OP_LOAD,
         .coprocessor = 0x1,
     }, {
         .name = "lsiu",
         .translate = translate_ldsti,
         .par = (const uint32_t[]){false, true},
-        .windowed_register_op = 0x2,
+        .op_flags = XTENSA_OP_LOAD,
         .coprocessor = 0x1,
     }, {
         .name = "lsx",
         .translate = translate_ldstx,
         .par = (const uint32_t[]){false, false},
-        .windowed_register_op = 0x6,
+        .op_flags = XTENSA_OP_LOAD,
         .coprocessor = 0x1,
     }, {
         .name = "lsxu",
         .translate = translate_ldstx,
         .par = (const uint32_t[]){false, true},
-        .windowed_register_op = 0x6,
+        .op_flags = XTENSA_OP_LOAD,
         .coprocessor = 0x1,
     }, {
         .name = "madd.s",
@@ -5554,7 +5592,6 @@ static const XtensaOpcodeOps fpu2000_ops[] = {
         .name = "moveqz.s",
         .translate = translate_movcond_s,
         .par = (const uint32_t[]){TCG_COND_EQ},
-        .windowed_register_op = 0x4,
         .coprocessor = 0x1,
     }, {
         .name = "movf.s",
@@ -5565,19 +5602,16 @@ static const XtensaOpcodeOps fpu2000_ops[] = {
         .name = "movgez.s",
         .translate = translate_movcond_s,
         .par = (const uint32_t[]){TCG_COND_GE},
-        .windowed_register_op = 0x4,
         .coprocessor = 0x1,
     }, {
         .name = "movltz.s",
         .translate = translate_movcond_s,
         .par = (const uint32_t[]){TCG_COND_LT},
-        .windowed_register_op = 0x4,
         .coprocessor = 0x1,
     }, {
         .name = "movnez.s",
         .translate = translate_movcond_s,
         .par = (const uint32_t[]){TCG_COND_NE},
-        .windowed_register_op = 0x4,
         .coprocessor = 0x1,
     }, {
         .name = "movt.s",
@@ -5614,37 +5648,35 @@ static const XtensaOpcodeOps fpu2000_ops[] = {
     }, {
         .name = "rfr",
         .translate = translate_rfr_s,
-        .windowed_register_op = 0x1,
         .coprocessor = 0x1,
     }, {
         .name = "round.s",
         .translate = translate_ftoi_s,
         .par = (const uint32_t[]){float_round_nearest_even, false},
-        .windowed_register_op = 0x1,
         .coprocessor = 0x1,
     }, {
         .name = "ssi",
         .translate = translate_ldsti,
         .par = (const uint32_t[]){true, false},
-        .windowed_register_op = 0x2,
+        .op_flags = XTENSA_OP_STORE,
         .coprocessor = 0x1,
     }, {
         .name = "ssiu",
         .translate = translate_ldsti,
         .par = (const uint32_t[]){true, true},
-        .windowed_register_op = 0x2,
+        .op_flags = XTENSA_OP_STORE,
         .coprocessor = 0x1,
     }, {
         .name = "ssx",
         .translate = translate_ldstx,
         .par = (const uint32_t[]){true, false},
-        .windowed_register_op = 0x6,
+        .op_flags = XTENSA_OP_STORE,
         .coprocessor = 0x1,
     }, {
         .name = "ssxu",
         .translate = translate_ldstx,
         .par = (const uint32_t[]){true, true},
-        .windowed_register_op = 0x6,
+        .op_flags = XTENSA_OP_STORE,
         .coprocessor = 0x1,
     }, {
         .name = "sub.s",
@@ -5654,7 +5686,6 @@ static const XtensaOpcodeOps fpu2000_ops[] = {
         .name = "trunc.s",
         .translate = translate_ftoi_s,
         .par = (const uint32_t[]){float_round_to_zero, false},
-        .windowed_register_op = 0x1,
         .coprocessor = 0x1,
     }, {
         .name = "ueq.s",
@@ -5665,7 +5696,6 @@ static const XtensaOpcodeOps fpu2000_ops[] = {
         .name = "ufloat.s",
         .translate = translate_float_s,
         .par = (const uint32_t[]){true},
-        .windowed_register_op = 0x2,
         .coprocessor = 0x1,
     }, {
         .name = "ule.s",
@@ -5686,12 +5716,10 @@ static const XtensaOpcodeOps fpu2000_ops[] = {
         .name = "utrunc.s",
         .translate = translate_ftoi_s,
         .par = (const uint32_t[]){float_round_to_zero, true},
-        .windowed_register_op = 0x1,
         .coprocessor = 0x1,
     }, {
         .name = "wfr",
         .translate = translate_wfr_s,
-        .windowed_register_op = 0x2,
         .coprocessor = 0x1,
     },
 };
diff --git a/target/xtensa/win_helper.c b/target/xtensa/win_helper.c
index 7d793d4f9c..f6f96a64c3 100644
--- a/target/xtensa/win_helper.c
+++ b/target/xtensa/win_helper.c
@@ -96,9 +96,9 @@ void xtensa_rotate_window(CPUXtensaState *env, uint32_t delta)
     xtensa_rotate_window_abs(env, env->sregs[WINDOW_BASE] + delta);
 }
 
-void HELPER(wsr_windowbase)(CPUXtensaState *env, uint32_t v)
+void HELPER(sync_windowbase)(CPUXtensaState *env)
 {
-    xtensa_rotate_window_abs(env, v);
+    xtensa_rotate_window_abs(env, env->windowbase_next);
 }
 
 void HELPER(entry)(CPUXtensaState *env, uint32_t pc, uint32_t s, uint32_t imm)
@@ -106,9 +106,8 @@ void HELPER(entry)(CPUXtensaState *env, uint32_t pc, uint32_t s, uint32_t imm)
     int callinc = (env->sregs[PS] & PS_CALLINC) >> PS_CALLINC_SHIFT;
 
     env->regs[(callinc << 2) | (s & 3)] = env->regs[s] - imm;
-    xtensa_rotate_window(env, callinc);
-    env->sregs[WINDOW_START] |=
-        windowstart_bit(env->sregs[WINDOW_BASE], env);
+    env->windowbase_next = env->sregs[WINDOW_BASE] + callinc;
+    env->sregs[WINDOW_START] |= windowstart_bit(env->windowbase_next, env);
 }
 
 void HELPER(window_check)(CPUXtensaState *env, uint32_t pc, uint32_t w)
@@ -185,20 +184,11 @@ void HELPER(test_underflow_retw)(CPUXtensaState *env, uint32_t pc)
     }
 }
 
-uint32_t HELPER(retw)(CPUXtensaState *env, uint32_t pc)
+void HELPER(retw)(CPUXtensaState *env, uint32_t a0)
 {
-    int n = (env->regs[0] >> 30) & 0x3;
-    uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env);
-    uint32_t ret_pc = (pc & 0xc0000000) | (env->regs[0] & 0x3fffffff);
+    int n = (a0 >> 30) & 0x3;
 
     xtensa_rotate_window(env, -n);
-    env->sregs[WINDOW_START] &= ~windowstart_bit(windowbase, env);
-    return ret_pc;
-}
-
-void HELPER(rotw)(CPUXtensaState *env, uint32_t imm4)
-{
-    xtensa_rotate_window(env, imm4);
 }
 
 void xtensa_restore_owb(CPUXtensaState *env)
diff --git a/tests/.gitignore b/tests/.gitignore
index 72c18aaab0..f2bf85c8c4 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -5,6 +5,7 @@ benchmark-crypto-hmac
 check-*
 !check-*.c
 !check-*.sh
+fp/*.out
 qht-bench
 rcutorture
 test-*
diff --git a/tests/Makefile.include b/tests/Makefile.include
index b39e989f72..c2ac4b8d4c 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -70,6 +70,7 @@ check-unit-y += tests/test-throttle$(EXESUF)
 check-unit-y += tests/test-thread-pool$(EXESUF)
 check-unit-y += tests/test-hbitmap$(EXESUF)
 check-unit-y += tests/test-bdrv-drain$(EXESUF)
+check-unit-y += tests/test-bdrv-graph-mod$(EXESUF)
 check-unit-y += tests/test-blockjob$(EXESUF)
 check-unit-y += tests/test-blockjob-txn$(EXESUF)
 check-unit-y += tests/test-block-backend$(EXESUF)
@@ -111,10 +112,15 @@ check-unit-y += tests/test-crypto-secret$(EXESUF)
 check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
 check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF)
 ifneq (,$(findstring qemu-ga,$(TOOLS)))
-check-unit-$(land,$(CONFIG_LINUX),$(CONFIG_VIRTIO_SERIAL)) += tests/test-qga$(EXESUF)
+check-unit-$(call land,$(CONFIG_LINUX),$(CONFIG_VIRTIO_SERIAL)) += tests/test-qga$(EXESUF)
 endif
 check-unit-y += tests/test-timed-average$(EXESUF)
+check-unit-$(CONFIG_INOTIFY1) += tests/test-util-filemonitor$(EXESUF)
 check-unit-y += tests/test-util-sockets$(EXESUF)
+check-unit-y += tests/test-authz-simple$(EXESUF)
+check-unit-y += tests/test-authz-list$(EXESUF)
+check-unit-y += tests/test-authz-listfile$(EXESUF)
+check-unit-$(CONFIG_AUTH_PAM) += tests/test-authz-pam$(EXESUF)
 check-unit-y += tests/test-io-task$(EXESUF)
 check-unit-y += tests/test-io-channel-socket$(EXESUF)
 check-unit-y += tests/test-io-channel-file$(EXESUF)
@@ -192,6 +198,7 @@ check-qtest-i386-$(CONFIG_ISA_IPMI_KCS) += tests/ipmi-kcs-test$(EXESUF)
 # check-qtest-i386-$(CONFIG_ISA_IPMI_BT) += tests/ipmi-bt-test$(EXESUF)
 check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
+check-qtest-i386-y += tests/device-plug-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
 check-qtest-i386-$(CONFIG_WDT_IB700) += tests/wdt_ib700-test$(EXESUF)
 check-qtest-i386-y += tests/tco-test$(EXESUF)
@@ -209,10 +216,7 @@ check-qtest-i386-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF)
 check-qtest-i386-y += tests/cpu-plug-test$(EXESUF)
 check-qtest-i386-y += tests/q35-test$(EXESUF)
 check-qtest-i386-y += tests/vmgenid-test$(EXESUF)
-check-qtest-i386-$(CONFIG_VHOST_USER_NET_TEST_i386) += tests/vhost-user-test$(EXESUF)
-ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),)
-check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF)
-endif
+check-qtest-i386-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test$(EXESUF)
 check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-swtpm-test$(EXESUF)
 check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-test$(EXESUF)
 check-qtest-i386-$(CONFIG_TPM_TIS) += tests/tpm-tis-swtpm-test$(EXESUF)
@@ -256,6 +260,7 @@ check-qtest-ppc-$(CONFIG_M48T59) += tests/m48t59-test$(EXESUF)
 
 check-qtest-ppc64-y += $(check-qtest-ppc-y)
 check-qtest-ppc64-$(CONFIG_PSERIES) += tests/spapr-phb-test$(EXESUF)
+check-qtest-ppc64-$(CONFIG_PSERIES) += tests/device-plug-test$(EXESUF)
 check-qtest-ppc64-$(CONFIG_POWERNV) += tests/pnv-xscom-test$(EXESUF)
 check-qtest-ppc64-y += tests/migration-test$(EXESUF)
 check-qtest-ppc64-$(CONFIG_PSERIES) += tests/rtas-test$(EXESUF)
@@ -310,6 +315,7 @@ check-qtest-s390x-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF)
 check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF)
 check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF)
 check-qtest-s390x-y += tests/drive_del-test$(EXESUF)
+check-qtest-s390x-y += tests/device-plug-test$(EXESUF)
 check-qtest-s390x-y += tests/virtio-ccw-test$(EXESUF)
 check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF)
 check-qtest-s390x-y += tests/migration-test$(EXESUF)
@@ -532,9 +538,10 @@ test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y)
 test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
 	tests/test-qapi-introspect.o \
 	$(test-qom-obj-y)
-benchmark-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y)
-test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y)
+benchmark-crypto-obj-y = $(authz-obj-y) $(crypto-obj-y) $(test-qom-obj-y)
+test-crypto-obj-y = $(authz-obj-y) $(crypto-obj-y) $(test-qom-obj-y)
 test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
+test-authz-obj-y = $(test-qom-obj-y) $(authz-obj-y)
 test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
 
 tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y)
@@ -555,6 +562,7 @@ tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
 tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
 tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
 tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
+tests/test-bdrv-graph-mod$(EXESUF): tests/test-bdrv-graph-mod.o $(test-block-obj-y) $(test-util-obj-y)
 tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
 tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
 tests/test-block-backend$(EXESUF): tests/test-block-backend.o $(test-block-obj-y) $(test-util-obj-y)
@@ -657,8 +665,14 @@ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
 	tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \
 	tests/crypto-tls-psk-helpers.o \
         $(test-crypto-obj-y)
+tests/test-util-filemonitor$(EXESUF): tests/test-util-filemonitor.o \
+	$(test-util-obj-y)
 tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \
 	tests/socket-helpers.o $(test-util-obj-y)
+tests/test-authz-simple$(EXESUF): tests/test-authz-simple.o $(test-authz-obj-y)
+tests/test-authz-list$(EXESUF): tests/test-authz-list.o $(test-authz-obj-y)
+tests/test-authz-listfile$(EXESUF): tests/test-authz-listfile.o $(test-authz-obj-y)
+tests/test-authz-pam$(EXESUF): tests/test-authz-pam.o $(test-authz-obj-y)
 tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
 tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
         tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y)
@@ -750,6 +764,7 @@ tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o
 tests/qom-test$(EXESUF): tests/qom-test.o
 tests/test-hmp$(EXESUF): tests/test-hmp.o
 tests/machine-none-test$(EXESUF): tests/machine-none-test.o
+tests/device-plug-test$(EXESUF): tests/device-plug-test.o
 tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-virtio-obj-y)
 tests/nvme-test$(EXESUF): tests/nvme-test.o $(libqos-pc-obj-y)
 tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o
@@ -896,23 +911,20 @@ $(FP_TEST_BIN):
 	         "BUILD", "$(notdir $@)")
 
 # The full test suite can take a bit of time, default to a quick run
-ifeq ($(SPEED), quick)
-FP_TL=-l 1
-else
-FP_TL=-l 2 -r all
-endif
+# "-l 2 -r all" can take more than a day for some operations and is best
+# run manually
+FP_TL=-l 1 -r all
 
-# $1 = tests, $2 = description
+# $1 = tests, $2 = description, $3 = test flags
 test-softfloat = $(call quiet-command, \
 			cd $(BUILD_DIR)/tests/fp && \
-			./fp-test -s $(FP_TL) $1 > $2.out 2>&1 || \
+			./fp-test -s $(if $3,$3,$(FP_TL)) $1 > $2.out 2>&1 || \
 			(cat $2.out && exit 1;), \
 			"FLOAT TEST", $2)
 
 # Conversion Routines:
 # FIXME: i32_to_extF80 (broken), i64_to_extF80 (broken)
-#        ui32_to_f128 (not implemented), f128_to_ui32 (not implemented)
-#        extF80_roundToInt (broken)
+#        ui32_to_f128 (not implemented), extF80_roundToInt (broken)
 #
 check-softfloat-conv: $(FP_TEST_BIN)
 	$(call test-softfloat, \
@@ -941,9 +953,11 @@ check-softfloat-conv: $(FP_TEST_BIN)
 		f16_to_ui32 f16_to_ui32_r_minMag \
 		f32_to_ui32 f32_to_ui32_r_minMag \
 		f64_to_ui32 f64_to_ui32_r_minMag \
+		f128_to_ui32 f128_to_ui32_r_minMag \
 		f16_to_ui64 f16_to_ui64_r_minMag \
 		f32_to_ui64 f32_to_ui64_r_minMag \
-		f64_to_ui64 f64_to_ui64_r_minMag, \
+		f64_to_ui64 f64_to_ui64_r_minMag \
+		f128_to_ui64 f128_to_ui64_r_minMag, \
 		float-to-uint)
 	$(call test-softfloat, \
 		f16_roundToInt f32_roundToInt \
@@ -985,7 +999,7 @@ check-softfloat-compare: $(SF_COMPARE_RULES)
 check-softfloat-mulAdd: $(FP_TEST_BIN)
 	$(call test-softfloat, \
 		f16_mulAdd f32_mulAdd f64_mulAdd f128_mulAdd, \
-		mulAdd)
+		mulAdd,-l 1)
 
 # FIXME: extF80_rem (broken)
 check-softfloat-rem: $(FP_TEST_BIN)
diff --git a/tests/cdrom-test.c b/tests/cdrom-test.c
index 14bd981336..05611da648 100644
--- a/tests/cdrom-test.c
+++ b/tests/cdrom-test.c
@@ -132,8 +132,14 @@ static void add_x86_tests(void)
     qtest_add_data_func("cdrom/boot/virtio-scsi",
                         "-device virtio-scsi -device scsi-cd,drive=cdr "
                         "-blockdev file,node-name=cdr,filename=", test_cdboot);
-    qtest_add_data_func("cdrom/boot/isapc", "-M isapc "
-                        "-drive if=ide,media=cdrom,file=", test_cdboot);
+    /*
+     * Unstable CI test under load
+     * See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html
+     */
+    if (g_test_slow()) {
+        qtest_add_data_func("cdrom/boot/isapc", "-M isapc "
+                            "-drive if=ide,media=cdrom,file=", test_cdboot);
+    }
     qtest_add_data_func("cdrom/boot/am53c974",
                         "-device am53c974 -device scsi-cd,drive=cd1 "
                         "-drive if=none,id=cd1,format=raw,file=", test_cdboot);
diff --git a/tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2
new file mode 100644
index 0000000000..ac0b7b1b8f
--- /dev/null
+++ b/tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2
Binary files differdiff --git a/tests/data/uefi-boot-images/bios-tables-test.arm.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.arm.iso.qcow2
new file mode 100644
index 0000000000..d20fa7c819
--- /dev/null
+++ b/tests/data/uefi-boot-images/bios-tables-test.arm.iso.qcow2
Binary files differdiff --git a/tests/data/uefi-boot-images/bios-tables-test.i386.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.i386.iso.qcow2
new file mode 100644
index 0000000000..26c882baea
--- /dev/null
+++ b/tests/data/uefi-boot-images/bios-tables-test.i386.iso.qcow2
Binary files differdiff --git a/tests/data/uefi-boot-images/bios-tables-test.x86_64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.x86_64.iso.qcow2
new file mode 100644
index 0000000000..9ec3c1f20b
--- /dev/null
+++ b/tests/data/uefi-boot-images/bios-tables-test.x86_64.iso.qcow2
Binary files differdiff --git a/tests/device-plug-test.c b/tests/device-plug-test.c
new file mode 100644
index 0000000000..318e422d51
--- /dev/null
+++ b/tests/device-plug-test.c
@@ -0,0 +1,178 @@
+/*
+ * QEMU device plug/unplug handling
+ *
+ * Copyright (C) 2019 Red Hat Inc.
+ *
+ * Authors:
+ *  David Hildenbrand <david@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qstring.h"
+
+static void device_del_start(QTestState *qtest, const char *id)
+{
+    qtest_qmp_send(qtest,
+                   "{'execute': 'device_del', 'arguments': { 'id': %s } }", id);
+}
+
+static void device_del_finish(QTestState *qtest)
+{
+    QDict *resp = qtest_qmp_receive(qtest);
+
+    g_assert(qdict_haskey(resp, "return"));
+    qobject_unref(resp);
+}
+
+static void device_del_request(QTestState *qtest, const char *id)
+{
+    device_del_start(qtest, id);
+    device_del_finish(qtest);
+}
+
+static void system_reset(QTestState *qtest)
+{
+    QDict *resp;
+
+    resp = qtest_qmp(qtest, "{'execute': 'system_reset'}");
+    g_assert(qdict_haskey(resp, "return"));
+    qobject_unref(resp);
+}
+
+static void wait_device_deleted_event(QTestState *qtest, const char *id)
+{
+    QDict *resp, *data;
+    QString *qstr;
+
+    /*
+     * Other devices might get removed along with the removed device. Skip
+     * these. The device of interest will be the last one.
+     */
+    for (;;) {
+        resp = qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED");
+        data = qdict_get_qdict(resp, "data");
+        if (!data || !qdict_get(data, "device")) {
+            qobject_unref(resp);
+            continue;
+        }
+        qstr = qobject_to(QString, qdict_get(data, "device"));
+        g_assert(qstr);
+        if (!strcmp(qstring_get_str(qstr), id)) {
+            qobject_unref(resp);
+            break;
+        }
+        qobject_unref(resp);
+    }
+}
+
+static void test_pci_unplug_request(void)
+{
+    QTestState *qtest = qtest_initf("-device virtio-mouse-pci,id=dev0");
+
+    /*
+     * Request device removal. As the guest is not running, the request won't
+     * be processed. However during system reset, the removal will be
+     * handled, removing the device.
+     */
+    device_del_request(qtest, "dev0");
+    system_reset(qtest);
+    wait_device_deleted_event(qtest, "dev0");
+
+    qtest_quit(qtest);
+}
+
+static void test_ccw_unplug(void)
+{
+    QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0");
+
+    /*
+     * The DEVICE_DELETED events will be sent before the command
+     * completes.
+     */
+    device_del_start(qtest, "dev0");
+    wait_device_deleted_event(qtest, "dev0");
+    device_del_finish(qtest);
+
+    qtest_quit(qtest);
+}
+
+static void test_spapr_cpu_unplug_request(void)
+{
+    QTestState *qtest;
+
+    qtest = qtest_initf("-cpu power9_v2.0 -smp 1,maxcpus=2 "
+                        "-device power9_v2.0-spapr-cpu-core,core-id=1,id=dev0");
+
+    /* similar to test_pci_unplug_request */
+    device_del_request(qtest, "dev0");
+    system_reset(qtest);
+    wait_device_deleted_event(qtest, "dev0");
+
+    qtest_quit(qtest);
+}
+
+static void test_spapr_memory_unplug_request(void)
+{
+    QTestState *qtest;
+
+    qtest = qtest_initf("-m 256M,slots=1,maxmem=768M "
+                        "-object memory-backend-ram,id=mem0,size=512M "
+                        "-device pc-dimm,id=dev0,memdev=mem0");
+
+    /* similar to test_pci_unplug_request */
+    device_del_request(qtest, "dev0");
+    system_reset(qtest);
+    wait_device_deleted_event(qtest, "dev0");
+
+    qtest_quit(qtest);
+}
+
+static void test_spapr_phb_unplug_request(void)
+{
+    QTestState *qtest;
+
+    qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0");
+
+    /* similar to test_pci_unplug_request */
+    device_del_request(qtest, "dev0");
+    system_reset(qtest);
+    wait_device_deleted_event(qtest, "dev0");
+
+    qtest_quit(qtest);
+}
+
+int main(int argc, char **argv)
+{
+    const char *arch = qtest_get_arch();
+
+    g_test_init(&argc, &argv, NULL);
+
+    /*
+     * We need a system that will process unplug requests during system resets
+     * and does not do PCI surprise removal. This holds for x86 ACPI,
+     * s390x and spapr.
+     */
+    qtest_add_func("/device-plug/pci-unplug-request",
+                   test_pci_unplug_request);
+
+    if (!strcmp(arch, "s390x")) {
+        qtest_add_func("/device-plug/ccw-unplug",
+                       test_ccw_unplug);
+    }
+
+    if (!strcmp(arch, "ppc64")) {
+        qtest_add_func("/device-plug/spapr-cpu-unplug-request",
+                       test_spapr_cpu_unplug_request);
+        qtest_add_func("/device-plug/spapr-memory-unplug-request",
+                       test_spapr_memory_unplug_request);
+        qtest_add_func("/device-plug/spapr-phb-unplug-request",
+                       test_spapr_phb_unplug_request);
+    }
+
+    return g_test_run();
+}
diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker
index 954fcf9606..d770a11a52 100644
--- a/tests/docker/dockerfiles/debian-amd64.docker
+++ b/tests/docker/dockerfiles/debian-amd64.docker
@@ -33,6 +33,7 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
     apt-get install -y --no-install-recommends \
         linux-headers-amd64
 RUN git clone https://github.com/luigirizzo/netmap.git /usr/src/netmap
+RUN cd /usr/src/netmap && git checkout v11.3
 RUN cd /usr/src/netmap/LINUX && ./configure --no-drivers --no-apps --kernel-dir=$(ls -d /usr/src/linux-headers-*-amd64) && make install
 ENV QEMU_CONFIGURE_OPTS --enable-netmap
 
diff --git a/tests/docker/dockerfiles/debian9.docker b/tests/docker/dockerfiles/debian9.docker
index 154ae2a455..5f23a35404 100644
--- a/tests/docker/dockerfiles/debian9.docker
+++ b/tests/docker/dockerfiles/debian9.docker
@@ -13,8 +13,8 @@ FROM debian:stretch-slim
 RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list
 
 # Install common build utilities
-RUN apt update
-RUN DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata
+RUN apt-get update && \
+    DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata
 RUN DEBIAN_FRONTEND=noninteractive eatmydata \
     apt install -y --no-install-recommends \
         bison \
diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c
index 2a35ef601d..7d0faf2b47 100644
--- a/tests/fp/fp-test.c
+++ b/tests/fp/fp-test.c
@@ -125,17 +125,42 @@ static void not_implemented(void)
 
 static bool blacklisted(unsigned op, int rmode)
 {
-    /* odd has only been implemented for a few 128-bit ops */
+    /* odd has not been implemented for any 80-bit ops */
     if (rmode == softfloat_round_odd) {
         switch (op) {
-        case F128_ADD:
-        case F128_SUB:
-        case F128_MUL:
-        case F128_DIV:
-        case F128_TO_F64:
-        case F128_SQRT:
-            return false;
-        default:
+        case EXTF80_TO_UI32:
+        case EXTF80_TO_UI64:
+        case EXTF80_TO_I32:
+        case EXTF80_TO_I64:
+        case EXTF80_TO_UI32_R_MINMAG:
+        case EXTF80_TO_UI64_R_MINMAG:
+        case EXTF80_TO_I32_R_MINMAG:
+        case EXTF80_TO_I64_R_MINMAG:
+        case EXTF80_TO_F16:
+        case EXTF80_TO_F32:
+        case EXTF80_TO_F64:
+        case EXTF80_TO_F128:
+        case EXTF80_ROUNDTOINT:
+        case EXTF80_ADD:
+        case EXTF80_SUB:
+        case EXTF80_MUL:
+        case EXTF80_DIV:
+        case EXTF80_REM:
+        case EXTF80_SQRT:
+        case EXTF80_EQ:
+        case EXTF80_LE:
+        case EXTF80_LT:
+        case EXTF80_EQ_SIGNALING:
+        case EXTF80_LE_QUIET:
+        case EXTF80_LT_QUIET:
+        case UI32_TO_EXTF80:
+        case UI64_TO_EXTF80:
+        case I32_TO_EXTF80:
+        case I64_TO_EXTF80:
+        case F16_TO_EXTF80:
+        case F32_TO_EXTF80:
+        case F64_TO_EXTF80:
+        case F128_TO_EXTF80:
             return true;
         }
     }
@@ -622,7 +647,8 @@ static void do_testfloat(int op, int rmode, bool exact)
         test_ab_extF80_z_bool(true_ab_extF80M_z_bool, subj_ab_extF80M_z_bool);
         break;
     case F128_TO_UI32:
-        not_implemented();
+        test_a_f128_z_ui32_rx(slow_f128M_to_ui32, qemu_f128M_to_ui32, rmode,
+                              exact);
         break;
     case F128_TO_UI64:
         test_a_f128_z_ui64_rx(slow_f128M_to_ui64, qemu_f128M_to_ui64, rmode,
diff --git a/tests/fp/wrap.inc.c b/tests/fp/wrap.inc.c
index d3bf600cd0..0cbd20013e 100644
--- a/tests/fp/wrap.inc.c
+++ b/tests/fp/wrap.inc.c
@@ -367,6 +367,7 @@ WRAP_80_TO_INT_MINMAG(qemu_extF80M_to_i64_r_minMag,
 WRAP_128_TO_INT(qemu_f128M_to_i32, float128_to_int32, int_fast32_t)
 WRAP_128_TO_INT(qemu_f128M_to_i64, float128_to_int64, int_fast64_t)
 
+WRAP_128_TO_INT(qemu_f128M_to_ui32, float128_to_uint32, uint_fast32_t)
 WRAP_128_TO_INT(qemu_f128M_to_ui64, float128_to_uint64, uint_fast64_t)
 #undef WRAP_128_TO_INT
 
diff --git a/tests/qemu-iotests/045 b/tests/qemu-iotests/045
index 55a5d31ca8..d5484a0ee1 100755
--- a/tests/qemu-iotests/045
+++ b/tests/qemu-iotests/045
@@ -132,7 +132,7 @@ class TestSCMFd(iotests.QMPTestCase):
         qemu_img('create', '-f', iotests.imgfmt, image0, '128K')
         # Add an unused monitor, to verify it works fine when two monitor
         # instances present
-        self.vm.add_monitor_telnet("0",4445)
+        self.vm.add_monitor_null()
         self.vm.launch()
 
     def tearDown(self):
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 793af2ab96..b900935fbc 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -82,7 +82,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
 Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig,if=none,id=drive0 -nodefaults
 QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.orig"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
     Removable device: not locked, tray closed
     Cache mode:       writeback
     Backing file:     TEST_DIR/t.qcow2.orig (chain depth: 1)
@@ -172,7 +172,7 @@ QEMU_PROG: -drive driver=null-co,cache=invalid_value: invalid cache option
 Testing: -drive file=TEST_DIR/t.qcow2,cache=writeback,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
 QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
     Removable device: not locked, tray closed
     Cache mode:       writeback
     Backing file:     TEST_DIR/t.qcow2.base (chain depth: 1)
@@ -192,7 +192,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only)
 Testing: -drive file=TEST_DIR/t.qcow2,cache=writethrough,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
 QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
     Removable device: not locked, tray closed
     Cache mode:       writethrough
     Backing file:     TEST_DIR/t.qcow2.base (chain depth: 1)
@@ -212,7 +212,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only)
 Testing: -drive file=TEST_DIR/t.qcow2,cache=unsafe,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
 QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
     Removable device: not locked, tray closed
     Cache mode:       writeback, ignore flushes
     Backing file:     TEST_DIR/t.qcow2.base (chain depth: 1)
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
index ca64edae6a..8c5c735dfd 100644
--- a/tests/qemu-iotests/051.pc.out
+++ b/tests/qemu-iotests/051.pc.out
@@ -82,7 +82,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
 Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig,if=none,id=drive0 -nodefaults
 QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.orig"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
     Removable device: not locked, tray closed
     Cache mode:       writeback
     Backing file:     TEST_DIR/t.qcow2.orig (chain depth: 1)
@@ -244,7 +244,7 @@ QEMU_PROG: -drive driver=null-co,cache=invalid_value: invalid cache option
 Testing: -drive file=TEST_DIR/t.qcow2,cache=writeback,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
 QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
     Removable device: not locked, tray closed
     Cache mode:       writeback
     Backing file:     TEST_DIR/t.qcow2.base (chain depth: 1)
@@ -264,7 +264,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only)
 Testing: -drive file=TEST_DIR/t.qcow2,cache=writethrough,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
 QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
     Removable device: not locked, tray closed
     Cache mode:       writethrough
     Backing file:     TEST_DIR/t.qcow2.base (chain depth: 1)
@@ -284,7 +284,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only)
 Testing: -drive file=TEST_DIR/t.qcow2,cache=unsafe,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
 QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
     Removable device: not locked, tray closed
     Cache mode:       writeback, ignore flushes
     Backing file:     TEST_DIR/t.qcow2.base (chain depth: 1)
diff --git a/tests/qemu-iotests/110 b/tests/qemu-iotests/110
index b64b3b215a..185ad5437e 100755
--- a/tests/qemu-iotests/110
+++ b/tests/qemu-iotests/110
@@ -29,6 +29,7 @@ status=1	# failure is the default!
 _cleanup()
 {
 	_cleanup_test_img
+        rm -f "$TEST_IMG.copy"
 }
 trap "_cleanup; exit \$status" 0 1 2 3 15
 
@@ -60,7 +61,8 @@ echo '=== Non-reconstructable filename ==='
 echo
 
 # Across blkdebug without a config file, you cannot reconstruct filenames, so
-# qemu is incapable of knowing the directory of the top image
+# qemu is incapable of knowing the directory of the top image from the filename
+# alone. However, using bdrv_dirname(), it should still work.
 TEST_IMG="json:{
     'driver': '$IMGFMT',
     'file': {
@@ -85,6 +87,31 @@ echo
 # omit the image size; it should work anyway
 _make_test_img -b "$TEST_IMG_REL.base"
 
+echo
+echo '=== Nodes without a common directory ==='
+echo
+
+cp "$TEST_IMG" "$TEST_IMG.copy"
+
+# Should inform us that the actual path of the backing file cannot be determined
+TEST_IMG="json:{
+    'driver': '$IMGFMT',
+    'file': {
+        'driver': 'quorum',
+        'vote-threshold': 1,
+        'children': [
+            {
+                'driver': 'file',
+                'filename': '$TEST_IMG'
+            },
+            {
+                'driver': 'file',
+                'filename': '$TEST_IMG.copy'
+            }
+        ]
+    }
+}" _img_info | _filter_img_info
+
 
 # success, all done
 echo '*** done'
diff --git a/tests/qemu-iotests/110.out b/tests/qemu-iotests/110.out
index b3584ff87f..46e6a60510 100644
--- a/tests/qemu-iotests/110.out
+++ b/tests/qemu-iotests/110.out
@@ -14,9 +14,16 @@ backing file: t.IMGFMT.base (actual path: TEST_DIR/t.IMGFMT.base)
 image: json:{"driver": "IMGFMT", "file": {"set-state.0.event": "read_aio", "image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "set-state.0.new_state": 42}}
 file format: IMGFMT
 virtual size: 64M (67108864 bytes)
-backing file: t.IMGFMT.base (cannot determine actual path)
+backing file: t.IMGFMT.base (actual path: TEST_DIR/t.IMGFMT.base)
 
 === Backing name is always relative to the backed image ===
 
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=t.IMGFMT.base
+
+=== Nodes without a common directory ===
+
+image: json:{"driver": "IMGFMT", "file": {"children": [{"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, {"driver": "file", "filename": "TEST_DIR/t.IMGFMT.copy"}], "driver": "quorum", "vote-threshold": 1}}
+file format: IMGFMT
+virtual size: 64M (67108864 bytes)
+backing file: t.IMGFMT.base (cannot determine actual path)
 *** done
diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178
index 3f4b4a4564..927bf06e4d 100755
--- a/tests/qemu-iotests/178
+++ b/tests/qemu-iotests/178
@@ -142,6 +142,14 @@ for ofmt in human json; do
             # The backing file doesn't need to exist :)
             $QEMU_IMG measure --output=$ofmt -o backing_file=x \
                               -f "$fmt" -O "$IMGFMT" "$TEST_IMG"
+
+            echo
+            echo "== $fmt input image and LUKS encryption =="
+            echo
+            $QEMU_IMG measure --output=$ofmt \
+                              --object secret,id=sec0,data=base \
+                              -o encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10 \
+                              -f "$fmt" -O "$IMGFMT" "$TEST_IMG"
         fi
 
         echo
diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2
index d42d4a4597..55a8dc926f 100644
--- a/tests/qemu-iotests/178.out.qcow2
+++ b/tests/qemu-iotests/178.out.qcow2
@@ -68,6 +68,11 @@ converted image file size in bytes: 458752
 required size: 1074135040
 fully allocated size: 1074135040
 
+== qcow2 input image and LUKS encryption ==
+
+required size: 2686976
+fully allocated size: 1076232192
+
 == qcow2 input image and preallocation (human) ==
 
 required size: 1074135040
@@ -114,6 +119,11 @@ converted image file size in bytes: 524288
 required size: 1074135040
 fully allocated size: 1074135040
 
+== raw input image and LUKS encryption ==
+
+required size: 2686976
+fully allocated size: 1076232192
+
 == raw input image and preallocation (human) ==
 
 required size: 1074135040
@@ -205,6 +215,13 @@ converted image file size in bytes: 458752
     "fully-allocated": 1074135040
 }
 
+== qcow2 input image and LUKS encryption ==
+
+{
+    "required": 2686976,
+    "fully-allocated": 1076232192
+}
+
 == qcow2 input image and preallocation (json) ==
 
 {
@@ -263,6 +280,13 @@ converted image file size in bytes: 524288
     "fully-allocated": 1074135040
 }
 
+== raw input image and LUKS encryption ==
+
+{
+    "required": 2686976,
+    "fully-allocated": 1076232192
+}
+
 == raw input image and preallocation (json) ==
 
 {
diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out
index 91f4db55d3..0f1c23babb 100644
--- a/tests/qemu-iotests/206.out
+++ b/tests/qemu-iotests/206.out
@@ -1,13 +1,13 @@
 === Successful image creation (defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "node-name": "imgfile"}}
 {"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "imgfile", "size": 134217728}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "imgfile", "size": 134217728}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -24,12 +24,12 @@ Format specific information:
 
 === Successful image creation (inline blockdev-add, explicit defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": false, "preallocation": "off", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": false, "preallocation": "off", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 65536, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": false, "preallocation": "off", "refcount-bits": 16, "size": 67108864, "version": "v3"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 65536, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": false, "preallocation": "off", "refcount-bits": 16, "size": 67108864, "version": "v3"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -46,12 +46,12 @@ Format specific information:
 
 === Successful image creation (v3 non-default options) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": true, "preallocation": "falloc", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": true, "preallocation": "falloc", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 2097152, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": true, "preallocation": "metadata", "refcount-bits": 1, "size": 33554432, "version": "v3"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 2097152, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": true, "preallocation": "metadata", "refcount-bits": 1, "size": 33554432, "version": "v3"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -68,12 +68,12 @@ Format specific information:
 
 === Successful image creation (v2 non-default options) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"backing-file": "TEST_DIR/PID-t.qcow2.base", "backing-fmt": "qcow2", "cluster-size": 512, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432, "version": "v2"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"backing-file": "TEST_DIR/PID-t.qcow2.base", "backing-fmt": "qcow2", "cluster-size": 512, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432, "version": "v2"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -90,7 +90,7 @@ Format specific information:
 
 === Successful image creation (encrypted) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "encrypt": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "format": "luks", "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0"}, "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "encrypt": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "format": "luks", "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0"}, "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -144,111 +144,111 @@ Format specific information:
 
 === Invalid BlockdevRef ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "this doesn't exist", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "this doesn't exist", "size": 33554432}}}
 {"return": {}}
 Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
 === Invalid sizes ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 1234}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 1234}}}
 {"return": {}}
 Job failed: Image size must be a multiple of 512 bytes
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 18446744073709551104}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 18446744073709551104}}}
 {"return": {}}
 Job failed: Could not resize image: Image size cannot be negative
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775808}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775808}}}
 {"return": {}}
 Job failed: Could not resize image: Image size cannot be negative
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775296}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775296}}}
 {"return": {}}
 Job failed: Could not resize image: Failed to grow the L1 table: File too large
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
 === Invalid version ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 67108864, "version": "v1"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 67108864, "version": "v1"}}}
 {"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "lazy-refcounts": true, "size": 67108864, "version": "v2"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "lazy-refcounts": true, "size": 67108864, "version": "v2"}}}
 {"return": {}}
 Job failed: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 8, "size": 67108864, "version": "v2"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 8, "size": 67108864, "version": "v2"}}}
 {"return": {}}
 Job failed: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
 === Invalid backing file options ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"backing-file": "/dev/null", "driver": "qcow2", "file": "node0", "preallocation": "full", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"backing-file": "/dev/null", "driver": "qcow2", "file": "node0", "preallocation": "full", "size": 67108864}}}
 {"return": {}}
 Job failed: Backing file and preallocation cannot be used at the same time
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"backing-fmt": "qcow2", "driver": "qcow2", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"backing-fmt": "qcow2", "driver": "qcow2", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Backing format cannot be used without backing file
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
 === Invalid cluster size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 1234, "driver": "qcow2", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 1234, "driver": "qcow2", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Cluster size must be a power of two between 512 and 2048k
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 128, "driver": "qcow2", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 128, "driver": "qcow2", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Cluster size must be a power of two between 512 and 2048k
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 4194304, "driver": "qcow2", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 4194304, "driver": "qcow2", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Cluster size must be a power of two between 512 and 2048k
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 0, "driver": "qcow2", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 0, "driver": "qcow2", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Cluster size must be a power of two between 512 and 2048k
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 512, "driver": "qcow2", "file": "node0", "size": 281474976710656}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 512, "driver": "qcow2", "file": "node0", "size": 281474976710656}}}
 {"return": {}}
 Job failed: Could not resize image: Failed to grow the L1 table: File too large
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
 === Invalid refcount width ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 128, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 128, "size": 67108864}}}
 {"return": {}}
 Job failed: Refcount width must be a power of two and may not exceed 64 bits
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 0, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 0, "size": 67108864}}}
 {"return": {}}
 Job failed: Refcount width must be a power of two and may not exceed 64 bits
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 7, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 7, "size": 67108864}}}
 {"return": {}}
 Job failed: Refcount width must be a power of two and may not exceed 64 bits
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
index c617ee7453..dfd3c51bd1 100755
--- a/tests/qemu-iotests/207
+++ b/tests/qemu-iotests/207
@@ -27,12 +27,16 @@ import re
 iotests.verify_image_format(supported_fmts=['raw'])
 iotests.verify_protocol(supported=['ssh'])
 
-def filter_hash(msg):
-    return re.sub('"hash": "[0-9a-f]+"', '"hash": HASH', msg)
+def filter_hash(qmsg):
+    def _filter(key, value):
+        if key == 'hash' and re.match('[0-9a-f]+', value):
+            return 'HASH'
+        return value
+    return iotests.filter_qmp(qmsg, _filter)
 
 def blockdev_create(vm, options):
     result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
-                        filters=[iotests.filter_testfiles, filter_hash])
+                        filters=[iotests.filter_qmp_testfiles, filter_hash])
 
     if 'return' in result:
         assert result['return'] == {}
diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out
index 45ac7c2a8f..568e8619d0 100644
--- a/tests/qemu-iotests/207.out
+++ b/tests/qemu-iotests/207.out
@@ -1,6 +1,6 @@
 === Successful image creation (defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -16,7 +16,7 @@ virtual size: 4.0M (4194304 bytes)
 
 === Test host-key-check options ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -25,7 +25,7 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po
 file format: IMGFMT
 virtual size: 8.0M (8388608 bytes)
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "known_hosts"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "known_hosts"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -34,13 +34,13 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po
 file format: IMGFMT
 virtual size: 4.0M (4194304 bytes)
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
 {"return": {}}
 Job failed: remote host key does not match host_key_check 'wrong'
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": HASH, "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "HASH", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -49,13 +49,13 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po
 file format: IMGFMT
 virtual size: 8.0M (8388608 bytes)
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
 {"return": {}}
 Job failed: remote host key does not match host_key_check 'wrong'
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": HASH, "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "HASH", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -66,13 +66,13 @@ virtual size: 4.0M (4194304 bytes)
 
 === Invalid path and user ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "/this/is/not/an/existing/path", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "/this/is/not/an/existing/path", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
 {"return": {}}
 Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}, "user": "invalid user"}, "size": 4194304}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}, "user": "invalid user"}, "size": 4194304}}}
 {"return": {}}
 Job failed: failed to authenticate using publickey authentication and the identities held by your ssh-agent
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210
index d142841e2b..565e3b7b9b 100755
--- a/tests/qemu-iotests/210
+++ b/tests/qemu-iotests/210
@@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['luks'])
 iotests.verify_protocol(supported=['file'])
 
 def blockdev_create(vm, options):
-    result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+    result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
+                        filters=[iotests.filter_qmp_testfiles])
 
     if 'return' in result:
         assert result['return'] == {}
@@ -53,7 +54,7 @@ with iotests.FilePath('t.luks') as disk_path, \
                           'size': 0 })
 
     vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
-               node_name='imgfile')
+               node_name='imgfile', filters=[iotests.filter_qmp_testfiles])
 
     blockdev_create(vm, { 'driver': imgfmt,
                           'file': 'imgfile',
diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out
index 923cb05117..a3692ce00d 100644
--- a/tests/qemu-iotests/210.out
+++ b/tests/qemu-iotests/210.out
@@ -1,13 +1,13 @@
 === Successful image creation (defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "node-name": "imgfile"}}
 {"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "imgfile", "iter-time": 10, "key-secret": "keysec0", "size": 134217728}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "imgfile", "iter-time": 10, "key-secret": "keysec0", "size": 134217728}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -54,12 +54,12 @@ Format specific information:
 
 === Successful image creation (with non-default options) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "driver": "luks", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.luks"}, "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "driver": "luks", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.luks"}, "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0", "size": 67108864}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -106,7 +106,7 @@ Format specific information:
 
 === Invalid BlockdevRef ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "this doesn't exist", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "this doesn't exist", "size": 67108864}}}
 {"return": {}}
 Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -114,7 +114,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi
 
 === Zero size ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "iter-time": 10, "key-secret": "keysec0", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "iter-time": 10, "key-secret": "keysec0", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -161,19 +161,19 @@ Format specific information:
 
 === Invalid sizes ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 18446744073709551104}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 18446744073709551104}}}
 {"return": {}}
 Job failed: The requested file size is too large
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775808}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775808}}}
 {"return": {}}
 Job failed: The requested file size is too large
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775296}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775296}}}
 {"return": {}}
 Job failed: The requested file size is too large
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -181,13 +181,13 @@ Job failed: The requested file size is too large
 
 === Resize image with invalid sizes ===
 
-{"execute": "block_resize", "arguments": {"node_name": "node1", "size": 9223372036854775296}}
+{"execute": "block_resize", "arguments": {"node-name": "node1", "size": 9223372036854775296}}
 {"error": {"class": "GenericError", "desc": "The requested file size is too large"}}
-{"execute": "block_resize", "arguments": {"node_name": "node1", "size": 9223372036854775808}}
+{"execute": "block_resize", "arguments": {"node-name": "node1", "size": 9223372036854775808}}
 {"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}}
-{"execute": "block_resize", "arguments": {"node_name": "node1", "size": 18446744073709551104}}
+{"execute": "block_resize", "arguments": {"node-name": "node1", "size": 18446744073709551104}}
 {"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}}
-{"execute": "block_resize", "arguments": {"node_name": "node1", "size": -9223372036854775808}}
+{"execute": "block_resize", "arguments": {"node-name": "node1", "size": -9223372036854775808}}
 {"error": {"class": "GenericError", "desc": "Parameter 'size' expects a >0 size"}}
 image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"}
 file format: IMGFMT
diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211
index 7b7985db6c..6afc894f76 100755
--- a/tests/qemu-iotests/211
+++ b/tests/qemu-iotests/211
@@ -27,11 +27,14 @@ iotests.verify_image_format(supported_fmts=['vdi'])
 iotests.verify_protocol(supported=['file'])
 
 def blockdev_create(vm, options):
-    result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+    result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
+                        filters=[iotests.filter_qmp_testfiles])
 
     if 'return' in result:
         assert result['return'] == {}
-        vm.run_job('job0')
+        error = vm.run_job('job0')
+        if error and 'Could not allocate bmap' in error:
+            iotests.notrun('Insufficient memory')
     iotests.log("")
 
 with iotests.FilePath('t.vdi') as disk_path, \
@@ -51,7 +54,7 @@ with iotests.FilePath('t.vdi') as disk_path, \
                           'size': 0 })
 
     vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
-               node_name='imgfile')
+               node_name='imgfile', filters=[iotests.filter_qmp_testfiles])
 
     blockdev_create(vm, { 'driver': imgfmt,
                           'file': 'imgfile',
diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out
index eebb0ea086..682adc2a10 100644
--- a/tests/qemu-iotests/211.out
+++ b/tests/qemu-iotests/211.out
@@ -1,13 +1,13 @@
 === Successful image creation (defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "node-name": "imgfile"}}
 {"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "imgfile", "size": 134217728}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "imgfile", "size": 134217728}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -21,12 +21,12 @@ cluster_size: 1048576
 
 === Successful image creation (explicit defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "off", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "off", "size": 67108864}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -40,12 +40,12 @@ cluster_size: 1048576
 
 === Successful image creation (with non-default options) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "metadata", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "metadata", "size": 33554432}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -60,7 +60,7 @@ cluster_size: 1048576
 
 === Invalid BlockdevRef ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "this doesn't exist", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "this doesn't exist", "size": 33554432}}}
 {"return": {}}
 Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -68,7 +68,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi
 
 === Zero size ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -80,7 +80,7 @@ cluster_size: 1048576
 
 === Maximum size ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203584}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203584}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -92,19 +92,19 @@ cluster_size: 1048576
 
 === Invalid sizes ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 18446744073709551104}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 18446744073709551104}}}
 {"return": {}}
 Job failed: Unsupported VDI image size (size is 0xfffffffffffffe00, max supported is 0x1fffff8000000)
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 9223372036854775808}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 9223372036854775808}}}
 {"return": {}}
 Job failed: Unsupported VDI image size (size is 0x8000000000000000, max supported is 0x1fffff8000000)
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203585}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203585}}}
 {"return": {}}
 Job failed: Unsupported VDI image size (size is 0x1fffff8000001, max supported is 0x1fffff8000000)
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212
index 95c8810d83..42b74f208b 100755
--- a/tests/qemu-iotests/212
+++ b/tests/qemu-iotests/212
@@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['parallels'])
 iotests.verify_protocol(supported=['file'])
 
 def blockdev_create(vm, options):
-    result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+    result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
+                        filters=[iotests.filter_qmp_testfiles])
 
     if 'return' in result:
         assert result['return'] == {}
@@ -51,7 +52,7 @@ with iotests.FilePath('t.parallels') as disk_path, \
                           'size': 0 })
 
     vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
-               node_name='imgfile')
+               node_name='imgfile', filters=[iotests.filter_qmp_testfiles])
 
     blockdev_create(vm, { 'driver': imgfmt,
                           'file': 'imgfile',
diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out
index 01da467282..22810720cf 100644
--- a/tests/qemu-iotests/212.out
+++ b/tests/qemu-iotests/212.out
@@ -1,13 +1,13 @@
 === Successful image creation (defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "node-name": "imgfile"}}
 {"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "imgfile", "size": 134217728}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "imgfile", "size": 134217728}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -18,12 +18,12 @@ virtual size: 128M (134217728 bytes)
 
 === Successful image creation (explicit defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 1048576, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 1048576, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 67108864}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -34,12 +34,12 @@ virtual size: 64M (67108864 bytes)
 
 === Successful image creation (with non-default options) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 65536, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 65536, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 33554432}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -50,7 +50,7 @@ virtual size: 32M (33554432 bytes)
 
 === Invalid BlockdevRef ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "this doesn't exist", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "this doesn't exist", "size": 33554432}}}
 {"return": {}}
 Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -58,7 +58,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi
 
 === Zero size ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -69,7 +69,7 @@ virtual size: 0 (0 bytes)
 
 === Maximum size ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627369984}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627369984}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -80,31 +80,31 @@ virtual size: 4096T (4503599627369984 bytes)
 
 === Invalid sizes ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 1234}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 1234}}}
 {"return": {}}
 Job failed: Image size must be a multiple of 512 bytes
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 18446744073709551104}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 18446744073709551104}}}
 {"return": {}}
 Job failed: Image size is too large for this cluster size
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775808}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775808}}}
 {"return": {}}
 Job failed: Image size is too large for this cluster size
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775296}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775296}}}
 {"return": {}}
 Job failed: Image size is too large for this cluster size
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627370497}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627370497}}}
 {"return": {}}
 Job failed: Image size is too large for this cluster size
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -112,43 +112,43 @@ Job failed: Image size is too large for this cluster size
 
 === Invalid cluster size ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 1234, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 1234, "driver": "parallels", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Cluster size must be a multiple of 512 bytes
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 128, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 128, "driver": "parallels", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Cluster size must be a multiple of 512 bytes
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 4294967296, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 4294967296, "driver": "parallels", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Cluster size is too large
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 9223372036854775808, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 9223372036854775808, "driver": "parallels", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Cluster size is too large
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 18446744073709551104, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 18446744073709551104, "driver": "parallels", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Cluster size is too large
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 0, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 0, "driver": "parallels", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Image size is too large for this cluster size
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 512, "driver": "parallels", "file": "node0", "size": 281474976710656}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 512, "driver": "parallels", "file": "node0", "size": 281474976710656}}}
 {"return": {}}
 Job failed: Image size is too large for this cluster size
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213
index 4054439e3c..5604f3cebb 100755
--- a/tests/qemu-iotests/213
+++ b/tests/qemu-iotests/213
@@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['vhdx'])
 iotests.verify_protocol(supported=['file'])
 
 def blockdev_create(vm, options):
-    result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+    result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
+                        filters=[iotests.filter_qmp_testfiles])
 
     if 'return' in result:
         assert result['return'] == {}
@@ -51,7 +52,7 @@ with iotests.FilePath('t.vhdx') as disk_path, \
                           'size': 0 })
 
     vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
-               node_name='imgfile')
+               node_name='imgfile', filters=[iotests.filter_qmp_testfiles])
 
     blockdev_create(vm, { 'driver': imgfmt,
                           'file': 'imgfile',
diff --git a/tests/qemu-iotests/213.out b/tests/qemu-iotests/213.out
index 0c9d65b2fe..169083e08e 100644
--- a/tests/qemu-iotests/213.out
+++ b/tests/qemu-iotests/213.out
@@ -1,13 +1,13 @@
 === Successful image creation (defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "node-name": "imgfile"}}
 {"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "imgfile", "size": 134217728}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "imgfile", "size": 134217728}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -19,12 +19,12 @@ cluster_size: 8388608
 
 === Successful image creation (explicit defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 8388608, "block-state-zero": true, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 1048576, "size": 67108864, "subformat": "dynamic"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 8388608, "block-state-zero": true, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 1048576, "size": 67108864, "subformat": "dynamic"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -36,12 +36,12 @@ cluster_size: 8388608
 
 === Successful image creation (with non-default options) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 268435456, "block-state-zero": false, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 8388608, "size": 33554432, "subformat": "fixed"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 268435456, "block-state-zero": false, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 8388608, "size": 33554432, "subformat": "fixed"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -53,7 +53,7 @@ cluster_size: 268435456
 
 === Invalid BlockdevRef ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "this doesn't exist", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "this doesn't exist", "size": 33554432}}}
 {"return": {}}
 Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -61,7 +61,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi
 
 === Zero size ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -73,7 +73,7 @@ cluster_size: 8388608
 
 === Maximum size ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177664}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177664}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -85,25 +85,25 @@ cluster_size: 67108864
 
 === Invalid sizes ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 18446744073709551104}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 18446744073709551104}}}
 {"return": {}}
 Job failed: Image size too large; max of 64TB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775808}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775808}}}
 {"return": {}}
 Job failed: Image size too large; max of 64TB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775296}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775296}}}
 {"return": {}}
 Job failed: Image size too large; max of 64TB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177665}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177665}}}
 {"return": {}}
 Job failed: Image size too large; max of 64TB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -111,31 +111,31 @@ Job failed: Image size too large; max of 64TB
 
 === Invalid block size ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 1234567, "driver": "vhdx", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 1234567, "driver": "vhdx", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Block size must be a multiple of 1 MB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 128, "driver": "vhdx", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 128, "driver": "vhdx", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Block size must be a multiple of 1 MB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 3145728, "driver": "vhdx", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 3145728, "driver": "vhdx", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Block size must be a power of two
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 536870912, "driver": "vhdx", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 536870912, "driver": "vhdx", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Block size must not exceed 268435456
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 0, "driver": "vhdx", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 0, "driver": "vhdx", "file": "node0", "size": 67108864}}}
 {"return": {}}
 Job failed: Block size must be a multiple of 1 MB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -143,25 +143,25 @@ Job failed: Block size must be a multiple of 1 MB
 
 === Invalid log size ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 1234567, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 1234567, "size": 67108864}}}
 {"return": {}}
 Job failed: Log size must be a multiple of 1 MB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 128, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 128, "size": 67108864}}}
 {"return": {}}
 Job failed: Log size must be a multiple of 1 MB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 4294967296, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 4294967296, "size": 67108864}}}
 {"return": {}}
 Job failed: Log size must be smaller than 4 GB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 0, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 0, "size": 67108864}}}
 {"return": {}}
 Job failed: Log size must be a multiple of 1 MB
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
diff --git a/tests/qemu-iotests/224 b/tests/qemu-iotests/224
new file mode 100755
index 0000000000..b4dfaa639f
--- /dev/null
+++ b/tests/qemu-iotests/224
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+#
+# Test json:{} filenames with qemu-internal BDSs
+# (the one of commit, to be precise)
+#
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Creator/Owner: Max Reitz <mreitz@redhat.com>
+
+import iotests
+from iotests import log, qemu_img, qemu_io_silent, filter_qmp_testfiles, \
+                    filter_qmp_imgfmt
+import json
+
+# Need backing file support (for arbitrary backing formats)
+iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed'])
+iotests.verify_platform(['linux'])
+
+
+# There are two variations of this test:
+# (1) We do not set filter_node_name.  In that case, the commit_top
+#     driver should not appear anywhere.
+# (2) We do set filter_node_name.  In that case, it should appear.
+#
+# This for loop executes both.
+for filter_node_name in False, True:
+    log('')
+    log('--- filter_node_name: %s ---' % filter_node_name)
+    log('')
+
+    with iotests.FilePath('base.img') as base_img_path, \
+         iotests.FilePath('mid.img') as mid_img_path, \
+         iotests.FilePath('top.img') as top_img_path, \
+         iotests.VM() as vm:
+
+        assert qemu_img('create', '-f', iotests.imgfmt,
+                        base_img_path, '64M') == 0
+        assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
+                        mid_img_path) == 0
+        assert qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path,
+                        top_img_path) == 0
+
+        # Something to commit
+        assert qemu_io_silent(mid_img_path, '-c', 'write -P 1 0 1M') == 0
+
+        vm.launch()
+
+        # Change the bottom-most image's backing file (to null-co://)
+        # to enforce json:{} filenames
+        vm.qmp_log('blockdev-add',
+                    node_name='top',
+                    driver=iotests.imgfmt,
+                    file={
+                        'driver': 'file',
+                        'filename': top_img_path
+                    },
+                    backing={
+                        'node-name': 'mid',
+                        'driver': iotests.imgfmt,
+                        'file': {
+                            'driver': 'file',
+                            'filename': mid_img_path
+                        },
+                        'backing': {
+                            'node-name': 'base',
+                            'driver': iotests.imgfmt,
+                            'file': {
+                                'driver': 'file',
+                                'filename': base_img_path
+                            },
+                            'backing': {
+                                'driver': 'null-co'
+                            }
+                        }
+                    },
+                    filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+        # As long as block-commit does not accept node names, we have to
+        # get our mid/base filenames here
+        mid_name = vm.node_info('mid')['image']['filename']
+        base_name = vm.node_info('base')['image']['filename']
+
+        assert mid_name[:5] == 'json:'
+        assert base_name[:5] == 'json:'
+
+        # Start the block job
+        if filter_node_name:
+            vm.qmp_log('block-commit',
+                        job_id='commit',
+                        device='top',
+                        filter_node_name='filter_node',
+                        top=mid_name,
+                        base=base_name,
+                        speed=1,
+                        filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+        else:
+            vm.qmp_log('block-commit',
+                        job_id='commit',
+                        device='top',
+                        top=mid_name,
+                        base=base_name,
+                        speed=1,
+                        filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+        vm.qmp_log('job-pause', id='commit')
+
+        # Get and parse top's json:{} filename
+        top_name = vm.node_info('top')['image']['filename']
+
+        vm.shutdown()
+
+        assert top_name[:5] == 'json:'
+        top_options = json.loads(top_name[5:])
+
+        if filter_node_name:
+            # This should be present and set
+            assert top_options['backing']['driver'] == 'commit_top'
+            # And the mid image is commit_top's backing image
+            mid_options = top_options['backing']['backing']
+        else:
+            # The mid image should appear as the immediate backing BDS
+            # of top
+            mid_options = top_options['backing']
+
+        assert mid_options['driver'] == iotests.imgfmt
+        assert mid_options['file']['filename'] == mid_img_path
diff --git a/tests/qemu-iotests/224.out b/tests/qemu-iotests/224.out
new file mode 100644
index 0000000000..23374a1d29
--- /dev/null
+++ b/tests/qemu-iotests/224.out
@@ -0,0 +1,18 @@
+
+--- filter_node_name: False ---
+
+{"execute": "blockdev-add", "arguments": {"backing": {"backing": {"backing": {"driver": "null-co"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}, "node-name": "base"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-mid.img"}, "node-name": "mid"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "top"}}
+{"return": {}}
+{"execute": "block-commit", "arguments": {"base": "json:{\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}", "device": "top", "job-id": "commit", "speed": 1, "top": "json:{\"backing\": {\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-mid.img\"}}"}}
+{"return": {}}
+{"execute": "job-pause", "arguments": {"id": "commit"}}
+{"return": {}}
+
+--- filter_node_name: True ---
+
+{"execute": "blockdev-add", "arguments": {"backing": {"backing": {"backing": {"driver": "null-co"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}, "node-name": "base"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-mid.img"}, "node-name": "mid"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "top"}}
+{"return": {}}
+{"execute": "block-commit", "arguments": {"base": "json:{\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}", "device": "top", "filter-node-name": "filter_node", "job-id": "commit", "speed": 1, "top": "json:{\"backing\": {\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-mid.img\"}}"}}
+{"return": {}}
+{"execute": "job-pause", "arguments": {"id": "commit"}}
+{"return": {}}
diff --git a/tests/qemu-iotests/228 b/tests/qemu-iotests/228
new file mode 100755
index 0000000000..9a50afd205
--- /dev/null
+++ b/tests/qemu-iotests/228
@@ -0,0 +1,239 @@
+#!/usr/bin/env python
+#
+# Test for when a backing file is considered overridden (thus, a
+# json:{} filename is generated for the overlay) and when it is not
+#
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Creator/Owner: Max Reitz <mreitz@redhat.com>
+
+import iotests
+from iotests import log, qemu_img, filter_testfiles, filter_imgfmt, \
+        filter_qmp_testfiles, filter_qmp_imgfmt
+
+# Need backing file and change-backing-file support
+iotests.verify_image_format(supported_fmts=['qcow2', 'qed'])
+iotests.verify_platform(['linux'])
+
+
+def log_node_info(node):
+    log('')
+
+    log('bs->filename: ' + node['image']['filename'],
+        filters=[filter_testfiles, filter_imgfmt])
+    log('bs->backing_file: ' + node['backing_file'],
+        filters=[filter_testfiles, filter_imgfmt])
+
+    if 'backing-image' in node['image']:
+        log('bs->backing->bs->filename: ' +
+            node['image']['backing-image']['filename'],
+            filters=[filter_testfiles, filter_imgfmt])
+    else:
+        log('bs->backing: (none)')
+
+    log('')
+
+
+with iotests.FilePath('base.img') as base_img_path, \
+     iotests.FilePath('top.img') as top_img_path, \
+     iotests.VM() as vm:
+
+    assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
+    # Choose a funny way to describe the backing filename
+    assert qemu_img('create', '-f', iotests.imgfmt, '-b',
+                    'file:' + base_img_path, top_img_path) == 0
+
+    vm.launch()
+
+    log('--- Implicit backing file ---')
+    log('')
+
+    vm.qmp_log('blockdev-add',
+                node_name='node0',
+                driver=iotests.imgfmt,
+                file={
+                    'driver': 'file',
+                    'filename': top_img_path
+                },
+                filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+    # Filename should be plain, and the backing filename should not
+    # contain the "file:" prefix
+    log_node_info(vm.node_info('node0'))
+
+    vm.qmp_log('blockdev-del', node_name='node0')
+
+    log('')
+    log('--- change-backing-file ---')
+    log('')
+
+    vm.qmp_log('blockdev-add',
+               node_name='node0',
+               driver=iotests.imgfmt,
+               file={
+                   'driver': 'file',
+                   'filename': top_img_path
+               },
+               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+    # Changing the backing file to a qemu-reported filename should
+    # result in qemu accepting the corresponding BDS as the implicit
+    # backing BDS (and thus not generate a json:{} filename).
+    # So, first, query the backing filename.
+
+    backing_filename = \
+        vm.node_info('node0')['image']['backing-image']['filename']
+
+    # Next, change the backing file to something different
+
+    vm.qmp_log('change-backing-file',
+               image_node_name='node0',
+               device='node0',
+               backing_file='null-co://',
+               filters=[filter_qmp_testfiles])
+
+    # Now, verify that we get a json:{} filename
+    # (Image header says "null-co://", actual backing file still is
+    # base_img_path)
+
+    log_node_info(vm.node_info('node0'))
+
+    # Change it back
+    # (To get header and backing file in sync)
+
+    vm.qmp_log('change-backing-file',
+               image_node_name='node0',
+               device='node0',
+               backing_file=backing_filename,
+               filters=[filter_qmp_testfiles])
+
+    # And verify that we get our original results
+
+    log_node_info(vm.node_info('node0'))
+
+    # Finally, try a "file:" prefix.  While this is actually what we
+    # originally had in the image header, qemu will not reopen the
+    # backing file here, so it cannot verify that this filename
+    # "resolves" to the actual backing BDS's filename and will thus
+    # consider both to be different.
+    # (This may be fixed in the future.)
+
+    vm.qmp_log('change-backing-file',
+               image_node_name='node0',
+               device='node0',
+               backing_file=('file:' + backing_filename),
+               filters=[filter_qmp_testfiles])
+
+    # So now we should get a json:{} filename
+
+    log_node_info(vm.node_info('node0'))
+
+    # Remove and re-attach so we can see that (as in our first try),
+    # opening the image anew helps qemu resolve the header backing
+    # filename.
+
+    vm.qmp_log('blockdev-del', node_name='node0')
+
+    vm.qmp_log('blockdev-add',
+               node_name='node0',
+               driver=iotests.imgfmt,
+               file={
+                   'driver': 'file',
+                   'filename': top_img_path
+               },
+               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+    log_node_info(vm.node_info('node0'))
+
+    vm.qmp_log('blockdev-del', node_name='node0')
+
+    log('')
+    log('--- Override backing file ---')
+    log('')
+
+    # For this test, we need the plain filename in the image header
+    # (because qemu cannot "canonicalize"/"resolve" the backing
+    # filename unless the backing file is opened implicitly with the
+    # overlay)
+    assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
+                    top_img_path) == 0
+
+    # You can only reliably override backing options by using a node
+    # reference (or by specifying file.filename, but, well...)
+    vm.qmp_log('blockdev-add', node_name='null', driver='null-co')
+
+    vm.qmp_log('blockdev-add',
+               node_name='node0',
+               driver=iotests.imgfmt,
+               file={
+                   'driver': 'file',
+                   'filename': top_img_path
+               },
+               backing='null',
+               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+    # Should get a json:{} filename (and bs->backing_file is
+    # null-co://, because that field actually has not much to do
+    # with the header backing filename (except that it is changed by
+    # change-backing-file))
+
+    log_node_info(vm.node_info('node0'))
+
+    # Detach the backing file by reopening the whole thing
+
+    vm.qmp_log('blockdev-del', node_name='node0')
+    vm.qmp_log('blockdev-del', node_name='null')
+
+    vm.qmp_log('blockdev-add',
+               node_name='node0',
+               driver=iotests.imgfmt,
+               file={
+                   'driver': 'file',
+                   'filename': top_img_path
+               },
+               backing=None,
+               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+    # Should get a json:{} filename (because we overrode the backing
+    # file to not be there)
+
+    log_node_info(vm.node_info('node0'))
+
+    # Open the original backing file
+
+    vm.qmp_log('blockdev-add',
+               node_name='original-backing',
+               driver=iotests.imgfmt,
+               file={
+                   'driver': 'file',
+                   'filename': base_img_path
+               },
+               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+    # Attach the original backing file to its overlay
+
+    vm.qmp_log('blockdev-snapshot',
+               node='original-backing',
+               overlay='node0')
+
+    # This should give us the original plain result
+
+    log_node_info(vm.node_info('node0'))
+
+    vm.qmp_log('blockdev-del', node_name='node0')
+    vm.qmp_log('blockdev-del', node_name='original-backing')
+
+    vm.shutdown()
diff --git a/tests/qemu-iotests/228.out b/tests/qemu-iotests/228.out
new file mode 100644
index 0000000000..4217df24fe
--- /dev/null
+++ b/tests/qemu-iotests/228.out
@@ -0,0 +1,84 @@
+--- Implicit backing file ---
+
+{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}}
+{"return": {}}
+
+bs->filename: TEST_DIR/PID-top.img
+bs->backing_file: TEST_DIR/PID-base.img
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "blockdev-del", "arguments": {"node-name": "node0"}}
+{"return": {}}
+
+--- change-backing-file ---
+
+{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}}
+{"return": {}}
+{"execute": "change-backing-file", "arguments": {"backing-file": "null-co://", "device": "node0", "image-node-name": "node0"}}
+{"return": {}}
+
+bs->filename: json:{"backing": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}}
+bs->backing_file: null-co://
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "change-backing-file", "arguments": {"backing-file": "TEST_DIR/PID-base.img", "device": "node0", "image-node-name": "node0"}}
+{"return": {}}
+
+bs->filename: TEST_DIR/PID-top.img
+bs->backing_file: TEST_DIR/PID-base.img
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "change-backing-file", "arguments": {"backing-file": "file:TEST_DIR/PID-base.img", "device": "node0", "image-node-name": "node0"}}
+{"return": {}}
+
+bs->filename: json:{"backing": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}}
+bs->backing_file: file:TEST_DIR/PID-base.img
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "blockdev-del", "arguments": {"node-name": "node0"}}
+{"return": {}}
+{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}}
+{"return": {}}
+
+bs->filename: TEST_DIR/PID-top.img
+bs->backing_file: TEST_DIR/PID-base.img
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "blockdev-del", "arguments": {"node-name": "node0"}}
+{"return": {}}
+
+--- Override backing file ---
+
+{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "null"}}
+{"return": {}}
+{"execute": "blockdev-add", "arguments": {"backing": "null", "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}}
+{"return": {}}
+
+bs->filename: json:{"backing": {"driver": "null-co"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}}
+bs->backing_file: null-co://
+bs->backing->bs->filename: null-co://
+
+{"execute": "blockdev-del", "arguments": {"node-name": "node0"}}
+{"return": {}}
+{"execute": "blockdev-del", "arguments": {"node-name": "null"}}
+{"return": {}}
+{"execute": "blockdev-add", "arguments": {"backing": null, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}}
+{"return": {}}
+
+bs->filename: json:{"backing": null, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}}
+bs->backing_file: TEST_DIR/PID-base.img
+bs->backing: (none)
+
+{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}, "node-name": "original-backing"}}
+{"return": {}}
+{"execute": "blockdev-snapshot", "arguments": {"node": "original-backing", "overlay": "node0"}}
+{"return": {}}
+
+bs->filename: TEST_DIR/PID-top.img
+bs->backing_file: TEST_DIR/PID-base.img
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "blockdev-del", "arguments": {"node-name": "node0"}}
+{"return": {}}
+{"execute": "blockdev-del", "arguments": {"node-name": "original-backing"}}
+{"return": {}}
diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232
index 0708b8b155..e48bc8f5db 100755
--- a/tests/qemu-iotests/232
+++ b/tests/qemu-iotests/232
@@ -29,7 +29,6 @@ status=1	# failure is the default!
 _cleanup()
 {
     _cleanup_test_img
-    rm -f $TEST_IMG.snap
 }
 trap "_cleanup; exit \$status" 0 1 2 3 15
 
@@ -70,6 +69,10 @@ size=128M
 
 _make_test_img $size
 
+if [ -n "$TEST_IMG_FILE" ]; then
+    TEST_IMG=$TEST_IMG_FILE
+fi
+
 echo
 echo "=== -drive with read-write image: read-only/auto-read-only combinations ==="
 echo
diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233
index fc345a1a46..adb742fafb 100755
--- a/tests/qemu-iotests/233
+++ b/tests/qemu-iotests/233
@@ -30,6 +30,8 @@ _cleanup()
 {
     nbd_server_stop
     _cleanup_test_img
+    # If we aborted early we want to see this log for diagnosis
+    test -f "$TEST_DIR/server.log" && cat "$TEST_DIR/server.log"
     rm -f "$TEST_DIR/server.log"
     tls_x509_cleanup
 }
@@ -120,6 +122,7 @@ $QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io
 echo
 echo "== final server log =="
 cat "$TEST_DIR/server.log"
+rm -f "$TEST_DIR/server.log"
 
 # success, all done
 echo "*** done"
diff --git a/tests/qemu-iotests/237 b/tests/qemu-iotests/237
index 251771d7fb..06897f8c87 100755
--- a/tests/qemu-iotests/237
+++ b/tests/qemu-iotests/237
@@ -27,7 +27,8 @@ from iotests import imgfmt
 iotests.verify_image_format(supported_fmts=['vmdk'])
 
 def blockdev_create(vm, options):
-    result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+    result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
+                        filters=[iotests.filter_qmp_testfiles])
 
     if 'return' in result:
         assert result['return'] == {}
@@ -54,7 +55,7 @@ with iotests.FilePath('t.vmdk') as disk_path, \
                           'size': 0 })
 
     vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
-               node_name='imgfile')
+               node_name='imgfile', filters=[iotests.filter_qmp_testfiles])
 
     blockdev_create(vm, { 'driver': imgfmt,
                           'file': 'imgfile',
@@ -223,7 +224,7 @@ with iotests.FilePath('t.vmdk') as disk_path, \
             iotests.log("= %s %d =" % (subfmt, size))
             iotests.log("")
 
-            num_extents = math.ceil(size / 2.0**31)
+            num_extents = int(math.ceil(size / 2.0**31))
             extents = [ "ext%d" % (i) for i in range(1, num_extents + 1) ]
 
             vm.launch()
diff --git a/tests/qemu-iotests/237.out b/tests/qemu-iotests/237.out
index 241c864369..2aaa68f672 100644
--- a/tests/qemu-iotests/237.out
+++ b/tests/qemu-iotests/237.out
@@ -1,13 +1,13 @@
 === Successful image creation (defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "node-name": "imgfile"}}
 {"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "imgfile", "size": 5368709120}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "file": "imgfile", "size": 5368709120}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -29,12 +29,12 @@ Format specific information:
 
 === Successful image creation (inline blockdev-add, explicit defaults) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "hwversion": "4", "size": 67108864, "subformat": "monolithicSparse", "zeroed-grain": false}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "hwversion": "4", "size": 67108864, "subformat": "monolithicSparse", "zeroed-grain": false}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -56,12 +56,12 @@ Format specific information:
 
 === Successful image creation (with non-default options) ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "size": 33554432, "subformat": "monolithicSparse", "zeroed-grain": true}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "size": 33554432, "subformat": "monolithicSparse", "zeroed-grain": true}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -83,7 +83,7 @@ Format specific information:
 
 === Invalid BlockdevRef ===
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "this doesn't exist", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "file": "this doesn't exist", "size": 33554432}}}
 {"return": {}}
 Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -93,38 +93,38 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi
 
 == Valid adapter types ==
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "file": "node0", "size": 33554432}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "lsilogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "lsilogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "legacyESX", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "legacyESX", "driver": "vmdk", "file": "node0", "size": 33554432}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
 == Invalid adapter types ==
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "foo", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "foo", "driver": "vmdk", "file": "node0", "size": 33554432}}}
 {"error": {"class": "GenericError", "desc": "Invalid parameter 'foo'"}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "IDE", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "IDE", "driver": "vmdk", "file": "node0", "size": 33554432}}}
 {"error": {"class": "GenericError", "desc": "Invalid parameter 'IDE'"}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "legacyesx", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "legacyesx", "driver": "vmdk", "file": "node0", "size": 33554432}}}
 {"error": {"class": "GenericError", "desc": "Invalid parameter 'legacyesx'"}}
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": 1, "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": 1, "driver": "vmdk", "file": "node0", "size": 33554432}}}
 {"error": {"class": "GenericError", "desc": "Invalid parameter type for 'options.adapter-type', expected: string"}}
 
 === Other subformats ===
@@ -137,7 +137,7 @@ Formatting 'TEST_DIR/PID-t.vmdk.3', fmt=vmdk size=0 compat6=off hwversion=undefi
 
 == Missing extent ==
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
 {"return": {}}
 Job failed: Extent [0] not specified
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -145,14 +145,14 @@ Job failed: Extent [0] not specified
 
 == Correct extent ==
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
 
 == Extra extent ==
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 512, "subformat": "monolithicFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 512, "subformat": "monolithicFlat"}}}
 {"return": {}}
 Job failed: List of extents contains unused extents
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -162,7 +162,7 @@ Job failed: List of extents contains unused extents
 
 = twoGbMaxExtentFlat 512 =
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentFlat"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -182,7 +182,7 @@ Format specific information:
 
 = twoGbMaxExtentSparse 512 =
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentSparse"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentSparse"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -204,7 +204,7 @@ Format specific information:
 
 = twoGbMaxExtentFlat 1073741824 =
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentFlat"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -224,7 +224,7 @@ Format specific information:
 
 = twoGbMaxExtentSparse 1073741824 =
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentSparse"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentSparse"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -246,7 +246,7 @@ Format specific information:
 
 = twoGbMaxExtentFlat 2147483648 =
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentFlat"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -266,7 +266,7 @@ Format specific information:
 
 = twoGbMaxExtentSparse 2147483648 =
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentSparse"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentSparse"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -288,7 +288,7 @@ Format specific information:
 
 = twoGbMaxExtentFlat 5368709120 =
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentFlat"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
@@ -316,7 +316,7 @@ Format specific information:
 
 = twoGbMaxExtentSparse 5368709120 =
 
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentSparse"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentSparse"}}}
 {"return": {}}
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
 {"return": {}}
diff --git a/tests/qemu-iotests/242 b/tests/qemu-iotests/242
index 16c65edcd7..c176e92da6 100755
--- a/tests/qemu-iotests/242
+++ b/tests/qemu-iotests/242
@@ -20,6 +20,7 @@
 
 import iotests
 import json
+import struct
 from iotests import qemu_img_create, qemu_io, qemu_img_pipe, \
     file_path, img_info_log, log, filter_qemu_io
 
@@ -64,10 +65,11 @@ def write_to_disk(offset, size):
 def toggle_flag(offset):
     with open(disk, "r+b") as f:
         f.seek(offset, 0)
-        c = f.read(1)
-        toggled = chr(ord(c) ^ bitmap_flag_unknown)
+        # Read one byte in a way compatible with Python 2
+        flags = struct.unpack("B", f.read(1))
+        toggled = flags[0] ^ bitmap_flag_unknown
         f.seek(-1, 1)
-        f.write(toggled)
+        f.write(struct.pack("B", toggled))
 
 
 qemu_img_create('-f', iotests.imgfmt, disk, '1M')
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index e15e7a7c8e..09a27f02d0 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -145,6 +145,7 @@ else
         TEST_IMG="nbd:127.0.0.1:10810"
     elif [ "$IMGPROTO" = "ssh" ]; then
         TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
+        REMOTE_TEST_DIR="ssh://127.0.0.1$TEST_DIR"
         TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE"
     elif [ "$IMGPROTO" = "nfs" ]; then
         TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
diff --git a/tests/qemu-iotests/common.tls b/tests/qemu-iotests/common.tls
index eae81789bb..3caf989d28 100644
--- a/tests/qemu-iotests/common.tls
+++ b/tests/qemu-iotests/common.tls
@@ -29,6 +29,17 @@ tls_x509_cleanup()
 }
 
 
+tls_certtool()
+{
+    certtool "$@" 1>"${tls_dir}"/certtool.log 2>&1
+    if test "$?" = 0; then
+      head -1 "${tls_dir}"/certtool.log
+    else
+      cat "${tls_dir}"/certtool.log
+    fi
+    rm -f "${tls_dir}"/certtool.log
+}
+
 tls_x509_init()
 {
     (certtool --help) >/dev/null 2>&1 || \
@@ -71,10 +82,11 @@ ca
 cert_signing_key
 EOF
 
-    certtool --generate-self-signed \
-             --load-privkey "${tls_dir}/key.pem" \
-             --template "${tls_dir}/ca.info" \
-             --outfile "${tls_dir}/$name-cert.pem" 2>&1 | head -1
+    tls_certtool \
+        --generate-self-signed \
+        --load-privkey "${tls_dir}/key.pem" \
+        --template "${tls_dir}/ca.info" \
+        --outfile "${tls_dir}/$name-cert.pem"
 
     rm -f "${tls_dir}/ca.info"
 }
@@ -98,12 +110,14 @@ encryption_key
 signing_key
 EOF
 
-    certtool --generate-certificate \
-             --load-ca-privkey "${tls_dir}/key.pem" \
-             --load-ca-certificate "${tls_dir}/$caname-cert.pem" \
-             --load-privkey "${tls_dir}/key.pem" \
-             --template "${tls_dir}/cert.info" \
-             --outfile "${tls_dir}/$name/server-cert.pem" 2>&1 | head -1
+    tls_certtool \
+        --generate-certificate \
+        --load-ca-privkey "${tls_dir}/key.pem" \
+        --load-ca-certificate "${tls_dir}/$caname-cert.pem" \
+        --load-privkey "${tls_dir}/key.pem" \
+        --template "${tls_dir}/cert.info" \
+        --outfile "${tls_dir}/$name/server-cert.pem"
+
     ln -s "${tls_dir}/$caname-cert.pem" "${tls_dir}/$name/ca-cert.pem"
     ln -s "${tls_dir}/key.pem" "${tls_dir}/$name/server-key.pem"
 
@@ -127,12 +141,14 @@ encryption_key
 signing_key
 EOF
 
-    certtool --generate-certificate \
-             --load-ca-privkey "${tls_dir}/key.pem" \
-             --load-ca-certificate "${tls_dir}/$caname-cert.pem" \
-             --load-privkey "${tls_dir}/key.pem" \
-             --template "${tls_dir}/cert.info" \
-             --outfile "${tls_dir}/$name/client-cert.pem" 2>&1 | head -1
+    tls_certtool \
+        --generate-certificate \
+        --load-ca-privkey "${tls_dir}/key.pem" \
+        --load-ca-certificate "${tls_dir}/$caname-cert.pem" \
+        --load-privkey "${tls_dir}/key.pem" \
+        --template "${tls_dir}/cert.info" \
+        --outfile "${tls_dir}/$name/client-cert.pem"
+
     ln -s "${tls_dir}/$caname-cert.pem" "${tls_dir}/$name/ca-cert.pem"
     ln -s "${tls_dir}/key.pem" "${tls_dir}/$name/client-key.pem"
 
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index fc4c416fa3..b5ca63cf72 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -224,9 +224,11 @@
 221 rw auto quick
 222 rw auto quick
 223 rw auto quick
+224 rw auto quick
 225 rw auto quick
 226 auto quick
 227 auto quick
+228 rw auto quick
 229 auto quick
 231 auto quick
 232 auto quick
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index b461f53abf..4910fb2005 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -76,14 +76,16 @@ def qemu_img(*args):
         sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
     return exitcode
 
-def ordered_qmp(qmsg):
+def ordered_qmp(qmsg, conv_keys=True):
     # Dictionaries are not ordered prior to 3.6, therefore:
     if isinstance(qmsg, list):
         return [ordered_qmp(atom) for atom in qmsg]
     if isinstance(qmsg, dict):
         od = OrderedDict()
         for k, v in sorted(qmsg.items()):
-            od[k] = ordered_qmp(v)
+            if conv_keys:
+                k = k.replace('_', '-')
+            od[k] = ordered_qmp(v, conv_keys=False)
         return od
     return qmsg
 
@@ -236,6 +238,12 @@ def image_size(img):
     r = qemu_img_pipe('info', '--output=json', '-f', imgfmt, img)
     return json.loads(r)['virtual-size']
 
+def is_str(val):
+    if sys.version_info.major >= 3:
+        return isinstance(val, str)
+    else:
+        return isinstance(val, str) or isinstance(val, unicode)
+
 test_dir_re = re.compile(r"%s" % test_dir)
 def filter_test_dir(msg):
     return test_dir_re.sub("TEST_DIR", msg)
@@ -283,7 +291,7 @@ def filter_testfiles(msg):
 
 def filter_qmp_testfiles(qmsg):
     def _filter(key, value):
-        if key == 'filename' or key == 'backing-file':
+        if is_str(value):
             return filter_testfiles(value)
         return value
     return filter_qmp(qmsg, _filter)
@@ -304,6 +312,16 @@ def filter_img_info(output, filename):
         lines.append(line)
     return '\n'.join(lines)
 
+def filter_imgfmt(msg):
+    return msg.replace(imgfmt, 'IMGFMT')
+
+def filter_qmp_imgfmt(qmsg):
+    def _filter(key, value):
+        if is_str(value):
+            return filter_imgfmt(value)
+        return value
+    return filter_qmp(qmsg, _filter)
+
 def log(msg, filters=[], indent=None):
     '''Logs either a string message or a JSON serializable message (like QMP).
     If indent is provided, JSON serializable messages are pretty-printed.'''
@@ -514,7 +532,9 @@ class VM(qtest.QEMUQtestMachine):
         log(result, filters, indent=indent)
         return result
 
+    # Returns None on success, and an error string on failure
     def run_job(self, job, auto_finalize=True, auto_dismiss=False):
+        error = None
         while True:
             for ev in self.get_qmp_events_filtered(wait=True):
                 if ev['event'] == 'JOB_STATUS_CHANGE':
@@ -523,16 +543,24 @@ class VM(qtest.QEMUQtestMachine):
                         result = self.qmp('query-jobs')
                         for j in result['return']:
                             if j['id'] == job:
+                                error = j['error']
                                 log('Job failed: %s' % (j['error']))
                     elif status == 'pending' and not auto_finalize:
                         self.qmp_log('job-finalize', id=job)
                     elif status == 'concluded' and not auto_dismiss:
                         self.qmp_log('job-dismiss', id=job)
                     elif status == 'null':
-                        return
+                        return error
                 else:
                     iotests.log(ev)
 
+    def node_info(self, node_name):
+        nodes = self.qmp('query-named-block-nodes')
+        for x in nodes['return']:
+            if x['node-name'] == node_name:
+                return x
+        return None
+
 
 index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
 
diff --git a/tests/tcg/mips/include/wrappers_msa.h b/tests/tcg/mips/include/wrappers_msa.h
index 3280f9b084..302f0ab54c 100644
--- a/tests/tcg/mips/include/wrappers_msa.h
+++ b/tests/tcg/mips/include/wrappers_msa.h
@@ -122,5 +122,35 @@ DO_MSA__WD__WS_WT(CLT_U_H, clt_u.h)
 DO_MSA__WD__WS_WT(CLT_U_W, clt_u.w)
 DO_MSA__WD__WS_WT(CLT_U_D, clt_u.d)
 
+DO_MSA__WD__WS_WT(MAX_A_B, max_a.b)
+DO_MSA__WD__WS_WT(MAX_A_H, max_a.h)
+DO_MSA__WD__WS_WT(MAX_A_W, max_a.w)
+DO_MSA__WD__WS_WT(MAX_A_D, max_a.d)
+
+DO_MSA__WD__WS_WT(MIN_A_B, min_a.b)
+DO_MSA__WD__WS_WT(MIN_A_H, min_a.h)
+DO_MSA__WD__WS_WT(MIN_A_W, min_a.w)
+DO_MSA__WD__WS_WT(MIN_A_D, min_a.d)
+
+DO_MSA__WD__WS_WT(MAX_S_B, max_s.b)
+DO_MSA__WD__WS_WT(MAX_S_H, max_s.h)
+DO_MSA__WD__WS_WT(MAX_S_W, max_s.w)
+DO_MSA__WD__WS_WT(MAX_S_D, max_s.d)
+
+DO_MSA__WD__WS_WT(MIN_S_B, min_s.b)
+DO_MSA__WD__WS_WT(MIN_S_H, min_s.h)
+DO_MSA__WD__WS_WT(MIN_S_W, min_s.w)
+DO_MSA__WD__WS_WT(MIN_S_D, min_s.d)
+
+DO_MSA__WD__WS_WT(MAX_U_B, max_u.b)
+DO_MSA__WD__WS_WT(MAX_U_H, max_u.h)
+DO_MSA__WD__WS_WT(MAX_U_W, max_u.w)
+DO_MSA__WD__WS_WT(MAX_U_D, max_u.d)
+
+DO_MSA__WD__WS_WT(MIN_U_B, min_u.b)
+DO_MSA__WD__WS_WT(MIN_U_H, min_u.h)
+DO_MSA__WD__WS_WT(MIN_U_W, min_u.w)
+DO_MSA__WD__WS_WT(MIN_U_D, min_u.d)
+
 
 #endif
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_b.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_b.c
new file mode 100644
index 0000000000..b47b42e76e
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CEQ.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CEQ.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  72  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CEQ_B(b128_pattern[i], b128_pattern[j],
+                         b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CEQ_B(b128_random[i], b128_random[j],
+                         b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                      (PATTERN_INPUTS_SHORT_COUNT)) +
+                                     RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_d.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_d.c
new file mode 100644
index 0000000000..e169bdc320
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CEQ.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CEQ.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  72  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CEQ_D(b128_pattern[i], b128_pattern[j],
+                         b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CEQ_D(b128_random[i], b128_random[j],
+                         b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                      (PATTERN_INPUTS_SHORT_COUNT)) +
+                                     RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_h.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_h.c
new file mode 100644
index 0000000000..9f03cb0b2b
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CEQ.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CEQ.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  72  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CEQ_H(b128_pattern[i], b128_pattern[j],
+                         b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CEQ_H(b128_random[i], b128_random[j],
+                         b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                      (PATTERN_INPUTS_SHORT_COUNT)) +
+                                     RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_w.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_w.c
new file mode 100644
index 0000000000..a927d96a89
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CEQ.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CEQ.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  72  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CEQ_W(b128_pattern[i], b128_pattern[j],
+                         b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CEQ_W(b128_random[i], b128_random[j],
+                         b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                      (PATTERN_INPUTS_SHORT_COUNT)) +
+                                     RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_b.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_b.c
new file mode 100644
index 0000000000..1c1cb3bc4a
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLE_S.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLE_S.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },    /*  48  */
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },    /*  56  */
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0xff00ffffff000000ULL, 0x00000000ff00ff00ULL, },
+        { 0xff00000000000000ULL, 0x000000000000ffffULL, },
+        { 0xff00ffffff0000ffULL, 0x000000000000ff00ULL, },
+        { 0x00ff000000ffffffULL, 0xffffffff00ff00ffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00ff000000ff0000ULL, 0xff00ff00000000ffULL, },
+        { 0xffffff00ffffffffULL, 0x0000000000ff0000ULL, },
+        { 0x00ffffffffffffffULL, 0xffffffffffff0000ULL, },    /*  72  */
+        { 0xff00ffffff00ffffULL, 0x00ff00ffffffff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xff00ffffffffffffULL, 0x00ff000000ff0000ULL, },
+        { 0x00ff000000ffff00ULL, 0xffffffffffff00ffULL, },
+        { 0x000000ff00000000ULL, 0xffffffffff00ffffULL, },
+        { 0x00ff000000000000ULL, 0xff00ffffff00ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_S_B(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_S_B(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_d.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_d.c
new file mode 100644
index 0000000000..b51907cbc0
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLE_S.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLE_S.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },    /*  48  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },    /*  72  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_S_D(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_S_D(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_h.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_h.c
new file mode 100644
index 0000000000..20ebbfbafb
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLE_S.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLE_S.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },    /*  48  */
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },    /*  56  */
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0xffffffffffff0000ULL, 0x00000000ffffffffULL, },
+        { 0xffff000000000000ULL, 0x000000000000ffffULL, },
+        { 0xffffffffffff0000ULL, 0x000000000000ffffULL, },
+        { 0x000000000000ffffULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000ffffffffffffULL, 0xffffffffffff0000ULL, },    /*  72  */
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x000000000000ffffULL, 0xffffffffffff0000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_S_H(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_S_H(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_w.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_w.c
new file mode 100644
index 0000000000..4a78cce460
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLE_S.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLE_S.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffff00000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00000000ffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffff00000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0x00000000ffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },    /*  48  */
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x00000000ffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00000000ffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffff00000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffff00000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffff00000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffffffffffffULL, },    /*  72  */
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_S_W(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_S_W(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_b.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_b.c
new file mode 100644
index 0000000000..9990a5b08a
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLE_U.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLE_U.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0xffff0000ffffff00ULL, 0x00ffff00000000ffULL, },
+        { 0xff000000ffffffffULL, 0x00ffffff000000ffULL, },
+        { 0x00000000ff00ffffULL, 0xffffffff0000ffffULL, },
+        { 0x0000ffff000000ffULL, 0xff0000ffffffff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ffffffff00ffULL, 0xff00ffffff000000ULL, },
+        { 0x0000ff00ff00ffffULL, 0xff0000ffffffff00ULL, },
+        { 0x00ffffff00000000ULL, 0xff000000ffffff00ULL, },    /*  72  */
+        { 0xffff00000000ff00ULL, 0x00ff000000ffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x000000000000ff00ULL, 0xffff000000ffffffULL, },
+        { 0xffffffff00ff0000ULL, 0x00000000ffff0000ULL, },
+        { 0xffff00ff00ff0000ULL, 0x00ffff00000000ffULL, },
+        { 0xffffffffffff00ffULL, 0x0000ffffff000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_U_B(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_U_B(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_d.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_d.c
new file mode 100644
index 0000000000..cde86d824a
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLE_U.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLE_U.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },    /*  72  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_U_D(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_U_D(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_h.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_h.c
new file mode 100644
index 0000000000..70e4b48de5
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLE_U.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLE_U.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0xffff0000ffffffffULL, 0x0000ffff00000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffff00000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffff0000ffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff0000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ffffffff0000ULL, 0xffffffffffff0000ULL, },
+        { 0x0000ffffffffffffULL, 0xffff0000ffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff0000ffffffffULL, },    /*  72  */
+        { 0xffff00000000ffffULL, 0x000000000000ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x000000000000ffffULL, 0xffff00000000ffffULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffff0000ULL, },
+        { 0xffff000000000000ULL, 0x0000ffff00000000ULL, },
+        { 0xffffffffffff0000ULL, 0x0000ffffffff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_U_H(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_U_H(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_w.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_w.c
new file mode 100644
index 0000000000..f38feacef2
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLE_U.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLE_U.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x00000000ffffffffULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00000000ffffffffULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  64  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00000000ffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00000000ffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },    /*  72  */
+        { 0xffffffff00000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffffffffULL, },
+        { 0xffffffff00000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_U_W(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLE_U_W(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_b.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_b.c
new file mode 100644
index 0000000000..b81b569498
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLT_S.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLT_S.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },    /*  48  */
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },    /*  56  */
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  64  */
+        { 0xff00ffffff000000ULL, 0x00000000ff00ff00ULL, },
+        { 0xff00000000000000ULL, 0x000000000000ffffULL, },
+        { 0xff00ffffff0000ffULL, 0x000000000000ff00ULL, },
+        { 0x00ff000000ffffffULL, 0xffffffff00ff00ffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00ff000000ff0000ULL, 0xff00ff00000000ffULL, },
+        { 0xffffff00ffffffffULL, 0x0000000000ff0000ULL, },
+        { 0x00ffffffffffffffULL, 0xffffffffffff0000ULL, },    /*  72  */
+        { 0xff00ffffff00ffffULL, 0x00ff00ffffffff00ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xff00ffffffffffffULL, 0x00ff000000ff0000ULL, },
+        { 0x00ff000000ffff00ULL, 0xffffffffffff00ffULL, },
+        { 0x000000ff00000000ULL, 0xffffffffff00ffffULL, },
+        { 0x00ff000000000000ULL, 0xff00ffffff00ffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_S_B(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_S_B(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_d.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_d.c
new file mode 100644
index 0000000000..393457a3c1
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLT_S.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLT_S.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },    /*  48  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  64  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },    /*  72  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_S_D(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_S_D(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_h.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_h.c
new file mode 100644
index 0000000000..56860d7bcc
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLT_S.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLT_S.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },    /*  48  */
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },    /*  56  */
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  64  */
+        { 0xffffffffffff0000ULL, 0x00000000ffffffffULL, },
+        { 0xffff000000000000ULL, 0x000000000000ffffULL, },
+        { 0xffffffffffff0000ULL, 0x000000000000ffffULL, },
+        { 0x000000000000ffffULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000ffffffffffffULL, 0xffffffffffff0000ULL, },    /*  72  */
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x000000000000ffffULL, 0xffffffffffff0000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_S_H(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_S_H(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_w.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_w.c
new file mode 100644
index 0000000000..346b2939bb
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLT_S.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLT_S.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffff00000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00000000ffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffff00000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0x00000000ffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },    /*  48  */
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x00000000ffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00000000ffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffff00000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffff00000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  64  */
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffff00000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffffffffffffULL, },    /*  72  */
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_S_W(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_S_W(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_b.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_b.c
new file mode 100644
index 0000000000..be79646b45
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLT_U.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLT_U.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, },
+        { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  64  */
+        { 0xffff0000ffffff00ULL, 0x00ffff00000000ffULL, },
+        { 0xff000000ffffffffULL, 0x00ffffff000000ffULL, },
+        { 0x00000000ff00ffffULL, 0xffffffff0000ffffULL, },
+        { 0x0000ffff000000ffULL, 0xff0000ffffffff00ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000ffffffff00ffULL, 0xff00ffffff000000ULL, },
+        { 0x0000ff00ff00ffffULL, 0xff0000ffffffff00ULL, },
+        { 0x00ffffff00000000ULL, 0xff000000ffffff00ULL, },    /*  72  */
+        { 0xffff00000000ff00ULL, 0x00ff000000ffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x000000000000ff00ULL, 0xffff000000ffffffULL, },
+        { 0xffffffff00ff0000ULL, 0x00000000ffff0000ULL, },
+        { 0xffff00ff00ff0000ULL, 0x00ffff00000000ffULL, },
+        { 0xffffffffffff00ffULL, 0x0000ffffff000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_U_B(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_U_B(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_d.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_d.c
new file mode 100644
index 0000000000..afbe4904f8
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLT_U.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLT_U.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  64  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },    /*  72  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_U_D(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_U_D(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_h.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_h.c
new file mode 100644
index 0000000000..126597af9d
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLT_U.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLT_U.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  64  */
+        { 0xffff0000ffffffffULL, 0x0000ffff00000000ULL, },
+        { 0xffff0000ffffffffULL, 0x0000ffff00000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffff0000ffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff0000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000ffffffff0000ULL, 0xffffffffffff0000ULL, },
+        { 0x0000ffffffffffffULL, 0xffff0000ffffffffULL, },
+        { 0x0000ffff00000000ULL, 0xffff0000ffffffffULL, },    /*  72  */
+        { 0xffff00000000ffffULL, 0x000000000000ffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x000000000000ffffULL, 0xffff00000000ffffULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffff0000ULL, },
+        { 0xffff000000000000ULL, 0x0000ffff00000000ULL, },
+        { 0xffffffffffff0000ULL, 0x0000ffffffff0000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_U_H(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_U_H(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_w.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_w.c
new file mode 100644
index 0000000000..b4059292cd
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction CLT_U.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "CLT_U.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x00000000ffffffffULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00000000ffffffffULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffffffffULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*  64  */
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x0000000000000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffff00000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x00000000ffffffffULL, 0xffffffffffffffffULL, },
+        { 0x00000000ffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0xffffffffffffffffULL, },    /*  72  */
+        { 0xffffffff00000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xffffffff00000000ULL, },
+        { 0xffffffff00000000ULL, 0x00000000ffffffffULL, },
+        { 0xffffffff00000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0x00000000ffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_U_W(b128_pattern[i], b128_pattern[j],
+                          b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_CLT_U_W(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_b.c
new file mode 100644
index 0000000000..b7a9a61548
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_A.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_A.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaa8eaaaa8eaaaa8eULL, 0xaaaa8eaaaa8eaaaaULL, },
+        { 0xaa71aaaa71aaaa71ULL, 0xaaaa71aaaa71aaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x558e55558e55558eULL, 0x55558e55558e5555ULL, },
+        { 0x5571555571555571ULL, 0x5555715555715555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xcc8e38cc8e38cc8eULL, 0x38cc8e38cc8e38ccULL, },
+        { 0xcc71c7cc71c7cc71ULL, 0xc7cc71c7cc71c7ccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x338e38338e38338eULL, 0x38338e38338e3833ULL, },
+        { 0x3371c73371c73371ULL, 0xc73371c73371c733ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },    /*  48  */
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xaa8eaaaa8eaaaa8eULL, 0xaaaa8eaaaa8eaaaaULL, },
+        { 0x558e55558e55558eULL, 0x55558e55558e5555ULL, },
+        { 0xcc8e38cc8e38cc8eULL, 0x38cc8e38cc8e38ccULL, },
+        { 0x338e38338e38338eULL, 0x38338e38338e3833ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },    /*  56  */
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaa71aaaa71aaaa71ULL, 0xaaaa71aaaa71aaaaULL, },
+        { 0x5571555571555571ULL, 0x5555715555715555ULL, },
+        { 0xcc71c7cc71c7cc71ULL, 0xc7cc71c7cc71c7ccULL, },
+        { 0x3371c73371c73371ULL, 0xc73371c73371c733ULL, },
+        { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886ae6634d935540ULL, 0x4b67bb5e157b520cULL, },
+        { 0x886aaeaab9628b80ULL, 0x4b67c65eab7bb014ULL, },
+        { 0x886ae64d5e62554eULL, 0x8d67885ea97bb0a0ULL, },
+        { 0x886ae6634d935540ULL, 0x4b67bb5e157b520cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aae634d938b80ULL, 0x27d8bb1aab3f5214ULL, },
+        { 0x704f16635e93c74eULL, 0x8df188d8a94252a0ULL, },
+        { 0x886aaeaab9628b80ULL, 0x4b67c65eab7bb014ULL, },    /*  72  */
+        { 0xac5aae634d938b80ULL, 0x27d8bb1aab3f5214ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x705aaeaa5e318b80ULL, 0x8dd888d8a94225a0ULL, },
+        { 0x886ae64d5e62554eULL, 0x8d67885ea97bb0a0ULL, },
+        { 0x704f16635e93c74eULL, 0x8df188d8a94252a0ULL, },
+        { 0x705aaeaa5ecf8b80ULL, 0x8dd888d8a94225a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_A_B(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_A_B(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_d.c
new file mode 100644
index 0000000000..dfffaf5b5e
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_A.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_A.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, },
+        { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, },
+        { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },    /*  48  */
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, },
+        { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },    /*  56  */
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, },
+        { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },
+        { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  72  */
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_A_D(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_A_D(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_h.c
new file mode 100644
index 0000000000..e0c1bd4c4b
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_A.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_A.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaa8e38aaaaULL, 0xaaaa8e38aaaaaaaaULL, },
+        { 0xaaaaaaaa71c7aaaaULL, 0xaaaa71c7aaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x555555558e385555ULL, 0x55558e3855555555ULL, },
+        { 0x5555555571c75555ULL, 0x555571c755555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xcccc38e38e38ccccULL, 0x38e38e38cccc38e3ULL, },
+        { 0xccccc71c71c7ccccULL, 0xc71c71c7ccccc71cULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x333338e38e383333ULL, 0x38e38e38333338e3ULL, },
+        { 0x3333c71c71c73333ULL, 0xc71c71c73333c71cULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },    /*  48  */
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xaaaaaaaa8e38aaaaULL, 0xaaaa8e38aaaaaaaaULL, },
+        { 0x555555558e385555ULL, 0x55558e3855555555ULL, },
+        { 0xcccc38e38e38ccccULL, 0x38e38e38cccc38e3ULL, },
+        { 0x333338e38e383333ULL, 0x38e38e38333338e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },    /*  56  */
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaa71c7aaaaULL, 0xaaaa71c7aaaaaaaaULL, },
+        { 0x5555555571c75555ULL, 0x555571c755555555ULL, },
+        { 0xccccc71c71c7ccccULL, 0xc71c71c7ccccc71cULL, },
+        { 0x3333c71c71c73333ULL, 0xc71c71c73333c71cULL, },
+        { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886ae6cc4d935540ULL, 0x4b67bb1a153f52fcULL, },
+        { 0x886aaeaab9cf8b80ULL, 0x4b67c6ffab2bb00cULL, },
+        { 0x886ae6cc5e315540ULL, 0x8df188d8a942b00cULL, },
+        { 0x886ae6cc4d935540ULL, 0x4b67bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaa4d938b80ULL, 0x27d8bb1aab2b52fcULL, },
+        { 0x704f164d5e31c708ULL, 0x8df188d8a94252fcULL, },
+        { 0x886aaeaab9cf8b80ULL, 0x4b67c6ffab2bb00cULL, },    /*  72  */
+        { 0xac5aaeaa4d938b80ULL, 0x27d8bb1aab2b52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704faeaa5e318b80ULL, 0x8df188d8a9422514ULL, },
+        { 0x886ae6cc5e315540ULL, 0x8df188d8a942b00cULL, },
+        { 0x704f164d5e31c708ULL, 0x8df188d8a94252fcULL, },
+        { 0x704faeaa5e318b80ULL, 0x8df188d8a9422514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_A_H(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_A_H(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_w.c
new file mode 100644
index 0000000000..40c30c5569
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_A.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_A.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaa8e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaa71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x555555558e38e38eULL, 0x5555555555555555ULL, },
+        { 0x5555555571c71c71ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xcccccccc8e38e38eULL, 0x38e38e38ccccccccULL, },
+        { 0xcccccccc71c71c71ULL, 0xc71c71c7ccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x333333338e38e38eULL, 0x38e38e3833333333ULL, },
+        { 0x3333333371c71c71ULL, 0xc71c71c733333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },    /*  48  */
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xaaaaaaaa8e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x555555558e38e38eULL, 0x5555555555555555ULL, },
+        { 0xcccccccc8e38e38eULL, 0x38e38e38ccccccccULL, },
+        { 0x333333338e38e38eULL, 0x38e38e3833333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },    /*  56  */
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaa71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555571c71c71ULL, 0x5555555555555555ULL, },
+        { 0xcccccccc71c71c71ULL, 0xc71c71c7ccccccccULL, },
+        { 0x3333333371c71c71ULL, 0xc71c71c733333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886ae6cc4d93c708ULL, 0x4b670b5e153f52fcULL, },
+        { 0x886ae6ccb9cf8b80ULL, 0x4b670b5eab2b2514ULL, },
+        { 0x886ae6cc5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc4d93c708ULL, 0x4b670b5e153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaa4d93c708ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6ccb9cf8b80ULL, 0x4b670b5eab2b2514ULL, },    /*  72  */
+        { 0xac5aaeaa4d93c708ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_A_W(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_A_W(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_b.c
new file mode 100644
index 0000000000..ab50eee187
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_S.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_S.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xffff38ffff38ffffULL, 0x38ffff38ffff38ffULL, },
+        { 0x1c71ff1c71ff1c71ULL, 0xff1c71ff1c71ff1cULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x0000380000380000ULL, 0x3800003800003800ULL, },
+        { 0x1c71001c71001c71ULL, 0x001c71001c71001cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe3aa38e3aa38e3aaULL, 0x38e3aa38e3aa38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5571555571555571ULL, 0x5555715555715555ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe3cc38e3cc38e3ccULL, 0x38e3cc38e3cc38e3ULL, },
+        { 0x1c71cc1c71cc1c71ULL, 0xcc1c71cc1c71cc1cULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333383333383333ULL, 0x3833333833333833ULL, },
+        { 0x3371333371333371ULL, 0x3333713333713333ULL, },
+        { 0xffff38ffff38ffffULL, 0x38ffff38ffff38ffULL, },    /*  48  */
+        { 0x0000380000380000ULL, 0x3800003800003800ULL, },
+        { 0xe3aa38e3aa38e3aaULL, 0x38e3aa38e3aa38e3ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xe3cc38e3cc38e3ccULL, 0x38e3cc38e3cc38e3ULL, },
+        { 0x3333383333383333ULL, 0x3833333833333833ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, },
+        { 0x1c71ff1c71ff1c71ULL, 0xff1c71ff1c71ff1cULL, },    /*  56  */
+        { 0x1c71001c71001c71ULL, 0x001c71001c71001cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x5571555571555571ULL, 0x5555715555715555ULL, },
+        { 0x1c71cc1c71cc1c71ULL, 0xcc1c71cc1c71cc1cULL, },
+        { 0x3371333371333371ULL, 0x3333713333713333ULL, },
+        { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfb6a00634d625540ULL, 0x4b670b5e157b520cULL, },
+        { 0xac6ae6cc28625540ULL, 0x4b670b5efe7b2514ULL, },
+        { 0x706a164d5e62554eULL, 0x4b670b5efe7be20cULL, },
+        { 0xfb6a00634d625540ULL, 0x4b670b5e157b520cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfb5a00634dcfc708ULL, 0x27f7c61a153f5214ULL, },
+        { 0x704f16635e31e24eULL, 0x12f7bb1a154252fcULL, },
+        { 0xac6ae6cc28625540ULL, 0x4b670b5efe7b2514ULL, },    /*  72  */
+        { 0xfb5a00634dcfc708ULL, 0x27f7c61a153f5214ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x705a164d5e31e24eULL, 0x27f1c6ffab422514ULL, },
+        { 0x706a164d5e62554eULL, 0x4b670b5efe7be20cULL, },
+        { 0x704f16635e31e24eULL, 0x12f7bb1a154252fcULL, },
+        { 0x705a164d5e31e24eULL, 0x27f1c6ffab422514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_S_B(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_S_B(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_d.c
new file mode 100644
index 0000000000..2957db4abc
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_S.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_S.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xffffffffffffffffULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x0000000000000000ULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xffffffffffffffffULL, 0x38e38e38e38e38e3ULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x1c71c71c71c71c71ULL, 0x0000000000000000ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, },
+        { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, },    /*  72  */
+        { 0xfbbe00634d93c708ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, },
+        { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, },
+        { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_S_D(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_S_D(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_h.c
new file mode 100644
index 0000000000..e101764d73
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_S.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_S.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xffff38e3ffffffffULL, 0x38e3ffffffff38e3ULL, },
+        { 0x1c71ffff71c71c71ULL, 0xffff71c71c71ffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x000038e300000000ULL, 0x38e30000000038e3ULL, },
+        { 0x1c71000071c71c71ULL, 0x000071c71c710000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e3aaaae38eULL, 0x38e3aaaae38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555571c75555ULL, 0x555571c755555555ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e3cccce38eULL, 0x38e3cccce38e38e3ULL, },
+        { 0x1c71cccc71c71c71ULL, 0xcccc71c71c71ccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x333338e333333333ULL, 0x38e33333333338e3ULL, },
+        { 0x3333333371c73333ULL, 0x333371c733333333ULL, },
+        { 0xffff38e3ffffffffULL, 0x38e3ffffffff38e3ULL, },    /*  48  */
+        { 0x000038e300000000ULL, 0x38e30000000038e3ULL, },
+        { 0xe38e38e3aaaae38eULL, 0x38e3aaaae38e38e3ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xe38e38e3cccce38eULL, 0x38e3cccce38e38e3ULL, },
+        { 0x333338e333333333ULL, 0x38e33333333338e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, },
+        { 0x1c71ffff71c71c71ULL, 0xffff71c71c71ffffULL, },    /*  56  */
+        { 0x1c71000071c71c71ULL, 0x000071c71c710000ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x5555555571c75555ULL, 0x555571c755555555ULL, },
+        { 0x1c71cccc71c71c71ULL, 0xcccc71c71c71ccccULL, },
+        { 0x3333333371c73333ULL, 0x333371c733333333ULL, },
+        { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbe00634d935540ULL, 0x4b670b5e153f52fcULL, },
+        { 0xac5ae6cc28625540ULL, 0x4b670b5efe7b2514ULL, },
+        { 0x704f164d5e315540ULL, 0x4b670b5efe7be2a0ULL, },
+        { 0xfbbe00634d935540ULL, 0x4b670b5e153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x27d8c6ff153f52fcULL, },
+        { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5ae6cc28625540ULL, 0x4b670b5efe7b2514ULL, },    /*  72  */
+        { 0xfbbe00634d93c708ULL, 0x27d8c6ff153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e315540ULL, 0x4b670b5efe7be2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, },
+        { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_S_H(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_S_H(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_w.c
new file mode 100644
index 0000000000..119f03ffba
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_S.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_S.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xffffffffffffffffULL, 0x38e38e38ffffffffULL, },
+        { 0x1c71c71c71c71c71ULL, 0xffffffff1c71c71cULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x0000000000000000ULL, 0x38e38e3800000000ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x000000001c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e3aaaaaaaaULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555571c71c71ULL, 0x5555555555555555ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e3ccccccccULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xcccccccc1c71c71cULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x38e38e3833333333ULL, },
+        { 0x3333333371c71c71ULL, 0x3333333333333333ULL, },
+        { 0xffffffffffffffffULL, 0x38e38e38ffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x38e38e3800000000ULL, },
+        { 0xe38e38e3aaaaaaaaULL, 0x38e38e38e38e38e3ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xe38e38e3ccccccccULL, 0x38e38e38e38e38e3ULL, },
+        { 0x3333333333333333ULL, 0x38e38e3833333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xffffffff1c71c71cULL, },    /*  56  */
+        { 0x1c71c71c71c71c71ULL, 0x000000001c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x5555555571c71c71ULL, 0x5555555555555555ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xcccccccc1c71c71cULL, },
+        { 0x3333333371c71c71ULL, 0x3333333333333333ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbe00634d93c708ULL, 0x4b670b5e153f52fcULL, },
+        { 0xac5aaeaa28625540ULL, 0x4b670b5efe7bb00cULL, },
+        { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x4b670b5e153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x27d8c6ff153f52fcULL, },
+        { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaa28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  72  */
+        { 0xfbbe00634d93c708ULL, 0x27d8c6ff153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, },
+        { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, },
+        { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_S_W(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_S_W(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_b.c
new file mode 100644
index 0000000000..d18b6bf0cc
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_U.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_U.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xe3aaaae3aaaae3aaULL, 0xaae3aaaae3aaaae3ULL, },
+        { 0xaaaac7aaaac7aaaaULL, 0xc7aaaac7aaaac7aaULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xe38e55e38e55e38eULL, 0x55e38e55e38e55e3ULL, },
+        { 0x5571c75571c75571ULL, 0xc75571c75571c755ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xe3cccce3cccce3ccULL, 0xcce3cccce3cccce3ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x3371c73371c73371ULL, 0xc73371c73371c733ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe3aaaae3aaaae3aaULL, 0xaae3aaaae3aaaae3ULL, },
+        { 0xe38e55e38e55e38eULL, 0x55e38e55e38e55e3ULL, },
+        { 0xe3cccce3cccce3ccULL, 0xcce3cccce3cccce3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaac7aaaac7aaaaULL, 0xc7aaaac7aaaac7aaULL, },
+        { 0x5571c75571c75571ULL, 0xc75571c75571c755ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3371c73371c73371ULL, 0xc73371c73371c733ULL, },
+        { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbee6cc4d93c740ULL, 0x4bf7bb5efe7bb0fcULL, },
+        { 0xac6ae6ccb9cf8b80ULL, 0x4bd8c6fffe7bb014ULL, },
+        { 0x886ae6cc5e62e24eULL, 0x8df188d8fe7be2a0ULL, },
+        { 0xfbbee6cc4d93c740ULL, 0x4bf7bb5efe7bb0fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbeaeaab9cfc780ULL, 0x27f7c6ffab3f52fcULL, },
+        { 0xfbbe16635e93e24eULL, 0x8df7bbd8a942e2fcULL, },
+        { 0xac6ae6ccb9cf8b80ULL, 0x4bd8c6fffe7bb014ULL, },    /*  72  */
+        { 0xfbbeaeaab9cfc780ULL, 0x27f7c6ffab3f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cfe280ULL, 0x8df1c6ffab42e2a0ULL, },
+        { 0x886ae6cc5e62e24eULL, 0x8df188d8fe7be2a0ULL, },
+        { 0xfbbe16635e93e24eULL, 0x8df7bbd8a942e2fcULL, },
+        { 0xac5aaeaab9cfe280ULL, 0x8df1c6ffab42e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_U_B(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_U_B(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_d.c
new file mode 100644
index 0000000000..1396e740ff
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_U.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_U.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xe38e38e38e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xe38e38e38e38e38eULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xe38e38e38e38e38eULL, 0x5555555555555555ULL, },
+        { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c71c71c71cULL, },
+        { 0x5555555555555555ULL, 0xc71c71c71c71c71cULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, },
+        { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, },
+        { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, },    /*  72  */
+        { 0xfbbe00634d93c708ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, },
+        { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_U_D(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_U_D(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_h.c
new file mode 100644
index 0000000000..c7dff10709
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_U.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_U.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xe38eaaaaaaaae38eULL, 0xaaaaaaaae38eaaaaULL, },
+        { 0xaaaac71caaaaaaaaULL, 0xc71caaaaaaaac71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xe38e55558e38e38eULL, 0x55558e38e38e5555ULL, },
+        { 0x5555c71c71c75555ULL, 0xc71c71c75555c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xe38ecccccccce38eULL, 0xcccccccce38eccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x3333c71c71c73333ULL, 0xc71c71c73333c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38eaaaaaaaae38eULL, 0xaaaaaaaae38eaaaaULL, },
+        { 0xe38e55558e38e38eULL, 0x55558e38e38e5555ULL, },
+        { 0xe38ecccccccce38eULL, 0xcccccccce38eccccULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaac71caaaaaaaaULL, 0xc71caaaaaaaac71cULL, },
+        { 0x5555c71c71c75555ULL, 0xc71c71c75555c71cULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333c71c71c73333ULL, 0xc71c71c73333c71cULL, },
+        { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbee6cc4d93c708ULL, 0x4b67bb1afe7bb00cULL, },
+        { 0xac5ae6ccb9cf8b80ULL, 0x4b67c6fffe7bb00cULL, },
+        { 0x886ae6cc5e31e24eULL, 0x8df188d8fe7be2a0ULL, },
+        { 0xfbbee6cc4d93c708ULL, 0x4b67bb1afe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbeaeaab9cfc708ULL, 0x27d8c6ffab2b52fcULL, },
+        { 0xfbbe164d5e31e24eULL, 0x8df1bb1aa942e2a0ULL, },
+        { 0xac5ae6ccb9cf8b80ULL, 0x4b67c6fffe7bb00cULL, },    /*  72  */
+        { 0xfbbeaeaab9cfc708ULL, 0x27d8c6ffab2b52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cfe24eULL, 0x8df1c6ffab2be2a0ULL, },
+        { 0x886ae6cc5e31e24eULL, 0x8df188d8fe7be2a0ULL, },
+        { 0xfbbe164d5e31e24eULL, 0x8df1bb1aa942e2a0ULL, },
+        { 0xac5aaeaab9cfe24eULL, 0x8df1c6ffab2be2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_U_H(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_U_H(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_w.c
new file mode 100644
index 0000000000..910dbfc0ca
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MAX_U.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MAX_U.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xe38e38e3aaaaaaaaULL, 0xaaaaaaaae38e38e3ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c7aaaaaaaaULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xe38e38e38e38e38eULL, 0x55555555e38e38e3ULL, },
+        { 0x5555555571c71c71ULL, 0xc71c71c755555555ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xe38e38e3ccccccccULL, 0xcccccccce38e38e3ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x3333333371c71c71ULL, 0xc71c71c733333333ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e3aaaaaaaaULL, 0xaaaaaaaae38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x55555555e38e38e3ULL, },
+        { 0xe38e38e3ccccccccULL, 0xcccccccce38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c7aaaaaaaaULL, },
+        { 0x5555555571c71c71ULL, 0xc71c71c755555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333371c71c71ULL, 0xc71c71c733333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, },
+        { 0x886ae6cc5e31e24eULL, 0x8df188d8fe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe0063b9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xfbbe00635e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, },    /*  72  */
+        { 0xfbbe0063b9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x8df188d8ab2b2514ULL, },
+        { 0x886ae6cc5e31e24eULL, 0x8df188d8fe7bb00cULL, },
+        { 0xfbbe00635e31e24eULL, 0x8df188d8a942e2a0ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x8df188d8ab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_U_W(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MAX_U_W(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_b.c
new file mode 100644
index 0000000000..c632fe974f
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_A.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_A.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe3aa38e3aa38e3aaULL, 0x38e3aa38e3aa38e3ULL, },
+        { 0x1caac71caac71caaULL, 0xc71caac71caac71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe35538e35538e355ULL, 0x38e35538e35538e3ULL, },
+        { 0x1c55c71c55c71c55ULL, 0xc71c55c71c55c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe3cccce3cccce3ccULL, 0xcce3cccce3cccce3ULL, },
+        { 0x1ccccc1ccccc1cccULL, 0xcc1ccccc1ccccc1cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe33333e33333e333ULL, 0x33e33333e33333e3ULL, },
+        { 0x1c33331c33331c33ULL, 0x331c33331c33331cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xe3aa38e3aa38e3aaULL, 0x38e3aa38e3aa38e3ULL, },
+        { 0xe35538e35538e355ULL, 0x38e35538e35538e3ULL, },
+        { 0xe3cccce3cccce3ccULL, 0xcce3cccce3cccce3ULL, },
+        { 0xe33333e33333e333ULL, 0x33e33333e33333e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x1caac71caac71caaULL, 0xc71caac71caac71cULL, },
+        { 0x1c55c71c55c71c55ULL, 0xc71c55c71c55c71cULL, },
+        { 0x1ccccc1ccccc1cccULL, 0xcc1ccccc1ccccc1cULL, },
+        { 0x1c33331c33331c33ULL, 0x331c33331c33331cULL, },
+        { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbe00cc2862c708ULL, 0x12f70b1afe3fb0fcULL, },
+        { 0xac5ae6cc28cf5540ULL, 0x27d80bfffe2b250cULL, },
+        { 0x704f16cc2831e240ULL, 0x4bf10bd8fe42e20cULL, },
+        { 0xfbbe00cc2862c708ULL, 0x12f70b1afe3fb0fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00aab9cfc708ULL, 0x12f7c6ff152b25fcULL, },
+        { 0xfbbe004d4d31e208ULL, 0x12f7bb1a153fe2fcULL, },
+        { 0xac5ae6cc28cf5540ULL, 0x27d80bfffe2b250cULL, },    /*  72  */
+        { 0xfbbe00aab9cfc708ULL, 0x12f7c6ff152b25fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac4f164db931e24eULL, 0x27f1c6ffab2be214ULL, },
+        { 0x704f16cc2831e240ULL, 0x4bf10bd8fe42e20cULL, },
+        { 0xfbbe004d4d31e208ULL, 0x12f7bb1a153fe2fcULL, },
+        { 0xac4f164db9cfe24eULL, 0x27f1c6ffab2be214ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_A_B(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_A_B(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_d.c
new file mode 100644
index 0000000000..5f9a9d4706
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_A.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_A.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, },
+        { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, },
+        { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, },
+        { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },    /*  72  */
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_A_D(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_A_D(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_h.c
new file mode 100644
index 0000000000..dc73927bb7
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_A.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_A.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e3aaaae38eULL, 0x38e3aaaae38e38e3ULL, },
+        { 0x1c71c71caaaa1c71ULL, 0xc71caaaa1c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e35555e38eULL, 0x38e35555e38e38e3ULL, },
+        { 0x1c71c71c55551c71ULL, 0xc71c55551c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38ecccccccce38eULL, 0xcccccccce38eccccULL, },
+        { 0x1c71cccccccc1c71ULL, 0xcccccccc1c71ccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e33333333e38eULL, 0x33333333e38e3333ULL, },
+        { 0x1c71333333331c71ULL, 0x333333331c713333ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xe38e38e3aaaae38eULL, 0x38e3aaaae38e38e3ULL, },
+        { 0xe38e38e35555e38eULL, 0x38e35555e38e38e3ULL, },
+        { 0xe38ecccccccce38eULL, 0xcccccccce38eccccULL, },
+        { 0xe38e33333333e38eULL, 0x33333333e38e3333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x1c71c71caaaa1c71ULL, 0xc71caaaa1c71c71cULL, },
+        { 0x1c71c71c55551c71ULL, 0xc71c55551c71c71cULL, },
+        { 0x1c71cccccccc1c71ULL, 0xcccccccc1c71ccccULL, },
+        { 0x1c71333333331c71ULL, 0x333333331c713333ULL, },
+        { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbe00632862c708ULL, 0x12f70b5efe7bb00cULL, },
+        { 0xac5ae6cc28625540ULL, 0x27d80b5efe7b2514ULL, },
+        { 0x704f164d2862e24eULL, 0x4b670b5efe7be2a0ULL, },
+        { 0xfbbe00632862c708ULL, 0x12f70b5efe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe0063b9cfc708ULL, 0x12f7c6ff153f2514ULL, },
+        { 0xfbbe00634d93e24eULL, 0x12f7bb1a153fe2a0ULL, },
+        { 0xac5ae6cc28625540ULL, 0x27d80b5efe7b2514ULL, },    /*  72  */
+        { 0xfbbe0063b9cfc708ULL, 0x12f7c6ff153f2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5a164db9cfe24eULL, 0x27d8c6ffab2be2a0ULL, },
+        { 0x704f164d2862e24eULL, 0x4b670b5efe7be2a0ULL, },
+        { 0xfbbe00634d93e24eULL, 0x12f7bb1a153fe2a0ULL, },
+        { 0xac5a164db9cfe24eULL, 0x27d8c6ffab2be2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_A_H(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_A_H(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_w.c
new file mode 100644
index 0000000000..67b33f3751
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_A.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_A.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e3aaaaaaaaULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71caaaaaaaaULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e355555555ULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c55555555ULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e3ccccccccULL, 0xcccccccce38e38e3ULL, },
+        { 0x1c71c71cccccccccULL, 0xcccccccc1c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e333333333ULL, 0x33333333e38e38e3ULL, },
+        { 0x1c71c71c33333333ULL, 0x333333331c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xe38e38e3aaaaaaaaULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e355555555ULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e3ccccccccULL, 0xcccccccce38e38e3ULL, },
+        { 0xe38e38e333333333ULL, 0x33333333e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x1c71c71caaaaaaaaULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71c55555555ULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71cccccccccULL, 0xcccccccc1c71c71cULL, },
+        { 0x1c71c71c33333333ULL, 0x333333331c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0xfbbe006328625540ULL, 0x12f7bb1afe7bb00cULL, },
+        { 0xac5aaeaa28625540ULL, 0x27d8c6fffe7bb00cULL, },
+        { 0x704f164d28625540ULL, 0x4b670b5efe7bb00cULL, },
+        { 0xfbbe006328625540ULL, 0x12f7bb1afe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe0063b9cf8b80ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaa28625540ULL, 0x27d8c6fffe7bb00cULL, },    /*  72  */
+        { 0xfbbe0063b9cf8b80ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d28625540ULL, 0x4b670b5efe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_A_W(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_A_W(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_b.c
new file mode 100644
index 0000000000..76bb133da7
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_S.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_S.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xe38effe38effe38eULL, 0xffe38effe38effe3ULL, },
+        { 0xffffc7ffffc7ffffULL, 0xc7ffffc7ffffc7ffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xe38e00e38e00e38eULL, 0x00e38e00e38e00e3ULL, },
+        { 0x0000c70000c70000ULL, 0xc70000c70000c700ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaa8eaaaa8eaaaa8eULL, 0xaaaa8eaaaa8eaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c55c71c55c71c55ULL, 0xc71c55c71c55c71cULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xcc8ecccc8ecccc8eULL, 0xcccc8ecccc8eccccULL, },
+        { 0xccccc7ccccc7ccccULL, 0xc7ccccc7ccccc7ccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e33e38e33e38eULL, 0x33e38e33e38e33e3ULL, },
+        { 0x1c33c71c33c71c33ULL, 0xc71c33c71c33c71cULL, },
+        { 0xe38effe38effe38eULL, 0xffe38effe38effe3ULL, },    /*  48  */
+        { 0xe38e00e38e00e38eULL, 0x00e38e00e38e00e3ULL, },
+        { 0xaa8eaaaa8eaaaa8eULL, 0xaaaa8eaaaa8eaaaaULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xcc8ecccc8ecccc8eULL, 0xcccc8ecccc8eccccULL, },
+        { 0xe38e33e38e33e38eULL, 0x33e38e33e38e33e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, },
+        { 0xffffc7ffffc7ffffULL, 0xc7ffffc7ffffc7ffULL, },    /*  56  */
+        { 0x0000c70000c70000ULL, 0xc70000c70000c700ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x1c55c71c55c71c55ULL, 0xc71c55c71c55c71cULL, },
+        { 0xccccc7ccccc7ccccULL, 0xc7ccccc7ccccc7ccULL, },
+        { 0x1c33c71c33c71c33ULL, 0xc71c33c71c33c71cULL, },
+        { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x88bee6cc2893c708ULL, 0x12f7bb1afe3fb0fcULL, },
+        { 0x885aaeaab9cf8b80ULL, 0x27d8c6ffab2bb00cULL, },
+        { 0x884fe6cc2831e240ULL, 0x8df188d8a942b0a0ULL, },
+        { 0x88bee6cc2893c708ULL, 0x12f7bb1afe3fb0fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xacbeaeaab9938b80ULL, 0x12d8bbffab2b25fcULL, },
+        { 0xfbbe004d4d93c708ULL, 0x8df188d8a93fe2a0ULL, },
+        { 0x885aaeaab9cf8b80ULL, 0x27d8c6ffab2bb00cULL, },    /*  72  */
+        { 0xacbeaeaab9938b80ULL, 0x12d8bbffab2b25fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac4faeaab9cf8b80ULL, 0x8dd888d8a92be2a0ULL, },
+        { 0x884fe6cc2831e240ULL, 0x8df188d8a942b0a0ULL, },
+        { 0xfbbe004d4d93c708ULL, 0x8df188d8a93fe2a0ULL, },
+        { 0xac4faeaab9cf8b80ULL, 0x8dd888d8a92be2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_S_B(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_S_B(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_d.c
new file mode 100644
index 0000000000..8ef57a9533
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_S.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_S.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xe38e38e38e38e38eULL, 0xffffffffffffffffULL, },
+        { 0xffffffffffffffffULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xe38e38e38e38e38eULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xe38e38e38e38e38eULL, 0xffffffffffffffffULL, },    /*  48  */
+        { 0xe38e38e38e38e38eULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, },
+        { 0xffffffffffffffffULL, 0xc71c71c71c71c71cULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0xc71c71c71c71c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, },
+        { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, },    /*  72  */
+        { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, },
+        { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_S_D(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_S_D(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_h.c
new file mode 100644
index 0000000000..e206040f98
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_S.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_S.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xe38effff8e38e38eULL, 0xffff8e38e38effffULL, },
+        { 0xffffc71cffffffffULL, 0xc71cffffffffc71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xe38e00008e38e38eULL, 0x00008e38e38e0000ULL, },
+        { 0x0000c71c00000000ULL, 0xc71c00000000c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaa8e38aaaaULL, 0xaaaa8e38aaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c55551c71ULL, 0xc71c55551c71c71cULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xcccccccc8e38ccccULL, 0xcccc8e38ccccccccULL, },
+        { 0xccccc71cccccccccULL, 0xc71cccccccccc71cULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e33338e38e38eULL, 0x33338e38e38e3333ULL, },
+        { 0x1c71c71c33331c71ULL, 0xc71c33331c71c71cULL, },
+        { 0xe38effff8e38e38eULL, 0xffff8e38e38effffULL, },    /*  48  */
+        { 0xe38e00008e38e38eULL, 0x00008e38e38e0000ULL, },
+        { 0xaaaaaaaa8e38aaaaULL, 0xaaaa8e38aaaaaaaaULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xcccccccc8e38ccccULL, 0xcccc8e38ccccccccULL, },
+        { 0xe38e33338e38e38eULL, 0x33338e38e38e3333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, },
+        { 0xffffc71cffffffffULL, 0xc71cffffffffc71cULL, },    /*  56  */
+        { 0x0000c71c00000000ULL, 0xc71c00000000c71cULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x1c71c71c55551c71ULL, 0xc71c55551c71c71cULL, },
+        { 0xccccc71cccccccccULL, 0xc71cccccccccc71cULL, },
+        { 0x1c71c71c33331c71ULL, 0xc71c33331c71c71cULL, },
+        { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886ae6cc2862c708ULL, 0x12f7bb1afe7bb00cULL, },
+        { 0x886aaeaab9cf8b80ULL, 0x27d8c6ffab2bb00cULL, },
+        { 0x886ae6cc2862e24eULL, 0x8df188d8a942b00cULL, },
+        { 0x886ae6cc2862c708ULL, 0x12f7bb1afe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1aab2b2514ULL, },
+        { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886aaeaab9cf8b80ULL, 0x27d8c6ffab2bb00cULL, },    /*  72  */
+        { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1aab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc2862e24eULL, 0x8df188d8a942b00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_S_H(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_S_H(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_w.c
new file mode 100644
index 0000000000..7532bce550
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_S.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_S.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },
+        { 0xe38e38e38e38e38eULL, 0xffffffffe38e38e3ULL, },
+        { 0xffffffffffffffffULL, 0xc71c71c7ffffffffULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xe38e38e38e38e38eULL, 0x00000000e38e38e3ULL, },
+        { 0x0000000000000000ULL, 0xc71c71c700000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaa8e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c55555555ULL, 0xc71c71c71c71c71cULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0xcccccccc8e38e38eULL, 0xccccccccccccccccULL, },
+        { 0xccccccccccccccccULL, 0xc71c71c7ccccccccULL, },
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x33333333e38e38e3ULL, },
+        { 0x1c71c71c33333333ULL, 0xc71c71c71c71c71cULL, },
+        { 0xe38e38e38e38e38eULL, 0xffffffffe38e38e3ULL, },    /*  48  */
+        { 0xe38e38e38e38e38eULL, 0x00000000e38e38e3ULL, },
+        { 0xaaaaaaaa8e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xcccccccc8e38e38eULL, 0xccccccccccccccccULL, },
+        { 0xe38e38e38e38e38eULL, 0x33333333e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, },
+        { 0xffffffffffffffffULL, 0xc71c71c7ffffffffULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0xc71c71c700000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x1c71c71c55555555ULL, 0xc71c71c71c71c71cULL, },
+        { 0xccccccccccccccccULL, 0xc71c71c7ccccccccULL, },
+        { 0x1c71c71c33333333ULL, 0xc71c71c71c71c71cULL, },
+        { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886ae6cc28625540ULL, 0x12f7bb1afe7bb00cULL, },
+        { 0x886ae6ccb9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc28625540ULL, 0x12f7bb1afe7bb00cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1aab2b2514ULL, },
+        { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6ccb9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },    /*  72  */
+        { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1aab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, },
+        { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_S_W(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_S_W(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_b.c
new file mode 100644
index 0000000000..1f611453a2
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_b.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_U.B
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_U.B";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaa8e38aa8e38aa8eULL, 0x38aa8e38aa8e38aaULL, },
+        { 0x1c71aa1c71aa1c71ULL, 0xaa1c71aa1c71aa1cULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x5555385555385555ULL, 0x3855553855553855ULL, },
+        { 0x1c55551c55551c55ULL, 0x551c55551c55551cULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xcc8e38cc8e38cc8eULL, 0x38cc8e38cc8e38ccULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x1c33331c33331c33ULL, 0x331c33331c33331cULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaa8e38aa8e38aa8eULL, 0x38aa8e38aa8e38aaULL, },
+        { 0x5555385555385555ULL, 0x3855553855553855ULL, },
+        { 0xcc8e38cc8e38cc8eULL, 0x38cc8e38cc8e38ccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x1c71aa1c71aa1c71ULL, 0xaa1c71aa1c71aa1cULL, },
+        { 0x1c55551c55551c55ULL, 0x551c55551c55551cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c33331c33331c33ULL, 0x331c33331c33331cULL, },
+        { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886a006328625508ULL, 0x12670b1a153f520cULL, },
+        { 0x885aaeaa28625540ULL, 0x27670b5eab2b250cULL, },
+        { 0x704f164d28315540ULL, 0x4b670b5ea942b00cULL, },
+        { 0x886a006328625508ULL, 0x12670b1a153f520cULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5a00634d938b08ULL, 0x12d8bb1a152b2514ULL, },
+        { 0x704f004d4d31c708ULL, 0x12f1881a153f52a0ULL, },
+        { 0x885aaeaa28625540ULL, 0x27670b5eab2b250cULL, },    /*  72  */
+        { 0xac5a00634d938b08ULL, 0x12d8bb1a152b2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e318b4eULL, 0x27d888d8a92b2514ULL, },
+        { 0x704f164d28315540ULL, 0x4b670b5ea942b00cULL, },
+        { 0x704f004d4d31c708ULL, 0x12f1881a153f52a0ULL, },
+        { 0x704f164d5e318b4eULL, 0x27d888d8a92b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_U_B(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_U_B(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_d.c
new file mode 100644
index 0000000000..4626c62354
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_d.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_U.D
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_U.D";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x5555555555555555ULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0x38e38e38e38e38e3ULL, },
+        { 0x5555555555555555ULL, 0x38e38e38e38e38e3ULL, },
+        { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x1c71c71c71c71c71ULL, 0x5555555555555555ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, },
+        { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, },
+        { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1a153f52fcULL, },
+        { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, },
+        { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, },    /*  72  */
+        { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, },
+        { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, },
+        { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_U_D(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_U_D(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_h.c
new file mode 100644
index 0000000000..5eeb8d034b
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_h.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_U.H
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_U.H";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaa38e38e38aaaaULL, 0x38e38e38aaaa38e3ULL, },
+        { 0x1c71aaaa71c71c71ULL, 0xaaaa71c71c71aaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x555538e355555555ULL, 0x38e35555555538e3ULL, },
+        { 0x1c71555555551c71ULL, 0x555555551c715555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xcccc38e38e38ccccULL, 0x38e38e38cccc38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x1c71333333331c71ULL, 0x333333331c713333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaa38e38e38aaaaULL, 0x38e38e38aaaa38e3ULL, },
+        { 0x555538e355555555ULL, 0x38e35555555538e3ULL, },
+        { 0xcccc38e38e38ccccULL, 0x38e38e38cccc38e3ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x1c71aaaa71c71c71ULL, 0xaaaa71c71c71aaaaULL, },
+        { 0x1c71555555551c71ULL, 0x555555551c715555ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71333333331c71ULL, 0x333333331c713333ULL, },
+        { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886a006328625540ULL, 0x12f70b5e153f52fcULL, },
+        { 0x886aaeaa28625540ULL, 0x27d80b5eab2b2514ULL, },
+        { 0x704f164d28625540ULL, 0x4b670b5ea942b00cULL, },
+        { 0x886a006328625540ULL, 0x12f70b5e153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5a00634d938b80ULL, 0x12f7bb1a153f2514ULL, },
+        { 0x704f00634d93c708ULL, 0x12f788d8153f52fcULL, },
+        { 0x886aaeaa28625540ULL, 0x27d80b5eab2b2514ULL, },    /*  72  */
+        { 0xac5a00634d938b80ULL, 0x12f7bb1a153f2514ULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e318b80ULL, 0x27d888d8a9422514ULL, },
+        { 0x704f164d28625540ULL, 0x4b670b5ea942b00cULL, },
+        { 0x704f00634d93c708ULL, 0x12f788d8153f52fcULL, },
+        { 0x704f164d5e318b80ULL, 0x27d888d8a9422514ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_U_H(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_U_H(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_w.c
new file mode 100644
index 0000000000..e70964afa8
--- /dev/null
+++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_w.c
@@ -0,0 +1,153 @@
+/*
+ *  Test program for MSA instruction MIN_U.W
+ *
+ *  Copyright (C) 2018  Wave Computing, Inc.
+ *  Copyright (C) 2018  Aleksandar Markovic <amarkovic@wavecomp.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sys/time.h>
+#include <stdint.h>
+
+#include "../../../../include/wrappers_msa.h"
+#include "../../../../include/test_inputs.h"
+#include "../../../../include/test_utils.h"
+
+#define TEST_COUNT_TOTAL (                                                \
+            (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \
+            (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT))
+
+
+int32_t main(void)
+{
+    char *instruction_name = "MIN_U.W";
+    int32_t ret;
+    uint32_t i, j;
+    struct timeval start, end;
+    double elapsed_time;
+
+    uint64_t b128_result[TEST_COUNT_TOTAL][2];
+    uint64_t b128_expect[TEST_COUNT_TOTAL][2] = {
+        { 0xffffffffffffffffULL, 0xffffffffffffffffULL, },    /*   0  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },    /*   8  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },    /*  16  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xaaaaaaaa8e38e38eULL, 0x38e38e38aaaaaaaaULL, },
+        { 0x1c71c71c71c71c71ULL, 0xaaaaaaaa1c71c71cULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },    /*  24  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x5555555555555555ULL, 0x38e38e3855555555ULL, },
+        { 0x1c71c71c55555555ULL, 0x555555551c71c71cULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },    /*  32  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x5555555555555555ULL, },
+        { 0xccccccccccccccccULL, 0xccccccccccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xcccccccc8e38e38eULL, 0x38e38e38ccccccccULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },    /*  40  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0x1c71c71c33333333ULL, 0x333333331c71c71cULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },    /*  48  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0xaaaaaaaa8e38e38eULL, 0x38e38e38aaaaaaaaULL, },
+        { 0x5555555555555555ULL, 0x38e38e3855555555ULL, },
+        { 0xcccccccc8e38e38eULL, 0x38e38e38ccccccccULL, },
+        { 0x3333333333333333ULL, 0x3333333333333333ULL, },
+        { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },    /*  56  */
+        { 0x0000000000000000ULL, 0x0000000000000000ULL, },
+        { 0x1c71c71c71c71c71ULL, 0xaaaaaaaa1c71c71cULL, },
+        { 0x1c71c71c55555555ULL, 0x555555551c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x1c71c71c33333333ULL, 0x333333331c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, },
+        { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, },
+        { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, },    /*  64  */
+        { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, },
+        { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d28625540ULL, 0x4b670b5ea942e2a0ULL, },
+        { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaa4d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0x704f164d4d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, },    /*  72  */
+        { 0xac5aaeaa4d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, },
+        { 0x704f164d5e31e24eULL, 0x27d8c6ffa942e2a0ULL, },
+        { 0x704f164d28625540ULL, 0x4b670b5ea942e2a0ULL, },
+        { 0x704f164d4d93c708ULL, 0x12f7bb1a153f52fcULL, },
+        { 0x704f164d5e31e24eULL, 0x27d8c6ffa942e2a0ULL, },
+        { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, },
+    };
+
+    gettimeofday(&start, NULL);
+
+    for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_U_W(b128_pattern[i], b128_pattern[j],
+                           b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) {
+        for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) {
+            do_msa_MIN_U_W(b128_random[i], b128_random[j],
+                           b128_result[((PATTERN_INPUTS_SHORT_COUNT) *
+                                        (PATTERN_INPUTS_SHORT_COUNT)) +
+                                       RANDOM_INPUTS_SHORT_COUNT * i + j]);
+        }
+    }
+
+    gettimeofday(&end, NULL);
+
+    elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0;
+    elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0;
+
+    ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time,
+                        &b128_result[0][0], &b128_expect[0][0]);
+
+    return ret;
+}
diff --git a/tests/tcg/xtensa/Makefile b/tests/tcg/xtensa/Makefile
index 2f5691f75b..2bd4491769 100644
--- a/tests/tcg/xtensa/Makefile
+++ b/tests/tcg/xtensa/Makefile
@@ -18,6 +18,9 @@ CC      = $(CROSS)gcc
 AS      = $(CROSS)gcc -x assembler-with-cpp
 LD      = $(CROSS)ld
 
+ASFLAGS = -Wa,--no-absolute-literals
+vectors_ASFLAGS = -mtext-section-literals
+
 XTENSA_SRC_PATH = $(SRC_PATH)/tests/tcg/xtensa
 INCLUDE_DIRS = $(XTENSA_SRC_PATH) $(SRC_PATH)/target/xtensa/core-$(CORE)
 XTENSA_INC = $(addprefix -I,$(INCLUDE_DIRS))
@@ -28,15 +31,21 @@ CRT        = crt.o vectors.o
 
 TESTCASES += test_b.tst
 TESTCASES += test_bi.tst
-#TESTCASES += test_boolean.tst
+TESTCASES += test_boolean.tst
 TESTCASES += test_break.tst
 TESTCASES += test_bz.tst
 TESTCASES += test_cache.tst
 TESTCASES += test_clamps.tst
 TESTCASES += test_extui.tst
 TESTCASES += test_fail.tst
+TESTCASES += test_flix.tst
+TESTCASES += test_fp0_arith.tst
+TESTCASES += test_fp0_conv.tst
+TESTCASES += test_fp1.tst
+TESTCASES += test_fp_cpenable.tst
 TESTCASES += test_interrupt.tst
 TESTCASES += test_loop.tst
+TESTCASES += test_lsc.tst
 TESTCASES += test_mac16.tst
 TESTCASES += test_max.tst
 TESTCASES += test_min.tst
@@ -68,7 +77,7 @@ linker.ld: $(XTENSA_SRC_PATH)/linker.ld.S
 	$(CC) $(XTENSA_INC) $(CFLAGS) -c $< -o $@
 
 %.o: $(XTENSA_SRC_PATH)/%.S
-	$(CC) $(XTENSA_INC) $(ASFLAGS) -c $< -o $@
+	$(CC) $(XTENSA_INC) $($*_ASFLAGS) $(ASFLAGS) -c $< -o $@
 
 %.tst: %.o linker.ld $(XTENSA_SRC_PATH)/macros.inc $(CRT) Makefile
 	$(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $< -o $@
diff --git a/tests/tcg/xtensa/linker.ld.S b/tests/tcg/xtensa/linker.ld.S
index d0f33157ca..ac89b0054e 100644
--- a/tests/tcg/xtensa/linker.ld.S
+++ b/tests/tcg/xtensa/linker.ld.S
@@ -1,17 +1,29 @@
 #include "core-isa.h"
 
-#if XTENSA_HAVE_BE
+#ifndef XCHAL_VECBASE_RESET_VADDR
+#define XCHAL_VECBASE_RESET_VADDR XCHAL_WINDOW_VECTORS_VADDR
+#define XCHAL_WINDOW_OF4_VECOFS   0x00000000
+#define XCHAL_WINDOW_UF4_VECOFS   0x00000040
+#define XCHAL_WINDOW_OF8_VECOFS   0x00000080
+#define XCHAL_WINDOW_UF8_VECOFS   0x000000C0
+#define XCHAL_WINDOW_OF12_VECOFS  0x00000100
+#define XCHAL_WINDOW_UF12_VECOFS  0x00000140
+#endif
+
+#define RAM_SIZE 0x08000000  /* 128M */
+#define ROM_SIZE 0x00001000  /* 4k */
+#define VECTORS_RESERVED_SIZE 0x1000
+
+#if XCHAL_HAVE_BE
 OUTPUT_FORMAT("elf32-xtensa-be")
 #else
 OUTPUT_FORMAT("elf32-xtensa-le")
 #endif
 ENTRY(_start)
 
-__DYNAMIC = 0;
-
 MEMORY {
-    ram : ORIGIN = XCHAL_VECBASE_RESET_VADDR, LENGTH = 0x08000000  /* 128M */
-    rom : ORIGIN = XCHAL_RESET_VECTOR_VADDR, LENGTH = 0x00001000  /* 4k */
+    ram : ORIGIN = XCHAL_VECBASE_RESET_VADDR, LENGTH = RAM_SIZE
+    rom : ORIGIN = XCHAL_RESET_VECTOR_VADDR, LENGTH = ROM_SIZE
 }
 
 SECTIONS
@@ -22,9 +34,9 @@ SECTIONS
         *(.init.*)
     } > rom
 
-    .vector :
-    {
 #if XCHAL_HAVE_WINDOWED
+    .vector.window XCHAL_WINDOW_VECTORS_VADDR :
+    {
     . = XCHAL_WINDOW_OF4_VECOFS;
         *(.vector.window_overflow_4)
     . = XCHAL_WINDOW_UF4_VECOFS;
@@ -37,41 +49,58 @@ SECTIONS
         *(.vector.window_overflow_12)
     . = XCHAL_WINDOW_UF12_VECOFS;
         *(.vector.window_underflow_12)
+    }
 #endif
 #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 2
-    . = XCHAL_INTLEVEL2_VECOFS;
+    .vector.level2 XCHAL_INTLEVEL2_VECTOR_VADDR :
+    {
         *(.vector.level2)
+    }
 #endif
 #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 3
-    . = XCHAL_INTLEVEL3_VECOFS;
+    .vector.level3 XCHAL_INTLEVEL3_VECTOR_VADDR :
+    {
         *(.vector.level3)
+    }
 #endif
 #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 4
-    . = XCHAL_INTLEVEL4_VECOFS;
+    .vector.level4 XCHAL_INTLEVEL4_VECTOR_VADDR :
+    {
         *(.vector.level4)
+    }
 #endif
 #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 5
-    . = XCHAL_INTLEVEL5_VECOFS;
+    .vector.level5 XCHAL_INTLEVEL5_VECTOR_VADDR :
+    {
         *(.vector.level5)
+    }
 #endif
 #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 6
-    . = XCHAL_INTLEVEL6_VECOFS;
+    .vector.level6 XCHAL_INTLEVEL6_VECTOR_VADDR :
+    {
         *(.vector.level6)
+    }
 #endif
 #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 7
-    . = XCHAL_INTLEVEL7_VECOFS;
+    .vector.level7 XCHAL_INTLEVEL7_VECTOR_VADDR :
+    {
         *(.vector.level7)
+    }
 #endif
-
-    . = XCHAL_KERNEL_VECOFS;
+    .vector.kernel XCHAL_KERNEL_VECTOR_VADDR :
+    {
         *(.vector.kernel)
-    . = XCHAL_USER_VECOFS;
+    }
+    .vector.user XCHAL_USER_VECTOR_VADDR :
+    {
         *(.vector.user)
-    . = XCHAL_DOUBLEEXC_VECOFS;
+    }
+    .vector.double XCHAL_DOUBLEEXC_VECTOR_VADDR :
+    {
         *(.vector.double)
-    } > ram
+    }
 
-    .vector.text :
+    .vector.text XCHAL_VECBASE_RESET_VADDR + VECTORS_RESERVED_SIZE :
     {
         *(.vector.window_overflow_4.*)
         *(.vector.window_underflow_4.*)
diff --git a/tests/tcg/xtensa/macros.inc b/tests/tcg/xtensa/macros.inc
index 4ebd30ab86..aa8f95bce8 100644
--- a/tests/tcg/xtensa/macros.inc
+++ b/tests/tcg/xtensa/macros.inc
@@ -23,11 +23,14 @@ main:
     movi    a0, result
     sub     a2, a2, a0
     movi    a3, 0
-    loopnez a2, 1f
-    l8ui    a2, a0, 0
-    or      a3, a3, a2
-    addi    a0, a0, 1
+    beqz    a2, 2f
 1:
+    l8ui    a1, a0, 0
+    or      a3, a3, a1
+    addi    a0, a0, 1
+    addi    a2, a2, -1
+    bnez    a2, 1b
+2:
     exit
 .endm
 
@@ -49,7 +52,9 @@ main:
 .endm
 
 .macro test name
-    //print test_\name
+#ifdef DEBUG
+    print test_\name
+#endif
     test_init
 test_\name:
 .global test_\name
@@ -74,6 +79,9 @@ test_\name:
     l32i    a2, a2, 0
     movi    a3, 1
     s8i     a3, a2, 0
+#ifdef DEBUG
+    print   failed
+#endif
     j       99f
 .endm
 
@@ -89,3 +97,26 @@ test_\name:
     movi    a3, \addr
     s32i    a3, a2, 0
 .endm
+
+.macro dump r
+#ifdef DEBUG
+.data
+.align 4
+1: .word 0
+.text
+    movi    a4, 1b
+    s32i    a2, a4, 0
+    movi    a2, 4
+    movi    a3, 1
+    movi    a5, 4
+    simcall
+    movi    a4, 1b
+    l32i    a2, a4, 0
+#endif
+.endm
+
+#define glue(a, b) _glue(a, b)
+#define _glue(a, b) a ## b
+
+#define glue3(a, b, c) _glue3(a, b, c)
+#define _glue3(a, b, c) a ## b ## c
diff --git a/tests/tcg/xtensa/test_b.S b/tests/tcg/xtensa/test_b.S
index 8e81f956df..713a454c53 100644
--- a/tests/tcg/xtensa/test_b.S
+++ b/tests/tcg/xtensa/test_b.S
@@ -84,12 +84,24 @@ test_end
 
 test bbc
     movi    a2, 0xfffffffd
-    movi    a3, 0xffffff01
+#undef BIT
+#if XCHAL_HAVE_BE
+#define BIT 0xfffffffe
+#else
+#define BIT 0xffffff01
+#endif
+    movi    a3, BIT
     bbc     a2, a3, 1f
     test_fail
 1:
     movi    a2, 8
-    movi    a3, 0xffffff03
+#undef BIT
+#if XCHAL_HAVE_BE
+#define BIT 0xfffffffc
+#else
+#define BIT 0xffffff03
+#endif
+    movi    a3, BIT
     bbc     a2, a3, 1f
     j       2f
 1:
@@ -99,11 +111,11 @@ test_end
 
 test bbci
     movi    a2, 0xfffdffff
-    bbci    a2, 17, 1f
+    bbci.l  a2, 17, 1f
     test_fail
 1:
     movi    a2, 0x00020000
-    bbci    a2, 17, 1f
+    bbci.l  a2, 17, 1f
     j       2f
 1:
     test_fail
@@ -192,12 +204,24 @@ test_end
 
 test bbs
     movi    a2, 8
-    movi    a3, 0xffffff03
+#undef BIT
+#if XCHAL_HAVE_BE
+#define BIT 0xfffffffc
+#else
+#define BIT 0xffffff03
+#endif
+    movi    a3, BIT
     bbs     a2, a3, 1f
     test_fail
 1:
     movi    a2, 0xfffffffd
-    movi    a3, 0xffffff01
+#undef BIT
+#if XCHAL_HAVE_BE
+#define BIT 0xfffffffe
+#else
+#define BIT 0xffffff01
+#endif
+    movi    a3, BIT
     bbs     a2, a3, 1f
     j       2f
 1:
@@ -207,11 +231,11 @@ test_end
 
 test bbsi
     movi    a2, 0x00020000
-    bbsi    a2, 17, 1f
+    bbsi.l  a2, 17, 1f
     test_fail
 1:
     movi    a2, 0xfffdffff
-    bbsi    a2, 17, 1f
+    bbsi.l  a2, 17, 1f
     j       2f
 1:
     test_fail
diff --git a/tests/tcg/xtensa/test_boolean.S b/tests/tcg/xtensa/test_boolean.S
index eac40e0973..5a850bfe7e 100644
--- a/tests/tcg/xtensa/test_boolean.S
+++ b/tests/tcg/xtensa/test_boolean.S
@@ -2,6 +2,8 @@
 
 test_suite boolean
 
+#if XCHAL_HAVE_BOOLEANS
+
 test all4
     movi    a2, 0xfec0
     wsr     a2, br
@@ -20,4 +22,6 @@ test all4
     assert  eq, a2, a3
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_break.S b/tests/tcg/xtensa/test_break.S
index 775cd7c260..3379a3f9f0 100644
--- a/tests/tcg/xtensa/test_break.S
+++ b/tests/tcg/xtensa/test_break.S
@@ -1,10 +1,13 @@
 #include "macros.inc"
 
-#define debug_level 6
-#define debug_vector level6
-
 test_suite break
 
+#if XCHAL_HAVE_DEBUG
+
+#define debug_level XCHAL_DEBUGLEVEL
+#define debug_vector glue(level, XCHAL_DEBUGLEVEL)
+#define EPC_DEBUG glue(epc, XCHAL_DEBUGLEVEL)
+
 test break
     set_vector debug_vector, 0
     rsil    a2, debug_level
@@ -21,7 +24,7 @@ test break
     and     a2, a2, a3
     movi    a3, 0x10 | debug_level
     assert  eq, a2, a3
-    rsr     a2, epc6
+    rsr     a2, EPC_DEBUG
     movi    a3, 1b
     assert  eq, a2, a3
     rsr     a2, debugcause
@@ -45,7 +48,7 @@ test breakn
     and     a2, a2, a3
     movi    a3, 0x10 | debug_level
     assert  eq, a2, a3
-    rsr     a2, epc6
+    rsr     a2, EPC_DEBUG
     movi    a3, 1b
     assert  eq, a2, a3
     rsr     a2, debugcause
@@ -53,6 +56,7 @@ test breakn
     assert  eq, a2, a3
 test_end
 
+#if XCHAL_NUM_IBREAK
 test ibreak
     set_vector debug_vector, 0
     rsil    a2, debug_level
@@ -83,7 +87,7 @@ test ibreak
     and     a2, a2, a3
     movi    a3, 0x10 | debug_level
     assert  eq, a2, a3
-    rsr     a2, epc6
+    rsr     a2, EPC_DEBUG
     movi    a3, 1b
     assert  eq, a2, a3
     rsr     a2, debugcause
@@ -110,7 +114,7 @@ test ibreak_remove
     and     a2, a2, a3
     movi    a3, 0x10 | debug_level
     assert  eq, a2, a3
-    rsr     a2, epc6
+    rsr     a2, EPC_DEBUG
     movi    a3, 2b
     assert  eq, a2, a3
     rsr     a2, debugcause
@@ -141,6 +145,7 @@ test ibreak_priority
     movi    a3, 0x2
     assert  eq, a2, a3
 test_end
+#endif
 
 test icount
     set_vector debug_vector, 2f
@@ -158,7 +163,7 @@ test icount
 2:
     movi    a2, 0
     wsr     a2, icountlevel
-    rsr     a2, epc6
+    rsr     a2, EPC_DEBUG
     movi    a3, 1b
     assert  eq, a2, a3
     rsr     a2, debugcause
@@ -167,7 +172,7 @@ test icount
 test_end
 
 .macro check_dbreak dr
-    rsr     a2, epc6
+    rsr     a2, EPC_DEBUG
     movi    a3, 1b
     assert  eq, a2, a3
     rsr     a2, debugcause
@@ -194,6 +199,7 @@ test_end
     reset_ps
 .endm
 
+#if XCHAL_NUM_DBREAK
 test dbreak_exact
     dbreak_test 0, 0x4000003f, 0xd000007f, 0xd000007f, l8ui
     dbreak_test 1, 0x4000003e, 0xd000007e, 0xd000007e, l16ui
@@ -253,5 +259,8 @@ test dbreak_invalid
     dbreak_test 0, 0x40000030, 0xd0000071, 0xd0000070, l16ui
     dbreak_test 1, 0x40000035, 0xd0000072, 0xd0000070, l32i
 test_end
+#endif
+
+#endif
 
 test_suite_end
diff --git a/tests/tcg/xtensa/test_cache.S b/tests/tcg/xtensa/test_cache.S
index 6b2df9734b..7e6ba4c18a 100644
--- a/tests/tcg/xtensa/test_cache.S
+++ b/tests/tcg/xtensa/test_cache.S
@@ -7,6 +7,8 @@
 
 test_suite cache
 
+#if XCHAL_HAVE_PTP_MMU
+
 .macro      pf_op op
     \op     a2, 0
     \op     a3, 0
@@ -18,14 +20,23 @@ test prefetch
     movi    a3, 0xd8000000 /* non-cacheable */
     movi    a4, 0x00001235 /* unmapped */
 
+#if XCHAL_DCACHE_SIZE
     pf_op   dpfr
     pf_op   dpfro
     pf_op   dpfw
     pf_op   dpfwo
+#endif
+#ifdef XCHAL_ICACHE_SIZE
     pf_op   ipf
-
+#endif
+#if XCHAL_HAVE_PTP_MMU && !XCHAL_HAVE_SPANNING_WAY
+#if XCHAL_DCACHE_LINE_LOCKABLE
     dpfl    a2, 0
+#endif
+#if XCHAL_ICACHE_LINE_LOCKABLE
     ipfl    a2, 0
+#endif
+#endif
 test_end
 
 .macro cache_fault op, addr, exc_code
@@ -46,10 +57,16 @@ test_end
     assert  eq, a2, a3
 .endm
 
+#if XCHAL_HAVE_PTP_MMU && !XCHAL_HAVE_SPANNING_WAY
+
+#if XCHAL_DCACHE_LINE_LOCKABLE
 test dpfl_tlb_miss
     cache_fault dpfl, 0x00002345, 24
 test_end
+#endif
 
+#if XCHAL_DCACHE_SIZE
+#if XCHAL_DCACHE_IS_WRITEBACK
 test dhwb_tlb_miss
     cache_fault dhwb, 0x00002345, 24
 test_end
@@ -57,16 +74,21 @@ test_end
 test dhwbi_tlb_miss
     cache_fault dhwbi, 0x00002345, 24
 test_end
+#endif
 
 test dhi_tlb_miss
     cache_fault dhi, 0x00002345, 24
 test_end
 
+#if XCHAL_DCACHE_LINE_LOCKABLE
 test dhu_tlb_miss
     cache_fault dhu, 0x00002345, 24
 test_end
+#endif
+#endif
 
-
+#if XCHAL_ICACHE_SIZE
+#if XCHAL_ICACHE_LINE_LOCKABLE
 test ipfl_tlb_miss
     cache_fault ipfl, 0x00002345, 16
 test_end
@@ -74,24 +96,40 @@ test_end
 test ihu_tlb_miss
     cache_fault ihu, 0x00002345, 16
 test_end
+#endif
 
 test ihi_tlb_miss
     cache_fault ihi, 0x00002345, 16
 test_end
+#endif
+
+#endif
+
+#endif
 
 test_suite_end
 
-.macro cache_all op1, op2, size, linesize
+cache_unlock_invalidate:
+#if XCHAL_DCACHE_SIZE
     movi    a2, 0
-    movi    a3, \size
+    movi    a3, XCHAL_DCACHE_SIZE
 1:
-    \op1    a2, 0
-    \op2    a2, 0
-    addi    a2, a2, \linesize
+#if XCHAL_DCACHE_LINE_LOCKABLE
+    diu     a2, 0
+#endif
+    dii     a2, 0
+    addi    a2, a2, XCHAL_DCACHE_LINESIZE
     bltu    a2, a3, 1b
-.endm
-
-cache_unlock_invalidate:
-    cache_all diu, dii, XCHAL_DCACHE_SIZE, XCHAL_DCACHE_LINESIZE
-    cache_all iiu, iii, XCHAL_ICACHE_SIZE, XCHAL_ICACHE_LINESIZE
+#endif
+#if XCHAL_ICACHE_SIZE
+    movi    a2, 0
+    movi    a3, XCHAL_ICACHE_SIZE
+1:
+#if XCHAL_ICACHE_LINE_LOCKABLE
+    iiu     a2, 0
+#endif
+    iii     a2, 0
+    addi    a2, a2, XCHAL_ICACHE_LINESIZE
+    bltu    a2, a3, 1b
+#endif
     ret
diff --git a/tests/tcg/xtensa/test_clamps.S b/tests/tcg/xtensa/test_clamps.S
index 3efabfd9d3..d9b2c38ac1 100644
--- a/tests/tcg/xtensa/test_clamps.S
+++ b/tests/tcg/xtensa/test_clamps.S
@@ -2,6 +2,8 @@
 
 test_suite clamps
 
+#if XCHAL_HAVE_CLAMPS
+
 test clamps
     movi    a2, 0
     movi    a3, 0
@@ -39,4 +41,6 @@ test clamps
     assert  eq, a3, a2
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_flix.S b/tests/tcg/xtensa/test_flix.S
new file mode 100644
index 0000000000..7c259e7018
--- /dev/null
+++ b/tests/tcg/xtensa/test_flix.S
@@ -0,0 +1,60 @@
+#include "macros.inc"
+
+test_suite flix
+
+#if XCHAL_HAVE_FLIX3
+
+test misc
+    {
+        mov     a3, a4
+        mov     a2, a3
+        nop
+    }
+    {
+        nop
+        bne.w18 a2, a3, 1f
+    }
+    movi    a2, 1f
+    {
+        mov     a2, a3
+        mov     a3, a2
+        nop
+    }
+    {
+        l32i    a2, a3, 0
+        add     a4, a4, a2
+        nop
+    }
+    {
+        mov     a3, a4
+        jx      a3
+        nop
+    }
+1:
+test_end
+
+test sum
+
+    movi    a2, 0
+    movi    a3, 2f
+    movi    a4, 0
+    movi    a5, 4
+
+    loop    a5, 1f
+    {
+        l32i    a2, a3, 0
+        addi    a3, a3, 4
+        add     a4, a4, a2
+    }
+1:
+    add     a4, a4, a2
+    assert  eqi, a4, 10
+    .data
+2:
+    .word   1, 2, 3, 4
+    .previous
+test_end
+
+#endif
+
+test_suite_end
diff --git a/tests/tcg/xtensa/test_fp0_arith.S b/tests/tcg/xtensa/test_fp0_arith.S
new file mode 100644
index 0000000000..253d033a33
--- /dev/null
+++ b/tests/tcg/xtensa/test_fp0_arith.S
@@ -0,0 +1,173 @@
+#include "macros.inc"
+
+test_suite fp0_arith
+
+#if XCHAL_HAVE_FP
+
+.macro movfp fr, v
+    movi    a2, \v
+    wfr     \fr, a2
+.endm
+
+.macro check_res fr, r
+    rfr     a2, \fr
+    dump    a2
+    movi    a3, \r
+    assert  eq, a2, a3
+    rur     a2, fsr
+    assert  eqi, a2, 0
+.endm
+
+.macro test_op2_rm op, fr0, fr1, fr2, v0, v1, r
+    movi    a2, 0
+    wur     a2, fsr
+    movfp   \fr0, \v0
+    movfp   \fr1, \v1
+    \op     \fr2, \fr0, \fr1
+    check_res \fr2, \r
+.endm
+
+.macro test_op3_rm op, fr0, fr1, fr2, fr3, v0, v1, v2, r
+    movi    a2, 0
+    wur     a2, fsr
+    movfp   \fr0, \v0
+    movfp   \fr1, \v1
+    movfp   \fr2, \v2
+    \op     \fr0, \fr1, \fr2
+    check_res \fr3, \r
+.endm
+
+.macro test_op2_ex op, fr0, fr1, fr2, v0, v1, rm, r
+    movi    a2, \rm
+    wur     a2, fcr
+    test_op2_rm \op, \fr0, \fr1, \fr2, \v0, \v1, \r
+    movi    a2, (\rm) | 0x7c
+    wur     a2, fcr
+    test_op2_rm \op, \fr0, \fr1, \fr2, \v0, \v1, \r
+.endm
+
+.macro test_op3_ex op, fr0, fr1, fr2, fr3, v0, v1, v2, rm, r
+    movi    a2, \rm
+    wur     a2, fcr
+    test_op3_rm \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, \r
+    movi    a2, (\rm) | 0x7c
+    wur     a2, fcr
+    test_op3_rm \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, \r
+.endm
+
+.macro test_op2 op, fr0, fr1, fr2, v0, v1, r0, r1, r2, r3
+    test_op2_ex \op, \fr0, \fr1, \fr2, \v0, \v1, 0, \r0
+    test_op2_ex \op, \fr0, \fr1, \fr2, \v0, \v1, 1, \r1
+    test_op2_ex \op, \fr0, \fr1, \fr2, \v0, \v1, 2, \r2
+    test_op2_ex \op, \fr0, \fr1, \fr2, \v0, \v1, 3, \r3
+.endm
+
+.macro test_op3 op, fr0, fr1, fr2, fr3, v0, v1, v2, r0, r1, r2, r3
+    test_op3_ex \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, 0, \r0
+    test_op3_ex \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, 1, \r1
+    test_op3_ex \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, 2, \r2
+    test_op3_ex \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, 3, \r3
+.endm
+
+.macro test_op2_cpe op
+    set_vector  kernel, 2f
+    movi    a2, 0
+    wsr     a2, cpenable
+1:
+    \op     f2, f0, f1
+    test_fail
+2:
+    rsr     a2, excvaddr
+    movi    a3, 1b
+    assert  eq, a2, a3
+    rsr     a2, exccause
+    movi    a3, 32
+    assert  eq, a2, a3
+
+    set_vector  kernel, 0
+    movi    a2, 1
+    wsr     a2, cpenable
+.endm
+
+test add_s
+    movi    a2, 1
+    wsr     a2, cpenable
+
+    test_op2 add.s, f0, f1, f2, 0x3fc00000, 0x34400000, \
+        0x3fc00002, 0x3fc00001, 0x3fc00002, 0x3fc00001
+    test_op2 add.s, f3, f4, f5, 0x3fc00000, 0x34a00000, \
+        0x3fc00002, 0x3fc00002, 0x3fc00003, 0x3fc00002
+
+    /* MAX_FLOAT + MAX_FLOAT = +inf/MAX_FLOAT  */
+    test_op2 add.s, f6, f7, f8, 0x7f7fffff, 0x7f7fffff, \
+        0x7f800000, 0x7f7fffff, 0x7f800000, 0x7f7fffff
+test_end
+
+test add_s_inf
+    /* 1 + +inf = +inf  */
+    test_op2 add.s, f6, f7, f8, 0x3fc00000, 0x7f800000, \
+        0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000
+
+    /* +inf + -inf = default NaN */
+    test_op2 add.s, f0, f1, f2, 0x7f800000, 0xff800000, \
+        0x7fc00000, 0x7fc00000, 0x7fc00000, 0x7fc00000
+test_end
+
+test add_s_nan
+    /* 1 + NaN = NaN  */
+    test_op2 add.s, f9, f10, f11, 0x3fc00000, 0x7fc00001, \
+        0x7fc00001, 0x7fc00001, 0x7fc00001, 0x7fc00001
+    test_op2 add.s, f12, f13, f14, 0x3fc00000, 0x7f800001, \
+        0x7f800001, 0x7f800001, 0x7f800001, 0x7f800001
+
+    /* NaN1 + NaN2 = NaN1 */
+    test_op2 add.s, f15, f0, f1, 0x7f800001, 0x7fbfffff, \
+        0x7f800001, 0x7f800001, 0x7f800001, 0x7f800001
+    test_op2 add.s, f2, f3, f4, 0x7fbfffff, 0x7f800001, \
+        0x7fbfffff, 0x7fbfffff, 0x7fbfffff, 0x7fbfffff
+    test_op2 add.s, f5, f6, f7, 0x7fc00001, 0x7fbfffff, \
+        0x7fc00001, 0x7fc00001, 0x7fc00001, 0x7fc00001
+    test_op2 add.s, f8, f9, f10, 0x7fbfffff, 0x7fc00001, \
+        0x7fbfffff, 0x7fbfffff, 0x7fbfffff, 0x7fbfffff
+test_end
+
+test sub_s
+    test_op2 sub.s, f0, f1, f0, 0x3f800001, 0x33800000, \
+        0x3f800000, 0x3f800000, 0x3f800001, 0x3f800000
+    test_op2 sub.s, f0, f1, f1, 0x3f800002, 0x33800000, \
+        0x3f800002, 0x3f800001, 0x3f800002, 0x3f800001
+
+    /* norm - norm = denorm */
+    test_op2 sub.s, f6, f7, f8, 0x00800001, 0x00800000, \
+        0x00000001, 0x00000001, 0x00000001, 0x00000001
+test_end
+
+test mul_s
+    test_op2 mul.s, f0, f1, f2, 0x3f800001, 0x3f800001, \
+        0x3f800002, 0x3f800002, 0x3f800003, 0x3f800002
+
+    /* MAX_FLOAT/2 * MAX_FLOAT/2 = +inf/MAX_FLOAT  */
+    test_op2 mul.s, f6, f7, f8, 0x7f000000, 0x7f000000, \
+        0x7f800000, 0x7f7fffff, 0x7f800000, 0x7f7fffff
+    /* min norm * min norm = 0/denorm */
+    test_op2 mul.s, f6, f7, f8, 0x00800001, 0x00800000, \
+        0x00000000, 0x00000000, 0x00000001, 0x00000000
+
+    /* inf * 0 = default NaN */
+    test_op2 mul.s, f6, f7, f8, 0x7f800000, 0x00000000, \
+        0x7fc00000, 0x7fc00000, 0x7fc00000, 0x7fc00000
+test_end
+
+test madd_s
+    test_op3 madd.s, f0, f1, f2, f0, 0, 0x3f800001, 0x3f800001, \
+        0x3f800002, 0x3f800002, 0x3f800003, 0x3f800002
+test_end
+
+test msub_s
+    test_op3 msub.s, f0, f1, f2, f0, 0x3f800000, 0x3f800001, 0x3f800001, \
+        0xb4800000, 0xb4800000, 0xb4800000, 0xb4800001
+test_end
+
+#endif
+
+test_suite_end
diff --git a/tests/tcg/xtensa/test_fp0_conv.S b/tests/tcg/xtensa/test_fp0_conv.S
new file mode 100644
index 0000000000..147e3d5062
--- /dev/null
+++ b/tests/tcg/xtensa/test_fp0_conv.S
@@ -0,0 +1,304 @@
+#include "macros.inc"
+
+test_suite fp0_conv
+
+#if XCHAL_HAVE_FP
+
+.macro movfp fr, v
+    movi    a2, \v
+    wfr     \fr, a2
+.endm
+
+.macro test_ftoi_ex op, r0, fr0, v, c, r
+    movi    a2, 0
+    wur     a2, fsr
+    movfp   \fr0, \v
+    \op     \r0, \fr0, \c
+    dump    \r0
+    movi    a3, \r
+    assert  eq, \r0, a3
+    rur     a2, fsr
+    assert  eqi, a2, 0
+.endm
+
+.macro test_ftoi op, r0, fr0, v, c, r
+    movi    a2, 0
+    wur     a2, fcr
+    test_ftoi_ex \op, \r0, \fr0, \v, \c, \r
+    movi    a2, 0x7c
+    wur     a2, fcr
+    test_ftoi_ex \op, \r0, \fr0, \v, \c, \r
+.endm
+
+
+.macro test_itof_ex op, fr0, ar0, v, c, r
+    movi    a2, 0
+    wur     a2, fsr
+    movi    \ar0, \v
+    \op     \fr0, \ar0, \c
+
+    rfr     a2, \fr0
+    dump    a2
+    movi    a3, \r
+    assert  eq, a2, a3
+    rur     a2, fsr
+    assert  eqi, a2, 0
+.endm
+
+.macro test_itof_rm op, fr0, ar0, v, c, rm, r
+    movi    a2, \rm
+    wur     a2, fcr
+    test_itof_ex \op, \fr0, \ar0, \v, \c, \r
+    movi    a2, (\rm) | 0x7c
+    wur     a2, fcr
+    test_itof_ex \op, \fr0, \ar0, \v, \c, \r
+.endm
+
+.macro test_itof op, fr0, ar0, v, c, r0, r1, r2, r3
+    test_itof_rm \op, \fr0, \ar0, \v, \c, 0, \r0
+    test_itof_rm \op, \fr0, \ar0, \v, \c, 1, \r1
+    test_itof_rm \op, \fr0, \ar0, \v, \c, 2, \r2
+    test_itof_rm \op, \fr0, \ar0, \v, \c, 3, \r3
+.endm
+
+test round_s
+    movi    a2, 1
+    wsr     a2, cpenable
+
+    /* NaN */
+    test_ftoi round.s, a2, f0, 0xffc00001, 0, 0x7fffffff
+    test_ftoi round.s, a2, f0, 0xff800001, 0, 0x7fffffff
+
+    /* -inf */
+    test_ftoi round.s, a2, f0, 0xff800000, 0, 0x80000000
+
+    /* negative overflow */
+    test_ftoi round.s, a2, f0, 0xceffffff, 1, 0x80000000
+    test_ftoi round.s, a2, f0, 0xcf000000, 0, 0x80000000
+    test_ftoi round.s, a2, f0, 0xceffffff, 0, 0x80000080
+
+    /* negative */
+    test_ftoi round.s, a2, f0, 0xbfa00000, 1, -2 /* -1.25 * 2 */
+    test_ftoi round.s, a2, f0, 0xbfc00000, 0, -2 /* -1.5 */
+    test_ftoi round.s, a2, f0, 0xbf800000, 1, -2 /* -1 * 2 */
+    test_ftoi round.s, a2, f0, 0xbf800000, 0, -1 /* -1 */
+    test_ftoi round.s, a2, f0, 0xbf400000, 0, -1 /* -0.75 */
+    test_ftoi round.s, a2, f0, 0xbf000000, 0, 0  /* -0.5 */
+
+    /* positive */
+    test_ftoi round.s, a2, f0, 0x3f000000, 0, 0 /* 0.5 */
+    test_ftoi round.s, a2, f0, 0x3f400000, 0, 1 /* 0.75 */
+    test_ftoi round.s, a2, f0, 0x3f800000, 0, 1 /* 1 */
+    test_ftoi round.s, a2, f0, 0x3f800000, 1, 2 /* 1 * 2 */
+    test_ftoi round.s, a2, f0, 0x3fc00000, 0, 2 /* 1.5 */
+    test_ftoi round.s, a2, f0, 0x3fa00000, 1, 2 /* 1.25 * 2 */
+
+    /* positive overflow */
+    test_ftoi round.s, a2, f0, 0x4effffff, 0, 0x7fffff80
+    test_ftoi round.s, a2, f0, 0x4f000000, 0, 0x7fffffff
+    test_ftoi round.s, a2, f0, 0x4effffff, 1, 0x7fffffff
+
+    /* +inf */
+    test_ftoi round.s, a2, f0, 0x7f800000, 0, 0x7fffffff
+
+    /* NaN */
+    test_ftoi round.s, a2, f0, 0x7f800001, 0, 0x7fffffff
+    test_ftoi round.s, a2, f0, 0x7fc00000, 0, 0x7fffffff
+test_end
+
+test trunc_s
+    /* NaN */
+    test_ftoi trunc.s, a2, f0, 0xffc00001, 0, 0x7fffffff
+    test_ftoi trunc.s, a2, f0, 0xff800001, 0, 0x7fffffff
+
+    /* -inf */
+    test_ftoi trunc.s, a2, f0, 0xff800000, 0, 0x80000000
+
+    /* negative overflow */
+    test_ftoi trunc.s, a2, f0, 0xceffffff, 1, 0x80000000
+    test_ftoi trunc.s, a2, f0, 0xcf000000, 0, 0x80000000
+    test_ftoi trunc.s, a2, f0, 0xceffffff, 0, 0x80000080
+
+    /* negative */
+    test_ftoi trunc.s, a2, f0, 0xbfa00000, 1, -2 /* -1.25 * 2 */
+    test_ftoi trunc.s, a2, f0, 0xbfc00000, 0, -1 /* -1.5 */
+    test_ftoi trunc.s, a2, f0, 0xbf800000, 1, -2 /* -1 * 2 */
+    test_ftoi trunc.s, a2, f0, 0xbf800000, 0, -1 /* -1 */
+    test_ftoi trunc.s, a2, f0, 0xbf400000, 0, 0  /* -0.75 */
+    test_ftoi trunc.s, a2, f0, 0xbf000000, 0, 0  /* -0.5 */
+
+    /* positive */
+    test_ftoi trunc.s, a2, f0, 0x3f000000, 0, 0 /* 0.5 */
+    test_ftoi trunc.s, a2, f0, 0x3f400000, 0, 0 /* 0.75 */
+    test_ftoi trunc.s, a2, f0, 0x3f800000, 0, 1 /* 1 */
+    test_ftoi trunc.s, a2, f0, 0x3f800000, 1, 2 /* 1 * 2 */
+    test_ftoi trunc.s, a2, f0, 0x3fc00000, 0, 1 /* 1.5 */
+    test_ftoi trunc.s, a2, f0, 0x3fa00000, 1, 2 /* 1.25 * 2 */
+
+    /* positive overflow */
+    test_ftoi trunc.s, a2, f0, 0x4effffff, 0, 0x7fffff80
+    test_ftoi trunc.s, a2, f0, 0x4f000000, 0, 0x7fffffff
+    test_ftoi trunc.s, a2, f0, 0x4effffff, 1, 0x7fffffff
+
+    /* +inf */
+    test_ftoi trunc.s, a2, f0, 0x7f800000, 0, 0x7fffffff
+
+    /* NaN */
+    test_ftoi trunc.s, a2, f0, 0x7f800001, 0, 0x7fffffff
+    test_ftoi trunc.s, a2, f0, 0x7fc00000, 0, 0x7fffffff
+test_end
+
+test floor_s
+    /* NaN */
+    test_ftoi floor.s, a2, f0, 0xffc00001, 0, 0x7fffffff
+    test_ftoi floor.s, a2, f0, 0xff800001, 0, 0x7fffffff
+
+    /* -inf */
+    test_ftoi floor.s, a2, f0, 0xff800000, 0, 0x80000000
+
+    /* negative overflow */
+    test_ftoi floor.s, a2, f0, 0xceffffff, 1, 0x80000000
+    test_ftoi floor.s, a2, f0, 0xcf000000, 0, 0x80000000
+    test_ftoi floor.s, a2, f0, 0xceffffff, 0, 0x80000080
+
+    /* negative */
+    test_ftoi floor.s, a2, f0, 0xbfa00000, 1, -3 /* -1.25 * 2 */
+    test_ftoi floor.s, a2, f0, 0xbfc00000, 0, -2 /* -1.5 */
+    test_ftoi floor.s, a2, f0, 0xbf800000, 1, -2 /* -1 * 2 */
+    test_ftoi floor.s, a2, f0, 0xbf800000, 0, -1 /* -1 */
+    test_ftoi floor.s, a2, f0, 0xbf400000, 0, -1 /* -0.75 */
+    test_ftoi floor.s, a2, f0, 0xbf000000, 0, -1 /* -0.5 */
+
+    /* positive */
+    test_ftoi floor.s, a2, f0, 0x3f000000, 0, 0 /* 0.5 */
+    test_ftoi floor.s, a2, f0, 0x3f400000, 0, 0 /* 0.75 */
+    test_ftoi floor.s, a2, f0, 0x3f800000, 0, 1 /* 1 */
+    test_ftoi floor.s, a2, f0, 0x3f800000, 1, 2 /* 1 * 2 */
+    test_ftoi floor.s, a2, f0, 0x3fc00000, 0, 1 /* 1.5 */
+    test_ftoi floor.s, a2, f0, 0x3fa00000, 1, 2 /* 1.25 * 2 */
+
+    /* positive overflow */
+    test_ftoi floor.s, a2, f0, 0x4effffff, 0, 0x7fffff80
+    test_ftoi floor.s, a2, f0, 0x4f000000, 0, 0x7fffffff
+    test_ftoi floor.s, a2, f0, 0x4effffff, 1, 0x7fffffff
+
+    /* +inf */
+    test_ftoi floor.s, a2, f0, 0x7f800000, 0, 0x7fffffff
+
+    /* NaN */
+    test_ftoi floor.s, a2, f0, 0x7f800001, 0, 0x7fffffff
+    test_ftoi floor.s, a2, f0, 0x7fc00000, 0, 0x7fffffff
+test_end
+
+test ceil_s
+    /* NaN */
+    test_ftoi ceil.s, a2, f0, 0xffc00001, 0, 0x7fffffff
+    test_ftoi ceil.s, a2, f0, 0xff800001, 0, 0x7fffffff
+
+    /* -inf */
+    test_ftoi ceil.s, a2, f0, 0xff800000, 0, 0x80000000
+
+    /* negative overflow */
+    test_ftoi ceil.s, a2, f0, 0xceffffff, 1, 0x80000000
+    test_ftoi ceil.s, a2, f0, 0xcf000000, 0, 0x80000000
+    test_ftoi ceil.s, a2, f0, 0xceffffff, 0, 0x80000080
+
+    /* negative */
+    test_ftoi ceil.s, a2, f0, 0xbfa00000, 1, -2 /* -1.25 * 2 */
+    test_ftoi ceil.s, a2, f0, 0xbfc00000, 0, -1 /* -1.5 */
+    test_ftoi ceil.s, a2, f0, 0xbf800000, 1, -2 /* -1 * 2 */
+    test_ftoi ceil.s, a2, f0, 0xbf800000, 0, -1 /* -1 */
+    test_ftoi ceil.s, a2, f0, 0xbf400000, 0, 0  /* -0.75 */
+    test_ftoi ceil.s, a2, f0, 0xbf000000, 0, 0  /* -0.5 */
+
+    /* positive */
+    test_ftoi ceil.s, a2, f0, 0x3f000000, 0, 1 /* 0.5 */
+    test_ftoi ceil.s, a2, f0, 0x3f400000, 0, 1 /* 0.75 */
+    test_ftoi ceil.s, a2, f0, 0x3f800000, 0, 1 /* 1 */
+    test_ftoi ceil.s, a2, f0, 0x3f800000, 1, 2 /* 1 * 2 */
+    test_ftoi ceil.s, a2, f0, 0x3fc00000, 0, 2 /* 1.5 */
+    test_ftoi ceil.s, a2, f0, 0x3fa00000, 1, 3 /* 1.25 * 2 */
+
+    /* positive overflow */
+    test_ftoi ceil.s, a2, f0, 0x4effffff, 0, 0x7fffff80
+    test_ftoi ceil.s, a2, f0, 0x4f000000, 0, 0x7fffffff
+    test_ftoi ceil.s, a2, f0, 0x4effffff, 1, 0x7fffffff
+
+    /* +inf */
+    test_ftoi ceil.s, a2, f0, 0x7f800000, 0, 0x7fffffff
+
+    /* NaN */
+    test_ftoi ceil.s, a2, f0, 0x7f800001, 0, 0x7fffffff
+    test_ftoi ceil.s, a2, f0, 0x7fc00000, 0, 0x7fffffff
+test_end
+
+test utrunc_s
+    /* NaN */
+    test_ftoi utrunc.s, a2, f0, 0xffc00001, 0, 0xffffffff
+    test_ftoi utrunc.s, a2, f0, 0xff800001, 0, 0xffffffff
+
+    /* -inf */
+    test_ftoi utrunc.s, a2, f0, 0xff800000, 0, 0x80000000
+
+    /* negative overflow */
+    test_ftoi utrunc.s, a2, f0, 0xceffffff, 1, 0x80000000
+    test_ftoi utrunc.s, a2, f0, 0xcf000000, 0, 0x80000000
+    test_ftoi utrunc.s, a2, f0, 0xceffffff, 0, 0x80000080
+
+    /* negative */
+    test_ftoi utrunc.s, a2, f0, 0xbfa00000, 1, -2 /* -1.25 * 2 */
+    test_ftoi utrunc.s, a2, f0, 0xbfc00000, 0, -1 /* -1.5 */
+    test_ftoi utrunc.s, a2, f0, 0xbf800000, 1, -2 /* -1 * 2 */
+    test_ftoi utrunc.s, a2, f0, 0xbf800000, 0, -1 /* -1 */
+    test_ftoi utrunc.s, a2, f0, 0xbf400000, 0, 0  /* -0.75 */
+    test_ftoi utrunc.s, a2, f0, 0xbf000000, 0, 0  /* -0.5 */
+
+    /* positive */
+    test_ftoi utrunc.s, a2, f0, 0x3f000000, 0, 0 /* 0.5 */
+    test_ftoi utrunc.s, a2, f0, 0x3f400000, 0, 0 /* 0.75 */
+    test_ftoi utrunc.s, a2, f0, 0x3f800000, 0, 1 /* 1 */
+    test_ftoi utrunc.s, a2, f0, 0x3f800000, 1, 2 /* 1 * 2 */
+    test_ftoi utrunc.s, a2, f0, 0x3fc00000, 0, 1 /* 1.5 */
+    test_ftoi utrunc.s, a2, f0, 0x3fa00000, 1, 2 /* 1.25 * 2 */
+
+    /* positive overflow */
+    test_ftoi utrunc.s, a2, f0, 0x4effffff, 0, 0x7fffff80
+    test_ftoi utrunc.s, a2, f0, 0x4f000000, 0, 0x80000000
+    test_ftoi utrunc.s, a2, f0, 0x4effffff, 1, 0xffffff00
+    test_ftoi utrunc.s, a2, f0, 0x4f800000, 1, 0xffffffff
+
+    /* +inf */
+    test_ftoi utrunc.s, a2, f0, 0x7f800000, 0, 0xffffffff
+
+    /* NaN */
+    test_ftoi utrunc.s, a2, f0, 0x7f800001, 0, 0xffffffff
+    test_ftoi utrunc.s, a2, f0, 0x7fc00000, 0, 0xffffffff
+test_end
+
+test float_s
+    test_itof float.s, f0, a2, -1, 0, \
+        0xbf800000, 0xbf800000, 0xbf800000, 0xbf800000
+    test_itof float.s, f0, a2, 0, 0, 0, 0, 0, 0
+    test_itof float.s, f0, a2, 1, 1, \
+        0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000
+    test_itof float.s, f0, a2, 1, 0, \
+        0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000
+    test_itof float.s, f0, a2, 0x7fffffff, 0, \
+        0x4f000000, 0x4effffff, 0x4f000000, 0x4effffff
+test_end
+
+test ufloat_s
+    test_itof ufloat.s, f0, a2, 0, 0, 0, 0, 0, 0
+    test_itof ufloat.s, f0, a2, 1, 1, \
+        0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000
+    test_itof ufloat.s, f0, a2, 1, 0, \
+        0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000
+    test_itof ufloat.s, f0, a2, 0x7fffffff, 0, \
+        0x4f000000, 0x4effffff, 0x4f000000, 0x4effffff
+    test_itof ufloat.s, f0, a2, 0xffffffff, 0, \
+        0x4f800000, 0x4f7fffff, 0x4f800000, 0x4f7fffff
+test_end
+
+#endif
+
+test_suite_end
diff --git a/tests/tcg/xtensa/test_fp1.S b/tests/tcg/xtensa/test_fp1.S
new file mode 100644
index 0000000000..6e182e5964
--- /dev/null
+++ b/tests/tcg/xtensa/test_fp1.S
@@ -0,0 +1,141 @@
+#include "macros.inc"
+
+test_suite fp1
+
+#if XCHAL_HAVE_FP
+
+.macro movfp fr, v
+    movi    a2, \v
+    wfr     \fr, a2
+.endm
+
+.macro test_ord_ex op, br, fr0, fr1, v0, v1, r
+    movi    a2, 0
+    wur     a2, fsr
+    movfp   \fr0, \v0
+    movfp   \fr1, \v1
+    \op     \br, \fr0, \fr1
+    movi    a2, 0
+    movi    a3, 1
+    movt    a2, a3, \br
+    assert  eqi, a2, \r
+    rur     a2, fsr
+    assert  eqi, a2, 0
+.endm
+
+.macro test_ord op, br, fr0, fr1, v0, v1, r
+    movi    a2, 0
+    wur     a2, fcr
+    test_ord_ex \op, \br, \fr0, \fr1, \v0, \v1, \r
+    movi    a2, 0x7c
+    wur     a2, fcr
+    test_ord_ex \op, \br, \fr0, \fr1, \v0, \v1, \r
+.endm
+
+.macro test_ord_all op, aa, ab, ba, aPI, PIa, aN, Na, II, IN, NI
+    test_ord \op  b0,  f0,  f1, 0x3f800000, 0x3f800000, \aa
+    test_ord \op  b1,  f2,  f3, 0x3f800000, 0x3fc00000, \ab
+    test_ord \op  b2,  f4,  f5, 0x3fc00000, 0x3f800000, \ba
+    test_ord \op  b3,  f6,  f7, 0x3f800000, 0x7f800000, \aPI
+    test_ord \op  b4,  f8,  f9, 0x7f800000, 0x3f800000, \PIa
+    test_ord \op  b5, f10, f11, 0x3f800000, 0xffc00001, \aN
+    test_ord \op  b6, f12, f13, 0x3f800000, 0xff800001, \aN
+    test_ord \op  b7, f14, f15, 0x3f800000, 0x7f800001, \aN
+    test_ord \op  b8,  f0,  f1, 0x3f800000, 0x7fc00000, \aN
+    test_ord \op  b9,  f2,  f3, 0xffc00001, 0x3f800000, \Na
+    test_ord \op b10,  f4,  f5, 0xff800001, 0x3f800000, \Na
+    test_ord \op b11,  f6,  f7, 0x7f800001, 0x3f800000, \Na
+    test_ord \op b12,  f8,  f9, 0x7fc00000, 0x3f800000, \Na
+    test_ord \op b13, f10, f11, 0x7f800000, 0x7f800000, \II
+    test_ord \op b14, f12, f13, 0x7f800000, 0x7fc00000, \IN
+    test_ord \op b15, f14, f15, 0x7fc00000, 0x7f800000, \NI
+.endm
+
+test un_s
+    movi    a2, 1
+    wsr     a2, cpenable
+    test_ord_all un.s, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1
+test_end
+
+test oeq_s
+    test_ord_all oeq.s, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0
+test_end
+
+test ueq_s
+    test_ord_all ueq.s, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1
+test_end
+
+test olt_s
+    test_ord_all olt.s, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0
+test_end
+
+test ult_s
+    test_ord_all ult.s, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1
+test_end
+
+test ole_s
+    test_ord_all ole.s, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0
+test_end
+
+test ule_s
+    test_ord_all ule.s, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1
+test_end
+
+.macro test_cond op, fr0, fr1, cr, v0, v1, r
+    movfp   \fr0, \v0
+    movfp   \fr1, \v1
+    \op     \fr0, \fr1, \cr
+    rfr     a2, \fr0
+    movi    a3, \r
+    assert  eq, a2, a3
+.endm
+
+test moveqz_s
+    movi    a3, 0
+    test_cond moveqz.s, f0, f1, a3, 0, 0x3f800000, 0x3f800000
+    movi    a3, 1
+    test_cond moveqz.s, f0, f1, a3, 0, 0x3f800000, 0
+test_end
+
+test movnez_s
+    movi    a3, 0
+    test_cond movnez.s, f0, f1, a3, 0, 0x3f800000, 0
+    movi    a3, 1
+    test_cond movnez.s, f0, f1, a3, 0, 0x3f800000, 0x3f800000
+test_end
+
+test movltz_s
+    movi    a3, -1
+    test_cond movltz.s, f0, f1, a3, 0, 0x3f800000, 0x3f800000
+    movi    a3, 0
+    test_cond movltz.s, f0, f1, a3, 0, 0x3f800000, 0
+    movi    a3, 1
+    test_cond movltz.s, f0, f1, a3, 0, 0x3f800000, 0
+test_end
+
+test movgez_s
+    movi    a3, -1
+    test_cond movgez.s, f0, f1, a3, 0, 0x3f800000, 0
+    movi    a3, 0
+    test_cond movgez.s, f0, f1, a3, 0, 0x3f800000, 0x3f800000
+    movi    a3, 1
+    test_cond movgez.s, f0, f1, a3, 0, 0x3f800000, 0x3f800000
+test_end
+
+test movf_s
+    olt.s   b0, f0, f0
+    test_cond movf.s, f0, f1, b0, 0, 0x3f800000, 0x3f800000
+    ueq.s   b0, f0, f0
+    test_cond movf.s, f0, f1, b0, 0, 0x3f800000, 0
+test_end
+
+test movt_s
+    ueq.s   b0, f0, f0
+    test_cond movt.s, f0, f1, b0, 0, 0x3f800000, 0x3f800000
+    olt.s   b0, f0, f0
+    test_cond movt.s, f0, f1, b0, 0, 0x3f800000, 0
+test_end
+
+#endif
+
+test_suite_end
diff --git a/tests/tcg/xtensa/test_fp_cpenable.S b/tests/tcg/xtensa/test_fp_cpenable.S
new file mode 100644
index 0000000000..882bb2f3ce
--- /dev/null
+++ b/tests/tcg/xtensa/test_fp_cpenable.S
@@ -0,0 +1,27 @@
+#include "macros.inc"
+
+test_suite fp_cpenable
+
+#if XCHAL_HAVE_FP
+
+test rur
+    set_vector kernel, 2f
+    movi    a2, 0
+    wsr     a2, cpenable
+    isync
+1:
+    rur     a2, fsr
+    //wfr     f0, a2
+    test_fail
+2:
+    movi    a2, 1b
+    rsr     a3, epc1
+    assert  eq, a2, a3
+    movi    a2, 32
+    rsr     a3, exccause
+    assert  eq, a2, a3
+test_end
+
+#endif
+
+test_suite_end
diff --git a/tests/tcg/xtensa/test_interrupt.S b/tests/tcg/xtensa/test_interrupt.S
index 876683518e..efedc43f60 100644
--- a/tests/tcg/xtensa/test_interrupt.S
+++ b/tests/tcg/xtensa/test_interrupt.S
@@ -1,15 +1,59 @@
 #include "macros.inc"
 
-#define LSBIT(v) ((v) ^ ((v) & ((v) - 1)))
+#define LSBIT(v) ((v) & -(v))
+
+#define LEVEL_MASK(x) glue3(XCHAL_INTLEVEL, x, _MASK)
+#define LEVEL_SOFT_MASK(x) (LEVEL_MASK(x) & XCHAL_INTTYPE_MASK_SOFTWARE)
+
+#define L1_SOFT_MASK LEVEL_SOFT_MASK(1)
+#define L1_SOFT LSBIT(L1_SOFT_MASK)
+
+#if LEVEL_SOFT_MASK(2)
+#define HIGH_LEVEL_SOFT_MASK LEVEL_SOFT_MASK(2)
+#elif LEVEL_SOFT_MASK(3)
+#define HIGH_LEVEL_SOFT_MASK LEVEL_SOFT_MASK(3)
+#elif LEVEL_SOFT_MASK(4)
+#define HIGH_LEVEL_SOFT_MASK LEVEL_SOFT_MASK(4)
+#elif LEVEL_SOFT_MASK(5)
+#define HIGH_LEVEL_SOFT_MASK LEVEL_SOFT_MASK(5)
+#elif LEVEL_SOFT_MASK(6)
+#define HIGH_LEVEL_SOFT_MASK LEVEL_SOFT_MASK(6)
+#else
+#define HIGH_LEVEL_SOFT_MASK 0
+#endif
+
+#define HIGH_LEVEL_SOFT LSBIT(HIGH_LEVEL_SOFT_MASK)
+
+#if LEVEL_SOFT_MASK(2)
+#define HIGH_LEVEL_SOFT_LEVEL 2
+#elif LEVEL_SOFT_MASK(3)
+#define HIGH_LEVEL_SOFT_LEVEL 3
+#elif LEVEL_SOFT_MASK(4)
+#define HIGH_LEVEL_SOFT_LEVEL 4
+#elif LEVEL_SOFT_MASK(5)
+#define HIGH_LEVEL_SOFT_LEVEL 5
+#elif LEVEL_SOFT_MASK(6)
+#define HIGH_LEVEL_SOFT_LEVEL 6
+#else
+#define HIGH_LEVEL_SOFT_LEVEL 0
+#endif
 
 test_suite interrupt
 
+#if XCHAL_HAVE_INTERRUPTS
+
 .macro clear_interrupts
     movi    a2, 0
     wsr     a2, intenable
+#if XCHAL_NUM_TIMERS
     wsr     a2, ccompare0
+#endif
+#if XCHAL_NUM_TIMERS > 1
     wsr     a2, ccompare1
+#endif
+#if XCHAL_NUM_TIMERS > 2
     wsr     a2, ccompare2
+#endif
     esync
     rsr     a2, interrupt
     wsr     a2, intclear
@@ -44,11 +88,12 @@ test rsil
     assert  eqi, a2, 0
 test_end
 
+#if L1_SOFT
 test soft_disabled
     set_vector kernel, 1f
     clear_interrupts
 
-    movi    a2, LSBIT(XCHAL_INTTYPE_MASK_SOFTWARE)
+    movi    a2, L1_SOFT
     wsr     a2, intset
     esync
     rsr     a3, interrupt
@@ -70,7 +115,7 @@ test soft_intenable
     set_vector kernel, 1f
     clear_interrupts
 
-    movi    a2, LSBIT(XCHAL_INTTYPE_MASK_SOFTWARE)
+    movi    a2, L1_SOFT
     wsr     a2, intset
     esync
     rsr     a3, interrupt
@@ -89,7 +134,7 @@ test soft_rsil
     set_vector kernel, 1f
     clear_interrupts
 
-    movi    a2, LSBIT(XCHAL_INTTYPE_MASK_SOFTWARE)
+    movi    a2, L1_SOFT
     wsr     a2, intset
     esync
     rsr     a3, interrupt
@@ -108,7 +153,7 @@ test soft_waiti
     set_vector kernel, 1f
     clear_interrupts
 
-    movi    a2, LSBIT(XCHAL_INTTYPE_MASK_SOFTWARE)
+    movi    a2, L1_SOFT
     wsr     a2, intset
     esync
     rsr     a3, interrupt
@@ -127,7 +172,7 @@ test soft_user
     set_vector user, 2f
     clear_interrupts
 
-    movi    a2, LSBIT(XCHAL_INTTYPE_MASK_SOFTWARE)
+    movi    a2, L1_SOFT
     wsr     a2, intset
     esync
     rsr     a3, interrupt
@@ -147,12 +192,13 @@ test soft_user
     check_l1
 test_end
 
+#if HIGH_LEVEL_SOFT
 test soft_priority
     set_vector kernel, 1f
-    set_vector level3, 2f
+    set_vector glue(level, HIGH_LEVEL_SOFT_LEVEL), 2f
     clear_interrupts
 
-    movi    a2, XCHAL_INTTYPE_MASK_SOFTWARE
+    movi    a2, L1_SOFT | HIGH_LEVEL_SOFT
     wsr     a2, intenable
     rsil    a3, 0
     esync
@@ -164,17 +210,20 @@ test soft_priority
     rsr     a2, ps
     movi    a3, 0x1f        /* EXCM | INTMASK */
     and     a2, a2, a3
-    movi    a3, 0x13
+    movi    a3, 0x10 | HIGH_LEVEL_SOFT_LEVEL
     assert  eq, a2, a3      /* EXCM and INTMASK are set
                                for high-priority interrupt */
 test_end
+#endif
+#endif
 
+#if HIGH_LEVEL_SOFT
 test eps_epc_rfi
-    set_vector level3, 3f
+    set_vector glue(level, HIGH_LEVEL_SOFT_LEVEL), 3f
     clear_interrupts
     reset_ps
 
-    movi    a2, XCHAL_INTTYPE_MASK_SOFTWARE
+    movi    a2, L1_SOFT_MASK | HIGH_LEVEL_SOFT_MASK
     wsr     a2, intenable
     rsil    a3, 0
     rsr     a3, ps
@@ -185,23 +234,26 @@ test eps_epc_rfi
 2:
     test_fail
 3:
-    rsr     a2, eps3
+    rsr     a2, glue(eps, HIGH_LEVEL_SOFT_LEVEL)
     assert  eq, a2, a3
-    rsr     a2, epc3
+    rsr     a2, glue(epc, HIGH_LEVEL_SOFT_LEVEL)
     movi    a3, 1b
     assert  ge, a2, a3
     movi    a3, 2b
     assert  ge, a3, a2
     movi    a2, 4f
-    wsr     a2, epc3
-    movi    a2, 0x40003
-    wsr     a2, eps3
-    rfi     3
+    wsr     a2, glue(epc, HIGH_LEVEL_SOFT_LEVEL)
+    movi    a2, 0x40000 | HIGH_LEVEL_SOFT_LEVEL
+    wsr     a2, glue(eps, HIGH_LEVEL_SOFT_LEVEL)
+    rfi     HIGH_LEVEL_SOFT_LEVEL
     test_fail
 4:
     rsr     a2, ps
-    movi    a3, 0x40003
+    movi    a3, 0x40000 | HIGH_LEVEL_SOFT_LEVEL
     assert  eq, a2, a3
 test_end
+#endif
+
+#endif
 
 test_suite_end
diff --git a/tests/tcg/xtensa/test_loop.S b/tests/tcg/xtensa/test_loop.S
index 5755578d01..0cfd8661ea 100644
--- a/tests/tcg/xtensa/test_loop.S
+++ b/tests/tcg/xtensa/test_loop.S
@@ -2,6 +2,8 @@
 
 test_suite loop
 
+#if XCHAL_HAVE_LOOPS
+
 test loop
     movi    a2, 0
     movi    a3, 5
@@ -160,4 +162,6 @@ test loopgtz
 1:
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_lsc.S b/tests/tcg/xtensa/test_lsc.S
new file mode 100644
index 0000000000..0578bf19e7
--- /dev/null
+++ b/tests/tcg/xtensa/test_lsc.S
@@ -0,0 +1,122 @@
+#include "macros.inc"
+
+test_suite lsc
+
+#if XCHAL_HAVE_FP
+
+test lsi
+    movi    a2, 1
+    wsr     a2, cpenable
+
+    movi    a2, 1f
+    lsi     f0, a2, 0
+    lsi     f1, a2, 4
+    lsiu    f2, a2, 8
+    movi    a3, 1f + 8
+    assert  eq, a2, a3
+    rfr     a2, f0
+    movi    a3, 0x3f800000
+    assert  eq, a2, a3
+    rfr     a2, f1
+    movi    a3, 0x40000000
+    assert  eq, a2, a3
+    rfr     a2, f2
+    movi    a3, 0x40400000
+    assert  eq, a2, a3
+.data
+    .align  4
+1:
+.float 1, 2, 3
+.text
+test_end
+
+test ssi
+    movi    a2, 1f
+    movi    a3, 0x40800000
+    wfr     f3, a3
+    ssi     f3, a2, 0
+    movi    a3, 0x40a00000
+    wfr     f4, a3
+    ssi     f4, a2, 4
+    movi    a3, 0x40c00000
+    wfr     f5, a3
+    ssiu    f5, a2, 8
+    movi    a3, 1f + 8
+    assert  eq, a2, a3
+    l32i    a4, a2, -8
+    movi    a3, 0x40800000
+    assert  eq, a4, a3
+    l32i    a4, a2, -4
+    movi    a3, 0x40a00000
+    assert  eq, a4, a3
+    l32i    a4, a2, 0
+    movi    a3, 0x40c00000
+    assert  eq, a4, a3
+.data
+    .align  4
+1:
+.float 0, 0, 0
+.text
+test_end
+
+test lsx
+    movi    a2, 1f
+    movi    a3, 0
+    lsx     f6, a2, a3
+    movi    a3, 4
+    lsx     f7, a2, a3
+    movi    a3, 8
+    lsxu    f8, a2, a3
+    movi    a3, 1f + 8
+    assert  eq, a2, a3
+    rfr     a2, f6
+    movi    a3, 0x40e00000
+    assert  eq, a2, a3
+    rfr     a2, f7
+    movi    a3, 0x41000000
+    assert  eq, a2, a3
+    rfr     a2, f8
+    movi    a3, 0x41100000
+    assert  eq, a2, a3
+.data
+    .align  4
+1:
+.float 7, 8, 9
+.text
+test_end
+
+test ssx
+    movi    a2, 1f
+    movi    a3, 0
+    movi    a4, 0x41200000
+    wfr     f9, a4
+    ssx     f9, a2, a3
+    movi    a3, 4
+    movi    a4, 0x41300000
+    wfr     f10, a4
+    ssx     f10, a2, a3
+    movi    a3, 8
+    movi    a4, 0x41400000
+    wfr     f11, a4
+    ssxu    f11, a2, a3
+    movi    a3, 1f + 8
+    assert  eq, a2, a3
+    l32i    a4, a2, -8
+    movi    a3, 0x41200000
+    assert  eq, a4, a3
+    l32i    a4, a2, -4
+    movi    a3, 0x41300000
+    assert  eq, a4, a3
+    l32i    a4, a2, 0
+    movi    a3, 0x41400000
+    assert  eq, a4, a3
+.data
+    .align  4
+1:
+.float 0, 0, 0
+.text
+test_end
+
+#endif
+
+test_suite_end
diff --git a/tests/tcg/xtensa/test_mac16.S b/tests/tcg/xtensa/test_mac16.S
index 512025d842..ee0cedd2ae 100644
--- a/tests/tcg/xtensa/test_mac16.S
+++ b/tests/tcg/xtensa/test_mac16.S
@@ -2,6 +2,8 @@
 
 test_suite mac16
 
+#if XCHAL_HAVE_MAC16
+
 #define ext16(v) (((v) & 0xffff) | (((v) & 0x8000) * 0x1ffffffe))
 #define mul16(a, b) ((ext16(a) * ext16(b)))
 
@@ -240,4 +242,6 @@ test mula_dd_lddec
 .text
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_max.S b/tests/tcg/xtensa/test_max.S
index 3caa207ea5..f349d578e3 100644
--- a/tests/tcg/xtensa/test_max.S
+++ b/tests/tcg/xtensa/test_max.S
@@ -2,6 +2,8 @@
 
 test_suite max
 
+#if XCHAL_HAVE_MINMAX
+
 test max
     movi    a2, 0xffffffff
     movi    a3, 1
@@ -78,4 +80,6 @@ test maxu
     assert  eq, a3, a4
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_min.S b/tests/tcg/xtensa/test_min.S
index 551cf591e5..89ee10334f 100644
--- a/tests/tcg/xtensa/test_min.S
+++ b/tests/tcg/xtensa/test_min.S
@@ -2,6 +2,8 @@
 
 test_suite min
 
+#if XCHAL_HAVE_MINMAX
+
 test min
     movi    a2, 0xffffffff
     movi    a3, 1
@@ -78,4 +80,6 @@ test minu
     assert  eq, a3, a4
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_mmu.S b/tests/tcg/xtensa/test_mmu.S
index a15316ffb3..4cbd6ef4f9 100644
--- a/tests/tcg/xtensa/test_mmu.S
+++ b/tests/tcg/xtensa/test_mmu.S
@@ -2,6 +2,8 @@
 
 test_suite mmu
 
+#if XCHAL_HAVE_PTP_MMU && !XCHAL_HAVE_SPANNING_WAY
+
 .purgem test_init
 
 .macro clean_tlb_way way, page_size, n_entries
@@ -740,4 +742,6 @@ test cross_page_tb
     assert  eq, a2, a3
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_mul16.S b/tests/tcg/xtensa/test_mul16.S
index 98fa7042b5..32507f7f1e 100644
--- a/tests/tcg/xtensa/test_mul16.S
+++ b/tests/tcg/xtensa/test_mul16.S
@@ -2,6 +2,8 @@
 
 test_suite mul16
 
+#if XCHAL_HAVE_MUL16
+
 test mul16u_pp
     movi    a2, 0x137f5a5a
     mov     a3, a2
@@ -80,4 +82,6 @@ test mul16s_nn
     assert  eq, a3, a6
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_mul32.S b/tests/tcg/xtensa/test_mul32.S
index b288ead9f6..862d45abce 100644
--- a/tests/tcg/xtensa/test_mul32.S
+++ b/tests/tcg/xtensa/test_mul32.S
@@ -2,6 +2,8 @@
 
 test_suite mul32
 
+#if XCHAL_HAVE_MUL32
+
 test mull
     movi    a2, 0x137f5a5a
     mov     a3, a2
@@ -15,6 +17,8 @@ test mull
     assert  eq, a3, a6
 test_end
 
+#endif
+
 /* unfortunately dc232b doesn't have muluh/mulsh*/
 
 test_suite_end
diff --git a/tests/tcg/xtensa/test_nsa.S b/tests/tcg/xtensa/test_nsa.S
index 479b2e2429..0af7d1f50d 100644
--- a/tests/tcg/xtensa/test_nsa.S
+++ b/tests/tcg/xtensa/test_nsa.S
@@ -2,6 +2,8 @@
 
 test_suite nsa
 
+#if XCHAL_HAVE_NSA
+
 test nsa
     movi    a2, 0
     movi    a3, 31
@@ -56,4 +58,6 @@ test nsau
     assert  eq, a3, a2
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_phys_mem.S b/tests/tcg/xtensa/test_phys_mem.S
index aae0a793a7..9bb3ee3866 100644
--- a/tests/tcg/xtensa/test_phys_mem.S
+++ b/tests/tcg/xtensa/test_phys_mem.S
@@ -2,6 +2,8 @@
 
 test_suite phys_mem
 
+#if XCHAL_HAVE_PTP_MMU && !XCHAL_HAVE_SPANNING_WAY
+
 .purgem test_init
 
 .macro test_init
@@ -67,6 +69,8 @@ test write_get_pte_no_phys
     assert  eq, a2, a3
 test_end
 
+#endif
+
 test inst_fetch_no_phys
     set_vector kernel, 2f
 
diff --git a/tests/tcg/xtensa/test_quo.S b/tests/tcg/xtensa/test_quo.S
index 5b3ae383d0..32886b913b 100644
--- a/tests/tcg/xtensa/test_quo.S
+++ b/tests/tcg/xtensa/test_quo.S
@@ -2,6 +2,8 @@
 
 test_suite quo
 
+#if XCHAL_HAVE_DIV32
+
 test quou_pp
     movi    a2, 0x5a5a137f
     mov     a3, a2
@@ -144,4 +146,6 @@ test quos_exc
     assert  eq, a2, a3
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_rem.S b/tests/tcg/xtensa/test_rem.S
index 6357e520d9..0b96bb3390 100644
--- a/tests/tcg/xtensa/test_rem.S
+++ b/tests/tcg/xtensa/test_rem.S
@@ -2,6 +2,8 @@
 
 test_suite rem
 
+#if XCHAL_HAVE_DIV32
+
 test remu_pp
     movi    a2, 0x5a5a137f
     mov     a3, a2
@@ -144,4 +146,6 @@ test rems_exc
     assert  eq, a2, a3
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_rst0.S b/tests/tcg/xtensa/test_rst0.S
index a73366b120..143e90b401 100644
--- a/tests/tcg/xtensa/test_rst0.S
+++ b/tests/tcg/xtensa/test_rst0.S
@@ -54,6 +54,8 @@ test add
     assert  eq, a4, a6
 test_end
 
+#if XCHAL_HAVE_ADDX
+
 test addx2
     movi    a2, 0x137fa5a5
     mov     a3, a2
@@ -93,6 +95,8 @@ test addx8
     assert  eq, a4, a6
 test_end
 
+#endif
+
 test sub
     movi    a2, 0x137fa5a5
     mov     a3, a2
@@ -106,6 +110,8 @@ test sub
     assert  eq, a4, a6
 test_end
 
+#if XCHAL_HAVE_ADDX
+
 test subx2
     movi    a2, 0x137fa5a5
     mov     a3, a2
@@ -145,4 +151,6 @@ test subx8
     assert  eq, a4, a6
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_s32c1i.S b/tests/tcg/xtensa/test_s32c1i.S
index 93b575db95..2885d9d003 100644
--- a/tests/tcg/xtensa/test_s32c1i.S
+++ b/tests/tcg/xtensa/test_s32c1i.S
@@ -2,7 +2,13 @@
 
 test_suite s32c1i
 
+#if XCHAL_HAVE_S32C1I
+
 test s32c1i_nowrite
+#if XCHAL_HW_VERSION >= 230000
+    movi    a2, 0x29
+    wsr     a2, atomctl
+#endif
     movi    a2, 1f
     movi    a3, 1
     wsr     a3, scompare1
@@ -20,6 +26,10 @@ test s32c1i_nowrite
 test_end
 
 test s32c1i_write
+#if XCHAL_HW_VERSION >= 230000
+    movi    a2, 0x29
+    wsr     a2, atomctl
+#endif
     movi    a2, 1f
     movi    a3, 3
     wsr     a3, scompare1
@@ -36,4 +46,6 @@ test s32c1i_write
 .text
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_sext.S b/tests/tcg/xtensa/test_sext.S
index 087a6333a4..483d2176e4 100644
--- a/tests/tcg/xtensa/test_sext.S
+++ b/tests/tcg/xtensa/test_sext.S
@@ -2,6 +2,8 @@
 
 test_suite sext
 
+#if XCHAL_HAVE_SEXT
+
 test sext
     movi    a2, 0xffffff5a
     movi    a3, 0x0000005a
@@ -66,4 +68,6 @@ test sext_same_rs
     assert  eq, a3, a2
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_sr.S b/tests/tcg/xtensa/test_sr.S
index 052f1e04a7..b1a91a0637 100644
--- a/tests/tcg/xtensa/test_sr.S
+++ b/tests/tcg/xtensa/test_sr.S
@@ -2,11 +2,23 @@
 
 test_suite sr
 
+#if XCHAL_HAVE_BE
+#define LOW__SR 0x04
+#define HI_RSR 0x30
+#define HI_WSR 0x31
+#define HI_XSR 0x16
+#else
+#define LOW__SR 0x40
+#define HI_RSR 0x03
+#define HI_WSR 0x13
+#define HI_XSR 0x61
+#endif
+
 .macro  sr_op sym, op_sym, op_byte, sr
     .if \sym
     \op_sym a4, \sr
     .else
-    .byte 0x40, \sr, \op_byte
+    .byte LOW__SR, \sr, \op_byte
     .endif
 .endm
 
@@ -32,9 +44,9 @@ test_suite sr
 
 .macro  test_sr_mask sr, sym, mask
 test \sr
-    test_sr_op \sym, \mask & 1, rsr, 0x03, \sr
-    test_sr_op \sym, \mask & 2, wsr, 0x13, \sr
-    test_sr_op \sym, \mask & 4, xsr, 0x61, \sr
+    test_sr_op \sym, \mask & 1, rsr, HI_RSR, \sr
+    test_sr_op \sym, \mask & 2, wsr, HI_WSR, \sr
+    test_sr_op \sym, \mask & 4, xsr, HI_XSR, \sr
 test_end
 .endm
 
@@ -42,50 +54,183 @@ test_end
     test_sr_mask    \sr, \conf, 7
 .endm
 
+#if XCHAL_HAVE_MAC16
 test_sr acchi, 1
 test_sr acclo, 1
+#else
+test_sr_mask /*acchi*/17, 0, 0
+test_sr_mask /*acclo*/16, 0, 0
+#endif
+
+#if XCHAL_HAVE_S32C1I && XCHAL_HW_VERSION >= 230000
+test_sr atomctl, 1
+#else
 test_sr_mask /*atomctl*/99, 0, 0
+#endif
+
+#if XCHAL_HAVE_BOOLEANS
+test_sr br, 1
+#else
 test_sr_mask /*br*/4, 0, 0
+#endif
+
 test_sr_mask /*cacheattr*/98, 0, 0
+
+#if XCHAL_HAVE_CCOUNT
 test_sr ccompare0, 1
 test_sr ccount, 1
+#else
+test_sr_mask /*ccompare0*/240, 0, 0
+test_sr_mask /*ccount*/234, 0, 0
+#endif
+
+#if XCHAL_HAVE_CP
 test_sr cpenable, 1
+#else
+test_sr_mask /*cpenable*/224, 0, 0
+#endif
+
+#if XCHAL_HAVE_DEBUG
+#if XCHAL_NUM_DBREAK
 test_sr dbreaka0, 1
 test_sr dbreakc0, 1
+#endif
 test_sr_mask debugcause, 1, 1
+#else
+test_sr_mask /*dbreaka0*/144, 0, 0
+test_sr_mask /*dbreakc0*/160, 0, 0
+test_sr_mask /*debugcause*/233, 0, 0
+#endif
+
 test_sr depc, 1
+
+#if XCHAL_HAVE_PTP_MMU
 test_sr dtlbcfg, 1
+#else
+test_sr_mask /*dtlbcfg*/92, 0, 0
+#endif
+
 test_sr epc1, 1
+
+#if XCHAL_NUM_INTLEVELS > 1
 test_sr epc2, 1
 test_sr eps2, 1
+#else
+test_sr_mask /*epc2*/178, 0, 0
+test_sr_mask /*eps2*/194, 0, 0
+#endif
+
 test_sr exccause, 1
 test_sr excsave1, 1
+
+#if XCHAL_NUM_INTLEVELS > 1
 test_sr excsave2, 1
+#else
+test_sr_mask /*excsave2*/210, 0, 0
+#endif
+
 test_sr excvaddr, 1
+
+#if XCHAL_HAVE_DEBUG
+#if XCHAL_NUM_IBREAK
 test_sr ibreaka0, 1
 test_sr ibreakenable, 1
+#endif
 test_sr icount, 1
 test_sr icountlevel, 1
+#else
+test_sr_mask /*ibreaka0*/128, 0, 0
+test_sr_mask /*ibreakenable*/96, 0, 0
+test_sr_mask /*icount*/236, 0, 0
+test_sr_mask /*icountlevel*/237, 0, 0
+#endif
+
 test_sr_mask /*intclear*/227, 0, 2
 test_sr_mask /*interrupt*/226, 0, 3
 test_sr intenable, 1
+
+#if XCHAL_HAVE_PTP_MMU
 test_sr itlbcfg, 1
+#else
+test_sr_mask /*itlbcfg*/91, 0, 0
+#endif
+
+#if XCHAL_HAVE_LOOPS
 test_sr lbeg, 1
 test_sr lcount, 1
 test_sr lend, 1
+#else
+test_sr_mask /*lbeg*/0, 0, 0
+test_sr_mask /*lcount*/2, 0, 0
+test_sr_mask /*lend*/1, 0, 0
+#endif
+
+#if XCHAL_HAVE_ABSOLUTE_LITERALS
 test_sr litbase, 1
+#else
+test_sr_mask /*litbase*/5, 0, 0
+#endif
+
+#if XCHAL_HAVE_MAC16
 test_sr m0, 1
+#else
+test_sr_mask /*m0*/32, 0, 0
+#endif
+
+#if XCHAL_HW_VERSION >= 250000
+test_sr_mask /*memctl*/97, 0, 7
+#else
 test_sr_mask /*memctl*/97, 0, 0
+#endif
+
+#if XCHAL_NUM_MISC_REGS
 test_sr misc0, 1
+#else
+test_sr_mask /*misc0*/244, 0, 0
+#endif
+
+#if XCHAL_HAVE_PREFETCH
+test_sr prefctl, 1
+#else
 test_sr_mask /*prefctl*/40, 0, 0
+#endif
+
+#if XCHAL_HAVE_PRID
 test_sr_mask /*prid*/235, 0, 1
+#else
+test_sr_mask /*prid*/235, 0, 0
+#endif
+
 test_sr ps, 1
+
+#if XCHAL_HAVE_PTP_MMU
 test_sr ptevaddr, 1
 test_sr rasid, 1
+#else
+test_sr_mask /*ptevaddr*/83, 0, 0
+test_sr_mask /*rasid*/90, 0, 0
+#endif
+
 test_sr sar, 1
+
+#if XCHAL_HAVE_S32C1I
 test_sr scompare1, 1
+#else
+test_sr_mask /*scompare1*/12, 0, 0
+#endif
+
+#if XCHAL_HAVE_VECBASE
 test_sr vecbase, 1
+#else
+test_sr_mask /*vecbase*/231, 0, 0
+#endif
+
+#if XCHAL_HAVE_WINDOWED
 test_sr windowbase, 1
 test_sr windowstart, 1
+#else
+test_sr_mask /*windowbase*/72, 0, 0
+test_sr_mask /*windowstart*/73, 0, 0
+#endif
 
 test_suite_end
diff --git a/tests/tcg/xtensa/test_timer.S b/tests/tcg/xtensa/test_timer.S
index 6cda71adbb..1ec8e20883 100644
--- a/tests/tcg/xtensa/test_timer.S
+++ b/tests/tcg/xtensa/test_timer.S
@@ -2,6 +2,8 @@
 
 #define CCOUNT_SHIFT 4
 #define WAIT_LOOPS 20
+#define level1 kernel
+#define INTERRUPT_LEVEL(n) glue3(XCHAL_INT, n, _LEVEL)
 
 .macro      make_ccount_delta target, delta
     rsr     \delta, ccount
@@ -13,6 +15,8 @@
 
 test_suite timer
 
+#if XCHAL_HAVE_CCOUNT
+
 test ccount
     rsr     a3, ccount
     rsr     a4, ccount
@@ -32,14 +36,20 @@ test ccount_write
     assert  ltu, a3, a4
 test_end
 
+#if XCHAL_NUM_TIMERS
+
 test ccount_update_deadline
     movi    a2, 0
     wsr     a2, intenable
     rsr     a2, interrupt
     wsr     a2, intclear
     movi    a2, 0
+#if XCHAL_NUM_TIMERS > 1
     wsr     a2, ccompare1
+#endif
+#if XCHAL_NUM_TIMERS > 2
     wsr     a2, ccompare2
+#endif
     movi    a2, 0x12345678
     wsr     a2, ccompare0
     rsr     a3, interrupt
@@ -59,8 +69,12 @@ test ccompare
     rsr     a2, interrupt
     wsr     a2, intclear
     movi    a2, 0
+#if XCHAL_NUM_TIMERS > 1
     wsr     a2, ccompare1
+#endif
+#if XCHAL_NUM_TIMERS > 2
     wsr     a2, ccompare2
+#endif
 
     make_ccount_delta a2, a15
     wsr     a2, ccompare0
@@ -76,6 +90,7 @@ test ccompare
     assert  nei, a5, 0
 test_end
 
+#if INTERRUPT_LEVEL(XCHAL_TIMER0_INTERRUPT) == 1
 test ccompare0_interrupt
     set_vector kernel, 2f
     movi    a2, 0
@@ -83,8 +98,12 @@ test ccompare0_interrupt
     rsr     a2, interrupt
     wsr     a2, intclear
     movi    a2, 0
+#if XCHAL_NUM_TIMERS > 1
     wsr     a2, ccompare1
+#endif
+#if XCHAL_NUM_TIMERS > 2
     wsr     a2, ccompare2
+#endif
 
     movi    a3, WAIT_LOOPS
     make_ccount_delta a2, a15
@@ -104,16 +123,21 @@ test ccompare0_interrupt
     rsr     a2, exccause
     assert  eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */
 test_end
+#endif
+
+#if XCHAL_NUM_TIMERS > 1
 
 test ccompare1_interrupt
-    set_vector level3, 2f
+    set_vector glue(level, INTERRUPT_LEVEL(XCHAL_TIMER1_INTERRUPT)), 2f
     movi    a2, 0
     wsr     a2, intenable
     rsr     a2, interrupt
     wsr     a2, intclear
     movi    a2, 0
     wsr     a2, ccompare0
+#if XCHAL_NUM_TIMERS > 2
     wsr     a2, ccompare2
+#endif
 
     movi    a3, WAIT_LOOPS
     make_ccount_delta a2, a15
@@ -123,7 +147,7 @@ test ccompare1_interrupt
     assert  eqi, a2, 0
     movi    a2, 1 << XCHAL_TIMER1_INTERRUPT
     wsr     a2, intenable
-    rsil    a2, 2
+    rsil    a2, INTERRUPT_LEVEL(XCHAL_TIMER1_INTERRUPT) - 1
     loop    a3, 1f
     nop
 1:
@@ -131,8 +155,11 @@ test ccompare1_interrupt
 2:
 test_end
 
+#endif
+#if XCHAL_NUM_TIMERS > 2
+
 test ccompare2_interrupt
-    set_vector level5, 2f
+    set_vector glue(level, INTERRUPT_LEVEL(XCHAL_TIMER2_INTERRUPT)), 2f
     movi    a2, 0
     wsr     a2, intenable
     rsr     a2, interrupt
@@ -149,7 +176,7 @@ test ccompare2_interrupt
     assert  eqi, a2, 0
     movi    a2, 1 << XCHAL_TIMER2_INTERRUPT
     wsr     a2, intenable
-    rsil    a2, 4
+    rsil    a2, INTERRUPT_LEVEL(XCHAL_TIMER2_INTERRUPT) - 1
     loop    a3, 1f
     nop
 1:
@@ -157,6 +184,8 @@ test ccompare2_interrupt
 2:
 test_end
 
+#endif
+
 test ccompare_interrupt_masked
     set_vector kernel, 2f
     movi    a2, 0
@@ -164,11 +193,15 @@ test ccompare_interrupt_masked
     rsr     a2, interrupt
     wsr     a2, intclear
     movi    a2, 0
+#if XCHAL_NUM_TIMERS > 2
     wsr     a2, ccompare2
+#endif
 
     movi    a3, 2 * WAIT_LOOPS
     make_ccount_delta a2, a15
+#if XCHAL_NUM_TIMERS > 1
     wsr     a2, ccompare1
+#endif
     add     a2, a2, a15
     wsr     a2, ccompare0
     rsync
@@ -194,11 +227,15 @@ test ccompare_interrupt_masked_waiti
     rsr     a2, interrupt
     wsr     a2, intclear
     movi    a2, 0
+#if XCHAL_NUM_TIMERS > 2
     wsr     a2, ccompare2
+#endif
 
     movi    a3, 2 * WAIT_LOOPS
     make_ccount_delta a2, a15
+#if XCHAL_NUM_TIMERS > 1
     wsr     a2, ccompare1
+#endif
     add     a2, a2, a15
     wsr     a2, ccompare0
     rsync
@@ -214,4 +251,7 @@ test ccompare_interrupt_masked_waiti
     assert  eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */
 test_end
 
+#endif
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/test_windowed.S b/tests/tcg/xtensa/test_windowed.S
index d851e8f43c..5ead90a790 100644
--- a/tests/tcg/xtensa/test_windowed.S
+++ b/tests/tcg/xtensa/test_windowed.S
@@ -2,10 +2,12 @@
 
 test_suite windowed
 
+#if XCHAL_HAVE_WINDOWED
+
 .altmacro
 
 .macro reset_window start
-    movi    a2, 0xff
+    movi    a2, 0xffff
     wsr     a2, windowstart
     rsync
     movi    a2, 0
@@ -105,7 +107,8 @@ test_end
     movi    a3, 0x4001f
     assert  eq, a2, a3
     rsr     a2, windowbase
-    assert  eqi, a2, 8 - ((\window) / 4)
+    movi    a3, (XCHAL_NUM_AREGS - (\window)) / 4
+    assert  eq, a2, a3
     rsr     a2, windowstart
     assert  eqi, a2, 1
     rfwu
@@ -116,8 +119,8 @@ test_end
     rsr     a2, windowbase
     assert  eqi, a2, 0
     rsr     a2, windowstart
-    assert  bsi, a2, 0
-    assert  bsi, a2, 8 - ((\window) / 4)
+    assert  bsi.l, a2, 0
+    assert  bsi.l, a2, (XCHAL_NUM_AREGS - (\window)) / 4
 .endm
 
 test underflow
@@ -132,7 +135,7 @@ test_end
 
 
 .macro retw_test window
-    reset_window %(1 | (1 << (8 - (\window) / 4)))
+    reset_window %(1 | (1 << ((XCHAL_NUM_AREGS - \window) / 4)))
     reset_ps
 
     ssai    2
@@ -147,10 +150,11 @@ test_end
     movi    a3, 0x4000f
     assert  eq, a2, a3
     rsr     a2, windowbase
-    assert  eqi, a2, 8 - ((\window) / 4)
+    movi    a3, (XCHAL_NUM_AREGS - (\window)) / 4
+    assert  eq, a2, a3
     rsr     a2, windowstart
-    assert  bci, a2, 0
-    assert  bsi, a2, 8 - ((\window) / 4)
+    assert  bci.l, a2, 0
+    assert  bsi.l, a2, (XCHAL_NUM_AREGS - (\window)) / 4
 .endm
 
 test retw
@@ -180,7 +184,7 @@ test movsp
 
     set_vector kernel, 0
 
-    reset_window 0x81
+    reset_window %(0x1 | (1 << ((XCHAL_NUM_AREGS / 4) - 1)))
     reset_ps
 
     movsp   a2, a3
@@ -211,8 +215,16 @@ test rotw
     movi    a3, 0x16
     movi    a7, 0x17
 
+#if XCHAL_NUM_AREGS == 32
     movi    a2, 0x44
     wsr     a2, windowstart
+#elif XCHAL_NUM_AREGS == 64
+    movi    a2, 0x4004
+    wsr     a2, windowstart
+    rotw    -8
+#else
+#error XCHAL_NUM_AREGS unsupported
+#endif
     rsync
 
     movi    a2, 0x10
@@ -350,4 +362,6 @@ test entry_overflow
     all_entry_overflow_tests
 test_end
 
+#endif
+
 test_suite_end
diff --git a/tests/tcg/xtensa/vectors.S b/tests/tcg/xtensa/vectors.S
index 6a9cb3cde4..cd48cfb656 100644
--- a/tests/tcg/xtensa/vectors.S
+++ b/tests/tcg/xtensa/vectors.S
@@ -2,10 +2,20 @@
 
 .macro vector name
 
-.section .vector.\name
+.section .vector.\name, "ax"
+.global vector_\name
+vector_\name\():
     j       1f
-.section .vector.\name\().text
+    .literal_position
 1:
+    wsr     a0, excsave1
+    movi    a0, 1f
+    ret.n
+
+.section .vector.\name\().text, "ax"
+    .literal_position
+1:
+    rsr     a0, excsave1
     wsr     a2, excsave1
     movi    a2, handler_\name
     l32i    a2, a2, 0
diff --git a/tests/test-authz-list.c b/tests/test-authz-list.c
new file mode 100644
index 0000000000..24347a6ac3
--- /dev/null
+++ b/tests/test-authz-list.c
@@ -0,0 +1,159 @@
+/*
+ * QEMU list file authorization object tests
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "authz/list.h"
+
+static void test_authz_default_deny(void)
+{
+    QAuthZList *auth = qauthz_list_new("auth0",
+                                       QAUTHZ_LIST_POLICY_DENY,
+                                       &error_abort);
+
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_default_allow(void)
+{
+    QAuthZList *auth = qauthz_list_new("auth0",
+                                       QAUTHZ_LIST_POLICY_ALLOW,
+                                       &error_abort);
+
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_explicit_deny(void)
+{
+    QAuthZList *auth = qauthz_list_new("auth0",
+                                       QAUTHZ_LIST_POLICY_ALLOW,
+                                       &error_abort);
+
+    qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_DENY,
+                            QAUTHZ_LIST_FORMAT_EXACT, &error_abort);
+
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_explicit_allow(void)
+{
+    QAuthZList *auth = qauthz_list_new("auth0",
+                                       QAUTHZ_LIST_POLICY_DENY,
+                                       &error_abort);
+
+    qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW,
+                            QAUTHZ_LIST_FORMAT_EXACT, &error_abort);
+
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+
+static void test_authz_complex(void)
+{
+    QAuthZList *auth = qauthz_list_new("auth0",
+                                       QAUTHZ_LIST_POLICY_DENY,
+                                       &error_abort);
+
+    qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW,
+                            QAUTHZ_LIST_FORMAT_EXACT, &error_abort);
+    qauthz_list_append_rule(auth, "bob", QAUTHZ_LIST_POLICY_ALLOW,
+                            QAUTHZ_LIST_FORMAT_EXACT, &error_abort);
+    qauthz_list_append_rule(auth, "dan", QAUTHZ_LIST_POLICY_DENY,
+                            QAUTHZ_LIST_FORMAT_EXACT, &error_abort);
+    qauthz_list_append_rule(auth, "dan*", QAUTHZ_LIST_POLICY_ALLOW,
+                            QAUTHZ_LIST_FORMAT_GLOB, &error_abort);
+
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort));
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_add_remove(void)
+{
+    QAuthZList *auth = qauthz_list_new("auth0",
+                                       QAUTHZ_LIST_POLICY_ALLOW,
+                                       &error_abort);
+
+    g_assert_cmpint(qauthz_list_append_rule(auth, "fred",
+                                            QAUTHZ_LIST_POLICY_ALLOW,
+                                            QAUTHZ_LIST_FORMAT_EXACT,
+                                            &error_abort),
+                    ==, 0);
+    g_assert_cmpint(qauthz_list_append_rule(auth, "bob",
+                                            QAUTHZ_LIST_POLICY_ALLOW,
+                                            QAUTHZ_LIST_FORMAT_EXACT,
+                                            &error_abort),
+                    ==, 1);
+    g_assert_cmpint(qauthz_list_append_rule(auth, "dan",
+                                            QAUTHZ_LIST_POLICY_DENY,
+                                            QAUTHZ_LIST_FORMAT_EXACT,
+                                            &error_abort),
+                    ==, 2);
+    g_assert_cmpint(qauthz_list_append_rule(auth, "frank",
+                                            QAUTHZ_LIST_POLICY_DENY,
+                                            QAUTHZ_LIST_FORMAT_EXACT,
+                                            &error_abort),
+                    ==, 3);
+
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+
+    g_assert_cmpint(qauthz_list_delete_rule(auth, "dan"),
+                    ==, 2);
+
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+
+    g_assert_cmpint(qauthz_list_insert_rule(auth, "dan",
+                                            QAUTHZ_LIST_POLICY_DENY,
+                                            QAUTHZ_LIST_FORMAT_EXACT,
+                                            2,
+                                            &error_abort),
+                    ==, 2);
+
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    module_call_init(MODULE_INIT_QOM);
+
+    g_test_add_func("/auth/list/default/deny", test_authz_default_deny);
+    g_test_add_func("/auth/list/default/allow", test_authz_default_allow);
+    g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny);
+    g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow);
+    g_test_add_func("/auth/list/complex", test_authz_complex);
+    g_test_add_func("/auth/list/add-remove", test_authz_add_remove);
+
+    return g_test_run();
+}
diff --git a/tests/test-authz-listfile.c b/tests/test-authz-listfile.c
new file mode 100644
index 0000000000..1e452fef6d
--- /dev/null
+++ b/tests/test-authz-listfile.c
@@ -0,0 +1,195 @@
+/*
+ * QEMU list authorization object tests
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "authz/listfile.h"
+
+static char *workdir;
+
+static gchar *qemu_authz_listfile_test_save(const gchar *name,
+                                            const gchar *cfg)
+{
+    gchar *path = g_strdup_printf("%s/default-deny.cfg", workdir);
+    GError *gerr = NULL;
+
+    if (!g_file_set_contents(path, cfg, -1, &gerr)) {
+        g_printerr("Unable to save config %s: %s\n",
+                   path, gerr->message);
+        g_error_free(gerr);
+        g_free(path);
+        rmdir(workdir);
+        abort();
+    }
+
+    return path;
+}
+
+static void test_authz_default_deny(void)
+{
+    gchar *file = qemu_authz_listfile_test_save(
+        "default-deny.cfg",
+        "{ \"policy\": \"deny\" }");
+    Error *local_err = NULL;
+
+    QAuthZListFile *auth = qauthz_list_file_new("auth0",
+                                                file, false,
+                                                &local_err);
+    unlink(file);
+    g_free(file);
+    g_assert(local_err == NULL);
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_default_allow(void)
+{
+    gchar *file = qemu_authz_listfile_test_save(
+        "default-allow.cfg",
+        "{ \"policy\": \"allow\" }");
+    Error *local_err = NULL;
+
+    QAuthZListFile *auth = qauthz_list_file_new("auth0",
+                                                file, false,
+                                                &local_err);
+    unlink(file);
+    g_free(file);
+    g_assert(local_err == NULL);
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_explicit_deny(void)
+{
+    gchar *file = qemu_authz_listfile_test_save(
+        "explicit-deny.cfg",
+        "{ \"rules\": [ "
+        "    { \"match\": \"fred\","
+        "      \"policy\": \"deny\","
+        "      \"format\": \"exact\" } ],"
+        "  \"policy\": \"allow\" }");
+    Error *local_err = NULL;
+
+    QAuthZListFile *auth = qauthz_list_file_new("auth0",
+                                                file, false,
+                                                &local_err);
+    unlink(file);
+    g_free(file);
+    g_assert(local_err == NULL);
+
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_explicit_allow(void)
+{
+    gchar *file = qemu_authz_listfile_test_save(
+        "explicit-allow.cfg",
+        "{ \"rules\": [ "
+        "    { \"match\": \"fred\","
+        "      \"policy\": \"allow\","
+        "      \"format\": \"exact\" } ],"
+        "  \"policy\": \"deny\" }");
+    Error *local_err = NULL;
+
+    QAuthZListFile *auth = qauthz_list_file_new("auth0",
+                                                file, false,
+                                                &local_err);
+    unlink(file);
+    g_free(file);
+    g_assert(local_err == NULL);
+
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+
+static void test_authz_complex(void)
+{
+    gchar *file = qemu_authz_listfile_test_save(
+        "complex.cfg",
+        "{ \"rules\": [ "
+        "    { \"match\": \"fred\","
+        "      \"policy\": \"allow\","
+        "      \"format\": \"exact\" },"
+        "    { \"match\": \"bob\","
+        "      \"policy\": \"allow\","
+        "      \"format\": \"exact\" },"
+        "    { \"match\": \"dan\","
+        "      \"policy\": \"deny\","
+        "      \"format\": \"exact\" },"
+        "    { \"match\": \"dan*\","
+        "      \"policy\": \"allow\","
+        "      \"format\": \"glob\" } ],"
+        "  \"policy\": \"deny\" }");
+
+    Error *local_err = NULL;
+
+    QAuthZListFile *auth = qauthz_list_file_new("auth0",
+                                                file, false,
+                                                &local_err);
+    unlink(file);
+    g_free(file);
+    g_assert(local_err == NULL);
+
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort));
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+
+int main(int argc, char **argv)
+{
+    int ret;
+    GError *gerr = NULL;
+
+    g_test_init(&argc, &argv, NULL);
+
+    module_call_init(MODULE_INIT_QOM);
+
+    workdir = g_dir_make_tmp("qemu-test-authz-listfile-XXXXXX",
+                             &gerr);
+    if (!workdir) {
+        g_printerr("Unable to create temporary dir: %s\n",
+                   gerr->message);
+        g_error_free(gerr);
+        abort();
+    }
+
+    g_test_add_func("/auth/list/default/deny", test_authz_default_deny);
+    g_test_add_func("/auth/list/default/allow", test_authz_default_allow);
+    g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny);
+    g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow);
+    g_test_add_func("/auth/list/complex", test_authz_complex);
+
+    ret = g_test_run();
+
+    rmdir(workdir);
+    g_free(workdir);
+
+    return ret;
+}
diff --git a/tests/test-authz-pam.c b/tests/test-authz-pam.c
new file mode 100644
index 0000000000..93d5ac8bbf
--- /dev/null
+++ b/tests/test-authz-pam.c
@@ -0,0 +1,124 @@
+/*
+ * QEMU PAM authorization object tests
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "authz/pamacct.h"
+
+#include <security/pam_appl.h>
+
+static bool failauth;
+
+/*
+ * These two functions are exported by libpam.so.
+ *
+ * By defining them again here, our impls are resolved
+ * by the linker instead of those in libpam.so
+ *
+ * The test suite is thus isolated from the host system
+ * PAM setup, so we can do predictable test scenarios
+ */
+int
+pam_start(const char *service_name, const char *user,
+          const struct pam_conv *pam_conversation,
+          pam_handle_t **pamh)
+{
+    failauth = true;
+    if (!g_str_equal(service_name, "qemu-vnc")) {
+        return PAM_AUTH_ERR;
+    }
+
+    if (g_str_equal(user, "fred")) {
+        failauth = false;
+    }
+
+    return PAM_SUCCESS;
+}
+
+
+int
+pam_acct_mgmt(pam_handle_t *pamh, int flags)
+{
+    if (failauth) {
+        return PAM_AUTH_ERR;
+    }
+
+    return PAM_SUCCESS;
+}
+
+
+static void test_authz_unknown_service(void)
+{
+    Error *local_err = NULL;
+    QAuthZPAM *auth = qauthz_pam_new("auth0",
+                                     "qemu-does-not-exist",
+                                     &error_abort);
+
+    g_assert_nonnull(auth);
+
+    g_assert_false(qauthz_is_allowed(QAUTHZ(auth), "fred", &local_err));
+
+    error_free_or_abort(&local_err);
+    object_unparent(OBJECT(auth));
+}
+
+
+static void test_authz_good_user(void)
+{
+    QAuthZPAM *auth = qauthz_pam_new("auth0",
+                                     "qemu-vnc",
+                                     &error_abort);
+
+    g_assert_nonnull(auth);
+
+    g_assert_true(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+
+static void test_authz_bad_user(void)
+{
+    Error *local_err = NULL;
+    QAuthZPAM *auth = qauthz_pam_new("auth0",
+                                     "qemu-vnc",
+                                     &error_abort);
+
+    g_assert_nonnull(auth);
+
+    g_assert_false(qauthz_is_allowed(QAUTHZ(auth), "bob", &local_err));
+
+    error_free_or_abort(&local_err);
+    object_unparent(OBJECT(auth));
+}
+
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    module_call_init(MODULE_INIT_QOM);
+
+    g_test_add_func("/auth/pam/unknown-service", test_authz_unknown_service);
+    g_test_add_func("/auth/pam/good-user", test_authz_good_user);
+    g_test_add_func("/auth/pam/bad-user", test_authz_bad_user);
+
+    return g_test_run();
+}
diff --git a/tests/test-authz-simple.c b/tests/test-authz-simple.c
new file mode 100644
index 0000000000..2cf14fb87e
--- /dev/null
+++ b/tests/test-authz-simple.c
@@ -0,0 +1,50 @@
+/*
+ * QEMU simple authorization object testing
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+
+#include "authz/simple.h"
+
+
+static void test_authz_simple(void)
+{
+    QAuthZSimple *authz = qauthz_simple_new("authz0",
+                                            "cthulu",
+                                            &error_abort);
+
+    g_assert(!qauthz_is_allowed(QAUTHZ(authz), "cthul", &error_abort));
+    g_assert(qauthz_is_allowed(QAUTHZ(authz), "cthulu", &error_abort));
+    g_assert(!qauthz_is_allowed(QAUTHZ(authz), "cthuluu", &error_abort));
+    g_assert(!qauthz_is_allowed(QAUTHZ(authz), "fred", &error_abort));
+
+    object_unparent(OBJECT(authz));
+}
+
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+    module_call_init(MODULE_INIT_QOM);
+
+    g_test_add_func("/authz/simple", test_authz_simple);
+
+    return g_test_run();
+}
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
index ee1740ff06..eda90750eb 100644
--- a/tests/test-bdrv-drain.c
+++ b/tests/test-bdrv-drain.c
@@ -204,12 +204,7 @@ static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
     BlockAIOCB *acb;
     int aio_ret;
 
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base = NULL,
-        .iov_len = 0,
-    };
-    qemu_iovec_init_external(&qiov, &iov, 1);
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0);
 
     blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
     bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
@@ -670,12 +665,7 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread)
     AioContext *ctx_a = iothread_get_aio_context(a);
     AioContext *ctx_b = iothread_get_aio_context(b);
 
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base = NULL,
-        .iov_len = 0,
-    };
-    qemu_iovec_init_external(&qiov, &iov, 1);
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0);
 
     /* bdrv_drain_all() may only be called from the main loop thread */
     if (drain_type == BDRV_DRAIN_ALL && drain_thread != 0) {
@@ -1148,13 +1138,7 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque)
     BlockDriverState *bs = blk_bs(blk);
     BDRVTestTopState *tts = bs->opaque;
     void *buffer = g_malloc(65536);
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base = buffer,
-        .iov_len  = 65536,
-    };
-
-    qemu_iovec_init_external(&qiov, &iov, 1);
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buffer, 65536);
 
     /* Pretend some internal write operation from parent to child.
      * Important: We have to read from the child, not from the parent!
@@ -1365,12 +1349,7 @@ static void test_detach_indirect(bool by_parent_cb)
     BdrvChild *child_a, *child_b;
     BlockAIOCB *acb;
 
-    QEMUIOVector qiov;
-    struct iovec iov = {
-        .iov_base = NULL,
-        .iov_len = 0,
-    };
-    qemu_iovec_init_external(&qiov, &iov, 1);
+    QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0);
 
     if (!by_parent_cb) {
         detach_by_driver_cb_role = child_file;
@@ -1522,6 +1501,36 @@ static void test_append_to_drained(void)
     blk_unref(blk);
 }
 
+static void test_set_aio_context(void)
+{
+    BlockDriverState *bs;
+    IOThread *a = iothread_new();
+    IOThread *b = iothread_new();
+    AioContext *ctx_a = iothread_get_aio_context(a);
+    AioContext *ctx_b = iothread_get_aio_context(b);
+
+    bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
+                              &error_abort);
+
+    bdrv_drained_begin(bs);
+    bdrv_set_aio_context(bs, ctx_a);
+
+    aio_context_acquire(ctx_a);
+    bdrv_drained_end(bs);
+
+    bdrv_drained_begin(bs);
+    bdrv_set_aio_context(bs, ctx_b);
+    aio_context_release(ctx_a);
+    aio_context_acquire(ctx_b);
+    bdrv_set_aio_context(bs, qemu_get_aio_context());
+    aio_context_release(ctx_b);
+    bdrv_drained_end(bs);
+
+    bdrv_unref(bs);
+    iothread_join(a);
+    iothread_join(b);
+}
+
 int main(int argc, char **argv)
 {
     int ret;
@@ -1603,6 +1612,8 @@ int main(int argc, char **argv)
 
     g_test_add_func("/bdrv-drain/attach/drain", test_append_to_drained);
 
+    g_test_add_func("/bdrv-drain/set_aio_context", test_set_aio_context);
+
     ret = g_test_run();
     qemu_event_destroy(&done_event);
     return ret;
diff --git a/tests/test-bdrv-graph-mod.c b/tests/test-bdrv-graph-mod.c
new file mode 100644
index 0000000000..458dfa6661
--- /dev/null
+++ b/tests/test-bdrv-graph-mod.c
@@ -0,0 +1,198 @@
+/*
+ * Block node graph modifications tests
+ *
+ * Copyright (c) 2019 Virtuozzo International GmbH. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "block/block_int.h"
+#include "sysemu/block-backend.h"
+
+static BlockDriver bdrv_pass_through = {
+    .format_name = "pass-through",
+    .bdrv_child_perm = bdrv_filter_default_perms,
+};
+
+static void no_perm_default_perms(BlockDriverState *bs, BdrvChild *c,
+                                         const BdrvChildRole *role,
+                                         BlockReopenQueue *reopen_queue,
+                                         uint64_t perm, uint64_t shared,
+                                         uint64_t *nperm, uint64_t *nshared)
+{
+    *nperm = 0;
+    *nshared = BLK_PERM_ALL;
+}
+
+static BlockDriver bdrv_no_perm = {
+    .format_name = "no-perm",
+    .bdrv_child_perm = no_perm_default_perms,
+};
+
+static BlockDriverState *no_perm_node(const char *name)
+{
+    return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort);
+}
+
+static BlockDriverState *pass_through_node(const char *name)
+{
+    return bdrv_new_open_driver(&bdrv_pass_through, name,
+                                BDRV_O_RDWR, &error_abort);
+}
+
+/*
+ * test_update_perm_tree
+ *
+ * When checking node for a possibility to update permissions, it's subtree
+ * should be correctly checked too. New permissions for each node should be
+ * calculated and checked in context of permissions of other nodes. If we
+ * check new permissions of the node only in context of old permissions of
+ * its neighbors, we can finish up with wrong permission graph.
+ *
+ * This test firstly create the following graph:
+ *                                +--------+
+ *                                |  root  |
+ *                                +--------+
+ *                                    |
+ *                                    | perm: write, read
+ *                                    | shared: except write
+ *                                    v
+ *  +-------------------+           +----------------+
+ *  | passtrough filter |---------->|  null-co node  |
+ *  +-------------------+           +----------------+
+ *
+ *
+ * and then, tries to append filter under node. Expected behavior: fail.
+ * Otherwise we'll get the following picture, with two BdrvChild'ren, having
+ * write permission to one node, without actually sharing it.
+ *
+ *                     +--------+
+ *                     |  root  |
+ *                     +--------+
+ *                         |
+ *                         | perm: write, read
+ *                         | shared: except write
+ *                         v
+ *                +-------------------+
+ *                | passtrough filter |
+ *                +-------------------+
+ *                       |   |
+ *     perm: write, read |   | perm: write, read
+ *  shared: except write |   | shared: except write
+ *                       v   v
+ *                +----------------+
+ *                |  null co node  |
+ *                +----------------+
+ */
+static void test_update_perm_tree(void)
+{
+    Error *local_err = NULL;
+
+    BlockBackend *root = blk_new(BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ,
+                                 BLK_PERM_ALL & ~BLK_PERM_WRITE);
+    BlockDriverState *bs = no_perm_node("node");
+    BlockDriverState *filter = pass_through_node("filter");
+
+    blk_insert_bs(root, bs, &error_abort);
+
+    bdrv_attach_child(filter, bs, "child", &child_file, &error_abort);
+
+    bdrv_append(filter, bs, &local_err);
+
+    g_assert_nonnull(local_err);
+
+    bdrv_unref(bs);
+    blk_unref(root);
+}
+
+/*
+ * test_should_update_child
+ *
+ * Test that bdrv_replace_node, and concretely should_update_child
+ * do the right thing, i.e. not creating loops on the graph.
+ *
+ * The test does the following:
+ * 1. initial graph:
+ *
+ *   +------+          +--------+
+ *   | root |          | filter |
+ *   +------+          +--------+
+ *      |                  |
+ *  root|            target|
+ *      v                  v
+ *   +------+          +--------+
+ *   | node |<---------| target |
+ *   +------+  backing +--------+
+ *
+ * 2. Append @filter above @node. If should_update_child works correctly,
+ * it understands, that backing child of @target should not be updated,
+ * as it will create a loop on node graph. Resulting picture should
+ * be the left one, not the right:
+ *
+ *     +------+                            +------+
+ *     | root |                            | root |
+ *     +------+                            +------+
+ *        |                                   |
+ *    root|                               root|
+ *        v                                   v
+ *    +--------+   target                 +--------+   target
+ *    | filter |--------------+           | filter |--------------+
+ *    +--------+              |           +--------+              |
+ *        |                   |               |  ^                v
+ * backing|                   |        backing|  |           +--------+
+ *        v                   v               |  +-----------| target |
+ *     +------+          +--------+           v      backing +--------+
+ *     | node |<---------| target |        +------+
+ *     +------+  backing +--------+        | node |
+ *                                         +------+
+ *
+ *    (good picture)                       (bad picture)
+ *
+ */
+static void test_should_update_child(void)
+{
+    BlockBackend *root = blk_new(0, BLK_PERM_ALL);
+    BlockDriverState *bs = no_perm_node("node");
+    BlockDriverState *filter = no_perm_node("filter");
+    BlockDriverState *target = no_perm_node("target");
+
+    blk_insert_bs(root, bs, &error_abort);
+
+    bdrv_set_backing_hd(target, bs, &error_abort);
+
+    g_assert(target->backing->bs == bs);
+    bdrv_attach_child(filter, target, "target", &child_file, &error_abort);
+    bdrv_append(filter, bs, &error_abort);
+    g_assert(target->backing->bs == bs);
+
+    bdrv_unref(bs);
+    blk_unref(root);
+}
+
+int main(int argc, char *argv[])
+{
+    bdrv_init();
+    qemu_init_main_loop(&error_abort);
+
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree);
+    g_test_add_func("/bdrv-graph-mod/should-update-child",
+                    test_should_update_child);
+
+    return g_test_run();
+}
diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c
index 6fa9950afb..15212ec276 100644
--- a/tests/test-crypto-tlssession.c
+++ b/tests/test-crypto-tlssession.c
@@ -28,7 +28,7 @@
 #include "qom/object_interfaces.h"
 #include "qapi/error.h"
 #include "qemu/sockets.h"
-#include "qemu/acl.h"
+#include "authz/list.h"
 
 #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
 
@@ -229,7 +229,7 @@ static void test_crypto_tls_session_x509(const void *opaque)
     QCryptoTLSCreds *serverCreds;
     QCryptoTLSSession *clientSess = NULL;
     QCryptoTLSSession *serverSess = NULL;
-    qemu_acl *acl;
+    QAuthZList *auth;
     const char * const *wildcards;
     int channel[2];
     bool clientShake = false;
@@ -285,11 +285,15 @@ static void test_crypto_tls_session_x509(const void *opaque)
         SERVER_CERT_DIR);
     g_assert(serverCreds != NULL);
 
-    acl = qemu_acl_init("tlssessionacl");
-    qemu_acl_reset(acl);
+    auth = qauthz_list_new("tlssessionacl",
+                           QAUTHZ_LIST_POLICY_DENY,
+                           &error_abort);
     wildcards = data->wildcards;
     while (wildcards && *wildcards) {
-        qemu_acl_append(acl, 0, *wildcards);
+        qauthz_list_append_rule(auth, *wildcards,
+                                QAUTHZ_LIST_POLICY_ALLOW,
+                                QAUTHZ_LIST_FORMAT_GLOB,
+                                &error_abort);
         wildcards++;
     }
 
@@ -377,6 +381,7 @@ static void test_crypto_tls_session_x509(const void *opaque)
 
     object_unparent(OBJECT(serverCreds));
     object_unparent(OBJECT(clientCreds));
+    object_unparent(OBJECT(auth));
 
     qcrypto_tls_session_free(serverSess);
     qcrypto_tls_session_free(clientSess);
diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c
index 4900c6d433..43b707eba7 100644
--- a/tests/test-io-channel-tls.c
+++ b/tests/test-io-channel-tls.c
@@ -29,8 +29,8 @@
 #include "io-channel-helpers.h"
 #include "crypto/init.h"
 #include "crypto/tlscredsx509.h"
-#include "qemu/acl.h"
 #include "qapi/error.h"
+#include "authz/list.h"
 #include "qom/object_interfaces.h"
 
 #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
@@ -113,7 +113,7 @@ static void test_io_channel_tls(const void *opaque)
     QIOChannelTLS *serverChanTLS;
     QIOChannelSocket *clientChanSock;
     QIOChannelSocket *serverChanSock;
-    qemu_acl *acl;
+    QAuthZList *auth;
     const char * const *wildcards;
     int channel[2];
     struct QIOChannelTLSHandshakeData clientHandshake = { false, false };
@@ -161,11 +161,15 @@ static void test_io_channel_tls(const void *opaque)
         SERVER_CERT_DIR);
     g_assert(serverCreds != NULL);
 
-    acl = qemu_acl_init("channeltlsacl");
-    qemu_acl_reset(acl);
+    auth = qauthz_list_new("channeltlsacl",
+                           QAUTHZ_LIST_POLICY_DENY,
+                           &error_abort);
     wildcards = data->wildcards;
     while (wildcards && *wildcards) {
-        qemu_acl_append(acl, 0, *wildcards);
+        qauthz_list_append_rule(auth, *wildcards,
+                                QAUTHZ_LIST_POLICY_ALLOW,
+                                QAUTHZ_LIST_FORMAT_GLOB,
+                                &error_abort);
         wildcards++;
     }
 
@@ -253,6 +257,8 @@ static void test_io_channel_tls(const void *opaque)
     object_unref(OBJECT(serverChanSock));
     object_unref(OBJECT(clientChanSock));
 
+    object_unparent(OBJECT(auth));
+
     close(channel[0]);
     close(channel[1]);
 }
diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c
new file mode 100644
index 0000000000..5d95cea5ee
--- /dev/null
+++ b/tests/test-util-filemonitor.c
@@ -0,0 +1,685 @@
+/*
+ * Tests for util/filemonitor-*.c
+ *
+ * Copyright 2018 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qapi/error.h"
+#include "qemu/filemonitor.h"
+
+#include <utime.h>
+
+enum {
+    QFILE_MONITOR_TEST_OP_CREATE,
+    QFILE_MONITOR_TEST_OP_APPEND,
+    QFILE_MONITOR_TEST_OP_TRUNC,
+    QFILE_MONITOR_TEST_OP_RENAME,
+    QFILE_MONITOR_TEST_OP_TOUCH,
+    QFILE_MONITOR_TEST_OP_UNLINK,
+};
+
+typedef struct {
+    int type;
+    const char *filesrc;
+    const char *filedst;
+} QFileMonitorTestOp;
+
+typedef struct {
+    const char *file;
+} QFileMonitorTestWatch;
+
+typedef struct {
+    gsize nwatches;
+    const QFileMonitorTestWatch *watches;
+
+    gsize nops;
+    const QFileMonitorTestOp *ops;
+} QFileMonitorTestPlan;
+
+typedef struct {
+    int id;
+    QFileMonitorEvent event;
+    char *filename;
+} QFileMonitorTestRecord;
+
+
+typedef struct {
+    QemuMutex lock;
+    GList *records;
+} QFileMonitorTestData;
+
+static QemuMutex evlock;
+static bool evstopping;
+static bool evrunning;
+
+/*
+ * Main function for a background thread that is
+ * running the event loop during the test
+ */
+static void *
+qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED)
+{
+    qemu_mutex_lock(&evlock);
+
+    while (!evstopping) {
+        qemu_mutex_unlock(&evlock);
+        main_loop_wait(true);
+        qemu_mutex_lock(&evlock);
+    }
+
+    evrunning = false;
+    qemu_mutex_unlock(&evlock);
+    return NULL;
+}
+
+
+/*
+ * File monitor event handler which simply maintains
+ * an ordered list of all events that it receives
+ */
+static void
+qemu_file_monitor_test_handler(int id,
+                               QFileMonitorEvent event,
+                               const char *filename,
+                               void *opaque)
+{
+    QFileMonitorTestData *data = opaque;
+    QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1);
+
+    rec->id = id;
+    rec->event = event;
+    rec->filename = g_strdup(filename);
+
+    qemu_mutex_lock(&data->lock);
+    data->records = g_list_append(data->records, rec);
+    qemu_mutex_unlock(&data->lock);
+}
+
+
+static void
+qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec)
+{
+    g_free(rec->filename);
+    g_free(rec);
+}
+
+
+/*
+ * Get the next event record that has been received by
+ * the file monitor event handler. Since events are
+ * emitted in the background thread running the event
+ * loop, we can't assume there is a record available
+ * immediately. Thus we will sleep for upto 5 seconds
+ * to wait for the event to be queued for us.
+ */
+static QFileMonitorTestRecord *
+qemu_file_monitor_test_next_record(QFileMonitorTestData *data)
+{
+    GTimer *timer = g_timer_new();
+    QFileMonitorTestRecord *record = NULL;
+    GList *tmp;
+
+    qemu_mutex_lock(&data->lock);
+    while (!data->records && g_timer_elapsed(timer, NULL) < 5) {
+        qemu_mutex_unlock(&data->lock);
+        usleep(10 * 1000);
+        qemu_mutex_lock(&data->lock);
+    }
+    if (data->records) {
+        record = data->records->data;
+        tmp = data->records;
+        data->records = g_list_remove_link(data->records, tmp);
+        g_list_free(tmp);
+    }
+    qemu_mutex_unlock(&data->lock);
+
+    g_timer_destroy(timer);
+    return record;
+}
+
+
+/*
+ * Check whether the event record we retrieved matches
+ * data we were expecting to see for the event
+ */
+static bool
+qemu_file_monitor_test_expect(QFileMonitorTestData *data,
+                              int id,
+                              QFileMonitorEvent event,
+                              const char *filename)
+{
+    QFileMonitorTestRecord *rec;
+    bool ret = false;
+
+    rec = qemu_file_monitor_test_next_record(data);
+
+    if (!rec) {
+        g_printerr("Missing event watch id %d event %d file %s\n",
+                   id, event, filename);
+        return false;
+    }
+
+    if (id != rec->id) {
+        g_printerr("Expected watch id %d but got %d\n", id, rec->id);
+        goto cleanup;
+    }
+
+    if (event != rec->event) {
+        g_printerr("Expected event %d but got %d\n", event, rec->event);
+        goto cleanup;
+    }
+
+    if (!g_str_equal(filename, rec->filename)) {
+        g_printerr("Expected filename %s but got %s\n",
+                   filename, rec->filename);
+        goto cleanup;
+    }
+
+    ret = true;
+
+ cleanup:
+    qemu_file_monitor_test_record_free(rec);
+    return ret;
+}
+
+
+static void
+test_file_monitor_events(const void *opaque)
+{
+    const QFileMonitorTestPlan *plan = opaque;
+    Error *local_err = NULL;
+    GError *gerr = NULL;
+    QFileMonitor *mon = qemu_file_monitor_new(&local_err);
+    QemuThread th;
+    GTimer *timer;
+    gchar *dir = NULL;
+    int err = -1;
+    gsize i, j;
+    char *pathsrc = NULL;
+    char *pathdst = NULL;
+    QFileMonitorTestData data;
+
+    qemu_mutex_init(&data.lock);
+    data.records = NULL;
+
+    /*
+     * The file monitor needs the main loop running in
+     * order to receive events from inotify. We must
+     * thus spawn a background thread to run an event
+     * loop impl, while this thread triggers the
+     * actual file operations we're testing
+     */
+    evrunning = 1;
+    evstopping = 0;
+    qemu_thread_create(&th, "event-loop",
+                       qemu_file_monitor_test_event_loop, NULL,
+                       QEMU_THREAD_JOINABLE);
+
+    if (local_err) {
+        g_printerr("File monitoring not available: %s",
+                   error_get_pretty(local_err));
+        error_free(local_err);
+        return;
+    }
+
+    dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX",
+                         &gerr);
+    if (!dir) {
+        g_printerr("Unable to create tmp dir %s",
+                   gerr->message);
+        g_error_free(gerr);
+        abort();
+    }
+
+    /*
+     * First register all the directory / file watches
+     * we're interested in seeing events against
+     */
+    for (i = 0; i < plan->nwatches; i++) {
+        int watchid;
+        watchid = qemu_file_monitor_add_watch(mon,
+                                              dir,
+                                              plan->watches[i].file,
+                                              qemu_file_monitor_test_handler,
+                                              &data,
+                                              &local_err);
+        if (watchid < 0) {
+            g_printerr("Unable to add watch %s",
+                       error_get_pretty(local_err));
+            goto cleanup;
+        }
+    }
+
+
+    /*
+     * Now invoke all the file operations (create,
+     * delete, rename, chmod, etc). These operations
+     * will trigger the various file monitor events
+     */
+    for (i = 0; i < plan->nops; i++) {
+        const QFileMonitorTestOp *op = &(plan->ops[i]);
+        int fd;
+        struct utimbuf ubuf;
+
+        pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
+        if (op->filedst) {
+            pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
+        }
+
+        switch (op->type) {
+        case QFILE_MONITOR_TEST_OP_CREATE:
+            fd = open(pathsrc, O_WRONLY | O_CREAT, 0700);
+            if (fd < 0) {
+                g_printerr("Unable to create %s: %s",
+                           pathsrc, strerror(errno));
+                goto cleanup;
+            }
+            close(fd);
+            break;
+
+        case QFILE_MONITOR_TEST_OP_APPEND:
+            fd = open(pathsrc, O_WRONLY | O_APPEND, 0700);
+            if (fd < 0) {
+                g_printerr("Unable to open %s: %s",
+                           pathsrc, strerror(errno));
+                goto cleanup;
+            }
+
+            if (write(fd, "Hello World", 10) != 10) {
+                g_printerr("Unable to write %s: %s",
+                           pathsrc, strerror(errno));
+                close(fd);
+                goto cleanup;
+            }
+            close(fd);
+            break;
+
+        case QFILE_MONITOR_TEST_OP_TRUNC:
+            if (truncate(pathsrc, 4) < 0) {
+                g_printerr("Unable to truncate %s: %s",
+                           pathsrc, strerror(errno));
+                goto cleanup;
+            }
+            break;
+
+        case QFILE_MONITOR_TEST_OP_RENAME:
+            if (rename(pathsrc, pathdst) < 0) {
+                g_printerr("Unable to rename %s to %s: %s",
+                           pathsrc, pathdst, strerror(errno));
+                goto cleanup;
+            }
+            break;
+
+        case QFILE_MONITOR_TEST_OP_UNLINK:
+            if (unlink(pathsrc) < 0) {
+                g_printerr("Unable to unlink %s: %s",
+                           pathsrc, strerror(errno));
+                goto cleanup;
+            }
+            break;
+
+        case QFILE_MONITOR_TEST_OP_TOUCH:
+            ubuf.actime = 1024;
+            ubuf.modtime = 1025;
+            if (utime(pathsrc, &ubuf) < 0) {
+                g_printerr("Unable to touch %s: %s",
+                           pathsrc, strerror(errno));
+                goto cleanup;
+            }
+            break;
+
+        default:
+            g_assert_not_reached();
+        }
+
+        g_free(pathsrc);
+        g_free(pathdst);
+        pathsrc = pathdst = NULL;
+    }
+
+
+    /*
+     * Finally validate that we have received all the events
+     * we expect to see for the combination of watches and
+     * file operations
+     */
+    for (i = 0; i < plan->nops; i++) {
+        const QFileMonitorTestOp *op = &(plan->ops[i]);
+
+        switch (op->type) {
+        case QFILE_MONITOR_TEST_OP_CREATE:
+            for (j = 0; j < plan->nwatches; j++) {
+                if (plan->watches[j].file &&
+                    !g_str_equal(plan->watches[j].file, op->filesrc))
+                    continue;
+
+                if (!qemu_file_monitor_test_expect(
+                        &data, j, QFILE_MONITOR_EVENT_CREATED, op->filesrc))
+                    goto cleanup;
+            }
+            break;
+
+        case QFILE_MONITOR_TEST_OP_APPEND:
+        case QFILE_MONITOR_TEST_OP_TRUNC:
+            for (j = 0; j < plan->nwatches; j++) {
+                if (plan->watches[j].file &&
+                    !g_str_equal(plan->watches[j].file, op->filesrc))
+                    continue;
+
+                if (!qemu_file_monitor_test_expect(
+                        &data, j, QFILE_MONITOR_EVENT_MODIFIED, op->filesrc))
+                    goto cleanup;
+            }
+            break;
+
+        case QFILE_MONITOR_TEST_OP_RENAME:
+            for (j = 0; j < plan->nwatches; j++) {
+                if (plan->watches[j].file &&
+                    !g_str_equal(plan->watches[j].file, op->filesrc))
+                    continue;
+
+                if (!qemu_file_monitor_test_expect(
+                        &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc))
+                    goto cleanup;
+            }
+
+            for (j = 0; j < plan->nwatches; j++) {
+                if (plan->watches[j].file &&
+                    !g_str_equal(plan->watches[j].file, op->filedst))
+                    continue;
+
+                if (!qemu_file_monitor_test_expect(
+                        &data, j, QFILE_MONITOR_EVENT_CREATED, op->filedst))
+                    goto cleanup;
+            }
+            break;
+
+        case QFILE_MONITOR_TEST_OP_TOUCH:
+            for (j = 0; j < plan->nwatches; j++) {
+                if (plan->watches[j].file &&
+                    !g_str_equal(plan->watches[j].file, op->filesrc))
+                    continue;
+
+                if (!qemu_file_monitor_test_expect(
+                        &data, j, QFILE_MONITOR_EVENT_ATTRIBUTES, op->filesrc))
+                    goto cleanup;
+            }
+            break;
+
+        case QFILE_MONITOR_TEST_OP_UNLINK:
+            for (j = 0; j < plan->nwatches; j++) {
+                if (plan->watches[j].file &&
+                    !g_str_equal(plan->watches[j].file, op->filesrc))
+                    continue;
+
+                if (!qemu_file_monitor_test_expect(
+                        &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc))
+                    goto cleanup;
+            }
+            break;
+
+        default:
+            g_assert_not_reached();
+        }
+    }
+
+    err = 0;
+
+ cleanup:
+    g_free(pathsrc);
+    g_free(pathdst);
+
+    qemu_mutex_lock(&evlock);
+    evstopping = 1;
+    timer = g_timer_new();
+    while (evrunning && g_timer_elapsed(timer, NULL) < 5) {
+        qemu_mutex_unlock(&evlock);
+        usleep(10 * 1000);
+        qemu_mutex_lock(&evlock);
+    }
+    qemu_mutex_unlock(&evlock);
+
+    if (g_timer_elapsed(timer, NULL) >= 5) {
+        g_printerr("Event loop failed to quit after 5 seconds\n");
+    }
+    g_timer_destroy(timer);
+
+    for (i = 0; i < plan->nops; i++) {
+        const QFileMonitorTestOp *op = &(plan->ops[i]);
+        pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
+        unlink(pathsrc);
+        g_free(pathsrc);
+        if (op->filedst) {
+            pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
+            unlink(pathdst);
+            g_free(pathdst);
+        }
+    }
+
+    qemu_file_monitor_free(mon);
+    g_list_foreach(data.records,
+                   (GFunc)qemu_file_monitor_test_record_free, NULL);
+    g_list_free(data.records);
+    qemu_mutex_destroy(&data.lock);
+    if (dir) {
+        rmdir(dir);
+    }
+    g_free(dir);
+    g_assert(err == 0);
+}
+
+
+/*
+ * Set of structs which define which file name patterns
+ * we're trying to watch against. NULL, means all files
+ * in the directory
+ */
+static const QFileMonitorTestWatch watches_any[] = {
+    { NULL },
+};
+
+static const QFileMonitorTestWatch watches_one[] = {
+    { "one.txt" },
+};
+
+static const QFileMonitorTestWatch watches_two[] = {
+    { "two.txt" },
+};
+
+static const QFileMonitorTestWatch watches_many[] = {
+    { NULL },
+    { "one.txt" },
+    { "two.txt" },
+};
+
+
+/*
+ * Various sets of file operations we're going to
+ * trigger and validate events for
+ */
+static const QFileMonitorTestOp ops_create_one[] = {
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "one.txt", }
+};
+
+static const QFileMonitorTestOp ops_delete_one[] = {
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "one.txt", },
+    { .type = QFILE_MONITOR_TEST_OP_UNLINK,
+      .filesrc = "one.txt", }
+};
+
+static const QFileMonitorTestOp ops_create_many[] = {
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "one.txt", },
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "two.txt", },
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "three.txt", }
+};
+
+static const QFileMonitorTestOp ops_rename_one[] = {
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "one.txt", },
+    { .type = QFILE_MONITOR_TEST_OP_RENAME,
+      .filesrc = "one.txt", .filedst = "two.txt" }
+};
+
+static const QFileMonitorTestOp ops_rename_many[] = {
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "one.txt", },
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "two.txt", },
+    { .type = QFILE_MONITOR_TEST_OP_RENAME,
+      .filesrc = "one.txt", .filedst = "two.txt" }
+};
+
+static const QFileMonitorTestOp ops_append_one[] = {
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "one.txt", },
+    { .type = QFILE_MONITOR_TEST_OP_APPEND,
+      .filesrc = "one.txt", },
+};
+
+static const QFileMonitorTestOp ops_trunc_one[] = {
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "one.txt", },
+    { .type = QFILE_MONITOR_TEST_OP_TRUNC,
+      .filesrc = "one.txt", },
+};
+
+static const QFileMonitorTestOp ops_touch_one[] = {
+    { .type = QFILE_MONITOR_TEST_OP_CREATE,
+      .filesrc = "one.txt", },
+    { .type = QFILE_MONITOR_TEST_OP_TOUCH,
+      .filesrc = "one.txt", },
+};
+
+
+/*
+ * No we define data sets for the combinatorial
+ * expansion of file watches and operation sets
+ */
+#define PLAN_DATA(o, w) \
+    static const QFileMonitorTestPlan plan_ ## o ## _ ## w = { \
+        .nops = G_N_ELEMENTS(ops_ ##o), \
+        .ops = ops_ ##o, \
+        .nwatches = G_N_ELEMENTS(watches_ ##w), \
+        .watches = watches_ ## w, \
+    }
+
+PLAN_DATA(create_one, any);
+PLAN_DATA(create_one, one);
+PLAN_DATA(create_one, two);
+PLAN_DATA(create_one, many);
+
+PLAN_DATA(delete_one, any);
+PLAN_DATA(delete_one, one);
+PLAN_DATA(delete_one, two);
+PLAN_DATA(delete_one, many);
+
+PLAN_DATA(create_many, any);
+PLAN_DATA(create_many, one);
+PLAN_DATA(create_many, two);
+PLAN_DATA(create_many, many);
+
+PLAN_DATA(rename_one, any);
+PLAN_DATA(rename_one, one);
+PLAN_DATA(rename_one, two);
+PLAN_DATA(rename_one, many);
+
+PLAN_DATA(rename_many, any);
+PLAN_DATA(rename_many, one);
+PLAN_DATA(rename_many, two);
+PLAN_DATA(rename_many, many);
+
+PLAN_DATA(append_one, any);
+PLAN_DATA(append_one, one);
+PLAN_DATA(append_one, two);
+PLAN_DATA(append_one, many);
+
+PLAN_DATA(trunc_one, any);
+PLAN_DATA(trunc_one, one);
+PLAN_DATA(trunc_one, two);
+PLAN_DATA(trunc_one, many);
+
+PLAN_DATA(touch_one, any);
+PLAN_DATA(touch_one, one);
+PLAN_DATA(touch_one, two);
+PLAN_DATA(touch_one, many);
+
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    qemu_init_main_loop(&error_abort);
+
+    qemu_mutex_init(&evlock);
+
+    /*
+     * Register test cases for the combinatorial
+     * expansion of file watches and operation sets
+     */
+    #define PLAN_REGISTER(o, w)                                         \
+        g_test_add_data_func("/util/filemonitor/" # o "/" # w,          \
+                             &plan_ ## o ## _ ## w, test_file_monitor_events)
+
+    PLAN_REGISTER(create_one, any);
+    PLAN_REGISTER(create_one, one);
+    PLAN_REGISTER(create_one, two);
+    PLAN_REGISTER(create_one, many);
+
+    PLAN_REGISTER(delete_one, any);
+    PLAN_REGISTER(delete_one, one);
+    PLAN_REGISTER(delete_one, two);
+    PLAN_REGISTER(delete_one, many);
+
+    PLAN_REGISTER(create_many, any);
+    PLAN_REGISTER(create_many, one);
+    PLAN_REGISTER(create_many, two);
+    PLAN_REGISTER(create_many, many);
+
+    PLAN_REGISTER(rename_one, any);
+    PLAN_REGISTER(rename_one, one);
+    PLAN_REGISTER(rename_one, two);
+    PLAN_REGISTER(rename_one, many);
+
+    PLAN_REGISTER(rename_many, any);
+    PLAN_REGISTER(rename_many, one);
+    PLAN_REGISTER(rename_many, two);
+    PLAN_REGISTER(rename_many, many);
+
+    PLAN_REGISTER(append_one, any);
+    PLAN_REGISTER(append_one, one);
+    PLAN_REGISTER(append_one, two);
+    PLAN_REGISTER(append_one, many);
+
+    PLAN_REGISTER(trunc_one, any);
+    PLAN_REGISTER(trunc_one, one);
+    PLAN_REGISTER(trunc_one, two);
+    PLAN_REGISTER(trunc_one, many);
+
+    PLAN_REGISTER(touch_one, any);
+    PLAN_REGISTER(touch_one, one);
+    PLAN_REGISTER(touch_one, two);
+    PLAN_REGISTER(touch_one, many);
+
+    return g_test_run();
+}
diff --git a/tests/uefi-test-tools/.gitignore b/tests/uefi-test-tools/.gitignore
new file mode 100644
index 0000000000..9f246701de
--- /dev/null
+++ b/tests/uefi-test-tools/.gitignore
@@ -0,0 +1,3 @@
+Build
+Conf
+log
diff --git a/tests/uefi-test-tools/LICENSE b/tests/uefi-test-tools/LICENSE
new file mode 100644
index 0000000000..38b78aecdb
--- /dev/null
+++ b/tests/uefi-test-tools/LICENSE
@@ -0,0 +1,25 @@
+All the files in this directory and subdirectories are released under the
+2-Clause BSD License (see header in each file).
+
+Copyright (C) 2019, Red Hat, Inc.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tests/uefi-test-tools/Makefile b/tests/uefi-test-tools/Makefile
new file mode 100644
index 0000000000..1d78bc14d5
--- /dev/null
+++ b/tests/uefi-test-tools/Makefile
@@ -0,0 +1,106 @@
+# Makefile for the test helper UEFI applications that run in guests.
+#
+# Copyright (C) 2019, Red Hat, Inc.
+#
+# This program and the accompanying materials are licensed and made available
+# under the terms and conditions of the BSD License that accompanies this
+# distribution. The full text of the license may be found at
+# <http://opensource.org/licenses/bsd-license.php>.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+edk2_dir              := ../../roms/edk2
+images_dir            := ../data/uefi-boot-images
+emulation_targets     := arm aarch64 i386 x86_64
+uefi_binaries         := bios-tables-test
+intermediate_suffixes := .efi .fat .iso.raw
+
+images: $(foreach binary,$(uefi_binaries), \
+		$(foreach target,$(emulation_targets), \
+			$(images_dir)/$(binary).$(target).iso.qcow2))
+
+# Preserve all intermediate targets if the build succeeds.
+# - Intermediate targets help with development & debugging.
+# - Preserving intermediate targets also keeps spurious changes out of the
+#   final build products, in case the user re-runs "make" without any changes
+#   to the UEFI source code. Normally, the intermediate files would have been
+#   removed by the last "make" invocation, hence the re-run would rebuild them
+#   from the unchanged UEFI sources. Unfortunately, the "mkdosfs" and
+#   "genisoimage" utilities embed timestamp-based information in their outputs,
+#   which causes git to report differences for the tracked qcow2 ISO images.
+.SECONDARY: $(foreach binary,$(uefi_binaries), \
+		$(foreach target,$(emulation_targets), \
+			$(foreach suffix,$(intermediate_suffixes), \
+				Build/$(binary).$(target)$(suffix))))
+
+# In the pattern rules below, the stem (%, $*) stands for
+# "$(binary).$(target)".
+
+# Convert the raw ISO image to a qcow2 one, enabling compression, and using a
+# small cluster size. This allows for small binary files under git control,
+# hence for small binary patches.
+$(images_dir)/%.iso.qcow2: Build/%.iso.raw
+	mkdir -p -- $(images_dir)
+	$${QTEST_QEMU_IMG:-qemu-img} convert -f raw -O qcow2 -c \
+		-o cluster_size=512 -- $< $@
+
+# Embed the "UEFI system partition" into an ISO9660 file system as an ElTorito
+# boot image.
+Build/%.iso.raw: Build/%.fat
+	genisoimage -input-charset ASCII -efi-boot $(notdir $<) -no-emul-boot \
+		-quiet -o $@ -- $<
+
+# Define chained macros in order to map QEMU system emulation targets to
+# *short* UEFI architecture identifiers. Periods are allowed in, and ultimately
+# stripped from, the argument.
+map_arm_to_uefi     = $(subst arm,ARM,$(1))
+map_aarch64_to_uefi = $(subst aarch64,AA64,$(call map_arm_to_uefi,$(1)))
+map_i386_to_uefi    = $(subst i386,IA32,$(call map_aarch64_to_uefi,$(1)))
+map_x86_64_to_uefi  = $(subst x86_64,X64,$(call map_i386_to_uefi,$(1)))
+map_to_uefi         = $(subst .,,$(call map_x86_64_to_uefi,$(1)))
+
+# Format a "UEFI system partition", using the UEFI binary as the default boot
+# loader. Add 10% size for filesystem metadata, round up to the next KB, and
+# make sure the size is large enough for a FAT filesystem. Name the filesystem
+# after the UEFI binary. (Excess characters are automatically dropped from the
+# filesystem label.)
+Build/%.fat: Build/%.efi
+	rm -f -- $@
+	uefi_bin_b=$$(stat --format=%s -- $<) && \
+		uefi_fat_kb=$$(( (uefi_bin_b * 11 / 10 + 1023) / 1024 )) && \
+		uefi_fat_kb=$$(( uefi_fat_kb >= 64 ? uefi_fat_kb : 64 )) && \
+		mkdosfs -C $@ -n $(basename $(@F)) -- $$uefi_fat_kb
+	MTOOLS_SKIP_CHECK=1 mmd -i $@ ::EFI
+	MTOOLS_SKIP_CHECK=1 mmd -i $@ ::EFI/BOOT
+	MTOOLS_SKIP_CHECK=1 mcopy -i $@ -- $< \
+		::EFI/BOOT/BOOT$(call map_to_uefi,$(suffix $*)).EFI
+
+# In the pattern rules below, the stem (%, $*) stands for "$(target)" only. The
+# association between the UEFI binary (such as "bios-tables-test") and the
+# component name from the edk2 platform DSC file (such as "BiosTablesTest") is
+# explicit in each rule.
+
+# "build.sh" invokes the "build" utility of edk2 BaseTools. In any given edk2
+# workspace, at most one "build" instance may be operating at a time. Therefore
+# we must serialize the rebuilding of targets in this Makefile.
+.NOTPARALLEL:
+
+# In turn, the "build" utility of edk2 BaseTools invokes another "make".
+# Although the outer "make" process advertizes its job server to all child
+# processes via MAKEFLAGS in the environment, the outer "make" closes the job
+# server file descriptors (exposed in MAKEFLAGS) before executing a recipe --
+# unless the recipe is recognized as a recursive "make" recipe. Recipes that
+# call $(MAKE) are classified automatically as recursive; for "build.sh" below,
+# we must mark the recipe manually as recursive, by using the "+" indicator.
+# This way, when the inner "make" starts a parallel build of the target edk2
+# module, it can communicate with the outer "make"'s job server.
+Build/bios-tables-test.%.efi: build-edk2-tools
+	+./build.sh $(edk2_dir) BiosTablesTest $* $@
+
+build-edk2-tools:
+	$(MAKE) -C $(edk2_dir)/BaseTools
+
+clean:
+	rm -rf Build Conf log
+	$(MAKE) -C $(edk2_dir)/BaseTools clean
diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.c b/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.c
new file mode 100644
index 0000000000..b208e17fb0
--- /dev/null
+++ b/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.c
@@ -0,0 +1,130 @@
+/** @file
+  Populate the BIOS_TABLES_TEST structure.
+
+  Copyright (C) 2019, Red Hat, Inc.
+
+  This program and the accompanying materials are licensed and made available
+  under the terms and conditions of the BSD License that accompanies this
+  distribution. The full text of the license may be found at
+  <http://opensource.org/licenses/bsd-license.php>.
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+**/
+
+#include <Guid/Acpi.h>
+#include <Guid/BiosTablesTest.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+/**
+  Wait for a keypress with a message that the application is about to exit.
+**/
+STATIC
+VOID
+WaitForExitKeyPress (
+  VOID
+  )
+{
+  EFI_STATUS    Status;
+  UINTN         Idx;
+  EFI_INPUT_KEY Key;
+
+  if (gST->ConIn == NULL) {
+    return;
+  }
+  AsciiPrint ("%a: press any key to exit\n", gEfiCallerBaseName);
+  Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Idx);
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+  gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+}
+
+EFI_STATUS
+EFIAPI
+BiosTablesTestMain (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  VOID                          *Pages;
+  volatile BIOS_TABLES_TEST     *BiosTablesTest;
+  CONST VOID                    *Rsdp10;
+  CONST VOID                    *Rsdp20;
+  CONST EFI_CONFIGURATION_TABLE *ConfigTable;
+  CONST EFI_CONFIGURATION_TABLE *ConfigTablesEnd;
+  volatile EFI_GUID             *InverseSignature;
+  UINTN                         Idx;
+
+  Pages = AllocateAlignedPages (EFI_SIZE_TO_PAGES (sizeof *BiosTablesTest),
+            SIZE_1MB);
+  if (Pages == NULL) {
+    AsciiErrorPrint ("%a: AllocateAlignedPages() failed\n",
+      gEfiCallerBaseName);
+    //
+    // Assuming the application was launched by the boot manager as a boot
+    // loader, exiting with error will cause the boot manager to proceed with
+    // the remaining boot options. If there are no other boot options, the boot
+    // manager menu will be pulled up. Give the user a chance to read the error
+    // message.
+    //
+    WaitForExitKeyPress ();
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Locate both gEfiAcpi10TableGuid and gEfiAcpi20TableGuid config tables in
+  // one go.
+  //
+  Rsdp10 = NULL;
+  Rsdp20 = NULL;
+  ConfigTable = gST->ConfigurationTable;
+  ConfigTablesEnd = gST->ConfigurationTable + gST->NumberOfTableEntries;
+  while ((Rsdp10 == NULL || Rsdp20 == NULL) && ConfigTable < ConfigTablesEnd) {
+    if (CompareGuid (&ConfigTable->VendorGuid, &gEfiAcpi10TableGuid)) {
+      Rsdp10 = ConfigTable->VendorTable;
+    } else if (CompareGuid (&ConfigTable->VendorGuid, &gEfiAcpi20TableGuid)) {
+      Rsdp20 = ConfigTable->VendorTable;
+    }
+    ++ConfigTable;
+  }
+
+  AsciiPrint ("%a: BiosTablesTest=%p Rsdp10=%p Rsdp20=%p\n",
+    gEfiCallerBaseName, Pages, Rsdp10, Rsdp20);
+
+  //
+  // Store the RSD PTR address(es) first, then the signature second.
+  //
+  BiosTablesTest = Pages;
+  BiosTablesTest->Rsdp10 = (UINTN)Rsdp10;
+  BiosTablesTest->Rsdp20 = (UINTN)Rsdp20;
+
+  MemoryFence();
+
+  InverseSignature = &BiosTablesTest->InverseSignatureGuid;
+  InverseSignature->Data1  = gBiosTablesTestGuid.Data1;
+  InverseSignature->Data1 ^= MAX_UINT32;
+  InverseSignature->Data2  = gBiosTablesTestGuid.Data2;
+  InverseSignature->Data2 ^= MAX_UINT16;
+  InverseSignature->Data3  = gBiosTablesTestGuid.Data3;
+  InverseSignature->Data3 ^= MAX_UINT16;
+  for (Idx = 0; Idx < sizeof InverseSignature->Data4; ++Idx) {
+    InverseSignature->Data4[Idx]  = gBiosTablesTestGuid.Data4[Idx];
+    InverseSignature->Data4[Idx] ^= MAX_UINT8;
+  }
+
+  //
+  // The wait below has dual purpose. First, it blocks the application without
+  // wasting VCPU cycles while the hypervisor is scanning guest RAM. Second,
+  // assuming the application was launched by the boot manager as a boot
+  // loader, exiting the app with success causes the boot manager to pull up
+  // the boot manager menu at once (regardless of other boot options); the wait
+  // gives the user a chance to read the info printed above.
+  //
+  WaitForExitKeyPress ();
+  return EFI_SUCCESS;
+}
diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.inf b/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.inf
new file mode 100644
index 0000000000..924d8a80d0
--- /dev/null
+++ b/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.inf
@@ -0,0 +1,41 @@
+## @file
+# Populate the BIOS_TABLES_TEST structure.
+#
+# Copyright (C) 2019, Red Hat, Inc.
+#
+# This program and the accompanying materials are licensed and made available
+# under the terms and conditions of the BSD License that accompanies this
+# distribution. The full text of the license may be found at
+# <http://opensource.org/licenses/bsd-license.php>.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+##
+
+[Defines]
+  INF_VERSION                = 1.27
+  BASE_NAME                  = BiosTablesTest
+  UEFI_SPECIFICATION_VERSION = 2.31
+  FILE_GUID                  = 87f00433-3b7c-45c3-ae78-a56495bd4e62
+  MODULE_TYPE                = UEFI_APPLICATION
+  ENTRY_POINT                = BiosTablesTestMain
+
+[Sources]
+  BiosTablesTest.c
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  MemoryAllocationLib
+  UefiApplicationEntryPoint
+  UefiBootServicesTableLib
+  UefiLib
+
+[Guids]
+  gBiosTablesTestGuid
+  gEfiAcpi10TableGuid
+  gEfiAcpi20TableGuid
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UefiTestToolsPkg/UefiTestToolsPkg.dec
diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/Include/Guid/BiosTablesTest.h b/tests/uefi-test-tools/UefiTestToolsPkg/Include/Guid/BiosTablesTest.h
new file mode 100644
index 0000000000..0b72c61254
--- /dev/null
+++ b/tests/uefi-test-tools/UefiTestToolsPkg/Include/Guid/BiosTablesTest.h
@@ -0,0 +1,67 @@
+/** @file
+  Expose the address(es) of the ACPI RSD PTR table(s) in a MB-aligned structure
+  to the hypervisor.
+
+  The hypervisor locates the MB-aligned structure based on the signature GUID
+  that is at offset 0 in the structure. Once the RSD PTR address(es) are
+  retrieved, the hypervisor may perform various ACPI checks.
+
+  This feature is a development aid, for supporting ACPI table unit tests in
+  hypervisors. Do not enable in production builds.
+
+  Copyright (C) 2019, Red Hat, Inc.
+
+  This program and the accompanying materials are licensed and made available
+  under the terms and conditions of the BSD License that accompanies this
+  distribution. The full text of the license may be found at
+  <http://opensource.org/licenses/bsd-license.php>.
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+**/
+
+#ifndef __BIOS_TABLES_TEST_H__
+#define __BIOS_TABLES_TEST_H__
+
+#include <Uefi/UefiBaseType.h>
+
+#define BIOS_TABLES_TEST_GUID                          \
+  {                                                    \
+    0x5478594e,                                        \
+    0xdfcb,                                            \
+    0x425f,                                            \
+    { 0x8e, 0x42, 0xc8, 0xaf, 0xf8, 0x8a, 0x88, 0x7a } \
+  }
+
+extern EFI_GUID gBiosTablesTestGuid;
+
+//
+// The following structure must be allocated in Boot Services Data type memory,
+// aligned at a 1MB boundary.
+//
+#pragma pack (1)
+typedef struct {
+  //
+  // The signature GUID is written to the MB-aligned structure from
+  // gBiosTablesTestGuid, but with all bits inverted. That's the actual GUID
+  // value that the hypervisor should look for at each MB boundary, looping
+  // over all guest RAM pages with that alignment, until a match is found. The
+  // bit-flipping occurs in order not to store the actual GUID in any UEFI
+  // executable, which might confuse guest memory analysis. Note that EFI_GUID
+  // has little endian representation.
+  //
+  EFI_GUID             InverseSignatureGuid;
+  //
+  // The Rsdp10 and Rsdp20 fields may be read when the signature GUID matches.
+  // Rsdp10 is the guest-physical address of the ACPI 1.0 specification RSD PTR
+  // table, in 8-byte little endian representation. Rsdp20 is the same, for the
+  // ACPI 2.0 or later specification RSD PTR table. Each of these fields may be
+  // zero (independently of the other) if the UEFI System Table does not
+  // provide the corresponding UEFI Configuration Table.
+  //
+  EFI_PHYSICAL_ADDRESS Rsdp10;
+  EFI_PHYSICAL_ADDRESS Rsdp20;
+} BIOS_TABLES_TEST;
+#pragma pack ()
+
+#endif // __BIOS_TABLES_TEST_H__
diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dec b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dec
new file mode 100644
index 0000000000..ed3a2fe110
--- /dev/null
+++ b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dec
@@ -0,0 +1,27 @@
+## @file
+# edk2 package declaration for the test helper UEFI applications that run in
+# guests.
+#
+# Copyright (C) 2019, Red Hat, Inc.
+#
+# This program and the accompanying materials are licensed and made available
+# under the terms and conditions of the BSD License that accompanies this
+# distribution. The full text of the license may be found at
+# <http://opensource.org/licenses/bsd-license.php>.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+##
+
+[Defines]
+  DEC_SPECIFICATION = 1.27
+  PACKAGE_NAME      = UefiTestToolsPkg
+  PACKAGE_GUID      = 7b3f1794-0c85-4b27-a536-44dbf0b0669c
+  PACKAGE_VERSION   = 0.1
+
+[Includes]
+  Include
+
+[Guids]
+  gBiosTablesTestGuid = {0x5478594e, 0xdfcb, 0x425f, {0x8e, 0x42, 0xc8, 0xaf, 0xf8, 0x8a, 0x88, 0x7a}}
+
diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc
new file mode 100644
index 0000000000..c8511cd732
--- /dev/null
+++ b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc
@@ -0,0 +1,69 @@
+## @file
+# edk2 platform description for the test helper UEFI applications that run in
+# guests.
+#
+# Copyright (C) 2019, Red Hat, Inc.
+#
+# This program and the accompanying materials are licensed and made available
+# under the terms and conditions of the BSD License that accompanies this
+# distribution. The full text of the license may be found at
+# <http://opensource.org/licenses/bsd-license.php>.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+##
+
+[Defines]
+  DSC_SPECIFICATION       = 1.28
+  PLATFORM_GUID           = 6750ccc1-8365-49f0-8437-948e516a9f55
+  PLATFORM_VERSION        = 0.1
+  PLATFORM_NAME           = UefiTestTools
+  SKUID_IDENTIFIER        = DEFAULT
+  SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64
+  BUILD_TARGETS           = DEBUG
+
+[BuildOptions.IA32]
+  GCC:*_*_IA32_CC_FLAGS = -mno-mmx -mno-sse
+
+[BuildOptions.X64]
+  GCC:*_*_X64_CC_FLAGS = -mno-mmx -mno-sse
+
+[BuildOptions.ARM.EDKII.UEFI_APPLICATION]
+  GCC:*_*_ARM_DLINK_FLAGS = -z common-page-size=0x1000
+
+[BuildOptions.AARCH64.EDKII.UEFI_APPLICATION]
+  GCC:*_*_AARCH64_DLINK_FLAGS = -z common-page-size=0x1000
+
+[BuildOptions]
+  GCC:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
+
+[SkuIds]
+  0|DEFAULT
+
+[LibraryClasses]
+  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
+  DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
+  DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+  DevicePathLib|MdePkg/Library/UefiDevicePathLibDevicePathProtocol/UefiDevicePathLibDevicePathProtocol.inf
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
+  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
+  UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+
+[LibraryClasses.ARM, LibraryClasses.AARCH64]
+  BaseMemoryLib|MdePkg/Library/BaseMemoryLibOptDxe/BaseMemoryLibOptDxe.inf
+  NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
+  NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf
+
+[LibraryClasses.IA32, LibraryClasses.X64]
+  BaseMemoryLib|MdePkg/Library/BaseMemoryLibRepStr/BaseMemoryLibRepStr.inf
+
+[PcdsFixedAtBuild]
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8040004F
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2F
+
+[Components]
+  UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.inf
diff --git a/tests/uefi-test-tools/build.sh b/tests/uefi-test-tools/build.sh
new file mode 100755
index 0000000000..155cb75c4d
--- /dev/null
+++ b/tests/uefi-test-tools/build.sh
@@ -0,0 +1,145 @@
+#!/bin/bash
+
+# Build script that determines the edk2 toolchain to use, invokes the edk2
+# "build" utility, and copies the built UEFI binary to the requested location.
+#
+# Copyright (C) 2019, Red Hat, Inc.
+#
+# This program and the accompanying materials are licensed and made available
+# under the terms and conditions of the BSD License that accompanies this
+# distribution. The full text of the license may be found at
+# <http://opensource.org/licenses/bsd-license.php>.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+set -e -u -C
+
+# Save the command line arguments. We need to reset $# to 0 before sourcing
+# "edksetup.sh", as it will inherit $@.
+program_name=$(basename -- "$0")
+edk2_dir=$1
+dsc_component=$2
+emulation_target=$3
+uefi_binary=$4
+shift 4
+
+# Set up the environment for edk2 building.
+export PACKAGES_PATH=$(realpath -- "$edk2_dir")
+export WORKSPACE=$PWD
+mkdir -p Conf
+
+# Source "edksetup.sh" carefully.
+set +e +u +C
+source "$PACKAGES_PATH/edksetup.sh"
+ret=$?
+set -e -u -C
+if [ $ret -ne 0 ]; then
+  exit $ret
+fi
+
+# Map the QEMU system emulation target to the following types of architecture
+# identifiers:
+# - edk2,
+# - gcc cross-compilation.
+# Cover only those targets that are supported by the UEFI spec and edk2.
+case "$emulation_target" in
+  (arm)
+    edk2_arch=ARM
+    gcc_arch=arm
+    ;;
+  (aarch64)
+    edk2_arch=AARCH64
+    gcc_arch=aarch64
+    ;;
+  (i386)
+    edk2_arch=IA32
+    gcc_arch=i686
+    ;;
+  (x86_64)
+    edk2_arch=X64
+    gcc_arch=x86_64
+    ;;
+  (*)
+    printf '%s: unknown/unsupported QEMU system emulation target "%s"\n' \
+      "$program_name" "$emulation_target" >&2
+    exit 1
+    ;;
+esac
+
+# Check if cross-compilation is needed.
+host_arch=$(uname -m)
+if [ "$gcc_arch" == "$host_arch" ] ||
+   ( [ "$gcc_arch" == i686 ] && [ "$host_arch" == x86_64 ] ); then
+  cross_prefix=
+else
+  cross_prefix=${gcc_arch}-linux-gnu-
+fi
+
+# Expose cross_prefix (which is possibly empty) to the edk2 tools. While at it,
+# determine the suitable edk2 toolchain as well.
+# - For ARM and AARCH64, edk2 only offers the GCC5 toolchain tag, which covers
+#   the gcc-5+ releases.
+# - For IA32 and X64, edk2 offers the GCC44 through GCC49 toolchain tags, in
+#   addition to GCC5. Unfortunately, the mapping between the toolchain tags and
+#   the actual gcc releases isn't entirely trivial. Run "git-blame" on
+#   "OvmfPkg/build.sh" in edk2 for more information.
+# And, because the above is too simple, we have to assign cross_prefix to an
+# edk2 build variable that is specific to both the toolchain tag and the target
+# architecture.
+case "$edk2_arch" in
+  (ARM)
+    edk2_toolchain=GCC5
+    export GCC5_ARM_PREFIX=$cross_prefix
+    ;;
+  (AARCH64)
+    edk2_toolchain=GCC5
+    export GCC5_AARCH64_PREFIX=$cross_prefix
+    ;;
+  (IA32|X64)
+    gcc_version=$("${cross_prefix}gcc" -v 2>&1 | tail -1 | awk '{print $3}')
+    case "$gcc_version" in
+      ([1-3].*|4.[0-3].*)
+        printf '%s: unsupported gcc version "%s"\n' \
+          "$program_name" "$gcc_version" >&2
+        exit 1
+        ;;
+      (4.4.*)
+        edk2_toolchain=GCC44
+        ;;
+      (4.5.*)
+        edk2_toolchain=GCC45
+        ;;
+      (4.6.*)
+        edk2_toolchain=GCC46
+        ;;
+      (4.7.*)
+        edk2_toolchain=GCC47
+        ;;
+      (4.8.*)
+        edk2_toolchain=GCC48
+        ;;
+      (4.9.*|6.[0-2].*)
+        edk2_toolchain=GCC49
+        ;;
+      (*)
+        edk2_toolchain=GCC5
+        ;;
+    esac
+    eval "export ${edk2_toolchain}_BIN=\$cross_prefix"
+    ;;
+esac
+
+# Build the UEFI binary
+mkdir -p log
+build \
+  --arch="$edk2_arch" \
+  --buildtarget=DEBUG \
+  --platform=UefiTestToolsPkg/UefiTestToolsPkg.dsc \
+  --tagname="$edk2_toolchain" \
+  --module="UefiTestToolsPkg/$dsc_component/$dsc_component.inf" \
+  --log="log/$dsc_component.$edk2_arch.log" \
+  --report-file="log/$dsc_component.$edk2_arch.report"
+cp -a -- \
+  "Build/UefiTestTools/DEBUG_${edk2_toolchain}/$edk2_arch/$dsc_component.efi" \
+  "$uefi_binary"
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index d961bd09d1..4cd0a97f13 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -27,10 +27,13 @@
 #include "libqos/malloc-pc.h"
 #include "hw/virtio/virtio-net.h"
 
-#include <linux/vhost.h>
-#include <linux/virtio_ids.h>
-#include <linux/virtio_net.h>
+#include "standard-headers/linux/vhost_types.h"
+#include "standard-headers/linux/virtio_ids.h"
+#include "standard-headers/linux/virtio_net.h"
+
+#ifdef CONFIG_LINUX
 #include <sys/vfs.h>
+#endif
 
 
 #define QEMU_CMD_MEM    " -m %d -object memory-backend-file,id=mem,size=%dM," \
@@ -139,10 +142,15 @@ typedef struct TestServer {
     gchar *socket_path;
     gchar *mig_path;
     gchar *chr_name;
+    const gchar *mem_path;
+    gchar *tmpfs;
     CharBackend chr;
     int fds_num;
     int fds[VHOST_MEMORY_MAX_NREGIONS];
     VhostUserMemory memory;
+    GMainContext *context;
+    GMainLoop *loop;
+    GThread *thread;
     GMutex data_mutex;
     GCond data_cond;
     int log_fd;
@@ -157,9 +165,6 @@ static TestServer *test_server_new(const gchar *name);
 static void test_server_free(TestServer *server);
 static void test_server_listen(TestServer *server);
 
-static const char *tmpfs;
-static const char *root;
-
 enum test_memfd {
     TEST_MEMFD_AUTO,
     TEST_MEMFD_YES,
@@ -167,7 +172,7 @@ enum test_memfd {
 };
 
 static char *get_qemu_cmd(TestServer *s,
-                          int mem, enum test_memfd memfd, const char *mem_path,
+                          int mem, enum test_memfd memfd,
                           const char *chr_opts, const char *extra)
 {
     if (memfd == TEST_MEMFD_AUTO && qemu_memfd_check(0)) {
@@ -182,7 +187,7 @@ static char *get_qemu_cmd(TestServer *s,
     } else {
         return g_strdup_printf(QEMU_CMD_MEM QEMU_CMD_CHR
                                QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem,
-                               mem_path, s->chr_name, s->socket_path,
+                               s->mem_path, s->chr_name, s->socket_path,
                                chr_opts, s->chr_name, extra);
     }
 }
@@ -459,13 +464,20 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
     g_mutex_unlock(&s->data_mutex);
 }
 
-static const char *init_hugepagefs(const char *path)
+static const char *init_hugepagefs(void)
 {
+#ifdef CONFIG_LINUX
+    const char *path = getenv("QTEST_HUGETLBFS_PATH");
     struct statfs fs;
     int ret;
 
+    if (!path) {
+        return NULL;
+    }
+
     if (access(path, R_OK | W_OK | X_OK)) {
         g_test_message("access on path (%s): %s\n", path, strerror(errno));
+        abort();
         return NULL;
     }
 
@@ -475,21 +487,42 @@ static const char *init_hugepagefs(const char *path)
 
     if (ret != 0) {
         g_test_message("statfs on path (%s): %s\n", path, strerror(errno));
+        abort();
         return NULL;
     }
 
     if (fs.f_type != HUGETLBFS_MAGIC) {
         g_test_message("Warning: path not on HugeTLBFS: %s\n", path);
+        abort();
         return NULL;
     }
 
     return path;
+#else
+    return NULL;
+#endif
 }
 
 static TestServer *test_server_new(const gchar *name)
 {
     TestServer *server = g_new0(TestServer, 1);
+    char template[] = "/tmp/vhost-test-XXXXXX";
+    const char *tmpfs;
+
+    server->context = g_main_context_new();
+    server->loop = g_main_loop_new(server->context, FALSE);
+
+    /* run the main loop thread so the chardev may operate */
+    server->thread = g_thread_new(NULL, thread_function, server->loop);
+
+    tmpfs = mkdtemp(template);
+    if (!tmpfs) {
+        g_test_message("mkdtemp on path (%s): %s", template, strerror(errno));
+    }
+    g_assert(tmpfs);
 
+    server->tmpfs = g_strdup(tmpfs);
+    server->mem_path = init_hugepagefs() ? : server->tmpfs;
     server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name);
     server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name);
     server->chr_name = g_strdup_printf("chr-%s", name);
@@ -519,13 +552,13 @@ static void test_server_create_chr(TestServer *server, const gchar *opt)
     Chardev *chr;
 
     chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt);
-    chr = qemu_chr_new(server->chr_name, chr_path, NULL);
+    chr = qemu_chr_new(server->chr_name, chr_path, server->context);
     g_free(chr_path);
 
     g_assert_nonnull(chr);
     qemu_chr_fe_init(&server->chr, chr, &error_abort);
     qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read,
-                             chr_event, NULL, server, NULL, true);
+                             chr_event, NULL, server, server->context, true);
 }
 
 static void test_server_listen(TestServer *server)
@@ -533,9 +566,28 @@ static void test_server_listen(TestServer *server)
     test_server_create_chr(server, ",server,nowait");
 }
 
-static gboolean _test_server_free(TestServer *server)
+static void test_server_free(TestServer *server)
 {
-    int i;
+    int i, ret;
+
+    /* finish the helper thread and dispatch pending sources */
+    g_main_loop_quit(server->loop);
+    g_thread_join(server->thread);
+    while (g_main_context_pending(NULL)) {
+        g_main_context_iteration(NULL, TRUE);
+    }
+
+    unlink(server->socket_path);
+    g_free(server->socket_path);
+
+    unlink(server->mig_path);
+    g_free(server->mig_path);
+
+    ret = rmdir(server->tmpfs);
+    if (ret != 0) {
+        g_test_message("unable to rmdir: path (%s): %s",
+                       server->tmpfs, strerror(errno));
+    }
 
     qemu_chr_fe_deinit(&server->chr, true);
 
@@ -547,24 +599,13 @@ static gboolean _test_server_free(TestServer *server)
         close(server->log_fd);
     }
 
-    unlink(server->socket_path);
-    g_free(server->socket_path);
-
-    unlink(server->mig_path);
-    g_free(server->mig_path);
-
     g_free(server->chr_name);
     g_assert(server->bus);
     qpci_free_pc(server->bus);
 
+    g_main_loop_unref(server->loop);
+    g_main_context_unref(server->context);
     g_free(server);
-
-    return FALSE;
-}
-
-static void test_server_free(TestServer *server)
-{
-    g_idle_add((GSourceFunc)_test_server_free, server);
 }
 
 static void wait_for_log_fd(TestServer *s)
@@ -665,7 +706,7 @@ static void test_read_guest_mem(const void *arg)
                              "read-guest-memfd" : "read-guest-mem");
     test_server_listen(server);
 
-    qemu_cmd = get_qemu_cmd(server, 512, memfd, root, "", "");
+    qemu_cmd = get_qemu_cmd(server, 512, memfd, "", "");
 
     s = qtest_start(qemu_cmd);
     g_free(qemu_cmd);
@@ -700,7 +741,7 @@ static void test_migrate(void)
     test_server_listen(s);
     test_server_listen(dest);
 
-    cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, "", "");
+    cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, "", "");
     from = qtest_start(cmd);
     g_free(cmd);
 
@@ -713,7 +754,7 @@ static void test_migrate(void)
     g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
 
     tmp = g_strdup_printf(" -incoming %s", uri);
-    cmd = get_qemu_cmd(dest, 2, TEST_MEMFD_AUTO, root, "", tmp);
+    cmd = get_qemu_cmd(dest, 2, TEST_MEMFD_AUTO, "", tmp);
     g_free(tmp);
     to = qtest_init(cmd);
     g_free(cmd);
@@ -723,7 +764,7 @@ static void test_migrate(void)
                           sizeof(TestMigrateSource));
     ((TestMigrateSource *)source)->src = s;
     ((TestMigrateSource *)source)->dest = dest;
-    g_source_attach(source, NULL);
+    g_source_attach(source, s->context);
 
     /* slow down migration to have time to fiddle with log */
     /* TODO: qtest could learn to break on some places */
@@ -820,10 +861,11 @@ connect_thread(gpointer data)
 static void test_reconnect_subprocess(void)
 {
     TestServer *s = test_server_new("reconnect");
+    GSource *src;
     char *cmd;
 
     g_thread_new("connect", connect_thread, s);
-    cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", "");
+    cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, ",server", "");
     qtest_start(cmd);
     g_free(cmd);
 
@@ -837,7 +879,10 @@ static void test_reconnect_subprocess(void)
     /* reconnect */
     s->fds_num = 0;
     s->rings = 0;
-    g_idle_add(reconnect_cb, s);
+    src = g_idle_source_new();
+    g_source_set_callback(src, reconnect_cb, s, NULL);
+    g_source_attach(src, s->context);
+    g_source_unref(src);
     g_assert(wait_for_fds(s));
     wait_for_rings_started(s, 2);
 
@@ -865,7 +910,7 @@ static void test_connect_fail_subprocess(void)
 
     s->test_fail = true;
     g_thread_new("connect", connect_thread, s);
-    cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", "");
+    cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, ",server", "");
     qtest_start(cmd);
     g_free(cmd);
 
@@ -898,7 +943,7 @@ static void test_flags_mismatch_subprocess(void)
 
     s->test_flags = TEST_FLAGS_DISCONNECT;
     g_thread_new("connect", connect_thread, s);
-    cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", "");
+    cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, ",server", "");
     qtest_start(cmd);
     g_free(cmd);
 
@@ -946,7 +991,7 @@ static void test_multiqueue(void)
         cmd = g_strdup_printf(
             QEMU_CMD_MEM QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d "
             "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d",
-            512, 512, root, s->chr_name,
+            512, 512, s->mem_path, s->chr_name,
             s->socket_path, "", s->chr_name,
             s->queues, s->queues * 2 + 2);
     }
@@ -966,35 +1011,11 @@ static void test_multiqueue(void)
 
 int main(int argc, char **argv)
 {
-    const char *hugefs;
-    int ret;
-    char template[] = "/tmp/vhost-test-XXXXXX";
-    GMainLoop *loop;
-    GThread *thread;
-
     g_test_init(&argc, &argv, NULL);
 
     module_call_init(MODULE_INIT_QOM);
     qemu_add_opts(&qemu_chardev_opts);
 
-    tmpfs = mkdtemp(template);
-    if (!tmpfs) {
-        g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno));
-    }
-    g_assert(tmpfs);
-
-    hugefs = getenv("QTEST_HUGETLBFS_PATH");
-    if (hugefs) {
-        root = init_hugepagefs(hugefs);
-        g_assert(root);
-    } else {
-        root = tmpfs;
-    }
-
-    loop = g_main_loop_new(NULL, FALSE);
-    /* run the main loop thread so the chardev may operate */
-    thread = g_thread_new(NULL, thread_function, loop);
-
     if (qemu_memfd_check(0)) {
         qtest_add_data_func("/vhost-user/read-guest-mem/memfd",
                             GINT_TO_POINTER(TEST_MEMFD_YES),
@@ -1018,24 +1039,5 @@ int main(int argc, char **argv)
         qtest_add_func("/vhost-user/flags-mismatch", test_flags_mismatch);
     }
 
-    ret = g_test_run();
-
-    /* cleanup */
-
-    /* finish the helper thread and dispatch pending sources */
-    g_main_loop_quit(loop);
-    g_thread_join(thread);
-    while (g_main_context_pending(NULL)) {
-        g_main_context_iteration (NULL, TRUE);
-    }
-    g_main_loop_unref(loop);
-
-    ret = rmdir(tmpfs);
-    if (ret != 0) {
-        g_test_message("unable to rmdir: path (%s): %s\n",
-                       tmpfs, strerror(errno));
-    }
-    g_assert_cmpint(ret, ==, 0);
-
-    return ret;
+    return g_test_run();
 }
diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c
index 04c608764b..8d2fc9c710 100644
--- a/tests/virtio-blk-test.c
+++ b/tests/virtio-blk-test.c
@@ -46,6 +46,12 @@ typedef struct QVirtioBlkReq {
     uint8_t status;
 } QVirtioBlkReq;
 
+#ifdef HOST_WORDS_BIGENDIAN
+const bool host_is_big_endian = true;
+#else
+const bool host_is_big_endian; /* false */
+#endif
+
 static char *drive_create(void)
 {
     int fd, ret;
@@ -125,12 +131,6 @@ static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot)
 
 static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
 {
-#ifdef HOST_WORDS_BIGENDIAN
-    const bool host_is_big_endian = true;
-#else
-    const bool host_is_big_endian = false;
-#endif
-
     if (qvirtio_is_big_endian(d) != host_is_big_endian) {
         req->type = bswap32(req->type);
         req->ioprio = bswap32(req->ioprio);
@@ -138,13 +138,37 @@ static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
     }
 }
 
+
+static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d,
+    struct virtio_blk_discard_write_zeroes *dwz_hdr)
+{
+    if (qvirtio_is_big_endian(d) != host_is_big_endian) {
+        dwz_hdr->sector = bswap64(dwz_hdr->sector);
+        dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors);
+        dwz_hdr->flags = bswap32(dwz_hdr->flags);
+    }
+}
+
 static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
                                    QVirtioBlkReq *req, uint64_t data_size)
 {
     uint64_t addr;
     uint8_t status = 0xFF;
 
-    g_assert_cmpuint(data_size % 512, ==, 0);
+    switch (req->type) {
+    case VIRTIO_BLK_T_IN:
+    case VIRTIO_BLK_T_OUT:
+        g_assert_cmpuint(data_size % 512, ==, 0);
+        break;
+    case VIRTIO_BLK_T_DISCARD:
+    case VIRTIO_BLK_T_WRITE_ZEROES:
+        g_assert_cmpuint(data_size %
+                         sizeof(struct virtio_blk_discard_write_zeroes), ==, 0);
+        break;
+    default:
+        g_assert_cmpuint(data_size, ==, 0);
+    }
+
     addr = guest_alloc(alloc, sizeof(*req) + data_size);
 
     virtio_blk_fix_request(d, req);
@@ -231,6 +255,95 @@ static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc,
 
     guest_free(alloc, req_addr);
 
+    if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) {
+        struct virtio_blk_discard_write_zeroes dwz_hdr;
+        void *expected;
+
+        /*
+         * WRITE_ZEROES request on the same sector of previous test where
+         * we wrote "TEST".
+         */
+        req.type = VIRTIO_BLK_T_WRITE_ZEROES;
+        req.data = (char *) &dwz_hdr;
+        dwz_hdr.sector = 0;
+        dwz_hdr.num_sectors = 1;
+        dwz_hdr.flags = 0;
+
+        virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
+
+        req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
+
+        free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
+        qvirtqueue_add(vq, req_addr + 16, sizeof(dwz_hdr), false, true);
+        qvirtqueue_add(vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false);
+
+        qvirtqueue_kick(dev, vq, free_head);
+
+        qvirtio_wait_used_elem(dev, vq, free_head, NULL,
+                               QVIRTIO_BLK_TIMEOUT_US);
+        status = readb(req_addr + 16 + sizeof(dwz_hdr));
+        g_assert_cmpint(status, ==, 0);
+
+        guest_free(alloc, req_addr);
+
+        /* Read request to check if the sector contains all zeroes */
+        req.type = VIRTIO_BLK_T_IN;
+        req.ioprio = 1;
+        req.sector = 0;
+        req.data = g_malloc0(512);
+
+        req_addr = virtio_blk_request(alloc, dev, &req, 512);
+
+        g_free(req.data);
+
+        free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
+        qvirtqueue_add(vq, req_addr + 16, 512, true, true);
+        qvirtqueue_add(vq, req_addr + 528, 1, true, false);
+
+        qvirtqueue_kick(dev, vq, free_head);
+
+        qvirtio_wait_used_elem(dev, vq, free_head, NULL,
+                               QVIRTIO_BLK_TIMEOUT_US);
+        status = readb(req_addr + 528);
+        g_assert_cmpint(status, ==, 0);
+
+        data = g_malloc(512);
+        expected = g_malloc0(512);
+        memread(req_addr + 16, data, 512);
+        g_assert_cmpmem(data, 512, expected, 512);
+        g_free(expected);
+        g_free(data);
+
+        guest_free(alloc, req_addr);
+    }
+
+    if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
+        struct virtio_blk_discard_write_zeroes dwz_hdr;
+
+        req.type = VIRTIO_BLK_T_DISCARD;
+        req.data = (char *) &dwz_hdr;
+        dwz_hdr.sector = 0;
+        dwz_hdr.num_sectors = 1;
+        dwz_hdr.flags = 0;
+
+        virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
+
+        req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
+
+        free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
+        qvirtqueue_add(vq, req_addr + 16, sizeof(dwz_hdr), false, true);
+        qvirtqueue_add(vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false);
+
+        qvirtqueue_kick(dev, vq, free_head);
+
+        qvirtio_wait_used_elem(dev, vq, free_head, NULL,
+                               QVIRTIO_BLK_TIMEOUT_US);
+        status = readb(req_addr + 16 + sizeof(dwz_hdr));
+        g_assert_cmpint(status, ==, 0);
+
+        guest_free(alloc, req_addr);
+    }
+
     if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
         /* Write and read with 2 descriptor layout */
         /* Write request */
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 7f8b3da791..fe1a7aed97 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -49,6 +49,11 @@ curses.mo-objs := curses.o
 curses.mo-cflags := $(CURSES_CFLAGS)
 curses.mo-libs := $(CURSES_LIBS)
 
+common-obj-$(call land,$(CONFIG_SPICE),$(CONFIG_GIO)) += spice-app.mo
+spice-app.mo-objs := spice-app.o
+spice-app.mo-cflags := $(GIO_CFLAGS)
+spice-app.mo-libs := $(GIO_LIBS)
+
 common-obj-$(CONFIG_OPENGL) += shader.o
 common-obj-$(CONFIG_OPENGL) += console-gl.o
 common-obj-$(CONFIG_OPENGL) += egl-helpers.o
diff --git a/ui/gtk.c b/ui/gtk.c
index 949b143e4e..e96e15435a 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -6,29 +6,25 @@
  * Authors:
  *  Anthony Liguori   <aliguori@us.ibm.com>
  *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
  *
- * Portions from gtk-vnc:
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Portions from gtk-vnc (originally licensed under the LGPL v2+):
  *
  * GTK VNC Widget
  *
  * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
  * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.0 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
 #define GETTEXT_PACKAGE "qemu"
diff --git a/ui/kbd-state.c b/ui/kbd-state.c
index ac14add70e..f3ab2d7a66 100644
--- a/ui/kbd-state.c
+++ b/ui/kbd-state.c
@@ -42,14 +42,18 @@ void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down)
 {
     bool state = test_bit(qcode, kbd->keys);
 
-    if (state == down) {
+    if (down == false  /* got key-up event   */ &&
+        state == false /* key is not pressed */) {
         /*
-         * Filter out events which don't change the keyboard state.
+         * Filter out suspicious key-up events.
          *
-         * Most notably this allows to simply send along all key-up
-         * events, and this function will filter out everything where
-         * the corresponding key-down event wasn't send to the guest,
-         * for example due to being a host hotkey.
+         * This allows simply sending along all key-up events, and
+         * this function will filter out everything where the
+         * corresponding key-down event wasn't sent to the guest, for
+         * example due to being a host hotkey.
+         *
+         * Note that key-down events on already pressed keys are *not*
+         * suspicious, those are keyboard autorepeat events.
          */
         return;
     }
diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c
index 664364a5e5..fb345f45fb 100644
--- a/ui/sdl2-input.c
+++ b/ui/sdl2-input.c
@@ -54,8 +54,5 @@ void sdl2_process_key(struct sdl2_console *scon,
                 break;
             }
         }
-    } else {
-        qemu_input_event_send_key_qcode(con, qcode,
-                                        ev->type == SDL_KEYDOWN);
     }
 }
diff --git a/ui/spice-app.c b/ui/spice-app.c
new file mode 100644
index 0000000000..925b27b708
--- /dev/null
+++ b/ui/spice-app.c
@@ -0,0 +1,202 @@
+/*
+ * QEMU external Spice client display driver
+ *
+ * Copyright (c) 2018 Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+
+#include <gio/gio.h>
+
+#include "qemu-common.h"
+#include "ui/console.h"
+#include "qemu/config-file.h"
+#include "qemu/option.h"
+#include "qemu/cutils.h"
+#include "qapi/error.h"
+#include "io/channel-command.h"
+#include "chardev/spice.h"
+#include "sysemu/sysemu.h"
+
+static const char *tmp_dir;
+static char *app_dir;
+static char *sock_path;
+
+typedef struct VCChardev {
+    SpiceChardev parent;
+} VCChardev;
+
+#define TYPE_CHARDEV_VC "chardev-vc"
+#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+
+static ChardevBackend *
+chr_spice_backend_new(void)
+{
+    ChardevBackend *be = g_new0(ChardevBackend, 1);
+
+    be->type = CHARDEV_BACKEND_KIND_SPICEPORT;
+    be->u.spiceport.data = g_new0(ChardevSpicePort, 1);
+
+    return be;
+}
+
+static void vc_chr_open(Chardev *chr,
+                        ChardevBackend *backend,
+                        bool *be_opened,
+                        Error **errp)
+{
+    ChardevBackend *be;
+    const char *fqdn = NULL;
+
+    if (strstart(chr->label, "serial", NULL)) {
+        fqdn = "org.qemu.console.serial.0";
+    } else if (strstart(chr->label, "parallel", NULL)) {
+        fqdn = "org.qemu.console.parallel.0";
+    } else if (strstart(chr->label, "compat_monitor", NULL)) {
+        fqdn = "org.qemu.monitor.hmp.0";
+    }
+
+    be = chr_spice_backend_new();
+    be->u.spiceport.data->fqdn = fqdn ?
+        g_strdup(fqdn) : g_strdup_printf("org.qemu.console.%s", chr->label);
+    qemu_chr_open_spice_port(chr, be, be_opened, errp);
+    qapi_free_ChardevBackend(be);
+}
+
+static void vc_chr_set_echo(Chardev *chr, bool echo)
+{
+    /* TODO: set echo for frontends QMP and qtest */
+}
+
+static void char_vc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_vc;
+    cc->open = vc_chr_open;
+    cc->chr_set_echo = vc_chr_set_echo;
+}
+
+static const TypeInfo char_vc_type_info = {
+    .name = TYPE_CHARDEV_VC,
+    .parent = TYPE_CHARDEV_SPICEPORT,
+    .instance_size = sizeof(VCChardev),
+    .class_init = char_vc_class_init,
+};
+
+static void spice_app_atexit(void)
+{
+    if (sock_path) {
+        unlink(sock_path);
+    }
+    if (tmp_dir) {
+        rmdir(tmp_dir);
+    }
+    g_free(sock_path);
+    g_free(app_dir);
+}
+
+static void spice_app_display_early_init(DisplayOptions *opts)
+{
+    QemuOpts *qopts;
+    ChardevBackend *be = chr_spice_backend_new();
+    GError *err = NULL;
+
+    if (opts->has_full_screen) {
+        error_report("spice-app full-screen isn't supported yet.");
+        exit(1);
+    }
+    if (opts->has_window_close) {
+        error_report("spice-app window-close isn't supported yet.");
+        exit(1);
+    }
+
+    atexit(spice_app_atexit);
+
+    if (qemu_name) {
+        app_dir = g_build_filename(g_get_user_runtime_dir(),
+                                   "qemu", qemu_name, NULL);
+        if (g_mkdir_with_parents(app_dir, S_IRWXU) < -1) {
+            error_report("Failed to create directory %s: %s",
+                         app_dir, strerror(errno));
+            exit(1);
+        }
+    } else {
+        app_dir = g_dir_make_tmp(NULL, &err);
+        tmp_dir = app_dir;
+        if (err) {
+            error_report("Failed to create temporary directory: %s",
+                         err->message);
+            exit(1);
+        }
+    }
+
+    type_register(&char_vc_type_info);
+
+    sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL);
+    qopts = qemu_opts_create(qemu_find_opts("spice"), NULL, 0, &error_abort);
+    qemu_opt_set(qopts, "disable-ticketing", "on", &error_abort);
+    qemu_opt_set(qopts, "unix", "on", &error_abort);
+    qemu_opt_set(qopts, "addr", sock_path, &error_abort);
+    qemu_opt_set(qopts, "image-compression", "off", &error_abort);
+    qemu_opt_set(qopts, "streaming-video", "off", &error_abort);
+    qemu_opt_set(qopts, "gl", opts->has_gl ? "on" : "off", &error_abort);
+    display_opengl = opts->has_gl;
+
+    be->u.spiceport.data->fqdn = g_strdup("org.qemu.monitor.qmp.0");
+    qemu_chardev_new("org.qemu.monitor.qmp", TYPE_CHARDEV_SPICEPORT,
+                     be, NULL, &error_abort);
+    qopts = qemu_opts_create(qemu_find_opts("mon"),
+                             NULL, 0, &error_fatal);
+    qemu_opt_set(qopts, "chardev", "org.qemu.monitor.qmp", &error_abort);
+    qemu_opt_set(qopts, "mode", "control", &error_abort);
+
+    qapi_free_ChardevBackend(be);
+}
+
+static void spice_app_display_init(DisplayState *ds, DisplayOptions *opts)
+{
+    GError *err = NULL;
+    gchar *uri;
+
+    uri = g_strjoin("", "spice+unix://", app_dir, "/", "spice.sock", NULL);
+    info_report("Launching display with URI: %s", uri);
+    g_app_info_launch_default_for_uri(uri, NULL, &err);
+    if (err) {
+        error_report("Failed to launch %s URI: %s", uri, err->message);
+        error_report("You need a capable Spice client, "
+                     "such as virt-viewer 8.0");
+        exit(1);
+    }
+    g_free(uri);
+}
+
+static QemuDisplay qemu_display_spice_app = {
+    .type       = DISPLAY_TYPE_SPICE_APP,
+    .early_init = spice_app_display_early_init,
+    .init       = spice_app_display_init,
+};
+
+static void register_spice_app(void)
+{
+    qemu_display_register(&qemu_display_spice_app);
+}
+
+type_init(register_spice_app);
diff --git a/ui/spice-core.c b/ui/spice-core.c
index a40fb2c00d..0632c74e9f 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -34,6 +34,7 @@
 #include "qemu/option.h"
 #include "migration/misc.h"
 #include "hw/hw.h"
+#include "hw/pci/pci_bus.h"
 #include "ui/spice-display.h"
 
 /* core bits */
@@ -397,6 +398,7 @@ static SpiceChannelList *qmp_query_spice_channels(void)
 static QemuOptsList qemu_spice_opts = {
     .name = "spice",
     .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head),
+    .merge_lists = true,
     .desc = {
         {
             .name = "port",
@@ -626,7 +628,7 @@ static void vm_change_state_handler(void *opaque, int running,
 {
     if (running) {
         qemu_spice_display_start();
-    } else {
+    } else if (state != RUN_STATE_PAUSED) {
         qemu_spice_display_stop();
     }
 }
@@ -783,7 +785,7 @@ void qemu_spice_init(void)
 
     qemu_opt_foreach(opts, add_channel, &tls_port, &error_fatal);
 
-    spice_server_set_name(spice_server, qemu_name);
+    spice_server_set_name(spice_server, qemu_name ?: "QEMU " QEMU_VERSION);
     spice_server_set_uuid(spice_server, (unsigned char *)&qemu_uuid);
 
     seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0);
@@ -863,6 +865,56 @@ bool qemu_spice_have_display_interface(QemuConsole *con)
     return false;
 }
 
+/*
+ * Recursively (in reverse order) appends addresses of PCI devices as it moves
+ * up in the PCI hierarchy.
+ *
+ * @returns true on success, false when the buffer wasn't large enough
+ */
+static bool append_pci_address(char *buf, size_t buf_size, const PCIDevice *pci)
+{
+    PCIBus *bus = pci_get_bus(pci);
+    /*
+     * equivalent to if (!pci_bus_is_root(bus)), but the function is not built
+     * with PCI_CONFIG=n, avoid using an #ifdef by checking directly
+     */
+    if (bus->parent_dev != NULL) {
+        append_pci_address(buf, buf_size, bus->parent_dev);
+    }
+
+    size_t len = strlen(buf);
+    ssize_t written = snprintf(buf + len, buf_size - len, "/%02x.%x",
+        PCI_SLOT(pci->devfn), PCI_FUNC(pci->devfn));
+
+    return written > 0 && written < buf_size - len;
+}
+
+bool qemu_spice_fill_device_address(QemuConsole *con,
+                                    char *device_address,
+                                    size_t size)
+{
+    DeviceState *dev = DEVICE(object_property_get_link(OBJECT(con),
+                                                       "device",
+                                                       &error_abort));
+    PCIDevice *pci = (PCIDevice *) object_dynamic_cast(OBJECT(dev),
+                                                       TYPE_PCI_DEVICE);
+
+    if (pci == NULL) {
+        warn_report("Setting device address of a display device to SPICE: "
+                    "Not a PCI device.");
+        return false;
+    }
+
+    strncpy(device_address, "pci/0000", size);
+    if (!append_pci_address(device_address, size, pci)) {
+        warn_report("Setting device address of a display device to SPICE: "
+            "Too many PCI devices in the chain.");
+        return false;
+    }
+
+    return true;
+}
+
 int qemu_spice_add_display_interface(QXLInstance *qxlin, QemuConsole *con)
 {
     if (g_slist_find(spice_consoles, con)) {
@@ -921,12 +973,20 @@ int qemu_spice_display_add_client(int csock, int skipauth, int tls)
 
 void qemu_spice_display_start(void)
 {
+    if (spice_display_is_running) {
+        return;
+    }
+
     spice_display_is_running = true;
     spice_server_vm_start(spice_server);
 }
 
 void qemu_spice_display_stop(void)
 {
+    if (!spice_display_is_running) {
+        return;
+    }
+
     spice_server_vm_stop(spice_server);
     spice_display_is_running = false;
 }
diff --git a/ui/spice-display.c b/ui/spice-display.c
index aea6f6ebce..a5e26479a8 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -1147,6 +1147,17 @@ static void qemu_spice_display_init_one(QemuConsole *con)
 
     ssd->qxl.base.sif = &dpy_interface.base;
     qemu_spice_add_display_interface(&ssd->qxl, con);
+
+#if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */
+    char device_address[256] = "";
+    if (qemu_spice_fill_device_address(con, device_address, 256)) {
+        spice_qxl_set_device_info(&ssd->qxl,
+                                  device_address,
+                                  qemu_console_get_head(con),
+                                  1);
+    }
+#endif
+
     qemu_spice_create_host_memslot(ssd);
 
     register_displaychangelistener(&ssd->dcl);
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index 3751a777a4..7b2b09f242 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -24,6 +24,7 @@
 
 #include "qemu/osdep.h"
 #include "qapi/error.h"
+#include "authz/base.h"
 #include "vnc.h"
 #include "trace.h"
 
@@ -146,13 +147,14 @@ size_t vnc_client_read_sasl(VncState *vs)
 static int vnc_auth_sasl_check_access(VncState *vs)
 {
     const void *val;
-    int err;
-    int allow;
+    int rv;
+    Error *err = NULL;
+    bool allow;
 
-    err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
-    if (err != SASL_OK) {
+    rv = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
+    if (rv != SASL_OK) {
         trace_vnc_auth_fail(vs, vs->auth, "Cannot fetch SASL username",
-                            sasl_errstring(err, NULL, NULL));
+                            sasl_errstring(rv, NULL, NULL));
         return -1;
     }
     if (val == NULL) {
@@ -163,12 +165,19 @@ static int vnc_auth_sasl_check_access(VncState *vs)
     vs->sasl.username = g_strdup((const char*)val);
     trace_vnc_auth_sasl_username(vs, vs->sasl.username);
 
-    if (vs->vd->sasl.acl == NULL) {
+    if (vs->vd->sasl.authzid == NULL) {
         trace_vnc_auth_sasl_acl(vs, 1);
         return 0;
     }
 
-    allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username);
+    allow = qauthz_is_allowed_by_id(vs->vd->sasl.authzid,
+                                    vs->sasl.username, &err);
+    if (err) {
+        trace_vnc_auth_fail(vs, vs->auth, "Error from authz",
+                            error_get_pretty(err));
+        error_free(err);
+        return -1;
+    }
 
     trace_vnc_auth_sasl_acl(vs, allow);
     return allow ? 0 : -1;
diff --git a/ui/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h
index 2ae224ee3a..fb55fe04ca 100644
--- a/ui/vnc-auth-sasl.h
+++ b/ui/vnc-auth-sasl.h
@@ -30,8 +30,8 @@
 typedef struct VncStateSASL VncStateSASL;
 typedef struct VncDisplaySASL VncDisplaySASL;
 
-#include "qemu/acl.h"
 #include "qemu/main-loop.h"
+#include "authz/base.h"
 
 struct VncStateSASL {
     sasl_conn_t *conn;
@@ -60,7 +60,8 @@ struct VncStateSASL {
 };
 
 struct VncDisplaySASL {
-    qemu_acl *acl;
+    QAuthZ *authz;
+    char *authzid;
 };
 
 void vnc_sasl_client_cleanup(VncState *vs);
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index d99ea362c1..f072e16ace 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -109,7 +109,7 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
         tls = qio_channel_tls_new_server(
             vs->ioc,
             vs->vd->tlscreds,
-            vs->vd->tlsaclname,
+            vs->vd->tlsauthzid,
             &err);
         if (!tls) {
             trace_vnc_auth_fail(vs, vs->auth, "TLS setup failed",
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index 950f1cd2ac..95c9703c72 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -62,7 +62,7 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
     tls = qio_channel_tls_new_server(
         vs->ioc,
         vs->vd->tlscreds,
-        vs->vd->tlsaclname,
+        vs->vd->tlsauthzid,
         &err);
     if (!tls) {
         VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
diff --git a/ui/vnc.c b/ui/vnc.c
index 7e0710ed8f..da4a21d4ce 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -33,7 +33,7 @@
 #include "qemu/option.h"
 #include "qemu/sockets.h"
 #include "qemu/timer.h"
-#include "qemu/acl.h"
+#include "authz/list.h"
 #include "qemu/config-file.h"
 #include "qapi/qapi-emit-events.h"
 #include "qapi/qapi-events-ui.h"
@@ -3229,12 +3229,24 @@ static void vnc_display_close(VncDisplay *vd)
         object_unparent(OBJECT(vd->tlscreds));
         vd->tlscreds = NULL;
     }
-    g_free(vd->tlsaclname);
-    vd->tlsaclname = NULL;
+    if (vd->tlsauthz) {
+        object_unparent(OBJECT(vd->tlsauthz));
+        vd->tlsauthz = NULL;
+    }
+    g_free(vd->tlsauthzid);
+    vd->tlsauthzid = NULL;
     if (vd->lock_key_sync) {
         qemu_remove_led_event_handler(vd->led);
         vd->led = NULL;
     }
+#ifdef CONFIG_VNC_SASL
+    if (vd->sasl.authz) {
+        object_unparent(OBJECT(vd->sasl.authz));
+        vd->sasl.authz = NULL;
+    }
+    g_free(vd->sasl.authzid);
+    vd->sasl.authzid = NULL;
+#endif
 }
 
 int vnc_display_password(const char *id, const char *password)
@@ -3887,23 +3899,24 @@ void vnc_display_open(const char *id, Error **errp)
 
     if (acl) {
         if (strcmp(vd->id, "default") == 0) {
-            vd->tlsaclname = g_strdup("vnc.x509dname");
+            vd->tlsauthzid = g_strdup("vnc.x509dname");
         } else {
-            vd->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vd->id);
+            vd->tlsauthzid = g_strdup_printf("vnc.%s.x509dname", vd->id);
         }
-        qemu_acl_init(vd->tlsaclname);
+        vd->tlsauthz = QAUTHZ(qauthz_list_new(vd->tlsauthzid,
+                                              QAUTHZ_LIST_POLICY_DENY,
+                                              &error_abort));
     }
 #ifdef CONFIG_VNC_SASL
     if (acl && sasl) {
-        char *aclname;
-
         if (strcmp(vd->id, "default") == 0) {
-            aclname = g_strdup("vnc.username");
+            vd->sasl.authzid = g_strdup("vnc.username");
         } else {
-            aclname = g_strdup_printf("vnc.%s.username", vd->id);
+            vd->sasl.authzid = g_strdup_printf("vnc.%s.username", vd->id);
         }
-        vd->sasl.acl = qemu_acl_init(aclname);
-        g_free(aclname);
+        vd->sasl.authz = QAUTHZ(qauthz_list_new(vd->sasl.authzid,
+                                                QAUTHZ_LIST_POLICY_DENY,
+                                                &error_abort));
     }
 #endif
 
diff --git a/ui/vnc.h b/ui/vnc.h
index 81daa7a0eb..ee3da08f4a 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -39,6 +39,7 @@
 #include "io/channel-socket.h"
 #include "io/channel-tls.h"
 #include "io/net-listener.h"
+#include "authz/base.h"
 #include <zlib.h>
 
 #include "keymaps.h"
@@ -178,7 +179,8 @@ struct VncDisplay
     bool lossy;
     bool non_adaptive;
     QCryptoTLSCreds *tlscreds;
-    char *tlsaclname;
+    QAuthZ *tlsauthz;
+    char *tlsauthzid;
 #ifdef CONFIG_VNC_SASL
     VncDisplaySASL sasl;
 #endif
diff --git a/util/Makefile.objs b/util/Makefile.objs
index 0820923c18..0808575e3e 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -20,7 +20,6 @@ util-obj-y += envlist.o path.o module.o
 util-obj-y += host-utils.o
 util-obj-y += bitmap.o bitops.o hbitmap.o
 util-obj-y += fifo8.o
-util-obj-y += acl.o
 util-obj-y += cacheinfo.o
 util-obj-y += error.o qemu-error.o
 util-obj-y += id.o
@@ -50,5 +49,8 @@ util-obj-y += range.o
 util-obj-y += stats64.o
 util-obj-y += systemd.o
 util-obj-y += iova-tree.o
+util-obj-$(CONFIG_INOTIFY1) += filemonitor-inotify.o
 util-obj-$(CONFIG_LINUX) += vfio-helpers.o
 util-obj-$(CONFIG_OPENGL) += drm.o
+
+stub-obj-y += filemonitor-stub.o
diff --git a/util/acl.c b/util/acl.c
deleted file mode 100644
index c105addadc..0000000000
--- a/util/acl.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * QEMU access control list management
- *
- * Copyright (C) 2009 Red Hat, Inc
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "qemu/acl.h"
-
-#ifdef CONFIG_FNMATCH
-#include <fnmatch.h>
-#endif
-
-
-static unsigned int nacls = 0;
-static qemu_acl **acls = NULL;
-
-
-
-qemu_acl *qemu_acl_find(const char *aclname)
-{
-    int i;
-    for (i = 0 ; i < nacls ; i++) {
-        if (strcmp(acls[i]->aclname, aclname) == 0)
-            return acls[i];
-    }
-
-    return NULL;
-}
-
-qemu_acl *qemu_acl_init(const char *aclname)
-{
-    qemu_acl *acl;
-
-    acl = qemu_acl_find(aclname);
-    if (acl)
-        return acl;
-
-    acl = g_malloc(sizeof(*acl));
-    acl->aclname = g_strdup(aclname);
-    /* Deny by default, so there is no window of "open
-     * access" between QEMU starting, and the user setting
-     * up ACLs in the monitor */
-    acl->defaultDeny = 1;
-
-    acl->nentries = 0;
-    QTAILQ_INIT(&acl->entries);
-
-    acls = g_realloc(acls, sizeof(*acls) * (nacls +1));
-    acls[nacls] = acl;
-    nacls++;
-
-    return acl;
-}
-
-int qemu_acl_party_is_allowed(qemu_acl *acl,
-                              const char *party)
-{
-    qemu_acl_entry *entry;
-
-    QTAILQ_FOREACH(entry, &acl->entries, next) {
-#ifdef CONFIG_FNMATCH
-        if (fnmatch(entry->match, party, 0) == 0)
-            return entry->deny ? 0 : 1;
-#else
-        /* No fnmatch, so fallback to exact string matching
-         * instead of allowing wildcards */
-        if (strcmp(entry->match, party) == 0)
-            return entry->deny ? 0 : 1;
-#endif
-    }
-
-    return acl->defaultDeny ? 0 : 1;
-}
-
-
-void qemu_acl_reset(qemu_acl *acl)
-{
-    qemu_acl_entry *entry, *next_entry;
-
-    /* Put back to deny by default, so there is no window
-     * of "open access" while the user re-initializes the
-     * access control list */
-    acl->defaultDeny = 1;
-    QTAILQ_FOREACH_SAFE(entry, &acl->entries, next, next_entry) {
-        QTAILQ_REMOVE(&acl->entries, entry, next);
-        g_free(entry->match);
-        g_free(entry);
-    }
-    acl->nentries = 0;
-}
-
-
-int qemu_acl_append(qemu_acl *acl,
-                    int deny,
-                    const char *match)
-{
-    qemu_acl_entry *entry;
-
-    entry = g_malloc(sizeof(*entry));
-    entry->match = g_strdup(match);
-    entry->deny = deny;
-
-    QTAILQ_INSERT_TAIL(&acl->entries, entry, next);
-    acl->nentries++;
-
-    return acl->nentries;
-}
-
-
-int qemu_acl_insert(qemu_acl *acl,
-                    int deny,
-                    const char *match,
-                    int index)
-{
-    qemu_acl_entry *tmp;
-    int i = 0;
-
-    if (index <= 0)
-        return -1;
-    if (index > acl->nentries) {
-        return qemu_acl_append(acl, deny, match);
-    }
-
-    QTAILQ_FOREACH(tmp, &acl->entries, next) {
-        i++;
-        if (i == index) {
-            qemu_acl_entry *entry;
-            entry = g_malloc(sizeof(*entry));
-            entry->match = g_strdup(match);
-            entry->deny = deny;
-
-            QTAILQ_INSERT_BEFORE(tmp, entry, next);
-            acl->nentries++;
-            break;
-        }
-    }
-
-    return i;
-}
-
-int qemu_acl_remove(qemu_acl *acl,
-                    const char *match)
-{
-    qemu_acl_entry *entry;
-    int i = 0;
-
-    QTAILQ_FOREACH(entry, &acl->entries, next) {
-        i++;
-        if (strcmp(entry->match, match) == 0) {
-            QTAILQ_REMOVE(&acl->entries, entry, next);
-            acl->nentries--;
-            g_free(entry->match);
-            g_free(entry);
-            return i;
-        }
-    }
-    return -1;
-}
diff --git a/util/aio-posix.c b/util/aio-posix.c
index 8640dfde9f..6fbfa7924f 100644
--- a/util/aio-posix.c
+++ b/util/aio-posix.c
@@ -613,6 +613,8 @@ bool aio_poll(AioContext *ctx, bool blocking)
     int64_t timeout;
     int64_t start = 0;
 
+    assert(in_aio_context_home_thread(ctx));
+
     /* aio_notify can avoid the expensive event_notifier_set if
      * everything (file descriptors, bottom halves, timers) will
      * be re-evaluated before the next blocking poll().  This is
@@ -621,7 +623,6 @@ bool aio_poll(AioContext *ctx, bool blocking)
      * so disable the optimization now.
      */
     if (blocking) {
-        assert(in_aio_context_home_thread(ctx));
         atomic_add(&ctx->notify_me, 2);
     }
 
diff --git a/util/filemonitor-inotify.c b/util/filemonitor-inotify.c
new file mode 100644
index 0000000000..3a72be037f
--- /dev/null
+++ b/util/filemonitor-inotify.c
@@ -0,0 +1,339 @@
+/*
+ * QEMU file monitor Linux inotify impl
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/filemonitor.h"
+#include "qemu/main-loop.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "trace.h"
+
+#include <sys/inotify.h>
+
+struct QFileMonitor {
+    int fd;
+
+    QemuMutex lock; /* protects dirs & idmap */
+    GHashTable *dirs; /* dirname => QFileMonitorDir */
+    GHashTable *idmap; /* inotify ID => dirname */
+};
+
+
+typedef struct {
+    int id; /* watch ID */
+    char *filename; /* optional filter */
+    QFileMonitorHandler cb;
+    void *opaque;
+} QFileMonitorWatch;
+
+
+typedef struct {
+    char *path;
+    int id; /* inotify ID */
+    int nextid; /* watch ID counter */
+    GArray *watches; /* QFileMonitorWatch elements */
+} QFileMonitorDir;
+
+
+static void qemu_file_monitor_watch(void *arg)
+{
+    QFileMonitor *mon = arg;
+    char buf[4096]
+        __attribute__ ((aligned(__alignof__(struct inotify_event))));
+    int used = 0;
+    int len;
+
+    qemu_mutex_lock(&mon->lock);
+
+    if (mon->fd == -1) {
+        qemu_mutex_unlock(&mon->lock);
+        return;
+    }
+
+    len = read(mon->fd, buf, sizeof(buf));
+
+    if (len < 0) {
+        if (errno != EAGAIN) {
+            error_report("Failure monitoring inotify FD '%s',"
+                         "disabling events", strerror(errno));
+            goto cleanup;
+        }
+
+        /* no more events right now */
+        goto cleanup;
+    }
+
+    /* Loop over all events in the buffer */
+    while (used < len) {
+        struct inotify_event *ev =
+            (struct inotify_event *)(buf + used);
+        const char *name = ev->len ? ev->name : "";
+        QFileMonitorDir *dir = g_hash_table_lookup(mon->idmap,
+                                                   GINT_TO_POINTER(ev->wd));
+        uint32_t iev = ev->mask &
+            (IN_CREATE | IN_MODIFY | IN_DELETE | IN_IGNORED |
+             IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB);
+        int qev;
+        gsize i;
+
+        used += sizeof(struct inotify_event) + ev->len;
+
+        if (!dir) {
+            continue;
+        }
+
+        /*
+         * During a rename operation, the old name gets
+         * IN_MOVED_FROM and the new name gets IN_MOVED_TO.
+         * To simplify life for callers, we turn these into
+         * DELETED and CREATED events
+         */
+        switch (iev) {
+        case IN_CREATE:
+        case IN_MOVED_TO:
+            qev = QFILE_MONITOR_EVENT_CREATED;
+            break;
+        case IN_MODIFY:
+            qev = QFILE_MONITOR_EVENT_MODIFIED;
+            break;
+        case IN_DELETE:
+        case IN_MOVED_FROM:
+            qev = QFILE_MONITOR_EVENT_DELETED;
+            break;
+        case IN_ATTRIB:
+            qev = QFILE_MONITOR_EVENT_ATTRIBUTES;
+            break;
+        case IN_IGNORED:
+            qev = QFILE_MONITOR_EVENT_IGNORED;
+            break;
+        default:
+            g_assert_not_reached();
+        }
+
+        trace_qemu_file_monitor_event(mon, dir->path, name, ev->mask, dir->id);
+        for (i = 0; i < dir->watches->len; i++) {
+            QFileMonitorWatch *watch = &g_array_index(dir->watches,
+                                                      QFileMonitorWatch,
+                                                      i);
+
+            if (watch->filename == NULL ||
+                (name && g_str_equal(watch->filename, name))) {
+                trace_qemu_file_monitor_dispatch(mon, dir->path, name,
+                                                 qev, watch->cb,
+                                                 watch->opaque, watch->id);
+                watch->cb(watch->id, qev, name, watch->opaque);
+            }
+        }
+    }
+
+ cleanup:
+    qemu_mutex_unlock(&mon->lock);
+}
+
+
+static void
+qemu_file_monitor_dir_free(void *data)
+{
+    QFileMonitorDir *dir = data;
+    gsize i;
+
+    for (i = 0; i < dir->watches->len; i++) {
+        QFileMonitorWatch *watch = &g_array_index(dir->watches,
+                                                  QFileMonitorWatch, i);
+        g_free(watch->filename);
+    }
+    g_array_unref(dir->watches);
+    g_free(dir->path);
+    g_free(dir);
+}
+
+
+QFileMonitor *
+qemu_file_monitor_new(Error **errp)
+{
+    int fd;
+    QFileMonitor *mon;
+
+    fd = inotify_init1(IN_NONBLOCK);
+    if (fd < 0) {
+        error_setg_errno(errp, errno,
+                         "Unable to initialize inotify");
+        return NULL;
+    }
+
+    mon = g_new0(QFileMonitor, 1);
+    qemu_mutex_init(&mon->lock);
+    mon->fd = fd;
+
+    mon->dirs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+                                      qemu_file_monitor_dir_free);
+    mon->idmap = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+    trace_qemu_file_monitor_new(mon, mon->fd);
+
+    return mon;
+}
+
+static gboolean
+qemu_file_monitor_free_idle(void *opaque)
+{
+    QFileMonitor *mon = opaque;
+
+    if (!mon) {
+        return G_SOURCE_REMOVE;
+    }
+
+    qemu_mutex_lock(&mon->lock);
+
+    g_hash_table_unref(mon->idmap);
+    g_hash_table_unref(mon->dirs);
+
+    qemu_mutex_unlock(&mon->lock);
+
+    qemu_mutex_destroy(&mon->lock);
+    g_free(mon);
+
+    return G_SOURCE_REMOVE;
+}
+
+void
+qemu_file_monitor_free(QFileMonitor *mon)
+{
+    if (!mon) {
+        return;
+    }
+
+    qemu_mutex_lock(&mon->lock);
+    if (mon->fd != -1) {
+        qemu_set_fd_handler(mon->fd, NULL, NULL, NULL);
+        close(mon->fd);
+        mon->fd = -1;
+    }
+    qemu_mutex_unlock(&mon->lock);
+
+    /*
+     * Can't free it yet, because another thread
+     * may be running event loop, so the inotify
+     * callback might be pending. Using an idle
+     * source ensures we'll only free after the
+     * pending callback is done
+     */
+    g_idle_add((GSourceFunc)qemu_file_monitor_free_idle, mon);
+}
+
+int
+qemu_file_monitor_add_watch(QFileMonitor *mon,
+                            const char *dirpath,
+                            const char *filename,
+                            QFileMonitorHandler cb,
+                            void *opaque,
+                            Error **errp)
+{
+    QFileMonitorDir *dir;
+    QFileMonitorWatch watch;
+    int ret = -1;
+
+    qemu_mutex_lock(&mon->lock);
+    dir = g_hash_table_lookup(mon->dirs, dirpath);
+    if (!dir) {
+        int rv = inotify_add_watch(mon->fd, dirpath,
+                                   IN_CREATE | IN_DELETE | IN_MODIFY |
+                                   IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB);
+
+        if (rv < 0) {
+            error_setg_errno(errp, errno, "Unable to watch '%s'", dirpath);
+            goto cleanup;
+        }
+
+        trace_qemu_file_monitor_enable_watch(mon, dirpath, rv);
+
+        dir = g_new0(QFileMonitorDir, 1);
+        dir->path = g_strdup(dirpath);
+        dir->id = rv;
+        dir->watches = g_array_new(FALSE, TRUE, sizeof(QFileMonitorWatch));
+
+        g_hash_table_insert(mon->dirs, dir->path, dir);
+        g_hash_table_insert(mon->idmap, GINT_TO_POINTER(rv), dir);
+
+        if (g_hash_table_size(mon->dirs) == 1) {
+            qemu_set_fd_handler(mon->fd, qemu_file_monitor_watch, NULL, mon);
+        }
+    }
+
+    watch.id = dir->nextid++;
+    watch.filename = g_strdup(filename);
+    watch.cb = cb;
+    watch.opaque = opaque;
+
+    g_array_append_val(dir->watches, watch);
+
+    trace_qemu_file_monitor_add_watch(mon, dirpath,
+                                      filename ? filename : "<none>",
+                                      cb, opaque, watch.id);
+
+    ret = watch.id;
+
+ cleanup:
+    qemu_mutex_unlock(&mon->lock);
+    return ret;
+}
+
+
+void qemu_file_monitor_remove_watch(QFileMonitor *mon,
+                                    const char *dirpath,
+                                    int id)
+{
+    QFileMonitorDir *dir;
+    gsize i;
+
+    qemu_mutex_lock(&mon->lock);
+
+    trace_qemu_file_monitor_remove_watch(mon, dirpath, id);
+
+    dir = g_hash_table_lookup(mon->dirs, dirpath);
+    if (!dir) {
+        goto cleanup;
+    }
+
+    for (i = 0; i < dir->watches->len; i++) {
+        QFileMonitorWatch *watch = &g_array_index(dir->watches,
+                                                  QFileMonitorWatch, i);
+        if (watch->id == id) {
+            g_free(watch->filename);
+            g_array_remove_index(dir->watches, i);
+            break;
+        }
+    }
+
+    if (dir->watches->len == 0) {
+        inotify_rm_watch(mon->fd, dir->id);
+        trace_qemu_file_monitor_disable_watch(mon, dir->path, dir->id);
+
+        g_hash_table_remove(mon->idmap, GINT_TO_POINTER(dir->id));
+        g_hash_table_remove(mon->dirs, dir->path);
+
+        if (g_hash_table_size(mon->dirs) == 0) {
+            qemu_set_fd_handler(mon->fd, NULL, NULL, NULL);
+        }
+    }
+
+ cleanup:
+    qemu_mutex_unlock(&mon->lock);
+}
diff --git a/util/filemonitor-stub.c b/util/filemonitor-stub.c
new file mode 100644
index 0000000000..48268b2bb6
--- /dev/null
+++ b/util/filemonitor-stub.c
@@ -0,0 +1,59 @@
+/*
+ * QEMU file monitor stub impl
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/filemonitor.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+
+
+QFileMonitor *
+qemu_file_monitor_new(Error **errp)
+{
+    error_setg(errp, "File monitoring not available on this platform");
+    return NULL;
+}
+
+
+void
+qemu_file_monitor_free(QFileMonitor *mon G_GNUC_UNUSED)
+{
+}
+
+
+int
+qemu_file_monitor_add_watch(QFileMonitor *mon G_GNUC_UNUSED,
+                            const char *dirpath G_GNUC_UNUSED,
+                            const char *filename G_GNUC_UNUSED,
+                            QFileMonitorHandler cb G_GNUC_UNUSED,
+                            void *opaque G_GNUC_UNUSED,
+                            Error **errp)
+{
+    error_setg(errp, "File monitoring not available on this platform");
+    return -1;
+}
+
+
+void
+qemu_file_monitor_remove_watch(QFileMonitor *mon G_GNUC_UNUSED,
+                               const char *dirpath G_GNUC_UNUSED,
+                               int id G_GNUC_UNUSED)
+{
+}
diff --git a/util/trace-events b/util/trace-events
index 79569b7fdf..ff19b253e2 100644
--- a/util/trace-events
+++ b/util/trace-events
@@ -21,6 +21,15 @@ buffer_move_empty(const char *buf, size_t len, const char *from) "%s: %zd bytes
 buffer_move(const char *buf, size_t len, const char *from) "%s: %zd bytes from %s"
 buffer_free(const char *buf, size_t len) "%s: capacity %zd"
 
+# util/filemonitor.c
+qemu_file_monitor_add_watch(void *mon, const char *dirpath, const char *filename, void *cb, void *opaque, int id) "File monitor %p add watch dir='%s' file='%s' cb=%p opaque=%p id=%u"
+qemu_file_monitor_remove_watch(void *mon, const char *dirpath, int id) "File monitor %p remove watch dir='%s' id=%u"
+qemu_file_monitor_new(void *mon, int fd) "File monitor %p created fd=%d"
+qemu_file_monitor_enable_watch(void *mon, const char *dirpath, int id) "File monitor %p enable watch dir='%s' id=%u"
+qemu_file_monitor_disable_watch(void *mon, const char *dirpath, int id) "Fle monitor %p disable watch dir='%s' id=%u"
+qemu_file_monitor_event(void *mon, const char *dirpath, const char *filename, int mask, unsigned int id) "File monitor %p event dir='%s' file='%s' mask=0x%x id=%u"
+qemu_file_monitor_dispatch(void *mon, const char *dirpath, const char *filename, int ev, void *cb, void *opaque, unsigned int id) "File monitor %p dispatch dir='%s' file='%s' ev=%d cb=%p opaque=%p id=%u"
+
 # util/qemu-coroutine.c
 qemu_aio_coroutine_enter(void *ctx, void *from, void *to, void *opaque) "ctx %p from %p to %p opaque %p"
 qemu_coroutine_yield(void *from, void *to) "from %p to %p"