summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules14
-rw-r--r--Changelog2
-rw-r--r--MAINTAINERS48
-rw-r--r--Makefile1
-rw-r--r--README2
-rw-r--r--block.c61
-rw-r--r--block/backup.c6
-rw-r--r--block/blkdebug.c8
-rw-r--r--block/blkverify.c13
-rw-r--r--block/commit.c6
-rw-r--r--block/iscsi.c6
-rw-r--r--block/mirror.c6
-rw-r--r--block/qapi.c124
-rw-r--r--block/qcow2-cache.c8
-rw-r--r--block/qcow2-cluster.c27
-rw-r--r--block/qcow2-refcount.c45
-rw-r--r--block/qcow2-snapshot.c30
-rw-r--r--block/qcow2.c129
-rw-r--r--block/qcow2.h30
-rw-r--r--block/raw-posix.c72
-rw-r--r--block/raw-win32.c16
-rw-r--r--block/raw_bsd.c7
-rw-r--r--block/stream.c6
-rw-r--r--block/vhdx.c10
-rw-r--r--block/vmdk.c127
-rw-r--r--blockdev.c678
-rw-r--r--blockjob.c22
-rwxr-xr-xconfigure11
-rw-r--r--cpu-exec.c4
-rw-r--r--cputlb.c15
-rw-r--r--docs/qapi-code-gen.txt17
-rw-r--r--docs/qmp/README2
-rw-r--r--docs/rdma.txt2
-rw-r--r--docs/specs/qcow2.txt3
-rw-r--r--exec.c54
-rw-r--r--gdbstub.c6
-rw-r--r--hw/9pfs/virtio-9p-xattr.c6
-rw-r--r--hw/alpha/typhoon.c2
-rw-r--r--hw/block/m25p80.c5
-rw-r--r--hw/block/xen_disk.c6
-rw-r--r--hw/char/sh_serial.c2
-rw-r--r--hw/ide/ahci.c10
-rw-r--r--hw/misc/vfio.c627
-rw-r--r--hw/scsi/scsi-bus.c45
-rw-r--r--hw/sd/milkymist-memcard.c4
-rw-r--r--hw/sd/omap_mmc.c6
-rw-r--r--hw/sd/pl181.c4
-rw-r--r--hw/sd/pxa2xx_mmci.c3
-rw-r--r--hw/sd/sd.c5
-rw-r--r--hw/sd/sdhci.c3
-rw-r--r--hw/sd/ssi-sd.c3
-rw-r--r--hw/usb/hcd-ohci.c2
-rw-r--r--hw/xen/xen_backend.c19
-rw-r--r--include/block/block.h19
-rw-r--r--include/block/block_int.h7
-rw-r--r--include/block/blockjob.h14
-rw-r--r--include/block/qapi.h2
-rw-r--r--include/exec/cpu-defs.h2
-rw-r--r--include/exec/def-helper.h3
-rw-r--r--include/exec/exec-all.h46
-rw-r--r--include/exec/softmmu_template.h286
-rw-r--r--include/hw/i386/pc.h8
-rw-r--r--include/hw/qdev-core.h16
-rw-r--r--include/hw/scsi/scsi.h2
-rw-r--r--include/qapi/qmp/dispatch.h7
-rw-r--r--include/qemu/bitops.h80
-rw-r--r--include/qemu/option.h1
-rw-r--r--include/qemu/sockets.h1
-rw-r--r--include/sysemu/blockdev.h1
-rw-r--r--include/sysemu/char.h1
-rw-r--r--linux-user/main.c33
-rw-r--r--migration.c1
-rw-r--r--net/socket.c19
-rw-r--r--pc-bios/README4
-rw-r--r--pc-bios/openbios-ppcbin733976 -> 729880 bytes
-rw-r--r--pc-bios/openbios-sparc32bin381484 -> 381488 bytes
-rw-r--r--pc-bios/openbios-sparc64bin1598328 -> 1598328 bytes
-rw-r--r--qapi-schema.json288
-rw-r--r--qapi/qmp-registry.c33
-rw-r--r--qdev-monitor.c85
-rw-r--r--qemu-char.c93
-rw-r--r--qemu-io-cmds.c9
-rw-r--r--qemu-io.c39
-rw-r--r--qemu-seccomp.c1
-rw-r--r--qemu.nsi2
-rw-r--r--qga/commands-posix.c4
-rw-r--r--qga/commands.c39
-rw-r--r--qga/main.c75
-rw-r--r--qga/qapi-schema.json5
-rw-r--r--qmp-commands.hx55
m---------roms/openbios0
-rwxr-xr-xscripts/get_maintainer.pl2
-rw-r--r--scripts/qapi-types.py15
-rw-r--r--scripts/qapi-visit.py26
-rwxr-xr-xscripts/qmp/qemu-ga-client2
-rw-r--r--slirp/misc.c3
-rw-r--r--slirp/socket.c4
-rw-r--r--slirp/tcp_subr.c6
-rw-r--r--slirp/udp.c4
-rw-r--r--target-alpha/cpu.c4
-rw-r--r--target-alpha/helper.h2
-rw-r--r--target-alpha/translate.c4
-rw-r--r--target-arm/helper.c3
-rw-r--r--target-arm/helper.h8
-rw-r--r--target-arm/iwmmxt_helper.c2
-rw-r--r--target-arm/translate.c3
-rw-r--r--target-cris/helper.h8
-rw-r--r--target-cris/translate.c3
-rw-r--r--target-i386/arch_memory_mapping.c2
-rw-r--r--target-i386/cpu.c7
-rw-r--r--target-i386/translate.c4
-rw-r--r--target-m68k/helper.c3
-rw-r--r--target-m68k/helper.h (renamed from target-m68k/helpers.h)0
-rw-r--r--target-m68k/op_helper.c2
-rw-r--r--target-m68k/translate.c7
-rw-r--r--target-microblaze/translate.c2
-rw-r--r--target-mips/helper.h12
-rw-r--r--target-mips/translate.c5
-rw-r--r--target-moxie/cpu.c1
-rw-r--r--target-openrisc/cpu.c1
-rw-r--r--target-openrisc/translate.c2
-rw-r--r--target-ppc/helper.h10
-rw-r--r--target-ppc/translate.c4
-rw-r--r--target-ppc/translate_init.c3
-rw-r--r--target-s390x/helper.c3
-rw-r--r--target-s390x/kvm.c4
-rw-r--r--target-s390x/translate.c4
-rw-r--r--target-sh4/cpu.c3
-rw-r--r--target-sh4/translate.c4
-rw-r--r--target-sparc/cpu.c1
-rw-r--r--target-sparc/helper.h18
-rw-r--r--target-sparc/translate.c5
-rw-r--r--target-unicore32/helper.c1
-rw-r--r--target-unicore32/translate.c3
-rw-r--r--target-xtensa/translate.c2
-rw-r--r--tcg/README43
-rw-r--r--tcg/aarch64/tcg-target.c51
-rw-r--r--tcg/aarch64/tcg-target.h2
-rw-r--r--tcg/arm/tcg-target.c412
-rw-r--r--tcg/arm/tcg-target.h2
-rw-r--r--tcg/hppa/tcg-target.c1831
-rw-r--r--tcg/hppa/tcg-target.h123
-rw-r--r--tcg/i386/tcg-target.c30
-rw-r--r--tcg/i386/tcg-target.h2
-rw-r--r--tcg/ia64/tcg-target.c2
-rw-r--r--tcg/ia64/tcg-target.h2
-rw-r--r--tcg/mips/tcg-target.c2
-rw-r--r--tcg/mips/tcg-target.h2
-rw-r--r--tcg/optimize.c12
-rw-r--r--tcg/ppc/tcg-target.c28
-rw-r--r--tcg/ppc/tcg-target.h2
-rw-r--r--tcg/ppc64/tcg-target.c26
-rw-r--r--tcg/ppc64/tcg-target.h2
-rw-r--r--tcg/s390/tcg-target.c2
-rw-r--r--tcg/s390/tcg-target.h2
-rw-r--r--tcg/sparc/tcg-target.c2
-rw-r--r--tcg/sparc/tcg-target.h2
-rw-r--r--tcg/tcg-be-ldst.h90
-rw-r--r--tcg/tcg-be-null.h43
-rw-r--r--tcg/tcg-op.h239
-rw-r--r--tcg/tcg-opc.h96
-rw-r--r--tcg/tcg.c338
-rw-r--r--tcg/tcg.h166
-rw-r--r--tcg/tci/tcg-target.c3
-rw-r--r--tcg/tci/tcg-target.h2
-rw-r--r--tci.c14
-rw-r--r--tests/.gitignore3
-rw-r--r--tests/Makefile24
-rw-r--r--tests/qemu-iotests/.gitignore1
-rw-r--r--tests/qemu-iotests/026.out32
-rw-r--r--tests/qemu-iotests/026.out.nocache32
-rw-r--r--tests/qemu-iotests/051.out7
-rwxr-xr-xtests/qemu-iotests/05912
-rw-r--r--tests/qemu-iotests/059.out18
-rwxr-xr-xtests/qemu-iotests/06047
-rw-r--r--tests/qemu-iotests/060.out40
-rwxr-xr-xtests/qemu-iotests/06462
-rw-r--r--tests/qemu-iotests/064.out14
-rwxr-xr-xtests/qemu-iotests/065125
-rw-r--r--tests/qemu-iotests/065.out5
-rwxr-xr-xtests/qemu-iotests/06663
-rw-r--r--tests/qemu-iotests/066.out13
-rwxr-xr-xtests/qemu-iotests/067133
-rw-r--r--tests/qemu-iotests/067.out80
-rw-r--r--tests/qemu-iotests/common8
-rw-r--r--tests/qemu-iotests/common.filter8
-rw-r--r--tests/qemu-iotests/common.rc22
-rw-r--r--tests/qemu-iotests/group4
-rw-r--r--tests/qemu-iotests/iotests.py4
-rw-r--r--tests/qemu-iotests/sample_images/iotest-dynamic-1G.vhdx.bz2bin0 -> 874 bytes
-rw-r--r--translate-all.c12
-rw-r--r--util/oslib-posix.c12
-rw-r--r--util/oslib-win32.c10
-rw-r--r--util/path.c4
-rw-r--r--util/qemu-option.c6
-rw-r--r--util/qemu-sockets.c6
-rw-r--r--version.rc2
-rw-r--r--vl.c6
-rw-r--r--xen-all.c2
199 files changed, 4333 insertions, 3920 deletions
diff --git a/.gitmodules b/.gitmodules
index d7e3f3c7cd..45e51e79be 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,27 +1,27 @@
[submodule "roms/vgabios"]
path = roms/vgabios
- url = git://git.qemu.org/vgabios.git/
+ url = git://git.qemu-project.org/vgabios.git/
[submodule "roms/seabios"]
path = roms/seabios
- url = git://git.qemu.org/seabios.git/
+ url = git://git.qemu-project.org/seabios.git/
[submodule "roms/SLOF"]
path = roms/SLOF
- url = git://git.qemu.org/SLOF.git
+ url = git://git.qemu-project.org/SLOF.git
[submodule "roms/ipxe"]
path = roms/ipxe
- url = git://git.qemu.org/ipxe.git
+ url = git://git.qemu-project.org/ipxe.git
[submodule "roms/openbios"]
path = roms/openbios
- url = git://git.qemu.org/openbios.git
+ url = git://git.qemu-project.org/openbios.git
[submodule "roms/qemu-palcode"]
path = roms/qemu-palcode
url = git://github.com/rth7680/qemu-palcode.git
[submodule "roms/sgabios"]
path = roms/sgabios
- url = git://git.qemu.org/sgabios.git
+ url = git://git.qemu-project.org/sgabios.git
[submodule "pixman"]
path = pixman
url = git://anongit.freedesktop.org/pixman
[submodule "dtc"]
path = dtc
- url = git://git.qemu.org/dtc.git
+ url = git://git.qemu-project.org/dtc.git
diff --git a/Changelog b/Changelog
index 13eebefb74..1249b8aac5 100644
--- a/Changelog
+++ b/Changelog
@@ -1,6 +1,6 @@
This file documents changes for QEMU releases 0.12 and earlier.
For changelog information for later releases, see
-http://wiki.qemu.org/ChangeLog or look at the git history for
+http://wiki.qemu-project.org/ChangeLog or look at the git history for
more detailed information.
diff --git a/MAINTAINERS b/MAINTAINERS
index 5c3c70c89b..77edacf271 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -50,8 +50,7 @@ Descriptions of section entries:
General Project Administration
------------------------------
-M: Anthony Liguori <anthony@codemonkey.ws>
-M: Paul Brook <paul@codesourcery.com>
+M: Anthony Liguori <aliguori@amazon.com>
Guest CPU cores (TCG):
----------------------
@@ -62,7 +61,6 @@ F: target-alpha/
F: hw/alpha/
ARM
-M: Paul Brook <paul@codesourcery.com>
M: Peter Maydell <peter.maydell@linaro.org>
S: Maintained
F: target-arm/
@@ -83,8 +81,7 @@ F: hw/lm32/
F: hw/char/lm32_*
M68K
-M: Paul Brook <paul@codesourcery.com>
-S: Odd Fixes
+S: Orphan
F: target-m68k/
F: hw/m68k/
@@ -248,7 +245,6 @@ F: hw/*/imx*
F: hw/arm/kzm.c
Integrator CP
-M: Paul Brook <paul@codesourcery.com>
M: Peter Maydell <peter.maydell@linaro.org>
S: Maintained
F: hw/arm/integratorcp.c
@@ -274,7 +270,6 @@ S: Maintained
F: hw/arm/palm.c
Real View
-M: Paul Brook <paul@codesourcery.com>
M: Peter Maydell <peter.maydell@linaro.org>
S: Maintained
F: hw/arm/realview*
@@ -285,13 +280,11 @@ S: Maintained
F: hw/arm/spitz.c
Stellaris
-M: Paul Brook <paul@codesourcery.com>
M: Peter Maydell <peter.maydell@linaro.org>
S: Maintained
F: hw/*/stellaris*
Versatile PB
-M: Paul Brook <paul@codesourcery.com>
M: Peter Maydell <peter.maydell@linaro.org>
S: Maintained
F: hw/*/versatile*
@@ -327,18 +320,15 @@ F: hw/lm32/milkymist.c
M68K Machines
-------------
an5206
-M: Paul Brook <paul@codesourcery.com>
-S: Maintained
+S: Orphan
F: hw/m68k/an5206.c
dummy_m68k
-M: Paul Brook <paul@codesourcery.com>
-S: Maintained
+S: Orphan
F: hw/m68k/dummy_m68k.c
mcf5208
-M: Paul Brook <paul@codesourcery.com>
-S: Maintained
+S: Orphan
F: hw/m68k/mcf5208.c
MicroBlaze Machines
@@ -509,7 +499,7 @@ F: hw/unicore32/
X86 Machines
------------
PC
-M: Anthony Liguori <anthony@codemonkey.ws>
+M: Anthony Liguori <aliguori@amazon.com>
S: Supported
F: hw/i386/pc.[ch]
F: hw/i386/pc_piix.c
@@ -567,8 +557,7 @@ F: hw/scsi/*
T: git git://github.com/bonzini/qemu.git scsi-next
LSI53C895A
-M: Paul Brook <paul@codesourcery.com>
-S: Odd Fixes
+S: Orphan
F: hw/scsi/lsi53c895a.c
SSI
@@ -593,7 +582,7 @@ S: Supported
F: hw/*/*vhost*
virtio
-M: Anthony Liguori <anthony@codemonkey.ws>
+M: Anthony Liguori <aliguori@amazon.com>
S: Supported
F: hw/*/virtio*
@@ -652,7 +641,7 @@ F: block/
F: hw/block/
Character Devices
-M: Anthony Liguori <anthony@codemonkey.ws>
+M: Anthony Liguori <aliguori@amazon.com>
S: Maintained
F: qemu-char.c
@@ -690,7 +679,7 @@ F: audio/spiceaudio.c
F: hw/display/qxl*
Graphics
-M: Anthony Liguori <anthony@codemonkey.ws>
+M: Anthony Liguori <aliguori@amazon.com>
S: Maintained
F: ui/
@@ -700,7 +689,7 @@ S: Odd Fixes
F: ui/cocoa.m
Main loop
-M: Anthony Liguori <anthony@codemonkey.ws>
+M: Anthony Liguori <aliguori@amazon.com>
S: Supported
F: vl.c
@@ -712,7 +701,7 @@ F: hmp.c
F: hmp-commands.hx
Network device layer
-M: Anthony Liguori <anthony@codemonkey.ws>
+M: Anthony Liguori <aliguori@amazon.com>
M: Stefan Hajnoczi <stefanha@redhat.com>
S: Maintained
F: net/
@@ -804,11 +793,6 @@ M: Andrzej Zaborowski <balrogg@gmail.com>
S: Maintained
F: tcg/arm/
-HPPA target
-M: Richard Henderson <rth@twiddle.net>
-S: Maintained
-F: tcg/hppa/
-
i386 target
M: qemu-devel@nongnu.org
S: Maintained
@@ -855,21 +839,21 @@ Stable branches
---------------
Stable 1.0
L: qemu-stable@nongnu.org
-T: git git://git.qemu.org/qemu-stable-1.0.git
+T: git git://git.qemu-project.org/qemu-stable-1.0.git
S: Orphan
Stable 0.15
L: qemu-stable@nongnu.org
M: Andreas Färber <afaerber@suse.de>
-T: git git://git.qemu.org/qemu-stable-0.15.git
+T: git git://git.qemu-project.org/qemu-stable-0.15.git
S: Supported
Stable 0.14
L: qemu-stable@nongnu.org
-T: git git://git.qemu.org/qemu-stable-0.14.git
+T: git git://git.qemu-project.org/qemu-stable-0.14.git
S: Orphan
Stable 0.10
L: qemu-stable@nongnu.org
-T: git git://git.qemu.org/qemu-stable-0.10.git
+T: git git://git.qemu-project.org/qemu-stable-0.10.git
S: Orphan
diff --git a/Makefile b/Makefile
index 60fb87e2dd..b15003f9d2 100644
--- a/Makefile
+++ b/Makefile
@@ -246,7 +246,6 @@ clean:
rm -f $(foreach f,$(GENERATED_SOURCES),$(f) $(f)-timestamp)
rm -rf qapi-generated
rm -rf qga/qapi-generated
- $(MAKE) -C tests/tcg clean
for d in $(ALL_SUBDIRS); do \
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
rm -f $$d/qemu-options.def; \
diff --git a/README b/README
index c77d12642d..c7c990d895 100644
--- a/README
+++ b/README
@@ -1,3 +1,3 @@
-Read the documentation in qemu-doc.html or on http://wiki.qemu.org
+Read the documentation in qemu-doc.html or on http://wiki.qemu-project.org
- QEMU team
diff --git a/block.c b/block.c
index 93e113ad7c..fd05a8008a 100644
--- a/block.c
+++ b/block.c
@@ -769,13 +769,22 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
bs->read_only = !(open_flags & BDRV_O_RDWR);
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
- error_setg(errp, "Driver '%s' is not whitelisted", drv->format_name);
+ error_setg(errp,
+ !bs->read_only && bdrv_is_whitelisted(drv, true)
+ ? "Driver '%s' can only be used for read-only devices"
+ : "Driver '%s' is not whitelisted",
+ drv->format_name);
return -ENOTSUP;
}
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
- if (!bs->read_only && (flags & BDRV_O_COPY_ON_READ)) {
- bdrv_enable_copy_on_read(bs);
+ if (flags & BDRV_O_COPY_ON_READ) {
+ if (!bs->read_only) {
+ bdrv_enable_copy_on_read(bs);
+ } else {
+ error_setg(errp, "Can't use copy-on-read on read-only device");
+ return -EINVAL;
+ }
}
if (filename != NULL) {
@@ -808,8 +817,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
if (ret < 0) {
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
- } else if (filename) {
- error_setg_errno(errp, -ret, "Could not open '%s'", filename);
+ } else if (bs->filename[0]) {
+ error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename);
} else {
error_setg_errno(errp, -ret, "Could not open image");
}
@@ -824,8 +833,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
#ifndef _WIN32
if (bs->is_temporary) {
- assert(filename != NULL);
- unlink(filename);
+ assert(bs->filename[0] != '\0');
+ unlink(bs->filename);
}
#endif
return 0;
@@ -881,7 +890,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
/* Find the right block driver */
drvname = qdict_get_try_str(options, "driver");
if (drvname) {
- drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR));
+ drv = bdrv_find_format(drvname);
if (!drv) {
error_setg(errp, "Unknown driver '%s'", drvname);
}
@@ -1123,7 +1132,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
/* Find the right image format driver */
drvname = qdict_get_try_str(options, "driver");
if (drvname) {
- drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR));
+ drv = bdrv_find_format(drvname);
qdict_del(options, "driver");
}
@@ -3147,6 +3156,12 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
return ret;
}
+ if (ret & BDRV_BLOCK_RAW) {
+ assert(ret & BDRV_BLOCK_OFFSET_VALID);
+ return bdrv_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS,
+ *pnum, pnum);
+ }
+
if (!(ret & BDRV_BLOCK_DATA)) {
if (bdrv_has_zero_init(bs)) {
ret |= BDRV_BLOCK_ZERO;
@@ -3322,6 +3337,15 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return drv->bdrv_get_info(bs, bdi);
}
+ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (drv && drv->bdrv_get_specific_info) {
+ return drv->bdrv_get_specific_info(bs);
+ }
+ return NULL;
+}
+
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
int64_t pos, int size)
{
@@ -4632,3 +4656,22 @@ int bdrv_amend_options(BlockDriverState *bs, QEMUOptionParameter *options)
}
return bs->drv->bdrv_amend_options(bs, options);
}
+
+ExtSnapshotPerm bdrv_check_ext_snapshot(BlockDriverState *bs)
+{
+ if (bs->drv->bdrv_check_ext_snapshot) {
+ return bs->drv->bdrv_check_ext_snapshot(bs);
+ }
+
+ if (bs->file && bs->file->drv && bs->file->drv->bdrv_check_ext_snapshot) {
+ return bs->file->drv->bdrv_check_ext_snapshot(bs);
+ }
+
+ /* external snapshots are allowed by default */
+ return EXT_SNAPSHOT_ALLOWED;
+}
+
+ExtSnapshotPerm bdrv_check_ext_snapshot_forbidden(BlockDriverState *bs)
+{
+ return EXT_SNAPSHOT_FORBIDDEN;
+}
diff --git a/block/backup.c b/block/backup.c
index 04c4b5c263..cad14c90b2 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -202,9 +202,9 @@ static void backup_iostatus_reset(BlockJob *job)
bdrv_iostatus_reset(s->target);
}
-static const BlockJobType backup_job_type = {
+static const BlockJobDriver backup_job_driver = {
.instance_size = sizeof(BackupBlockJob),
- .job_type = "backup",
+ .job_type = BLOCK_JOB_TYPE_BACKUP,
.set_speed = backup_set_speed,
.iostatus_reset = backup_iostatus_reset,
};
@@ -370,7 +370,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
return;
}
- BackupBlockJob *job = block_job_create(&backup_job_type, bs, speed,
+ BackupBlockJob *job = block_job_create(&backup_job_driver, bs, speed,
cb, opaque, errp);
if (!job) {
return;
diff --git a/block/blkdebug.c b/block/blkdebug.c
index be948b2fdd..16d2b91ac9 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -362,8 +362,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
opts = qemu_opts_create_nofail(&runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
@@ -373,6 +372,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
if (config) {
ret = read_config(s, config);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not read blkdebug config file");
goto fail;
}
}
@@ -383,14 +383,14 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
/* Open the backing file */
filename = qemu_opt_get(opts, "x-image");
if (filename == NULL) {
+ error_setg(errp, "Could not retrieve image file name");
ret = -EINVAL;
goto fail;
}
ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err);
if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
goto fail;
}
diff --git a/block/blkverify.c b/block/blkverify.c
index bff95d2a45..3c6352898f 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -128,8 +128,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
opts = qemu_opts_create_nofail(&runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
@@ -137,20 +136,21 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
/* Parse the raw image filename */
raw = qemu_opt_get(opts, "x-raw");
if (raw == NULL) {
+ error_setg(errp, "Could not retrieve raw image filename");
ret = -EINVAL;
goto fail;
}
ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err);
if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
goto fail;
}
/* Open the test file */
filename = qemu_opt_get(opts, "x-image");
if (filename == NULL) {
+ error_setg(errp, "Could not retrieve test image filename");
ret = -EINVAL;
goto fail;
}
@@ -158,8 +158,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
s->test_file = bdrv_new("");
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err);
if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
bdrv_unref(s->test_file);
s->test_file = NULL;
goto fail;
@@ -417,6 +416,8 @@ static BlockDriver bdrv_blkverify = {
.bdrv_aio_readv = blkverify_aio_readv,
.bdrv_aio_writev = blkverify_aio_writev,
.bdrv_aio_flush = blkverify_aio_flush,
+
+ .bdrv_check_ext_snapshot = bdrv_check_ext_snapshot_forbidden,
};
static void bdrv_blkverify_init(void)
diff --git a/block/commit.c b/block/commit.c
index ac4b7ccbc9..d4090cbf7d 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -173,9 +173,9 @@ static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
}
-static const BlockJobType commit_job_type = {
+static const BlockJobDriver commit_job_driver = {
.instance_size = sizeof(CommitBlockJob),
- .job_type = "commit",
+ .job_type = BLOCK_JOB_TYPE_COMMIT,
.set_speed = commit_set_speed,
};
@@ -238,7 +238,7 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
}
- s = block_job_create(&commit_job_type, bs, speed, cb, opaque, errp);
+ s = block_job_create(&commit_job_driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
diff --git a/block/iscsi.c b/block/iscsi.c
index 6152ef1891..a2a961e163 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -811,7 +811,7 @@ iscsi_getlength(BlockDriverState *bs)
return len;
}
-#if defined(SCSI_PROVISIONING_TYPE_DEALLOCATED)
+#if defined(LIBISCSI_FEATURE_IOVECTOR)
static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
int64_t sector_num,
@@ -903,7 +903,7 @@ out:
return ret;
}
-#endif /* SCSI_PROVISIONING_TYPE_DEALLOCATED */
+#endif /* LIBISCSI_FEATURE_IOVECTOR */
static int
coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
@@ -1529,7 +1529,7 @@ static BlockDriver bdrv_iscsi = {
.bdrv_getlength = iscsi_getlength,
.bdrv_truncate = iscsi_truncate,
-#if defined(SCSI_PROVISIONING_TYPE_DEALLOCATED)
+#if defined(LIBISCSI_FEATURE_IOVECTOR)
.bdrv_co_get_block_status = iscsi_co_get_block_status,
#endif
.bdrv_co_discard = iscsi_co_discard,
diff --git a/block/mirror.c b/block/mirror.c
index 6e7a274e43..7b95acf88c 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -525,9 +525,9 @@ static void mirror_complete(BlockJob *job, Error **errp)
block_job_resume(job);
}
-static const BlockJobType mirror_job_type = {
+static const BlockJobDriver mirror_job_driver = {
.instance_size = sizeof(MirrorBlockJob),
- .job_type = "mirror",
+ .job_type = BLOCK_JOB_TYPE_MIRROR,
.set_speed = mirror_set_speed,
.iostatus_reset= mirror_iostatus_reset,
.complete = mirror_complete,
@@ -563,7 +563,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
return;
}
- s = block_job_create(&mirror_job_type, bs, speed, cb, opaque, errp);
+ s = block_job_create(&mirror_job_driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
diff --git a/block/qapi.c b/block/qapi.c
index 782051c65d..5880b3e42b 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -25,6 +25,9 @@
#include "block/qapi.h"
#include "block/block_int.h"
#include "qmp-commands.h"
+#include "qapi-visit.h"
+#include "qapi/qmp-output-visitor.h"
+#include "qapi/qmp/types.h"
/*
* Returns 0 on success, with *p_list either set to describe snapshot
@@ -134,6 +137,9 @@ void bdrv_query_image_info(BlockDriverState *bs,
info->dirty_flag = bdi.is_dirty;
info->has_dirty_flag = true;
}
+ info->format_specific = bdrv_get_specific_info(bs);
+ info->has_format_specific = info->format_specific != NULL;
+
backing_filename = bs->backing_file;
if (backing_filename[0] != '\0') {
info->backing_filename = g_strdup(backing_filename);
@@ -423,6 +429,119 @@ void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
}
}
+static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
+ QDict *dict);
+static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
+ QList *list);
+
+static void dump_qobject(fprintf_function func_fprintf, void *f,
+ int comp_indent, QObject *obj)
+{
+ switch (qobject_type(obj)) {
+ case QTYPE_QINT: {
+ QInt *value = qobject_to_qint(obj);
+ func_fprintf(f, "%" PRId64, qint_get_int(value));
+ break;
+ }
+ case QTYPE_QSTRING: {
+ QString *value = qobject_to_qstring(obj);
+ func_fprintf(f, "%s", qstring_get_str(value));
+ break;
+ }
+ case QTYPE_QDICT: {
+ QDict *value = qobject_to_qdict(obj);
+ dump_qdict(func_fprintf, f, comp_indent, value);
+ break;
+ }
+ case QTYPE_QLIST: {
+ QList *value = qobject_to_qlist(obj);
+ dump_qlist(func_fprintf, f, comp_indent, value);
+ break;
+ }
+ case QTYPE_QFLOAT: {
+ QFloat *value = qobject_to_qfloat(obj);
+ func_fprintf(f, "%g", qfloat_get_double(value));
+ break;
+ }
+ case QTYPE_QBOOL: {
+ QBool *value = qobject_to_qbool(obj);
+ func_fprintf(f, "%s", qbool_get_int(value) ? "true" : "false");
+ break;
+ }
+ case QTYPE_QERROR: {
+ QString *value = qerror_human((QError *)obj);
+ func_fprintf(f, "%s", qstring_get_str(value));
+ break;
+ }
+ case QTYPE_NONE:
+ break;
+ case QTYPE_MAX:
+ default:
+ abort();
+ }
+}
+
+static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
+ QList *list)
+{
+ const QListEntry *entry;
+ int i = 0;
+
+ for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) {
+ qtype_code type = qobject_type(entry->value);
+ bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
+ const char *format = composite ? "%*s[%i]:\n" : "%*s[%i]: ";
+
+ func_fprintf(f, format, indentation * 4, "", i);
+ dump_qobject(func_fprintf, f, indentation + 1, entry->value);
+ if (!composite) {
+ func_fprintf(f, "\n");
+ }
+ }
+}
+
+static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
+ QDict *dict)
+{
+ const QDictEntry *entry;
+
+ for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) {
+ qtype_code type = qobject_type(entry->value);
+ bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
+ const char *format = composite ? "%*s%s:\n" : "%*s%s: ";
+ char key[strlen(entry->key) + 1];
+ int i;
+
+ /* replace dashes with spaces in key (variable) names */
+ for (i = 0; entry->key[i]; i++) {
+ key[i] = entry->key[i] == '-' ? ' ' : entry->key[i];
+ }
+ key[i] = 0;
+
+ func_fprintf(f, format, indentation * 4, "", key);
+ dump_qobject(func_fprintf, f, indentation + 1, entry->value);
+ if (!composite) {
+ func_fprintf(f, "\n");
+ }
+ }
+}
+
+void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
+ ImageInfoSpecific *info_spec)
+{
+ Error *local_err = NULL;
+ QmpOutputVisitor *ov = qmp_output_visitor_new();
+ QObject *obj, *data;
+
+ visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), &info_spec, NULL,
+ &local_err);
+ obj = qmp_output_get_qobject(ov);
+ assert(qobject_type(obj) == QTYPE_QDICT);
+ data = qdict_get(qobject_to_qdict(obj), "data");
+ dump_qobject(func_fprintf, f, 1, data);
+ qmp_output_visitor_cleanup(ov);
+}
+
void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
ImageInfo *info)
{
@@ -493,4 +612,9 @@ void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
func_fprintf(f, "\n");
}
}
+
+ if (info->has_format_specific) {
+ func_fprintf(f, "Format specific information:\n");
+ bdrv_image_info_specific_dump(func_fprintf, f, info->format_specific);
+ }
}
diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c
index 40a5a3fc39..8ecbb5bc00 100644
--- a/block/qcow2-cache.c
+++ b/block/qcow2-cache.c
@@ -115,15 +115,13 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
}
if (c == s->refcount_block_cache) {
- ret = qcow2_pre_write_overlap_check(bs,
- QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_BLOCK,
+ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK,
c->entries[i].offset, s->cluster_size);
} else if (c == s->l2_table_cache) {
- ret = qcow2_pre_write_overlap_check(bs,
- QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2,
+ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
c->entries[i].offset, s->cluster_size);
} else {
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
+ ret = qcow2_pre_write_overlap_check(bs, 0,
c->entries[i].offset, s->cluster_size);
}
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 39323ace38..0348b971b1 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -35,6 +35,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
BDRVQcowState *s = bs->opaque;
int new_l1_size2, ret, i;
uint64_t *new_l1_table;
+ int64_t old_l1_table_offset, old_l1_size;
int64_t new_l1_table_offset, new_l1_size;
uint8_t data[12];
@@ -82,8 +83,8 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
/* the L1 position has not yet been updated, so these clusters must
* indeed be completely free */
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
- new_l1_table_offset, new_l1_size2);
+ ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset,
+ new_l1_size2);
if (ret < 0) {
goto fail;
}
@@ -106,11 +107,13 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
goto fail;
}
g_free(s->l1_table);
- qcow2_free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t),
- QCOW2_DISCARD_OTHER);
+ old_l1_table_offset = s->l1_table_offset;
s->l1_table_offset = new_l1_table_offset;
s->l1_table = new_l1_table;
+ old_l1_size = s->l1_size;
s->l1_size = new_l1_size;
+ qcow2_free_clusters(bs, old_l1_table_offset, old_l1_size * sizeof(uint64_t),
+ QCOW2_DISCARD_OTHER);
return 0;
fail:
g_free(new_l1_table);
@@ -157,8 +160,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
}
- ret = qcow2_pre_write_overlap_check(bs,
- QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
+ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
s->l1_table_offset + 8 * l1_start_index, sizeof(buf));
if (ret < 0) {
return ret;
@@ -270,6 +272,10 @@ fail:
qcow2_cache_put(bs, s->l2_table_cache, (void**) table);
}
s->l1_table[l1_index] = old_l2_offset;
+ if (l2_offset > 0) {
+ qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t),
+ QCOW2_DISCARD_ALWAYS);
+ }
return ret;
}
@@ -389,7 +395,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
&s->aes_encrypt_key);
}
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
+ ret = qcow2_pre_write_overlap_check(bs, 0,
cluster_offset + n_start * BDRV_SECTOR_SIZE, n * BDRV_SECTOR_SIZE);
if (ret < 0) {
goto out;
@@ -1597,8 +1603,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
}
}
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
- offset, s->cluster_size);
+ ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
if (ret < 0) {
if (!preallocated) {
qcow2_free_clusters(bs, offset, s->cluster_size,
@@ -1654,8 +1659,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
}
} else {
if (l2_dirty) {
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT &
- ~(QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2), l2_offset,
+ ret = qcow2_pre_write_overlap_check(bs,
+ QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, l2_offset,
s->cluster_size);
if (ret < 0) {
goto fail;
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index d2b7064a02..1ff43d0906 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -796,11 +796,13 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
}
break;
case QCOW2_CLUSTER_NORMAL:
- qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
- nb_clusters << s->cluster_bits, type);
+ case QCOW2_CLUSTER_ZERO:
+ if (l2_entry & L2E_OFFSET_MASK) {
+ qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
+ nb_clusters << s->cluster_bits, type);
+ }
break;
case QCOW2_CLUSTER_UNALLOCATED:
- case QCOW2_CLUSTER_ZERO:
break;
default:
abort();
@@ -1034,7 +1036,6 @@ static void inc_refcounts(BlockDriverState *bs,
/* Flags for check_refcounts_l1() and check_refcounts_l2() */
enum {
- CHECK_OFLAG_COPIED = 0x1, /* check QCOW_OFLAG_COPIED matches refcount */
CHECK_FRAG_INFO = 0x2, /* update BlockFragInfo counters */
};
@@ -1310,9 +1311,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
}
if (l2_dirty) {
- ret = qcow2_pre_write_overlap_check(bs,
- QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2, l2_offset,
- s->cluster_size);
+ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
+ l2_offset, s->cluster_size);
if (ret < 0) {
fprintf(stderr, "ERROR: Could not write L2 table; metadata "
"overlap check failed: %s\n", strerror(-ret));
@@ -1353,8 +1353,7 @@ static int write_reftable_entry(BlockDriverState *bs, int rt_index)
buf[i] = cpu_to_be64(s->refcount_table[rt_start_index + i]);
}
- ret = qcow2_pre_write_overlap_check(bs,
- QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_TABLE,
+ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_TABLE,
s->refcount_table_offset + rt_start_index * sizeof(uint64_t),
sizeof(buf));
if (ret < 0) {
@@ -1405,8 +1404,7 @@ static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index,
/* new block has not yet been entered into refcount table, therefore it is
* no refcount block yet (regarding this check) */
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, new_offset,
- s->cluster_size);
+ ret = qcow2_pre_write_overlap_check(bs, 0, new_offset, s->cluster_size);
if (ret < 0) {
fprintf(stderr, "Could not write refcount block; metadata overlap "
"check failed: %s\n", strerror(-ret));
@@ -1481,8 +1479,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
/* current L1 table */
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
- s->l1_table_offset, s->l1_size,
- CHECK_OFLAG_COPIED | CHECK_FRAG_INFO);
+ s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO);
if (ret < 0) {
goto fail;
}
@@ -1639,8 +1636,8 @@ fail:
* looking for overlaps with important metadata sections (L1/L2 tables etc.),
* i.e. a sanity check without relying on the refcount tables.
*
- * The chk parameter specifies exactly what checks to perform (being a bitmask
- * of QCow2MetadataOverlap values).
+ * The ign parameter specifies what checks not to perform (being a bitmask of
+ * QCow2MetadataOverlap values), i.e., what sections to ignore.
*
* Returns:
* - 0 if writing to this offset will not affect the mentioned metadata
@@ -1648,10 +1645,11 @@ fail:
* - a negative value (-errno) indicating an error while performing a check,
* e.g. when bdrv_read failed on QCOW2_OL_INACTIVE_L2
*/
-int qcow2_check_metadata_overlap(BlockDriverState *bs, int chk, int64_t offset,
+int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
int64_t size)
{
BDRVQcowState *s = bs->opaque;
+ int chk = s->overlap_check & ~ign;
int i, j;
if (!size) {
@@ -1721,20 +1719,19 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int chk, int64_t offset,
for (i = 0; i < s->nb_snapshots; i++) {
uint64_t l1_ofs = s->snapshots[i].l1_table_offset;
uint32_t l1_sz = s->snapshots[i].l1_size;
- uint64_t *l1 = g_malloc(l1_sz * sizeof(uint64_t));
+ uint64_t l1_sz2 = l1_sz * sizeof(uint64_t);
+ uint64_t *l1 = g_malloc(l1_sz2);
int ret;
- ret = bdrv_read(bs->file, l1_ofs / BDRV_SECTOR_SIZE, (uint8_t *)l1,
- l1_sz * sizeof(uint64_t) / BDRV_SECTOR_SIZE);
-
+ ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2);
if (ret < 0) {
g_free(l1);
return ret;
}
for (j = 0; j < l1_sz; j++) {
- if ((l1[j] & L1E_OFFSET_MASK) &&
- overlaps_with(l1[j] & L1E_OFFSET_MASK, s->cluster_size)) {
+ uint64_t l2_ofs = be64_to_cpu(l1[j]) & L1E_OFFSET_MASK;
+ if (l2_ofs && overlaps_with(l2_ofs, s->cluster_size)) {
g_free(l1);
return QCOW2_OL_INACTIVE_L2;
}
@@ -1768,10 +1765,10 @@ static const char *metadata_ol_names[] = {
* Returns 0 if there were neither overlaps nor errors while checking for
* overlaps; or a negative value (-errno) on error.
*/
-int qcow2_pre_write_overlap_check(BlockDriverState *bs, int chk, int64_t offset,
+int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
int64_t size)
{
- int ret = qcow2_check_metadata_overlap(bs, chk, offset, size);
+ int ret = qcow2_check_metadata_overlap(bs, ign, offset, size);
if (ret < 0) {
return ret;
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index 5e8a7794f4..3529c683c6 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -182,19 +182,19 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
offset = snapshots_offset;
if (offset < 0) {
- return offset;
+ ret = offset;
+ goto fail;
}
ret = bdrv_flush(bs);
if (ret < 0) {
- return ret;
+ goto fail;
}
/* The snapshot list position has not yet been updated, so these clusters
* must indeed be completely free */
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, offset,
- snapshots_size);
+ ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size);
if (ret < 0) {
- return ret;
+ goto fail;
}
@@ -220,6 +220,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
id_str_size = strlen(sn->id_str);
name_size = strlen(sn->name);
+ assert(id_str_size <= UINT16_MAX && name_size <= UINT16_MAX);
h.id_str_size = cpu_to_be16(id_str_size);
h.name_size = cpu_to_be16(name_size);
offset = align_offset(offset, 8);
@@ -278,6 +279,10 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
return 0;
fail:
+ if (snapshots_offset > 0) {
+ qcow2_free_clusters(bs, snapshots_offset, snapshots_size,
+ QCOW2_DISCARD_ALWAYS);
+ }
return ret;
}
@@ -286,7 +291,8 @@ static void find_new_snapshot_id(BlockDriverState *bs,
{
BDRVQcowState *s = bs->opaque;
QCowSnapshot *sn;
- int i, id, id_max = 0;
+ int i;
+ unsigned long id, id_max = 0;
for(i = 0; i < s->nb_snapshots; i++) {
sn = s->snapshots + i;
@@ -294,7 +300,7 @@ static void find_new_snapshot_id(BlockDriverState *bs,
if (id > id_max)
id_max = id;
}
- snprintf(id_str, id_str_size, "%d", id_max + 1);
+ snprintf(id_str, id_str_size, "%lu", id_max + 1);
}
static int find_snapshot_by_id_and_name(BlockDriverState *bs,
@@ -388,8 +394,8 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
l1_table[i] = cpu_to_be64(s->l1_table[i]);
}
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
- sn->l1_table_offset, s->l1_size * sizeof(uint64_t));
+ ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset,
+ s->l1_size * sizeof(uint64_t));
if (ret < 0) {
goto fail;
}
@@ -427,6 +433,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
if (ret < 0) {
g_free(s->snapshots);
s->snapshots = old_snapshot_list;
+ s->nb_snapshots--;
goto fail;
}
@@ -513,9 +520,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
goto fail;
}
- ret = qcow2_pre_write_overlap_check(bs,
- QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
- s->l1_table_offset, cur_l1_bytes);
+ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
+ s->l1_table_offset, cur_l1_bytes);
if (ret < 0) {
goto fail;
}
diff --git a/block/qcow2.c b/block/qcow2.c
index 4a9888cc7f..c1abaffa19 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -354,10 +354,67 @@ static QemuOptsList qcow2_runtime_opts = {
.type = QEMU_OPT_BOOL,
.help = "Generate discard requests when other clusters are freed",
},
+ {
+ .name = QCOW2_OPT_OVERLAP,
+ .type = QEMU_OPT_STRING,
+ .help = "Selects which overlap checks to perform from a range of "
+ "templates (none, constant, cached, all)",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_MAIN_HEADER,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into the main qcow2 header",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_ACTIVE_L1,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into the active L1 table",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_ACTIVE_L2,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into an active L2 table",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into the refcount table",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into a refcount block",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into the snapshot table",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_INACTIVE_L1,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into an inactive L1 table",
+ },
+ {
+ .name = QCOW2_OPT_OVERLAP_INACTIVE_L2,
+ .type = QEMU_OPT_BOOL,
+ .help = "Check for unintended writes into an inactive L2 table",
+ },
{ /* end of list */ }
},
};
+static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = {
+ [QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER,
+ [QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1,
+ [QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2,
+ [QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
+ [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
+ [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
+ [QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1,
+ [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2,
+};
+
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
@@ -368,6 +425,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
Error *local_err = NULL;
uint64_t ext_end;
uint64_t l1_vm_state_index;
+ const char *opt_overlap_check;
+ int overlap_check_template = 0;
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
if (ret < 0) {
@@ -631,6 +690,33 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
s->discard_passthrough[QCOW2_DISCARD_OTHER] =
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false);
+ opt_overlap_check = qemu_opt_get(opts, "overlap-check") ?: "cached";
+ if (!strcmp(opt_overlap_check, "none")) {
+ overlap_check_template = 0;
+ } else if (!strcmp(opt_overlap_check, "constant")) {
+ overlap_check_template = QCOW2_OL_CONSTANT;
+ } else if (!strcmp(opt_overlap_check, "cached")) {
+ overlap_check_template = QCOW2_OL_CACHED;
+ } else if (!strcmp(opt_overlap_check, "all")) {
+ overlap_check_template = QCOW2_OL_ALL;
+ } else {
+ error_setg(errp, "Unsupported value '%s' for qcow2 option "
+ "'overlap-check'. Allowed are either of the following: "
+ "none, constant, cached, all", opt_overlap_check);
+ qemu_opts_del(opts);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ s->overlap_check = 0;
+ for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) {
+ /* overlap-check defines a template bitmask, but every flag may be
+ * overwritten through the associated boolean option */
+ s->overlap_check |=
+ qemu_opt_get_bool(opts, overlap_bool_option_names[i],
+ overlap_check_template & (1 << i)) << i;
+ }
+
qemu_opts_del(opts);
if (s->use_lazy_refcounts && s->qcow_version < 3) {
@@ -965,7 +1051,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
cur_nr_sectors * 512);
}
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
+ ret = qcow2_pre_write_overlap_check(bs, 0,
cluster_offset + index_in_cluster * BDRV_SECTOR_SIZE,
cur_nr_sectors * BDRV_SECTOR_SIZE);
if (ret < 0) {
@@ -1738,14 +1824,6 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
/* could not compress: write normal cluster */
-
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
- sector_num * BDRV_SECTOR_SIZE,
- s->cluster_sectors * BDRV_SECTOR_SIZE);
- if (ret < 0) {
- goto fail;
- }
-
ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors);
if (ret < 0) {
goto fail;
@@ -1759,8 +1837,7 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
}
cluster_offset &= s->cluster_offset_mask;
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
- cluster_offset, out_len);
+ ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len);
if (ret < 0) {
goto fail;
}
@@ -1810,6 +1887,33 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return 0;
}
+static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1);
+
+ *spec_info = (ImageInfoSpecific){
+ .kind = IMAGE_INFO_SPECIFIC_KIND_QCOW2,
+ {
+ .qcow2 = g_new(ImageInfoSpecificQCow2, 1),
+ },
+ };
+ if (s->qcow_version == 2) {
+ *spec_info->qcow2 = (ImageInfoSpecificQCow2){
+ .compat = g_strdup("0.10"),
+ };
+ } else if (s->qcow_version == 3) {
+ *spec_info->qcow2 = (ImageInfoSpecificQCow2){
+ .compat = g_strdup("1.1"),
+ .lazy_refcounts = s->compatible_features &
+ QCOW2_COMPAT_LAZY_REFCOUNTS,
+ .has_lazy_refcounts = true,
+ };
+ }
+
+ return spec_info;
+}
+
#if 0
static void dump_refcounts(BlockDriverState *bs)
{
@@ -1888,7 +1992,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version)
* support anything different than 4 anyway, there is no point in doing
* so right now; however, we should error out (if qemu supports this in
* the future and this code has not been adapted) */
- error_report("qcow2_downgrade: Image refcount orders other than 4 are"
+ error_report("qcow2_downgrade: Image refcount orders other than 4 are "
"currently not supported.");
return -ENOTSUP;
}
@@ -2130,6 +2234,7 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_snapshot_list = qcow2_snapshot_list,
.bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
.bdrv_get_info = qcow2_get_info,
+ .bdrv_get_specific_info = qcow2_get_specific_info,
.bdrv_save_vmstate = qcow2_save_vmstate,
.bdrv_load_vmstate = qcow2_load_vmstate,
diff --git a/block/qcow2.h b/block/qcow2.h
index 455e38de64..922e19062a 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -63,6 +63,15 @@
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"
#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot"
#define QCOW2_OPT_DISCARD_OTHER "pass-discard-other"
+#define QCOW2_OPT_OVERLAP "overlap-check"
+#define QCOW2_OPT_OVERLAP_MAIN_HEADER "overlap-check.main-header"
+#define QCOW2_OPT_OVERLAP_ACTIVE_L1 "overlap-check.active-l1"
+#define QCOW2_OPT_OVERLAP_ACTIVE_L2 "overlap-check.active-l2"
+#define QCOW2_OPT_OVERLAP_REFCOUNT_TABLE "overlap-check.refcount-table"
+#define QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK "overlap-check.refcount-block"
+#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table"
+#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1"
+#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2"
typedef struct QCowHeader {
uint32_t magic;
@@ -203,6 +212,8 @@ typedef struct BDRVQcowState {
bool discard_passthrough[QCOW2_DISCARD_MAX];
+ int overlap_check; /* bitmask of Qcow2MetadataOverlap values */
+
uint64_t incompatible_features;
uint64_t compatible_features;
uint64_t autoclear_features;
@@ -315,14 +326,19 @@ typedef enum QCow2MetadataOverlap {
QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR),
} QCow2MetadataOverlap;
+/* Perform all overlap checks which can be done in constant time */
+#define QCOW2_OL_CONSTANT \
+ (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \
+ QCOW2_OL_SNAPSHOT_TABLE)
+
/* Perform all overlap checks which don't require disk access */
#define QCOW2_OL_CACHED \
- (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_ACTIVE_L2 | \
- QCOW2_OL_REFCOUNT_TABLE | QCOW2_OL_REFCOUNT_BLOCK | \
- QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_INACTIVE_L1)
+ (QCOW2_OL_CONSTANT | QCOW2_OL_ACTIVE_L2 | QCOW2_OL_REFCOUNT_BLOCK | \
+ QCOW2_OL_INACTIVE_L1)
-/* The default checks to perform */
-#define QCOW2_OL_DEFAULT QCOW2_OL_CACHED
+/* Perform all overlap checks */
+#define QCOW2_OL_ALL \
+ (QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2)
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL
@@ -433,9 +449,9 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
void qcow2_process_discards(BlockDriverState *bs, int ret);
-int qcow2_check_metadata_overlap(BlockDriverState *bs, int chk, int64_t offset,
+int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
int64_t size);
-int qcow2_pre_write_overlap_check(BlockDriverState *bs, int chk, int64_t offset,
+int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
int64_t size);
/* qcow2-cluster.c functions */
diff --git a/block/raw-posix.c b/block/raw-posix.c
index f7f102d2e2..6f03fbf793 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -276,7 +276,7 @@ static QemuOptsList raw_runtime_opts = {
};
static int raw_open_common(BlockDriverState *bs, QDict *options,
- int bdrv_flags, int open_flags)
+ int bdrv_flags, int open_flags, Error **errp)
{
BDRVRawState *s = bs->opaque;
QemuOpts *opts;
@@ -287,8 +287,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
opts = qemu_opts_create_nofail(&raw_runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
@@ -297,6 +296,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
ret = raw_normalize_devicepath(&filename);
if (ret != 0) {
+ error_setg_errno(errp, -ret, "Could not normalize device path");
goto fail;
}
@@ -310,6 +310,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
if (ret == -EROFS) {
ret = -EACCES;
}
+ error_setg_errno(errp, -ret, "Could not open file");
goto fail;
}
s->fd = fd;
@@ -318,6 +319,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
if (raw_set_aio(&s->aio_ctx, &s->use_aio, bdrv_flags)) {
qemu_close(fd);
ret = -errno;
+ error_setg_errno(errp, -ret, "Could not set AIO state");
goto fail;
}
#endif
@@ -339,9 +341,15 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVRawState *s = bs->opaque;
+ Error *local_err = NULL;
+ int ret;
s->type = FTYPE_FILE;
- return raw_open_common(bs, options, flags, 0);
+ ret = raw_open_common(bs, options, flags, 0, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ }
+ return ret;
}
static int raw_reopen_prepare(BDRVReopenState *state,
@@ -366,6 +374,7 @@ static int raw_reopen_prepare(BDRVReopenState *state,
* valid in the 'false' condition even if aio_ctx is set, and raw_set_aio()
* won't override aio_ctx if aio_ctx is non-NULL */
if (raw_set_aio(&s->aio_ctx, &raw_s->use_aio, state->flags)) {
+ error_setg(errp, "Could not set AIO state");
return -1;
}
#endif
@@ -417,6 +426,7 @@ static int raw_reopen_prepare(BDRVReopenState *state,
assert(!(raw_s->open_flags & O_CREAT));
raw_s->fd = qemu_open(state->bs->filename, raw_s->open_flags);
if (raw_s->fd == -1) {
+ error_setg_errno(errp, errno, "Could not reopen file");
ret = -1;
}
}
@@ -1060,12 +1070,15 @@ static int raw_create(const char *filename, QEMUOptionParameter *options,
0644);
if (fd < 0) {
result = -errno;
+ error_setg_errno(errp, -result, "Could not create file");
} else {
if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
result = -errno;
+ error_setg_errno(errp, -result, "Could not resize file");
}
if (qemu_close(fd) != 0) {
result = -errno;
+ error_setg_errno(errp, -result, "Could not close the new file");
}
}
return result;
@@ -1338,6 +1351,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVRawState *s = bs->opaque;
+ Error *local_err = NULL;
int ret;
const char *filename = qdict_get_str(options, "filename");
@@ -1381,8 +1395,11 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
}
#endif
- ret = raw_open_common(bs, options, flags, 0);
+ ret = raw_open_common(bs, options, flags, 0, &local_err);
if (ret < 0) {
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ }
return ret;
}
@@ -1390,6 +1407,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
ret = check_hdev_writable(s);
if (ret < 0) {
raw_close(bs);
+ error_setg_errno(errp, -ret, "The device is not writable");
return ret;
}
}
@@ -1525,15 +1543,23 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options,
}
fd = qemu_open(filename, O_WRONLY | O_BINARY);
- if (fd < 0)
- return -errno;
+ if (fd < 0) {
+ ret = -errno;
+ error_setg_errno(errp, -ret, "Could not open device");
+ return ret;
+ }
- if (fstat(fd, &stat_buf) < 0)
+ if (fstat(fd, &stat_buf) < 0) {
ret = -errno;
- else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode))
+ error_setg_errno(errp, -ret, "Could not stat device");
+ } else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) {
+ error_setg(errp,
+ "The given file is neither a block nor a character device");
ret = -ENODEV;
- else if (lseek(fd, 0, SEEK_END) < total_size * BDRV_SECTOR_SIZE)
+ } else if (lseek(fd, 0, SEEK_END) < total_size * BDRV_SECTOR_SIZE) {
+ error_setg(errp, "Device is too small");
ret = -ENOSPC;
+ }
qemu_close(fd);
return ret;
@@ -1575,14 +1601,19 @@ static int floppy_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVRawState *s = bs->opaque;
+ Error *local_err = NULL;
int ret;
s->type = FTYPE_FD;
/* open will not fail even if no floppy is inserted, so add O_NONBLOCK */
- ret = raw_open_common(bs, options, flags, O_NONBLOCK);
- if (ret)
+ ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
+ if (ret) {
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ }
return ret;
+ }
/* close fd so that we can reopen it as needed */
qemu_close(s->fd);
@@ -1698,11 +1729,17 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVRawState *s = bs->opaque;
+ Error *local_err = NULL;
+ int ret;
s->type = FTYPE_CD;
/* open will not fail even if no CD is inserted, so add O_NONBLOCK */
- return raw_open_common(bs, options, flags, O_NONBLOCK);
+ ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ }
+ return ret;
}
static int cdrom_probe_device(const char *filename)
@@ -1806,13 +1843,18 @@ static BlockDriver bdrv_host_cdrom = {
static int cdrom_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVRawState *s = bs->opaque;
+ Error *local_err = NULL;
int ret;
s->type = FTYPE_CD;
- ret = raw_open_common(bs, options, flags, 0);
- if (ret)
+ ret = raw_open_common(bs, options, flags, 0, &local_err);
+ if (ret) {
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ }
return ret;
+ }
/* make sure the door isn't locked at this time */
ioctl(s->fd, CDIOCALLOW);
diff --git a/block/raw-win32.c b/block/raw-win32.c
index 6ef320f16a..c3e4c62d53 100644
--- a/block/raw-win32.c
+++ b/block/raw-win32.c
@@ -251,8 +251,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
opts = qemu_opts_create_nofail(&raw_runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
@@ -264,6 +263,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
if ((flags & BDRV_O_NATIVE_AIO) && aio == NULL) {
aio = win32_aio_init();
if (aio == NULL) {
+ error_setg(errp, "Could not initialize AIO");
ret = -EINVAL;
goto fail;
}
@@ -280,6 +280,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
} else {
ret = -EINVAL;
}
+ error_setg_errno(errp, -ret, "Could not open file");
goto fail;
}
@@ -287,6 +288,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
ret = win32_aio_attach(aio, s->hfile);
if (ret < 0) {
CloseHandle(s->hfile);
+ error_setg_errno(errp, -ret, "Could not enable AIO");
goto fail;
}
s->aio = aio;
@@ -438,8 +440,10 @@ static int raw_create(const char *filename, QEMUOptionParameter *options,
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
0644);
- if (fd < 0)
+ if (fd < 0) {
+ error_setg_errno(errp, errno, "Could not create file");
return -EIO;
+ }
set_sparse(fd);
ftruncate(fd, total_size * 512);
qemu_close(fd);
@@ -550,8 +554,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
QemuOpts *opts = qemu_opts_create_nofail(&raw_runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto done;
}
@@ -560,6 +563,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
if (strstart(filename, "/dev/cdrom", NULL)) {
if (find_cdrom(device_name, sizeof(device_name)) < 0) {
+ error_setg(errp, "Could not open CD-ROM drive");
ret = -ENOENT;
goto done;
}
@@ -586,8 +590,10 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
int err = GetLastError();
if (err == ERROR_ACCESS_DENIED) {
+ error_setg_errno(errp, EACCES, "Could not open device");
ret = -EACCES;
} else {
+ error_setg(errp, "Could not open device");
ret = -1;
}
goto done;
diff --git a/block/raw_bsd.c b/block/raw_bsd.c
index d4ace6020b..0078c1baeb 100644
--- a/block/raw_bsd.c
+++ b/block/raw_bsd.c
@@ -62,7 +62,9 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors, int *pnum)
{
- return bdrv_get_block_status(bs->file, sector_num, nb_sectors, pnum);
+ *pnum = nb_sectors;
+ return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
+ (sector_num << BDRV_SECTOR_BITS);
}
static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs,
@@ -138,8 +140,7 @@ static int raw_create(const char *filename, QEMUOptionParameter *options,
ret = bdrv_create_file(filename, options, &local_err);
if (error_is_set(&local_err)) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
}
return ret;
}
diff --git a/block/stream.c b/block/stream.c
index 45837f4476..694fd42e41 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -203,9 +203,9 @@ static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
}
-static const BlockJobType stream_job_type = {
+static const BlockJobDriver stream_job_driver = {
.instance_size = sizeof(StreamBlockJob),
- .job_type = "stream",
+ .job_type = BLOCK_JOB_TYPE_STREAM,
.set_speed = stream_set_speed,
};
@@ -224,7 +224,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
return;
}
- s = block_job_create(&stream_job_type, bs, speed, cb, opaque, errp);
+ s = block_job_create(&stream_job_driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
diff --git a/block/vhdx.c b/block/vhdx.c
index b8aa49ce4e..6cb04122bb 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -20,6 +20,7 @@
#include "qemu/module.h"
#include "qemu/crc32c.h"
#include "block/vhdx.h"
+#include "migration/migration.h"
/* Several metadata and region table data entries are identified by
@@ -159,6 +160,7 @@ typedef struct BDRVVHDXState {
VHDXParentLocatorHeader parent_header;
VHDXParentLocatorEntry *parent_entries;
+ Error *migration_blocker;
} BDRVVHDXState;
uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
@@ -806,6 +808,12 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
/* TODO: differencing files, write */
+ /* Disable migration when VHDX images are used */
+ error_set(&s->migration_blocker,
+ QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
+ "vhdx", bs->device_name, "live migration");
+ migrate_add_blocker(s->migration_blocker);
+
return 0;
fail:
qemu_vfree(s->headers[0]);
@@ -952,6 +960,8 @@ static void vhdx_close(BlockDriverState *bs)
qemu_vfree(s->headers[1]);
qemu_vfree(s->bat);
qemu_vfree(s->parent_entries);
+ migrate_del_blocker(s->migration_blocker);
+ error_free(s->migration_blocker);
}
static BlockDriver bdrv_vhdx = {
diff --git a/block/vmdk.c b/block/vmdk.c
index 5d56e316f1..5a9f2787f8 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -331,8 +331,7 @@ static int vmdk_reopen_prepare(BDRVReopenState *state,
assert(state->bs != NULL);
if (queue == NULL) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "No reopen queue for VMDK extents");
+ error_setg(errp, "No reopen queue for VMDK extents");
goto exit;
}
@@ -391,22 +390,23 @@ static int vmdk_add_extent(BlockDriverState *bs,
int64_t l1_offset, int64_t l1_backup_offset,
uint32_t l1_size,
int l2_size, uint64_t cluster_sectors,
- VmdkExtent **new_extent)
+ VmdkExtent **new_extent,
+ Error **errp)
{
VmdkExtent *extent;
BDRVVmdkState *s = bs->opaque;
if (cluster_sectors > 0x200000) {
/* 0x200000 * 512Bytes = 1GB for one cluster is unrealistic */
- error_report("invalid granularity, image may be corrupt");
- return -EINVAL;
+ error_setg(errp, "Invalid granularity, image may be corrupt");
+ return -EFBIG;
}
if (l1_size > 512 * 1024 * 1024) {
/* Although with big capacity and small l1_entry_sectors, we can get a
* big l1_size, we don't want unbounded value to allocate the table.
* Limit it to 512M, which is 16PB for default cluster and L2 table
* size */
- error_report("L1 size too big");
+ error_setg(errp, "L1 size too big");
return -EFBIG;
}
@@ -438,7 +438,8 @@ static int vmdk_add_extent(BlockDriverState *bs,
return 0;
}
-static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
+static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
+ Error **errp)
{
int ret;
int l1_size, i;
@@ -447,10 +448,13 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
l1_size = extent->l1_size * sizeof(uint32_t);
extent->l1_table = g_malloc(l1_size);
ret = bdrv_pread(extent->file,
- extent->l1_table_offset,
- extent->l1_table,
- l1_size);
+ extent->l1_table_offset,
+ extent->l1_table,
+ l1_size);
if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Could not read l1 table from extent '%s'",
+ extent->file->filename);
goto fail_l1;
}
for (i = 0; i < extent->l1_size; i++) {
@@ -460,10 +464,13 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
if (extent->l1_backup_table_offset) {
extent->l1_backup_table = g_malloc(l1_size);
ret = bdrv_pread(extent->file,
- extent->l1_backup_table_offset,
- extent->l1_backup_table,
- l1_size);
+ extent->l1_backup_table_offset,
+ extent->l1_backup_table,
+ l1_size);
if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Could not read l1 backup table from extent '%s'",
+ extent->file->filename);
goto fail_l1b;
}
for (i = 0; i < extent->l1_size; i++) {
@@ -483,7 +490,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent)
static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
BlockDriverState *file,
- int flags)
+ int flags, Error **errp)
{
int ret;
uint32_t magic;
@@ -492,6 +499,9 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Could not read header from file '%s'",
+ file->filename);
return ret;
}
ret = vmdk_add_extent(bs, file, false,
@@ -501,11 +511,12 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
le32_to_cpu(header.l1dir_size),
4096,
le32_to_cpu(header.granularity),
- &extent);
+ &extent,
+ errp);
if (ret < 0) {
return ret;
}
- ret = vmdk_init_tables(bs, extent);
+ ret = vmdk_init_tables(bs, extent, errp);
if (ret) {
/* free extent allocated by vmdk_add_extent */
vmdk_free_last_extent(bs);
@@ -514,11 +525,11 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
}
static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
- uint64_t desc_offset);
+ uint64_t desc_offset, Error **errp);
static int vmdk_open_vmdk4(BlockDriverState *bs,
BlockDriverState *file,
- int flags)
+ int flags, Error **errp)
{
int ret;
uint32_t magic;
@@ -529,12 +540,14 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
if (ret < 0) {
- return ret;
+ error_setg_errno(errp, -ret,
+ "Could not read header from file '%s'",
+ file->filename);
}
if (header.capacity == 0) {
uint64_t desc_offset = le64_to_cpu(header.desc_offset);
if (desc_offset) {
- return vmdk_open_desc_file(bs, flags, desc_offset << 9);
+ return vmdk_open_desc_file(bs, flags, desc_offset << 9, errp);
}
}
@@ -616,7 +629,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
l1_size,
le32_to_cpu(header.num_gtes_per_gt),
le64_to_cpu(header.granularity),
- &extent);
+ &extent,
+ errp);
if (ret < 0) {
return ret;
}
@@ -625,7 +639,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
extent->has_marker = le32_to_cpu(header.flags) & VMDK4_FLAG_MARKER;
extent->version = le32_to_cpu(header.version);
extent->has_zero_grain = le32_to_cpu(header.flags) & VMDK4_FLAG_ZERO_GRAIN;
- ret = vmdk_init_tables(bs, extent);
+ ret = vmdk_init_tables(bs, extent, errp);
if (ret) {
/* free extent allocated by vmdk_add_extent */
vmdk_free_last_extent(bs);
@@ -663,7 +677,7 @@ static int vmdk_parse_description(const char *desc, const char *opt_name,
/* Open an extent file and append to bs array */
static int vmdk_open_sparse(BlockDriverState *bs,
BlockDriverState *file,
- int flags)
+ int flags, Error **errp)
{
uint32_t magic;
@@ -674,10 +688,10 @@ static int vmdk_open_sparse(BlockDriverState *bs,
magic = be32_to_cpu(magic);
switch (magic) {
case VMDK3_MAGIC:
- return vmdk_open_vmfs_sparse(bs, file, flags);
+ return vmdk_open_vmfs_sparse(bs, file, flags, errp);
break;
case VMDK4_MAGIC:
- return vmdk_open_vmdk4(bs, file, flags);
+ return vmdk_open_vmdk4(bs, file, flags, errp);
break;
default:
return -EMEDIUMTYPE;
@@ -686,7 +700,7 @@ static int vmdk_open_sparse(BlockDriverState *bs,
}
static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
- const char *desc_file_path)
+ const char *desc_file_path, Error **errp)
{
int ret;
char access[11];
@@ -697,7 +711,6 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
int64_t flat_offset;
char extent_path[PATH_MAX];
BlockDriverState *extent_file;
- Error *local_err = NULL;
while (*p) {
/* parse extent line:
@@ -712,9 +725,11 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
goto next_line;
} else if (!strcmp(type, "FLAT")) {
if (ret != 5 || flat_offset < 0) {
+ error_setg(errp, "Invalid extent lines: \n%s", p);
return -EINVAL;
}
} else if (ret != 4) {
+ error_setg(errp, "Invalid extent lines: \n%s", p);
return -EINVAL;
}
@@ -728,10 +743,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
path_combine(extent_path, sizeof(extent_path),
desc_file_path, fname);
ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags,
- &local_err);
+ errp);
if (ret) {
- qerror_report_err(local_err);
- error_free(local_err);
return ret;
}
@@ -741,35 +754,37 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
VmdkExtent *extent;
ret = vmdk_add_extent(bs, extent_file, true, sectors,
- 0, 0, 0, 0, 0, &extent);
+ 0, 0, 0, 0, 0, &extent, errp);
if (ret < 0) {
return ret;
}
extent->flat_start_offset = flat_offset << 9;
} else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) {
/* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/
- ret = vmdk_open_sparse(bs, extent_file, bs->open_flags);
+ ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, errp);
if (ret) {
bdrv_unref(extent_file);
return ret;
}
} else {
- fprintf(stderr,
- "VMDK: Not supported extent type \"%s\""".\n", type);
+ error_setg(errp, "Unsupported extent type '%s'", type);
return -ENOTSUP;
}
next_line:
/* move to next line */
- while (*p && *p != '\n') {
+ while (*p) {
+ if (*p == '\n') {
+ p++;
+ break;
+ }
p++;
}
- p++;
}
return 0;
}
static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
- uint64_t desc_offset)
+ uint64_t desc_offset, Error **errp)
{
int ret;
char *buf = NULL;
@@ -798,13 +813,12 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
strcmp(ct, "vmfsSparse") &&
strcmp(ct, "twoGbMaxExtentSparse") &&
strcmp(ct, "twoGbMaxExtentFlat")) {
- fprintf(stderr,
- "VMDK: Not supported image type \"%s\""".\n", ct);
+ error_setg(errp, "Unsupported image type '%s'", ct);
ret = -ENOTSUP;
goto exit;
}
s->desc_offset = 0;
- ret = vmdk_parse_extents(buf, bs, bs->file->filename);
+ ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp);
exit:
g_free(buf);
return ret;
@@ -816,10 +830,10 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
int ret;
BDRVVmdkState *s = bs->opaque;
- if (vmdk_open_sparse(bs, bs->file, flags) == 0) {
+ if (vmdk_open_sparse(bs, bs->file, flags, errp) == 0) {
s->desc_offset = 0x200;
} else {
- ret = vmdk_open_desc_file(bs, flags, 0);
+ ret = vmdk_open_desc_file(bs, flags, 0, errp);
if (ret) {
goto fail;
}
@@ -1286,8 +1300,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
VmdkMetaData m_data;
if (sector_num > bs->total_sectors) {
- fprintf(stderr,
- "(VMDK) Wrong offset: sector_num=0x%" PRIx64
+ error_report("Wrong offset: sector_num=0x%" PRIx64
" total_sectors=0x%" PRIx64 "\n",
sector_num, bs->total_sectors);
return -EIO;
@@ -1307,9 +1320,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
if (extent->compressed) {
if (ret == VMDK_OK) {
/* Refuse write to allocated cluster for streamOptimized */
- fprintf(stderr,
- "VMDK: can't write to allocated cluster"
- " for streamOptimized\n");
+ error_report("Could not write to allocated cluster"
+ " for streamOptimized");
return -EIO;
} else {
/* allocate */
@@ -1517,12 +1529,12 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
}
static int filename_decompose(const char *filename, char *path, char *prefix,
- char *postfix, size_t buf_len)
+ char *postfix, size_t buf_len, Error **errp)
{
const char *p, *q;
if (filename == NULL || !strlen(filename)) {
- fprintf(stderr, "Vmdk: no filename provided.\n");
+ error_setg(errp, "No filename provided");
return VMDK_ERROR;
}
p = strrchr(filename, '/');
@@ -1595,9 +1607,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
"ddb.geometry.heads = \"%d\"\n"
"ddb.geometry.sectors = \"63\"\n"
"ddb.adapterType = \"%s\"\n";
- Error *local_err = NULL;
- if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) {
+ if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
return -EINVAL;
}
/* Read out options */
@@ -1623,7 +1634,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
strcmp(adapter_type, "buslogic") &&
strcmp(adapter_type, "lsilogic") &&
strcmp(adapter_type, "legacyESX")) {
- fprintf(stderr, "VMDK: Unknown adapter type: '%s'.\n", adapter_type);
+ error_setg(errp, "Unknown adapter type: '%s'", adapter_type);
return -EINVAL;
}
if (strcmp(adapter_type, "ide") != 0) {
@@ -1639,7 +1650,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
strcmp(fmt, "twoGbMaxExtentSparse") &&
strcmp(fmt, "twoGbMaxExtentFlat") &&
strcmp(fmt, "streamOptimized")) {
- fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt);
+ error_setg(errp, "Unknown subformat: '%s'", fmt);
return -EINVAL;
}
split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
@@ -1653,15 +1664,17 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
desc_extent_line = "RW %lld SPARSE \"%s\"\n";
}
if (flat && backing_file) {
- /* not supporting backing file for flat image */
+ error_setg(errp, "Flat image can't have backing file");
+ return -ENOTSUP;
+ }
+ if (flat && zeroed_grain) {
+ error_setg(errp, "Flat image can't enable zeroed grain");
return -ENOTSUP;
}
if (backing_file) {
BlockDriverState *bs = bdrv_new("");
- ret = bdrv_open(bs, backing_file, NULL, 0, NULL, &local_err);
+ ret = bdrv_open(bs, backing_file, NULL, 0, NULL, errp);
if (ret != 0) {
- qerror_report_err(local_err);
- error_free(local_err);
bdrv_unref(bs);
return ret;
}
diff --git a/blockdev.c b/blockdev.c
index 8aa66a949c..4f76e28164 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -38,6 +38,8 @@
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "qapi/qmp/types.h"
+#include "qapi-visit.h"
+#include "qapi/qmp-output-visitor.h"
#include "sysemu/sysemu.h"
#include "block/block_int.h"
#include "qmp-commands.h"
@@ -89,6 +91,10 @@ void blockdev_mark_auto_del(BlockDriverState *bs)
{
DriveInfo *dinfo = drive_get_by_blockdev(bs);
+ if (dinfo && !dinfo->enable_auto_del) {
+ return;
+ }
+
if (bs->job) {
block_job_cancel(bs->job);
}
@@ -211,7 +217,10 @@ static void bdrv_format_print(void *opaque, const char *name)
static void drive_uninit(DriveInfo *dinfo)
{
- qemu_opts_del(dinfo->opts);
+ if (dinfo->opts) {
+ qemu_opts_del(dinfo->opts);
+ }
+
bdrv_unref(dinfo->bdrv);
g_free(dinfo->id);
QTAILQ_REMOVE(&drives, dinfo, next);
@@ -263,7 +272,7 @@ static void bdrv_put_ref_bh_schedule(BlockDriverState *bs)
qemu_bh_schedule(s->bh);
}
-static int parse_block_error_action(const char *buf, bool is_read)
+static int parse_block_error_action(const char *buf, bool is_read, Error **errp)
{
if (!strcmp(buf, "ignore")) {
return BLOCKDEV_ON_ERROR_IGNORE;
@@ -274,8 +283,8 @@ static int parse_block_error_action(const char *buf, bool is_read)
} else if (!strcmp(buf, "report")) {
return BLOCKDEV_ON_ERROR_REPORT;
} else {
- error_report("'%s' invalid %s error action",
- buf, is_read ? "read" : "write");
+ error_setg(errp, "'%s' invalid %s error action",
+ buf, is_read ? "read" : "write");
return -1;
}
}
@@ -296,23 +305,19 @@ static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
return true;
}
-static DriveInfo *blockdev_init(QemuOpts *all_opts,
- BlockInterfaceType block_default_type)
+typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
+
+/* Takes the ownership of bs_opts */
+static DriveInfo *blockdev_init(QDict *bs_opts,
+ BlockInterfaceType type,
+ Error **errp)
{
const char *buf;
const char *file = NULL;
const char *serial;
- const char *mediastr = "";
- BlockInterfaceType type;
- enum { MEDIA_DISK, MEDIA_CDROM } media;
- int bus_id, unit_id;
- int cyls, heads, secs, translation;
- int max_devs;
- int index;
int ro = 0;
int bdrv_flags = 0;
int on_read_error, on_write_error;
- const char *devaddr;
DriveInfo *dinfo;
ThrottleConfig cfg;
int snapshot = 0;
@@ -320,30 +325,22 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
int ret;
Error *error = NULL;
QemuOpts *opts;
- QDict *bs_opts;
const char *id;
bool has_driver_specific_opts;
BlockDriver *drv = NULL;
- translation = BIOS_ATA_TRANSLATION_AUTO;
- media = MEDIA_DISK;
-
- /* Check common options by copying from all_opts to opts, all other options
- * are stored in bs_opts. */
- id = qemu_opts_id(all_opts);
+ /* Check common options by copying from bs_opts to opts, all other options
+ * stay in bs_opts for processing by bdrv_open(). */
+ id = qdict_get_try_str(bs_opts, "id");
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
if (error_is_set(&error)) {
- qerror_report_err(error);
- error_free(error);
+ error_propagate(errp, error);
return NULL;
}
- bs_opts = qdict_new();
- qemu_opts_to_qdict(all_opts, bs_opts);
qemu_opts_absorb_qdict(opts, bs_opts, &error);
if (error_is_set(&error)) {
- qerror_report_err(error);
- error_free(error);
+ error_propagate(errp, error);
return NULL;
}
@@ -354,14 +351,6 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
has_driver_specific_opts = !!qdict_size(bs_opts);
/* extract parameters */
- bus_id = qemu_opt_get_number(opts, "bus", 0);
- unit_id = qemu_opt_get_number(opts, "unit", -1);
- index = qemu_opt_get_number(opts, "index", -1);
-
- cyls = qemu_opt_get_number(opts, "cyls", 0);
- heads = qemu_opt_get_number(opts, "heads", 0);
- secs = qemu_opt_get_number(opts, "secs", 0);
-
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
ro = qemu_opt_get_bool(opts, "read-only", 0);
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
@@ -369,70 +358,9 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
file = qemu_opt_get(opts, "file");
serial = qemu_opt_get(opts, "serial");
- if ((buf = qemu_opt_get(opts, "if")) != NULL) {
- for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++)
- ;
- if (type == IF_COUNT) {
- error_report("unsupported bus type '%s'", buf);
- return NULL;
- }
- } else {
- type = block_default_type;
- }
-
- max_devs = if_max_devs[type];
-
- if (cyls || heads || secs) {
- if (cyls < 1) {
- error_report("invalid physical cyls number");
- return NULL;
- }
- if (heads < 1) {
- error_report("invalid physical heads number");
- return NULL;
- }
- if (secs < 1) {
- error_report("invalid physical secs number");
- return NULL;
- }
- }
-
- if ((buf = qemu_opt_get(opts, "trans")) != NULL) {
- if (!cyls) {
- error_report("'%s' trans must be used with cyls, heads and secs",
- buf);
- return NULL;
- }
- if (!strcmp(buf, "none"))
- translation = BIOS_ATA_TRANSLATION_NONE;
- else if (!strcmp(buf, "lba"))
- translation = BIOS_ATA_TRANSLATION_LBA;
- else if (!strcmp(buf, "auto"))
- translation = BIOS_ATA_TRANSLATION_AUTO;
- else {
- error_report("'%s' invalid translation type", buf);
- return NULL;
- }
- }
-
- if ((buf = qemu_opt_get(opts, "media")) != NULL) {
- if (!strcmp(buf, "disk")) {
- media = MEDIA_DISK;
- } else if (!strcmp(buf, "cdrom")) {
- if (cyls || secs || heads) {
- error_report("CHS can't be set with media=%s", buf);
- return NULL;
- }
- media = MEDIA_CDROM;
- } else {
- error_report("'%s' invalid media", buf);
- return NULL;
- }
- }
-
if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
- error_report("invalid discard option");
+ error_setg(errp, "invalid discard option");
return NULL;
}
}
@@ -454,7 +382,7 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
} else if (!strcmp(buf, "threads")) {
/* this is the default */
} else {
- error_report("invalid aio option");
+ error_setg(errp, "invalid aio option");
return NULL;
}
}
@@ -468,13 +396,9 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
return NULL;
}
- drv = bdrv_find_whitelisted_format(buf, ro);
+ drv = bdrv_find_format(buf);
if (!drv) {
- if (!ro && bdrv_find_whitelisted_format(buf, !ro)) {
- error_report("'%s' can be only used as read-only device.", buf);
- } else {
- error_report("'%s' invalid format", buf);
- }
+ error_setg(errp, "'%s' invalid format", buf);
return NULL;
}
}
@@ -510,26 +434,20 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0);
if (!check_throttle_config(&cfg, &error)) {
- error_report("%s", error_get_pretty(error));
- error_free(error);
+ error_propagate(errp, error);
return NULL;
}
- if (qemu_opt_get(opts, "boot") != NULL) {
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
- "ignored. Future versions will reject this parameter. Please "
- "update your scripts.\n");
- }
-
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
- error_report("werror is not supported by this bus type");
+ error_setg(errp, "werror is not supported by this bus type");
return NULL;
}
- on_write_error = parse_block_error_action(buf, 0);
- if (on_write_error < 0) {
+ on_write_error = parse_block_error_action(buf, 0, &error);
+ if (error_is_set(&error)) {
+ error_propagate(errp, error);
return NULL;
}
}
@@ -541,92 +459,20 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
return NULL;
}
- on_read_error = parse_block_error_action(buf, 1);
- if (on_read_error < 0) {
- return NULL;
- }
- }
-
- if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) {
- if (type != IF_VIRTIO) {
- error_report("addr is not supported by this bus type");
+ on_read_error = parse_block_error_action(buf, 1, &error);
+ if (error_is_set(&error)) {
+ error_propagate(errp, error);
return NULL;
}
}
- /* compute bus and unit according index */
-
- if (index != -1) {
- if (bus_id != 0 || unit_id != -1) {
- error_report("index cannot be used with bus and unit");
- return NULL;
- }
- bus_id = drive_index_to_bus_id(type, index);
- unit_id = drive_index_to_unit_id(type, index);
- }
-
- /* if user doesn't specify a unit_id,
- * try to find the first free
- */
-
- if (unit_id == -1) {
- unit_id = 0;
- while (drive_get(type, bus_id, unit_id) != NULL) {
- unit_id++;
- if (max_devs && unit_id >= max_devs) {
- unit_id -= max_devs;
- bus_id++;
- }
- }
- }
-
- /* check unit id */
-
- if (max_devs && unit_id >= max_devs) {
- error_report("unit %d too big (max is %d)",
- unit_id, max_devs - 1);
- return NULL;
- }
-
- /*
- * catch multiple definitions
- */
-
- if (drive_get(type, bus_id, unit_id) != NULL) {
- error_report("drive with bus=%d, unit=%d (index=%d) exists",
- bus_id, unit_id, index);
- return NULL;
- }
-
/* init */
-
dinfo = g_malloc0(sizeof(*dinfo));
- if ((buf = qemu_opts_id(opts)) != NULL) {
- dinfo->id = g_strdup(buf);
- } else {
- /* no id supplied -> create one */
- dinfo->id = g_malloc0(32);
- if (type == IF_IDE || type == IF_SCSI)
- mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
- if (max_devs)
- snprintf(dinfo->id, 32, "%s%i%s%i",
- if_name[type], bus_id, mediastr, unit_id);
- else
- snprintf(dinfo->id, 32, "%s%s%i",
- if_name[type], mediastr, unit_id);
- }
+ dinfo->id = g_strdup(qemu_opts_id(opts));
dinfo->bdrv = bdrv_new(dinfo->id);
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
dinfo->bdrv->read_only = ro;
- dinfo->devaddr = devaddr;
dinfo->type = type;
- dinfo->bus = bus_id;
- dinfo->unit = unit_id;
- dinfo->cyls = cyls;
- dinfo->heads = heads;
- dinfo->secs = secs;
- dinfo->trans = translation;
- dinfo->opts = all_opts;
dinfo->refcount = 1;
if (serial != NULL) {
dinfo->serial = g_strdup(serial);
@@ -641,36 +487,6 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
bdrv_set_io_limits(dinfo->bdrv, &cfg);
}
- switch(type) {
- case IF_IDE:
- case IF_SCSI:
- case IF_XEN:
- case IF_NONE:
- dinfo->media_cd = media == MEDIA_CDROM;
- break;
- case IF_SD:
- case IF_FLOPPY:
- case IF_PFLASH:
- case IF_MTD:
- break;
- case IF_VIRTIO:
- {
- /* add virtio block device */
- QemuOpts *devopts;
- devopts = qemu_opts_create_nofail(qemu_find_opts("device"));
- if (arch_type == QEMU_ARCH_S390X) {
- qemu_opt_set(devopts, "driver", "virtio-blk-s390");
- } else {
- qemu_opt_set(devopts, "driver", "virtio-blk-pci");
- }
- qemu_opt_set(devopts, "drive", dinfo->id);
- if (devaddr)
- qemu_opt_set(devopts, "addr", devaddr);
- break;
- }
- default:
- abort();
- }
if (!file || !*file) {
if (has_driver_specific_opts) {
file = NULL;
@@ -692,29 +508,15 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
bdrv_flags |= BDRV_O_INCOMING;
}
- if (media == MEDIA_CDROM) {
- /* CDROM is fine for any interface, don't check. */
- ro = 1;
- } else if (ro == 1) {
- if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY &&
- type != IF_NONE && type != IF_PFLASH) {
- error_report("read-only not supported by this bus type");
- goto err;
- }
- }
-
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
- if (ro && copy_on_read) {
- error_report("warning: disabling copy_on_read on read-only drive");
- }
-
QINCREF(bs_opts);
ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error);
if (ret < 0) {
- error_report("could not open disk image %s: %s",
- file ?: dinfo->id, error_get_pretty(error));
+ error_setg(errp, "could not open disk image %s: %s",
+ file ?: dinfo->id, error_get_pretty(error));
+ error_free(error);
goto err;
}
@@ -747,9 +549,84 @@ static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to)
}
}
+QemuOptsList qemu_legacy_drive_opts = {
+ .name = "drive",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_legacy_drive_opts.head),
+ .desc = {
+ {
+ .name = "bus",
+ .type = QEMU_OPT_NUMBER,
+ .help = "bus number",
+ },{
+ .name = "unit",
+ .type = QEMU_OPT_NUMBER,
+ .help = "unit number (i.e. lun for scsi)",
+ },{
+ .name = "index",
+ .type = QEMU_OPT_NUMBER,
+ .help = "index number",
+ },{
+ .name = "media",
+ .type = QEMU_OPT_STRING,
+ .help = "media type (disk, cdrom)",
+ },{
+ .name = "if",
+ .type = QEMU_OPT_STRING,
+ .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
+ },{
+ .name = "cyls",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of cylinders (ide disk geometry)",
+ },{
+ .name = "heads",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of heads (ide disk geometry)",
+ },{
+ .name = "secs",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of sectors (ide disk geometry)",
+ },{
+ .name = "trans",
+ .type = QEMU_OPT_STRING,
+ .help = "chs translation (auto, lba, none)",
+ },{
+ .name = "boot",
+ .type = QEMU_OPT_BOOL,
+ .help = "(deprecated, ignored)",
+ },{
+ .name = "addr",
+ .type = QEMU_OPT_STRING,
+ .help = "pci address (virtio only)",
+ },
+
+ /* Options that are passed on, but have special semantics with -drive */
+ {
+ .name = "read-only",
+ .type = QEMU_OPT_BOOL,
+ .help = "open drive file as read-only",
+ },{
+ .name = "copy-on-read",
+ .type = QEMU_OPT_BOOL,
+ .help = "copy read data from backing file into image file",
+ },
+
+ { /* end of list */ }
+ },
+};
+
DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
{
const char *value;
+ DriveInfo *dinfo = NULL;
+ QDict *bs_opts;
+ QemuOpts *legacy_opts;
+ DriveMediaType media = MEDIA_DISK;
+ BlockInterfaceType type;
+ int cyls, heads, secs, translation;
+ int max_devs, bus_id, unit_id, index;
+ const char *devaddr;
+ bool read_only, copy_on_read;
+ Error *local_err = NULL;
/* Change legacy command line options into QMP ones */
qemu_opt_rename(all_opts, "iops", "throttling.iops-total");
@@ -798,7 +675,232 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
qemu_opt_unset(all_opts, "cache");
}
- return blockdev_init(all_opts, block_default_type);
+ /* Get a QDict for processing the options */
+ bs_opts = qdict_new();
+ qemu_opts_to_qdict(all_opts, bs_opts);
+
+ legacy_opts = qemu_opts_create_nofail(&qemu_legacy_drive_opts);
+ qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ goto fail;
+ }
+
+ /* Deprecated option boot=[on|off] */
+ if (qemu_opt_get(legacy_opts, "boot") != NULL) {
+ fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
+ "ignored. Future versions will reject this parameter. Please "
+ "update your scripts.\n");
+ }
+
+ /* Media type */
+ value = qemu_opt_get(legacy_opts, "media");
+ if (value) {
+ if (!strcmp(value, "disk")) {
+ media = MEDIA_DISK;
+ } else if (!strcmp(value, "cdrom")) {
+ media = MEDIA_CDROM;
+ qdict_put(bs_opts, "read-only", qstring_from_str("on"));
+ } else {
+ error_report("'%s' invalid media", value);
+ goto fail;
+ }
+ }
+
+ /* copy-on-read is disabled with a warning for read-only devices */
+ read_only = qemu_opt_get_bool(legacy_opts, "read-only", false);
+ copy_on_read = qemu_opt_get_bool(legacy_opts, "copy-on-read", false);
+
+ if (read_only && copy_on_read) {
+ error_report("warning: disabling copy-on-read on read-only drive");
+ copy_on_read = false;
+ }
+
+ qdict_put(bs_opts, "read-only",
+ qstring_from_str(read_only ? "on" : "off"));
+ qdict_put(bs_opts, "copy-on-read",
+ qstring_from_str(copy_on_read ? "on" :"off"));
+
+ /* Controller type */
+ value = qemu_opt_get(legacy_opts, "if");
+ if (value) {
+ for (type = 0;
+ type < IF_COUNT && strcmp(value, if_name[type]);
+ type++) {
+ }
+ if (type == IF_COUNT) {
+ error_report("unsupported bus type '%s'", value);
+ goto fail;
+ }
+ } else {
+ type = block_default_type;
+ }
+
+ /* Geometry */
+ cyls = qemu_opt_get_number(legacy_opts, "cyls", 0);
+ heads = qemu_opt_get_number(legacy_opts, "heads", 0);
+ secs = qemu_opt_get_number(legacy_opts, "secs", 0);
+
+ if (cyls || heads || secs) {
+ if (cyls < 1) {
+ error_report("invalid physical cyls number");
+ goto fail;
+ }
+ if (heads < 1) {
+ error_report("invalid physical heads number");
+ goto fail;
+ }
+ if (secs < 1) {
+ error_report("invalid physical secs number");
+ goto fail;
+ }
+ }
+
+ translation = BIOS_ATA_TRANSLATION_AUTO;
+ value = qemu_opt_get(legacy_opts, "trans");
+ if (value != NULL) {
+ if (!cyls) {
+ error_report("'%s' trans must be used with cyls, heads and secs",
+ value);
+ goto fail;
+ }
+ if (!strcmp(value, "none")) {
+ translation = BIOS_ATA_TRANSLATION_NONE;
+ } else if (!strcmp(value, "lba")) {
+ translation = BIOS_ATA_TRANSLATION_LBA;
+ } else if (!strcmp(value, "auto")) {
+ translation = BIOS_ATA_TRANSLATION_AUTO;
+ } else {
+ error_report("'%s' invalid translation type", value);
+ goto fail;
+ }
+ }
+
+ if (media == MEDIA_CDROM) {
+ if (cyls || secs || heads) {
+ error_report("CHS can't be set with media=cdrom");
+ goto fail;
+ }
+ }
+
+ /* Device address specified by bus/unit or index.
+ * If none was specified, try to find the first free one. */
+ bus_id = qemu_opt_get_number(legacy_opts, "bus", 0);
+ unit_id = qemu_opt_get_number(legacy_opts, "unit", -1);
+ index = qemu_opt_get_number(legacy_opts, "index", -1);
+
+ max_devs = if_max_devs[type];
+
+ if (index != -1) {
+ if (bus_id != 0 || unit_id != -1) {
+ error_report("index cannot be used with bus and unit");
+ goto fail;
+ }
+ bus_id = drive_index_to_bus_id(type, index);
+ unit_id = drive_index_to_unit_id(type, index);
+ }
+
+ if (unit_id == -1) {
+ unit_id = 0;
+ while (drive_get(type, bus_id, unit_id) != NULL) {
+ unit_id++;
+ if (max_devs && unit_id >= max_devs) {
+ unit_id -= max_devs;
+ bus_id++;
+ }
+ }
+ }
+
+ if (max_devs && unit_id >= max_devs) {
+ error_report("unit %d too big (max is %d)", unit_id, max_devs - 1);
+ goto fail;
+ }
+
+ if (drive_get(type, bus_id, unit_id) != NULL) {
+ error_report("drive with bus=%d, unit=%d (index=%d) exists",
+ bus_id, unit_id, index);
+ goto fail;
+ }
+
+ /* no id supplied -> create one */
+ if (qemu_opts_id(all_opts) == NULL) {
+ char *new_id;
+ const char *mediastr = "";
+ if (type == IF_IDE || type == IF_SCSI) {
+ mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
+ }
+ if (max_devs) {
+ new_id = g_strdup_printf("%s%i%s%i", if_name[type], bus_id,
+ mediastr, unit_id);
+ } else {
+ new_id = g_strdup_printf("%s%s%i", if_name[type],
+ mediastr, unit_id);
+ }
+ qdict_put(bs_opts, "id", qstring_from_str(new_id));
+ g_free(new_id);
+ }
+
+ /* Add virtio block device */
+ devaddr = qemu_opt_get(legacy_opts, "addr");
+ if (devaddr && type != IF_VIRTIO) {
+ error_report("addr is not supported by this bus type");
+ goto fail;
+ }
+
+ if (type == IF_VIRTIO) {
+ QemuOpts *devopts;
+ devopts = qemu_opts_create_nofail(qemu_find_opts("device"));
+ if (arch_type == QEMU_ARCH_S390X) {
+ qemu_opt_set(devopts, "driver", "virtio-blk-s390");
+ } else {
+ qemu_opt_set(devopts, "driver", "virtio-blk-pci");
+ }
+ qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"));
+ if (devaddr) {
+ qemu_opt_set(devopts, "addr", devaddr);
+ }
+ }
+
+ /* Actual block device init: Functionality shared with blockdev-add */
+ dinfo = blockdev_init(bs_opts, type, &local_err);
+ if (dinfo == NULL) {
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ }
+ goto fail;
+ } else {
+ assert(!error_is_set(&local_err));
+ }
+
+ /* Set legacy DriveInfo fields */
+ dinfo->enable_auto_del = true;
+ dinfo->opts = all_opts;
+
+ dinfo->cyls = cyls;
+ dinfo->heads = heads;
+ dinfo->secs = secs;
+ dinfo->trans = translation;
+
+ dinfo->bus = bus_id;
+ dinfo->unit = unit_id;
+ dinfo->devaddr = devaddr;
+
+ switch(type) {
+ case IF_IDE:
+ case IF_SCSI:
+ case IF_XEN:
+ case IF_NONE:
+ dinfo->media_cd = media == MEDIA_CDROM;
+ break;
+ default:
+ break;
+ }
+
+fail:
+ qemu_opts_del(legacy_opts);
+ return dinfo;
}
void do_commit(Monitor *mon, const QDict *qdict)
@@ -1131,6 +1233,11 @@ static void external_snapshot_prepare(BlkTransactionState *common,
}
}
+ if (bdrv_check_ext_snapshot(state->old_bs) != EXT_SNAPSHOT_ALLOWED) {
+ error_set(errp, QERR_FEATURE_DISABLED, "snapshot");
+ return;
+ }
+
flags = state->old_bs->open_flags;
/* create new image w/backing file */
@@ -1926,7 +2033,6 @@ void qmp_drive_mirror(const char *device, const char *target,
} else {
switch (mode) {
case NEW_IMAGE_MODE_EXISTING:
- ret = 0;
break;
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
/* create new image with backing file */
@@ -2051,6 +2157,54 @@ void qmp_block_job_complete(const char *device, Error **errp)
block_job_complete(job, errp);
}
+void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
+{
+ QmpOutputVisitor *ov = qmp_output_visitor_new();
+ QObject *obj;
+ QDict *qdict;
+ Error *local_err = NULL;
+
+ /* Require an ID in the top level */
+ if (!options->has_id) {
+ error_setg(errp, "Block device needs an ID");
+ goto fail;
+ }
+
+ /* TODO Sort it out in raw-posix and drive_init: Reject aio=native with
+ * cache.direct=false instead of silently switching to aio=threads, except
+ * if called from drive_init.
+ *
+ * For now, simply forbidding the combination for all drivers will do. */
+ if (options->has_aio && options->aio == BLOCKDEV_AIO_OPTIONS_NATIVE) {
+ bool direct = options->cache->has_direct && options->cache->direct;
+ if (!options->has_cache && !direct) {
+ error_setg(errp, "aio=native requires cache.direct=true");
+ goto fail;
+ }
+ }
+
+ visit_type_BlockdevOptions(qmp_output_get_visitor(ov),
+ &options, NULL, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ obj = qmp_output_get_qobject(ov);
+ qdict = qobject_to_qdict(obj);
+
+ qdict_flatten(qdict);
+
+ blockdev_init(qdict, IF_NONE, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+fail:
+ qmp_output_visitor_cleanup(ov);
+}
+
static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
{
BlockJobInfoList **prev = opaque;
@@ -2078,42 +2232,6 @@ QemuOptsList qemu_common_drive_opts = {
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
.desc = {
{
- .name = "bus",
- .type = QEMU_OPT_NUMBER,
- .help = "bus number",
- },{
- .name = "unit",
- .type = QEMU_OPT_NUMBER,
- .help = "unit number (i.e. lun for scsi)",
- },{
- .name = "if",
- .type = QEMU_OPT_STRING,
- .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
- },{
- .name = "index",
- .type = QEMU_OPT_NUMBER,
- .help = "index number",
- },{
- .name = "cyls",
- .type = QEMU_OPT_NUMBER,
- .help = "number of cylinders (ide disk geometry)",
- },{
- .name = "heads",
- .type = QEMU_OPT_NUMBER,
- .help = "number of heads (ide disk geometry)",
- },{
- .name = "secs",
- .type = QEMU_OPT_NUMBER,
- .help = "number of sectors (ide disk geometry)",
- },{
- .name = "trans",
- .type = QEMU_OPT_STRING,
- .help = "chs translation (auto, lba. none)",
- },{
- .name = "media",
- .type = QEMU_OPT_STRING,
- .help = "media type (disk, cdrom)",
- },{
.name = "snapshot",
.type = QEMU_OPT_BOOL,
.help = "enable/disable snapshot mode",
@@ -2158,10 +2276,6 @@ QemuOptsList qemu_common_drive_opts = {
.type = QEMU_OPT_STRING,
.help = "write error action",
},{
- .name = "addr",
- .type = QEMU_OPT_STRING,
- .help = "pci address (virtio only)",
- },{
.name = "read-only",
.type = QEMU_OPT_BOOL,
.help = "open drive file as read-only",
@@ -2221,10 +2335,6 @@ QemuOptsList qemu_common_drive_opts = {
.name = "copy-on-read",
.type = QEMU_OPT_BOOL,
.help = "copy read data from backing file into image file",
- },{
- .name = "boot",
- .type = QEMU_OPT_BOOL,
- .help = "(deprecated, ignored)",
},
{ /* end of list */ }
},
diff --git a/blockjob.c b/blockjob.c
index e7d49b7169..9e5fd5c162 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -35,7 +35,7 @@
#include "qmp-commands.h"
#include "qemu/timer.h"
-void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
+void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
int64_t speed, BlockDriverCompletionFunc *cb,
void *opaque, Error **errp)
{
@@ -48,8 +48,8 @@ void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
bdrv_ref(bs);
bdrv_set_in_use(bs, 1);
- job = g_malloc0(job_type->instance_size);
- job->job_type = job_type;
+ job = g_malloc0(driver->instance_size);
+ job->driver = driver;
job->bs = bs;
job->cb = cb;
job->opaque = opaque;
@@ -87,11 +87,11 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
Error *local_err = NULL;
- if (!job->job_type->set_speed) {
+ if (!job->driver->set_speed) {
error_set(errp, QERR_NOT_SUPPORTED);
return;
}
- job->job_type->set_speed(job, speed, &local_err);
+ job->driver->set_speed(job, speed, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
return;
@@ -102,12 +102,12 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
void block_job_complete(BlockJob *job, Error **errp)
{
- if (job->paused || job->cancelled || !job->job_type->complete) {
+ if (job->paused || job->cancelled || !job->driver->complete) {
error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
return;
}
- job->job_type->complete(job, errp);
+ job->driver->complete(job, errp);
}
void block_job_pause(BlockJob *job)
@@ -143,8 +143,8 @@ bool block_job_is_cancelled(BlockJob *job)
void block_job_iostatus_reset(BlockJob *job)
{
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
- if (job->job_type->iostatus_reset) {
- job->job_type->iostatus_reset(job);
+ if (job->driver->iostatus_reset) {
+ job->driver->iostatus_reset(job);
}
}
@@ -209,7 +209,7 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
BlockJobInfo *block_job_query(BlockJob *job)
{
BlockJobInfo *info = g_new0(BlockJobInfo, 1);
- info->type = g_strdup(job->job_type->job_type);
+ info->type = g_strdup(BlockJobType_lookup[job->driver->job_type]);
info->device = g_strdup(bdrv_get_device_name(job->bs));
info->len = job->len;
info->busy = job->busy;
@@ -236,7 +236,7 @@ QObject *qobject_from_block_job(BlockJob *job)
"'len': %" PRId64 ","
"'offset': %" PRId64 ","
"'speed': %" PRId64 " }",
- job->job_type->job_type,
+ BlockJobType_lookup[job->driver->job_type],
bdrv_get_device_name(job->bs),
job->len,
job->offset,
diff --git a/configure b/configure
index 23dbaaffcc..57ee62a696 100755
--- a/configure
+++ b/configure
@@ -429,9 +429,6 @@ case "$cpu" in
aarch64)
cpu="aarch64"
;;
- hppa|parisc|parisc64)
- cpu="hppa"
- ;;
mips*)
cpu="mips"
;;
@@ -3794,14 +3791,6 @@ echo "libs_softmmu=$libs_softmmu" >> $config_host_mak
echo "ARCH=$ARCH" >> $config_host_mak
-case "$cpu" in
- aarch64 | arm | i386 | x86_64 | x32 | ppc*)
- # The TCG interpreter currently does not support ld/st optimization.
- if test "$tcg_interpreter" = "no" ; then
- echo "CONFIG_QEMU_LDST_OPTIMIZATION=y" >> $config_host_mak
- fi
- ;;
-esac
if test "$debug_tcg" = "yes" ; then
echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak
fi
diff --git a/cpu-exec.c b/cpu-exec.c
index 5a4399509e..30cfa2a63a 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -681,6 +681,10 @@ int cpu_exec(CPUArchState *env)
* local variables as longjmp is marked 'noreturn'. */
cpu = current_cpu;
env = cpu->env_ptr;
+#if !(defined(CONFIG_USER_ONLY) && \
+ (defined(TARGET_M68K) || defined(TARGET_PPC) || defined(TARGET_S390X)))
+ cc = CPU_GET_CLASS(cpu);
+#endif
}
} /* for(;;) */
diff --git a/cputlb.c b/cputlb.c
index 19ecf60983..fff0afbd4a 100644
--- a/cputlb.c
+++ b/cputlb.c
@@ -169,21 +169,6 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr)
return ram_addr;
}
-static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry)
-{
- ram_addr_t ram_addr;
- void *p;
-
- if (tlb_is_dirty_ram(tlb_entry)) {
- p = (void *)(uintptr_t)((tlb_entry->addr_write & TARGET_PAGE_MASK)
- + tlb_entry->addend);
- ram_addr = qemu_ram_addr_from_host_nofail(p);
- if (!cpu_physical_memory_is_dirty(ram_addr)) {
- tlb_entry->addr_write |= TLB_NOTDIRTY;
- }
- }
-}
-
void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length)
{
CPUState *cpu;
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 0ce045c0b3..91f44d01b9 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -53,6 +53,23 @@ The use of '*' as a prefix to the name means the member is optional. Optional
members should always be added to the end of the dictionary to preserve
backwards compatibility.
+
+A complex type definition can specify another complex type as its base.
+In this case, the fields of the base type are included as top-level fields
+of the new complex type's dictionary in the QMP wire format. An example
+definition is:
+
+ { 'type': 'BlockdevOptionsGenericFormat', 'data': { 'file': 'str' } }
+ { 'type': 'BlockdevOptionsGenericCOWFormat',
+ 'base': 'BlockdevOptionsGenericFormat',
+ 'data': { '*backing': 'str' } }
+
+An example BlockdevOptionsGenericCOWFormat object on the wire could use
+both fields like this:
+
+ { "file": "/some/place/my-image",
+ "backing": "/some/place/my-backing-file" }
+
=== Enumeration types ===
An enumeration type is a dictionary containing a single key whose value is a
diff --git a/docs/qmp/README b/docs/qmp/README
index 85c4bc17bf..f6a3a031e9 100644
--- a/docs/qmp/README
+++ b/docs/qmp/README
@@ -84,4 +84,4 @@ Please, refer to the qapi-schema.json file for a complete command reference.
QMP wiki page
-------------
-http://wiki.qemu.org/QMP
+http://wiki.qemu-project.org/QMP
diff --git a/docs/rdma.txt b/docs/rdma.txt
index 8d1e003f92..2aca63bd72 100644
--- a/docs/rdma.txt
+++ b/docs/rdma.txt
@@ -1,7 +1,7 @@
(RDMA: Remote Direct Memory Access)
RDMA Live Migration Specification, Version # 1
==============================================
-Wiki: http://wiki.qemu.org/Features/RDMALiveMigration
+Wiki: http://wiki.qemu-project.org/Features/RDMALiveMigration
Github: git@github.com:hinesmr/qemu.git, 'rdma' branch
Copyright (C) 2013 Michael R. Hines <mrhines@us.ibm.com>
diff --git a/docs/specs/qcow2.txt b/docs/specs/qcow2.txt
index 33eca360cc..f19536a46f 100644
--- a/docs/specs/qcow2.txt
+++ b/docs/specs/qcow2.txt
@@ -355,3 +355,6 @@ Snapshot table entry:
variable: Unique ID string for the snapshot (not null terminated)
variable: Name of the snapshot (not null terminated)
+
+ variable: Padding to round up the snapshot table entry size to the
+ next multiple of 8.
diff --git a/exec.c b/exec.c
index 26681ce021..bea2cffd94 100644
--- a/exec.c
+++ b/exec.c
@@ -625,38 +625,6 @@ void cpu_abort(CPUArchState *env, const char *fmt, ...)
abort();
}
-CPUArchState *cpu_copy(CPUArchState *env)
-{
- CPUArchState *new_env = cpu_init(env->cpu_model_str);
-#if defined(TARGET_HAS_ICE)
- CPUBreakpoint *bp;
- CPUWatchpoint *wp;
-#endif
-
- /* Reset non arch specific state */
- cpu_reset(ENV_GET_CPU(new_env));
-
- /* Copy arch specific state into the new CPU */
- memcpy(new_env, env, sizeof(CPUArchState));
-
- /* Clone all break/watchpoints.
- Note: Once we support ptrace with hw-debug register access, make sure
- BP_CPU break/watchpoints are handled correctly on clone. */
- QTAILQ_INIT(&env->breakpoints);
- QTAILQ_INIT(&env->watchpoints);
-#if defined(TARGET_HAS_ICE)
- QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
- cpu_breakpoint_insert(new_env, bp->pc, bp->flags, NULL);
- }
- QTAILQ_FOREACH(wp, &env->watchpoints, entry) {
- cpu_watchpoint_insert(new_env, wp->vaddr, (~wp->len_mask) + 1,
- wp->flags, NULL);
- }
-#endif
-
- return new_env;
-}
-
#if !defined(CONFIG_USER_ONLY)
static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t end,
uintptr_t length)
@@ -749,14 +717,14 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
uint16_t section);
static subpage_t *subpage_init(AddressSpace *as, hwaddr base);
-static void *(*phys_mem_alloc)(ram_addr_t size) = qemu_anon_ram_alloc;
+static void *(*phys_mem_alloc)(size_t size) = qemu_anon_ram_alloc;
/*
* Set a custom physical guest memory alloator.
* Accelerators with unusual needs may need this. Hopefully, we can
* get rid of it eventually.
*/
-void phys_mem_set_alloc(void *(*alloc)(ram_addr_t))
+void phys_mem_set_alloc(void *(*alloc)(size_t))
{
phys_mem_alloc = alloc;
}
@@ -1573,7 +1541,7 @@ static uint64_t subpage_read(void *opaque, hwaddr addr,
uint8_t buf[4];
#if defined(DEBUG_SUBPAGE)
- printf("%s: subpage %p len %d addr " TARGET_FMT_plx "\n", __func__,
+ printf("%s: subpage %p len %u addr " TARGET_FMT_plx "\n", __func__,
subpage, len, addr);
#endif
address_space_read(subpage->as, addr + subpage->base, buf, len);
@@ -1596,7 +1564,7 @@ static void subpage_write(void *opaque, hwaddr addr,
uint8_t buf[4];
#if defined(DEBUG_SUBPAGE)
- printf("%s: subpage %p len %d addr " TARGET_FMT_plx
+ printf("%s: subpage %p len %u addr " TARGET_FMT_plx
" value %"PRIx64"\n",
__func__, subpage, len, addr, value);
#endif
@@ -1617,16 +1585,16 @@ static void subpage_write(void *opaque, hwaddr addr,
}
static bool subpage_accepts(void *opaque, hwaddr addr,
- unsigned size, bool is_write)
+ unsigned len, bool is_write)
{
subpage_t *subpage = opaque;
#if defined(DEBUG_SUBPAGE)
- printf("%s: subpage %p %c len %d addr " TARGET_FMT_plx "\n",
+ printf("%s: subpage %p %c len %u addr " TARGET_FMT_plx "\n",
__func__, subpage, is_write ? 'w' : 'r', len, addr);
#endif
return address_space_access_valid(subpage->as, addr + subpage->base,
- size, is_write);
+ len, is_write);
}
static const MemoryRegionOps subpage_ops = {
@@ -1646,8 +1614,8 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
idx = SUBPAGE_IDX(start);
eidx = SUBPAGE_IDX(end);
#if defined(DEBUG_SUBPAGE)
- printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %ld\n", __func__,
- mmio, start, end, idx, eidx, memory);
+ printf("%s: %p start %08x end %08x idx %08x eidx %08x section %d\n",
+ __func__, mmio, start, end, idx, eidx, section);
#endif
for (; idx <= eidx; idx++) {
mmio->sub_section[idx] = section;
@@ -1668,8 +1636,8 @@ static subpage_t *subpage_init(AddressSpace *as, hwaddr base)
"subpage", TARGET_PAGE_SIZE);
mmio->iomem.subpage = true;
#if defined(DEBUG_SUBPAGE)
- printf("%s: %p base " TARGET_FMT_plx " len %08x %d\n", __func__,
- mmio, base, TARGET_PAGE_SIZE, subpage_memory);
+ printf("%s: %p base " TARGET_FMT_plx " len %08x\n", __func__,
+ mmio, base, TARGET_PAGE_SIZE);
#endif
subpage_register(mmio, 0, TARGET_PAGE_SIZE-1, PHYS_SECTION_UNASSIGNED);
diff --git a/gdbstub.c b/gdbstub.c
index 2b7f22b2d2..0e5a3f5bf9 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1553,7 +1553,7 @@ static void gdb_accept(void)
static int gdbserver_open(int port)
{
struct sockaddr_in sockaddr;
- int fd, val, ret;
+ int fd, ret;
fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
@@ -1564,9 +1564,7 @@ static int gdbserver_open(int port)
fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
- /* allow fast reuse */
- val = 1;
- qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ socket_set_fast_reuse(fd);
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
diff --git a/hw/9pfs/virtio-9p-xattr.c b/hw/9pfs/virtio-9p-xattr.c
index 90ae565c19..3fae557a84 100644
--- a/hw/9pfs/virtio-9p-xattr.c
+++ b/hw/9pfs/virtio-9p-xattr.c
@@ -36,7 +36,7 @@ ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
if (xops) {
return xops->getxattr(ctx, path, name, value, size);
}
- errno = -EOPNOTSUPP;
+ errno = EOPNOTSUPP;
return -1;
}
@@ -123,7 +123,7 @@ int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
if (xops) {
return xops->setxattr(ctx, path, name, value, size, flags);
}
- errno = -EOPNOTSUPP;
+ errno = EOPNOTSUPP;
return -1;
}
@@ -135,7 +135,7 @@ int v9fs_remove_xattr(FsContext *ctx,
if (xops) {
return xops->removexattr(ctx, path, name);
}
- errno = -EOPNOTSUPP;
+ errno = EOPNOTSUPP;
return -1;
}
diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c
index aac9a32e0c..59e1bb8388 100644
--- a/hw/alpha/typhoon.c
+++ b/hw/alpha/typhoon.c
@@ -700,7 +700,7 @@ static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr)
}
}
- if (addr >= 0x80000000000 && addr <= 0xfffffffffff) {
+ if (addr >= 0x80000000000ull && addr <= 0xfffffffffffull) {
/* Check the fourth window for DAC enable and window enable. */
if ((pchip->win[3].wba & 0x80000000001ull) == 0x80000000001ull) {
uint64_t pte_addr;
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 8c3b7f0d3b..02a15441fa 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -624,6 +624,11 @@ static int m25p80_init(SSISlave *ss)
if (dinfo && dinfo->bdrv) {
DB_PRINT_L(0, "Binding to IF_MTD drive\n");
s->bdrv = dinfo->bdrv;
+ if (bdrv_is_read_only(s->bdrv)) {
+ fprintf(stderr, "Can't use a read-only drive");
+ return 1;
+ }
+
/* FIXME: Move to late init */
if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size,
BDRV_SECTOR_SIZE))) {
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
index f35fc5944a..098f6c62c7 100644
--- a/hw/block/xen_disk.c
+++ b/hw/block/xen_disk.c
@@ -405,6 +405,7 @@ static int ioreq_map(struct ioreq *ioreq)
xen_be_printf(&ioreq->blkdev->xendev, 0,
"can't map grant ref %d (%s, %d maps)\n",
refs[i], strerror(errno), ioreq->blkdev->cnt_map);
+ ioreq->mapped = 1;
ioreq_unmap(ioreq);
return -1;
}
@@ -829,6 +830,11 @@ static int blk_connect(struct XenDevice *xendev)
/* setup via qemu cmdline -> already setup for us */
xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
blkdev->bs = blkdev->dinfo->bdrv;
+ if (bdrv_is_read_only(blkdev->bs) && !readonly) {
+ xen_be_printf(&blkdev->xendev, 0, "Unexpected read-only drive");
+ blkdev->bs = NULL;
+ return -1;
+ }
/* blkdev->bs is not create by us, we get a reference
* so we can bdrv_unref() unconditionally */
bdrv_ref(blkdev->bs);
diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c
index 6223a557b2..9328dd1b57 100644
--- a/hw/char/sh_serial.c
+++ b/hw/char/sh_serial.c
@@ -248,11 +248,9 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs,
s->flags &= ~SH_SERIAL_FLAG_RDF;
}
break;
-#if 0
case 0x18:
ret = s->fcr;
break;
-#endif
case 0x1c:
ret = s->rx_cnt;
break;
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index a71a4ca47c..a8be62cf99 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -1198,7 +1198,15 @@ void ahci_reset(AHCIState *s)
int i;
s->control_regs.irqstatus = 0;
- s->control_regs.ghc = 0;
+ /* AHCI Enable (AE)
+ * The implementation of this bit is dependent upon the value of the
+ * CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and
+ * shall have a reset value of '0'. If CAP.SAM is '1', then AE shall be
+ * read-only and shall have a reset value of '1'.
+ *
+ * We set HOST_CAP_AHCI so we must enable AHCI at reset.
+ */
+ s->control_regs.ghc = HOST_CTL_AHCI_EN;
for (i = 0; i < s->ports; i++) {
pr = &s->dev[i].port_regs;
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
index a1c08fb74e..a2d5283ff9 100644
--- a/hw/misc/vfio.c
+++ b/hw/misc/vfio.c
@@ -119,6 +119,7 @@ typedef struct VFIOINTx {
typedef struct VFIOMSIVector {
EventNotifier interrupt; /* eventfd triggered on interrupt */
struct VFIODevice *vdev; /* back pointer to device */
+ MSIMessage msg; /* cache the MSI message so we know when it changes */
int virq; /* KVM irqchip route for QEMU bypass */
bool use;
} VFIOMSIVector;
@@ -165,6 +166,7 @@ typedef struct VFIODevice {
off_t config_offset; /* Offset of config space region within device fd */
unsigned int rom_size;
off_t rom_offset; /* Offset of ROM region within device fd */
+ void *rom;
int msi_cap_size;
VFIOMSIVector *msi_vectors;
VFIOMSIXInfo *msix;
@@ -184,6 +186,9 @@ typedef struct VFIODevice {
bool reset_works;
bool has_vga;
bool pci_aer;
+ bool has_flr;
+ bool has_pm_reset;
+ bool needs_reset;
} VFIODevice;
typedef struct VFIOGroup {
@@ -795,7 +800,6 @@ retry:
vdev->msi_vectors = g_malloc0(vdev->nr_vectors * sizeof(VFIOMSIVector));
for (i = 0; i < vdev->nr_vectors; i++) {
- MSIMessage msg;
VFIOMSIVector *vector = &vdev->msi_vectors[i];
vector->vdev = vdev;
@@ -805,13 +809,13 @@ retry:
error_report("vfio: Error: event_notifier_init failed");
}
- msg = msi_get_message(&vdev->pdev, i);
+ vector->msg = msi_get_message(&vdev->pdev, i);
/*
* Attempt to enable route through KVM irqchip,
* default to userspace handling if unavailable.
*/
- vector->virq = kvm_irqchip_add_msi_route(kvm_state, msg);
+ vector->virq = kvm_irqchip_add_msi_route(kvm_state, vector->msg);
if (vector->virq < 0 ||
kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt,
NULL, vector->virq) < 0) {
@@ -917,6 +921,33 @@ static void vfio_disable_msi(VFIODevice *vdev)
vdev->host.bus, vdev->host.slot, vdev->host.function);
}
+static void vfio_update_msi(VFIODevice *vdev)
+{
+ int i;
+
+ for (i = 0; i < vdev->nr_vectors; i++) {
+ VFIOMSIVector *vector = &vdev->msi_vectors[i];
+ MSIMessage msg;
+
+ if (!vector->use || vector->virq < 0) {
+ continue;
+ }
+
+ msg = msi_get_message(&vdev->pdev, i);
+
+ if (msg.address != vector->msg.address ||
+ msg.data != vector->msg.data) {
+
+ DPRINTF("%s(%04x:%02x:%02x.%x) MSI vector %d changed\n",
+ __func__, vdev->host.domain, vdev->host.bus,
+ vdev->host.slot, vdev->host.function, i);
+
+ kvm_irqchip_update_msi_route(kvm_state, vector->virq, msg);
+ vector->msg = msg;
+ }
+ }
+}
+
/*
* IO Port/MMIO - Beware of the endians, VFIO is always little endian
*/
@@ -1029,6 +1060,131 @@ static const MemoryRegionOps vfio_bar_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
+static void vfio_pci_load_rom(VFIODevice *vdev)
+{
+ struct vfio_region_info reg_info = {
+ .argsz = sizeof(reg_info),
+ .index = VFIO_PCI_ROM_REGION_INDEX
+ };
+ uint64_t size;
+ off_t off = 0;
+ size_t bytes;
+
+ if (ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info)) {
+ error_report("vfio: Error getting ROM info: %m");
+ return;
+ }
+
+ DPRINTF("Device %04x:%02x:%02x.%x ROM:\n", vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+ DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n",
+ (unsigned long)reg_info.size, (unsigned long)reg_info.offset,
+ (unsigned long)reg_info.flags);
+
+ vdev->rom_size = size = reg_info.size;
+ vdev->rom_offset = reg_info.offset;
+
+ if (!vdev->rom_size) {
+ return;
+ }
+
+ vdev->rom = g_malloc(size);
+ memset(vdev->rom, 0xff, size);
+
+ while (size) {
+ bytes = pread(vdev->fd, vdev->rom + off, size, vdev->rom_offset + off);
+ if (bytes == 0) {
+ break;
+ } else if (bytes > 0) {
+ off += bytes;
+ size -= bytes;
+ } else {
+ if (errno == EINTR || errno == EAGAIN) {
+ continue;
+ }
+ error_report("vfio: Error reading device ROM: %m");
+ break;
+ }
+ }
+}
+
+static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size)
+{
+ VFIODevice *vdev = opaque;
+ uint64_t val = ((uint64_t)1 << (size * 8)) - 1;
+
+ /* Load the ROM lazily when the guest tries to read it */
+ if (unlikely(!vdev->rom)) {
+ vfio_pci_load_rom(vdev);
+ }
+
+ memcpy(&val, vdev->rom + addr,
+ (addr < vdev->rom_size) ? MIN(size, vdev->rom_size - addr) : 0);
+
+ DPRINTF("%s(%04x:%02x:%02x.%x, 0x%"HWADDR_PRIx", 0x%x) = 0x%"PRIx64"\n",
+ __func__, vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, addr, size, val);
+
+ return val;
+}
+
+static void vfio_rom_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+}
+
+static const MemoryRegionOps vfio_rom_ops = {
+ .read = vfio_rom_read,
+ .write = vfio_rom_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_pci_size_rom(VFIODevice *vdev)
+{
+ uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK);
+ off_t offset = vdev->config_offset + PCI_ROM_ADDRESS;
+ char name[32];
+
+ if (vdev->pdev.romfile || !vdev->pdev.rom_bar) {
+ return;
+ }
+
+ /*
+ * Use the same size ROM BAR as the physical device. The contents
+ * will get filled in later when the guest tries to read it.
+ */
+ if (pread(vdev->fd, &orig, 4, offset) != 4 ||
+ pwrite(vdev->fd, &size, 4, offset) != 4 ||
+ pread(vdev->fd, &size, 4, offset) != 4 ||
+ pwrite(vdev->fd, &orig, 4, offset) != 4) {
+ error_report("%s(%04x:%02x:%02x.%x) failed: %m",
+ __func__, vdev->host.domain, vdev->host.bus,
+ vdev->host.slot, vdev->host.function);
+ return;
+ }
+
+ size = ~(le32_to_cpu(size) & PCI_ROM_ADDRESS_MASK) + 1;
+
+ if (!size) {
+ return;
+ }
+
+ DPRINTF("%04x:%02x:%02x.%x ROM size 0x%x\n", vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function, size);
+
+ snprintf(name, sizeof(name), "vfio[%04x:%02x:%02x.%x].rom",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
+
+ memory_region_init_io(&vdev->pdev.rom, OBJECT(vdev),
+ &vfio_rom_ops, vdev, name, size);
+
+ pci_register_bar(&vdev->pdev, PCI_ROM_SLOT,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, &vdev->pdev.rom);
+
+ vdev->pdev.has_rom = true;
+}
+
static void vfio_vga_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size)
{
@@ -1834,10 +1990,16 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
is_enabled = msi_enabled(pdev);
- if (!was_enabled && is_enabled) {
- vfio_enable_msi(vdev);
- } else if (was_enabled && !is_enabled) {
- vfio_disable_msi(vdev);
+ if (!was_enabled) {
+ if (is_enabled) {
+ vfio_enable_msi(vdev);
+ }
+ } else {
+ if (!is_enabled) {
+ vfio_disable_msi(vdev);
+ } else {
+ vfio_update_msi(vdev);
+ }
}
} else if (pdev->cap_present & QEMU_PCI_CAP_MSIX &&
ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) {
@@ -1928,7 +2090,8 @@ static void vfio_listener_region_add(MemoryListener *listener,
if (vfio_listener_skipped_section(section)) {
DPRINTF("SKIPPING region_add %"HWADDR_PRIx" - %"PRIx64"\n",
section->offset_within_address_space,
- section->offset_within_address_space + section->size - 1);
+ section->offset_within_address_space +
+ int128_get64(int128_sub(section->size, int128_one())));
return;
}
@@ -1973,7 +2136,8 @@ static void vfio_listener_region_del(MemoryListener *listener,
if (vfio_listener_skipped_section(section)) {
DPRINTF("SKIPPING region_del %"HWADDR_PRIx" - %"PRIx64"\n",
section->offset_within_address_space,
- section->offset_within_address_space + section->size - 1);
+ section->offset_within_address_space +
+ int128_get64(int128_sub(section->size, int128_one())));
return;
}
@@ -2480,6 +2644,42 @@ static int vfio_setup_pcie_cap(VFIODevice *vdev, int pos, uint8_t size)
return pos;
}
+static void vfio_check_pcie_flr(VFIODevice *vdev, uint8_t pos)
+{
+ uint32_t cap = pci_get_long(vdev->pdev.config + pos + PCI_EXP_DEVCAP);
+
+ if (cap & PCI_EXP_DEVCAP_FLR) {
+ DPRINTF("%04x:%02x:%02x.%x Supports FLR via PCIe cap\n",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
+ vdev->has_flr = true;
+ }
+}
+
+static void vfio_check_pm_reset(VFIODevice *vdev, uint8_t pos)
+{
+ uint16_t csr = pci_get_word(vdev->pdev.config + pos + PCI_PM_CTRL);
+
+ if (!(csr & PCI_PM_CTRL_NO_SOFT_RESET)) {
+ DPRINTF("%04x:%02x:%02x.%x Supports PM reset\n",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
+ vdev->has_pm_reset = true;
+ }
+}
+
+static void vfio_check_af_flr(VFIODevice *vdev, uint8_t pos)
+{
+ uint8_t cap = pci_get_byte(vdev->pdev.config + pos + PCI_AF_CAP);
+
+ if ((cap & PCI_AF_CAP_TP) && (cap & PCI_AF_CAP_FLR)) {
+ DPRINTF("%04x:%02x:%02x.%x Supports FLR via AF cap\n",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
+ vdev->has_flr = true;
+ }
+}
+
static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos)
{
PCIDevice *pdev = &vdev->pdev;
@@ -2524,13 +2724,21 @@ static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos)
ret = vfio_setup_msi(vdev, pos);
break;
case PCI_CAP_ID_EXP:
+ vfio_check_pcie_flr(vdev, pos);
ret = vfio_setup_pcie_cap(vdev, pos, size);
break;
case PCI_CAP_ID_MSIX:
ret = vfio_setup_msix(vdev, pos);
break;
case PCI_CAP_ID_PM:
+ vfio_check_pm_reset(vdev, pos);
vdev->pm_cap = pos;
+ ret = pci_add_capability(pdev, cap_id, pos, size);
+ break;
+ case PCI_CAP_ID_AF:
+ vfio_check_af_flr(vdev, pos);
+ ret = pci_add_capability(pdev, cap_id, pos, size);
+ break;
default:
ret = pci_add_capability(pdev, cap_id, pos, size);
break;
@@ -2559,49 +2767,277 @@ static int vfio_add_capabilities(VFIODevice *vdev)
return vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]);
}
-static int vfio_load_rom(VFIODevice *vdev)
+static void vfio_pci_pre_reset(VFIODevice *vdev)
{
- uint64_t size = vdev->rom_size;
- char name[32];
- off_t off = 0, voff = vdev->rom_offset;
- ssize_t bytes;
- void *ptr;
+ PCIDevice *pdev = &vdev->pdev;
+ uint16_t cmd;
- /* If loading ROM from file, pci handles it */
- if (vdev->pdev.romfile || !vdev->pdev.rom_bar || !size) {
- return 0;
+ vfio_disable_interrupts(vdev);
+
+ /* Make sure the device is in D0 */
+ if (vdev->pm_cap) {
+ uint16_t pmcsr;
+ uint8_t state;
+
+ pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2);
+ state = pmcsr & PCI_PM_CTRL_STATE_MASK;
+ if (state) {
+ pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+ vfio_pci_write_config(pdev, vdev->pm_cap + PCI_PM_CTRL, pmcsr, 2);
+ /* vfio handles the necessary delay here */
+ pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2);
+ state = pmcsr & PCI_PM_CTRL_STATE_MASK;
+ if (state) {
+ error_report("vfio: Unable to power on device, stuck in D%d\n",
+ state);
+ }
+ }
}
- DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
- vdev->host.bus, vdev->host.slot, vdev->host.function);
+ /*
+ * Stop any ongoing DMA by disconecting I/O, MMIO, and bus master.
+ * Also put INTx Disable in known state.
+ */
+ cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2);
+ cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+ PCI_COMMAND_INTX_DISABLE);
+ vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2);
+}
- snprintf(name, sizeof(name), "vfio[%04x:%02x:%02x.%x].rom",
- vdev->host.domain, vdev->host.bus, vdev->host.slot,
- vdev->host.function);
- memory_region_init_ram(&vdev->pdev.rom, OBJECT(vdev), name, size);
- ptr = memory_region_get_ram_ptr(&vdev->pdev.rom);
- memset(ptr, 0xff, size);
+static void vfio_pci_post_reset(VFIODevice *vdev)
+{
+ vfio_enable_intx(vdev);
+}
- while (size) {
- bytes = pread(vdev->fd, ptr + off, size, voff + off);
- if (bytes == 0) {
- break; /* expect that we could get back less than the ROM BAR */
- } else if (bytes > 0) {
- off += bytes;
- size -= bytes;
- } else {
- if (errno == EINTR || errno == EAGAIN) {
- continue;
+static bool vfio_pci_host_match(PCIHostDeviceAddress *host1,
+ PCIHostDeviceAddress *host2)
+{
+ return (host1->domain == host2->domain && host1->bus == host2->bus &&
+ host1->slot == host2->slot && host1->function == host2->function);
+}
+
+static int vfio_pci_hot_reset(VFIODevice *vdev, bool single)
+{
+ VFIOGroup *group;
+ struct vfio_pci_hot_reset_info *info;
+ struct vfio_pci_dependent_device *devices;
+ struct vfio_pci_hot_reset *reset;
+ int32_t *fds;
+ int ret, i, count;
+ bool multi = false;
+
+ DPRINTF("%s(%04x:%02x:%02x.%x) %s\n", __func__, vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function,
+ single ? "one" : "multi");
+
+ vfio_pci_pre_reset(vdev);
+ vdev->needs_reset = false;
+
+ info = g_malloc0(sizeof(*info));
+ info->argsz = sizeof(*info);
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, info);
+ if (ret && errno != ENOSPC) {
+ ret = -errno;
+ if (!vdev->has_pm_reset) {
+ error_report("vfio: Cannot reset device %04x:%02x:%02x.%x, "
+ "no available reset mechanism.", vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+ }
+ goto out_single;
+ }
+
+ count = info->count;
+ info = g_realloc(info, sizeof(*info) + (count * sizeof(*devices)));
+ info->argsz = sizeof(*info) + (count * sizeof(*devices));
+ devices = &info->devices[0];
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, info);
+ if (ret) {
+ ret = -errno;
+ error_report("vfio: hot reset info failed: %m");
+ goto out_single;
+ }
+
+ DPRINTF("%04x:%02x:%02x.%x: hot reset dependent devices:\n",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
+
+ /* Verify that we have all the groups required */
+ for (i = 0; i < info->count; i++) {
+ PCIHostDeviceAddress host;
+ VFIODevice *tmp;
+
+ host.domain = devices[i].segment;
+ host.bus = devices[i].bus;
+ host.slot = PCI_SLOT(devices[i].devfn);
+ host.function = PCI_FUNC(devices[i].devfn);
+
+ DPRINTF("\t%04x:%02x:%02x.%x group %d\n", host.domain,
+ host.bus, host.slot, host.function, devices[i].group_id);
+
+ if (vfio_pci_host_match(&host, &vdev->host)) {
+ continue;
+ }
+
+ QLIST_FOREACH(group, &group_list, next) {
+ if (group->groupid == devices[i].group_id) {
+ break;
+ }
+ }
+
+ if (!group) {
+ if (!vdev->has_pm_reset) {
+ error_report("vfio: Cannot reset device %04x:%02x:%02x.%x, "
+ "depends on group %d which is not owned.",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, devices[i].group_id);
+ }
+ ret = -EPERM;
+ goto out;
+ }
+
+ /* Prep dependent devices for reset and clear our marker. */
+ QLIST_FOREACH(tmp, &group->device_list, next) {
+ if (vfio_pci_host_match(&host, &tmp->host)) {
+ if (single) {
+ DPRINTF("vfio: found another in-use device "
+ "%04x:%02x:%02x.%x\n", host.domain, host.bus,
+ host.slot, host.function);
+ ret = -EINVAL;
+ goto out_single;
+ }
+ vfio_pci_pre_reset(tmp);
+ tmp->needs_reset = false;
+ multi = true;
+ break;
}
- error_report("vfio: Error reading device ROM: %m");
- memory_region_destroy(&vdev->pdev.rom);
- return -errno;
}
}
- pci_register_bar(&vdev->pdev, PCI_ROM_SLOT, 0, &vdev->pdev.rom);
- vdev->pdev.has_rom = true;
- return 0;
+ if (!single && !multi) {
+ DPRINTF("vfio: No other in-use devices for multi hot reset\n");
+ ret = -EINVAL;
+ goto out_single;
+ }
+
+ /* Determine how many group fds need to be passed */
+ count = 0;
+ QLIST_FOREACH(group, &group_list, next) {
+ for (i = 0; i < info->count; i++) {
+ if (group->groupid == devices[i].group_id) {
+ count++;
+ break;
+ }
+ }
+ }
+
+ reset = g_malloc0(sizeof(*reset) + (count * sizeof(*fds)));
+ reset->argsz = sizeof(*reset) + (count * sizeof(*fds));
+ fds = &reset->group_fds[0];
+
+ /* Fill in group fds */
+ QLIST_FOREACH(group, &group_list, next) {
+ for (i = 0; i < info->count; i++) {
+ if (group->groupid == devices[i].group_id) {
+ fds[reset->count++] = group->fd;
+ break;
+ }
+ }
+ }
+
+ /* Bus reset! */
+ ret = ioctl(vdev->fd, VFIO_DEVICE_PCI_HOT_RESET, reset);
+ g_free(reset);
+
+ DPRINTF("%04x:%02x:%02x.%x hot reset: %s\n", vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function,
+ ret ? "%m" : "Success");
+
+out:
+ /* Re-enable INTx on affected devices */
+ for (i = 0; i < info->count; i++) {
+ PCIHostDeviceAddress host;
+ VFIODevice *tmp;
+
+ host.domain = devices[i].segment;
+ host.bus = devices[i].bus;
+ host.slot = PCI_SLOT(devices[i].devfn);
+ host.function = PCI_FUNC(devices[i].devfn);
+
+ if (vfio_pci_host_match(&host, &vdev->host)) {
+ continue;
+ }
+
+ QLIST_FOREACH(group, &group_list, next) {
+ if (group->groupid == devices[i].group_id) {
+ break;
+ }
+ }
+
+ if (!group) {
+ break;
+ }
+
+ QLIST_FOREACH(tmp, &group->device_list, next) {
+ if (vfio_pci_host_match(&host, &tmp->host)) {
+ vfio_pci_post_reset(tmp);
+ break;
+ }
+ }
+ }
+out_single:
+ vfio_pci_post_reset(vdev);
+ g_free(info);
+
+ return ret;
+}
+
+/*
+ * We want to differentiate hot reset of mulitple in-use devices vs hot reset
+ * of a single in-use device. VFIO_DEVICE_RESET will already handle the case
+ * of doing hot resets when there is only a single device per bus. The in-use
+ * here refers to how many VFIODevices are affected. A hot reset that affects
+ * multiple devices, but only a single in-use device, means that we can call
+ * it from our bus ->reset() callback since the extent is effectively a single
+ * device. This allows us to make use of it in the hotplug path. When there
+ * are multiple in-use devices, we can only trigger the hot reset during a
+ * system reset and thus from our reset handler. We separate _one vs _multi
+ * here so that we don't overlap and do a double reset on the system reset
+ * path where both our reset handler and ->reset() callback are used. Calling
+ * _one() will only do a hot reset for the one in-use devices case, calling
+ * _multi() will do nothing if a _one() would have been sufficient.
+ */
+static int vfio_pci_hot_reset_one(VFIODevice *vdev)
+{
+ return vfio_pci_hot_reset(vdev, true);
+}
+
+static int vfio_pci_hot_reset_multi(VFIODevice *vdev)
+{
+ return vfio_pci_hot_reset(vdev, false);
+}
+
+static void vfio_pci_reset_handler(void *opaque)
+{
+ VFIOGroup *group;
+ VFIODevice *vdev;
+
+ QLIST_FOREACH(group, &group_list, next) {
+ QLIST_FOREACH(vdev, &group->device_list, next) {
+ if (!vdev->reset_works || (!vdev->has_flr && vdev->has_pm_reset)) {
+ vdev->needs_reset = true;
+ }
+ }
+ }
+
+ QLIST_FOREACH(group, &group_list, next) {
+ QLIST_FOREACH(vdev, &group->device_list, next) {
+ if (vdev->needs_reset) {
+ vfio_pci_hot_reset_multi(vdev);
+ }
+ }
+ }
}
static int vfio_connect_container(VFIOGroup *group)
@@ -2746,6 +3182,10 @@ static VFIOGroup *vfio_get_group(int groupid)
return NULL;
}
+ if (QLIST_EMPTY(&group_list)) {
+ qemu_register_reset(vfio_pci_reset_handler, NULL);
+ }
+
QLIST_INSERT_HEAD(&group_list, group, next);
return group;
@@ -2762,6 +3202,10 @@ static void vfio_put_group(VFIOGroup *group)
DPRINTF("vfio_put_group: close group->fd\n");
close(group->fd);
g_free(group);
+
+ if (QLIST_EMPTY(&group_list)) {
+ qemu_unregister_reset(vfio_pci_reset_handler, NULL);
+ }
}
static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)
@@ -2800,9 +3244,6 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)
}
vdev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET);
- if (!vdev->reset_works) {
- error_report("Warning, device %s does not support reset", name);
- }
if (dev_info.num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) {
error_report("vfio: unexpected number of io regions %u",
@@ -2837,22 +3278,6 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)
QLIST_INIT(&vdev->bars[i].quirks);
}
- reg_info.index = VFIO_PCI_ROM_REGION_INDEX;
-
- ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
- if (ret) {
- error_report("vfio: Error getting ROM info: %m");
- goto error;
- }
-
- DPRINTF("Device %s ROM:\n", name);
- DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n",
- (unsigned long)reg_info.size, (unsigned long)reg_info.offset,
- (unsigned long)reg_info.flags);
-
- vdev->rom_size = reg_info.size;
- vdev->rom_offset = reg_info.offset;
-
reg_info.index = VFIO_PCI_CONFIG_REGION_INDEX;
ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
@@ -2917,13 +3342,15 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)
ret = ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info);
if (ret) {
/* This can fail for an old kernel or legacy PCI dev */
- DPRINTF("VFIO_DEVICE_GET_IRQ_INFO failure ret=%d\n", ret);
+ DPRINTF("VFIO_DEVICE_GET_IRQ_INFO failure: %m\n");
ret = 0;
} else if (irq_info.count == 1) {
vdev->pci_aer = true;
} else {
- error_report("vfio: Warning: "
- "Could not enable error recovery for the device\n");
+ error_report("vfio: %04x:%02x:%02x.%x "
+ "Could not enable error recovery for the device",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
}
error:
@@ -2964,11 +3391,10 @@ static void vfio_err_notifier_handler(void *opaque)
* guest to contain the error.
*/
- error_report("%s (%04x:%02x:%02x.%x)"
- "Unrecoverable error detected...\n"
- "Please collect any data possible and then kill the guest",
- __func__, vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function);
+ error_report("%s(%04x:%02x:%02x.%x) Unrecoverable error detected. "
+ "Please collect any data possible and then kill the guest",
+ __func__, vdev->host.domain, vdev->host.bus,
+ vdev->host.slot, vdev->host.function);
vm_stop(RUN_STATE_IO_ERROR);
}
@@ -2991,8 +3417,7 @@ static void vfio_register_err_notifier(VFIODevice *vdev)
}
if (event_notifier_init(&vdev->err_notifier, 0)) {
- error_report("vfio: Warning: "
- "Unable to init event notifier for error detection\n");
+ error_report("vfio: Unable to init event notifier for error detection");
vdev->pci_aer = false;
return;
}
@@ -3013,7 +3438,7 @@ static void vfio_register_err_notifier(VFIODevice *vdev)
ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
if (ret) {
- error_report("vfio: Failed to set up error notification\n");
+ error_report("vfio: Failed to set up error notification");
qemu_set_fd_handler(*pfd, NULL, NULL, vdev);
event_notifier_cleanup(&vdev->err_notifier);
vdev->pci_aer = false;
@@ -3046,7 +3471,7 @@ static void vfio_unregister_err_notifier(VFIODevice *vdev)
ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
if (ret) {
- error_report("vfio: Failed to de-assign error fd: %d\n", ret);
+ error_report("vfio: Failed to de-assign error fd: %m");
}
g_free(irq_set);
qemu_set_fd_handler(event_notifier_get_fd(&vdev->err_notifier),
@@ -3150,7 +3575,7 @@ static int vfio_initfn(PCIDevice *pdev)
memset(&vdev->pdev.config[PCI_BASE_ADDRESS_0], 0, 24);
memset(&vdev->pdev.config[PCI_ROM_ADDRESS], 0, 4);
- vfio_load_rom(vdev);
+ vfio_pci_size_rom(vdev);
ret = vfio_early_setup_msix(vdev);
if (ret) {
@@ -3215,6 +3640,7 @@ static void vfio_exitfn(PCIDevice *pdev)
vfio_teardown_msi(vdev);
vfio_unmap_bars(vdev);
g_free(vdev->emulated_config_bits);
+ g_free(vdev->rom);
vfio_put_device(vdev);
vfio_put_group(group);
}
@@ -3223,51 +3649,34 @@ static void vfio_pci_reset(DeviceState *dev)
{
PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev);
VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
- uint16_t cmd;
DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
vdev->host.bus, vdev->host.slot, vdev->host.function);
- vfio_disable_interrupts(vdev);
-
- /* Make sure the device is in D0 */
- if (vdev->pm_cap) {
- uint16_t pmcsr;
- uint8_t state;
+ vfio_pci_pre_reset(vdev);
- pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2);
- state = pmcsr & PCI_PM_CTRL_STATE_MASK;
- if (state) {
- pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
- vfio_pci_write_config(pdev, vdev->pm_cap + PCI_PM_CTRL, pmcsr, 2);
- /* vfio handles the necessary delay here */
- pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2);
- state = pmcsr & PCI_PM_CTRL_STATE_MASK;
- if (state) {
- error_report("vfio: Unable to power on device, stuck in D%d\n",
- state);
- }
- }
+ if (vdev->reset_works && (vdev->has_flr || !vdev->has_pm_reset) &&
+ !ioctl(vdev->fd, VFIO_DEVICE_RESET)) {
+ DPRINTF("%04x:%02x:%02x.%x FLR/VFIO_DEVICE_RESET\n", vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+ goto post_reset;
}
- /*
- * Stop any ongoing DMA by disconecting I/O, MMIO, and bus master.
- * Also put INTx Disable in known state.
- */
- cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2);
- cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
- PCI_COMMAND_INTX_DISABLE);
- vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2);
+ /* See if we can do our own bus reset */
+ if (!vfio_pci_hot_reset_one(vdev)) {
+ goto post_reset;
+ }
- if (vdev->reset_works) {
- if (ioctl(vdev->fd, VFIO_DEVICE_RESET)) {
- error_report("vfio: Error unable to reset physical device "
- "(%04x:%02x:%02x.%x): %m", vdev->host.domain,
- vdev->host.bus, vdev->host.slot, vdev->host.function);
- }
+ /* If nothing else works and the device supports PM reset, use it */
+ if (vdev->reset_works && vdev->has_pm_reset &&
+ !ioctl(vdev->fd, VFIO_DEVICE_RESET)) {
+ DPRINTF("%04x:%02x:%02x.%x PCI PM Reset\n", vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+ goto post_reset;
}
- vfio_enable_intx(vdev);
+post_reset:
+ vfio_pci_post_reset(vdev);
}
static Property vfio_pci_dev_properties[] = {
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 4d36841d40..24ec52f8f9 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -11,6 +11,8 @@ static char *scsibus_get_dev_path(DeviceState *dev);
static char *scsibus_get_fw_dev_path(DeviceState *dev);
static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf);
static void scsi_req_dequeue(SCSIRequest *req);
+static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len);
+static void scsi_target_free_buf(SCSIRequest *req);
static Property scsi_props[] = {
DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0),
@@ -317,7 +319,8 @@ typedef struct SCSITargetReq SCSITargetReq;
struct SCSITargetReq {
SCSIRequest req;
int len;
- uint8_t buf[2056];
+ uint8_t *buf;
+ int buf_len;
};
static void store_lun(uint8_t *outbuf, int lun)
@@ -361,14 +364,12 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
if (!found_lun0) {
n += 8;
}
- len = MIN(n + 8, r->req.cmd.xfer & ~7);
- if (len > sizeof(r->buf)) {
- /* TODO: > 256 LUNs? */
- return false;
- }
+ scsi_target_alloc_buf(&r->req, n + 8);
+
+ len = MIN(n + 8, r->req.cmd.xfer & ~7);
memset(r->buf, 0, len);
- stl_be_p(&r->buf, n);
+ stl_be_p(&r->buf[0], n);
i = found_lun0 ? 8 : 16;
QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) {
DeviceState *qdev = kid->child;
@@ -387,6 +388,9 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
{
assert(r->req.dev->lun != r->req.lun);
+
+ scsi_target_alloc_buf(&r->req, SCSI_INQUIRY_LEN);
+
if (r->req.cmd.buf[1] & 0x2) {
/* Command support data - optional, not implemented */
return false;
@@ -411,7 +415,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
return false;
}
/* done with EVPD */
- assert(r->len < sizeof(r->buf));
+ assert(r->len < r->buf_len);
r->len = MIN(r->req.cmd.xfer, r->len);
return true;
}
@@ -422,7 +426,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
}
/* PAGE CODE == 0 */
- r->len = MIN(r->req.cmd.xfer, 36);
+ r->len = MIN(r->req.cmd.xfer, SCSI_INQUIRY_LEN);
memset(r->buf, 0, r->len);
if (r->req.lun != 0) {
r->buf[0] = TYPE_NO_LUN;
@@ -455,8 +459,9 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
}
break;
case REQUEST_SENSE:
+ scsi_target_alloc_buf(&r->req, SCSI_SENSE_LEN);
r->len = scsi_device_get_sense(r->req.dev, r->buf,
- MIN(req->cmd.xfer, sizeof r->buf),
+ MIN(req->cmd.xfer, r->buf_len),
(req->cmd.buf[1] & 1) == 0);
if (r->req.dev->sense_is_ua) {
scsi_device_unit_attention_reported(req->dev);
@@ -501,11 +506,29 @@ static uint8_t *scsi_target_get_buf(SCSIRequest *req)
return r->buf;
}
+static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+
+ r->buf = g_malloc(len);
+ r->buf_len = len;
+
+ return r->buf;
+}
+
+static void scsi_target_free_buf(SCSIRequest *req)
+{
+ SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+
+ g_free(r->buf);
+}
+
static const struct SCSIReqOps reqops_target_command = {
.size = sizeof(SCSITargetReq),
.send_command = scsi_target_send_command,
.read_data = scsi_target_read_data,
.get_buf = scsi_target_get_buf,
+ .free_req = scsi_target_free_buf,
};
@@ -1365,7 +1388,7 @@ int scsi_build_sense(uint8_t *in_buf, int in_len,
buf[7] = 10;
buf[12] = sense.asc;
buf[13] = sense.ascq;
- return MIN(len, 18);
+ return MIN(len, SCSI_SENSE_LEN);
} else {
/* Return descriptor format sense buffer */
buf[0] = 0x72;
diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c
index 42613b3aff..d1168c9e04 100644
--- a/hw/sd/milkymist-memcard.c
+++ b/hw/sd/milkymist-memcard.c
@@ -255,6 +255,10 @@ static int milkymist_memcard_init(SysBusDevice *dev)
dinfo = drive_get_next(IF_SD);
s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false);
+ if (s->card == NULL) {
+ return -1;
+ }
+
s->enabled = dinfo ? bdrv_is_inserted(dinfo->bdrv) : 0;
memory_region_init_io(&s->regs_region, OBJECT(s), &memcard_mmio_ops, s,
diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c
index bf5d1fbf6d..937a47869a 100644
--- a/hw/sd/omap_mmc.c
+++ b/hw/sd/omap_mmc.c
@@ -593,6 +593,9 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base,
/* Instantiate the storage */
s->card = sd_init(bd, false);
+ if (s->card == NULL) {
+ exit(1);
+ }
return s;
}
@@ -618,6 +621,9 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
/* Instantiate the storage */
s->card = sd_init(bd, false);
+ if (s->card == NULL) {
+ exit(1);
+ }
s->cdet = qemu_allocate_irqs(omap_mmc_cover_cb, s, 1)[0];
sd_set_cb(s->card, NULL, s->cdet);
diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c
index 03875bf6ca..c35896d28c 100644
--- a/hw/sd/pl181.c
+++ b/hw/sd/pl181.c
@@ -491,6 +491,10 @@ static int pl181_init(SysBusDevice *sbd)
qdev_init_gpio_out(dev, s->cardstatus, 2);
dinfo = drive_get_next(IF_SD);
s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false);
+ if (s->card == NULL) {
+ return -1;
+ }
+
return 0;
}
diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c
index 90c955fe62..b9d8b1a3e1 100644
--- a/hw/sd/pxa2xx_mmci.c
+++ b/hw/sd/pxa2xx_mmci.c
@@ -539,6 +539,9 @@ PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
/* Instantiate the actual storage */
s->card = sd_init(bd, false);
+ if (s->card == NULL) {
+ exit(1);
+ }
register_savevm(NULL, "pxa2xx_mmci", 0, 0,
pxa2xx_mmci_save, pxa2xx_mmci_load, s);
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 346d86f69c..7380f063f7 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -494,6 +494,11 @@ SDState *sd_init(BlockDriverState *bs, bool is_spi)
{
SDState *sd;
+ if (bdrv_is_read_only(bs)) {
+ fprintf(stderr, "sd_init: Cannot use read-only drive\n");
+ return NULL;
+ }
+
sd = (SDState *) g_malloc0(sizeof(SDState));
sd->buf = qemu_blockalign(bs, 512);
sd->spi = is_spi;
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 1483e196cd..0906a1d62b 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -1166,6 +1166,9 @@ static void sdhci_initfn(Object *obj)
di = drive_get_next(IF_SD);
s->card = sd_init(di ? di->bdrv : NULL, false);
+ if (s->card == NULL) {
+ exit(1);
+ }
s->eject_cb = qemu_allocate_irqs(sdhci_insert_eject_cb, s, 1)[0];
s->ro_cb = qemu_allocate_irqs(sdhci_card_readonly_cb, s, 1)[0];
sd_set_cb(s->card, s->ro_cb, s->eject_cb);
diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c
index d47e2377f9..1bb56c4d54 100644
--- a/hw/sd/ssi-sd.c
+++ b/hw/sd/ssi-sd.c
@@ -246,6 +246,9 @@ static int ssi_sd_init(SSISlave *dev)
s->mode = SSI_SD_CMD;
dinfo = drive_get_next(IF_SD);
s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, true);
+ if (s->sd == NULL) {
+ return -1;
+ }
register_savevm(&dev->qdev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s);
return 0;
}
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 35f0878409..0396e334ed 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -1143,7 +1143,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
switch (ret) {
case USB_RET_IOERROR:
case USB_RET_NODEV:
+ DPRINTF("usb-ohci: got DEV ERROR\n");
OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
+ break;
case USB_RET_NAK:
DPRINTF("usb-ohci: got NAK\n");
return 1;
diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c
index d82ce5d8a6..197795ffe1 100644
--- a/hw/xen/xen_backend.c
+++ b/hw/xen/xen_backend.c
@@ -205,7 +205,6 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
struct XenDevOps *ops)
{
struct XenDevice *xendev;
- char *dom0;
xendev = xen_be_find_xendev(type, dom, dev);
if (xendev) {
@@ -219,12 +218,10 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
xendev->dev = dev;
xendev->ops = ops;
- dom0 = xs_get_domain_path(xenstore, 0);
- snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d",
- dom0, xendev->type, xendev->dom, xendev->dev);
+ snprintf(xendev->be, sizeof(xendev->be), "backend/%s/%d/%d",
+ xendev->type, xendev->dom, xendev->dev);
snprintf(xendev->name, sizeof(xendev->name), "%s-%d",
xendev->type, xendev->dev);
- free(dom0);
xendev->debug = debug;
xendev->local_port = -1;
@@ -570,14 +567,12 @@ static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
{
struct XenDevice *xendev;
char path[XEN_BUFSIZE], token[XEN_BUFSIZE];
- char **dev = NULL, *dom0;
+ char **dev = NULL;
unsigned int cdev, j;
/* setup watch */
- dom0 = xs_get_domain_path(xenstore, 0);
snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops);
- snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom);
- free(dom0);
+ snprintf(path, sizeof(path), "backend/%s/%d", type, dom);
if (!xs_watch(xenstore, path, token)) {
xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path);
return -1;
@@ -603,12 +598,10 @@ static void xenstore_update_be(char *watch, char *type, int dom,
struct XenDevOps *ops)
{
struct XenDevice *xendev;
- char path[XEN_BUFSIZE], *dom0, *bepath;
+ char path[XEN_BUFSIZE], *bepath;
unsigned int len, dev;
- dom0 = xs_get_domain_path(xenstore, 0);
- len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom);
- free(dom0);
+ len = snprintf(path, sizeof(path), "backend/%s/%d", type, dom);
if (strncmp(path, watch, len) != 0) {
return;
}
diff --git a/include/block/block.h b/include/block/block.h
index f808550959..3560deb883 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -84,6 +84,9 @@ typedef struct BlockDevOps {
/* BDRV_BLOCK_DATA: data is read from bs->file or another file
* BDRV_BLOCK_ZERO: sectors read as zero
* BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data
+ * BDRV_BLOCK_RAW: used internally to indicate that the request
+ * was answered by the raw driver and that one
+ * should look in bs->file directly.
*
* If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 represent the offset in
* bs->file where sector data can be read from as raw data.
@@ -105,6 +108,7 @@ typedef struct BlockDevOps {
#define BDRV_BLOCK_DATA 1
#define BDRV_BLOCK_ZERO 2
#define BDRV_BLOCK_OFFSET_VALID 4
+#define BDRV_BLOCK_RAW 8
#define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK
typedef enum {
@@ -244,6 +248,20 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
int bdrv_amend_options(BlockDriverState *bs_new, QEMUOptionParameter *options);
+/* external snapshots */
+
+typedef enum {
+ EXT_SNAPSHOT_ALLOWED,
+ EXT_SNAPSHOT_FORBIDDEN,
+} ExtSnapshotPerm;
+
+/* return EXT_SNAPSHOT_ALLOWED if external snapshot is allowed
+ * return EXT_SNAPSHOT_FORBIDDEN if external snapshot is forbidden
+ */
+ExtSnapshotPerm bdrv_check_ext_snapshot(BlockDriverState *bs);
+/* helper used to forbid external snapshots like in blkverify */
+ExtSnapshotPerm bdrv_check_ext_snapshot_forbidden(BlockDriverState *bs);
+
/* async block I/O */
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
int sector_num);
@@ -335,6 +353,7 @@ int bdrv_get_flags(BlockDriverState *bs);
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
+ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs);
void bdrv_round_to_clusters(BlockDriverState *bs,
int64_t sector_num, int nb_sectors,
int64_t *cluster_sector_num,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 211087aa91..a48731d539 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -67,6 +67,12 @@ typedef struct BdrvTrackedRequest {
struct BlockDriver {
const char *format_name;
int instance_size;
+
+ /* if not defined external snapshots are allowed
+ * future block filters will query their children to build the response
+ */
+ ExtSnapshotPerm (*bdrv_check_ext_snapshot)(BlockDriverState *bs);
+
int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
int (*bdrv_probe_device)(const char *filename);
@@ -168,6 +174,7 @@ struct BlockDriver {
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
const char *snapshot_name);
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
+ ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t pos);
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index d530409ff5..d76de62a46 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -28,16 +28,16 @@
#include "block/block.h"
/**
- * BlockJobType:
+ * BlockJobDriver:
*
- * A class type for block job objects.
+ * A class type for block job driver.
*/
-typedef struct BlockJobType {
+typedef struct BlockJobDriver {
/** Derived BlockJob struct size */
size_t instance_size;
/** String describing the operation, part of query-block-jobs QMP API */
- const char *job_type;
+ BlockJobType job_type;
/** Optional callback for job types that support setting a speed limit */
void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
@@ -50,7 +50,7 @@ typedef struct BlockJobType {
* manually.
*/
void (*complete)(BlockJob *job, Error **errp);
-} BlockJobType;
+} BlockJobDriver;
/**
* BlockJob:
@@ -59,7 +59,7 @@ typedef struct BlockJobType {
*/
struct BlockJob {
/** The job type, including the job vtable. */
- const BlockJobType *job_type;
+ const BlockJobDriver *driver;
/** The block device on which the job is operating. */
BlockDriverState *bs;
@@ -128,7 +128,7 @@ struct BlockJob {
* This function is not part of the public job interface; it should be
* called from a wrapper that is specific to the job type.
*/
-void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
+void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
int64_t speed, BlockDriverCompletionFunc *cb,
void *opaque, Error **errp);
diff --git a/include/block/qapi.h b/include/block/qapi.h
index 0496cc9282..9518ee4001 100644
--- a/include/block/qapi.h
+++ b/include/block/qapi.h
@@ -42,6 +42,8 @@ BlockStats *bdrv_query_stats(const BlockDriverState *bs);
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
QEMUSnapshotInfo *sn);
+void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
+ ImageInfoSpecific *info_spec);
void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
ImageInfo *info);
#endif
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index a5c028c536..01cd8c7a2b 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -178,7 +178,5 @@ typedef struct CPUWatchpoint {
\
/* user data */ \
void *opaque; \
- \
- const char *cpu_model_str;
#endif
diff --git a/include/exec/def-helper.h b/include/exec/def-helper.h
index 022a9ceb6a..73d51f9cf5 100644
--- a/include/exec/def-helper.h
+++ b/include/exec/def-helper.h
@@ -240,8 +240,7 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \
#elif GEN_HELPER == 2
/* Register helpers. */
-#define DEF_HELPER_FLAGS_0(name, flags, ret) \
-tcg_register_helper(HELPER(name), #name);
+#define DEF_HELPER_FLAGS_0(name, flags, ret) { HELPER(name), #name },
#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \
DEF_HELPER_FLAGS_0(name, flags, ret)
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index dc27f33152..ea90b649d4 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -320,53 +320,9 @@ extern uintptr_t tci_tb_ptr;
#define GETPC() (GETRA() - GETPC_ADJ)
-/* The LDST optimizations splits code generation into fast and slow path.
- In some implementations, we pass the "logical" return address manually;
- in others, we must infer the logical return from the true return. */
-#if defined(CONFIG_QEMU_LDST_OPTIMIZATION) && defined(CONFIG_SOFTMMU)
-# if defined(__arm__)
-/* We define two insns between the return address and the branch back to
- straight-line. Find and decode that branch insn. */
-# define GETRA_LDST(RA) tcg_getra_ldst(RA)
-static inline uintptr_t tcg_getra_ldst(uintptr_t ra)
-{
- int32_t b;
- ra += 8; /* skip the two insns */
- b = *(int32_t *)ra; /* load the branch insn */
- b = (b << 8) >> (8 - 2); /* extract the displacement */
- ra += 8; /* branches are relative to pc+8 */
- ra += b; /* apply the displacement */
- return ra;
-}
-# elif defined(__aarch64__)
-# define GETRA_LDST(RA) tcg_getra_ldst(RA)
-static inline uintptr_t tcg_getra_ldst(uintptr_t ra)
-{
- int32_t b;
- ra += 4; /* skip one instruction */
- b = *(int32_t *)ra; /* load the branch insn */
- b = (b << 6) >> (6 - 2); /* extract the displacement */
- ra += b; /* apply the displacement */
- return ra;
-}
-# endif
-#endif /* CONFIG_QEMU_LDST_OPTIMIZATION */
-
-/* ??? Delete these once they are no longer used. */
-bool is_tcg_gen_code(uintptr_t pc_ptr);
-#ifdef GETRA_LDST
-# define GETRA_EXT() tcg_getra_ext(GETRA())
-static inline uintptr_t tcg_getra_ext(uintptr_t ra)
-{
- return is_tcg_gen_code(ra) ? GETRA_LDST(ra) : ra;
-}
-#else
-# define GETRA_EXT() GETRA()
-#endif
-
#if !defined(CONFIG_USER_ONLY)
-void phys_mem_set_alloc(void *(*alloc)(ram_addr_t));
+void phys_mem_set_alloc(void *(*alloc)(size_t));
struct MemoryRegion *iotlb_to_region(hwaddr index);
bool io_mem_read(struct MemoryRegion *mr, hwaddr addr,
diff --git a/include/exec/softmmu_template.h b/include/exec/softmmu_template.h
index 5bbc56afd5..c6a544069c 100644
--- a/include/exec/softmmu_template.h
+++ b/include/exec/softmmu_template.h
@@ -70,6 +70,48 @@
#define ADDR_READ addr_read
#endif
+#if DATA_SIZE == 8
+# define BSWAP(X) bswap64(X)
+#elif DATA_SIZE == 4
+# define BSWAP(X) bswap32(X)
+#elif DATA_SIZE == 2
+# define BSWAP(X) bswap16(X)
+#else
+# define BSWAP(X) (X)
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+# define TGT_BE(X) (X)
+# define TGT_LE(X) BSWAP(X)
+#else
+# define TGT_BE(X) BSWAP(X)
+# define TGT_LE(X) (X)
+#endif
+
+#if DATA_SIZE == 1
+# define helper_le_ld_name glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)
+# define helper_be_ld_name helper_le_ld_name
+# define helper_le_lds_name glue(glue(helper_ret_ld, SSUFFIX), MMUSUFFIX)
+# define helper_be_lds_name helper_le_lds_name
+# define helper_le_st_name glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)
+# define helper_be_st_name helper_le_st_name
+#else
+# define helper_le_ld_name glue(glue(helper_le_ld, USUFFIX), MMUSUFFIX)
+# define helper_be_ld_name glue(glue(helper_be_ld, USUFFIX), MMUSUFFIX)
+# define helper_le_lds_name glue(glue(helper_le_ld, SSUFFIX), MMUSUFFIX)
+# define helper_be_lds_name glue(glue(helper_be_ld, SSUFFIX), MMUSUFFIX)
+# define helper_le_st_name glue(glue(helper_le_st, SUFFIX), MMUSUFFIX)
+# define helper_be_st_name glue(glue(helper_be_st, SUFFIX), MMUSUFFIX)
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+# define helper_te_ld_name helper_be_ld_name
+# define helper_te_st_name helper_be_st_name
+#else
+# define helper_te_ld_name helper_le_ld_name
+# define helper_te_st_name helper_le_st_name
+#endif
+
static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
hwaddr physaddr,
target_ulong addr,
@@ -89,18 +131,16 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
return val;
}
-/* handle all cases except unaligned access which span two pages */
#ifdef SOFTMMU_CODE_ACCESS
-static
+static __attribute__((unused))
#endif
-WORD_TYPE
-glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)(CPUArchState *env,
- target_ulong addr, int mmu_idx,
- uintptr_t retaddr)
+WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
+ uintptr_t retaddr)
{
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
uintptr_t haddr;
+ DATA_TYPE res;
/* Adjust the given return address. */
retaddr -= GETPC_ADJ;
@@ -124,7 +164,12 @@ glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)(CPUArchState *env,
goto do_unaligned_access;
}
ioaddr = env->iotlb[mmu_idx][index];
- return glue(io_read, SUFFIX)(env, ioaddr, addr, retaddr);
+
+ /* ??? Note that the io helpers always read data in the target
+ byte ordering. We should push the LE/BE request down into io. */
+ res = glue(io_read, SUFFIX)(env, ioaddr, addr, retaddr);
+ res = TGT_LE(res);
+ return res;
}
/* Handle slow unaligned access (it spans two pages or IO). */
@@ -132,7 +177,7 @@ glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)(CPUArchState *env,
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
>= TARGET_PAGE_SIZE)) {
target_ulong addr1, addr2;
- DATA_TYPE res1, res2, res;
+ DATA_TYPE res1, res2;
unsigned shift;
do_unaligned_access:
#ifdef ALIGNED_ONLY
@@ -142,16 +187,94 @@ glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)(CPUArchState *env,
addr2 = addr1 + DATA_SIZE;
/* Note the adjustment at the beginning of the function.
Undo that for the recursion. */
- res1 = glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)
- (env, addr1, mmu_idx, retaddr + GETPC_ADJ);
- res2 = glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)
- (env, addr2, mmu_idx, retaddr + GETPC_ADJ);
+ res1 = helper_le_ld_name(env, addr1, mmu_idx, retaddr + GETPC_ADJ);
+ res2 = helper_le_ld_name(env, addr2, mmu_idx, retaddr + GETPC_ADJ);
shift = (addr & (DATA_SIZE - 1)) * 8;
-#ifdef TARGET_WORDS_BIGENDIAN
- res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
-#else
+
+ /* Little-endian combine. */
res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
+ return res;
+ }
+
+ /* Handle aligned access or unaligned access in the same page. */
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0) {
+ do_unaligned_access(env, addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+ }
+#endif
+
+ haddr = addr + env->tlb_table[mmu_idx][index].addend;
+#if DATA_SIZE == 1
+ res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
+#else
+ res = glue(glue(ld, LSUFFIX), _le_p)((uint8_t *)haddr);
+#endif
+ return res;
+}
+
+#if DATA_SIZE > 1
+#ifdef SOFTMMU_CODE_ACCESS
+static __attribute__((unused))
+#endif
+WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
+ uintptr_t retaddr)
+{
+ int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
+ uintptr_t haddr;
+ DATA_TYPE res;
+
+ /* Adjust the given return address. */
+ retaddr -= GETPC_ADJ;
+
+ /* If the TLB entry is for a different page, reload and try again. */
+ if ((addr & TARGET_PAGE_MASK)
+ != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0) {
+ do_unaligned_access(env, addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+ }
+#endif
+ tlb_fill(env, addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+ tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
+ }
+
+ /* Handle an IO access. */
+ if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
+ hwaddr ioaddr;
+ if ((addr & (DATA_SIZE - 1)) != 0) {
+ goto do_unaligned_access;
+ }
+ ioaddr = env->iotlb[mmu_idx][index];
+
+ /* ??? Note that the io helpers always read data in the target
+ byte ordering. We should push the LE/BE request down into io. */
+ res = glue(io_read, SUFFIX)(env, ioaddr, addr, retaddr);
+ res = TGT_BE(res);
+ return res;
+ }
+
+ /* Handle slow unaligned access (it spans two pages or IO). */
+ if (DATA_SIZE > 1
+ && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
+ >= TARGET_PAGE_SIZE)) {
+ target_ulong addr1, addr2;
+ DATA_TYPE res1, res2;
+ unsigned shift;
+ do_unaligned_access:
+#ifdef ALIGNED_ONLY
+ do_unaligned_access(env, addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
#endif
+ addr1 = addr & ~(DATA_SIZE - 1);
+ addr2 = addr1 + DATA_SIZE;
+ /* Note the adjustment at the beginning of the function.
+ Undo that for the recursion. */
+ res1 = helper_be_ld_name(env, addr1, mmu_idx, retaddr + GETPC_ADJ);
+ res2 = helper_be_ld_name(env, addr2, mmu_idx, retaddr + GETPC_ADJ);
+ shift = (addr & (DATA_SIZE - 1)) * 8;
+
+ /* Big-endian combine. */
+ res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
return res;
}
@@ -163,16 +286,16 @@ glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)(CPUArchState *env,
#endif
haddr = addr + env->tlb_table[mmu_idx][index].addend;
- /* Note that ldl_raw is defined with type "int". */
- return (DATA_TYPE) glue(glue(ld, LSUFFIX), _raw)((uint8_t *)haddr);
+ res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
+ return res;
}
+#endif /* DATA_SIZE > 1 */
DATA_TYPE
glue(glue(helper_ld, SUFFIX), MMUSUFFIX)(CPUArchState *env, target_ulong addr,
int mmu_idx)
{
- return glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)(env, addr, mmu_idx,
- GETRA_EXT());
+ return helper_te_ld_name (env, addr, mmu_idx, GETRA());
}
#ifndef SOFTMMU_CODE_ACCESS
@@ -180,14 +303,19 @@ glue(glue(helper_ld, SUFFIX), MMUSUFFIX)(CPUArchState *env, target_ulong addr,
/* Provide signed versions of the load routines as well. We can of course
avoid this for 64-bit data, or for 32-bit data on 32-bit host. */
#if DATA_SIZE * 8 < TCG_TARGET_REG_BITS
-WORD_TYPE
-glue(glue(helper_ret_ld, SSUFFIX), MMUSUFFIX)(CPUArchState *env,
- target_ulong addr, int mmu_idx,
- uintptr_t retaddr)
+WORD_TYPE helper_le_lds_name(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr)
+{
+ return (SDATA_TYPE)helper_le_ld_name(env, addr, mmu_idx, retaddr);
+}
+
+# if DATA_SIZE > 1
+WORD_TYPE helper_be_lds_name(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr)
{
- return (SDATA_TYPE) glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)
- (env, addr, mmu_idx, retaddr);
+ return (SDATA_TYPE)helper_be_ld_name(env, addr, mmu_idx, retaddr);
}
+# endif
#endif
static inline void glue(io_write, SUFFIX)(CPUArchState *env,
@@ -208,10 +336,8 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
io_mem_write(mr, physaddr, val, 1 << SHIFT);
}
-void
-glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(CPUArchState *env,
- target_ulong addr, DATA_TYPE val,
- int mmu_idx, uintptr_t retaddr)
+void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
+ int mmu_idx, uintptr_t retaddr)
{
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
@@ -239,6 +365,10 @@ glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(CPUArchState *env,
goto do_unaligned_access;
}
ioaddr = env->iotlb[mmu_idx][index];
+
+ /* ??? Note that the io helpers always read data in the target
+ byte ordering. We should push the LE/BE request down into io. */
+ val = TGT_LE(val);
glue(io_write, SUFFIX)(env, ioaddr, val, addr, retaddr);
return;
}
@@ -256,11 +386,84 @@ glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(CPUArchState *env,
/* Note: relies on the fact that tlb_fill() does not remove the
* previous page from the TLB cache. */
for (i = DATA_SIZE - 1; i >= 0; i--) {
-#ifdef TARGET_WORDS_BIGENDIAN
- uint8_t val8 = val >> (((DATA_SIZE - 1) * 8) - (i * 8));
-#else
+ /* Little-endian extract. */
uint8_t val8 = val >> (i * 8);
+ /* Note the adjustment at the beginning of the function.
+ Undo that for the recursion. */
+ glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
+ mmu_idx, retaddr + GETPC_ADJ);
+ }
+ return;
+ }
+
+ /* Handle aligned access or unaligned access in the same page. */
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0) {
+ do_unaligned_access(env, addr, 1, mmu_idx, retaddr);
+ }
+#endif
+
+ haddr = addr + env->tlb_table[mmu_idx][index].addend;
+#if DATA_SIZE == 1
+ glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
+#else
+ glue(glue(st, SUFFIX), _le_p)((uint8_t *)haddr, val);
#endif
+}
+
+#if DATA_SIZE > 1
+void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
+ int mmu_idx, uintptr_t retaddr)
+{
+ int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+ uintptr_t haddr;
+
+ /* Adjust the given return address. */
+ retaddr -= GETPC_ADJ;
+
+ /* If the TLB entry is for a different page, reload and try again. */
+ if ((addr & TARGET_PAGE_MASK)
+ != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0) {
+ do_unaligned_access(env, addr, 1, mmu_idx, retaddr);
+ }
+#endif
+ tlb_fill(env, addr, 1, mmu_idx, retaddr);
+ tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+ }
+
+ /* Handle an IO access. */
+ if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
+ hwaddr ioaddr;
+ if ((addr & (DATA_SIZE - 1)) != 0) {
+ goto do_unaligned_access;
+ }
+ ioaddr = env->iotlb[mmu_idx][index];
+
+ /* ??? Note that the io helpers always read data in the target
+ byte ordering. We should push the LE/BE request down into io. */
+ val = TGT_BE(val);
+ glue(io_write, SUFFIX)(env, ioaddr, val, addr, retaddr);
+ return;
+ }
+
+ /* Handle slow unaligned access (it spans two pages or IO). */
+ if (DATA_SIZE > 1
+ && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
+ >= TARGET_PAGE_SIZE)) {
+ int i;
+ do_unaligned_access:
+#ifdef ALIGNED_ONLY
+ do_unaligned_access(env, addr, 1, mmu_idx, retaddr);
+#endif
+ /* XXX: not efficient, but simple */
+ /* Note: relies on the fact that tlb_fill() does not remove the
+ * previous page from the TLB cache. */
+ for (i = DATA_SIZE - 1; i >= 0; i--) {
+ /* Big-endian extract. */
+ uint8_t val8 = val >> (((DATA_SIZE - 1) * 8) - (i * 8));
/* Note the adjustment at the beginning of the function.
Undo that for the recursion. */
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
@@ -277,15 +480,15 @@ glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(CPUArchState *env,
#endif
haddr = addr + env->tlb_table[mmu_idx][index].addend;
- glue(glue(st, SUFFIX), _raw)((uint8_t *)haddr, val);
+ glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
}
+#endif /* DATA_SIZE > 1 */
void
glue(glue(helper_st, SUFFIX), MMUSUFFIX)(CPUArchState *env, target_ulong addr,
DATA_TYPE val, int mmu_idx)
{
- glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(env, addr, val, mmu_idx,
- GETRA_EXT());
+ helper_te_st_name(env, addr, val, mmu_idx, GETRA());
}
#endif /* !defined(SOFTMMU_CODE_ACCESS) */
@@ -301,3 +504,16 @@ glue(glue(helper_st, SUFFIX), MMUSUFFIX)(CPUArchState *env, target_ulong addr,
#undef SDATA_TYPE
#undef USUFFIX
#undef SSUFFIX
+#undef BSWAP
+#undef TGT_BE
+#undef TGT_LE
+#undef CPU_BE
+#undef CPU_LE
+#undef helper_le_ld_name
+#undef helper_be_ld_name
+#undef helper_le_lds_name
+#undef helper_be_lds_name
+#undef helper_le_st_name
+#undef helper_be_st_name
+#undef helper_te_ld_name
+#undef helper_te_st_name
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 9b2ddc4acc..6083839084 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -230,6 +230,14 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t);
.driver = "e1000",\
.property = "mitigation",\
.value = "off",\
+ },{\
+ .driver = "qemu64-" TYPE_X86_CPU,\
+ .property = "model",\
+ .value = stringify(2),\
+ },{\
+ .driver = "qemu32-" TYPE_X86_CPU,\
+ .property = "model",\
+ .value = stringify(3),\
}
#define PC_COMPAT_1_5 \
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index a62f231eb9..e191ca0bd2 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -30,22 +30,6 @@ typedef enum DeviceCategory {
DEVICE_CATEGORY_MAX
} DeviceCategory;
-static inline const char *qdev_category_get_name(DeviceCategory category)
-{
- static const char *category_names[DEVICE_CATEGORY_MAX] = {
- [DEVICE_CATEGORY_BRIDGE] = "Controller/Bridge/Hub",
- [DEVICE_CATEGORY_USB] = "USB",
- [DEVICE_CATEGORY_STORAGE] = "Storage",
- [DEVICE_CATEGORY_NETWORK] = "Network",
- [DEVICE_CATEGORY_INPUT] = "Input",
- [DEVICE_CATEGORY_DISPLAY] = "Display",
- [DEVICE_CATEGORY_SOUND] = "Sound",
- [DEVICE_CATEGORY_MISC] = "Misc",
- };
-
- return category_names[category];
-};
-
typedef int (*qdev_initfn)(DeviceState *dev);
typedef int (*qdev_event)(DeviceState *dev);
typedef void (*qdev_resetfn)(DeviceState *dev);
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index 1b6651054a..76f6ac24a7 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -9,6 +9,8 @@
#define MAX_SCSI_DEVS 255
#define SCSI_CMD_BUF_SIZE 16
+#define SCSI_SENSE_LEN 18
+#define SCSI_INQUIRY_LEN 36
typedef struct SCSIBus SCSIBus;
typedef struct SCSIBusInfo SCSIBusInfo;
diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h
index 1ce11f5df0..cea38181bf 100644
--- a/include/qapi/qmp/dispatch.h
+++ b/include/qapi/qmp/dispatch.h
@@ -47,9 +47,12 @@ QmpCommand *qmp_find_command(const char *name);
QObject *qmp_dispatch(QObject *request);
void qmp_disable_command(const char *name);
void qmp_enable_command(const char *name);
-bool qmp_command_is_enabled(const char *name);
-char **qmp_get_command_list(void);
+bool qmp_command_is_enabled(const QmpCommand *cmd);
+const char *qmp_command_name(const QmpCommand *cmd);
+bool qmp_has_success_response(const QmpCommand *cmd);
QObject *qmp_build_error_object(Error *errp);
+typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque);
+void qmp_for_each_command(qmp_cmd_callback_fn fn, void *opaque);
#endif
diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h
index 06e2e6f0ee..304c90c2b4 100644
--- a/include/qemu/bitops.h
+++ b/include/qemu/bitops.h
@@ -184,6 +184,86 @@ static inline unsigned long hweight_long(unsigned long w)
}
/**
+ * rol8 - rotate an 8-bit value left
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint8_t rol8(uint8_t word, unsigned int shift)
+{
+ return (word << shift) | (word >> (8 - shift));
+}
+
+/**
+ * ror8 - rotate an 8-bit value right
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint8_t ror8(uint8_t word, unsigned int shift)
+{
+ return (word >> shift) | (word << (8 - shift));
+}
+
+/**
+ * rol16 - rotate a 16-bit value left
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint16_t rol16(uint16_t word, unsigned int shift)
+{
+ return (word << shift) | (word >> (16 - shift));
+}
+
+/**
+ * ror16 - rotate a 16-bit value right
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint16_t ror16(uint16_t word, unsigned int shift)
+{
+ return (word >> shift) | (word << (16 - shift));
+}
+
+/**
+ * rol32 - rotate a 32-bit value left
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint32_t rol32(uint32_t word, unsigned int shift)
+{
+ return (word << shift) | (word >> (32 - shift));
+}
+
+/**
+ * ror32 - rotate a 32-bit value right
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint32_t ror32(uint32_t word, unsigned int shift)
+{
+ return (word >> shift) | (word << (32 - shift));
+}
+
+/**
+ * rol64 - rotate a 64-bit value left
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint64_t rol64(uint64_t word, unsigned int shift)
+{
+ return (word << shift) | (word >> (64 - shift));
+}
+
+/**
+ * ror64 - rotate a 64-bit value right
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint64_t ror64(uint64_t word, unsigned int shift)
+{
+ return (word >> shift) | (word << (64 - shift));
+}
+
+/**
* extract32:
* @value: the value to extract the bit field from
* @start: the lowest bit in the bit field (numbered from 0)
diff --git a/include/qemu/option.h b/include/qemu/option.h
index 63db4ccb9a..5c0c6dd294 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -142,6 +142,7 @@ void qemu_opts_loc_restore(QemuOpts *opts);
int qemu_opts_set(QemuOptsList *list, const char *id,
const char *name, const char *value);
const char *qemu_opts_id(QemuOpts *opts);
+void qemu_opts_set_id(QemuOpts *opts, char *id);
void qemu_opts_del(QemuOpts *opts);
void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp);
int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname);
diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index c5174d76a7..45588d7d58 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -39,6 +39,7 @@ int socket_set_cork(int fd, int v);
int socket_set_nodelay(int fd);
void qemu_set_block(int fd);
void qemu_set_nonblock(int fd);
+int socket_set_fast_reuse(int fd);
int send_all(int fd, const void *buf, int len1);
int recv_all(int fd, void *buf, int len1, bool single_read);
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
index 804ec8839b..10820910d7 100644
--- a/include/sysemu/blockdev.h
+++ b/include/sysemu/blockdev.h
@@ -37,6 +37,7 @@ struct DriveInfo {
int bus;
int unit;
int auto_del; /* see blockdev_mark_auto_del() */
+ bool enable_auto_del; /* Only for legacy drive_init() */
int media_cd;
int cyls, heads, secs, trans;
QemuOpts *opts;
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 8053130a97..ad101d9258 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -78,6 +78,7 @@ struct CharDriverState {
int explicit_be_open;
int avail_connections;
int is_mux;
+ guint fd_in_tag;
QemuOpts *opts;
QTAILQ_ENTRY(CharDriverState) next;
};
diff --git a/linux-user/main.c b/linux-user/main.c
index 1561950bf5..6b4ab0930e 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -42,7 +42,7 @@ const char *filename;
const char *argv0;
int gdbstub_port;
envlist_t *envlist;
-const char *cpu_model;
+static const char *cpu_model;
unsigned long mmap_min_addr;
#if defined(CONFIG_USE_GUEST_BASE)
unsigned long guest_base;
@@ -3285,6 +3285,37 @@ void init_task_state(TaskState *ts)
ts->sigqueue_table[i].next = NULL;
}
+CPUArchState *cpu_copy(CPUArchState *env)
+{
+ CPUArchState *new_env = cpu_init(cpu_model);
+#if defined(TARGET_HAS_ICE)
+ CPUBreakpoint *bp;
+ CPUWatchpoint *wp;
+#endif
+
+ /* Reset non arch specific state */
+ cpu_reset(ENV_GET_CPU(new_env));
+
+ memcpy(new_env, env, sizeof(CPUArchState));
+
+ /* Clone all break/watchpoints.
+ Note: Once we support ptrace with hw-debug register access, make sure
+ BP_CPU break/watchpoints are handled correctly on clone. */
+ QTAILQ_INIT(&env->breakpoints);
+ QTAILQ_INIT(&env->watchpoints);
+#if defined(TARGET_HAS_ICE)
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ cpu_breakpoint_insert(new_env, bp->pc, bp->flags, NULL);
+ }
+ QTAILQ_FOREACH(wp, &env->watchpoints, entry) {
+ cpu_watchpoint_insert(new_env, wp->vaddr, (~wp->len_mask) + 1,
+ wp->flags, NULL);
+ }
+#endif
+
+ return new_env;
+}
+
static void handle_arg_help(const char *arg)
{
usage();
diff --git a/migration.c b/migration.c
index b4f8462ae4..2b1ab20c54 100644
--- a/migration.c
+++ b/migration.c
@@ -150,6 +150,7 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
MigrationState *s = migrate_get_current();
int i;
+ caps = NULL; /* silence compiler warning */
for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
if (head == NULL) {
head = g_malloc0(sizeof(*caps));
diff --git a/net/socket.c b/net/socket.c
index e61309d8d5..fb21e20a54 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -262,6 +262,11 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr
return -1;
}
+ /* Allow multiple sockets to bind the same multicast ip and port by setting
+ * SO_REUSEADDR. This is the only situation where SO_REUSEADDR should be set
+ * on windows. Use socket_set_fast_reuse otherwise as it sets SO_REUSEADDR
+ * only on posix systems.
+ */
val = 1;
ret = qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
if (ret < 0) {
@@ -510,7 +515,7 @@ static int net_socket_listen_init(NetClientState *peer,
NetClientState *nc;
NetSocketState *s;
struct sockaddr_in saddr;
- int fd, val, ret;
+ int fd, ret;
if (parse_host_port(&saddr, host_str) < 0)
return -1;
@@ -522,9 +527,7 @@ static int net_socket_listen_init(NetClientState *peer,
}
qemu_set_nonblock(fd);
- /* allow fast reuse */
- val = 1;
- qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ socket_set_fast_reuse(fd);
ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
if (ret < 0) {
@@ -645,7 +648,7 @@ static int net_socket_udp_init(NetClientState *peer,
const char *lhost)
{
NetSocketState *s;
- int fd, val, ret;
+ int fd, ret;
struct sockaddr_in laddr, raddr;
if (parse_host_port(&laddr, lhost) < 0) {
@@ -661,11 +664,9 @@ static int net_socket_udp_init(NetClientState *peer,
perror("socket(PF_INET, SOCK_DGRAM)");
return -1;
}
- val = 1;
- ret = qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
- &val, sizeof(val));
+
+ ret = socket_set_fast_reuse(fd);
if (ret < 0) {
- perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
closesocket(fd);
return -1;
}
diff --git a/pc-bios/README b/pc-bios/README
index e404a228a4..be8dae0aa9 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -12,7 +12,7 @@
1275-1994 (referred to as Open Firmware) compliant firmware.
The included images for PowerPC (for 32 and 64 bit PPC CPUs),
Sparc32 and Sparc64 are built from OpenBIOS SVN revision
- 1198.
+ 1229.
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
@@ -23,7 +23,7 @@
legacy x86 software to communicate with an attached serial console as
if a video card were attached. The master sources reside in a subversion
repository at http://sgabios.googlecode.com/svn/trunk. A git mirror is
- available at git://git.qemu.org/sgabios.git.
+ available at git://git.qemu-project.org/sgabios.git.
- The PXE roms come from the iPXE project. Built with BANNER_TIME 0.
Sources available at http://ipxe.org. Vendor:Device ID -> ROM mapping:
diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc
index c6b3319fab..550273a5ef 100644
--- a/pc-bios/openbios-ppc
+++ b/pc-bios/openbios-ppc
Binary files differ
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
index 2aa400cfd9..01105fc904 100644
--- a/pc-bios/openbios-sparc32
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64
index f6ee286034..62c9e77983 100644
--- a/pc-bios/openbios-sparc64
+++ b/pc-bios/openbios-sparc64
Binary files differ
diff --git a/qapi-schema.json b/qapi-schema.json
index 145eca8855..60f3fd1db6 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -210,6 +210,34 @@
'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
##
+# @ImageInfoSpecificQCow2:
+#
+# @compat: compatibility level
+#
+# @lazy-refcounts: #optional on or off; only valid for compat >= 1.1
+#
+# Since: 1.7
+##
+{ 'type': 'ImageInfoSpecificQCow2',
+ 'data': {
+ 'compat': 'str',
+ '*lazy-refcounts': 'bool'
+ } }
+
+##
+# @ImageInfoSpecific:
+#
+# A discriminated record of image format specific information structures.
+#
+# Since: 1.7
+##
+
+{ 'union': 'ImageInfoSpecific',
+ 'data': {
+ 'qcow2': 'ImageInfoSpecificQCow2'
+ } }
+
+##
# @ImageInfo:
#
# Information about a QEMU image file
@@ -238,6 +266,9 @@
#
# @backing-image: #optional info of the backing image (since 1.6)
#
+# @format-specific: #optional structure supplying additional format-specific
+# information (since 1.7)
+#
# Since: 1.3
#
##
@@ -248,7 +279,8 @@
'*cluster-size': 'int', '*encrypted': 'bool',
'*backing-filename': 'str', '*full-backing-filename': 'str',
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'],
- '*backing-image': 'ImageInfo' } }
+ '*backing-image': 'ImageInfo',
+ '*format-specific': 'ImageInfoSpecific' } }
##
# @ImageCheck:
@@ -1366,6 +1398,24 @@
'data': ['top', 'full', 'none'] }
##
+# @BlockJobType:
+#
+# Type of a block job.
+#
+# @commit: block commit job type, see "block-commit"
+#
+# @stream: block stream job type, see "block-stream"
+#
+# @mirror: drive mirror job type, see "drive-mirror"
+#
+# @backup: drive backup job type, see "drive-backup"
+#
+# Since: 1.7
+##
+{ 'enum': 'BlockJobType',
+ 'data': ['commit', 'stream', 'mirror', 'backup'] }
+
+##
# @BlockJobInfo:
#
# Information about a long-running block device operation.
@@ -3902,3 +3952,239 @@
##
{ 'command': 'query-rx-filter', 'data': { '*name': 'str' },
'returns': ['RxFilterInfo'] }
+
+
+##
+# @BlockdevDiscardOptions
+#
+# Determines how to handle discard requests.
+#
+# @ignore: Ignore the request
+# @unmap: Forward as an unmap request
+#
+# Since: 1.7
+##
+{ 'enum': 'BlockdevDiscardOptions',
+ 'data': [ 'ignore', 'unmap' ] }
+
+##
+# @BlockdevAioOptions
+#
+# Selects the AIO backend to handle I/O requests
+#
+# @threads: Use qemu's thread pool
+# @native: Use native AIO backend (only Linux and Windows)
+#
+# Since: 1.7
+##
+{ 'enum': 'BlockdevAioOptions',
+ 'data': [ 'threads', 'native' ] }
+
+##
+# @BlockdevCacheOptions
+#
+# Includes cache-related options for block devices
+#
+# @writeback: #optional enables writeback mode for any caches (default: true)
+# @direct: #optional enables use of O_DIRECT (bypass the host page cache;
+# default: false)
+# @no-flush: #optional ignore any flush requests for the device (default:
+# false)
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevCacheOptions',
+ 'data': { '*writeback': 'bool',
+ '*direct': 'bool',
+ '*no-flush': 'bool' } }
+
+##
+# @BlockdevOptionsBase
+#
+# Options that are available for all block devices, independent of the block
+# driver.
+#
+# @driver: block driver name
+# @id: #optional id by which the new block device can be referred to.
+# This is a required option on the top level of blockdev-add, and
+# currently not allowed on any other level.
+# @discard: #optional discard-related options (default: ignore)
+# @cache: #optional cache-related options
+# @aio: #optional AIO backend (default: threads)
+# @rerror: #optional how to handle read errors on the device
+# (default: report)
+# @werror: #optional how to handle write errors on the device
+# (default: enospc)
+# @read-only: #optional whether the block device should be read-only
+# (default: false)
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsBase',
+ 'data': { 'driver': 'str',
+ '*id': 'str',
+ '*discard': 'BlockdevDiscardOptions',
+ '*cache': 'BlockdevCacheOptions',
+ '*aio': 'BlockdevAioOptions',
+ '*rerror': 'BlockdevOnError',
+ '*werror': 'BlockdevOnError',
+ '*read-only': 'bool' } }
+
+##
+# @BlockdevOptionsFile
+#
+# Driver specific block device options for the file backend and similar
+# protocols.
+#
+# @filename: path to the image file
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsFile',
+ 'data': { 'filename': 'str' } }
+
+##
+# @BlockdevOptionsVVFAT
+#
+# Driver specific block device options for the vvfat protocol.
+#
+# @dir: directory to be exported as FAT image
+# @fat-type: #optional FAT type: 12, 16 or 32
+# @floppy: #optional whether to export a floppy image (true) or
+# partitioned hard disk (false; default)
+# @rw: #optional whether to allow write operations (default: false)
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsVVFAT',
+ 'data': { 'dir': 'str', '*fat-type': 'int', '*floppy': 'bool',
+ '*rw': 'bool' } }
+
+##
+# @BlockdevOptionsGenericFormat
+#
+# Driver specific block device options for image format that have no option
+# besides their data source.
+#
+# @file: reference to or definition of the data source block device
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsGenericFormat',
+ 'data': { 'file': 'BlockdevRef' } }
+
+##
+# @BlockdevOptionsGenericCOWFormat
+#
+# Driver specific block device options for image format that have no option
+# besides their data source and an optional backing file.
+#
+# @backing: #optional reference to or definition of the backing file block
+# device (if missing, taken from the image file content). It is
+# allowed to pass an empty string here in order to disable the
+# default backing file.
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsGenericCOWFormat',
+ 'base': 'BlockdevOptionsGenericFormat',
+ 'data': { '*backing': 'BlockdevRef' } }
+
+##
+# @BlockdevOptionsQcow2
+#
+# Driver specific block device options for qcow2.
+#
+# @lazy-refcounts: #optional whether to enable the lazy refcounts
+# feature (default is taken from the image file)
+#
+# @pass-discard-request: #optional whether discard requests to the qcow2
+# device should be forwarded to the data source
+#
+# @pass-discard-snapshot: #optional whether discard requests for the data source
+# should be issued when a snapshot operation (e.g.
+# deleting a snapshot) frees clusters in the qcow2 file
+#
+# @pass-discard-other: #optional whether discard requests for the data source
+# should be issued on other occasions where a cluster
+# gets freed
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsQcow2',
+ 'base': 'BlockdevOptionsGenericCOWFormat',
+ 'data': { '*lazy-refcounts': 'bool',
+ '*pass-discard-request': 'bool',
+ '*pass-discard-snapshot': 'bool',
+ '*pass-discard-other': 'bool' } }
+
+##
+# @BlockdevOptions
+#
+# Options for creating a block device.
+#
+# Since: 1.7
+##
+{ 'union': 'BlockdevOptions',
+ 'base': 'BlockdevOptionsBase',
+ 'discriminator': 'driver',
+ 'data': {
+ 'file': 'BlockdevOptionsFile',
+ 'http': 'BlockdevOptionsFile',
+ 'https': 'BlockdevOptionsFile',
+ 'ftp': 'BlockdevOptionsFile',
+ 'ftps': 'BlockdevOptionsFile',
+ 'tftp': 'BlockdevOptionsFile',
+# TODO gluster: Wait for structured options
+# TODO iscsi: Wait for structured options
+# TODO nbd: Should take InetSocketAddress for 'host'?
+# TODO rbd: Wait for structured options
+# TODO sheepdog: Wait for structured options
+# TODO ssh: Should take InetSocketAddress for 'host'?
+ 'vvfat': 'BlockdevOptionsVVFAT',
+
+# TODO blkdebug: Wait for structured options
+# TODO blkverify: Wait for structured options
+
+ 'bochs': 'BlockdevOptionsGenericFormat',
+ 'cloop': 'BlockdevOptionsGenericFormat',
+ 'cow': 'BlockdevOptionsGenericCOWFormat',
+ 'dmg': 'BlockdevOptionsGenericFormat',
+ 'parallels': 'BlockdevOptionsGenericFormat',
+ 'qcow': 'BlockdevOptionsGenericCOWFormat',
+ 'qcow2': 'BlockdevOptionsQcow2',
+ 'qed': 'BlockdevOptionsGenericCOWFormat',
+ 'raw': 'BlockdevOptionsGenericFormat',
+ 'vdi': 'BlockdevOptionsGenericFormat',
+ 'vhdx': 'BlockdevOptionsGenericFormat',
+ 'vmdk': 'BlockdevOptionsGenericCOWFormat',
+ 'vpc': 'BlockdevOptionsGenericFormat'
+ } }
+
+##
+# @BlockdevRef
+#
+# Reference to a block device.
+#
+# @definition: defines a new block device inline
+# @reference: references the ID of an existing block device. An
+# empty string means that no block device should be
+# referenced.
+#
+# Since: 1.7
+##
+{ 'union': 'BlockdevRef',
+ 'discriminator': {},
+ 'data': { 'definition': 'BlockdevOptions',
+ 'reference': 'str' } }
+
+##
+# @blockdev-add:
+#
+# Creates a new block device.
+#
+# @options: block device options for the new device
+#
+# Since: 1.7
+##
+{ 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c
index 28bbbe849e..3e4498a3f6 100644
--- a/qapi/qmp-registry.c
+++ b/qapi/qmp-registry.c
@@ -66,35 +66,26 @@ void qmp_enable_command(const char *name)
qmp_toggle_command(name, true);
}
-bool qmp_command_is_enabled(const char *name)
+bool qmp_command_is_enabled(const QmpCommand *cmd)
{
- QmpCommand *cmd;
+ return cmd->enabled;
+}
- QTAILQ_FOREACH(cmd, &qmp_commands, node) {
- if (strcmp(cmd->name, name) == 0) {
- return cmd->enabled;
- }
- }
+const char *qmp_command_name(const QmpCommand *cmd)
+{
+ return cmd->name;
+}
- return false;
+bool qmp_has_success_response(const QmpCommand *cmd)
+{
+ return !(cmd->options & QCO_NO_SUCCESS_RESP);
}
-char **qmp_get_command_list(void)
+void qmp_for_each_command(qmp_cmd_callback_fn fn, void *opaque)
{
QmpCommand *cmd;
- int count = 1;
- char **list_head, **list;
QTAILQ_FOREACH(cmd, &qmp_commands, node) {
- count++;
+ fn(cmd, opaque);
}
-
- list_head = list = g_malloc0(count * sizeof(char *));
-
- QTAILQ_FOREACH(cmd, &qmp_commands, node) {
- *list = g_strdup(cmd->name);
- list++;
- }
-
- return list_head;
}
diff --git a/qdev-monitor.c b/qdev-monitor.c
index 410cdcbe97..a02c925cb3 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -75,14 +75,8 @@ static bool qdev_class_has_alias(DeviceClass *dc)
return (qdev_class_get_alias(dc) != NULL);
}
-static void qdev_print_class_devinfo(DeviceClass *dc)
+static void qdev_print_devinfo(DeviceClass *dc)
{
- DeviceCategory category;
-
- if (!dc) {
- return;
- }
-
error_printf("name \"%s\"", object_class_get_name(OBJECT_CLASS(dc)));
if (dc->bus_type) {
error_printf(", bus %s", dc->bus_type);
@@ -90,12 +84,6 @@ static void qdev_print_class_devinfo(DeviceClass *dc)
if (qdev_class_has_alias(dc)) {
error_printf(", alias \"%s\"", qdev_class_get_alias(dc));
}
- error_printf(", categories");
- for (category = 0; category < DEVICE_CATEGORY_MAX; ++category) {
- if (test_bit(category, dc->categories)) {
- error_printf(" \"%s\"", qdev_category_get_name(category));
- }
- }
if (dc->desc) {
error_printf(", desc \"%s\"", dc->desc);
}
@@ -105,13 +93,53 @@ static void qdev_print_class_devinfo(DeviceClass *dc)
error_printf("\n");
}
-static void qdev_print_devinfo(ObjectClass *klass, void *opaque)
+static gint devinfo_cmp(gconstpointer a, gconstpointer b)
{
- DeviceClass *dc;
+ return strcasecmp(object_class_get_name((ObjectClass *)a),
+ object_class_get_name((ObjectClass *)b));
+}
- dc = (DeviceClass *)object_class_dynamic_cast(klass, TYPE_DEVICE);
+static void qdev_print_devinfos(bool show_no_user)
+{
+ static const char *cat_name[DEVICE_CATEGORY_MAX + 1] = {
+ [DEVICE_CATEGORY_BRIDGE] = "Controller/Bridge/Hub",
+ [DEVICE_CATEGORY_USB] = "USB",
+ [DEVICE_CATEGORY_STORAGE] = "Storage",
+ [DEVICE_CATEGORY_NETWORK] = "Network",
+ [DEVICE_CATEGORY_INPUT] = "Input",
+ [DEVICE_CATEGORY_DISPLAY] = "Display",
+ [DEVICE_CATEGORY_SOUND] = "Sound",
+ [DEVICE_CATEGORY_MISC] = "Misc",
+ [DEVICE_CATEGORY_MAX] = "Uncategorized",
+ };
+ GSList *list, *elt;
+ int i;
+ bool cat_printed;
+
+ list = g_slist_sort(object_class_get_list(TYPE_DEVICE, false),
+ devinfo_cmp);
+
+ for (i = 0; i <= DEVICE_CATEGORY_MAX; i++) {
+ cat_printed = false;
+ for (elt = list; elt; elt = elt->next) {
+ DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data,
+ TYPE_DEVICE);
+ if ((i < DEVICE_CATEGORY_MAX
+ ? !test_bit(i, dc->categories)
+ : !bitmap_empty(dc->categories, DEVICE_CATEGORY_MAX))
+ || (!show_no_user && dc->no_user)) {
+ continue;
+ }
+ if (!cat_printed) {
+ error_printf("%s%s devices:\n", i ? "\n" : "",
+ cat_name[i]);
+ cat_printed = true;
+ }
+ qdev_print_devinfo(dc);
+ }
+ }
- qdev_print_class_devinfo(dc);
+ g_slist_free(list);
}
static int set_property(const char *name, const char *value, void *opaque)
@@ -151,21 +179,6 @@ static const char *find_typename_by_alias(const char *alias)
return NULL;
}
-static void qdev_print_category_devices(DeviceCategory category)
-{
- DeviceClass *dc;
- GSList *list, *curr;
-
- list = object_class_get_list(TYPE_DEVICE, false);
- for (curr = list; curr; curr = g_slist_next(curr)) {
- dc = (DeviceClass *)object_class_dynamic_cast(curr->data, TYPE_DEVICE);
- if (!dc->no_user && test_bit(category, dc->categories)) {
- qdev_print_class_devinfo(dc);
- }
- }
- g_slist_free(list);
-}
-
int qdev_device_help(QemuOpts *opts)
{
const char *driver;
@@ -174,11 +187,7 @@ int qdev_device_help(QemuOpts *opts)
driver = qemu_opt_get(opts, "driver");
if (driver && is_help_option(driver)) {
- DeviceCategory category;
- for (category = 0; category < DEVICE_CATEGORY_MAX; ++category) {
- qdev_print_category_devices(category);
- }
-
+ qdev_print_devinfos(false);
return 1;
}
@@ -617,7 +626,7 @@ void do_info_qtree(Monitor *mon, const QDict *qdict)
void do_info_qdm(Monitor *mon, const QDict *qdict)
{
- object_class_foreach(qdev_print_devinfo, TYPE_DEVICE, false, NULL);
+ qdev_print_devinfos(true);
}
int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
diff --git a/qemu-char.c b/qemu-char.c
index f7f5464b67..e00f84c8e9 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -193,6 +193,8 @@ void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...)
va_end(ap);
}
+static void remove_fd_in_watch(CharDriverState *chr);
+
void qemu_chr_add_handlers(CharDriverState *s,
IOCanReadHandler *fd_can_read,
IOReadHandler *fd_read,
@@ -203,6 +205,7 @@ void qemu_chr_add_handlers(CharDriverState *s,
if (!opaque && !fd_can_read && !fd_read && !fd_event) {
fe_open = 0;
+ remove_fd_in_watch(s);
} else {
fe_open = 1;
}
@@ -725,6 +728,14 @@ static void io_remove_watch_poll(guint tag)
g_source_destroy(&iwp->parent);
}
+static void remove_fd_in_watch(CharDriverState *chr)
+{
+ if (chr->fd_in_tag) {
+ io_remove_watch_poll(chr->fd_in_tag);
+ chr->fd_in_tag = 0;
+ }
+}
+
#ifndef _WIN32
static GIOChannel *io_channel_from_fd(int fd)
{
@@ -798,7 +809,6 @@ static int io_channel_send(GIOChannel *fd, const void *buf, size_t len)
typedef struct FDCharDriver {
CharDriverState *chr;
GIOChannel *fd_in, *fd_out;
- guint fd_in_tag;
int max_size;
QTAILQ_ENTRY(FDCharDriver) node;
} FDCharDriver;
@@ -830,10 +840,7 @@ static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
status = g_io_channel_read_chars(chan, (gchar *)buf,
len, &bytes_read, NULL);
if (status == G_IO_STATUS_EOF) {
- if (s->fd_in_tag) {
- io_remove_watch_poll(s->fd_in_tag);
- s->fd_in_tag = 0;
- }
+ remove_fd_in_watch(chr);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
return FALSE;
}
@@ -863,13 +870,10 @@ static void fd_chr_update_read_handler(CharDriverState *chr)
{
FDCharDriver *s = chr->opaque;
- if (s->fd_in_tag) {
- io_remove_watch_poll(s->fd_in_tag);
- s->fd_in_tag = 0;
- }
-
+ remove_fd_in_watch(chr);
if (s->fd_in) {
- s->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll, fd_chr_read, chr);
+ chr->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll,
+ fd_chr_read, chr);
}
}
@@ -877,11 +881,7 @@ static void fd_chr_close(struct CharDriverState *chr)
{
FDCharDriver *s = chr->opaque;
- if (s->fd_in_tag) {
- io_remove_watch_poll(s->fd_in_tag);
- s->fd_in_tag = 0;
- }
-
+ remove_fd_in_watch(chr);
if (s->fd_in) {
g_io_channel_unref(s->fd_in);
}
@@ -1012,7 +1012,6 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
typedef struct {
GIOChannel *fd;
- guint fd_tag;
int connected;
int read_bytes;
guint timer_tag;
@@ -1123,10 +1122,7 @@ static void pty_chr_state(CharDriverState *chr, int connected)
PtyCharDriver *s = chr->opaque;
if (!connected) {
- if (s->fd_tag) {
- io_remove_watch_poll(s->fd_tag);
- s->fd_tag = 0;
- }
+ remove_fd_in_watch(chr);
s->connected = 0;
/* (re-)connect poll interval for idle guests: once per second.
* We check more frequently in case the guests sends data to
@@ -1140,7 +1136,8 @@ static void pty_chr_state(CharDriverState *chr, int connected)
if (!s->connected) {
s->connected = 1;
qemu_chr_be_generic_open(chr);
- s->fd_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, pty_chr_read, chr);
+ chr->fd_in_tag = io_add_watch_poll(s->fd, pty_chr_read_poll,
+ pty_chr_read, chr);
}
}
}
@@ -1151,10 +1148,7 @@ static void pty_chr_close(struct CharDriverState *chr)
PtyCharDriver *s = chr->opaque;
int fd;
- if (s->fd_tag) {
- io_remove_watch_poll(s->fd_tag);
- s->fd_tag = 0;
- }
+ remove_fd_in_watch(chr);
fd = g_io_channel_unix_get_fd(s->fd);
g_io_channel_unref(s->fd);
close(fd);
@@ -2161,7 +2155,6 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
typedef struct {
int fd;
GIOChannel *chan;
- guint tag;
uint8_t buf[READ_BUF_LEN];
int bufcnt;
int bufptr;
@@ -2217,10 +2210,7 @@ static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
s->bufcnt = bytes_read;
s->bufptr = s->bufcnt;
if (status != G_IO_STATUS_NORMAL) {
- if (s->tag) {
- io_remove_watch_poll(s->tag);
- s->tag = 0;
- }
+ remove_fd_in_watch(chr);
return FALSE;
}
@@ -2238,23 +2228,18 @@ static void udp_chr_update_read_handler(CharDriverState *chr)
{
NetCharDriver *s = chr->opaque;
- if (s->tag) {
- io_remove_watch_poll(s->tag);
- s->tag = 0;
- }
-
+ remove_fd_in_watch(chr);
if (s->chan) {
- s->tag = io_add_watch_poll(s->chan, udp_chr_read_poll, udp_chr_read, chr);
+ chr->fd_in_tag = io_add_watch_poll(s->chan, udp_chr_read_poll,
+ udp_chr_read, chr);
}
}
static void udp_chr_close(CharDriverState *chr)
{
NetCharDriver *s = chr->opaque;
- if (s->tag) {
- io_remove_watch_poll(s->tag);
- s->tag = 0;
- }
+
+ remove_fd_in_watch(chr);
if (s->chan) {
g_io_channel_unref(s->chan);
closesocket(s->fd);
@@ -2304,7 +2289,7 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
typedef struct {
GIOChannel *chan, *listen_chan;
- guint tag, listen_tag;
+ guint listen_tag;
int fd, listen_fd;
int connected;
int max_size;
@@ -2489,10 +2474,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
if (s->listen_chan) {
s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
}
- if (s->tag) {
- io_remove_watch_poll(s->tag);
- s->tag = 0;
- }
+ remove_fd_in_watch(chr);
g_io_channel_unref(s->chan);
s->chan = NULL;
closesocket(s->fd);
@@ -2522,7 +2504,8 @@ static void tcp_chr_connect(void *opaque)
s->connected = 1;
if (s->chan) {
- s->tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, tcp_chr_read, chr);
+ chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll,
+ tcp_chr_read, chr);
}
qemu_chr_be_generic_open(chr);
}
@@ -2605,10 +2588,7 @@ static void tcp_chr_close(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
if (s->fd >= 0) {
- if (s->tag) {
- io_remove_watch_poll(s->tag);
- s->tag = 0;
- }
+ remove_fd_in_watch(chr);
if (s->chan) {
g_io_channel_unref(s->chan);
}
@@ -2989,11 +2969,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
if (strstart(filename, "vc", &p)) {
qemu_opt_set(opts, "backend", "vc");
if (*p == ':') {
- if (sscanf(p+1, "%8[0-9]x%8[0-9]", width, height) == 2) {
+ if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) {
/* pixels */
qemu_opt_set(opts, "width", width);
qemu_opt_set(opts, "height", height);
- } else if (sscanf(p+1, "%8[0-9]Cx%8[0-9]C", width, height) == 2) {
+ } else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) {
/* chars */
qemu_opt_set(opts, "cols", width);
qemu_opt_set(opts, "rows", height);
@@ -3271,7 +3251,12 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
backend->kind = CHARDEV_BACKEND_KIND_MUX;
backend->mux->chardev = g_strdup(bid);
ret = qmp_chardev_add(id, backend, errp);
- assert(!error_is_set(errp));
+ if (error_is_set(errp)) {
+ chr = qemu_chr_find(bid);
+ qemu_chr_delete(chr);
+ chr = NULL;
+ goto qapi_out;
+ }
}
chr = qemu_chr_find(id);
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 8565d49336..667f4e4f3a 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -10,6 +10,7 @@
#include "qemu-io.h"
#include "block/block_int.h"
+#include "block/qapi.h"
#include "qemu/main-loop.h"
#define CMD_NOFILE_OK 0x01
@@ -1678,6 +1679,7 @@ static const cmdinfo_t length_cmd = {
static int info_f(BlockDriverState *bs, int argc, char **argv)
{
BlockDriverInfo bdi;
+ ImageInfoSpecific *spec_info;
char s1[64], s2[64];
int ret;
@@ -1699,6 +1701,13 @@ static int info_f(BlockDriverState *bs, int argc, char **argv)
printf("cluster size: %s\n", s1);
printf("vm state offset: %s\n", s2);
+ spec_info = bdrv_get_specific_info(bs);
+ if (spec_info) {
+ printf("Format specific information:\n");
+ bdrv_image_info_specific_dump(fprintf, stdout, spec_info);
+ qapi_free_ImageInfoSpecific(spec_info);
+ }
+
return 0;
}
diff --git a/qemu-io.c b/qemu-io.c
index f4b8efcceb..3b3340ab1b 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -16,6 +16,8 @@
#include "qemu-io.h"
#include "qemu/main-loop.h"
+#include "qemu/option.h"
+#include "qemu/config-file.h"
#include "block/block_int.h"
#include "trace/control.h"
@@ -44,7 +46,7 @@ static const cmdinfo_t close_cmd = {
.oneline = "close the current open file",
};
-static int openfile(char *name, int flags, int growable)
+static int openfile(char *name, int flags, int growable, QDict *opts)
{
Error *local_err = NULL;
@@ -54,7 +56,7 @@ static int openfile(char *name, int flags, int growable)
}
if (growable) {
- if (bdrv_file_open(&qemuio_bs, name, NULL, flags, &local_err)) {
+ if (bdrv_file_open(&qemuio_bs, name, opts, flags, &local_err)) {
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
error_get_pretty(local_err));
error_free(local_err);
@@ -63,7 +65,7 @@ static int openfile(char *name, int flags, int growable)
} else {
qemuio_bs = bdrv_new("hda");
- if (bdrv_open(qemuio_bs, name, NULL, flags, NULL, &local_err) < 0) {
+ if (bdrv_open(qemuio_bs, name, opts, flags, NULL, &local_err) < 0) {
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
error_get_pretty(local_err));
error_free(local_err);
@@ -89,7 +91,8 @@ static void open_help(void)
" -r, -- open file read-only\n"
" -s, -- use snapshot file\n"
" -n, -- disable host cache\n"
-" -g, -- allow file to grow (only applies to protocols)"
+" -g, -- allow file to grow (only applies to protocols)\n"
+" -o, -- options to be given to the block driver"
"\n");
}
@@ -102,19 +105,30 @@ static const cmdinfo_t open_cmd = {
.argmin = 1,
.argmax = -1,
.flags = CMD_NOFILE_OK,
- .args = "[-Crsn] [path]",
+ .args = "[-Crsn] [-o options] [path]",
.oneline = "open the file specified by path",
.help = open_help,
};
+static QemuOptsList empty_opts = {
+ .name = "drive",
+ .head = QTAILQ_HEAD_INITIALIZER(empty_opts.head),
+ .desc = {
+ /* no elements => accept any params */
+ { /* end of list */ }
+ },
+};
+
static int open_f(BlockDriverState *bs, int argc, char **argv)
{
int flags = 0;
int readonly = 0;
int growable = 0;
int c;
+ QemuOpts *qopts;
+ QDict *opts = NULL;
- while ((c = getopt(argc, argv, "snrg")) != EOF) {
+ while ((c = getopt(argc, argv, "snrgo:")) != EOF) {
switch (c) {
case 's':
flags |= BDRV_O_SNAPSHOT;
@@ -128,6 +142,15 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
case 'g':
growable = 1;
break;
+ case 'o':
+ qopts = qemu_opts_parse(&empty_opts, optarg, 0);
+ if (qopts == NULL) {
+ printf("could not parse option list -- %s\n", optarg);
+ return 0;
+ }
+ opts = qemu_opts_to_qdict(qopts, opts);
+ qemu_opts_del(qopts);
+ break;
default:
return qemuio_command_usage(&open_cmd);
}
@@ -141,7 +164,7 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
return qemuio_command_usage(&open_cmd);
}
- return openfile(argv[optind], flags, growable);
+ return openfile(argv[optind], flags, growable, opts);
}
static int quit_f(BlockDriverState *bs, int argc, char **argv)
@@ -418,7 +441,7 @@ int main(int argc, char **argv)
}
if ((argc - optind) == 1) {
- openfile(argv[optind], flags, growable);
+ openfile(argv[optind], flags, growable, NULL);
}
command_loop();
diff --git a/qemu-seccomp.c b/qemu-seccomp.c
index 37d38f881c..69cee443af 100644
--- a/qemu-seccomp.c
+++ b/qemu-seccomp.c
@@ -90,6 +90,7 @@ static const struct QemuSeccompSyscall seccomp_whitelist[] = {
{ SCMP_SYS(getuid), 245 },
{ SCMP_SYS(geteuid), 245 },
{ SCMP_SYS(timer_create), 245 },
+ { SCMP_SYS(times), 245 },
{ SCMP_SYS(exit), 245 },
{ SCMP_SYS(clock_gettime), 245 },
{ SCMP_SYS(time), 245 },
diff --git a/qemu.nsi b/qemu.nsi
index 1d57455956..0dc1f52693 100644
--- a/qemu.nsi
+++ b/qemu.nsi
@@ -20,7 +20,7 @@
; NSIS_WIN32_MAKENSIS
!define PRODUCT "QEMU"
-!define URL "http://www.qemu.org/"
+!define URL "http://www.qemu-project.org/"
!define UNINST_EXE "$INSTDIR\qemu-uninstall.exe"
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT}"
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index e199738c71..f453132b92 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -566,7 +566,7 @@ typedef struct FsMount {
QTAILQ_ENTRY(FsMount) next;
} FsMount;
-typedef QTAILQ_HEAD(, FsMount) FsMountList;
+typedef QTAILQ_HEAD(FsMountList, FsMount) FsMountList;
static void free_fs_mount_list(FsMountList *mounts)
{
@@ -728,7 +728,7 @@ int64_t qmp_guest_fsfreeze_freeze(Error **err)
/* cannot risk guest agent blocking itself on a write in this state */
ga_set_frozen(ga_state);
- QTAILQ_FOREACH(mount, &mounts, next) {
+ QTAILQ_FOREACH_REVERSE(mount, &mounts, FsMountList, next) {
fd = qemu_open(mount->dirname, O_RDONLY);
if (fd == -1) {
error_setg_errno(err, errno, "failed to open %s", mount->dirname);
diff --git a/qga/commands.c b/qga/commands.c
index 528b082fa8..a0c2de07ec 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -45,35 +45,28 @@ void qmp_guest_ping(Error **err)
slog("guest-ping called");
}
-struct GuestAgentInfo *qmp_guest_info(Error **err)
+static void qmp_command_info(QmpCommand *cmd, void *opaque)
{
- GuestAgentInfo *info = g_malloc0(sizeof(GuestAgentInfo));
+ GuestAgentInfo *info = opaque;
GuestAgentCommandInfo *cmd_info;
GuestAgentCommandInfoList *cmd_info_list;
- char **cmd_list_head, **cmd_list;
-
- info->version = g_strdup(QEMU_VERSION);
-
- cmd_list_head = cmd_list = qmp_get_command_list();
- if (*cmd_list_head == NULL) {
- goto out;
- }
- while (*cmd_list) {
- cmd_info = g_malloc0(sizeof(GuestAgentCommandInfo));
- cmd_info->name = g_strdup(*cmd_list);
- cmd_info->enabled = qmp_command_is_enabled(cmd_info->name);
+ cmd_info = g_malloc0(sizeof(GuestAgentCommandInfo));
+ cmd_info->name = g_strdup(qmp_command_name(cmd));
+ cmd_info->enabled = qmp_command_is_enabled(cmd);
+ cmd_info->success_response = qmp_has_success_response(cmd);
- cmd_info_list = g_malloc0(sizeof(GuestAgentCommandInfoList));
- cmd_info_list->value = cmd_info;
- cmd_info_list->next = info->supported_commands;
- info->supported_commands = cmd_info_list;
+ cmd_info_list = g_malloc0(sizeof(GuestAgentCommandInfoList));
+ cmd_info_list->value = cmd_info;
+ cmd_info_list->next = info->supported_commands;
+ info->supported_commands = cmd_info_list;
+}
- g_free(*cmd_list);
- cmd_list++;
- }
+struct GuestAgentInfo *qmp_guest_info(Error **err)
+{
+ GuestAgentInfo *info = g_malloc0(sizeof(GuestAgentInfo));
-out:
- g_free(cmd_list_head);
+ info->version = g_strdup(QEMU_VERSION);
+ qmp_for_each_command(qmp_command_info, info);
return info;
}
diff --git a/qga/main.c b/qga/main.c
index 6c746c8f3a..c58b26a9a0 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -347,48 +347,35 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2)
}
/* disable commands that aren't safe for fsfreeze */
-static void ga_disable_non_whitelisted(void)
+static void ga_disable_non_whitelisted(QmpCommand *cmd, void *opaque)
{
- char **list_head, **list;
- bool whitelisted;
- int i;
-
- list_head = list = qmp_get_command_list();
- while (*list != NULL) {
- whitelisted = false;
- i = 0;
- while (ga_freeze_whitelist[i] != NULL) {
- if (strcmp(*list, ga_freeze_whitelist[i]) == 0) {
- whitelisted = true;
- }
- i++;
- }
- if (!whitelisted) {
- g_debug("disabling command: %s", *list);
- qmp_disable_command(*list);
+ bool whitelisted = false;
+ int i = 0;
+ const char *name = qmp_command_name(cmd);
+
+ while (ga_freeze_whitelist[i] != NULL) {
+ if (strcmp(name, ga_freeze_whitelist[i]) == 0) {
+ whitelisted = true;
}
- g_free(*list);
- list++;
+ i++;
+ }
+ if (!whitelisted) {
+ g_debug("disabling command: %s", name);
+ qmp_disable_command(name);
}
- g_free(list_head);
}
/* [re-]enable all commands, except those explicitly blacklisted by user */
-static void ga_enable_non_blacklisted(GList *blacklist)
+static void ga_enable_non_blacklisted(QmpCommand *cmd, void *opaque)
{
- char **list_head, **list;
-
- list_head = list = qmp_get_command_list();
- while (*list != NULL) {
- if (g_list_find_custom(blacklist, *list, ga_strcmp) == NULL &&
- !qmp_command_is_enabled(*list)) {
- g_debug("enabling command: %s", *list);
- qmp_enable_command(*list);
- }
- g_free(*list);
- list++;
+ GList *blacklist = opaque;
+ const char *name = qmp_command_name(cmd);
+
+ if (g_list_find_custom(blacklist, name, ga_strcmp) == NULL &&
+ !qmp_command_is_enabled(cmd)) {
+ g_debug("enabling command: %s", name);
+ qmp_enable_command(name);
}
- g_free(list_head);
}
static bool ga_create_file(const char *path)
@@ -424,7 +411,7 @@ void ga_set_frozen(GAState *s)
return;
}
/* disable all non-whitelisted (for frozen state) commands */
- ga_disable_non_whitelisted();
+ qmp_for_each_command(ga_disable_non_whitelisted, NULL);
g_warning("disabling logging due to filesystem freeze");
ga_disable_logging(s);
s->frozen = true;
@@ -460,7 +447,7 @@ void ga_unset_frozen(GAState *s)
}
/* enable all disabled, non-blacklisted commands */
- ga_enable_non_blacklisted(s->blacklist);
+ qmp_for_each_command(ga_enable_non_blacklisted, s->blacklist);
s->frozen = false;
if (!ga_delete_file(s->state_filepath_isfrozen)) {
g_warning("unable to delete %s, fsfreeze may not function properly",
@@ -920,6 +907,11 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp)
return handle;
}
+static void ga_print_cmd(QmpCommand *cmd, void *opaque)
+{
+ printf("%s\n", qmp_command_name(cmd));
+}
+
int main(int argc, char **argv)
{
const char *sopt = "hVvdm:p:l:f:F::b:s:t:";
@@ -996,15 +988,8 @@ int main(int argc, char **argv)
daemonize = 1;
break;
case 'b': {
- char **list_head, **list;
if (is_help_option(optarg)) {
- list_head = list = qmp_get_command_list();
- while (*list != NULL) {
- printf("%s\n", *list);
- g_free(*list);
- list++;
- }
- g_free(list_head);
+ qmp_for_each_command(ga_print_cmd, NULL);
return 0;
}
for (j = 0, i = 0, len = strlen(optarg); i < len; i++) {
@@ -1126,7 +1111,7 @@ int main(int argc, char **argv)
s->deferred_options.log_filepath = log_filepath;
}
ga_disable_logging(s);
- ga_disable_non_whitelisted();
+ qmp_for_each_command(ga_disable_non_whitelisted, NULL);
} else {
if (daemonize) {
become_daemon(pid_filepath);
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 7155b7ab55..245f968bc2 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -141,10 +141,13 @@
#
# @enabled: whether command is currently enabled by guest admin
#
+# @success-response: whether command returns a response on success
+# (since 1.7)
+#
# Since 1.1.0
##
{ 'type': 'GuestAgentCommandInfo',
- 'data': { 'name': 'str', 'enabled': 'bool' } }
+ 'data': { 'name': 'str', 'enabled': 'bool', 'success-response': 'bool' } }
##
# @GuestAgentInfo
diff --git a/qmp-commands.hx b/qmp-commands.hx
index b17c46e0b1..fba15cdc3b 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -3240,3 +3240,58 @@ Example:
}
EQMP
+
+ {
+ .name = "blockdev-add",
+ .args_type = "options:q",
+ .mhandler.cmd_new = qmp_marshal_input_blockdev_add,
+ },
+
+SQMP
+blockdev-add
+------------
+
+Add a block device.
+
+Arguments:
+
+- "options": block driver options
+
+Example (1):
+
+-> { "execute": "blockdev-add",
+ "arguments": { "options" : { "driver": "qcow2",
+ "file": { "driver": "file",
+ "filename": "test.qcow2" } } } }
+<- { "return": {} }
+
+Example (2):
+
+-> { "execute": "blockdev-add",
+ "arguments": {
+ "options": {
+ "driver": "qcow2",
+ "id": "my_disk",
+ "discard": "unmap",
+ "cache": {
+ "direct": true,
+ "writeback": true
+ },
+ "file": {
+ "driver": "file",
+ "filename": "/tmp/test.qcow2"
+ },
+ "backing": {
+ "driver": "raw",
+ "file": {
+ "driver": "file",
+ "filename": "/dev/fdset/4"
+ }
+ }
+ }
+ }
+ }
+
+<- { "return": {} }
+
+EQMP
diff --git a/roms/openbios b/roms/openbios
-Subproject 0f3d51ef22ec9166beb3ed434d253029ed7cfe8
+Subproject d363cf50c50c268da7e6d0bf707adde1893d1ab
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl
index bf5342a08d..38334de875 100755
--- a/scripts/get_maintainer.pl
+++ b/scripts/get_maintainer.pl
@@ -1385,7 +1385,7 @@ sub vcs_exists {
warn("$P: No supported VCS found. Add --nogit to options?\n");
warn("Using a git repository produces better results.\n");
warn("Try latest git repository using:\n");
- warn("git clone git://git.qemu.org/qemu.git\n");
+ warn("git clone git://git.qemu-project.org/qemu.git\n");
$printed_novcs = 1;
}
return 0;
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 5222463893..4a1652b56f 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -71,7 +71,7 @@ def generate_struct_fields(members):
c_name=c_var(argname))
if structured:
push_indent()
- ret += generate_struct("", argname, argentry)
+ ret += generate_struct({ "field": argname, "data": argentry})
pop_indent()
else:
ret += mcgen('''
@@ -81,13 +81,22 @@ def generate_struct_fields(members):
return ret
-def generate_struct(structname, fieldname, members):
+def generate_struct(expr):
+
+ structname = expr.get('type', "")
+ fieldname = expr.get('field', "")
+ members = expr['data']
+ base = expr.get('base')
+
ret = mcgen('''
struct %(name)s
{
''',
name=structname)
+ if base:
+ ret += generate_struct_fields({'base': base})
+
ret += generate_struct_fields(members)
if len(fieldname):
@@ -417,7 +426,7 @@ if do_builtins:
for expr in exprs:
ret = "\n"
if expr.has_key('type'):
- ret += generate_struct(expr['type'], "", expr['data']) + "\n"
+ ret += generate_struct(expr) + "\n"
ret += generate_type_cleanup_decl(expr['type'] + "List")
fdef.write(generate_type_cleanup(expr['type'] + "List") + "\n")
ret += generate_type_cleanup_decl(expr['type'])
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 597cca4b66..c39e6284b8 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -17,7 +17,7 @@ import os
import getopt
import errno
-def generate_visit_struct_fields(name, field_prefix, fn_prefix, members):
+def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None):
substructs = []
ret = ''
full_name = name if not fn_prefix else "%s_%s" % (name, fn_prefix)
@@ -42,6 +42,19 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error *
name=name, full_name=full_name)
push_indent()
+ if base:
+ ret += mcgen('''
+visit_start_implicit_struct(m, obj ? (void**) &(*obj)->%(c_name)s : NULL, sizeof(%(type)s), &err);
+if (!err) {
+ visit_type_%(type)s_fields(m, obj ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, &err);
+ error_propagate(errp, err);
+ err = NULL;
+ visit_end_implicit_struct(m, &err);
+}
+''',
+ c_prefix=c_var(field_prefix),
+ type=type_name(base), c_name=c_var('base'))
+
for argname, argentry, optional, structured in parse_args(members):
if optional:
ret += mcgen('''
@@ -120,8 +133,13 @@ if (!err) {
''')
return ret
-def generate_visit_struct(name, members):
- ret = generate_visit_struct_fields(name, "", "", members)
+def generate_visit_struct(expr):
+
+ name = expr['type']
+ members = expr['data']
+ base = expr.get('base')
+
+ ret = generate_visit_struct_fields(name, "", "", members, base)
ret += mcgen('''
@@ -472,7 +490,7 @@ if do_builtins:
for expr in exprs:
if expr.has_key('type'):
- ret = generate_visit_struct(expr['type'], expr['data'])
+ ret = generate_visit_struct(expr)
ret += generate_visit_list(expr['type'], expr['data'])
fdef.write(ret)
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
index b5f7e7c5ff..9908f21093 100755
--- a/scripts/qmp/qemu-ga-client
+++ b/scripts/qmp/qemu-ga-client
@@ -33,7 +33,7 @@
# $ qemu-ga-client fsfreeze freeze
# 2 filesystems frozen
#
-# See also: http://wiki.qemu.org/Features/QAPI/GuestAgent
+# See also: http://wiki.qemu-project.org/Features/QAPI/GuestAgent
#
import base64
diff --git a/slirp/misc.c b/slirp/misc.c
index c0d489950a..6c1636f7b6 100644
--- a/slirp/misc.c
+++ b/slirp/misc.c
@@ -212,8 +212,7 @@ fork_exec(struct socket *so, const char *ex, int do_pty)
so->s = accept(s, (struct sockaddr *)&addr, &addrlen);
} while (so->s < 0 && errno == EINTR);
closesocket(s);
- opt = 1;
- qemu_setsockopt(so->s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
+ socket_set_fast_reuse(so->s);
opt = 1;
qemu_setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
qemu_set_nonblock(so->s);
diff --git a/slirp/socket.c b/slirp/socket.c
index 25d60e7a89..37ac5cf2fb 100644
--- a/slirp/socket.c
+++ b/slirp/socket.c
@@ -627,9 +627,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
addr.sin_port = hport;
if (((s = qemu_socket(AF_INET,SOCK_STREAM,0)) < 0) ||
-#ifndef _WIN32
- (qemu_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0) ||
-#endif
+ (socket_set_fast_reuse(s) < 0) ||
(bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) ||
(listen(s,1) < 0)) {
int tmperrno = errno; /* Don't clobber the real reason we failed */
diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c
index 043f28fcae..7571c5a282 100644
--- a/slirp/tcp_subr.c
+++ b/slirp/tcp_subr.c
@@ -337,8 +337,7 @@ int tcp_fconnect(struct socket *so)
struct sockaddr_in addr;
qemu_set_nonblock(s);
- opt = 1;
- qemu_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+ socket_set_fast_reuse(s);
opt = 1;
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt));
@@ -426,8 +425,7 @@ void tcp_connect(struct socket *inso)
return;
}
qemu_set_nonblock(s);
- opt = 1;
- qemu_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
+ socket_set_fast_reuse(s);
opt = 1;
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
socket_set_nodelay(s);
diff --git a/slirp/udp.c b/slirp/udp.c
index b105f871f3..8cc6cb66da 100644
--- a/slirp/udp.c
+++ b/slirp/udp.c
@@ -354,7 +354,7 @@ udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
{
struct sockaddr_in addr;
struct socket *so;
- socklen_t addrlen = sizeof(struct sockaddr_in), opt = 1;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
so = socreate(slirp);
if (!so) {
@@ -372,7 +372,7 @@ udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
udp_detach(so);
return NULL;
}
- qemu_setsockopt(so->s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
+ socket_set_fast_reuse(so->s);
getsockname(so->s,(struct sockaddr *)&addr,&addrlen);
so->so_fport = addr.sin_port;
diff --git a/target-alpha/cpu.c b/target-alpha/cpu.c
index cfad2ea121..a0d5d5bd93 100644
--- a/target-alpha/cpu.c
+++ b/target-alpha/cpu.c
@@ -131,7 +131,6 @@ static ObjectClass *alpha_cpu_class_by_name(const char *cpu_model)
AlphaCPU *cpu_alpha_init(const char *cpu_model)
{
AlphaCPU *cpu;
- CPUAlphaState *env;
ObjectClass *cpu_class;
cpu_class = alpha_cpu_class_by_name(cpu_model);
@@ -140,9 +139,6 @@ AlphaCPU *cpu_alpha_init(const char *cpu_model)
cpu_class = object_class_by_name(TYPE("ev67"));
}
cpu = ALPHA_CPU(object_new(object_class_get_name(cpu_class)));
- env = &cpu->env;
-
- env->cpu_model_str = cpu_model;
object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
diff --git a/target-alpha/helper.h b/target-alpha/helper.h
index 732b701d53..5a0e78cefb 100644
--- a/target-alpha/helper.h
+++ b/target-alpha/helper.h
@@ -114,7 +114,7 @@ DEF_HELPER_FLAGS_1(tbia, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_2(tbis, TCG_CALL_NO_RWG, void, env, i64)
DEF_HELPER_FLAGS_1(tb_flush, TCG_CALL_NO_RWG, void, env)
-DEF_HELPER_1(halt, void, i64);
+DEF_HELPER_1(halt, void, i64)
DEF_HELPER_FLAGS_0(get_vmtime, TCG_CALL_NO_RWG, i64)
DEF_HELPER_FLAGS_0(get_walltime, TCG_CALL_NO_RWG, i64)
diff --git a/target-alpha/translate.c b/target-alpha/translate.c
index 28ce4363f1..9cb8084057 100644
--- a/target-alpha/translate.c
+++ b/target-alpha/translate.c
@@ -140,10 +140,6 @@ void alpha_translate_init(void)
offsetof(CPUAlphaState, usp), "usp");
#endif
- /* register helpers */
-#define GEN_HELPER 2
-#include "helper.h"
-
done_init = 1;
}
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 2a98be7436..c63bbd7fc1 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -1749,7 +1749,6 @@ void register_cp_regs_for_features(ARMCPU *cpu)
ARMCPU *cpu_arm_init(const char *cpu_model)
{
ARMCPU *cpu;
- CPUARMState *env;
ObjectClass *oc;
oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
@@ -1757,8 +1756,6 @@ ARMCPU *cpu_arm_init(const char *cpu_model)
return NULL;
}
cpu = ARM_CPU(object_new(object_class_get_name(oc)));
- env = &cpu->env;
- env->cpu_model_str = cpu_model;
/* TODO this should be set centrally, once possible */
object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 63ae13acff..cac9564f5f 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -247,10 +247,10 @@ DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32)
DEF_HELPER_3(neon_qshl_s32, i32, env, i32, i32)
DEF_HELPER_3(neon_qshl_u64, i64, env, i64, i64)
DEF_HELPER_3(neon_qshl_s64, i64, env, i64, i64)
-DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32);
-DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32);
-DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32);
-DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64);
+DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64)
DEF_HELPER_3(neon_qrshl_u8, i32, env, i32, i32)
DEF_HELPER_3(neon_qrshl_s8, i32, env, i32, i32)
DEF_HELPER_3(neon_qrshl_u16, i32, env, i32, i32)
diff --git a/target-arm/iwmmxt_helper.c b/target-arm/iwmmxt_helper.c
index 7953b53f7e..e6cfa62da8 100644
--- a/target-arm/iwmmxt_helper.c
+++ b/target-arm/iwmmxt_helper.c
@@ -577,7 +577,7 @@ uint64_t HELPER(iwmmxt_rorl)(CPUARMState *env, uint64_t x, uint32_t n)
uint64_t HELPER(iwmmxt_rorq)(CPUARMState *env, uint64_t x, uint32_t n)
{
- x = (x >> n) | (x << (64 - n));
+ x = ror64(x, n);
env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
return x;
}
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 998bde268d..5f003e785e 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -115,9 +115,6 @@ void arm_translate_init(void)
#endif
a64_translate_init();
-
-#define GEN_HELPER 2
-#include "helper.h"
}
static inline TCGv_i32 load_cpu_offset(int offset)
diff --git a/target-cris/helper.h b/target-cris/helper.h
index 8e8365cf69..0ac31f5670 100644
--- a/target-cris/helper.h
+++ b/target-cris/helper.h
@@ -4,14 +4,14 @@ DEF_HELPER_2(raise_exception, void, env, i32)
DEF_HELPER_2(tlb_flush_pid, void, env, i32)
DEF_HELPER_2(spc_write, void, env, i32)
DEF_HELPER_3(dump, void, i32, i32, i32)
-DEF_HELPER_1(rfe, void, env);
-DEF_HELPER_1(rfn, void, env);
+DEF_HELPER_1(rfe, void, env)
+DEF_HELPER_1(rfn, void, env)
DEF_HELPER_3(movl_sreg_reg, void, env, i32, i32)
DEF_HELPER_3(movl_reg_sreg, void, env, i32, i32)
-DEF_HELPER_FLAGS_1(lz, TCG_CALL_NO_SE, i32, i32);
-DEF_HELPER_FLAGS_4(btst, TCG_CALL_NO_SE, i32, env, i32, i32, i32);
+DEF_HELPER_FLAGS_1(lz, TCG_CALL_NO_SE, i32, i32)
+DEF_HELPER_FLAGS_4(btst, TCG_CALL_NO_SE, i32, env, i32, i32, i32)
DEF_HELPER_FLAGS_4(evaluate_flags_muls, TCG_CALL_NO_SE, i32, env, i32, i32, i32)
DEF_HELPER_FLAGS_4(evaluate_flags_mulu, TCG_CALL_NO_SE, i32, env, i32, i32, i32)
diff --git a/target-cris/translate.c b/target-cris/translate.c
index 617e1b4242..5faa44c1ea 100644
--- a/target-cris/translate.c
+++ b/target-cris/translate.c
@@ -3480,9 +3480,6 @@ void cris_initialize_tcg(void)
{
int i;
-#define GEN_HELPER 2
-#include "helper.h"
-
cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
cc_x = tcg_global_mem_new(TCG_AREG0,
offsetof(CPUCRISState, cc_x), "cc_x");
diff --git a/target-i386/arch_memory_mapping.c b/target-i386/arch_memory_mapping.c
index 2566a040a6..462f984a26 100644
--- a/target-i386/arch_memory_mapping.c
+++ b/target-i386/arch_memory_mapping.c
@@ -75,7 +75,7 @@ static void walk_pte2(MemoryMappingList *list,
}
/* PAE Paging or IA-32e Paging */
-#define PLM4_ADDR_MASK 0xffffffffff000 /* selects bits 51:12 */
+#define PLM4_ADDR_MASK 0xffffffffff000ULL /* selects bits 51:12 */
static void walk_pde(MemoryMappingList *list, hwaddr pde_start_addr,
int32_t a20_mask, target_ulong start_line_addr)
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index b6828022bc..d0c9bdb629 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -545,7 +545,7 @@ static x86_def_t builtin_x86_defs[] = {
.level = 4,
.vendor = CPUID_VENDOR_AMD,
.family = 6,
- .model = 2,
+ .model = 6,
.stepping = 3,
.features[FEAT_1_EDX] =
PPRO_FEATURES |
@@ -648,7 +648,7 @@ static x86_def_t builtin_x86_defs[] = {
.level = 4,
.vendor = CPUID_VENDOR_INTEL,
.family = 6,
- .model = 3,
+ .model = 6,
.stepping = 3,
.features[FEAT_1_EDX] =
PPRO_FEATURES,
@@ -1899,7 +1899,6 @@ X86CPU *cpu_x86_create(const char *cpu_model, DeviceState *icc_bridge,
Error **errp)
{
X86CPU *cpu = NULL;
- CPUX86State *env;
gchar **model_pieces;
char *name, *features;
char *typename;
@@ -1922,8 +1921,6 @@ X86CPU *cpu_x86_create(const char *cpu_model, DeviceState *icc_bridge,
qdev_set_parent_bus(DEVICE(cpu), qdev_get_child_bus(icc_bridge, "icc"));
object_unref(OBJECT(cpu));
#endif
- env = &cpu->env;
- env->cpu_model_str = cpu_model;
cpu_x86_register(cpu, name, &error);
if (error) {
diff --git a/target-i386/translate.c b/target-i386/translate.c
index be74ebc278..eb0ea93dbb 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -8261,10 +8261,6 @@ void optimize_flags_init(void)
cpu_regs[R_EDI] = tcg_global_mem_new_i32(TCG_AREG0,
offsetof(CPUX86State, regs[R_EDI]), "edi");
#endif
-
- /* register helpers */
-#define GEN_HELPER 2
-#include "helper.h"
}
/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
diff --git a/target-m68k/helper.c b/target-m68k/helper.c
index 00a7a08e83..a364eb1e5c 100644
--- a/target-m68k/helper.c
+++ b/target-m68k/helper.c
@@ -21,7 +21,7 @@
#include "cpu.h"
#include "exec/gdbstub.h"
-#include "helpers.h"
+#include "helper.h"
#define SIGNBIT (1u << 31)
@@ -110,7 +110,6 @@ M68kCPU *cpu_m68k_init(const char *cpu_model)
}
cpu = M68K_CPU(object_new(object_class_get_name(oc)));
env = &cpu->env;
- env->cpu_model_str = cpu_model;
register_m68k_insns(env);
diff --git a/target-m68k/helpers.h b/target-m68k/helper.h
index 2b024502ba..2b024502ba 100644
--- a/target-m68k/helpers.h
+++ b/target-m68k/helper.h
diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c
index 30f7d8b1ab..bbbfd7f130 100644
--- a/target-m68k/op_helper.c
+++ b/target-m68k/op_helper.c
@@ -17,7 +17,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "cpu.h"
-#include "helpers.h"
+#include "helper.h"
#if defined(CONFIG_USER_ONLY)
diff --git a/target-m68k/translate.c b/target-m68k/translate.c
index 0be0a96732..f54b94a53f 100644
--- a/target-m68k/translate.c
+++ b/target-m68k/translate.c
@@ -23,9 +23,9 @@
#include "tcg-op.h"
#include "qemu/log.h"
-#include "helpers.h"
+#include "helper.h"
#define GEN_HELPER 1
-#include "helpers.h"
+#include "helper.h"
//#define DEBUG_DISPATCH 1
@@ -108,9 +108,6 @@ void m68k_tcg_init(void)
NULL_QREG = tcg_global_mem_new(TCG_AREG0, -4, "NULL");
store_dummy = tcg_global_mem_new(TCG_AREG0, -8, "NULL");
-
-#define GEN_HELPER 2
-#include "helpers.h"
}
static inline void qemu_assert(int cond, const char *msg)
diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c
index 0673176957..1b937b3f0d 100644
--- a/target-microblaze/translate.c
+++ b/target-microblaze/translate.c
@@ -2024,8 +2024,6 @@ void mb_tcg_init(void)
offsetof(CPUMBState, sregs[i]),
special_regnames[i]);
}
-#define GEN_HELPER 2
-#include "helper.h"
}
void restore_state_to_opc(CPUMBState *env, TranslationBlock *tb, int pc_pos)
diff --git a/target-mips/helper.h b/target-mips/helper.h
index ed75e2c9f2..1a8b86dea5 100644
--- a/target-mips/helper.h
+++ b/target-mips/helper.h
@@ -148,7 +148,7 @@ DEF_HELPER_2(mtc0_taghi, void, env, tl)
DEF_HELPER_2(mtc0_datahi, void, env, tl)
/* MIPS MT functions */
-DEF_HELPER_2(mftgpr, tl, env, i32);
+DEF_HELPER_2(mftgpr, tl, env, i32)
DEF_HELPER_2(mftlo, tl, env, i32)
DEF_HELPER_2(mfthi, tl, env, i32)
DEF_HELPER_2(mftacx, tl, env, i32)
@@ -165,11 +165,11 @@ DEF_HELPER_1(evpe, tl, env)
#endif /* !CONFIG_USER_ONLY */
/* microMIPS functions */
-DEF_HELPER_4(lwm, void, env, tl, tl, i32);
-DEF_HELPER_4(swm, void, env, tl, tl, i32);
+DEF_HELPER_4(lwm, void, env, tl, tl, i32)
+DEF_HELPER_4(swm, void, env, tl, tl, i32)
#ifdef TARGET_MIPS64
-DEF_HELPER_4(ldm, void, env, tl, tl, i32);
-DEF_HELPER_4(sdm, void, env, tl, tl, i32);
+DEF_HELPER_4(ldm, void, env, tl, tl, i32)
+DEF_HELPER_4(sdm, void, env, tl, tl, i32)
#endif
DEF_HELPER_2(fork, void, tl, tl)
@@ -615,7 +615,7 @@ DEF_HELPER_FLAGS_4(dmsubu, 0, void, tl, tl, i32, env)
DEF_HELPER_FLAGS_1(bitrev, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_FLAGS_3(insv, 0, tl, env, tl, tl)
#if defined(TARGET_MIPS64)
-DEF_HELPER_FLAGS_3(dinsv, 0, tl, env, tl, tl);
+DEF_HELPER_FLAGS_3(dinsv, 0, tl, env, tl, tl)
#endif
/* DSP Compare-Pick Sub-class insns */
diff --git a/target-mips/translate.c b/target-mips/translate.c
index ad43d59103..67f326b205 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -15886,10 +15886,6 @@ void mips_tcg_init(void)
offsetof(CPUMIPSState, active_fpu.fcr31),
"fcr31");
- /* register helpers */
-#define GEN_HELPER 2
-#include "helper.h"
-
inited = 1;
}
@@ -15907,7 +15903,6 @@ MIPSCPU *cpu_mips_init(const char *cpu_model)
cpu = MIPS_CPU(object_new(TYPE_MIPS_CPU));
env = &cpu->env;
env->cpu_model = def;
- env->cpu_model_str = cpu_model;
#ifndef CONFIG_USER_ONLY
mmu_init(env, def);
diff --git a/target-moxie/cpu.c b/target-moxie/cpu.c
index d97a091eb4..484ecc2124 100644
--- a/target-moxie/cpu.c
+++ b/target-moxie/cpu.c
@@ -138,7 +138,6 @@ MoxieCPU *cpu_moxie_init(const char *cpu_model)
return NULL;
}
cpu = MOXIE_CPU(object_new(object_class_get_name(oc)));
- cpu->env.cpu_model_str = cpu_model;
object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
diff --git a/target-openrisc/cpu.c b/target-openrisc/cpu.c
index 075f00a897..813794300b 100644
--- a/target-openrisc/cpu.c
+++ b/target-openrisc/cpu.c
@@ -209,7 +209,6 @@ OpenRISCCPU *cpu_openrisc_init(const char *cpu_model)
return NULL;
}
cpu = OPENRISC_CPU(object_new(object_class_get_name(oc)));
- cpu->env.cpu_model_str = cpu_model;
object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
diff --git a/target-openrisc/translate.c b/target-openrisc/translate.c
index 723b77d3b4..8908a2e32b 100644
--- a/target-openrisc/translate.c
+++ b/target-openrisc/translate.c
@@ -110,8 +110,6 @@ void openrisc_translate_init(void)
offsetof(CPUOpenRISCState, gpr[i]),
regnames[i]);
}
-#define GEN_HELPER 2
-#include "helper.h"
}
/* Writeback SR_F transaltion-space to execution-space. */
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 56814b501f..6d282bb32d 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -168,8 +168,8 @@ DEF_HELPER_3(vslo, void, avr, avr, avr)
DEF_HELPER_3(vsro, void, avr, avr, avr)
DEF_HELPER_3(vaddcuw, void, avr, avr, avr)
DEF_HELPER_3(vsubcuw, void, avr, avr, avr)
-DEF_HELPER_2(lvsl, void, avr, tl);
-DEF_HELPER_2(lvsr, void, avr, tl);
+DEF_HELPER_2(lvsl, void, avr, tl)
+DEF_HELPER_2(lvsr, void, avr, tl)
DEF_HELPER_4(vaddsbs, void, env, avr, avr, avr)
DEF_HELPER_4(vaddshs, void, env, avr, avr, avr)
DEF_HELPER_4(vaddsws, void, env, avr, avr, avr)
@@ -220,7 +220,7 @@ DEF_HELPER_5(vmsumuhs, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vmsumshm, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vmsumshs, void, env, avr, avr, avr, avr)
DEF_HELPER_4(vmladduhm, void, avr, avr, avr, avr)
-DEF_HELPER_2(mtvscr, void, env, avr);
+DEF_HELPER_2(mtvscr, void, env, avr)
DEF_HELPER_3(lvebx, void, env, avr, tl)
DEF_HELPER_3(lvehx, void, env, avr, tl)
DEF_HELPER_3(lvewx, void, env, avr, tl)
@@ -349,7 +349,7 @@ DEF_HELPER_2(load_slb_vsid, tl, env, tl)
DEF_HELPER_FLAGS_1(slbia, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_2(slbie, TCG_CALL_NO_RWG, void, env, tl)
#endif
-DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_NO_RWG, tl, env, tl);
+DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_NO_RWG, tl, env, tl)
DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl)
DEF_HELPER_FLAGS_1(602_mfrom, TCG_CALL_NO_RWG_SE, tl, tl)
@@ -367,7 +367,7 @@ DEF_HELPER_3(divo, tl, env, tl, tl)
DEF_HELPER_3(divs, tl, env, tl, tl)
DEF_HELPER_3(divso, tl, env, tl, tl)
-DEF_HELPER_2(load_dcr, tl, env, tl);
+DEF_HELPER_2(load_dcr, tl, env, tl)
DEF_HELPER_3(store_dcr, void, env, tl, tl)
DEF_HELPER_2(load_dump_spr, void, env, i32)
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 9c59f69ee1..66c777174c 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -175,10 +175,6 @@ void ppc_translate_init(void)
cpu_access_type = tcg_global_mem_new_i32(TCG_AREG0,
offsetof(CPUPPCState, access_type), "access_type");
- /* register helpers */
-#define GEN_HELPER 2
-#include "helper.h"
-
done_init = 1;
}
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index d2645bad28..651da6b0d5 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -8267,7 +8267,6 @@ static ObjectClass *ppc_cpu_class_by_name(const char *name)
PowerPCCPU *cpu_ppc_init(const char *cpu_model)
{
PowerPCCPU *cpu;
- CPUPPCState *env;
ObjectClass *oc;
Error *err = NULL;
@@ -8277,8 +8276,6 @@ PowerPCCPU *cpu_ppc_init(const char *cpu_model)
}
cpu = POWERPC_CPU(object_new(object_class_get_name(oc)));
- env = &cpu->env;
- env->cpu_model_str = cpu_model;
object_property_set_bool(OBJECT(cpu), true, "realized", &err);
if (err != NULL) {
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
index 61abfd7d9e..da33b38009 100644
--- a/target-s390x/helper.c
+++ b/target-s390x/helper.c
@@ -73,11 +73,8 @@ void s390x_cpu_timer(void *opaque)
S390CPU *cpu_s390x_init(const char *cpu_model)
{
S390CPU *cpu;
- CPUS390XState *env;
cpu = S390_CPU(object_new(TYPE_S390_CPU));
- env = &cpu->env;
- env->cpu_model_str = cpu_model;
object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index a444f6999b..02ac4ba995 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -93,7 +93,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
static int cap_sync_regs;
-static void *legacy_s390_alloc(ram_addr_t size);
+static void *legacy_s390_alloc(size_t size);
int kvm_arch_init(KVMState *s)
{
@@ -325,7 +325,7 @@ int kvm_s390_get_registers_partial(CPUState *cs)
* to grow. We also have to use MAP parameters that avoid
* read-only mapping of guest pages.
*/
-static void *legacy_s390_alloc(ram_addr_t size)
+static void *legacy_s390_alloc(size_t size)
{
void *mem;
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index afe90eb8be..bc99a378a7 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -188,10 +188,6 @@ void s390x_translate_init(void)
offsetof(CPUS390XState, fregs[i].d),
cpu_reg_names[i + 16]);
}
-
- /* register helpers */
-#define GEN_HELPER 2
-#include "helper.h"
}
static TCGv_i64 load_reg(int reg)
diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c
index 34b2b57ba7..c23294d410 100644
--- a/target-sh4/cpu.c
+++ b/target-sh4/cpu.c
@@ -144,7 +144,6 @@ static ObjectClass *superh_cpu_class_by_name(const char *cpu_model)
SuperHCPU *cpu_sh4_init(const char *cpu_model)
{
SuperHCPU *cpu;
- CPUSH4State *env;
ObjectClass *oc;
oc = superh_cpu_class_by_name(cpu_model);
@@ -152,8 +151,6 @@ SuperHCPU *cpu_sh4_init(const char *cpu_model)
return NULL;
}
cpu = SUPERH_CPU(object_new(object_class_get_name(oc)));
- env = &cpu->env;
- env->cpu_model_str = cpu_model;
object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
diff --git a/target-sh4/translate.c b/target-sh4/translate.c
index c06b29f1dc..2272eb0beb 100644
--- a/target-sh4/translate.c
+++ b/target-sh4/translate.c
@@ -143,10 +143,6 @@ void sh4_translate_init(void)
offsetof(CPUSH4State, fregs[i]),
fregnames[i]);
- /* register helpers */
-#define GEN_HELPER 2
-#include "helper.h"
-
done_init = 1;
}
diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c
index 47ce60de4a..e7f878ee81 100644
--- a/target-sparc/cpu.c
+++ b/target-sparc/cpu.c
@@ -84,7 +84,6 @@ static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model)
env->def->features |= CPU_FEATURE_FLOAT128;
}
#endif
- env->cpu_model_str = cpu_model;
env->version = def->iu_version;
env->fsr = def->fpu_version;
env->nwindows = def->nwindows;
diff --git a/target-sparc/helper.h b/target-sparc/helper.h
index 15f73283fa..2a771b2093 100644
--- a/target-sparc/helper.h
+++ b/target-sparc/helper.h
@@ -103,7 +103,7 @@ DEF_HELPER_3(fmuls, f32, env, f32, f32)
DEF_HELPER_3(fdivs, f32, env, f32, f32)
DEF_HELPER_3(fsmuld, f64, env, f32, f32)
-DEF_HELPER_3(fdmulq, void, env, f64, f64);
+DEF_HELPER_3(fdmulq, void, env, f64, f64)
DEF_HELPER_FLAGS_1(fnegs, TCG_CALL_NO_RWG_SE, f32, f32)
DEF_HELPER_2(fitod, f64, env, s32)
@@ -156,22 +156,22 @@ DEF_HELPER_FLAGS_3(bshuffle, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64)
DEF_HELPER_FLAGS_2(f ## name ## 32s, TCG_CALL_NO_RWG_SE, \
i32, i32, i32)
-VIS_HELPER(padd);
-VIS_HELPER(psub);
+VIS_HELPER(padd)
+VIS_HELPER(psub)
#define VIS_CMPHELPER(name) \
DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \
i64, i64, i64) \
DEF_HELPER_FLAGS_2(f##name##32, TCG_CALL_NO_RWG_SE, \
i64, i64, i64)
-VIS_CMPHELPER(cmpgt);
-VIS_CMPHELPER(cmpeq);
-VIS_CMPHELPER(cmple);
-VIS_CMPHELPER(cmpne);
+VIS_CMPHELPER(cmpgt)
+VIS_CMPHELPER(cmpeq)
+VIS_CMPHELPER(cmple)
+VIS_CMPHELPER(cmpne)
#endif
#undef F_HELPER_0_1
#undef VIS_HELPER
#undef VIS_CMPHELPER
-DEF_HELPER_1(compute_psr, void, env);
-DEF_HELPER_1(compute_C_icc, i32, env);
+DEF_HELPER_1(compute_psr, void, env)
+DEF_HELPER_1(compute_C_icc, i32, env)
#include "exec/def-helper.h"
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
index 36615f1979..dce64c3c4a 100644
--- a/target-sparc/translate.c
+++ b/target-sparc/translate.c
@@ -5456,11 +5456,6 @@ void gen_intermediate_code_init(CPUSPARCState *env)
offsetof(CPUSPARCState, fpr[i]),
fregnames[i]);
}
-
- /* register helpers */
-
-#define GEN_HELPER 2
-#include "helper.h"
}
}
diff --git a/target-unicore32/helper.c b/target-unicore32/helper.c
index 61eb2c374a..9bf4fea5db 100644
--- a/target-unicore32/helper.c
+++ b/target-unicore32/helper.c
@@ -37,7 +37,6 @@ CPUUniCore32State *uc32_cpu_init(const char *cpu_model)
}
cpu = UNICORE32_CPU(object_new(object_class_get_name(oc)));
env = &cpu->env;
- env->cpu_model_str = cpu_model;
object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
diff --git a/target-unicore32/translate.c b/target-unicore32/translate.c
index 1246895f86..4572890ffa 100644
--- a/target-unicore32/translate.c
+++ b/target-unicore32/translate.c
@@ -74,9 +74,6 @@ void uc32_translate_init(void)
cpu_R[i] = tcg_global_mem_new_i32(TCG_AREG0,
offsetof(CPUUniCore32State, regs[i]), regnames[i]);
}
-
-#define GEN_HELPER 2
-#include "helper.h"
}
static int num_temps;
diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c
index 24343bdf60..06641bb7d0 100644
--- a/target-xtensa/translate.c
+++ b/target-xtensa/translate.c
@@ -238,8 +238,6 @@ void xtensa_translate_init(void)
uregnames[i].name);
}
}
-#define GEN_HELPER 2
-#include "helper.h"
}
static inline bool option_bits_enabled(DisasContext *dc, uint64_t opt)
diff --git a/tcg/README b/tcg/README
index 063aeb95ea..f1782123b7 100644
--- a/tcg/README
+++ b/tcg/README
@@ -412,30 +412,25 @@ current TB was linked to this TB. Otherwise execute the next
instructions. Only indices 0 and 1 are valid and tcg_gen_goto_tb may be issued
at most once with each slot index per TB.
-* qemu_ld8u t0, t1, flags
-qemu_ld8s t0, t1, flags
-qemu_ld16u t0, t1, flags
-qemu_ld16s t0, t1, flags
-qemu_ld32 t0, t1, flags
-qemu_ld32u t0, t1, flags
-qemu_ld32s t0, t1, flags
-qemu_ld64 t0, t1, flags
-
-Load data at the QEMU CPU address t1 into t0. t1 has the QEMU CPU address
-type. 'flags' contains the QEMU memory index (selects user or kernel access)
-for example.
-
-Note that "qemu_ld32" implies a 32-bit result, while "qemu_ld32u" and
-"qemu_ld32s" imply a 64-bit result appropriately extended from 32 bits.
-
-* qemu_st8 t0, t1, flags
-qemu_st16 t0, t1, flags
-qemu_st32 t0, t1, flags
-qemu_st64 t0, t1, flags
-
-Store the data t0 at the QEMU CPU Address t1. t1 has the QEMU CPU
-address type. 'flags' contains the QEMU memory index (selects user or
-kernel access) for example.
+* qemu_ld_i32/i64 t0, t1, flags, memidx
+* qemu_st_i32/i64 t0, t1, flags, memidx
+
+Load data at the guest address t1 into t0, or store data in t0 at guest
+address t1. The _i32/_i64 size applies to the size of the input/output
+register t0 only. The address t1 is always sized according to the guest,
+and the width of the memory operation is controlled by flags.
+
+Both t0 and t1 may be split into little-endian ordered pairs of registers
+if dealing with 64-bit quantities on a 32-bit host.
+
+The memidx selects the qemu tlb index to use (e.g. user or kernel access).
+The flags are the TCGMemOp bits, selecting the sign, width, and endianness
+of the memory access.
+
+For a 32-bit host, qemu_ld/st_i64 is guaranteed to only be used with a
+64-bit memory access specified in flags.
+
+*********
Note 1: Some shortcuts are defined when the last operand is known to be
a constant (e.g. addi for add, movi for mov).
diff --git a/tcg/aarch64/tcg-target.c b/tcg/aarch64/tcg-target.c
index 6379df1f68..04d7ae328d 100644
--- a/tcg/aarch64/tcg-target.c
+++ b/tcg/aarch64/tcg-target.c
@@ -10,6 +10,7 @@
* See the COPYING file in the top-level directory for details.
*/
+#include "tcg-be-ldst.h"
#include "qemu/bitops.h"
#ifndef NDEBUG
@@ -778,22 +779,24 @@ static inline void tcg_out_nop(TCGContext *s)
}
#ifdef CONFIG_SOFTMMU
-/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr,
- int mmu_idx) */
+/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr,
+ * int mmu_idx, uintptr_t ra)
+ */
static const void * const qemu_ld_helpers[4] = {
- helper_ldb_mmu,
- helper_ldw_mmu,
- helper_ldl_mmu,
- helper_ldq_mmu,
+ helper_ret_ldub_mmu,
+ helper_ret_lduw_mmu,
+ helper_ret_ldul_mmu,
+ helper_ret_ldq_mmu,
};
-/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr,
- uintxx_t val, int mmu_idx) */
+/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr,
+ * uintxx_t val, int mmu_idx, uintptr_t ra)
+ */
static const void * const qemu_st_helpers[4] = {
- helper_stb_mmu,
- helper_stw_mmu,
- helper_stl_mmu,
- helper_stq_mmu,
+ helper_ret_stb_mmu,
+ helper_ret_stw_mmu,
+ helper_ret_stl_mmu,
+ helper_ret_stq_mmu,
};
static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
@@ -802,6 +805,7 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
tcg_out_movr(s, 1, TCG_REG_X0, TCG_AREG0);
tcg_out_movr(s, (TARGET_LONG_BITS == 64), TCG_REG_X1, lb->addrlo_reg);
tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_X2, lb->mem_index);
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_X3, (tcg_target_long)lb->raddr);
tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP,
(tcg_target_long)qemu_ld_helpers[lb->opc & 3]);
tcg_out_callr(s, TCG_REG_TMP);
@@ -822,6 +826,7 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
tcg_out_movr(s, (TARGET_LONG_BITS == 64), TCG_REG_X1, lb->addrlo_reg);
tcg_out_movr(s, 1, TCG_REG_X2, lb->datalo_reg);
tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_X3, lb->mem_index);
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_X4, (tcg_target_long)lb->raddr);
tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP,
(tcg_target_long)qemu_st_helpers[lb->opc & 3]);
tcg_out_callr(s, TCG_REG_TMP);
@@ -830,33 +835,13 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
tcg_out_goto(s, (tcg_target_long)lb->raddr);
}
-void tcg_out_tb_finalize(TCGContext *s)
-{
- int i;
- for (i = 0; i < s->nb_qemu_ldst_labels; i++) {
- TCGLabelQemuLdst *label = &s->qemu_ldst_labels[i];
- if (label->is_ld) {
- tcg_out_qemu_ld_slow_path(s, label);
- } else {
- tcg_out_qemu_st_slow_path(s, label);
- }
- }
-}
-
static void add_qemu_ldst_label(TCGContext *s, int is_ld, int opc,
TCGReg data_reg, TCGReg addr_reg,
int mem_index,
uint8_t *raddr, uint8_t *label_ptr)
{
- int idx;
- TCGLabelQemuLdst *label;
-
- if (s->nb_qemu_ldst_labels >= TCG_MAX_QEMU_LDST) {
- tcg_abort();
- }
+ TCGLabelQemuLdst *label = new_ldst_label(s);
- idx = s->nb_qemu_ldst_labels++;
- label = &s->qemu_ldst_labels[idx];
label->is_ld = is_ld;
label->opc = opc;
label->datalo_reg = data_reg;
diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h
index d3a1bc2437..82ad919518 100644
--- a/tcg/aarch64/tcg-target.h
+++ b/tcg/aarch64/tcg-target.h
@@ -96,6 +96,8 @@ enum {
TCG_AREG0 = TCG_REG_X19,
};
+#define TCG_TARGET_HAS_new_ldst 0
+
static inline void flush_icache_range(uintptr_t start, uintptr_t stop)
{
__builtin___clear_cache((char *)start, (char *)stop);
diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c
index eb0e84ce44..c0e14661b2 100644
--- a/tcg/arm/tcg-target.c
+++ b/tcg/arm/tcg-target.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "tcg-be-ldst.h"
+
/* The __ARM_ARCH define is provided by gcc 4.8. Construct it otherwise. */
#ifndef __ARM_ARCH
# if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \
@@ -175,20 +177,12 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
ct->ct |= TCG_CT_REG;
tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
#ifdef CONFIG_SOFTMMU
- /* r0-r2 will be overwritten when reading the tlb entry,
+ /* r0-r2,lr will be overwritten when reading the tlb entry,
so don't use these. */
tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2);
-#endif
- break;
- case 'L':
- ct->ct |= TCG_CT_REG;
- tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
-#ifdef CONFIG_SOFTMMU
- /* r1 is still needed to load data_reg or data_reg2,
- so don't use it. */
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R14);
#endif
break;
@@ -207,6 +201,7 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
/* Avoid clashes with registers being used for helper args */
tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3);
#endif
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R14);
#endif
break;
@@ -320,6 +315,9 @@ typedef enum {
INSN_STRB_REG = 0x06400000,
INSN_LDRD_IMM = 0x004000d0,
+ INSN_LDRD_REG = 0x000000d0,
+ INSN_STRD_IMM = 0x004000f0,
+ INSN_STRD_REG = 0x000000f0,
} ARMInsn;
#define SHIFT_IMM_LSL(im) (((im) << 7) | 0x00)
@@ -379,13 +377,17 @@ static inline void tcg_out_b_noaddr(TCGContext *s, int cond)
/* We pay attention here to not modify the branch target by skipping
the corresponding bytes. This ensure that caches and memory are
kept coherent during retranslation. */
-#ifdef HOST_WORDS_BIGENDIAN
- tcg_out8(s, (cond << 4) | 0x0a);
- s->code_ptr += 3;
-#else
s->code_ptr += 3;
tcg_out8(s, (cond << 4) | 0x0a);
-#endif
+}
+
+static inline void tcg_out_bl_noaddr(TCGContext *s, int cond)
+{
+ /* We pay attention here to not modify the branch target by skipping
+ the corresponding bytes. This ensure that caches and memory are
+ kept coherent during retranslation. */
+ s->code_ptr += 3;
+ tcg_out8(s, (cond << 4) | 0x0b);
}
static inline void tcg_out_bl(TCGContext *s, int cond, int32_t offset)
@@ -810,6 +812,30 @@ static inline void tcg_out_st32_r(TCGContext *s, int cond, TCGReg rt,
tcg_out_memop_r(s, cond, INSN_STR_REG, rt, rn, rm, 1, 1, 0);
}
+static inline void tcg_out_ldrd_8(TCGContext *s, int cond, TCGReg rt,
+ TCGReg rn, int imm8)
+{
+ tcg_out_memop_8(s, cond, INSN_LDRD_IMM, rt, rn, imm8, 1, 0);
+}
+
+static inline void tcg_out_ldrd_r(TCGContext *s, int cond, TCGReg rt,
+ TCGReg rn, TCGReg rm)
+{
+ tcg_out_memop_r(s, cond, INSN_LDRD_REG, rt, rn, rm, 1, 1, 0);
+}
+
+static inline void tcg_out_strd_8(TCGContext *s, int cond, TCGReg rt,
+ TCGReg rn, int imm8)
+{
+ tcg_out_memop_8(s, cond, INSN_STRD_IMM, rt, rn, imm8, 1, 0);
+}
+
+static inline void tcg_out_strd_r(TCGContext *s, int cond, TCGReg rt,
+ TCGReg rn, TCGReg rm)
+{
+ tcg_out_memop_r(s, cond, INSN_STRD_REG, rt, rn, rm, 1, 1, 0);
+}
+
/* Register pre-increment with base writeback. */
static inline void tcg_out_ld32_rwb(TCGContext *s, int cond, TCGReg rt,
TCGReg rn, TCGReg rm)
@@ -975,34 +1001,27 @@ static inline void tcg_out_st8(TCGContext *s, int cond,
tcg_out_st8_12(s, cond, rd, rn, offset);
}
-/* The _goto case is normally between TBs within the same code buffer,
- * and with the code buffer limited to 16MB we shouldn't need the long
- * case.
- *
- * .... except to the prologue that is in its own buffer.
+/* The _goto case is normally between TBs within the same code buffer, and
+ * with the code buffer limited to 16MB we wouldn't need the long case.
+ * But we also use it for the tail-call to the qemu_ld/st helpers, which does.
*/
static inline void tcg_out_goto(TCGContext *s, int cond, uint32_t addr)
{
- int32_t val;
+ int32_t disp = addr - (tcg_target_long) s->code_ptr;
- if (addr & 1) {
- /* goto to a Thumb destination isn't supported */
- tcg_abort();
+ if ((addr & 1) == 0 && disp - 8 < 0x01fffffd && disp - 8 > -0x01fffffd) {
+ tcg_out_b(s, cond, disp);
+ return;
}
- val = addr - (tcg_target_long) s->code_ptr;
- if (val - 8 < 0x01fffffd && val - 8 > -0x01fffffd)
- tcg_out_b(s, cond, val);
- else {
- if (cond == COND_AL) {
- tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4);
- tcg_out32(s, addr);
- } else {
- tcg_out_movi32(s, cond, TCG_REG_TMP, val - 8);
- tcg_out_dat_reg(s, cond, ARITH_ADD,
- TCG_REG_PC, TCG_REG_PC,
- TCG_REG_TMP, SHIFT_IMM_LSL(0));
+ tcg_out_movi32(s, cond, TCG_REG_TMP, addr);
+ if (use_armv5t_instructions) {
+ tcg_out_bx(s, cond, TCG_REG_TMP);
+ } else {
+ if (addr & 1) {
+ tcg_abort();
}
+ tcg_out_mov_reg(s, cond, TCG_REG_PC, TCG_REG_TMP);
}
}
@@ -1057,23 +1076,29 @@ static inline void tcg_out_goto_label(TCGContext *s, int cond, int label_index)
}
#ifdef CONFIG_SOFTMMU
-
-/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr,
- int mmu_idx) */
-static const void * const qemu_ld_helpers[4] = {
- helper_ldb_mmu,
- helper_ldw_mmu,
- helper_ldl_mmu,
- helper_ldq_mmu,
+/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr,
+ * int mmu_idx, uintptr_t ra)
+ */
+static const void * const qemu_ld_helpers[8] = {
+ helper_ret_ldub_mmu,
+ helper_ret_lduw_mmu,
+ helper_ret_ldul_mmu,
+ helper_ret_ldq_mmu,
+
+ helper_ret_ldsb_mmu,
+ helper_ret_ldsw_mmu,
+ helper_ret_ldul_mmu,
+ helper_ret_ldq_mmu,
};
-/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr,
- uintxx_t val, int mmu_idx) */
+/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr,
+ * uintxx_t val, int mmu_idx, uintptr_t ra)
+ */
static const void * const qemu_st_helpers[4] = {
- helper_stb_mmu,
- helper_stw_mmu,
- helper_stl_mmu,
- helper_stq_mmu,
+ helper_ret_stb_mmu,
+ helper_ret_stw_mmu,
+ helper_ret_stl_mmu,
+ helper_ret_stq_mmu,
};
/* Helper routines for marshalling helper function arguments into
@@ -1117,53 +1142,62 @@ static TCGReg tcg_out_arg_reg64(TCGContext *s, TCGReg argreg,
if (argreg & 1) {
argreg++;
}
- argreg = tcg_out_arg_reg32(s, argreg, arglo);
- argreg = tcg_out_arg_reg32(s, argreg, arghi);
- return argreg;
+ if (use_armv6_instructions && argreg >= 4
+ && (arglo & 1) == 0 && arghi == arglo + 1) {
+ tcg_out_strd_8(s, COND_AL, arglo,
+ TCG_REG_CALL_STACK, (argreg - 4) * 4);
+ return argreg + 2;
+ } else {
+ argreg = tcg_out_arg_reg32(s, argreg, arglo);
+ argreg = tcg_out_arg_reg32(s, argreg, arghi);
+ return argreg;
+ }
}
#define TLB_SHIFT (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS)
-/* Load and compare a TLB entry, leaving the flags set. Leaves R2 pointing
- to the tlb entry. Clobbers R1 and TMP. */
+/* We're expecting to use an 8-bit immediate and to mask. */
+QEMU_BUILD_BUG_ON(CPU_TLB_BITS > 8);
+
+/* We're expecting to use an 8-bit immediate add + 8-bit ldrd offset.
+ Using the offset of the second entry in the last tlb table ensures
+ that we can index all of the elements of the first entry. */
+QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1][1])
+ > 0xffff);
-static void tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi,
- int s_bits, int tlb_offset)
+/* Load and compare a TLB entry, leaving the flags set. Returns the register
+ containing the addend of the tlb entry. Clobbers R0, R1, R2, TMP. */
+
+static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi,
+ int s_bits, int mem_index, bool is_load)
{
TCGReg base = TCG_AREG0;
+ int cmp_off =
+ (is_load
+ ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read)
+ : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write));
+ int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend);
/* Should generate something like the following:
- * pre-v7:
* shr tmp, addr_reg, #TARGET_PAGE_BITS (1)
- * add r2, env, #off & 0xff00
+ * add r2, env, #high
* and r0, tmp, #(CPU_TLB_SIZE - 1) (2)
* add r2, r2, r0, lsl #CPU_TLB_ENTRY_BITS (3)
- * ldr r0, [r2, #off & 0xff]! (4)
+ * ldr r0, [r2, #cmp] (4)
* tst addr_reg, #s_mask
- * cmpeq r0, tmp, lsl #TARGET_PAGE_BITS (5)
- *
- * v7 (not implemented yet):
- * ubfx r2, addr_reg, #TARGET_PAGE_BITS, #CPU_TLB_BITS (1)
- * movw tmp, #~TARGET_PAGE_MASK & ~s_mask
- * movw r0, #off
- * add r2, env, r2, lsl #CPU_TLB_ENTRY_BITS (2)
- * bic tmp, addr_reg, tmp
- * ldr r0, [r2, r0]! (3)
- * cmp r0, tmp (4)
+ * ldr r1, [r2, #add] (5)
+ * cmpeq r0, tmp, lsl #TARGET_PAGE_BITS
*/
-# if CPU_TLB_BITS > 8
-# error
-# endif
tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP,
0, addrlo, SHIFT_IMM_LSR(TARGET_PAGE_BITS));
- /* We assume that the offset is contained within 16 bits. */
- assert((tlb_offset & ~0xffff) == 0);
- if (tlb_offset > 0xff) {
+ /* We checked that the offset is contained within 16 bits above. */
+ if (add_off > 0xfff || (use_armv6_instructions && cmp_off > 0xff)) {
tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R2, base,
- (24 << 7) | (tlb_offset >> 8));
- tlb_offset &= 0xff;
+ (24 << 7) | (cmp_off >> 8));
base = TCG_REG_R2;
+ add_off -= cmp_off & 0xff00;
+ cmp_off &= 0xff;
}
tcg_out_dat_imm(s, COND_AL, ARITH_AND,
@@ -1175,14 +1209,11 @@ static void tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi,
but due to how the pointer needs setting up, ldm isn't useful.
Base arm5 doesn't have ldrd, but armv5te does. */
if (use_armv6_instructions && TARGET_LONG_BITS == 64) {
- tcg_out_memop_8(s, COND_AL, INSN_LDRD_IMM, TCG_REG_R0,
- TCG_REG_R2, tlb_offset, 1, 1);
+ tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_REG_R2, cmp_off);
} else {
- tcg_out_memop_12(s, COND_AL, INSN_LDR_IMM, TCG_REG_R0,
- TCG_REG_R2, tlb_offset, 1, 1);
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_R2, cmp_off);
if (TARGET_LONG_BITS == 64) {
- tcg_out_memop_12(s, COND_AL, INSN_LDR_IMM, TCG_REG_R1,
- TCG_REG_R2, 4, 1, 0);
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R2, cmp_off + 4);
}
}
@@ -1192,6 +1223,9 @@ static void tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi,
0, addrlo, (1 << s_bits) - 1);
}
+ /* Load the tlb addend. */
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R2, add_off);
+
tcg_out_dat_reg(s, (s_bits ? COND_EQ : COND_AL), ARITH_CMP, 0,
TCG_REG_R0, TCG_REG_TMP, SHIFT_IMM_LSL(TARGET_PAGE_BITS));
@@ -1199,6 +1233,8 @@ static void tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi,
tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0,
TCG_REG_R1, addrhi, SHIFT_IMM_LSL(0));
}
+
+ return TCG_REG_R2;
}
/* Record the context of a call to the out of line helper code for the slow
@@ -1209,15 +1245,8 @@ static void add_qemu_ldst_label(TCGContext *s, int is_ld, int opc,
int addrhi_reg, int mem_index,
uint8_t *raddr, uint8_t *label_ptr)
{
- int idx;
- TCGLabelQemuLdst *label;
+ TCGLabelQemuLdst *label = new_ldst_label(s);
- if (s->nb_qemu_ldst_labels >= TCG_MAX_QEMU_LDST) {
- tcg_abort();
- }
-
- idx = s->nb_qemu_ldst_labels++;
- label = (TCGLabelQemuLdst *)&s->qemu_ldst_labels[idx];
label->is_ld = is_ld;
label->opc = opc;
label->datalo_reg = data_reg;
@@ -1232,7 +1261,8 @@ static void add_qemu_ldst_label(TCGContext *s, int is_ld, int opc,
static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
{
TCGReg argreg, data_reg, data_reg2;
- uint8_t *start;
+ int opc = lb->opc;
+ uintptr_t func;
reloc_pc24(lb->label_ptr[0], (tcg_target_long)s->code_ptr);
@@ -1243,46 +1273,46 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
argreg = tcg_out_arg_reg32(s, argreg, lb->addrlo_reg);
}
argreg = tcg_out_arg_imm32(s, argreg, lb->mem_index);
- tcg_out_call(s, (tcg_target_long) qemu_ld_helpers[lb->opc & 3]);
+ argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14);
+
+ /* For armv6 we can use the canonical unsigned helpers and minimize
+ icache usage. For pre-armv6, use the signed helpers since we do
+ not have a single insn sign-extend. */
+ if (use_armv6_instructions) {
+ func = (uintptr_t)qemu_ld_helpers[opc & 3];
+ } else {
+ func = (uintptr_t)qemu_ld_helpers[opc];
+ if (opc & 4) {
+ opc = 2;
+ }
+ }
+ tcg_out_call(s, func);
data_reg = lb->datalo_reg;
data_reg2 = lb->datahi_reg;
-
- start = s->code_ptr;
- switch (lb->opc) {
+ switch (opc) {
case 0 | 4:
tcg_out_ext8s(s, COND_AL, data_reg, TCG_REG_R0);
break;
case 1 | 4:
tcg_out_ext16s(s, COND_AL, data_reg, TCG_REG_R0);
break;
- case 0:
- case 1:
- case 2:
default:
tcg_out_mov_reg(s, COND_AL, data_reg, TCG_REG_R0);
break;
case 3:
- tcg_out_mov_reg(s, COND_AL, data_reg, TCG_REG_R0);
- tcg_out_mov_reg(s, COND_AL, data_reg2, TCG_REG_R1);
- break;
- }
-
- /* For GETPC_LDST in exec-all.h, we architect exactly 2 insns between
- the call and the branch back to straight-line code. Note that the
- moves above could be elided by register allocation, nor do we know
- which code alternative we chose for extension. */
- switch (s->code_ptr - start) {
- case 0:
- tcg_out_nop(s);
- /* FALLTHRU */
- case 4:
- tcg_out_nop(s);
- /* FALLTHRU */
- case 8:
+ if (data_reg != TCG_REG_R1) {
+ tcg_out_mov_reg(s, COND_AL, data_reg, TCG_REG_R0);
+ tcg_out_mov_reg(s, COND_AL, data_reg2, TCG_REG_R1);
+ } else if (data_reg2 != TCG_REG_R0) {
+ tcg_out_mov_reg(s, COND_AL, data_reg2, TCG_REG_R1);
+ tcg_out_mov_reg(s, COND_AL, data_reg, TCG_REG_R0);
+ } else {
+ tcg_out_mov_reg(s, COND_AL, TCG_REG_TMP, TCG_REG_R0);
+ tcg_out_mov_reg(s, COND_AL, data_reg2, TCG_REG_R1);
+ tcg_out_mov_reg(s, COND_AL, data_reg, TCG_REG_TMP);
+ }
break;
- default:
- abort();
}
tcg_out_goto(s, COND_AL, (tcg_target_long)lb->raddr);
@@ -1320,13 +1350,10 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
}
argreg = tcg_out_arg_imm32(s, argreg, lb->mem_index);
- tcg_out_call(s, (tcg_target_long) qemu_st_helpers[lb->opc & 3]);
+ argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14);
- /* For GETPC_LDST in exec-all.h, we architect exactly 2 insns between
- the call and the branch back to straight-line code. */
- tcg_out_nop(s);
- tcg_out_nop(s);
- tcg_out_goto(s, COND_AL, (tcg_target_long)lb->raddr);
+ /* Tail-call to the helper, which will return to the fast path. */
+ tcg_out_goto(s, COND_AL, (tcg_target_long) qemu_st_helpers[lb->opc & 3]);
}
#endif /* SOFTMMU */
@@ -1336,7 +1363,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
bool bswap;
#ifdef CONFIG_SOFTMMU
int mem_index, s_bits;
- TCGReg addr_reg2;
+ TCGReg addr_reg2, addend;
uint8_t *label_ptr;
#endif
#ifdef TARGET_WORDS_BIGENDIAN
@@ -1353,53 +1380,63 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
mem_index = *args;
s_bits = opc & 3;
- tcg_out_tlb_read(s, addr_reg, addr_reg2, s_bits,
- offsetof(CPUArchState, tlb_table[mem_index][0].addr_read));
+ addend = tcg_out_tlb_read(s, addr_reg, addr_reg2, s_bits, mem_index, 1);
+ /* This a conditional BL only to load a pointer within this opcode into LR
+ for the slow path. We will not be using the value for a tail call. */
label_ptr = s->code_ptr;
- tcg_out_b_noaddr(s, COND_NE);
-
- tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R2,
- offsetof(CPUTLBEntry, addend)
- - offsetof(CPUTLBEntry, addr_read));
+ tcg_out_bl_noaddr(s, COND_NE);
switch (opc) {
case 0:
- tcg_out_ld8_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1);
+ tcg_out_ld8_r(s, COND_AL, data_reg, addr_reg, addend);
break;
case 0 | 4:
- tcg_out_ld8s_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1);
+ tcg_out_ld8s_r(s, COND_AL, data_reg, addr_reg, addend);
break;
case 1:
- tcg_out_ld16u_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1);
+ tcg_out_ld16u_r(s, COND_AL, data_reg, addr_reg, addend);
if (bswap) {
tcg_out_bswap16(s, COND_AL, data_reg, data_reg);
}
break;
case 1 | 4:
if (bswap) {
- tcg_out_ld16u_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1);
+ tcg_out_ld16u_r(s, COND_AL, data_reg, addr_reg, addend);
tcg_out_bswap16s(s, COND_AL, data_reg, data_reg);
} else {
- tcg_out_ld16s_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1);
+ tcg_out_ld16s_r(s, COND_AL, data_reg, addr_reg, addend);
}
break;
case 2:
default:
- tcg_out_ld32_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1);
+ tcg_out_ld32_r(s, COND_AL, data_reg, addr_reg, addend);
if (bswap) {
tcg_out_bswap32(s, COND_AL, data_reg, data_reg);
}
break;
case 3:
- if (bswap) {
- tcg_out_ld32_rwb(s, COND_AL, data_reg2, TCG_REG_R1, addr_reg);
- tcg_out_ld32_12(s, COND_AL, data_reg, TCG_REG_R1, 4);
- tcg_out_bswap32(s, COND_AL, data_reg2, data_reg2);
- tcg_out_bswap32(s, COND_AL, data_reg, data_reg);
- } else {
- tcg_out_ld32_rwb(s, COND_AL, data_reg, TCG_REG_R1, addr_reg);
- tcg_out_ld32_12(s, COND_AL, data_reg2, TCG_REG_R1, 4);
+ {
+ /* Be careful not to modify data_reg and data_reg2
+ for the slow path below. */
+ TCGReg dl = (bswap ? data_reg2 : data_reg);
+ TCGReg dh = (bswap ? data_reg : data_reg2);
+
+ if (use_armv6_instructions && (dl & 1) == 0 && dh == dl + 1) {
+ tcg_out_ldrd_r(s, COND_AL, dl, addr_reg, addend);
+ } else if (dl != addend) {
+ tcg_out_ld32_rwb(s, COND_AL, dl, addend, addr_reg);
+ tcg_out_ld32_12(s, COND_AL, dh, addend, 4);
+ } else {
+ tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_TMP,
+ addend, addr_reg, SHIFT_IMM_LSL(0));
+ tcg_out_ld32_12(s, COND_AL, dl, TCG_REG_TMP, 0);
+ tcg_out_ld32_12(s, COND_AL, dh, TCG_REG_TMP, 4);
+ }
+ if (bswap) {
+ tcg_out_bswap32(s, COND_AL, dh, dh);
+ tcg_out_bswap32(s, COND_AL, dl, dl);
+ }
}
break;
}
@@ -1450,9 +1487,13 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
}
break;
case 3:
- /* TODO: use block load -
- * check that data_reg2 > data_reg or the other way */
- if (data_reg == addr_reg) {
+ if (use_armv6_instructions && !bswap
+ && (data_reg & 1) == 0 && data_reg2 == data_reg + 1) {
+ tcg_out_ldrd_8(s, COND_AL, data_reg, addr_reg, 0);
+ } else if (use_armv6_instructions && bswap
+ && (data_reg2 & 1) == 0 && data_reg == data_reg2 + 1) {
+ tcg_out_ldrd_8(s, COND_AL, data_reg2, addr_reg, 0);
+ } else if (data_reg == addr_reg) {
tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4);
tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0);
} else {
@@ -1474,7 +1515,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc)
bool bswap;
#ifdef CONFIG_SOFTMMU
int mem_index, s_bits;
- TCGReg addr_reg2;
+ TCGReg addr_reg2, addend;
uint8_t *label_ptr;
#endif
#ifdef TARGET_WORDS_BIGENDIAN
@@ -1491,51 +1532,49 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc)
mem_index = *args;
s_bits = opc & 3;
- tcg_out_tlb_read(s, addr_reg, addr_reg2, s_bits,
- offsetof(CPUArchState,
- tlb_table[mem_index][0].addr_write));
-
- label_ptr = s->code_ptr;
- tcg_out_b_noaddr(s, COND_NE);
-
- tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R2,
- offsetof(CPUTLBEntry, addend)
- - offsetof(CPUTLBEntry, addr_write));
+ addend = tcg_out_tlb_read(s, addr_reg, addr_reg2, s_bits, mem_index, 0);
switch (opc) {
case 0:
- tcg_out_st8_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1);
+ tcg_out_st8_r(s, COND_EQ, data_reg, addr_reg, addend);
break;
case 1:
if (bswap) {
- tcg_out_bswap16st(s, COND_AL, TCG_REG_R0, data_reg);
- tcg_out_st16_r(s, COND_AL, TCG_REG_R0, addr_reg, TCG_REG_R1);
+ tcg_out_bswap16st(s, COND_EQ, TCG_REG_R0, data_reg);
+ tcg_out_st16_r(s, COND_EQ, TCG_REG_R0, addr_reg, addend);
} else {
- tcg_out_st16_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1);
+ tcg_out_st16_r(s, COND_EQ, data_reg, addr_reg, addend);
}
break;
case 2:
default:
if (bswap) {
- tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg);
- tcg_out_st32_r(s, COND_AL, TCG_REG_R0, addr_reg, TCG_REG_R1);
+ tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg);
+ tcg_out_st32_r(s, COND_EQ, TCG_REG_R0, addr_reg, addend);
} else {
- tcg_out_st32_r(s, COND_AL, data_reg, addr_reg, TCG_REG_R1);
+ tcg_out_st32_r(s, COND_EQ, data_reg, addr_reg, addend);
}
break;
case 3:
if (bswap) {
- tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg2);
- tcg_out_st32_rwb(s, COND_AL, TCG_REG_R0, TCG_REG_R1, addr_reg);
- tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg);
- tcg_out_st32_12(s, COND_AL, TCG_REG_R0, TCG_REG_R1, 4);
+ tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg2);
+ tcg_out_st32_rwb(s, COND_EQ, TCG_REG_R0, addend, addr_reg);
+ tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg);
+ tcg_out_st32_12(s, COND_EQ, TCG_REG_R0, addend, 4);
+ } else if (use_armv6_instructions
+ && (data_reg & 1) == 0 && data_reg2 == data_reg + 1) {
+ tcg_out_strd_r(s, COND_EQ, data_reg, addr_reg, addend);
} else {
- tcg_out_st32_rwb(s, COND_AL, data_reg, TCG_REG_R1, addr_reg);
- tcg_out_st32_12(s, COND_AL, data_reg2, TCG_REG_R1, 4);
+ tcg_out_st32_rwb(s, COND_EQ, data_reg, addend, addr_reg);
+ tcg_out_st32_12(s, COND_EQ, data_reg2, addend, 4);
}
break;
}
+ /* The conditional call must come last, as we're going to return here. */
+ label_ptr = s->code_ptr;
+ tcg_out_bl_noaddr(s, COND_NE);
+
add_qemu_ldst_label(s, 0, opc, data_reg, data_reg2, addr_reg, addr_reg2,
mem_index, s->code_ptr, label_ptr);
#else /* !CONFIG_SOFTMMU */
@@ -1576,13 +1615,14 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc)
}
break;
case 3:
- /* TODO: use block store -
- * check that data_reg2 > data_reg or the other way */
if (bswap) {
tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg2);
tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 0);
tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg);
tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 4);
+ } else if (use_armv6_instructions
+ && (data_reg & 1) == 0 && data_reg2 == data_reg + 1) {
+ tcg_out_strd_8(s, COND_AL, data_reg, addr_reg, 0);
} else {
tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0);
tcg_out_st32_12(s, COND_AL, data_reg2, addr_reg, 4);
@@ -1923,22 +1963,6 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
}
}
-#ifdef CONFIG_SOFTMMU
-/* Generate TB finalization at the end of block. */
-void tcg_out_tb_finalize(TCGContext *s)
-{
- int i;
- for (i = 0; i < s->nb_qemu_ldst_labels; i++) {
- TCGLabelQemuLdst *label = &s->qemu_ldst_labels[i];
- if (label->is_ld) {
- tcg_out_qemu_ld_slow_path(s, label);
- } else {
- tcg_out_qemu_st_slow_path(s, label);
- }
- }
-}
-#endif /* SOFTMMU */
-
static const TCGTargetOpDef arm_op_defs[] = {
{ INDEX_op_exit_tb, { } },
{ INDEX_op_goto_tb, { } },
@@ -1991,7 +2015,7 @@ static const TCGTargetOpDef arm_op_defs[] = {
{ INDEX_op_qemu_ld16u, { "r", "l" } },
{ INDEX_op_qemu_ld16s, { "r", "l" } },
{ INDEX_op_qemu_ld32, { "r", "l" } },
- { INDEX_op_qemu_ld64, { "L", "L", "l" } },
+ { INDEX_op_qemu_ld64, { "r", "r", "l" } },
{ INDEX_op_qemu_st8, { "s", "s" } },
{ INDEX_op_qemu_st16, { "s", "s" } },
@@ -2003,7 +2027,7 @@ static const TCGTargetOpDef arm_op_defs[] = {
{ INDEX_op_qemu_ld16u, { "r", "l", "l" } },
{ INDEX_op_qemu_ld16s, { "r", "l", "l" } },
{ INDEX_op_qemu_ld32, { "r", "l", "l" } },
- { INDEX_op_qemu_ld64, { "L", "L", "l", "l" } },
+ { INDEX_op_qemu_ld64, { "r", "r", "l", "l" } },
{ INDEX_op_qemu_st8, { "s", "s", "s" } },
{ INDEX_op_qemu_st16, { "s", "s", "s" } },
diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h
index 9482bfa993..25e1e2876f 100644
--- a/tcg/arm/tcg-target.h
+++ b/tcg/arm/tcg-target.h
@@ -85,6 +85,8 @@ extern bool use_idiv_instructions;
#define TCG_TARGET_HAS_div_i32 use_idiv_instructions
#define TCG_TARGET_HAS_rem_i32 0
+#define TCG_TARGET_HAS_new_ldst 0
+
extern bool tcg_target_deposit_valid(int ofs, int len);
#define TCG_TARGET_deposit_i32_valid tcg_target_deposit_valid
diff --git a/tcg/hppa/tcg-target.c b/tcg/hppa/tcg-target.c
deleted file mode 100644
index 236b39c31f..0000000000
--- a/tcg/hppa/tcg-target.c
+++ /dev/null
@@ -1,1831 +0,0 @@
-/*
- * Tiny Code Generator for QEMU
- *
- * Copyright (c) 2008 Fabrice Bellard
- *
- * 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.
- */
-
-#if TCG_TARGET_REG_BITS != 32
-#error unsupported
-#endif
-
-#ifndef NDEBUG
-static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
- "%r0", "%r1", "%rp", "%r3", "%r4", "%r5", "%r6", "%r7",
- "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
- "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23",
- "%r24", "%r25", "%r26", "%dp", "%ret0", "%ret1", "%sp", "%r31",
-};
-#endif
-
-/* This is an 8 byte temp slot in the stack frame. */
-#define STACK_TEMP_OFS -16
-
-#ifdef CONFIG_USE_GUEST_BASE
-#define TCG_GUEST_BASE_REG TCG_REG_R16
-#else
-#define TCG_GUEST_BASE_REG TCG_REG_R0
-#endif
-
-static const int tcg_target_reg_alloc_order[] = {
- TCG_REG_R4,
- TCG_REG_R5,
- TCG_REG_R6,
- TCG_REG_R7,
- TCG_REG_R8,
- TCG_REG_R9,
- TCG_REG_R10,
- TCG_REG_R11,
- TCG_REG_R12,
- TCG_REG_R13,
-
- TCG_REG_R17,
- TCG_REG_R14,
- TCG_REG_R15,
- TCG_REG_R16,
-
- TCG_REG_R26,
- TCG_REG_R25,
- TCG_REG_R24,
- TCG_REG_R23,
-
- TCG_REG_RET0,
- TCG_REG_RET1,
-};
-
-static const int tcg_target_call_iarg_regs[4] = {
- TCG_REG_R26,
- TCG_REG_R25,
- TCG_REG_R24,
- TCG_REG_R23,
-};
-
-static const int tcg_target_call_oarg_regs[2] = {
- TCG_REG_RET0,
- TCG_REG_RET1,
-};
-
-/* True iff val fits a signed field of width BITS. */
-static inline int check_fit_tl(tcg_target_long val, unsigned int bits)
-{
- return (val << ((sizeof(tcg_target_long) * 8 - bits))
- >> (sizeof(tcg_target_long) * 8 - bits)) == val;
-}
-
-/* True iff depi can be used to compute (reg | MASK).
- Accept a bit pattern like:
- 0....01....1
- 1....10....0
- 0..01..10..0
- Copied from gcc sources. */
-static inline int or_mask_p(tcg_target_ulong mask)
-{
- if (mask == 0 || mask == -1) {
- return 0;
- }
- mask += mask & -mask;
- return (mask & (mask - 1)) == 0;
-}
-
-/* True iff depi or extru can be used to compute (reg & mask).
- Accept a bit pattern like these:
- 0....01....1
- 1....10....0
- 1..10..01..1
- Copied from gcc sources. */
-static inline int and_mask_p(tcg_target_ulong mask)
-{
- return or_mask_p(~mask);
-}
-
-static int low_sign_ext(int val, int len)
-{
- return (((val << 1) & ~(-1u << len)) | ((val >> (len - 1)) & 1));
-}
-
-static int reassemble_12(int as12)
-{
- return (((as12 & 0x800) >> 11) |
- ((as12 & 0x400) >> 8) |
- ((as12 & 0x3ff) << 3));
-}
-
-static int reassemble_17(int as17)
-{
- return (((as17 & 0x10000) >> 16) |
- ((as17 & 0x0f800) << 5) |
- ((as17 & 0x00400) >> 8) |
- ((as17 & 0x003ff) << 3));
-}
-
-static int reassemble_21(int as21)
-{
- return (((as21 & 0x100000) >> 20) |
- ((as21 & 0x0ffe00) >> 8) |
- ((as21 & 0x000180) << 7) |
- ((as21 & 0x00007c) << 14) |
- ((as21 & 0x000003) << 12));
-}
-
-/* ??? Bizzarely, there is no PCREL12F relocation type. I guess all
- such relocations are simply fully handled by the assembler. */
-#define R_PARISC_PCREL12F R_PARISC_NONE
-
-static void patch_reloc(uint8_t *code_ptr, int type,
- intptr_t value, intptr_t addend)
-{
- uint32_t *insn_ptr = (uint32_t *)code_ptr;
- uint32_t insn = *insn_ptr;
- intptr_t pcrel;
-
- value += addend;
- pcrel = (value - ((intptr_t)code_ptr + 8)) >> 2;
-
- switch (type) {
- case R_PARISC_PCREL12F:
- assert(check_fit_tl(pcrel, 12));
- /* ??? We assume all patches are forward. See tcg_out_brcond
- re setting the NUL bit on the branch and eliding the nop. */
- assert(pcrel >= 0);
- insn &= ~0x1ffdu;
- insn |= reassemble_12(pcrel);
- break;
- case R_PARISC_PCREL17F:
- assert(check_fit_tl(pcrel, 17));
- insn &= ~0x1f1ffdu;
- insn |= reassemble_17(pcrel);
- break;
- default:
- tcg_abort();
- }
-
- *insn_ptr = insn;
-}
-
-/* parse target specific constraints */
-static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
-{
- const char *ct_str;
-
- ct_str = *pct_str;
- switch (ct_str[0]) {
- case 'r':
- ct->ct |= TCG_CT_REG;
- tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
- break;
- case 'L': /* qemu_ld/st constraint */
- ct->ct |= TCG_CT_REG;
- tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_R26);
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_R25);
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_R24);
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_R23);
- break;
- case 'Z':
- ct->ct |= TCG_CT_CONST_0;
- break;
- case 'I':
- ct->ct |= TCG_CT_CONST_S11;
- break;
- case 'J':
- ct->ct |= TCG_CT_CONST_S5;
- break;
- case 'K':
- ct->ct |= TCG_CT_CONST_MS11;
- break;
- case 'M':
- ct->ct |= TCG_CT_CONST_AND;
- break;
- case 'O':
- ct->ct |= TCG_CT_CONST_OR;
- break;
- default:
- return -1;
- }
- ct_str++;
- *pct_str = ct_str;
- return 0;
-}
-
-/* test if a constant matches the constraint */
-static int tcg_target_const_match(tcg_target_long val,
- const TCGArgConstraint *arg_ct)
-{
- int ct = arg_ct->ct;
- if (ct & TCG_CT_CONST) {
- return 1;
- } else if (ct & TCG_CT_CONST_0) {
- return val == 0;
- } else if (ct & TCG_CT_CONST_S5) {
- return check_fit_tl(val, 5);
- } else if (ct & TCG_CT_CONST_S11) {
- return check_fit_tl(val, 11);
- } else if (ct & TCG_CT_CONST_MS11) {
- return check_fit_tl(-val, 11);
- } else if (ct & TCG_CT_CONST_AND) {
- return and_mask_p(val);
- } else if (ct & TCG_CT_CONST_OR) {
- return or_mask_p(val);
- }
- return 0;
-}
-
-#define INSN_OP(x) ((x) << 26)
-#define INSN_EXT3BR(x) ((x) << 13)
-#define INSN_EXT3SH(x) ((x) << 10)
-#define INSN_EXT4(x) ((x) << 6)
-#define INSN_EXT5(x) (x)
-#define INSN_EXT6(x) ((x) << 6)
-#define INSN_EXT7(x) ((x) << 6)
-#define INSN_EXT8A(x) ((x) << 6)
-#define INSN_EXT8B(x) ((x) << 5)
-#define INSN_T(x) (x)
-#define INSN_R1(x) ((x) << 16)
-#define INSN_R2(x) ((x) << 21)
-#define INSN_DEP_LEN(x) (32 - (x))
-#define INSN_SHDEP_CP(x) ((31 - (x)) << 5)
-#define INSN_SHDEP_P(x) ((x) << 5)
-#define INSN_COND(x) ((x) << 13)
-#define INSN_IM11(x) low_sign_ext(x, 11)
-#define INSN_IM14(x) low_sign_ext(x, 14)
-#define INSN_IM5(x) (low_sign_ext(x, 5) << 16)
-
-#define COND_NEVER 0
-#define COND_EQ 1
-#define COND_LT 2
-#define COND_LE 3
-#define COND_LTU 4
-#define COND_LEU 5
-#define COND_SV 6
-#define COND_OD 7
-#define COND_FALSE 8
-
-#define INSN_ADD (INSN_OP(0x02) | INSN_EXT6(0x18))
-#define INSN_ADDC (INSN_OP(0x02) | INSN_EXT6(0x1c))
-#define INSN_ADDI (INSN_OP(0x2d))
-#define INSN_ADDIL (INSN_OP(0x0a))
-#define INSN_ADDL (INSN_OP(0x02) | INSN_EXT6(0x28))
-#define INSN_AND (INSN_OP(0x02) | INSN_EXT6(0x08))
-#define INSN_ANDCM (INSN_OP(0x02) | INSN_EXT6(0x00))
-#define INSN_COMCLR (INSN_OP(0x02) | INSN_EXT6(0x22))
-#define INSN_COMICLR (INSN_OP(0x24))
-#define INSN_DEP (INSN_OP(0x35) | INSN_EXT3SH(3))
-#define INSN_DEPI (INSN_OP(0x35) | INSN_EXT3SH(7))
-#define INSN_EXTRS (INSN_OP(0x34) | INSN_EXT3SH(7))
-#define INSN_EXTRU (INSN_OP(0x34) | INSN_EXT3SH(6))
-#define INSN_LDIL (INSN_OP(0x08))
-#define INSN_LDO (INSN_OP(0x0d))
-#define INSN_MTCTL (INSN_OP(0x00) | INSN_EXT8B(0xc2))
-#define INSN_OR (INSN_OP(0x02) | INSN_EXT6(0x09))
-#define INSN_SHD (INSN_OP(0x34) | INSN_EXT3SH(2))
-#define INSN_SUB (INSN_OP(0x02) | INSN_EXT6(0x10))
-#define INSN_SUBB (INSN_OP(0x02) | INSN_EXT6(0x14))
-#define INSN_SUBI (INSN_OP(0x25))
-#define INSN_VEXTRS (INSN_OP(0x34) | INSN_EXT3SH(5))
-#define INSN_VEXTRU (INSN_OP(0x34) | INSN_EXT3SH(4))
-#define INSN_VSHD (INSN_OP(0x34) | INSN_EXT3SH(0))
-#define INSN_XOR (INSN_OP(0x02) | INSN_EXT6(0x0a))
-#define INSN_ZDEP (INSN_OP(0x35) | INSN_EXT3SH(2))
-#define INSN_ZVDEP (INSN_OP(0x35) | INSN_EXT3SH(0))
-
-#define INSN_BL (INSN_OP(0x3a) | INSN_EXT3BR(0))
-#define INSN_BL_N (INSN_OP(0x3a) | INSN_EXT3BR(0) | 2)
-#define INSN_BLR (INSN_OP(0x3a) | INSN_EXT3BR(2))
-#define INSN_BV (INSN_OP(0x3a) | INSN_EXT3BR(6))
-#define INSN_BV_N (INSN_OP(0x3a) | INSN_EXT3BR(6) | 2)
-#define INSN_BLE_SR4 (INSN_OP(0x39) | (1 << 13))
-
-#define INSN_LDB (INSN_OP(0x10))
-#define INSN_LDH (INSN_OP(0x11))
-#define INSN_LDW (INSN_OP(0x12))
-#define INSN_LDWM (INSN_OP(0x13))
-#define INSN_FLDDS (INSN_OP(0x0b) | INSN_EXT4(0) | (1 << 12))
-
-#define INSN_LDBX (INSN_OP(0x03) | INSN_EXT4(0))
-#define INSN_LDHX (INSN_OP(0x03) | INSN_EXT4(1))
-#define INSN_LDWX (INSN_OP(0x03) | INSN_EXT4(2))
-
-#define INSN_STB (INSN_OP(0x18))
-#define INSN_STH (INSN_OP(0x19))
-#define INSN_STW (INSN_OP(0x1a))
-#define INSN_STWM (INSN_OP(0x1b))
-#define INSN_FSTDS (INSN_OP(0x0b) | INSN_EXT4(8) | (1 << 12))
-
-#define INSN_COMBT (INSN_OP(0x20))
-#define INSN_COMBF (INSN_OP(0x22))
-#define INSN_COMIBT (INSN_OP(0x21))
-#define INSN_COMIBF (INSN_OP(0x23))
-
-/* supplied by libgcc */
-extern void *__canonicalize_funcptr_for_compare(const void *);
-
-static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg)
-{
- /* PA1.1 defines COPY as OR r,0,t; PA2.0 defines COPY as LDO 0(r),t
- but hppa-dis.c is unaware of this definition */
- if (ret != arg) {
- tcg_out32(s, INSN_OR | INSN_T(ret) | INSN_R1(arg)
- | INSN_R2(TCG_REG_R0));
- }
-}
-
-static void tcg_out_movi(TCGContext *s, TCGType type,
- TCGReg ret, tcg_target_long arg)
-{
- if (check_fit_tl(arg, 14)) {
- tcg_out32(s, INSN_LDO | INSN_R1(ret)
- | INSN_R2(TCG_REG_R0) | INSN_IM14(arg));
- } else {
- uint32_t hi, lo;
- hi = arg >> 11;
- lo = arg & 0x7ff;
-
- tcg_out32(s, INSN_LDIL | INSN_R2(ret) | reassemble_21(hi));
- if (lo) {
- tcg_out32(s, INSN_LDO | INSN_R1(ret)
- | INSN_R2(ret) | INSN_IM14(lo));
- }
- }
-}
-
-static void tcg_out_ldst(TCGContext *s, int ret, int addr,
- tcg_target_long offset, int op)
-{
- if (!check_fit_tl(offset, 14)) {
- uint32_t hi, lo, op;
-
- hi = offset >> 11;
- lo = offset & 0x7ff;
-
- if (addr == TCG_REG_R0) {
- op = INSN_LDIL | INSN_R2(TCG_REG_R1);
- } else {
- op = INSN_ADDIL | INSN_R2(addr);
- }
- tcg_out32(s, op | reassemble_21(hi));
-
- addr = TCG_REG_R1;
- offset = lo;
- }
-
- if (ret != addr || offset != 0 || op != INSN_LDO) {
- tcg_out32(s, op | INSN_R1(ret) | INSN_R2(addr) | INSN_IM14(offset));
- }
-}
-
-/* This function is required by tcg.c. */
-static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret,
- TCGReg arg1, intptr_t arg2)
-{
- tcg_out_ldst(s, ret, arg1, arg2, INSN_LDW);
-}
-
-/* This function is required by tcg.c. */
-static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg ret,
- TCGReg arg1, intptr_t arg2)
-{
- tcg_out_ldst(s, ret, arg1, arg2, INSN_STW);
-}
-
-static void tcg_out_ldst_index(TCGContext *s, int data,
- int base, int index, int op)
-{
- tcg_out32(s, op | INSN_T(data) | INSN_R1(index) | INSN_R2(base));
-}
-
-static inline void tcg_out_addi2(TCGContext *s, int ret, int arg1,
- tcg_target_long val)
-{
- tcg_out_ldst(s, ret, arg1, val, INSN_LDO);
-}
-
-/* This function is required by tcg.c. */
-static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
-{
- tcg_out_addi2(s, reg, reg, val);
-}
-
-static inline void tcg_out_arith(TCGContext *s, int t, int r1, int r2, int op)
-{
- tcg_out32(s, op | INSN_T(t) | INSN_R1(r1) | INSN_R2(r2));
-}
-
-static inline void tcg_out_arithi(TCGContext *s, int t, int r1,
- tcg_target_long val, int op)
-{
- assert(check_fit_tl(val, 11));
- tcg_out32(s, op | INSN_R1(t) | INSN_R2(r1) | INSN_IM11(val));
-}
-
-static inline void tcg_out_nop(TCGContext *s)
-{
- tcg_out_arith(s, TCG_REG_R0, TCG_REG_R0, TCG_REG_R0, INSN_OR);
-}
-
-static inline void tcg_out_mtctl_sar(TCGContext *s, int arg)
-{
- tcg_out32(s, INSN_MTCTL | INSN_R2(11) | INSN_R1(arg));
-}
-
-/* Extract LEN bits at position OFS from ARG and place in RET.
- Note that here the bit ordering is reversed from the PA-RISC
- standard, such that the right-most bit is 0. */
-static inline void tcg_out_extr(TCGContext *s, int ret, int arg,
- unsigned ofs, unsigned len, int sign)
-{
- assert(ofs < 32 && len <= 32 - ofs);
- tcg_out32(s, (sign ? INSN_EXTRS : INSN_EXTRU)
- | INSN_R1(ret) | INSN_R2(arg)
- | INSN_SHDEP_P(31 - ofs) | INSN_DEP_LEN(len));
-}
-
-/* Likewise with OFS interpreted little-endian. */
-static inline void tcg_out_dep(TCGContext *s, int ret, int arg,
- unsigned ofs, unsigned len)
-{
- assert(ofs < 32 && len <= 32 - ofs);
- tcg_out32(s, INSN_DEP | INSN_R2(ret) | INSN_R1(arg)
- | INSN_SHDEP_CP(31 - ofs) | INSN_DEP_LEN(len));
-}
-
-static inline void tcg_out_depi(TCGContext *s, int ret, int arg,
- unsigned ofs, unsigned len)
-{
- assert(ofs < 32 && len <= 32 - ofs);
- tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(arg)
- | INSN_SHDEP_CP(31 - ofs) | INSN_DEP_LEN(len));
-}
-
-static inline void tcg_out_shd(TCGContext *s, int ret, int hi, int lo,
- unsigned count)
-{
- assert(count < 32);
- tcg_out32(s, INSN_SHD | INSN_R1(hi) | INSN_R2(lo) | INSN_T(ret)
- | INSN_SHDEP_CP(count));
-}
-
-static void tcg_out_vshd(TCGContext *s, int ret, int hi, int lo, int creg)
-{
- tcg_out_mtctl_sar(s, creg);
- tcg_out32(s, INSN_VSHD | INSN_T(ret) | INSN_R1(hi) | INSN_R2(lo));
-}
-
-static void tcg_out_ori(TCGContext *s, int ret, int arg, tcg_target_ulong m)
-{
- int bs0, bs1;
-
- /* Note that the argument is constrained to match or_mask_p. */
- for (bs0 = 0; bs0 < 32; bs0++) {
- if ((m & (1u << bs0)) != 0) {
- break;
- }
- }
- for (bs1 = bs0; bs1 < 32; bs1++) {
- if ((m & (1u << bs1)) == 0) {
- break;
- }
- }
- assert(bs1 == 32 || (1ul << bs1) > m);
-
- tcg_out_mov(s, TCG_TYPE_I32, ret, arg);
- tcg_out_depi(s, ret, -1, bs0, bs1 - bs0);
-}
-
-static void tcg_out_andi(TCGContext *s, int ret, int arg, tcg_target_ulong m)
-{
- int ls0, ls1, ms0;
-
- /* Note that the argument is constrained to match and_mask_p. */
- for (ls0 = 0; ls0 < 32; ls0++) {
- if ((m & (1u << ls0)) == 0) {
- break;
- }
- }
- for (ls1 = ls0; ls1 < 32; ls1++) {
- if ((m & (1u << ls1)) != 0) {
- break;
- }
- }
- for (ms0 = ls1; ms0 < 32; ms0++) {
- if ((m & (1u << ms0)) == 0) {
- break;
- }
- }
- assert (ms0 == 32);
-
- if (ls1 == 32) {
- tcg_out_extr(s, ret, arg, 0, ls0, 0);
- } else {
- tcg_out_mov(s, TCG_TYPE_I32, ret, arg);
- tcg_out_depi(s, ret, 0, ls0, ls1 - ls0);
- }
-}
-
-static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg)
-{
- tcg_out_extr(s, ret, arg, 0, 8, 1);
-}
-
-static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg)
-{
- tcg_out_extr(s, ret, arg, 0, 16, 1);
-}
-
-static void tcg_out_shli(TCGContext *s, int ret, int arg, int count)
-{
- count &= 31;
- tcg_out32(s, INSN_ZDEP | INSN_R2(ret) | INSN_R1(arg)
- | INSN_SHDEP_CP(31 - count) | INSN_DEP_LEN(32 - count));
-}
-
-static void tcg_out_shl(TCGContext *s, int ret, int arg, int creg)
-{
- tcg_out_arithi(s, TCG_REG_R20, creg, 31, INSN_SUBI);
- tcg_out_mtctl_sar(s, TCG_REG_R20);
- tcg_out32(s, INSN_ZVDEP | INSN_R2(ret) | INSN_R1(arg) | INSN_DEP_LEN(32));
-}
-
-static void tcg_out_shri(TCGContext *s, int ret, int arg, int count)
-{
- count &= 31;
- tcg_out_extr(s, ret, arg, count, 32 - count, 0);
-}
-
-static void tcg_out_shr(TCGContext *s, int ret, int arg, int creg)
-{
- tcg_out_vshd(s, ret, TCG_REG_R0, arg, creg);
-}
-
-static void tcg_out_sari(TCGContext *s, int ret, int arg, int count)
-{
- count &= 31;
- tcg_out_extr(s, ret, arg, count, 32 - count, 1);
-}
-
-static void tcg_out_sar(TCGContext *s, int ret, int arg, int creg)
-{
- tcg_out_arithi(s, TCG_REG_R20, creg, 31, INSN_SUBI);
- tcg_out_mtctl_sar(s, TCG_REG_R20);
- tcg_out32(s, INSN_VEXTRS | INSN_R1(ret) | INSN_R2(arg) | INSN_DEP_LEN(32));
-}
-
-static void tcg_out_rotli(TCGContext *s, int ret, int arg, int count)
-{
- count &= 31;
- tcg_out_shd(s, ret, arg, arg, 32 - count);
-}
-
-static void tcg_out_rotl(TCGContext *s, int ret, int arg, int creg)
-{
- tcg_out_arithi(s, TCG_REG_R20, creg, 32, INSN_SUBI);
- tcg_out_vshd(s, ret, arg, arg, TCG_REG_R20);
-}
-
-static void tcg_out_rotri(TCGContext *s, int ret, int arg, int count)
-{
- count &= 31;
- tcg_out_shd(s, ret, arg, arg, count);
-}
-
-static void tcg_out_rotr(TCGContext *s, int ret, int arg, int creg)
-{
- tcg_out_vshd(s, ret, arg, arg, creg);
-}
-
-static void tcg_out_bswap16(TCGContext *s, int ret, int arg, int sign)
-{
- if (ret != arg) {
- tcg_out_mov(s, TCG_TYPE_I32, ret, arg); /* arg = xxAB */
- }
- tcg_out_dep(s, ret, ret, 16, 8); /* ret = xBAB */
- tcg_out_extr(s, ret, ret, 8, 16, sign); /* ret = ..BA */
-}
-
-static void tcg_out_bswap32(TCGContext *s, int ret, int arg, int temp)
-{
- /* arg = ABCD */
- tcg_out_rotri(s, temp, arg, 16); /* temp = CDAB */
- tcg_out_dep(s, temp, temp, 16, 8); /* temp = CBAB */
- tcg_out_shd(s, ret, arg, temp, 8); /* ret = DCBA */
-}
-
-static void tcg_out_call(TCGContext *s, const void *func)
-{
- tcg_target_long val, hi, lo, disp;
-
- val = (uint32_t)__canonicalize_funcptr_for_compare(func);
- disp = (val - ((tcg_target_long)s->code_ptr + 8)) >> 2;
-
- if (check_fit_tl(disp, 17)) {
- tcg_out32(s, INSN_BL_N | INSN_R2(TCG_REG_RP) | reassemble_17(disp));
- } else {
- hi = val >> 11;
- lo = val & 0x7ff;
-
- tcg_out32(s, INSN_LDIL | INSN_R2(TCG_REG_R20) | reassemble_21(hi));
- tcg_out32(s, INSN_BLE_SR4 | INSN_R2(TCG_REG_R20)
- | reassemble_17(lo >> 2));
- tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_RP, TCG_REG_R31);
- }
-}
-
-static void tcg_out_xmpyu(TCGContext *s, int retl, int reth,
- int arg1, int arg2)
-{
- /* Store both words into the stack for copy to the FPU. */
- tcg_out_ldst(s, arg1, TCG_REG_CALL_STACK, STACK_TEMP_OFS, INSN_STW);
- tcg_out_ldst(s, arg2, TCG_REG_CALL_STACK, STACK_TEMP_OFS + 4, INSN_STW);
-
- /* Load both words into the FPU at the same time. We get away
- with this because we can address the left and right half of the
- FPU registers individually once loaded. */
- /* fldds stack_temp(sp),fr22 */
- tcg_out32(s, INSN_FLDDS | INSN_R2(TCG_REG_CALL_STACK)
- | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22));
-
- /* xmpyu fr22r,fr22,fr22 */
- tcg_out32(s, 0x3ad64796);
-
- /* Store the 64-bit result back into the stack. */
- /* fstds stack_temp(sp),fr22 */
- tcg_out32(s, INSN_FSTDS | INSN_R2(TCG_REG_CALL_STACK)
- | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22));
-
- /* Load the pieces of the result that the caller requested. */
- if (reth) {
- tcg_out_ldst(s, reth, TCG_REG_CALL_STACK, STACK_TEMP_OFS, INSN_LDW);
- }
- if (retl) {
- tcg_out_ldst(s, retl, TCG_REG_CALL_STACK, STACK_TEMP_OFS + 4,
- INSN_LDW);
- }
-}
-
-static void tcg_out_add2(TCGContext *s, int destl, int desth,
- int al, int ah, int bl, int bh, int blconst)
-{
- int tmp = (destl == ah || destl == bh ? TCG_REG_R20 : destl);
-
- if (blconst) {
- tcg_out_arithi(s, tmp, al, bl, INSN_ADDI);
- } else {
- tcg_out_arith(s, tmp, al, bl, INSN_ADD);
- }
- tcg_out_arith(s, desth, ah, bh, INSN_ADDC);
-
- tcg_out_mov(s, TCG_TYPE_I32, destl, tmp);
-}
-
-static void tcg_out_sub2(TCGContext *s, int destl, int desth, int al, int ah,
- int bl, int bh, int alconst, int blconst)
-{
- int tmp = (destl == ah || destl == bh ? TCG_REG_R20 : destl);
-
- if (alconst) {
- if (blconst) {
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R20, bl);
- bl = TCG_REG_R20;
- }
- tcg_out_arithi(s, tmp, bl, al, INSN_SUBI);
- } else if (blconst) {
- tcg_out_arithi(s, tmp, al, -bl, INSN_ADDI);
- } else {
- tcg_out_arith(s, tmp, al, bl, INSN_SUB);
- }
- tcg_out_arith(s, desth, ah, bh, INSN_SUBB);
-
- tcg_out_mov(s, TCG_TYPE_I32, destl, tmp);
-}
-
-static void tcg_out_branch(TCGContext *s, int label_index, int nul)
-{
- TCGLabel *l = &s->labels[label_index];
- uint32_t op = nul ? INSN_BL_N : INSN_BL;
-
- if (l->has_value) {
- tcg_target_long val = l->u.value;
-
- val -= (tcg_target_long)s->code_ptr + 8;
- val >>= 2;
- assert(check_fit_tl(val, 17));
-
- tcg_out32(s, op | reassemble_17(val));
- } else {
- /* We need to keep the offset unchanged for retranslation. */
- uint32_t old_insn = *(uint32_t *)s->code_ptr;
-
- tcg_out_reloc(s, s->code_ptr, R_PARISC_PCREL17F, label_index, 0);
- tcg_out32(s, op | (old_insn & 0x1f1ffdu));
- }
-}
-
-static const uint8_t tcg_cond_to_cmp_cond[] =
-{
- [TCG_COND_EQ] = COND_EQ,
- [TCG_COND_NE] = COND_EQ | COND_FALSE,
- [TCG_COND_LT] = COND_LT,
- [TCG_COND_GE] = COND_LT | COND_FALSE,
- [TCG_COND_LE] = COND_LE,
- [TCG_COND_GT] = COND_LE | COND_FALSE,
- [TCG_COND_LTU] = COND_LTU,
- [TCG_COND_GEU] = COND_LTU | COND_FALSE,
- [TCG_COND_LEU] = COND_LEU,
- [TCG_COND_GTU] = COND_LEU | COND_FALSE,
-};
-
-static void tcg_out_brcond(TCGContext *s, int cond, TCGArg c1,
- TCGArg c2, int c2const, int label_index)
-{
- TCGLabel *l = &s->labels[label_index];
- int op, pacond;
-
- /* Note that COMIB operates as if the immediate is the first
- operand. We model brcond with the immediate in the second
- to better match what targets are likely to give us. For
- consistency, model COMB with reversed operands as well. */
- pacond = tcg_cond_to_cmp_cond[tcg_swap_cond(cond)];
-
- if (c2const) {
- op = (pacond & COND_FALSE ? INSN_COMIBF : INSN_COMIBT);
- op |= INSN_IM5(c2);
- } else {
- op = (pacond & COND_FALSE ? INSN_COMBF : INSN_COMBT);
- op |= INSN_R1(c2);
- }
- op |= INSN_R2(c1);
- op |= INSN_COND(pacond & 7);
-
- if (l->has_value) {
- tcg_target_long val = l->u.value;
-
- val -= (tcg_target_long)s->code_ptr + 8;
- val >>= 2;
- assert(check_fit_tl(val, 12));
-
- /* ??? Assume that all branches to defined labels are backward.
- Which means that if the nul bit is set, the delay slot is
- executed if the branch is taken, and not executed in fallthru. */
- tcg_out32(s, op | reassemble_12(val));
- tcg_out_nop(s);
- } else {
- /* We need to keep the offset unchanged for retranslation. */
- uint32_t old_insn = *(uint32_t *)s->code_ptr;
-
- tcg_out_reloc(s, s->code_ptr, R_PARISC_PCREL12F, label_index, 0);
- /* ??? Assume that all branches to undefined labels are forward.
- Which means that if the nul bit is set, the delay slot is
- not executed if the branch is taken, which is what we want. */
- tcg_out32(s, op | 2 | (old_insn & 0x1ffdu));
- }
-}
-
-static void tcg_out_comclr(TCGContext *s, int cond, TCGArg ret,
- TCGArg c1, TCGArg c2, int c2const)
-{
- int op, pacond;
-
- /* Note that COMICLR operates as if the immediate is the first
- operand. We model setcond with the immediate in the second
- to better match what targets are likely to give us. For
- consistency, model COMCLR with reversed operands as well. */
- pacond = tcg_cond_to_cmp_cond[tcg_swap_cond(cond)];
-
- if (c2const) {
- op = INSN_COMICLR | INSN_R2(c1) | INSN_R1(ret) | INSN_IM11(c2);
- } else {
- op = INSN_COMCLR | INSN_R2(c1) | INSN_R1(c2) | INSN_T(ret);
- }
- op |= INSN_COND(pacond & 7);
- op |= pacond & COND_FALSE ? 1 << 12 : 0;
-
- tcg_out32(s, op);
-}
-
-static void tcg_out_brcond2(TCGContext *s, int cond, TCGArg al, TCGArg ah,
- TCGArg bl, int blconst, TCGArg bh, int bhconst,
- int label_index)
-{
- switch (cond) {
- case TCG_COND_EQ:
- tcg_out_comclr(s, TCG_COND_NE, TCG_REG_R0, al, bl, blconst);
- tcg_out_brcond(s, TCG_COND_EQ, ah, bh, bhconst, label_index);
- break;
- case TCG_COND_NE:
- tcg_out_brcond(s, TCG_COND_NE, al, bl, blconst, label_index);
- tcg_out_brcond(s, TCG_COND_NE, ah, bh, bhconst, label_index);
- break;
- default:
- tcg_out_brcond(s, tcg_high_cond(cond), ah, bh, bhconst, label_index);
- tcg_out_comclr(s, TCG_COND_NE, TCG_REG_R0, ah, bh, bhconst);
- tcg_out_brcond(s, tcg_unsigned_cond(cond),
- al, bl, blconst, label_index);
- break;
- }
-}
-
-static void tcg_out_setcond(TCGContext *s, int cond, TCGArg ret,
- TCGArg c1, TCGArg c2, int c2const)
-{
- tcg_out_comclr(s, tcg_invert_cond(cond), ret, c1, c2, c2const);
- tcg_out_movi(s, TCG_TYPE_I32, ret, 1);
-}
-
-static void tcg_out_setcond2(TCGContext *s, int cond, TCGArg ret,
- TCGArg al, TCGArg ah, TCGArg bl, int blconst,
- TCGArg bh, int bhconst)
-{
- int scratch = TCG_REG_R20;
-
- /* Note that the low parts are fully consumed before scratch is set. */
- if (ret != ah && (bhconst || ret != bh)) {
- scratch = ret;
- }
-
- switch (cond) {
- case TCG_COND_EQ:
- case TCG_COND_NE:
- tcg_out_setcond(s, cond, scratch, al, bl, blconst);
- tcg_out_comclr(s, TCG_COND_EQ, TCG_REG_R0, ah, bh, bhconst);
- tcg_out_movi(s, TCG_TYPE_I32, scratch, cond == TCG_COND_NE);
- break;
-
- case TCG_COND_GE:
- case TCG_COND_GEU:
- case TCG_COND_LT:
- case TCG_COND_LTU:
- /* Optimize compares with low part zero. */
- if (bl == 0) {
- tcg_out_setcond(s, cond, ret, ah, bh, bhconst);
- return;
- }
- /* FALLTHRU */
-
- case TCG_COND_LE:
- case TCG_COND_LEU:
- case TCG_COND_GT:
- case TCG_COND_GTU:
- /* <= : ah < bh | (ah == bh && al <= bl) */
- tcg_out_setcond(s, tcg_unsigned_cond(cond), scratch, al, bl, blconst);
- tcg_out_comclr(s, TCG_COND_EQ, TCG_REG_R0, ah, bh, bhconst);
- tcg_out_movi(s, TCG_TYPE_I32, scratch, 0);
- tcg_out_comclr(s, tcg_invert_cond(tcg_high_cond(cond)),
- TCG_REG_R0, ah, bh, bhconst);
- tcg_out_movi(s, TCG_TYPE_I32, scratch, 1);
- break;
-
- default:
- tcg_abort();
- }
-
- tcg_out_mov(s, TCG_TYPE_I32, ret, scratch);
-}
-
-static void tcg_out_movcond(TCGContext *s, int cond, TCGArg ret,
- TCGArg c1, TCGArg c2, int c2const,
- TCGArg v1, int v1const)
-{
- tcg_out_comclr(s, tcg_invert_cond(cond), TCG_REG_R0, c1, c2, c2const);
- if (v1const) {
- tcg_out_movi(s, TCG_TYPE_I32, ret, v1);
- } else {
- tcg_out_mov(s, TCG_TYPE_I32, ret, v1);
- }
-}
-
-#if defined(CONFIG_SOFTMMU)
-/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr,
- int mmu_idx) */
-static const void * const qemu_ld_helpers[4] = {
- helper_ldb_mmu,
- helper_ldw_mmu,
- helper_ldl_mmu,
- helper_ldq_mmu,
-};
-
-/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr,
- uintxx_t val, int mmu_idx) */
-static const void * const qemu_st_helpers[4] = {
- helper_stb_mmu,
- helper_stw_mmu,
- helper_stl_mmu,
- helper_stq_mmu,
-};
-
-/* Load and compare a TLB entry, and branch if TLB miss. OFFSET is set to
- the offset of the first ADDR_READ or ADDR_WRITE member of the appropriate
- TLB for the memory index. The return value is the offset from ENV
- contained in R1 afterward (to be used when loading ADDEND); if the
- return value is 0, R1 is not used. */
-
-static int tcg_out_tlb_read(TCGContext *s, int r0, int r1, int addrlo,
- int addrhi, int s_bits, int lab_miss, int offset)
-{
- int ret;
-
- /* Extracting the index into the TLB. The "normal C operation" is
- r1 = addr_reg >> TARGET_PAGE_BITS;
- r1 &= CPU_TLB_SIZE - 1;
- r1 <<= CPU_TLB_ENTRY_BITS;
- What this does is extract CPU_TLB_BITS beginning at TARGET_PAGE_BITS
- and place them at CPU_TLB_ENTRY_BITS. We can combine the first two
- operations with an EXTRU. Unfortunately, the current value of
- CPU_TLB_ENTRY_BITS is > 3, so we can't merge that shift with the
- add that follows. */
- tcg_out_extr(s, r1, addrlo, TARGET_PAGE_BITS, CPU_TLB_BITS, 0);
- tcg_out_shli(s, r1, r1, CPU_TLB_ENTRY_BITS);
- tcg_out_arith(s, r1, r1, TCG_AREG0, INSN_ADDL);
-
- /* Make sure that both the addr_{read,write} and addend can be
- read with a 14-bit offset from the same base register. */
- if (check_fit_tl(offset + CPU_TLB_SIZE, 14)) {
- ret = 0;
- } else {
- ret = (offset + 0x400) & ~0x7ff;
- offset = ret - offset;
- tcg_out_addi2(s, TCG_REG_R1, r1, ret);
- r1 = TCG_REG_R1;
- }
-
- /* Load the entry from the computed slot. */
- if (TARGET_LONG_BITS == 64) {
- tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R23, r1, offset);
- tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, r1, offset + 4);
- } else {
- tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, r1, offset);
- }
-
- /* Compute the value that ought to appear in the TLB for a hit, namely,
- the page of the address. We include the low N bits of the address
- to catch unaligned accesses and force them onto the slow path. Do
- this computation after having issued the load from the TLB slot to
- give the load time to complete. */
- tcg_out_andi(s, r0, addrlo, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
-
- /* If not equal, jump to lab_miss. */
- if (TARGET_LONG_BITS == 64) {
- tcg_out_brcond2(s, TCG_COND_NE, TCG_REG_R20, TCG_REG_R23,
- r0, 0, addrhi, 0, lab_miss);
- } else {
- tcg_out_brcond(s, TCG_COND_NE, TCG_REG_R20, r0, 0, lab_miss);
- }
-
- return ret;
-}
-
-static int tcg_out_arg_reg32(TCGContext *s, int argno, TCGArg v, bool vconst)
-{
- if (argno < 4) {
- if (vconst) {
- tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[argno], v);
- } else {
- tcg_out_mov(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[argno], v);
- }
- } else {
- if (vconst && v != 0) {
- tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R20, v);
- v = TCG_REG_R20;
- }
- tcg_out_st(s, TCG_TYPE_I32, v, TCG_REG_CALL_STACK,
- TCG_TARGET_CALL_STACK_OFFSET - ((argno - 3) * 4));
- }
- return argno + 1;
-}
-
-static int tcg_out_arg_reg64(TCGContext *s, int argno, TCGArg vl, TCGArg vh)
-{
- /* 64-bit arguments must go in even reg pairs and stack slots. */
- if (argno & 1) {
- argno++;
- }
- argno = tcg_out_arg_reg32(s, argno, vl, false);
- argno = tcg_out_arg_reg32(s, argno, vh, false);
- return argno;
-}
-#endif
-
-static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo_reg, int datahi_reg,
- int addr_reg, int addend_reg, int opc)
-{
-#ifdef TARGET_WORDS_BIGENDIAN
- const int bswap = 0;
-#else
- const int bswap = 1;
-#endif
-
- switch (opc) {
- case 0:
- tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDBX);
- break;
- case 0 | 4:
- tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDBX);
- tcg_out_ext8s(s, datalo_reg, datalo_reg);
- break;
- case 1:
- tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDHX);
- if (bswap) {
- tcg_out_bswap16(s, datalo_reg, datalo_reg, 0);
- }
- break;
- case 1 | 4:
- tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDHX);
- if (bswap) {
- tcg_out_bswap16(s, datalo_reg, datalo_reg, 1);
- } else {
- tcg_out_ext16s(s, datalo_reg, datalo_reg);
- }
- break;
- case 2:
- tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDWX);
- if (bswap) {
- tcg_out_bswap32(s, datalo_reg, datalo_reg, TCG_REG_R20);
- }
- break;
- case 3:
- if (bswap) {
- int t = datahi_reg;
- datahi_reg = datalo_reg;
- datalo_reg = t;
- }
- /* We can't access the low-part with a reg+reg addressing mode,
- so perform the addition now and use reg_ofs addressing mode. */
- if (addend_reg != TCG_REG_R0) {
- tcg_out_arith(s, TCG_REG_R20, addr_reg, addend_reg, INSN_ADD);
- addr_reg = TCG_REG_R20;
- }
- /* Make sure not to clobber the base register. */
- if (datahi_reg == addr_reg) {
- tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_LDW);
- tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_LDW);
- } else {
- tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_LDW);
- tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_LDW);
- }
- if (bswap) {
- tcg_out_bswap32(s, datalo_reg, datalo_reg, TCG_REG_R20);
- tcg_out_bswap32(s, datahi_reg, datahi_reg, TCG_REG_R20);
- }
- break;
- default:
- tcg_abort();
- }
-}
-
-static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
-{
- int datalo_reg = *args++;
- /* Note that datahi_reg is only used for 64-bit loads. */
- int datahi_reg = (opc == 3 ? *args++ : TCG_REG_R0);
- int addrlo_reg = *args++;
-
-#if defined(CONFIG_SOFTMMU)
- /* Note that addrhi_reg is only used for 64-bit guests. */
- int addrhi_reg = (TARGET_LONG_BITS == 64 ? *args++ : TCG_REG_R0);
- int mem_index = *args;
- int lab1, lab2, argno, offset;
-
- lab1 = gen_new_label();
- lab2 = gen_new_label();
-
- offset = offsetof(CPUArchState, tlb_table[mem_index][0].addr_read);
- offset = tcg_out_tlb_read(s, TCG_REG_R26, TCG_REG_R25, addrlo_reg,
- addrhi_reg, opc & 3, lab1, offset);
-
- /* TLB Hit. */
- tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20,
- (offset ? TCG_REG_R1 : TCG_REG_R25),
- offsetof(CPUArchState, tlb_table[mem_index][0].addend) - offset);
- tcg_out_qemu_ld_direct(s, datalo_reg, datahi_reg, addrlo_reg,
- TCG_REG_R20, opc);
- tcg_out_branch(s, lab2, 1);
-
- /* TLB Miss. */
- /* label1: */
- tcg_out_label(s, lab1, s->code_ptr);
-
- argno = 0;
- argno = tcg_out_arg_reg32(s, argno, TCG_AREG0, false);
- if (TARGET_LONG_BITS == 64) {
- argno = tcg_out_arg_reg64(s, argno, addrlo_reg, addrhi_reg);
- } else {
- argno = tcg_out_arg_reg32(s, argno, addrlo_reg, false);
- }
- argno = tcg_out_arg_reg32(s, argno, mem_index, true);
-
- tcg_out_call(s, qemu_ld_helpers[opc & 3]);
-
- switch (opc) {
- case 0:
- tcg_out_andi(s, datalo_reg, TCG_REG_RET0, 0xff);
- break;
- case 0 | 4:
- tcg_out_ext8s(s, datalo_reg, TCG_REG_RET0);
- break;
- case 1:
- tcg_out_andi(s, datalo_reg, TCG_REG_RET0, 0xffff);
- break;
- case 1 | 4:
- tcg_out_ext16s(s, datalo_reg, TCG_REG_RET0);
- break;
- case 2:
- case 2 | 4:
- tcg_out_mov(s, TCG_TYPE_I32, datalo_reg, TCG_REG_RET0);
- break;
- case 3:
- tcg_out_mov(s, TCG_TYPE_I32, datahi_reg, TCG_REG_RET0);
- tcg_out_mov(s, TCG_TYPE_I32, datalo_reg, TCG_REG_RET1);
- break;
- default:
- tcg_abort();
- }
-
- /* label2: */
- tcg_out_label(s, lab2, s->code_ptr);
-#else
- tcg_out_qemu_ld_direct(s, datalo_reg, datahi_reg, addrlo_reg,
- (GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_R0), opc);
-#endif
-}
-
-static void tcg_out_qemu_st_direct(TCGContext *s, int datalo_reg,
- int datahi_reg, int addr_reg, int opc)
-{
-#ifdef TARGET_WORDS_BIGENDIAN
- const int bswap = 0;
-#else
- const int bswap = 1;
-#endif
-
- switch (opc) {
- case 0:
- tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STB);
- break;
- case 1:
- if (bswap) {
- tcg_out_bswap16(s, TCG_REG_R20, datalo_reg, 0);
- datalo_reg = TCG_REG_R20;
- }
- tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STH);
- break;
- case 2:
- if (bswap) {
- tcg_out_bswap32(s, TCG_REG_R20, datalo_reg, TCG_REG_R20);
- datalo_reg = TCG_REG_R20;
- }
- tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STW);
- break;
- case 3:
- if (bswap) {
- tcg_out_bswap32(s, TCG_REG_R20, datalo_reg, TCG_REG_R20);
- tcg_out_bswap32(s, TCG_REG_R23, datahi_reg, TCG_REG_R23);
- datahi_reg = TCG_REG_R20;
- datalo_reg = TCG_REG_R23;
- }
- tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_STW);
- tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_STW);
- break;
- default:
- tcg_abort();
- }
-
-}
-
-static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc)
-{
- int datalo_reg = *args++;
- /* Note that datahi_reg is only used for 64-bit loads. */
- int datahi_reg = (opc == 3 ? *args++ : TCG_REG_R0);
- int addrlo_reg = *args++;
-
-#if defined(CONFIG_SOFTMMU)
- /* Note that addrhi_reg is only used for 64-bit guests. */
- int addrhi_reg = (TARGET_LONG_BITS == 64 ? *args++ : TCG_REG_R0);
- int mem_index = *args;
- int lab1, lab2, argno, next, offset;
-
- lab1 = gen_new_label();
- lab2 = gen_new_label();
-
- offset = offsetof(CPUArchState, tlb_table[mem_index][0].addr_write);
- offset = tcg_out_tlb_read(s, TCG_REG_R26, TCG_REG_R25, addrlo_reg,
- addrhi_reg, opc, lab1, offset);
-
- /* TLB Hit. */
- tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20,
- (offset ? TCG_REG_R1 : TCG_REG_R25),
- offsetof(CPUArchState, tlb_table[mem_index][0].addend) - offset);
-
- /* There are no indexed stores, so we must do this addition explitly.
- Careful to avoid R20, which is used for the bswaps to follow. */
- tcg_out_arith(s, TCG_REG_R31, addrlo_reg, TCG_REG_R20, INSN_ADDL);
- tcg_out_qemu_st_direct(s, datalo_reg, datahi_reg, TCG_REG_R31, opc);
- tcg_out_branch(s, lab2, 1);
-
- /* TLB Miss. */
- /* label1: */
- tcg_out_label(s, lab1, s->code_ptr);
-
- argno = 0;
- argno = tcg_out_arg_reg32(s, argno, TCG_AREG0, false);
- if (TARGET_LONG_BITS == 64) {
- argno = tcg_out_arg_reg64(s, argno, addrlo_reg, addrhi_reg);
- } else {
- argno = tcg_out_arg_reg32(s, argno, addrlo_reg, false);
- }
-
- next = (argno < 4 ? tcg_target_call_iarg_regs[argno] : TCG_REG_R20);
- switch(opc) {
- case 0:
- tcg_out_andi(s, next, datalo_reg, 0xff);
- argno = tcg_out_arg_reg32(s, argno, next, false);
- break;
- case 1:
- tcg_out_andi(s, next, datalo_reg, 0xffff);
- argno = tcg_out_arg_reg32(s, argno, next, false);
- break;
- case 2:
- argno = tcg_out_arg_reg32(s, argno, datalo_reg, false);
- break;
- case 3:
- argno = tcg_out_arg_reg64(s, argno, datalo_reg, datahi_reg);
- break;
- default:
- tcg_abort();
- }
- argno = tcg_out_arg_reg32(s, argno, mem_index, true);
-
- tcg_out_call(s, qemu_st_helpers[opc]);
-
- /* label2: */
- tcg_out_label(s, lab2, s->code_ptr);
-#else
- /* There are no indexed stores, so if GUEST_BASE is set we must do
- the add explicitly. Careful to avoid R20, which is used for the
- bswaps to follow. */
- if (GUEST_BASE != 0) {
- tcg_out_arith(s, TCG_REG_R31, addrlo_reg,
- TCG_GUEST_BASE_REG, INSN_ADDL);
- addrlo_reg = TCG_REG_R31;
- }
- tcg_out_qemu_st_direct(s, datalo_reg, datahi_reg, addrlo_reg, opc);
-#endif
-}
-
-static void tcg_out_exit_tb(TCGContext *s, TCGArg arg)
-{
- if (!check_fit_tl(arg, 14)) {
- uint32_t hi, lo;
- hi = arg & ~0x7ff;
- lo = arg & 0x7ff;
- if (lo) {
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, hi);
- tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_R18));
- tcg_out_addi(s, TCG_REG_RET0, lo);
- return;
- }
- arg = hi;
- }
- tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_R18));
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, arg);
-}
-
-static void tcg_out_goto_tb(TCGContext *s, TCGArg arg)
-{
- if (s->tb_jmp_offset) {
- /* direct jump method */
- fprintf(stderr, "goto_tb direct\n");
- tcg_abort();
- } else {
- /* indirect jump method */
- tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, TCG_REG_R0,
- (tcg_target_long)(s->tb_next + arg));
- tcg_out32(s, INSN_BV_N | INSN_R2(TCG_REG_R20));
- }
- s->tb_next_offset[arg] = s->code_ptr - s->code_buf;
-}
-
-static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
- const int *const_args)
-{
- switch (opc) {
- case INDEX_op_exit_tb:
- tcg_out_exit_tb(s, args[0]);
- break;
- case INDEX_op_goto_tb:
- tcg_out_goto_tb(s, args[0]);
- break;
-
- case INDEX_op_call:
- if (const_args[0]) {
- tcg_out_call(s, (void *)args[0]);
- } else {
- /* ??? FIXME: the value in the register in args[0] is almost
- certainly a procedure descriptor, not a code address. We
- probably need to use the millicode $$dyncall routine. */
- tcg_abort();
- }
- break;
-
- case INDEX_op_br:
- tcg_out_branch(s, args[0], 1);
- break;
-
- case INDEX_op_movi_i32:
- tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]);
- break;
-
- case INDEX_op_ld8u_i32:
- tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDB);
- break;
- case INDEX_op_ld8s_i32:
- tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDB);
- tcg_out_ext8s(s, args[0], args[0]);
- break;
- case INDEX_op_ld16u_i32:
- tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDH);
- break;
- case INDEX_op_ld16s_i32:
- tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDH);
- tcg_out_ext16s(s, args[0], args[0]);
- break;
- case INDEX_op_ld_i32:
- tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDW);
- break;
-
- case INDEX_op_st8_i32:
- tcg_out_ldst(s, args[0], args[1], args[2], INSN_STB);
- break;
- case INDEX_op_st16_i32:
- tcg_out_ldst(s, args[0], args[1], args[2], INSN_STH);
- break;
- case INDEX_op_st_i32:
- tcg_out_ldst(s, args[0], args[1], args[2], INSN_STW);
- break;
-
- case INDEX_op_add_i32:
- if (const_args[2]) {
- tcg_out_addi2(s, args[0], args[1], args[2]);
- } else {
- tcg_out_arith(s, args[0], args[1], args[2], INSN_ADDL);
- }
- break;
-
- case INDEX_op_sub_i32:
- if (const_args[1]) {
- if (const_args[2]) {
- tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1] - args[2]);
- } else {
- /* Recall that SUBI is a reversed subtract. */
- tcg_out_arithi(s, args[0], args[2], args[1], INSN_SUBI);
- }
- } else if (const_args[2]) {
- tcg_out_addi2(s, args[0], args[1], -args[2]);
- } else {
- tcg_out_arith(s, args[0], args[1], args[2], INSN_SUB);
- }
- break;
-
- case INDEX_op_and_i32:
- if (const_args[2]) {
- tcg_out_andi(s, args[0], args[1], args[2]);
- } else {
- tcg_out_arith(s, args[0], args[1], args[2], INSN_AND);
- }
- break;
-
- case INDEX_op_or_i32:
- if (const_args[2]) {
- tcg_out_ori(s, args[0], args[1], args[2]);
- } else {
- tcg_out_arith(s, args[0], args[1], args[2], INSN_OR);
- }
- break;
-
- case INDEX_op_xor_i32:
- tcg_out_arith(s, args[0], args[1], args[2], INSN_XOR);
- break;
-
- case INDEX_op_andc_i32:
- if (const_args[2]) {
- tcg_out_andi(s, args[0], args[1], ~args[2]);
- } else {
- tcg_out_arith(s, args[0], args[1], args[2], INSN_ANDCM);
- }
- break;
-
- case INDEX_op_shl_i32:
- if (const_args[2]) {
- tcg_out_shli(s, args[0], args[1], args[2]);
- } else {
- tcg_out_shl(s, args[0], args[1], args[2]);
- }
- break;
-
- case INDEX_op_shr_i32:
- if (const_args[2]) {
- tcg_out_shri(s, args[0], args[1], args[2]);
- } else {
- tcg_out_shr(s, args[0], args[1], args[2]);
- }
- break;
-
- case INDEX_op_sar_i32:
- if (const_args[2]) {
- tcg_out_sari(s, args[0], args[1], args[2]);
- } else {
- tcg_out_sar(s, args[0], args[1], args[2]);
- }
- break;
-
- case INDEX_op_rotl_i32:
- if (const_args[2]) {
- tcg_out_rotli(s, args[0], args[1], args[2]);
- } else {
- tcg_out_rotl(s, args[0], args[1], args[2]);
- }
- break;
-
- case INDEX_op_rotr_i32:
- if (const_args[2]) {
- tcg_out_rotri(s, args[0], args[1], args[2]);
- } else {
- tcg_out_rotr(s, args[0], args[1], args[2]);
- }
- break;
-
- case INDEX_op_mul_i32:
- tcg_out_xmpyu(s, args[0], TCG_REG_R0, args[1], args[2]);
- break;
- case INDEX_op_mulu2_i32:
- tcg_out_xmpyu(s, args[0], args[1], args[2], args[3]);
- break;
-
- case INDEX_op_bswap16_i32:
- tcg_out_bswap16(s, args[0], args[1], 0);
- break;
- case INDEX_op_bswap32_i32:
- tcg_out_bswap32(s, args[0], args[1], TCG_REG_R20);
- break;
-
- case INDEX_op_not_i32:
- tcg_out_arithi(s, args[0], args[1], -1, INSN_SUBI);
- break;
- case INDEX_op_ext8s_i32:
- tcg_out_ext8s(s, args[0], args[1]);
- break;
- case INDEX_op_ext16s_i32:
- tcg_out_ext16s(s, args[0], args[1]);
- break;
-
- case INDEX_op_brcond_i32:
- tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], args[3]);
- break;
- case INDEX_op_brcond2_i32:
- tcg_out_brcond2(s, args[4], args[0], args[1],
- args[2], const_args[2],
- args[3], const_args[3], args[5]);
- break;
-
- case INDEX_op_setcond_i32:
- tcg_out_setcond(s, args[3], args[0], args[1], args[2], const_args[2]);
- break;
- case INDEX_op_setcond2_i32:
- tcg_out_setcond2(s, args[5], args[0], args[1], args[2],
- args[3], const_args[3], args[4], const_args[4]);
- break;
-
- case INDEX_op_movcond_i32:
- tcg_out_movcond(s, args[5], args[0], args[1], args[2], const_args[2],
- args[3], const_args[3]);
- break;
-
- case INDEX_op_add2_i32:
- tcg_out_add2(s, args[0], args[1], args[2], args[3],
- args[4], args[5], const_args[4]);
- break;
-
- case INDEX_op_sub2_i32:
- tcg_out_sub2(s, args[0], args[1], args[2], args[3],
- args[4], args[5], const_args[2], const_args[4]);
- break;
-
- case INDEX_op_deposit_i32:
- if (const_args[2]) {
- tcg_out_depi(s, args[0], args[2], args[3], args[4]);
- } else {
- tcg_out_dep(s, args[0], args[2], args[3], args[4]);
- }
- break;
-
- case INDEX_op_qemu_ld8u:
- tcg_out_qemu_ld(s, args, 0);
- break;
- case INDEX_op_qemu_ld8s:
- tcg_out_qemu_ld(s, args, 0 | 4);
- break;
- case INDEX_op_qemu_ld16u:
- tcg_out_qemu_ld(s, args, 1);
- break;
- case INDEX_op_qemu_ld16s:
- tcg_out_qemu_ld(s, args, 1 | 4);
- break;
- case INDEX_op_qemu_ld32:
- tcg_out_qemu_ld(s, args, 2);
- break;
- case INDEX_op_qemu_ld64:
- tcg_out_qemu_ld(s, args, 3);
- break;
-
- case INDEX_op_qemu_st8:
- tcg_out_qemu_st(s, args, 0);
- break;
- case INDEX_op_qemu_st16:
- tcg_out_qemu_st(s, args, 1);
- break;
- case INDEX_op_qemu_st32:
- tcg_out_qemu_st(s, args, 2);
- break;
- case INDEX_op_qemu_st64:
- tcg_out_qemu_st(s, args, 3);
- break;
-
- default:
- fprintf(stderr, "unknown opcode 0x%x\n", opc);
- tcg_abort();
- }
-}
-
-static const TCGTargetOpDef hppa_op_defs[] = {
- { INDEX_op_exit_tb, { } },
- { INDEX_op_goto_tb, { } },
-
- { INDEX_op_call, { "ri" } },
- { INDEX_op_br, { } },
-
- { INDEX_op_mov_i32, { "r", "r" } },
- { INDEX_op_movi_i32, { "r" } },
-
- { INDEX_op_ld8u_i32, { "r", "r" } },
- { INDEX_op_ld8s_i32, { "r", "r" } },
- { INDEX_op_ld16u_i32, { "r", "r" } },
- { INDEX_op_ld16s_i32, { "r", "r" } },
- { INDEX_op_ld_i32, { "r", "r" } },
- { INDEX_op_st8_i32, { "rZ", "r" } },
- { INDEX_op_st16_i32, { "rZ", "r" } },
- { INDEX_op_st_i32, { "rZ", "r" } },
-
- { INDEX_op_add_i32, { "r", "rZ", "ri" } },
- { INDEX_op_sub_i32, { "r", "rI", "ri" } },
- { INDEX_op_and_i32, { "r", "rZ", "rM" } },
- { INDEX_op_or_i32, { "r", "rZ", "rO" } },
- { INDEX_op_xor_i32, { "r", "rZ", "rZ" } },
- /* Note that the second argument will be inverted, which means
- we want a constant whose inversion matches M, and that O = ~M.
- See the implementation of and_mask_p. */
- { INDEX_op_andc_i32, { "r", "rZ", "rO" } },
-
- { INDEX_op_mul_i32, { "r", "r", "r" } },
- { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } },
-
- { INDEX_op_shl_i32, { "r", "r", "ri" } },
- { INDEX_op_shr_i32, { "r", "r", "ri" } },
- { INDEX_op_sar_i32, { "r", "r", "ri" } },
- { INDEX_op_rotl_i32, { "r", "r", "ri" } },
- { INDEX_op_rotr_i32, { "r", "r", "ri" } },
-
- { INDEX_op_bswap16_i32, { "r", "r" } },
- { INDEX_op_bswap32_i32, { "r", "r" } },
- { INDEX_op_not_i32, { "r", "r" } },
-
- { INDEX_op_ext8s_i32, { "r", "r" } },
- { INDEX_op_ext16s_i32, { "r", "r" } },
-
- { INDEX_op_brcond_i32, { "rZ", "rJ" } },
- { INDEX_op_brcond2_i32, { "rZ", "rZ", "rJ", "rJ" } },
-
- { INDEX_op_setcond_i32, { "r", "rZ", "rI" } },
- { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rI", "rI" } },
-
- /* ??? We can actually support a signed 14-bit arg3, but we
- only have existing constraints for a signed 11-bit. */
- { INDEX_op_movcond_i32, { "r", "rZ", "rI", "rI", "0" } },
-
- { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rI", "rZ" } },
- { INDEX_op_sub2_i32, { "r", "r", "rI", "rZ", "rK", "rZ" } },
-
- { INDEX_op_deposit_i32, { "r", "0", "rJ" } },
-
-#if TARGET_LONG_BITS == 32
- { INDEX_op_qemu_ld8u, { "r", "L" } },
- { INDEX_op_qemu_ld8s, { "r", "L" } },
- { INDEX_op_qemu_ld16u, { "r", "L" } },
- { INDEX_op_qemu_ld16s, { "r", "L" } },
- { INDEX_op_qemu_ld32, { "r", "L" } },
- { INDEX_op_qemu_ld64, { "r", "r", "L" } },
-
- { INDEX_op_qemu_st8, { "LZ", "L" } },
- { INDEX_op_qemu_st16, { "LZ", "L" } },
- { INDEX_op_qemu_st32, { "LZ", "L" } },
- { INDEX_op_qemu_st64, { "LZ", "LZ", "L" } },
-#else
- { INDEX_op_qemu_ld8u, { "r", "L", "L" } },
- { INDEX_op_qemu_ld8s, { "r", "L", "L" } },
- { INDEX_op_qemu_ld16u, { "r", "L", "L" } },
- { INDEX_op_qemu_ld16s, { "r", "L", "L" } },
- { INDEX_op_qemu_ld32, { "r", "L", "L" } },
- { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } },
-
- { INDEX_op_qemu_st8, { "LZ", "L", "L" } },
- { INDEX_op_qemu_st16, { "LZ", "L", "L" } },
- { INDEX_op_qemu_st32, { "LZ", "L", "L" } },
- { INDEX_op_qemu_st64, { "LZ", "LZ", "L", "L" } },
-#endif
- { -1 },
-};
-
-static int tcg_target_callee_save_regs[] = {
- /* R2, the return address register, is saved specially
- in the caller's frame. */
- /* R3, the frame pointer, is not currently modified. */
- TCG_REG_R4,
- TCG_REG_R5,
- TCG_REG_R6,
- TCG_REG_R7,
- TCG_REG_R8,
- TCG_REG_R9,
- TCG_REG_R10,
- TCG_REG_R11,
- TCG_REG_R12,
- TCG_REG_R13,
- TCG_REG_R14,
- TCG_REG_R15,
- TCG_REG_R16,
- TCG_REG_R17, /* R17 is the global env. */
- TCG_REG_R18
-};
-
-#define FRAME_SIZE ((-TCG_TARGET_CALL_STACK_OFFSET \
- + TCG_TARGET_STATIC_CALL_ARGS_SIZE \
- + ARRAY_SIZE(tcg_target_callee_save_regs) * 4 \
- + CPU_TEMP_BUF_NLONGS * sizeof(long) \
- + TCG_TARGET_STACK_ALIGN - 1) \
- & -TCG_TARGET_STACK_ALIGN)
-
-static void tcg_target_qemu_prologue(TCGContext *s)
-{
- int frame_size, i;
-
- frame_size = FRAME_SIZE;
-
- /* The return address is stored in the caller's frame. */
- tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_CALL_STACK, -20);
-
- /* Allocate stack frame, saving the first register at the same time. */
- tcg_out_ldst(s, tcg_target_callee_save_regs[0],
- TCG_REG_CALL_STACK, frame_size, INSN_STWM);
-
- /* Save all callee saved registers. */
- for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) {
- tcg_out_st(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i],
- TCG_REG_CALL_STACK, -frame_size + i * 4);
- }
-
- /* Record the location of the TCG temps. */
- tcg_set_frame(s, TCG_REG_CALL_STACK, -frame_size + i * 4,
- CPU_TEMP_BUF_NLONGS * sizeof(long));
-
-#ifdef CONFIG_USE_GUEST_BASE
- if (GUEST_BASE != 0) {
- tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE);
- tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG);
- }
-#endif
-
- tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]);
-
- /* Jump to TB, and adjust R18 to be the return address. */
- tcg_out32(s, INSN_BLE_SR4 | INSN_R2(tcg_target_call_iarg_regs[1]));
- tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R18, TCG_REG_R31);
-
- /* Restore callee saved registers. */
- tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_CALL_STACK,
- -frame_size - 20);
- for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) {
- tcg_out_ld(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i],
- TCG_REG_CALL_STACK, -frame_size + i * 4);
- }
-
- /* Deallocate stack frame and return. */
- tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_RP));
- tcg_out_ldst(s, tcg_target_callee_save_regs[0],
- TCG_REG_CALL_STACK, -frame_size, INSN_LDWM);
-}
-
-static void tcg_target_init(TCGContext *s)
-{
- tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff);
-
- tcg_regset_clear(tcg_target_call_clobber_regs);
- tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R20);
- tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R21);
- tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R22);
- tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R23);
- tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R24);
- tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R25);
- tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R26);
- tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RET0);
- tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RET1);
-
- tcg_regset_clear(s->reserved_regs);
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* hardwired to zero */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* addil target */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_RP); /* link register */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_R3); /* frame pointer */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_R18); /* return pointer */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_R19); /* clobbered w/o pic */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_R20); /* reserved */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_DP); /* data pointer */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); /* stack pointer */
- tcg_regset_set_reg(s->reserved_regs, TCG_REG_R31); /* ble link reg */
-
- tcg_add_target_add_op_defs(hppa_op_defs);
-}
-
-typedef struct {
- DebugFrameCIE cie;
- DebugFrameFDEHeader fde;
- uint8_t fde_def_cfa[4];
- uint8_t fde_ret_ofs[3];
- uint8_t fde_reg_ofs[ARRAY_SIZE(tcg_target_callee_save_regs) * 2];
-} DebugFrame;
-
-#define ELF_HOST_MACHINE EM_PARISC
-#define ELF_HOST_FLAGS EFA_PARISC_1_1
-
-/* ??? BFD (and thus GDB) wants very much to distinguish between HPUX
- and other extensions. We don't really care, but if we don't set this
- to *something* then the object file won't be properly matched. */
-#define ELF_OSABI ELFOSABI_LINUX
-
-static DebugFrame debug_frame = {
- .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */
- .cie.id = -1,
- .cie.version = 1,
- .cie.code_align = 1,
- .cie.data_align = 1,
- .cie.return_column = 2,
-
- /* Total FDE size does not include the "len" member. */
- .fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, fde.cie_offset),
-
- .fde_def_cfa = {
- 0x12, 30, /* DW_CFA_def_cfa_sf sp, ... */
- (-FRAME_SIZE & 0x7f) | 0x80, /* ... sleb128 -FRAME_SIZE */
- (-FRAME_SIZE >> 7) & 0x7f
- },
- .fde_ret_ofs = {
- 0x11, 2, (-20 / 4) & 0x7f /* DW_CFA_offset_extended_sf r2, 20 */
- },
- .fde_reg_ofs = {
- /* This must match the ordering in tcg_target_callee_save_regs. */
- 0x80 + 4, 0, /* DW_CFA_offset r4, 0 */
- 0x80 + 5, 4, /* DW_CFA_offset r5, 4 */
- 0x80 + 6, 8, /* DW_CFA_offset r6, 8 */
- 0x80 + 7, 12, /* ... */
- 0x80 + 8, 16,
- 0x80 + 9, 20,
- 0x80 + 10, 24,
- 0x80 + 11, 28,
- 0x80 + 12, 32,
- 0x80 + 13, 36,
- 0x80 + 14, 40,
- 0x80 + 15, 44,
- 0x80 + 16, 48,
- 0x80 + 17, 52,
- 0x80 + 18, 56,
- }
-};
-
-void tcg_register_jit(void *buf, size_t buf_size)
-{
- debug_frame.fde.func_start = (tcg_target_long) buf;
- debug_frame.fde.func_len = buf_size;
-
- tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame));
-}
diff --git a/tcg/hppa/tcg-target.h b/tcg/hppa/tcg-target.h
deleted file mode 100644
index 122edce7a7..0000000000
--- a/tcg/hppa/tcg-target.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Tiny Code Generator for QEMU
- *
- * Copyright (c) 2008 Fabrice Bellard
- *
- * 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 TCG_TARGET_HPPA
-#define TCG_TARGET_HPPA 1
-
-#define TCG_TARGET_WORDS_BIGENDIAN
-
-#define TCG_TARGET_NB_REGS 32
-
-typedef enum {
- TCG_REG_R0 = 0,
- TCG_REG_R1,
- TCG_REG_RP,
- TCG_REG_R3,
- TCG_REG_R4,
- TCG_REG_R5,
- TCG_REG_R6,
- TCG_REG_R7,
- TCG_REG_R8,
- TCG_REG_R9,
- TCG_REG_R10,
- TCG_REG_R11,
- TCG_REG_R12,
- TCG_REG_R13,
- TCG_REG_R14,
- TCG_REG_R15,
- TCG_REG_R16,
- TCG_REG_R17,
- TCG_REG_R18,
- TCG_REG_R19,
- TCG_REG_R20,
- TCG_REG_R21,
- TCG_REG_R22,
- TCG_REG_R23,
- TCG_REG_R24,
- TCG_REG_R25,
- TCG_REG_R26,
- TCG_REG_DP,
- TCG_REG_RET0,
- TCG_REG_RET1,
- TCG_REG_SP,
- TCG_REG_R31,
-} TCGReg;
-
-#define TCG_CT_CONST_0 0x0100
-#define TCG_CT_CONST_S5 0x0200
-#define TCG_CT_CONST_S11 0x0400
-#define TCG_CT_CONST_MS11 0x0800
-#define TCG_CT_CONST_AND 0x1000
-#define TCG_CT_CONST_OR 0x2000
-
-/* used for function call generation */
-#define TCG_REG_CALL_STACK TCG_REG_SP
-#define TCG_TARGET_STACK_ALIGN 64
-#define TCG_TARGET_CALL_STACK_OFFSET -48
-#define TCG_TARGET_STATIC_CALL_ARGS_SIZE 8*4
-#define TCG_TARGET_CALL_ALIGN_ARGS 1
-#define TCG_TARGET_STACK_GROWSUP
-
-/* optional instructions */
-#define TCG_TARGET_HAS_div_i32 0
-#define TCG_TARGET_HAS_rem_i32 0
-#define TCG_TARGET_HAS_rot_i32 1
-#define TCG_TARGET_HAS_ext8s_i32 1
-#define TCG_TARGET_HAS_ext16s_i32 1
-#define TCG_TARGET_HAS_bswap16_i32 1
-#define TCG_TARGET_HAS_bswap32_i32 1
-#define TCG_TARGET_HAS_not_i32 1
-#define TCG_TARGET_HAS_andc_i32 1
-#define TCG_TARGET_HAS_orc_i32 0
-#define TCG_TARGET_HAS_eqv_i32 0
-#define TCG_TARGET_HAS_nand_i32 0
-#define TCG_TARGET_HAS_nor_i32 0
-#define TCG_TARGET_HAS_deposit_i32 1
-#define TCG_TARGET_HAS_movcond_i32 1
-#define TCG_TARGET_HAS_muls2_i32 0
-#define TCG_TARGET_HAS_muluh_i32 0
-#define TCG_TARGET_HAS_mulsh_i32 0
-
-/* optional instructions automatically implemented */
-#define TCG_TARGET_HAS_neg_i32 0 /* sub rd, 0, rs */
-#define TCG_TARGET_HAS_ext8u_i32 0 /* and rd, rs, 0xff */
-#define TCG_TARGET_HAS_ext16u_i32 0 /* and rd, rs, 0xffff */
-
-#define TCG_AREG0 TCG_REG_R17
-
-
-static inline void flush_icache_range(uintptr_t start, uintptr_t stop)
-{
- start &= ~31;
- while (start <= stop) {
- asm volatile ("fdc 0(%0)\n\t"
- "sync\n\t"
- "fic 0(%%sr4, %0)\n\t"
- "sync"
- : : "r"(start) : "memory");
- start += 32;
- }
-}
-
-#endif
diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c
index c1f07415ab..b865b4b662 100644
--- a/tcg/i386/tcg-target.c
+++ b/tcg/i386/tcg-target.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "tcg-be-ldst.h"
+
#ifndef NDEBUG
static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
#if TCG_TARGET_REG_BITS == 64
@@ -1455,15 +1457,8 @@ static void add_qemu_ldst_label(TCGContext *s,
uint8_t *raddr,
uint8_t **label_ptr)
{
- int idx;
- TCGLabelQemuLdst *label;
-
- if (s->nb_qemu_ldst_labels >= TCG_MAX_QEMU_LDST) {
- tcg_abort();
- }
+ TCGLabelQemuLdst *label = new_ldst_label(s);
- idx = s->nb_qemu_ldst_labels++;
- label = (TCGLabelQemuLdst *)&s->qemu_ldst_labels[idx];
label->is_ld = is_ld;
label->opc = opc;
label->datalo_reg = data_reg;
@@ -1628,25 +1623,6 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
tcg_out_push(s, retaddr);
tcg_out_jmp(s, (uintptr_t)qemu_st_helpers[s_bits]);
}
-
-/*
- * Generate TB finalization at the end of block
- */
-void tcg_out_tb_finalize(TCGContext *s)
-{
- int i;
- TCGLabelQemuLdst *label;
-
- /* qemu_ld/st slow paths */
- for (i = 0; i < s->nb_qemu_ldst_labels; i++) {
- label = (TCGLabelQemuLdst *)&s->qemu_ldst_labels[i];
- if (label->is_ld) {
- tcg_out_qemu_ld_slow_path(s, label);
- } else {
- tcg_out_qemu_st_slow_path(s, label);
- }
- }
-}
#endif /* CONFIG_SOFTMMU */
static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h
index d32d7ef6f0..fa7d966461 100644
--- a/tcg/i386/tcg-target.h
+++ b/tcg/i386/tcg-target.h
@@ -130,6 +130,8 @@ typedef enum {
#define TCG_TARGET_HAS_mulsh_i64 0
#endif
+#define TCG_TARGET_HAS_new_ldst 0
+
#define TCG_TARGET_deposit_i32_valid(ofs, len) \
(((ofs) == 0 && (len) == 8) || ((ofs) == 8 && (len) == 8) || \
((ofs) == 0 && (len) == 16))
diff --git a/tcg/ia64/tcg-target.c b/tcg/ia64/tcg-target.c
index cd4f1ae1db..0656d3907a 100644
--- a/tcg/ia64/tcg-target.c
+++ b/tcg/ia64/tcg-target.c
@@ -23,6 +23,8 @@
* THE SOFTWARE.
*/
+#include "tcg-be-null.h"
+
/*
* Register definitions
*/
diff --git a/tcg/ia64/tcg-target.h b/tcg/ia64/tcg-target.h
index 4330c9cdd3..c90038aae5 100644
--- a/tcg/ia64/tcg-target.h
+++ b/tcg/ia64/tcg-target.h
@@ -151,6 +151,8 @@ typedef enum {
#define TCG_TARGET_HAS_mulsh_i32 0
#define TCG_TARGET_HAS_mulsh_i64 0
+#define TCG_TARGET_HAS_new_ldst 0
+
#define TCG_TARGET_deposit_i32_valid(ofs, len) ((len) <= 16)
#define TCG_TARGET_deposit_i64_valid(ofs, len) ((len) <= 16)
diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c
index 5f0a65b4ea..40551cdcb5 100644
--- a/tcg/mips/tcg-target.c
+++ b/tcg/mips/tcg-target.c
@@ -24,6 +24,8 @@
* THE SOFTWARE.
*/
+#include "tcg-be-null.h"
+
#if defined(TCG_TARGET_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
# define TCG_NEED_BSWAP 0
#else
diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h
index c37252269f..683c6af8b9 100644
--- a/tcg/mips/tcg-target.h
+++ b/tcg/mips/tcg-target.h
@@ -122,6 +122,8 @@ extern bool use_mips32r2_instructions;
#define TCG_TARGET_HAS_ext16s_i32 use_mips32r2_instructions
#define TCG_TARGET_HAS_rot_i32 use_mips32r2_instructions
+#define TCG_TARGET_HAS_new_ldst 0
+
/* optional instructions automatically implemented */
#define TCG_TARGET_HAS_neg_i32 0 /* sub rd, zero, rt */
#define TCG_TARGET_HAS_ext8u_i32 0 /* andi rt, rs, 0xff */
diff --git a/tcg/optimize.c b/tcg/optimize.c
index b29bf25b67..89e2d6a3b3 100644
--- a/tcg/optimize.c
+++ b/tcg/optimize.c
@@ -238,20 +238,16 @@ static TCGArg do_constant_folding_2(TCGOpcode op, TCGArg x, TCGArg y)
return (int64_t)x >> (int64_t)y;
case INDEX_op_rotr_i32:
- x = ((uint32_t)x << (32 - y)) | ((uint32_t)x >> y);
- return x;
+ return ror32(x, y);
case INDEX_op_rotr_i64:
- x = ((uint64_t)x << (64 - y)) | ((uint64_t)x >> y);
- return x;
+ return ror64(x, y);
case INDEX_op_rotl_i32:
- x = ((uint32_t)x << y) | ((uint32_t)x >> (32 - y));
- return x;
+ return rol32(x, y);
case INDEX_op_rotl_i64:
- x = ((uint64_t)x << y) | ((uint64_t)x >> (64 - y));
- return x;
+ return rol64(x, y);
CASE_OP_32_64(not):
return ~x;
diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c
index 97e33edcfd..68778c2bc4 100644
--- a/tcg/ppc/tcg-target.c
+++ b/tcg/ppc/tcg-target.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "tcg-be-ldst.h"
+
static uint8_t *tb_ret_addr;
#if defined _CALL_DARWIN || defined __APPLE__
@@ -532,15 +534,8 @@ static void add_qemu_ldst_label (TCGContext *s,
uint8_t *raddr,
uint8_t *label_ptr)
{
- int idx;
- TCGLabelQemuLdst *label;
-
- if (s->nb_qemu_ldst_labels >= TCG_MAX_QEMU_LDST) {
- tcg_abort();
- }
+ TCGLabelQemuLdst *label = new_ldst_label(s);
- idx = s->nb_qemu_ldst_labels++;
- label = (TCGLabelQemuLdst *)&s->qemu_ldst_labels[idx];
label->is_ld = is_ld;
label->opc = opc;
label->datalo_reg = data_reg;
@@ -889,23 +884,6 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
tcg_out_b(s, LK, (uintptr_t)st_trampolines[l->opc]);
tcg_out_b(s, 0, (uintptr_t)l->raddr);
}
-
-void tcg_out_tb_finalize(TCGContext *s)
-{
- int i;
- TCGLabelQemuLdst *label;
-
- /* qemu_ld/st slow paths */
- for (i = 0; i < s->nb_qemu_ldst_labels; i++) {
- label = (TCGLabelQemuLdst *) &s->qemu_ldst_labels[i];
- if (label->is_ld) {
- tcg_out_qemu_ld_slow_path (s, label);
- }
- else {
- tcg_out_qemu_st_slow_path (s, label);
- }
- }
-}
#endif
#ifdef CONFIG_SOFTMMU
diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h
index c9f8ff5206..e3ac6296e0 100644
--- a/tcg/ppc/tcg-target.h
+++ b/tcg/ppc/tcg-target.h
@@ -99,6 +99,8 @@ typedef enum {
#define TCG_TARGET_HAS_muluh_i32 0
#define TCG_TARGET_HAS_mulsh_i32 0
+#define TCG_TARGET_HAS_new_ldst 0
+
#define TCG_AREG0 TCG_REG_R27
#define tcg_qemu_tb_exec(env, tb_ptr) \
diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c
index 332f4d8df1..12c1f61b03 100644
--- a/tcg/ppc64/tcg-target.c
+++ b/tcg/ppc64/tcg-target.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "tcg-be-ldst.h"
+
#define TCG_CT_CONST_S16 0x100
#define TCG_CT_CONST_U16 0x200
#define TCG_CT_CONST_S32 0x400
@@ -931,15 +933,8 @@ static void add_qemu_ldst_label(TCGContext *s, bool is_ld, int opc,
int data_reg, int addr_reg, int mem_index,
uint8_t *raddr, uint8_t *label_ptr)
{
- int idx;
- TCGLabelQemuLdst *label;
-
- if (s->nb_qemu_ldst_labels >= TCG_MAX_QEMU_LDST) {
- tcg_abort();
- }
+ TCGLabelQemuLdst *label = new_ldst_label(s);
- idx = s->nb_qemu_ldst_labels++;
- label = (TCGLabelQemuLdst *)&s->qemu_ldst_labels[idx];
label->is_ld = is_ld;
label->opc = opc;
label->datalo_reg = data_reg;
@@ -998,21 +993,6 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
tcg_out_b(s, 0, (uintptr_t)lb->raddr);
}
-
-void tcg_out_tb_finalize(TCGContext *s)
-{
- int i, n = s->nb_qemu_ldst_labels;
-
- /* qemu_ld/st slow paths */
- for (i = 0; i < n; i++) {
- TCGLabelQemuLdst *label = &s->qemu_ldst_labels[i];
- if (label->is_ld) {
- tcg_out_qemu_ld_slow_path(s, label);
- } else {
- tcg_out_qemu_st_slow_path(s, label);
- }
- }
-}
#endif /* SOFTMMU */
static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
diff --git a/tcg/ppc64/tcg-target.h b/tcg/ppc64/tcg-target.h
index fa4b9da093..457ea69de6 100644
--- a/tcg/ppc64/tcg-target.h
+++ b/tcg/ppc64/tcg-target.h
@@ -123,6 +123,8 @@ typedef enum {
#define TCG_TARGET_HAS_muluh_i64 1
#define TCG_TARGET_HAS_mulsh_i64 1
+#define TCG_TARGET_HAS_new_ldst 0
+
#define TCG_AREG0 TCG_REG_R27
#define TCG_TARGET_EXTEND_ARGS 1
diff --git a/tcg/s390/tcg-target.c b/tcg/s390/tcg-target.c
index 1b44aeee96..0a4f3be0e9 100644
--- a/tcg/s390/tcg-target.c
+++ b/tcg/s390/tcg-target.c
@@ -24,6 +24,8 @@
* THE SOFTWARE.
*/
+#include "tcg-be-null.h"
+
/* We only support generating code for 64-bit mode. */
#if TCG_TARGET_REG_BITS != 64
#error "unsupported code generation mode"
diff --git a/tcg/s390/tcg-target.h b/tcg/s390/tcg-target.h
index 6142fb26a2..10adb778c7 100644
--- a/tcg/s390/tcg-target.h
+++ b/tcg/s390/tcg-target.h
@@ -99,6 +99,8 @@ typedef enum TCGReg {
#define TCG_TARGET_HAS_muluh_i64 0
#define TCG_TARGET_HAS_mulsh_i64 0
+#define TCG_TARGET_HAS_new_ldst 0
+
extern bool tcg_target_deposit_valid(int ofs, int len);
#define TCG_TARGET_deposit_i32_valid tcg_target_deposit_valid
#define TCG_TARGET_deposit_i64_valid tcg_target_deposit_valid
diff --git a/tcg/sparc/tcg-target.c b/tcg/sparc/tcg-target.c
index 9574954ac4..cbd1c91779 100644
--- a/tcg/sparc/tcg-target.c
+++ b/tcg/sparc/tcg-target.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "tcg-be-null.h"
+
#ifndef NDEBUG
static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
"%g0",
diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h
index 1ff2922bbe..00f3a1848b 100644
--- a/tcg/sparc/tcg-target.h
+++ b/tcg/sparc/tcg-target.h
@@ -148,6 +148,8 @@ typedef enum {
#define TCG_TARGET_HAS_mulsh_i64 0
#endif
+#define TCG_TARGET_HAS_new_ldst 0
+
#define TCG_AREG0 TCG_REG_I0
static inline void flush_icache_range(uintptr_t start, uintptr_t stop)
diff --git a/tcg/tcg-be-ldst.h b/tcg/tcg-be-ldst.h
new file mode 100644
index 0000000000..2826d296d7
--- /dev/null
+++ b/tcg/tcg-be-ldst.h
@@ -0,0 +1,90 @@
+/*
+ * TCG Backend Data: load-store optimization only.
+ *
+ * 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.
+ */
+
+#ifdef CONFIG_SOFTMMU
+#define TCG_MAX_QEMU_LDST 640
+
+typedef struct TCGLabelQemuLdst {
+ int is_ld:1; /* qemu_ld: 1, qemu_st: 0 */
+ int opc:4;
+ TCGReg addrlo_reg; /* reg index for low word of guest virtual addr */
+ TCGReg addrhi_reg; /* reg index for high word of guest virtual addr */
+ TCGReg datalo_reg; /* reg index for low word to be loaded or stored */
+ TCGReg datahi_reg; /* reg index for high word to be loaded or stored */
+ int mem_index; /* soft MMU memory index */
+ uint8_t *raddr; /* gen code addr of the next IR of qemu_ld/st IR */
+ uint8_t *label_ptr[2]; /* label pointers to be updated */
+} TCGLabelQemuLdst;
+
+typedef struct TCGBackendData {
+ int nb_ldst_labels;
+ TCGLabelQemuLdst ldst_labels[TCG_MAX_QEMU_LDST];
+} TCGBackendData;
+
+
+/*
+ * Initialize TB backend data at the beginning of the TB.
+ */
+
+static inline void tcg_out_tb_init(TCGContext *s)
+{
+ s->be->nb_ldst_labels = 0;
+}
+
+/*
+ * Generate TB finalization at the end of block
+ */
+
+static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l);
+static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l);
+
+static void tcg_out_tb_finalize(TCGContext *s)
+{
+ TCGLabelQemuLdst *lb = s->be->ldst_labels;
+ int i, n = s->be->nb_ldst_labels;
+
+ /* qemu_ld/st slow paths */
+ for (i = 0; i < n; i++) {
+ if (lb[i].is_ld) {
+ tcg_out_qemu_ld_slow_path(s, lb + i);
+ } else {
+ tcg_out_qemu_st_slow_path(s, lb + i);
+ }
+ }
+}
+
+/*
+ * Allocate a new TCGLabelQemuLdst entry.
+ */
+
+static inline TCGLabelQemuLdst *new_ldst_label(TCGContext *s)
+{
+ TCGBackendData *be = s->be;
+ int n = be->nb_ldst_labels;
+
+ assert(n < TCG_MAX_QEMU_LDST);
+ be->nb_ldst_labels = n + 1;
+ return &be->ldst_labels[n];
+}
+#else
+#include "tcg-be-null.h"
+#endif /* CONFIG_SOFTMMU */
diff --git a/tcg/tcg-be-null.h b/tcg/tcg-be-null.h
new file mode 100644
index 0000000000..74c57d5a6c
--- /dev/null
+++ b/tcg/tcg-be-null.h
@@ -0,0 +1,43 @@
+/*
+ * TCG Backend Data: No backend data
+ *
+ * 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.
+ */
+
+typedef struct TCGBackendData {
+ /* Empty */
+ char dummy;
+} TCGBackendData;
+
+
+/*
+ * Initialize TB backend data at the beginning of the TB.
+ */
+
+static inline void tcg_out_tb_init(TCGContext *s)
+{
+}
+
+/*
+ * Generate TB finalization at the end of block
+ */
+
+static inline void tcg_out_tb_finalize(TCGContext *s)
+{
+}
diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
index bb30a7cf39..7eabf22f01 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -137,24 +137,6 @@ static inline void tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val,
*tcg_ctx.gen_opparam_ptr++ = offset;
}
-static inline void tcg_gen_qemu_ldst_op_i64_i32(TCGOpcode opc, TCGv_i64 val,
- TCGv_i32 addr, TCGArg mem_index)
-{
- *tcg_ctx.gen_opc_ptr++ = opc;
- *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(val);
- *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(addr);
- *tcg_ctx.gen_opparam_ptr++ = mem_index;
-}
-
-static inline void tcg_gen_qemu_ldst_op_i64_i64(TCGOpcode opc, TCGv_i64 val,
- TCGv_i64 addr, TCGArg mem_index)
-{
- *tcg_ctx.gen_opc_ptr++ = opc;
- *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(val);
- *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(addr);
- *tcg_ctx.gen_opparam_ptr++ = mem_index;
-}
-
static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2,
TCGv_i32 arg3, TCGv_i32 arg4)
{
@@ -361,6 +343,21 @@ static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 arg1,
*tcg_ctx.gen_opparam_ptr++ = arg6;
}
+static inline void tcg_add_param_i32(TCGv_i32 val)
+{
+ *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(val);
+}
+
+static inline void tcg_add_param_i64(TCGv_i64 val)
+{
+#if TCG_TARGET_REG_BITS == 32
+ *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(TCGV_LOW(val));
+ *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(TCGV_HIGH(val));
+#else
+ *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(val);
+#endif
+}
+
static inline void gen_set_label(int n)
{
tcg_gen_op1i(INDEX_op_set_label, n);
@@ -2600,11 +2597,12 @@ static inline void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh,
#define tcg_global_mem_new tcg_global_mem_new_i32
#define tcg_temp_local_new() tcg_temp_local_new_i32()
#define tcg_temp_free tcg_temp_free_i32
-#define tcg_gen_qemu_ldst_op tcg_gen_op3i_i32
-#define tcg_gen_qemu_ldst_op_i64 tcg_gen_qemu_ldst_op_i64_i32
#define TCGV_UNUSED(x) TCGV_UNUSED_I32(x)
#define TCGV_IS_UNUSED(x) TCGV_IS_UNUSED_I32(x)
#define TCGV_EQUAL(a, b) TCGV_EQUAL_I32(a, b)
+#define tcg_add_param_tl tcg_add_param_i32
+#define tcg_gen_qemu_ld_tl tcg_gen_qemu_ld_i32
+#define tcg_gen_qemu_st_tl tcg_gen_qemu_st_i32
#else
#define TCGv TCGv_i64
#define tcg_temp_new() tcg_temp_new_i64()
@@ -2612,11 +2610,12 @@ static inline void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh,
#define tcg_global_mem_new tcg_global_mem_new_i64
#define tcg_temp_local_new() tcg_temp_local_new_i64()
#define tcg_temp_free tcg_temp_free_i64
-#define tcg_gen_qemu_ldst_op tcg_gen_op3i_i64
-#define tcg_gen_qemu_ldst_op_i64 tcg_gen_qemu_ldst_op_i64_i64
#define TCGV_UNUSED(x) TCGV_UNUSED_I64(x)
#define TCGV_IS_UNUSED(x) TCGV_IS_UNUSED_I64(x)
#define TCGV_EQUAL(a, b) TCGV_EQUAL_I64(a, b)
+#define tcg_add_param_tl tcg_add_param_i64
+#define tcg_gen_qemu_ld_tl tcg_gen_qemu_ld_i64
+#define tcg_gen_qemu_st_tl tcg_gen_qemu_st_i64
#endif
/* debug info: write the PC of the corresponding QEMU CPU instruction */
@@ -2648,197 +2647,67 @@ static inline void tcg_gen_goto_tb(unsigned idx)
tcg_gen_op1i(INDEX_op_goto_tb, idx);
}
-#if TCG_TARGET_REG_BITS == 32
-static inline void tcg_gen_qemu_ld8u(TCGv ret, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op3i_i32(INDEX_op_qemu_ld8u, ret, addr, mem_index);
-#else
- tcg_gen_op4i_i32(INDEX_op_qemu_ld8u, TCGV_LOW(ret), TCGV_LOW(addr),
- TCGV_HIGH(addr), mem_index);
- tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
-#endif
-}
-
-static inline void tcg_gen_qemu_ld8s(TCGv ret, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op3i_i32(INDEX_op_qemu_ld8s, ret, addr, mem_index);
-#else
- tcg_gen_op4i_i32(INDEX_op_qemu_ld8s, TCGV_LOW(ret), TCGV_LOW(addr),
- TCGV_HIGH(addr), mem_index);
- tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
-#endif
-}
-static inline void tcg_gen_qemu_ld16u(TCGv ret, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op3i_i32(INDEX_op_qemu_ld16u, ret, addr, mem_index);
-#else
- tcg_gen_op4i_i32(INDEX_op_qemu_ld16u, TCGV_LOW(ret), TCGV_LOW(addr),
- TCGV_HIGH(addr), mem_index);
- tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
-#endif
-}
-
-static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op3i_i32(INDEX_op_qemu_ld16s, ret, addr, mem_index);
-#else
- tcg_gen_op4i_i32(INDEX_op_qemu_ld16s, TCGV_LOW(ret), TCGV_LOW(addr),
- TCGV_HIGH(addr), mem_index);
- tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
-#endif
-}
-
-static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op3i_i32(INDEX_op_qemu_ld32, ret, addr, mem_index);
-#else
- tcg_gen_op4i_i32(INDEX_op_qemu_ld32, TCGV_LOW(ret), TCGV_LOW(addr),
- TCGV_HIGH(addr), mem_index);
- tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
-#endif
-}
-
-static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op3i_i32(INDEX_op_qemu_ld32, ret, addr, mem_index);
-#else
- tcg_gen_op4i_i32(INDEX_op_qemu_ld32, TCGV_LOW(ret), TCGV_LOW(addr),
- TCGV_HIGH(addr), mem_index);
- tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
-#endif
-}
-
-static inline void tcg_gen_qemu_ld64(TCGv_i64 ret, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op4i_i32(INDEX_op_qemu_ld64, TCGV_LOW(ret), TCGV_HIGH(ret), addr, mem_index);
-#else
- tcg_gen_op5i_i32(INDEX_op_qemu_ld64, TCGV_LOW(ret), TCGV_HIGH(ret),
- TCGV_LOW(addr), TCGV_HIGH(addr), mem_index);
-#endif
-}
-
-static inline void tcg_gen_qemu_st8(TCGv arg, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op3i_i32(INDEX_op_qemu_st8, arg, addr, mem_index);
-#else
- tcg_gen_op4i_i32(INDEX_op_qemu_st8, TCGV_LOW(arg), TCGV_LOW(addr),
- TCGV_HIGH(addr), mem_index);
-#endif
-}
-
-static inline void tcg_gen_qemu_st16(TCGv arg, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op3i_i32(INDEX_op_qemu_st16, arg, addr, mem_index);
-#else
- tcg_gen_op4i_i32(INDEX_op_qemu_st16, TCGV_LOW(arg), TCGV_LOW(addr),
- TCGV_HIGH(addr), mem_index);
-#endif
-}
-
-static inline void tcg_gen_qemu_st32(TCGv arg, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op3i_i32(INDEX_op_qemu_st32, arg, addr, mem_index);
-#else
- tcg_gen_op4i_i32(INDEX_op_qemu_st32, TCGV_LOW(arg), TCGV_LOW(addr),
- TCGV_HIGH(addr), mem_index);
-#endif
-}
-
-static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index)
-{
-#if TARGET_LONG_BITS == 32
- tcg_gen_op4i_i32(INDEX_op_qemu_st64, TCGV_LOW(arg), TCGV_HIGH(arg), addr,
- mem_index);
-#else
- tcg_gen_op5i_i32(INDEX_op_qemu_st64, TCGV_LOW(arg), TCGV_HIGH(arg),
- TCGV_LOW(addr), TCGV_HIGH(addr), mem_index);
-#endif
-}
-
-#define tcg_gen_ld_ptr(R, A, O) tcg_gen_ld_i32(TCGV_PTR_TO_NAT(R), (A), (O))
-#define tcg_gen_discard_ptr(A) tcg_gen_discard_i32(TCGV_PTR_TO_NAT(A))
-
-#else /* TCG_TARGET_REG_BITS == 32 */
+void tcg_gen_qemu_ld_i32(TCGv_i32, TCGv, TCGArg, TCGMemOp);
+void tcg_gen_qemu_st_i32(TCGv_i32, TCGv, TCGArg, TCGMemOp);
+void tcg_gen_qemu_ld_i64(TCGv_i64, TCGv, TCGArg, TCGMemOp);
+void tcg_gen_qemu_st_i64(TCGv_i64, TCGv, TCGArg, TCGMemOp);
static inline void tcg_gen_qemu_ld8u(TCGv ret, TCGv addr, int mem_index)
{
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld8u, ret, addr, mem_index);
+ tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_UB);
}
static inline void tcg_gen_qemu_ld8s(TCGv ret, TCGv addr, int mem_index)
{
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld8s, ret, addr, mem_index);
+ tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_SB);
}
static inline void tcg_gen_qemu_ld16u(TCGv ret, TCGv addr, int mem_index)
{
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld16u, ret, addr, mem_index);
+ tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_TEUW);
}
static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index)
{
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld16s, ret, addr, mem_index);
+ tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_TESW);
}
static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index)
{
-#if TARGET_LONG_BITS == 32
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32, ret, addr, mem_index);
-#else
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32u, ret, addr, mem_index);
-#endif
+ tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_TEUL);
}
static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index)
{
-#if TARGET_LONG_BITS == 32
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32, ret, addr, mem_index);
-#else
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32s, ret, addr, mem_index);
-#endif
+ tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_TESL);
}
static inline void tcg_gen_qemu_ld64(TCGv_i64 ret, TCGv addr, int mem_index)
{
- tcg_gen_qemu_ldst_op_i64(INDEX_op_qemu_ld64, ret, addr, mem_index);
+ tcg_gen_qemu_ld_i64(ret, addr, mem_index, MO_TEQ);
}
static inline void tcg_gen_qemu_st8(TCGv arg, TCGv addr, int mem_index)
{
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_st8, arg, addr, mem_index);
+ tcg_gen_qemu_st_tl(arg, addr, mem_index, MO_UB);
}
static inline void tcg_gen_qemu_st16(TCGv arg, TCGv addr, int mem_index)
{
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_st16, arg, addr, mem_index);
+ tcg_gen_qemu_st_tl(arg, addr, mem_index, MO_TEUW);
}
static inline void tcg_gen_qemu_st32(TCGv arg, TCGv addr, int mem_index)
{
- tcg_gen_qemu_ldst_op(INDEX_op_qemu_st32, arg, addr, mem_index);
+ tcg_gen_qemu_st_tl(arg, addr, mem_index, MO_TEUL);
}
static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index)
{
- tcg_gen_qemu_ldst_op_i64(INDEX_op_qemu_st64, arg, addr, mem_index);
+ tcg_gen_qemu_st_i64(arg, addr, mem_index, MO_TEQ);
}
-#define tcg_gen_ld_ptr(R, A, O) tcg_gen_ld_i64(TCGV_PTR_TO_NAT(R), (A), (O))
-#define tcg_gen_discard_ptr(A) tcg_gen_discard_i64(TCGV_PTR_TO_NAT(A))
-
-#endif /* TCG_TARGET_REG_BITS != 32 */
-
#if TARGET_LONG_BITS == 64
#define tcg_gen_movi_tl tcg_gen_movi_i64
#define tcg_gen_mov_tl tcg_gen_mov_i64
@@ -2997,17 +2866,25 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index)
#endif
#if TCG_TARGET_REG_BITS == 32
-#define tcg_gen_add_ptr(R, A, B) tcg_gen_add_i32(TCGV_PTR_TO_NAT(R), \
- TCGV_PTR_TO_NAT(A), \
- TCGV_PTR_TO_NAT(B))
-#define tcg_gen_addi_ptr(R, A, B) tcg_gen_addi_i32(TCGV_PTR_TO_NAT(R), \
- TCGV_PTR_TO_NAT(A), (B))
-#define tcg_gen_ext_i32_ptr(R, A) tcg_gen_mov_i32(TCGV_PTR_TO_NAT(R), (A))
-#else /* TCG_TARGET_REG_BITS == 32 */
-#define tcg_gen_add_ptr(R, A, B) tcg_gen_add_i64(TCGV_PTR_TO_NAT(R), \
- TCGV_PTR_TO_NAT(A), \
- TCGV_PTR_TO_NAT(B))
-#define tcg_gen_addi_ptr(R, A, B) tcg_gen_addi_i64(TCGV_PTR_TO_NAT(R), \
- TCGV_PTR_TO_NAT(A), (B))
-#define tcg_gen_ext_i32_ptr(R, A) tcg_gen_ext_i32_i64(TCGV_PTR_TO_NAT(R), (A))
-#endif /* TCG_TARGET_REG_BITS != 32 */
+# define tcg_gen_ld_ptr(R, A, O) \
+ tcg_gen_ld_i32(TCGV_PTR_TO_NAT(R), (A), (O))
+# define tcg_gen_discard_ptr(A) \
+ tcg_gen_discard_i32(TCGV_PTR_TO_NAT(A))
+# define tcg_gen_add_ptr(R, A, B) \
+ tcg_gen_add_i32(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), TCGV_PTR_TO_NAT(B))
+# define tcg_gen_addi_ptr(R, A, B) \
+ tcg_gen_addi_i32(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), (B))
+# define tcg_gen_ext_i32_ptr(R, A) \
+ tcg_gen_mov_i32(TCGV_PTR_TO_NAT(R), (A))
+#else
+# define tcg_gen_ld_ptr(R, A, O) \
+ tcg_gen_ld_i64(TCGV_PTR_TO_NAT(R), (A), (O))
+# define tcg_gen_discard_ptr(A) \
+ tcg_gen_discard_i64(TCGV_PTR_TO_NAT(A))
+# define tcg_gen_add_ptr(R, A, B) \
+ tcg_gen_add_i64(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), TCGV_PTR_TO_NAT(B))
+# define tcg_gen_addi_ptr(R, A, B) \
+ tcg_gen_addi_i64(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), (B))
+# define tcg_gen_ext_i32_ptr(R, A) \
+ tcg_gen_ext_i32_i64(TCGV_PTR_TO_NAT(R), (A))
+#endif /* TCG_TARGET_REG_BITS == 32 */
diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h
index a75c29d518..d71707d9bb 100644
--- a/tcg/tcg-opc.h
+++ b/tcg/tcg-opc.h
@@ -180,79 +180,107 @@ DEF(debug_insn_start, 0, 0, 1, TCG_OPF_NOT_PRESENT)
#endif
DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END)
DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_END)
-/* Note: even if TARGET_LONG_BITS is not defined, the INDEX_op
- constants must be defined */
+
+#define IMPL_NEW_LDST \
+ (TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS \
+ | IMPL(TCG_TARGET_HAS_new_ldst))
+
+#if TARGET_LONG_BITS <= TCG_TARGET_REG_BITS
+DEF(qemu_ld_i32, 1, 1, 2, IMPL_NEW_LDST)
+DEF(qemu_st_i32, 0, 2, 2, IMPL_NEW_LDST)
+# if TCG_TARGET_REG_BITS == 64
+DEF(qemu_ld_i64, 1, 1, 2, IMPL_NEW_LDST | TCG_OPF_64BIT)
+DEF(qemu_st_i64, 0, 2, 2, IMPL_NEW_LDST | TCG_OPF_64BIT)
+# else
+DEF(qemu_ld_i64, 2, 1, 2, IMPL_NEW_LDST | TCG_OPF_64BIT)
+DEF(qemu_st_i64, 0, 3, 2, IMPL_NEW_LDST | TCG_OPF_64BIT)
+# endif
+#else
+DEF(qemu_ld_i32, 1, 2, 2, IMPL_NEW_LDST)
+DEF(qemu_st_i32, 0, 3, 2, IMPL_NEW_LDST)
+DEF(qemu_ld_i64, 2, 2, 2, IMPL_NEW_LDST | TCG_OPF_64BIT)
+DEF(qemu_st_i64, 0, 4, 2, IMPL_NEW_LDST | TCG_OPF_64BIT)
+#endif
+
+#undef IMPL_NEW_LDST
+
+#define IMPL_OLD_LDST \
+ (TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS \
+ | IMPL(!TCG_TARGET_HAS_new_ldst))
+
#if TCG_TARGET_REG_BITS == 32
#if TARGET_LONG_BITS == 32
-DEF(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld8u, 1, 1, 1, IMPL_OLD_LDST)
#else
-DEF(qemu_ld8u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld8u, 1, 2, 1, IMPL_OLD_LDST)
#endif
#if TARGET_LONG_BITS == 32
-DEF(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld8s, 1, 1, 1, IMPL_OLD_LDST)
#else
-DEF(qemu_ld8s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld8s, 1, 2, 1, IMPL_OLD_LDST)
#endif
#if TARGET_LONG_BITS == 32
-DEF(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld16u, 1, 1, 1, IMPL_OLD_LDST)
#else
-DEF(qemu_ld16u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld16u, 1, 2, 1, IMPL_OLD_LDST)
#endif
#if TARGET_LONG_BITS == 32
-DEF(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld16s, 1, 1, 1, IMPL_OLD_LDST)
#else
-DEF(qemu_ld16s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld16s, 1, 2, 1, IMPL_OLD_LDST)
#endif
#if TARGET_LONG_BITS == 32
-DEF(qemu_ld32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld32, 1, 1, 1, IMPL_OLD_LDST)
#else
-DEF(qemu_ld32, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld32, 1, 2, 1, IMPL_OLD_LDST)
#endif
#if TARGET_LONG_BITS == 32
-DEF(qemu_ld64, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld64, 2, 1, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
#else
-DEF(qemu_ld64, 2, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld64, 2, 2, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
#endif
#if TARGET_LONG_BITS == 32
-DEF(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st8, 0, 2, 1, IMPL_OLD_LDST)
#else
-DEF(qemu_st8, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st8, 0, 3, 1, IMPL_OLD_LDST)
#endif
#if TARGET_LONG_BITS == 32
-DEF(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st16, 0, 2, 1, IMPL_OLD_LDST)
#else
-DEF(qemu_st16, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st16, 0, 3, 1, IMPL_OLD_LDST)
#endif
#if TARGET_LONG_BITS == 32
-DEF(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st32, 0, 2, 1, IMPL_OLD_LDST)
#else
-DEF(qemu_st32, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st32, 0, 3, 1, IMPL_OLD_LDST)
#endif
#if TARGET_LONG_BITS == 32
-DEF(qemu_st64, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st64, 0, 3, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
#else
-DEF(qemu_st64, 0, 4, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st64, 0, 4, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
#endif
#else /* TCG_TARGET_REG_BITS == 32 */
-DEF(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
-DEF(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
-DEF(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
-DEF(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
-DEF(qemu_ld32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
-DEF(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
-DEF(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
-DEF(qemu_ld64, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld8u, 1, 1, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
+DEF(qemu_ld8s, 1, 1, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
+DEF(qemu_ld16u, 1, 1, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
+DEF(qemu_ld16s, 1, 1, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
+DEF(qemu_ld32, 1, 1, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
+DEF(qemu_ld32u, 1, 1, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
+DEF(qemu_ld32s, 1, 1, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
+DEF(qemu_ld64, 1, 1, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
-DEF(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
-DEF(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
-DEF(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
-DEF(qemu_st64, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st8, 0, 2, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
+DEF(qemu_st16, 0, 2, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
+DEF(qemu_st32, 0, 2, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
+DEF(qemu_st64, 0, 2, 1, IMPL_OLD_LDST | TCG_OPF_64BIT)
#endif /* TCG_TARGET_REG_BITS != 32 */
+#undef IMPL_OLD_LDST
+
#undef IMPL
#undef IMPL64
#undef DEF
diff --git a/tcg/tcg.c b/tcg/tcg.c
index fd7fb6b85e..66d3f3de80 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -103,6 +103,9 @@ static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1,
intptr_t arg2);
static int tcg_target_const_match(tcg_target_long val,
const TCGArgConstraint *arg_ct);
+static void tcg_out_tb_init(TCGContext *s);
+static void tcg_out_tb_finalize(TCGContext *s);
+
TCGOpDef tcg_op_defs[] = {
#define DEF(s, oargs, iargs, cargs, flags) { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags },
@@ -254,12 +257,41 @@ void tcg_pool_reset(TCGContext *s)
s->pool_current = NULL;
}
+#include "helper.h"
+
+typedef struct TCGHelperInfo {
+ void *func;
+ const char *name;
+} TCGHelperInfo;
+
+static const TCGHelperInfo all_helpers[] = {
+#define GEN_HELPER 2
+#include "helper.h"
+
+ /* Include tcg-runtime.c functions. */
+ { tcg_helper_div_i32, "div_i32" },
+ { tcg_helper_rem_i32, "rem_i32" },
+ { tcg_helper_divu_i32, "divu_i32" },
+ { tcg_helper_remu_i32, "remu_i32" },
+
+ { tcg_helper_shl_i64, "shl_i64" },
+ { tcg_helper_shr_i64, "shr_i64" },
+ { tcg_helper_sar_i64, "sar_i64" },
+ { tcg_helper_div_i64, "div_i64" },
+ { tcg_helper_rem_i64, "rem_i64" },
+ { tcg_helper_divu_i64, "divu_i64" },
+ { tcg_helper_remu_i64, "remu_i64" },
+ { tcg_helper_mulsh_i64, "mulsh_i64" },
+ { tcg_helper_muluh_i64, "muluh_i64" },
+};
+
void tcg_context_init(TCGContext *s)
{
- int op, total_args, n;
+ int op, total_args, n, i;
TCGOpDef *def;
TCGArgConstraint *args_ct;
int *sorted_args;
+ GHashTable *helper_table;
memset(s, 0, sizeof(*s));
s->nb_globals = 0;
@@ -284,7 +316,16 @@ void tcg_context_init(TCGContext *s)
sorted_args += n;
args_ct += n;
}
-
+
+ /* Register helpers. */
+ /* Use g_direct_hash/equal for direct pointer comparisons on func. */
+ s->helpers = helper_table = g_hash_table_new(NULL, NULL);
+
+ for (i = 0; i < ARRAY_SIZE(all_helpers); ++i) {
+ g_hash_table_insert(helper_table, (gpointer)all_helpers[i].func,
+ (gpointer)all_helpers[i].name);
+ }
+
tcg_target_init(s);
}
@@ -332,13 +373,7 @@ void tcg_func_start(TCGContext *s)
s->gen_opc_ptr = s->gen_opc_buf;
s->gen_opparam_ptr = s->gen_opparam_buf;
-#if defined(CONFIG_QEMU_LDST_OPTIMIZATION) && defined(CONFIG_SOFTMMU)
- /* Initialize qemu_ld/st labels to assist code generation at the end of TB
- for TLB miss cases at the end of TB */
- s->qemu_ldst_labels = tcg_malloc(sizeof(TCGLabelQemuLdst) *
- TCG_MAX_QEMU_LDST);
- s->nb_qemu_ldst_labels = 0;
-#endif
+ s->be = tcg_malloc(sizeof(TCGBackendData));
}
static inline void tcg_temp_alloc(TCGContext *s, int n)
@@ -620,25 +655,6 @@ int tcg_check_temp_count(void)
}
#endif
-void tcg_register_helper(void *func, const char *name)
-{
- TCGContext *s = &tcg_ctx;
- int n;
- if ((s->nb_helpers + 1) > s->allocated_helpers) {
- n = s->allocated_helpers;
- if (n == 0) {
- n = 4;
- } else {
- n *= 2;
- }
- s->helpers = realloc(s->helpers, n * sizeof(TCGHelperInfo));
- s->allocated_helpers = n;
- }
- s->helpers[s->nb_helpers].func = (uintptr_t)func;
- s->helpers[s->nb_helpers].name = name;
- s->nb_helpers++;
-}
-
/* Note: we convert the 64 bit args to 32 bit and do some alignment
and endian swap. Maybe it would be better to do the alignment
and endian swap in tcg_reg_alloc_call(). */
@@ -795,6 +811,188 @@ void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1,
}
#endif
+static inline TCGMemOp tcg_canonicalize_memop(TCGMemOp op, bool is64, bool st)
+{
+ switch (op & MO_SIZE) {
+ case MO_8:
+ op &= ~MO_BSWAP;
+ break;
+ case MO_16:
+ break;
+ case MO_32:
+ if (!is64) {
+ op &= ~MO_SIGN;
+ }
+ break;
+ case MO_64:
+ if (!is64) {
+ tcg_abort();
+ }
+ break;
+ }
+ if (st) {
+ op &= ~MO_SIGN;
+ }
+ return op;
+}
+
+static const TCGOpcode old_ld_opc[8] = {
+ [MO_UB] = INDEX_op_qemu_ld8u,
+ [MO_SB] = INDEX_op_qemu_ld8s,
+ [MO_UW] = INDEX_op_qemu_ld16u,
+ [MO_SW] = INDEX_op_qemu_ld16s,
+#if TCG_TARGET_REG_BITS == 32
+ [MO_UL] = INDEX_op_qemu_ld32,
+ [MO_SL] = INDEX_op_qemu_ld32,
+#else
+ [MO_UL] = INDEX_op_qemu_ld32u,
+ [MO_SL] = INDEX_op_qemu_ld32s,
+#endif
+ [MO_Q] = INDEX_op_qemu_ld64,
+};
+
+static const TCGOpcode old_st_opc[4] = {
+ [MO_UB] = INDEX_op_qemu_st8,
+ [MO_UW] = INDEX_op_qemu_st16,
+ [MO_UL] = INDEX_op_qemu_st32,
+ [MO_Q] = INDEX_op_qemu_st64,
+};
+
+void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
+{
+ memop = tcg_canonicalize_memop(memop, 0, 0);
+
+ if (TCG_TARGET_HAS_new_ldst) {
+ *tcg_ctx.gen_opc_ptr++ = INDEX_op_qemu_ld_i32;
+ tcg_add_param_i32(val);
+ tcg_add_param_tl(addr);
+ *tcg_ctx.gen_opparam_ptr++ = memop;
+ *tcg_ctx.gen_opparam_ptr++ = idx;
+ return;
+ }
+
+ /* The old opcodes only support target-endian memory operations. */
+ assert((memop & MO_BSWAP) == MO_TE || (memop & MO_SIZE) == MO_8);
+ assert(old_ld_opc[memop & MO_SSIZE] != 0);
+
+ if (TCG_TARGET_REG_BITS == 32) {
+ *tcg_ctx.gen_opc_ptr++ = old_ld_opc[memop & MO_SSIZE];
+ tcg_add_param_i32(val);
+ tcg_add_param_tl(addr);
+ *tcg_ctx.gen_opparam_ptr++ = idx;
+ } else {
+ TCGv_i64 val64 = tcg_temp_new_i64();
+
+ *tcg_ctx.gen_opc_ptr++ = old_ld_opc[memop & MO_SSIZE];
+ tcg_add_param_i64(val64);
+ tcg_add_param_tl(addr);
+ *tcg_ctx.gen_opparam_ptr++ = idx;
+
+ tcg_gen_trunc_i64_i32(val, val64);
+ tcg_temp_free_i64(val64);
+ }
+}
+
+void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
+{
+ memop = tcg_canonicalize_memop(memop, 0, 1);
+
+ if (TCG_TARGET_HAS_new_ldst) {
+ *tcg_ctx.gen_opc_ptr++ = INDEX_op_qemu_st_i32;
+ tcg_add_param_i32(val);
+ tcg_add_param_tl(addr);
+ *tcg_ctx.gen_opparam_ptr++ = memop;
+ *tcg_ctx.gen_opparam_ptr++ = idx;
+ return;
+ }
+
+ /* The old opcodes only support target-endian memory operations. */
+ assert((memop & MO_BSWAP) == MO_TE || (memop & MO_SIZE) == MO_8);
+ assert(old_st_opc[memop & MO_SIZE] != 0);
+
+ if (TCG_TARGET_REG_BITS == 32) {
+ *tcg_ctx.gen_opc_ptr++ = old_st_opc[memop & MO_SIZE];
+ tcg_add_param_i32(val);
+ tcg_add_param_tl(addr);
+ *tcg_ctx.gen_opparam_ptr++ = idx;
+ } else {
+ TCGv_i64 val64 = tcg_temp_new_i64();
+
+ tcg_gen_extu_i32_i64(val64, val);
+
+ *tcg_ctx.gen_opc_ptr++ = old_st_opc[memop & MO_SIZE];
+ tcg_add_param_i64(val64);
+ tcg_add_param_tl(addr);
+ *tcg_ctx.gen_opparam_ptr++ = idx;
+
+ tcg_temp_free_i64(val64);
+ }
+}
+
+void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
+{
+ memop = tcg_canonicalize_memop(memop, 1, 0);
+
+#if TCG_TARGET_REG_BITS == 32
+ if ((memop & MO_SIZE) < MO_64) {
+ tcg_gen_qemu_ld_i32(TCGV_LOW(val), addr, idx, memop);
+ if (memop & MO_SIGN) {
+ tcg_gen_sari_i32(TCGV_HIGH(val), TCGV_LOW(val), 31);
+ } else {
+ tcg_gen_movi_i32(TCGV_HIGH(val), 0);
+ }
+ return;
+ }
+#endif
+
+ if (TCG_TARGET_HAS_new_ldst) {
+ *tcg_ctx.gen_opc_ptr++ = INDEX_op_qemu_ld_i64;
+ tcg_add_param_i64(val);
+ tcg_add_param_tl(addr);
+ *tcg_ctx.gen_opparam_ptr++ = memop;
+ *tcg_ctx.gen_opparam_ptr++ = idx;
+ return;
+ }
+
+ /* The old opcodes only support target-endian memory operations. */
+ assert((memop & MO_BSWAP) == MO_TE || (memop & MO_SIZE) == MO_8);
+ assert(old_ld_opc[memop & MO_SSIZE] != 0);
+
+ *tcg_ctx.gen_opc_ptr++ = old_ld_opc[memop & MO_SSIZE];
+ tcg_add_param_i64(val);
+ tcg_add_param_tl(addr);
+ *tcg_ctx.gen_opparam_ptr++ = idx;
+}
+
+void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
+{
+ memop = tcg_canonicalize_memop(memop, 1, 1);
+
+#if TCG_TARGET_REG_BITS == 32
+ if ((memop & MO_SIZE) < MO_64) {
+ tcg_gen_qemu_st_i32(TCGV_LOW(val), addr, idx, memop);
+ return;
+ }
+#endif
+
+ if (TCG_TARGET_HAS_new_ldst) {
+ *tcg_ctx.gen_opc_ptr++ = INDEX_op_qemu_st_i64;
+ tcg_add_param_i64(val);
+ tcg_add_param_tl(addr);
+ *tcg_ctx.gen_opparam_ptr++ = memop;
+ *tcg_ctx.gen_opparam_ptr++ = idx;
+ return;
+ }
+
+ /* The old opcodes only support target-endian memory operations. */
+ assert((memop & MO_BSWAP) == MO_TE || (memop & MO_SIZE) == MO_8);
+ assert(old_st_opc[memop & MO_SIZE] != 0);
+
+ *tcg_ctx.gen_opc_ptr++ = old_st_opc[memop & MO_SIZE];
+ tcg_add_param_i64(val);
+ tcg_add_param_tl(addr);
+ *tcg_ctx.gen_opparam_ptr++ = idx;
+}
static void tcg_reg_alloc_start(TCGContext *s)
{
@@ -851,47 +1049,14 @@ char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg)
return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV_I64(arg));
}
-static int helper_cmp(const void *p1, const void *p2)
+/* Find helper name. */
+static inline const char *tcg_find_helper(TCGContext *s, uintptr_t val)
{
- const TCGHelperInfo *th1 = p1;
- const TCGHelperInfo *th2 = p2;
- if (th1->func < th2->func)
- return -1;
- else if (th1->func == th2->func)
- return 0;
- else
- return 1;
-}
-
-/* find helper definition (Note: A hash table would be better) */
-static TCGHelperInfo *tcg_find_helper(TCGContext *s, uintptr_t val)
-{
- int m, m_min, m_max;
- TCGHelperInfo *th;
- uintptr_t v;
-
- if (unlikely(!s->helpers_sorted)) {
- qsort(s->helpers, s->nb_helpers, sizeof(TCGHelperInfo),
- helper_cmp);
- s->helpers_sorted = 1;
+ const char *ret = NULL;
+ if (s->helpers) {
+ ret = g_hash_table_lookup(s->helpers, (gpointer)val);
}
-
- /* binary search */
- m_min = 0;
- m_max = s->nb_helpers - 1;
- while (m_min <= m_max) {
- m = (m_min + m_max) >> 1;
- th = &s->helpers[m];
- v = th->func;
- if (v == val)
- return th;
- else if (val < v) {
- m_max = m - 1;
- } else {
- m_min = m + 1;
- }
- }
- return NULL;
+ return ret;
}
static const char * const cond_name[] =
@@ -910,6 +1075,22 @@ static const char * const cond_name[] =
[TCG_COND_GTU] = "gtu"
};
+static const char * const ldst_name[] =
+{
+ [MO_UB] = "ub",
+ [MO_SB] = "sb",
+ [MO_LEUW] = "leuw",
+ [MO_LESW] = "lesw",
+ [MO_LEUL] = "leul",
+ [MO_LESL] = "lesl",
+ [MO_LEQ] = "leq",
+ [MO_BEUW] = "beuw",
+ [MO_BESW] = "besw",
+ [MO_BEUL] = "beul",
+ [MO_BESL] = "besl",
+ [MO_BEQ] = "beq",
+};
+
void tcg_dump_ops(TCGContext *s)
{
const uint16_t *opc_ptr;
@@ -976,7 +1157,7 @@ void tcg_dump_ops(TCGContext *s)
}
} else if (c == INDEX_op_movi_i32 || c == INDEX_op_movi_i64) {
tcg_target_ulong val;
- TCGHelperInfo *th;
+ const char *name;
nb_oargs = def->nb_oargs;
nb_iargs = def->nb_iargs;
@@ -984,9 +1165,9 @@ void tcg_dump_ops(TCGContext *s)
qemu_log(" %s %s,$", def->name,
tcg_get_arg_str_idx(s, buf, sizeof(buf), args[0]));
val = args[1];
- th = tcg_find_helper(s, val);
- if (th) {
- qemu_log("%s", th->name);
+ name = tcg_find_helper(s, val);
+ if (name) {
+ qemu_log("%s", name);
} else {
if (c == INDEX_op_movi_i32) {
qemu_log("0x%x", (uint32_t)val);
@@ -1038,6 +1219,17 @@ void tcg_dump_ops(TCGContext *s)
}
i = 1;
break;
+ case INDEX_op_qemu_ld_i32:
+ case INDEX_op_qemu_st_i32:
+ case INDEX_op_qemu_ld_i64:
+ case INDEX_op_qemu_st_i64:
+ if (args[k] < ARRAY_SIZE(ldst_name) && ldst_name[args[k]]) {
+ qemu_log(",%s", ldst_name[args[k++]]);
+ } else {
+ qemu_log(",$0x%" TCG_PRIlx, args[k++]);
+ }
+ i = 1;
+ break;
default:
i = 0;
break;
@@ -2311,6 +2503,8 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
s->code_buf = gen_code_buf;
s->code_ptr = gen_code_buf;
+ tcg_out_tb_init(s);
+
args = s->gen_opparam_buf;
op_index = 0;
@@ -2384,10 +2578,8 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
#endif
}
the_end:
-#if defined(CONFIG_QEMU_LDST_OPTIMIZATION) && defined(CONFIG_SOFTMMU)
/* Generate TB finalization at the end of block */
tcg_out_tb_finalize(s);
-#endif
return -1;
}
diff --git a/tcg/tcg.h b/tcg/tcg.h
index 902c751d26..0d9bd293b5 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -197,6 +197,60 @@ typedef enum TCGType {
#endif
} TCGType;
+/* Constants for qemu_ld and qemu_st for the Memory Operation field. */
+typedef enum TCGMemOp {
+ MO_8 = 0,
+ MO_16 = 1,
+ MO_32 = 2,
+ MO_64 = 3,
+ MO_SIZE = 3, /* Mask for the above. */
+
+ MO_SIGN = 4, /* Sign-extended, otherwise zero-extended. */
+
+ MO_BSWAP = 8, /* Host reverse endian. */
+#ifdef HOST_WORDS_BIGENDIAN
+ MO_LE = MO_BSWAP,
+ MO_BE = 0,
+#else
+ MO_LE = 0,
+ MO_BE = MO_BSWAP,
+#endif
+#ifdef TARGET_WORDS_BIGENDIAN
+ MO_TE = MO_BE,
+#else
+ MO_TE = MO_LE,
+#endif
+
+ /* Combinations of the above, for ease of use. */
+ MO_UB = MO_8,
+ MO_UW = MO_16,
+ MO_UL = MO_32,
+ MO_SB = MO_SIGN | MO_8,
+ MO_SW = MO_SIGN | MO_16,
+ MO_SL = MO_SIGN | MO_32,
+ MO_Q = MO_64,
+
+ MO_LEUW = MO_LE | MO_UW,
+ MO_LEUL = MO_LE | MO_UL,
+ MO_LESW = MO_LE | MO_SW,
+ MO_LESL = MO_LE | MO_SL,
+ MO_LEQ = MO_LE | MO_Q,
+
+ MO_BEUW = MO_BE | MO_UW,
+ MO_BEUL = MO_BE | MO_UL,
+ MO_BESW = MO_BE | MO_SW,
+ MO_BESL = MO_BE | MO_SL,
+ MO_BEQ = MO_BE | MO_Q,
+
+ MO_TEUW = MO_TE | MO_UW,
+ MO_TEUL = MO_TE | MO_UL,
+ MO_TESW = MO_TE | MO_SW,
+ MO_TESL = MO_TE | MO_SL,
+ MO_TEQ = MO_TE | MO_Q,
+
+ MO_SSIZE = MO_SIZE | MO_SIGN,
+} TCGMemOp;
+
typedef tcg_target_ulong TCGArg;
/* Define a type and accessor macros for variables. Using a struct is
@@ -211,24 +265,6 @@ typedef tcg_target_ulong TCGArg;
are aliases for target_ulong and host pointer sized values respectively.
*/
-#if defined(CONFIG_QEMU_LDST_OPTIMIZATION) && defined(CONFIG_SOFTMMU)
-/* Macros/structures for qemu_ld/st IR code optimization:
- TCG_MAX_HELPER_LABELS is defined as same as OPC_BUF_SIZE in exec-all.h. */
-#define TCG_MAX_QEMU_LDST 640
-
-typedef struct TCGLabelQemuLdst {
- int is_ld:1; /* qemu_ld: 1, qemu_st: 0 */
- int opc:4;
- int addrlo_reg; /* reg index for low word of guest virtual addr */
- int addrhi_reg; /* reg index for high word of guest virtual addr */
- int datalo_reg; /* reg index for low word to be loaded or stored */
- int datahi_reg; /* reg index for high word to be loaded or stored */
- int mem_index; /* soft MMU memory index */
- uint8_t *raddr; /* gen code addr of the next IR of qemu_ld/st IR */
- uint8_t *label_ptr[2]; /* label pointers to be updated */
-} TCGLabelQemuLdst;
-#endif
-
#ifdef CONFIG_DEBUG_TCG
#define DEBUG_TCGV 1
#endif
@@ -405,11 +441,6 @@ typedef struct TCGTemp {
const char *name;
} TCGTemp;
-typedef struct TCGHelperInfo {
- uintptr_t func;
- const char *name;
-} TCGHelperInfo;
-
typedef struct TCGContext TCGContext;
struct TCGContext {
@@ -447,10 +478,7 @@ struct TCGContext {
uint8_t *code_ptr;
TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */
- TCGHelperInfo *helpers;
- int nb_helpers;
- int allocated_helpers;
- int helpers_sorted;
+ GHashTable *helpers;
#ifdef CONFIG_PROFILER
/* profiling info */
@@ -496,12 +524,8 @@ struct TCGContext {
TBContext tb_ctx;
-#if defined(CONFIG_QEMU_LDST_OPTIMIZATION) && defined(CONFIG_SOFTMMU)
- /* labels info for qemu_ld/st IRs
- The labels help to generate TLB miss case codes at the end of TB */
- TCGLabelQemuLdst *qemu_ldst_labels;
- int nb_qemu_ldst_labels;
-#endif
+ /* The TCGBackendData structure is private to tcg-target.c. */
+ struct TCGBackendData *be;
};
extern TCGContext tcg_ctx;
@@ -680,8 +704,6 @@ TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr, TCGArg *args,
TCGOpDef *tcg_op_def);
/* only used for debugging purposes */
-void tcg_register_helper(void *func, const char *name);
-const char *tcg_helper_get_name(TCGContext *s, void *func);
void tcg_dump_ops(TCGContext *s);
void dump_ops(const uint16_t *opc_buf, const TCGArg *opparam_buf);
@@ -745,11 +767,6 @@ TCGv_i64 tcg_const_local_i64(int64_t val);
void tcg_register_jit(void *buf, size_t buf_size);
-#if defined(CONFIG_QEMU_LDST_OPTIMIZATION) && defined(CONFIG_SOFTMMU)
-/* Generate TB finalization at the end of block */
-void tcg_out_tb_finalize(TCGContext *s);
-#endif
-
/*
* Memory helpers that will be used by TCG generated code.
*/
@@ -757,29 +774,66 @@ void tcg_out_tb_finalize(TCGContext *s);
/* Value zero-extended to tcg register size. */
tcg_target_ulong helper_ret_ldub_mmu(CPUArchState *env, target_ulong addr,
int mmu_idx, uintptr_t retaddr);
-tcg_target_ulong helper_ret_lduw_mmu(CPUArchState *env, target_ulong addr,
- int mmu_idx, uintptr_t retaddr);
-tcg_target_ulong helper_ret_ldul_mmu(CPUArchState *env, target_ulong addr,
- int mmu_idx, uintptr_t retaddr);
-uint64_t helper_ret_ldq_mmu(CPUArchState *env, target_ulong addr,
- int mmu_idx, uintptr_t retaddr);
+tcg_target_ulong helper_le_lduw_mmu(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr);
+tcg_target_ulong helper_le_ldul_mmu(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr);
+uint64_t helper_le_ldq_mmu(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr);
+tcg_target_ulong helper_be_lduw_mmu(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr);
+tcg_target_ulong helper_be_ldul_mmu(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr);
+uint64_t helper_be_ldq_mmu(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr);
/* Value sign-extended to tcg register size. */
tcg_target_ulong helper_ret_ldsb_mmu(CPUArchState *env, target_ulong addr,
int mmu_idx, uintptr_t retaddr);
-tcg_target_ulong helper_ret_ldsw_mmu(CPUArchState *env, target_ulong addr,
- int mmu_idx, uintptr_t retaddr);
-tcg_target_ulong helper_ret_ldsl_mmu(CPUArchState *env, target_ulong addr,
- int mmu_idx, uintptr_t retaddr);
+tcg_target_ulong helper_le_ldsw_mmu(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr);
+tcg_target_ulong helper_le_ldsl_mmu(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr);
+tcg_target_ulong helper_be_ldsw_mmu(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr);
+tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr,
+ int mmu_idx, uintptr_t retaddr);
void helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val,
int mmu_idx, uintptr_t retaddr);
-void helper_ret_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val,
- int mmu_idx, uintptr_t retaddr);
-void helper_ret_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val,
- int mmu_idx, uintptr_t retaddr);
-void helper_ret_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
- int mmu_idx, uintptr_t retaddr);
+void helper_le_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val,
+ int mmu_idx, uintptr_t retaddr);
+void helper_le_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val,
+ int mmu_idx, uintptr_t retaddr);
+void helper_le_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
+ int mmu_idx, uintptr_t retaddr);
+void helper_be_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val,
+ int mmu_idx, uintptr_t retaddr);
+void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val,
+ int mmu_idx, uintptr_t retaddr);
+void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
+ int mmu_idx, uintptr_t retaddr);
+
+/* Temporary aliases until backends are converted. */
+#ifdef TARGET_WORDS_BIGENDIAN
+# define helper_ret_ldsw_mmu helper_be_ldsw_mmu
+# define helper_ret_lduw_mmu helper_be_lduw_mmu
+# define helper_ret_ldsl_mmu helper_be_ldsl_mmu
+# define helper_ret_ldul_mmu helper_be_ldul_mmu
+# define helper_ret_ldq_mmu helper_be_ldq_mmu
+# define helper_ret_stw_mmu helper_be_stw_mmu
+# define helper_ret_stl_mmu helper_be_stl_mmu
+# define helper_ret_stq_mmu helper_be_stq_mmu
+#else
+# define helper_ret_ldsw_mmu helper_le_ldsw_mmu
+# define helper_ret_lduw_mmu helper_le_lduw_mmu
+# define helper_ret_ldsl_mmu helper_le_ldsl_mmu
+# define helper_ret_ldul_mmu helper_le_ldul_mmu
+# define helper_ret_ldq_mmu helper_le_ldq_mmu
+# define helper_ret_stw_mmu helper_le_stw_mmu
+# define helper_ret_stl_mmu helper_le_stl_mmu
+# define helper_ret_stq_mmu helper_le_stq_mmu
+#endif
uint8_t helper_ldb_mmu(CPUArchState *env, target_ulong addr, int mmu_idx);
uint16_t helper_ldw_mmu(CPUArchState *env, target_ulong addr, int mmu_idx);
diff --git a/tcg/tci/tcg-target.c b/tcg/tci/tcg-target.c
index 233ab3bf35..fc80704de8 100644
--- a/tcg/tci/tcg-target.c
+++ b/tcg/tci/tcg-target.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "tcg-be-null.h"
+
/* TODO list:
* - See TODO comments in code.
*/
@@ -670,7 +672,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
case INDEX_op_shl_i64:
case INDEX_op_shr_i64:
case INDEX_op_sar_i64:
- /* TODO: Implementation of rotl_i64, rotr_i64 missing in tci.c. */
case INDEX_op_rotl_i64: /* Optional (TCG_TARGET_HAS_rot_i64). */
case INDEX_op_rotr_i64: /* Optional (TCG_TARGET_HAS_rot_i64). */
tcg_out_r(s, args[0]);
diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h
index c2ecfbe047..6e1da8c007 100644
--- a/tcg/tci/tcg-target.h
+++ b/tcg/tci/tcg-target.h
@@ -120,6 +120,8 @@
#define TCG_TARGET_HAS_mulsh_i64 0
#endif /* TCG_TARGET_REG_BITS == 64 */
+#define TCG_TARGET_HAS_new_ldst 0
+
/* Number of registers available.
For 32 bit hosts, we need more than 8 registers (call arguments). */
/* #define TCG_TARGET_NB_REGS 8 */
diff --git a/tci.c b/tci.c
index 6d64891557..0202ed97d1 100644
--- a/tci.c
+++ b/tci.c
@@ -688,13 +688,13 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr)
t0 = *tb_ptr++;
t1 = tci_read_ri32(&tb_ptr);
t2 = tci_read_ri32(&tb_ptr);
- tci_write_reg32(t0, (t1 << t2) | (t1 >> (32 - t2)));
+ tci_write_reg32(t0, rol32(t1, t2));
break;
case INDEX_op_rotr_i32:
t0 = *tb_ptr++;
t1 = tci_read_ri32(&tb_ptr);
t2 = tci_read_ri32(&tb_ptr);
- tci_write_reg32(t0, (t1 >> t2) | (t1 << (32 - t2)));
+ tci_write_reg32(t0, ror32(t1, t2));
break;
#endif
#if TCG_TARGET_HAS_deposit_i32
@@ -952,8 +952,16 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr)
break;
#if TCG_TARGET_HAS_rot_i64
case INDEX_op_rotl_i64:
+ t0 = *tb_ptr++;
+ t1 = tci_read_ri64(&tb_ptr);
+ t2 = tci_read_ri64(&tb_ptr);
+ tci_write_reg64(t0, rol64(t1, t2));
+ break;
case INDEX_op_rotr_i64:
- TODO();
+ t0 = *tb_ptr++;
+ t1 = tci_read_ri64(&tb_ptr);
+ t2 = tci_read_ri64(&tb_ptr);
+ tci_write_reg64(t0, ror64(t1, t2));
break;
#endif
#if TCG_TARGET_HAS_deposit_i64
diff --git a/tests/.gitignore b/tests/.gitignore
index ae5280ef68..425757cfe1 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -5,9 +5,11 @@ check-qjson
check-qlist
check-qstring
test-aio
+test-bitops
test-throttle
test-cutils
test-hbitmap
+test-int128
test-iov
test-mul64
test-qapi-types.[ch]
@@ -21,3 +23,4 @@ test-thread-pool
test-x86-cpuid
test-xbzrle
*-test
+qapi-schema/*.test.*
diff --git a/tests/Makefile b/tests/Makefile
index 994fef1839..fa4c9f0cbb 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -196,6 +196,7 @@ check-help:
@echo " make check-qapi-schema Run QAPI schema tests"
@echo " make check-block Run block tests"
@echo " make check-report.html Generates an HTML test report"
+ @echo " make check-clean Clean the tests"
@echo
@echo "Please note that HTML reports do not regenerate if the unit tests"
@echo "has not changed."
@@ -252,8 +253,10 @@ check-report.html: check-report.xml
# Other tests
+QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) = tests/qemu-iotests/socket_scm_helper$(EXESUF)
+
.PHONY: check-tests/qemu-iotests-quick.sh
-check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) tests/qemu-iotests/socket_scm_helper$(EXESUF)
+check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) $(QEMU_IOTESTS_HELPERS-y)
$<
.PHONY: check-tests/test-qapi.py
@@ -261,19 +264,28 @@ check-tests/test-qapi.py: tests/test-qapi.py
.PHONY: $(patsubst %, check-%, $(check-qapi-schema-y))
$(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json
- $(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py <$^ >$*.out 2>$*.err; echo $$? >$*.exit, " TEST $*.out")
- @diff -q $(SRC_PATH)/$*.out $*.out
- @diff -q $(SRC_PATH)/$*.err $*.err
- @diff -q $(SRC_PATH)/$*.exit $*.exit
+ $(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py <$^ >$*.test.out 2>$*.test.err; echo $$? >$*.test.exit, " TEST $*.out")
+ @diff -q $(SRC_PATH)/$*.out $*.test.out
+ @diff -q $(SRC_PATH)/$*.err $*.test.err
+ @diff -q $(SRC_PATH)/$*.exit $*.test.exit
# Consolidated targets
-.PHONY: check-qapi-schema check-qtest check-unit check
+.PHONY: check-qapi-schema check-qtest check-unit check check-clean
check-qapi-schema: $(patsubst %,check-%, $(check-qapi-schema-y))
check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS))
check-unit: $(patsubst %,check-%, $(check-unit-y))
check-block: $(patsubst %,check-%, $(check-block-y))
check: check-qapi-schema check-unit check-qtest
+check-clean:
+ $(MAKE) -C tests/tcg clean
+ rm -rf $(check-unit-y) $(check-qtest-i386-y) $(check-qtest-x86_64-y) $(check-qtest-sparc64-y) $(check-qtest-sparc-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y)
+
+clean: check-clean
+
+# Build the help program automatically
+
+all: $(QEMU_IOTESTS_HELPERS-y)
-include $(wildcard tests/*.d)
-include $(wildcard tests/libqos/*.d)
diff --git a/tests/qemu-iotests/.gitignore b/tests/qemu-iotests/.gitignore
index 62b4002995..0541f80daa 100644
--- a/tests/qemu-iotests/.gitignore
+++ b/tests/qemu-iotests/.gitignore
@@ -2,6 +2,7 @@ check.log
check.time
*.out.bad
*.notrun
+socket_scm_helper
# ignore everything in the scratch directory
scratch/
diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out
index 0764389f8e..15045799a2 100644
--- a/tests/qemu-iotests/026.out
+++ b/tests/qemu-iotests/026.out
@@ -5,16 +5,12 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 5; imm: off; once: on; write
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 5; imm: off; once: on; write -b
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 5; imm: off; once: off; write
@@ -33,16 +29,12 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: on; write
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: on; write -b
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: off; write
@@ -181,16 +173,12 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 5; imm: off; once: on; write
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 5; imm: off; once: off; write
@@ -207,16 +195,12 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 28; imm: off; once: on; write
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 28; imm: off; once: off; write
diff --git a/tests/qemu-iotests/026.out.nocache b/tests/qemu-iotests/026.out.nocache
index 33bad0d6ae..c9d242e9ec 100644
--- a/tests/qemu-iotests/026.out.nocache
+++ b/tests/qemu-iotests/026.out.nocache
@@ -5,16 +5,12 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 5; imm: off; once: on; write
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 5; imm: off; once: on; write -b
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 5; imm: off; once: off; write
@@ -33,16 +29,12 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: on; write
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: on; write -b
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: off; write
@@ -189,16 +181,12 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 5; imm: off; once: on; write
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 5; imm: off; once: off; write
@@ -215,16 +203,12 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 28; imm: off; once: on; write
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc.write; errno: 28; imm: off; once: off; write
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 04bb236655..2839e32807 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -139,7 +139,10 @@ QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on: read-only not supported by this bus type
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) QEMU_PROG: Can't use a read-only drive
+QEMU_PROG: Device initialization failed.
+QEMU_PROG: Initialization of device ide-hd failed
Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on
QEMU X.Y.Z monitor - type 'help' for more information
@@ -223,6 +226,6 @@ Testing: -drive file=foo:bar
QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: Unknown protocol
Testing: -drive file.filename=foo:bar
-QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open 'foo:bar': No such file or directory
+QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open file: No such file or directory
*** done
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
index dd6addfaab..b81c575d94 100755
--- a/tests/qemu-iotests/059
+++ b/tests/qemu-iotests/059
@@ -47,30 +47,34 @@ capacity_offset=16
granularity_offset=20
grain_table_size_offset=44
-echo "=== Testing invalid granularity ==="
echo
+echo "=== Testing invalid granularity ==="
_make_test_img 64M
poke_file "$TEST_IMG" "$granularity_offset" "\xff\xff\xff\xff\xff\xff\xff\xff"
{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
-echo "=== Testing too big L2 table size ==="
echo
+echo "=== Testing too big L2 table size ==="
_make_test_img 64M
poke_file "$TEST_IMG" "$grain_table_size_offset" "\xff\xff\xff\xff"
{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
-echo "=== Testing too big L1 table size ==="
echo
+echo "=== Testing too big L1 table size ==="
_make_test_img 64M
poke_file "$TEST_IMG" "$capacity_offset" "\xff\xff\xff\xff"
poke_file "$TEST_IMG" "$grain_table_size_offset" "\x01\x00\x00\x00"
{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
-echo "=== Testing monolithicFlat creation and opening ==="
echo
+echo "=== Testing monolithicFlat creation and opening ==="
IMGOPTS="subformat=monolithicFlat" _make_test_img 2G
$QEMU_IMG info $TEST_IMG | _filter_testdir
+echo
+echo "=== Testing monolithicFlat with zeroed_grain ==="
+IMGOPTS="subformat=monolithicFlat,zeroed_grain=on" _make_test_img 2G
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
index 9159dbec60..9b12efb466 100644
--- a/tests/qemu-iotests/059.out
+++ b/tests/qemu-iotests/059.out
@@ -1,27 +1,29 @@
QA output created by 059
-=== Testing invalid granularity ===
+=== Testing invalid granularity ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-invalid granularity, image may be corrupt
-qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type
+qemu-io: can't open device TEST_DIR/t.vmdk: Invalid granularity, image may be corrupt
no file open, try 'help open'
-=== Testing too big L2 table size ===
+=== Testing too big L2 table size ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
L2 table size too big
qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type
no file open, try 'help open'
-=== Testing too big L1 table size ===
+=== Testing too big L1 table size ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-L1 size too big
-qemu-io: can't open device TEST_DIR/t.vmdk: Could not open 'TEST_DIR/t.vmdk': Wrong medium type
+qemu-io: can't open device TEST_DIR/t.vmdk: L1 size too big
no file open, try 'help open'
-=== Testing monolithicFlat creation and opening ===
+=== Testing monolithicFlat creation and opening ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648
image: TEST_DIR/t.vmdk
file format: vmdk
virtual size: 2.0G (2147483648 bytes)
disk size: 4.0K
+
+=== Testing monolithicFlat with zeroed_grain ===
+qemu-img: TEST_DIR/t.IMGFMT: Flat image can't enable zeroed grain
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648
*** done
diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060
index 9bbc43b706..bbb19090a1 100755
--- a/tests/qemu-iotests/060
+++ b/tests/qemu-iotests/060
@@ -21,10 +21,10 @@
# creator
owner=mreitz@redhat.com
-seq=`basename $0`
+seq="$(basename $0)"
echo "QA output created by $seq"
-here=`pwd`
+here="$PWD"
tmp=/tmp/$$
status=1 # failure is the default!
@@ -47,9 +47,15 @@ rt_offset=65536 # 0x10000 (XXX: just an assumption)
rb_offset=131072 # 0x20000 (XXX: just an assumption)
l1_offset=196608 # 0x30000 (XXX: just an assumption)
l2_offset=262144 # 0x40000 (XXX: just an assumption)
+l2_offset_after_snapshot=524288 # 0x80000 (XXX: just an assumption)
IMGOPTS="compat=1.1"
+OPEN_RW="open -o overlap-check=all $TEST_IMG"
+# Overlap checks are done before write operations only, therefore opening an
+# image read-only makes the overlap-check option irrelevant
+OPEN_RO="open -r $TEST_IMG"
+
echo
echo "=== Testing L2 reference into L1 ==="
echo
@@ -65,16 +71,18 @@ _check_test_img
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
# Try to write something, thereby forcing the corrupt bit to be set
-$QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
# The corrupt bit must now be set
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
# Try to open the image R/W (which should fail)
-$QEMU_IO -c "read 0 512" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir | _filter_imgfmt
+$QEMU_IO -c "$OPEN_RW" -c "read 0 512" 2>&1 | _filter_qemu_io \
+ | _filter_testdir \
+ | _filter_imgfmt
# Try to open it RO (which should succeed)
-$QEMU_IO -c "read 0 512" -r "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "$OPEN_RO" -c "read 0 512" | _filter_qemu_io
# We could now try to fix the image, but this would probably fail (how should an
# L2 table linked onto the L1 table be fixed?)
@@ -92,7 +100,7 @@ poke_file "$TEST_IMG" "$(($rb_offset+8))" "\x00\x01"
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x02\x00\x00"
_check_test_img
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
-$QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
# Try to fix it
@@ -102,8 +110,33 @@ _check_test_img -r all
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
# Look if it's really really fixed
-$QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
+./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+
+echo
+echo "=== Testing cluster data reference into inactive L2 table ==="
+echo
+_make_test_img 64M
+$QEMU_IO -c "$OPEN_RW" -c "write -P 1 0 512" | _filter_qemu_io
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+$QEMU_IO -c "$OPEN_RW" -c "write -P 2 0 512" | _filter_qemu_io
+# The inactive L2 table remains at its old offset
+poke_file "$TEST_IMG" "$l2_offset_after_snapshot" \
+ "\x80\x00\x00\x00\x00\x04\x00\x00"
+_check_test_img
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+$QEMU_IO -c "$OPEN_RW" -c "write -P 3 0 512" | _filter_qemu_io
+./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_check_test_img -r all
+./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+$QEMU_IO -c "$OPEN_RW" -c "write -P 4 0 512" | _filter_qemu_io
+./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+
+# Check data
+$QEMU_IO -c "$OPEN_RO" -c "read -P 4 0 512" | _filter_qemu_io
+$QEMU_IMG snapshot -a foo "$TEST_IMG"
+_check_test_img
+$QEMU_IO -c "$OPEN_RO" -c "read -P 1 0 512" | _filter_qemu_io
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out
index 648f7437a2..6c7bdbb2f2 100644
--- a/tests/qemu-iotests/060.out
+++ b/tests/qemu-iotests/060.out
@@ -12,7 +12,6 @@ qcow2: Preventing invalid write on metadata (overlaps with active L1 table); ima
write failed: Input/output error
incompatible_features 0x2
qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write
-no file open, try 'help open'
read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@@ -40,4 +39,43 @@ incompatible_features 0x0
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
incompatible_features 0x0
+
+=== Testing cluster data reference into inactive L2 table ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ERROR cluster 4 refcount=1 reference=2
+Leaked cluster 9 refcount=1 reference=0
+
+1 errors were found on the image.
+Data may be corrupted, or further writes to the image may corrupt it.
+
+1 leaked clusters were found on the image.
+This means waste of disk space, but no harm to data.
+incompatible_features 0x0
+qcow2: Preventing invalid write on metadata (overlaps with inactive L2 table); image marked as corrupt.
+write failed: Input/output error
+incompatible_features 0x2
+Repairing cluster 4 refcount=1 reference=2
+Repairing cluster 9 refcount=1 reference=0
+Repairing OFLAG_COPIED data cluster: l2_entry=8000000000040000 refcount=2
+The following inconsistencies were found and repaired:
+
+ 1 leaked clusters
+ 2 corruptions
+
+Double checking the fixed image now...
+No errors were found on the image.
+incompatible_features 0x0
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+incompatible_features 0x0
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
diff --git a/tests/qemu-iotests/064 b/tests/qemu-iotests/064
new file mode 100755
index 0000000000..6789aa6ee4
--- /dev/null
+++ b/tests/qemu-iotests/064
@@ -0,0 +1,62 @@
+#!/bin/bash
+#
+# Test VHDX read/write from a sample image created with Hyper-V
+#
+# Copyright (C) 2013 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=jcody@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt vhdx
+_supported_proto generic
+_supported_os Linux
+
+_use_sample_img iotest-dynamic-1G.vhdx.bz2
+
+echo
+echo "=== Verify pattern 0xa5, 0 - 33MB ==="
+$QEMU_IO -r -c "read -pP 0xa5 0 33M" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Verify pattern 0x96, 33M - 66M ==="
+$QEMU_IO -r -c "read -pP 0x96 33M 33M" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Verify pattern 0x00, 66M - 1024M ==="
+$QEMU_IO -r -c "read -pP 0x00 66M 958M" "$TEST_IMG" | _filter_qemu_io
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/064.out b/tests/qemu-iotests/064.out
new file mode 100644
index 0000000000..b9e8e4a873
--- /dev/null
+++ b/tests/qemu-iotests/064.out
@@ -0,0 +1,14 @@
+QA output created by 064
+
+=== Verify pattern 0xa5, 0 - 33MB ===
+read 34603008/34603008 bytes at offset 0
+33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Verify pattern 0x96, 33M - 66M ===
+read 34603008/34603008 bytes at offset 34603008
+33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Verify pattern 0x00, 66M - 1024M ===
+read 1004535808/1004535808 bytes at offset 69206016
+958 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065
new file mode 100755
index 0000000000..ab5445f62d
--- /dev/null
+++ b/tests/qemu-iotests/065
@@ -0,0 +1,125 @@
+#!/usr/bin/env python2
+#
+# Test for additional information emitted by qemu-img info on qcow2
+# images
+#
+# Copyright (C) 2013 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/>.
+#
+
+import os
+import re
+import json
+import iotests
+from iotests import qemu_img, qemu_img_pipe
+import unittest
+
+test_img = os.path.join(iotests.test_dir, 'test.img')
+
+class TestImageInfoSpecific(iotests.QMPTestCase):
+ '''Abstract base class for ImageInfoSpecific tests'''
+
+ def setUp(self):
+ if self.img_options is None:
+ self.skipTest('Skipping abstract test class')
+ qemu_img('create', '-f', iotests.imgfmt, '-o', self.img_options,
+ test_img, '128K')
+
+ def tearDown(self):
+ os.remove(test_img)
+
+class TestQemuImgInfo(TestImageInfoSpecific):
+ '''Abstract base class for qemu-img info tests'''
+
+ img_options = None
+ json_compare = None
+ human_compare = None
+
+ def test_json(self):
+ data = json.loads(qemu_img_pipe('info', '--output=json', test_img))
+ data = data['format-specific']
+ self.assertEqual(data['type'], iotests.imgfmt)
+ self.assertEqual(data['data'], self.json_compare)
+
+ def test_human(self):
+ data = qemu_img_pipe('info', '--output=human', test_img).split('\n')
+ data = data[(data.index('Format specific information:') + 1)
+ :data.index('')]
+ for field in data:
+ self.assertTrue(re.match('^ {4}[^ ]', field) is not None)
+ data = map(lambda line: line.strip(), data)
+ self.assertEqual(data, self.human_compare)
+
+class TestQMP(TestImageInfoSpecific):
+ '''Abstract base class for qemu QMP tests'''
+
+ img_options = None
+ qemu_options = ''
+ TestImageInfoSpecific = TestImageInfoSpecific
+
+ def setUp(self):
+ self.TestImageInfoSpecific.setUp(self)
+ self.vm = iotests.VM().add_drive(test_img, self.qemu_options)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ self.TestImageInfoSpecific.tearDown(self)
+
+ def test_qmp(self):
+ result = self.vm.qmp('query-block')['return']
+ drive = filter(lambda drive: drive['device'] == 'drive0', result)[0]
+ data = drive['inserted']['image']['format-specific']
+ self.assertEqual(data['type'], iotests.imgfmt)
+ self.assertEqual(data['data'], self.compare)
+
+class TestQCow2(TestQemuImgInfo):
+ '''Testing a qcow2 version 2 image'''
+ img_options = 'compat=0.10'
+ json_compare = { 'compat': '0.10' }
+ human_compare = [ 'compat: 0.10' ]
+
+class TestQCow3NotLazy(TestQemuImgInfo):
+ '''Testing a qcow2 version 3 image with lazy refcounts disabled'''
+ img_options = 'compat=1.1,lazy_refcounts=off'
+ json_compare = { 'compat': '1.1', 'lazy-refcounts': False }
+ human_compare = [ 'compat: 1.1', 'lazy refcounts: false' ]
+
+class TestQCow3Lazy(TestQemuImgInfo):
+ '''Testing a qcow2 version 3 image with lazy refcounts enabled'''
+ img_options = 'compat=1.1,lazy_refcounts=on'
+ json_compare = { 'compat': '1.1', 'lazy-refcounts': True }
+ human_compare = [ 'compat: 1.1', 'lazy refcounts: true' ]
+
+class TestQCow3NotLazyQMP(TestQMP):
+ '''Testing a qcow2 version 3 image with lazy refcounts disabled, opening
+ with lazy refcounts enabled'''
+ img_options = 'compat=1.1,lazy_refcounts=off'
+ qemu_options = 'lazy-refcounts=on'
+ compare = { 'compat': '1.1', 'lazy-refcounts': False }
+
+class TestQCow3LazyQMP(TestQMP):
+ '''Testing a qcow2 version 3 image with lazy refcounts enabled, opening
+ with lazy refcounts disabled'''
+ img_options = 'compat=1.1,lazy_refcounts=on'
+ qemu_options = 'lazy-refcounts=off'
+ compare = { 'compat': '1.1', 'lazy-refcounts': True }
+
+TestImageInfoSpecific = None
+TestQemuImgInfo = None
+TestQMP = None
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=['qcow2'])
diff --git a/tests/qemu-iotests/065.out b/tests/qemu-iotests/065.out
new file mode 100644
index 0000000000..594c16f49f
--- /dev/null
+++ b/tests/qemu-iotests/065.out
@@ -0,0 +1,5 @@
+........
+----------------------------------------------------------------------
+Ran 8 tests
+
+OK
diff --git a/tests/qemu-iotests/066 b/tests/qemu-iotests/066
new file mode 100755
index 0000000000..1c2452b0c5
--- /dev/null
+++ b/tests/qemu-iotests/066
@@ -0,0 +1,63 @@
+#!/bin/bash
+#
+# Test case for discarding preallocated zero clusters in qcow2
+#
+# Copyright (C) 2013 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=mreitz@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+# This tests qocw2-specific low-level functionality
+_supported_fmt qcow2
+_supported_proto generic
+_supported_os Linux
+
+IMGOPTS="compat=1.1"
+IMG_SIZE=64M
+
+echo
+echo "=== Testing snapshotting an image with zero clusters ==="
+echo
+_make_test_img $IMG_SIZE
+# Write some normal clusters, zero them (creating preallocated zero clusters)
+# and discard those
+$QEMU_IO -c "write 0 256k" -c "write -z 0 256k" -c "discard 0 256k" "$TEST_IMG" \
+ | _filter_qemu_io
+# Check the image (there shouldn't be any leaks)
+_check_test_img
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/066.out b/tests/qemu-iotests/066.out
new file mode 100644
index 0000000000..9139780f49
--- /dev/null
+++ b/tests/qemu-iotests/066.out
@@ -0,0 +1,13 @@
+QA output created by 066
+
+=== Testing snapshotting an image with zero clusters ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 262144/262144 bytes at offset 0
+256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 262144/262144 bytes at offset 0
+256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+discard 262144/262144 bytes at offset 0
+256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+*** done
diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067
new file mode 100755
index 0000000000..79dc38bc04
--- /dev/null
+++ b/tests/qemu-iotests/067
@@ -0,0 +1,133 @@
+#!/bin/bash
+#
+# Test automatic deletion of BDSes created by -drive/drive_add
+#
+# Copyright (C) 2013 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=kwolf@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+function do_run_qemu()
+{
+ echo Testing: "$@"
+ $QEMU -nographic -qmp stdio -serial none "$@"
+ echo
+}
+
+function run_qemu()
+{
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp
+}
+
+size=128M
+
+_make_test_img $size
+
+echo
+echo === -drive/-device and device_del ===
+echo
+
+run_qemu -drive file=$TEST_IMG,format=$IMGFMT,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0 <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "query-block" }
+{ "execute": "device_del", "arguments": { "id": "virtio0" } }
+{ "execute": "system_reset" }
+{ "execute": "query-block" }
+{ "execute": "quit" }
+EOF
+
+echo
+echo === -drive/device_add and device_del ===
+echo
+
+run_qemu -drive file=$TEST_IMG,format=$IMGFMT,if=none,id=disk <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "query-block" }
+{ "execute": "device_add",
+ "arguments": { "driver": "virtio-blk-pci", "drive": "disk",
+ "id": "virtio0" } }
+{ "execute": "device_del", "arguments": { "id": "virtio0" } }
+{ "execute": "system_reset" }
+{ "execute": "query-block" }
+{ "execute": "quit" }
+EOF
+
+echo
+echo === drive_add/device_add and device_del ===
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "human-monitor-command",
+ "arguments": { "command-line": "drive_add 0 file=$TEST_IMG,format=$IMGFMT,if=none,id=disk" } }
+{ "execute": "query-block" }
+{ "execute": "device_add",
+ "arguments": { "driver": "virtio-blk-pci", "drive": "disk",
+ "id": "virtio0" } }
+{ "execute": "device_del", "arguments": { "id": "virtio0" } }
+{ "execute": "system_reset" }
+{ "execute": "query-block" }
+{ "execute": "quit" }
+EOF
+
+echo
+echo === blockdev_add/device_add and device_del ===
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+ "arguments": {
+ "options": {
+ "driver": "$IMGFMT",
+ "id": "disk",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
+ }
+ }
+ }
+ }
+{ "execute": "query-block" }
+{ "execute": "device_add",
+ "arguments": { "driver": "virtio-blk-pci", "drive": "disk",
+ "id": "virtio0" } }
+{ "execute": "device_del", "arguments": { "id": "virtio0" } }
+{ "execute": "system_reset" }
+{ "execute": "query-block" }
+{ "execute": "quit" }
+EOF
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/067.out b/tests/qemu-iotests/067.out
new file mode 100644
index 0000000000..4bb9ff9652
--- /dev/null
+++ b/tests/qemu-iotests/067.out
@@ -0,0 +1,80 @@
+QA output created by 067
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+
+=== -drive/-device and device_del ===
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0
+QMP_VERSION
+{"return": {}}
+{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
+{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+
+
+=== -drive/device_add and device_del ===
+
+Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk
+QMP_VERSION
+{"return": {}}
+{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
+{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+
+
+=== drive_add/device_add and device_del ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": "OK\r\n"}
+{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
+{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+
+
+=== blockdev_add/device_add and device_del ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
+{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+
+*** done
diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common
index fecaf85074..2932e14e73 100644
--- a/tests/qemu-iotests/common
+++ b/tests/qemu-iotests/common
@@ -45,6 +45,7 @@ valgrind=false
rm -f $tmp.list $tmp.tmp $tmp.sed
export IMGFMT=raw
+export IMGFMT_GENERIC=true
export IMGPROTO=file
export IMGOPTS=""
export QEMU_IO_OPTIONS=""
@@ -133,6 +134,7 @@ check options
-qed test qed
-vdi test vdi
-vpc test vpc
+ -vhdx test vhdx
-vmdk test vmdk
-rbd test rbd
-sheepdog test sheepdog
@@ -195,6 +197,12 @@ testlist options
xpand=false
;;
+ -vhdx)
+ IMGFMT=vhdx
+ xpand=false
+ IMGFMT_GENERIC=false
+ ;;
+
-rbd)
IMGPROTO=rbd
xpand=false
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
index 5dfda63e59..8e7b1a4195 100644
--- a/tests/qemu-iotests/common.filter
+++ b/tests/qemu-iotests/common.filter
@@ -159,5 +159,13 @@ _filter_qemu()
-e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#'
}
+# replace problematic QMP output like timestamps
+_filter_qmp()
+{
+ _filter_win32 | \
+ sed -e 's#\("\(micro\)\?seconds": \)[0-9]\+#\1 TIMESTAMP#g' \
+ -e 's#^{"QMP":.*}$#QMP_VERSION#'
+}
+
# make sure this script returns success
/bin/true
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 1b22db04f4..4e826040d4 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -197,12 +197,30 @@ _check_test_img()
_img_info()
{
+ discard=0
+ regex_json_spec_start='^ *"format-specific": \{'
$QEMU_IMG info "$@" "$TEST_IMG" 2>&1 | \
sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
-e "s#$TEST_DIR#TEST_DIR#g" \
-e "s#$IMGFMT#IMGFMT#g" \
-e "/^disk size:/ D" \
- -e "/actual-size/ D"
+ -e "/actual-size/ D" | \
+ while IFS='' read line; do
+ if [[ $line == "Format specific information:" ]]; then
+ discard=1
+ elif [[ $line =~ $regex_json_spec_start ]]; then
+ discard=2
+ regex_json_spec_end="^${line%%[^ ]*}\\},? *$"
+ fi
+ if [[ $discard == 0 ]]; then
+ echo "$line"
+ elif [[ $discard == 1 && ! $line ]]; then
+ echo
+ discard=0
+ elif [[ $discard == 2 && $line =~ $regex_json_spec_end ]]; then
+ discard=0
+ fi
+ done
}
_get_pids_by_name()
@@ -321,7 +339,7 @@ _fail()
_supported_fmt()
{
for f; do
- if [ "$f" = "$IMGFMT" -o "$f" = "generic" ]; then
+ if [ "$f" = "$IMGFMT" -o "$f" = "generic" -a "$IMGFMT_GENERIC" = "true" ]; then
return
fi
done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 1ad02e5a2c..13c5500f54 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -69,3 +69,7 @@
061 rw auto
062 rw auto
063 rw auto
+064 rw auto
+065 rw auto
+066 rw auto
+067 rw auto
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 376d6e8ffe..fb10ff43a7 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -49,6 +49,10 @@ def qemu_img_verbose(*args):
'''Run qemu-img without suppressing its output and return the exit code'''
return subprocess.call(qemu_img_args + list(args))
+def qemu_img_pipe(*args):
+ '''Run qemu-img and return its output'''
+ return subprocess.Popen(qemu_img_args + list(args), stdout=subprocess.PIPE).communicate()[0]
+
def qemu_io(*args):
'''Run qemu-io and return the stdout data'''
args = qemu_io_args + list(args)
diff --git a/tests/qemu-iotests/sample_images/iotest-dynamic-1G.vhdx.bz2 b/tests/qemu-iotests/sample_images/iotest-dynamic-1G.vhdx.bz2
new file mode 100644
index 0000000000..77d97a0bae
--- /dev/null
+++ b/tests/qemu-iotests/sample_images/iotest-dynamic-1G.vhdx.bz2
Binary files differ
diff --git a/translate-all.c b/translate-all.c
index e7aff928b6..aeda54dfbd 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -1318,18 +1318,6 @@ static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
mmap_unlock();
}
-#if defined(CONFIG_QEMU_LDST_OPTIMIZATION) && defined(CONFIG_SOFTMMU)
-/* check whether the given addr is in TCG generated code buffer or not */
-bool is_tcg_gen_code(uintptr_t tc_ptr)
-{
- /* This can be called during code generation, code_gen_buffer_size
- is used instead of code_gen_ptr for upper boundary checking */
- return (tc_ptr >= (uintptr_t)tcg_ctx.code_gen_buffer &&
- tc_ptr < (uintptr_t)(tcg_ctx.code_gen_buffer +
- tcg_ctx.code_gen_buffer_size));
-}
-#endif
-
/* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr <
tb[1].tc_ptr. Return NULL if not found */
static TranslationBlock *tb_find_pc(uintptr_t tc_ptr)
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 253bc3df2e..e00a44c86f 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -157,6 +157,18 @@ void qemu_set_nonblock(int fd)
fcntl(fd, F_SETFL, f | O_NONBLOCK);
}
+int socket_set_fast_reuse(int fd)
+{
+ int val = 1, ret;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&val, sizeof(val));
+
+ assert(ret == 0);
+
+ return ret;
+}
+
void qemu_set_cloexec(int fd)
{
int f;
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index 983b7a2375..776ccfaaf0 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -124,6 +124,16 @@ void qemu_set_nonblock(int fd)
qemu_fd_register(fd);
}
+int socket_set_fast_reuse(int fd)
+{
+ /* Enabling the reuse of an endpoint that was used by a socket still in
+ * TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows
+ * fast reuse is the default and SO_REUSEADDR does strange things. So we
+ * don't have to do anything here. More info can be found at:
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */
+ return 0;
+}
+
int inet_aton(const char *cp, struct in_addr *ia)
{
uint32_t addr = inet_addr(cp);
diff --git a/util/path.c b/util/path.c
index f0c69627c7..623219e4c5 100644
--- a/util/path.c
+++ b/util/path.c
@@ -39,7 +39,7 @@ static int strneq(const char *s1, unsigned int n, const char *s2)
}
static struct pathelem *add_entry(struct pathelem *root, const char *name,
- unsigned char type);
+ unsigned type);
static struct pathelem *new_entry(const char *root,
struct pathelem *parent,
@@ -82,7 +82,7 @@ static struct pathelem *add_dir_maybe(struct pathelem *path)
}
static struct pathelem *add_entry(struct pathelem *root, const char *name,
- unsigned char type)
+ unsigned type)
{
struct pathelem **e;
diff --git a/util/qemu-option.c b/util/qemu-option.c
index e0844a966c..efcb5dcfcb 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -834,6 +834,12 @@ const char *qemu_opts_id(QemuOpts *opts)
return opts->id;
}
+/* The id string will be g_free()d by qemu_opts_del */
+void qemu_opts_set_id(QemuOpts *opts, char *id)
+{
+ opts->id = id;
+}
+
void qemu_opts_del(QemuOpts *opts)
{
QemuOpt *opt;
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 095716ecdb..6b97dc11f9 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -155,7 +155,7 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
continue;
}
- qemu_setsockopt(slisten, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ socket_set_fast_reuse(slisten);
#ifdef IPV6_V6ONLY
if (e->ai_family == PF_INET6) {
/* listen on both ipv4 and ipv6 */
@@ -274,7 +274,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
return -1;
}
- qemu_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ socket_set_fast_reuse(sock);
if (connect_state != NULL) {
qemu_set_nonblock(sock);
}
@@ -455,7 +455,7 @@ int inet_dgram_opts(QemuOpts *opts, Error **errp)
error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED);
goto err;
}
- qemu_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ socket_set_fast_reuse(sock);
/* bind socket */
if (bind(sock, local->ai_addr, local->ai_addrlen) < 0) {
diff --git a/version.rc b/version.rc
index a50d62fa0c..d42ef62962 100644
--- a/version.rc
+++ b/version.rc
@@ -13,7 +13,7 @@ FILESUBTYPE VFT2_UNKNOWN
{
BLOCK "040904E4"
{
- VALUE "CompanyName", "http://www.qemu.org"
+ VALUE "CompanyName", "http://www.qemu-project.org"
VALUE "FileDescription", "QEMU machine emulators and tools"
VALUE "FileVersion", QEMU_VERSION
VALUE "LegalCopyright", "Copyright various authors. Released under the GNU General Public License."
diff --git a/vl.c b/vl.c
index 983cdc6eaa..7e1f40880d 100644
--- a/vl.c
+++ b/vl.c
@@ -2825,7 +2825,7 @@ int main(int argc, char **argv, char **envp)
const char *icount_option = NULL;
const char *initrd_filename;
const char *kernel_filename, *kernel_cmdline;
- const char *boot_order = NULL;
+ const char *boot_order;
DisplayState *ds;
int cyls, heads, secs, translation;
QemuOpts *hda_opts = NULL, *opts, *machine_opts;
@@ -4050,9 +4050,7 @@ int main(int argc, char **argv, char **envp)
initrd_filename = qemu_opt_get(machine_opts, "initrd");
kernel_cmdline = qemu_opt_get(machine_opts, "append");
- if (!boot_order) {
- boot_order = machine->default_boot_order;
- }
+ boot_order = machine->default_boot_order;
opts = qemu_opts_find(qemu_find_opts("boot-opts"), NULL);
if (opts) {
char *normal_boot_order;
diff --git a/xen-all.c b/xen-all.c
index 48e881bc29..9a27899ca1 100644
--- a/xen-all.c
+++ b/xen-all.c
@@ -949,7 +949,7 @@ static void xenstore_record_dm_state(struct xs_handle *xs, const char *state)
exit(1);
}
- snprintf(path, sizeof (path), "/local/domain/0/device-model/%u/state", xen_domid);
+ snprintf(path, sizeof (path), "device-model/%u/state", xen_domid);
if (!xs_write(xs, XBT_NULL, path, state, strlen(state))) {
fprintf(stderr, "error recording dm state\n");
exit(1);