aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--Makefile4
-rw-r--r--audio/audio_int.h4
-rw-r--r--block/iscsi.c2
-rw-r--r--block/nbd-client.c9
-rw-r--r--block/sheepdog.c2
-rw-r--r--blockdev-nbd.c10
-rwxr-xr-xconfigure17
-rw-r--r--contrib/rdmacm-mux/Makefile.objs1
-rw-r--r--contrib/rdmacm-mux/main.c12
-rw-r--r--default-configs/virtio.mak3
-rw-r--r--docs/devel/migration.rst9
-rw-r--r--docs/pvrdma.txt4
-rw-r--r--docs/specs/ivshmem-spec.txt8
-rw-r--r--docs/specs/tpm.txt104
-rw-r--r--hw/acpi/Makefile.objs1
-rw-r--r--hw/acpi/core.c12
-rw-r--r--hw/acpi/memory_hotplug.c10
-rw-r--r--hw/acpi/tpm.c459
-rw-r--r--hw/arm/virt-acpi-build.c1
-rw-r--r--hw/block/vhost-user-blk.c7
-rw-r--r--hw/block/xen-block.c2
-rw-r--r--hw/char/stm32f2xx_usart.c3
-rw-r--r--hw/core/machine.c7
-rw-r--r--hw/display/virtio-gpu-pci.c14
-rw-r--r--hw/display/virtio-vga.c1
-rw-r--r--hw/i386/acpi-build.c29
-rw-r--r--hw/i386/pc_piix.c1
-rw-r--r--hw/intc/xics_kvm.c1
-rw-r--r--hw/intc/xics_spapr.c1
-rw-r--r--hw/microblaze/petalogix_s3adsp1800_mmu.c4
-rw-r--r--hw/mips/cps.c8
-rw-r--r--hw/misc/edu.c1
-rw-r--r--hw/misc/ivshmem.c210
-rw-r--r--hw/misc/mips_itu.c73
-rw-r--r--hw/net/ftgmac100.c80
-rw-r--r--hw/net/virtio-net.c667
-rw-r--r--hw/pci/msix.c2
-rw-r--r--hw/pci/pcie.c25
-rw-r--r--hw/pci/pcie_port.c1
-rw-r--r--hw/ppc/spapr_irq.c1
-rw-r--r--hw/rdma/rdma_backend.c63
-rw-r--r--hw/rdma/rdma_backend.h12
-rw-r--r--hw/rdma/rdma_backend_defs.h1
-rw-r--r--hw/rdma/rdma_rm.c9
-rw-r--r--hw/rdma/vmw/pvrdma_main.c10
-rw-r--r--hw/rdma/vmw/pvrdma_qp_ops.c44
-rw-r--r--hw/rdma/vmw/pvrdma_qp_ops.h4
-rw-r--r--hw/s390x/s390-pci-bus.c62
-rw-r--r--hw/s390x/s390-pci-bus.h29
-rw-r--r--hw/s390x/s390-pci-inst.c133
-rw-r--r--hw/s390x/s390-pci-inst.h1
-rw-r--r--hw/scsi/vhost-scsi.c3
-rw-r--r--hw/scsi/vhost-user-scsi.c3
-rw-r--r--hw/tpm/Makefile.objs1
-rw-r--r--hw/tpm/tpm_crb.c13
-rw-r--r--hw/tpm/tpm_ppi.c53
-rw-r--r--hw/tpm/tpm_ppi.h46
-rw-r--r--hw/tpm/tpm_tis.c13
-rw-r--r--hw/tpm/trace-events3
-rw-r--r--hw/virtio/Makefile.objs15
-rw-r--r--hw/virtio/vhost-scsi-pci.c97
-rw-r--r--hw/virtio/vhost-user-blk-pci.c103
-rw-r--r--hw/virtio/vhost-user-scsi-pci.c103
-rw-r--r--hw/virtio/vhost-user.c2
-rw-r--r--hw/virtio/vhost-vsock-pci.c86
-rw-r--r--hw/virtio/virtio-9p-pci.c88
-rw-r--r--hw/virtio/virtio-balloon-pci.c95
-rw-r--r--hw/virtio/virtio-balloon.c2
-rw-r--r--hw/virtio/virtio-blk-pci.c100
-rw-r--r--hw/virtio/virtio-crypto-pci.c14
-rw-r--r--hw/virtio/virtio-input-host-pci.c48
-rw-r--r--hw/virtio/virtio-input-pci.c157
-rw-r--r--hw/virtio/virtio-net-pci.c98
-rw-r--r--hw/virtio/virtio-pci.c811
-rw-r--r--hw/virtio/virtio-pci.h234
-rw-r--r--hw/virtio/virtio-rng-pci.c88
-rw-r--r--hw/virtio/virtio-scsi-pci.c107
-rw-r--r--hw/virtio/virtio-serial-pci.c115
-rw-r--r--include/block/nbd.h31
-rw-r--r--include/hw/acpi/acpi-defs.h15
-rw-r--r--include/hw/acpi/tpm.h21
-rw-r--r--include/hw/arm/pxa.h1
-rw-r--r--include/hw/char/serial.h4
-rw-r--r--include/hw/devices.h1
-rw-r--r--include/hw/i2c/smbus.h2
-rw-r--r--include/hw/i386/pc.h4
-rw-r--r--include/hw/ide/ahci.h4
-rw-r--r--include/hw/input/ps2.h2
-rw-r--r--include/hw/misc/mips_itu.h8
-rw-r--r--include/hw/pci/pcie.h2
-rw-r--r--include/hw/pcmcia.h4
-rw-r--r--include/hw/ppc/spapr.h5
-rw-r--r--include/hw/ppc/spapr_xive.h2
-rw-r--r--include/hw/ppc/xics.h7
-rw-r--r--include/hw/ppc/xics_spapr.h37
-rw-r--r--include/hw/qdev-core.h3
-rw-r--r--include/hw/virtio/virtio-net.h86
-rw-r--r--include/migration/vmstate.h1
-rw-r--r--include/net/eth.h2
-rw-r--r--include/qemu/compiler.h19
-rw-r--r--include/qemu/typedefs.h13
-rw-r--r--include/sysemu/bt.h4
-rw-r--r--include/ui/console.h24
-rw-r--r--include/ui/qemu-pixman.h10
-rw-r--r--linux-user/qemu.h6
-rw-r--r--migration/global_state.c15
-rw-r--r--migration/vmstate.c13
-rw-r--r--nbd/client.c761
-rw-r--r--nbd/server.c24
-rw-r--r--nbd/trace-events17
-rw-r--r--pc-bios/efi-e1000.rombin240128 -> 240128 bytes
-rw-r--r--pc-bios/efi-e1000e.rombin240128 -> 240128 bytes
-rw-r--r--pc-bios/efi-eepro100.rombin240128 -> 240128 bytes
-rw-r--r--pc-bios/efi-ne2k_pci.rombin238080 -> 238592 bytes
-rw-r--r--pc-bios/efi-pcnet.rombin238080 -> 238592 bytes
-rw-r--r--pc-bios/efi-rtl8139.rombin241664 -> 242688 bytes
-rw-r--r--pc-bios/efi-virtio.rombin242176 -> 242688 bytes
-rw-r--r--pc-bios/efi-vmxnet3.rombin236032 -> 236032 bytes
-rw-r--r--pc-bios/s390-ccw/start.S14
-rw-r--r--qemu-deprecated.texi5
-rw-r--r--qemu-nbd.c224
-rw-r--r--qemu-nbd.texi119
-rw-r--r--qemu-seccomp.c3
-rw-r--r--qom/object.c3
m---------roms/ipxe0
-rwxr-xr-xscripts/analyse-locks-simpletrace.py1
-rwxr-xr-xscripts/analyze-migration.py1
-rwxr-xr-xscripts/device-crash-test17
-rw-r--r--scripts/qemu.py2
-rwxr-xr-xscripts/replay-dump.py2
-rwxr-xr-xscripts/simpletrace.py1
-rwxr-xr-xscripts/texi2pod.pl2
-rwxr-xr-xscripts/tracetool.py2
-rw-r--r--scripts/tracetool/format/simpletrace_stap.py2
-rw-r--r--stubs/tpm.c5
-rw-r--r--target/arm/Makefile.objs1
-rw-r--r--target/arm/cpu.c19
-rw-r--r--target/arm/cpu.h244
-rw-r--r--target/arm/cpu64.c68
-rw-r--r--target/arm/helper-a64.c155
-rw-r--r--target/arm/helper-a64.h14
-rw-r--r--target/arm/helper.c1216
-rw-r--r--target/arm/helper.h1
-rw-r--r--target/arm/internals.h77
-rw-r--r--target/arm/machine.c24
-rw-r--r--target/arm/op_helper.c174
-rw-r--r--target/arm/pauth_helper.c497
-rw-r--r--target/arm/translate-a64.c541
-rw-r--r--target/arm/translate.h5
-rw-r--r--target/microblaze/cpu.c14
-rw-r--r--target/microblaze/cpu.h9
-rw-r--r--target/microblaze/op_helper.c22
-rw-r--r--target/mips/cpu.h331
-rw-r--r--target/mips/helper.h6
-rw-r--r--target/mips/internal.h1
-rw-r--r--target/mips/machine.c7
-rw-r--r--target/mips/op_helper.c64
-rw-r--r--target/mips/translate.c1192
-rw-r--r--target/ppc/cpu.h9
-rw-r--r--target/s390x/diag.c2
-rw-r--r--tests/Makefile.include55
-rw-r--r--tests/acceptance/linux_initrd.py48
-rw-r--r--tests/acpi-utils.c35
-rw-r--r--tests/acpi-utils.h44
-rw-r--r--tests/bios-tables-test.c261
-rw-r--r--tests/boot-order-test.c59
-rw-r--r--tests/data/acpi/pc/DSDT.dimmpxmbin6790 -> 6784 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.memhpbin6496 -> 6490 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.dimmpxmbin9474 -> 9468 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.memhpbin9180 -> 9174 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.mmio64bin8947 -> 8945 bytes
-rw-r--r--tests/endianness-test.c329
-rw-r--r--tests/hexloader-test.c2
-rw-r--r--tests/ivshmem-test.c67
-rw-r--r--tests/pnv-xscom-test.c29
-rwxr-xr-xtests/qemu-iotests/2232
-rw-r--r--tests/qemu-iotests/223.out20
-rwxr-xr-xtests/qemu-iotests/23330
-rw-r--r--tests/qemu-iotests/233.out19
-rw-r--r--tests/vhost-user-test.c2
-rw-r--r--tests/vmgenid-test.c64
-rw-r--r--util/oslib-posix.c8
183 files changed, 8322 insertions, 3676 deletions
diff --git a/.travis.yml b/.travis.yml
index 93fd0164a0..87d9fa971c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -175,12 +175,14 @@ matrix:
# Python builds
- env:
- CONFIG="--target-list=x86_64-softmmu"
+ language: python
python:
- - "3.0"
+ - "3.4"
- env:
- CONFIG="--target-list=x86_64-softmmu"
+ language: python
python:
- "3.6"
diff --git a/Makefile b/Makefile
index e944f77eae..de898eab62 100644
--- a/Makefile
+++ b/Makefile
@@ -581,6 +581,8 @@ vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a
$(call LINK, $^)
vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a
$(call LINK, $^)
+
+rdmacm-mux$(EXESUF): LIBS += "-libumad"
rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS)
$(call LINK, $^)
@@ -872,6 +874,8 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
+$(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl
+
# Reports/Analysis
%/coverage-report.html:
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 244b454012..6c451b995c 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -191,7 +191,7 @@ struct SWVoiceCap {
QLIST_ENTRY (SWVoiceCap) entries;
};
-struct AudioState {
+typedef struct AudioState {
struct audio_driver *drv;
void *drv_opaque;
@@ -203,7 +203,7 @@ struct AudioState {
int nb_hw_voices_out;
int nb_hw_voices_in;
int vm_running;
-};
+} AudioState;
extern const struct mixeng_volume nominal_volume;
diff --git a/block/iscsi.c b/block/iscsi.c
index a7e8c1ffaf..ff473206e6 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -49,7 +49,9 @@
/* Conflict between scsi/utils.h and libiscsi! :( */
#define SCSI_XFER_NONE ISCSI_XFER_NONE
#include <iscsi/iscsi.h>
+#define inline __attribute__((gnu_inline)) /* required for libiscsi v1.9.0 */
#include <iscsi/scsi-lowlevel.h>
+#undef inline
#undef SCSI_XFER_NONE
QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE);
diff --git a/block/nbd-client.c b/block/nbd-client.c
index ef32075971..813539676d 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -249,11 +249,11 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
}
context_id = payload_advance32(&payload);
- if (client->info.meta_base_allocation_id != context_id) {
+ if (client->info.context_id != context_id) {
error_setg(errp, "Protocol error: unexpected context id %d for "
"NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context "
"id is %d", context_id,
- client->info.meta_base_allocation_id);
+ client->info.context_id);
return -EINVAL;
}
@@ -999,10 +999,11 @@ int nbd_client_init(BlockDriverState *bs,
client->info.structured_reply = true;
client->info.base_allocation = true;
client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap);
- ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
- tlscreds, hostname,
+ client->info.name = g_strdup(export ?: "");
+ ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, hostname,
&client->ioc, &client->info, errp);
g_free(client->info.x_dirty_bitmap);
+ g_free(client->info.name);
if (ret < 0) {
logout("Failed to negotiate with the NBD server\n");
return ret;
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 90ab43baa4..ed14f7afbe 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -1224,7 +1224,7 @@ static int find_vdi_name(BDRVSheepdogState *s, const char *filename,
SheepdogVdiReq hdr;
SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
unsigned int wlen, rlen = 0;
- char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
+ char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN] QEMU_NONSTRING;
fd = connect_to_sdog(s, errp);
if (fd < 0) {
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index c76d5416b9..d73ac1b026 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -146,6 +146,7 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
BlockDriverState *bs = NULL;
BlockBackend *on_eject_blk;
NBDExport *exp;
+ int64_t len;
if (!nbd_server) {
error_setg(errp, "NBD server not running");
@@ -168,6 +169,13 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
return;
}
+ len = bdrv_getlength(bs);
+ if (len < 0) {
+ error_setg_errno(errp, -len,
+ "Failed to determine the NBD export's length");
+ return;
+ }
+
if (!has_writable) {
writable = false;
}
@@ -175,7 +183,7 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
writable = false;
}
- exp = nbd_export_new(bs, 0, -1, name, NULL, bitmap,
+ exp = nbd_export_new(bs, 0, len, name, NULL, bitmap,
writable ? 0 : NBD_FLAG_READ_ONLY,
NULL, false, on_eject_blk, errp);
if (!exp) {
diff --git a/configure b/configure
index 9bdcedc87a..8f312ac3e2 100755
--- a/configure
+++ b/configure
@@ -107,6 +107,9 @@ update_cxxflags() {
-Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\
-Wold-style-declaration|-Wold-style-definition|-Wredundant-decls)
;;
+ -std=gnu99)
+ QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }"-std=gnu++98"
+ ;;
*)
QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }$arg
;;
@@ -585,7 +588,7 @@ ARFLAGS="${ARFLAGS-rv}"
# left shift of signed integers is well defined and has the expected
# 2s-complement style results. (Both clang and gcc agree that it
# provides these semantics.)
-QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv $QEMU_CFLAGS"
+QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv -std=gnu99 $QEMU_CFLAGS"
QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
@@ -1803,6 +1806,9 @@ if ! $python -c 'import sys; sys.exit(sys.version_info < (2,7))'; then
"Use --python=/path/to/python to specify a supported Python."
fi
+# Preserve python version since some functionality is dependent on it
+python_version=$($python -V 2>&1 | sed -e 's/Python\ //')
+
# Suppress writing compiled files
python="$python -B"
@@ -5930,8 +5936,12 @@ if test "$cpu" = "ppc64" -a "$targetos" != "Darwin" ; then
roms="$roms spapr-rtas"
fi
+# Only build s390-ccw bios if we're on s390x and the compiler has -march=z900
if test "$cpu" = "s390x" ; then
- roms="$roms s390-ccw"
+ write_c_skeleton
+ if compile_prog "-march=z900" ""; then
+ roms="$roms s390-ccw"
+ fi
fi
# Probe for the need for relocating the user-only binary.
@@ -6051,7 +6061,7 @@ echo "LDFLAGS $LDFLAGS"
echo "QEMU_LDFLAGS $QEMU_LDFLAGS"
echo "make $make"
echo "install $install"
-echo "python $python"
+echo "python $python ($python_version)"
if test "$slirp" = "yes" ; then
echo "smbd $smbd"
fi
@@ -7006,6 +7016,7 @@ echo "INSTALL_DATA=$install -c -m 0644" >> $config_host_mak
echo "INSTALL_PROG=$install -c -m 0755" >> $config_host_mak
echo "INSTALL_LIB=$install -c -m 0644" >> $config_host_mak
echo "PYTHON=$python" >> $config_host_mak
+echo "PYTHON_VERSION=$python_version" >> $config_host_mak
echo "CC=$cc" >> $config_host_mak
if $iasl -h > /dev/null 2>&1; then
echo "IASL=$iasl" >> $config_host_mak
diff --git a/contrib/rdmacm-mux/Makefile.objs b/contrib/rdmacm-mux/Makefile.objs
index be3eacb6f7..3df744af89 100644
--- a/contrib/rdmacm-mux/Makefile.objs
+++ b/contrib/rdmacm-mux/Makefile.objs
@@ -1,4 +1,3 @@
ifdef CONFIG_PVRDMA
-CFLAGS += -libumad -Wno-format-truncation
rdmacm-mux-obj-y = main.o
endif
diff --git a/contrib/rdmacm-mux/main.c b/contrib/rdmacm-mux/main.c
index 835a7f9214..ae88c77a1e 100644
--- a/contrib/rdmacm-mux/main.c
+++ b/contrib/rdmacm-mux/main.c
@@ -42,6 +42,8 @@
/* The below can be override by command line parameter */
#define UNIX_SOCKET_PATH "/var/run/rdmacm-mux"
+/* Has format %s-%s-%d" <path>-<rdma-dev--name>-<port> */
+#define SOCKET_PATH_MAX (PATH_MAX - NAME_MAX - sizeof(int) - 2)
#define RDMA_PORT_NUM 1
typedef struct RdmaCmServerArgs {
@@ -95,7 +97,7 @@ static void help(const char *progname)
static void parse_args(int argc, char *argv[])
{
int c;
- char unix_socket_path[PATH_MAX];
+ char unix_socket_path[SOCKET_PATH_MAX];
strcpy(server.args.rdma_dev_name, "");
strcpy(unix_socket_path, UNIX_SOCKET_PATH);
@@ -113,7 +115,7 @@ static void parse_args(int argc, char *argv[])
case 's':
/* This is temporary, final name will build below */
- strncpy(unix_socket_path, optarg, PATH_MAX);
+ strncpy(unix_socket_path, optarg, SOCKET_PATH_MAX);
break;
case 'p':
@@ -348,7 +350,7 @@ static int get_fd(const char *mad, int *fd, __be64 *gid_ifid)
static void *umad_recv_thread_func(void *args)
{
int rc;
- RdmaCmMuxMsg msg = {0};
+ RdmaCmMuxMsg msg = {};
int fd = -2;
msg.hdr.msg_type = RDMACM_MUX_MSG_TYPE_REQ;
@@ -385,7 +387,7 @@ static void *umad_recv_thread_func(void *args)
static int read_and_process(int fd)
{
int rc;
- RdmaCmMuxMsg msg = {0};
+ RdmaCmMuxMsg msg = {};
struct umad_hdr *hdr;
uint32_t *comm_id = 0;
uint16_t attr_id;
@@ -742,7 +744,7 @@ static void signal_handler(int sig, siginfo_t *siginfo, void *context)
static int init(void)
{
int rc;
- struct sigaction sig = {0};
+ struct sigaction sig = {};
rc = init_listener();
if (rc) {
diff --git a/default-configs/virtio.mak b/default-configs/virtio.mak
index 1304849018..ecb4420e74 100644
--- a/default-configs/virtio.mak
+++ b/default-configs/virtio.mak
@@ -1,7 +1,7 @@
CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX))
CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX))
CONFIG_VIRTIO=y
-CONFIG_VIRTIO_9P=y
+CONFIG_VIRTIO_9P=$(CONFIG_VIRTFS)
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_CRYPTO=y
@@ -12,3 +12,4 @@ CONFIG_VIRTIO_RNG=y
CONFIG_SCSI=y
CONFIG_VIRTIO_SCSI=y
CONFIG_VIRTIO_SERIAL=y
+CONFIG_VIRTIO_INPUT_HOST=$(CONFIG_LINUX)
diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst
index e7658ab050..220059679a 100644
--- a/docs/devel/migration.rst
+++ b/docs/devel/migration.rst
@@ -419,8 +419,13 @@ The functions to do that are inside a vmstate definition, and are called:
This function is called before we save the state of one device.
-Example: You can look at hpet.c, that uses the three function to
-massage the state that is transferred.
+- ``int (*post_save)(void *opaque);``
+
+ This function is called after we save the state of one device
+ (even upon failure, unless the call to pre_save returned an error).
+
+Example: You can look at hpet.c, that uses the first three functions
+to massage the state that is transferred.
The ``VMSTATE_WITH_TMP`` macro may be useful when the migration
data doesn't match the stored device data well; it allows an
diff --git a/docs/pvrdma.txt b/docs/pvrdma.txt
index 5175251b47..0f0dd8a7e5 100644
--- a/docs/pvrdma.txt
+++ b/docs/pvrdma.txt
@@ -99,6 +99,9 @@ MAD layer to send and receive RDMA-CM MAD packets.
To build rdmacm-mux run
# make rdmacm-mux
+Before running the rdmacm-mux make sure that both ib_cm and rdma_cm kernel
+modules aren't loaded, otherwise the rdmacm-mux service will fail to start.
+
The application accepts 3 command line arguments and exposes a UNIX socket
to pass control and data to it.
-d rdma-device-name Name of RDMA device to register with
@@ -153,7 +156,6 @@ Ethernet function can be used for other Ethernet purposes such as IP.
specify the port to use. If not set 1 will be used.
- dev-caps-max-mr-size: The maximum size of MR.
- dev-caps-max-qp: Maximum number of QPs.
-- dev-caps-max-sge: Maximum number of SGE elements in WR.
- dev-caps-max-cq: Maximum number of CQs.
- dev-caps-max-mr: Maximum number of MRs.
- dev-caps-max-pd: Maximum number of PDs.
diff --git a/docs/specs/ivshmem-spec.txt b/docs/specs/ivshmem-spec.txt
index a1f5499796..042f7eae22 100644
--- a/docs/specs/ivshmem-spec.txt
+++ b/docs/specs/ivshmem-spec.txt
@@ -17,12 +17,16 @@ get interrupted by its peers.
There are two basic configurations:
-- Just shared memory: -device ivshmem-plain,memdev=HMB,...
+- Just shared memory:
+
+ -device ivshmem-plain,memdev=HMB,...
This uses host memory backend HMB. It should have option "share"
set.
-- Shared memory plus interrupts: -device ivshmem,chardev=CHR,vectors=N,...
+- Shared memory plus interrupts:
+
+ -device ivshmem-doorbell,chardev=CHR,vectors=N,...
An ivshmem server must already be running on the host. The device
connects to the server's UNIX domain socket via character device
diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt
index 1af82bba86..5d8c26b1ad 100644
--- a/docs/specs/tpm.txt
+++ b/docs/specs/tpm.txt
@@ -34,6 +34,25 @@ The CRB interface makes a memory mapped IO region in the area 0xfed40000 -
QEMU files related to TPM CRB interface:
- hw/tpm/tpm_crb.c
+= fw_cfg interface =
+
+The bios/firmware may read the "etc/tpm/config" fw_cfg entry for
+configuring the guest appropriately.
+
+The entry of 6 bytes has the following content, in little-endian:
+
+ #define TPM_VERSION_UNSPEC 0
+ #define TPM_VERSION_1_2 1
+ #define TPM_VERSION_2_0 2
+
+ #define TPM_PPI_VERSION_NONE 0
+ #define TPM_PPI_VERSION_1_30 1
+
+ struct FwCfgTPMConfig {
+ uint32_t tpmppi_address; /* PPI memory location */
+ uint8_t tpm_version; /* TPM version */
+ uint8_t tpmppi_version; /* PPI version */
+ };
= ACPI Interface =
@@ -57,6 +76,91 @@ URL:
https://trustedcomputinggroup.org/tcg-acpi-specification/
+== ACPI PPI Interface ==
+
+QEMU supports the Physical Presence Interface (PPI) for TPM 1.2 and TPM 2. This
+interface requires ACPI and firmware support. The specification can be found at
+the following URL:
+
+https://trustedcomputinggroup.org/resource/tcg-physical-presence-interface-specification/
+
+PPI enables a system administrator (root) to request a modification to the
+TPM upon reboot. The PPI specification defines the operation requests and the
+actions the firmware has to take. The system administrator passes the operation
+request number to the firmware through an ACPI interface which writes this
+number to a memory location that the firmware knows. Upon reboot, the firmware
+finds the number and sends commands to the the TPM. The firmware writes the TPM
+result code and the operation request number to a memory location that ACPI can
+read from and pass the result on to the administrator.
+
+The PPI specification defines a set of mandatory and optional operations for
+the firmware to implement. The ACPI interface also allows an administrator to
+list the supported operations. In QEMU the ACPI code is generated by QEMU, yet
+the firmware needs to implement support on a per-operations basis, and
+different firmwares may support a different subset. Therefore, QEMU introduces
+the virtual memory device for PPI where the firmware can indicate which
+operations it supports and ACPI can enable the ones that are supported and
+disable all others. This interface lies in main memory and has the following
+layout:
+
+ +----------+--------+--------+-------------------------------------------+
+ | Field | Length | Offset | Description |
+ +----------+--------+--------+-------------------------------------------+
+ | func | 0x100 | 0x000 | Firmware sets values for each supported |
+ | | | | operation. See defined values below. |
+ +----------+--------+--------+-------------------------------------------+
+ | ppin | 0x1 | 0x100 | SMI interrupt to use. Set by firmware. |
+ | | | | Not supported. |
+ +----------+--------+--------+-------------------------------------------+
+ | ppip | 0x4 | 0x101 | ACPI function index to pass to SMM code. |
+ | | | | Set by ACPI. Not supported. |
+ +----------+--------+--------+-------------------------------------------+
+ | pprp | 0x4 | 0x105 | Result of last executed operation. Set by |
+ | | | | firmware. See function index 5 for values.|
+ +----------+--------+--------+-------------------------------------------+
+ | pprq | 0x4 | 0x109 | Operation request number to execute. See |
+ | | | | 'Physical Presence Interface Operation |
+ | | | | Summary' tables in specs. Set by ACPI. |
+ +----------+--------+--------+-------------------------------------------+
+ | pprm | 0x4 | 0x10d | Operation request optional parameter. |
+ | | | | Values depend on operation. Set by ACPI. |
+ +----------+--------+--------+-------------------------------------------+
+ | lppr | 0x4 | 0x111 | Last executed operation request number. |
+ | | | | Copied from pprq field by firmware. |
+ +----------+--------+--------+-------------------------------------------+
+ | fret | 0x4 | 0x115 | Result code from SMM function. |
+ | | | | Not supported. |
+ +----------+--------+--------+-------------------------------------------+
+ | res1 | 0x40 | 0x119 | Reserved for future use |
+ +----------+--------+--------+-------------------------------------------+
+ | next_step| 0x1 | 0x159 | Operation to execute after reboot by |
+ | | | | firmware. Used by firmware. |
+ +----------+--------+--------+-------------------------------------------+
+ | movv | 0x1 | 0x15a | Memory overwrite variable |
+ +----------+--------+--------+-------------------------------------------+
+
+ The following values are supported for the 'func' field. They correspond
+ to the values used by ACPI function index 8.
+
+ +----------+-------------------------------------------------------------+
+ | value | Description |
+ +----------+-------------------------------------------------------------+
+ | 0 | Operation is not implemented. |
+ +----------+-------------------------------------------------------------+
+ | 1 | Operation is only accessible through firmware. |
+ +----------+-------------------------------------------------------------+
+ | 2 | Operation is blocked for OS by firmware configuration. |
+ +----------+-------------------------------------------------------------+
+ | 3 | Operation is allowed and physically present user required. |
+ +----------+-------------------------------------------------------------+
+ | 4 | Operation is allowed and physically present user is not |
+ | | required. |
+ +----------+-------------------------------------------------------------+
+
+The location of the table is given by the fw_cfg tpmppi_address field.
+The PPI memory region size is 0x400 (TPM_PPI_ADDR_SIZE) to leave
+enough room for future updates.
+
QEMU files related to TPM ACPI tables:
- hw/i386/acpi-build.c
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 11c35bcb44..2d46e3789a 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -11,6 +11,7 @@ common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
common-obj-y += acpi_interface.o
common-obj-y += bios-linker-loader.o
common-obj-y += aml-build.o
+common-obj-$(CONFIG_TPM) += tpm.o
common-obj-$(CONFIG_IPMI) += ipmi.o
common-obj-$(call lnot,$(CONFIG_IPMI)) += ipmi-stub.o
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index d6f0709691..47877c0ec1 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -35,14 +35,18 @@
struct acpi_table_header {
uint16_t _length; /* our length, not actual part of the hdr */
/* allows easier parsing for fw_cfg clients */
- char sig[4]; /* ACPI signature (4 ASCII characters) */
+ char sig[4]
+ QEMU_NONSTRING; /* ACPI signature (4 ASCII characters) */
uint32_t length; /* Length of table, in bytes, including header */
uint8_t revision; /* ACPI Specification minor version # */
uint8_t checksum; /* To make sum of entire table == 0 */
- char oem_id[6]; /* OEM identification */
- char oem_table_id[8]; /* OEM table identification */
+ char oem_id[6]
+ QEMU_NONSTRING; /* OEM identification */
+ char oem_table_id[8]
+ QEMU_NONSTRING; /* OEM table identification */
uint32_t oem_revision; /* OEM revision number */
- char asl_compiler_id[4]; /* ASL compiler vendor ID */
+ char asl_compiler_id[4]
+ QEMU_NONSTRING; /* ASL compiler vendor ID */
uint32_t asl_compiler_revision; /* ASL compiler revision number */
} QEMU_PACKED;
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index 8c7c1013f3..921cad2c5e 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -686,15 +686,15 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
method = aml_method("_OST", 3, AML_NOTSERIALIZED);
s = MEMORY_SLOT_OST_METHOD;
- aml_append(method, aml_return(aml_call4(
- s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2)
- )));
+ aml_append(method,
+ aml_call4(s, aml_name("_UID"), aml_arg(0),
+ aml_arg(1), aml_arg(2)));
aml_append(dev, method);
method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
s = MEMORY_SLOT_EJECT_METHOD;
- aml_append(method, aml_return(aml_call2(
- s, aml_name("_UID"), aml_arg(0))));
+ aml_append(method,
+ aml_call2(s, aml_name("_UID"), aml_arg(0)));
aml_append(dev, method);
aml_append(dev_container, dev);
diff --git a/hw/acpi/tpm.c b/hw/acpi/tpm.c
new file mode 100644
index 0000000000..b96459e45b
--- /dev/null
+++ b/hw/acpi/tpm.c
@@ -0,0 +1,459 @@
+/* Support for generating ACPI TPM tables
+ *
+ * Copyright (C) 2018 IBM, Corp.
+ * Copyright (C) 2018 Red Hat Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/acpi/tpm.h"
+
+void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev)
+{
+ Aml *method, *field, *ifctx, *ifctx2, *ifctx3, *func_mask,
+ *not_implemented, *pak, *tpm2, *tpm3, *pprm, *pprq, *zero, *one;
+
+ if (!object_property_get_bool(OBJECT(tpm), "ppi", &error_abort)) {
+ return;
+ }
+
+ zero = aml_int(0);
+ one = aml_int(1);
+ func_mask = aml_int(TPM_PPI_FUNC_MASK);
+ not_implemented = aml_int(TPM_PPI_FUNC_NOT_IMPLEMENTED);
+
+ /*
+ * TPP2 is for the registers that ACPI code used to pass
+ * the PPI code and parameter (PPRQ, PPRM) to the firmware.
+ */
+ aml_append(dev,
+ aml_operation_region("TPP2", AML_SYSTEM_MEMORY,
+ aml_int(TPM_PPI_ADDR_BASE + 0x100),
+ 0x5A));
+ field = aml_field("TPP2", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("PPIN", 8));
+ aml_append(field, aml_named_field("PPIP", 32));
+ aml_append(field, aml_named_field("PPRP", 32));
+ aml_append(field, aml_named_field("PPRQ", 32));
+ aml_append(field, aml_named_field("PPRM", 32));
+ aml_append(field, aml_named_field("LPPR", 32));
+ aml_append(dev, field);
+ pprq = aml_name("PPRQ");
+ pprm = aml_name("PPRM");
+
+ aml_append(dev,
+ aml_operation_region(
+ "TPP3", AML_SYSTEM_MEMORY,
+ aml_int(TPM_PPI_ADDR_BASE +
+ 0x15a /* movv, docs/specs/tpm.txt */),
+ 0x1));
+ field = aml_field("TPP3", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("MOVV", 8));
+ aml_append(dev, field);
+
+ /*
+ * DerefOf in Windows is broken with SYSTEM_MEMORY. Use a dynamic
+ * operation region inside of a method for getting FUNC[op].
+ */
+ method = aml_method("TPFN", 1, AML_SERIALIZED);
+ {
+ Aml *op = aml_arg(0);
+ ifctx = aml_if(aml_lgreater_equal(op, aml_int(0x100)));
+ {
+ aml_append(ifctx, aml_return(zero));
+ }
+ aml_append(method, ifctx);
+
+ aml_append(method,
+ aml_operation_region("TPP1", AML_SYSTEM_MEMORY,
+ aml_add(aml_int(TPM_PPI_ADDR_BASE), op, NULL), 0x1));
+ field = aml_field("TPP1", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("TPPF", 8));
+ aml_append(method, field);
+ aml_append(method, aml_return(aml_name("TPPF")));
+ }
+ aml_append(dev, method);
+
+ /*
+ * Use global TPM2 & TPM3 variables to workaround Windows ACPI bug
+ * when returning packages.
+ */
+ pak = aml_package(2);
+ aml_append(pak, zero);
+ aml_append(pak, zero);
+ aml_append(dev, aml_name_decl("TPM2", pak));
+ tpm2 = aml_name("TPM2");
+
+ pak = aml_package(3);
+ aml_append(pak, zero);
+ aml_append(pak, zero);
+ aml_append(pak, zero);
+ aml_append(dev, aml_name_decl("TPM3", pak));
+ tpm3 = aml_name("TPM3");
+
+ method = aml_method("_DSM", 4, AML_SERIALIZED);
+ {
+ uint8_t zerobyte[1] = { 0 };
+ Aml *function, *arguments, *rev, *op, *op_arg, *op_flags, *uuid;
+
+ uuid = aml_arg(0);
+ rev = aml_arg(1);
+ function = aml_arg(2);
+ arguments = aml_arg(3);
+ op = aml_local(0);
+ op_flags = aml_local(1);
+
+ /* Physical Presence Interface */
+ ifctx = aml_if(
+ aml_equal(uuid,
+ aml_touuid("3DDDFAA6-361B-4EB4-A424-8D10089D1653")));
+ {
+ /* standard DSM query function */
+ ifctx2 = aml_if(aml_equal(function, zero));
+ {
+ uint8_t byte_list[2] = { 0xff, 0x01 }; /* functions 1-8 */
+
+ aml_append(ifctx2,
+ aml_return(aml_buffer(sizeof(byte_list),
+ byte_list)));
+ }
+ aml_append(ifctx, ifctx2);
+
+ /*
+ * PPI 1.0: 2.1.1 Get Physical Presence Interface Version
+ *
+ * Arg 2 (Integer): Function Index = 1
+ * Arg 3 (Package): Arguments = Empty Package
+ * Returns: Type: String
+ */
+ ifctx2 = aml_if(aml_equal(function, one));
+ {
+ aml_append(ifctx2, aml_return(aml_string("1.3")));
+ }
+ aml_append(ifctx, ifctx2);
+
+ /*
+ * PPI 1.0: 2.1.3 Submit TPM Operation Request to Pre-OS Environment
+ *
+ * Arg 2 (Integer): Function Index = 2
+ * Arg 3 (Package): Arguments = Package: Type: Integer
+ * Operation Value of the Request
+ * Returns: Type: Integer
+ * 0: Success
+ * 1: Operation Value of the Request Not Supported
+ * 2: General Failure
+ */
+ ifctx2 = aml_if(aml_equal(function, aml_int(2)));
+ {
+ /* get opcode */
+ aml_append(ifctx2,
+ aml_store(aml_derefof(aml_index(arguments,
+ zero)), op));
+
+ /* get opcode flags */
+ aml_append(ifctx2,
+ aml_store(aml_call1("TPFN", op), op_flags));
+
+ /* if func[opcode] & TPM_PPI_FUNC_NOT_IMPLEMENTED */
+ ifctx3 = aml_if(
+ aml_equal(
+ aml_and(op_flags, func_mask, NULL),
+ not_implemented));
+ {
+ /* 1: Operation Value of the Request Not Supported */
+ aml_append(ifctx3, aml_return(one));
+ }
+ aml_append(ifctx2, ifctx3);
+
+ aml_append(ifctx2, aml_store(op, pprq));
+ aml_append(ifctx2, aml_store(zero, pprm));
+ /* 0: success */
+ aml_append(ifctx2, aml_return(zero));
+ }
+ aml_append(ifctx, ifctx2);
+
+ /*
+ * PPI 1.0: 2.1.4 Get Pending TPM Operation Requested By the OS
+ *
+ * Arg 2 (Integer): Function Index = 3
+ * Arg 3 (Package): Arguments = Empty Package
+ * Returns: Type: Package of Integers
+ * Integer 1: Function Return code
+ * 0: Success
+ * 1: General Failure
+ * Integer 2: Pending operation requested by the OS
+ * 0: None
+ * >0: Operation Value of the Pending Request
+ * Integer 3: Optional argument to pending operation
+ * requested by the OS
+ * 0: None
+ * >0: Argument Value of the Pending Request
+ */
+ ifctx2 = aml_if(aml_equal(function, aml_int(3)));
+ {
+ /*
+ * Revision ID of 1, no integer parameter beyond
+ * parameter two are expected
+ */
+ ifctx3 = aml_if(aml_equal(rev, one));
+ {
+ /* TPM2[1] = PPRQ */
+ aml_append(ifctx3,
+ aml_store(pprq, aml_index(tpm2, one)));
+ aml_append(ifctx3, aml_return(tpm2));
+ }
+ aml_append(ifctx2, ifctx3);
+
+ /*
+ * A return value of {0, 23, 1} indicates that
+ * operation 23 with argument 1 is pending.
+ */
+ ifctx3 = aml_if(aml_equal(rev, aml_int(2)));
+ {
+ /* TPM3[1] = PPRQ */
+ aml_append(ifctx3,
+ aml_store(pprq, aml_index(tpm3, one)));
+ /* TPM3[2] = PPRM */
+ aml_append(ifctx3,
+ aml_store(pprm, aml_index(tpm3, aml_int(2))));
+ aml_append(ifctx3, aml_return(tpm3));
+ }
+ aml_append(ifctx2, ifctx3);
+ }
+ aml_append(ifctx, ifctx2);
+
+ /*
+ * PPI 1.0: 2.1.5 Get Platform-Specific Action to Transition to
+ * Pre-OS Environment
+ *
+ * Arg 2 (Integer): Function Index = 4
+ * Arg 3 (Package): Arguments = Empty Package
+ * Returns: Type: Integer
+ * 0: None
+ * 1: Shutdown
+ * 2: Reboot
+ * 3: OS Vendor-specific
+ */
+ ifctx2 = aml_if(aml_equal(function, aml_int(4)));
+ {
+ /* reboot */
+ aml_append(ifctx2, aml_return(aml_int(2)));
+ }
+ aml_append(ifctx, ifctx2);
+
+ /*
+ * PPI 1.0: 2.1.6 Return TPM Operation Response to OS Environment
+ *
+ * Arg 2 (Integer): Function Index = 5
+ * Arg 3 (Package): Arguments = Empty Package
+ * Returns: Type: Package of Integer
+ * Integer 1: Function Return code
+ * 0: Success
+ * 1: General Failure
+ * Integer 2: Most recent operation request
+ * 0: None
+ * >0: Operation Value of the most recent request
+ * Integer 3: Response to the most recent operation request
+ * 0: Success
+ * 0x00000001..0x00000FFF: Corresponding TPM
+ * error code
+ * 0xFFFFFFF0: User Abort or timeout of dialog
+ * 0xFFFFFFF1: firmware Failure
+ */
+ ifctx2 = aml_if(aml_equal(function, aml_int(5)));
+ {
+ /* TPM3[1] = LPPR */
+ aml_append(ifctx2,
+ aml_store(aml_name("LPPR"),
+ aml_index(tpm3, one)));
+ /* TPM3[2] = PPRP */
+ aml_append(ifctx2,
+ aml_store(aml_name("PPRP"),
+ aml_index(tpm3, aml_int(2))));
+ aml_append(ifctx2, aml_return(tpm3));
+ }
+ aml_append(ifctx, ifctx2);
+
+ /*
+ * PPI 1.0: 2.1.7 Submit preferred user language
+ *
+ * Arg 2 (Integer): Function Index = 6
+ * Arg 3 (Package): Arguments = String Package
+ * Preferred language code
+ * Returns: Type: Integer
+ * Function Return Code
+ * 3: Not implemented
+ */
+ ifctx2 = aml_if(aml_equal(function, aml_int(6)));
+ {
+ /* 3 = not implemented */
+ aml_append(ifctx2, aml_return(aml_int(3)));
+ }
+ aml_append(ifctx, ifctx2);
+
+ /*
+ * PPI 1.1: 2.1.7 Submit TPM Operation Request to
+ * Pre-OS Environment 2
+ *
+ * Arg 2 (Integer): Function Index = 7
+ * Arg 3 (Package): Arguments = Package: Type: Integer
+ * Integer 1: Operation Value of the Request
+ * Integer 2: Argument for Operation (optional)
+ * Returns: Type: Integer
+ * 0: Success
+ * 1: Not Implemented
+ * 2: General Failure
+ * 3: Operation blocked by current firmware settings
+ */
+ ifctx2 = aml_if(aml_equal(function, aml_int(7)));
+ {
+ /* get opcode */
+ aml_append(ifctx2, aml_store(aml_derefof(aml_index(arguments,
+ zero)),
+ op));
+
+ /* get opcode flags */
+ aml_append(ifctx2, aml_store(aml_call1("TPFN", op),
+ op_flags));
+ /* if func[opcode] & TPM_PPI_FUNC_NOT_IMPLEMENTED */
+ ifctx3 = aml_if(
+ aml_equal(
+ aml_and(op_flags, func_mask, NULL),
+ not_implemented));
+ {
+ /* 1: not implemented */
+ aml_append(ifctx3, aml_return(one));
+ }
+ aml_append(ifctx2, ifctx3);
+
+ /* if func[opcode] & TPM_PPI_FUNC_BLOCKED */
+ ifctx3 = aml_if(
+ aml_equal(
+ aml_and(op_flags, func_mask, NULL),
+ aml_int(TPM_PPI_FUNC_BLOCKED)));
+ {
+ /* 3: blocked by firmware */
+ aml_append(ifctx3, aml_return(aml_int(3)));
+ }
+ aml_append(ifctx2, ifctx3);
+
+ /* revision to integer */
+ ifctx3 = aml_if(aml_equal(rev, one));
+ {
+ /* revision 1 */
+ /* PPRQ = op */
+ aml_append(ifctx3, aml_store(op, pprq));
+ /* no argument, PPRM = 0 */
+ aml_append(ifctx3, aml_store(zero, pprm));
+ }
+ aml_append(ifctx2, ifctx3);
+
+ ifctx3 = aml_if(aml_equal(rev, aml_int(2)));
+ {
+ /* revision 2 */
+ /* PPRQ = op */
+ op_arg = aml_derefof(aml_index(arguments, one));
+ aml_append(ifctx3, aml_store(op, pprq));
+ /* PPRM = arg3[1] */
+ aml_append(ifctx3, aml_store(op_arg, pprm));
+ }
+ aml_append(ifctx2, ifctx3);
+ /* 0: success */
+ aml_append(ifctx2, aml_return(zero));
+ }
+ aml_append(ifctx, ifctx2);
+
+ /*
+ * PPI 1.1: 2.1.8 Get User Confirmation Status for Operation
+ *
+ * Arg 2 (Integer): Function Index = 8
+ * Arg 3 (Package): Arguments = Package: Type: Integer
+ * Operation Value that may need user confirmation
+ * Returns: Type: Integer
+ * 0: Not implemented
+ * 1: Firmware only
+ * 2: Blocked for OS by firmware configuration
+ * 3: Allowed and physically present user required
+ * 4: Allowed and physically present user not required
+ */
+ ifctx2 = aml_if(aml_equal(function, aml_int(8)));
+ {
+ /* get opcode */
+ aml_append(ifctx2,
+ aml_store(aml_derefof(aml_index(arguments,
+ zero)),
+ op));
+
+ /* get opcode flags */
+ aml_append(ifctx2, aml_store(aml_call1("TPFN", op),
+ op_flags));
+ /* return confirmation status code */
+ aml_append(ifctx2,
+ aml_return(
+ aml_and(op_flags, func_mask, NULL)));
+ }
+ aml_append(ifctx, ifctx2);
+
+ aml_append(ifctx, aml_return(aml_buffer(1, zerobyte)));
+ }
+ aml_append(method, ifctx);
+
+ /*
+ * "TCG Platform Reset Attack Mitigation Specification 1.00",
+ * Chapter 6 "ACPI _DSM Function"
+ */
+ ifctx = aml_if(
+ aml_equal(uuid,
+ aml_touuid("376054ED-CC13-4675-901C-4756D7F2D45D")));
+ {
+ /* standard DSM query function */
+ ifctx2 = aml_if(aml_equal(function, zero));
+ {
+ uint8_t byte_list[1] = { 0x03 }; /* functions 1-2 supported */
+
+ aml_append(ifctx2,
+ aml_return(aml_buffer(sizeof(byte_list),
+ byte_list)));
+ }
+ aml_append(ifctx, ifctx2);
+
+ /*
+ * TCG Platform Reset Attack Mitigation Specification 1.0 Ch.6
+ *
+ * Arg 2 (Integer): Function Index = 1
+ * Arg 3 (Package): Arguments = Package: Type: Integer
+ * Operation Value of the Request
+ * Returns: Type: Integer
+ * 0: Success
+ * 1: General Failure
+ */
+ ifctx2 = aml_if(aml_equal(function, one));
+ {
+ aml_append(ifctx2,
+ aml_store(aml_derefof(aml_index(arguments, zero)),
+ op));
+ {
+ aml_append(ifctx2, aml_store(op, aml_name("MOVV")));
+
+ /* 0: success */
+ aml_append(ifctx2, aml_return(zero));
+ }
+ }
+ aml_append(ifctx, ifctx2);
+ }
+ aml_append(method, ifctx);
+ }
+ aml_append(dev, method);
+}
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 95fad6f0ce..04b62c714d 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -418,6 +418,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
smmu->mapping_count = cpu_to_le32(1);
smmu->mapping_offset = cpu_to_le32(sizeof(*smmu));
smmu->base_address = cpu_to_le64(vms->memmap[VIRT_SMMU].base);
+ smmu->flags = cpu_to_le32(ACPI_IORT_SMMU_V3_COHACC_OVERRIDE);
smmu->event_gsiv = cpu_to_le32(irq);
smmu->pri_gsiv = cpu_to_le32(irq + 1);
smmu->gerr_gsiv = cpu_to_le32(irq + 2);
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index 1451940845..c3af28fad4 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -250,6 +250,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBlk *s = VHOST_USER_BLK(vdev);
VhostUserState *user;
+ struct vhost_virtqueue *vqs = NULL;
int i, ret;
if (!s->chardev.chr) {
@@ -288,6 +289,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs);
s->dev.vq_index = 0;
s->dev.backend_features = 0;
+ vqs = s->dev.vqs;
vhost_dev_set_config_notifier(&s->dev, &blk_ops);
@@ -314,7 +316,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
vhost_err:
vhost_dev_cleanup(&s->dev);
virtio_err:
- g_free(s->dev.vqs);
+ g_free(vqs);
virtio_cleanup(vdev);
vhost_user_cleanup(user);
@@ -326,10 +328,11 @@ static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBlk *s = VHOST_USER_BLK(dev);
+ struct vhost_virtqueue *vqs = s->dev.vqs;
vhost_user_blk_set_status(vdev, 0);
vhost_dev_cleanup(&s->dev);
- g_free(s->dev.vqs);
+ g_free(vqs);
virtio_cleanup(vdev);
if (s->vhost_user) {
diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c
index be28b63442..a636487b3e 100644
--- a/hw/block/xen-block.c
+++ b/hw/block/xen-block.c
@@ -215,7 +215,7 @@ static void xen_block_realize(XenDevice *xendev, Error **errp)
xen_device_backend_printf(xendev, "sector-size", "%u",
conf->logical_block_size);
- xen_device_backend_printf(xendev, "sectors", "%lu",
+ xen_device_backend_printf(xendev, "sectors", "%"PRIi64,
blk_getlength(conf->blk) /
conf->logical_block_size);
diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index f3363a2952..10392c70e2 100644
--- a/hw/char/stm32f2xx_usart.c
+++ b/hw/char/stm32f2xx_usart.c
@@ -53,14 +53,13 @@ static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size)
{
STM32F2XXUsartState *s = opaque;
- s->usart_dr = *buf;
-
if (!(s->usart_cr1 & USART_CR1_UE && s->usart_cr1 & USART_CR1_RE)) {
/* USART not enabled - drop the chars */
DB_PRINT("Dropping the chars\n");
return;
}
+ s->usart_dr = *buf;
s->usart_sr |= USART_SR_RXNE;
if (s->usart_cr1 & USART_CR1_RXNEIE) {
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 95dc7c3913..2629515363 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -28,6 +28,8 @@ GlobalProperty hw_compat_3_1[] = {
{ "pcie-root-port", "x-width", "1" },
{ "memory-backend-file", "x-use-canonical-path-for-ramblock-id", "true" },
{ "memory-backend-memfd", "x-use-canonical-path-for-ramblock-id", "true" },
+ { "tpm-crb", "ppi", "false" },
+ { "tpm-tis", "ppi", "false" },
};
const size_t hw_compat_3_1_len = G_N_ELEMENTS(hw_compat_3_1);
@@ -91,8 +93,9 @@ const size_t hw_compat_2_7_len = G_N_ELEMENTS(hw_compat_2_7);
GlobalProperty hw_compat_2_6[] = {
{ "virtio-mmio", "format_transport_address", "off" },
- { "virtio-pci", "disable-modern", "on" },
- { "virtio-pci", "disable-legacy", "off" },
+ /* Optional because not all virtio-pci devices support legacy mode */
+ { "virtio-pci", "disable-modern", "on", .optional = true },
+ { "virtio-pci", "disable-legacy", "off", .optional = true },
};
const size_t hw_compat_2_6_len = G_N_ELEMENTS(hw_compat_2_6);
diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
index faf76a8bc4..bdcd33c925 100644
--- a/hw/display/virtio-gpu-pci.c
+++ b/hw/display/virtio-gpu-pci.c
@@ -19,6 +19,20 @@
#include "hw/virtio/virtio-pci.h"
#include "hw/virtio/virtio-gpu.h"
+typedef struct VirtIOGPUPCI VirtIOGPUPCI;
+
+/*
+ * virtio-gpu-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
+#define VIRTIO_GPU_PCI(obj) \
+ OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI)
+
+struct VirtIOGPUPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOGPU vdev;
+};
+
static Property virtio_gpu_pci_properties[] = {
DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index 8db4d916f2..1e48009b74 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -3,6 +3,7 @@
#include "hw/pci/pci.h"
#include "vga_int.h"
#include "hw/virtio/virtio-pci.h"
+#include "hw/virtio/virtio-gpu.h"
#include "qapi/error.h"
/*
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 14f757fc36..2e21a31f82 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -119,6 +119,12 @@ typedef struct AcpiBuildPciBusHotplugState {
bool pcihp_bridge_en;
} AcpiBuildPciBusHotplugState;
+typedef struct FwCfgTPMConfig {
+ uint32_t tpmppi_address;
+ uint8_t tpm_version;
+ uint8_t tpmppi_version;
+} QEMU_PACKED FwCfgTPMConfig;
+
static void init_common_fadt_data(Object *o, AcpiFadtData *data)
{
uint32_t io = object_property_get_uint(o, ACPI_PM_PROP_PM_IO_BASE, NULL);
@@ -1796,6 +1802,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
uint32_t nr_mem = machine->ram_slots;
int root_bus_limit = 0xFF;
PCIBus *bus = NULL;
+ TPMIf *tpm = tpm_find();
int i;
dsdt = init_aml_allocator();
@@ -2133,7 +2140,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
/* Scan all PCI buses. Generate tables to support hotplug. */
build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en);
- if (TPM_IS_TIS(tpm_find())) {
+ if (TPM_IS_TIS(tpm)) {
dev = aml_device("ISA.TPM");
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31")));
aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
@@ -2147,6 +2154,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
*/
/* aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ)); */
aml_append(dev, aml_name_decl("_CRS", crs));
+
+ tpm_build_ppi_acpi(tpm, dev);
+
aml_append(scope, dev);
}
@@ -2154,7 +2164,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
}
}
- if (TPM_IS_CRB(tpm_find())) {
+ if (TPM_IS_CRB(tpm)) {
dev = aml_device("TPM");
aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
crs = aml_resource_template();
@@ -2166,6 +2176,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
aml_append(method, aml_return(aml_int(0x0f)));
aml_append(dev, method);
+ tpm_build_ppi_acpi(tpm, dev);
+
aml_append(sb_scope, dev);
}
@@ -2847,6 +2859,8 @@ void acpi_setup(void)
AcpiBuildTables tables;
AcpiBuildState *build_state;
Object *vmgenid_dev;
+ TPMIf *tpm;
+ static FwCfgTPMConfig tpm_config;
if (!pcms->fw_cfg) {
ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
@@ -2881,6 +2895,17 @@ void acpi_setup(void)
fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
tables.tcpalog->data, acpi_data_len(tables.tcpalog));
+ tpm = tpm_find();
+ if (tpm && object_property_get_bool(OBJECT(tpm), "ppi", &error_abort)) {
+ tpm_config = (FwCfgTPMConfig) {
+ .tpmppi_address = cpu_to_le32(TPM_PPI_ADDR_BASE),
+ .tpm_version = tpm_get_version(tpm),
+ .tpmppi_version = TPM_PPI_VERSION_1_30
+ };
+ fw_cfg_add_file(pcms->fw_cfg, "etc/tpm/config",
+ &tpm_config, sizeof tpm_config);
+ }
+
vmgenid_dev = find_vmgenid_dev();
if (vmgenid_dev) {
vmgenid_add_fw_cfg(VMGENID(vmgenid_dev), pcms->fw_cfg,
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 5088e2f492..63c84e3827 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -715,7 +715,6 @@ static void pc_i440fx_1_2_machine_options(MachineClass *m)
PC_CPU_MODEL_IDS("1.2.0")
{ "nec-usb-xhci", "msi", "off" },
{ "nec-usb-xhci", "msix", "off" },
- { "ivshmem", "use64", "0" },
{ "qxl", "revision", "3" },
{ "qxl-vga", "revision", "3" },
{ "VGA", "mmio", "off" },
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
index ac94594b19..dff1330050 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -34,6 +34,7 @@
#include "sysemu/kvm.h"
#include "hw/ppc/spapr.h"
#include "hw/ppc/xics.h"
+#include "hw/ppc/xics_spapr.h"
#include "kvm_ppc.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
index 9c1a90d709..de6cc15b64 100644
--- a/hw/intc/xics_spapr.c
+++ b/hw/intc/xics_spapr.c
@@ -32,6 +32,7 @@
#include "qemu/timer.h"
#include "hw/ppc/spapr.h"
#include "hw/ppc/xics.h"
+#include "hw/ppc/xics_spapr.h"
#include "hw/ppc/fdt.h"
#include "qapi/visitor.h"
diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
index 5cf7b84c79..b9f0b0d06e 100644
--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
+++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
@@ -35,6 +35,7 @@
#include "sysemu/sysemu.h"
#include "hw/devices.h"
#include "hw/boards.h"
+#include "hw/misc/unimp.h"
#include "exec/address-spaces.h"
#include "hw/char/xilinx_uartlite.h"
@@ -47,6 +48,7 @@
#define MEMORY_BASEADDR 0x90000000
#define FLASH_BASEADDR 0xa0000000
+#define GPIO_BASEADDR 0x81400000
#define INTC_BASEADDR 0x81800000
#define TIMER_BASEADDR 0x83c00000
#define UARTLITE_BASEADDR 0x84000000
@@ -122,6 +124,8 @@ petalogix_s3adsp1800_init(MachineState *machine)
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]);
+ create_unimplemented_device("gpio", GPIO_BASEADDR, 0x10000);
+
microblaze_load_kernel(cpu, ddr_base, ram_size,
machine->initrd_filename,
BINARY_DEVICE_TREE_FILE,
diff --git a/hw/mips/cps.c b/hw/mips/cps.c
index 4285d1964e..fc97f59af4 100644
--- a/hw/mips/cps.c
+++ b/hw/mips/cps.c
@@ -69,6 +69,7 @@ static void mips_cps_realize(DeviceState *dev, Error **errp)
Error *err = NULL;
target_ulong gcr_base;
bool itu_present = false;
+ bool saar_present = false;
for (i = 0; i < s->num_vp; i++) {
cpu = MIPS_CPU(cpu_create(s->cpu_type));
@@ -82,12 +83,14 @@ static void mips_cps_realize(DeviceState *dev, Error **errp)
itu_present = true;
/* Attach ITC Tag to the VP */
env->itc_tag = mips_itu_get_tag_region(&s->itu);
+ env->itu = &s->itu;
}
qemu_register_reset(main_cpu_reset, cpu);
}
cpu = MIPS_CPU(first_cpu);
env = &cpu->env;
+ saar_present = (bool)env->saarp;
/* Inter-Thread Communication Unit */
if (itu_present) {
@@ -96,6 +99,11 @@ static void mips_cps_realize(DeviceState *dev, Error **errp)
object_property_set_int(OBJECT(&s->itu), 16, "num-fifo", &err);
object_property_set_int(OBJECT(&s->itu), 16, "num-semaphores", &err);
+ object_property_set_bool(OBJECT(&s->itu), saar_present, "saar-present",
+ &err);
+ if (saar_present) {
+ qdev_prop_set_ptr(DEVICE(&s->itu), "saar", (void *)&env->CP0_SAAR);
+ }
object_property_set_bool(OBJECT(&s->itu), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
diff --git a/hw/misc/edu.c b/hw/misc/edu.c
index cdcf550dd7..ceaf688bfb 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -377,6 +377,7 @@ static void pci_edu_uninit(PCIDevice *pdev)
qemu_mutex_destroy(&edu->thr_mutex);
timer_del(&edu->dma_timer);
+ msi_uninit(pdev);
}
static void edu_obj_uint64(Object *obj, Visitor *v, const char *name,
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index 8213659602..c7b6bbc974 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -112,13 +112,6 @@ typedef struct IVShmemState {
/* migration stuff */
OnOffAuto master;
Error *migration_blocker;
-
- /* legacy cruft */
- char *role;
- char *shmobj;
- char *sizearg;
- size_t legacy_size;
- uint32_t not_legacy_32bit;
} IVShmemState;
/* registers for the Inter-VM shared memory device */
@@ -529,17 +522,6 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
size = buf.st_size;
- /* Legacy cruft */
- if (s->legacy_size != SIZE_MAX) {
- if (size < s->legacy_size) {
- error_setg(errp, "server sent only %zd bytes of shared memory",
- (size_t)buf.st_size);
- close(fd);
- return;
- }
- size = s->legacy_size;
- }
-
/* mmap the region and map into the BAR2 */
memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s),
"ivshmem.bar2", size, true, fd, &local_err);
@@ -882,8 +864,6 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
IVShmemState *s = IVSHMEM_COMMON(dev);
Error *err = NULL;
uint8_t *pci_conf;
- uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
- PCI_BASE_ADDRESS_MEM_PREFETCH;
Error *local_err = NULL;
/* IRQFD requires MSI */
@@ -903,10 +883,6 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
&s->ivshmem_mmio);
- if (s->not_legacy_32bit) {
- attr |= PCI_BASE_ADDRESS_MEM_TYPE_64;
- }
-
if (s->hostmem != NULL) {
IVSHMEM_DPRINTF("using hostmem\n");
@@ -964,7 +940,11 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
}
vmstate_register_ram(s->ivshmem_bar2, DEVICE(s));
- pci_register_bar(PCI_DEVICE(s), 2, attr, s->ivshmem_bar2);
+ pci_register_bar(PCI_DEVICE(s), 2,
+ PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH |
+ PCI_BASE_ADDRESS_MEM_TYPE_64,
+ s->ivshmem_bar2);
}
static void ivshmem_exit(PCIDevice *dev)
@@ -1084,13 +1064,6 @@ static Property ivshmem_plain_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
-static void ivshmem_plain_init(Object *obj)
-{
- IVShmemState *s = IVSHMEM_PLAIN(obj);
-
- s->not_legacy_32bit = 1;
-}
-
static void ivshmem_plain_realize(PCIDevice *dev, Error **errp)
{
IVShmemState *s = IVSHMEM_COMMON(dev);
@@ -1122,7 +1095,6 @@ static const TypeInfo ivshmem_plain_info = {
.name = TYPE_IVSHMEM_PLAIN,
.parent = TYPE_IVSHMEM_COMMON,
.instance_size = sizeof(IVShmemState),
- .instance_init = ivshmem_plain_init,
.class_init = ivshmem_plain_class_init,
};
@@ -1155,8 +1127,6 @@ static void ivshmem_doorbell_init(Object *obj)
IVShmemState *s = IVSHMEM_DOORBELL(obj);
s->features |= (1 << IVSHMEM_MSI);
- s->legacy_size = SIZE_MAX; /* whatever the server sends */
- s->not_legacy_32bit = 1;
}
static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp)
@@ -1189,181 +1159,11 @@ static const TypeInfo ivshmem_doorbell_info = {
.class_init = ivshmem_doorbell_class_init,
};
-static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id)
-{
- IVShmemState *s = opaque;
- PCIDevice *pdev = PCI_DEVICE(s);
- int ret;
-
- IVSHMEM_DPRINTF("ivshmem_load_old\n");
-
- if (version_id != 0) {
- return -EINVAL;
- }
-
- ret = ivshmem_pre_load(s);
- if (ret) {
- return ret;
- }
-
- ret = pci_device_load(pdev, f);
- if (ret) {
- return ret;
- }
-
- if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
- msix_load(pdev, f);
- ivshmem_msix_vector_use(s);
- } else {
- s->intrstatus = qemu_get_be32(f);
- s->intrmask = qemu_get_be32(f);
- }
-
- return 0;
-}
-
-static bool test_msix(void *opaque, int version_id)
-{
- IVShmemState *s = opaque;
-
- return ivshmem_has_feature(s, IVSHMEM_MSI);
-}
-
-static bool test_no_msix(void *opaque, int version_id)
-{
- return !test_msix(opaque, version_id);
-}
-
-static const VMStateDescription ivshmem_vmsd = {
- .name = "ivshmem",
- .version_id = 1,
- .minimum_version_id = 1,
- .pre_load = ivshmem_pre_load,
- .post_load = ivshmem_post_load,
- .fields = (VMStateField[]) {
- VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
-
- VMSTATE_MSIX_TEST(parent_obj, IVShmemState, test_msix),
- VMSTATE_UINT32_TEST(intrstatus, IVShmemState, test_no_msix),
- VMSTATE_UINT32_TEST(intrmask, IVShmemState, test_no_msix),
-
- VMSTATE_END_OF_LIST()
- },
- .load_state_old = ivshmem_load_old,
- .minimum_version_id_old = 0
-};
-
-static Property ivshmem_properties[] = {
- DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
- DEFINE_PROP_STRING("size", IVShmemState, sizearg),
- DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
- DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD,
- false),
- DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true),
- DEFINE_PROP_STRING("shm", IVShmemState, shmobj),
- DEFINE_PROP_STRING("role", IVShmemState, role),
- DEFINE_PROP_UINT32("use64", IVShmemState, not_legacy_32bit, 1),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void desugar_shm(IVShmemState *s)
-{
- Object *obj;
- char *path;
-
- obj = object_new("memory-backend-file");
- path = g_strdup_printf("/dev/shm/%s", s->shmobj);
- object_property_set_str(obj, path, "mem-path", &error_abort);
- g_free(path);
- object_property_set_int(obj, s->legacy_size, "size", &error_abort);
- object_property_set_bool(obj, true, "share", &error_abort);
- object_property_add_child(OBJECT(s), "internal-shm-backend", obj,
- &error_abort);
- object_unref(obj);
- user_creatable_complete(USER_CREATABLE(obj), &error_abort);
- s->hostmem = MEMORY_BACKEND(obj);
-}
-
-static void ivshmem_realize(PCIDevice *dev, Error **errp)
-{
- IVShmemState *s = IVSHMEM_COMMON(dev);
-
- if (!qtest_enabled()) {
- warn_report("ivshmem is deprecated, please use ivshmem-plain"
- " or ivshmem-doorbell instead");
- }
-
- if (qemu_chr_fe_backend_connected(&s->server_chr) + !!s->shmobj != 1) {
- error_setg(errp, "You must specify either 'shm' or 'chardev'");
- return;
- }
-
- if (s->sizearg == NULL) {
- s->legacy_size = 4 * MiB; /* 4 MB default */
- } else {
- int ret;
- uint64_t size;
-
- ret = qemu_strtosz_MiB(s->sizearg, NULL, &size);
- if (ret < 0 || (size_t)size != size || !is_power_of_2(size)) {
- error_setg(errp, "Invalid size %s", s->sizearg);
- return;
- }
- s->legacy_size = size;
- }
-
- /* check that role is reasonable */
- if (s->role) {
- if (strncmp(s->role, "peer", 5) == 0) {
- s->master = ON_OFF_AUTO_OFF;
- } else if (strncmp(s->role, "master", 7) == 0) {
- s->master = ON_OFF_AUTO_ON;
- } else {
- error_setg(errp, "'role' must be 'peer' or 'master'");
- return;
- }
- } else {
- s->master = ON_OFF_AUTO_AUTO;
- }
-
- if (s->shmobj) {
- desugar_shm(s);
- }
-
- /*
- * Note: we don't use INTx with IVSHMEM_MSI at all, so this is a
- * bald-faced lie then. But it's a backwards compatible lie.
- */
- pci_config_set_interrupt_pin(dev->config, 1);
-
- ivshmem_common_realize(dev, errp);
-}
-
-static void ivshmem_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
- k->realize = ivshmem_realize;
- k->revision = 0;
- dc->desc = "Inter-VM shared memory (legacy)";
- dc->props = ivshmem_properties;
- dc->vmsd = &ivshmem_vmsd;
-}
-
-static const TypeInfo ivshmem_info = {
- .name = TYPE_IVSHMEM,
- .parent = TYPE_IVSHMEM_COMMON,
- .instance_size = sizeof(IVShmemState),
- .class_init = ivshmem_class_init,
-};
-
static void ivshmem_register_types(void)
{
type_register_static(&ivshmem_common_info);
type_register_static(&ivshmem_plain_info);
type_register_static(&ivshmem_doorbell_info);
- type_register_static(&ivshmem_info);
}
type_init(ivshmem_register_types)
diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c
index 43bbec46cf..1257d8fce6 100644
--- a/hw/misc/mips_itu.c
+++ b/hw/misc/mips_itu.c
@@ -55,9 +55,17 @@ typedef enum ITCView {
ITCVIEW_EF_SYNC = 2,
ITCVIEW_EF_TRY = 3,
ITCVIEW_PV_SYNC = 4,
- ITCVIEW_PV_TRY = 5
+ ITCVIEW_PV_TRY = 5,
+ ITCVIEW_PV_ICR0 = 15,
} ITCView;
+#define ITC_ICR0_CELL_NUM 16
+#define ITC_ICR0_BLK_GRAIN 8
+#define ITC_ICR0_BLK_GRAIN_MASK 0x7
+#define ITC_ICR0_ERR_AXI 2
+#define ITC_ICR0_ERR_PARITY 1
+#define ITC_ICR0_ERR_EXEC 0
+
MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu)
{
return &itu->tag_io;
@@ -76,7 +84,7 @@ static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size)
return tag->ITCAddressMap[index];
}
-static void itc_reconfigure(MIPSITUState *tag)
+void itc_reconfigure(MIPSITUState *tag)
{
uint64_t *am = &tag->ITCAddressMap[0];
MemoryRegion *mr = &tag->storage_io;
@@ -84,6 +92,12 @@ static void itc_reconfigure(MIPSITUState *tag)
uint64_t size = (1 * KiB) + (am[1] & ITC_AM1_ADDR_MASK_MASK);
bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0;
+ if (tag->saar_present) {
+ address = ((*(uint64_t *) tag->saar) & 0xFFFFFFFFE000ULL) << 4;
+ size = 1 << ((*(uint64_t *) tag->saar >> 1) & 0x1f);
+ is_enabled = *(uint64_t *) tag->saar & 1;
+ }
+
memory_region_transaction_begin();
if (!(size & (size - 1))) {
memory_region_set_size(mr, size);
@@ -142,7 +156,12 @@ static inline ITCView get_itc_view(hwaddr addr)
static inline int get_cell_stride_shift(const MIPSITUState *s)
{
/* Minimum interval (for EntryGain = 0) is 128 B */
- return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK);
+ if (s->saar_present) {
+ return 7 + ((s->icr0 >> ITC_ICR0_BLK_GRAIN) &
+ ITC_ICR0_BLK_GRAIN_MASK);
+ } else {
+ return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK);
+ }
}
static inline ITCStorageCell *get_cell(MIPSITUState *s,
@@ -356,6 +375,12 @@ static void view_pv_try_write(ITCStorageCell *c)
view_pv_common_write(c);
}
+static void raise_exception(int excp)
+{
+ current_cpu->exception_index = excp;
+ cpu_loop_exit(current_cpu);
+}
+
static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size)
{
MIPSITUState *s = (MIPSITUState *)opaque;
@@ -363,6 +388,14 @@ static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size)
ITCView view = get_itc_view(addr);
uint64_t ret = -1;
+ switch (size) {
+ case 1:
+ case 2:
+ s->icr0 |= 1 << ITC_ICR0_ERR_AXI;
+ raise_exception(EXCP_DBE);
+ return 0;
+ }
+
switch (view) {
case ITCVIEW_BYPASS:
ret = view_bypass_read(cell);
@@ -382,6 +415,9 @@ static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size)
case ITCVIEW_PV_TRY:
ret = view_pv_try_read(cell);
break;
+ case ITCVIEW_PV_ICR0:
+ ret = s->icr0;
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"itc_storage_read: Bad ITC View %d\n", (int)view);
@@ -398,6 +434,14 @@ static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data,
ITCStorageCell *cell = get_cell(s, addr);
ITCView view = get_itc_view(addr);
+ switch (size) {
+ case 1:
+ case 2:
+ s->icr0 |= 1 << ITC_ICR0_ERR_AXI;
+ raise_exception(EXCP_DBE);
+ return;
+ }
+
switch (view) {
case ITCVIEW_BYPASS:
view_bypass_write(cell, data);
@@ -417,6 +461,15 @@ static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data,
case ITCVIEW_PV_TRY:
view_pv_try_write(cell);
break;
+ case ITCVIEW_PV_ICR0:
+ if (data & 0x7) {
+ /* clear ERROR bits */
+ s->icr0 &= ~(data & 0x7);
+ }
+ /* set BLK_GRAIN */
+ s->icr0 &= ~0x700;
+ s->icr0 |= data & 0x700;
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"itc_storage_write: Bad ITC View %d\n", (int)view);
@@ -479,10 +532,15 @@ static void mips_itu_reset(DeviceState *dev)
{
MIPSITUState *s = MIPS_ITU(dev);
- s->ITCAddressMap[0] = 0;
- s->ITCAddressMap[1] =
- ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) |
- (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS);
+ if (s->saar_present) {
+ *(uint64_t *) s->saar = 0x11 << 1;
+ s->icr0 = get_num_cells(s) << ITC_ICR0_CELL_NUM;
+ } else {
+ s->ITCAddressMap[0] = 0;
+ s->ITCAddressMap[1] =
+ ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) |
+ (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS);
+ }
itc_reconfigure(s);
itc_reset_cells(s);
@@ -493,6 +551,7 @@ static Property mips_itu_properties[] = {
ITC_FIFO_NUM_MAX),
DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores,
ITC_SEMAPH_NUM_MAX),
+ DEFINE_PROP_BOOL("saar-present", MIPSITUState, saar_present, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c
index 909c1182ee..790430346b 100644
--- a/hw/net/ftgmac100.c
+++ b/hw/net/ftgmac100.c
@@ -90,6 +90,18 @@
#define FTGMAC100_PHYDATA_MIIRDATA(x) (((x) >> 16) & 0xffff)
/*
+ * PHY control register - New MDC/MDIO interface
+ */
+#define FTGMAC100_PHYCR_NEW_DATA(x) (((x) >> 16) & 0xffff)
+#define FTGMAC100_PHYCR_NEW_FIRE (1 << 15)
+#define FTGMAC100_PHYCR_NEW_ST_22 (1 << 12)
+#define FTGMAC100_PHYCR_NEW_OP(x) (((x) >> 10) & 3)
+#define FTGMAC100_PHYCR_NEW_OP_WRITE 0x1
+#define FTGMAC100_PHYCR_NEW_OP_READ 0x2
+#define FTGMAC100_PHYCR_NEW_DEV(x) (((x) >> 5) & 0x1f)
+#define FTGMAC100_PHYCR_NEW_REG(x) ((x) & 0x1f)
+
+/*
* Feature Register
*/
#define FTGMAC100_REVR_NEW_MDIO_INTERFACE (1 << 31)
@@ -269,9 +281,9 @@ static void phy_reset(FTGMAC100State *s)
s->phy_int = 0;
}
-static uint32_t do_phy_read(FTGMAC100State *s, int reg)
+static uint16_t do_phy_read(FTGMAC100State *s, uint8_t reg)
{
- uint32_t val;
+ uint16_t val;
switch (reg) {
case MII_BMCR: /* Basic Control */
@@ -336,7 +348,7 @@ static uint32_t do_phy_read(FTGMAC100State *s, int reg)
MII_BMCR_FD | MII_BMCR_CTST)
#define MII_ANAR_MASK 0x2d7f
-static void do_phy_write(FTGMAC100State *s, int reg, uint32_t val)
+static void do_phy_write(FTGMAC100State *s, uint8_t reg, uint16_t val)
{
switch (reg) {
case MII_BMCR: /* Basic Control */
@@ -373,6 +385,55 @@ static void do_phy_write(FTGMAC100State *s, int reg, uint32_t val)
}
}
+static void do_phy_new_ctl(FTGMAC100State *s)
+{
+ uint8_t reg;
+ uint16_t data;
+
+ if (!(s->phycr & FTGMAC100_PHYCR_NEW_ST_22)) {
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported ST code\n", __func__);
+ return;
+ }
+
+ /* Nothing to do */
+ if (!(s->phycr & FTGMAC100_PHYCR_NEW_FIRE)) {
+ return;
+ }
+
+ reg = FTGMAC100_PHYCR_NEW_REG(s->phycr);
+ data = FTGMAC100_PHYCR_NEW_DATA(s->phycr);
+
+ switch (FTGMAC100_PHYCR_NEW_OP(s->phycr)) {
+ case FTGMAC100_PHYCR_NEW_OP_WRITE:
+ do_phy_write(s, reg, data);
+ break;
+ case FTGMAC100_PHYCR_NEW_OP_READ:
+ s->phydata = do_phy_read(s, reg) & 0xffff;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid OP code %08x\n",
+ __func__, s->phycr);
+ }
+
+ s->phycr &= ~FTGMAC100_PHYCR_NEW_FIRE;
+}
+
+static void do_phy_ctl(FTGMAC100State *s)
+{
+ uint8_t reg = FTGMAC100_PHYCR_REG(s->phycr);
+
+ if (s->phycr & FTGMAC100_PHYCR_MIIWR) {
+ do_phy_write(s, reg, s->phydata & 0xffff);
+ s->phycr &= ~FTGMAC100_PHYCR_MIIWR;
+ } else if (s->phycr & FTGMAC100_PHYCR_MIIRD) {
+ s->phydata = do_phy_read(s, reg) << 16;
+ s->phycr &= ~FTGMAC100_PHYCR_MIIRD;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: no OP code %08x\n",
+ __func__, s->phycr);
+ }
+}
+
static int ftgmac100_read_bd(FTGMAC100Desc *bd, dma_addr_t addr)
{
if (dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd))) {
@@ -628,7 +689,6 @@ static void ftgmac100_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
FTGMAC100State *s = FTGMAC100(opaque);
- int reg;
switch (addr & 0xff) {
case FTGMAC100_ISR: /* Interrupt status */
@@ -711,14 +771,11 @@ static void ftgmac100_write(void *opaque, hwaddr addr,
break;
case FTGMAC100_PHYCR: /* PHY Device control */
- reg = FTGMAC100_PHYCR_REG(value);
s->phycr = value;
- if (value & FTGMAC100_PHYCR_MIIWR) {
- do_phy_write(s, reg, s->phydata & 0xffff);
- s->phycr &= ~FTGMAC100_PHYCR_MIIWR;
+ if (s->revr & FTGMAC100_REVR_NEW_MDIO_INTERFACE) {
+ do_phy_new_ctl(s);
} else {
- s->phydata = do_phy_read(s, reg) << 16;
- s->phycr &= ~FTGMAC100_PHYCR_MIIRD;
+ do_phy_ctl(s);
}
break;
case FTGMAC100_PHYDATA:
@@ -728,8 +785,7 @@ static void ftgmac100_write(void *opaque, hwaddr addr,
s->dblac = value;
break;
case FTGMAC100_REVR: /* Feature Register */
- /* TODO: Only Old MDIO interface is supported */
- s->revr = value & ~FTGMAC100_REVR_NEW_MDIO_INTERFACE;
+ s->revr = value;
break;
case FTGMAC100_FEAR1: /* Feature Register 1 */
s->fear1 = value;
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index e37fc34839..3f319ef723 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -41,6 +41,47 @@
#define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE
#define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE
+#define VIRTIO_NET_IP4_ADDR_SIZE 8 /* ipv4 saddr + daddr */
+
+#define VIRTIO_NET_TCP_FLAG 0x3F
+#define VIRTIO_NET_TCP_HDR_LENGTH 0xF000
+
+/* IPv4 max payload, 16 bits in the header */
+#define VIRTIO_NET_MAX_IP4_PAYLOAD (65535 - sizeof(struct ip_header))
+#define VIRTIO_NET_MAX_TCP_PAYLOAD 65535
+
+/* header length value in ip header without option */
+#define VIRTIO_NET_IP4_HEADER_LENGTH 5
+
+#define VIRTIO_NET_IP6_ADDR_SIZE 32 /* ipv6 saddr + daddr */
+#define VIRTIO_NET_MAX_IP6_PAYLOAD VIRTIO_NET_MAX_TCP_PAYLOAD
+
+/* Purge coalesced packets timer interval, This value affects the performance
+ a lot, and should be tuned carefully, '300000'(300us) is the recommended
+ value to pass the WHQL test, '50000' can gain 2x netperf throughput with
+ tso/gso/gro 'off'. */
+#define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000
+
+/* temporary until standard header include it */
+#if !defined(VIRTIO_NET_HDR_F_RSC_INFO)
+
+#define VIRTIO_NET_HDR_F_RSC_INFO 4 /* rsc_ext data in csum_ fields */
+#define VIRTIO_NET_F_RSC_EXT 61
+
+static inline __virtio16 *virtio_net_rsc_ext_num_packets(
+ struct virtio_net_hdr *hdr)
+{
+ return &hdr->csum_start;
+}
+
+static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
+ struct virtio_net_hdr *hdr)
+{
+ return &hdr->csum_offset;
+}
+
+#endif
+
/*
* Calculate the number of bytes up to and including the given 'field' of
* 'container'.
@@ -628,6 +669,7 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
if (!get_vhost_net(nc->peer)) {
return features;
}
+
features = vhost_net_get_features(get_vhost_net(nc->peer), features);
vdev->backend_features = features;
@@ -701,6 +743,11 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
virtio_has_feature(features,
VIRTIO_F_VERSION_1));
+ n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
+ virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4);
+ n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
+ virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6);
+
if (n->has_vnet_hdr) {
n->curr_guest_offloads =
virtio_net_guest_offloads_by_features(features);
@@ -781,6 +828,12 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd,
return VIRTIO_NET_ERR;
}
+ n->rsc4_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
+ virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO4);
+ n->rsc6_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
+ virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO6);
+ virtio_clear_feature(&offloads, VIRTIO_NET_F_RSC_EXT);
+
supported_offloads = virtio_net_supported_guest_offloads(n);
if (offloads & ~supported_offloads) {
return VIRTIO_NET_ERR;
@@ -1292,7 +1345,7 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
return size;
}
-static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf,
+static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf,
size_t size)
{
ssize_t r;
@@ -1303,6 +1356,612 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf,
return r;
}
+static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
+ const uint8_t *buf,
+ VirtioNetRscUnit *unit)
+{
+ uint16_t ip_hdrlen;
+ struct ip_header *ip;
+
+ ip = (struct ip_header *)(buf + chain->n->guest_hdr_len
+ + sizeof(struct eth_header));
+ unit->ip = (void *)ip;
+ ip_hdrlen = (ip->ip_ver_len & 0xF) << 2;
+ unit->ip_plen = &ip->ip_len;
+ unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen);
+ unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
+ unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen;
+}
+
+static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
+ const uint8_t *buf,
+ VirtioNetRscUnit *unit)
+{
+ struct ip6_header *ip6;
+
+ ip6 = (struct ip6_header *)(buf + chain->n->guest_hdr_len
+ + sizeof(struct eth_header));
+ unit->ip = ip6;
+ unit->ip_plen = &(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
+ unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip)\
+ + sizeof(struct ip6_header));
+ unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
+
+ /* There is a difference between payload lenght in ipv4 and v6,
+ ip header is excluded in ipv6 */
+ unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen;
+}
+
+static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain,
+ VirtioNetRscSeg *seg)
+{
+ int ret;
+ struct virtio_net_hdr *h;
+
+ h = (struct virtio_net_hdr *)seg->buf;
+ h->flags = 0;
+ h->gso_type = VIRTIO_NET_HDR_GSO_NONE;
+
+ if (seg->is_coalesced) {
+ *virtio_net_rsc_ext_num_packets(h) = seg->packets;
+ *virtio_net_rsc_ext_num_dupacks(h) = seg->dup_ack;
+ h->flags = VIRTIO_NET_HDR_F_RSC_INFO;
+ if (chain->proto == ETH_P_IP) {
+ h->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
+ } else {
+ h->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
+ }
+ }
+
+ ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size);
+ QTAILQ_REMOVE(&chain->buffers, seg, next);
+ g_free(seg->buf);
+ g_free(seg);
+
+ return ret;
+}
+
+static void virtio_net_rsc_purge(void *opq)
+{
+ VirtioNetRscSeg *seg, *rn;
+ VirtioNetRscChain *chain = (VirtioNetRscChain *)opq;
+
+ QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn) {
+ if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
+ chain->stat.purge_failed++;
+ continue;
+ }
+ }
+
+ chain->stat.timer++;
+ if (!QTAILQ_EMPTY(&chain->buffers)) {
+ timer_mod(chain->drain_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
+ }
+}
+
+static void virtio_net_rsc_cleanup(VirtIONet *n)
+{
+ VirtioNetRscChain *chain, *rn_chain;
+ VirtioNetRscSeg *seg, *rn_seg;
+
+ QTAILQ_FOREACH_SAFE(chain, &n->rsc_chains, next, rn_chain) {
+ QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn_seg) {
+ QTAILQ_REMOVE(&chain->buffers, seg, next);
+ g_free(seg->buf);
+ g_free(seg);
+ }
+
+ timer_del(chain->drain_timer);
+ timer_free(chain->drain_timer);
+ QTAILQ_REMOVE(&n->rsc_chains, chain, next);
+ g_free(chain);
+ }
+}
+
+static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain,
+ NetClientState *nc,
+ const uint8_t *buf, size_t size)
+{
+ uint16_t hdr_len;
+ VirtioNetRscSeg *seg;
+
+ hdr_len = chain->n->guest_hdr_len;
+ seg = g_malloc(sizeof(VirtioNetRscSeg));
+ seg->buf = g_malloc(hdr_len + sizeof(struct eth_header)
+ + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD);
+ memcpy(seg->buf, buf, size);
+ seg->size = size;
+ seg->packets = 1;
+ seg->dup_ack = 0;
+ seg->is_coalesced = 0;
+ seg->nc = nc;
+
+ QTAILQ_INSERT_TAIL(&chain->buffers, seg, next);
+ chain->stat.cache++;
+
+ switch (chain->proto) {
+ case ETH_P_IP:
+ virtio_net_rsc_extract_unit4(chain, seg->buf, &seg->unit);
+ break;
+ case ETH_P_IPV6:
+ virtio_net_rsc_extract_unit6(chain, seg->buf, &seg->unit);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static int32_t virtio_net_rsc_handle_ack(VirtioNetRscChain *chain,
+ VirtioNetRscSeg *seg,
+ const uint8_t *buf,
+ struct tcp_header *n_tcp,
+ struct tcp_header *o_tcp)
+{
+ uint32_t nack, oack;
+ uint16_t nwin, owin;
+
+ nack = htonl(n_tcp->th_ack);
+ nwin = htons(n_tcp->th_win);
+ oack = htonl(o_tcp->th_ack);
+ owin = htons(o_tcp->th_win);
+
+ if ((nack - oack) >= VIRTIO_NET_MAX_TCP_PAYLOAD) {
+ chain->stat.ack_out_of_win++;
+ return RSC_FINAL;
+ } else if (nack == oack) {
+ /* duplicated ack or window probe */
+ if (nwin == owin) {
+ /* duplicated ack, add dup ack count due to whql test up to 1 */
+ chain->stat.dup_ack++;
+ return RSC_FINAL;
+ } else {
+ /* Coalesce window update */
+ o_tcp->th_win = n_tcp->th_win;
+ chain->stat.win_update++;
+ return RSC_COALESCE;
+ }
+ } else {
+ /* pure ack, go to 'C', finalize*/
+ chain->stat.pure_ack++;
+ return RSC_FINAL;
+ }
+}
+
+static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain,
+ VirtioNetRscSeg *seg,
+ const uint8_t *buf,
+ VirtioNetRscUnit *n_unit)
+{
+ void *data;
+ uint16_t o_ip_len;
+ uint32_t nseq, oseq;
+ VirtioNetRscUnit *o_unit;
+
+ o_unit = &seg->unit;
+ o_ip_len = htons(*o_unit->ip_plen);
+ nseq = htonl(n_unit->tcp->th_seq);
+ oseq = htonl(o_unit->tcp->th_seq);
+
+ /* out of order or retransmitted. */
+ if ((nseq - oseq) > VIRTIO_NET_MAX_TCP_PAYLOAD) {
+ chain->stat.data_out_of_win++;
+ return RSC_FINAL;
+ }
+
+ data = ((uint8_t *)n_unit->tcp) + n_unit->tcp_hdrlen;
+ if (nseq == oseq) {
+ if ((o_unit->payload == 0) && n_unit->payload) {
+ /* From no payload to payload, normal case, not a dup ack or etc */
+ chain->stat.data_after_pure_ack++;
+ goto coalesce;
+ } else {
+ return virtio_net_rsc_handle_ack(chain, seg, buf,
+ n_unit->tcp, o_unit->tcp);
+ }
+ } else if ((nseq - oseq) != o_unit->payload) {
+ /* Not a consistent packet, out of order */
+ chain->stat.data_out_of_order++;
+ return RSC_FINAL;
+ } else {
+coalesce:
+ if ((o_ip_len + n_unit->payload) > chain->max_payload) {
+ chain->stat.over_size++;
+ return RSC_FINAL;
+ }
+
+ /* Here comes the right data, the payload length in v4/v6 is different,
+ so use the field value to update and record the new data len */
+ o_unit->payload += n_unit->payload; /* update new data len */
+
+ /* update field in ip header */
+ *o_unit->ip_plen = htons(o_ip_len + n_unit->payload);
+
+ /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced
+ for windows guest, while this may change the behavior for linux
+ guest (only if it uses RSC feature). */
+ o_unit->tcp->th_offset_flags = n_unit->tcp->th_offset_flags;
+
+ o_unit->tcp->th_ack = n_unit->tcp->th_ack;
+ o_unit->tcp->th_win = n_unit->tcp->th_win;
+
+ memmove(seg->buf + seg->size, data, n_unit->payload);
+ seg->size += n_unit->payload;
+ seg->packets++;
+ chain->stat.coalesced++;
+ return RSC_COALESCE;
+ }
+}
+
+static int32_t virtio_net_rsc_coalesce4(VirtioNetRscChain *chain,
+ VirtioNetRscSeg *seg,
+ const uint8_t *buf, size_t size,
+ VirtioNetRscUnit *unit)
+{
+ struct ip_header *ip1, *ip2;
+
+ ip1 = (struct ip_header *)(unit->ip);
+ ip2 = (struct ip_header *)(seg->unit.ip);
+ if ((ip1->ip_src ^ ip2->ip_src) || (ip1->ip_dst ^ ip2->ip_dst)
+ || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
+ || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
+ chain->stat.no_match++;
+ return RSC_NO_MATCH;
+ }
+
+ return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
+}
+
+static int32_t virtio_net_rsc_coalesce6(VirtioNetRscChain *chain,
+ VirtioNetRscSeg *seg,
+ const uint8_t *buf, size_t size,
+ VirtioNetRscUnit *unit)
+{
+ struct ip6_header *ip1, *ip2;
+
+ ip1 = (struct ip6_header *)(unit->ip);
+ ip2 = (struct ip6_header *)(seg->unit.ip);
+ if (memcmp(&ip1->ip6_src, &ip2->ip6_src, sizeof(struct in6_address))
+ || memcmp(&ip1->ip6_dst, &ip2->ip6_dst, sizeof(struct in6_address))
+ || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
+ || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
+ chain->stat.no_match++;
+ return RSC_NO_MATCH;
+ }
+
+ return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
+}
+
+/* Packets with 'SYN' should bypass, other flag should be sent after drain
+ * to prevent out of order */
+static int virtio_net_rsc_tcp_ctrl_check(VirtioNetRscChain *chain,
+ struct tcp_header *tcp)
+{
+ uint16_t tcp_hdr;
+ uint16_t tcp_flag;
+
+ tcp_flag = htons(tcp->th_offset_flags);
+ tcp_hdr = (tcp_flag & VIRTIO_NET_TCP_HDR_LENGTH) >> 10;
+ tcp_flag &= VIRTIO_NET_TCP_FLAG;
+ tcp_flag = htons(tcp->th_offset_flags) & 0x3F;
+ if (tcp_flag & TH_SYN) {
+ chain->stat.tcp_syn++;
+ return RSC_BYPASS;
+ }
+
+ if (tcp_flag & (TH_FIN | TH_URG | TH_RST | TH_ECE | TH_CWR)) {
+ chain->stat.tcp_ctrl_drain++;
+ return RSC_FINAL;
+ }
+
+ if (tcp_hdr > sizeof(struct tcp_header)) {
+ chain->stat.tcp_all_opt++;
+ return RSC_FINAL;
+ }
+
+ return RSC_CANDIDATE;
+}
+
+static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain,
+ NetClientState *nc,
+ const uint8_t *buf, size_t size,
+ VirtioNetRscUnit *unit)
+{
+ int ret;
+ VirtioNetRscSeg *seg, *nseg;
+
+ if (QTAILQ_EMPTY(&chain->buffers)) {
+ chain->stat.empty_cache++;
+ virtio_net_rsc_cache_buf(chain, nc, buf, size);
+ timer_mod(chain->drain_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
+ return size;
+ }
+
+ QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
+ if (chain->proto == ETH_P_IP) {
+ ret = virtio_net_rsc_coalesce4(chain, seg, buf, size, unit);
+ } else {
+ ret = virtio_net_rsc_coalesce6(chain, seg, buf, size, unit);
+ }
+
+ if (ret == RSC_FINAL) {
+ if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
+ /* Send failed */
+ chain->stat.final_failed++;
+ return 0;
+ }
+
+ /* Send current packet */
+ return virtio_net_do_receive(nc, buf, size);
+ } else if (ret == RSC_NO_MATCH) {
+ continue;
+ } else {
+ /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */
+ seg->is_coalesced = 1;
+ return size;
+ }
+ }
+
+ chain->stat.no_match_cache++;
+ virtio_net_rsc_cache_buf(chain, nc, buf, size);
+ return size;
+}
+
+/* Drain a connection data, this is to avoid out of order segments */
+static size_t virtio_net_rsc_drain_flow(VirtioNetRscChain *chain,
+ NetClientState *nc,
+ const uint8_t *buf, size_t size,
+ uint16_t ip_start, uint16_t ip_size,
+ uint16_t tcp_port)
+{
+ VirtioNetRscSeg *seg, *nseg;
+ uint32_t ppair1, ppair2;
+
+ ppair1 = *(uint32_t *)(buf + tcp_port);
+ QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
+ ppair2 = *(uint32_t *)(seg->buf + tcp_port);
+ if (memcmp(buf + ip_start, seg->buf + ip_start, ip_size)
+ || (ppair1 != ppair2)) {
+ continue;
+ }
+ if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
+ chain->stat.drain_failed++;
+ }
+
+ break;
+ }
+
+ return virtio_net_do_receive(nc, buf, size);
+}
+
+static int32_t virtio_net_rsc_sanity_check4(VirtioNetRscChain *chain,
+ struct ip_header *ip,
+ const uint8_t *buf, size_t size)
+{
+ uint16_t ip_len;
+
+ /* Not an ipv4 packet */
+ if (((ip->ip_ver_len & 0xF0) >> 4) != IP_HEADER_VERSION_4) {
+ chain->stat.ip_option++;
+ return RSC_BYPASS;
+ }
+
+ /* Don't handle packets with ip option */
+ if ((ip->ip_ver_len & 0xF) != VIRTIO_NET_IP4_HEADER_LENGTH) {
+ chain->stat.ip_option++;
+ return RSC_BYPASS;
+ }
+
+ if (ip->ip_p != IPPROTO_TCP) {
+ chain->stat.bypass_not_tcp++;
+ return RSC_BYPASS;
+ }
+
+ /* Don't handle packets with ip fragment */
+ if (!(htons(ip->ip_off) & IP_DF)) {
+ chain->stat.ip_frag++;
+ return RSC_BYPASS;
+ }
+
+ /* Don't handle packets with ecn flag */
+ if (IPTOS_ECN(ip->ip_tos)) {
+ chain->stat.ip_ecn++;
+ return RSC_BYPASS;
+ }
+
+ ip_len = htons(ip->ip_len);
+ if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header))
+ || ip_len > (size - chain->n->guest_hdr_len -
+ sizeof(struct eth_header))) {
+ chain->stat.ip_hacked++;
+ return RSC_BYPASS;
+ }
+
+ return RSC_CANDIDATE;
+}
+
+static size_t virtio_net_rsc_receive4(VirtioNetRscChain *chain,
+ NetClientState *nc,
+ const uint8_t *buf, size_t size)
+{
+ int32_t ret;
+ uint16_t hdr_len;
+ VirtioNetRscUnit unit;
+
+ hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
+
+ if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header)
+ + sizeof(struct tcp_header))) {
+ chain->stat.bypass_not_tcp++;
+ return virtio_net_do_receive(nc, buf, size);
+ }
+
+ virtio_net_rsc_extract_unit4(chain, buf, &unit);
+ if (virtio_net_rsc_sanity_check4(chain, unit.ip, buf, size)
+ != RSC_CANDIDATE) {
+ return virtio_net_do_receive(nc, buf, size);
+ }
+
+ ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
+ if (ret == RSC_BYPASS) {
+ return virtio_net_do_receive(nc, buf, size);
+ } else if (ret == RSC_FINAL) {
+ return virtio_net_rsc_drain_flow(chain, nc, buf, size,
+ ((hdr_len + sizeof(struct eth_header)) + 12),
+ VIRTIO_NET_IP4_ADDR_SIZE,
+ hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header));
+ }
+
+ return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
+}
+
+static int32_t virtio_net_rsc_sanity_check6(VirtioNetRscChain *chain,
+ struct ip6_header *ip6,
+ const uint8_t *buf, size_t size)
+{
+ uint16_t ip_len;
+
+ if (((ip6->ip6_ctlun.ip6_un1.ip6_un1_flow & 0xF0) >> 4)
+ != IP_HEADER_VERSION_6) {
+ return RSC_BYPASS;
+ }
+
+ /* Both option and protocol is checked in this */
+ if (ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) {
+ chain->stat.bypass_not_tcp++;
+ return RSC_BYPASS;
+ }
+
+ ip_len = htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
+ if (ip_len < sizeof(struct tcp_header) ||
+ ip_len > (size - chain->n->guest_hdr_len - sizeof(struct eth_header)
+ - sizeof(struct ip6_header))) {
+ chain->stat.ip_hacked++;
+ return RSC_BYPASS;
+ }
+
+ /* Don't handle packets with ecn flag */
+ if (IP6_ECN(ip6->ip6_ctlun.ip6_un3.ip6_un3_ecn)) {
+ chain->stat.ip_ecn++;
+ return RSC_BYPASS;
+ }
+
+ return RSC_CANDIDATE;
+}
+
+static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc,
+ const uint8_t *buf, size_t size)
+{
+ int32_t ret;
+ uint16_t hdr_len;
+ VirtioNetRscChain *chain;
+ VirtioNetRscUnit unit;
+
+ chain = (VirtioNetRscChain *)opq;
+ hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
+
+ if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header)
+ + sizeof(tcp_header))) {
+ return virtio_net_do_receive(nc, buf, size);
+ }
+
+ virtio_net_rsc_extract_unit6(chain, buf, &unit);
+ if (RSC_CANDIDATE != virtio_net_rsc_sanity_check6(chain,
+ unit.ip, buf, size)) {
+ return virtio_net_do_receive(nc, buf, size);
+ }
+
+ ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
+ if (ret == RSC_BYPASS) {
+ return virtio_net_do_receive(nc, buf, size);
+ } else if (ret == RSC_FINAL) {
+ return virtio_net_rsc_drain_flow(chain, nc, buf, size,
+ ((hdr_len + sizeof(struct eth_header)) + 8),
+ VIRTIO_NET_IP6_ADDR_SIZE,
+ hdr_len + sizeof(struct eth_header)
+ + sizeof(struct ip6_header));
+ }
+
+ return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
+}
+
+static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n,
+ NetClientState *nc,
+ uint16_t proto)
+{
+ VirtioNetRscChain *chain;
+
+ if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) {
+ return NULL;
+ }
+
+ QTAILQ_FOREACH(chain, &n->rsc_chains, next) {
+ if (chain->proto == proto) {
+ return chain;
+ }
+ }
+
+ chain = g_malloc(sizeof(*chain));
+ chain->n = n;
+ chain->proto = proto;
+ if (proto == (uint16_t)ETH_P_IP) {
+ chain->max_payload = VIRTIO_NET_MAX_IP4_PAYLOAD;
+ chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
+ } else {
+ chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD;
+ chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
+ }
+ chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST,
+ virtio_net_rsc_purge, chain);
+ memset(&chain->stat, 0, sizeof(chain->stat));
+
+ QTAILQ_INIT(&chain->buffers);
+ QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next);
+
+ return chain;
+}
+
+static ssize_t virtio_net_rsc_receive(NetClientState *nc,
+ const uint8_t *buf,
+ size_t size)
+{
+ uint16_t proto;
+ VirtioNetRscChain *chain;
+ struct eth_header *eth;
+ VirtIONet *n;
+
+ n = qemu_get_nic_opaque(nc);
+ if (size < (n->host_hdr_len + sizeof(struct eth_header))) {
+ return virtio_net_do_receive(nc, buf, size);
+ }
+
+ eth = (struct eth_header *)(buf + n->guest_hdr_len);
+ proto = htons(eth->h_proto);
+
+ chain = virtio_net_rsc_lookup_chain(n, nc, proto);
+ if (chain) {
+ chain->stat.received++;
+ if (proto == (uint16_t)ETH_P_IP && n->rsc4_enabled) {
+ return virtio_net_rsc_receive4(chain, nc, buf, size);
+ } else if (proto == (uint16_t)ETH_P_IPV6 && n->rsc6_enabled) {
+ return virtio_net_rsc_receive6(chain, nc, buf, size);
+ }
+ }
+ return virtio_net_do_receive(nc, buf, size);
+}
+
+static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf,
+ size_t size)
+{
+ VirtIONet *n = qemu_get_nic_opaque(nc);
+ if ((n->rsc4_enabled || n->rsc6_enabled)) {
+ return virtio_net_rsc_receive(nc, buf, size);
+ } else {
+ return virtio_net_do_receive(nc, buf, size);
+ }
+}
+
static int32_t virtio_net_flush_tx(VirtIONetQueue *q);
static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
@@ -2075,6 +2734,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
nc = qemu_get_queue(n->nic);
nc->rxfilter_notify_enabled = 1;
+ QTAILQ_INIT(&n->rsc_chains);
n->qdev = dev;
}
@@ -2104,6 +2764,7 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
timer_free(n->announce_timer);
g_free(n->vqs);
qemu_del_nic(n->nic);
+ virtio_net_rsc_cleanup(n);
virtio_cleanup(vdev);
}
@@ -2184,6 +2845,10 @@ static Property virtio_net_properties[] = {
DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features,
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
+ DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features,
+ VIRTIO_NET_F_RSC_EXT, false),
+ DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout,
+ VIRTIO_NET_RSC_DEFAULT_INTERVAL),
DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf),
DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer,
TX_TIMER_INTERVAL),
diff --git a/hw/pci/msix.c b/hw/pci/msix.c
index c7bdbeda9e..4e336416a7 100644
--- a/hw/pci/msix.c
+++ b/hw/pci/msix.c
@@ -345,7 +345,7 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
char *name;
uint32_t bar_size = 4096;
uint32_t bar_pba_offset = bar_size / 2;
- uint32_t bar_pba_size = (nentries / 8 + 1) * 8;
+ uint32_t bar_pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
/*
* Migration compatibility dictates that this remains a 4k
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 2d3d8a047b..230478faab 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -391,10 +391,10 @@ static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event)
}
static void pcie_cap_slot_plug_common(PCIDevice *hotplug_dev, DeviceState *dev,
- uint8_t **exp_cap, Error **errp)
+ Error **errp)
{
- *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap;
- uint16_t sltsta = pci_get_word(*exp_cap + PCI_EXP_SLTSTA);
+ uint8_t *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap;
+ uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
PCIE_DEV_PRINTF(PCI_DEVICE(dev), "hotplug state: 0x%x\n", sltsta);
if (sltsta & PCI_EXP_SLTSTA_EIS) {
@@ -405,14 +405,19 @@ static void pcie_cap_slot_plug_common(PCIDevice *hotplug_dev, DeviceState *dev,
}
}
+void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ pcie_cap_slot_plug_common(PCI_DEVICE(hotplug_dev), dev, errp);
+}
+
void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
- uint8_t *exp_cap;
+ PCIDevice *hotplug_pdev = PCI_DEVICE(hotplug_dev);
+ uint8_t *exp_cap = hotplug_pdev->config + hotplug_pdev->exp.exp_cap;
PCIDevice *pci_dev = PCI_DEVICE(dev);
- pcie_cap_slot_plug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp);
-
/* Don't send event when device is enabled during qemu machine creation:
* it is present on boot, no hotplug event is necessary. We do send an
* event when the device is disabled later. */
@@ -458,11 +463,15 @@ static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque)
void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- uint8_t *exp_cap;
+ Error *local_err = NULL;
PCIDevice *pci_dev = PCI_DEVICE(dev);
PCIBus *bus = pci_get_bus(pci_dev);
- pcie_cap_slot_plug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp);
+ pcie_cap_slot_plug_common(PCI_DEVICE(hotplug_dev), dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
/* In case user cancel the operation of multi-function hot-add,
* remove the function that is unexposed to guest individually,
diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c
index bc07abc31b..a30291ef54 100644
--- a/hw/pci/pcie_port.c
+++ b/hw/pci/pcie_port.c
@@ -154,6 +154,7 @@ static void pcie_slot_class_init(ObjectClass *oc, void *data)
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
dc->props = pcie_slot_props;
+ hc->pre_plug = pcie_cap_slot_pre_plug_cb;
hc->plug = pcie_cap_slot_plug_cb;
hc->unplug = pcie_cap_slot_unplug_cb;
hc->unplug_request = pcie_cap_slot_unplug_request_cb;
diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c
index 5fce72fe0f..1da7a32348 100644
--- a/hw/ppc/spapr_irq.c
+++ b/hw/ppc/spapr_irq.c
@@ -14,6 +14,7 @@
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_xive.h"
#include "hw/ppc/xics.h"
+#include "hw/ppc/xics_spapr.h"
#include "sysemu/kvm.h"
#include "trace.h"
diff --git a/hw/rdma/rdma_backend.c b/hw/rdma/rdma_backend.c
index c28bfbd44d..fd571f21e5 100644
--- a/hw/rdma/rdma_backend.c
+++ b/hw/rdma/rdma_backend.c
@@ -32,17 +32,6 @@
#include "rdma_rm.h"
#include "rdma_backend.h"
-/* Vendor Errors */
-#define VENDOR_ERR_FAIL_BACKEND 0x201
-#define VENDOR_ERR_TOO_MANY_SGES 0x202
-#define VENDOR_ERR_NOMEM 0x203
-#define VENDOR_ERR_QP0 0x204
-#define VENDOR_ERR_INV_NUM_SGE 0x205
-#define VENDOR_ERR_MAD_SEND 0x206
-#define VENDOR_ERR_INVLKEY 0x207
-#define VENDOR_ERR_MR_SMALL 0x208
-#define VENDOR_ERR_INV_MAD_BUFF 0x209
-
#define THR_NAME_LEN 16
#define THR_POLL_TO 5000
@@ -190,7 +179,7 @@ static inline int rdmacm_mux_can_process_async(RdmaBackendDev *backend_dev)
static int check_mux_op_status(CharBackend *mad_chr_be)
{
- RdmaCmMuxMsg msg = {0};
+ RdmaCmMuxMsg msg = {};
int ret;
pr_dbg("Reading response\n");
@@ -387,7 +376,7 @@ static int build_host_sge_array(RdmaDeviceResources *rdma_dev_res,
static int mad_send(RdmaBackendDev *backend_dev, uint8_t sgid_idx,
union ibv_gid *sgid, struct ibv_sge *sge, uint32_t num_sge)
{
- RdmaCmMuxMsg msg = {0};
+ RdmaCmMuxMsg msg = {};
char *hdr, *data;
int ret;
@@ -475,11 +464,6 @@ void rdma_backend_post_send(RdmaBackendDev *backend_dev,
}
pr_dbg("num_sge=%d\n", num_sge);
- if (!num_sge || num_sge > MAX_SGE) {
- pr_dbg("invalid num_sge=%d\n", num_sge);
- complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_INV_NUM_SGE, ctx);
- return;
- }
bctx = g_malloc0(sizeof(*bctx));
bctx->up_ctx = ctx;
@@ -602,11 +586,6 @@ void rdma_backend_post_recv(RdmaBackendDev *backend_dev,
}
pr_dbg("num_sge=%d\n", num_sge);
- if (!num_sge || num_sge > MAX_SGE) {
- pr_dbg("invalid num_sge=%d\n", num_sge);
- complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_INV_NUM_SGE, ctx);
- return;
- }
bctx = g_malloc0(sizeof(*bctx));
bctx->up_ctx = ctx;
@@ -938,21 +917,25 @@ void rdma_backend_destroy_qp(RdmaBackendQP *qp)
static int init_device_caps(RdmaBackendDev *backend_dev,
struct ibv_device_attr *dev_attr)
{
- if (ibv_query_device(backend_dev->context, &backend_dev->dev_attr)) {
+ struct ibv_device_attr bk_dev_attr;
+
+ if (ibv_query_device(backend_dev->context, &bk_dev_attr)) {
return -EIO;
}
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_mr_size, "%" PRId64);
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_qp, "%d");
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_sge, "%d");
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_qp_wr, "%d");
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_cq, "%d");
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_cqe, "%d");
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_mr, "%d");
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_pd, "%d");
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_qp_rd_atom, "%d");
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_qp_init_rd_atom, "%d");
- CHK_ATTR(dev_attr, backend_dev->dev_attr, max_ah, "%d");
+ dev_attr->max_sge = MAX_SGE;
+
+ CHK_ATTR(dev_attr, bk_dev_attr, max_mr_size, "%" PRId64);
+ CHK_ATTR(dev_attr, bk_dev_attr, max_qp, "%d");
+ CHK_ATTR(dev_attr, bk_dev_attr, max_sge, "%d");
+ CHK_ATTR(dev_attr, bk_dev_attr, max_qp_wr, "%d");
+ CHK_ATTR(dev_attr, bk_dev_attr, max_cq, "%d");
+ CHK_ATTR(dev_attr, bk_dev_attr, max_cqe, "%d");
+ CHK_ATTR(dev_attr, bk_dev_attr, max_mr, "%d");
+ CHK_ATTR(dev_attr, bk_dev_attr, max_pd, "%d");
+ CHK_ATTR(dev_attr, bk_dev_attr, max_qp_rd_atom, "%d");
+ CHK_ATTR(dev_attr, bk_dev_attr, max_qp_init_rd_atom, "%d");
+ CHK_ATTR(dev_attr, bk_dev_attr, max_ah, "%d");
return 0;
}
@@ -1083,8 +1066,10 @@ static void mad_fini(RdmaBackendDev *backend_dev)
pr_dbg("Stopping MAD\n");
disable_rdmacm_mux_async(backend_dev);
qemu_chr_fe_disconnect(backend_dev->rdmacm_mux.chr_be);
- qlist_destroy_obj(QOBJECT(backend_dev->recv_mads_list.list));
- qemu_mutex_destroy(&backend_dev->recv_mads_list.lock);
+ if (backend_dev->recv_mads_list.list) {
+ qlist_destroy_obj(QOBJECT(backend_dev->recv_mads_list.list));
+ qemu_mutex_destroy(&backend_dev->recv_mads_list.lock);
+ }
}
int rdma_backend_get_gid_index(RdmaBackendDev *backend_dev,
@@ -1112,7 +1097,7 @@ int rdma_backend_get_gid_index(RdmaBackendDev *backend_dev,
int rdma_backend_add_gid(RdmaBackendDev *backend_dev, const char *ifname,
union ibv_gid *gid)
{
- RdmaCmMuxMsg msg = {0};
+ RdmaCmMuxMsg msg = {};
int ret;
pr_dbg("0x%llx, 0x%llx\n",
@@ -1138,7 +1123,7 @@ int rdma_backend_add_gid(RdmaBackendDev *backend_dev, const char *ifname,
int rdma_backend_del_gid(RdmaBackendDev *backend_dev, const char *ifname,
union ibv_gid *gid)
{
- RdmaCmMuxMsg msg = {0};
+ RdmaCmMuxMsg msg = {};
int ret;
pr_dbg("0x%llx, 0x%llx\n",
diff --git a/hw/rdma/rdma_backend.h b/hw/rdma/rdma_backend.h
index 8cae40f827..5114c90e67 100644
--- a/hw/rdma/rdma_backend.h
+++ b/hw/rdma/rdma_backend.h
@@ -22,6 +22,18 @@
#include "rdma_rm_defs.h"
#include "rdma_backend_defs.h"
+/* Vendor Errors */
+#define VENDOR_ERR_FAIL_BACKEND 0x201
+#define VENDOR_ERR_TOO_MANY_SGES 0x202
+#define VENDOR_ERR_NOMEM 0x203
+#define VENDOR_ERR_QP0 0x204
+#define VENDOR_ERR_INV_NUM_SGE 0x205
+#define VENDOR_ERR_MAD_SEND 0x206
+#define VENDOR_ERR_INVLKEY 0x207
+#define VENDOR_ERR_MR_SMALL 0x208
+#define VENDOR_ERR_INV_MAD_BUFF 0x209
+#define VENDOR_ERR_INV_GID_IDX 0x210
+
/* Add definition for QP0 and QP1 as there is no userspace enums for them */
enum ibv_special_qp_type {
IBV_QPT_SMI = 0,
diff --git a/hw/rdma/rdma_backend_defs.h b/hw/rdma/rdma_backend_defs.h
index 1e5c3dd3bf..15ae8b970e 100644
--- a/hw/rdma/rdma_backend_defs.h
+++ b/hw/rdma/rdma_backend_defs.h
@@ -41,7 +41,6 @@ typedef struct RdmaCmMux {
} RdmaCmMux;
typedef struct RdmaBackendDev {
- struct ibv_device_attr dev_attr;
RdmaBackendThread comp_thread;
PCIDevice *dev;
RdmaDeviceResources *rdma_dev_res;
diff --git a/hw/rdma/rdma_rm.c b/hw/rdma/rdma_rm.c
index f5b1295890..268ff633a4 100644
--- a/hw/rdma/rdma_rm.c
+++ b/hw/rdma/rdma_rm.c
@@ -41,6 +41,9 @@ static inline void res_tbl_init(const char *name, RdmaRmResTbl *tbl,
static inline void res_tbl_free(RdmaRmResTbl *tbl)
{
+ if (!tbl->bitmap) {
+ return;
+ }
qemu_mutex_destroy(&tbl->lock);
g_free(tbl->tbl);
g_free(tbl->bitmap);
@@ -576,7 +579,7 @@ int rdma_rm_del_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev,
int rdma_rm_get_backend_gid_index(RdmaDeviceResources *dev_res,
RdmaBackendDev *backend_dev, int sgid_idx)
{
- if (unlikely(sgid_idx < 0 || sgid_idx > MAX_PORT_GIDS)) {
+ if (unlikely(sgid_idx < 0 || sgid_idx >= MAX_PORT_GIDS)) {
pr_dbg("Got invalid sgid_idx %d\n", sgid_idx);
return -EINVAL;
}
@@ -655,5 +658,7 @@ void rdma_rm_fini(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev,
res_tbl_free(&dev_res->cq_tbl);
res_tbl_free(&dev_res->pd_tbl);
- g_hash_table_destroy(dev_res->qp_hash);
+ if (dev_res->qp_hash) {
+ g_hash_table_destroy(dev_res->qp_hash);
+ }
}
diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c
index 838ad8a949..d2bdb5ba8c 100644
--- a/hw/rdma/vmw/pvrdma_main.c
+++ b/hw/rdma/vmw/pvrdma_main.c
@@ -43,7 +43,6 @@ static Property pvrdma_dev_properties[] = {
DEFINE_PROP_UINT64("dev-caps-max-mr-size", PVRDMADev, dev_attr.max_mr_size,
MAX_MR_SIZE),
DEFINE_PROP_INT32("dev-caps-max-qp", PVRDMADev, dev_attr.max_qp, MAX_QP),
- DEFINE_PROP_INT32("dev-caps-max-sge", PVRDMADev, dev_attr.max_sge, MAX_SGE),
DEFINE_PROP_INT32("dev-caps-max-cq", PVRDMADev, dev_attr.max_cq, MAX_CQ),
DEFINE_PROP_INT32("dev-caps-max-mr", PVRDMADev, dev_attr.max_mr, MAX_MR),
DEFINE_PROP_INT32("dev-caps-max-pd", PVRDMADev, dev_attr.max_pd, MAX_PD),
@@ -549,8 +548,9 @@ static void init_dev_caps(PVRDMADev *dev)
sizeof(struct pvrdma_rq_wqe_hdr));
dev->dev_attr.max_qp_wr = pg_tbl_bytes /
- (wr_sz + sizeof(struct pvrdma_sge) * MAX_SGE) -
- TARGET_PAGE_SIZE; /* First page is ring state */
+ (wr_sz + sizeof(struct pvrdma_sge) *
+ dev->dev_attr.max_sge) - TARGET_PAGE_SIZE;
+ /* First page is ring state ^^^^ */
pr_dbg("max_qp_wr=%d\n", dev->dev_attr.max_qp_wr);
dev->dev_attr.max_cqe = pg_tbl_bytes / sizeof(struct pvrdma_cqe) -
@@ -626,8 +626,6 @@ static void pvrdma_realize(PCIDevice *pdev, Error **errp)
init_regs(pdev);
- init_dev_caps(dev);
-
rc = init_msix(pdev, errp);
if (rc) {
goto out;
@@ -640,6 +638,8 @@ static void pvrdma_realize(PCIDevice *pdev, Error **errp)
goto out;
}
+ init_dev_caps(dev);
+
rc = rdma_rm_init(&dev->rdma_dev_res, &dev->dev_attr, errp);
if (rc) {
goto out;
diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c
index 300471a4c9..ce5a60e184 100644
--- a/hw/rdma/vmw/pvrdma_qp_ops.c
+++ b/hw/rdma/vmw/pvrdma_qp_ops.c
@@ -121,6 +121,16 @@ static void pvrdma_qp_ops_comp_handler(void *ctx, struct ibv_wc *wc)
g_free(ctx);
}
+static void complete_with_error(uint32_t vendor_err, void *ctx)
+{
+ struct ibv_wc wc = {0};
+
+ wc.status = IBV_WC_GENERAL_ERR;
+ wc.vendor_err = vendor_err;
+
+ pvrdma_qp_ops_comp_handler(ctx, &wc);
+}
+
void pvrdma_qp_ops_fini(void)
{
rdma_backend_unregister_comp_handler();
@@ -133,7 +143,7 @@ int pvrdma_qp_ops_init(void)
return 0;
}
-int pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle)
+void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle)
{
RdmaRmQP *qp;
PvrdmaSqWqe *wqe;
@@ -145,7 +155,8 @@ int pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle)
qp = rdma_rm_get_qp(&dev->rdma_dev_res, qp_handle);
if (unlikely(!qp)) {
- return -EINVAL;
+ pr_dbg("Invalid qpn\n");
+ return;
}
ring = (PvrdmaRing *)qp->opaque;
@@ -168,7 +179,8 @@ int pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle)
sgid = rdma_rm_get_gid(&dev->rdma_dev_res, wqe->hdr.wr.ud.av.gid_index);
if (!sgid) {
pr_dbg("Fail to get gid for idx %d\n", wqe->hdr.wr.ud.av.gid_index);
- return -EIO;
+ complete_with_error(VENDOR_ERR_INV_GID_IDX, comp_ctx);
+ continue;
}
pr_dbg("sgid_id=%d, sgid=0x%llx\n", wqe->hdr.wr.ud.av.gid_index,
sgid->global.interface_id);
@@ -179,7 +191,15 @@ int pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle)
if (sgid_idx <= 0) {
pr_dbg("Fail to get bk sgid_idx for sgid_idx %d\n",
wqe->hdr.wr.ud.av.gid_index);
- return -EIO;
+ complete_with_error(VENDOR_ERR_INV_GID_IDX, comp_ctx);
+ continue;
+ }
+
+ if (wqe->hdr.num_sge > dev->dev_attr.max_sge) {
+ pr_dbg("Invalid num_sge=%d (max %d)\n", wqe->hdr.num_sge,
+ dev->dev_attr.max_sge);
+ complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx);
+ continue;
}
rdma_backend_post_send(&dev->backend_dev, &qp->backend_qp, qp->qp_type,
@@ -193,11 +213,9 @@ int pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle)
wqe = pvrdma_ring_next_elem_read(ring);
}
-
- return 0;
}
-int pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle)
+void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle)
{
RdmaRmQP *qp;
PvrdmaRqWqe *wqe;
@@ -207,7 +225,8 @@ int pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle)
qp = rdma_rm_get_qp(&dev->rdma_dev_res, qp_handle);
if (unlikely(!qp)) {
- return -EINVAL;
+ pr_dbg("Invalid qpn\n");
+ return;
}
ring = &((PvrdmaRing *)qp->opaque)[1];
@@ -227,6 +246,13 @@ int pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle)
comp_ctx->cqe.qp = qp_handle;
comp_ctx->cqe.opcode = IBV_WC_RECV;
+ if (wqe->hdr.num_sge > dev->dev_attr.max_sge) {
+ pr_dbg("Invalid num_sge=%d (max %d)\n", wqe->hdr.num_sge,
+ dev->dev_attr.max_sge);
+ complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx);
+ continue;
+ }
+
rdma_backend_post_recv(&dev->backend_dev, &dev->rdma_dev_res,
&qp->backend_qp, qp->qp_type,
(struct ibv_sge *)&wqe->sge[0], wqe->hdr.num_sge,
@@ -236,8 +262,6 @@ int pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle)
wqe = pvrdma_ring_next_elem_read(ring);
}
-
- return 0;
}
void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle)
diff --git a/hw/rdma/vmw/pvrdma_qp_ops.h b/hw/rdma/vmw/pvrdma_qp_ops.h
index ac46bf7fdf..31cb48ba29 100644
--- a/hw/rdma/vmw/pvrdma_qp_ops.h
+++ b/hw/rdma/vmw/pvrdma_qp_ops.h
@@ -20,8 +20,8 @@
int pvrdma_qp_ops_init(void);
void pvrdma_qp_ops_fini(void);
-int pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle);
-int pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle);
+void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle);
+void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle);
void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle);
#endif
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 15759b6514..f017c1ded0 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -660,7 +660,7 @@ void s390_pci_iommu_enable(S390PCIIOMMU *iommu)
char *name = g_strdup_printf("iommu-s390-%04x", iommu->pbdev->uid);
memory_region_init_iommu(&iommu->iommu_mr, sizeof(iommu->iommu_mr),
TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr),
- name, iommu->pal + 1);
+ name, iommu->pal - iommu->pba + 1);
iommu->enabled = true;
memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&iommu->iommu_mr));
g_free(name);
@@ -818,28 +818,43 @@ static bool s390_pci_alloc_idx(S390pciState *s, S390PCIBusDevice *pbdev)
}
pbdev->idx = idx;
- s->next_idx = (idx + 1) & FH_MASK_INDEX;
-
return true;
}
+static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
+ PCIDevice *pdev = PCI_DEVICE(dev);
+
+ if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
+ error_setg(errp, "multifunction not supported in s390");
+ return;
+ }
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
+ S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev);
+
+ if (!s390_pci_alloc_idx(s, pbdev)) {
+ error_setg(errp, "no slot for plugging zpci device");
+ return;
+ }
+ }
+}
+
static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
+ S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
PCIDevice *pdev = NULL;
S390PCIBusDevice *pbdev = NULL;
- S390pciState *s = s390_get_phb();
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
BusState *bus;
PCIBridge *pb = PCI_BRIDGE(dev);
PCIDevice *pdev = PCI_DEVICE(dev);
- if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
- error_setg(errp, "multifunction not supported in s390");
- return;
- }
-
pci_bridge_map_irq(pb, dev->id, s390_pci_map_irq);
pci_setup_iommu(&pb->sec_bus, s390_pci_dma_iommu, s);
@@ -859,11 +874,6 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
pdev = PCI_DEVICE(dev);
- if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
- error_setg(errp, "multifunction not supported in s390");
- return;
- }
-
if (!dev->id) {
/* In the case the PCI device does not define an id */
/* we generate one based on the PCI address */
@@ -899,19 +909,19 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
}
if (dev->hotplugged) {
- s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY,
+ s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED ,
pbdev->fh, pbdev->fid);
}
} else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
pbdev = S390_PCI_DEVICE(dev);
- if (!s390_pci_alloc_idx(s, pbdev)) {
- error_setg(errp, "no slot for plugging zpci device");
- return;
- }
+ /* the allocated idx is actually getting used */
+ s->next_idx = (pbdev->idx + 1) & FH_MASK_INDEX;
pbdev->fh = pbdev->idx;
QTAILQ_INSERT_TAIL(&s->zpci_devs, pbdev, link);
g_hash_table_insert(s->zpci_table, &pbdev->idx, pbdev);
+ } else {
+ g_assert_not_reached();
}
}
@@ -935,11 +945,11 @@ static void s390_pcihost_timer_cb(void *opaque)
static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
+ S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
PCIDevice *pci_dev = NULL;
PCIBus *bus;
int32_t devfn;
S390PCIBusDevice *pbdev = NULL;
- S390pciState *s = s390_get_phb();
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
error_setg(errp, "PCI bridge hot unplug currently not supported");
@@ -956,6 +966,8 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
} else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
pbdev = S390_PCI_DEVICE(dev);
pci_dev = pbdev->pdev;
+ } else {
+ g_assert_not_reached();
}
switch (pbdev->state) {
@@ -964,6 +976,9 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
case ZPCI_FS_STANDBY:
break;
default:
+ if (pbdev->release_timer) {
+ return;
+ }
s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST,
pbdev->fh, pbdev->fid);
pbdev->release_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
@@ -974,7 +989,7 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
return;
}
- if (pbdev->release_timer && timer_pending(pbdev->release_timer)) {
+ if (pbdev->release_timer) {
timer_del(pbdev->release_timer);
timer_free(pbdev->release_timer);
pbdev->release_timer = NULL;
@@ -985,6 +1000,7 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
bus = pci_get_bus(pci_dev);
devfn = pci_dev->devfn;
object_unparent(OBJECT(pci_dev));
+ fmb_timer_free(pbdev);
s390_pci_msix_free(pbdev);
s390_pci_iommu_free(s, bus, devfn);
pbdev->pdev = NULL;
@@ -1041,6 +1057,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, void *data)
dc->reset = s390_pcihost_reset;
dc->realize = s390_pcihost_realize;
+ hc->pre_plug = s390_pcihost_pre_plug;
hc->plug = s390_pcihost_plug;
hc->unplug = s390_pcihost_unplug;
msi_nonbroken = true;
@@ -1132,6 +1149,7 @@ static void s390_pci_device_realize(DeviceState *dev, Error **errp)
}
zpci->state = ZPCI_FS_RESERVED;
+ zpci->fmb.format = ZPCI_FMB_FORMAT;
}
static void s390_pci_device_reset(DeviceState *dev)
@@ -1156,7 +1174,7 @@ static void s390_pci_device_reset(DeviceState *dev)
pci_dereg_ioat(pbdev->iommu);
}
- pbdev->fmb_addr = 0;
+ fmb_timer_free(pbdev);
}
static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name,
diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h
index f47a0f2da5..dadad1f758 100644
--- a/hw/s390x/s390-pci-bus.h
+++ b/hw/s390x/s390-pci-bus.h
@@ -285,6 +285,33 @@ typedef struct S390PCIIOMMUTable {
S390PCIIOMMU *iommu[PCI_SLOT_MAX];
} S390PCIIOMMUTable;
+/* Function Measurement Block */
+#define DEFAULT_MUI 4000
+#define UPDATE_U_BIT 0x1ULL
+#define FMBK_MASK 0xfULL
+
+typedef struct ZpciFmbFmt0 {
+ uint64_t dma_rbytes;
+ uint64_t dma_wbytes;
+} ZpciFmbFmt0;
+
+#define ZPCI_FMB_CNT_LD 0
+#define ZPCI_FMB_CNT_ST 1
+#define ZPCI_FMB_CNT_STB 2
+#define ZPCI_FMB_CNT_RPCIT 3
+#define ZPCI_FMB_CNT_MAX 4
+
+#define ZPCI_FMB_FORMAT 0
+
+typedef struct ZpciFmb {
+ uint32_t format;
+ uint32_t sample;
+ uint64_t last_update;
+ uint64_t counter[ZPCI_FMB_CNT_MAX];
+ ZpciFmbFmt0 fmt0;
+} ZpciFmb;
+QEMU_BUILD_BUG_MSG(offsetof(ZpciFmb, fmt0) != 48, "padding in ZpciFmb");
+
struct S390PCIBusDevice {
DeviceState qdev;
PCIDevice *pdev;
@@ -296,6 +323,8 @@ struct S390PCIBusDevice {
uint32_t fid;
bool fid_defined;
uint64_t fmb_addr;
+ ZpciFmb fmb;
+ QEMUTimer *fmb_timer;
uint8_t isc;
uint16_t noi;
uint16_t maxstbl;
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 7b61367ee3..be2896232d 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -19,6 +19,7 @@
#include "exec/memory-internal.h"
#include "qemu/error-report.h"
#include "sysemu/hw_accel.h"
+#include "hw/s390x/tod.h"
#ifndef DEBUG_S390PCI_INST
#define DEBUG_S390PCI_INST 0
@@ -293,7 +294,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra)
resgrp->fr = 1;
stq_p(&resgrp->dasm, 0);
stq_p(&resgrp->msia, ZPCI_MSI_ADDR);
- stw_p(&resgrp->mui, 0);
+ stw_p(&resgrp->mui, DEFAULT_MUI);
stw_p(&resgrp->i, 128);
stw_p(&resgrp->maxstbl, 128);
resgrp->version = 0;
@@ -456,6 +457,8 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
return 0;
}
+ pbdev->fmb.counter[ZPCI_FMB_CNT_LD]++;
+
env->regs[r1] = data;
setcc(cpu, ZPCI_PCI_LS_OK);
return 0;
@@ -561,6 +564,8 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
return 0;
}
+ pbdev->fmb.counter[ZPCI_FMB_CNT_ST]++;
+
setcc(cpu, ZPCI_PCI_LS_OK);
return 0;
}
@@ -681,6 +686,7 @@ err:
s390_set_status_code(env, r1, ZPCI_PCI_ST_FUNC_IN_ERR);
s390_pci_generate_error_event(error, pbdev->fh, pbdev->fid, start, 0);
} else {
+ pbdev->fmb.counter[ZPCI_FMB_CNT_RPCIT]++;
setcc(cpu, ZPCI_PCI_LS_OK);
}
return 0;
@@ -783,6 +789,8 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
}
}
+ pbdev->fmb.counter[ZPCI_FMB_CNT_STB]++;
+
setcc(cpu, ZPCI_PCI_LS_OK);
return 0;
@@ -889,6 +897,99 @@ void pci_dereg_ioat(S390PCIIOMMU *iommu)
iommu->g_iota = 0;
}
+void fmb_timer_free(S390PCIBusDevice *pbdev)
+{
+ if (pbdev->fmb_timer) {
+ timer_del(pbdev->fmb_timer);
+ timer_free(pbdev->fmb_timer);
+ pbdev->fmb_timer = NULL;
+ }
+ pbdev->fmb_addr = 0;
+ memset(&pbdev->fmb, 0, sizeof(ZpciFmb));
+}
+
+static int fmb_do_update(S390PCIBusDevice *pbdev, int offset, uint64_t val,
+ int len)
+{
+ MemTxResult ret;
+ uint64_t dst = pbdev->fmb_addr + offset;
+
+ switch (len) {
+ case 8:
+ address_space_stq_be(&address_space_memory, dst, val,
+ MEMTXATTRS_UNSPECIFIED,
+ &ret);
+ break;
+ case 4:
+ address_space_stl_be(&address_space_memory, dst, val,
+ MEMTXATTRS_UNSPECIFIED,
+ &ret);
+ break;
+ case 2:
+ address_space_stw_be(&address_space_memory, dst, val,
+ MEMTXATTRS_UNSPECIFIED,
+ &ret);
+ break;
+ case 1:
+ address_space_stb(&address_space_memory, dst, val,
+ MEMTXATTRS_UNSPECIFIED,
+ &ret);
+ break;
+ default:
+ ret = MEMTX_ERROR;
+ break;
+ }
+ if (ret != MEMTX_OK) {
+ s390_pci_generate_error_event(ERR_EVENT_FMBA, pbdev->fh, pbdev->fid,
+ pbdev->fmb_addr, 0);
+ fmb_timer_free(pbdev);
+ }
+
+ return ret;
+}
+
+static void fmb_update(void *opaque)
+{
+ S390PCIBusDevice *pbdev = opaque;
+ int64_t t = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
+ int i;
+
+ /* Update U bit */
+ pbdev->fmb.last_update *= 2;
+ pbdev->fmb.last_update |= UPDATE_U_BIT;
+ if (fmb_do_update(pbdev, offsetof(ZpciFmb, last_update),
+ pbdev->fmb.last_update,
+ sizeof(pbdev->fmb.last_update))) {
+ return;
+ }
+
+ /* Update FMB sample count */
+ if (fmb_do_update(pbdev, offsetof(ZpciFmb, sample),
+ pbdev->fmb.sample++,
+ sizeof(pbdev->fmb.sample))) {
+ return;
+ }
+
+ /* Update FMB counters */
+ for (i = 0; i < ZPCI_FMB_CNT_MAX; i++) {
+ if (fmb_do_update(pbdev, offsetof(ZpciFmb, counter[i]),
+ pbdev->fmb.counter[i],
+ sizeof(pbdev->fmb.counter[0]))) {
+ return;
+ }
+ }
+
+ /* Clear U bit and update the time */
+ pbdev->fmb.last_update = time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+ pbdev->fmb.last_update *= 2;
+ if (fmb_do_update(pbdev, offsetof(ZpciFmb, last_update),
+ pbdev->fmb.last_update,
+ sizeof(pbdev->fmb.last_update))) {
+ return;
+ }
+ timer_mod(pbdev->fmb_timer, t + DEFAULT_MUI);
+}
+
int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
uintptr_t ra)
{
@@ -1018,9 +1119,35 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
}
break;
- case ZPCI_MOD_FC_SET_MEASURE:
- pbdev->fmb_addr = ldq_p(&fib.fmb_addr);
+ case ZPCI_MOD_FC_SET_MEASURE: {
+ uint64_t fmb_addr = ldq_p(&fib.fmb_addr);
+
+ if (fmb_addr & FMBK_MASK) {
+ cc = ZPCI_PCI_LS_ERR;
+ s390_pci_generate_error_event(ERR_EVENT_FMBPRO, pbdev->fh,
+ pbdev->fid, fmb_addr, 0);
+ fmb_timer_free(pbdev);
+ break;
+ }
+
+ if (!fmb_addr) {
+ /* Stop updating FMB. */
+ fmb_timer_free(pbdev);
+ break;
+ }
+
+ if (!pbdev->fmb_timer) {
+ pbdev->fmb_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ fmb_update, pbdev);
+ } else if (timer_pending(pbdev->fmb_timer)) {
+ /* Remove pending timer to update FMB address. */
+ timer_del(pbdev->fmb_timer);
+ }
+ pbdev->fmb_addr = fmb_addr;
+ timer_mod(pbdev->fmb_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + DEFAULT_MUI);
break;
+ }
default:
s390_program_interrupt(&cpu->env, PGM_OPERAND, 6, ra);
cc = ZPCI_PCI_LS_ERR;
diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h
index 91c3d61f2a..fa3bf8b5aa 100644
--- a/hw/s390x/s390-pci-inst.h
+++ b/hw/s390x/s390-pci-inst.h
@@ -303,6 +303,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
uintptr_t ra);
int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
uintptr_t ra);
+void fmb_timer_free(S390PCIBusDevice *pbdev);
#define ZPCI_IO_BAR_MIN 0
#define ZPCI_IO_BAR_MAX 5
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
index 7f21b4f9d6..61e2e57da9 100644
--- a/hw/scsi/vhost-scsi.c
+++ b/hw/scsi/vhost-scsi.c
@@ -215,6 +215,7 @@ static void vhost_scsi_unrealize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev);
+ struct vhost_virtqueue *vqs = vsc->dev.vqs;
migrate_del_blocker(vsc->migration_blocker);
error_free(vsc->migration_blocker);
@@ -223,7 +224,7 @@ static void vhost_scsi_unrealize(DeviceState *dev, Error **errp)
vhost_scsi_set_status(vdev, 0);
vhost_dev_cleanup(&vsc->dev);
- g_free(vsc->dev.vqs);
+ g_free(vqs);
virtio_scsi_common_unrealize(dev, errp);
}
diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c
index 2e1ba4a87b..6728878a52 100644
--- a/hw/scsi/vhost-user-scsi.c
+++ b/hw/scsi/vhost-user-scsi.c
@@ -121,12 +121,13 @@ static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp)
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserSCSI *s = VHOST_USER_SCSI(dev);
VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
+ struct vhost_virtqueue *vqs = vsc->dev.vqs;
/* This will stop the vhost backend. */
vhost_user_scsi_set_status(vdev, 0);
vhost_dev_cleanup(&vsc->dev);
- g_free(vsc->dev.vqs);
+ g_free(vqs);
virtio_scsi_common_unrealize(dev, errp);
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
index 1dc9f8bf2c..700c878622 100644
--- a/hw/tpm/Makefile.objs
+++ b/hw/tpm/Makefile.objs
@@ -1,4 +1,5 @@
common-obj-y += tpm_util.o
+obj-y += tpm_ppi.o
common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o
common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index a92dd50437..3087acc4ab 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -29,6 +29,7 @@
#include "sysemu/reset.h"
#include "tpm_int.h"
#include "tpm_util.h"
+#include "tpm_ppi.h"
#include "trace.h"
typedef struct CRBState {
@@ -41,6 +42,9 @@ typedef struct CRBState {
MemoryRegion cmdmem;
size_t be_buffer_size;
+
+ bool ppi_enabled;
+ TPMPPI ppi;
} CRBState;
#define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB)
@@ -221,6 +225,7 @@ static const VMStateDescription vmstate_tpm_crb = {
static Property tpm_crb_properties[] = {
DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe),
+ DEFINE_PROP_BOOL("ppi", CRBState, ppi_enabled, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -228,6 +233,9 @@ static void tpm_crb_reset(void *dev)
{
CRBState *s = CRB(dev);
+ if (s->ppi_enabled) {
+ tpm_ppi_reset(&s->ppi);
+ }
tpm_backend_reset(s->tpmbe);
memset(s->regs, 0, sizeof(s->regs));
@@ -291,6 +299,11 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(get_system_memory(),
TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem);
+ if (s->ppi_enabled) {
+ tpm_ppi_init(&s->ppi, get_system_memory(),
+ TPM_PPI_ADDR_BASE, OBJECT(s));
+ }
+
qemu_register_reset(tpm_crb_reset, dev);
}
diff --git a/hw/tpm/tpm_ppi.c b/hw/tpm/tpm_ppi.c
new file mode 100644
index 0000000000..cd8205f212
--- /dev/null
+++ b/hw/tpm/tpm_ppi.c
@@ -0,0 +1,53 @@
+/*
+ * tpm_ppi.c - TPM Physical Presence Interface
+ *
+ * Copyright (C) 2018 IBM Corporation
+ *
+ * Authors:
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "qapi/error.h"
+#include "cpu.h"
+#include "sysemu/memory_mapping.h"
+#include "sysemu/reset.h"
+#include "migration/vmstate.h"
+#include "tpm_ppi.h"
+#include "trace.h"
+
+void tpm_ppi_reset(TPMPPI *tpmppi)
+{
+ if (tpmppi->buf[0x15a /* movv, docs/specs/tpm.txt */] & 0x1) {
+ GuestPhysBlockList guest_phys_blocks;
+ GuestPhysBlock *block;
+
+ guest_phys_blocks_init(&guest_phys_blocks);
+ guest_phys_blocks_append(&guest_phys_blocks);
+ QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
+ trace_tpm_ppi_memset(block->host_addr,
+ block->target_end - block->target_start);
+ memset(block->host_addr, 0,
+ block->target_end - block->target_start);
+ memory_region_set_dirty(block->mr, 0,
+ block->target_end - block->target_start);
+ }
+ guest_phys_blocks_free(&guest_phys_blocks);
+ }
+}
+
+void tpm_ppi_init(TPMPPI *tpmppi, struct MemoryRegion *m,
+ hwaddr addr, Object *obj)
+{
+ tpmppi->buf = g_malloc0(HOST_PAGE_ALIGN(TPM_PPI_ADDR_SIZE));
+ memory_region_init_ram_device_ptr(&tpmppi->ram, obj, "tpm-ppi",
+ TPM_PPI_ADDR_SIZE, tpmppi->buf);
+ vmstate_register_ram(&tpmppi->ram, DEVICE(obj));
+
+ memory_region_add_subregion(m, addr, &tpmppi->ram);
+}
diff --git a/hw/tpm/tpm_ppi.h b/hw/tpm/tpm_ppi.h
new file mode 100644
index 0000000000..d33ef27de6
--- /dev/null
+++ b/hw/tpm/tpm_ppi.h
@@ -0,0 +1,46 @@
+/*
+ * TPM Physical Presence Interface
+ *
+ * Copyright (C) 2018 IBM Corporation
+ *
+ * Authors:
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef TPM_TPM_PPI_H
+#define TPM_TPM_PPI_H
+
+#include "hw/acpi/tpm.h"
+#include "exec/address-spaces.h"
+
+typedef struct TPMPPI {
+ MemoryRegion ram;
+ uint8_t *buf;
+} TPMPPI;
+
+/**
+ * tpm_ppi_init:
+ * @tpmppi: a TPMPPI
+ * @m: the address-space / MemoryRegion to use
+ * @addr: the address of the PPI region
+ * @obj: the owner object
+ *
+ * Register the TPM PPI memory region at @addr on the given address
+ * space for the object @obj.
+ **/
+void tpm_ppi_init(TPMPPI *tpmppi, struct MemoryRegion *m,
+ hwaddr addr, Object *obj);
+
+/**
+ * tpm_ppi_reset:
+ * @tpmppi: a TPMPPI
+ *
+ * Function to call on machine reset. It will check if the "Memory
+ * overwrite" variable is set, and perform a memory clear on volatile
+ * memory if requested.
+ **/
+void tpm_ppi_reset(TPMPPI *tpmppi);
+
+#endif /* TPM_TPM_PPI_H */
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c
index 2563d7501f..fd6bb9b59a 100644
--- a/hw/tpm/tpm_tis.c
+++ b/hw/tpm/tpm_tis.c
@@ -31,6 +31,7 @@
#include "sysemu/tpm_backend.h"
#include "tpm_int.h"
#include "tpm_util.h"
+#include "tpm_ppi.h"
#include "trace.h"
#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */
@@ -81,6 +82,9 @@ typedef struct TPMState {
TPMVersion be_tpm_version;
size_t be_buffer_size;
+
+ bool ppi_enabled;
+ TPMPPI ppi;
} TPMState;
#define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS)
@@ -868,6 +872,9 @@ static void tpm_tis_reset(DeviceState *dev)
s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver),
TPM_TIS_BUFFER_MAX);
+ if (s->ppi_enabled) {
+ tpm_ppi_reset(&s->ppi);
+ }
tpm_backend_reset(s->be_driver);
s->active_locty = TPM_TIS_NO_LOCALITY;
@@ -954,6 +961,7 @@ static const VMStateDescription vmstate_tpm_tis = {
static Property tpm_tis_properties[] = {
DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ),
DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver),
+ DEFINE_PROP_BOOL("ppi", TPMState, ppi_enabled, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -980,6 +988,11 @@ static void tpm_tis_realizefn(DeviceState *dev, Error **errp)
memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)),
TPM_TIS_ADDR_BASE, &s->mmio);
+
+ if (s->ppi_enabled) {
+ tpm_ppi_init(&s->ppi, isa_address_space(ISA_DEVICE(dev)),
+ TPM_PPI_ADDR_BASE, OBJECT(s));
+ }
}
static void tpm_tis_initfn(Object *obj)
diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events
index 25bee0cecf..920d32ad55 100644
--- a/hw/tpm/trace-events
+++ b/hw/tpm/trace-events
@@ -51,3 +51,6 @@ tpm_tis_mmio_write_init_abort(void) "Initiating abort"
tpm_tis_mmio_write_lowering_irq(void) "Lowering IRQ"
tpm_tis_mmio_write_data2send(uint32_t value, unsigned size) "Data to send to TPM: 0x%08x (size=%d)"
tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u"
+
+# hw/tpm/tpm_ppi.c
+tpm_ppi_memset(uint8_t *ptr, size_t size) "memset: %p %zu"
diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs
index 1b2799cfd8..ea7913d532 100644
--- a/hw/virtio/Makefile.objs
+++ b/hw/virtio/Makefile.objs
@@ -11,6 +11,21 @@ obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-p
obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o
obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o
+ifeq ($(CONFIG_PCI),y)
+obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock-pci.o
+obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk-pci.o
+obj-$(CONFIG_VHOST_USER_SCSI) += vhost-user-scsi-pci.o
+obj-$(CONFIG_VHOST_SCSI) += vhost-scsi-pci.o
+obj-$(CONFIG_VIRTIO_INPUT_HOST) += virtio-input-host-pci.o
+obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-pci.o
+obj-$(CONFIG_VIRTIO_RNG) += virtio-rng-pci.o
+obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon-pci.o
+obj-$(CONFIG_VIRTIO_9P) += virtio-9p-pci.o
+obj-$(CONFIG_VIRTIO_SCSI) += virtio-scsi-pci.o
+obj-$(CONFIG_VIRTIO_BLK) += virtio-blk-pci.o
+obj-$(CONFIG_VIRTIO_NET) += virtio-net-pci.o
+obj-$(CONFIG_VIRTIO_SERIAL) += virtio-serial-pci.o
+endif
endif
common-obj-$(call lnot,$(call land,$(CONFIG_VIRTIO),$(CONFIG_LINUX))) += vhost-stub.o
diff --git a/hw/virtio/vhost-scsi-pci.c b/hw/virtio/vhost-scsi-pci.c
new file mode 100644
index 0000000000..523f7cb3ce
--- /dev/null
+++ b/hw/virtio/vhost-scsi-pci.c
@@ -0,0 +1,97 @@
+/*
+ * Vhost scsi PCI bindings
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ *
+ * Changes for QEMU mainline + tcm_vhost kernel upstream:
+ * Nicholas Bellinger <nab@risingtidesystems.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "standard-headers/linux/virtio_pci.h"
+#include "hw/virtio/vhost-scsi.h"
+#include "qapi/error.h"
+#include "virtio-pci.h"
+
+typedef struct VHostSCSIPCI VHostSCSIPCI;
+
+/*
+ * vhost-scsi-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VHOST_SCSI_PCI "vhost-scsi-pci-base"
+#define VHOST_SCSI_PCI(obj) \
+ OBJECT_CHECK(VHostSCSIPCI, (obj), TYPE_VHOST_SCSI_PCI)
+
+struct VHostSCSIPCI {
+ VirtIOPCIProxy parent_obj;
+ VHostSCSI vdev;
+};
+
+static Property vhost_scsi_pci_properties[] = {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vhost_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VHostSCSIPCI *dev = VHOST_SCSI_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+ VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
+
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = vs->conf.num_queues + 3;
+ }
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+ k->realize = vhost_scsi_pci_realize;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ dc->props = vhost_scsi_pci_properties;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
+ pcidev_k->revision = 0x00;
+ pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
+}
+
+static void vhost_scsi_pci_instance_init(Object *obj)
+{
+ VHostSCSIPCI *dev = VHOST_SCSI_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VHOST_SCSI);
+ object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
+ "bootindex", &error_abort);
+}
+
+static const VirtioPCIDeviceTypeInfo vhost_scsi_pci_info = {
+ .base_name = TYPE_VHOST_SCSI_PCI,
+ .generic_name = "vhost-scsi-pci",
+ .transitional_name = "vhost-scsi-pci-transitional",
+ .non_transitional_name = "vhost-scsi-pci-non-transitional",
+ .instance_size = sizeof(VHostSCSIPCI),
+ .instance_init = vhost_scsi_pci_instance_init,
+ .class_init = vhost_scsi_pci_class_init,
+};
+
+static void vhost_scsi_pci_register(void)
+{
+ virtio_pci_types_register(&vhost_scsi_pci_info);
+}
+
+type_init(vhost_scsi_pci_register)
diff --git a/hw/virtio/vhost-user-blk-pci.c b/hw/virtio/vhost-user-blk-pci.c
new file mode 100644
index 0000000000..ca66c217a7
--- /dev/null
+++ b/hw/virtio/vhost-user-blk-pci.c
@@ -0,0 +1,103 @@
+/*
+ * Vhost user blk PCI Bindings
+ *
+ * Copyright(C) 2017 Intel Corporation.
+ *
+ * Authors:
+ * Changpeng Liu <changpeng.liu@intel.com>
+ *
+ * Largely based on the "vhost-user-scsi.c" and "vhost-scsi.c" implemented by:
+ * Felipe Franciosi <felipe@nutanix.com>
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Nicholas Bellinger <nab@risingtidesystems.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "standard-headers/linux/virtio_pci.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/vhost-user-blk.h"
+#include "hw/pci/pci.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "virtio-pci.h"
+
+typedef struct VHostUserBlkPCI VHostUserBlkPCI;
+
+/*
+ * vhost-user-blk-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VHOST_USER_BLK_PCI "vhost-user-blk-pci-base"
+#define VHOST_USER_BLK_PCI(obj) \
+ OBJECT_CHECK(VHostUserBlkPCI, (obj), TYPE_VHOST_USER_BLK_PCI)
+
+struct VHostUserBlkPCI {
+ VirtIOPCIProxy parent_obj;
+ VHostUserBlk vdev;
+};
+
+static Property vhost_user_blk_pci_properties[] = {
+ DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = dev->vdev.num_queues + 1;
+ }
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void vhost_user_blk_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ dc->props = vhost_user_blk_pci_properties;
+ k->realize = vhost_user_blk_pci_realize;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK;
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+ pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
+}
+
+static void vhost_user_blk_pci_instance_init(Object *obj)
+{
+ VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VHOST_USER_BLK);
+ object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
+ "bootindex", &error_abort);
+}
+
+static const VirtioPCIDeviceTypeInfo vhost_user_blk_pci_info = {
+ .base_name = TYPE_VHOST_USER_BLK_PCI,
+ .generic_name = "vhost-user-blk-pci",
+ .transitional_name = "vhost-user-blk-pci-transitional",
+ .non_transitional_name = "vhost-user-blk-pci-non-transitional",
+ .instance_size = sizeof(VHostUserBlkPCI),
+ .instance_init = vhost_user_blk_pci_instance_init,
+ .class_init = vhost_user_blk_pci_class_init,
+};
+
+static void vhost_user_blk_pci_register(void)
+{
+ virtio_pci_types_register(&vhost_user_blk_pci_info);
+}
+
+type_init(vhost_user_blk_pci_register)
diff --git a/hw/virtio/vhost-user-scsi-pci.c b/hw/virtio/vhost-user-scsi-pci.c
new file mode 100644
index 0000000000..46f7193cc7
--- /dev/null
+++ b/hw/virtio/vhost-user-scsi-pci.c
@@ -0,0 +1,103 @@
+/*
+ * Vhost user scsi PCI Bindings
+ *
+ * Copyright (c) 2016 Nutanix Inc. All rights reserved.
+ *
+ * Author:
+ * Felipe Franciosi <felipe@nutanix.com>
+ *
+ * This work is largely based on the "vhost-scsi" implementation by:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Nicholas Bellinger <nab@risingtidesystems.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "standard-headers/linux/virtio_pci.h"
+#include "hw/virtio/vhost-user-scsi.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-scsi.h"
+#include "hw/pci/pci.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/msix.h"
+#include "hw/loader.h"
+#include "sysemu/kvm.h"
+#include "virtio-pci.h"
+
+typedef struct VHostUserSCSIPCI VHostUserSCSIPCI;
+
+#define TYPE_VHOST_USER_SCSI_PCI "vhost-user-scsi-pci-base"
+#define VHOST_USER_SCSI_PCI(obj) \
+ OBJECT_CHECK(VHostUserSCSIPCI, (obj), TYPE_VHOST_USER_SCSI_PCI)
+
+struct VHostUserSCSIPCI {
+ VirtIOPCIProxy parent_obj;
+ VHostUserSCSI vdev;
+};
+
+static Property vhost_user_scsi_pci_properties[] = {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+ VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
+
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = vs->conf.num_queues + 3;
+ }
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void vhost_user_scsi_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+ k->realize = vhost_user_scsi_pci_realize;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ dc->props = vhost_user_scsi_pci_properties;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
+ pcidev_k->revision = 0x00;
+ pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
+}
+
+static void vhost_user_scsi_pci_instance_init(Object *obj)
+{
+ VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VHOST_USER_SCSI);
+ object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
+ "bootindex", &error_abort);
+}
+
+static const VirtioPCIDeviceTypeInfo vhost_user_scsi_pci_info = {
+ .base_name = TYPE_VHOST_USER_SCSI_PCI,
+ .generic_name = "vhost-user-scsi-pci",
+ .transitional_name = "vhost-user-scsi-pci-transitional",
+ .non_transitional_name = "vhost-user-scsi-pci-non-transitional",
+ .instance_size = sizeof(VHostUserSCSIPCI),
+ .instance_init = vhost_user_scsi_pci_instance_init,
+ .class_init = vhost_user_scsi_pci_class_init,
+};
+
+static void vhost_user_scsi_pci_register(void)
+{
+ virtio_pci_types_register(&vhost_user_scsi_pci_info);
+}
+
+type_init(vhost_user_scsi_pci_register)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index e09bed0e4a..564a31d12c 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -207,7 +207,7 @@ struct vhost_user {
static bool ioeventfd_enabled(void)
{
- return kvm_enabled() && kvm_eventfds_enabled();
+ return !kvm_enabled() || kvm_eventfds_enabled();
}
static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
diff --git a/hw/virtio/vhost-vsock-pci.c b/hw/virtio/vhost-vsock-pci.c
new file mode 100644
index 0000000000..6f43ca35fb
--- /dev/null
+++ b/hw/virtio/vhost-vsock-pci.c
@@ -0,0 +1,86 @@
+/*
+ * Vhost vsock PCI Bindings
+ *
+ * Copyright 2015 Red Hat, Inc.
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "virtio-pci.h"
+#include "hw/virtio/vhost-vsock.h"
+
+typedef struct VHostVSockPCI VHostVSockPCI;
+
+/*
+ * vhost-vsock-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VHOST_VSOCK_PCI "vhost-vsock-pci-base"
+#define VHOST_VSOCK_PCI(obj) \
+ OBJECT_CHECK(VHostVSockPCI, (obj), TYPE_VHOST_VSOCK_PCI)
+
+struct VHostVSockPCI {
+ VirtIOPCIProxy parent_obj;
+ VHostVSock vdev;
+};
+
+/* vhost-vsock-pci */
+
+static Property vhost_vsock_pci_properties[] = {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vhost_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VHostVSockPCI *dev = VHOST_VSOCK_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void vhost_vsock_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+ k->realize = vhost_vsock_pci_realize;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->props = vhost_vsock_pci_properties;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_VSOCK;
+ pcidev_k->revision = 0x00;
+ pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
+}
+
+static void vhost_vsock_pci_instance_init(Object *obj)
+{
+ VHostVSockPCI *dev = VHOST_VSOCK_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VHOST_VSOCK);
+}
+
+static const VirtioPCIDeviceTypeInfo vhost_vsock_pci_info = {
+ .base_name = TYPE_VHOST_VSOCK_PCI,
+ .generic_name = "vhost-vsock-pci",
+ .transitional_name = "vhost-vsock-pci-transitional",
+ .non_transitional_name = "vhost-vsock-pci-non-transitional",
+ .instance_size = sizeof(VHostVSockPCI),
+ .instance_init = vhost_vsock_pci_instance_init,
+ .class_init = vhost_vsock_pci_class_init,
+};
+
+static void virtio_pci_vhost_register(void)
+{
+ virtio_pci_types_register(&vhost_vsock_pci_info);
+}
+
+type_init(virtio_pci_vhost_register)
diff --git a/hw/virtio/virtio-9p-pci.c b/hw/virtio/virtio-9p-pci.c
new file mode 100644
index 0000000000..7bf1130966
--- /dev/null
+++ b/hw/virtio/virtio-9p-pci.c
@@ -0,0 +1,88 @@
+/*
+ * Virtio 9p PCI Bindings
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+
+#include "virtio-pci.h"
+#include "hw/9pfs/virtio-9p.h"
+
+/*
+ * virtio-9p-pci: This extends VirtioPCIProxy.
+ */
+
+#define TYPE_VIRTIO_9P_PCI "virtio-9p-pci-base"
+#define VIRTIO_9P_PCI(obj) \
+ OBJECT_CHECK(V9fsPCIState, (obj), TYPE_VIRTIO_9P_PCI)
+
+typedef struct V9fsPCIState {
+ VirtIOPCIProxy parent_obj;
+ V9fsVirtioState vdev;
+} V9fsPCIState;
+
+static void virtio_9p_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ V9fsPCIState *dev = VIRTIO_9P_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static Property virtio_9p_pci_properties[] = {
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_9p_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+
+ k->realize = virtio_9p_pci_realize;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_9P;
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+ pcidev_k->class_id = 0x2;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ dc->props = virtio_9p_pci_properties;
+}
+
+static void virtio_9p_pci_instance_init(Object *obj)
+{
+ V9fsPCIState *dev = VIRTIO_9P_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_9P);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_9p_pci_info = {
+ .base_name = TYPE_VIRTIO_9P_PCI,
+ .generic_name = "virtio-9p-pci",
+ .transitional_name = "virtio-9p-pci-transitional",
+ .non_transitional_name = "virtio-9p-pci-non-transitional",
+ .instance_size = sizeof(V9fsPCIState),
+ .instance_init = virtio_9p_pci_instance_init,
+ .class_init = virtio_9p_pci_class_init,
+};
+
+static void virtio_9p_pci_register(void)
+{
+ virtio_pci_types_register(&virtio_9p_pci_info);
+}
+
+type_init(virtio_9p_pci_register)
diff --git a/hw/virtio/virtio-balloon-pci.c b/hw/virtio/virtio-balloon-pci.c
new file mode 100644
index 0000000000..2a213bbb38
--- /dev/null
+++ b/hw/virtio/virtio-balloon-pci.c
@@ -0,0 +1,95 @@
+/*
+ * Virtio balloon PCI Bindings
+ *
+ * Copyright IBM, Corp. 2007
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Paul Brook <paul@codesourcery.com>
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+
+#include "virtio-pci.h"
+#include "hw/virtio/virtio-balloon.h"
+#include "qapi/error.h"
+
+typedef struct VirtIOBalloonPCI VirtIOBalloonPCI;
+
+/*
+ * virtio-balloon-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_BALLOON_PCI "virtio-balloon-pci-base"
+#define VIRTIO_BALLOON_PCI(obj) \
+ OBJECT_CHECK(VirtIOBalloonPCI, (obj), TYPE_VIRTIO_BALLOON_PCI)
+
+struct VirtIOBalloonPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOBalloon vdev;
+};
+static Property virtio_balloon_pci_properties[] = {
+ DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_balloon_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ if (vpci_dev->class_code != PCI_CLASS_OTHERS &&
+ vpci_dev->class_code != PCI_CLASS_MEMORY_RAM) { /* qemu < 1.1 */
+ vpci_dev->class_code = PCI_CLASS_OTHERS;
+ }
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+ k->realize = virtio_balloon_pci_realize;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->props = virtio_balloon_pci_properties;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON;
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+ pcidev_k->class_id = PCI_CLASS_OTHERS;
+}
+
+static void virtio_balloon_pci_instance_init(Object *obj)
+{
+ VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_BALLOON);
+ object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev),
+ "guest-stats", &error_abort);
+ object_property_add_alias(obj, "guest-stats-polling-interval",
+ OBJECT(&dev->vdev),
+ "guest-stats-polling-interval", &error_abort);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_balloon_pci_info = {
+ .base_name = TYPE_VIRTIO_BALLOON_PCI,
+ .generic_name = "virtio-balloon-pci",
+ .transitional_name = "virtio-balloon-pci-transitional",
+ .non_transitional_name = "virtio-balloon-pci-non-transitional",
+ .instance_size = sizeof(VirtIOBalloonPCI),
+ .instance_init = virtio_balloon_pci_instance_init,
+ .class_init = virtio_balloon_pci_class_init,
+};
+
+static void virtio_balloon_pci_register(void)
+{
+ virtio_pci_types_register(&virtio_balloon_pci_info);
+}
+
+type_init(virtio_balloon_pci_register)
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 1728e4f83a..a12677d4d5 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -311,7 +311,7 @@ out:
static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data)
{
VirtIOBalloon *dev = VIRTIO_BALLOON(vdev);
- struct virtio_balloon_config config;
+ struct virtio_balloon_config config = {};
config.num_pages = cpu_to_le32(dev->num_pages);
config.actual = cpu_to_le32(dev->actual);
diff --git a/hw/virtio/virtio-blk-pci.c b/hw/virtio/virtio-blk-pci.c
new file mode 100644
index 0000000000..60c9185c39
--- /dev/null
+++ b/hw/virtio/virtio-blk-pci.c
@@ -0,0 +1,100 @@
+/*
+ * Virtio blk PCI Bindings
+ *
+ * Copyright IBM, Corp. 2007
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Paul Brook <paul@codesourcery.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/virtio/virtio-blk.h"
+#include "virtio-pci.h"
+#include "qapi/error.h"
+
+typedef struct VirtIOBlkPCI VirtIOBlkPCI;
+
+/*
+ * virtio-blk-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_BLK_PCI "virtio-blk-pci-base"
+#define VIRTIO_BLK_PCI(obj) \
+ OBJECT_CHECK(VirtIOBlkPCI, (obj), TYPE_VIRTIO_BLK_PCI)
+
+struct VirtIOBlkPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOBlock vdev;
+};
+
+static Property virtio_blk_pci_properties[] = {
+ DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = dev->vdev.conf.num_queues + 1;
+ }
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void virtio_blk_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ dc->props = virtio_blk_pci_properties;
+ k->realize = virtio_blk_pci_realize;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK;
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+ pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
+}
+
+static void virtio_blk_pci_instance_init(Object *obj)
+{
+ VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_BLK);
+ object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
+ "bootindex", &error_abort);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_blk_pci_info = {
+ .base_name = TYPE_VIRTIO_BLK_PCI,
+ .generic_name = "virtio-blk-pci",
+ .transitional_name = "virtio-blk-pci-transitional",
+ .non_transitional_name = "virtio-blk-pci-non-transitional",
+ .instance_size = sizeof(VirtIOBlkPCI),
+ .instance_init = virtio_blk_pci_instance_init,
+ .class_init = virtio_blk_pci_class_init,
+};
+
+static void virtio_blk_pci_register(void)
+{
+ virtio_pci_types_register(&virtio_blk_pci_info);
+}
+
+type_init(virtio_blk_pci_register)
diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c
index 8cc3fa3ef7..90a6e0dc2e 100644
--- a/hw/virtio/virtio-crypto-pci.c
+++ b/hw/virtio/virtio-crypto-pci.c
@@ -19,6 +19,20 @@
#include "hw/virtio/virtio-crypto.h"
#include "qapi/error.h"
+typedef struct VirtIOCryptoPCI VirtIOCryptoPCI;
+
+/*
+ * virtio-crypto-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_CRYPTO_PCI "virtio-crypto-pci"
+#define VIRTIO_CRYPTO_PCI(obj) \
+ OBJECT_CHECK(VirtIOCryptoPCI, (obj), TYPE_VIRTIO_CRYPTO_PCI)
+
+struct VirtIOCryptoPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOCrypto vdev;
+};
+
static Property virtio_crypto_pci_properties[] = {
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
diff --git a/hw/virtio/virtio-input-host-pci.c b/hw/virtio/virtio-input-host-pci.c
new file mode 100644
index 0000000000..725a51ad30
--- /dev/null
+++ b/hw/virtio/virtio-input-host-pci.c
@@ -0,0 +1,48 @@
+/*
+ * Virtio input host PCI Bindings
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "virtio-pci.h"
+#include "hw/virtio/virtio-input.h"
+
+typedef struct VirtIOInputHostPCI VirtIOInputHostPCI;
+
+#define TYPE_VIRTIO_INPUT_HOST_PCI "virtio-input-host-pci-base"
+#define VIRTIO_INPUT_HOST_PCI(obj) \
+ OBJECT_CHECK(VirtIOInputHostPCI, (obj), TYPE_VIRTIO_INPUT_HOST_PCI)
+
+struct VirtIOInputHostPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOInputHost vdev;
+};
+
+static void virtio_host_initfn(Object *obj)
+{
+ VirtIOInputHostPCI *dev = VIRTIO_INPUT_HOST_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_INPUT_HOST);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_input_host_pci_info = {
+ .base_name = TYPE_VIRTIO_INPUT_HOST_PCI,
+ .generic_name = "virtio-input-host-pci",
+ .transitional_name = "virtio-input-host-pci-transitional",
+ .non_transitional_name = "virtio-input-host-pci-non-transitional",
+ .parent = TYPE_VIRTIO_INPUT_PCI,
+ .instance_size = sizeof(VirtIOInputHostPCI),
+ .instance_init = virtio_host_initfn,
+};
+
+static void virtio_input_host_pci_register(void)
+{
+ virtio_pci_types_register(&virtio_input_host_pci_info);
+}
+
+type_init(virtio_input_host_pci_register)
diff --git a/hw/virtio/virtio-input-pci.c b/hw/virtio/virtio-input-pci.c
new file mode 100644
index 0000000000..2c1397842b
--- /dev/null
+++ b/hw/virtio/virtio-input-pci.c
@@ -0,0 +1,157 @@
+/*
+ * Virtio input PCI Bindings
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "virtio-pci.h"
+#include "hw/virtio/virtio-input.h"
+
+typedef struct VirtIOInputPCI VirtIOInputPCI;
+typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
+
+/*
+ * virtio-input-pci: This extends VirtioPCIProxy.
+ */
+#define VIRTIO_INPUT_PCI(obj) \
+ OBJECT_CHECK(VirtIOInputPCI, (obj), TYPE_VIRTIO_INPUT_PCI)
+
+struct VirtIOInputPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOInput vdev;
+};
+
+#define TYPE_VIRTIO_INPUT_HID_PCI "virtio-input-hid-pci"
+#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci"
+#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci"
+#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-pci"
+#define VIRTIO_INPUT_HID_PCI(obj) \
+ OBJECT_CHECK(VirtIOInputHIDPCI, (obj), TYPE_VIRTIO_INPUT_HID_PCI)
+
+struct VirtIOInputHIDPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOInputHID vdev;
+};
+
+static Property virtio_input_pci_properties[] = {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&vinput->vdev);
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ virtio_pci_force_virtio_1(vpci_dev);
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void virtio_input_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ dc->props = virtio_input_pci_properties;
+ k->realize = virtio_input_pci_realize;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+
+ pcidev_k->class_id = PCI_CLASS_INPUT_OTHER;
+}
+
+static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ pcidev_k->class_id = PCI_CLASS_INPUT_KEYBOARD;
+}
+
+static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass,
+ void *data)
+{
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ pcidev_k->class_id = PCI_CLASS_INPUT_MOUSE;
+}
+
+static void virtio_keyboard_initfn(Object *obj)
+{
+ VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_KEYBOARD);
+}
+
+static void virtio_mouse_initfn(Object *obj)
+{
+ VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_MOUSE);
+}
+
+static void virtio_tablet_initfn(Object *obj)
+{
+ VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_TABLET);
+}
+
+static const TypeInfo virtio_input_pci_info = {
+ .name = TYPE_VIRTIO_INPUT_PCI,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(VirtIOInputPCI),
+ .class_init = virtio_input_pci_class_init,
+ .abstract = true,
+};
+
+static const TypeInfo virtio_input_hid_pci_info = {
+ .name = TYPE_VIRTIO_INPUT_HID_PCI,
+ .parent = TYPE_VIRTIO_INPUT_PCI,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .abstract = true,
+};
+
+static const VirtioPCIDeviceTypeInfo virtio_keyboard_pci_info = {
+ .generic_name = TYPE_VIRTIO_KEYBOARD_PCI,
+ .parent = TYPE_VIRTIO_INPUT_HID_PCI,
+ .class_init = virtio_input_hid_kbd_pci_class_init,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .instance_init = virtio_keyboard_initfn,
+};
+
+static const VirtioPCIDeviceTypeInfo virtio_mouse_pci_info = {
+ .generic_name = TYPE_VIRTIO_MOUSE_PCI,
+ .parent = TYPE_VIRTIO_INPUT_HID_PCI,
+ .class_init = virtio_input_hid_mouse_pci_class_init,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .instance_init = virtio_mouse_initfn,
+};
+
+static const VirtioPCIDeviceTypeInfo virtio_tablet_pci_info = {
+ .generic_name = TYPE_VIRTIO_TABLET_PCI,
+ .parent = TYPE_VIRTIO_INPUT_HID_PCI,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .instance_init = virtio_tablet_initfn,
+};
+
+static void virtio_pci_input_register(void)
+{
+ /* Base types: */
+ type_register_static(&virtio_input_pci_info);
+ type_register_static(&virtio_input_hid_pci_info);
+
+ /* Implementations: */
+ virtio_pci_types_register(&virtio_keyboard_pci_info);
+ virtio_pci_types_register(&virtio_mouse_pci_info);
+ virtio_pci_types_register(&virtio_tablet_pci_info);
+}
+
+type_init(virtio_pci_input_register)
diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c
new file mode 100644
index 0000000000..db07ab9e21
--- /dev/null
+++ b/hw/virtio/virtio-net-pci.c
@@ -0,0 +1,98 @@
+/*
+ * Virtio net PCI Bindings
+ *
+ * Copyright IBM, Corp. 2007
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Paul Brook <paul@codesourcery.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/virtio/virtio-net.h"
+#include "virtio-pci.h"
+#include "qapi/error.h"
+
+typedef struct VirtIONetPCI VirtIONetPCI;
+
+/*
+ * virtio-net-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_NET_PCI "virtio-net-pci-base"
+#define VIRTIO_NET_PCI(obj) \
+ OBJECT_CHECK(VirtIONetPCI, (obj), TYPE_VIRTIO_NET_PCI)
+
+struct VirtIONetPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIONet vdev;
+};
+
+static Property virtio_net_properties[] = {
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ DeviceState *qdev = DEVICE(vpci_dev);
+ VirtIONetPCI *dev = VIRTIO_NET_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ virtio_net_set_netclient_name(&dev->vdev, qdev->id,
+ object_get_typename(OBJECT(qdev)));
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void virtio_net_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass);
+
+ k->romfile = "efi-virtio.rom";
+ k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ k->device_id = PCI_DEVICE_ID_VIRTIO_NET;
+ k->revision = VIRTIO_PCI_ABI_VERSION;
+ k->class_id = PCI_CLASS_NETWORK_ETHERNET;
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+ dc->props = virtio_net_properties;
+ vpciklass->realize = virtio_net_pci_realize;
+}
+
+static void virtio_net_pci_instance_init(Object *obj)
+{
+ VirtIONetPCI *dev = VIRTIO_NET_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_NET);
+ object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
+ "bootindex", &error_abort);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_net_pci_info = {
+ .base_name = TYPE_VIRTIO_NET_PCI,
+ .generic_name = "virtio-net-pci",
+ .transitional_name = "virtio-net-pci-transitional",
+ .non_transitional_name = "virtio-net-pci-non-transitional",
+ .instance_size = sizeof(VirtIONetPCI),
+ .instance_init = virtio_net_pci_instance_init,
+ .class_init = virtio_net_pci_class_init,
+};
+
+static void virtio_net_pci_register(void)
+{
+ virtio_pci_types_register(&virtio_net_pci_info);
+}
+
+type_init(virtio_net_pci_register)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index d05066deb8..b282109343 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -19,12 +19,6 @@
#include "standard-headers/linux/virtio_pci.h"
#include "hw/virtio/virtio.h"
-#include "hw/virtio/virtio-blk.h"
-#include "hw/virtio/virtio-net.h"
-#include "hw/virtio/virtio-serial.h"
-#include "hw/virtio/virtio-scsi.h"
-#include "hw/virtio/virtio-balloon.h"
-#include "hw/virtio/virtio-input.h"
#include "hw/pci/pci.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
@@ -1079,57 +1073,6 @@ static void virtio_pci_vmstate_change(DeviceState *d, bool running)
}
}
-#ifdef CONFIG_VIRTFS
-static void virtio_9p_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- V9fsPCIState *dev = VIRTIO_9P_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static Property virtio_9p_pci_properties[] = {
- DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
- VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_9p_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
-
- k->realize = virtio_9p_pci_realize;
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_9P;
- pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
- pcidev_k->class_id = 0x2;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->props = virtio_9p_pci_properties;
-}
-
-static void virtio_9p_pci_instance_init(Object *obj)
-{
- V9fsPCIState *dev = VIRTIO_9P_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_9P);
-}
-
-static const VirtioPCIDeviceTypeInfo virtio_9p_pci_info = {
- .base_name = TYPE_VIRTIO_9P_PCI,
- .generic_name = "virtio-9p-pci",
- .transitional_name = "virtio-9p-pci-transitional",
- .non_transitional_name = "virtio-9p-pci-non-transitional",
- .instance_size = sizeof(V9fsPCIState),
- .instance_init = virtio_9p_pci_instance_init,
- .class_init = virtio_9p_pci_class_init,
-};
-#endif /* CONFIG_VIRTFS */
-
/*
* virtio-pci: This is the PCIDevice which has a virtio-pci-bus.
*/
@@ -2055,728 +1998,6 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t)
}
}
-/* virtio-blk-pci */
-
-static Property virtio_blk_pci_properties[] = {
- DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
- DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
- VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
- DEV_NVECTORS_UNSPECIFIED),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
-
- if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
- vpci_dev->nvectors = dev->vdev.conf.num_queues + 1;
- }
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static void virtio_blk_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
-
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->props = virtio_blk_pci_properties;
- k->realize = virtio_blk_pci_realize;
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK;
- pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
- pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
-}
-
-static void virtio_blk_pci_instance_init(Object *obj)
-{
- VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_BLK);
- object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
- "bootindex", &error_abort);
-}
-
-static const VirtioPCIDeviceTypeInfo virtio_blk_pci_info = {
- .base_name = TYPE_VIRTIO_BLK_PCI,
- .generic_name = "virtio-blk-pci",
- .transitional_name = "virtio-blk-pci-transitional",
- .non_transitional_name = "virtio-blk-pci-non-transitional",
- .instance_size = sizeof(VirtIOBlkPCI),
- .instance_init = virtio_blk_pci_instance_init,
- .class_init = virtio_blk_pci_class_init,
-};
-
-#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
-/* vhost-user-blk */
-
-static Property vhost_user_blk_pci_properties[] = {
- DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
- DEV_NVECTORS_UNSPECIFIED),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
-
- if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
- vpci_dev->nvectors = dev->vdev.num_queues + 1;
- }
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static void vhost_user_blk_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
-
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->props = vhost_user_blk_pci_properties;
- k->realize = vhost_user_blk_pci_realize;
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK;
- pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
- pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
-}
-
-static void vhost_user_blk_pci_instance_init(Object *obj)
-{
- VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VHOST_USER_BLK);
- object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
- "bootindex", &error_abort);
-}
-
-static const VirtioPCIDeviceTypeInfo vhost_user_blk_pci_info = {
- .base_name = TYPE_VHOST_USER_BLK_PCI,
- .generic_name = "vhost-user-blk-pci",
- .transitional_name = "vhost-user-blk-pci-transitional",
- .non_transitional_name = "vhost-user-blk-pci-non-transitional",
- .instance_size = sizeof(VHostUserBlkPCI),
- .instance_init = vhost_user_blk_pci_instance_init,
- .class_init = vhost_user_blk_pci_class_init,
-};
-#endif
-
-/* virtio-scsi-pci */
-
-static Property virtio_scsi_pci_properties[] = {
- DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
- VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
- DEV_NVECTORS_UNSPECIFIED),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
- DeviceState *proxy = DEVICE(vpci_dev);
- char *bus_name;
-
- if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
- vpci_dev->nvectors = vs->conf.num_queues + 3;
- }
-
- /*
- * For command line compatibility, this sets the virtio-scsi-device bus
- * name as before.
- */
- if (proxy->id) {
- bus_name = g_strdup_printf("%s.0", proxy->id);
- virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name);
- g_free(bus_name);
- }
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
-
- k->realize = virtio_scsi_pci_realize;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->props = virtio_scsi_pci_properties;
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
- pcidev_k->revision = 0x00;
- pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
-}
-
-static void virtio_scsi_pci_instance_init(Object *obj)
-{
- VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_SCSI);
-}
-
-static const VirtioPCIDeviceTypeInfo virtio_scsi_pci_info = {
- .base_name = TYPE_VIRTIO_SCSI_PCI,
- .generic_name = "virtio-scsi-pci",
- .transitional_name = "virtio-scsi-pci-transitional",
- .non_transitional_name = "virtio-scsi-pci-non-transitional",
- .instance_size = sizeof(VirtIOSCSIPCI),
- .instance_init = virtio_scsi_pci_instance_init,
- .class_init = virtio_scsi_pci_class_init,
-};
-
-/* vhost-scsi-pci */
-
-#ifdef CONFIG_VHOST_SCSI
-static Property vhost_scsi_pci_properties[] = {
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
- DEV_NVECTORS_UNSPECIFIED),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void vhost_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- VHostSCSIPCI *dev = VHOST_SCSI_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
-
- if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
- vpci_dev->nvectors = vs->conf.num_queues + 3;
- }
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
- k->realize = vhost_scsi_pci_realize;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->props = vhost_scsi_pci_properties;
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
- pcidev_k->revision = 0x00;
- pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
-}
-
-static void vhost_scsi_pci_instance_init(Object *obj)
-{
- VHostSCSIPCI *dev = VHOST_SCSI_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VHOST_SCSI);
- object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
- "bootindex", &error_abort);
-}
-
-static const VirtioPCIDeviceTypeInfo vhost_scsi_pci_info = {
- .base_name = TYPE_VHOST_SCSI_PCI,
- .generic_name = "vhost-scsi-pci",
- .transitional_name = "vhost-scsi-pci-transitional",
- .non_transitional_name = "vhost-scsi-pci-non-transitional",
- .instance_size = sizeof(VHostSCSIPCI),
- .instance_init = vhost_scsi_pci_instance_init,
- .class_init = vhost_scsi_pci_class_init,
-};
-#endif
-
-#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
-/* vhost-user-scsi-pci */
-static Property vhost_user_scsi_pci_properties[] = {
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
- DEV_NVECTORS_UNSPECIFIED),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
-
- if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
- vpci_dev->nvectors = vs->conf.num_queues + 3;
- }
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static void vhost_user_scsi_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
- k->realize = vhost_user_scsi_pci_realize;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->props = vhost_user_scsi_pci_properties;
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
- pcidev_k->revision = 0x00;
- pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
-}
-
-static void vhost_user_scsi_pci_instance_init(Object *obj)
-{
- VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VHOST_USER_SCSI);
- object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
- "bootindex", &error_abort);
-}
-
-static const VirtioPCIDeviceTypeInfo vhost_user_scsi_pci_info = {
- .base_name = TYPE_VHOST_USER_SCSI_PCI,
- .generic_name = "vhost-user-scsi-pci",
- .transitional_name = "vhost-user-scsi-pci-transitional",
- .non_transitional_name = "vhost-user-scsi-pci-non-transitional",
- .instance_size = sizeof(VHostUserSCSIPCI),
- .instance_init = vhost_user_scsi_pci_instance_init,
- .class_init = vhost_user_scsi_pci_class_init,
-};
-#endif
-
-/* vhost-vsock-pci */
-
-#ifdef CONFIG_VHOST_VSOCK
-static Property vhost_vsock_pci_properties[] = {
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void vhost_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- VHostVSockPCI *dev = VHOST_VSOCK_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static void vhost_vsock_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
- k->realize = vhost_vsock_pci_realize;
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- dc->props = vhost_vsock_pci_properties;
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_VSOCK;
- pcidev_k->revision = 0x00;
- pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
-}
-
-static void vhost_vsock_pci_instance_init(Object *obj)
-{
- VHostVSockPCI *dev = VHOST_VSOCK_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VHOST_VSOCK);
-}
-
-static const VirtioPCIDeviceTypeInfo vhost_vsock_pci_info = {
- .base_name = TYPE_VHOST_VSOCK_PCI,
- .generic_name = "vhost-vsock-pci",
- .transitional_name = "vhost-vsock-pci-transitional",
- .non_transitional_name = "vhost-vsock-pci-non-transitional",
- .instance_size = sizeof(VHostVSockPCI),
- .instance_init = vhost_vsock_pci_instance_init,
- .class_init = vhost_vsock_pci_class_init,
-};
-#endif
-
-/* virtio-balloon-pci */
-
-static Property virtio_balloon_pci_properties[] = {
- DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_balloon_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
-
- if (vpci_dev->class_code != PCI_CLASS_OTHERS &&
- vpci_dev->class_code != PCI_CLASS_MEMORY_RAM) { /* qemu < 1.1 */
- vpci_dev->class_code = PCI_CLASS_OTHERS;
- }
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
- k->realize = virtio_balloon_pci_realize;
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- dc->props = virtio_balloon_pci_properties;
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON;
- pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
- pcidev_k->class_id = PCI_CLASS_OTHERS;
-}
-
-static void virtio_balloon_pci_instance_init(Object *obj)
-{
- VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_BALLOON);
- object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev),
- "guest-stats", &error_abort);
- object_property_add_alias(obj, "guest-stats-polling-interval",
- OBJECT(&dev->vdev),
- "guest-stats-polling-interval", &error_abort);
-}
-
-static const VirtioPCIDeviceTypeInfo virtio_balloon_pci_info = {
- .base_name = TYPE_VIRTIO_BALLOON_PCI,
- .generic_name = "virtio-balloon-pci",
- .transitional_name = "virtio-balloon-pci-transitional",
- .non_transitional_name = "virtio-balloon-pci-non-transitional",
- .instance_size = sizeof(VirtIOBalloonPCI),
- .instance_init = virtio_balloon_pci_instance_init,
- .class_init = virtio_balloon_pci_class_init,
-};
-
-/* virtio-serial-pci */
-
-static void virtio_serial_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- DeviceState *proxy = DEVICE(vpci_dev);
- char *bus_name;
-
- if (vpci_dev->class_code != PCI_CLASS_COMMUNICATION_OTHER &&
- vpci_dev->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */
- vpci_dev->class_code != PCI_CLASS_OTHERS) { /* qemu-kvm */
- vpci_dev->class_code = PCI_CLASS_COMMUNICATION_OTHER;
- }
-
- /* backwards-compatibility with machines that were created with
- DEV_NVECTORS_UNSPECIFIED */
- if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
- vpci_dev->nvectors = dev->vdev.serial.max_virtserial_ports + 1;
- }
-
- /*
- * For command line compatibility, this sets the virtio-serial-device bus
- * name as before.
- */
- if (proxy->id) {
- bus_name = g_strdup_printf("%s.0", proxy->id);
- virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name);
- g_free(bus_name);
- }
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static Property virtio_serial_pci_properties[] = {
- DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
- VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
- DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_serial_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
- k->realize = virtio_serial_pci_realize;
- set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
- dc->props = virtio_serial_pci_properties;
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE;
- pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
- pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
-}
-
-static void virtio_serial_pci_instance_init(Object *obj)
-{
- VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_SERIAL);
-}
-
-static const VirtioPCIDeviceTypeInfo virtio_serial_pci_info = {
- .base_name = TYPE_VIRTIO_SERIAL_PCI,
- .generic_name = "virtio-serial-pci",
- .transitional_name = "virtio-serial-pci-transitional",
- .non_transitional_name = "virtio-serial-pci-non-transitional",
- .instance_size = sizeof(VirtIOSerialPCI),
- .instance_init = virtio_serial_pci_instance_init,
- .class_init = virtio_serial_pci_class_init,
-};
-
-/* virtio-net-pci */
-
-static Property virtio_net_properties[] = {
- DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
- VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- DeviceState *qdev = DEVICE(vpci_dev);
- VirtIONetPCI *dev = VIRTIO_NET_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
-
- virtio_net_set_netclient_name(&dev->vdev, qdev->id,
- object_get_typename(OBJECT(qdev)));
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static void virtio_net_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass);
-
- k->romfile = "efi-virtio.rom";
- k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- k->device_id = PCI_DEVICE_ID_VIRTIO_NET;
- k->revision = VIRTIO_PCI_ABI_VERSION;
- k->class_id = PCI_CLASS_NETWORK_ETHERNET;
- set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
- dc->props = virtio_net_properties;
- vpciklass->realize = virtio_net_pci_realize;
-}
-
-static void virtio_net_pci_instance_init(Object *obj)
-{
- VirtIONetPCI *dev = VIRTIO_NET_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_NET);
- object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
- "bootindex", &error_abort);
-}
-
-static const VirtioPCIDeviceTypeInfo virtio_net_pci_info = {
- .base_name = TYPE_VIRTIO_NET_PCI,
- .generic_name = "virtio-net-pci",
- .transitional_name = "virtio-net-pci-transitional",
- .non_transitional_name = "virtio-net-pci-non-transitional",
- .instance_size = sizeof(VirtIONetPCI),
- .instance_init = virtio_net_pci_instance_init,
- .class_init = virtio_net_pci_class_init,
-};
-
-/* virtio-rng-pci */
-
-static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&vrng->vdev);
- Error *err = NULL;
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- object_property_set_link(OBJECT(vrng),
- OBJECT(vrng->vdev.conf.rng), "rng",
- NULL);
-}
-
-static void virtio_rng_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
-
- k->realize = virtio_rng_pci_realize;
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
-
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
- pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
- pcidev_k->class_id = PCI_CLASS_OTHERS;
-}
-
-static void virtio_rng_initfn(Object *obj)
-{
- VirtIORngPCI *dev = VIRTIO_RNG_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_RNG);
-}
-
-static const VirtioPCIDeviceTypeInfo virtio_rng_pci_info = {
- .base_name = TYPE_VIRTIO_RNG_PCI,
- .generic_name = "virtio-rng-pci",
- .transitional_name = "virtio-rng-pci-transitional",
- .non_transitional_name = "virtio-rng-pci-non-transitional",
- .instance_size = sizeof(VirtIORngPCI),
- .instance_init = virtio_rng_initfn,
- .class_init = virtio_rng_pci_class_init,
-};
-
-/* virtio-input-pci */
-
-static Property virtio_input_pci_properties[] = {
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
-{
- VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev);
- DeviceState *vdev = DEVICE(&vinput->vdev);
-
- qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- virtio_pci_force_virtio_1(vpci_dev);
- object_property_set_bool(OBJECT(vdev), true, "realized", errp);
-}
-
-static void virtio_input_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
-
- dc->props = virtio_input_pci_properties;
- k->realize = virtio_input_pci_realize;
- set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
-
- pcidev_k->class_id = PCI_CLASS_INPUT_OTHER;
-}
-
-static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data)
-{
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
-
- pcidev_k->class_id = PCI_CLASS_INPUT_KEYBOARD;
-}
-
-static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass,
- void *data)
-{
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
-
- pcidev_k->class_id = PCI_CLASS_INPUT_MOUSE;
-}
-
-static void virtio_keyboard_initfn(Object *obj)
-{
- VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_KEYBOARD);
-}
-
-static void virtio_mouse_initfn(Object *obj)
-{
- VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_MOUSE);
-}
-
-static void virtio_tablet_initfn(Object *obj)
-{
- VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_TABLET);
-}
-
-static const TypeInfo virtio_input_pci_info = {
- .name = TYPE_VIRTIO_INPUT_PCI,
- .parent = TYPE_VIRTIO_PCI,
- .instance_size = sizeof(VirtIOInputPCI),
- .class_init = virtio_input_pci_class_init,
- .abstract = true,
-};
-
-static const TypeInfo virtio_input_hid_pci_info = {
- .name = TYPE_VIRTIO_INPUT_HID_PCI,
- .parent = TYPE_VIRTIO_INPUT_PCI,
- .instance_size = sizeof(VirtIOInputHIDPCI),
- .abstract = true,
-};
-
-static const VirtioPCIDeviceTypeInfo virtio_keyboard_pci_info = {
- .generic_name = TYPE_VIRTIO_KEYBOARD_PCI,
- .parent = TYPE_VIRTIO_INPUT_HID_PCI,
- .class_init = virtio_input_hid_kbd_pci_class_init,
- .instance_size = sizeof(VirtIOInputHIDPCI),
- .instance_init = virtio_keyboard_initfn,
-};
-
-static const VirtioPCIDeviceTypeInfo virtio_mouse_pci_info = {
- .generic_name = TYPE_VIRTIO_MOUSE_PCI,
- .parent = TYPE_VIRTIO_INPUT_HID_PCI,
- .class_init = virtio_input_hid_mouse_pci_class_init,
- .instance_size = sizeof(VirtIOInputHIDPCI),
- .instance_init = virtio_mouse_initfn,
-};
-
-static const VirtioPCIDeviceTypeInfo virtio_tablet_pci_info = {
- .generic_name = TYPE_VIRTIO_TABLET_PCI,
- .parent = TYPE_VIRTIO_INPUT_HID_PCI,
- .instance_size = sizeof(VirtIOInputHIDPCI),
- .instance_init = virtio_tablet_initfn,
-};
-
-#ifdef CONFIG_LINUX
-static void virtio_host_initfn(Object *obj)
-{
- VirtIOInputHostPCI *dev = VIRTIO_INPUT_HOST_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_INPUT_HOST);
-}
-
-static const VirtioPCIDeviceTypeInfo virtio_host_pci_info = {
- .base_name = TYPE_VIRTIO_INPUT_HOST_PCI,
- .generic_name = "virtio-input-host-pci",
- .transitional_name = "virtio-input-host-pci-transitional",
- .non_transitional_name = "virtio-input-host-pci-non-transitional",
- .parent = TYPE_VIRTIO_INPUT_PCI,
- .instance_size = sizeof(VirtIOInputHostPCI),
- .instance_init = virtio_host_initfn,
-};
-#endif
-
/* virtio-pci-bus */
static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
@@ -2827,37 +2048,7 @@ static void virtio_pci_register_types(void)
/* Base types: */
type_register_static(&virtio_pci_bus_info);
type_register_static(&virtio_pci_info);
- type_register_static(&virtio_input_pci_info);
- type_register_static(&virtio_input_hid_pci_info);
-
- /* Implementations: */
- virtio_pci_types_register(&virtio_rng_pci_info);
- virtio_pci_types_register(&virtio_keyboard_pci_info);
- virtio_pci_types_register(&virtio_mouse_pci_info);
- virtio_pci_types_register(&virtio_tablet_pci_info);
-#ifdef CONFIG_LINUX
- virtio_pci_types_register(&virtio_host_pci_info);
-#endif
-#ifdef CONFIG_VIRTFS
- virtio_pci_types_register(&virtio_9p_pci_info);
-#endif
- virtio_pci_types_register(&virtio_blk_pci_info);
-#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
- virtio_pci_types_register(&vhost_user_blk_pci_info);
-#endif
- virtio_pci_types_register(&virtio_scsi_pci_info);
- virtio_pci_types_register(&virtio_balloon_pci_info);
- virtio_pci_types_register(&virtio_serial_pci_info);
- virtio_pci_types_register(&virtio_net_pci_info);
-#ifdef CONFIG_VHOST_SCSI
- virtio_pci_types_register(&vhost_scsi_pci_info);
-#endif
-#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
- virtio_pci_types_register(&vhost_user_scsi_pci_info);
-#endif
-#ifdef CONFIG_VHOST_VSOCK
- virtio_pci_types_register(&vhost_vsock_pci_info);
-#endif
}
type_init(virtio_pci_register_types)
+
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index 29b4216107..bd223a6e3b 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -16,47 +16,9 @@
#define QEMU_VIRTIO_PCI_H
#include "hw/pci/msi.h"
-#include "hw/virtio/virtio-blk.h"
-#include "hw/virtio/virtio-net.h"
-#include "hw/virtio/virtio-rng.h"
-#include "hw/virtio/virtio-serial.h"
-#include "hw/virtio/virtio-scsi.h"
-#include "hw/virtio/virtio-balloon.h"
#include "hw/virtio/virtio-bus.h"
-#include "hw/virtio/virtio-input.h"
-#include "hw/virtio/virtio-gpu.h"
-#include "hw/virtio/virtio-crypto.h"
-#include "hw/virtio/vhost-user-scsi.h"
-#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
-#include "hw/virtio/vhost-user-blk.h"
-#endif
-
-#ifdef CONFIG_VIRTFS
-#include "hw/9pfs/virtio-9p.h"
-#endif
-#ifdef CONFIG_VHOST_SCSI
-#include "hw/virtio/vhost-scsi.h"
-#endif
-#ifdef CONFIG_VHOST_VSOCK
-#include "hw/virtio/vhost-vsock.h"
-#endif
typedef struct VirtIOPCIProxy VirtIOPCIProxy;
-typedef struct VirtIOBlkPCI VirtIOBlkPCI;
-typedef struct VirtIOSCSIPCI VirtIOSCSIPCI;
-typedef struct VirtIOBalloonPCI VirtIOBalloonPCI;
-typedef struct VirtIOSerialPCI VirtIOSerialPCI;
-typedef struct VirtIONetPCI VirtIONetPCI;
-typedef struct VHostSCSIPCI VHostSCSIPCI;
-typedef struct VHostUserSCSIPCI VHostUserSCSIPCI;
-typedef struct VHostUserBlkPCI VHostUserBlkPCI;
-typedef struct VirtIORngPCI VirtIORngPCI;
-typedef struct VirtIOInputPCI VirtIOInputPCI;
-typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
-typedef struct VirtIOInputHostPCI VirtIOInputHostPCI;
-typedef struct VirtIOGPUPCI VirtIOGPUPCI;
-typedef struct VHostVSockPCI VHostVSockPCI;
-typedef struct VirtIOCryptoPCI VirtIOCryptoPCI;
/* virtio-pci-bus */
@@ -214,205 +176,9 @@ static inline void virtio_pci_disable_modern(VirtIOPCIProxy *proxy)
}
/*
- * virtio-scsi-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VIRTIO_SCSI_PCI "virtio-scsi-pci-base"
-#define VIRTIO_SCSI_PCI(obj) \
- OBJECT_CHECK(VirtIOSCSIPCI, (obj), TYPE_VIRTIO_SCSI_PCI)
-
-struct VirtIOSCSIPCI {
- VirtIOPCIProxy parent_obj;
- VirtIOSCSI vdev;
-};
-
-#ifdef CONFIG_VHOST_SCSI
-/*
- * vhost-scsi-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VHOST_SCSI_PCI "vhost-scsi-pci-base"
-#define VHOST_SCSI_PCI(obj) \
- OBJECT_CHECK(VHostSCSIPCI, (obj), TYPE_VHOST_SCSI_PCI)
-
-struct VHostSCSIPCI {
- VirtIOPCIProxy parent_obj;
- VHostSCSI vdev;
-};
-#endif
-
-#define TYPE_VHOST_USER_SCSI_PCI "vhost-user-scsi-pci-base"
-#define VHOST_USER_SCSI_PCI(obj) \
- OBJECT_CHECK(VHostUserSCSIPCI, (obj), TYPE_VHOST_USER_SCSI_PCI)
-
-struct VHostUserSCSIPCI {
- VirtIOPCIProxy parent_obj;
- VHostUserSCSI vdev;
-};
-
-#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
-/*
- * vhost-user-blk-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VHOST_USER_BLK_PCI "vhost-user-blk-pci-base"
-#define VHOST_USER_BLK_PCI(obj) \
- OBJECT_CHECK(VHostUserBlkPCI, (obj), TYPE_VHOST_USER_BLK_PCI)
-
-struct VHostUserBlkPCI {
- VirtIOPCIProxy parent_obj;
- VHostUserBlk vdev;
-};
-#endif
-
-/*
- * virtio-blk-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VIRTIO_BLK_PCI "virtio-blk-pci-base"
-#define VIRTIO_BLK_PCI(obj) \
- OBJECT_CHECK(VirtIOBlkPCI, (obj), TYPE_VIRTIO_BLK_PCI)
-
-struct VirtIOBlkPCI {
- VirtIOPCIProxy parent_obj;
- VirtIOBlock vdev;
-};
-
-/*
- * virtio-balloon-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VIRTIO_BALLOON_PCI "virtio-balloon-pci-base"
-#define VIRTIO_BALLOON_PCI(obj) \
- OBJECT_CHECK(VirtIOBalloonPCI, (obj), TYPE_VIRTIO_BALLOON_PCI)
-
-struct VirtIOBalloonPCI {
- VirtIOPCIProxy parent_obj;
- VirtIOBalloon vdev;
-};
-
-/*
- * virtio-serial-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VIRTIO_SERIAL_PCI "virtio-serial-pci-base"
-#define VIRTIO_SERIAL_PCI(obj) \
- OBJECT_CHECK(VirtIOSerialPCI, (obj), TYPE_VIRTIO_SERIAL_PCI)
-
-struct VirtIOSerialPCI {
- VirtIOPCIProxy parent_obj;
- VirtIOSerial vdev;
-};
-
-/*
- * virtio-net-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VIRTIO_NET_PCI "virtio-net-pci-base"
-#define VIRTIO_NET_PCI(obj) \
- OBJECT_CHECK(VirtIONetPCI, (obj), TYPE_VIRTIO_NET_PCI)
-
-struct VirtIONetPCI {
- VirtIOPCIProxy parent_obj;
- VirtIONet vdev;
-};
-
-/*
- * virtio-9p-pci: This extends VirtioPCIProxy.
- */
-
-#ifdef CONFIG_VIRTFS
-
-#define TYPE_VIRTIO_9P_PCI "virtio-9p-pci-base"
-#define VIRTIO_9P_PCI(obj) \
- OBJECT_CHECK(V9fsPCIState, (obj), TYPE_VIRTIO_9P_PCI)
-
-typedef struct V9fsPCIState {
- VirtIOPCIProxy parent_obj;
- V9fsVirtioState vdev;
-} V9fsPCIState;
-
-#endif
-
-/*
- * virtio-rng-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VIRTIO_RNG_PCI "virtio-rng-pci-base"
-#define VIRTIO_RNG_PCI(obj) \
- OBJECT_CHECK(VirtIORngPCI, (obj), TYPE_VIRTIO_RNG_PCI)
-
-struct VirtIORngPCI {
- VirtIOPCIProxy parent_obj;
- VirtIORNG vdev;
-};
-
-/*
* virtio-input-pci: This extends VirtioPCIProxy.
*/
#define TYPE_VIRTIO_INPUT_PCI "virtio-input-pci"
-#define VIRTIO_INPUT_PCI(obj) \
- OBJECT_CHECK(VirtIOInputPCI, (obj), TYPE_VIRTIO_INPUT_PCI)
-
-struct VirtIOInputPCI {
- VirtIOPCIProxy parent_obj;
- VirtIOInput vdev;
-};
-
-#define TYPE_VIRTIO_INPUT_HID_PCI "virtio-input-hid-pci"
-#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci"
-#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci"
-#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-pci"
-#define VIRTIO_INPUT_HID_PCI(obj) \
- OBJECT_CHECK(VirtIOInputHIDPCI, (obj), TYPE_VIRTIO_INPUT_HID_PCI)
-
-struct VirtIOInputHIDPCI {
- VirtIOPCIProxy parent_obj;
- VirtIOInputHID vdev;
-};
-
-#ifdef CONFIG_LINUX
-
-#define TYPE_VIRTIO_INPUT_HOST_PCI "virtio-input-host-pci-base"
-#define VIRTIO_INPUT_HOST_PCI(obj) \
- OBJECT_CHECK(VirtIOInputHostPCI, (obj), TYPE_VIRTIO_INPUT_HOST_PCI)
-
-struct VirtIOInputHostPCI {
- VirtIOPCIProxy parent_obj;
- VirtIOInputHost vdev;
-};
-
-#endif
-
-/*
- * virtio-gpu-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
-#define VIRTIO_GPU_PCI(obj) \
- OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI)
-
-struct VirtIOGPUPCI {
- VirtIOPCIProxy parent_obj;
- VirtIOGPU vdev;
-};
-
-#ifdef CONFIG_VHOST_VSOCK
-/*
- * vhost-vsock-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VHOST_VSOCK_PCI "vhost-vsock-pci-base"
-#define VHOST_VSOCK_PCI(obj) \
- OBJECT_CHECK(VHostVSockPCI, (obj), TYPE_VHOST_VSOCK_PCI)
-
-struct VHostVSockPCI {
- VirtIOPCIProxy parent_obj;
- VHostVSock vdev;
-};
-#endif
-
-/*
- * virtio-crypto-pci: This extends VirtioPCIProxy.
- */
-#define TYPE_VIRTIO_CRYPTO_PCI "virtio-crypto-pci"
-#define VIRTIO_CRYPTO_PCI(obj) \
- OBJECT_CHECK(VirtIOCryptoPCI, (obj), TYPE_VIRTIO_CRYPTO_PCI)
-
-struct VirtIOCryptoPCI {
- VirtIOPCIProxy parent_obj;
- VirtIOCrypto vdev;
-};
/* Virtio ABI version, if we increment this, we break the guest driver. */
#define VIRTIO_PCI_ABI_VERSION 0
diff --git a/hw/virtio/virtio-rng-pci.c b/hw/virtio/virtio-rng-pci.c
new file mode 100644
index 0000000000..6cc6374289
--- /dev/null
+++ b/hw/virtio/virtio-rng-pci.c
@@ -0,0 +1,88 @@
+/*
+ * Virtio rng PCI Bindings
+ *
+ * Copyright 2012 Red Hat, Inc.
+ * Copyright 2012 Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "virtio-pci.h"
+#include "hw/virtio/virtio-rng.h"
+#include "qapi/error.h"
+
+typedef struct VirtIORngPCI VirtIORngPCI;
+
+/*
+ * virtio-rng-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_RNG_PCI "virtio-rng-pci-base"
+#define VIRTIO_RNG_PCI(obj) \
+ OBJECT_CHECK(VirtIORngPCI, (obj), TYPE_VIRTIO_RNG_PCI)
+
+struct VirtIORngPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIORNG vdev;
+};
+
+static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&vrng->vdev);
+ Error *err = NULL;
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_link(OBJECT(vrng),
+ OBJECT(vrng->vdev.conf.rng), "rng",
+ NULL);
+}
+
+static void virtio_rng_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = virtio_rng_pci_realize;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+ pcidev_k->class_id = PCI_CLASS_OTHERS;
+}
+
+static void virtio_rng_initfn(Object *obj)
+{
+ VirtIORngPCI *dev = VIRTIO_RNG_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_RNG);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_rng_pci_info = {
+ .base_name = TYPE_VIRTIO_RNG_PCI,
+ .generic_name = "virtio-rng-pci",
+ .transitional_name = "virtio-rng-pci-transitional",
+ .non_transitional_name = "virtio-rng-pci-non-transitional",
+ .instance_size = sizeof(VirtIORngPCI),
+ .instance_init = virtio_rng_initfn,
+ .class_init = virtio_rng_pci_class_init,
+};
+
+static void virtio_rng_pci_register(void)
+{
+ virtio_pci_types_register(&virtio_rng_pci_info);
+}
+
+type_init(virtio_rng_pci_register)
diff --git a/hw/virtio/virtio-scsi-pci.c b/hw/virtio/virtio-scsi-pci.c
new file mode 100644
index 0000000000..2830849729
--- /dev/null
+++ b/hw/virtio/virtio-scsi-pci.c
@@ -0,0 +1,107 @@
+/*
+ * Virtio scsi PCI Bindings
+ *
+ * Copyright IBM, Corp. 2007
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Paul Brook <paul@codesourcery.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/virtio/virtio-scsi.h"
+#include "virtio-pci.h"
+
+typedef struct VirtIOSCSIPCI VirtIOSCSIPCI;
+
+/*
+ * virtio-scsi-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_SCSI_PCI "virtio-scsi-pci-base"
+#define VIRTIO_SCSI_PCI(obj) \
+ OBJECT_CHECK(VirtIOSCSIPCI, (obj), TYPE_VIRTIO_SCSI_PCI)
+
+struct VirtIOSCSIPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOSCSI vdev;
+};
+
+static Property virtio_scsi_pci_properties[] = {
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+ VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
+ DeviceState *proxy = DEVICE(vpci_dev);
+ char *bus_name;
+
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = vs->conf.num_queues + 3;
+ }
+
+ /*
+ * For command line compatibility, this sets the virtio-scsi-device bus
+ * name as before.
+ */
+ if (proxy->id) {
+ bus_name = g_strdup_printf("%s.0", proxy->id);
+ virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name);
+ g_free(bus_name);
+ }
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = virtio_scsi_pci_realize;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ dc->props = virtio_scsi_pci_properties;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
+ pcidev_k->revision = 0x00;
+ pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
+}
+
+static void virtio_scsi_pci_instance_init(Object *obj)
+{
+ VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_SCSI);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_scsi_pci_info = {
+ .base_name = TYPE_VIRTIO_SCSI_PCI,
+ .generic_name = "virtio-scsi-pci",
+ .transitional_name = "virtio-scsi-pci-transitional",
+ .non_transitional_name = "virtio-scsi-pci-non-transitional",
+ .instance_size = sizeof(VirtIOSCSIPCI),
+ .instance_init = virtio_scsi_pci_instance_init,
+ .class_init = virtio_scsi_pci_class_init,
+};
+
+static void virtio_scsi_pci_register(void)
+{
+ virtio_pci_types_register(&virtio_scsi_pci_info);
+}
+
+type_init(virtio_scsi_pci_register)
diff --git a/hw/virtio/virtio-serial-pci.c b/hw/virtio/virtio-serial-pci.c
new file mode 100644
index 0000000000..971b2eb8d8
--- /dev/null
+++ b/hw/virtio/virtio-serial-pci.c
@@ -0,0 +1,115 @@
+/*
+ * Virtio serial PCI Bindings
+ *
+ * Copyright IBM, Corp. 2007
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Paul Brook <paul@codesourcery.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/virtio/virtio-serial.h"
+#include "virtio-pci.h"
+
+typedef struct VirtIOSerialPCI VirtIOSerialPCI;
+
+/*
+ * virtio-serial-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_SERIAL_PCI "virtio-serial-pci-base"
+#define VIRTIO_SERIAL_PCI(obj) \
+ OBJECT_CHECK(VirtIOSerialPCI, (obj), TYPE_VIRTIO_SERIAL_PCI)
+
+struct VirtIOSerialPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOSerial vdev;
+};
+
+static void virtio_serial_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+ DeviceState *proxy = DEVICE(vpci_dev);
+ char *bus_name;
+
+ if (vpci_dev->class_code != PCI_CLASS_COMMUNICATION_OTHER &&
+ vpci_dev->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */
+ vpci_dev->class_code != PCI_CLASS_OTHERS) { /* qemu-kvm */
+ vpci_dev->class_code = PCI_CLASS_COMMUNICATION_OTHER;
+ }
+
+ /* backwards-compatibility with machines that were created with
+ DEV_NVECTORS_UNSPECIFIED */
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = dev->vdev.serial.max_virtserial_ports + 1;
+ }
+
+ /*
+ * For command line compatibility, this sets the virtio-serial-device bus
+ * name as before.
+ */
+ if (proxy->id) {
+ bus_name = g_strdup_printf("%s.0", proxy->id);
+ virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name);
+ g_free(bus_name);
+ }
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static Property virtio_serial_pci_properties[] = {
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_serial_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+ k->realize = virtio_serial_pci_realize;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+ dc->props = virtio_serial_pci_properties;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE;
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+ pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
+}
+
+static void virtio_serial_pci_instance_init(Object *obj)
+{
+ VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_SERIAL);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_serial_pci_info = {
+ .base_name = TYPE_VIRTIO_SERIAL_PCI,
+ .generic_name = "virtio-serial-pci",
+ .transitional_name = "virtio-serial-pci-transitional",
+ .non_transitional_name = "virtio-serial-pci-non-transitional",
+ .instance_size = sizeof(VirtIOSerialPCI),
+ .instance_init = virtio_serial_pci_instance_init,
+ .class_init = virtio_serial_pci_class_init,
+};
+
+static void virtio_serial_pci_register(void)
+{
+ virtio_pci_types_register(&virtio_serial_pci_info);
+}
+
+type_init(virtio_serial_pci_register)
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 1971b55789..4faf394e34 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016-2017 Red Hat, Inc.
+ * Copyright (C) 2016-2019 Red Hat, Inc.
* Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
*
* Network Block Device
@@ -263,26 +263,39 @@ struct NBDExportInfo {
bool request_sizes;
char *x_dirty_bitmap;
+ /* Set by client before nbd_receive_negotiate(), or by server results
+ * during nbd_receive_export_list() */
+ char *name; /* must be non-NULL */
+
/* In-out fields, set by client before nbd_receive_negotiate() and
* updated by server results during nbd_receive_negotiate() */
bool structured_reply;
bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */
- /* Set by server results during nbd_receive_negotiate() */
+ /* Set by server results during nbd_receive_negotiate() and
+ * nbd_receive_export_list() */
uint64_t size;
uint16_t flags;
uint32_t min_block;
uint32_t opt_block;
uint32_t max_block;
- uint32_t meta_base_allocation_id;
+ uint32_t context_id;
+
+ /* Set by server results during nbd_receive_export_list() */
+ char *description;
+ int n_contexts;
+ char **contexts;
};
typedef struct NBDExportInfo NBDExportInfo;
-int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
- QCryptoTLSCreds *tlscreds, const char *hostname,
- QIOChannel **outioc, NBDExportInfo *info,
- Error **errp);
+int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
+ const char *hostname, QIOChannel **outioc,
+ NBDExportInfo *info, Error **errp);
+void nbd_free_export_list(NBDExportInfo *info, int count);
+int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
+ const char *hostname, NBDExportInfo **info,
+ Error **errp);
int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info,
Error **errp);
int nbd_send_request(QIOChannel *ioc, NBDRequest *request);
@@ -294,8 +307,8 @@ int nbd_errno_to_system_errno(int err);
typedef struct NBDExport NBDExport;
typedef struct NBDClient NBDClient;
-NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
- const char *name, const char *description,
+NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
+ uint64_t size, const char *name, const char *desc,
const char *bitmap, uint16_t nbdflags,
void (*close)(NBDExport *), bool writethrough,
BlockBackend *on_eject_blk, Error **errp);
diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h
index 5021cb9e79..f9aa4bd398 100644
--- a/include/hw/acpi/acpi-defs.h
+++ b/include/hw/acpi/acpi-defs.h
@@ -41,8 +41,8 @@ enum {
};
typedef struct AcpiRsdpData {
- uint8_t oem_id[6]; /* OEM identification */
- uint8_t revision; /* Must be 0 for 1.0, 2 for 2.0 */
+ uint8_t oem_id[6] QEMU_NONSTRING; /* OEM identification */
+ uint8_t revision; /* Must be 0 for 1.0, 2 for 2.0 */
unsigned *rsdt_tbl_offset;
unsigned *xsdt_tbl_offset;
@@ -57,10 +57,13 @@ typedef struct AcpiRsdpData {
uint32_t length; /* Length of table, in bytes, including header */ \
uint8_t revision; /* ACPI Specification minor version # */ \
uint8_t checksum; /* To make sum of entire table == 0 */ \
- uint8_t oem_id [6]; /* OEM identification */ \
- uint8_t oem_table_id [8]; /* OEM table identification */ \
+ uint8_t oem_id[6] \
+ QEMU_NONSTRING; /* OEM identification */ \
+ uint8_t oem_table_id[8] \
+ QEMU_NONSTRING; /* OEM table identification */ \
uint32_t oem_revision; /* OEM revision number */ \
- uint8_t asl_compiler_id [4]; /* ASL compiler vendor ID */ \
+ uint8_t asl_compiler_id[4] \
+ QEMU_NONSTRING; /* ASL compiler vendor ID */ \
uint32_t asl_compiler_revision; /* ASL compiler revision number */
@@ -623,6 +626,8 @@ struct AcpiIortItsGroup {
} QEMU_PACKED;
typedef struct AcpiIortItsGroup AcpiIortItsGroup;
+#define ACPI_IORT_SMMU_V3_COHACC_OVERRIDE 1
+
struct AcpiIortSmmu3 {
ACPI_IORT_NODE_HEADER_DEF
uint64_t base_address;
diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h
index 3580ffd50c..1a2a57a21f 100644
--- a/include/hw/acpi/tpm.h
+++ b/include/hw/acpi/tpm.h
@@ -18,6 +18,8 @@
#include "qemu/units.h"
#include "hw/registerfields.h"
+#include "hw/acpi/aml-build.h"
+#include "sysemu/tpm.h"
#define TPM_TIS_ADDR_BASE 0xFED40000
#define TPM_TIS_ADDR_SIZE 0x5000
@@ -188,4 +190,23 @@ REG32(CRB_DATA_BUFFER, 0x80)
#define TPM2_START_METHOD_MMIO 6
#define TPM2_START_METHOD_CRB 7
+/*
+ * Physical Presence Interface
+ */
+#define TPM_PPI_ADDR_SIZE 0x400
+#define TPM_PPI_ADDR_BASE 0xFED45000
+
+#define TPM_PPI_VERSION_NONE 0
+#define TPM_PPI_VERSION_1_30 1
+
+/* whether function is blocked by BIOS settings; bits 0, 1, 2 */
+#define TPM_PPI_FUNC_NOT_IMPLEMENTED (0 << 0)
+#define TPM_PPI_FUNC_BIOS_ONLY (1 << 0)
+#define TPM_PPI_FUNC_BLOCKED (2 << 0)
+#define TPM_PPI_FUNC_ALLOWED_USR_REQ (3 << 0)
+#define TPM_PPI_FUNC_ALLOWED_USR_NOT_REQ (4 << 0)
+#define TPM_PPI_FUNC_MASK (7 << 0)
+
+void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev);
+
#endif /* HW_ACPI_TPM_H */
diff --git a/include/hw/arm/pxa.h b/include/hw/arm/pxa.h
index 0df1199caa..f6dfb5c0cf 100644
--- a/include/hw/arm/pxa.h
+++ b/include/hw/arm/pxa.h
@@ -12,6 +12,7 @@
#include "exec/memory.h"
#include "target/arm/cpu-qom.h"
+#include "hw/pcmcia.h"
/* Interrupt numbers */
# define PXA2XX_PIC_SSP3 0
diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h
index 0acfbbc382..abd5cf71a9 100644
--- a/include/hw/char/serial.h
+++ b/include/hw/char/serial.h
@@ -35,7 +35,7 @@
#define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */
-struct SerialState {
+typedef struct SerialState {
uint16_t divider;
uint8_t rbr; /* receive register */
uint8_t thr; /* transmit holding register */
@@ -77,7 +77,7 @@ struct SerialState {
QEMUTimer *modem_status_poll;
MemoryRegion io;
-};
+} SerialState;
extern const VMStateDescription vmstate_serial;
extern const MemoryRegionOps serial_io_ops;
diff --git a/include/hw/devices.h b/include/hw/devices.h
index 0e27feb0c2..b5f1662225 100644
--- a/include/hw/devices.h
+++ b/include/hw/devices.h
@@ -4,6 +4,7 @@
/* Devices that have nowhere better to go. */
#include "hw/hw.h"
+#include "ui/console.h"
/* smc91c111.c */
void smc91c111_init(NICInfo *, uint32_t, qemu_irq);
diff --git a/include/hw/i2c/smbus.h b/include/hw/i2c/smbus.h
index d8b1b9ee81..5c61c05999 100644
--- a/include/hw/i2c/smbus.h
+++ b/include/hw/i2c/smbus.h
@@ -35,6 +35,8 @@
#define SMBUS_DEVICE_GET_CLASS(obj) \
OBJECT_GET_CLASS(SMBusDeviceClass, (obj), TYPE_SMBUS_DEVICE)
+typedef struct SMBusDevice SMBusDevice;
+
typedef struct SMBusDeviceClass
{
I2CSlaveClass parent_class;
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 0abbe45637..882fd8dfd2 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -96,7 +96,7 @@ struct PCMachineState {
* way we can use 1GByte pages in the host.
*
*/
-struct PCMachineClass {
+typedef struct PCMachineClass {
/*< private >*/
MachineClass parent_class;
@@ -133,7 +133,7 @@ struct PCMachineClass {
/* use DMA capable linuxboot option rom */
bool linuxboot_dma_enabled;
-};
+} PCMachineClass;
#define TYPE_PC_MACHINE "generic-pc-machine"
#define PC_MACHINE(obj) \
diff --git a/include/hw/ide/ahci.h b/include/hw/ide/ahci.h
index b7bb2b02d6..b44e3000cf 100644
--- a/include/hw/ide/ahci.h
+++ b/include/hw/ide/ahci.h
@@ -73,13 +73,13 @@ typedef struct SysbusAHCIState {
#define ALLWINNER_AHCI_MMIO_OFF 0x80
#define ALLWINNER_AHCI_MMIO_SIZE 0x80
-struct AllwinnerAHCIState {
+typedef struct AllwinnerAHCIState {
/*< private >*/
SysbusAHCIState parent_obj;
/*< public >*/
MemoryRegion mmio;
uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE/4];
-};
+} AllwinnerAHCIState;
#endif /* HW_IDE_AHCI_H */
diff --git a/include/hw/input/ps2.h b/include/hw/input/ps2.h
index 213aa16aa3..b60455d4f6 100644
--- a/include/hw/input/ps2.h
+++ b/include/hw/input/ps2.h
@@ -31,6 +31,8 @@
#define PS2_MOUSE_BUTTON_SIDE 0x08
#define PS2_MOUSE_BUTTON_EXTRA 0x10
+typedef struct PS2State PS2State;
+
/* ps2.c */
void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
diff --git a/include/hw/misc/mips_itu.h b/include/hw/misc/mips_itu.h
index 030eb4ac62..c44e7672b6 100644
--- a/include/hw/misc/mips_itu.h
+++ b/include/hw/misc/mips_itu.h
@@ -66,6 +66,14 @@ typedef struct MIPSITUState {
/* ITC Configuration Tags */
uint64_t ITCAddressMap[ITC_ADDRESSMAP_NUM];
MemoryRegion tag_io;
+
+ /* ITU Control Register */
+ uint64_t icr0;
+
+ /* SAAR */
+ bool saar_present;
+ void *saar;
+
} MIPSITUState;
/* Get ITC Configuration Tag memory region. */
diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h
index cd318646a2..5b82a0d244 100644
--- a/include/hw/pci/pcie.h
+++ b/include/hw/pci/pcie.h
@@ -132,6 +132,8 @@ void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn);
void pcie_dev_ser_num_init(PCIDevice *dev, uint16_t offset, uint64_t ser_num);
void pcie_ats_init(PCIDevice *dev, uint16_t offset);
+void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp);
void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp);
void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
diff --git a/include/hw/pcmcia.h b/include/hw/pcmcia.h
index 79cac9c761..1b4080764f 100644
--- a/include/hw/pcmcia.h
+++ b/include/hw/pcmcia.h
@@ -18,13 +18,13 @@ typedef struct PCMCIASocket {
#define PCMCIA_CARD_CLASS(cls) \
OBJECT_CLASS_CHECK(PCMCIACardClass, cls, TYPE_PCMCIA_CARD)
-struct PCMCIACardState {
+typedef struct PCMCIACardState {
/*< private >*/
DeviceState parent_obj;
/*< public >*/
PCMCIASocket *slot;
-};
+} PCMCIACardState;
typedef struct PCMCIACardClass {
/*< private >*/
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 9e01a5a12e..a947a0a0dc 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -8,15 +8,16 @@
#include "hw/mem/pc-dimm.h"
#include "hw/ppc/spapr_ovec.h"
#include "hw/ppc/spapr_irq.h"
+#include "hw/ppc/spapr_xive.h" /* For sPAPRXive */
+#include "hw/ppc/xics.h" /* For ICSState */
struct VIOsPAPRBus;
struct sPAPRPHBState;
struct sPAPRNVRAM;
+
typedef struct sPAPREventLogEntry sPAPREventLogEntry;
typedef struct sPAPREventSource sPAPREventSource;
typedef struct sPAPRPendingHPT sPAPRPendingHPT;
-typedef struct ICSState ICSState;
-typedef struct sPAPRXive sPAPRXive;
#define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL
#define SPAPR_ENTRY_POINT 0x100
diff --git a/include/hw/ppc/spapr_xive.h b/include/hw/ppc/spapr_xive.h
index 7fdc250574..9bec9192e4 100644
--- a/include/hw/ppc/spapr_xive.h
+++ b/include/hw/ppc/spapr_xive.h
@@ -41,8 +41,6 @@ bool spapr_xive_irq_claim(sPAPRXive *xive, uint32_t lisn, bool lsi);
bool spapr_xive_irq_free(sPAPRXive *xive, uint32_t lisn);
void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon);
-typedef struct sPAPRMachineState sPAPRMachineState;
-
void spapr_xive_hcall_init(sPAPRMachineState *spapr);
void spapr_dt_xive(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
uint32_t phandle);
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index 07508cbd21..fad786e8b2 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -200,13 +200,6 @@ void ics_pic_print_info(ICSState *ics, Monitor *mon);
void ics_resend(ICSState *ics);
void icp_resend(ICPState *ss);
-typedef struct sPAPRMachineState sPAPRMachineState;
-
-void spapr_dt_xics(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
- uint32_t phandle);
-int xics_kvm_init(sPAPRMachineState *spapr, Error **errp);
-void xics_spapr_init(sPAPRMachineState *spapr);
-
Object *icp_create(Object *cpu, const char *type, XICSFabric *xi,
Error **errp);
diff --git a/include/hw/ppc/xics_spapr.h b/include/hw/ppc/xics_spapr.h
new file mode 100644
index 0000000000..b1ab27d022
--- /dev/null
+++ b/include/hw/ppc/xics_spapr.h
@@ -0,0 +1,37 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics
+ *
+ * Copyright (c) 2010, 2011 David Gibson, IBM Corporation.
+ *
+ * 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 XICS_SPAPR_H
+#define XICS_SPAPR_H
+
+#include "hw/ppc/spapr.h"
+
+void spapr_dt_xics(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
+ uint32_t phandle);
+int xics_kvm_init(sPAPRMachineState *spapr, Error **errp);
+void xics_spapr_init(sPAPRMachineState *spapr);
+
+#endif /* XICS_SPAPR_H */
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 9614f76ae6..0a84c42756 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -250,6 +250,8 @@ struct PropertyInfo {
/**
* GlobalProperty:
* @used: Set to true if property was used when initializing a device.
+ * @optional: If set to true, GlobalProperty will be skipped without errors
+ * if the property doesn't exist.
*
* An error is fatal for non-hotplugged devices, when the global is applied.
*/
@@ -258,6 +260,7 @@ typedef struct GlobalProperty {
const char *property;
const char *value;
bool used;
+ bool optional;
} GlobalProperty;
static inline void
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
index 4d7f3c82ca..bd662752d2 100644
--- a/include/hw/virtio/virtio-net.h
+++ b/include/hw/virtio/virtio-net.h
@@ -44,6 +44,82 @@ typedef struct virtio_net_conf
uint8_t duplex;
} virtio_net_conf;
+/* Coalesced packets type & status */
+typedef enum {
+ RSC_COALESCE, /* Data been coalesced */
+ RSC_FINAL, /* Will terminate current connection */
+ RSC_NO_MATCH, /* No matched in the buffer pool */
+ RSC_BYPASS, /* Packet to be bypass, not tcp, tcp ctrl, etc */
+ RSC_CANDIDATE /* Data want to be coalesced */
+} CoalesceStatus;
+
+typedef struct VirtioNetRscStat {
+ uint32_t received;
+ uint32_t coalesced;
+ uint32_t over_size;
+ uint32_t cache;
+ uint32_t empty_cache;
+ uint32_t no_match_cache;
+ uint32_t win_update;
+ uint32_t no_match;
+ uint32_t tcp_syn;
+ uint32_t tcp_ctrl_drain;
+ uint32_t dup_ack;
+ uint32_t dup_ack1;
+ uint32_t dup_ack2;
+ uint32_t pure_ack;
+ uint32_t ack_out_of_win;
+ uint32_t data_out_of_win;
+ uint32_t data_out_of_order;
+ uint32_t data_after_pure_ack;
+ uint32_t bypass_not_tcp;
+ uint32_t tcp_option;
+ uint32_t tcp_all_opt;
+ uint32_t ip_frag;
+ uint32_t ip_ecn;
+ uint32_t ip_hacked;
+ uint32_t ip_option;
+ uint32_t purge_failed;
+ uint32_t drain_failed;
+ uint32_t final_failed;
+ int64_t timer;
+} VirtioNetRscStat;
+
+/* Rsc unit general info used to checking if can coalescing */
+typedef struct VirtioNetRscUnit {
+ void *ip; /* ip header */
+ uint16_t *ip_plen; /* data len pointer in ip header field */
+ struct tcp_header *tcp; /* tcp header */
+ uint16_t tcp_hdrlen; /* tcp header len */
+ uint16_t payload; /* pure payload without virtio/eth/ip/tcp */
+} VirtioNetRscUnit;
+
+/* Coalesced segmant */
+typedef struct VirtioNetRscSeg {
+ QTAILQ_ENTRY(VirtioNetRscSeg) next;
+ void *buf;
+ size_t size;
+ uint16_t packets;
+ uint16_t dup_ack;
+ bool is_coalesced; /* need recal ipv4 header checksum, mark here */
+ VirtioNetRscUnit unit;
+ NetClientState *nc;
+} VirtioNetRscSeg;
+
+typedef struct VirtIONet VirtIONet;
+
+/* Chain is divided by protocol(ipv4/v6) and NetClientInfo */
+typedef struct VirtioNetRscChain {
+ QTAILQ_ENTRY(VirtioNetRscChain) next;
+ VirtIONet *n; /* VirtIONet */
+ uint16_t proto;
+ uint8_t gso_type;
+ uint16_t max_payload;
+ QEMUTimer *drain_timer;
+ QTAILQ_HEAD(, VirtioNetRscSeg) buffers;
+ VirtioNetRscStat stat;
+} VirtioNetRscChain;
+
/* Maximum packet size we can receive from tap device: header + 64k */
#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 * KiB))
@@ -59,19 +135,25 @@ typedef struct VirtIONetQueue {
struct VirtIONet *n;
} VirtIONetQueue;
-typedef struct VirtIONet {
+struct VirtIONet {
VirtIODevice parent_obj;
uint8_t mac[ETH_ALEN];
uint16_t status;
VirtIONetQueue *vqs;
VirtQueue *ctrl_vq;
NICState *nic;
+ /* RSC Chains - temporary storage of coalesced data,
+ all these data are lost in case of migration */
+ QTAILQ_HEAD(, VirtioNetRscChain) rsc_chains;
uint32_t tx_timeout;
int32_t tx_burst;
uint32_t has_vnet_hdr;
size_t host_hdr_len;
size_t guest_hdr_len;
uint64_t host_features;
+ uint32_t rsc_timeout;
+ uint8_t rsc4_enabled;
+ uint8_t rsc6_enabled;
uint8_t has_ufo;
uint32_t mergeable_rx_bufs;
uint8_t promisc;
@@ -103,7 +185,7 @@ typedef struct VirtIONet {
int announce_counter;
bool needs_vnet_hdr_swap;
bool mtu_bypass_backend;
-} VirtIONet;
+};
void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
const char *type);
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 61bef3ef5c..067b126cf1 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -185,6 +185,7 @@ struct VMStateDescription {
int (*pre_load)(void *opaque);
int (*post_load)(void *opaque, int version_id);
int (*pre_save)(void *opaque);
+ int (*post_save)(void *opaque);
bool (*needed)(void *opaque);
const VMStateField *fields;
const VMStateDescription **subsections;
diff --git a/include/net/eth.h b/include/net/eth.h
index e6dc8a7ba0..7f45c678e7 100644
--- a/include/net/eth.h
+++ b/include/net/eth.h
@@ -177,6 +177,8 @@ struct tcp_hdr {
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
+#define TH_ECE 0x40
+#define TH_CWR 0x80
u_short th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h
index 261842beae..296b2fd572 100644
--- a/include/qemu/compiler.h
+++ b/include/qemu/compiler.h
@@ -113,6 +113,10 @@
#define GCC_FMT_ATTR(n, m)
#endif
+#ifndef __has_warning
+#define __has_warning(x) 0 /* compatibility with non-clang compilers */
+#endif
+
#ifndef __has_feature
#define __has_feature(x) 0 /* compatibility with non-clang compilers */
#endif
@@ -151,6 +155,21 @@
# define QEMU_ERROR(X)
#endif
+/*
+ * The nonstring variable attribute specifies that an object or member
+ * declaration with type array of char or pointer to char is intended
+ * to store character arrays that do not necessarily contain a terminating
+ * NUL character. This is useful in detecting uses of such arrays or pointers
+ * with functions that expect NUL-terminated strings, and to avoid warnings
+ * when such an array or pointer is used as an argument to a bounded string
+ * manipulation function such as strncpy.
+ */
+#if __has_attribute(nonstring)
+# define QEMU_NONSTRING __attribute__((nonstring))
+#else
+# define QEMU_NONSTRING
+#endif
+
/* Implement C11 _Generic via GCC builtins. Example:
*
* QEMU_GENERIC(x, (float, sinf), (long double, sinl), sin) (x)
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 741935fe36..5d1a2d8329 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -8,8 +8,6 @@
typedef struct AdapterInfo AdapterInfo;
typedef struct AddressSpace AddressSpace;
typedef struct AioContext AioContext;
-typedef struct AllwinnerAHCIState AllwinnerAHCIState;
-typedef struct AudioState AudioState;
typedef struct BdrvDirtyBitmap BdrvDirtyBitmap;
typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter;
typedef struct BlockBackend BlockBackend;
@@ -26,8 +24,6 @@ typedef struct DeviceListener DeviceListener;
typedef struct DeviceState DeviceState;
typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot;
typedef struct DisplayChangeListener DisplayChangeListener;
-typedef struct DisplayState DisplayState;
-typedef struct DisplaySurface DisplaySurface;
typedef struct DriveInfo DriveInfo;
typedef struct Error Error;
typedef struct EventNotifier EventNotifier;
@@ -36,7 +32,6 @@ typedef struct FWCfgEntry FWCfgEntry;
typedef struct FWCfgIoState FWCfgIoState;
typedef struct FWCfgMemState FWCfgMemState;
typedef struct FWCfgState FWCfgState;
-typedef struct HCIInfo HCIInfo;
typedef struct HVFX86EmulatorState HVFX86EmulatorState;
typedef struct I2CBus I2CBus;
typedef struct I2SCodec I2SCodec;
@@ -56,7 +51,6 @@ typedef struct MigrationIncomingState MigrationIncomingState;
typedef struct MigrationState MigrationState;
typedef struct Monitor Monitor;
typedef struct MonitorDef MonitorDef;
-typedef struct MouseTransformInfo MouseTransformInfo;
typedef struct MSIMessage MSIMessage;
typedef struct NetClientState NetClientState;
typedef struct NetFilterState NetFilterState;
@@ -76,19 +70,14 @@ typedef struct PCIExpressDevice PCIExpressDevice;
typedef struct PCIExpressHost PCIExpressHost;
typedef struct PCIHostDeviceAddress PCIHostDeviceAddress;
typedef struct PCIHostState PCIHostState;
-typedef struct PCMachineClass PCMachineClass;
typedef struct PCMachineState PCMachineState;
-typedef struct PCMCIACardState PCMCIACardState;
-typedef struct PixelFormat PixelFormat;
typedef struct PostcopyDiscardState PostcopyDiscardState;
typedef struct Property Property;
typedef struct PropertyInfo PropertyInfo;
-typedef struct PS2State PS2State;
typedef struct QBool QBool;
typedef struct QDict QDict;
typedef struct QEMUBH QEMUBH;
typedef struct QemuConsole QemuConsole;
-typedef struct QemuDmaBuf QemuDmaBuf;
typedef struct QEMUFile QEMUFile;
typedef struct QemuLockable QemuLockable;
typedef struct QemuMutex QemuMutex;
@@ -107,9 +96,7 @@ typedef struct QObject QObject;
typedef struct QString QString;
typedef struct RAMBlock RAMBlock;
typedef struct Range Range;
-typedef struct SerialState SerialState;
typedef struct SHPCDevice SHPCDevice;
-typedef struct SMBusDevice SMBusDevice;
typedef struct SSIBus SSIBus;
typedef struct uWireSlave uWireSlave;
typedef struct VirtIODevice VirtIODevice;
diff --git a/include/sysemu/bt.h b/include/sysemu/bt.h
index ddb05cd109..2fd8c0f14b 100644
--- a/include/sysemu/bt.h
+++ b/include/sysemu/bt.h
@@ -3,7 +3,7 @@
/* BT HCI info */
-struct HCIInfo {
+typedef struct HCIInfo {
int (*bdaddr_set)(struct HCIInfo *hci, const uint8_t *bd_addr);
void (*cmd_send)(struct HCIInfo *hci, const uint8_t *data, int len);
void (*sco_send)(struct HCIInfo *hci, const uint8_t *data, int len);
@@ -11,7 +11,7 @@ struct HCIInfo {
void *opaque;
void (*evt_recv)(void *opaque, const uint8_t *data, int len);
void (*acl_recv)(void *opaque, const uint8_t *data, int len);
-};
+} HCIInfo;
/* bt-host.c */
struct HCIInfo *bt_host_hci(const char *id);
diff --git a/include/ui/console.h b/include/ui/console.h
index 853fcf4eb7..fef900db76 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -65,13 +65,13 @@ void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry);
void kbd_put_ledstate(int ledstate);
-struct MouseTransformInfo {
+typedef struct MouseTransformInfo {
/* Touchscreen resolution */
int x;
int y;
/* Calibration values as used/generated by tslib */
int a[7];
-};
+} MouseTransformInfo;
void hmp_mouse_set(Monitor *mon, const QDict *qdict);
@@ -121,17 +121,7 @@ struct QemuConsoleClass {
#define QEMU_ALLOCATED_FLAG 0x01
-struct PixelFormat {
- uint8_t bits_per_pixel;
- uint8_t bytes_per_pixel;
- uint8_t depth; /* color depth in bits */
- uint32_t rmask, gmask, bmask, amask;
- uint8_t rshift, gshift, bshift, ashift;
- uint8_t rmax, gmax, bmax, amax;
- uint8_t rbits, gbits, bbits, abits;
-};
-
-struct DisplaySurface {
+typedef struct DisplaySurface {
pixman_format_code_t format;
pixman_image_t *image;
uint8_t flags;
@@ -140,7 +130,7 @@ struct DisplaySurface {
GLenum gltype;
GLuint texture;
#endif
-};
+} DisplaySurface;
typedef struct QemuUIInfo {
/* geometry */
@@ -179,7 +169,7 @@ struct QEMUGLParams {
int minor_ver;
};
-struct QemuDmaBuf {
+typedef struct QemuDmaBuf {
int fd;
uint32_t width;
uint32_t height;
@@ -187,7 +177,9 @@ struct QemuDmaBuf {
uint32_t fourcc;
uint32_t texture;
bool y0_top;
-};
+} QemuDmaBuf;
+
+typedef struct DisplayState DisplayState;
typedef struct DisplayChangeListenerOps {
const char *dpy_name;
diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h
index b7c82d17fc..0668109305 100644
--- a/include/ui/qemu-pixman.h
+++ b/include/ui/qemu-pixman.h
@@ -53,6 +53,16 @@
/* -------------------------------------------------------------------- */
+typedef struct PixelFormat {
+ uint8_t bits_per_pixel;
+ uint8_t bytes_per_pixel;
+ uint8_t depth; /* color depth in bits */
+ uint32_t rmask, gmask, bmask, amask;
+ uint8_t rshift, gshift, bshift, ashift;
+ uint8_t rmax, gmax, bmax, amax;
+ uint8_t rbits, gbits, bbits, abits;
+} PixelFormat;
+
PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format);
pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian);
pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format);
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 069df8f1f9..ef400cb78a 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -474,17 +474,13 @@ static inline int access_ok(int type, abi_ulong addr, abi_ulong size)
* functions than host-endian unaligned load/store plus tswapN.
* - The pragmas are necessary only to silence a clang false-positive
* warning: see https://bugs.llvm.org/show_bug.cgi?id=39113 .
- * - We have to disable -Wpragmas warnings to avoid a complaint about
- * an unknown warning type from older compilers that don't know about
- * -Waddress-of-packed-member.
* - gcc has bugs in its _Pragma() support in some versions, eg
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83256 -- so we only
* include the warning-suppression pragmas for clang
*/
-#ifdef __clang__
+#if defined(__clang__) && __has_warning("-Waddress-of-packed-member")
#define PRAGMA_DISABLE_PACKED_WARNING \
_Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wpragmas\""); \
_Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"")
#define PRAGMA_REENABLE_PACKED_WARNING \
diff --git a/migration/global_state.c b/migration/global_state.c
index 8e8ab5c51e..2c8c447239 100644
--- a/migration/global_state.c
+++ b/migration/global_state.c
@@ -42,6 +42,7 @@ int global_state_store(void)
void global_state_store_running(void)
{
const char *state = RunState_str(RUN_STATE_RUNNING);
+ assert(strlen(state) < sizeof(global_state.runstate));
strncpy((char *)global_state.runstate,
state, sizeof(global_state.runstate));
}
@@ -88,6 +89,17 @@ static int global_state_post_load(void *opaque, int version_id)
s->received = true;
trace_migrate_global_state_post_load(runstate);
+ if (strnlen((char *)s->runstate,
+ sizeof(s->runstate)) == sizeof(s->runstate)) {
+ /*
+ * This condition should never happen during migration, because
+ * all runstate names are shorter than 100 bytes (the size of
+ * s->runstate). However, a malicious stream could overflow
+ * the qapi_enum_parse() call, so we force the last character
+ * to a NUL byte.
+ */
+ s->runstate[sizeof(s->runstate) - 1] = '\0';
+ }
r = qapi_enum_parse(&RunState_lookup, runstate, -1, &local_err);
if (r == -1) {
@@ -106,7 +118,8 @@ static int global_state_pre_save(void *opaque)
GlobalState *s = opaque;
trace_migrate_global_state_pre_save((char *)s->runstate);
- s->size = strlen((char *)s->runstate) + 1;
+ s->size = strnlen((char *)s->runstate, sizeof(s->runstate)) + 1;
+ assert(s->size <= sizeof(s->runstate));
return 0;
}
diff --git a/migration/vmstate.c b/migration/vmstate.c
index 80b59009aa..e2bbb7b5f7 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -390,6 +390,9 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
if (ret) {
error_report("Save of field %s/%s failed",
vmsd->name, field->name);
+ if (vmsd->post_save) {
+ vmsd->post_save(opaque);
+ }
return ret;
}
@@ -415,7 +418,15 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
json_end_array(vmdesc);
}
- return vmstate_subsection_save(f, vmsd, opaque, vmdesc);
+ ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc);
+
+ if (vmsd->post_save) {
+ int ps_ret = vmsd->post_save(opaque);
+ if (!ret) {
+ ret = ps_ret;
+ }
+ }
+ return ret;
}
static const VMStateDescription *
diff --git a/nbd/client.c b/nbd/client.c
index f625c207c5..8a083c2f42 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -21,6 +21,7 @@
#include "qapi/error.h"
#include "trace.h"
#include "nbd-internal.h"
+#include "qemu/cutils.h"
/* Definitions for opaque data types */
@@ -234,18 +235,24 @@ static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply,
return result;
}
-/* Process another portion of the NBD_OPT_LIST reply. Set *@match if
- * the current reply matches @want or if the server does not support
- * NBD_OPT_LIST, otherwise leave @match alone. Return 0 if iteration
- * is complete, positive if more replies are expected, or negative
- * with @errp set if an unrecoverable error occurred. */
-static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
+/* nbd_receive_list:
+ * Process another portion of the NBD_OPT_LIST reply, populating any
+ * name received into *@name. If @description is non-NULL, and the
+ * server provided a description, that is also populated. The caller
+ * must eventually call g_free() on success.
+ * Returns 1 if name and description were set and iteration must continue,
+ * 0 if iteration is complete (including if OPT_LIST unsupported),
+ * -1 with @errp set if an unrecoverable error occurred.
+ */
+static int nbd_receive_list(QIOChannel *ioc, char **name, char **description,
Error **errp)
{
+ int ret = -1;
NBDOptionReply reply;
uint32_t len;
uint32_t namelen;
- char name[NBD_MAX_NAME_SIZE + 1];
+ char *local_name = NULL;
+ char *local_desc = NULL;
int error;
if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) {
@@ -253,9 +260,6 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
}
error = nbd_handle_reply_err(ioc, &reply, errp);
if (error <= 0) {
- /* The server did not support NBD_OPT_LIST, so set *match on
- * the assumption that any name will be accepted. */
- *match = true;
return error;
}
len = reply.length;
@@ -292,45 +296,54 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
nbd_send_opt_abort(ioc);
return -1;
}
- if (namelen != strlen(want)) {
- if (nbd_drop(ioc, len, errp) < 0) {
- error_prepend(errp,
- "failed to skip export name with wrong length: ");
- nbd_send_opt_abort(ioc);
- return -1;
- }
- return 1;
- }
- assert(namelen < sizeof(name));
- if (nbd_read(ioc, name, namelen, errp) < 0) {
+ local_name = g_malloc(namelen + 1);
+ if (nbd_read(ioc, local_name, namelen, errp) < 0) {
error_prepend(errp, "failed to read export name: ");
nbd_send_opt_abort(ioc);
- return -1;
+ goto out;
}
- name[namelen] = '\0';
+ local_name[namelen] = '\0';
len -= namelen;
- if (nbd_drop(ioc, len, errp) < 0) {
- error_prepend(errp, "failed to read export description: ");
- nbd_send_opt_abort(ioc);
- return -1;
+ if (len) {
+ local_desc = g_malloc(len + 1);
+ if (nbd_read(ioc, local_desc, len, errp) < 0) {
+ error_prepend(errp, "failed to read export description: ");
+ nbd_send_opt_abort(ioc);
+ goto out;
+ }
+ local_desc[len] = '\0';
}
- if (!strcmp(name, want)) {
- *match = true;
+
+ trace_nbd_receive_list(local_name, local_desc ?: "");
+ *name = local_name;
+ local_name = NULL;
+ if (description) {
+ *description = local_desc;
+ local_desc = NULL;
}
- return 1;
+ ret = 1;
+
+ out:
+ g_free(local_name);
+ g_free(local_desc);
+ return ret;
}
-/* Returns -1 if NBD_OPT_GO proves the export @wantname cannot be
- * used, 0 if NBD_OPT_GO is unsupported (fall back to NBD_OPT_LIST and
+/*
+ * nbd_opt_info_or_go:
+ * Send option for NBD_OPT_INFO or NBD_OPT_GO and parse the reply.
+ * Returns -1 if the option proves the export @info->name cannot be
+ * used, 0 if the option is unsupported (fall back to NBD_OPT_LIST and
* NBD_OPT_EXPORT_NAME in that case), and > 0 if the export is good to
- * go (with @info populated). */
-static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
- NBDExportInfo *info, Error **errp)
+ * go (with the rest of @info populated).
+ */
+static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt,
+ NBDExportInfo *info, Error **errp)
{
NBDOptionReply reply;
- uint32_t len = strlen(wantname);
+ uint32_t len = strlen(info->name);
uint16_t type;
int error;
char *buf;
@@ -340,16 +353,17 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
* flags still 0 is a witness of a broken server. */
info->flags = 0;
- trace_nbd_opt_go_start(wantname);
+ assert(opt == NBD_OPT_GO || opt == NBD_OPT_INFO);
+ trace_nbd_opt_info_go_start(nbd_opt_lookup(opt), info->name);
buf = g_malloc(4 + len + 2 + 2 * info->request_sizes + 1);
stl_be_p(buf, len);
- memcpy(buf + 4, wantname, len);
+ memcpy(buf + 4, info->name, len);
/* At most one request, everything else up to server */
stw_be_p(buf + 4 + len, info->request_sizes);
if (info->request_sizes) {
stw_be_p(buf + 4 + len + 2, NBD_INFO_BLOCK_SIZE);
}
- error = nbd_send_option_request(ioc, NBD_OPT_GO,
+ error = nbd_send_option_request(ioc, opt,
4 + len + 2 + 2 * info->request_sizes,
buf, errp);
g_free(buf);
@@ -358,7 +372,7 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
}
while (1) {
- if (nbd_receive_option_reply(ioc, NBD_OPT_GO, &reply, errp) < 0) {
+ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) {
return -1;
}
error = nbd_handle_reply_err(ioc, &reply, errp);
@@ -368,8 +382,10 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
len = reply.length;
if (reply.type == NBD_REP_ACK) {
- /* Server is done sending info and moved into transmission
- phase, but make sure it sent flags */
+ /*
+ * Server is done sending info, and moved into transmission
+ * phase for NBD_OPT_GO, but make sure it sent flags
+ */
if (len) {
error_setg(errp, "server sent invalid NBD_REP_ACK");
return -1;
@@ -378,7 +394,7 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
error_setg(errp, "broken server omitted NBD_INFO_EXPORT");
return -1;
}
- trace_nbd_opt_go_success();
+ trace_nbd_opt_info_go_success(nbd_opt_lookup(opt));
return 1;
}
if (reply.type != NBD_REP_INFO) {
@@ -472,12 +488,12 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
nbd_send_opt_abort(ioc);
return -1;
}
- trace_nbd_opt_go_info_block_size(info->min_block, info->opt_block,
- info->max_block);
+ trace_nbd_opt_info_block_size(info->min_block, info->opt_block,
+ info->max_block);
break;
default:
- trace_nbd_opt_go_info_unknown(type, nbd_info_lookup(type));
+ trace_nbd_opt_info_unknown(type, nbd_info_lookup(type));
if (nbd_drop(ioc, len, errp) < 0) {
error_prepend(errp, "Failed to read info payload: ");
nbd_send_opt_abort(ioc);
@@ -493,7 +509,8 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
const char *wantname,
Error **errp)
{
- bool foundExport = false;
+ bool list_empty = true;
+ bool found_export = false;
trace_nbd_receive_query_exports_start(wantname);
if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) {
@@ -501,14 +518,25 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
}
while (1) {
- int ret = nbd_receive_list(ioc, wantname, &foundExport, errp);
+ char *name;
+ int ret = nbd_receive_list(ioc, &name, NULL, errp);
if (ret < 0) {
/* Server gave unexpected reply */
return -1;
} else if (ret == 0) {
/* Done iterating. */
- if (!foundExport) {
+ if (list_empty) {
+ /*
+ * We don't have enough context to tell a server that
+ * sent an empty list apart from a server that does
+ * not support the list command; but as this function
+ * is just used to trigger a nicer error message
+ * before trying NBD_OPT_EXPORT_NAME, assume the
+ * export is available.
+ */
+ return 0;
+ } else if (!found_export) {
error_setg(errp, "No export with name '%s' available",
wantname);
nbd_send_opt_abort(ioc);
@@ -517,6 +545,11 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
trace_nbd_receive_query_exports_success(wantname);
return 0;
}
+ list_empty = false;
+ if (!strcmp(name, wantname)) {
+ found_export = true;
+ }
+ g_free(name);
}
}
@@ -605,51 +638,67 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
return QIO_CHANNEL(tioc);
}
-/* nbd_negotiate_simple_meta_context:
- * Set one meta context. Simple means that reply must contain zero (not
- * negotiated) or one (negotiated) contexts. More contexts would be considered
- * as a protocol error. It's also implied that meta-data query equals queried
- * context name, so, if server replies with something different than @context,
- * it is considered an error too.
- * return 1 for successful negotiation, context_id is set
- * 0 if operation is unsupported,
- * -1 with errp set for any other error
+/*
+ * nbd_send_meta_query:
+ * Send 0 or 1 set/list meta context queries.
+ * Return 0 on success, -1 with errp set for any error
*/
-static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
- const char *export,
- const char *context,
- uint32_t *context_id,
- Error **errp)
+static int nbd_send_meta_query(QIOChannel *ioc, uint32_t opt,
+ const char *export, const char *query,
+ Error **errp)
{
int ret;
- NBDOptionReply reply;
- uint32_t received_id = 0;
- bool received = false;
uint32_t export_len = strlen(export);
- uint32_t context_len = strlen(context);
- uint32_t data_len = sizeof(export_len) + export_len +
- sizeof(uint32_t) + /* number of queries */
- sizeof(context_len) + context_len;
- char *data = g_malloc(data_len);
- char *p = data;
-
- trace_nbd_opt_meta_request(context, export);
+ uint32_t queries = !!query;
+ uint32_t query_len = 0;
+ uint32_t data_len;
+ char *data;
+ char *p;
+
+ data_len = sizeof(export_len) + export_len + sizeof(queries);
+ if (query) {
+ query_len = strlen(query);
+ data_len += sizeof(query_len) + query_len;
+ } else {
+ assert(opt == NBD_OPT_LIST_META_CONTEXT);
+ }
+ p = data = g_malloc(data_len);
+
+ trace_nbd_opt_meta_request(nbd_opt_lookup(opt), query ?: "(all)", export);
stl_be_p(p, export_len);
memcpy(p += sizeof(export_len), export, export_len);
- stl_be_p(p += export_len, 1);
- stl_be_p(p += sizeof(uint32_t), context_len);
- memcpy(p += sizeof(context_len), context, context_len);
+ stl_be_p(p += export_len, queries);
+ if (query) {
+ stl_be_p(p += sizeof(queries), query_len);
+ memcpy(p += sizeof(query_len), query, query_len);
+ }
- ret = nbd_send_option_request(ioc, NBD_OPT_SET_META_CONTEXT, data_len, data,
- errp);
+ ret = nbd_send_option_request(ioc, opt, data_len, data, errp);
g_free(data);
- if (ret < 0) {
- return ret;
- }
+ return ret;
+}
- if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply,
- errp) < 0)
- {
+/*
+ * nbd_receive_one_meta_context:
+ * Called in a loop to receive and trace one set/list meta context reply.
+ * Pass non-NULL @name or @id to collect results back to the caller, which
+ * must eventually call g_free().
+ * return 1 if name is set and iteration must continue,
+ * 0 if iteration is complete (including if option is unsupported),
+ * -1 with errp set for any error
+ */
+static int nbd_receive_one_meta_context(QIOChannel *ioc,
+ uint32_t opt,
+ char **name,
+ uint32_t *id,
+ Error **errp)
+{
+ int ret;
+ NBDOptionReply reply;
+ char *local_name = NULL;
+ uint32_t local_id;
+
+ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) {
return -1;
}
@@ -658,29 +707,92 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
return ret;
}
- if (reply.type == NBD_REP_META_CONTEXT) {
- char *name;
-
- if (reply.length != sizeof(received_id) + context_len) {
- error_setg(errp, "Failed to negotiate meta context '%s', server "
- "answered with unexpected length %" PRIu32, context,
- reply.length);
+ if (reply.type == NBD_REP_ACK) {
+ if (reply.length != 0) {
+ error_setg(errp, "Unexpected length to ACK response");
nbd_send_opt_abort(ioc);
return -1;
}
+ return 0;
+ } else if (reply.type != NBD_REP_META_CONTEXT) {
+ error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)",
+ reply.type, nbd_rep_lookup(reply.type),
+ NBD_REP_META_CONTEXT, nbd_rep_lookup(NBD_REP_META_CONTEXT));
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
- if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) {
- return -1;
- }
- received_id = be32_to_cpu(received_id);
+ if (reply.length <= sizeof(local_id) ||
+ reply.length > NBD_MAX_BUFFER_SIZE) {
+ error_setg(errp, "Failed to negotiate meta context, server "
+ "answered with unexpected length %" PRIu32,
+ reply.length);
+ nbd_send_opt_abort(ioc);
+ return -1;
+ }
- reply.length -= sizeof(received_id);
- name = g_malloc(reply.length + 1);
- if (nbd_read(ioc, name, reply.length, errp) < 0) {
- g_free(name);
- return -1;
- }
- name[reply.length] = '\0';
+ if (nbd_read(ioc, &local_id, sizeof(local_id), errp) < 0) {
+ return -1;
+ }
+ local_id = be32_to_cpu(local_id);
+
+ reply.length -= sizeof(local_id);
+ local_name = g_malloc(reply.length + 1);
+ if (nbd_read(ioc, local_name, reply.length, errp) < 0) {
+ g_free(local_name);
+ return -1;
+ }
+ local_name[reply.length] = '\0';
+ trace_nbd_opt_meta_reply(nbd_opt_lookup(opt), local_name, local_id);
+
+ if (name) {
+ *name = local_name;
+ } else {
+ g_free(local_name);
+ }
+ if (id) {
+ *id = local_id;
+ }
+ return 1;
+}
+
+/*
+ * nbd_negotiate_simple_meta_context:
+ * Request the server to set the meta context for export @info->name
+ * using @info->x_dirty_bitmap with a fallback to "base:allocation",
+ * setting @info->context_id to the resulting id. Fail if the server
+ * responds with more than one context or with a context different
+ * than the query.
+ * return 1 for successful negotiation,
+ * 0 if operation is unsupported,
+ * -1 with errp set for any other error
+ */
+static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
+ NBDExportInfo *info,
+ Error **errp)
+{
+ /*
+ * TODO: Removing the x_dirty_bitmap hack will mean refactoring
+ * this function to request and store ids for multiple contexts
+ * (both base:allocation and a dirty bitmap), at which point this
+ * function should lose the term _simple.
+ */
+ int ret;
+ const char *context = info->x_dirty_bitmap ?: "base:allocation";
+ bool received = false;
+ char *name = NULL;
+
+ if (nbd_send_meta_query(ioc, NBD_OPT_SET_META_CONTEXT,
+ info->name, context, errp) < 0) {
+ return -1;
+ }
+
+ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT,
+ &name, &info->context_id, errp);
+ if (ret < 0) {
+ return -1;
+ }
+ if (ret == 1) {
if (strcmp(context, name)) {
error_setg(errp, "Failed to negotiate meta context '%s', server "
"answered with different context '%s'", context,
@@ -690,84 +802,115 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
return -1;
}
g_free(name);
-
- trace_nbd_opt_meta_reply(context, received_id);
received = true;
- /* receive NBD_REP_ACK */
- if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply,
- errp) < 0)
- {
+ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT,
+ NULL, NULL, errp);
+ if (ret < 0) {
return -1;
}
-
- ret = nbd_handle_reply_err(ioc, &reply, errp);
- if (ret <= 0) {
- return ret;
- }
}
-
- if (reply.type != NBD_REP_ACK) {
- error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)",
- reply.type, nbd_rep_lookup(reply.type),
- NBD_REP_ACK, nbd_rep_lookup(NBD_REP_ACK));
+ if (ret != 0) {
+ error_setg(errp, "Server answered with more than one context");
nbd_send_opt_abort(ioc);
return -1;
}
- if (reply.length) {
- error_setg(errp, "Unexpected length to ACK response");
- nbd_send_opt_abort(ioc);
+ return received;
+}
+
+/*
+ * nbd_list_meta_contexts:
+ * Request the server to list all meta contexts for export @info->name.
+ * return 0 if list is complete (even if empty),
+ * -1 with errp set for any error
+ */
+static int nbd_list_meta_contexts(QIOChannel *ioc,
+ NBDExportInfo *info,
+ Error **errp)
+{
+ int ret;
+ int seen_any = false;
+ int seen_qemu = false;
+
+ if (nbd_send_meta_query(ioc, NBD_OPT_LIST_META_CONTEXT,
+ info->name, NULL, errp) < 0) {
return -1;
}
- if (received) {
- *context_id = received_id;
- return 1;
+ while (1) {
+ char *context;
+
+ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_LIST_META_CONTEXT,
+ &context, NULL, errp);
+ if (ret == 0 && seen_any && !seen_qemu) {
+ /*
+ * Work around qemu 3.0 bug: the server forgot to send
+ * "qemu:" replies to 0 queries. If we saw at least one
+ * reply (probably base:allocation), but none of them were
+ * qemu:, then run a more specific query to make sure.
+ */
+ seen_qemu = true;
+ if (nbd_send_meta_query(ioc, NBD_OPT_LIST_META_CONTEXT,
+ info->name, "qemu:", errp) < 0) {
+ return -1;
+ }
+ continue;
+ }
+ if (ret <= 0) {
+ return ret;
+ }
+ seen_any = true;
+ seen_qemu |= strstart(context, "qemu:", NULL);
+ info->contexts = g_renew(char *, info->contexts, ++info->n_contexts);
+ info->contexts[info->n_contexts - 1] = context;
}
-
- return 0;
}
-int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
- QCryptoTLSCreds *tlscreds, const char *hostname,
- QIOChannel **outioc, NBDExportInfo *info,
- Error **errp)
+/*
+ * nbd_start_negotiate:
+ * Start the handshake to the server. After a positive return, the server
+ * is ready to accept additional NBD_OPT requests.
+ * Returns: negative errno: failure talking to server
+ * 0: server is oldstyle, must call nbd_negotiate_finish_oldstyle
+ * 1: server is newstyle, but can only accept EXPORT_NAME
+ * 2: server is newstyle, but lacks structured replies
+ * 3: server is newstyle and set up for structured replies
+ */
+static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
+ const char *hostname, QIOChannel **outioc,
+ bool structured_reply, bool *zeroes,
+ Error **errp)
{
uint64_t magic;
- int rc;
- bool zeroes = true;
- bool structured_reply = info->structured_reply;
- bool base_allocation = info->base_allocation;
- trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : "<null>");
-
- info->structured_reply = false;
- info->base_allocation = false;
- rc = -EINVAL;
+ trace_nbd_start_negotiate(tlscreds, hostname ? hostname : "<null>");
+ if (zeroes) {
+ *zeroes = true;
+ }
if (outioc) {
*outioc = NULL;
}
if (tlscreds && !outioc) {
error_setg(errp, "Output I/O channel required for TLS");
- goto fail;
+ return -EINVAL;
}
if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) {
error_prepend(errp, "Failed to read initial magic: ");
- goto fail;
+ return -EINVAL;
}
magic = be64_to_cpu(magic);
trace_nbd_receive_negotiate_magic(magic);
if (magic != NBD_INIT_MAGIC) {
error_setg(errp, "Bad initial magic received: 0x%" PRIx64, magic);
- goto fail;
+ return -EINVAL;
}
if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) {
error_prepend(errp, "Failed to read server magic: ");
- goto fail;
+ return -EINVAL;
}
magic = be64_to_cpu(magic);
trace_nbd_receive_negotiate_magic(magic);
@@ -779,7 +922,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
if (nbd_read(ioc, &globalflags, sizeof(globalflags), errp) < 0) {
error_prepend(errp, "Failed to read server flags: ");
- goto fail;
+ return -EINVAL;
}
globalflags = be16_to_cpu(globalflags);
trace_nbd_receive_negotiate_server_flags(globalflags);
@@ -788,136 +931,316 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE;
}
if (globalflags & NBD_FLAG_NO_ZEROES) {
- zeroes = false;
+ if (zeroes) {
+ *zeroes = false;
+ }
clientflags |= NBD_FLAG_C_NO_ZEROES;
}
/* client requested flags */
clientflags = cpu_to_be32(clientflags);
if (nbd_write(ioc, &clientflags, sizeof(clientflags), errp) < 0) {
error_prepend(errp, "Failed to send clientflags field: ");
- goto fail;
+ return -EINVAL;
}
if (tlscreds) {
if (fixedNewStyle) {
*outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp);
if (!*outioc) {
- goto fail;
+ return -EINVAL;
}
ioc = *outioc;
} else {
error_setg(errp, "Server does not support STARTTLS");
- goto fail;
+ return -EINVAL;
}
}
- if (!name) {
- trace_nbd_receive_negotiate_default_name();
- name = "";
- }
if (fixedNewStyle) {
- int result;
+ int result = 0;
if (structured_reply) {
result = nbd_request_simple_option(ioc,
NBD_OPT_STRUCTURED_REPLY,
errp);
if (result < 0) {
- goto fail;
+ return -EINVAL;
}
- info->structured_reply = result == 1;
}
+ return 2 + result;
+ } else {
+ return 1;
+ }
+ } else if (magic == NBD_CLIENT_MAGIC) {
+ if (tlscreds) {
+ error_setg(errp, "Server does not support STARTTLS");
+ return -EINVAL;
+ }
+ return 0;
+ } else {
+ error_setg(errp, "Bad server magic received: 0x%" PRIx64, magic);
+ return -EINVAL;
+ }
+}
- if (info->structured_reply && base_allocation) {
- result = nbd_negotiate_simple_meta_context(
- ioc, name, info->x_dirty_bitmap ?: "base:allocation",
- &info->meta_base_allocation_id, errp);
- if (result < 0) {
- goto fail;
- }
- info->base_allocation = result == 1;
- }
+/*
+ * nbd_negotiate_finish_oldstyle:
+ * Populate @info with the size and export flags from an oldstyle server,
+ * but does not consume 124 bytes of reserved zero padding.
+ * Returns 0 on success, -1 with @errp set on failure
+ */
+static int nbd_negotiate_finish_oldstyle(QIOChannel *ioc, NBDExportInfo *info,
+ Error **errp)
+{
+ uint32_t oldflags;
+
+ if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) {
+ error_prepend(errp, "Failed to read export length: ");
+ return -EINVAL;
+ }
+ info->size = be64_to_cpu(info->size);
+
+ if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) {
+ error_prepend(errp, "Failed to read export flags: ");
+ return -EINVAL;
+ }
+ oldflags = be32_to_cpu(oldflags);
+ if (oldflags & ~0xffff) {
+ error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags);
+ return -EINVAL;
+ }
+ info->flags = oldflags;
+ return 0;
+}
+
+/*
+ * nbd_receive_negotiate:
+ * Connect to server, complete negotiation, and move into transmission phase.
+ * Returns: negative errno: failure talking to server
+ * 0: server is connected
+ */
+int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
+ const char *hostname, QIOChannel **outioc,
+ NBDExportInfo *info, Error **errp)
+{
+ int result;
+ bool zeroes;
+ bool base_allocation = info->base_allocation;
+
+ assert(info->name);
+ trace_nbd_receive_negotiate_name(info->name);
+
+ result = nbd_start_negotiate(ioc, tlscreds, hostname, outioc,
+ info->structured_reply, &zeroes, errp);
- /* Try NBD_OPT_GO first - if it works, we are done (it
- * also gives us a good message if the server requires
- * TLS). If it is not available, fall back to
- * NBD_OPT_LIST for nicer error messages about a missing
- * export, then use NBD_OPT_EXPORT_NAME. */
- result = nbd_opt_go(ioc, name, info, errp);
+ info->structured_reply = false;
+ info->base_allocation = false;
+ if (tlscreds && *outioc) {
+ ioc = *outioc;
+ }
+
+ switch (result) {
+ case 3: /* newstyle, with structured replies */
+ info->structured_reply = true;
+ if (base_allocation) {
+ result = nbd_negotiate_simple_meta_context(ioc, info, errp);
if (result < 0) {
- goto fail;
- }
- if (result > 0) {
- return 0;
- }
- /* Check our desired export is present in the
- * server export list. Since NBD_OPT_EXPORT_NAME
- * cannot return an error message, running this
- * query gives us better error reporting if the
- * export name is not available.
- */
- if (nbd_receive_query_exports(ioc, name, errp) < 0) {
- goto fail;
+ return -EINVAL;
}
+ info->base_allocation = result == 1;
+ }
+ /* fall through */
+ case 2: /* newstyle, try OPT_GO */
+ /* Try NBD_OPT_GO first - if it works, we are done (it
+ * also gives us a good message if the server requires
+ * TLS). If it is not available, fall back to
+ * NBD_OPT_LIST for nicer error messages about a missing
+ * export, then use NBD_OPT_EXPORT_NAME. */
+ result = nbd_opt_info_or_go(ioc, NBD_OPT_GO, info, errp);
+ if (result < 0) {
+ return -EINVAL;
+ }
+ if (result > 0) {
+ return 0;
}
+ /* Check our desired export is present in the
+ * server export list. Since NBD_OPT_EXPORT_NAME
+ * cannot return an error message, running this
+ * query gives us better error reporting if the
+ * export name is not available.
+ */
+ if (nbd_receive_query_exports(ioc, info->name, errp) < 0) {
+ return -EINVAL;
+ }
+ /* fall through */
+ case 1: /* newstyle, but limited to EXPORT_NAME */
/* write the export name request */
- if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, name,
+ if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, info->name,
errp) < 0) {
- goto fail;
+ return -EINVAL;
}
/* Read the response */
if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) {
error_prepend(errp, "Failed to read export length: ");
- goto fail;
+ return -EINVAL;
}
info->size = be64_to_cpu(info->size);
if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) {
error_prepend(errp, "Failed to read export flags: ");
- goto fail;
+ return -EINVAL;
}
info->flags = be16_to_cpu(info->flags);
- } else if (magic == NBD_CLIENT_MAGIC) {
- uint32_t oldflags;
+ break;
+ case 0: /* oldstyle, parse length and flags */
+ if (*info->name) {
+ error_setg(errp, "Server does not support non-empty export names");
+ return -EINVAL;
+ }
+ if (nbd_negotiate_finish_oldstyle(ioc, info, errp) < 0) {
+ return -EINVAL;
+ }
+ break;
+ default:
+ return result;
+ }
+
+ trace_nbd_receive_negotiate_size_flags(info->size, info->flags);
+ if (zeroes && nbd_drop(ioc, 124, errp) < 0) {
+ error_prepend(errp, "Failed to read reserved block: ");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Clean up result of nbd_receive_export_list */
+void nbd_free_export_list(NBDExportInfo *info, int count)
+{
+ int i, j;
- if (name) {
- error_setg(errp, "Server does not support export names");
- goto fail;
+ if (!info) {
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ g_free(info[i].name);
+ g_free(info[i].description);
+ for (j = 0; j < info[i].n_contexts; j++) {
+ g_free(info[i].contexts[j]);
}
- if (tlscreds) {
- error_setg(errp, "Server does not support STARTTLS");
- goto fail;
+ g_free(info[i].contexts);
+ }
+ g_free(info);
+}
+
+/*
+ * nbd_receive_export_list:
+ * Query details about a server's exports, then disconnect without
+ * going into transmission phase. Return a count of the exports listed
+ * in @info by the server, or -1 on error. Caller must free @info using
+ * nbd_free_export_list().
+ */
+int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
+ const char *hostname, NBDExportInfo **info,
+ Error **errp)
+{
+ int result;
+ int count = 0;
+ int i;
+ int rc;
+ int ret = -1;
+ NBDExportInfo *array = NULL;
+ QIOChannel *sioc = NULL;
+
+ *info = NULL;
+ result = nbd_start_negotiate(ioc, tlscreds, hostname, &sioc, true, NULL,
+ errp);
+ if (tlscreds && sioc) {
+ ioc = sioc;
+ }
+
+ switch (result) {
+ case 2:
+ case 3:
+ /* newstyle - use NBD_OPT_LIST to populate array, then try
+ * NBD_OPT_INFO on each array member. If structured replies
+ * are enabled, also try NBD_OPT_LIST_META_CONTEXT. */
+ if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) {
+ goto out;
+ }
+ while (1) {
+ char *name;
+ char *desc;
+
+ rc = nbd_receive_list(ioc, &name, &desc, errp);
+ if (rc < 0) {
+ goto out;
+ } else if (rc == 0) {
+ break;
+ }
+ array = g_renew(NBDExportInfo, array, ++count);
+ memset(&array[count - 1], 0, sizeof(*array));
+ array[count - 1].name = name;
+ array[count - 1].description = desc;
+ array[count - 1].structured_reply = result == 3;
}
- if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) {
- error_prepend(errp, "Failed to read export length: ");
- goto fail;
+ for (i = 0; i < count; i++) {
+ array[i].request_sizes = true;
+ rc = nbd_opt_info_or_go(ioc, NBD_OPT_INFO, &array[i], errp);
+ if (rc < 0) {
+ goto out;
+ } else if (rc == 0) {
+ /*
+ * Pointless to try rest of loop. If OPT_INFO doesn't work,
+ * it's unlikely that meta contexts work either
+ */
+ break;
+ }
+
+ if (result == 3 &&
+ nbd_list_meta_contexts(ioc, &array[i], errp) < 0) {
+ goto out;
+ }
}
- info->size = be64_to_cpu(info->size);
- if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) {
- error_prepend(errp, "Failed to read export flags: ");
- goto fail;
+ /* Send NBD_OPT_ABORT as a courtesy before hanging up */
+ nbd_send_opt_abort(ioc);
+ break;
+ case 1: /* newstyle, but limited to EXPORT_NAME */
+ error_setg(errp, "Server does not support export lists");
+ /* We can't even send NBD_OPT_ABORT, so merely hang up */
+ goto out;
+ case 0: /* oldstyle, parse length and flags */
+ array = g_new0(NBDExportInfo, 1);
+ array->name = g_strdup("");
+ count = 1;
+
+ if (nbd_negotiate_finish_oldstyle(ioc, array, errp) < 0) {
+ goto out;
}
- oldflags = be32_to_cpu(oldflags);
- if (oldflags & ~0xffff) {
- error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags);
- goto fail;
+
+ /* Send NBD_CMD_DISC as a courtesy to the server, but ignore all
+ * errors now that we have the information we wanted. */
+ if (nbd_drop(ioc, 124, NULL) == 0) {
+ NBDRequest request = { .type = NBD_CMD_DISC };
+
+ nbd_send_request(ioc, &request);
}
- info->flags = oldflags;
- } else {
- error_setg(errp, "Bad server magic received: 0x%" PRIx64, magic);
- goto fail;
+ break;
+ default:
+ goto out;
}
- trace_nbd_receive_negotiate_size_flags(info->size, info->flags);
- if (zeroes && nbd_drop(ioc, 124, errp) < 0) {
- error_prepend(errp, "Failed to read reserved block: ");
- goto fail;
- }
- rc = 0;
+ *info = array;
+ array = NULL;
+ ret = count;
-fail:
- return rc;
+ out:
+ qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
+ qio_channel_close(ioc, NULL);
+ object_unref(OBJECT(sioc));
+ nbd_free_export_list(array, count);
+ return ret;
}
#ifdef __linux__
diff --git a/nbd/server.c b/nbd/server.c
index 6b136019f8..cb0d5634fa 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -77,8 +77,8 @@ struct NBDExport {
BlockBackend *blk;
char *name;
char *description;
- off_t dev_offset;
- off_t size;
+ uint64_t dev_offset;
+ uint64_t size;
uint16_t nbdflags;
QTAILQ_HEAD(, NBDClient) clients;
QTAILQ_ENTRY(NBDExport) next;
@@ -1455,8 +1455,8 @@ static void nbd_eject_notifier(Notifier *n, void *data)
nbd_export_close(exp);
}
-NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
- const char *name, const char *description,
+NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
+ uint64_t size, const char *name, const char *desc,
const char *bitmap, uint16_t nbdflags,
void (*close)(NBDExport *), bool writethrough,
BlockBackend *on_eject_blk, Error **errp)
@@ -1495,17 +1495,13 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
exp->refcount = 1;
QTAILQ_INIT(&exp->clients);
exp->blk = blk;
+ assert(dev_offset <= INT64_MAX);
exp->dev_offset = dev_offset;
exp->name = g_strdup(name);
- exp->description = g_strdup(description);
+ exp->description = g_strdup(desc);
exp->nbdflags = nbdflags;
- exp->size = size < 0 ? blk_getlength(blk) : size;
- if (exp->size < 0) {
- error_setg_errno(errp, -exp->size,
- "Failed to determine the NBD export's length");
- goto fail;
- }
- exp->size -= exp->size % BDRV_SECTOR_SIZE;
+ assert(size <= INT64_MAX - dev_offset);
+ exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE);
if (bitmap) {
BdrvDirtyBitmap *bm = NULL;
@@ -2134,10 +2130,10 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request,
return -EROFS;
}
if (request->from > client->exp->size ||
- request->from + request->len > client->exp->size) {
+ request->len > client->exp->size - request->from) {
error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32
", Size: %" PRIu64, request->from, request->len,
- (uint64_t)client->exp->size);
+ client->exp->size);
return (request->type == NBD_CMD_WRITE ||
request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL;
}
diff --git a/nbd/trace-events b/nbd/trace-events
index 5492042acb..7f10ebd4e0 100644
--- a/nbd/trace-events
+++ b/nbd/trace-events
@@ -3,20 +3,21 @@ nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending o
nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIu32" (%s), type %" PRIu32" (%s), len %" PRIu32
nbd_server_error_msg(uint32_t err, const char *type, const char *msg) "server reported error 0x%" PRIx32 " (%s) with additional message: %s"
nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIu32 " (%s), attempting fallback"
-nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'"
-nbd_opt_go_success(void) "Export is good to go"
-nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)"
-nbd_opt_go_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32
+nbd_receive_list(const char *name, const char *desc) "export list includes '%s', description '%s'"
+nbd_opt_info_go_start(const char *opt, const char *name) "Attempting %s for export '%s'"
+nbd_opt_info_go_success(const char *opt) "Export is ready after %s request"
+nbd_opt_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)"
+nbd_opt_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32
nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'"
nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'"
nbd_receive_starttls_new_client(void) "Setting up TLS"
nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake"
-nbd_opt_meta_request(const char *context, const char *export) "Requesting to set meta context %s for export %s"
-nbd_opt_meta_reply(const char *context, uint32_t id) "Received mapping of context %s to id %" PRIu32
-nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s"
+nbd_opt_meta_request(const char *optname, const char *context, const char *export) "Requesting %s %s for export %s"
+nbd_opt_meta_reply(const char *optname, const char *context, uint32_t id) "Received %s mapping of %s to id %" PRIu32
+nbd_start_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s"
nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64
nbd_receive_negotiate_server_flags(uint32_t globalflags) "Global flags are 0x%" PRIx32
-nbd_receive_negotiate_default_name(void) "Using default NBD export name \"\""
+nbd_receive_negotiate_name(const char *name) "Requesting NBD export name '%s'"
nbd_receive_negotiate_size_flags(uint64_t size, uint16_t flags) "Size is %" PRIu64 ", export flags 0x%" PRIx16
nbd_init_set_socket(void) "Setting NBD socket"
nbd_init_set_block_size(unsigned long block_size) "Setting block size to %lu"
diff --git a/pc-bios/efi-e1000.rom b/pc-bios/efi-e1000.rom
index 4da9de33da..6f088d41dd 100644
--- a/pc-bios/efi-e1000.rom
+++ b/pc-bios/efi-e1000.rom
Binary files differ
diff --git a/pc-bios/efi-e1000e.rom b/pc-bios/efi-e1000e.rom
index c2474a8fab..f536bdbd45 100644
--- a/pc-bios/efi-e1000e.rom
+++ b/pc-bios/efi-e1000e.rom
Binary files differ
diff --git a/pc-bios/efi-eepro100.rom b/pc-bios/efi-eepro100.rom
index 7950faf7cd..64d8891485 100644
--- a/pc-bios/efi-eepro100.rom
+++ b/pc-bios/efi-eepro100.rom
Binary files differ
diff --git a/pc-bios/efi-ne2k_pci.rom b/pc-bios/efi-ne2k_pci.rom
index 30edb1392a..02ad0cb505 100644
--- a/pc-bios/efi-ne2k_pci.rom
+++ b/pc-bios/efi-ne2k_pci.rom
Binary files differ
diff --git a/pc-bios/efi-pcnet.rom b/pc-bios/efi-pcnet.rom
index 23057c5724..88d25fa625 100644
--- a/pc-bios/efi-pcnet.rom
+++ b/pc-bios/efi-pcnet.rom
Binary files differ
diff --git a/pc-bios/efi-rtl8139.rom b/pc-bios/efi-rtl8139.rom
index beb9301839..53f125e3bb 100644
--- a/pc-bios/efi-rtl8139.rom
+++ b/pc-bios/efi-rtl8139.rom
Binary files differ
diff --git a/pc-bios/efi-virtio.rom b/pc-bios/efi-virtio.rom
index f4de5957ec..a87321e928 100644
--- a/pc-bios/efi-virtio.rom
+++ b/pc-bios/efi-virtio.rom
Binary files differ
diff --git a/pc-bios/efi-vmxnet3.rom b/pc-bios/efi-vmxnet3.rom
index 7501477ea6..d017cafe22 100644
--- a/pc-bios/efi-vmxnet3.rom
+++ b/pc-bios/efi-vmxnet3.rom
Binary files differ
diff --git a/pc-bios/s390-ccw/start.S b/pc-bios/s390-ccw/start.S
index eb8d024dbb..5c22cb0849 100644
--- a/pc-bios/s390-ccw/start.S
+++ b/pc-bios/s390-ccw/start.S
@@ -59,9 +59,9 @@ disabled_wait:
.globl consume_sclp_int
consume_sclp_int:
/* enable service interrupts in cr0 */
- stctg 0,0,0(15)
- oi 6(15), 0x2
- lctlg 0,0,0(15)
+ stctg %c0,%c0,0(%r15)
+ oi 6(%r15),0x2
+ lctlg %c0,%c0,0(%r15)
/* prepare external call handler */
larl %r1, external_new_code
stg %r1, 0x1b8
@@ -73,10 +73,10 @@ consume_sclp_int:
external_new_code:
/* disable service interrupts in cr0 */
- stctg 0,0,0(15)
- ni 6(15), 0xfd
- lctlg 0,0,0(15)
- br 14
+ stctg %c0,%c0,0(%r15)
+ ni 6(%r15),0xfd
+ lctlg %c0,%c0,0(%r15)
+ br %r14
.align 8
disabled_wait_psw:
diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi
index 81de5fc019..219206a836 100644
--- a/qemu-deprecated.texi
+++ b/qemu-deprecated.texi
@@ -126,11 +126,6 @@ documentation of ``query-hotpluggable-cpus'' for additional details.
@section System emulator devices
-@subsection ivshmem (since 2.6.0)
-
-The ``ivshmem'' device type is replaced by either the ``ivshmem-plain''
-or ``ivshmem-doorbell`` device types.
-
@subsection bluetooth (since 3.1)
The bluetooth subsystem is unmaintained since many years and likely bitrotten
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 51b55f2e06..1f7b2a03f5 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -76,7 +76,8 @@ static void usage(const char *name)
{
(printf) (
"Usage: %s [OPTIONS] FILE\n"
-"QEMU Disk Network Block Device Server\n"
+" or: %s -L [OPTIONS]\n"
+"QEMU Disk Network Block Device Utility\n"
"\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
@@ -98,6 +99,7 @@ static void usage(const char *name)
" -B, --bitmap=NAME expose a persistent dirty bitmap\n"
"\n"
"General purpose options:\n"
+" -L, --list list exports available from another NBD server\n"
" --object type,id=ID,... define an object such as 'secret' for providing\n"
" passwords and/or encryption keys\n"
" --tls-creds=ID use id of an earlier --object to provide TLS\n"
@@ -131,7 +133,7 @@ static void usage(const char *name)
" --image-opts treat FILE as a full set of image options\n"
"\n"
QEMU_HELP_BOTTOM "\n"
- , name, NBD_DEFAULT_PORT, "DEVICE");
+ , name, name, NBD_DEFAULT_PORT, "DEVICE");
}
static void version(const char *name)
@@ -176,7 +178,7 @@ static void read_partition(uint8_t *p, struct partition_record *r)
}
static int find_partition(BlockBackend *blk, int partition,
- off_t *offset, off_t *size)
+ uint64_t *offset, uint64_t *size)
{
struct partition_record mbr[4];
uint8_t data[MBR_SIZE];
@@ -243,6 +245,91 @@ static void termsig_handler(int signum)
}
+static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls,
+ const char *hostname)
+{
+ int ret = EXIT_FAILURE;
+ int rc;
+ Error *err = NULL;
+ QIOChannelSocket *sioc;
+ NBDExportInfo *list;
+ int i, j;
+
+ sioc = qio_channel_socket_new();
+ if (qio_channel_socket_connect_sync(sioc, saddr, &err) < 0) {
+ error_report_err(err);
+ return EXIT_FAILURE;
+ }
+ rc = nbd_receive_export_list(QIO_CHANNEL(sioc), tls, hostname, &list,
+ &err);
+ if (rc < 0) {
+ if (err) {
+ error_report_err(err);
+ }
+ goto out;
+ }
+ printf("exports available: %d\n", rc);
+ for (i = 0; i < rc; i++) {
+ printf(" export: '%s'\n", list[i].name);
+ if (list[i].description && *list[i].description) {
+ printf(" description: %s\n", list[i].description);
+ }
+ if (list[i].flags & NBD_FLAG_HAS_FLAGS) {
+ printf(" size: %" PRIu64 "\n", list[i].size);
+ printf(" flags: 0x%x (", list[i].flags);
+ if (list[i].flags & NBD_FLAG_READ_ONLY) {
+ printf(" readonly");
+ }
+ if (list[i].flags & NBD_FLAG_SEND_FLUSH) {
+ printf(" flush");
+ }
+ if (list[i].flags & NBD_FLAG_SEND_FUA) {
+ printf(" fua");
+ }
+ if (list[i].flags & NBD_FLAG_ROTATIONAL) {
+ printf(" rotational");
+ }
+ if (list[i].flags & NBD_FLAG_SEND_TRIM) {
+ printf(" trim");
+ }
+ if (list[i].flags & NBD_FLAG_SEND_WRITE_ZEROES) {
+ printf(" zeroes");
+ }
+ if (list[i].flags & NBD_FLAG_SEND_DF) {
+ printf(" df");
+ }
+ if (list[i].flags & NBD_FLAG_CAN_MULTI_CONN) {
+ printf(" multi");
+ }
+ if (list[i].flags & NBD_FLAG_SEND_RESIZE) {
+ printf(" resize");
+ }
+ if (list[i].flags & NBD_FLAG_SEND_CACHE) {
+ printf(" cache");
+ }
+ printf(" )\n");
+ }
+ if (list[i].min_block) {
+ printf(" min block: %u\n", list[i].min_block);
+ printf(" opt block: %u\n", list[i].opt_block);
+ printf(" max block: %u\n", list[i].max_block);
+ }
+ if (list[i].n_contexts) {
+ printf(" available meta contexts: %d\n", list[i].n_contexts);
+ for (j = 0; j < list[i].n_contexts; j++) {
+ printf(" %s\n", list[i].contexts[j]);
+ }
+ }
+ }
+ nbd_free_export_list(list, rc);
+
+ ret = EXIT_SUCCESS;
+ out:
+ object_unref(OBJECT(sioc));
+ return ret;
+}
+
+
#if HAVE_NBD_DEVICE
static void *show_parts(void *arg)
{
@@ -264,7 +351,7 @@ static void *show_parts(void *arg)
static void *nbd_client_thread(void *arg)
{
char *device = arg;
- NBDExportInfo info = { .request_sizes = false, };
+ NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") };
QIOChannelSocket *sioc;
int fd;
int ret;
@@ -279,7 +366,7 @@ static void *nbd_client_thread(void *arg)
goto out;
}
- ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL,
+ ret = nbd_receive_negotiate(QIO_CHANNEL(sioc),
NULL, NULL, NULL, &info, &local_error);
if (ret < 0) {
if (local_error) {
@@ -318,6 +405,7 @@ static void *nbd_client_thread(void *arg)
}
close(fd);
object_unref(OBJECT(sioc));
+ g_free(info.name);
kill(getpid(), SIGTERM);
return (void *) EXIT_SUCCESS;
@@ -326,6 +414,7 @@ out_fd:
out_socket:
object_unref(OBJECT(sioc));
out:
+ g_free(info.name);
kill(getpid(), SIGTERM);
return (void *) EXIT_FAILURE;
}
@@ -423,7 +512,8 @@ static QemuOptsList qemu_object_opts = {
-static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
+static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list,
+ Error **errp)
{
Object *obj;
QCryptoTLSCreds *creds;
@@ -443,10 +533,18 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
return NULL;
}
- if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
- error_setg(errp,
- "Expecting TLS credentials with a server endpoint");
- return NULL;
+ if (list) {
+ if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
+ error_setg(errp,
+ "Expecting TLS credentials with a client endpoint");
+ return NULL;
+ }
+ } else {
+ if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ error_setg(errp,
+ "Expecting TLS credentials with a server endpoint");
+ return NULL;
+ }
}
object_ref(obj);
return creds;
@@ -469,7 +567,8 @@ static void setup_address_and_port(const char **address, const char **port)
static const char *socket_activation_validate_opts(const char *device,
const char *sockpath,
const char *address,
- const char *port)
+ const char *port,
+ bool list)
{
if (device != NULL) {
return "NBD device can't be set when using socket activation";
@@ -487,6 +586,10 @@ static const char *socket_activation_validate_opts(const char *device,
return "TCP port number can't be set when using socket activation";
}
+ if (list) {
+ return "List mode is incompatible with socket activation";
+ }
+
return NULL;
}
@@ -500,17 +603,17 @@ int main(int argc, char **argv)
{
BlockBackend *blk;
BlockDriverState *bs;
- off_t dev_offset = 0;
+ uint64_t dev_offset = 0;
uint16_t nbdflags = 0;
bool disconnect = false;
const char *bindto = NULL;
const char *port = NULL;
char *sockpath = NULL;
char *device = NULL;
- off_t fd_size;
+ int64_t fd_size;
QemuOpts *sn_opts = NULL;
const char *sn_id_or_name = NULL;
- const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:";
+ const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:L";
struct option lopt[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
@@ -523,6 +626,7 @@ int main(int argc, char **argv)
{ "bitmap", required_argument, NULL, 'B' },
{ "connect", required_argument, NULL, 'c' },
{ "disconnect", no_argument, NULL, 'd' },
+ { "list", no_argument, NULL, 'L' },
{ "snapshot", no_argument, NULL, 's' },
{ "load-snapshot", required_argument, NULL, 'l' },
{ "nocache", no_argument, NULL, 'n' },
@@ -546,9 +650,8 @@ int main(int argc, char **argv)
};
int ch;
int opt_ind = 0;
- char *end;
int flags = BDRV_O_RDWR;
- int partition = -1;
+ int partition = 0;
int ret = 0;
bool seen_cache = false;
bool seen_discard = false;
@@ -558,7 +661,7 @@ int main(int argc, char **argv)
Error *local_err = NULL;
BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
QDict *options = NULL;
- const char *export_name = ""; /* Default export name */
+ const char *export_name = NULL; /* defaults to "" later for server mode */
const char *export_description = NULL;
const char *bitmap = NULL;
const char *tlscredsid = NULL;
@@ -566,6 +669,7 @@ int main(int argc, char **argv)
bool writethrough = true;
char *trace_file = NULL;
bool fork_process = false;
+ bool list = false;
int old_stderr = -1;
unsigned socket_activation;
@@ -660,13 +764,8 @@ int main(int argc, char **argv)
port = optarg;
break;
case 'o':
- dev_offset = strtoll (optarg, &end, 0);
- if (*end) {
- error_report("Invalid offset `%s'", optarg);
- exit(EXIT_FAILURE);
- }
- if (dev_offset < 0) {
- error_report("Offset must be positive `%s'", optarg);
+ if (qemu_strtou64(optarg, NULL, 0, &dev_offset) < 0) {
+ error_report("Invalid offset '%s'", optarg);
exit(EXIT_FAILURE);
}
break;
@@ -688,13 +787,9 @@ int main(int argc, char **argv)
flags &= ~BDRV_O_RDWR;
break;
case 'P':
- partition = strtol(optarg, &end, 0);
- if (*end) {
- error_report("Invalid partition `%s'", optarg);
- exit(EXIT_FAILURE);
- }
- if (partition < 1 || partition > 8) {
- error_report("Invalid partition %d", partition);
+ if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 ||
+ partition < 1 || partition > 8) {
+ error_report("Invalid partition '%s'", optarg);
exit(EXIT_FAILURE);
}
break;
@@ -715,15 +810,11 @@ int main(int argc, char **argv)
device = optarg;
break;
case 'e':
- shared = strtol(optarg, &end, 0);
- if (*end) {
+ if (qemu_strtoi(optarg, NULL, 0, &shared) < 0 ||
+ shared < 1) {
error_report("Invalid shared device number '%s'", optarg);
exit(EXIT_FAILURE);
}
- if (shared < 1) {
- error_report("Shared device number must be greater than 0");
- exit(EXIT_FAILURE);
- }
break;
case 'f':
fmt = optarg;
@@ -772,13 +863,33 @@ int main(int argc, char **argv)
case QEMU_NBD_OPT_FORK:
fork_process = true;
break;
+ case 'L':
+ list = true;
+ break;
}
}
- if ((argc - optind) != 1) {
+ if (list) {
+ if (argc != optind) {
+ error_report("List mode is incompatible with a file name");
+ exit(EXIT_FAILURE);
+ }
+ if (export_name || export_description || dev_offset || partition ||
+ device || disconnect || fmt || sn_id_or_name || bitmap ||
+ seen_aio || seen_discard || seen_cache) {
+ error_report("List mode is incompatible with per-device settings");
+ exit(EXIT_FAILURE);
+ }
+ if (fork_process) {
+ error_report("List mode is incompatible with forking");
+ exit(EXIT_FAILURE);
+ }
+ } else if ((argc - optind) != 1) {
error_report("Invalid number of arguments");
error_printf("Try `%s --help' for more information.\n", argv[0]);
exit(EXIT_FAILURE);
+ } else if (!export_name) {
+ export_name = "";
}
qemu_opts_foreach(&qemu_object_opts,
@@ -797,7 +908,8 @@ int main(int argc, char **argv)
} else {
/* Using socket activation - check user didn't use -p etc. */
const char *err_msg = socket_activation_validate_opts(device, sockpath,
- bindto, port);
+ bindto, port,
+ list);
if (err_msg != NULL) {
error_report("%s", err_msg);
exit(EXIT_FAILURE);
@@ -820,7 +932,7 @@ int main(int argc, char **argv)
error_report("TLS is not supported with a host device");
exit(EXIT_FAILURE);
}
- tlscreds = nbd_get_tls_creds(tlscredsid, &local_err);
+ tlscreds = nbd_get_tls_creds(tlscredsid, list, &local_err);
if (local_err) {
error_report("Failed to get TLS creds %s",
error_get_pretty(local_err));
@@ -828,6 +940,11 @@ int main(int argc, char **argv)
}
}
+ if (list) {
+ saddr = nbd_build_socket_address(sockpath, bindto, port);
+ return qemu_nbd_client_list(saddr, tlscreds, bindto);
+ }
+
#if !HAVE_NBD_DEVICE
if (disconnect || device) {
error_report("Kernel /dev/nbdN support not available");
@@ -1005,20 +1122,37 @@ int main(int argc, char **argv)
}
if (dev_offset >= fd_size) {
- error_report("Offset (%lld) has to be smaller than the image size "
- "(%lld)",
- (long long int)dev_offset, (long long int)fd_size);
+ error_report("Offset (%" PRIu64 ") has to be smaller than the image "
+ "size (%" PRId64 ")", dev_offset, fd_size);
exit(EXIT_FAILURE);
}
fd_size -= dev_offset;
- if (partition != -1) {
- ret = find_partition(blk, partition, &dev_offset, &fd_size);
+ if (partition) {
+ uint64_t limit;
+
+ if (dev_offset) {
+ error_report("Cannot request partition and offset together");
+ exit(EXIT_FAILURE);
+ }
+ ret = find_partition(blk, partition, &dev_offset, &limit);
if (ret < 0) {
error_report("Could not find partition %d: %s", partition,
strerror(-ret));
exit(EXIT_FAILURE);
}
+ /*
+ * MBR partition limits are (32-bit << 9); this assert lets
+ * the compiler know that we can't overflow 64 bits.
+ */
+ assert(dev_offset + limit >= dev_offset);
+ if (dev_offset + limit > fd_size) {
+ error_report("Discovered partition %d at offset %" PRIu64
+ " size %" PRIu64 ", but size exceeds file length %"
+ PRId64, partition, dev_offset, limit, fd_size);
+ exit(EXIT_FAILURE);
+ }
+ fd_size = limit;
}
export = nbd_export_new(bs, dev_offset, fd_size, export_name,
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
index 96b1546006..386bece468 100644
--- a/qemu-nbd.texi
+++ b/qemu-nbd.texi
@@ -2,6 +2,8 @@
@c man begin SYNOPSIS
@command{qemu-nbd} [OPTION]... @var{filename}
+@command{qemu-nbd} @option{-L} [OPTION]...
+
@command{qemu-nbd} @option{-d} @var{dev}
@c man end
@end example
@@ -10,11 +12,19 @@
Export a QEMU disk image using the NBD protocol.
+Other uses:
+@itemize
+@item
+Bind a /dev/nbdX block device to a QEMU server (on Linux).
+@item
+As a client to query exports of a remote NBD server.
+@end itemize
+
@c man end
@c man begin OPTIONS
@var{filename} is a disk image filename, or a set of block
-driver options if @var{--image-opts} is specified.
+driver options if @option{--image-opts} is specified.
@var{dev} is an NBD device.
@@ -25,26 +35,29 @@ See the @code{qemu(1)} manual page for full details of the properties
supported. The common object types that it makes sense to define are the
@code{secret} object, which is used to supply passwords and/or encryption
keys, and the @code{tls-creds} object, which is used to supply TLS
-credentials for the qemu-nbd server.
+credentials for the qemu-nbd server or client.
@item -p, --port=@var{port}
-The TCP port to listen on (default @samp{10809})
+The TCP port to listen on as a server, or connect to as a client
+(default @samp{10809}).
@item -o, --offset=@var{offset}
-The offset into the image
+The offset into the image.
@item -b, --bind=@var{iface}
-The interface to bind to (default @samp{0.0.0.0})
+The interface to bind to as a server, or connect to as a client
+(default @samp{0.0.0.0}).
@item -k, --socket=@var{path}
-Use a unix socket with path @var{path}
+Use a unix socket with path @var{path}.
@item --image-opts
Treat @var{filename} as a set of image options, instead of a plain
filename. If this flag is specified, the @var{-f} flag should
not be used, instead the '@code{format=}' option should be set.
@item -f, --format=@var{fmt}
Force the use of the block driver for format @var{fmt} instead of
-auto-detecting
+auto-detecting.
@item -r, --read-only
-Export the disk as read-only
+Export the disk as read-only.
@item -P, --partition=@var{num}
-Only expose partition @var{num}
+Only expose MBR partition @var{num}. Understands physical partitions
+1-4 and logical partitions 5-8.
@item -B, --bitmap=@var{name}
If @var{filename} has a qcow2 persistent bitmap @var{name}, expose
that bitmap via the ``qemu:dirty-bitmap:@var{name}'' context
@@ -52,7 +65,7 @@ accessible through NBD_OPT_SET_META_CONTEXT.
@item -s, --snapshot
Use @var{filename} as an external snapshot, create a temporary
file with backing_file=@var{filename}, redirect the write to
-the temporary one
+the temporary one.
@item -l, --load-snapshot=@var{snapshot_param}
Load an internal snapshot inside @var{filename} and export it
as an read-only device, @var{snapshot_param} format is
@@ -76,31 +89,38 @@ driver-specific optimized zero write commands. @var{detect-zeroes} is one of
converts a zero write to an unmap operation and can only be used if
@var{discard} is set to @samp{unmap}. The default is @samp{off}.
@item -c, --connect=@var{dev}
-Connect @var{filename} to NBD device @var{dev}
+Connect @var{filename} to NBD device @var{dev} (Linux only).
@item -d, --disconnect
-Disconnect the device @var{dev}
+Disconnect the device @var{dev} (Linux only).
@item -e, --shared=@var{num}
-Allow up to @var{num} clients to share the device (default @samp{1})
+Allow up to @var{num} clients to share the device (default
+@samp{1}). Safe for readers, but for now, consistency is not
+guaranteed between multiple writers.
@item -t, --persistent
-Don't exit on the last connection
+Don't exit on the last connection.
@item -x, --export-name=@var{name}
-Set the NBD volume export name. This switches the server to use
-the new style NBD protocol negotiation
+Set the NBD volume export name (default of a zero-length string).
@item -D, --description=@var{description}
Set the NBD volume export description, as a human-readable
-string. Requires the use of @option{-x}
+string.
+@item -L, --list
+Connect as a client and list all details about the exports exposed by
+a remote NBD server. This enables list mode, and is incompatible
+with options that change behavior related to a specific export (such as
+@option{--export-name}, @option{--offset}, ...).
@item --tls-creds=ID
Enable mandatory TLS encryption for the server by setting the ID
of the TLS credentials object previously created with the --object
-option.
+option; or provide the credentials needed for connecting as a client
+in list mode.
@item --fork
Fork off the server process and exit the parent once the server is running.
@item -v, --verbose
-Display extra debugging information
+Display extra debugging information.
@item -h, --help
-Display this help and exit
+Display this help and exit.
@item -V, --version
-Display version information and exit
+Display version information and exit.
@item -T, --trace [[enable=]@var{pattern}][,events=@var{file}][,file=@var{file}]
@findex --trace
@include qemu-option-trace.texi
@@ -108,6 +128,63 @@ Display version information and exit
@c man end
+@c man begin EXAMPLES
+Start a server listening on port 10809 that exposes only the
+guest-visible contents of a qcow2 file, with no TLS encryption, and
+with the default export name (an empty string). The command is
+one-shot, and will block until the first successful client
+disconnects:
+
+@example
+qemu-nbd -f qcow2 file.qcow2
+@end example
+
+Start a long-running server listening with encryption on port 10810,
+and require clients to have a correct X.509 certificate to connect to
+a 1 megabyte subset of a raw file, using the export name 'subset':
+
+@example
+qemu-nbd \
+ --object tls-creds-x509,id=tls0,endpoint=server,dir=/path/to/qemutls \
+ --tls-creds tls0 -t -x subset -p 10810 \
+ --image-opts driver=raw,offset=1M,size=1M,file.driver=file,file.filename=file.raw
+@end example
+
+Serve a read-only copy of just the first MBR partition of a guest
+image over a Unix socket with as many as 5 simultaneous readers, with
+a persistent process forked as a daemon:
+
+@example
+qemu-nbd --fork --persistent --shared=5 --socket=/path/to/sock \
+ --partition=1 --read-only --format=qcow2 file.qcow2
+@end example
+
+Expose the guest-visible contents of a qcow2 file via a block device
+/dev/nbd0 (and possibly creating /dev/nbd0p1 and friends for
+partitions found within), then disconnect the device when done.
+Access to bind qemu-nbd to an /dev/nbd device generally requires root
+privileges, and may also require the execution of @code{modprobe nbd}
+to enable the kernel NBD client module. @emph{CAUTION}: Do not use
+this method to mount filesystems from an untrusted guest image - a
+malicious guest may have prepared the image to attempt to trigger
+kernel bugs in partition probing or file system mounting.
+
+@example
+qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2
+qemu-nbd -d /dev/nbd0
+@end example
+
+Query a remote server to see details about what export(s) it is
+serving on port 10809, and authenticating via PSK:
+
+@example
+qemu-nbd \
+ --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=eblake,endpoint=client \
+ --tls-creds tls0 -L -b remote.example.com
+@end example
+
+@c man end
+
@ignore
@setfilename qemu-nbd
diff --git a/qemu-seccomp.c b/qemu-seccomp.c
index 5c73e6ad05..36d5829831 100644
--- a/qemu-seccomp.c
+++ b/qemu-seccomp.c
@@ -41,7 +41,8 @@ struct QemuSeccompSyscall {
};
const struct scmp_arg_cmp sched_setscheduler_arg[] = {
- SCMP_A1(SCMP_CMP_NE, SCHED_IDLE)
+ /* was SCMP_A1(SCMP_CMP_NE, SCHED_IDLE), but expanded due to GCC 4.x bug */
+ { .arg = 1, .op = SCMP_CMP_NE, .datum_a = SCHED_IDLE }
};
static const struct QemuSeccompSyscall blacklist[] = {
diff --git a/qom/object.c b/qom/object.c
index 4e5226ca12..b8c732063b 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -385,6 +385,9 @@ void object_apply_global_props(Object *obj, const GPtrArray *props, Error **errp
if (object_dynamic_cast(obj, p->driver) == NULL) {
continue;
}
+ if (p->optional && !object_property_find(obj, p->property, NULL)) {
+ continue;
+ }
p->used = true;
object_property_parse(obj, p->value, p->property, &err);
if (err != NULL) {
diff --git a/roms/ipxe b/roms/ipxe
-Subproject 0600d3ae94f93efd10fc6b3c7420a9557a3a167
+Subproject de4565cbe76ea9f7913a01f331be3ee901bb6e1
diff --git a/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py
index 30090bdfff..7d9b574300 100755
--- a/scripts/analyse-locks-simpletrace.py
+++ b/scripts/analyse-locks-simpletrace.py
@@ -7,7 +7,6 @@
#
from __future__ import print_function
-import os
import simpletrace
import argparse
import numpy as np
diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py
index 5c2010c917..e527eb168e 100755
--- a/scripts/analyze-migration.py
+++ b/scripts/analyze-migration.py
@@ -23,7 +23,6 @@ import json
import os
import argparse
import collections
-import pprint
def mkdir_p(path):
try:
diff --git a/scripts/device-crash-test b/scripts/device-crash-test
index e93a7c0c84..483dafb2fc 100755
--- a/scripts/device-crash-test
+++ b/scripts/device-crash-test
@@ -26,7 +26,6 @@ check for crashes and unexpected errors.
from __future__ import print_function
import sys
-import os
import glob
import logging
import traceback
@@ -83,7 +82,6 @@ ERROR_WHITELIST = [
{'device':'isa-ipmi-bt', 'expected':True}, # IPMI device requires a bmc attribute to be set
{'device':'isa-ipmi-kcs', 'expected':True}, # IPMI device requires a bmc attribute to be set
{'device':'isa-parallel', 'expected':True}, # Can't create serial device, empty char device
- {'device':'ivshmem', 'expected':True}, # You must specify either 'shm' or 'chardev'
{'device':'ivshmem-doorbell', 'expected':True}, # You must specify a 'chardev'
{'device':'ivshmem-plain', 'expected':True}, # You must specify a 'memdev'
{'device':'loader', 'expected':True}, # please include valid arguments
@@ -182,21 +180,6 @@ ERROR_WHITELIST = [
# other exitcode=1 failures not listed above will just generate INFO messages:
{'exitcode':1, 'loglevel':logging.INFO},
- # KNOWN CRASHES:
- # Known crashes will generate error messages, but won't be fatal.
- # Those entries must be removed once we fix the crashes.
- {'exitcode':-6, 'log':r"Device 'serial0' is in use", 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r"qemu_net_client_setup: Assertion `!peer->peer' failed", 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r'RAMBlock "[\w.-]+" already registered', 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r"find_ram_offset: Assertion `size != 0' failed.", 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r"add_cpreg_to_hashtable: code should not be reached", 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r"qemu_alloc_display: Assertion `surface->image != NULL' failed", 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r"Unexpected error in error_set_from_qdev_prop_error", 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r"Object .* is not an instance of type spapr-machine", 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r"Object .* is not an instance of type generic-pc-machine", 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r"Object .* is not an instance of type e500-ccsr", 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR},
-
# everything else (including SIGABRT and SIGSEGV) will be a fatal error:
{'exitcode':None, 'fatal':True, 'loglevel':logging.FATAL},
]
diff --git a/scripts/qemu.py b/scripts/qemu.py
index 6e3b0e6771..0a5e02eb56 100644
--- a/scripts/qemu.py
+++ b/scripts/qemu.py
@@ -351,7 +351,7 @@ class QEMUMachine(object):
command = ' '.join(self._qemu_full_args)
else:
command = ''
- LOG.warn(msg, exitcode, command)
+ LOG.warn(msg, -exitcode, command)
self._launched = False
diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py
index 5ae77c8a92..ee7fda2638 100755
--- a/scripts/replay-dump.py
+++ b/scripts/replay-dump.py
@@ -3,7 +3,7 @@
#
# Dump the contents of a recorded execution stream
#
-# Copyright (c) 2017 Alex Bennée <alex.bennee@linaro.org>
+# Copyright (c) 2017 Alex Bennée <alex.bennee@linaro.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py
index 4ad34f90cd..45485b864b 100755
--- a/scripts/simpletrace.py
+++ b/scripts/simpletrace.py
@@ -11,7 +11,6 @@
from __future__ import print_function
import struct
-import re
import inspect
from tracetool import read_events, Event
from tracetool.backend.simple import is_string
diff --git a/scripts/texi2pod.pl b/scripts/texi2pod.pl
index 39ce584a32..839b7917cf 100755
--- a/scripts/texi2pod.pl
+++ b/scripts/texi2pod.pl
@@ -398,7 +398,7 @@ $sects{NAME} = "$fn \- $tl\n";
$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES};
for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES
- BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) {
+ BUGS NOTES FOOTNOTES EXAMPLES SEEALSO AUTHOR COPYRIGHT)) {
if(exists $sects{$sect}) {
$head = $sect;
$head =~ s/SEEALSO/SEE ALSO/;
diff --git a/scripts/tracetool.py b/scripts/tracetool.py
index fe2b0771f2..3beaa66bd8 100755
--- a/scripts/tracetool.py
+++ b/scripts/tracetool.py
@@ -15,8 +15,6 @@ __email__ = "stefanha@linux.vnet.ibm.com"
import sys
import getopt
-import os.path
-import re
from tracetool import error_write, out
import tracetool.backend
diff --git a/scripts/tracetool/format/simpletrace_stap.py b/scripts/tracetool/format/simpletrace_stap.py
index e7e44842ca..57b04061cf 100644
--- a/scripts/tracetool/format/simpletrace_stap.py
+++ b/scripts/tracetool/format/simpletrace_stap.py
@@ -14,7 +14,7 @@ __email__ = "stefanha@redhat.com"
from tracetool import out
-from tracetool.backend.dtrace import binary, probeprefix
+from tracetool.backend.dtrace import probeprefix
from tracetool.backend.simple import is_string
from tracetool.format.stap import stap_escape
diff --git a/stubs/tpm.c b/stubs/tpm.c
index 80939cd3db..66c99d667d 100644
--- a/stubs/tpm.c
+++ b/stubs/tpm.c
@@ -8,6 +8,7 @@
#include "qemu/osdep.h"
#include "qapi/qapi-commands-tpm.h"
#include "sysemu/tpm.h"
+#include "hw/acpi/tpm.h"
void tpm_init(void)
{
@@ -31,3 +32,7 @@ TpmModelList *qmp_query_tpm_models(Error **errp)
{
return NULL;
}
+
+void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev)
+{
+}
diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs
index 11c7baf8a3..1a4fc06448 100644
--- a/target/arm/Makefile.objs
+++ b/target/arm/Makefile.objs
@@ -8,6 +8,7 @@ obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += neon_helper.o iwmmxt_helper.o vec_helper.o
obj-y += gdbstub.o
obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o
+obj-$(TARGET_AARCH64) += pauth_helper.o
obj-y += crypto_helper.o
obj-$(CONFIG_SOFTMMU) += arm-powerctl.o
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 4c4e9e169e..7e1f3dd637 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -162,6 +162,9 @@ static void arm_cpu_reset(CPUState *s)
env->pstate = PSTATE_MODE_EL0t;
/* Userspace expects access to DC ZVA, CTL_EL0 and the cache ops */
env->cp15.sctlr_el[1] |= SCTLR_UCT | SCTLR_UCI | SCTLR_DZE;
+ /* Enable all PAC instructions */
+ env->cp15.hcr_el2 |= HCR_API;
+ env->cp15.scr_el3 |= SCR_API;
/* and to the FP/Neon instructions */
env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 20, 2, 3);
/* and to the SVE instructions */
@@ -1034,7 +1037,19 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
if (!cpu->has_pmu) {
unset_feature(env, ARM_FEATURE_PMU);
+ }
+ if (arm_feature(env, ARM_FEATURE_PMU)) {
+ cpu->pmceid0 = get_pmceid(&cpu->env, 0);
+ cpu->pmceid1 = get_pmceid(&cpu->env, 1);
+
+ if (!kvm_enabled()) {
+ arm_register_pre_el_change_hook(cpu, &pmu_pre_el_change, 0);
+ arm_register_el_change_hook(cpu, &pmu_post_el_change, 0);
+ }
+ } else {
cpu->id_aa64dfr0 &= ~0xf00;
+ cpu->pmceid0 = 0;
+ cpu->pmceid1 = 0;
}
if (!arm_feature(env, ARM_FEATURE_EL2)) {
@@ -1679,8 +1694,6 @@ static void cortex_a7_initfn(Object *obj)
cpu->id_pfr0 = 0x00001131;
cpu->id_pfr1 = 0x00011011;
cpu->id_dfr0 = 0x02010555;
- cpu->pmceid0 = 0x00000000;
- cpu->pmceid1 = 0x00000000;
cpu->id_afr0 = 0x00000000;
cpu->id_mmfr0 = 0x10101105;
cpu->id_mmfr1 = 0x40000000;
@@ -1726,8 +1739,6 @@ static void cortex_a15_initfn(Object *obj)
cpu->id_pfr0 = 0x00001131;
cpu->id_pfr1 = 0x00011011;
cpu->id_dfr0 = 0x02010555;
- cpu->pmceid0 = 0x0000000;
- cpu->pmceid1 = 0x00000000;
cpu->id_afr0 = 0x00000000;
cpu->id_mmfr0 = 0x10201105;
cpu->id_mmfr1 = 0x20000000;
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 6f606eb97b..ff81db420d 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -201,11 +201,16 @@ typedef struct ARMVectorReg {
uint64_t d[2 * ARM_MAX_VQ] QEMU_ALIGNED(16);
} ARMVectorReg;
-/* In AArch32 mode, predicate registers do not exist at all. */
#ifdef TARGET_AARCH64
+/* In AArch32 mode, predicate registers do not exist at all. */
typedef struct ARMPredicateReg {
uint64_t p[2 * ARM_MAX_VQ / 8] QEMU_ALIGNED(16);
} ARMPredicateReg;
+
+/* In AArch32 mode, PAC keys do not exist at all. */
+typedef struct ARMPACKey {
+ uint64_t lo, hi;
+} ARMPACKey;
#endif
@@ -468,10 +473,23 @@ typedef struct CPUARMState {
uint64_t oslsr_el1; /* OS Lock Status */
uint64_t mdcr_el2;
uint64_t mdcr_el3;
- /* If the counter is enabled, this stores the last time the counter
- * was reset. Otherwise it stores the counter value
+ /* Stores the architectural value of the counter *the last time it was
+ * updated* by pmccntr_op_start. Accesses should always be surrounded
+ * by pmccntr_op_start/pmccntr_op_finish to guarantee the latest
+ * architecturally-correct value is being read/set.
*/
uint64_t c15_ccnt;
+ /* Stores the delta between the architectural value and the underlying
+ * cycle count during normal operation. It is used to update c15_ccnt
+ * to be the correct architectural value before accesses. During
+ * accesses, c15_ccnt_delta contains the underlying count being used
+ * for the access, after which it reverts to the delta value in
+ * pmccntr_op_finish.
+ */
+ uint64_t c15_ccnt_delta;
+ uint64_t c14_pmevcntr[31];
+ uint64_t c14_pmevcntr_delta[31];
+ uint64_t c14_pmevtyper[31];
uint64_t pmccfiltr_el0; /* Performance Monitor Filter Register */
uint64_t vpidr_el2; /* Virtualization Processor ID Register */
uint64_t vmpidr_el2; /* Virtualization Multiprocessor ID Register */
@@ -605,6 +623,14 @@ typedef struct CPUARMState {
uint32_t cregs[16];
} iwmmxt;
+#ifdef TARGET_AARCH64
+ ARMPACKey apia_key;
+ ARMPACKey apib_key;
+ ARMPACKey apda_key;
+ ARMPACKey apdb_key;
+ ARMPACKey apga_key;
+#endif
+
#if defined(CONFIG_USER_ONLY)
/* For usermode syscall translation. */
int eabi;
@@ -829,8 +855,8 @@ struct ARMCPU {
uint32_t id_pfr0;
uint32_t id_pfr1;
uint32_t id_dfr0;
- uint32_t pmceid0;
- uint32_t pmceid1;
+ uint64_t pmceid0;
+ uint64_t pmceid1;
uint32_t id_afr0;
uint32_t id_mmfr0;
uint32_t id_mmfr1;
@@ -958,15 +984,42 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo,
void *puc);
/**
- * pmccntr_sync
+ * pmccntr_op_start/finish
+ * @env: CPUARMState
+ *
+ * Convert the counter in the PMCCNTR between its delta form (the typical mode
+ * when it's enabled) and the guest-visible value. These two calls must always
+ * surround any action which might affect the counter.
+ */
+void pmccntr_op_start(CPUARMState *env);
+void pmccntr_op_finish(CPUARMState *env);
+
+/**
+ * pmu_op_start/finish
+ * @env: CPUARMState
+ *
+ * Convert all PMU counters between their delta form (the typical mode when
+ * they are enabled) and the guest-visible values. These two calls must
+ * surround any action which might affect the counters.
+ */
+void pmu_op_start(CPUARMState *env);
+void pmu_op_finish(CPUARMState *env);
+
+/**
+ * Functions to register as EL change hooks for PMU mode filtering
+ */
+void pmu_pre_el_change(ARMCPU *cpu, void *ignored);
+void pmu_post_el_change(ARMCPU *cpu, void *ignored);
+
+/*
+ * get_pmceid
* @env: CPUARMState
+ * @which: which PMCEID register to return (0 or 1)
*
- * Synchronises the counter in the PMCCNTR. This must always be called twice,
- * once before any action that might affect the timer and again afterwards.
- * The function is used to swap the state of the register if required.
- * This only happens when not in user mode (!CONFIG_USER_ONLY)
+ * Return the PMCEID[01]_EL0 register values corresponding to the counters
+ * which are supported given the current configuration
*/
-void pmccntr_sync(CPUARMState *env);
+uint64_t get_pmceid(CPUARMState *env, unsigned which);
/* SCTLR bit meanings. Several bits have been reused in newer
* versions of the architecture; in that case we define constants
@@ -978,12 +1031,15 @@ void pmccntr_sync(CPUARMState *env);
#define SCTLR_A (1U << 1)
#define SCTLR_C (1U << 2)
#define SCTLR_W (1U << 3) /* up to v6; RAO in v7 */
-#define SCTLR_SA (1U << 3)
+#define SCTLR_nTLSMD_32 (1U << 3) /* v8.2-LSMAOC, AArch32 only */
+#define SCTLR_SA (1U << 3) /* AArch64 only */
#define SCTLR_P (1U << 4) /* up to v5; RAO in v6 and v7 */
+#define SCTLR_LSMAOE_32 (1U << 4) /* v8.2-LSMAOC, AArch32 only */
#define SCTLR_SA0 (1U << 4) /* v8 onward, AArch64 only */
#define SCTLR_D (1U << 5) /* up to v5; RAO in v6 */
#define SCTLR_CP15BEN (1U << 5) /* v7 onward */
#define SCTLR_L (1U << 6) /* up to v5; RAO in v6 and v7; RAZ in v8 */
+#define SCTLR_nAA (1U << 6) /* when v8.4-LSE is implemented */
#define SCTLR_B (1U << 7) /* up to v6; RAZ in v7 */
#define SCTLR_ITD (1U << 7) /* v8 onward */
#define SCTLR_S (1U << 8) /* up to v6; RAZ in v7 */
@@ -991,35 +1047,53 @@ void pmccntr_sync(CPUARMState *env);
#define SCTLR_R (1U << 9) /* up to v6; RAZ in v7 */
#define SCTLR_UMA (1U << 9) /* v8 onward, AArch64 only */
#define SCTLR_F (1U << 10) /* up to v6 */
-#define SCTLR_SW (1U << 10) /* v7 onward */
-#define SCTLR_Z (1U << 11)
+#define SCTLR_SW (1U << 10) /* v7, RES0 in v8 */
+#define SCTLR_Z (1U << 11) /* in v7, RES1 in v8 */
+#define SCTLR_EOS (1U << 11) /* v8.5-ExS */
#define SCTLR_I (1U << 12)
-#define SCTLR_V (1U << 13)
+#define SCTLR_V (1U << 13) /* AArch32 only */
+#define SCTLR_EnDB (1U << 13) /* v8.3, AArch64 only */
#define SCTLR_RR (1U << 14) /* up to v7 */
#define SCTLR_DZE (1U << 14) /* v8 onward, AArch64 only */
#define SCTLR_L4 (1U << 15) /* up to v6; RAZ in v7 */
#define SCTLR_UCT (1U << 15) /* v8 onward, AArch64 only */
#define SCTLR_DT (1U << 16) /* up to ??, RAO in v6 and v7 */
#define SCTLR_nTWI (1U << 16) /* v8 onward */
-#define SCTLR_HA (1U << 17)
+#define SCTLR_HA (1U << 17) /* up to v7, RES0 in v8 */
#define SCTLR_BR (1U << 17) /* PMSA only */
#define SCTLR_IT (1U << 18) /* up to ??, RAO in v6 and v7 */
#define SCTLR_nTWE (1U << 18) /* v8 onward */
#define SCTLR_WXN (1U << 19)
#define SCTLR_ST (1U << 20) /* up to ??, RAZ in v6 */
-#define SCTLR_UWXN (1U << 20) /* v7 onward */
-#define SCTLR_FI (1U << 21)
-#define SCTLR_U (1U << 22)
+#define SCTLR_UWXN (1U << 20) /* v7 onward, AArch32 only */
+#define SCTLR_FI (1U << 21) /* up to v7, v8 RES0 */
+#define SCTLR_IESB (1U << 21) /* v8.2-IESB, AArch64 only */
+#define SCTLR_U (1U << 22) /* up to v6, RAO in v7 */
+#define SCTLR_EIS (1U << 22) /* v8.5-ExS */
#define SCTLR_XP (1U << 23) /* up to v6; v7 onward RAO */
+#define SCTLR_SPAN (1U << 23) /* v8.1-PAN */
#define SCTLR_VE (1U << 24) /* up to v7 */
#define SCTLR_E0E (1U << 24) /* v8 onward, AArch64 only */
#define SCTLR_EE (1U << 25)
#define SCTLR_L2 (1U << 26) /* up to v6, RAZ in v7 */
#define SCTLR_UCI (1U << 26) /* v8 onward, AArch64 only */
-#define SCTLR_NMFI (1U << 27)
-#define SCTLR_TRE (1U << 28)
-#define SCTLR_AFE (1U << 29)
-#define SCTLR_TE (1U << 30)
+#define SCTLR_NMFI (1U << 27) /* up to v7, RAZ in v7VE and v8 */
+#define SCTLR_EnDA (1U << 27) /* v8.3, AArch64 only */
+#define SCTLR_TRE (1U << 28) /* AArch32 only */
+#define SCTLR_nTLSMD_64 (1U << 28) /* v8.2-LSMAOC, AArch64 only */
+#define SCTLR_AFE (1U << 29) /* AArch32 only */
+#define SCTLR_LSMAOE_64 (1U << 29) /* v8.2-LSMAOC, AArch64 only */
+#define SCTLR_TE (1U << 30) /* AArch32 only */
+#define SCTLR_EnIB (1U << 30) /* v8.3, AArch64 only */
+#define SCTLR_EnIA (1U << 31) /* v8.3, AArch64 only */
+#define SCTLR_BT0 (1ULL << 35) /* v8.5-BTI */
+#define SCTLR_BT1 (1ULL << 36) /* v8.5-BTI */
+#define SCTLR_ITFSB (1ULL << 37) /* v8.5-MemTag */
+#define SCTLR_TCF0 (3ULL << 38) /* v8.5-MemTag */
+#define SCTLR_TCF (3ULL << 40) /* v8.5-MemTag */
+#define SCTLR_ATA0 (1ULL << 42) /* v8.5-MemTag */
+#define SCTLR_ATA (1ULL << 43) /* v8.5-MemTag */
+#define SCTLR_DSSBS (1ULL << 44) /* v8.5 */
#define CPTR_TCPAC (1U << 31)
#define CPTR_TTA (1U << 20)
@@ -1029,7 +1103,8 @@ void pmccntr_sync(CPUARMState *env);
#define MDCR_EPMAD (1U << 21)
#define MDCR_EDAD (1U << 20)
-#define MDCR_SPME (1U << 17)
+#define MDCR_SPME (1U << 17) /* MDCR_EL3 */
+#define MDCR_HPMD (1U << 17) /* MDCR_EL2 */
#define MDCR_SDD (1U << 16)
#define MDCR_SPD (3U << 14)
#define MDCR_TDRA (1U << 11)
@@ -1039,6 +1114,7 @@ void pmccntr_sync(CPUARMState *env);
#define MDCR_HPME (1U << 7)
#define MDCR_TPM (1U << 6)
#define MDCR_TPMCR (1U << 5)
+#define MDCR_HPMN (0x1fU)
/* Not all of the MDCR_EL3 bits are present in the 32-bit SDCR */
#define SDCR_VALID_MASK (MDCR_EPMAD | MDCR_EDAD | MDCR_SPME | MDCR_SPD)
@@ -1618,6 +1694,15 @@ FIELD(ID_AA64MMFR1, PAN, 20, 4)
FIELD(ID_AA64MMFR1, SPECSEI, 24, 4)
FIELD(ID_AA64MMFR1, XNX, 28, 4)
+FIELD(ID_DFR0, COPDBG, 0, 4)
+FIELD(ID_DFR0, COPSDBG, 4, 4)
+FIELD(ID_DFR0, MMAPDBG, 8, 4)
+FIELD(ID_DFR0, COPTRC, 12, 4)
+FIELD(ID_DFR0, MMAPTRC, 16, 4)
+FIELD(ID_DFR0, MPROFDBG, 20, 4)
+FIELD(ID_DFR0, PERFMON, 24, 4)
+FIELD(ID_DFR0, TRACEFILT, 28, 4)
+
QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK);
/* If adding a feature bit which corresponds to a Linux ELF
@@ -2707,54 +2792,23 @@ static inline int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx)
}
/* Return the MMU index for a v7M CPU in the specified security and
- * privilege state
+ * privilege state.
*/
-static inline ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env,
- bool secstate,
- bool priv)
-{
- ARMMMUIdx mmu_idx = ARM_MMU_IDX_M;
-
- if (priv) {
- mmu_idx |= ARM_MMU_IDX_M_PRIV;
- }
-
- if (armv7m_nvic_neg_prio_requested(env->nvic, secstate)) {
- mmu_idx |= ARM_MMU_IDX_M_NEGPRI;
- }
-
- if (secstate) {
- mmu_idx |= ARM_MMU_IDX_M_S;
- }
-
- return mmu_idx;
-}
+ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env,
+ bool secstate, bool priv);
/* Return the MMU index for a v7M CPU in the specified security state */
-static inline ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env,
- bool secstate)
-{
- bool priv = arm_current_el(env) != 0;
-
- return arm_v7m_mmu_idx_for_secstate_and_priv(env, secstate, priv);
-}
-
-/* Determine the current mmu_idx to use for normal loads/stores */
-static inline int cpu_mmu_index(CPUARMState *env, bool ifetch)
-{
- int el = arm_current_el(env);
+ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate);
- if (arm_feature(env, ARM_FEATURE_M)) {
- ARMMMUIdx mmu_idx = arm_v7m_mmu_idx_for_secstate(env, env->v7m.secure);
-
- return arm_to_core_mmu_idx(mmu_idx);
- }
-
- if (el < 2 && arm_is_secure_below_el3(env)) {
- return arm_to_core_mmu_idx(ARMMMUIdx_S1SE0 + el);
- }
- return el;
-}
+/**
+ * cpu_mmu_index:
+ * @env: The cpu environment
+ * @ifetch: True for code access, false for data access.
+ *
+ * Return the core mmu index for the current translation regime.
+ * This function is used by generic TCG code paths.
+ */
+int cpu_mmu_index(CPUARMState *env, bool ifetch);
/* Indexes used when registering address spaces with cpu_address_space_init */
typedef enum ARMASIdx {
@@ -2976,10 +3030,10 @@ FIELD(TBFLAG_A32, HANDLER, 21, 1)
FIELD(TBFLAG_A32, STACKCHECK, 22, 1)
/* Bit usage when in AArch64 state */
-FIELD(TBFLAG_A64, TBI0, 0, 1)
-FIELD(TBFLAG_A64, TBI1, 1, 1)
+FIELD(TBFLAG_A64, TBII, 0, 2)
FIELD(TBFLAG_A64, SVEEXC_EL, 2, 2)
FIELD(TBFLAG_A64, ZCR_LEN, 4, 4)
+FIELD(TBFLAG_A64, PAUTH_ACTIVE, 8, 1)
static inline bool bswap_code(bool sctlr_b)
{
@@ -3012,41 +3066,6 @@ static inline bool arm_cpu_bswap_data(CPUARMState *env)
}
#endif
-#ifndef CONFIG_USER_ONLY
-/**
- * arm_regime_tbi0:
- * @env: CPUARMState
- * @mmu_idx: MMU index indicating required translation regime
- *
- * Extracts the TBI0 value from the appropriate TCR for the current EL
- *
- * Returns: the TBI0 value.
- */
-uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx);
-
-/**
- * arm_regime_tbi1:
- * @env: CPUARMState
- * @mmu_idx: MMU index indicating required translation regime
- *
- * Extracts the TBI1 value from the appropriate TCR for the current EL
- *
- * Returns: the TBI1 value.
- */
-uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx);
-#else
-/* We can't handle tagged addresses properly in user-only mode */
-static inline uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx)
-{
- return 0;
-}
-
-static inline uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx)
-{
- return 0;
-}
-#endif
-
void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags);
@@ -3264,6 +3283,21 @@ static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id)
return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0;
}
+static inline bool isar_feature_aa64_pauth(const ARMISARegisters *id)
+{
+ /*
+ * Note that while QEMU will only implement the architected algorithm
+ * QARMA, and thus APA+GPA, the host cpu for kvm may use implementation
+ * defined algorithms, and thus API+GPI, and this predicate controls
+ * migration of the 128-bit keys.
+ */
+ return (id->id_aa64isar1 &
+ (FIELD_DP64(0, ID_AA64ISAR1, APA, 0xf) |
+ FIELD_DP64(0, ID_AA64ISAR1, API, 0xf) |
+ FIELD_DP64(0, ID_AA64ISAR1, GPA, 0xf) |
+ FIELD_DP64(0, ID_AA64ISAR1, GPI, 0xf))) != 0;
+}
+
static inline bool isar_feature_aa64_fp16(const ARMISARegisters *id)
{
/* We always set the AdvSIMD and FP fields identically wrt FP16. */
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 4b544a1c58..e9bc461c36 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -138,8 +138,6 @@ static void aarch64_a57_initfn(Object *obj)
cpu->isar.id_isar6 = 0;
cpu->isar.id_aa64pfr0 = 0x00002222;
cpu->id_aa64dfr0 = 0x10305106;
- cpu->pmceid0 = 0x00000000;
- cpu->pmceid1 = 0x00000000;
cpu->isar.id_aa64isar0 = 0x00011120;
cpu->isar.id_aa64mmfr0 = 0x00001124;
cpu->dbgdidr = 0x3516d000;
@@ -246,8 +244,6 @@ static void aarch64_a72_initfn(Object *obj)
cpu->isar.id_isar5 = 0x00011121;
cpu->isar.id_aa64pfr0 = 0x00002222;
cpu->id_aa64dfr0 = 0x10305106;
- cpu->pmceid0 = 0x00000000;
- cpu->pmceid1 = 0x00000000;
cpu->isar.id_aa64isar0 = 0x00011120;
cpu->isar.id_aa64mmfr0 = 0x00001124;
cpu->dbgdidr = 0x3516d000;
@@ -285,6 +281,38 @@ static void cpu_max_set_sve_vq(Object *obj, Visitor *v, const char *name,
error_propagate(errp, err);
}
+#ifdef CONFIG_USER_ONLY
+static void cpu_max_get_packey(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+ const uint64_t *bit = opaque;
+ bool enabled = (cpu->env.cp15.sctlr_el[1] & *bit) != 0;
+
+ visit_type_bool(v, name, &enabled, errp);
+}
+
+static void cpu_max_set_packey(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+ Error *err = NULL;
+ const uint64_t *bit = opaque;
+ bool enabled;
+
+ visit_type_bool(v, name, &enabled, errp);
+
+ if (!err) {
+ if (enabled) {
+ cpu->env.cp15.sctlr_el[1] |= *bit;
+ } else {
+ cpu->env.cp15.sctlr_el[1] &= ~*bit;
+ }
+ }
+ error_propagate(errp, err);
+}
+#endif
+
/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
* otherwise, a CPU with as many features enabled as our emulation supports.
* The version of '-cpu max' for qemu-system-arm is defined in cpu.c;
@@ -316,6 +344,10 @@ static void aarch64_max_initfn(Object *obj)
t = cpu->isar.id_aa64isar1;
t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1);
+ t = FIELD_DP64(t, ID_AA64ISAR1, APA, 1); /* PAuth, architected only */
+ t = FIELD_DP64(t, ID_AA64ISAR1, API, 0);
+ t = FIELD_DP64(t, ID_AA64ISAR1, GPA, 1);
+ t = FIELD_DP64(t, ID_AA64ISAR1, GPI, 0);
cpu->isar.id_aa64isar1 = t;
t = cpu->isar.id_aa64pfr0;
@@ -356,6 +388,34 @@ static void aarch64_max_initfn(Object *obj)
*/
cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
cpu->dcz_blocksize = 7; /* 512 bytes */
+
+ /*
+ * Note that Linux will enable enable all of the keys at once.
+ * But doing it this way will allow experimentation beyond that.
+ */
+ {
+ static const uint64_t apia_bit = SCTLR_EnIA;
+ static const uint64_t apib_bit = SCTLR_EnIB;
+ static const uint64_t apda_bit = SCTLR_EnDA;
+ static const uint64_t apdb_bit = SCTLR_EnDB;
+
+ object_property_add(obj, "apia", "bool", cpu_max_get_packey,
+ cpu_max_set_packey, NULL,
+ (void *)&apia_bit, &error_fatal);
+ object_property_add(obj, "apib", "bool", cpu_max_get_packey,
+ cpu_max_set_packey, NULL,
+ (void *)&apib_bit, &error_fatal);
+ object_property_add(obj, "apda", "bool", cpu_max_get_packey,
+ cpu_max_set_packey, NULL,
+ (void *)&apda_bit, &error_fatal);
+ object_property_add(obj, "apdb", "bool", cpu_max_get_packey,
+ cpu_max_set_packey, NULL,
+ (void *)&apdb_bit, &error_fatal);
+
+ /* Enable all PAC keys by default. */
+ cpu->env.cp15.sctlr_el[1] |= SCTLR_EnIA | SCTLR_EnIB;
+ cpu->env.cp15.sctlr_el[1] |= SCTLR_EnDA | SCTLR_EnDB;
+ }
#endif
cpu->sve_max_vq = ARM_MAX_VQ;
diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c
index 61799d20e1..101fa6d3ea 100644
--- a/target/arm/helper-a64.c
+++ b/target/arm/helper-a64.c
@@ -887,6 +887,161 @@ uint32_t HELPER(advsimd_f16touinth)(uint32_t a, void *fpstp)
return float16_to_uint16(a, fpst);
}
+static int el_from_spsr(uint32_t spsr)
+{
+ /* Return the exception level that this SPSR is requesting a return to,
+ * or -1 if it is invalid (an illegal return)
+ */
+ if (spsr & PSTATE_nRW) {
+ switch (spsr & CPSR_M) {
+ case ARM_CPU_MODE_USR:
+ return 0;
+ case ARM_CPU_MODE_HYP:
+ return 2;
+ case ARM_CPU_MODE_FIQ:
+ case ARM_CPU_MODE_IRQ:
+ case ARM_CPU_MODE_SVC:
+ case ARM_CPU_MODE_ABT:
+ case ARM_CPU_MODE_UND:
+ case ARM_CPU_MODE_SYS:
+ return 1;
+ case ARM_CPU_MODE_MON:
+ /* Returning to Mon from AArch64 is never possible,
+ * so this is an illegal return.
+ */
+ default:
+ return -1;
+ }
+ } else {
+ if (extract32(spsr, 1, 1)) {
+ /* Return with reserved M[1] bit set */
+ return -1;
+ }
+ if (extract32(spsr, 0, 4) == 1) {
+ /* return to EL0 with M[0] bit set */
+ return -1;
+ }
+ return extract32(spsr, 2, 2);
+ }
+}
+
+void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc)
+{
+ int cur_el = arm_current_el(env);
+ unsigned int spsr_idx = aarch64_banked_spsr_index(cur_el);
+ uint32_t spsr = env->banked_spsr[spsr_idx];
+ int new_el;
+ bool return_to_aa64 = (spsr & PSTATE_nRW) == 0;
+
+ aarch64_save_sp(env, cur_el);
+
+ arm_clear_exclusive(env);
+
+ /* We must squash the PSTATE.SS bit to zero unless both of the
+ * following hold:
+ * 1. debug exceptions are currently disabled
+ * 2. singlestep will be active in the EL we return to
+ * We check 1 here and 2 after we've done the pstate/cpsr write() to
+ * transition to the EL we're going to.
+ */
+ if (arm_generate_debug_exceptions(env)) {
+ spsr &= ~PSTATE_SS;
+ }
+
+ new_el = el_from_spsr(spsr);
+ if (new_el == -1) {
+ goto illegal_return;
+ }
+ if (new_el > cur_el
+ || (new_el == 2 && !arm_feature(env, ARM_FEATURE_EL2))) {
+ /* Disallow return to an EL which is unimplemented or higher
+ * than the current one.
+ */
+ goto illegal_return;
+ }
+
+ if (new_el != 0 && arm_el_is_aa64(env, new_el) != return_to_aa64) {
+ /* Return to an EL which is configured for a different register width */
+ goto illegal_return;
+ }
+
+ if (new_el == 2 && arm_is_secure_below_el3(env)) {
+ /* Return to the non-existent secure-EL2 */
+ goto illegal_return;
+ }
+
+ if (new_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) {
+ goto illegal_return;
+ }
+
+ qemu_mutex_lock_iothread();
+ arm_call_pre_el_change_hook(arm_env_get_cpu(env));
+ qemu_mutex_unlock_iothread();
+
+ if (!return_to_aa64) {
+ env->aarch64 = 0;
+ /* We do a raw CPSR write because aarch64_sync_64_to_32()
+ * will sort the register banks out for us, and we've already
+ * caught all the bad-mode cases in el_from_spsr().
+ */
+ cpsr_write(env, spsr, ~0, CPSRWriteRaw);
+ if (!arm_singlestep_active(env)) {
+ env->uncached_cpsr &= ~PSTATE_SS;
+ }
+ aarch64_sync_64_to_32(env);
+
+ if (spsr & CPSR_T) {
+ env->regs[15] = new_pc & ~0x1;
+ } else {
+ env->regs[15] = new_pc & ~0x3;
+ }
+ qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to "
+ "AArch32 EL%d PC 0x%" PRIx32 "\n",
+ cur_el, new_el, env->regs[15]);
+ } else {
+ env->aarch64 = 1;
+ pstate_write(env, spsr);
+ if (!arm_singlestep_active(env)) {
+ env->pstate &= ~PSTATE_SS;
+ }
+ aarch64_restore_sp(env, new_el);
+ env->pc = new_pc;
+ qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to "
+ "AArch64 EL%d PC 0x%" PRIx64 "\n",
+ cur_el, new_el, env->pc);
+ }
+ /*
+ * Note that cur_el can never be 0. If new_el is 0, then
+ * el0_a64 is return_to_aa64, else el0_a64 is ignored.
+ */
+ aarch64_sve_change_el(env, cur_el, new_el, return_to_aa64);
+
+ qemu_mutex_lock_iothread();
+ arm_call_el_change_hook(arm_env_get_cpu(env));
+ qemu_mutex_unlock_iothread();
+
+ return;
+
+illegal_return:
+ /* Illegal return events of various kinds have architecturally
+ * mandated behaviour:
+ * restore NZCV and DAIF from SPSR_ELx
+ * set PSTATE.IL
+ * restore PC from ELR_ELx
+ * no change to exception level, execution state or stack pointer
+ */
+ env->pstate |= PSTATE_IL;
+ env->pc = new_pc;
+ spsr &= PSTATE_NZCV | PSTATE_DAIF;
+ spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF);
+ pstate_write(env, spsr);
+ if (!arm_singlestep_active(env)) {
+ env->pstate &= ~PSTATE_SS;
+ }
+ qemu_log_mask(LOG_GUEST_ERROR, "Illegal exception return at EL%d: "
+ "resuming execution at 0x%" PRIx64 "\n", cur_el, env->pc);
+}
+
/*
* Square Root and Reciprocal square root
*/
diff --git a/target/arm/helper-a64.h b/target/arm/helper-a64.h
index 9d3a907049..aff8d6c9f3 100644
--- a/target/arm/helper-a64.h
+++ b/target/arm/helper-a64.h
@@ -85,3 +85,17 @@ DEF_HELPER_2(advsimd_rinth, f16, f16, ptr)
DEF_HELPER_2(advsimd_f16tosinth, i32, f16, ptr)
DEF_HELPER_2(advsimd_f16touinth, i32, f16, ptr)
DEF_HELPER_2(sqrt_f16, f16, f16, ptr)
+
+DEF_HELPER_2(exception_return, void, env, i64)
+
+DEF_HELPER_FLAGS_3(pacia, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(pacib, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(pacda, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(pacdb, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(pacga, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(autia, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(autib, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(autda, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(autdb, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_2(xpaci, TCG_CALL_NO_RWG_SE, i64, env, i64)
+DEF_HELPER_FLAGS_2(xpacd, TCG_CALL_NO_RWG_SE, i64, env, i64)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index f00c141ef9..92666e5208 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -15,6 +15,7 @@
#include "arm_ldst.h"
#include <zlib.h> /* For crc32 */
#include "exec/semihost.h"
+#include "sysemu/cpus.h"
#include "sysemu/kvm.h"
#include "fpu/softfloat.h"
#include "qemu/range.h"
@@ -976,10 +977,29 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
/* Definitions for the PMU registers */
#define PMCRN_MASK 0xf800
#define PMCRN_SHIFT 11
+#define PMCRDP 0x10
#define PMCRD 0x8
#define PMCRC 0x4
+#define PMCRP 0x2
#define PMCRE 0x1
+#define PMXEVTYPER_P 0x80000000
+#define PMXEVTYPER_U 0x40000000
+#define PMXEVTYPER_NSK 0x20000000
+#define PMXEVTYPER_NSU 0x10000000
+#define PMXEVTYPER_NSH 0x08000000
+#define PMXEVTYPER_M 0x04000000
+#define PMXEVTYPER_MT 0x02000000
+#define PMXEVTYPER_EVTCOUNT 0x0000ffff
+#define PMXEVTYPER_MASK (PMXEVTYPER_P | PMXEVTYPER_U | PMXEVTYPER_NSK | \
+ PMXEVTYPER_NSU | PMXEVTYPER_NSH | \
+ PMXEVTYPER_M | PMXEVTYPER_MT | \
+ PMXEVTYPER_EVTCOUNT)
+
+#define PMCCFILTR 0xf8000000
+#define PMCCFILTR_M PMXEVTYPER_M
+#define PMCCFILTR_EL0 (PMCCFILTR | PMCCFILTR_M)
+
static inline uint32_t pmu_num_counters(CPUARMState *env)
{
return (env->cp15.c9_pmcr & PMCRN_MASK) >> PMCRN_SHIFT;
@@ -991,6 +1011,128 @@ static inline uint64_t pmu_counter_mask(CPUARMState *env)
return (1 << 31) | ((1 << pmu_num_counters(env)) - 1);
}
+typedef struct pm_event {
+ uint16_t number; /* PMEVTYPER.evtCount is 16 bits wide */
+ /* If the event is supported on this CPU (used to generate PMCEID[01]) */
+ bool (*supported)(CPUARMState *);
+ /*
+ * Retrieve the current count of the underlying event. The programmed
+ * counters hold a difference from the return value from this function
+ */
+ uint64_t (*get_count)(CPUARMState *);
+} pm_event;
+
+static bool event_always_supported(CPUARMState *env)
+{
+ return true;
+}
+
+static uint64_t swinc_get_count(CPUARMState *env)
+{
+ /*
+ * SW_INCR events are written directly to the pmevcntr's by writes to
+ * PMSWINC, so there is no underlying count maintained by the PMU itself
+ */
+ return 0;
+}
+
+/*
+ * Return the underlying cycle count for the PMU cycle counters. If we're in
+ * usermode, simply return 0.
+ */
+static uint64_t cycles_get_count(CPUARMState *env)
+{
+#ifndef CONFIG_USER_ONLY
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ ARM_CPU_FREQ, NANOSECONDS_PER_SECOND);
+#else
+ return cpu_get_host_ticks();
+#endif
+}
+
+#ifndef CONFIG_USER_ONLY
+static bool instructions_supported(CPUARMState *env)
+{
+ return use_icount == 1 /* Precise instruction counting */;
+}
+
+static uint64_t instructions_get_count(CPUARMState *env)
+{
+ return (uint64_t)cpu_get_icount_raw();
+}
+#endif
+
+static const pm_event pm_events[] = {
+ { .number = 0x000, /* SW_INCR */
+ .supported = event_always_supported,
+ .get_count = swinc_get_count,
+ },
+#ifndef CONFIG_USER_ONLY
+ { .number = 0x008, /* INST_RETIRED, Instruction architecturally executed */
+ .supported = instructions_supported,
+ .get_count = instructions_get_count,
+ },
+ { .number = 0x011, /* CPU_CYCLES, Cycle */
+ .supported = event_always_supported,
+ .get_count = cycles_get_count,
+ }
+#endif
+};
+
+/*
+ * Note: Before increasing MAX_EVENT_ID beyond 0x3f into the 0x40xx range of
+ * events (i.e. the statistical profiling extension), this implementation
+ * should first be updated to something sparse instead of the current
+ * supported_event_map[] array.
+ */
+#define MAX_EVENT_ID 0x11
+#define UNSUPPORTED_EVENT UINT16_MAX
+static uint16_t supported_event_map[MAX_EVENT_ID + 1];
+
+/*
+ * Called upon initialization to build PMCEID0_EL0 or PMCEID1_EL0 (indicated by
+ * 'which'). We also use it to build a map of ARM event numbers to indices in
+ * our pm_events array.
+ *
+ * Note: Events in the 0x40XX range are not currently supported.
+ */
+uint64_t get_pmceid(CPUARMState *env, unsigned which)
+{
+ uint64_t pmceid = 0;
+ unsigned int i;
+
+ assert(which <= 1);
+
+ for (i = 0; i < ARRAY_SIZE(supported_event_map); i++) {
+ supported_event_map[i] = UNSUPPORTED_EVENT;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pm_events); i++) {
+ const pm_event *cnt = &pm_events[i];
+ assert(cnt->number <= MAX_EVENT_ID);
+ /* We do not currently support events in the 0x40xx range */
+ assert(cnt->number <= 0x3f);
+
+ if ((cnt->number & 0x20) == (which << 6) &&
+ cnt->supported(env)) {
+ pmceid |= (1 << (cnt->number & 0x1f));
+ supported_event_map[cnt->number] = i;
+ }
+ }
+ return pmceid;
+}
+
+/*
+ * Check at runtime whether a PMU event is supported for the current machine
+ */
+static bool event_supported(uint16_t number)
+{
+ if (number > MAX_EVENT_ID) {
+ return false;
+ }
+ return supported_event_map[number] != UNSUPPORTED_EVENT;
+}
+
static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
@@ -1044,8 +1186,6 @@ static CPAccessResult pmreg_access_swinc(CPUARMState *env,
return pmreg_access(env, ri, isread);
}
-#ifndef CONFIG_USER_ONLY
-
static CPAccessResult pmreg_access_selr(CPUARMState *env,
const ARMCPRegInfo *ri,
bool isread)
@@ -1075,68 +1215,222 @@ static CPAccessResult pmreg_access_ccntr(CPUARMState *env,
return pmreg_access(env, ri, isread);
}
-static inline bool arm_ccnt_enabled(CPUARMState *env)
+/* Returns true if the counter (pass 31 for PMCCNTR) should count events using
+ * the current EL, security state, and register configuration.
+ */
+static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter)
{
- /* This does not support checking PMCCFILTR_EL0 register */
+ uint64_t filter;
+ bool e, p, u, nsk, nsu, nsh, m;
+ bool enabled, prohibited, filtered;
+ bool secure = arm_is_secure(env);
+ int el = arm_current_el(env);
+ uint8_t hpmn = env->cp15.mdcr_el2 & MDCR_HPMN;
- if (!(env->cp15.c9_pmcr & PMCRE) || !(env->cp15.c9_pmcnten & (1 << 31))) {
- return false;
+ if (!arm_feature(env, ARM_FEATURE_EL2) ||
+ (counter < hpmn || counter == 31)) {
+ e = env->cp15.c9_pmcr & PMCRE;
+ } else {
+ e = env->cp15.mdcr_el2 & MDCR_HPME;
}
+ enabled = e && (env->cp15.c9_pmcnten & (1 << counter));
- return true;
+ if (!secure) {
+ if (el == 2 && (counter < hpmn || counter == 31)) {
+ prohibited = env->cp15.mdcr_el2 & MDCR_HPMD;
+ } else {
+ prohibited = false;
+ }
+ } else {
+ prohibited = arm_feature(env, ARM_FEATURE_EL3) &&
+ (env->cp15.mdcr_el3 & MDCR_SPME);
+ }
+
+ if (prohibited && counter == 31) {
+ prohibited = env->cp15.c9_pmcr & PMCRDP;
+ }
+
+ if (counter == 31) {
+ filter = env->cp15.pmccfiltr_el0;
+ } else {
+ filter = env->cp15.c14_pmevtyper[counter];
+ }
+
+ p = filter & PMXEVTYPER_P;
+ u = filter & PMXEVTYPER_U;
+ nsk = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSK);
+ nsu = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSU);
+ nsh = arm_feature(env, ARM_FEATURE_EL2) && (filter & PMXEVTYPER_NSH);
+ m = arm_el_is_aa64(env, 1) &&
+ arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_M);
+
+ if (el == 0) {
+ filtered = secure ? u : u != nsu;
+ } else if (el == 1) {
+ filtered = secure ? p : p != nsk;
+ } else if (el == 2) {
+ filtered = !nsh;
+ } else { /* EL3 */
+ filtered = m != p;
+ }
+
+ if (counter != 31) {
+ /*
+ * If not checking PMCCNTR, ensure the counter is setup to an event we
+ * support
+ */
+ uint16_t event = filter & PMXEVTYPER_EVTCOUNT;
+ if (!event_supported(event)) {
+ return false;
+ }
+ }
+
+ return enabled && !prohibited && !filtered;
+}
+
+/*
+ * Ensure c15_ccnt is the guest-visible count so that operations such as
+ * enabling/disabling the counter or filtering, modifying the count itself,
+ * etc. can be done logically. This is essentially a no-op if the counter is
+ * not enabled at the time of the call.
+ */
+void pmccntr_op_start(CPUARMState *env)
+{
+ uint64_t cycles = cycles_get_count(env);
+
+ if (pmu_counter_enabled(env, 31)) {
+ uint64_t eff_cycles = cycles;
+ if (env->cp15.c9_pmcr & PMCRD) {
+ /* Increment once every 64 processor clock cycles */
+ eff_cycles /= 64;
+ }
+
+ env->cp15.c15_ccnt = eff_cycles - env->cp15.c15_ccnt_delta;
+ }
+ env->cp15.c15_ccnt_delta = cycles;
}
-void pmccntr_sync(CPUARMState *env)
+/*
+ * If PMCCNTR is enabled, recalculate the delta between the clock and the
+ * guest-visible count. A call to pmccntr_op_finish should follow every call to
+ * pmccntr_op_start.
+ */
+void pmccntr_op_finish(CPUARMState *env)
{
- uint64_t temp_ticks;
+ if (pmu_counter_enabled(env, 31)) {
+ uint64_t prev_cycles = env->cp15.c15_ccnt_delta;
+
+ if (env->cp15.c9_pmcr & PMCRD) {
+ /* Increment once every 64 processor clock cycles */
+ prev_cycles /= 64;
+ }
+
+ env->cp15.c15_ccnt_delta = prev_cycles - env->cp15.c15_ccnt;
+ }
+}
+
+static void pmevcntr_op_start(CPUARMState *env, uint8_t counter)
+{
+
+ uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT;
+ uint64_t count = 0;
+ if (event_supported(event)) {
+ uint16_t event_idx = supported_event_map[event];
+ count = pm_events[event_idx].get_count(env);
+ }
+
+ if (pmu_counter_enabled(env, counter)) {
+ env->cp15.c14_pmevcntr[counter] =
+ count - env->cp15.c14_pmevcntr_delta[counter];
+ }
+ env->cp15.c14_pmevcntr_delta[counter] = count;
+}
- temp_ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- ARM_CPU_FREQ, NANOSECONDS_PER_SECOND);
+static void pmevcntr_op_finish(CPUARMState *env, uint8_t counter)
+{
+ if (pmu_counter_enabled(env, counter)) {
+ env->cp15.c14_pmevcntr_delta[counter] -=
+ env->cp15.c14_pmevcntr[counter];
+ }
+}
- if (env->cp15.c9_pmcr & PMCRD) {
- /* Increment once every 64 processor clock cycles */
- temp_ticks /= 64;
+void pmu_op_start(CPUARMState *env)
+{
+ unsigned int i;
+ pmccntr_op_start(env);
+ for (i = 0; i < pmu_num_counters(env); i++) {
+ pmevcntr_op_start(env, i);
}
+}
- if (arm_ccnt_enabled(env)) {
- env->cp15.c15_ccnt = temp_ticks - env->cp15.c15_ccnt;
+void pmu_op_finish(CPUARMState *env)
+{
+ unsigned int i;
+ pmccntr_op_finish(env);
+ for (i = 0; i < pmu_num_counters(env); i++) {
+ pmevcntr_op_finish(env, i);
}
}
+void pmu_pre_el_change(ARMCPU *cpu, void *ignored)
+{
+ pmu_op_start(&cpu->env);
+}
+
+void pmu_post_el_change(ARMCPU *cpu, void *ignored)
+{
+ pmu_op_finish(&cpu->env);
+}
+
static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- pmccntr_sync(env);
+ pmu_op_start(env);
if (value & PMCRC) {
/* The counter has been reset */
env->cp15.c15_ccnt = 0;
}
+ if (value & PMCRP) {
+ unsigned int i;
+ for (i = 0; i < pmu_num_counters(env); i++) {
+ env->cp15.c14_pmevcntr[i] = 0;
+ }
+ }
+
/* only the DP, X, D and E bits are writable */
env->cp15.c9_pmcr &= ~0x39;
env->cp15.c9_pmcr |= (value & 0x39);
- pmccntr_sync(env);
+ pmu_op_finish(env);
}
-static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
+static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
{
- uint64_t total_ticks;
-
- if (!arm_ccnt_enabled(env)) {
- /* Counter is disabled, do not change value */
- return env->cp15.c15_ccnt;
+ unsigned int i;
+ for (i = 0; i < pmu_num_counters(env); i++) {
+ /* Increment a counter's count iff: */
+ if ((value & (1 << i)) && /* counter's bit is set */
+ /* counter is enabled and not filtered */
+ pmu_counter_enabled(env, i) &&
+ /* counter is SW_INCR */
+ (env->cp15.c14_pmevtyper[i] & PMXEVTYPER_EVTCOUNT) == 0x0) {
+ pmevcntr_op_start(env, i);
+ env->cp15.c14_pmevcntr[i]++;
+ pmevcntr_op_finish(env, i);
+ }
}
+}
- total_ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- ARM_CPU_FREQ, NANOSECONDS_PER_SECOND);
-
- if (env->cp15.c9_pmcr & PMCRD) {
- /* Increment once every 64 processor clock cycles */
- total_ticks /= 64;
- }
- return total_ticks - env->cp15.c15_ccnt;
+static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ uint64_t ret;
+ pmccntr_op_start(env);
+ ret = env->cp15.c15_ccnt;
+ pmccntr_op_finish(env);
+ return ret;
}
static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -1153,22 +1447,9 @@ static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri,
static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- uint64_t total_ticks;
-
- if (!arm_ccnt_enabled(env)) {
- /* Counter is disabled, set the absolute value */
- env->cp15.c15_ccnt = value;
- return;
- }
-
- total_ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- ARM_CPU_FREQ, NANOSECONDS_PER_SECOND);
-
- if (env->cp15.c9_pmcr & PMCRD) {
- /* Increment once every 64 processor clock cycles */
- total_ticks /= 64;
- }
- env->cp15.c15_ccnt = total_ticks - value;
+ pmccntr_op_start(env);
+ env->cp15.c15_ccnt = value;
+ pmccntr_op_finish(env);
}
static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -1179,20 +1460,28 @@ static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri,
pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value));
}
-#else /* CONFIG_USER_ONLY */
-
-void pmccntr_sync(CPUARMState *env)
+static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
{
+ pmccntr_op_start(env);
+ env->cp15.pmccfiltr_el0 = value & PMCCFILTR_EL0;
+ pmccntr_op_finish(env);
}
-#endif
-
-static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+static void pmccfiltr_write_a32(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- pmccntr_sync(env);
- env->cp15.pmccfiltr_el0 = value & 0xfc000000;
- pmccntr_sync(env);
+ pmccntr_op_start(env);
+ /* M is not accessible from AArch32 */
+ env->cp15.pmccfiltr_el0 = (env->cp15.pmccfiltr_el0 & PMCCFILTR_M) |
+ (value & PMCCFILTR);
+ pmccntr_op_finish(env);
+}
+
+static uint64_t pmccfiltr_read_a32(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ /* M is not visible in AArch32 */
+ return env->cp15.pmccfiltr_el0 & PMCCFILTR;
}
static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -1216,30 +1505,181 @@ static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
env->cp15.c9_pmovsr &= ~value;
}
-static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
- uint64_t value)
+static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
{
+ value &= pmu_counter_mask(env);
+ env->cp15.c9_pmovsr |= value;
+}
+
+static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value, const uint8_t counter)
+{
+ if (counter == 31) {
+ pmccfiltr_write(env, ri, value);
+ } else if (counter < pmu_num_counters(env)) {
+ pmevcntr_op_start(env, counter);
+
+ /*
+ * If this counter's event type is changing, store the current
+ * underlying count for the new type in c14_pmevcntr_delta[counter] so
+ * pmevcntr_op_finish has the correct baseline when it converts back to
+ * a delta.
+ */
+ uint16_t old_event = env->cp15.c14_pmevtyper[counter] &
+ PMXEVTYPER_EVTCOUNT;
+ uint16_t new_event = value & PMXEVTYPER_EVTCOUNT;
+ if (old_event != new_event) {
+ uint64_t count = 0;
+ if (event_supported(new_event)) {
+ uint16_t event_idx = supported_event_map[new_event];
+ count = pm_events[event_idx].get_count(env);
+ }
+ env->cp15.c14_pmevcntr_delta[counter] = count;
+ }
+
+ env->cp15.c14_pmevtyper[counter] = value & PMXEVTYPER_MASK;
+ pmevcntr_op_finish(env, counter);
+ }
/* Attempts to access PMXEVTYPER are CONSTRAINED UNPREDICTABLE when
* PMSELR value is equal to or greater than the number of implemented
* counters, but not equal to 0x1f. We opt to behave as a RAZ/WI.
*/
- if (env->cp15.c9_pmselr == 0x1f) {
- pmccfiltr_write(env, ri, value);
+}
+
+static uint64_t pmevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ const uint8_t counter)
+{
+ if (counter == 31) {
+ return env->cp15.pmccfiltr_el0;
+ } else if (counter < pmu_num_counters(env)) {
+ return env->cp15.c14_pmevtyper[counter];
+ } else {
+ /*
+ * We opt to behave as a RAZ/WI when attempts to access PMXEVTYPER
+ * are CONSTRAINED UNPREDICTABLE. See comments in pmevtyper_write().
+ */
+ return 0;
}
}
+static void pmevtyper_writefn(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ pmevtyper_write(env, ri, value, counter);
+}
+
+static void pmevtyper_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ env->cp15.c14_pmevtyper[counter] = value;
+
+ /*
+ * pmevtyper_rawwrite is called between a pair of pmu_op_start and
+ * pmu_op_finish calls when loading saved state for a migration. Because
+ * we're potentially updating the type of event here, the value written to
+ * c14_pmevcntr_delta by the preceeding pmu_op_start call may be for a
+ * different counter type. Therefore, we need to set this value to the
+ * current count for the counter type we're writing so that pmu_op_finish
+ * has the correct count for its calculation.
+ */
+ uint16_t event = value & PMXEVTYPER_EVTCOUNT;
+ if (event_supported(event)) {
+ uint16_t event_idx = supported_event_map[event];
+ env->cp15.c14_pmevcntr_delta[counter] =
+ pm_events[event_idx].get_count(env);
+ }
+}
+
+static uint64_t pmevtyper_readfn(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ return pmevtyper_read(env, ri, counter);
+}
+
+static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ pmevtyper_write(env, ri, value, env->cp15.c9_pmselr & 31);
+}
+
static uint64_t pmxevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri)
{
- /* We opt to behave as a RAZ/WI when attempts to access PMXEVTYPER
- * are CONSTRAINED UNPREDICTABLE. See comments in pmxevtyper_write().
+ return pmevtyper_read(env, ri, env->cp15.c9_pmselr & 31);
+}
+
+static void pmevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value, uint8_t counter)
+{
+ if (counter < pmu_num_counters(env)) {
+ pmevcntr_op_start(env, counter);
+ env->cp15.c14_pmevcntr[counter] = value;
+ pmevcntr_op_finish(env, counter);
+ }
+ /*
+ * We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR
+ * are CONSTRAINED UNPREDICTABLE.
*/
- if (env->cp15.c9_pmselr == 0x1f) {
- return env->cp15.pmccfiltr_el0;
+}
+
+static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint8_t counter)
+{
+ if (counter < pmu_num_counters(env)) {
+ uint64_t ret;
+ pmevcntr_op_start(env, counter);
+ ret = env->cp15.c14_pmevcntr[counter];
+ pmevcntr_op_finish(env, counter);
+ return ret;
} else {
+ /* We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR
+ * are CONSTRAINED UNPREDICTABLE. */
return 0;
}
}
+static void pmevcntr_writefn(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ pmevcntr_write(env, ri, value, counter);
+}
+
+static uint64_t pmevcntr_readfn(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ return pmevcntr_read(env, ri, counter);
+}
+
+static void pmevcntr_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ assert(counter < pmu_num_counters(env));
+ env->cp15.c14_pmevcntr[counter] = value;
+ pmevcntr_write(env, ri, value, counter);
+}
+
+static uint64_t pmevcntr_rawread(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
+ assert(counter < pmu_num_counters(env));
+ return env->cp15.c14_pmevcntr[counter];
+}
+
+static void pmxevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ pmevcntr_write(env, ri, value, env->cp15.c9_pmselr & 31);
+}
+
+static uint64_t pmxevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ return pmevcntr_read(env, ri, env->cp15.c9_pmselr & 31);
+}
+
static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
@@ -1368,7 +1808,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.access = PL1_W, .type = ARM_CP_NOP },
/* Performance monitors are implementation defined in v7,
* but with an ARM recommended set of registers, which we
- * follow (although we don't actually implement any counters)
+ * follow.
*
* Performance registers fall into three categories:
* (a) always UNDEF in PL0, RW in PL1 (PMINTENSET, PMINTENCLR)
@@ -1413,10 +1853,13 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
.writefn = pmovsr_write,
.raw_writefn = raw_write },
- /* Unimplemented so WI. */
{ .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4,
- .access = PL0_W, .accessfn = pmreg_access_swinc, .type = ARM_CP_NOP },
-#ifndef CONFIG_USER_ONLY
+ .access = PL0_W, .accessfn = pmreg_access_swinc, .type = ARM_CP_NO_RAW,
+ .writefn = pmswinc_write },
+ { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4,
+ .access = PL0_W, .accessfn = pmreg_access_swinc, .type = ARM_CP_NO_RAW,
+ .writefn = pmswinc_write },
{ .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
.access = PL0_RW, .type = ARM_CP_ALIAS,
.fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmselr),
@@ -1435,26 +1878,39 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0,
.access = PL0_RW, .accessfn = pmreg_access_ccntr,
.type = ARM_CP_IO,
- .readfn = pmccntr_read, .writefn = pmccntr_write, },
-#endif
+ .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt),
+ .readfn = pmccntr_read, .writefn = pmccntr_write,
+ .raw_readfn = raw_read, .raw_writefn = raw_write, },
+ { .name = "PMCCFILTR", .cp = 15, .opc1 = 0, .crn = 14, .crm = 15, .opc2 = 7,
+ .writefn = pmccfiltr_write_a32, .readfn = pmccfiltr_read_a32,
+ .access = PL0_RW, .accessfn = pmreg_access,
+ .type = ARM_CP_ALIAS | ARM_CP_IO,
+ .resetvalue = 0, },
{ .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7,
- .writefn = pmccfiltr_write,
+ .writefn = pmccfiltr_write, .raw_writefn = raw_write,
.access = PL0_RW, .accessfn = pmreg_access,
.type = ARM_CP_IO,
.fieldoffset = offsetof(CPUARMState, cp15.pmccfiltr_el0),
.resetvalue = 0, },
{ .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
- .access = PL0_RW, .type = ARM_CP_NO_RAW, .accessfn = pmreg_access,
+ .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO,
+ .accessfn = pmreg_access,
.writefn = pmxevtyper_write, .readfn = pmxevtyper_read },
{ .name = "PMXEVTYPER_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 1,
- .access = PL0_RW, .type = ARM_CP_NO_RAW, .accessfn = pmreg_access,
+ .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO,
+ .accessfn = pmreg_access,
.writefn = pmxevtyper_write, .readfn = pmxevtyper_read },
- /* Unimplemented, RAZ/WI. */
{ .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2,
- .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
- .accessfn = pmreg_access_xevcntr },
+ .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO,
+ .accessfn = pmreg_access_xevcntr,
+ .writefn = pmxevcntr_write, .readfn = pmxevcntr_read },
+ { .name = "PMXEVCNTR_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 2,
+ .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO,
+ .accessfn = pmreg_access_xevcntr,
+ .writefn = pmxevcntr_write, .readfn = pmxevcntr_read },
{ .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0,
.access = PL0_R | PL1_RW, .accessfn = access_tpm,
.fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmuserenr),
@@ -1585,6 +2041,24 @@ static const ARMCPRegInfo v7mp_cp_reginfo[] = {
REGINFO_SENTINEL
};
+static const ARMCPRegInfo pmovsset_cp_reginfo[] = {
+ /* PMOVSSET is not implemented in v7 before v7ve */
+ { .name = "PMOVSSET", .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 3,
+ .access = PL0_RW, .accessfn = pmreg_access,
+ .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr),
+ .writefn = pmovsset_write,
+ .raw_writefn = raw_write },
+ { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3,
+ .access = PL0_RW, .accessfn = pmreg_access,
+ .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
+ .writefn = pmovsset_write,
+ .raw_writefn = raw_write },
+ REGINFO_SENTINEL
+};
+
static void teecr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
@@ -4284,7 +4758,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
#endif
/* The only field of MDCR_EL2 that has a defined architectural reset value
* is MDCR_EL2.HPMN which should reset to the value of PMCR_EL0.N; but we
- * don't impelment any PMU event counters, so using zero as a reset
+ * don't implement any PMU event counters, so using zero as a reset
* value for MDCR_EL2 is okay
*/
{ .name = "MDCR_EL2", .state = ARM_CP_STATE_BOTH,
@@ -5061,6 +5535,70 @@ static CPAccessResult access_lor_other(CPUARMState *env,
return access_lor_ns(env);
}
+#ifdef TARGET_AARCH64
+static CPAccessResult access_pauth(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
+{
+ int el = arm_current_el(env);
+
+ if (el < 2 &&
+ arm_feature(env, ARM_FEATURE_EL2) &&
+ !(arm_hcr_el2_eff(env) & HCR_APK)) {
+ return CP_ACCESS_TRAP_EL2;
+ }
+ if (el < 3 &&
+ arm_feature(env, ARM_FEATURE_EL3) &&
+ !(env->cp15.scr_el3 & SCR_APK)) {
+ return CP_ACCESS_TRAP_EL3;
+ }
+ return CP_ACCESS_OK;
+}
+
+static const ARMCPRegInfo pauth_reginfo[] = {
+ { .name = "APDAKEYLO_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 0,
+ .access = PL1_RW, .accessfn = access_pauth,
+ .fieldoffset = offsetof(CPUARMState, apda_key.lo) },
+ { .name = "APDAKEYHI_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 1,
+ .access = PL1_RW, .accessfn = access_pauth,
+ .fieldoffset = offsetof(CPUARMState, apda_key.hi) },
+ { .name = "APDBKEYLO_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 2,
+ .access = PL1_RW, .accessfn = access_pauth,
+ .fieldoffset = offsetof(CPUARMState, apdb_key.lo) },
+ { .name = "APDBKEYHI_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 3,
+ .access = PL1_RW, .accessfn = access_pauth,
+ .fieldoffset = offsetof(CPUARMState, apdb_key.hi) },
+ { .name = "APGAKEYLO_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 3, .opc2 = 0,
+ .access = PL1_RW, .accessfn = access_pauth,
+ .fieldoffset = offsetof(CPUARMState, apga_key.lo) },
+ { .name = "APGAKEYHI_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 3, .opc2 = 1,
+ .access = PL1_RW, .accessfn = access_pauth,
+ .fieldoffset = offsetof(CPUARMState, apga_key.hi) },
+ { .name = "APIAKEYLO_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 0,
+ .access = PL1_RW, .accessfn = access_pauth,
+ .fieldoffset = offsetof(CPUARMState, apia_key.lo) },
+ { .name = "APIAKEYHI_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 1,
+ .access = PL1_RW, .accessfn = access_pauth,
+ .fieldoffset = offsetof(CPUARMState, apia_key.hi) },
+ { .name = "APIBKEYLO_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 2,
+ .access = PL1_RW, .accessfn = access_pauth,
+ .fieldoffset = offsetof(CPUARMState, apib_key.lo) },
+ { .name = "APIBKEYHI_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 3,
+ .access = PL1_RW, .accessfn = access_pauth,
+ .fieldoffset = offsetof(CPUARMState, apib_key.hi) },
+ REGINFO_SENTINEL
+};
+#endif
+
void register_cp_regs_for_features(ARMCPU *cpu)
{
/* Register all the coprocessor registers based on feature bits */
@@ -5163,12 +5701,15 @@ void register_cp_regs_for_features(ARMCPU *cpu)
!arm_feature(env, ARM_FEATURE_PMSA)) {
define_arm_cp_regs(cpu, v7mp_cp_reginfo);
}
+ if (arm_feature(env, ARM_FEATURE_V7VE)) {
+ define_arm_cp_regs(cpu, pmovsset_cp_reginfo);
+ }
if (arm_feature(env, ARM_FEATURE_V7)) {
/* v7 performance monitor control register: same implementor
- * field as main ID register, and we implement only the cycle
- * count register.
+ * field as main ID register, and we implement four counters in
+ * addition to the cycle count register.
*/
-#ifndef CONFIG_USER_ONLY
+ unsigned int i, pmcrn = 4;
ARMCPRegInfo pmcr = {
.name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0,
.access = PL0_RW,
@@ -5183,12 +5724,48 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.access = PL0_RW, .accessfn = pmreg_access,
.type = ARM_CP_IO,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
- .resetvalue = cpu->midr & 0xff000000,
+ .resetvalue = (cpu->midr & 0xff000000) | (pmcrn << PMCRN_SHIFT),
.writefn = pmcr_write, .raw_writefn = raw_write,
};
define_one_arm_cp_reg(cpu, &pmcr);
define_one_arm_cp_reg(cpu, &pmcr64);
-#endif
+ for (i = 0; i < pmcrn; i++) {
+ char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i);
+ char *pmevcntr_el0_name = g_strdup_printf("PMEVCNTR%d_EL0", i);
+ char *pmevtyper_name = g_strdup_printf("PMEVTYPER%d", i);
+ char *pmevtyper_el0_name = g_strdup_printf("PMEVTYPER%d_EL0", i);
+ ARMCPRegInfo pmev_regs[] = {
+ { .name = pmevcntr_name, .cp = 15, .crn = 15,
+ .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7,
+ .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS,
+ .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn,
+ .accessfn = pmreg_access },
+ { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 15, .crm = 8 | (3 & (i >> 3)),
+ .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access,
+ .type = ARM_CP_IO,
+ .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn,
+ .raw_readfn = pmevcntr_rawread,
+ .raw_writefn = pmevcntr_rawwrite },
+ { .name = pmevtyper_name, .cp = 15, .crn = 15,
+ .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7,
+ .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS,
+ .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn,
+ .accessfn = pmreg_access },
+ { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 15, .crm = 12 | (3 & (i >> 3)),
+ .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access,
+ .type = ARM_CP_IO,
+ .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn,
+ .raw_writefn = pmevtyper_rawwrite },
+ REGINFO_SENTINEL
+ };
+ define_arm_cp_regs(cpu, pmev_regs);
+ g_free(pmevcntr_name);
+ g_free(pmevcntr_el0_name);
+ g_free(pmevtyper_name);
+ g_free(pmevtyper_el0_name);
+ }
ARMCPRegInfo clidr = {
.name = "CLIDR", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1,
@@ -5200,6 +5777,21 @@ void register_cp_regs_for_features(ARMCPU *cpu)
} else {
define_arm_cp_regs(cpu, not_v7_cp_reginfo);
}
+ if (FIELD_EX32(cpu->id_dfr0, ID_DFR0, PERFMON) >= 4 &&
+ FIELD_EX32(cpu->id_dfr0, ID_DFR0, PERFMON) != 0xf) {
+ ARMCPRegInfo v81_pmu_regs[] = {
+ { .name = "PMCEID2", .state = ARM_CP_STATE_AA32,
+ .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 4,
+ .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
+ .resetvalue = extract64(cpu->pmceid0, 32, 32) },
+ { .name = "PMCEID3", .state = ARM_CP_STATE_AA32,
+ .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 5,
+ .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
+ .resetvalue = extract64(cpu->pmceid1, 32, 32) },
+ REGINFO_SENTINEL
+ };
+ define_arm_cp_regs(cpu, v81_pmu_regs);
+ }
if (arm_feature(env, ARM_FEATURE_V8)) {
/* AArch64 ID registers, which all have impdef reset values.
* Note that within the ID register ranges the unused slots
@@ -5376,7 +5968,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "PMCEID0", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 6,
.access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
- .resetvalue = cpu->pmceid0 },
+ .resetvalue = extract64(cpu->pmceid0, 0, 32) },
{ .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6,
.access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
@@ -5384,7 +5976,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "PMCEID1", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 7,
.access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
- .resetvalue = cpu->pmceid1 },
+ .resetvalue = extract64(cpu->pmceid1, 0, 32) },
{ .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7,
.access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
@@ -5845,6 +6437,12 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_one_arm_cp_reg(cpu, &zcr_el3_reginfo);
}
}
+
+#ifdef TARGET_AARCH64
+ if (cpu_isar_feature(aa64_pauth, cpu)) {
+ define_arm_cp_regs(cpu, pauth_reginfo);
+ }
+#endif
}
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
@@ -6297,7 +6895,7 @@ static int bad_mode_switch(CPUARMState *env, int mode, CPSRWriteType write_type)
return 0;
case ARM_CPU_MODE_HYP:
return !arm_feature(env, ARM_FEATURE_EL2)
- || arm_current_el(env) < 2 || arm_is_secure(env);
+ || arm_current_el(env) < 2 || arm_is_secure_below_el3(env);
case ARM_CPU_MODE_MON:
return arm_current_el(env) < 3;
default:
@@ -7117,7 +7715,7 @@ static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain,
limit = env->v7m.msplim[M_REG_S];
}
} else {
- mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
+ mmu_idx = arm_mmu_idx(env);
frame_sp_p = &env->regs[13];
limit = v7m_sp_limit(env);
}
@@ -7298,7 +7896,7 @@ static bool v7m_push_stack(ARMCPU *cpu)
CPUARMState *env = &cpu->env;
uint32_t xpsr = xpsr_read(env);
uint32_t frameptr = env->regs[13];
- ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
+ ARMMMUIdx mmu_idx = arm_mmu_idx(env);
/* Align stack pointer if the guest wants that */
if ((frameptr & 4) &&
@@ -8957,48 +9555,6 @@ static inline ARMMMUIdx stage_1_mmu_idx(ARMMMUIdx mmu_idx)
return mmu_idx;
}
-/* Returns TBI0 value for current regime el */
-uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx)
-{
- TCR *tcr;
- uint32_t el;
-
- /* For EL0 and EL1, TBI is controlled by stage 1's TCR, so convert
- * a stage 1+2 mmu index into the appropriate stage 1 mmu index.
- */
- mmu_idx = stage_1_mmu_idx(mmu_idx);
-
- tcr = regime_tcr(env, mmu_idx);
- el = regime_el(env, mmu_idx);
-
- if (el > 1) {
- return extract64(tcr->raw_tcr, 20, 1);
- } else {
- return extract64(tcr->raw_tcr, 37, 1);
- }
-}
-
-/* Returns TBI1 value for current regime el */
-uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx)
-{
- TCR *tcr;
- uint32_t el;
-
- /* For EL0 and EL1, TBI is controlled by stage 1's TCR, so convert
- * a stage 1+2 mmu index into the appropriate stage 1 mmu index.
- */
- mmu_idx = stage_1_mmu_idx(mmu_idx);
-
- tcr = regime_tcr(env, mmu_idx);
- el = regime_el(env, mmu_idx);
-
- if (el > 1) {
- return 0;
- } else {
- return extract64(tcr->raw_tcr, 38, 1);
- }
-}
-
/* Return the TTBR associated with this translation regime */
static inline uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx,
int ttbrn)
@@ -9744,6 +10300,138 @@ static uint8_t convert_stage2_attrs(CPUARMState *env, uint8_t s2attrs)
return (hiattr << 6) | (hihint << 4) | (loattr << 2) | lohint;
}
+ARMVAParameters aa64_va_parameters_both(CPUARMState *env, uint64_t va,
+ ARMMMUIdx mmu_idx)
+{
+ uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr;
+ uint32_t el = regime_el(env, mmu_idx);
+ bool tbi, tbid, epd, hpd, using16k, using64k;
+ int select, tsz;
+
+ /*
+ * Bit 55 is always between the two regions, and is canonical for
+ * determining if address tagging is enabled.
+ */
+ select = extract64(va, 55, 1);
+
+ if (el > 1) {
+ tsz = extract32(tcr, 0, 6);
+ using64k = extract32(tcr, 14, 1);
+ using16k = extract32(tcr, 15, 1);
+ if (mmu_idx == ARMMMUIdx_S2NS) {
+ /* VTCR_EL2 */
+ tbi = tbid = hpd = false;
+ } else {
+ tbi = extract32(tcr, 20, 1);
+ hpd = extract32(tcr, 24, 1);
+ tbid = extract32(tcr, 29, 1);
+ }
+ epd = false;
+ } else if (!select) {
+ tsz = extract32(tcr, 0, 6);
+ epd = extract32(tcr, 7, 1);
+ using64k = extract32(tcr, 14, 1);
+ using16k = extract32(tcr, 15, 1);
+ tbi = extract64(tcr, 37, 1);
+ hpd = extract64(tcr, 41, 1);
+ tbid = extract64(tcr, 51, 1);
+ } else {
+ int tg = extract32(tcr, 30, 2);
+ using16k = tg == 1;
+ using64k = tg == 3;
+ tsz = extract32(tcr, 16, 6);
+ epd = extract32(tcr, 23, 1);
+ tbi = extract64(tcr, 38, 1);
+ hpd = extract64(tcr, 42, 1);
+ tbid = extract64(tcr, 52, 1);
+ }
+ tsz = MIN(tsz, 39); /* TODO: ARMv8.4-TTST */
+ tsz = MAX(tsz, 16); /* TODO: ARMv8.2-LVA */
+
+ return (ARMVAParameters) {
+ .tsz = tsz,
+ .select = select,
+ .tbi = tbi,
+ .tbid = tbid,
+ .epd = epd,
+ .hpd = hpd,
+ .using16k = using16k,
+ .using64k = using64k,
+ };
+}
+
+ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
+ ARMMMUIdx mmu_idx, bool data)
+{
+ ARMVAParameters ret = aa64_va_parameters_both(env, va, mmu_idx);
+
+ /* Present TBI as a composite with TBID. */
+ ret.tbi &= (data || !ret.tbid);
+ return ret;
+}
+
+static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va,
+ ARMMMUIdx mmu_idx)
+{
+ uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr;
+ uint32_t el = regime_el(env, mmu_idx);
+ int select, tsz;
+ bool epd, hpd;
+
+ if (mmu_idx == ARMMMUIdx_S2NS) {
+ /* VTCR */
+ bool sext = extract32(tcr, 4, 1);
+ bool sign = extract32(tcr, 3, 1);
+
+ /*
+ * If the sign-extend bit is not the same as t0sz[3], the result
+ * is unpredictable. Flag this as a guest error.
+ */
+ if (sign != sext) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "AArch32: VTCR.S / VTCR.T0SZ[3] mismatch\n");
+ }
+ tsz = sextract32(tcr, 0, 4) + 8;
+ select = 0;
+ hpd = false;
+ epd = false;
+ } else if (el == 2) {
+ /* HTCR */
+ tsz = extract32(tcr, 0, 3);
+ select = 0;
+ hpd = extract64(tcr, 24, 1);
+ epd = false;
+ } else {
+ int t0sz = extract32(tcr, 0, 3);
+ int t1sz = extract32(tcr, 16, 3);
+
+ if (t1sz == 0) {
+ select = va > (0xffffffffu >> t0sz);
+ } else {
+ /* Note that we will detect errors later. */
+ select = va >= ~(0xffffffffu >> t1sz);
+ }
+ if (!select) {
+ tsz = t0sz;
+ epd = extract32(tcr, 7, 1);
+ hpd = extract64(tcr, 41, 1);
+ } else {
+ tsz = t1sz;
+ epd = extract32(tcr, 23, 1);
+ hpd = extract64(tcr, 42, 1);
+ }
+ /* For aarch32, hpd0 is not enabled without t2e as well. */
+ hpd &= extract32(tcr, 6, 1);
+ }
+
+ return (ARMVAParameters) {
+ .tsz = tsz,
+ .select = select,
+ .epd = epd,
+ .hpd = hpd,
+ };
+}
+
static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
MMUAccessType access_type, ARMMMUIdx mmu_idx,
hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot,
@@ -9755,26 +10443,20 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
/* Read an LPAE long-descriptor translation table. */
ARMFaultType fault_type = ARMFault_Translation;
uint32_t level;
- uint32_t epd = 0;
- int32_t t0sz, t1sz;
- uint32_t tg;
+ ARMVAParameters param;
uint64_t ttbr;
- int ttbr_select;
hwaddr descaddr, indexmask, indexmask_grainsize;
uint32_t tableattrs;
- target_ulong page_size;
+ target_ulong page_size, top_bits;
uint32_t attrs;
- int32_t stride = 9;
- int32_t addrsize;
- int inputsize;
- int32_t tbi = 0;
+ int32_t stride;
+ int addrsize, inputsize;
TCR *tcr = regime_tcr(env, mmu_idx);
int ap, ns, xn, pxn;
uint32_t el = regime_el(env, mmu_idx);
- bool ttbr1_valid = true;
+ bool ttbr1_valid;
uint64_t descaddrmask;
bool aarch64 = arm_el_is_aa64(env, el);
- bool hpd = false;
/* TODO:
* This code does not handle the different format TCR for VTCR_EL2.
@@ -9783,91 +10465,44 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
* support for those page table walks.
*/
if (aarch64) {
+ param = aa64_va_parameters(env, address, mmu_idx,
+ access_type != MMU_INST_FETCH);
level = 0;
- addrsize = 64;
- if (el > 1) {
- if (mmu_idx != ARMMMUIdx_S2NS) {
- tbi = extract64(tcr->raw_tcr, 20, 1);
- }
- } else {
- if (extract64(address, 55, 1)) {
- tbi = extract64(tcr->raw_tcr, 38, 1);
- } else {
- tbi = extract64(tcr->raw_tcr, 37, 1);
- }
- }
- tbi *= 8;
-
/* If we are in 64-bit EL2 or EL3 then there is no TTBR1, so mark it
* invalid.
*/
- if (el > 1) {
- ttbr1_valid = false;
- }
+ ttbr1_valid = (el < 2);
+ addrsize = 64 - 8 * param.tbi;
+ inputsize = 64 - param.tsz;
} else {
+ param = aa32_va_parameters(env, address, mmu_idx);
level = 1;
- addrsize = 32;
/* There is no TTBR1 for EL2 */
- if (el == 2) {
- ttbr1_valid = false;
- }
+ ttbr1_valid = (el != 2);
+ addrsize = (mmu_idx == ARMMMUIdx_S2NS ? 40 : 32);
+ inputsize = addrsize - param.tsz;
}
- /* Determine whether this address is in the region controlled by
- * TTBR0 or TTBR1 (or if it is in neither region and should fault).
- * This is a Non-secure PL0/1 stage 1 translation, so controlled by
- * TTBCR/TTBR0/TTBR1 in accordance with ARM ARM DDI0406C table B-32:
+ /*
+ * We determined the region when collecting the parameters, but we
+ * have not yet validated that the address is valid for the region.
+ * Extract the top bits and verify that they all match select.
*/
- if (aarch64) {
- /* AArch64 translation. */
- t0sz = extract32(tcr->raw_tcr, 0, 6);
- t0sz = MIN(t0sz, 39);
- t0sz = MAX(t0sz, 16);
- } else if (mmu_idx != ARMMMUIdx_S2NS) {
- /* AArch32 stage 1 translation. */
- t0sz = extract32(tcr->raw_tcr, 0, 3);
- } else {
- /* AArch32 stage 2 translation. */
- bool sext = extract32(tcr->raw_tcr, 4, 1);
- bool sign = extract32(tcr->raw_tcr, 3, 1);
- /* Address size is 40-bit for a stage 2 translation,
- * and t0sz can be negative (from -8 to 7),
- * so we need to adjust it to use the TTBR selecting logic below.
- */
- addrsize = 40;
- t0sz = sextract32(tcr->raw_tcr, 0, 4) + 8;
-
- /* If the sign-extend bit is not the same as t0sz[3], the result
- * is unpredictable. Flag this as a guest error. */
- if (sign != sext) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "AArch32: VTCR.S / VTCR.T0SZ[3] mismatch\n");
- }
- }
- t1sz = extract32(tcr->raw_tcr, 16, 6);
- if (aarch64) {
- t1sz = MIN(t1sz, 39);
- t1sz = MAX(t1sz, 16);
- }
- if (t0sz && !extract64(address, addrsize - t0sz, t0sz - tbi)) {
- /* there is a ttbr0 region and we are in it (high bits all zero) */
- ttbr_select = 0;
- } else if (ttbr1_valid && t1sz &&
- !extract64(~address, addrsize - t1sz, t1sz - tbi)) {
- /* there is a ttbr1 region and we are in it (high bits all one) */
- ttbr_select = 1;
- } else if (!t0sz) {
- /* ttbr0 region is "everything not in the ttbr1 region" */
- ttbr_select = 0;
- } else if (!t1sz && ttbr1_valid) {
- /* ttbr1 region is "everything not in the ttbr0 region" */
- ttbr_select = 1;
- } else {
- /* in the gap between the two regions, this is a Translation fault */
+ top_bits = sextract64(address, inputsize, addrsize - inputsize);
+ if (-top_bits != param.select || (param.select && !ttbr1_valid)) {
+ /* In the gap between the two regions, this is a Translation fault */
fault_type = ARMFault_Translation;
goto do_fault;
}
+ if (param.using64k) {
+ stride = 13;
+ } else if (param.using16k) {
+ stride = 11;
+ } else {
+ stride = 9;
+ }
+
/* Note that QEMU ignores shareability and cacheability attributes,
* so we don't need to do anything with the SH, ORGN, IRGN fields
* in the TTBCR. Similarly, TTBCR:A1 selects whether we get the
@@ -9875,56 +10510,13 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
* implement any ASID-like capability so we can ignore it (instead
* we will always flush the TLB any time the ASID is changed).
*/
- if (ttbr_select == 0) {
- ttbr = regime_ttbr(env, mmu_idx, 0);
- if (el < 2) {
- epd = extract32(tcr->raw_tcr, 7, 1);
- }
- inputsize = addrsize - t0sz;
-
- tg = extract32(tcr->raw_tcr, 14, 2);
- if (tg == 1) { /* 64KB pages */
- stride = 13;
- }
- if (tg == 2) { /* 16KB pages */
- stride = 11;
- }
- if (aarch64 && el > 1) {
- hpd = extract64(tcr->raw_tcr, 24, 1);
- } else {
- hpd = extract64(tcr->raw_tcr, 41, 1);
- }
- if (!aarch64) {
- /* For aarch32, hpd0 is not enabled without t2e as well. */
- hpd &= extract64(tcr->raw_tcr, 6, 1);
- }
- } else {
- /* We should only be here if TTBR1 is valid */
- assert(ttbr1_valid);
-
- ttbr = regime_ttbr(env, mmu_idx, 1);
- epd = extract32(tcr->raw_tcr, 23, 1);
- inputsize = addrsize - t1sz;
-
- tg = extract32(tcr->raw_tcr, 30, 2);
- if (tg == 3) { /* 64KB pages */
- stride = 13;
- }
- if (tg == 1) { /* 16KB pages */
- stride = 11;
- }
- hpd = extract64(tcr->raw_tcr, 42, 1);
- if (!aarch64) {
- /* For aarch32, hpd1 is not enabled without t2e as well. */
- hpd &= extract64(tcr->raw_tcr, 6, 1);
- }
- }
+ ttbr = regime_ttbr(env, mmu_idx, param.select);
/* Here we should have set up all the parameters for the translation:
* inputsize, ttbr, epd, stride, tbi
*/
- if (epd) {
+ if (param.epd) {
/* Translation table walk disabled => Translation fault on TLB miss
* Note: This is always 0 on 64-bit EL2 and EL3.
*/
@@ -10037,7 +10629,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
}
/* Merge in attributes from table descriptors */
attrs |= nstable << 3; /* NS */
- if (hpd) {
+ if (param.hpd) {
/* HPD disables all the table attributes except NSTable. */
break;
}
@@ -11073,7 +11665,7 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
int prot;
bool ret;
ARMMMUFaultInfo fi = {};
- ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
+ ARMMMUIdx mmu_idx = arm_mmu_idx(env);
*attrs = (MemTxAttrs) {};
@@ -12949,10 +13541,66 @@ int fp_exception_el(CPUARMState *env, int cur_el)
return 0;
}
+ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env,
+ bool secstate, bool priv)
+{
+ ARMMMUIdx mmu_idx = ARM_MMU_IDX_M;
+
+ if (priv) {
+ mmu_idx |= ARM_MMU_IDX_M_PRIV;
+ }
+
+ if (armv7m_nvic_neg_prio_requested(env->nvic, secstate)) {
+ mmu_idx |= ARM_MMU_IDX_M_NEGPRI;
+ }
+
+ if (secstate) {
+ mmu_idx |= ARM_MMU_IDX_M_S;
+ }
+
+ return mmu_idx;
+}
+
+/* Return the MMU index for a v7M CPU in the specified security state */
+ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate)
+{
+ bool priv = arm_current_el(env) != 0;
+
+ return arm_v7m_mmu_idx_for_secstate_and_priv(env, secstate, priv);
+}
+
+ARMMMUIdx arm_mmu_idx(CPUARMState *env)
+{
+ int el;
+
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ return arm_v7m_mmu_idx_for_secstate(env, env->v7m.secure);
+ }
+
+ el = arm_current_el(env);
+ if (el < 2 && arm_is_secure_below_el3(env)) {
+ return ARMMMUIdx_S1SE0 + el;
+ } else {
+ return ARMMMUIdx_S12NSE0 + el;
+ }
+}
+
+int cpu_mmu_index(CPUARMState *env, bool ifetch)
+{
+ return arm_to_core_mmu_idx(arm_mmu_idx(env));
+}
+
+#ifndef CONFIG_USER_ONLY
+ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env)
+{
+ return stage_1_mmu_idx(arm_mmu_idx(env));
+}
+#endif
+
void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *pflags)
{
- ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
+ ARMMMUIdx mmu_idx = arm_mmu_idx(env);
int current_el = arm_current_el(env);
int fp_el = fp_exception_el(env, current_el);
uint32_t flags = 0;
@@ -12962,11 +13610,30 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
*pc = env->pc;
flags = FIELD_DP32(flags, TBFLAG_ANY, AARCH64_STATE, 1);
- /* Get control bits for tagged addresses */
- flags = FIELD_DP32(flags, TBFLAG_A64, TBI0,
- arm_regime_tbi0(env, mmu_idx));
- flags = FIELD_DP32(flags, TBFLAG_A64, TBI1,
- arm_regime_tbi1(env, mmu_idx));
+
+#ifndef CONFIG_USER_ONLY
+ /*
+ * Get control bits for tagged addresses. Note that the
+ * translator only uses this for instruction addresses.
+ */
+ {
+ ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx);
+ ARMVAParameters p0 = aa64_va_parameters_both(env, 0, stage1);
+ int tbii, tbid;
+
+ /* FIXME: ARMv8.1-VHE S2 translation regime. */
+ if (regime_el(env, stage1) < 2) {
+ ARMVAParameters p1 = aa64_va_parameters_both(env, -1, stage1);
+ tbid = (p1.tbi << 1) | p0.tbi;
+ tbii = tbid & ~((p1.tbid << 1) | p0.tbid);
+ } else {
+ tbid = p0.tbi;
+ tbii = tbid & !p0.tbid;
+ }
+
+ flags = FIELD_DP32(flags, TBFLAG_A64, TBII, tbii);
+ }
+#endif
if (cpu_isar_feature(aa64_sve, cpu)) {
int sve_el = sve_exception_el(env, current_el);
@@ -12983,6 +13650,25 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
flags = FIELD_DP32(flags, TBFLAG_A64, SVEEXC_EL, sve_el);
flags = FIELD_DP32(flags, TBFLAG_A64, ZCR_LEN, zcr_len);
}
+
+ if (cpu_isar_feature(aa64_pauth, cpu)) {
+ /*
+ * In order to save space in flags, we record only whether
+ * pauth is "inactive", meaning all insns are implemented as
+ * a nop, or "active" when some action must be performed.
+ * The decision of which action to take is left to a helper.
+ */
+ uint64_t sctlr;
+ if (current_el == 0) {
+ /* FIXME: ARMv8.1-VHE S2 translation regime. */
+ sctlr = env->cp15.sctlr_el[1];
+ } else {
+ sctlr = env->cp15.sctlr_el[current_el];
+ }
+ if (sctlr & (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB)) {
+ flags = FIELD_DP32(flags, TBFLAG_A64, PAUTH_ACTIVE, 1);
+ }
+ }
} else {
*pc = env->regs[15];
flags = FIELD_DP32(flags, TBFLAG_A32, THUMB, env->thumb);
diff --git a/target/arm/helper.h b/target/arm/helper.h
index 8c9590091b..53a38188c6 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -79,7 +79,6 @@ DEF_HELPER_2(get_cp_reg64, i64, env, ptr)
DEF_HELPER_3(msr_i_pstate, void, env, i32, i32)
DEF_HELPER_1(clear_pstate_ss, void, env)
-DEF_HELPER_1(exception_return, void, env)
DEF_HELPER_2(get_r13_banked, i32, env, i32)
DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 78e026d6e9..a6fd4582b2 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -104,6 +104,13 @@ void QEMU_NORETURN raise_exception(CPUARMState *env, uint32_t excp,
uint32_t syndrome, uint32_t target_el);
/*
+ * Similarly, but also use unwinding to restore cpu state.
+ */
+void QEMU_NORETURN raise_exception_ra(CPUARMState *env, uint32_t excp,
+ uint32_t syndrome, uint32_t target_el,
+ uintptr_t ra);
+
+/*
* For AArch64, map a given EL to an index in the banked_spsr array.
* Note that this mapping and the AArch32 mapping defined in bank_number()
* must agree such that the AArch64<->AArch32 SPSRs have the architecturally
@@ -259,6 +266,7 @@ enum arm_exception_class {
EC_CP14DTTRAP = 0x06,
EC_ADVSIMDFPACCESSTRAP = 0x07,
EC_FPIDTRAP = 0x08,
+ EC_PACTRAP = 0x09,
EC_CP14RRTTRAP = 0x0c,
EC_ILLEGALSTATE = 0x0e,
EC_AA32_SVC = 0x11,
@@ -426,6 +434,11 @@ static inline uint32_t syn_sve_access_trap(void)
return EC_SVEACCESSTRAP << ARM_EL_EC_SHIFT;
}
+static inline uint32_t syn_pactrap(void)
+{
+ return EC_PACTRAP << ARM_EL_EC_SHIFT;
+}
+
static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc)
{
return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT)
@@ -906,4 +919,68 @@ void arm_cpu_update_virq(ARMCPU *cpu);
*/
void arm_cpu_update_vfiq(ARMCPU *cpu);
+/**
+ * arm_mmu_idx:
+ * @env: The cpu environment
+ *
+ * Return the full ARMMMUIdx for the current translation regime.
+ */
+ARMMMUIdx arm_mmu_idx(CPUARMState *env);
+
+/**
+ * arm_stage1_mmu_idx:
+ * @env: The cpu environment
+ *
+ * Return the ARMMMUIdx for the stage1 traversal for the current regime.
+ */
+#ifdef CONFIG_USER_ONLY
+static inline ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env)
+{
+ return ARMMMUIdx_S1NSE0;
+}
+#else
+ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env);
+#endif
+
+/*
+ * Parameters of a given virtual address, as extracted from the
+ * translation control register (TCR) for a given regime.
+ */
+typedef struct ARMVAParameters {
+ unsigned tsz : 8;
+ unsigned select : 1;
+ bool tbi : 1;
+ bool tbid : 1;
+ bool epd : 1;
+ bool hpd : 1;
+ bool using16k : 1;
+ bool using64k : 1;
+} ARMVAParameters;
+
+#ifdef CONFIG_USER_ONLY
+static inline ARMVAParameters aa64_va_parameters_both(CPUARMState *env,
+ uint64_t va,
+ ARMMMUIdx mmu_idx)
+{
+ return (ARMVAParameters) {
+ /* 48-bit address space */
+ .tsz = 16,
+ /* We can't handle tagged addresses properly in user-only mode */
+ .tbi = false,
+ };
+}
+
+static inline ARMVAParameters aa64_va_parameters(CPUARMState *env,
+ uint64_t va,
+ ARMMMUIdx mmu_idx, bool data)
+{
+ return aa64_va_parameters_both(env, va, mmu_idx);
+}
+#else
+ARMVAParameters aa64_va_parameters_both(CPUARMState *env, uint64_t va,
+ ARMMMUIdx mmu_idx);
+ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
+ ARMMMUIdx mmu_idx, bool data);
+#endif
+
#endif
diff --git a/target/arm/machine.c b/target/arm/machine.c
index 7a22ebc209..b292549614 100644
--- a/target/arm/machine.c
+++ b/target/arm/machine.c
@@ -620,6 +620,10 @@ static int cpu_pre_save(void *opaque)
{
ARMCPU *cpu = opaque;
+ if (!kvm_enabled()) {
+ pmu_op_start(&cpu->env);
+ }
+
if (kvm_enabled()) {
if (!write_kvmstate_to_list(cpu)) {
/* This should never fail */
@@ -641,6 +645,17 @@ static int cpu_pre_save(void *opaque)
return 0;
}
+static int cpu_post_save(void *opaque)
+{
+ ARMCPU *cpu = opaque;
+
+ if (!kvm_enabled()) {
+ pmu_op_finish(&cpu->env);
+ }
+
+ return 0;
+}
+
static int cpu_pre_load(void *opaque)
{
ARMCPU *cpu = opaque;
@@ -653,6 +668,10 @@ static int cpu_pre_load(void *opaque)
*/
env->irq_line_state = UINT32_MAX;
+ if (!kvm_enabled()) {
+ pmu_op_start(&cpu->env);
+ }
+
return 0;
}
@@ -721,6 +740,10 @@ static int cpu_post_load(void *opaque, int version_id)
hw_breakpoint_update_all(cpu);
hw_watchpoint_update_all(cpu);
+ if (!kvm_enabled()) {
+ pmu_op_finish(&cpu->env);
+ }
+
return 0;
}
@@ -729,6 +752,7 @@ const VMStateDescription vmstate_arm_cpu = {
.version_id = 22,
.minimum_version_id = 22,
.pre_save = cpu_pre_save,
+ .post_save = cpu_post_save,
.pre_load = cpu_pre_load,
.post_load = cpu_post_load,
.fields = (VMStateField[]) {
diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
index ef72361a36..c998eadfaa 100644
--- a/target/arm/op_helper.c
+++ b/target/arm/op_helper.c
@@ -28,8 +28,8 @@
#define SIGNBIT (uint32_t)0x80000000
#define SIGNBIT64 ((uint64_t)1 << 63)
-void raise_exception(CPUARMState *env, uint32_t excp,
- uint32_t syndrome, uint32_t target_el)
+static CPUState *do_raise_exception(CPUARMState *env, uint32_t excp,
+ uint32_t syndrome, uint32_t target_el)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
@@ -50,9 +50,24 @@ void raise_exception(CPUARMState *env, uint32_t excp,
cs->exception_index = excp;
env->exception.syndrome = syndrome;
env->exception.target_el = target_el;
+
+ return cs;
+}
+
+void raise_exception(CPUARMState *env, uint32_t excp,
+ uint32_t syndrome, uint32_t target_el)
+{
+ CPUState *cs = do_raise_exception(env, excp, syndrome, target_el);
cpu_loop_exit(cs);
}
+void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome,
+ uint32_t target_el, uintptr_t ra)
+{
+ CPUState *cs = do_raise_exception(env, excp, syndrome, target_el);
+ cpu_loop_exit_restore(cs, ra);
+}
+
static int exception_target_el(CPUARMState *env)
{
int target_el = MAX(1, arm_current_el(env));
@@ -1014,161 +1029,6 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
}
}
-static int el_from_spsr(uint32_t spsr)
-{
- /* Return the exception level that this SPSR is requesting a return to,
- * or -1 if it is invalid (an illegal return)
- */
- if (spsr & PSTATE_nRW) {
- switch (spsr & CPSR_M) {
- case ARM_CPU_MODE_USR:
- return 0;
- case ARM_CPU_MODE_HYP:
- return 2;
- case ARM_CPU_MODE_FIQ:
- case ARM_CPU_MODE_IRQ:
- case ARM_CPU_MODE_SVC:
- case ARM_CPU_MODE_ABT:
- case ARM_CPU_MODE_UND:
- case ARM_CPU_MODE_SYS:
- return 1;
- case ARM_CPU_MODE_MON:
- /* Returning to Mon from AArch64 is never possible,
- * so this is an illegal return.
- */
- default:
- return -1;
- }
- } else {
- if (extract32(spsr, 1, 1)) {
- /* Return with reserved M[1] bit set */
- return -1;
- }
- if (extract32(spsr, 0, 4) == 1) {
- /* return to EL0 with M[0] bit set */
- return -1;
- }
- return extract32(spsr, 2, 2);
- }
-}
-
-void HELPER(exception_return)(CPUARMState *env)
-{
- int cur_el = arm_current_el(env);
- unsigned int spsr_idx = aarch64_banked_spsr_index(cur_el);
- uint32_t spsr = env->banked_spsr[spsr_idx];
- int new_el;
- bool return_to_aa64 = (spsr & PSTATE_nRW) == 0;
-
- aarch64_save_sp(env, cur_el);
-
- arm_clear_exclusive(env);
-
- /* We must squash the PSTATE.SS bit to zero unless both of the
- * following hold:
- * 1. debug exceptions are currently disabled
- * 2. singlestep will be active in the EL we return to
- * We check 1 here and 2 after we've done the pstate/cpsr write() to
- * transition to the EL we're going to.
- */
- if (arm_generate_debug_exceptions(env)) {
- spsr &= ~PSTATE_SS;
- }
-
- new_el = el_from_spsr(spsr);
- if (new_el == -1) {
- goto illegal_return;
- }
- if (new_el > cur_el
- || (new_el == 2 && !arm_feature(env, ARM_FEATURE_EL2))) {
- /* Disallow return to an EL which is unimplemented or higher
- * than the current one.
- */
- goto illegal_return;
- }
-
- if (new_el != 0 && arm_el_is_aa64(env, new_el) != return_to_aa64) {
- /* Return to an EL which is configured for a different register width */
- goto illegal_return;
- }
-
- if (new_el == 2 && arm_is_secure_below_el3(env)) {
- /* Return to the non-existent secure-EL2 */
- goto illegal_return;
- }
-
- if (new_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) {
- goto illegal_return;
- }
-
- qemu_mutex_lock_iothread();
- arm_call_pre_el_change_hook(arm_env_get_cpu(env));
- qemu_mutex_unlock_iothread();
-
- if (!return_to_aa64) {
- env->aarch64 = 0;
- /* We do a raw CPSR write because aarch64_sync_64_to_32()
- * will sort the register banks out for us, and we've already
- * caught all the bad-mode cases in el_from_spsr().
- */
- cpsr_write(env, spsr, ~0, CPSRWriteRaw);
- if (!arm_singlestep_active(env)) {
- env->uncached_cpsr &= ~PSTATE_SS;
- }
- aarch64_sync_64_to_32(env);
-
- if (spsr & CPSR_T) {
- env->regs[15] = env->elr_el[cur_el] & ~0x1;
- } else {
- env->regs[15] = env->elr_el[cur_el] & ~0x3;
- }
- qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to "
- "AArch32 EL%d PC 0x%" PRIx32 "\n",
- cur_el, new_el, env->regs[15]);
- } else {
- env->aarch64 = 1;
- pstate_write(env, spsr);
- if (!arm_singlestep_active(env)) {
- env->pstate &= ~PSTATE_SS;
- }
- aarch64_restore_sp(env, new_el);
- env->pc = env->elr_el[cur_el];
- qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to "
- "AArch64 EL%d PC 0x%" PRIx64 "\n",
- cur_el, new_el, env->pc);
- }
- /*
- * Note that cur_el can never be 0. If new_el is 0, then
- * el0_a64 is return_to_aa64, else el0_a64 is ignored.
- */
- aarch64_sve_change_el(env, cur_el, new_el, return_to_aa64);
-
- qemu_mutex_lock_iothread();
- arm_call_el_change_hook(arm_env_get_cpu(env));
- qemu_mutex_unlock_iothread();
-
- return;
-
-illegal_return:
- /* Illegal return events of various kinds have architecturally
- * mandated behaviour:
- * restore NZCV and DAIF from SPSR_ELx
- * set PSTATE.IL
- * restore PC from ELR_ELx
- * no change to exception level, execution state or stack pointer
- */
- env->pstate |= PSTATE_IL;
- env->pc = env->elr_el[cur_el];
- spsr &= PSTATE_NZCV | PSTATE_DAIF;
- spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF);
- pstate_write(env, spsr);
- if (!arm_singlestep_active(env)) {
- env->pstate &= ~PSTATE_SS;
- }
- qemu_log_mask(LOG_GUEST_ERROR, "Illegal exception return at EL%d: "
- "resuming execution at 0x%" PRIx64 "\n", cur_el, env->pc);
-}
-
/* Return true if the linked breakpoint entry lbn passes its checks */
static bool linked_bp_matches(ARMCPU *cpu, int lbn)
{
diff --git a/target/arm/pauth_helper.c b/target/arm/pauth_helper.c
new file mode 100644
index 0000000000..d750f96edf
--- /dev/null
+++ b/target/arm/pauth_helper.c
@@ -0,0 +1,497 @@
+/*
+ * ARM v8.3-PAuth Operations
+ *
+ * Copyright (c) 2019 Linaro, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "internals.h"
+#include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
+#include "exec/helper-proto.h"
+#include "tcg/tcg-gvec-desc.h"
+
+
+static uint64_t pac_cell_shuffle(uint64_t i)
+{
+ uint64_t o = 0;
+
+ o |= extract64(i, 52, 4);
+ o |= extract64(i, 24, 4) << 4;
+ o |= extract64(i, 44, 4) << 8;
+ o |= extract64(i, 0, 4) << 12;
+
+ o |= extract64(i, 28, 4) << 16;
+ o |= extract64(i, 48, 4) << 20;
+ o |= extract64(i, 4, 4) << 24;
+ o |= extract64(i, 40, 4) << 28;
+
+ o |= extract64(i, 32, 4) << 32;
+ o |= extract64(i, 12, 4) << 36;
+ o |= extract64(i, 56, 4) << 40;
+ o |= extract64(i, 20, 4) << 44;
+
+ o |= extract64(i, 8, 4) << 48;
+ o |= extract64(i, 36, 4) << 52;
+ o |= extract64(i, 16, 4) << 56;
+ o |= extract64(i, 60, 4) << 60;
+
+ return o;
+}
+
+static uint64_t pac_cell_inv_shuffle(uint64_t i)
+{
+ uint64_t o = 0;
+
+ o |= extract64(i, 12, 4);
+ o |= extract64(i, 24, 4) << 4;
+ o |= extract64(i, 48, 4) << 8;
+ o |= extract64(i, 36, 4) << 12;
+
+ o |= extract64(i, 56, 4) << 16;
+ o |= extract64(i, 44, 4) << 20;
+ o |= extract64(i, 4, 4) << 24;
+ o |= extract64(i, 16, 4) << 28;
+
+ o |= i & MAKE_64BIT_MASK(32, 4);
+ o |= extract64(i, 52, 4) << 36;
+ o |= extract64(i, 28, 4) << 40;
+ o |= extract64(i, 8, 4) << 44;
+
+ o |= extract64(i, 20, 4) << 48;
+ o |= extract64(i, 0, 4) << 52;
+ o |= extract64(i, 40, 4) << 56;
+ o |= i & MAKE_64BIT_MASK(60, 4);
+
+ return o;
+}
+
+static uint64_t pac_sub(uint64_t i)
+{
+ static const uint8_t sub[16] = {
+ 0xb, 0x6, 0x8, 0xf, 0xc, 0x0, 0x9, 0xe,
+ 0x3, 0x7, 0x4, 0x5, 0xd, 0x2, 0x1, 0xa,
+ };
+ uint64_t o = 0;
+ int b;
+
+ for (b = 0; b < 64; b += 16) {
+ o |= (uint64_t)sub[(i >> b) & 0xf] << b;
+ }
+ return o;
+}
+
+static uint64_t pac_inv_sub(uint64_t i)
+{
+ static const uint8_t inv_sub[16] = {
+ 0x5, 0xe, 0xd, 0x8, 0xa, 0xb, 0x1, 0x9,
+ 0x2, 0x6, 0xf, 0x0, 0x4, 0xc, 0x7, 0x3,
+ };
+ uint64_t o = 0;
+ int b;
+
+ for (b = 0; b < 64; b += 16) {
+ o |= (uint64_t)inv_sub[(i >> b) & 0xf] << b;
+ }
+ return o;
+}
+
+static int rot_cell(int cell, int n)
+{
+ /* 4-bit rotate left by n. */
+ cell |= cell << 4;
+ return extract32(cell, 4 - n, 4);
+}
+
+static uint64_t pac_mult(uint64_t i)
+{
+ uint64_t o = 0;
+ int b;
+
+ for (b = 0; b < 4 * 4; b += 4) {
+ int i0, i4, i8, ic, t0, t1, t2, t3;
+
+ i0 = extract64(i, b, 4);
+ i4 = extract64(i, b + 4 * 4, 4);
+ i8 = extract64(i, b + 8 * 4, 4);
+ ic = extract64(i, b + 12 * 4, 4);
+
+ t0 = rot_cell(i8, 1) ^ rot_cell(i4, 2) ^ rot_cell(i0, 1);
+ t1 = rot_cell(ic, 1) ^ rot_cell(i4, 1) ^ rot_cell(i0, 2);
+ t2 = rot_cell(ic, 2) ^ rot_cell(i8, 1) ^ rot_cell(i0, 1);
+ t3 = rot_cell(ic, 1) ^ rot_cell(i8, 2) ^ rot_cell(i4, 1);
+
+ o |= (uint64_t)t3 << b;
+ o |= (uint64_t)t2 << (b + 4 * 4);
+ o |= (uint64_t)t1 << (b + 8 * 4);
+ o |= (uint64_t)t0 << (b + 12 * 4);
+ }
+ return o;
+}
+
+static uint64_t tweak_cell_rot(uint64_t cell)
+{
+ return (cell >> 1) | (((cell ^ (cell >> 1)) & 1) << 3);
+}
+
+static uint64_t tweak_shuffle(uint64_t i)
+{
+ uint64_t o = 0;
+
+ o |= extract64(i, 16, 4) << 0;
+ o |= extract64(i, 20, 4) << 4;
+ o |= tweak_cell_rot(extract64(i, 24, 4)) << 8;
+ o |= extract64(i, 28, 4) << 12;
+
+ o |= tweak_cell_rot(extract64(i, 44, 4)) << 16;
+ o |= extract64(i, 8, 4) << 20;
+ o |= extract64(i, 12, 4) << 24;
+ o |= tweak_cell_rot(extract64(i, 32, 4)) << 28;
+
+ o |= extract64(i, 48, 4) << 32;
+ o |= extract64(i, 52, 4) << 36;
+ o |= extract64(i, 56, 4) << 40;
+ o |= tweak_cell_rot(extract64(i, 60, 4)) << 44;
+
+ o |= tweak_cell_rot(extract64(i, 0, 4)) << 48;
+ o |= extract64(i, 4, 4) << 52;
+ o |= tweak_cell_rot(extract64(i, 40, 4)) << 56;
+ o |= tweak_cell_rot(extract64(i, 36, 4)) << 60;
+
+ return o;
+}
+
+static uint64_t tweak_cell_inv_rot(uint64_t cell)
+{
+ return ((cell << 1) & 0xf) | ((cell & 1) ^ (cell >> 3));
+}
+
+static uint64_t tweak_inv_shuffle(uint64_t i)
+{
+ uint64_t o = 0;
+
+ o |= tweak_cell_inv_rot(extract64(i, 48, 4));
+ o |= extract64(i, 52, 4) << 4;
+ o |= extract64(i, 20, 4) << 8;
+ o |= extract64(i, 24, 4) << 12;
+
+ o |= extract64(i, 0, 4) << 16;
+ o |= extract64(i, 4, 4) << 20;
+ o |= tweak_cell_inv_rot(extract64(i, 8, 4)) << 24;
+ o |= extract64(i, 12, 4) << 28;
+
+ o |= tweak_cell_inv_rot(extract64(i, 28, 4)) << 32;
+ o |= tweak_cell_inv_rot(extract64(i, 60, 4)) << 36;
+ o |= tweak_cell_inv_rot(extract64(i, 56, 4)) << 40;
+ o |= tweak_cell_inv_rot(extract64(i, 16, 4)) << 44;
+
+ o |= extract64(i, 32, 4) << 48;
+ o |= extract64(i, 36, 4) << 52;
+ o |= extract64(i, 40, 4) << 56;
+ o |= tweak_cell_inv_rot(extract64(i, 44, 4)) << 60;
+
+ return o;
+}
+
+static uint64_t pauth_computepac(uint64_t data, uint64_t modifier,
+ ARMPACKey key)
+{
+ static const uint64_t RC[5] = {
+ 0x0000000000000000ull,
+ 0x13198A2E03707344ull,
+ 0xA4093822299F31D0ull,
+ 0x082EFA98EC4E6C89ull,
+ 0x452821E638D01377ull,
+ };
+ const uint64_t alpha = 0xC0AC29B7C97C50DDull;
+ /*
+ * Note that in the ARM pseudocode, key0 contains bits <127:64>
+ * and key1 contains bits <63:0> of the 128-bit key.
+ */
+ uint64_t key0 = key.hi, key1 = key.lo;
+ uint64_t workingval, runningmod, roundkey, modk0;
+ int i;
+
+ modk0 = (key0 << 63) | ((key0 >> 1) ^ (key0 >> 63));
+ runningmod = modifier;
+ workingval = data ^ key0;
+
+ for (i = 0; i <= 4; ++i) {
+ roundkey = key1 ^ runningmod;
+ workingval ^= roundkey;
+ workingval ^= RC[i];
+ if (i > 0) {
+ workingval = pac_cell_shuffle(workingval);
+ workingval = pac_mult(workingval);
+ }
+ workingval = pac_sub(workingval);
+ runningmod = tweak_shuffle(runningmod);
+ }
+ roundkey = modk0 ^ runningmod;
+ workingval ^= roundkey;
+ workingval = pac_cell_shuffle(workingval);
+ workingval = pac_mult(workingval);
+ workingval = pac_sub(workingval);
+ workingval = pac_cell_shuffle(workingval);
+ workingval = pac_mult(workingval);
+ workingval ^= key1;
+ workingval = pac_cell_inv_shuffle(workingval);
+ workingval = pac_inv_sub(workingval);
+ workingval = pac_mult(workingval);
+ workingval = pac_cell_inv_shuffle(workingval);
+ workingval ^= key0;
+ workingval ^= runningmod;
+ for (i = 0; i <= 4; ++i) {
+ workingval = pac_inv_sub(workingval);
+ if (i < 4) {
+ workingval = pac_mult(workingval);
+ workingval = pac_cell_inv_shuffle(workingval);
+ }
+ runningmod = tweak_inv_shuffle(runningmod);
+ roundkey = key1 ^ runningmod;
+ workingval ^= RC[4 - i];
+ workingval ^= roundkey;
+ workingval ^= alpha;
+ }
+ workingval ^= modk0;
+
+ return workingval;
+}
+
+static uint64_t pauth_addpac(CPUARMState *env, uint64_t ptr, uint64_t modifier,
+ ARMPACKey *key, bool data)
+{
+ ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env);
+ ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data);
+ uint64_t pac, ext_ptr, ext, test;
+ int bot_bit, top_bit;
+
+ /* If tagged pointers are in use, use ptr<55>, otherwise ptr<63>. */
+ if (param.tbi) {
+ ext = sextract64(ptr, 55, 1);
+ } else {
+ ext = sextract64(ptr, 63, 1);
+ }
+
+ /* Build a pointer with known good extension bits. */
+ top_bit = 64 - 8 * param.tbi;
+ bot_bit = 64 - param.tsz;
+ ext_ptr = deposit64(ptr, bot_bit, top_bit - bot_bit, ext);
+
+ pac = pauth_computepac(ext_ptr, modifier, *key);
+
+ /*
+ * Check if the ptr has good extension bits and corrupt the
+ * pointer authentication code if not.
+ */
+ test = sextract64(ptr, bot_bit, top_bit - bot_bit);
+ if (test != 0 && test != -1) {
+ pac ^= MAKE_64BIT_MASK(top_bit - 1, 1);
+ }
+
+ /*
+ * Preserve the determination between upper and lower at bit 55,
+ * and insert pointer authentication code.
+ */
+ if (param.tbi) {
+ ptr &= ~MAKE_64BIT_MASK(bot_bit, 55 - bot_bit + 1);
+ pac &= MAKE_64BIT_MASK(bot_bit, 54 - bot_bit + 1);
+ } else {
+ ptr &= MAKE_64BIT_MASK(0, bot_bit);
+ pac &= ~(MAKE_64BIT_MASK(55, 1) | MAKE_64BIT_MASK(0, bot_bit));
+ }
+ ext &= MAKE_64BIT_MASK(55, 1);
+ return pac | ext | ptr;
+}
+
+static uint64_t pauth_original_ptr(uint64_t ptr, ARMVAParameters param)
+{
+ uint64_t extfield = -param.select;
+ int bot_pac_bit = 64 - param.tsz;
+ int top_pac_bit = 64 - 8 * param.tbi;
+
+ return deposit64(ptr, bot_pac_bit, top_pac_bit - bot_pac_bit, extfield);
+}
+
+static uint64_t pauth_auth(CPUARMState *env, uint64_t ptr, uint64_t modifier,
+ ARMPACKey *key, bool data, int keynumber)
+{
+ ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env);
+ ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data);
+ int bot_bit, top_bit;
+ uint64_t pac, orig_ptr, test;
+
+ orig_ptr = pauth_original_ptr(ptr, param);
+ pac = pauth_computepac(orig_ptr, modifier, *key);
+ bot_bit = 64 - param.tsz;
+ top_bit = 64 - 8 * param.tbi;
+
+ test = (pac ^ ptr) & ~MAKE_64BIT_MASK(55, 1);
+ if (unlikely(extract64(test, bot_bit, top_bit - bot_bit))) {
+ int error_code = (keynumber << 1) | (keynumber ^ 1);
+ if (param.tbi) {
+ return deposit64(ptr, 53, 2, error_code);
+ } else {
+ return deposit64(ptr, 61, 2, error_code);
+ }
+ }
+ return orig_ptr;
+}
+
+static uint64_t pauth_strip(CPUARMState *env, uint64_t ptr, bool data)
+{
+ ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env);
+ ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data);
+
+ return pauth_original_ptr(ptr, param);
+}
+
+static void QEMU_NORETURN pauth_trap(CPUARMState *env, int target_el,
+ uintptr_t ra)
+{
+ raise_exception_ra(env, EXCP_UDEF, syn_pactrap(), target_el, ra);
+}
+
+static void pauth_check_trap(CPUARMState *env, int el, uintptr_t ra)
+{
+ if (el < 2 && arm_feature(env, ARM_FEATURE_EL2)) {
+ uint64_t hcr = arm_hcr_el2_eff(env);
+ bool trap = !(hcr & HCR_API);
+ /* FIXME: ARMv8.1-VHE: trap only applies to EL1&0 regime. */
+ /* FIXME: ARMv8.3-NV: HCR_NV trap takes precedence for ERETA[AB]. */
+ if (trap) {
+ pauth_trap(env, 2, ra);
+ }
+ }
+ if (el < 3 && arm_feature(env, ARM_FEATURE_EL3)) {
+ if (!(env->cp15.scr_el3 & SCR_API)) {
+ pauth_trap(env, 3, ra);
+ }
+ }
+}
+
+static bool pauth_key_enabled(CPUARMState *env, int el, uint32_t bit)
+{
+ uint32_t sctlr;
+ if (el == 0) {
+ /* FIXME: ARMv8.1-VHE S2 translation regime. */
+ sctlr = env->cp15.sctlr_el[1];
+ } else {
+ sctlr = env->cp15.sctlr_el[el];
+ }
+ return (sctlr & bit) != 0;
+}
+
+uint64_t HELPER(pacia)(CPUARMState *env, uint64_t x, uint64_t y)
+{
+ int el = arm_current_el(env);
+ if (!pauth_key_enabled(env, el, SCTLR_EnIA)) {
+ return x;
+ }
+ pauth_check_trap(env, el, GETPC());
+ return pauth_addpac(env, x, y, &env->apia_key, false);
+}
+
+uint64_t HELPER(pacib)(CPUARMState *env, uint64_t x, uint64_t y)
+{
+ int el = arm_current_el(env);
+ if (!pauth_key_enabled(env, el, SCTLR_EnIB)) {
+ return x;
+ }
+ pauth_check_trap(env, el, GETPC());
+ return pauth_addpac(env, x, y, &env->apib_key, false);
+}
+
+uint64_t HELPER(pacda)(CPUARMState *env, uint64_t x, uint64_t y)
+{
+ int el = arm_current_el(env);
+ if (!pauth_key_enabled(env, el, SCTLR_EnDA)) {
+ return x;
+ }
+ pauth_check_trap(env, el, GETPC());
+ return pauth_addpac(env, x, y, &env->apda_key, true);
+}
+
+uint64_t HELPER(pacdb)(CPUARMState *env, uint64_t x, uint64_t y)
+{
+ int el = arm_current_el(env);
+ if (!pauth_key_enabled(env, el, SCTLR_EnDB)) {
+ return x;
+ }
+ pauth_check_trap(env, el, GETPC());
+ return pauth_addpac(env, x, y, &env->apdb_key, true);
+}
+
+uint64_t HELPER(pacga)(CPUARMState *env, uint64_t x, uint64_t y)
+{
+ uint64_t pac;
+
+ pauth_check_trap(env, arm_current_el(env), GETPC());
+ pac = pauth_computepac(x, y, env->apga_key);
+
+ return pac & 0xffffffff00000000ull;
+}
+
+uint64_t HELPER(autia)(CPUARMState *env, uint64_t x, uint64_t y)
+{
+ int el = arm_current_el(env);
+ if (!pauth_key_enabled(env, el, SCTLR_EnIA)) {
+ return x;
+ }
+ pauth_check_trap(env, el, GETPC());
+ return pauth_auth(env, x, y, &env->apia_key, false, 0);
+}
+
+uint64_t HELPER(autib)(CPUARMState *env, uint64_t x, uint64_t y)
+{
+ int el = arm_current_el(env);
+ if (!pauth_key_enabled(env, el, SCTLR_EnIB)) {
+ return x;
+ }
+ pauth_check_trap(env, el, GETPC());
+ return pauth_auth(env, x, y, &env->apib_key, false, 1);
+}
+
+uint64_t HELPER(autda)(CPUARMState *env, uint64_t x, uint64_t y)
+{
+ int el = arm_current_el(env);
+ if (!pauth_key_enabled(env, el, SCTLR_EnDA)) {
+ return x;
+ }
+ pauth_check_trap(env, el, GETPC());
+ return pauth_auth(env, x, y, &env->apda_key, true, 0);
+}
+
+uint64_t HELPER(autdb)(CPUARMState *env, uint64_t x, uint64_t y)
+{
+ int el = arm_current_el(env);
+ if (!pauth_key_enabled(env, el, SCTLR_EnDB)) {
+ return x;
+ }
+ pauth_check_trap(env, el, GETPC());
+ return pauth_auth(env, x, y, &env->apdb_key, true, 1);
+}
+
+uint64_t HELPER(xpaci)(CPUARMState *env, uint64_t a)
+{
+ return pauth_strip(env, a, false);
+}
+
+uint64_t HELPER(xpacd)(CPUARMState *env, uint64_t a)
+{
+ return pauth_strip(env, a, true);
+}
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index b7b6ab6371..4d28a27c3b 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -261,7 +261,7 @@ void gen_a64_set_pc_im(uint64_t val)
/* Load the PC from a generic TCG variable.
*
* If address tagging is enabled via the TCR TBI bits, then loading
- * an address into the PC will clear out any tag in the it:
+ * an address into the PC will clear out any tag in it:
* + for EL2 and EL3 there is only one TBI bit, and if it is set
* then the address is zero-extended, clearing bits [63:56]
* + for EL0 and EL1, TBI0 controls addresses with bit 55 == 0
@@ -276,56 +276,38 @@ void gen_a64_set_pc_im(uint64_t val)
*/
static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src)
{
+ /* Note that TBII is TBI1:TBI0. */
+ int tbi = s->tbii;
if (s->current_el <= 1) {
- /* Test if NEITHER or BOTH TBI values are set. If so, no need to
- * examine bit 55 of address, can just generate code.
- * If mixed, then test via generated code
- */
- if (s->tbi0 && s->tbi1) {
- TCGv_i64 tmp_reg = tcg_temp_new_i64();
- /* Both bits set, sign extension from bit 55 into [63:56] will
- * cover both cases
- */
- tcg_gen_shli_i64(tmp_reg, src, 8);
- tcg_gen_sari_i64(cpu_pc, tmp_reg, 8);
- tcg_temp_free_i64(tmp_reg);
- } else if (!s->tbi0 && !s->tbi1) {
- /* Neither bit set, just load it as-is */
- tcg_gen_mov_i64(cpu_pc, src);
- } else {
- TCGv_i64 tcg_tmpval = tcg_temp_new_i64();
- TCGv_i64 tcg_bit55 = tcg_temp_new_i64();
- TCGv_i64 tcg_zero = tcg_const_i64(0);
-
- tcg_gen_andi_i64(tcg_bit55, src, (1ull << 55));
-
- if (s->tbi0) {
- /* tbi0==1, tbi1==0, so 0-fill upper byte if bit 55 = 0 */
- tcg_gen_andi_i64(tcg_tmpval, src,
- 0x00FFFFFFFFFFFFFFull);
- tcg_gen_movcond_i64(TCG_COND_EQ, cpu_pc, tcg_bit55, tcg_zero,
- tcg_tmpval, src);
- } else {
- /* tbi0==0, tbi1==1, so 1-fill upper byte if bit 55 = 1 */
- tcg_gen_ori_i64(tcg_tmpval, src,
- 0xFF00000000000000ull);
- tcg_gen_movcond_i64(TCG_COND_NE, cpu_pc, tcg_bit55, tcg_zero,
- tcg_tmpval, src);
+ if (tbi != 0) {
+ /* Sign-extend from bit 55. */
+ tcg_gen_sextract_i64(cpu_pc, src, 0, 56);
+
+ if (tbi != 3) {
+ TCGv_i64 tcg_zero = tcg_const_i64(0);
+
+ /*
+ * The two TBI bits differ.
+ * If tbi0, then !tbi1: only use the extension if positive.
+ * if !tbi0, then tbi1: only use the extension if negative.
+ */
+ tcg_gen_movcond_i64(tbi == 1 ? TCG_COND_GE : TCG_COND_LT,
+ cpu_pc, cpu_pc, tcg_zero, cpu_pc, src);
+ tcg_temp_free_i64(tcg_zero);
}
- tcg_temp_free_i64(tcg_zero);
- tcg_temp_free_i64(tcg_bit55);
- tcg_temp_free_i64(tcg_tmpval);
+ return;
}
- } else { /* EL > 1 */
- if (s->tbi0) {
+ } else {
+ if (tbi != 0) {
/* Force tag byte to all zero */
- tcg_gen_andi_i64(cpu_pc, src, 0x00FFFFFFFFFFFFFFull);
- } else {
- /* Load unmodified address */
- tcg_gen_mov_i64(cpu_pc, src);
+ tcg_gen_extract_i64(cpu_pc, src, 0, 56);
+ return;
}
}
+
+ /* Load unmodified address */
+ tcg_gen_mov_i64(cpu_pc, src);
}
typedef struct DisasCompare64 {
@@ -1471,33 +1453,102 @@ static void handle_hint(DisasContext *s, uint32_t insn,
}
switch (selector) {
- case 0: /* NOP */
- return;
- case 3: /* WFI */
+ case 0b00000: /* NOP */
+ break;
+ case 0b00011: /* WFI */
s->base.is_jmp = DISAS_WFI;
- return;
+ break;
+ case 0b00001: /* YIELD */
/* When running in MTTCG we don't generate jumps to the yield and
* WFE helpers as it won't affect the scheduling of other vCPUs.
* If we wanted to more completely model WFE/SEV so we don't busy
* spin unnecessarily we would need to do something more involved.
*/
- case 1: /* YIELD */
if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) {
s->base.is_jmp = DISAS_YIELD;
}
- return;
- case 2: /* WFE */
+ break;
+ case 0b00010: /* WFE */
if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) {
s->base.is_jmp = DISAS_WFE;
}
- return;
- case 4: /* SEV */
- case 5: /* SEVL */
+ break;
+ case 0b00100: /* SEV */
+ case 0b00101: /* SEVL */
/* we treat all as NOP at least for now */
- return;
+ break;
+ case 0b00111: /* XPACLRI */
+ if (s->pauth_active) {
+ gen_helper_xpaci(cpu_X[30], cpu_env, cpu_X[30]);
+ }
+ break;
+ case 0b01000: /* PACIA1716 */
+ if (s->pauth_active) {
+ gen_helper_pacia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]);
+ }
+ break;
+ case 0b01010: /* PACIB1716 */
+ if (s->pauth_active) {
+ gen_helper_pacib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]);
+ }
+ break;
+ case 0b01100: /* AUTIA1716 */
+ if (s->pauth_active) {
+ gen_helper_autia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]);
+ }
+ break;
+ case 0b01110: /* AUTIB1716 */
+ if (s->pauth_active) {
+ gen_helper_autib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]);
+ }
+ break;
+ case 0b11000: /* PACIAZ */
+ if (s->pauth_active) {
+ gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30],
+ new_tmp_a64_zero(s));
+ }
+ break;
+ case 0b11001: /* PACIASP */
+ if (s->pauth_active) {
+ gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]);
+ }
+ break;
+ case 0b11010: /* PACIBZ */
+ if (s->pauth_active) {
+ gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30],
+ new_tmp_a64_zero(s));
+ }
+ break;
+ case 0b11011: /* PACIBSP */
+ if (s->pauth_active) {
+ gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]);
+ }
+ break;
+ case 0b11100: /* AUTIAZ */
+ if (s->pauth_active) {
+ gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30],
+ new_tmp_a64_zero(s));
+ }
+ break;
+ case 0b11101: /* AUTIASP */
+ if (s->pauth_active) {
+ gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]);
+ }
+ break;
+ case 0b11110: /* AUTIBZ */
+ if (s->pauth_active) {
+ gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30],
+ new_tmp_a64_zero(s));
+ }
+ break;
+ case 0b11111: /* AUTIBSP */
+ if (s->pauth_active) {
+ gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]);
+ }
+ break;
default:
/* default specified as NOP equivalent */
- return;
+ break;
}
}
@@ -1912,6 +1963,8 @@ static void disas_exc(DisasContext *s, uint32_t insn)
static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
{
unsigned int opc, op2, op3, rn, op4;
+ TCGv_i64 dst;
+ TCGv_i64 modifier;
opc = extract32(insn, 21, 4);
op2 = extract32(insn, 16, 5);
@@ -1919,44 +1972,152 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
rn = extract32(insn, 5, 5);
op4 = extract32(insn, 0, 5);
- if (op4 != 0x0 || op3 != 0x0 || op2 != 0x1f) {
- unallocated_encoding(s);
- return;
+ if (op2 != 0x1f) {
+ goto do_unallocated;
}
switch (opc) {
case 0: /* BR */
case 1: /* BLR */
case 2: /* RET */
- gen_a64_set_pc(s, cpu_reg(s, rn));
+ switch (op3) {
+ case 0:
+ /* BR, BLR, RET */
+ if (op4 != 0) {
+ goto do_unallocated;
+ }
+ dst = cpu_reg(s, rn);
+ break;
+
+ case 2:
+ case 3:
+ if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ if (opc == 2) {
+ /* RETAA, RETAB */
+ if (rn != 0x1f || op4 != 0x1f) {
+ goto do_unallocated;
+ }
+ rn = 30;
+ modifier = cpu_X[31];
+ } else {
+ /* BRAAZ, BRABZ, BLRAAZ, BLRABZ */
+ if (op4 != 0x1f) {
+ goto do_unallocated;
+ }
+ modifier = new_tmp_a64_zero(s);
+ }
+ if (s->pauth_active) {
+ dst = new_tmp_a64(s);
+ if (op3 == 2) {
+ gen_helper_autia(dst, cpu_env, cpu_reg(s, rn), modifier);
+ } else {
+ gen_helper_autib(dst, cpu_env, cpu_reg(s, rn), modifier);
+ }
+ } else {
+ dst = cpu_reg(s, rn);
+ }
+ break;
+
+ default:
+ goto do_unallocated;
+ }
+
+ gen_a64_set_pc(s, dst);
/* BLR also needs to load return address */
if (opc == 1) {
tcg_gen_movi_i64(cpu_reg(s, 30), s->pc);
}
break;
+
+ case 8: /* BRAA */
+ case 9: /* BLRAA */
+ if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ if (op3 != 2 || op3 != 3) {
+ goto do_unallocated;
+ }
+ if (s->pauth_active) {
+ dst = new_tmp_a64(s);
+ modifier = cpu_reg_sp(s, op4);
+ if (op3 == 2) {
+ gen_helper_autia(dst, cpu_env, cpu_reg(s, rn), modifier);
+ } else {
+ gen_helper_autib(dst, cpu_env, cpu_reg(s, rn), modifier);
+ }
+ } else {
+ dst = cpu_reg(s, rn);
+ }
+ gen_a64_set_pc(s, dst);
+ /* BLRAA also needs to load return address */
+ if (opc == 9) {
+ tcg_gen_movi_i64(cpu_reg(s, 30), s->pc);
+ }
+ break;
+
case 4: /* ERET */
if (s->current_el == 0) {
- unallocated_encoding(s);
- return;
+ goto do_unallocated;
+ }
+ switch (op3) {
+ case 0: /* ERET */
+ if (op4 != 0) {
+ goto do_unallocated;
+ }
+ dst = tcg_temp_new_i64();
+ tcg_gen_ld_i64(dst, cpu_env,
+ offsetof(CPUARMState, elr_el[s->current_el]));
+ break;
+
+ case 2: /* ERETAA */
+ case 3: /* ERETAB */
+ if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ if (rn != 0x1f || op4 != 0x1f) {
+ goto do_unallocated;
+ }
+ dst = tcg_temp_new_i64();
+ tcg_gen_ld_i64(dst, cpu_env,
+ offsetof(CPUARMState, elr_el[s->current_el]));
+ if (s->pauth_active) {
+ modifier = cpu_X[31];
+ if (op3 == 2) {
+ gen_helper_autia(dst, cpu_env, dst, modifier);
+ } else {
+ gen_helper_autib(dst, cpu_env, dst, modifier);
+ }
+ }
+ break;
+
+ default:
+ goto do_unallocated;
}
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
gen_io_start();
}
- gen_helper_exception_return(cpu_env);
+
+ gen_helper_exception_return(cpu_env, dst);
+ tcg_temp_free_i64(dst);
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
gen_io_end();
}
/* Must exit loop to check un-masked IRQs */
s->base.is_jmp = DISAS_EXIT;
return;
+
case 5: /* DRPS */
- if (rn != 0x1f) {
- unallocated_encoding(s);
+ if (op3 != 0 || op4 != 0 || rn != 0x1f) {
+ goto do_unallocated;
} else {
unsupported_encoding(s, insn);
}
return;
+
default:
+ do_unallocated:
unallocated_encoding(s);
return;
}
@@ -2967,6 +3128,64 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn,
s->be_data | size | MO_ALIGN);
}
+/*
+ * PAC memory operations
+ *
+ * 31 30 27 26 24 22 21 12 11 10 5 0
+ * +------+-------+---+-----+-----+---+--------+---+---+----+-----+
+ * | size | 1 1 1 | V | 0 0 | M S | 1 | imm9 | W | 1 | Rn | Rt |
+ * +------+-------+---+-----+-----+---+--------+---+---+----+-----+
+ *
+ * Rt: the result register
+ * Rn: base address or SP
+ * V: vector flag (always 0 as of v8.3)
+ * M: clear for key DA, set for key DB
+ * W: pre-indexing flag
+ * S: sign for imm9.
+ */
+static void disas_ldst_pac(DisasContext *s, uint32_t insn,
+ int size, int rt, bool is_vector)
+{
+ int rn = extract32(insn, 5, 5);
+ bool is_wback = extract32(insn, 11, 1);
+ bool use_key_a = !extract32(insn, 23, 1);
+ int offset;
+ TCGv_i64 tcg_addr, tcg_rt;
+
+ if (size != 3 || is_vector || !dc_isar_feature(aa64_pauth, s)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (rn == 31) {
+ gen_check_sp_alignment(s);
+ }
+ tcg_addr = read_cpu_reg_sp(s, rn, 1);
+
+ if (s->pauth_active) {
+ if (use_key_a) {
+ gen_helper_autda(tcg_addr, cpu_env, tcg_addr, cpu_X[31]);
+ } else {
+ gen_helper_autdb(tcg_addr, cpu_env, tcg_addr, cpu_X[31]);
+ }
+ }
+
+ /* Form the 10-bit signed, scaled offset. */
+ offset = (extract32(insn, 22, 1) << 9) | extract32(insn, 12, 9);
+ offset = sextract32(offset << size, 0, 10 + size);
+ tcg_gen_addi_i64(tcg_addr, tcg_addr, offset);
+
+ tcg_rt = cpu_reg(s, rt);
+
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, /* is_signed */ false,
+ /* extend */ false, /* iss_valid */ !is_wback,
+ /* iss_srt */ rt, /* iss_sf */ true, /* iss_ar */ false);
+
+ if (is_wback) {
+ tcg_gen_mov_i64(cpu_reg_sp(s, rn), tcg_addr);
+ }
+}
+
/* Load/store register (all forms) */
static void disas_ldst_reg(DisasContext *s, uint32_t insn)
{
@@ -2992,6 +3211,9 @@ static void disas_ldst_reg(DisasContext *s, uint32_t insn)
case 2:
disas_ldst_reg_roffset(s, insn, opc, size, rt, is_vector);
return;
+ default:
+ disas_ldst_pac(s, insn, size, rt, is_vector);
+ return;
}
break;
case 1:
@@ -4494,38 +4716,197 @@ static void handle_rev16(DisasContext *s, unsigned int sf,
*/
static void disas_data_proc_1src(DisasContext *s, uint32_t insn)
{
- unsigned int sf, opcode, rn, rd;
+ unsigned int sf, opcode, opcode2, rn, rd;
+ TCGv_i64 tcg_rd;
- if (extract32(insn, 29, 1) || extract32(insn, 16, 5)) {
+ if (extract32(insn, 29, 1)) {
unallocated_encoding(s);
return;
}
sf = extract32(insn, 31, 1);
opcode = extract32(insn, 10, 6);
+ opcode2 = extract32(insn, 16, 5);
rn = extract32(insn, 5, 5);
rd = extract32(insn, 0, 5);
- switch (opcode) {
- case 0: /* RBIT */
+#define MAP(SF, O2, O1) ((SF) | (O1 << 1) | (O2 << 7))
+
+ switch (MAP(sf, opcode2, opcode)) {
+ case MAP(0, 0x00, 0x00): /* RBIT */
+ case MAP(1, 0x00, 0x00):
handle_rbit(s, sf, rn, rd);
break;
- case 1: /* REV16 */
+ case MAP(0, 0x00, 0x01): /* REV16 */
+ case MAP(1, 0x00, 0x01):
handle_rev16(s, sf, rn, rd);
break;
- case 2: /* REV32 */
+ case MAP(0, 0x00, 0x02): /* REV/REV32 */
+ case MAP(1, 0x00, 0x02):
handle_rev32(s, sf, rn, rd);
break;
- case 3: /* REV64 */
+ case MAP(1, 0x00, 0x03): /* REV64 */
handle_rev64(s, sf, rn, rd);
break;
- case 4: /* CLZ */
+ case MAP(0, 0x00, 0x04): /* CLZ */
+ case MAP(1, 0x00, 0x04):
handle_clz(s, sf, rn, rd);
break;
- case 5: /* CLS */
+ case MAP(0, 0x00, 0x05): /* CLS */
+ case MAP(1, 0x00, 0x05):
handle_cls(s, sf, rn, rd);
break;
+ case MAP(1, 0x01, 0x00): /* PACIA */
+ if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_pacia(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn));
+ } else if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ break;
+ case MAP(1, 0x01, 0x01): /* PACIB */
+ if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_pacib(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn));
+ } else if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ break;
+ case MAP(1, 0x01, 0x02): /* PACDA */
+ if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_pacda(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn));
+ } else if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ break;
+ case MAP(1, 0x01, 0x03): /* PACDB */
+ if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_pacdb(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn));
+ } else if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ break;
+ case MAP(1, 0x01, 0x04): /* AUTIA */
+ if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_autia(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn));
+ } else if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ break;
+ case MAP(1, 0x01, 0x05): /* AUTIB */
+ if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_autib(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn));
+ } else if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ break;
+ case MAP(1, 0x01, 0x06): /* AUTDA */
+ if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_autda(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn));
+ } else if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ break;
+ case MAP(1, 0x01, 0x07): /* AUTDB */
+ if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_autdb(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn));
+ } else if (!dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ break;
+ case MAP(1, 0x01, 0x08): /* PACIZA */
+ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) {
+ goto do_unallocated;
+ } else if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_pacia(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s));
+ }
+ break;
+ case MAP(1, 0x01, 0x09): /* PACIZB */
+ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) {
+ goto do_unallocated;
+ } else if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_pacib(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s));
+ }
+ break;
+ case MAP(1, 0x01, 0x0a): /* PACDZA */
+ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) {
+ goto do_unallocated;
+ } else if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_pacda(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s));
+ }
+ break;
+ case MAP(1, 0x01, 0x0b): /* PACDZB */
+ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) {
+ goto do_unallocated;
+ } else if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_pacdb(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s));
+ }
+ break;
+ case MAP(1, 0x01, 0x0c): /* AUTIZA */
+ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) {
+ goto do_unallocated;
+ } else if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_autia(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s));
+ }
+ break;
+ case MAP(1, 0x01, 0x0d): /* AUTIZB */
+ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) {
+ goto do_unallocated;
+ } else if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_autib(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s));
+ }
+ break;
+ case MAP(1, 0x01, 0x0e): /* AUTDZA */
+ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) {
+ goto do_unallocated;
+ } else if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_autda(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s));
+ }
+ break;
+ case MAP(1, 0x01, 0x0f): /* AUTDZB */
+ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) {
+ goto do_unallocated;
+ } else if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_autdb(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s));
+ }
+ break;
+ case MAP(1, 0x01, 0x10): /* XPACI */
+ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) {
+ goto do_unallocated;
+ } else if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_xpaci(tcg_rd, cpu_env, tcg_rd);
+ }
+ break;
+ case MAP(1, 0x01, 0x11): /* XPACD */
+ if (!dc_isar_feature(aa64_pauth, s) || rn != 31) {
+ goto do_unallocated;
+ } else if (s->pauth_active) {
+ tcg_rd = cpu_reg(s, rd);
+ gen_helper_xpacd(tcg_rd, cpu_env, tcg_rd);
+ }
+ break;
+ default:
+ do_unallocated:
+ unallocated_encoding(s);
+ break;
}
+
+#undef MAP
}
static void handle_div(DisasContext *s, bool is_signed, unsigned int sf,
@@ -4656,6 +5037,13 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn)
case 11: /* RORV */
handle_shift_reg(s, A64_SHIFT_TYPE_ROR, sf, rm, rn, rd);
break;
+ case 12: /* PACGA */
+ if (sf == 0 || !dc_isar_feature(aa64_pauth, s)) {
+ goto do_unallocated;
+ }
+ gen_helper_pacga(cpu_reg(s, rd), cpu_env,
+ cpu_reg(s, rn), cpu_reg_sp(s, rm));
+ break;
case 16:
case 17:
case 18:
@@ -4671,6 +5059,7 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn)
break;
}
default:
+ do_unallocated:
unallocated_encoding(s);
break;
}
@@ -13400,8 +13789,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->condexec_cond = 0;
core_mmu_idx = FIELD_EX32(tb_flags, TBFLAG_ANY, MMUIDX);
dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx);
- dc->tbi0 = FIELD_EX32(tb_flags, TBFLAG_A64, TBI0);
- dc->tbi1 = FIELD_EX32(tb_flags, TBFLAG_A64, TBI1);
+ dc->tbii = FIELD_EX32(tb_flags, TBFLAG_A64, TBII);
dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx);
#if !defined(CONFIG_USER_ONLY)
dc->user = (dc->current_el == 0);
@@ -13409,6 +13797,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->fp_excp_el = FIELD_EX32(tb_flags, TBFLAG_ANY, FPEXC_EL);
dc->sve_excp_el = FIELD_EX32(tb_flags, TBFLAG_A64, SVEEXC_EL);
dc->sve_len = (FIELD_EX32(tb_flags, TBFLAG_A64, ZCR_LEN) + 1) * 16;
+ dc->pauth_active = FIELD_EX32(tb_flags, TBFLAG_A64, PAUTH_ACTIVE);
dc->vec_len = 0;
dc->vec_stride = 0;
dc->cp_regs = arm_cpu->cp_regs;
diff --git a/target/arm/translate.h b/target/arm/translate.h
index 1550aa8bc7..bb37d35741 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -26,8 +26,7 @@ typedef struct DisasContext {
int user;
#endif
ARMMMUIdx mmu_idx; /* MMU index to use for normal loads/stores */
- bool tbi0; /* TBI0 for EL0/1 or TBI for EL2/3 */
- bool tbi1; /* TBI1 for EL0/1, not used for EL2/3 */
+ uint8_t tbii; /* TBI1|TBI0 for EL0/1 or TBI for EL2/3 */
bool ns; /* Use non-secure CPREG bank on access */
int fp_excp_el; /* FP exception EL or 0 if enabled */
int sve_excp_el; /* SVE exception EL or 0 if enabled */
@@ -68,6 +67,8 @@ typedef struct DisasContext {
bool is_ldex;
/* True if a single-step exception will be taken to the current EL */
bool ss_same_el;
+ /* True if v8.3-PAuth is active. */
+ bool pauth_active;
/* Bottom two bits of XScale c15_cpar coprocessor access control reg */
int c15_cpar;
/* TCG op of the current insn_start. */
diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c
index 9b546a2c18..5596cd5485 100644
--- a/target/microblaze/cpu.c
+++ b/target/microblaze/cpu.c
@@ -202,7 +202,11 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp)
(cpu->cfg.use_barrel ? PVR2_USE_BARREL_MASK : 0) |
(cpu->cfg.use_div ? PVR2_USE_DIV_MASK : 0) |
(cpu->cfg.use_msr_instr ? PVR2_USE_MSR_INSTR : 0) |
- (cpu->cfg.use_pcmp_instr ? PVR2_USE_PCMP_INSTR : 0);
+ (cpu->cfg.use_pcmp_instr ? PVR2_USE_PCMP_INSTR : 0) |
+ (cpu->cfg.dopb_bus_exception ?
+ PVR2_DOPB_BUS_EXC_MASK : 0) |
+ (cpu->cfg.iopb_bus_exception ?
+ PVR2_IOPB_BUS_EXC_MASK : 0);
env->pvr.regs[5] |= cpu->cfg.dcache_writeback ?
PVR5_DCACHE_WRITEBACK_MASK : 0;
@@ -265,6 +269,12 @@ static Property mb_properties[] = {
DEFINE_PROP_BOOL("dcache-writeback", MicroBlazeCPU, cfg.dcache_writeback,
false),
DEFINE_PROP_BOOL("endianness", MicroBlazeCPU, cfg.endi, false),
+ /* Enables bus exceptions on failed data accesses (load/stores). */
+ DEFINE_PROP_BOOL("dopb-bus-exception", MicroBlazeCPU,
+ cfg.dopb_bus_exception, false),
+ /* Enables bus exceptions on failed instruction fetches. */
+ DEFINE_PROP_BOOL("iopb-bus-exception", MicroBlazeCPU,
+ cfg.iopb_bus_exception, false),
DEFINE_PROP_STRING("version", MicroBlazeCPU, cfg.version),
DEFINE_PROP_UINT8("pvr", MicroBlazeCPU, cfg.pvr, C_PVR_FULL),
DEFINE_PROP_END_OF_LIST(),
@@ -297,7 +307,7 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data)
#ifdef CONFIG_USER_ONLY
cc->handle_mmu_fault = mb_cpu_handle_mmu_fault;
#else
- cc->do_unassigned_access = mb_cpu_unassigned_access;
+ cc->do_transaction_failed = mb_cpu_transaction_failed;
cc->get_phys_page_debug = mb_cpu_get_phys_page_debug;
#endif
dc->vmsd = &vmstate_mb_cpu;
diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h
index 3c4e0ba80a..792bbc97c7 100644
--- a/target/microblaze/cpu.h
+++ b/target/microblaze/cpu.h
@@ -308,6 +308,8 @@ struct MicroBlazeCPU {
bool use_mmu;
bool dcache_writeback;
bool endi;
+ bool dopb_bus_exception;
+ bool iopb_bus_exception;
char *version;
uint8_t pvr;
} cfg;
@@ -388,9 +390,10 @@ static inline void cpu_get_tb_cpu_state(CPUMBState *env, target_ulong *pc,
}
#if !defined(CONFIG_USER_ONLY)
-void mb_cpu_unassigned_access(CPUState *cpu, hwaddr addr,
- bool is_write, bool is_exec, int is_asi,
- unsigned size);
+void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
+ unsigned size, MMUAccessType access_type,
+ int mmu_idx, MemTxAttrs attrs,
+ MemTxResult response, uintptr_t retaddr);
#endif
#endif
diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c
index 7cdbbcccae..e23dcfdc20 100644
--- a/target/microblaze/op_helper.c
+++ b/target/microblaze/op_helper.c
@@ -486,26 +486,28 @@ void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v)
mmu_write(env, ext, rn, v);
}
-void mb_cpu_unassigned_access(CPUState *cs, hwaddr addr,
- bool is_write, bool is_exec, int is_asi,
- unsigned size)
+void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
+ unsigned size, MMUAccessType access_type,
+ int mmu_idx, MemTxAttrs attrs,
+ MemTxResult response, uintptr_t retaddr)
{
MicroBlazeCPU *cpu;
CPUMBState *env;
-
- qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n",
- addr, is_write ? 1 : 0, is_exec ? 1 : 0);
- if (cs == NULL) {
- return;
- }
+ qemu_log_mask(CPU_LOG_INT, "Transaction failed: vaddr 0x%" VADDR_PRIx
+ " physaddr 0x" TARGET_FMT_plx " size %d access type %s\n",
+ addr, physaddr, size,
+ access_type == MMU_INST_FETCH ? "INST_FETCH" :
+ (access_type == MMU_DATA_LOAD ? "DATA_LOAD" : "DATA_STORE"));
cpu = MICROBLAZE_CPU(cs);
env = &cpu->env;
+
+ cpu_restore_state(cs, retaddr, true);
if (!(env->sregs[SR_MSR] & MSR_EE)) {
return;
}
env->sregs[SR_EAR] = addr;
- if (is_exec) {
+ if (access_type == MMU_INST_FETCH) {
if ((env->pvr.regs[2] & PVR2_IOPB_BUS_EXC_MASK)) {
env->sregs[SR_ESR] = ESR_EC_INSN_BUS;
helper_raise_exception(env, EXCP_HW_EXCP);
diff --git a/target/mips/cpu.h b/target/mips/cpu.h
index 03c03fd8c6..c4da7dfbfd 100644
--- a/target/mips/cpu.h
+++ b/target/mips/cpu.h
@@ -123,87 +123,6 @@ typedef struct mips_def_t mips_def_t;
#define MIPS_KSCRATCH_NUM 6
#define MIPS_MAAR_MAX 16 /* Must be an even number. */
-typedef struct TCState TCState;
-struct TCState {
- target_ulong gpr[32];
- target_ulong PC;
- target_ulong HI[MIPS_DSP_ACC];
- target_ulong LO[MIPS_DSP_ACC];
- target_ulong ACX[MIPS_DSP_ACC];
- target_ulong DSPControl;
- int32_t CP0_TCStatus;
-#define CP0TCSt_TCU3 31
-#define CP0TCSt_TCU2 30
-#define CP0TCSt_TCU1 29
-#define CP0TCSt_TCU0 28
-#define CP0TCSt_TMX 27
-#define CP0TCSt_RNST 23
-#define CP0TCSt_TDS 21
-#define CP0TCSt_DT 20
-#define CP0TCSt_DA 15
-#define CP0TCSt_A 13
-#define CP0TCSt_TKSU 11
-#define CP0TCSt_IXMT 10
-#define CP0TCSt_TASID 0
- int32_t CP0_TCBind;
-#define CP0TCBd_CurTC 21
-#define CP0TCBd_TBE 17
-#define CP0TCBd_CurVPE 0
- target_ulong CP0_TCHalt;
- target_ulong CP0_TCContext;
- target_ulong CP0_TCSchedule;
- target_ulong CP0_TCScheFBack;
- int32_t CP0_Debug_tcstatus;
- target_ulong CP0_UserLocal;
-
- int32_t msacsr;
-
-#define MSACSR_FS 24
-#define MSACSR_FS_MASK (1 << MSACSR_FS)
-#define MSACSR_NX 18
-#define MSACSR_NX_MASK (1 << MSACSR_NX)
-#define MSACSR_CEF 2
-#define MSACSR_CEF_MASK (0xffff << MSACSR_CEF)
-#define MSACSR_RM 0
-#define MSACSR_RM_MASK (0x3 << MSACSR_RM)
-#define MSACSR_MASK (MSACSR_RM_MASK | MSACSR_CEF_MASK | MSACSR_NX_MASK | \
- MSACSR_FS_MASK)
-
- float_status msa_fp_status;
-
-#define NUMBER_OF_MXU_REGISTERS 16
- target_ulong mxu_gpr[NUMBER_OF_MXU_REGISTERS - 1];
- target_ulong mxu_cr;
-#define MXU_CR_LC 31
-#define MXU_CR_RC 30
-#define MXU_CR_BIAS 2
-#define MXU_CR_RD_EN 1
-#define MXU_CR_MXU_EN 0
-
-};
-
-typedef struct CPUMIPSState CPUMIPSState;
-struct CPUMIPSState {
- TCState active_tc;
- CPUMIPSFPUContext active_fpu;
-
- uint32_t current_tc;
- uint32_t current_fpu;
-
- uint32_t SEGBITS;
- uint32_t PABITS;
-#if defined(TARGET_MIPS64)
-# define PABITS_BASE 36
-#else
-# define PABITS_BASE 32
-#endif
- target_ulong SEGMask;
- uint64_t PAMask;
-#define PAMASK_BASE ((1ULL << PABITS_BASE) - 1)
-
- int32_t msair;
-#define MSAIR_ProcID 8
-#define MSAIR_Rev 0
/*
* Summary of CP0 registers
@@ -245,8 +164,8 @@ struct CPUMIPSState {
* 3 BadInstrX
* 4 GuestCtl1 GuestCtl0Ext
* 5 GuestCtl2
- * 6 GuestCtl3
- * 7
+ * 6 SAARI GuestCtl3
+ * 7 SAAR
*
*
* Register 12 Register 13 Register 14 Register 15
@@ -314,6 +233,240 @@ struct CPUMIPSState {
* 7 TagLo TagHi KScratch<n>
*
*/
+#define CP0_REGISTER_00 0
+#define CP0_REGISTER_01 1
+#define CP0_REGISTER_02 2
+#define CP0_REGISTER_03 3
+#define CP0_REGISTER_04 4
+#define CP0_REGISTER_05 5
+#define CP0_REGISTER_06 6
+#define CP0_REGISTER_07 7
+#define CP0_REGISTER_08 8
+#define CP0_REGISTER_09 9
+#define CP0_REGISTER_10 10
+#define CP0_REGISTER_11 11
+#define CP0_REGISTER_12 12
+#define CP0_REGISTER_13 13
+#define CP0_REGISTER_14 14
+#define CP0_REGISTER_15 15
+#define CP0_REGISTER_16 16
+#define CP0_REGISTER_17 17
+#define CP0_REGISTER_18 18
+#define CP0_REGISTER_19 19
+#define CP0_REGISTER_20 20
+#define CP0_REGISTER_21 21
+#define CP0_REGISTER_22 22
+#define CP0_REGISTER_23 23
+#define CP0_REGISTER_24 24
+#define CP0_REGISTER_25 25
+#define CP0_REGISTER_26 26
+#define CP0_REGISTER_27 27
+#define CP0_REGISTER_28 28
+#define CP0_REGISTER_29 29
+#define CP0_REGISTER_30 30
+#define CP0_REGISTER_31 31
+
+
+/* CP0 Register 00 */
+#define CP0_REG00__INDEX 0
+#define CP0_REG00__VPCONTROL 4
+/* CP0 Register 01 */
+/* CP0 Register 02 */
+#define CP0_REG02__ENTRYLO0 0
+/* CP0 Register 03 */
+#define CP0_REG03__ENTRYLO1 0
+#define CP0_REG03__GLOBALNUM 1
+/* CP0 Register 04 */
+#define CP0_REG04__CONTEXT 0
+#define CP0_REG04__USERLOCAL 2
+#define CP0_REG04__DBGCONTEXTID 4
+#define CP0_REG00__MMID 5
+/* CP0 Register 05 */
+#define CP0_REG05__PAGEMASK 0
+#define CP0_REG05__PAGEGRAIN 1
+/* CP0 Register 06 */
+#define CP0_REG06__WIRED 0
+/* CP0 Register 07 */
+#define CP0_REG07__HWRENA 0
+/* CP0 Register 08 */
+#define CP0_REG08__BADVADDR 0
+#define CP0_REG08__BADINSTR 1
+#define CP0_REG08__BADINSTRP 2
+/* CP0 Register 09 */
+#define CP0_REG09__COUNT 0
+#define CP0_REG09__SAARI 6
+#define CP0_REG09__SAAR 7
+/* CP0 Register 10 */
+#define CP0_REG10__ENTRYHI 0
+#define CP0_REG10__GUESTCTL1 4
+#define CP0_REG10__GUESTCTL2 5
+/* CP0 Register 11 */
+#define CP0_REG11__COMPARE 0
+#define CP0_REG11__GUESTCTL0EXT 4
+/* CP0 Register 12 */
+#define CP0_REG12__STATUS 0
+#define CP0_REG12__INTCTL 1
+#define CP0_REG12__SRSCTL 2
+#define CP0_REG12__GUESTCTL0 6
+#define CP0_REG12__GTOFFSET 7
+/* CP0 Register 13 */
+#define CP0_REG13__CAUSE 0
+/* CP0 Register 14 */
+#define CP0_REG14__EPC 0
+/* CP0 Register 15 */
+#define CP0_REG15__PRID 0
+#define CP0_REG15__EBASE 1
+#define CP0_REG15__CDMMBASE 2
+#define CP0_REG15__CMGCRBASE 3
+/* CP0 Register 16 */
+#define CP0_REG16__CONFIG 0
+#define CP0_REG16__CONFIG1 1
+#define CP0_REG16__CONFIG2 2
+#define CP0_REG16__CONFIG3 3
+#define CP0_REG16__CONFIG4 4
+#define CP0_REG16__CONFIG5 5
+#define CP0_REG00__CONFIG7 7
+/* CP0 Register 17 */
+#define CP0_REG17__LLADDR 0
+#define CP0_REG17__MAAR 1
+#define CP0_REG17__MAARI 2
+/* CP0 Register 18 */
+#define CP0_REG18__WATCHLO0 0
+#define CP0_REG18__WATCHLO1 1
+#define CP0_REG18__WATCHLO2 2
+#define CP0_REG18__WATCHLO3 3
+/* CP0 Register 19 */
+#define CP0_REG19__WATCHHI0 0
+#define CP0_REG19__WATCHHI1 1
+#define CP0_REG19__WATCHHI2 2
+#define CP0_REG19__WATCHHI3 3
+/* CP0 Register 20 */
+#define CP0_REG20__XCONTEXT 0
+/* CP0 Register 21 */
+/* CP0 Register 22 */
+/* CP0 Register 23 */
+#define CP0_REG23__DEBUG 0
+/* CP0 Register 24 */
+#define CP0_REG24__DEPC 0
+/* CP0 Register 25 */
+#define CP0_REG25__PERFCTL0 0
+#define CP0_REG25__PERFCNT0 1
+#define CP0_REG25__PERFCTL1 2
+#define CP0_REG25__PERFCNT1 3
+#define CP0_REG25__PERFCTL2 4
+#define CP0_REG25__PERFCNT2 5
+#define CP0_REG25__PERFCTL3 6
+#define CP0_REG25__PERFCNT3 7
+/* CP0 Register 26 */
+#define CP0_REG00__ERRCTL 0
+/* CP0 Register 27 */
+#define CP0_REG27__CACHERR 0
+/* CP0 Register 28 */
+#define CP0_REG28__ITAGLO 0
+#define CP0_REG28__IDATALO 1
+#define CP0_REG28__DTAGLO 2
+#define CP0_REG28__DDATALO 3
+/* CP0 Register 29 */
+#define CP0_REG29__IDATAHI 1
+#define CP0_REG29__DDATAHI 3
+/* CP0 Register 30 */
+#define CP0_REG30__ERROREPC 0
+/* CP0 Register 31 */
+#define CP0_REG31__DESAVE 0
+#define CP0_REG31__KSCRATCH1 2
+#define CP0_REG31__KSCRATCH2 3
+#define CP0_REG31__KSCRATCH3 4
+#define CP0_REG31__KSCRATCH4 5
+#define CP0_REG31__KSCRATCH5 6
+#define CP0_REG31__KSCRATCH6 7
+
+
+typedef struct TCState TCState;
+struct TCState {
+ target_ulong gpr[32];
+ target_ulong PC;
+ target_ulong HI[MIPS_DSP_ACC];
+ target_ulong LO[MIPS_DSP_ACC];
+ target_ulong ACX[MIPS_DSP_ACC];
+ target_ulong DSPControl;
+ int32_t CP0_TCStatus;
+#define CP0TCSt_TCU3 31
+#define CP0TCSt_TCU2 30
+#define CP0TCSt_TCU1 29
+#define CP0TCSt_TCU0 28
+#define CP0TCSt_TMX 27
+#define CP0TCSt_RNST 23
+#define CP0TCSt_TDS 21
+#define CP0TCSt_DT 20
+#define CP0TCSt_DA 15
+#define CP0TCSt_A 13
+#define CP0TCSt_TKSU 11
+#define CP0TCSt_IXMT 10
+#define CP0TCSt_TASID 0
+ int32_t CP0_TCBind;
+#define CP0TCBd_CurTC 21
+#define CP0TCBd_TBE 17
+#define CP0TCBd_CurVPE 0
+ target_ulong CP0_TCHalt;
+ target_ulong CP0_TCContext;
+ target_ulong CP0_TCSchedule;
+ target_ulong CP0_TCScheFBack;
+ int32_t CP0_Debug_tcstatus;
+ target_ulong CP0_UserLocal;
+
+ int32_t msacsr;
+
+#define MSACSR_FS 24
+#define MSACSR_FS_MASK (1 << MSACSR_FS)
+#define MSACSR_NX 18
+#define MSACSR_NX_MASK (1 << MSACSR_NX)
+#define MSACSR_CEF 2
+#define MSACSR_CEF_MASK (0xffff << MSACSR_CEF)
+#define MSACSR_RM 0
+#define MSACSR_RM_MASK (0x3 << MSACSR_RM)
+#define MSACSR_MASK (MSACSR_RM_MASK | MSACSR_CEF_MASK | MSACSR_NX_MASK | \
+ MSACSR_FS_MASK)
+
+ float_status msa_fp_status;
+
+ /* Upper 64-bit MMRs (multimedia registers); the lower 64-bit are GPRs */
+ uint64_t mmr[32];
+
+#define NUMBER_OF_MXU_REGISTERS 16
+ target_ulong mxu_gpr[NUMBER_OF_MXU_REGISTERS - 1];
+ target_ulong mxu_cr;
+#define MXU_CR_LC 31
+#define MXU_CR_RC 30
+#define MXU_CR_BIAS 2
+#define MXU_CR_RD_EN 1
+#define MXU_CR_MXU_EN 0
+
+};
+
+struct MIPSITUState;
+typedef struct CPUMIPSState CPUMIPSState;
+struct CPUMIPSState {
+ TCState active_tc;
+ CPUMIPSFPUContext active_fpu;
+
+ uint32_t current_tc;
+ uint32_t current_fpu;
+
+ uint32_t SEGBITS;
+ uint32_t PABITS;
+#if defined(TARGET_MIPS64)
+# define PABITS_BASE 36
+#else
+# define PABITS_BASE 32
+#endif
+ target_ulong SEGMask;
+ uint64_t PAMask;
+#define PAMASK_BASE ((1ULL << PABITS_BASE) - 1)
+
+ int32_t msair;
+#define MSAIR_ProcID 8
+#define MSAIR_Rev 0
+
/*
* CP0 Register 0
*/
@@ -386,6 +539,7 @@ struct CPUMIPSState {
*/
target_ulong CP0_Context;
target_ulong CP0_KScratch[MIPS_KSCRATCH_NUM];
+ int32_t CP0_MemoryMapID;
/*
* CP0 Register 5
*/
@@ -511,6 +665,12 @@ struct CPUMIPSState {
* CP0 Register 9
*/
int32_t CP0_Count;
+ uint32_t CP0_SAARI;
+#define CP0SAARI_TARGET 0 /* 5..0 */
+ uint64_t CP0_SAAR[2];
+#define CP0SAAR_BASE 12 /* 43..12 */
+#define CP0SAAR_SIZE 1 /* 5..1 */
+#define CP0SAAR_EN 0
/*
* CP0 Register 10
*/
@@ -860,6 +1020,7 @@ struct CPUMIPSState {
uint32_t CP0_Status_rw_bitmask; /* Read/write bits in CP0_Status */
uint32_t CP0_TCStatus_rw_bitmask; /* Read/write bits in CP0_TCStatus */
uint64_t insn_flags; /* Supported instruction set */
+ int saarp;
/* Fields up to this point are cleared by a CPU reset */
struct {} end_reset_fields;
@@ -875,6 +1036,7 @@ struct CPUMIPSState {
const mips_def_t *cpu_model;
void *irq[8];
QEMUTimer *timer; /* Internal timer */
+ struct MIPSITUState *itu;
MemoryRegion *itc_tag; /* ITC Configuration Tags */
target_ulong exception_base; /* ExceptionBase input to the core */
};
@@ -1017,6 +1179,9 @@ void cpu_set_exception_base(int vp_index, target_ulong address);
/* mips_int.c */
void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level);
+/* mips_itu.c */
+void itc_reconfigure(struct MIPSITUState *tag);
+
/* helper.c */
target_ulong exception_resume_pc (CPUMIPSState *env);
diff --git a/target/mips/helper.h b/target/mips/helper.h
index c23e4e5d97..8872c4647b 100644
--- a/target/mips/helper.h
+++ b/target/mips/helper.h
@@ -65,6 +65,8 @@ DEF_HELPER_1(mftc0_tcschedule, tl, env)
DEF_HELPER_1(mfc0_tcschefback, tl, env)
DEF_HELPER_1(mftc0_tcschefback, tl, env)
DEF_HELPER_1(mfc0_count, tl, env)
+DEF_HELPER_1(mfc0_saar, tl, env)
+DEF_HELPER_1(mfhc0_saar, tl, env)
DEF_HELPER_1(mftc0_entryhi, tl, env)
DEF_HELPER_1(mftc0_status, tl, env)
DEF_HELPER_1(mftc0_cause, tl, env)
@@ -87,6 +89,7 @@ DEF_HELPER_1(dmfc0_tcschefback, tl, env)
DEF_HELPER_1(dmfc0_lladdr, tl, env)
DEF_HELPER_1(dmfc0_maar, tl, env)
DEF_HELPER_2(dmfc0_watchlo, tl, env, i32)
+DEF_HELPER_1(dmfc0_saar, tl, env)
#endif /* TARGET_MIPS64 */
DEF_HELPER_2(mtc0_index, void, env, tl)
@@ -131,6 +134,9 @@ DEF_HELPER_2(mtc0_srsconf4, void, env, tl)
DEF_HELPER_2(mtc0_hwrena, void, env, tl)
DEF_HELPER_2(mtc0_pwctl, void, env, tl)
DEF_HELPER_2(mtc0_count, void, env, tl)
+DEF_HELPER_2(mtc0_saari, void, env, tl)
+DEF_HELPER_2(mtc0_saar, void, env, tl)
+DEF_HELPER_2(mthc0_saar, void, env, tl)
DEF_HELPER_2(mtc0_entryhi, void, env, tl)
DEF_HELPER_2(mttc0_entryhi, void, env, tl)
DEF_HELPER_2(mtc0_compare, void, env, tl)
diff --git a/target/mips/internal.h b/target/mips/internal.h
index 8b1b2456af..8f6fc919d5 100644
--- a/target/mips/internal.h
+++ b/target/mips/internal.h
@@ -61,6 +61,7 @@ struct mips_def_t {
target_ulong CP0_EBaseWG_rw_bitmask;
uint64_t insn_flags;
enum mips_mmu_types mmu_type;
+ int32_t SAARP;
};
extern const struct mips_def_t mips_defs[];
diff --git a/target/mips/machine.c b/target/mips/machine.c
index 704e9c01bf..1341ab1df9 100644
--- a/target/mips/machine.c
+++ b/target/mips/machine.c
@@ -214,8 +214,8 @@ const VMStateDescription vmstate_tlb = {
const VMStateDescription vmstate_mips_cpu = {
.name = "cpu",
- .version_id = 15,
- .minimum_version_id = 15,
+ .version_id = 17,
+ .minimum_version_id = 17,
.post_load = cpu_post_load,
.fields = (VMStateField[]) {
/* Active TC */
@@ -253,6 +253,7 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_UINT64(env.CP0_EntryLo0, MIPSCPU),
VMSTATE_UINT64(env.CP0_EntryLo1, MIPSCPU),
VMSTATE_UINTTL(env.CP0_Context, MIPSCPU),
+ VMSTATE_INT32(env.CP0_MemoryMapID, MIPSCPU),
VMSTATE_INT32(env.CP0_PageMask, MIPSCPU),
VMSTATE_INT32(env.CP0_PageGrain, MIPSCPU),
VMSTATE_UINTTL(env.CP0_SegCtl0, MIPSCPU),
@@ -274,6 +275,8 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_UINT32(env.CP0_BadInstrP, MIPSCPU),
VMSTATE_UINT32(env.CP0_BadInstrX, MIPSCPU),
VMSTATE_INT32(env.CP0_Count, MIPSCPU),
+ VMSTATE_UINT32(env.CP0_SAARI, MIPSCPU),
+ VMSTATE_UINT64_ARRAY(env.CP0_SAAR, MIPSCPU, 2),
VMSTATE_UINTTL(env.CP0_EntryHi, MIPSCPU),
VMSTATE_INT32(env.CP0_Compare, MIPSCPU),
VMSTATE_INT32(env.CP0_Status, MIPSCPU),
diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c
index d1f1d1aa35..aebad24ed6 100644
--- a/target/mips/op_helper.c
+++ b/target/mips/op_helper.c
@@ -938,6 +938,22 @@ target_ulong helper_mfc0_count(CPUMIPSState *env)
return count;
}
+target_ulong helper_mfc0_saar(CPUMIPSState *env)
+{
+ if ((env->CP0_SAARI & 0x3f) < 2) {
+ return (int32_t) env->CP0_SAAR[env->CP0_SAARI & 0x3f];
+ }
+ return 0;
+}
+
+target_ulong helper_mfhc0_saar(CPUMIPSState *env)
+{
+ if ((env->CP0_SAARI & 0x3f) < 2) {
+ return env->CP0_SAAR[env->CP0_SAARI & 0x3f] >> 32;
+ }
+ return 0;
+}
+
target_ulong helper_mftc0_entryhi(CPUMIPSState *env)
{
int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
@@ -1059,6 +1075,14 @@ target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel)
{
return env->CP0_WatchLo[sel];
}
+
+target_ulong helper_dmfc0_saar(CPUMIPSState *env)
+{
+ if ((env->CP0_SAARI & 0x3f) < 2) {
+ return env->CP0_SAAR[env->CP0_SAARI & 0x3f];
+ }
+ return 0;
+}
#endif /* TARGET_MIPS64 */
void helper_mtc0_index(CPUMIPSState *env, target_ulong arg1)
@@ -1598,6 +1622,46 @@ void helper_mtc0_count(CPUMIPSState *env, target_ulong arg1)
qemu_mutex_unlock_iothread();
}
+void helper_mtc0_saari(CPUMIPSState *env, target_ulong arg1)
+{
+ uint32_t target = arg1 & 0x3f;
+ if (target <= 1) {
+ env->CP0_SAARI = target;
+ }
+}
+
+void helper_mtc0_saar(CPUMIPSState *env, target_ulong arg1)
+{
+ uint32_t target = env->CP0_SAARI & 0x3f;
+ if (target < 2) {
+ env->CP0_SAAR[target] = arg1 & 0x00000ffffffff03fULL;
+ switch (target) {
+ case 0:
+ if (env->itu) {
+ itc_reconfigure(env->itu);
+ }
+ break;
+ }
+ }
+}
+
+void helper_mthc0_saar(CPUMIPSState *env, target_ulong arg1)
+{
+ uint32_t target = env->CP0_SAARI & 0x3f;
+ if (target < 2) {
+ env->CP0_SAAR[target] =
+ (((uint64_t) arg1 << 32) & 0x00000fff00000000ULL) |
+ (env->CP0_SAAR[target] & 0x00000000ffffffffULL);
+ switch (target) {
+ case 0:
+ if (env->itu) {
+ itc_reconfigure(env->itu);
+ }
+ break;
+ }
+ }
+}
+
void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1)
{
target_ulong old, val, mask;
diff --git a/target/mips/translate.c b/target/mips/translate.c
index 057aaf9a44..ab307c410c 100644
--- a/target/mips/translate.c
+++ b/target/mips/translate.c
@@ -2455,6 +2455,11 @@ static TCGv_i32 fpu_fcr0, fpu_fcr31;
static TCGv_i64 fpu_f64[32];
static TCGv_i64 msa_wr_d[64];
+#if defined(TARGET_MIPS64)
+/* Upper halves of R5900's 128-bit registers: MMRs (multimedia registers) */
+static TCGv_i64 cpu_mmr[32];
+#endif
+
#if !defined(TARGET_MIPS64)
/* MXU registers */
static TCGv mxu_gpr[NUMBER_OF_MXU_REGISTERS - 1];
@@ -2537,6 +2542,7 @@ typedef struct DisasContext {
bool mrp;
bool nan2008;
bool abs2008;
+ bool saar;
} DisasContext;
#define DISAS_STOP DISAS_TARGET_0
@@ -6567,55 +6573,66 @@ static inline void gen_mtc0_store32 (TCGv arg, target_ulong off)
static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{
- const char *rn = "invalid";
+ const char *register_name = "invalid";
switch (reg) {
- case 2:
+ case CP0_REGISTER_02:
switch (sel) {
case 0:
CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA);
gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0));
- rn = "EntryLo0";
+ register_name = "EntryLo0";
break;
default:
goto cp0_unimplemented;
}
break;
- case 3:
+ case CP0_REGISTER_03:
switch (sel) {
case 0:
CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA);
gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1));
- rn = "EntryLo1";
+ register_name = "EntryLo1";
+ break;
+ default:
+ goto cp0_unimplemented;
+ }
+ break;
+ case CP0_REGISTER_09:
+ switch (sel) {
+ case 7:
+ CP0_CHECK(ctx->saar);
+ gen_helper_mfhc0_saar(arg, cpu_env);
+ register_name = "SAAR";
break;
default:
goto cp0_unimplemented;
}
break;
- case 17:
+ case CP0_REGISTER_17:
switch (sel) {
case 0:
gen_mfhc0_load64(arg, offsetof(CPUMIPSState, lladdr),
ctx->CP0_LLAddr_shift);
- rn = "LLAddr";
+ register_name = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mfhc0_maar(arg, cpu_env);
- rn = "MAAR";
+ register_name = "MAAR";
break;
default:
goto cp0_unimplemented;
}
break;
- case 28:
+ case CP0_REGISTER_28:
switch (sel) {
case 0:
case 2:
case 4:
case 6:
gen_mfhc0_load64(arg, offsetof(CPUMIPSState, CP0_TagLo), 0);
- rn = "TagLo";
+ register_name = "TagLo";
break;
default:
goto cp0_unimplemented;
@@ -6624,63 +6641,74 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
default:
goto cp0_unimplemented;
}
- trace_mips_translate_c0("mfhc0", rn, reg, sel);
+ trace_mips_translate_c0("mfhc0", register_name, reg, sel);
return;
cp0_unimplemented:
- qemu_log_mask(LOG_UNIMP, "mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ qemu_log_mask(LOG_UNIMP, "mfhc0 %s (reg %d sel %d)\n",
+ register_name, reg, sel);
tcg_gen_movi_tl(arg, 0);
}
static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{
- const char *rn = "invalid";
+ const char *register_name = "invalid";
uint64_t mask = ctx->PAMask >> 36;
switch (reg) {
- case 2:
+ case CP0_REGISTER_02:
switch (sel) {
case 0:
CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA);
tcg_gen_andi_tl(arg, arg, mask);
gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0));
- rn = "EntryLo0";
+ register_name = "EntryLo0";
break;
default:
goto cp0_unimplemented;
}
break;
- case 3:
+ case CP0_REGISTER_03:
switch (sel) {
case 0:
CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA);
tcg_gen_andi_tl(arg, arg, mask);
gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1));
- rn = "EntryLo1";
+ register_name = "EntryLo1";
break;
default:
goto cp0_unimplemented;
}
break;
- case 17:
+ case CP0_REGISTER_09:
+ switch (sel) {
+ case 7:
+ CP0_CHECK(ctx->saar);
+ gen_helper_mthc0_saar(cpu_env, arg);
+ register_name = "SAAR";
+ break;
+ default:
+ goto cp0_unimplemented;
+ }
+ case CP0_REGISTER_17:
switch (sel) {
case 0:
/* LLAddr is read-only (the only exception is bit 0 if LLB is
supported); the CP0_LLAddr_rw_bitmask does not seem to be
relevant for modern MIPS cores supporting MTHC0, therefore
treating MTHC0 to LLAddr as NOP. */
- rn = "LLAddr";
+ register_name = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mthc0_maar(cpu_env, arg);
- rn = "MAAR";
+ register_name = "MAAR";
break;
default:
goto cp0_unimplemented;
}
break;
- case 28:
+ case CP0_REGISTER_28:
switch (sel) {
case 0:
case 2:
@@ -6688,7 +6716,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 6:
tcg_gen_andi_tl(arg, arg, mask);
gen_mthc0_store64(arg, offsetof(CPUMIPSState, CP0_TagLo));
- rn = "TagLo";
+ register_name = "TagLo";
break;
default:
goto cp0_unimplemented;
@@ -6697,10 +6725,11 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
default:
goto cp0_unimplemented;
}
- trace_mips_translate_c0("mthc0", rn, reg, sel);
+ trace_mips_translate_c0("mthc0", register_name, reg, sel);
cp0_unimplemented:
- qemu_log_mask(LOG_UNIMP, "mthc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ qemu_log_mask(LOG_UNIMP, "mthc0 %s (reg %d sel %d)\n",
+ register_name, reg, sel);
}
static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg)
@@ -6714,89 +6743,89 @@ static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg)
static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{
- const char *rn = "invalid";
+ const char *register_name = "invalid";
if (sel != 0)
check_insn(ctx, ISA_MIPS32);
switch (reg) {
- case 0:
+ case CP0_REGISTER_00:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Index));
- rn = "Index";
+ register_name = "Index";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_mvpcontrol(arg, cpu_env);
- rn = "MVPControl";
+ register_name = "MVPControl";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_mvpconf0(arg, cpu_env);
- rn = "MVPConf0";
+ register_name = "MVPConf0";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_mvpconf1(arg, cpu_env);
- rn = "MVPConf1";
+ register_name = "MVPConf1";
break;
case 4:
CP0_CHECK(ctx->vp);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPControl));
- rn = "VPControl";
+ register_name = "VPControl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 1:
+ case CP0_REGISTER_01:
switch (sel) {
case 0:
CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6));
gen_helper_mfc0_random(arg, cpu_env);
- rn = "Random";
+ register_name = "Random";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl));
- rn = "VPEControl";
+ register_name = "VPEControl";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0));
- rn = "VPEConf0";
+ register_name = "VPEConf0";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1));
- rn = "VPEConf1";
+ register_name = "VPEConf1";
break;
case 4:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_YQMask));
- rn = "YQMask";
+ register_name = "YQMask";
break;
case 5:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPESchedule));
- rn = "VPESchedule";
+ register_name = "VPESchedule";
break;
case 6:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack));
- rn = "VPEScheFBack";
+ register_name = "VPEScheFBack";
break;
case 7:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt));
- rn = "VPEOpt";
+ register_name = "VPEOpt";
break;
default:
goto cp0_unimplemented;
}
break;
- case 2:
+ case CP0_REGISTER_02:
switch (sel) {
case 0:
{
@@ -6813,48 +6842,48 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_move_low32(arg, tmp);
tcg_temp_free_i64(tmp);
}
- rn = "EntryLo0";
+ register_name = "EntryLo0";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_tcstatus(arg, cpu_env);
- rn = "TCStatus";
+ register_name = "TCStatus";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_tcbind(arg, cpu_env);
- rn = "TCBind";
+ register_name = "TCBind";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_tcrestart(arg, cpu_env);
- rn = "TCRestart";
+ register_name = "TCRestart";
break;
case 4:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_tchalt(arg, cpu_env);
- rn = "TCHalt";
+ register_name = "TCHalt";
break;
case 5:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_tccontext(arg, cpu_env);
- rn = "TCContext";
+ register_name = "TCContext";
break;
case 6:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_tcschedule(arg, cpu_env);
- rn = "TCSchedule";
+ register_name = "TCSchedule";
break;
case 7:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_tcschefback(arg, cpu_env);
- rn = "TCScheFBack";
+ register_name = "TCScheFBack";
break;
default:
goto cp0_unimplemented;
}
break;
- case 3:
+ case CP0_REGISTER_03:
switch (sel) {
case 0:
{
@@ -6871,166 +6900,166 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_move_low32(arg, tmp);
tcg_temp_free_i64(tmp);
}
- rn = "EntryLo1";
+ register_name = "EntryLo1";
break;
case 1:
CP0_CHECK(ctx->vp);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_GlobalNumber));
- rn = "GlobalNumber";
+ register_name = "GlobalNumber";
break;
default:
goto cp0_unimplemented;
}
break;
- case 4:
+ case CP0_REGISTER_04:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_Context));
tcg_gen_ext32s_tl(arg, arg);
- rn = "Context";
+ register_name = "Context";
break;
case 1:
// gen_helper_mfc0_contextconfig(arg); /* SmartMIPS ASE */
- rn = "ContextConfig";
+ register_name = "ContextConfig";
goto cp0_unimplemented;
case 2:
CP0_CHECK(ctx->ulri);
tcg_gen_ld_tl(arg, cpu_env,
offsetof(CPUMIPSState, active_tc.CP0_UserLocal));
tcg_gen_ext32s_tl(arg, arg);
- rn = "UserLocal";
+ register_name = "UserLocal";
break;
default:
goto cp0_unimplemented;
}
break;
- case 5:
+ case CP0_REGISTER_05:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageMask));
- rn = "PageMask";
+ register_name = "PageMask";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageGrain));
- rn = "PageGrain";
+ register_name = "PageGrain";
break;
case 2:
CP0_CHECK(ctx->sc);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl0));
tcg_gen_ext32s_tl(arg, arg);
- rn = "SegCtl0";
+ register_name = "SegCtl0";
break;
case 3:
CP0_CHECK(ctx->sc);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl1));
tcg_gen_ext32s_tl(arg, arg);
- rn = "SegCtl1";
+ register_name = "SegCtl1";
break;
case 4:
CP0_CHECK(ctx->sc);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl2));
tcg_gen_ext32s_tl(arg, arg);
- rn = "SegCtl2";
+ register_name = "SegCtl2";
break;
case 5:
check_pw(ctx);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PWBase));
- rn = "PWBase";
+ register_name = "PWBase";
break;
case 6:
check_pw(ctx);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PWField));
- rn = "PWField";
+ register_name = "PWField";
break;
case 7:
check_pw(ctx);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PWSize));
- rn = "PWSize";
+ register_name = "PWSize";
break;
default:
goto cp0_unimplemented;
}
break;
- case 6:
+ case CP0_REGISTER_06:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Wired));
- rn = "Wired";
+ register_name = "Wired";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf0));
- rn = "SRSConf0";
+ register_name = "SRSConf0";
break;
case 2:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf1));
- rn = "SRSConf1";
+ register_name = "SRSConf1";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf2));
- rn = "SRSConf2";
+ register_name = "SRSConf2";
break;
case 4:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf3));
- rn = "SRSConf3";
+ register_name = "SRSConf3";
break;
case 5:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf4));
- rn = "SRSConf4";
+ register_name = "SRSConf4";
break;
case 6:
check_pw(ctx);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PWCtl));
- rn = "PWCtl";
+ register_name = "PWCtl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 7:
+ case CP0_REGISTER_07:
switch (sel) {
case 0:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_HWREna));
- rn = "HWREna";
+ register_name = "HWREna";
break;
default:
goto cp0_unimplemented;
}
break;
- case 8:
+ case CP0_REGISTER_08:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr));
tcg_gen_ext32s_tl(arg, arg);
- rn = "BadVAddr";
+ register_name = "BadVAddr";
break;
case 1:
CP0_CHECK(ctx->bi);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstr));
- rn = "BadInstr";
+ register_name = "BadInstr";
break;
case 2:
CP0_CHECK(ctx->bp);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrP));
- rn = "BadInstrP";
+ register_name = "BadInstrP";
break;
case 3:
CP0_CHECK(ctx->bi);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrX));
tcg_gen_andi_tl(arg, arg, ~0xffff);
- rn = "BadInstrX";
+ register_name = "BadInstrX";
break;
default:
goto cp0_unimplemented;
}
break;
- case 9:
+ case CP0_REGISTER_09:
switch (sel) {
case 0:
/* Mark as an IO operation because we read the time. */
@@ -7046,164 +7075,173 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
ensure we break completely out of translated code. */
gen_save_pc(ctx->base.pc_next + 4);
ctx->base.is_jmp = DISAS_EXIT;
- rn = "Count";
+ register_name = "Count";
+ break;
+ case 6:
+ CP0_CHECK(ctx->saar);
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SAARI));
+ register_name = "SAARI";
+ break;
+ case 7:
+ CP0_CHECK(ctx->saar);
+ gen_helper_mfc0_saar(arg, cpu_env);
+ register_name = "SAAR";
break;
- /* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
}
break;
- case 10:
+ case CP0_REGISTER_10:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryHi));
tcg_gen_ext32s_tl(arg, arg);
- rn = "EntryHi";
+ register_name = "EntryHi";
break;
default:
goto cp0_unimplemented;
}
break;
- case 11:
+ case CP0_REGISTER_11:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Compare));
- rn = "Compare";
+ register_name = "Compare";
break;
/* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
}
break;
- case 12:
+ case CP0_REGISTER_12:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Status));
- rn = "Status";
+ register_name = "Status";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_IntCtl));
- rn = "IntCtl";
+ register_name = "IntCtl";
break;
case 2:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSCtl));
- rn = "SRSCtl";
+ register_name = "SRSCtl";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSMap));
- rn = "SRSMap";
+ register_name = "SRSMap";
break;
default:
goto cp0_unimplemented;
}
break;
- case 13:
+ case CP0_REGISTER_13:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Cause));
- rn = "Cause";
+ register_name = "Cause";
break;
default:
goto cp0_unimplemented;
}
break;
- case 14:
+ case CP0_REGISTER_14:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC));
tcg_gen_ext32s_tl(arg, arg);
- rn = "EPC";
+ register_name = "EPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 15:
+ case CP0_REGISTER_15:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PRid));
- rn = "PRid";
+ register_name = "PRid";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EBase));
tcg_gen_ext32s_tl(arg, arg);
- rn = "EBase";
+ register_name = "EBase";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
CP0_CHECK(ctx->cmgcr);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_CMGCRBase));
tcg_gen_ext32s_tl(arg, arg);
- rn = "CMGCRBase";
+ register_name = "CMGCRBase";
break;
default:
goto cp0_unimplemented;
}
break;
- case 16:
+ case CP0_REGISTER_16:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config0));
- rn = "Config";
+ register_name = "Config";
break;
case 1:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config1));
- rn = "Config1";
+ register_name = "Config1";
break;
case 2:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config2));
- rn = "Config2";
+ register_name = "Config2";
break;
case 3:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config3));
- rn = "Config3";
+ register_name = "Config3";
break;
case 4:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config4));
- rn = "Config4";
+ register_name = "Config4";
break;
case 5:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config5));
- rn = "Config5";
+ register_name = "Config5";
break;
/* 6,7 are implementation dependent */
case 6:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config6));
- rn = "Config6";
+ register_name = "Config6";
break;
case 7:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config7));
- rn = "Config7";
+ register_name = "Config7";
break;
default:
goto cp0_unimplemented;
}
break;
- case 17:
+ case CP0_REGISTER_17:
switch (sel) {
case 0:
gen_helper_mfc0_lladdr(arg, cpu_env);
- rn = "LLAddr";
+ register_name = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mfc0_maar(arg, cpu_env);
- rn = "MAAR";
+ register_name = "MAAR";
break;
case 2:
CP0_CHECK(ctx->mrp);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MAARI));
- rn = "MAARI";
+ register_name = "MAARI";
break;
default:
goto cp0_unimplemented;
}
break;
- case 18:
+ case CP0_REGISTER_18:
switch (sel) {
case 0:
case 1:
@@ -7215,13 +7253,13 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 7:
CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR));
gen_helper_1e0i(mfc0_watchlo, arg, sel);
- rn = "WatchLo";
+ register_name = "WatchLo";
break;
default:
goto cp0_unimplemented;
}
break;
- case 19:
+ case CP0_REGISTER_19:
switch (sel) {
case 0:
case 1:
@@ -7233,142 +7271,142 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 7:
CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR));
gen_helper_1e0i(mfc0_watchhi, arg, sel);
- rn = "WatchHi";
+ register_name = "WatchHi";
break;
default:
goto cp0_unimplemented;
}
break;
- case 20:
+ case CP0_REGISTER_20:
switch (sel) {
case 0:
#if defined(TARGET_MIPS64)
check_insn(ctx, ISA_MIPS3);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext));
tcg_gen_ext32s_tl(arg, arg);
- rn = "XContext";
+ register_name = "XContext";
break;
#endif
default:
goto cp0_unimplemented;
}
break;
- case 21:
+ case CP0_REGISTER_21:
/* Officially reserved, but sel 0 is used for R1x000 framemask */
CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6));
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Framemask));
- rn = "Framemask";
+ register_name = "Framemask";
break;
default:
goto cp0_unimplemented;
}
break;
- case 22:
+ case CP0_REGISTER_22:
tcg_gen_movi_tl(arg, 0); /* unimplemented */
- rn = "'Diagnostic"; /* implementation dependent */
+ register_name = "'Diagnostic"; /* implementation dependent */
break;
- case 23:
+ case CP0_REGISTER_23:
switch (sel) {
case 0:
gen_helper_mfc0_debug(arg, cpu_env); /* EJTAG support */
- rn = "Debug";
+ register_name = "Debug";
break;
case 1:
// gen_helper_mfc0_tracecontrol(arg); /* PDtrace support */
- rn = "TraceControl";
+ register_name = "TraceControl";
goto cp0_unimplemented;
case 2:
// gen_helper_mfc0_tracecontrol2(arg); /* PDtrace support */
- rn = "TraceControl2";
+ register_name = "TraceControl2";
goto cp0_unimplemented;
case 3:
// gen_helper_mfc0_usertracedata(arg); /* PDtrace support */
- rn = "UserTraceData";
+ register_name = "UserTraceData";
goto cp0_unimplemented;
case 4:
// gen_helper_mfc0_tracebpc(arg); /* PDtrace support */
- rn = "TraceBPC";
+ register_name = "TraceBPC";
goto cp0_unimplemented;
default:
goto cp0_unimplemented;
}
break;
- case 24:
+ case CP0_REGISTER_24:
switch (sel) {
case 0:
/* EJTAG support */
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC));
tcg_gen_ext32s_tl(arg, arg);
- rn = "DEPC";
+ register_name = "DEPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 25:
+ case CP0_REGISTER_25:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Performance0));
- rn = "Performance0";
+ register_name = "Performance0";
break;
case 1:
// gen_helper_mfc0_performance1(arg);
- rn = "Performance1";
+ register_name = "Performance1";
goto cp0_unimplemented;
case 2:
// gen_helper_mfc0_performance2(arg);
- rn = "Performance2";
+ register_name = "Performance2";
goto cp0_unimplemented;
case 3:
// gen_helper_mfc0_performance3(arg);
- rn = "Performance3";
+ register_name = "Performance3";
goto cp0_unimplemented;
case 4:
// gen_helper_mfc0_performance4(arg);
- rn = "Performance4";
+ register_name = "Performance4";
goto cp0_unimplemented;
case 5:
// gen_helper_mfc0_performance5(arg);
- rn = "Performance5";
+ register_name = "Performance5";
goto cp0_unimplemented;
case 6:
// gen_helper_mfc0_performance6(arg);
- rn = "Performance6";
+ register_name = "Performance6";
goto cp0_unimplemented;
case 7:
// gen_helper_mfc0_performance7(arg);
- rn = "Performance7";
+ register_name = "Performance7";
goto cp0_unimplemented;
default:
goto cp0_unimplemented;
}
break;
- case 26:
+ case CP0_REGISTER_26:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl));
- rn = "ErrCtl";
+ register_name = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 27:
+ case CP0_REGISTER_27:
switch (sel) {
case 0:
case 1:
case 2:
case 3:
tcg_gen_movi_tl(arg, 0); /* unimplemented */
- rn = "CacheErr";
+ register_name = "CacheErr";
break;
default:
goto cp0_unimplemented;
}
break;
- case 28:
+ case CP0_REGISTER_28:
switch (sel) {
case 0:
case 2:
@@ -7380,56 +7418,56 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_move_low32(arg, tmp);
tcg_temp_free_i64(tmp);
}
- rn = "TagLo";
+ register_name = "TagLo";
break;
case 1:
case 3:
case 5:
case 7:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataLo));
- rn = "DataLo";
+ register_name = "DataLo";
break;
default:
goto cp0_unimplemented;
}
break;
- case 29:
+ case CP0_REGISTER_29:
switch (sel) {
case 0:
case 2:
case 4:
case 6:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagHi));
- rn = "TagHi";
+ register_name = "TagHi";
break;
case 1:
case 3:
case 5:
case 7:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataHi));
- rn = "DataHi";
+ register_name = "DataHi";
break;
default:
goto cp0_unimplemented;
}
break;
- case 30:
+ case CP0_REGISTER_30:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC));
tcg_gen_ext32s_tl(arg, arg);
- rn = "ErrorEPC";
+ register_name = "ErrorEPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 31:
+ case CP0_REGISTER_31:
switch (sel) {
case 0:
/* EJTAG support */
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DESAVE));
- rn = "DESAVE";
+ register_name = "DESAVE";
break;
case 2:
case 3:
@@ -7441,7 +7479,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
tcg_gen_ld_tl(arg, cpu_env,
offsetof(CPUMIPSState, CP0_KScratch[sel-2]));
tcg_gen_ext32s_tl(arg, arg);
- rn = "KScratch";
+ register_name = "KScratch";
break;
default:
goto cp0_unimplemented;
@@ -7450,17 +7488,18 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
default:
goto cp0_unimplemented;
}
- trace_mips_translate_c0("mfc0", rn, reg, sel);
+ trace_mips_translate_c0("mfc0", register_name, reg, sel);
return;
cp0_unimplemented:
- qemu_log_mask(LOG_UNIMP, "mfc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ qemu_log_mask(LOG_UNIMP, "mfc0 %s (reg %d sel %d)\n",
+ register_name, reg, sel);
gen_mfc0_unimplemented(ctx, arg);
}
static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{
- const char *rn = "invalid";
+ const char *register_name = "invalid";
if (sel != 0)
check_insn(ctx, ISA_MIPS32);
@@ -7470,316 +7509,325 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
}
switch (reg) {
- case 0:
+ case CP0_REGISTER_00:
switch (sel) {
case 0:
gen_helper_mtc0_index(cpu_env, arg);
- rn = "Index";
+ register_name = "Index";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_mvpcontrol(cpu_env, arg);
- rn = "MVPControl";
+ register_name = "MVPControl";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
/* ignored */
- rn = "MVPConf0";
+ register_name = "MVPConf0";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
/* ignored */
- rn = "MVPConf1";
+ register_name = "MVPConf1";
break;
case 4:
CP0_CHECK(ctx->vp);
/* ignored */
- rn = "VPControl";
+ register_name = "VPControl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 1:
+ case CP0_REGISTER_01:
switch (sel) {
case 0:
/* ignored */
- rn = "Random";
+ register_name = "Random";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_vpecontrol(cpu_env, arg);
- rn = "VPEControl";
+ register_name = "VPEControl";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_vpeconf0(cpu_env, arg);
- rn = "VPEConf0";
+ register_name = "VPEConf0";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_vpeconf1(cpu_env, arg);
- rn = "VPEConf1";
+ register_name = "VPEConf1";
break;
case 4:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_yqmask(cpu_env, arg);
- rn = "YQMask";
+ register_name = "YQMask";
break;
case 5:
CP0_CHECK(ctx->insn_flags & ASE_MT);
tcg_gen_st_tl(arg, cpu_env,
offsetof(CPUMIPSState, CP0_VPESchedule));
- rn = "VPESchedule";
+ register_name = "VPESchedule";
break;
case 6:
CP0_CHECK(ctx->insn_flags & ASE_MT);
tcg_gen_st_tl(arg, cpu_env,
offsetof(CPUMIPSState, CP0_VPEScheFBack));
- rn = "VPEScheFBack";
+ register_name = "VPEScheFBack";
break;
case 7:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_vpeopt(cpu_env, arg);
- rn = "VPEOpt";
+ register_name = "VPEOpt";
break;
default:
goto cp0_unimplemented;
}
break;
- case 2:
+ case CP0_REGISTER_02:
switch (sel) {
case 0:
gen_helper_mtc0_entrylo0(cpu_env, arg);
- rn = "EntryLo0";
+ register_name = "EntryLo0";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tcstatus(cpu_env, arg);
- rn = "TCStatus";
+ register_name = "TCStatus";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tcbind(cpu_env, arg);
- rn = "TCBind";
+ register_name = "TCBind";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tcrestart(cpu_env, arg);
- rn = "TCRestart";
+ register_name = "TCRestart";
break;
case 4:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tchalt(cpu_env, arg);
- rn = "TCHalt";
+ register_name = "TCHalt";
break;
case 5:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tccontext(cpu_env, arg);
- rn = "TCContext";
+ register_name = "TCContext";
break;
case 6:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tcschedule(cpu_env, arg);
- rn = "TCSchedule";
+ register_name = "TCSchedule";
break;
case 7:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tcschefback(cpu_env, arg);
- rn = "TCScheFBack";
+ register_name = "TCScheFBack";
break;
default:
goto cp0_unimplemented;
}
break;
- case 3:
+ case CP0_REGISTER_03:
switch (sel) {
case 0:
gen_helper_mtc0_entrylo1(cpu_env, arg);
- rn = "EntryLo1";
+ register_name = "EntryLo1";
break;
case 1:
CP0_CHECK(ctx->vp);
/* ignored */
- rn = "GlobalNumber";
+ register_name = "GlobalNumber";
break;
default:
goto cp0_unimplemented;
}
break;
- case 4:
+ case CP0_REGISTER_04:
switch (sel) {
case 0:
gen_helper_mtc0_context(cpu_env, arg);
- rn = "Context";
+ register_name = "Context";
break;
case 1:
// gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */
- rn = "ContextConfig";
+ register_name = "ContextConfig";
goto cp0_unimplemented;
case 2:
CP0_CHECK(ctx->ulri);
tcg_gen_st_tl(arg, cpu_env,
offsetof(CPUMIPSState, active_tc.CP0_UserLocal));
- rn = "UserLocal";
+ register_name = "UserLocal";
break;
default:
goto cp0_unimplemented;
}
break;
- case 5:
+ case CP0_REGISTER_05:
switch (sel) {
case 0:
gen_helper_mtc0_pagemask(cpu_env, arg);
- rn = "PageMask";
+ register_name = "PageMask";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_pagegrain(cpu_env, arg);
- rn = "PageGrain";
+ register_name = "PageGrain";
ctx->base.is_jmp = DISAS_STOP;
break;
case 2:
CP0_CHECK(ctx->sc);
gen_helper_mtc0_segctl0(cpu_env, arg);
- rn = "SegCtl0";
+ register_name = "SegCtl0";
break;
case 3:
CP0_CHECK(ctx->sc);
gen_helper_mtc0_segctl1(cpu_env, arg);
- rn = "SegCtl1";
+ register_name = "SegCtl1";
break;
case 4:
CP0_CHECK(ctx->sc);
gen_helper_mtc0_segctl2(cpu_env, arg);
- rn = "SegCtl2";
+ register_name = "SegCtl2";
break;
case 5:
check_pw(ctx);
gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_PWBase));
- rn = "PWBase";
+ register_name = "PWBase";
break;
case 6:
check_pw(ctx);
gen_helper_mtc0_pwfield(cpu_env, arg);
- rn = "PWField";
+ register_name = "PWField";
break;
case 7:
check_pw(ctx);
gen_helper_mtc0_pwsize(cpu_env, arg);
- rn = "PWSize";
+ register_name = "PWSize";
break;
default:
goto cp0_unimplemented;
}
break;
- case 6:
+ case CP0_REGISTER_06:
switch (sel) {
case 0:
gen_helper_mtc0_wired(cpu_env, arg);
- rn = "Wired";
+ register_name = "Wired";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsconf0(cpu_env, arg);
- rn = "SRSConf0";
+ register_name = "SRSConf0";
break;
case 2:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsconf1(cpu_env, arg);
- rn = "SRSConf1";
+ register_name = "SRSConf1";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsconf2(cpu_env, arg);
- rn = "SRSConf2";
+ register_name = "SRSConf2";
break;
case 4:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsconf3(cpu_env, arg);
- rn = "SRSConf3";
+ register_name = "SRSConf3";
break;
case 5:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsconf4(cpu_env, arg);
- rn = "SRSConf4";
+ register_name = "SRSConf4";
break;
case 6:
check_pw(ctx);
gen_helper_mtc0_pwctl(cpu_env, arg);
- rn = "PWCtl";
+ register_name = "PWCtl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 7:
+ case CP0_REGISTER_07:
switch (sel) {
case 0:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_hwrena(cpu_env, arg);
ctx->base.is_jmp = DISAS_STOP;
- rn = "HWREna";
+ register_name = "HWREna";
break;
default:
goto cp0_unimplemented;
}
break;
- case 8:
+ case CP0_REGISTER_08:
switch (sel) {
case 0:
/* ignored */
- rn = "BadVAddr";
+ register_name = "BadVAddr";
break;
case 1:
/* ignored */
- rn = "BadInstr";
+ register_name = "BadInstr";
break;
case 2:
/* ignored */
- rn = "BadInstrP";
+ register_name = "BadInstrP";
break;
case 3:
/* ignored */
- rn = "BadInstrX";
+ register_name = "BadInstrX";
break;
default:
goto cp0_unimplemented;
}
break;
- case 9:
+ case CP0_REGISTER_09:
switch (sel) {
case 0:
gen_helper_mtc0_count(cpu_env, arg);
- rn = "Count";
+ register_name = "Count";
+ break;
+ case 6:
+ CP0_CHECK(ctx->saar);
+ gen_helper_mtc0_saari(cpu_env, arg);
+ register_name = "SAARI";
+ break;
+ case 7:
+ CP0_CHECK(ctx->saar);
+ gen_helper_mtc0_saar(cpu_env, arg);
+ register_name = "SAAR";
break;
- /* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
}
break;
- case 10:
+ case CP0_REGISTER_10:
switch (sel) {
case 0:
gen_helper_mtc0_entryhi(cpu_env, arg);
- rn = "EntryHi";
+ register_name = "EntryHi";
break;
default:
goto cp0_unimplemented;
}
break;
- case 11:
+ case CP0_REGISTER_11:
switch (sel) {
case 0:
gen_helper_mtc0_compare(cpu_env, arg);
- rn = "Compare";
+ register_name = "Compare";
break;
/* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
}
break;
- case 12:
+ case CP0_REGISTER_12:
switch (sel) {
case 0:
save_cpu_state(ctx, 1);
@@ -7787,34 +7835,34 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
/* DISAS_STOP isn't good enough here, hflags may have changed. */
gen_save_pc(ctx->base.pc_next + 4);
ctx->base.is_jmp = DISAS_EXIT;
- rn = "Status";
+ register_name = "Status";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_intctl(cpu_env, arg);
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "IntCtl";
+ register_name = "IntCtl";
break;
case 2:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsctl(cpu_env, arg);
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "SRSCtl";
+ register_name = "SRSCtl";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap));
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "SRSMap";
+ register_name = "SRSMap";
break;
default:
goto cp0_unimplemented;
}
break;
- case 13:
+ case CP0_REGISTER_13:
switch (sel) {
case 0:
save_cpu_state(ctx, 1);
@@ -7824,107 +7872,107 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
* translated code to check for pending interrupts. */
gen_save_pc(ctx->base.pc_next + 4);
ctx->base.is_jmp = DISAS_EXIT;
- rn = "Cause";
+ register_name = "Cause";
break;
default:
goto cp0_unimplemented;
}
break;
- case 14:
+ case CP0_REGISTER_14:
switch (sel) {
case 0:
tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC));
- rn = "EPC";
+ register_name = "EPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 15:
+ case CP0_REGISTER_15:
switch (sel) {
case 0:
/* ignored */
- rn = "PRid";
+ register_name = "PRid";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_ebase(cpu_env, arg);
- rn = "EBase";
+ register_name = "EBase";
break;
default:
goto cp0_unimplemented;
}
break;
- case 16:
+ case CP0_REGISTER_16:
switch (sel) {
case 0:
gen_helper_mtc0_config0(cpu_env, arg);
- rn = "Config";
+ register_name = "Config";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
break;
case 1:
/* ignored, read only */
- rn = "Config1";
+ register_name = "Config1";
break;
case 2:
gen_helper_mtc0_config2(cpu_env, arg);
- rn = "Config2";
+ register_name = "Config2";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
break;
case 3:
gen_helper_mtc0_config3(cpu_env, arg);
- rn = "Config3";
+ register_name = "Config3";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
break;
case 4:
gen_helper_mtc0_config4(cpu_env, arg);
- rn = "Config4";
+ register_name = "Config4";
ctx->base.is_jmp = DISAS_STOP;
break;
case 5:
gen_helper_mtc0_config5(cpu_env, arg);
- rn = "Config5";
+ register_name = "Config5";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
break;
/* 6,7 are implementation dependent */
case 6:
/* ignored */
- rn = "Config6";
+ register_name = "Config6";
break;
case 7:
/* ignored */
- rn = "Config7";
+ register_name = "Config7";
break;
default:
- rn = "Invalid config selector";
+ register_name = "Invalid config selector";
goto cp0_unimplemented;
}
break;
- case 17:
+ case CP0_REGISTER_17:
switch (sel) {
case 0:
gen_helper_mtc0_lladdr(cpu_env, arg);
- rn = "LLAddr";
+ register_name = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mtc0_maar(cpu_env, arg);
- rn = "MAAR";
+ register_name = "MAAR";
break;
case 2:
CP0_CHECK(ctx->mrp);
gen_helper_mtc0_maari(cpu_env, arg);
- rn = "MAARI";
+ register_name = "MAARI";
break;
default:
goto cp0_unimplemented;
}
break;
- case 18:
+ case CP0_REGISTER_18:
switch (sel) {
case 0:
case 1:
@@ -7936,13 +7984,13 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 7:
CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR));
gen_helper_0e1i(mtc0_watchlo, arg, sel);
- rn = "WatchLo";
+ register_name = "WatchLo";
break;
default:
goto cp0_unimplemented;
}
break;
- case 19:
+ case CP0_REGISTER_19:
switch (sel) {
case 0:
case 1:
@@ -7954,59 +8002,59 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 7:
CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR));
gen_helper_0e1i(mtc0_watchhi, arg, sel);
- rn = "WatchHi";
+ register_name = "WatchHi";
break;
default:
goto cp0_unimplemented;
}
break;
- case 20:
+ case CP0_REGISTER_20:
switch (sel) {
case 0:
#if defined(TARGET_MIPS64)
check_insn(ctx, ISA_MIPS3);
gen_helper_mtc0_xcontext(cpu_env, arg);
- rn = "XContext";
+ register_name = "XContext";
break;
#endif
default:
goto cp0_unimplemented;
}
break;
- case 21:
+ case CP0_REGISTER_21:
/* Officially reserved, but sel 0 is used for R1x000 framemask */
CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6));
switch (sel) {
case 0:
gen_helper_mtc0_framemask(cpu_env, arg);
- rn = "Framemask";
+ register_name = "Framemask";
break;
default:
goto cp0_unimplemented;
}
break;
- case 22:
+ case CP0_REGISTER_22:
/* ignored */
- rn = "Diagnostic"; /* implementation dependent */
+ register_name = "Diagnostic"; /* implementation dependent */
break;
- case 23:
+ case CP0_REGISTER_23:
switch (sel) {
case 0:
gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */
/* DISAS_STOP isn't good enough here, hflags may have changed. */
gen_save_pc(ctx->base.pc_next + 4);
ctx->base.is_jmp = DISAS_EXIT;
- rn = "Debug";
+ register_name = "Debug";
break;
case 1:
// gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */
- rn = "TraceControl";
+ register_name = "TraceControl";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
goto cp0_unimplemented;
case 2:
// gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */
- rn = "TraceControl2";
+ register_name = "TraceControl2";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
goto cp0_unimplemented;
@@ -8014,7 +8062,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
// gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */
- rn = "UserTraceData";
+ register_name = "UserTraceData";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
goto cp0_unimplemented;
@@ -8022,142 +8070,142 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
// gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "TraceBPC";
+ register_name = "TraceBPC";
goto cp0_unimplemented;
default:
goto cp0_unimplemented;
}
break;
- case 24:
+ case CP0_REGISTER_24:
switch (sel) {
case 0:
/* EJTAG support */
tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC));
- rn = "DEPC";
+ register_name = "DEPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 25:
+ case CP0_REGISTER_25:
switch (sel) {
case 0:
gen_helper_mtc0_performance0(cpu_env, arg);
- rn = "Performance0";
+ register_name = "Performance0";
break;
case 1:
// gen_helper_mtc0_performance1(arg);
- rn = "Performance1";
+ register_name = "Performance1";
goto cp0_unimplemented;
case 2:
// gen_helper_mtc0_performance2(arg);
- rn = "Performance2";
+ register_name = "Performance2";
goto cp0_unimplemented;
case 3:
// gen_helper_mtc0_performance3(arg);
- rn = "Performance3";
+ register_name = "Performance3";
goto cp0_unimplemented;
case 4:
// gen_helper_mtc0_performance4(arg);
- rn = "Performance4";
+ register_name = "Performance4";
goto cp0_unimplemented;
case 5:
// gen_helper_mtc0_performance5(arg);
- rn = "Performance5";
+ register_name = "Performance5";
goto cp0_unimplemented;
case 6:
// gen_helper_mtc0_performance6(arg);
- rn = "Performance6";
+ register_name = "Performance6";
goto cp0_unimplemented;
case 7:
// gen_helper_mtc0_performance7(arg);
- rn = "Performance7";
+ register_name = "Performance7";
goto cp0_unimplemented;
default:
goto cp0_unimplemented;
}
break;
- case 26:
+ case CP0_REGISTER_26:
switch (sel) {
case 0:
gen_helper_mtc0_errctl(cpu_env, arg);
ctx->base.is_jmp = DISAS_STOP;
- rn = "ErrCtl";
+ register_name = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 27:
+ case CP0_REGISTER_27:
switch (sel) {
case 0:
case 1:
case 2:
case 3:
/* ignored */
- rn = "CacheErr";
+ register_name = "CacheErr";
break;
default:
goto cp0_unimplemented;
}
break;
- case 28:
+ case CP0_REGISTER_28:
switch (sel) {
case 0:
case 2:
case 4:
case 6:
gen_helper_mtc0_taglo(cpu_env, arg);
- rn = "TagLo";
+ register_name = "TagLo";
break;
case 1:
case 3:
case 5:
case 7:
gen_helper_mtc0_datalo(cpu_env, arg);
- rn = "DataLo";
+ register_name = "DataLo";
break;
default:
goto cp0_unimplemented;
}
break;
- case 29:
+ case CP0_REGISTER_29:
switch (sel) {
case 0:
case 2:
case 4:
case 6:
gen_helper_mtc0_taghi(cpu_env, arg);
- rn = "TagHi";
+ register_name = "TagHi";
break;
case 1:
case 3:
case 5:
case 7:
gen_helper_mtc0_datahi(cpu_env, arg);
- rn = "DataHi";
+ register_name = "DataHi";
break;
default:
- rn = "invalid sel";
+ register_name = "invalid sel";
goto cp0_unimplemented;
}
break;
- case 30:
+ case CP0_REGISTER_30:
switch (sel) {
case 0:
tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC));
- rn = "ErrorEPC";
+ register_name = "ErrorEPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 31:
+ case CP0_REGISTER_31:
switch (sel) {
case 0:
/* EJTAG support */
gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_DESAVE));
- rn = "DESAVE";
+ register_name = "DESAVE";
break;
case 2:
case 3:
@@ -8168,7 +8216,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
CP0_CHECK(ctx->kscrexist & (1 << sel));
tcg_gen_st_tl(arg, cpu_env,
offsetof(CPUMIPSState, CP0_KScratch[sel-2]));
- rn = "KScratch";
+ register_name = "KScratch";
break;
default:
goto cp0_unimplemented;
@@ -8177,7 +8225,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
default:
goto cp0_unimplemented;
}
- trace_mips_translate_c0("mtc0", rn, reg, sel);
+ trace_mips_translate_c0("mtc0", register_name, reg, sel);
/* For simplicity assume that all writes can cause interrupts. */
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
@@ -8190,297 +8238,298 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
return;
cp0_unimplemented:
- qemu_log_mask(LOG_UNIMP, "mtc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ qemu_log_mask(LOG_UNIMP, "mtc0 %s (reg %d sel %d)\n",
+ register_name, reg, sel);
}
#if defined(TARGET_MIPS64)
static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{
- const char *rn = "invalid";
+ const char *register_name = "invalid";
if (sel != 0)
check_insn(ctx, ISA_MIPS64);
switch (reg) {
- case 0:
+ case CP0_REGISTER_00:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Index));
- rn = "Index";
+ register_name = "Index";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_mvpcontrol(arg, cpu_env);
- rn = "MVPControl";
+ register_name = "MVPControl";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_mvpconf0(arg, cpu_env);
- rn = "MVPConf0";
+ register_name = "MVPConf0";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_mvpconf1(arg, cpu_env);
- rn = "MVPConf1";
+ register_name = "MVPConf1";
break;
case 4:
CP0_CHECK(ctx->vp);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPControl));
- rn = "VPControl";
+ register_name = "VPControl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 1:
+ case CP0_REGISTER_01:
switch (sel) {
case 0:
CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6));
gen_helper_mfc0_random(arg, cpu_env);
- rn = "Random";
+ register_name = "Random";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl));
- rn = "VPEControl";
+ register_name = "VPEControl";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0));
- rn = "VPEConf0";
+ register_name = "VPEConf0";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1));
- rn = "VPEConf1";
+ register_name = "VPEConf1";
break;
case 4:
CP0_CHECK(ctx->insn_flags & ASE_MT);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_YQMask));
- rn = "YQMask";
+ register_name = "YQMask";
break;
case 5:
CP0_CHECK(ctx->insn_flags & ASE_MT);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule));
- rn = "VPESchedule";
+ register_name = "VPESchedule";
break;
case 6:
CP0_CHECK(ctx->insn_flags & ASE_MT);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack));
- rn = "VPEScheFBack";
+ register_name = "VPEScheFBack";
break;
case 7:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt));
- rn = "VPEOpt";
+ register_name = "VPEOpt";
break;
default:
goto cp0_unimplemented;
}
break;
- case 2:
+ case CP0_REGISTER_02:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo0));
- rn = "EntryLo0";
+ register_name = "EntryLo0";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_tcstatus(arg, cpu_env);
- rn = "TCStatus";
+ register_name = "TCStatus";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mfc0_tcbind(arg, cpu_env);
- rn = "TCBind";
+ register_name = "TCBind";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_dmfc0_tcrestart(arg, cpu_env);
- rn = "TCRestart";
+ register_name = "TCRestart";
break;
case 4:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_dmfc0_tchalt(arg, cpu_env);
- rn = "TCHalt";
+ register_name = "TCHalt";
break;
case 5:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_dmfc0_tccontext(arg, cpu_env);
- rn = "TCContext";
+ register_name = "TCContext";
break;
case 6:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_dmfc0_tcschedule(arg, cpu_env);
- rn = "TCSchedule";
+ register_name = "TCSchedule";
break;
case 7:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_dmfc0_tcschefback(arg, cpu_env);
- rn = "TCScheFBack";
+ register_name = "TCScheFBack";
break;
default:
goto cp0_unimplemented;
}
break;
- case 3:
+ case CP0_REGISTER_03:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1));
- rn = "EntryLo1";
+ register_name = "EntryLo1";
break;
case 1:
CP0_CHECK(ctx->vp);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_GlobalNumber));
- rn = "GlobalNumber";
+ register_name = "GlobalNumber";
break;
default:
goto cp0_unimplemented;
}
break;
- case 4:
+ case CP0_REGISTER_04:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_Context));
- rn = "Context";
+ register_name = "Context";
break;
case 1:
// gen_helper_dmfc0_contextconfig(arg); /* SmartMIPS ASE */
- rn = "ContextConfig";
+ register_name = "ContextConfig";
goto cp0_unimplemented;
case 2:
CP0_CHECK(ctx->ulri);
tcg_gen_ld_tl(arg, cpu_env,
offsetof(CPUMIPSState, active_tc.CP0_UserLocal));
- rn = "UserLocal";
+ register_name = "UserLocal";
break;
default:
goto cp0_unimplemented;
}
break;
- case 5:
+ case CP0_REGISTER_05:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageMask));
- rn = "PageMask";
+ register_name = "PageMask";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageGrain));
- rn = "PageGrain";
+ register_name = "PageGrain";
break;
case 2:
CP0_CHECK(ctx->sc);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl0));
- rn = "SegCtl0";
+ register_name = "SegCtl0";
break;
case 3:
CP0_CHECK(ctx->sc);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl1));
- rn = "SegCtl1";
+ register_name = "SegCtl1";
break;
case 4:
CP0_CHECK(ctx->sc);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl2));
- rn = "SegCtl2";
+ register_name = "SegCtl2";
break;
case 5:
check_pw(ctx);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWBase));
- rn = "PWBase";
+ register_name = "PWBase";
break;
case 6:
check_pw(ctx);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWField));
- rn = "PWField";
+ register_name = "PWField";
break;
case 7:
check_pw(ctx);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWSize));
- rn = "PWSize";
+ register_name = "PWSize";
break;
default:
goto cp0_unimplemented;
}
break;
- case 6:
+ case CP0_REGISTER_06:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Wired));
- rn = "Wired";
+ register_name = "Wired";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf0));
- rn = "SRSConf0";
+ register_name = "SRSConf0";
break;
case 2:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf1));
- rn = "SRSConf1";
+ register_name = "SRSConf1";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf2));
- rn = "SRSConf2";
+ register_name = "SRSConf2";
break;
case 4:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf3));
- rn = "SRSConf3";
+ register_name = "SRSConf3";
break;
case 5:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf4));
- rn = "SRSConf4";
+ register_name = "SRSConf4";
break;
case 6:
check_pw(ctx);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PWCtl));
- rn = "PWCtl";
+ register_name = "PWCtl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 7:
+ case CP0_REGISTER_07:
switch (sel) {
case 0:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_HWREna));
- rn = "HWREna";
+ register_name = "HWREna";
break;
default:
goto cp0_unimplemented;
}
break;
- case 8:
+ case CP0_REGISTER_08:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr));
- rn = "BadVAddr";
+ register_name = "BadVAddr";
break;
case 1:
CP0_CHECK(ctx->bi);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstr));
- rn = "BadInstr";
+ register_name = "BadInstr";
break;
case 2:
CP0_CHECK(ctx->bp);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrP));
- rn = "BadInstrP";
+ register_name = "BadInstrP";
break;
case 3:
CP0_CHECK(ctx->bi);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrX));
tcg_gen_andi_tl(arg, arg, ~0xffff);
- rn = "BadInstrX";
+ register_name = "BadInstrX";
break;
default:
goto cp0_unimplemented;
}
break;
- case 9:
+ case CP0_REGISTER_09:
switch (sel) {
case 0:
/* Mark as an IO operation because we read the time. */
@@ -8496,160 +8545,169 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
ensure we break completely out of translated code. */
gen_save_pc(ctx->base.pc_next + 4);
ctx->base.is_jmp = DISAS_EXIT;
- rn = "Count";
+ register_name = "Count";
+ break;
+ case 6:
+ CP0_CHECK(ctx->saar);
+ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SAARI));
+ register_name = "SAARI";
+ break;
+ case 7:
+ CP0_CHECK(ctx->saar);
+ gen_helper_dmfc0_saar(arg, cpu_env);
+ register_name = "SAAR";
break;
- /* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
}
break;
- case 10:
+ case CP0_REGISTER_10:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryHi));
- rn = "EntryHi";
+ register_name = "EntryHi";
break;
default:
goto cp0_unimplemented;
}
break;
- case 11:
+ case CP0_REGISTER_11:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Compare));
- rn = "Compare";
+ register_name = "Compare";
break;
/* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
}
break;
- case 12:
+ case CP0_REGISTER_12:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Status));
- rn = "Status";
+ register_name = "Status";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_IntCtl));
- rn = "IntCtl";
+ register_name = "IntCtl";
break;
case 2:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSCtl));
- rn = "SRSCtl";
+ register_name = "SRSCtl";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSMap));
- rn = "SRSMap";
+ register_name = "SRSMap";
break;
default:
goto cp0_unimplemented;
}
break;
- case 13:
+ case CP0_REGISTER_13:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Cause));
- rn = "Cause";
+ register_name = "Cause";
break;
default:
goto cp0_unimplemented;
}
break;
- case 14:
+ case CP0_REGISTER_14:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC));
- rn = "EPC";
+ register_name = "EPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 15:
+ case CP0_REGISTER_15:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PRid));
- rn = "PRid";
+ register_name = "PRid";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EBase));
- rn = "EBase";
+ register_name = "EBase";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
CP0_CHECK(ctx->cmgcr);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_CMGCRBase));
- rn = "CMGCRBase";
+ register_name = "CMGCRBase";
break;
default:
goto cp0_unimplemented;
}
break;
- case 16:
+ case CP0_REGISTER_16:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config0));
- rn = "Config";
+ register_name = "Config";
break;
case 1:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config1));
- rn = "Config1";
+ register_name = "Config1";
break;
case 2:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config2));
- rn = "Config2";
+ register_name = "Config2";
break;
case 3:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config3));
- rn = "Config3";
+ register_name = "Config3";
break;
case 4:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config4));
- rn = "Config4";
+ register_name = "Config4";
break;
case 5:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config5));
- rn = "Config5";
+ register_name = "Config5";
break;
/* 6,7 are implementation dependent */
case 6:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config6));
- rn = "Config6";
+ register_name = "Config6";
break;
case 7:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config7));
- rn = "Config7";
+ register_name = "Config7";
break;
default:
goto cp0_unimplemented;
}
break;
- case 17:
+ case CP0_REGISTER_17:
switch (sel) {
case 0:
gen_helper_dmfc0_lladdr(arg, cpu_env);
- rn = "LLAddr";
+ register_name = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_dmfc0_maar(arg, cpu_env);
- rn = "MAAR";
+ register_name = "MAAR";
break;
case 2:
CP0_CHECK(ctx->mrp);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MAARI));
- rn = "MAARI";
+ register_name = "MAARI";
break;
default:
goto cp0_unimplemented;
}
break;
- case 18:
+ case CP0_REGISTER_18:
switch (sel) {
case 0:
case 1:
@@ -8661,13 +8719,13 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 7:
CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR));
gen_helper_1e0i(dmfc0_watchlo, arg, sel);
- rn = "WatchLo";
+ register_name = "WatchLo";
break;
default:
goto cp0_unimplemented;
}
break;
- case 19:
+ case CP0_REGISTER_19:
switch (sel) {
case 0:
case 1:
@@ -8679,125 +8737,125 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 7:
CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR));
gen_helper_1e0i(mfc0_watchhi, arg, sel);
- rn = "WatchHi";
+ register_name = "WatchHi";
break;
default:
goto cp0_unimplemented;
}
break;
- case 20:
+ case CP0_REGISTER_20:
switch (sel) {
case 0:
check_insn(ctx, ISA_MIPS3);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext));
- rn = "XContext";
+ register_name = "XContext";
break;
default:
goto cp0_unimplemented;
}
break;
- case 21:
+ case CP0_REGISTER_21:
/* Officially reserved, but sel 0 is used for R1x000 framemask */
CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6));
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Framemask));
- rn = "Framemask";
+ register_name = "Framemask";
break;
default:
goto cp0_unimplemented;
}
break;
- case 22:
+ case CP0_REGISTER_22:
tcg_gen_movi_tl(arg, 0); /* unimplemented */
- rn = "'Diagnostic"; /* implementation dependent */
+ register_name = "'Diagnostic"; /* implementation dependent */
break;
- case 23:
+ case CP0_REGISTER_23:
switch (sel) {
case 0:
gen_helper_mfc0_debug(arg, cpu_env); /* EJTAG support */
- rn = "Debug";
+ register_name = "Debug";
break;
case 1:
// gen_helper_dmfc0_tracecontrol(arg, cpu_env); /* PDtrace support */
- rn = "TraceControl";
+ register_name = "TraceControl";
goto cp0_unimplemented;
case 2:
// gen_helper_dmfc0_tracecontrol2(arg, cpu_env); /* PDtrace support */
- rn = "TraceControl2";
+ register_name = "TraceControl2";
goto cp0_unimplemented;
case 3:
// gen_helper_dmfc0_usertracedata(arg, cpu_env); /* PDtrace support */
- rn = "UserTraceData";
+ register_name = "UserTraceData";
goto cp0_unimplemented;
case 4:
// gen_helper_dmfc0_tracebpc(arg, cpu_env); /* PDtrace support */
- rn = "TraceBPC";
+ register_name = "TraceBPC";
goto cp0_unimplemented;
default:
goto cp0_unimplemented;
}
break;
- case 24:
+ case CP0_REGISTER_24:
switch (sel) {
case 0:
/* EJTAG support */
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC));
- rn = "DEPC";
+ register_name = "DEPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 25:
+ case CP0_REGISTER_25:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Performance0));
- rn = "Performance0";
+ register_name = "Performance0";
break;
case 1:
// gen_helper_dmfc0_performance1(arg);
- rn = "Performance1";
+ register_name = "Performance1";
goto cp0_unimplemented;
case 2:
// gen_helper_dmfc0_performance2(arg);
- rn = "Performance2";
+ register_name = "Performance2";
goto cp0_unimplemented;
case 3:
// gen_helper_dmfc0_performance3(arg);
- rn = "Performance3";
+ register_name = "Performance3";
goto cp0_unimplemented;
case 4:
// gen_helper_dmfc0_performance4(arg);
- rn = "Performance4";
+ register_name = "Performance4";
goto cp0_unimplemented;
case 5:
// gen_helper_dmfc0_performance5(arg);
- rn = "Performance5";
+ register_name = "Performance5";
goto cp0_unimplemented;
case 6:
// gen_helper_dmfc0_performance6(arg);
- rn = "Performance6";
+ register_name = "Performance6";
goto cp0_unimplemented;
case 7:
// gen_helper_dmfc0_performance7(arg);
- rn = "Performance7";
+ register_name = "Performance7";
goto cp0_unimplemented;
default:
goto cp0_unimplemented;
}
break;
- case 26:
+ case CP0_REGISTER_26:
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl));
- rn = "ErrCtl";
+ register_name = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 27:
+ case CP0_REGISTER_27:
switch (sel) {
/* ignored */
case 0:
@@ -8805,68 +8863,68 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 2:
case 3:
tcg_gen_movi_tl(arg, 0); /* unimplemented */
- rn = "CacheErr";
+ register_name = "CacheErr";
break;
default:
goto cp0_unimplemented;
}
break;
- case 28:
+ case CP0_REGISTER_28:
switch (sel) {
case 0:
case 2:
case 4:
case 6:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagLo));
- rn = "TagLo";
+ register_name = "TagLo";
break;
case 1:
case 3:
case 5:
case 7:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataLo));
- rn = "DataLo";
+ register_name = "DataLo";
break;
default:
goto cp0_unimplemented;
}
break;
- case 29:
+ case CP0_REGISTER_29:
switch (sel) {
case 0:
case 2:
case 4:
case 6:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagHi));
- rn = "TagHi";
+ register_name = "TagHi";
break;
case 1:
case 3:
case 5:
case 7:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataHi));
- rn = "DataHi";
+ register_name = "DataHi";
break;
default:
goto cp0_unimplemented;
}
break;
- case 30:
+ case CP0_REGISTER_30:
switch (sel) {
case 0:
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC));
- rn = "ErrorEPC";
+ register_name = "ErrorEPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 31:
+ case CP0_REGISTER_31:
switch (sel) {
case 0:
/* EJTAG support */
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DESAVE));
- rn = "DESAVE";
+ register_name = "DESAVE";
break;
case 2:
case 3:
@@ -8877,7 +8935,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
CP0_CHECK(ctx->kscrexist & (1 << sel));
tcg_gen_ld_tl(arg, cpu_env,
offsetof(CPUMIPSState, CP0_KScratch[sel-2]));
- rn = "KScratch";
+ register_name = "KScratch";
break;
default:
goto cp0_unimplemented;
@@ -8886,17 +8944,18 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
default:
goto cp0_unimplemented;
}
- trace_mips_translate_c0("dmfc0", rn, reg, sel);
+ trace_mips_translate_c0("dmfc0", register_name, reg, sel);
return;
cp0_unimplemented:
- qemu_log_mask(LOG_UNIMP, "dmfc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ qemu_log_mask(LOG_UNIMP, "dmfc0 %s (reg %d sel %d)\n",
+ register_name, reg, sel);
gen_mfc0_unimplemented(ctx, arg);
}
static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{
- const char *rn = "invalid";
+ const char *register_name = "invalid";
if (sel != 0)
check_insn(ctx, ISA_MIPS64);
@@ -8906,308 +8965,317 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
}
switch (reg) {
- case 0:
+ case CP0_REGISTER_00:
switch (sel) {
case 0:
gen_helper_mtc0_index(cpu_env, arg);
- rn = "Index";
+ register_name = "Index";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_mvpcontrol(cpu_env, arg);
- rn = "MVPControl";
+ register_name = "MVPControl";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
/* ignored */
- rn = "MVPConf0";
+ register_name = "MVPConf0";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
/* ignored */
- rn = "MVPConf1";
+ register_name = "MVPConf1";
break;
case 4:
CP0_CHECK(ctx->vp);
/* ignored */
- rn = "VPControl";
+ register_name = "VPControl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 1:
+ case CP0_REGISTER_01:
switch (sel) {
case 0:
/* ignored */
- rn = "Random";
+ register_name = "Random";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_vpecontrol(cpu_env, arg);
- rn = "VPEControl";
+ register_name = "VPEControl";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_vpeconf0(cpu_env, arg);
- rn = "VPEConf0";
+ register_name = "VPEConf0";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_vpeconf1(cpu_env, arg);
- rn = "VPEConf1";
+ register_name = "VPEConf1";
break;
case 4:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_yqmask(cpu_env, arg);
- rn = "YQMask";
+ register_name = "YQMask";
break;
case 5:
CP0_CHECK(ctx->insn_flags & ASE_MT);
tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule));
- rn = "VPESchedule";
+ register_name = "VPESchedule";
break;
case 6:
CP0_CHECK(ctx->insn_flags & ASE_MT);
tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack));
- rn = "VPEScheFBack";
+ register_name = "VPEScheFBack";
break;
case 7:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_vpeopt(cpu_env, arg);
- rn = "VPEOpt";
+ register_name = "VPEOpt";
break;
default:
goto cp0_unimplemented;
}
break;
- case 2:
+ case CP0_REGISTER_02:
switch (sel) {
case 0:
gen_helper_dmtc0_entrylo0(cpu_env, arg);
- rn = "EntryLo0";
+ register_name = "EntryLo0";
break;
case 1:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tcstatus(cpu_env, arg);
- rn = "TCStatus";
+ register_name = "TCStatus";
break;
case 2:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tcbind(cpu_env, arg);
- rn = "TCBind";
+ register_name = "TCBind";
break;
case 3:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tcrestart(cpu_env, arg);
- rn = "TCRestart";
+ register_name = "TCRestart";
break;
case 4:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tchalt(cpu_env, arg);
- rn = "TCHalt";
+ register_name = "TCHalt";
break;
case 5:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tccontext(cpu_env, arg);
- rn = "TCContext";
+ register_name = "TCContext";
break;
case 6:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tcschedule(cpu_env, arg);
- rn = "TCSchedule";
+ register_name = "TCSchedule";
break;
case 7:
CP0_CHECK(ctx->insn_flags & ASE_MT);
gen_helper_mtc0_tcschefback(cpu_env, arg);
- rn = "TCScheFBack";
+ register_name = "TCScheFBack";
break;
default:
goto cp0_unimplemented;
}
break;
- case 3:
+ case CP0_REGISTER_03:
switch (sel) {
case 0:
gen_helper_dmtc0_entrylo1(cpu_env, arg);
- rn = "EntryLo1";
+ register_name = "EntryLo1";
break;
case 1:
CP0_CHECK(ctx->vp);
/* ignored */
- rn = "GlobalNumber";
+ register_name = "GlobalNumber";
break;
default:
goto cp0_unimplemented;
}
break;
- case 4:
+ case CP0_REGISTER_04:
switch (sel) {
case 0:
gen_helper_mtc0_context(cpu_env, arg);
- rn = "Context";
+ register_name = "Context";
break;
case 1:
// gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */
- rn = "ContextConfig";
+ register_name = "ContextConfig";
goto cp0_unimplemented;
case 2:
CP0_CHECK(ctx->ulri);
tcg_gen_st_tl(arg, cpu_env,
offsetof(CPUMIPSState, active_tc.CP0_UserLocal));
- rn = "UserLocal";
+ register_name = "UserLocal";
break;
default:
goto cp0_unimplemented;
}
break;
- case 5:
+ case CP0_REGISTER_05:
switch (sel) {
case 0:
gen_helper_mtc0_pagemask(cpu_env, arg);
- rn = "PageMask";
+ register_name = "PageMask";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_pagegrain(cpu_env, arg);
- rn = "PageGrain";
+ register_name = "PageGrain";
break;
case 2:
CP0_CHECK(ctx->sc);
gen_helper_mtc0_segctl0(cpu_env, arg);
- rn = "SegCtl0";
+ register_name = "SegCtl0";
break;
case 3:
CP0_CHECK(ctx->sc);
gen_helper_mtc0_segctl1(cpu_env, arg);
- rn = "SegCtl1";
+ register_name = "SegCtl1";
break;
case 4:
CP0_CHECK(ctx->sc);
gen_helper_mtc0_segctl2(cpu_env, arg);
- rn = "SegCtl2";
+ register_name = "SegCtl2";
break;
case 5:
check_pw(ctx);
tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWBase));
- rn = "PWBase";
+ register_name = "PWBase";
break;
case 6:
check_pw(ctx);
gen_helper_mtc0_pwfield(cpu_env, arg);
- rn = "PWField";
+ register_name = "PWField";
break;
case 7:
check_pw(ctx);
gen_helper_mtc0_pwsize(cpu_env, arg);
- rn = "PWSize";
+ register_name = "PWSize";
break;
default:
goto cp0_unimplemented;
}
break;
- case 6:
+ case CP0_REGISTER_06:
switch (sel) {
case 0:
gen_helper_mtc0_wired(cpu_env, arg);
- rn = "Wired";
+ register_name = "Wired";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsconf0(cpu_env, arg);
- rn = "SRSConf0";
+ register_name = "SRSConf0";
break;
case 2:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsconf1(cpu_env, arg);
- rn = "SRSConf1";
+ register_name = "SRSConf1";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsconf2(cpu_env, arg);
- rn = "SRSConf2";
+ register_name = "SRSConf2";
break;
case 4:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsconf3(cpu_env, arg);
- rn = "SRSConf3";
+ register_name = "SRSConf3";
break;
case 5:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsconf4(cpu_env, arg);
- rn = "SRSConf4";
+ register_name = "SRSConf4";
break;
case 6:
check_pw(ctx);
gen_helper_mtc0_pwctl(cpu_env, arg);
- rn = "PWCtl";
+ register_name = "PWCtl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 7:
+ case CP0_REGISTER_07:
switch (sel) {
case 0:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_hwrena(cpu_env, arg);
ctx->base.is_jmp = DISAS_STOP;
- rn = "HWREna";
+ register_name = "HWREna";
break;
default:
goto cp0_unimplemented;
}
break;
- case 8:
+ case CP0_REGISTER_08:
switch (sel) {
case 0:
/* ignored */
- rn = "BadVAddr";
+ register_name = "BadVAddr";
break;
case 1:
/* ignored */
- rn = "BadInstr";
+ register_name = "BadInstr";
break;
case 2:
/* ignored */
- rn = "BadInstrP";
+ register_name = "BadInstrP";
break;
case 3:
/* ignored */
- rn = "BadInstrX";
+ register_name = "BadInstrX";
break;
default:
goto cp0_unimplemented;
}
break;
- case 9:
+ case CP0_REGISTER_09:
switch (sel) {
case 0:
gen_helper_mtc0_count(cpu_env, arg);
- rn = "Count";
+ register_name = "Count";
+ break;
+ case 6:
+ CP0_CHECK(ctx->saar);
+ gen_helper_mtc0_saari(cpu_env, arg);
+ register_name = "SAARI";
+ break;
+ case 7:
+ CP0_CHECK(ctx->saar);
+ gen_helper_mtc0_saar(cpu_env, arg);
+ register_name = "SAAR";
break;
- /* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
}
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
break;
- case 10:
+ case CP0_REGISTER_10:
switch (sel) {
case 0:
gen_helper_mtc0_entryhi(cpu_env, arg);
- rn = "EntryHi";
+ register_name = "EntryHi";
break;
default:
goto cp0_unimplemented;
}
break;
- case 11:
+ case CP0_REGISTER_11:
switch (sel) {
case 0:
gen_helper_mtc0_compare(cpu_env, arg);
- rn = "Compare";
+ register_name = "Compare";
break;
/* 6,7 are implementation dependent */
default:
@@ -9216,7 +9284,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
break;
- case 12:
+ case CP0_REGISTER_12:
switch (sel) {
case 0:
save_cpu_state(ctx, 1);
@@ -9224,34 +9292,34 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
/* DISAS_STOP isn't good enough here, hflags may have changed. */
gen_save_pc(ctx->base.pc_next + 4);
ctx->base.is_jmp = DISAS_EXIT;
- rn = "Status";
+ register_name = "Status";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_intctl(cpu_env, arg);
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "IntCtl";
+ register_name = "IntCtl";
break;
case 2:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_srsctl(cpu_env, arg);
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "SRSCtl";
+ register_name = "SRSCtl";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap));
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "SRSMap";
+ register_name = "SRSMap";
break;
default:
goto cp0_unimplemented;
}
break;
- case 13:
+ case CP0_REGISTER_13:
switch (sel) {
case 0:
save_cpu_state(ctx, 1);
@@ -9261,98 +9329,98 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
* translated code to check for pending interrupts. */
gen_save_pc(ctx->base.pc_next + 4);
ctx->base.is_jmp = DISAS_EXIT;
- rn = "Cause";
+ register_name = "Cause";
break;
default:
goto cp0_unimplemented;
}
break;
- case 14:
+ case CP0_REGISTER_14:
switch (sel) {
case 0:
tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC));
- rn = "EPC";
+ register_name = "EPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 15:
+ case CP0_REGISTER_15:
switch (sel) {
case 0:
/* ignored */
- rn = "PRid";
+ register_name = "PRid";
break;
case 1:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_ebase(cpu_env, arg);
- rn = "EBase";
+ register_name = "EBase";
break;
default:
goto cp0_unimplemented;
}
break;
- case 16:
+ case CP0_REGISTER_16:
switch (sel) {
case 0:
gen_helper_mtc0_config0(cpu_env, arg);
- rn = "Config";
+ register_name = "Config";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
break;
case 1:
/* ignored, read only */
- rn = "Config1";
+ register_name = "Config1";
break;
case 2:
gen_helper_mtc0_config2(cpu_env, arg);
- rn = "Config2";
+ register_name = "Config2";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
break;
case 3:
gen_helper_mtc0_config3(cpu_env, arg);
- rn = "Config3";
+ register_name = "Config3";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
break;
case 4:
/* currently ignored */
- rn = "Config4";
+ register_name = "Config4";
break;
case 5:
gen_helper_mtc0_config5(cpu_env, arg);
- rn = "Config5";
+ register_name = "Config5";
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
break;
/* 6,7 are implementation dependent */
default:
- rn = "Invalid config selector";
+ register_name = "Invalid config selector";
goto cp0_unimplemented;
}
break;
- case 17:
+ case CP0_REGISTER_17:
switch (sel) {
case 0:
gen_helper_mtc0_lladdr(cpu_env, arg);
- rn = "LLAddr";
+ register_name = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mtc0_maar(cpu_env, arg);
- rn = "MAAR";
+ register_name = "MAAR";
break;
case 2:
CP0_CHECK(ctx->mrp);
gen_helper_mtc0_maari(cpu_env, arg);
- rn = "MAARI";
+ register_name = "MAARI";
break;
default:
goto cp0_unimplemented;
}
break;
- case 18:
+ case CP0_REGISTER_18:
switch (sel) {
case 0:
case 1:
@@ -9364,13 +9432,13 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 7:
CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR));
gen_helper_0e1i(mtc0_watchlo, arg, sel);
- rn = "WatchLo";
+ register_name = "WatchLo";
break;
default:
goto cp0_unimplemented;
}
break;
- case 19:
+ case CP0_REGISTER_19:
switch (sel) {
case 0:
case 1:
@@ -9382,206 +9450,206 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 7:
CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR));
gen_helper_0e1i(mtc0_watchhi, arg, sel);
- rn = "WatchHi";
+ register_name = "WatchHi";
break;
default:
goto cp0_unimplemented;
}
break;
- case 20:
+ case CP0_REGISTER_20:
switch (sel) {
case 0:
check_insn(ctx, ISA_MIPS3);
gen_helper_mtc0_xcontext(cpu_env, arg);
- rn = "XContext";
+ register_name = "XContext";
break;
default:
goto cp0_unimplemented;
}
break;
- case 21:
+ case CP0_REGISTER_21:
/* Officially reserved, but sel 0 is used for R1x000 framemask */
CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6));
switch (sel) {
case 0:
gen_helper_mtc0_framemask(cpu_env, arg);
- rn = "Framemask";
+ register_name = "Framemask";
break;
default:
goto cp0_unimplemented;
}
break;
- case 22:
+ case CP0_REGISTER_22:
/* ignored */
- rn = "Diagnostic"; /* implementation dependent */
+ register_name = "Diagnostic"; /* implementation dependent */
break;
- case 23:
+ case CP0_REGISTER_23:
switch (sel) {
case 0:
gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */
/* DISAS_STOP isn't good enough here, hflags may have changed. */
gen_save_pc(ctx->base.pc_next + 4);
ctx->base.is_jmp = DISAS_EXIT;
- rn = "Debug";
+ register_name = "Debug";
break;
case 1:
// gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "TraceControl";
+ register_name = "TraceControl";
goto cp0_unimplemented;
case 2:
// gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "TraceControl2";
+ register_name = "TraceControl2";
goto cp0_unimplemented;
case 3:
// gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "UserTraceData";
+ register_name = "UserTraceData";
goto cp0_unimplemented;
case 4:
// gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */
/* Stop translation as we may have switched the execution mode */
ctx->base.is_jmp = DISAS_STOP;
- rn = "TraceBPC";
+ register_name = "TraceBPC";
goto cp0_unimplemented;
default:
goto cp0_unimplemented;
}
break;
- case 24:
+ case CP0_REGISTER_24:
switch (sel) {
case 0:
/* EJTAG support */
tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC));
- rn = "DEPC";
+ register_name = "DEPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 25:
+ case CP0_REGISTER_25:
switch (sel) {
case 0:
gen_helper_mtc0_performance0(cpu_env, arg);
- rn = "Performance0";
+ register_name = "Performance0";
break;
case 1:
// gen_helper_mtc0_performance1(cpu_env, arg);
- rn = "Performance1";
+ register_name = "Performance1";
goto cp0_unimplemented;
case 2:
// gen_helper_mtc0_performance2(cpu_env, arg);
- rn = "Performance2";
+ register_name = "Performance2";
goto cp0_unimplemented;
case 3:
// gen_helper_mtc0_performance3(cpu_env, arg);
- rn = "Performance3";
+ register_name = "Performance3";
goto cp0_unimplemented;
case 4:
// gen_helper_mtc0_performance4(cpu_env, arg);
- rn = "Performance4";
+ register_name = "Performance4";
goto cp0_unimplemented;
case 5:
// gen_helper_mtc0_performance5(cpu_env, arg);
- rn = "Performance5";
+ register_name = "Performance5";
goto cp0_unimplemented;
case 6:
// gen_helper_mtc0_performance6(cpu_env, arg);
- rn = "Performance6";
+ register_name = "Performance6";
goto cp0_unimplemented;
case 7:
// gen_helper_mtc0_performance7(cpu_env, arg);
- rn = "Performance7";
+ register_name = "Performance7";
goto cp0_unimplemented;
default:
goto cp0_unimplemented;
}
break;
- case 26:
+ case CP0_REGISTER_26:
switch (sel) {
case 0:
gen_helper_mtc0_errctl(cpu_env, arg);
ctx->base.is_jmp = DISAS_STOP;
- rn = "ErrCtl";
+ register_name = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break;
- case 27:
+ case CP0_REGISTER_27:
switch (sel) {
case 0:
case 1:
case 2:
case 3:
/* ignored */
- rn = "CacheErr";
+ register_name = "CacheErr";
break;
default:
goto cp0_unimplemented;
}
break;
- case 28:
+ case CP0_REGISTER_28:
switch (sel) {
case 0:
case 2:
case 4:
case 6:
gen_helper_mtc0_taglo(cpu_env, arg);
- rn = "TagLo";
+ register_name = "TagLo";
break;
case 1:
case 3:
case 5:
case 7:
gen_helper_mtc0_datalo(cpu_env, arg);
- rn = "DataLo";
+ register_name = "DataLo";
break;
default:
goto cp0_unimplemented;
}
break;
- case 29:
+ case CP0_REGISTER_29:
switch (sel) {
case 0:
case 2:
case 4:
case 6:
gen_helper_mtc0_taghi(cpu_env, arg);
- rn = "TagHi";
+ register_name = "TagHi";
break;
case 1:
case 3:
case 5:
case 7:
gen_helper_mtc0_datahi(cpu_env, arg);
- rn = "DataHi";
+ register_name = "DataHi";
break;
default:
- rn = "invalid sel";
+ register_name = "invalid sel";
goto cp0_unimplemented;
}
break;
- case 30:
+ case CP0_REGISTER_30:
switch (sel) {
case 0:
tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC));
- rn = "ErrorEPC";
+ register_name = "ErrorEPC";
break;
default:
goto cp0_unimplemented;
}
break;
- case 31:
+ case CP0_REGISTER_31:
switch (sel) {
case 0:
/* EJTAG support */
gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_DESAVE));
- rn = "DESAVE";
+ register_name = "DESAVE";
break;
case 2:
case 3:
@@ -9592,7 +9660,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
CP0_CHECK(ctx->kscrexist & (1 << sel));
tcg_gen_st_tl(arg, cpu_env,
offsetof(CPUMIPSState, CP0_KScratch[sel-2]));
- rn = "KScratch";
+ register_name = "KScratch";
break;
default:
goto cp0_unimplemented;
@@ -9601,7 +9669,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
default:
goto cp0_unimplemented;
}
- trace_mips_translate_c0("dmtc0", rn, reg, sel);
+ trace_mips_translate_c0("dmtc0", register_name, reg, sel);
/* For simplicity assume that all writes can cause interrupts. */
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
@@ -9614,7 +9682,8 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
return;
cp0_unimplemented:
- qemu_log_mask(LOG_UNIMP, "dmtc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ qemu_log_mask(LOG_UNIMP, "dmtc0 %s (reg %d sel %d)\n",
+ register_name, reg, sel);
}
#endif /* TARGET_MIPS64 */
@@ -29781,6 +29850,17 @@ void mips_tcg_init(void)
fpu_fcr31 = tcg_global_mem_new_i32(cpu_env,
offsetof(CPUMIPSState, active_fpu.fcr31),
"fcr31");
+
+#if defined(TARGET_MIPS64)
+ cpu_mmr[0] = NULL;
+ for (i = 1; i < 32; i++) {
+ cpu_mmr[i] = tcg_global_mem_new_i64(cpu_env,
+ offsetof(CPUMIPSState,
+ active_tc.mmr[i]),
+ regnames[i]);
+ }
+#endif
+
#if !defined(TARGET_MIPS64)
for (i = 0; i < NUMBER_OF_MXU_REGISTERS - 1; i++) {
mxu_gpr[i] = tcg_global_mem_new(cpu_env,
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 486abaf99b..a62ff60414 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1177,8 +1177,9 @@ do { \
typedef struct PPCVirtualHypervisor PPCVirtualHypervisor;
typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass;
-typedef struct XiveTCTX XiveTCTX;
-typedef struct ICPState ICPState;
+
+struct XiveTCTX;
+struct ICPState;
/**
* PowerPCCPU:
@@ -1197,8 +1198,8 @@ struct PowerPCCPU {
int vcpu_id;
uint32_t compat_pvr;
PPCVirtualHypervisor *vhyp;
- ICPState *icp;
- XiveTCTX *tctx;
+ struct ICPState *icp;
+ struct XiveTCTX *tctx;
void *machine_data;
int32_t node_id; /* NUMA node this CPU belongs to */
PPCHash64Options *hash64_opts;
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index acb0f3d4af..aafa740f61 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -130,7 +130,7 @@ out:
}
return;
default:
- hw_error("Unhandled diag308 subcode %" PRIx64, subcode);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
break;
}
}
diff --git a/tests/Makefile.include b/tests/Makefile.include
index f403a6571d..4eea38ae99 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -3,7 +3,8 @@
check-help:
@echo "Regression testing targets:"
@echo
- @echo " $(MAKE) check Run all tests"
+ @echo " $(MAKE) check Run unit, qapi-schema, qtest and decodetree"
+ @echo
@echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target"
@echo " $(MAKE) check-qtest Run qtest tests"
@echo " $(MAKE) check-unit Run qobject tests"
@@ -12,12 +13,13 @@ check-help:
@echo " $(MAKE) check-block Run block tests"
@echo " $(MAKE) check-tcg Run TCG tests"
@echo " $(MAKE) check-acceptance Run all acceptance (functional) tests"
+ @echo
@echo " $(MAKE) check-report.html Generates an HTML test report"
@echo " $(MAKE) check-venv Creates a Python venv for tests"
- @echo " $(MAKE) check-clean Clean the tests"
+ @echo " $(MAKE) check-clean Clean the tests and related data"
@echo
@echo "Please note that HTML reports do not regenerate if the unit tests"
- @echo "has not changed."
+ @echo "have not changed."
@echo
@echo "The variable SPEED can be set to control the gtester speed setting."
@echo "Default options are -k and (for $(MAKE) V=1) --verbose; they can be"
@@ -107,7 +109,7 @@ check-unit-y += tests/test-crypto-secret$(EXESUF)
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF)
ifneq (,$(findstring qemu-ga,$(TOOLS)))
-check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF)
+check-unit-$(land,$(CONFIG_LINUX),$(CONFIG_VIRTIO_SERIAL)) += tests/test-qga$(EXESUF)
endif
check-unit-y += tests/test-timed-average$(EXESUF)
check-unit-y += tests/test-util-sockets$(EXESUF)
@@ -143,17 +145,17 @@ check-qtest-generic-y += tests/cdrom-test$(EXESUF)
check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF)
-check-qtest-virtioserial-y += tests/virtio-console-test$(EXESUF)
+check-qtest-virtioserial-$(CONFIG_VIRTIO_SERIAL) += tests/virtio-console-test$(EXESUF)
-check-qtest-virtio-y += tests/virtio-net-test$(EXESUF)
-check-qtest-virtio-y += tests/virtio-balloon-test$(EXESUF)
-check-qtest-virtio-y += tests/virtio-blk-test$(EXESUF)
-check-qtest-virtio-y += tests/virtio-rng-test$(EXESUF)
-check-qtest-virtio-y += tests/virtio-scsi-test$(EXESUF)
+check-qtest-virtio-$(CONFIG_VIRTIO_NET) += tests/virtio-net-test$(EXESUF)
+check-qtest-virtio-$(CONFIG_VIRTIO_BALLOON) += tests/virtio-balloon-test$(EXESUF)
+check-qtest-virtio-$(CONFIG_VIRTIO_BLK) += tests/virtio-blk-test$(EXESUF)
+check-qtest-virtio-$(CONFIG_VIRTIO_RNG) += tests/virtio-rng-test$(EXESUF)
+check-qtest-virtio-$(CONFIG_VIRTIO_SCSI) += tests/virtio-scsi-test$(EXESUF)
ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
-check-qtest-virtio-y += tests/virtio-9p-test$(EXESUF)
+check-qtest-virtio-$(CONFIG_VIRTIO_9P) += tests/virtio-9p-test$(EXESUF)
endif
-check-qtest-virtio-y += tests/virtio-serial-test$(EXESUF)
+check-qtest-virtio-$(CONFIG_VIRTIO_SERIAL) += tests/virtio-serial-test$(EXESUF)
check-qtest-virtio-y += $(check-qtest-virtioserial-y)
check-qtest-pci-y += tests/e1000-test$(EXESUF)
@@ -165,13 +167,13 @@ check-qtest-pci-$(CONFIG_NE2000_PCI) += tests/ne2000-test$(EXESUF)
check-qtest-pci-$(CONFIG_NVME_PCI) += tests/nvme-test$(EXESUF)
check-qtest-pci-$(CONFIG_AC97) += tests/ac97-test$(EXESUF)
check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF)
-check-qtest-pci-y += $(check-qtest-virtio-y)
+check-qtest-pci-$(CONFIG_VIRTIO) += $(check-qtest-virtio-y)
check-qtest-pci-$(CONFIG_IPACK) += tests/tpci200-test$(EXESUF)
check-qtest-pci-$(CONFIG_IPACK) += $(check-qtest-ipack-y)
-check-qtest-pci-y += tests/display-vga-test$(EXESUF)
+check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF)
check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF)
check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF)
-check-qtest-pci-y += tests/megasas-test$(EXESUF)
+check-qtest-pci-$(CONFIG_MEGASAS_SCSI_PCI) += tests/megasas-test$(EXESUF)
check-qtest-i386-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF)
check-qtest-i386-y += tests/fdc-test$(EXESUF)
@@ -183,8 +185,9 @@ check-qtest-i386-y += tests/bios-tables-test$(EXESUF)
check-qtest-i386-$(CONFIG_SGA) += tests/boot-serial-test$(EXESUF)
check-qtest-i386-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF)
check-qtest-i386-y += tests/rtc-test$(EXESUF)
-check-qtest-i386-y += tests/ipmi-kcs-test$(EXESUF)
-check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF)
+check-qtest-i386-$(CONFIG_ISA_IPMI_KCS) += tests/ipmi-kcs-test$(EXESUF)
+# Disabled temporarily as it fails intermittently especially under NetBSD VM
+# check-qtest-i386-$(CONFIG_ISA_IPMI_BT) += tests/ipmi-bt-test$(EXESUF)
check-qtest-i386-y += tests/i440fx-test$(EXESUF)
check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
check-qtest-i386-y += tests/drive_del-test$(EXESUF)
@@ -247,22 +250,22 @@ check-qtest-ppc-y += tests/boot-order-test$(EXESUF)
check-qtest-ppc-y += tests/prom-env-test$(EXESUF)
check-qtest-ppc-y += tests/drive_del-test$(EXESUF)
check-qtest-ppc-y += tests/boot-serial-test$(EXESUF)
-check-qtest-ppc-y += tests/m48t59-test$(EXESUF)
+check-qtest-ppc-$(CONFIG_M48T59) += tests/m48t59-test$(EXESUF)
check-qtest-ppc64-y += $(check-qtest-ppc-y)
-check-qtest-ppc64-y += tests/spapr-phb-test$(EXESUF)
-check-qtest-ppc64-y += tests/pnv-xscom-test$(EXESUF)
+check-qtest-ppc64-$(CONFIG_PSERIES) += tests/spapr-phb-test$(EXESUF)
+check-qtest-ppc64-$(CONFIG_POWERNV) += tests/pnv-xscom-test$(EXESUF)
check-qtest-ppc64-y += tests/migration-test$(EXESUF)
-check-qtest-ppc64-y += tests/rtas-test$(EXESUF)
+check-qtest-ppc64-$(CONFIG_PSERIES) += tests/rtas-test$(EXESUF)
check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF)
check-qtest-ppc64-$(CONFIG_USB_OHCI) += tests/usb-hcd-ohci-test$(EXESUF)
check-qtest-ppc64-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF)
check-qtest-ppc64-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF)
-check-qtest-ppc64-y += $(check-qtest-virtio-y)
+check-qtest-ppc64-$(CONFIG_VIRTIO) += $(check-qtest-virtio-y)
check-qtest-ppc64-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF)
check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF)
check-qtest-ppc64-$(CONFIG_RTL8139_PCI) += tests/test-filter-redirector$(EXESUF)
-check-qtest-ppc64-y += tests/display-vga-test$(EXESUF)
+check-qtest-ppc64-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF)
check-qtest-ppc64-y += tests/numa-test$(EXESUF)
check-qtest-ppc64-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF)
check-qtest-ppc64-y += tests/cpu-plug-test$(EXESUF)
@@ -284,7 +287,7 @@ check-qtest-arm-y += tests/pca9552-test$(EXESUF)
check-qtest-arm-y += tests/ds1338-test$(EXESUF)
check-qtest-arm-y += tests/microbit-test$(EXESUF)
check-qtest-arm-y += tests/m25p80-test$(EXESUF)
-check-qtest-arm-y += tests/virtio-blk-test$(EXESUF)
+check-qtest-arm-$(CONFIG_VIRTIO_BLK) += tests/virtio-blk-test$(EXESUF)
check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF)
check-qtest-arm-y += tests/boot-serial-test$(EXESUF)
check-qtest-arm-$(CONFIG_SDHCI) += tests/sdhci-test$(EXESUF)
@@ -745,7 +748,6 @@ tests/qom-test$(EXESUF): tests/qom-test.o
tests/test-hmp$(EXESUF): tests/test-hmp.o
tests/machine-none-test$(EXESUF): tests/machine-none-test.o
tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-virtio-obj-y)
-tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y)
tests/nvme-test$(EXESUF): tests/nvme-test.o $(libqos-pc-obj-y)
tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o
tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o
@@ -958,8 +960,7 @@ TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results
# information please refer to "avocado --help".
AVOCADO_SHOW=none
-PYTHON3 = $(shell $(PYTHON) -c 'import sys; print(1 if sys.version_info >= (3, 0) else 0)')
-ifeq ($(PYTHON3), 1)
+ifneq ($(findstring v2,"v$(PYTHON_VERSION)"),v2)
$(TESTS_VENV_DIR): $(TESTS_VENV_REQ)
$(call quiet-command, \
$(PYTHON) -m venv --system-site-packages $@, \
diff --git a/tests/acceptance/linux_initrd.py b/tests/acceptance/linux_initrd.py
new file mode 100644
index 0000000000..737355c2ef
--- /dev/null
+++ b/tests/acceptance/linux_initrd.py
@@ -0,0 +1,48 @@
+# Linux initrd acceptance test.
+#
+# Copyright (c) 2018 Red Hat, Inc.
+#
+# Author:
+# Wainer dos Santos Moschetta <wainersm@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import tempfile
+from avocado.utils.process import run
+
+from avocado_qemu import Test
+
+
+class LinuxInitrd(Test):
+ """
+ Checks QEMU evaluates correctly the initrd file passed as -initrd option.
+
+ :avocado: enable
+ :avocado: tags=x86_64
+ """
+
+ timeout = 60
+
+ def test_with_2gib_file_should_exit_error_msg(self):
+ """
+ Pretends to boot QEMU with an initrd file with size of 2GiB
+ and expect it exits with error message.
+ """
+ kernel_url = ('https://mirrors.kernel.org/fedora/releases/28/'
+ 'Everything/x86_64/os/images/pxeboot/vmlinuz')
+ kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ max_size = 2 * (1024 ** 3) - 1
+
+ with tempfile.NamedTemporaryFile() as initrd:
+ initrd.seek(max_size)
+ initrd.write(b'\0')
+ initrd.flush()
+ cmd = "%s -kernel %s -initrd %s" % (self.qemu_bin, kernel_path,
+ initrd.name)
+ res = run(cmd, ignore_status=True)
+ self.assertEqual(res.exit_status, 1)
+ expected_msg = r'.*initrd is too large.*max: \d+, need %s.*' % (
+ max_size + 1)
+ self.assertRegex(res.stderr_text, expected_msg)
diff --git a/tests/acpi-utils.c b/tests/acpi-utils.c
index 17abcc43a4..cc33b460ab 100644
--- a/tests/acpi-utils.c
+++ b/tests/acpi-utils.c
@@ -51,14 +51,6 @@ uint32_t acpi_find_rsdp_address(QTestState *qts)
return off;
}
-uint32_t acpi_get_rsdt_address(uint8_t *rsdp_table)
-{
- uint32_t rsdt_physical_address;
-
- memcpy(&rsdt_physical_address, &rsdp_table[16 /* RsdtAddress offset */], 4);
- return le32_to_cpu(rsdt_physical_address);
-}
-
uint64_t acpi_get_xsdt_address(uint8_t *rsdp_table)
{
uint64_t xsdt_physical_address;
@@ -92,3 +84,30 @@ void acpi_parse_rsdp_table(QTestState *qts, uint32_t addr, uint8_t *rsdp_table)
ACPI_ASSERT_CMP64(*((uint64_t *)(rsdp_table)), "RSD PTR ");
}
+
+/** acpi_fetch_table
+ * load ACPI table at @addr_ptr offset pointer into buffer and return it in
+ * @aml, its length in @aml_len and check that signature/checksum matches
+ * actual one.
+ */
+void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len,
+ const uint8_t *addr_ptr, const char *sig,
+ bool verify_checksum)
+{
+ uint32_t addr, len;
+
+ memcpy(&addr, addr_ptr , sizeof(addr));
+ addr = le32_to_cpu(addr);
+ qtest_memread(qts, addr + 4, &len, 4); /* Length of ACPI table */
+ *aml_len = le32_to_cpu(len);
+ *aml = g_malloc0(*aml_len);
+ /* get whole table */
+ qtest_memread(qts, addr, *aml, *aml_len);
+
+ if (sig) {
+ ACPI_ASSERT_CMP(**aml, sig);
+ }
+ if (verify_checksum) {
+ g_assert(!acpi_calc_checksum(*aml, *aml_len));
+ }
+}
diff --git a/tests/acpi-utils.h b/tests/acpi-utils.h
index c5b0e12aa2..ef388bbf12 100644
--- a/tests/acpi-utils.h
+++ b/tests/acpi-utils.h
@@ -13,14 +13,12 @@
#ifndef TEST_ACPI_UTILS_H
#define TEST_ACPI_UTILS_H
-#include "hw/acpi/acpi-defs.h"
#include "libqtest.h"
/* DSDT and SSDTs format */
typedef struct {
- AcpiTableHeader header;
- gchar *aml; /* aml bytecode from guest */
- gsize aml_len;
+ uint8_t *aml; /* aml bytecode from guest */
+ uint32_t aml_len;
gchar *aml_file;
gchar *asl; /* asl code generated from aml */
gsize asl_len;
@@ -28,36 +26,6 @@ typedef struct {
bool tmp_files_retain; /* do not delete the temp asl/aml */
} AcpiSdtTable;
-#define ACPI_READ_FIELD(qts, field, addr) \
- do { \
- qtest_memread(qts, addr, &field, sizeof(field)); \
- addr += sizeof(field); \
- } while (0)
-
-#define ACPI_READ_ARRAY_PTR(qts, arr, length, addr) \
- do { \
- int idx; \
- for (idx = 0; idx < length; ++idx) { \
- ACPI_READ_FIELD(qts, arr[idx], addr); \
- } \
- } while (0)
-
-#define ACPI_READ_ARRAY(qts, arr, addr) \
- ACPI_READ_ARRAY_PTR(qts, arr, sizeof(arr) / sizeof(arr[0]), addr)
-
-#define ACPI_READ_TABLE_HEADER(qts, table, addr) \
- do { \
- ACPI_READ_FIELD(qts, (table)->signature, addr); \
- ACPI_READ_FIELD(qts, (table)->length, addr); \
- ACPI_READ_FIELD(qts, (table)->revision, addr); \
- ACPI_READ_FIELD(qts, (table)->checksum, addr); \
- ACPI_READ_ARRAY(qts, (table)->oem_id, addr); \
- ACPI_READ_ARRAY(qts, (table)->oem_table_id, addr); \
- ACPI_READ_FIELD(qts, (table)->oem_revision, addr); \
- ACPI_READ_ARRAY(qts, (table)->asl_compiler_id, addr); \
- ACPI_READ_FIELD(qts, (table)->asl_compiler_revision, addr); \
- } while (0)
-
#define ACPI_ASSERT_CMP(actual, expected) do { \
char ACPI_ASSERT_CMP_str[5] = {}; \
memcpy(ACPI_ASSERT_CMP_str, &actual, 4); \
@@ -71,11 +39,17 @@ typedef struct {
} while (0)
+#define ACPI_FOREACH_RSDT_ENTRY(table, table_len, entry_ptr, entry_size) \
+ for (entry_ptr = table + 36 /* 1st Entry */; \
+ entry_ptr < table + table_len; \
+ entry_ptr += entry_size)
uint8_t acpi_calc_checksum(const uint8_t *data, int len);
uint32_t acpi_find_rsdp_address(QTestState *qts);
-uint32_t acpi_get_rsdt_address(uint8_t *rsdp_table);
uint64_t acpi_get_xsdt_address(uint8_t *rsdp_table);
void acpi_parse_rsdp_table(QTestState *qts, uint32_t addr, uint8_t *rsdp_table);
+void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len,
+ const uint8_t *addr_ptr, const char *sig,
+ bool verify_checksum);
#endif /* TEST_ACPI_UTILS_H */
diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c
index d455b2abfc..a506dcbb29 100644
--- a/tests/bios-tables-test.c
+++ b/tests/bios-tables-test.c
@@ -28,12 +28,6 @@ typedef struct {
const char *variant;
uint32_t rsdp_addr;
uint8_t rsdp_table[36 /* ACPI 2.0+ RSDP size */];
- AcpiRsdtDescriptorRev1 rsdt_table;
- uint32_t dsdt_addr;
- uint32_t facs_addr;
- AcpiFacsDescriptorRev1 facs_table;
- uint32_t *rsdt_tables_addr;
- int rsdt_tables_nr;
GArray *tables;
uint32_t smbios_ep_addr;
struct smbios_21_entry_point smbios_ep_table;
@@ -50,28 +44,34 @@ static const char *iasl = stringify(CONFIG_IASL);
static const char *iasl;
#endif
+static bool compare_signature(const AcpiSdtTable *sdt, const char *signature)
+{
+ return !memcmp(sdt->aml, signature, 4);
+}
+
+static void cleanup_table_descriptor(AcpiSdtTable *table)
+{
+ g_free(table->aml);
+ if (table->aml_file &&
+ !table->tmp_files_retain &&
+ g_strstr_len(table->aml_file, -1, "aml-")) {
+ unlink(table->aml_file);
+ }
+ g_free(table->aml_file);
+ g_free(table->asl);
+ if (table->asl_file &&
+ !table->tmp_files_retain) {
+ unlink(table->asl_file);
+ }
+ g_free(table->asl_file);
+}
+
static void free_test_data(test_data *data)
{
- AcpiSdtTable *temp;
int i;
- g_free(data->rsdt_tables_addr);
-
for (i = 0; i < data->tables->len; ++i) {
- temp = &g_array_index(data->tables, AcpiSdtTable, i);
- g_free(temp->aml);
- if (temp->aml_file &&
- !temp->tmp_files_retain &&
- g_strstr_len(temp->aml_file, -1, "aml-")) {
- unlink(temp->aml_file);
- }
- g_free(temp->aml_file);
- g_free(temp->asl);
- if (temp->asl_file &&
- !temp->tmp_files_retain) {
- unlink(temp->asl_file);
- }
- g_free(temp->asl_file);
+ cleanup_table_descriptor(&g_array_index(data->tables, AcpiSdtTable, i));
}
g_array_free(data->tables, true);
@@ -109,154 +109,53 @@ static void test_acpi_rsdp_table(test_data *data)
static void test_acpi_rsdt_table(test_data *data)
{
- AcpiRsdtDescriptorRev1 *rsdt_table = &data->rsdt_table;
- uint32_t addr = acpi_get_rsdt_address(data->rsdp_table);
- uint32_t *tables;
- int tables_nr;
- uint8_t checksum;
- uint32_t rsdt_table_length;
-
- /* read the header */
- ACPI_READ_TABLE_HEADER(data->qts, rsdt_table, addr);
- ACPI_ASSERT_CMP(rsdt_table->signature, "RSDT");
-
- rsdt_table_length = le32_to_cpu(rsdt_table->length);
-
- /* compute the table entries in rsdt */
- tables_nr = (rsdt_table_length - sizeof(AcpiRsdtDescriptorRev1)) /
- sizeof(uint32_t);
- g_assert(tables_nr > 0);
+ AcpiSdtTable rsdt = {};
+ uint8_t *ent;
- /* get the addresses of the tables pointed by rsdt */
- tables = g_new0(uint32_t, tables_nr);
- ACPI_READ_ARRAY_PTR(data->qts, tables, tables_nr, addr);
+ /* read RSDT table */
+ acpi_fetch_table(data->qts, &rsdt.aml, &rsdt.aml_len,
+ &data->rsdp_table[16 /* RsdtAddress */], "RSDT", true);
- checksum = acpi_calc_checksum((uint8_t *)rsdt_table, rsdt_table_length) +
- acpi_calc_checksum((uint8_t *)tables,
- tables_nr * sizeof(uint32_t));
- g_assert(!checksum);
+ /* Load all tables and add to test list directly RSDT referenced tables */
+ ACPI_FOREACH_RSDT_ENTRY(rsdt.aml, rsdt.aml_len, ent, 4 /* Entry size */) {
+ AcpiSdtTable ssdt_table = {};
- /* SSDT tables after FADT */
- data->rsdt_tables_addr = tables;
- data->rsdt_tables_nr = tables_nr;
-}
-
-static void fadt_fetch_facs_and_dsdt_ptrs(test_data *data)
-{
- uint32_t addr;
- AcpiTableHeader hdr;
-
- /* FADT table comes first */
- addr = le32_to_cpu(data->rsdt_tables_addr[0]);
- ACPI_READ_TABLE_HEADER(data->qts, &hdr, addr);
- ACPI_ASSERT_CMP(hdr.signature, "FACP");
-
- ACPI_READ_FIELD(data->qts, data->facs_addr, addr);
- ACPI_READ_FIELD(data->qts, data->dsdt_addr, addr);
-}
-
-static void sanitize_fadt_ptrs(test_data *data)
-{
- /* fixup pointers in FADT */
- int i;
-
- for (i = 0; i < data->tables->len; i++) {
- AcpiSdtTable *sdt = &g_array_index(data->tables, AcpiSdtTable, i);
-
- if (memcmp(&sdt->header.signature, "FACP", 4)) {
- continue;
- }
-
- /* check original FADT checksum before sanitizing table */
- g_assert(!(uint8_t)(
- acpi_calc_checksum((uint8_t *)sdt, sizeof(AcpiTableHeader)) +
- acpi_calc_checksum((uint8_t *)sdt->aml, sdt->aml_len)
- ));
-
- /* sdt->aml field offset := spec offset - header size */
- memset(sdt->aml + 0, 0, 4); /* sanitize FIRMWARE_CTRL(36) ptr */
- memset(sdt->aml + 4, 0, 4); /* sanitize DSDT(40) ptr */
- if (sdt->header.revision >= 3) {
- memset(sdt->aml + 96, 0, 8); /* sanitize X_FIRMWARE_CTRL(132) ptr */
- memset(sdt->aml + 104, 0, 8); /* sanitize X_DSDT(140) ptr */
- }
-
- /* update checksum */
- sdt->header.checksum = 0;
- sdt->header.checksum -=
- acpi_calc_checksum((uint8_t *)sdt, sizeof(AcpiTableHeader)) +
- acpi_calc_checksum((uint8_t *)sdt->aml, sdt->aml_len);
- break;
+ acpi_fetch_table(data->qts, &ssdt_table.aml, &ssdt_table.aml_len, ent,
+ NULL, true);
+ /* Add table to ASL test tables list */
+ g_array_append_val(data->tables, ssdt_table);
}
+ cleanup_table_descriptor(&rsdt);
}
-static void test_acpi_facs_table(test_data *data)
-{
- AcpiFacsDescriptorRev1 *facs_table = &data->facs_table;
- uint32_t addr = le32_to_cpu(data->facs_addr);
-
- ACPI_READ_FIELD(data->qts, facs_table->signature, addr);
- ACPI_READ_FIELD(data->qts, facs_table->length, addr);
- ACPI_READ_FIELD(data->qts, facs_table->hardware_signature, addr);
- ACPI_READ_FIELD(data->qts, facs_table->firmware_waking_vector, addr);
- ACPI_READ_FIELD(data->qts, facs_table->global_lock, addr);
- ACPI_READ_FIELD(data->qts, facs_table->flags, addr);
- ACPI_READ_ARRAY(data->qts, facs_table->resverved3, addr);
-
- ACPI_ASSERT_CMP(facs_table->signature, "FACS");
-}
-
-/** fetch_table
- * load ACPI table at @addr into table descriptor @sdt_table
- * and check that header checksum matches actual one.
- */
-static void fetch_table(QTestState *qts, AcpiSdtTable *sdt_table, uint32_t addr)
-{
- uint8_t checksum;
-
- memset(sdt_table, 0, sizeof(*sdt_table));
- ACPI_READ_TABLE_HEADER(qts, &sdt_table->header, addr);
-
- sdt_table->aml_len = le32_to_cpu(sdt_table->header.length)
- - sizeof(AcpiTableHeader);
- sdt_table->aml = g_malloc0(sdt_table->aml_len);
- ACPI_READ_ARRAY_PTR(qts, sdt_table->aml, sdt_table->aml_len, addr);
-
- checksum = acpi_calc_checksum((uint8_t *)sdt_table,
- sizeof(AcpiTableHeader)) +
- acpi_calc_checksum((uint8_t *)sdt_table->aml,
- sdt_table->aml_len);
- g_assert(!checksum);
-}
-
-static void test_acpi_dsdt_table(test_data *data)
+static void test_acpi_fadt_table(test_data *data)
{
- AcpiSdtTable dsdt_table;
- uint32_t addr = le32_to_cpu(data->dsdt_addr);
+ /* FADT table is 1st */
+ AcpiSdtTable table = g_array_index(data->tables, typeof(table), 0);
+ uint8_t *fadt_aml = table.aml;
+ uint32_t fadt_len = table.aml_len;
- fetch_table(data->qts, &dsdt_table, addr);
- ACPI_ASSERT_CMP(dsdt_table.header.signature, "DSDT");
-
- /* Since DSDT isn't in RSDT, add DSDT to ASL test tables list manually */
- g_array_append_val(data->tables, dsdt_table);
-}
-
-/* Load all tables and add to test list directly RSDT referenced tables */
-static void fetch_rsdt_referenced_tables(test_data *data)
-{
- int tables_nr = data->rsdt_tables_nr;
- int i;
+ g_assert(compare_signature(&table, "FACP"));
- for (i = 0; i < tables_nr; i++) {
- AcpiSdtTable ssdt_table;
- uint32_t addr;
+ /* Since DSDT/FACS isn't in RSDT, add them to ASL test list manually */
+ acpi_fetch_table(data->qts, &table.aml, &table.aml_len,
+ fadt_aml + 36 /* FIRMWARE_CTRL */, "FACS", false);
+ g_array_append_val(data->tables, table);
- addr = le32_to_cpu(data->rsdt_tables_addr[i]);
- fetch_table(data->qts, &ssdt_table, addr);
+ acpi_fetch_table(data->qts, &table.aml, &table.aml_len,
+ fadt_aml + 40 /* DSDT */, "DSDT", true);
+ g_array_append_val(data->tables, table);
- /* Add table to ASL test tables list */
- g_array_append_val(data->tables, ssdt_table);
+ memset(fadt_aml + 36, 0, 4); /* sanitize FIRMWARE_CTRL ptr */
+ memset(fadt_aml + 40, 0, 4); /* sanitize DSDT ptr */
+ if (fadt_aml[8 /* FADT Major Version */] >= 3) {
+ memset(fadt_aml + 132, 0, 8); /* sanitize X_FIRMWARE_CTRL ptr */
+ memset(fadt_aml + 140, 0, 8); /* sanitize X_DSDT ptr */
}
+
+ /* update checksum */
+ fadt_aml[9 /* Checksum */] = 0;
+ fadt_aml[9 /* Checksum */] -= acpi_calc_checksum(fadt_aml, fadt_len);
}
static void dump_aml_files(test_data *data, bool rebuild)
@@ -275,7 +174,7 @@ static void dump_aml_files(test_data *data, bool rebuild)
if (rebuild) {
aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine,
- (gchar *)&sdt->header.signature, ext);
+ sdt->aml, ext);
fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT,
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
} else {
@@ -284,8 +183,6 @@ static void dump_aml_files(test_data *data, bool rebuild)
}
g_assert(fd >= 0);
- ret = qemu_write_full(fd, sdt, sizeof(AcpiTableHeader));
- g_assert(ret == sizeof(AcpiTableHeader));
ret = qemu_write_full(fd, sdt->aml, sdt->aml_len);
g_assert(ret == sdt->aml_len);
@@ -295,11 +192,6 @@ static void dump_aml_files(test_data *data, bool rebuild)
}
}
-static bool compare_signature(AcpiSdtTable *sdt, const char *signature)
-{
- return !memcmp(&sdt->header.signature, signature, 4);
-}
-
static bool load_asl(GArray *sdts, AcpiSdtTable *sdt)
{
AcpiSdtTable *temp;
@@ -382,6 +274,7 @@ static GArray *load_expected_aml(test_data *data)
AcpiSdtTable *sdt;
GError *error = NULL;
gboolean ret;
+ gsize aml_len;
GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable));
if (getenv("V")) {
@@ -395,11 +288,10 @@ static GArray *load_expected_aml(test_data *data)
sdt = &g_array_index(data->tables, AcpiSdtTable, i);
memset(&exp_sdt, 0, sizeof(exp_sdt));
- exp_sdt.header.signature = sdt->header.signature;
try_again:
aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine,
- (gchar *)&sdt->header.signature, ext);
+ sdt->aml, ext);
if (getenv("V")) {
fprintf(stderr, "Looking for expected file '%s'\n", aml_file);
}
@@ -415,8 +307,9 @@ try_again:
if (getenv("V")) {
fprintf(stderr, "Using expected file '%s'\n", aml_file);
}
- ret = g_file_get_contents(aml_file, &exp_sdt.aml,
- &exp_sdt.aml_len, &error);
+ ret = g_file_get_contents(aml_file, (gchar **)&exp_sdt.aml,
+ &aml_len, &error);
+ exp_sdt.aml_len = aml_len;
g_assert(ret);
g_assert_no_error(error);
g_assert(exp_sdt.aml);
@@ -459,14 +352,12 @@ static void test_acpi_asl(test_data *data)
fprintf(stderr,
"Warning! iasl couldn't parse the expected aml\n");
} else {
- uint32_t signature = cpu_to_le32(exp_sdt->header.signature);
sdt->tmp_files_retain = true;
exp_sdt->tmp_files_retain = true;
fprintf(stderr,
"acpi-test: Warning! %.4s mismatch. "
"Actual [asl:%s, aml:%s], Expected [asl:%s, aml:%s].\n",
- (gchar *)&signature,
- sdt->asl_file, sdt->aml_file,
+ exp_sdt->aml, sdt->asl_file, sdt->aml_file,
exp_sdt->asl_file, exp_sdt->aml_file);
if (getenv("V")) {
const char *diff_cmd = getenv("DIFF");
@@ -498,32 +389,19 @@ static bool smbios_ep_table_ok(test_data *data)
struct smbios_21_entry_point *ep_table = &data->smbios_ep_table;
uint32_t addr = data->smbios_ep_addr;
- ACPI_READ_ARRAY(data->qts, ep_table->anchor_string, addr);
+ qtest_memread(data->qts, addr, ep_table, sizeof(*ep_table));
if (memcmp(ep_table->anchor_string, "_SM_", 4)) {
return false;
}
- ACPI_READ_FIELD(data->qts, ep_table->checksum, addr);
- ACPI_READ_FIELD(data->qts, ep_table->length, addr);
- ACPI_READ_FIELD(data->qts, ep_table->smbios_major_version, addr);
- ACPI_READ_FIELD(data->qts, ep_table->smbios_minor_version, addr);
- ACPI_READ_FIELD(data->qts, ep_table->max_structure_size, addr);
- ACPI_READ_FIELD(data->qts, ep_table->entry_point_revision, addr);
- ACPI_READ_ARRAY(data->qts, ep_table->formatted_area, addr);
- ACPI_READ_ARRAY(data->qts, ep_table->intermediate_anchor_string, addr);
if (memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)) {
return false;
}
- ACPI_READ_FIELD(data->qts, ep_table->intermediate_checksum, addr);
- ACPI_READ_FIELD(data->qts, ep_table->structure_table_length, addr);
if (ep_table->structure_table_length == 0) {
return false;
}
- ACPI_READ_FIELD(data->qts, ep_table->structure_table_address, addr);
- ACPI_READ_FIELD(data->qts, ep_table->number_of_structures, addr);
if (ep_table->number_of_structures == 0) {
return false;
}
- ACPI_READ_FIELD(data->qts, ep_table->smbios_bcd_revision, addr);
if (acpi_calc_checksum((uint8_t *)ep_table, sizeof *ep_table) ||
acpi_calc_checksum((uint8_t *)ep_table + 0x10,
sizeof *ep_table - 0x10)) {
@@ -644,12 +522,7 @@ static void test_acpi_one(const char *params, test_data *data)
test_acpi_rsdp_address(data);
test_acpi_rsdp_table(data);
test_acpi_rsdt_table(data);
- fadt_fetch_facs_and_dsdt_ptrs(data);
- test_acpi_facs_table(data);
- test_acpi_dsdt_table(data);
- fetch_rsdt_referenced_tables(data);
-
- sanitize_fadt_ptrs(data);
+ test_acpi_fadt_table(data);
if (iasl) {
if (getenv(ACPI_REBUILD_EXPECTED_AML)) {
diff --git a/tests/boot-order-test.c b/tests/boot-order-test.c
index c60ebcf9d9..a725bce729 100644
--- a/tests/boot-order-test.c
+++ b/tests/boot-order-test.c
@@ -17,7 +17,7 @@
#include "standard-headers/linux/qemu_fw_cfg.h"
/* TODO actually test the results and get rid of this */
-#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__))
+#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__))
typedef struct {
const char *args;
@@ -27,31 +27,30 @@ typedef struct {
static void test_a_boot_order(const char *machine,
const char *test_args,
- uint64_t (*read_boot_order)(void),
+ uint64_t (*read_boot_order)(QTestState *),
uint64_t expected_boot,
uint64_t expected_reboot)
{
uint64_t actual;
+ QTestState *qts;
- global_qtest = qtest_initf("-nodefaults%s%s %s",
- machine ? " -M " : "",
- machine ?: "",
- test_args);
- actual = read_boot_order();
+ qts = qtest_initf("-nodefaults%s%s %s", machine ? " -M " : "",
+ machine ?: "", test_args);
+ actual = read_boot_order(qts);
g_assert_cmphex(actual, ==, expected_boot);
- qmp_discard_response("{ 'execute': 'system_reset' }");
+ qmp_discard_response(qts, "{ 'execute': 'system_reset' }");
/*
* system_reset only requests reset. We get a RESET event after
* the actual reset completes. Need to wait for that.
*/
- qmp_eventwait("RESET");
- actual = read_boot_order();
+ qtest_qmp_eventwait(qts, "RESET");
+ actual = read_boot_order(qts);
g_assert_cmphex(actual, ==, expected_reboot);
- qtest_quit(global_qtest);
+ qtest_quit(qts);
}
static void test_boot_orders(const char *machine,
- uint64_t (*read_boot_order)(void),
+ uint64_t (*read_boot_order)(QTestState *),
const boot_order_test *tests)
{
int i;
@@ -64,16 +63,16 @@ static void test_boot_orders(const char *machine,
}
}
-static uint8_t read_mc146818(uint16_t port, uint8_t reg)
+static uint8_t read_mc146818(QTestState *qts, uint16_t port, uint8_t reg)
{
- outb(port, reg);
- return inb(port + 1);
+ qtest_outb(qts, port, reg);
+ return qtest_inb(qts, port + 1);
}
-static uint64_t read_boot_order_pc(void)
+static uint64_t read_boot_order_pc(QTestState *qts)
{
- uint8_t b1 = read_mc146818(0x70, 0x38);
- uint8_t b2 = read_mc146818(0x70, 0x3d);
+ uint8_t b1 = read_mc146818(qts, 0x70, 0x38);
+ uint8_t b2 = read_mc146818(qts, 0x70, 0x3d);
return b1 | (b2 << 8);
}
@@ -109,16 +108,16 @@ static void test_pc_boot_order(void)
test_boot_orders(NULL, read_boot_order_pc, test_cases_pc);
}
-static uint8_t read_m48t59(uint64_t addr, uint16_t reg)
+static uint8_t read_m48t59(QTestState *qts, uint64_t addr, uint16_t reg)
{
- writeb(addr, reg & 0xff);
- writeb(addr + 1, reg >> 8);
- return readb(addr + 3);
+ qtest_writeb(qts, addr, reg & 0xff);
+ qtest_writeb(qts, addr + 1, reg >> 8);
+ return qtest_readb(qts, addr + 3);
}
-static uint64_t read_boot_order_prep(void)
+static uint64_t read_boot_order_prep(QTestState *qts)
{
- return read_m48t59(0x80000000 + 0x74, 0x34);
+ return read_m48t59(qts, 0x80000000 + 0x74, 0x34);
}
static const boot_order_test test_cases_prep[] = {
@@ -133,9 +132,9 @@ static void test_prep_boot_order(void)
test_boot_orders("prep", read_boot_order_prep, test_cases_prep);
}
-static uint64_t read_boot_order_pmac(void)
+static uint64_t read_boot_order_pmac(QTestState *qts)
{
- QFWCFG *fw_cfg = mm_fw_cfg_init(global_qtest, 0xf0000510);
+ QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xf0000510);
return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE);
}
@@ -158,9 +157,9 @@ static void test_pmac_newworld_boot_order(void)
test_boot_orders("mac99", read_boot_order_pmac, test_cases_fw_cfg);
}
-static uint64_t read_boot_order_sun4m(void)
+static uint64_t read_boot_order_sun4m(QTestState *qts)
{
- QFWCFG *fw_cfg = mm_fw_cfg_init(global_qtest, 0xd00000510ULL);
+ QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xd00000510ULL);
return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE);
}
@@ -170,9 +169,9 @@ static void test_sun4m_boot_order(void)
test_boot_orders("SS-5", read_boot_order_sun4m, test_cases_fw_cfg);
}
-static uint64_t read_boot_order_sun4u(void)
+static uint64_t read_boot_order_sun4u(QTestState *qts)
{
- QFWCFG *fw_cfg = io_fw_cfg_init(global_qtest, 0x510);
+ QFWCFG *fw_cfg = io_fw_cfg_init(qts, 0x510);
return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE);
}
diff --git a/tests/data/acpi/pc/DSDT.dimmpxm b/tests/data/acpi/pc/DSDT.dimmpxm
index f6ec911b11..ad2800de67 100644
--- a/tests/data/acpi/pc/DSDT.dimmpxm
+++ b/tests/data/acpi/pc/DSDT.dimmpxm
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.memhp b/tests/data/acpi/pc/DSDT.memhp
index e31ef50296..9e75ac96e1 100644
--- a/tests/data/acpi/pc/DSDT.memhp
+++ b/tests/data/acpi/pc/DSDT.memhp
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm
index 3837792dec..7177116a21 100644
--- a/tests/data/acpi/q35/DSDT.dimmpxm
+++ b/tests/data/acpi/q35/DSDT.dimmpxm
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.memhp b/tests/data/acpi/q35/DSDT.memhp
index 8fba0baf79..0235461391 100644
--- a/tests/data/acpi/q35/DSDT.memhp
+++ b/tests/data/acpi/q35/DSDT.memhp
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.mmio64 b/tests/data/acpi/q35/DSDT.mmio64
index a058ff2ee3..f60ee77fb4 100644
--- a/tests/data/acpi/q35/DSDT.mmio64
+++ b/tests/data/acpi/q35/DSDT.mmio64
Binary files differ
diff --git a/tests/endianness-test.c b/tests/endianness-test.c
index 48680cd131..58527952a5 100644
--- a/tests/endianness-test.c
+++ b/tests/endianness-test.c
@@ -48,65 +48,68 @@ static const TestCase test_cases[] = {
{}
};
-static uint8_t isa_inb(const TestCase *test, uint16_t addr)
+static uint8_t isa_inb(QTestState *qts, const TestCase *test, uint16_t addr)
{
uint8_t value;
if (test->isa_base == -1) {
- value = inb(addr);
+ value = qtest_inb(qts, addr);
} else {
- value = readb(test->isa_base + addr);
+ value = qtest_readb(qts, test->isa_base + addr);
}
return value;
}
-static uint16_t isa_inw(const TestCase *test, uint16_t addr)
+static uint16_t isa_inw(QTestState *qts, const TestCase *test, uint16_t addr)
{
uint16_t value;
if (test->isa_base == -1) {
- value = inw(addr);
+ value = qtest_inw(qts, addr);
} else {
- value = readw(test->isa_base + addr);
+ value = qtest_readw(qts, test->isa_base + addr);
}
return test->bswap ? bswap16(value) : value;
}
-static uint32_t isa_inl(const TestCase *test, uint16_t addr)
+static uint32_t isa_inl(QTestState *qts, const TestCase *test, uint16_t addr)
{
uint32_t value;
if (test->isa_base == -1) {
- value = inl(addr);
+ value = qtest_inl(qts, addr);
} else {
- value = readl(test->isa_base + addr);
+ value = qtest_readl(qts, test->isa_base + addr);
}
return test->bswap ? bswap32(value) : value;
}
-static void isa_outb(const TestCase *test, uint16_t addr, uint8_t value)
+static void isa_outb(QTestState *qts, const TestCase *test, uint16_t addr,
+ uint8_t value)
{
if (test->isa_base == -1) {
- outb(addr, value);
+ qtest_outb(qts, addr, value);
} else {
- writeb(test->isa_base + addr, value);
+ qtest_writeb(qts, test->isa_base + addr, value);
}
}
-static void isa_outw(const TestCase *test, uint16_t addr, uint16_t value)
+static void isa_outw(QTestState *qts, const TestCase *test, uint16_t addr,
+ uint16_t value)
{
value = test->bswap ? bswap16(value) : value;
if (test->isa_base == -1) {
- outw(addr, value);
+ qtest_outw(qts, addr, value);
} else {
- writew(test->isa_base + addr, value);
+ qtest_writew(qts, test->isa_base + addr, value);
}
}
-static void isa_outl(const TestCase *test, uint16_t addr, uint32_t value)
+static void isa_outl(QTestState *qts, const TestCase *test, uint16_t addr,
+ uint32_t value)
{
value = test->bswap ? bswap32(value) : value;
if (test->isa_base == -1) {
- outl(addr, value);
+ qtest_outl(qts, addr, value);
} else {
- writel(test->isa_base + addr, value);
+ qtest_writel(qts, test->isa_base + addr, value);
}
}
@@ -114,161 +117,161 @@ static void isa_outl(const TestCase *test, uint16_t addr, uint32_t value)
static void test_endianness(gconstpointer data)
{
const TestCase *test = data;
-
- global_qtest = qtest_initf("-M %s%s%s -device pc-testdev",
- test->machine,
- test->superio ? " -device " : "",
- test->superio ?: "");
- isa_outl(test, 0xe0, 0x87654321);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654321);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321);
- g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x87);
- g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x65);
- g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x43);
- g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x21);
-
- isa_outw(test, 0xe2, 0x8866);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x88664321);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8866);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321);
- g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x88);
- g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x66);
- g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x43);
- g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x21);
-
- isa_outw(test, 0xe0, 0x4422);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x88664422);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8866);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4422);
- g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x88);
- g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x66);
- g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x44);
- g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x22);
-
- isa_outb(test, 0xe3, 0x87);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87664422);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8766);
- g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x87);
- g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x66);
- g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x44);
- g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x22);
-
- isa_outb(test, 0xe2, 0x65);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654422);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4422);
- g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x87);
- g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x65);
- g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x44);
- g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x22);
-
- isa_outb(test, 0xe1, 0x43);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654322);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4322);
- g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x87);
- g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x65);
- g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x43);
- g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x22);
-
- isa_outb(test, 0xe0, 0x21);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654321);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321);
- g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x87);
- g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x65);
- g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x43);
- g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x21);
- qtest_quit(global_qtest);
+ QTestState *qts;
+
+ qts = qtest_initf("-M %s%s%s -device pc-testdev", test->machine,
+ test->superio ? " -device " : "",
+ test->superio ?: "");
+ isa_outl(qts, test, 0xe0, 0x87654321);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654321);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321);
+ g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x87);
+ g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x65);
+ g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x43);
+ g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x21);
+
+ isa_outw(qts, test, 0xe2, 0x8866);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x88664321);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8866);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321);
+ g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x88);
+ g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x66);
+ g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x43);
+ g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x21);
+
+ isa_outw(qts, test, 0xe0, 0x4422);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x88664422);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8866);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4422);
+ g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x88);
+ g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x66);
+ g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x44);
+ g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x22);
+
+ isa_outb(qts, test, 0xe3, 0x87);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87664422);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8766);
+ g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x87);
+ g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x66);
+ g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x44);
+ g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x22);
+
+ isa_outb(qts, test, 0xe2, 0x65);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654422);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4422);
+ g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x87);
+ g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x65);
+ g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x44);
+ g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x22);
+
+ isa_outb(qts, test, 0xe1, 0x43);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654322);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4322);
+ g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x87);
+ g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x65);
+ g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x43);
+ g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x22);
+
+ isa_outb(qts, test, 0xe0, 0x21);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654321);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321);
+ g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x87);
+ g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x65);
+ g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x43);
+ g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x21);
+ qtest_quit(qts);
}
static void test_endianness_split(gconstpointer data)
{
const TestCase *test = data;
-
- global_qtest = qtest_initf("-M %s%s%s -device pc-testdev",
- test->machine,
- test->superio ? " -device " : "",
- test->superio ?: "");
- isa_outl(test, 0xe8, 0x87654321);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654321);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321);
-
- isa_outw(test, 0xea, 0x8866);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x88664321);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8866);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321);
-
- isa_outw(test, 0xe8, 0x4422);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x88664422);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8866);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4422);
-
- isa_outb(test, 0xeb, 0x87);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87664422);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8766);
-
- isa_outb(test, 0xea, 0x65);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654422);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4422);
-
- isa_outb(test, 0xe9, 0x43);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654322);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4322);
-
- isa_outb(test, 0xe8, 0x21);
- g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654321);
- g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321);
- qtest_quit(global_qtest);
+ QTestState *qts;
+
+ qts = qtest_initf("-M %s%s%s -device pc-testdev", test->machine,
+ test->superio ? " -device " : "",
+ test->superio ?: "");
+ isa_outl(qts, test, 0xe8, 0x87654321);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654321);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321);
+
+ isa_outw(qts, test, 0xea, 0x8866);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x88664321);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8866);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321);
+
+ isa_outw(qts, test, 0xe8, 0x4422);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x88664422);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8866);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4422);
+
+ isa_outb(qts, test, 0xeb, 0x87);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87664422);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8766);
+
+ isa_outb(qts, test, 0xea, 0x65);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654422);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4422);
+
+ isa_outb(qts, test, 0xe9, 0x43);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654322);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4322);
+
+ isa_outb(qts, test, 0xe8, 0x21);
+ g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654321);
+ g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321);
+ qtest_quit(qts);
}
static void test_endianness_combine(gconstpointer data)
{
const TestCase *test = data;
-
- global_qtest = qtest_initf("-M %s%s%s -device pc-testdev",
- test->machine,
- test->superio ? " -device " : "",
- test->superio ?: "");
- isa_outl(test, 0xe0, 0x87654321);
- g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x87654321);
- g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4321);
-
- isa_outw(test, 0xe2, 0x8866);
- g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x88664321);
- g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8866);
- g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4321);
-
- isa_outw(test, 0xe0, 0x4422);
- g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x88664422);
- g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8866);
- g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4422);
-
- isa_outb(test, 0xe3, 0x87);
- g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x87664422);
- g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8766);
-
- isa_outb(test, 0xe2, 0x65);
- g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x87654422);
- g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4422);
-
- isa_outb(test, 0xe1, 0x43);
- g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x87654322);
- g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4322);
-
- isa_outb(test, 0xe0, 0x21);
- g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x87654321);
- g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8765);
- g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4321);
- qtest_quit(global_qtest);
+ QTestState *qts;
+
+ qts = qtest_initf("-M %s%s%s -device pc-testdev", test->machine,
+ test->superio ? " -device " : "",
+ test->superio ?: "");
+ isa_outl(qts, test, 0xe0, 0x87654321);
+ g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x87654321);
+ g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4321);
+
+ isa_outw(qts, test, 0xe2, 0x8866);
+ g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x88664321);
+ g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8866);
+ g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4321);
+
+ isa_outw(qts, test, 0xe0, 0x4422);
+ g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x88664422);
+ g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8866);
+ g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4422);
+
+ isa_outb(qts, test, 0xe3, 0x87);
+ g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x87664422);
+ g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8766);
+
+ isa_outb(qts, test, 0xe2, 0x65);
+ g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x87654422);
+ g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4422);
+
+ isa_outb(qts, test, 0xe1, 0x43);
+ g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x87654322);
+ g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4322);
+
+ isa_outb(qts, test, 0xe0, 0x21);
+ g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x87654321);
+ g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8765);
+ g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4321);
+ qtest_quit(qts);
}
int main(int argc, char **argv)
diff --git a/tests/hexloader-test.c b/tests/hexloader-test.c
index 834ed52c22..8b7aa2d72d 100644
--- a/tests/hexloader-test.c
+++ b/tests/hexloader-test.c
@@ -23,7 +23,7 @@ static void hex_loader_test(void)
const unsigned int base_addr = 0x00010000;
QTestState *s = qtest_initf(
- "-M vexpress-a9 -nographic -device loader,file=tests/data/hex-loader/test.hex");
+ "-M vexpress-a9 -device loader,file=tests/data/hex-loader/test.hex");
for (i = 0; i < 256; ++i) {
uint8_t val = qtest_readb(s, base_addr + i);
diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c
index fe5eb304b1..4911b69317 100644
--- a/tests/ivshmem-test.c
+++ b/tests/ivshmem-test.c
@@ -291,20 +291,20 @@ static void *server_thread(void *data)
return NULL;
}
-static void setup_vm_with_server(IVState *s, int nvectors, bool msi)
+static void setup_vm_with_server(IVState *s, int nvectors)
{
- char *cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s,nowait "
- "-device ivshmem%s,chardev=chr0,vectors=%d",
- tmpserver,
- msi ? "-doorbell" : ",size=1M,msi=off",
- nvectors);
+ char *cmd;
- setup_vm_cmd(s, cmd, msi);
+ cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s,nowait "
+ "-device ivshmem-doorbell,chardev=chr0,vectors=%d",
+ tmpserver, nvectors);
+
+ setup_vm_cmd(s, cmd, true);
g_free(cmd);
}
-static void test_ivshmem_server(bool msi)
+static void test_ivshmem_server(void)
{
IVState state1, state2, *s1, *s2;
ServerThread thread;
@@ -327,9 +327,9 @@ static void test_ivshmem_server(bool msi)
thread.thread = g_thread_new("ivshmem-server", server_thread, &thread);
g_assert(thread.thread != NULL);
- setup_vm_with_server(&state1, nvectors, msi);
+ setup_vm_with_server(&state1, nvectors);
s1 = &state1;
- setup_vm_with_server(&state2, nvectors, msi);
+ setup_vm_with_server(&state2, nvectors);
s2 = &state2;
/* check got different VM ids */
@@ -340,38 +340,28 @@ static void test_ivshmem_server(bool msi)
g_assert_cmpint(vm1, !=, vm2);
/* check number of MSI-X vectors */
- if (msi) {
- ret = qpci_msix_table_size(s1->dev);
- g_assert_cmpuint(ret, ==, nvectors);
- }
+ ret = qpci_msix_table_size(s1->dev);
+ g_assert_cmpuint(ret, ==, nvectors);
/* TODO test behavior before MSI-X is enabled */
/* ping vm2 -> vm1 on vector 0 */
- if (msi) {
- ret = qpci_msix_pending(s1->dev, 0);
- g_assert_cmpuint(ret, ==, 0);
- } else {
- g_assert_cmpuint(in_reg(s1, INTRSTATUS), ==, 0);
- }
+ ret = qpci_msix_pending(s1->dev, 0);
+ g_assert_cmpuint(ret, ==, 0);
out_reg(s2, DOORBELL, vm1 << 16);
do {
g_usleep(10000);
- ret = msi ? qpci_msix_pending(s1->dev, 0) : in_reg(s1, INTRSTATUS);
+ ret = qpci_msix_pending(s1->dev, 0);
} while (ret == 0 && g_get_monotonic_time() < end_time);
g_assert_cmpuint(ret, !=, 0);
/* ping vm1 -> vm2 on vector 1 */
- if (msi) {
- ret = qpci_msix_pending(s2->dev, 1);
- g_assert_cmpuint(ret, ==, 0);
- } else {
- g_assert_cmpuint(in_reg(s2, INTRSTATUS), ==, 0);
- }
+ ret = qpci_msix_pending(s2->dev, 1);
+ g_assert_cmpuint(ret, ==, 0);
out_reg(s1, DOORBELL, vm2 << 16 | 1);
do {
g_usleep(10000);
- ret = msi ? qpci_msix_pending(s2->dev, 1) : in_reg(s2, INTRSTATUS);
+ ret = qpci_msix_pending(s2->dev, 1);
} while (ret == 0 && g_get_monotonic_time() < end_time);
g_assert_cmpuint(ret, !=, 0);
@@ -389,27 +379,17 @@ static void test_ivshmem_server(bool msi)
close(thread.pipe[0]);
}
-static void test_ivshmem_server_msi(void)
-{
- test_ivshmem_server(true);
-}
-
-static void test_ivshmem_server_irq(void)
-{
- test_ivshmem_server(false);
-}
-
#define PCI_SLOT_HP 0x06
static void test_ivshmem_hotplug(void)
{
const char *arch = qtest_get_arch();
- qtest_start("");
+ qtest_start("-object memory-backend-ram,size=1M,id=mb1");
- qtest_qmp_device_add("ivshmem",
- "iv1", "{'addr': %s, 'shm': %s, 'size': '1M'}",
- stringify(PCI_SLOT_HP), tmpshm);
+ qtest_qmp_device_add("ivshmem-plain", "iv1",
+ "{'addr': %s, 'memdev': 'mb1'}",
+ stringify(PCI_SLOT_HP));
if (strcmp(arch, "ppc64") != 0) {
qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP);
}
@@ -509,8 +489,7 @@ int main(int argc, char **argv)
if (g_test_slow()) {
qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
if (strcmp(arch, "ppc64") != 0) {
- qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi);
- qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq);
+ qtest_add_func("/ivshmem/server", test_ivshmem_server);
}
}
diff --git a/tests/pnv-xscom-test.c b/tests/pnv-xscom-test.c
index 70f4c84d1b..974f8da5b2 100644
--- a/tests/pnv-xscom-test.c
+++ b/tests/pnv-xscom-test.c
@@ -63,14 +63,15 @@ static uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba)
return addr;
}
-static uint64_t pnv_xscom_read(const PnvChip *chip, uint32_t pcba)
+static uint64_t pnv_xscom_read(QTestState *qts, const PnvChip *chip,
+ uint32_t pcba)
{
- return readq(pnv_xscom_addr(chip, pcba));
+ return qtest_readq(qts, pnv_xscom_addr(chip, pcba));
}
-static void test_xscom_cfam_id(const PnvChip *chip)
+static void test_xscom_cfam_id(QTestState *qts, const PnvChip *chip)
{
- uint64_t f000f = pnv_xscom_read(chip, 0xf000f);
+ uint64_t f000f = pnv_xscom_read(qts, chip, 0xf000f);
g_assert_cmphex(f000f, ==, chip->cfam_id);
}
@@ -78,11 +79,11 @@ static void test_xscom_cfam_id(const PnvChip *chip)
static void test_cfam_id(const void *data)
{
const PnvChip *chip = data;
+ QTestState *qts;
- global_qtest = qtest_initf("-M powernv,accel=tcg -cpu %s",
- chip->cpu_model);
- test_xscom_cfam_id(chip);
- qtest_quit(global_qtest);
+ qts = qtest_initf("-M powernv,accel=tcg -cpu %s", chip->cpu_model);
+ test_xscom_cfam_id(qts, chip);
+ qtest_quit(qts);
}
@@ -94,7 +95,7 @@ static void test_cfam_id(const void *data)
#define PNV_XSCOM_EX_DTS_RESULT0 0x50000
-static void test_xscom_core(const PnvChip *chip)
+static void test_xscom_core(QTestState *qts, const PnvChip *chip)
{
uint32_t first_core_dts0 = PNV_XSCOM_EX_DTS_RESULT0;
uint64_t dts0;
@@ -105,7 +106,7 @@ static void test_xscom_core(const PnvChip *chip)
first_core_dts0 |= PNV_XSCOM_P9_EC_BASE(chip->first_core);
}
- dts0 = pnv_xscom_read(chip, first_core_dts0);
+ dts0 = pnv_xscom_read(qts, chip, first_core_dts0);
g_assert_cmphex(dts0, ==, 0x26f024f023f0000ull);
}
@@ -113,11 +114,11 @@ static void test_xscom_core(const PnvChip *chip)
static void test_core(const void *data)
{
const PnvChip *chip = data;
+ QTestState *qts;
- global_qtest = qtest_initf("-M powernv,accel=tcg -cpu %s",
- chip->cpu_model);
- test_xscom_core(chip);
- qtest_quit(global_qtest);
+ qts = qtest_initf("-M powernv,accel=tcg -cpu %s", chip->cpu_model);
+ test_xscom_core(qts, chip);
+ qtest_quit(qts);
}
static void add_test(const char *name, void (*test)(const void *data))
diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223
index 773892dbe6..f120a01646 100755
--- a/tests/qemu-iotests/223
+++ b/tests/qemu-iotests/223
@@ -127,6 +127,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start",
"arguments":{"addr":{"type":"unix",
"data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server
+$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
"arguments":{"device":"n", "bitmap":"b"}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
@@ -142,6 +143,7 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
"arguments":{"device":"n", "name":"n2", "writable":true,
"bitmap":"b2"}}' "return"
+$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd"
echo
echo "=== Contrast normal status to large granularity dirty-bitmap ==="
diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out
index 0de5240a75..6476b77ba2 100644
--- a/tests/qemu-iotests/223.out
+++ b/tests/qemu-iotests/223.out
@@ -30,12 +30,32 @@ wrote 2097152/2097152 bytes at offset 2097152
{"error": {"class": "GenericError", "desc": "NBD server not running"}}
{"return": {}}
{"error": {"class": "GenericError", "desc": "NBD server already running"}}
+exports available: 0
{"return": {}}
{"error": {"class": "GenericError", "desc": "Cannot find device=nosuch nor node_name=nosuch"}}
{"error": {"class": "GenericError", "desc": "NBD server already has export named 'n'"}}
{"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}}
{"return": {}}
+exports available: 2
+ export: 'n'
+ size: 4194304
+ flags: 0x4ef ( readonly flush fua trim zeroes df cache )
+ min block: 512
+ opt block: 4096
+ max block: 33554432
+ available meta contexts: 2
+ base:allocation
+ qemu:dirty-bitmap:b
+ export: 'n2'
+ size: 4194304
+ flags: 0x4ed ( flush fua trim zeroes df cache )
+ min block: 512
+ opt block: 4096
+ max block: 33554432
+ available meta contexts: 2
+ base:allocation
+ qemu:dirty-bitmap:b2
=== Contrast normal status to large granularity dirty-bitmap ===
diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233
index 1814efe333..fc345a1a46 100755
--- a/tests/qemu-iotests/233
+++ b/tests/qemu-iotests/233
@@ -2,7 +2,7 @@
#
# Test NBD TLS certificate / authorization integration
#
-# Copyright (C) 2018 Red Hat, Inc.
+# Copyright (C) 2018-2019 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
@@ -30,6 +30,7 @@ _cleanup()
{
nbd_server_stop
_cleanup_test_img
+ rm -f "$TEST_DIR/server.log"
tls_x509_cleanup
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@@ -66,12 +67,14 @@ $QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" | _filter_qemu_io
echo
echo "== check TLS client to plain server fails =="
-nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG"
+nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG" 2> "$TEST_DIR/server.log"
-$QEMU_IMG info --image-opts \
- --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \
+obj=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0
+$QEMU_IMG info --image-opts --object $obj \
driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
2>&1 | sed "s/$nbd_tcp_port/PORT/g"
+$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \
+ --tls-creds=tls0
nbd_server_stop
@@ -81,23 +84,28 @@ echo "== check plain client to TLS server fails =="
nbd_server_start_tcp_socket \
--object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes \
--tls-creds tls0 \
- -f $IMGFMT "$TEST_IMG"
+ -f $IMGFMT "$TEST_IMG" 2>> "$TEST_DIR/server.log"
$QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g"
+$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port
echo
echo "== check TLS works =="
-$QEMU_IMG info --image-opts \
- --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \
+obj=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0
+$QEMU_IMG info --image-opts --object $obj \
driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
2>&1 | sed "s/$nbd_tcp_port/PORT/g"
+$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \
+ --tls-creds=tls0
echo
echo "== check TLS with different CA fails =="
-$QEMU_IMG info --image-opts \
- --object tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0 \
+obj=tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0
+$QEMU_IMG info --image-opts --object $obj \
driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \
2>&1 | sed "s/$nbd_tcp_port/PORT/g"
+$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \
+ --tls-creds=tls0
echo
echo "== perform I/O over TLS =="
@@ -109,6 +117,10 @@ $QEMU_IO -c 'r -P 0x11 1m 1m' -c 'w -P 0x22 1m 1m' --image-opts \
$QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io
+echo
+echo "== final server log =="
+cat "$TEST_DIR/server.log"
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out
index 5f416721b0..6d45f3b230 100644
--- a/tests/qemu-iotests/233.out
+++ b/tests/qemu-iotests/233.out
@@ -15,20 +15,33 @@ wrote 1048576/1048576 bytes at offset 1048576
== check TLS client to plain server fails ==
qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Denied by server for option 5 (starttls)
server reported: TLS not configured
+qemu-nbd: Denied by server for option 5 (starttls)
+server reported: TLS not configured
== check plain client to TLS server fails ==
qemu-img: Could not open 'nbd://localhost:PORT': TLS negotiation required before option 8 (structured reply)
server reported: Option 0x8 not permitted before TLS
+qemu-nbd: TLS negotiation required before option 8 (structured reply)
+server reported: Option 0x8 not permitted before TLS
== check TLS works ==
image: nbd://127.0.0.1:PORT
file format: nbd
virtual size: 64M (67108864 bytes)
disk size: unavailable
+exports available: 1
+ export: ''
+ size: 67108864
+ flags: 0x4ed ( flush fua trim zeroes df cache )
+ min block: 512
+ opt block: 4096
+ max block: 33554432
+ available meta contexts: 1
+ base:allocation
== check TLS with different CA fails ==
-qemu-nbd: option negotiation failed: Verify failed: No certificate was found.
qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer
+qemu-nbd: The certificate hasn't got a known issuer
== perform I/O over TLS ==
read 1048576/1048576 bytes at offset 1048576
@@ -37,4 +50,8 @@ wrote 1048576/1048576 bytes at offset 1048576
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 1048576/1048576 bytes at offset 1048576
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== final server log ==
+qemu-nbd: option negotiation failed: Verify failed: No certificate was found.
+qemu-nbd: option negotiation failed: Verify failed: No certificate was found.
*** done
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 54982f68e7..84e50d84e7 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -309,7 +309,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
CharBackend *chr = &s->chr;
VhostUserMsg msg;
uint8_t *p = (uint8_t *) &msg;
- int fd;
+ int fd = -1;
if (s->test_fail) {
qemu_chr_fe_disconnect(chr);
diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c
index 1c1d435bbd..52cdd83ec0 100644
--- a/tests/vmgenid-test.c
+++ b/tests/vmgenid-test.c
@@ -23,26 +23,13 @@
*/
#define RSDP_ADDR_INVALID 0x100000 /* RSDP must be below this address */
-typedef struct {
- AcpiTableHeader header;
- gchar name_op;
- gchar vgia[4];
- gchar val_op;
- uint32_t vgia_val;
-} QEMU_PACKED VgidTable;
-
static uint32_t acpi_find_vgia(QTestState *qts)
{
uint32_t rsdp_offset;
uint32_t guid_offset = 0;
uint8_t rsdp_table[36 /* ACPI 2.0+ RSDP size */];
- uint32_t rsdt, rsdt_table_length;
- AcpiRsdtDescriptorRev1 rsdt_table;
- size_t tables_nr;
- uint32_t *tables;
- AcpiTableHeader ssdt_table;
- VgidTable vgid_table;
- int i;
+ uint32_t rsdt_len, table_length;
+ uint8_t *rsdt, *ent;
/* Wait for guest firmware to finish and start the payload. */
boot_sector_test(qts);
@@ -52,48 +39,37 @@ static uint32_t acpi_find_vgia(QTestState *qts)
g_assert_cmphex(rsdp_offset, <, RSDP_ADDR_INVALID);
- acpi_parse_rsdp_table(qts, rsdp_offset, rsdp_table);
-
- rsdt = acpi_get_rsdt_address(rsdp_table);
- g_assert(rsdt);
- /* read the header */
- ACPI_READ_TABLE_HEADER(qts, &rsdt_table, rsdt);
- ACPI_ASSERT_CMP(rsdt_table.signature, "RSDT");
- rsdt_table_length = le32_to_cpu(rsdt_table.length);
-
- /* compute the table entries in rsdt */
- g_assert_cmpint(rsdt_table_length, >, sizeof(AcpiRsdtDescriptorRev1));
- tables_nr = (rsdt_table_length - sizeof(AcpiRsdtDescriptorRev1)) /
- sizeof(uint32_t);
+ acpi_parse_rsdp_table(qts, rsdp_offset, rsdp_table);
+ acpi_fetch_table(qts, &rsdt, &rsdt_len, &rsdp_table[16 /* RsdtAddress */],
+ "RSDT", true);
- /* get the addresses of the tables pointed by rsdt */
- tables = g_new0(uint32_t, tables_nr);
- ACPI_READ_ARRAY_PTR(qts, tables, tables_nr, rsdt);
+ ACPI_FOREACH_RSDT_ENTRY(rsdt, rsdt_len, ent, 4 /* Entry size */) {
+ uint8_t *table_aml;
- for (i = 0; i < tables_nr; i++) {
- uint32_t addr = le32_to_cpu(tables[i]);
- ACPI_READ_TABLE_HEADER(qts, &ssdt_table, addr);
- if (!strncmp((char *)ssdt_table.oem_table_id, "VMGENID", 7)) {
+ acpi_fetch_table(qts, &table_aml, &table_length, ent, NULL, true);
+ if (!memcmp(table_aml + 16 /* OEM Table ID */, "VMGENID", 7)) {
+ uint32_t vgia_val;
+ uint8_t *aml = &table_aml[36 /* AML byte-code start */];
/* the first entry in the table should be VGIA
* That's all we need
*/
- ACPI_READ_FIELD(qts, vgid_table.name_op, addr);
- g_assert(vgid_table.name_op == 0x08); /* name */
- ACPI_READ_ARRAY(qts, vgid_table.vgia, addr);
- g_assert(memcmp(vgid_table.vgia, "VGIA", 4) == 0);
- ACPI_READ_FIELD(qts, vgid_table.val_op, addr);
- g_assert(vgid_table.val_op == 0x0C); /* dword */
- ACPI_READ_FIELD(qts, vgid_table.vgia_val, addr);
+ g_assert(aml[0 /* name_op*/] == 0x08);
+ g_assert(memcmp(&aml[1 /* name */], "VGIA", 4) == 0);
+ g_assert(aml[5 /* value op */] == 0x0C /* dword */);
+ memcpy(&vgia_val, &aml[6 /* value */], 4);
+
/* The GUID is written at a fixed offset into the fw_cfg file
* in order to implement the "OVMF SDT Header probe suppressor"
* see docs/specs/vmgenid.txt for more details
*/
- guid_offset = le32_to_cpu(vgid_table.vgia_val) + VMGENID_GUID_OFFSET;
+ guid_offset = le32_to_cpu(vgia_val) + VMGENID_GUID_OFFSET;
+ g_free(table_aml);
break;
}
+ g_free(table_aml);
}
- g_free(tables);
+ g_free(rsdt);
return guid_offset;
}
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index c1bee2a581..4ce1ba9ca4 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -233,14 +233,18 @@ void qemu_set_block(int fd)
{
int f;
f = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, f & ~O_NONBLOCK);
+ assert(f != -1);
+ f = fcntl(fd, F_SETFL, f & ~O_NONBLOCK);
+ assert(f != -1);
}
void qemu_set_nonblock(int fd)
{
int f;
f = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, f | O_NONBLOCK);
+ assert(f != -1);
+ f = fcntl(fd, F_SETFL, f | O_NONBLOCK);
+ assert(f != -1);
}
int socket_set_fast_reuse(int fd)