From: Thomas Huth Date: Mon, 9 Sep 2019 10:04:01 +0000 (+0200) Subject: test: Move qtests to a separate directory X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=1e8a1fae7464ef79c9e50aa0f807d2c511be3c8e;p=qemu.git test: Move qtests to a separate directory The tests directory itself is pretty overcrowded, and it's hard to see which test belongs to which test subsystem (unit, qtest, ...). Let's move the qtests to a separate folder for more clarity. Message-Id: <20191218103059.11729-6-thuth@redhat.com> Reviewed-by: Paolo Bonzini Signed-off-by: Thomas Huth --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ebcef0ebe9..dce8f2d3f5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -87,11 +87,12 @@ build-tci: - ../configure --enable-tcg-interpreter --target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" - make -j2 - - make tests/boot-serial-test tests/cdrom-test tests/pxe-test + - make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test - for tg in $TARGETS ; do export QTEST_QEMU_BINARY="${tg}-softmmu/qemu-system-${tg}" ; - ./tests/boot-serial-test || exit 1 ; - ./tests/cdrom-test || exit 1 ; + ./tests/qtest/boot-serial-test || exit 1 ; + ./tests/qtest/cdrom-test || exit 1 ; done - - QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/pxe-test - - QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" ./tests/pxe-test -m slow + - QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/qtest/pxe-test + - QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" + ./tests/qtest/pxe-test -m slow diff --git a/MAINTAINERS b/MAINTAINERS index cd2dc137a3..02eab66b02 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -533,7 +533,7 @@ F: include/hw/misc/arm11scu.h F: include/hw/timer/a9gtimer.h F: include/hw/timer/arm_mptimer.h F: include/hw/timer/armv7m_systick.h -F: tests/test-arm-mptimer.c +F: tests/qtest/test-arm-mptimer.c Exynos M: Igor Mitsyanko @@ -864,7 +864,7 @@ F: hw/*/nrf51*.c F: hw/*/microbit*.c F: include/hw/*/nrf51*.h F: include/hw/*/microbit*.h -F: tests/microbit-test.c +F: tests/qtest/microbit-test.c CRIS Machines ------------- @@ -1101,9 +1101,9 @@ F: include/hw/*/xics* F: pc-bios/slof.bin F: docs/specs/ppc-spapr-hcalls.txt F: docs/specs/ppc-spapr-hotplug.txt -F: tests/spapr* +F: tests/qtest/spapr* F: tests/libqos/*spapr* -F: tests/rtas* +F: tests/qtest/rtas* F: tests/libqos/rtas* PowerNV (Non-Virtualized) @@ -1116,7 +1116,7 @@ F: hw/intc/pnv* F: hw/intc/xics_pnv.c F: include/hw/ppc/pnv* F: pc-bios/skiboot.lid -F: tests/pnv* +F: tests/qtest/pnv* virtex_ml507 M: Edgar E. Iglesias @@ -1264,7 +1264,7 @@ F: hw/misc/sga.c F: hw/isa/apm.c F: include/hw/isa/apm.h F: tests/test-x86-cpuid.c -F: tests/test-x86-cpuid-compat.c +F: tests/qtest/test-x86-cpuid-compat.c PC Chipset M: Michael S. Tsirkin @@ -1360,9 +1360,9 @@ F: hw/ide/ F: hw/block/block.c F: hw/block/cdrom.c F: hw/block/hd-geometry.c -F: tests/ide-test.c -F: tests/ahci-test.c -F: tests/cdrom-test.c +F: tests/qtest/ide-test.c +F: tests/qtest/ahci-test.c +F: tests/qtest/cdrom-test.c F: tests/libqos/ahci* T: git https://github.com/jnsnow/qemu.git ide @@ -1372,7 +1372,7 @@ S: Maintained F: include/hw/ipmi/* F: hw/ipmi/* F: hw/smbios/smbios_type_38.c -F: tests/ipmi* +F: tests/qtest/ipmi* T: git https://github.com/cminyard/qemu.git master-ipmi-rebase Floppy @@ -1381,7 +1381,7 @@ L: qemu-block@nongnu.org S: Supported F: hw/block/fdc.c F: include/hw/block/fdc.h -F: tests/fdc-test.c +F: tests/qtest/fdc-test.c T: git https://github.com/jnsnow/qemu.git ide OMAP @@ -1419,8 +1419,8 @@ F: hw/acpi/* F: hw/smbios/* F: hw/i386/acpi-build.[hc] F: hw/arm/virt-acpi-build.c -F: tests/bios-tables-test.c -F: tests/acpi-utils.[hc] +F: tests/qtest/bios-tables-test.c +F: tests/qtest/acpi-utils.[hc] F: tests/data/acpi/ ppc4xx @@ -1443,7 +1443,7 @@ M: Jason Wang S: Odd Fixes F: hw/net/ F: include/hw/net/ -F: tests/virtio-net-test.c +F: tests/qtest/virtio-net-test.c F: docs/virtio-net-failover.rst T: git https://github.com/jasowang/qemu.git net @@ -1460,7 +1460,7 @@ R: Fam Zheng S: Supported F: include/hw/scsi/* F: hw/scsi/* -F: tests/virtio-scsi-test.c +F: tests/qtest/virtio-scsi-test.c T: git https://github.com/bonzini/qemu.git scsi-next SSI @@ -1470,7 +1470,7 @@ F: hw/ssi/* F: hw/block/m25p80.c F: include/hw/ssi/ssi.h X: hw/ssi/xilinx_* -F: tests/m25p80-test.c +F: tests/qtest/m25p80-test.c Xilinx SPI M: Alistair Francis @@ -1484,13 +1484,13 @@ F: include/hw/sd/sd* F: hw/sd/core.c F: hw/sd/sd* F: hw/sd/ssi-sd.c -F: tests/sd* +F: tests/qtest/sd* USB M: Gerd Hoffmann S: Maintained F: hw/usb/* -F: tests/usb-*-test.c +F: tests/qtest/usb-*-test.c F: docs/usb2.txt F: docs/usb-storage.txt F: include/hw/usb.h @@ -1552,7 +1552,6 @@ F: hw/virtio/Makefile.objs F: hw/virtio/trace-events F: net/vhost-user.c F: include/hw/virtio/ -F: tests/virtio-balloon-test.c virtio-9p M: Greg Kurz @@ -1560,7 +1559,7 @@ S: Odd Fixes F: hw/9pfs/ X: hw/9pfs/xen-9p* F: fsdev/ -F: tests/virtio-9p-test.c +F: tests/qtest/virtio-9p-test.c T: git https://github.com/gkurz/qemu.git 9p-next virtio-blk @@ -1569,7 +1568,7 @@ L: qemu-block@nongnu.org S: Supported F: hw/block/virtio-blk.c F: hw/block/dataplane/* -F: tests/virtio-blk-test.c +F: tests/qtest/virtio-blk-test.c T: git https://github.com/stefanha/qemu.git block virtio-ccw @@ -1597,8 +1596,7 @@ S: Supported F: hw/char/virtio-serial-bus.c F: hw/char/virtio-console.c F: include/hw/virtio/virtio-serial.h -F: tests/virtio-console-test.c -F: tests/virtio-serial-test.c +F: tests/qtest/virtio-serial-test.c virtio-rng M: Laurent Vivier @@ -1608,7 +1606,7 @@ F: hw/virtio/virtio-rng.c F: include/hw/virtio/virtio-rng.h F: include/sysemu/rng*.h F: backends/rng*.c -F: tests/virtio-rng-test.c +F: tests/qtest/virtio-rng-test.c virtio-crypto M: Gonglei @@ -1622,7 +1620,7 @@ M: Keith Busch L: qemu-block@nongnu.org S: Supported F: hw/block/nvme* -F: tests/nvme-test.c +F: tests/qtest/nvme-test.c megasas M: Hannes Reinecke @@ -1630,7 +1628,7 @@ L: qemu-block@nongnu.org S: Supported F: hw/scsi/megasas.c F: hw/scsi/mfi.h -F: tests/megasas-test.c +F: tests/qtest/megasas-test.c Network packet abstractions M: Dmitry Fleytman @@ -1645,7 +1643,7 @@ M: Dmitry Fleytman S: Maintained F: hw/net/vmxnet* F: hw/scsi/vmw_pvscsi* -F: tests/vmxnet3-test.c +F: tests/qtest/vmxnet3-test.c Rocker M: Jiri Pirko @@ -1693,7 +1691,7 @@ F: docs/generic-loader.txt Intel Hexadecimal Object File Loader M: Su Hang S: Maintained -F: tests/hexloader-test.c +F: tests/qtest/hexloader-test.c F: tests/data/hex-loader/test.hex CHRP NVRAM @@ -1701,7 +1699,7 @@ M: Thomas Huth S: Maintained F: hw/nvram/chrp_nvram.c F: include/hw/nvram/chrp_nvram.h -F: tests/prom-env-test.c +F: tests/qtest/prom-env-test.c VM Generation ID M: Ben Warren @@ -1709,7 +1707,7 @@ S: Maintained F: hw/acpi/vmgenid.c F: include/hw/acpi/vmgenid.h F: docs/specs/vmgenid.txt -F: tests/vmgenid-test.c +F: tests/qtest/vmgenid-test.c F: stubs/vmgenid.c Unimplemented device @@ -1779,7 +1777,7 @@ F: stubs/fw_cfg.c F: include/hw/nvram/fw_cfg.h F: include/standard-headers/linux/qemu_fw_cfg.h F: tests/libqos/fw_cfg.c -F: tests/fw_cfg-test.c +F: tests/qtest/fw_cfg-test.c T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE @@ -1799,9 +1797,9 @@ S: Maintained F: audio/ F: hw/audio/ F: include/hw/audio/ -F: tests/ac97-test.c -F: tests/es1370-test.c -F: tests/intel-hda-test.c +F: tests/qtest/ac97-test.c +F: tests/qtest/es1370-test.c +F: tests/qtest/intel-hda-test.c Block layer core M: Kevin Wolf @@ -2002,7 +2000,7 @@ F: monitor/hmp* F: hmp.h F: hmp-commands*.hx F: include/monitor/hmp-target.h -F: tests/test-hmp.c +F: tests/qtest/test-hmp.c F: include/qemu/qemu-print.h F: util/qemu-print.c @@ -2128,8 +2126,8 @@ F: qapi/error.json F: docs/devel/*qmp-* F: docs/interop/*qmp-* F: scripts/qmp/ -F: tests/qmp-test.c -F: tests/qmp-cmd-test.c +F: tests/qtest/qmp-test.c +F: tests/qtest/qmp-cmd-test.c T: git https://repo.or.cz/qemu/armbru.git qapi-next qtest @@ -2139,9 +2137,8 @@ R: Paolo Bonzini S: Maintained F: qtest.c F: accel/qtest.c -F: tests/libqtest* F: tests/libqos/ -F: tests/*-test.c +F: tests/qtest/ Register API M: Alistair Francis @@ -2185,7 +2182,7 @@ F: include/hw/acpi/tpm.h F: include/sysemu/tpm* F: qapi/tpm.json F: backends/tpm.c -F: tests/*tpm* +F: tests/qtest/*tpm* T: git https://github.com/stefanberger/qemu-tpm.git tpm-next Checkpatch @@ -2202,7 +2199,7 @@ F: include/migration/ F: migration/ F: scripts/vmstate-static-checker.py F: tests/vmstate-static-checker-data/ -F: tests/migration-test.c +F: tests/qtest/migration-test.c F: docs/devel/migration.rst F: qapi/migration.json diff --git a/configure b/configure index 28ee2a254f..1b8796fc21 100755 --- a/configure +++ b/configure @@ -7963,8 +7963,8 @@ fi # so the build tree will be missing the link back to the new file, and # tests might fail. Prefer to keep the relevant files in their own # directory and symlink the directory instead. -DIRS="tests tests/tcg tests/tcg/lm32 tests/libqos tests/qapi-schema tests/qemu-iotests tests/vm" -DIRS="$DIRS tests/fp tests/qgraph" +DIRS="tests tests/tcg tests/tcg/lm32 tests/libqos tests/qapi-schema tests/qtest" +DIRS="$DIRS tests/qemu-iotests tests/vm tests/fp tests/qgraph" DIRS="$DIRS docs docs/interop fsdev scsi" DIRS="$DIRS pc-bios/optionrom pc-bios/s390-ccw" DIRS="$DIRS roms/seabios roms/vgabios" diff --git a/tests/Makefile.include b/tests/Makefile.include index 6bacf14d3f..bd2bcd6f1b 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -515,7 +515,7 @@ generated-files-y += tests/include/test-qapi-events-sub-module.h generated-files-y += tests/test-qapi-events-sub-sub-module.h generated-files-y += tests/test-qapi-introspect.h -QEMU_CFLAGS += -I$(SRC_PATH)/tests +QEMU_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest # Deps that are common to various different sets of tests below @@ -648,18 +648,18 @@ tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.jso @mv tests/qapi-schema/doc-good-qapi-doc.texi $@ @rm -f tests/qapi-schema/doc-good-qapi-*.[ch] tests/qapi-schema/doc-good-qmp-*.[ch] -tests/dbus-vmstate1.h tests/dbus-vmstate1.c: tests/dbus-vmstate1-gen-timestamp ; -tests/dbus-vmstate1-gen-timestamp: $(SRC_PATH)/tests/dbus-vmstate1.xml +tests/qtest/dbus-vmstate1.h tests/qtest/dbus-vmstate1.c: tests/qtest/dbus-vmstate1-gen-timestamp ; +tests/qtest/dbus-vmstate1-gen-timestamp: $(SRC_PATH)/tests/qtest/dbus-vmstate1.xml $(call quiet-command,$(GDBUS_CODEGEN) $< \ - --interface-prefix org.qemu --generate-c-code tests/dbus-vmstate1, \ + --interface-prefix org.qemu --generate-c-code tests/qtest/dbus-vmstate1, \ "GEN","$(@:%-timestamp=%)") @>$@ -tests/dbus-vmstate-test.o-cflags := -DSRCDIR="$(SRC_PATH)" -tests/dbus-vmstate1.o-cflags := $(GIO_CFLAGS) -tests/dbus-vmstate1.o-libs := $(GIO_LIBS) +tests/qtest/dbus-vmstate-test.o-cflags := -DSRCDIR="$(SRC_PATH)" +tests/qtest/dbus-vmstate1.o-cflags := $(GIO_CFLAGS) +tests/qtest/dbus-vmstate1.o-libs := $(GIO_LIBS) -tests/dbus-vmstate-test.o: tests/dbus-vmstate1.h +tests/qtest/dbus-vmstate-test.o: tests/qtest/dbus-vmstate1.h tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) @@ -708,12 +708,12 @@ tests/test-authz-pam$(EXESUF): tests/test-authz-pam.o $(test-authz-obj-y) tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y) -tests/tpm-crb-swtpm-test$(EXESUF): tests/tpm-crb-swtpm-test.o tests/tpm-emu.o \ - tests/tpm-util.o tests/tpm-tests.o $(test-io-obj-y) -tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y) -tests/tpm-tis-swtpm-test$(EXESUF): tests/tpm-tis-swtpm-test.o tests/tpm-emu.o \ - tests/tpm-util.o tests/tpm-tests.o $(test-io-obj-y) -tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y) +tests/qtest/tpm-crb-swtpm-test$(EXESUF): tests/qtest/tpm-crb-swtpm-test.o tests/qtest/tpm-emu.o \ + tests/qtest/tpm-util.o tests/qtest/tpm-tests.o $(test-io-obj-y) +tests/qtest/tpm-crb-test$(EXESUF): tests/qtest/tpm-crb-test.o tests/qtest/tpm-emu.o $(test-io-obj-y) +tests/qtest/tpm-tis-swtpm-test$(EXESUF): tests/qtest/tpm-tis-swtpm-test.o tests/qtest/tpm-emu.o \ + tests/qtest/tpm-util.o tests/qtest/tpm-tests.o $(test-io-obj-y) +tests/qtest/tpm-tis-test$(EXESUF): tests/qtest/tpm-tis-test.o tests/qtest/tpm-emu.o $(test-io-obj-y) tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ tests/io-channel-helpers.o $(test-io-obj-y) tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \ @@ -743,7 +743,7 @@ libqos-pc-obj-y += tests/libqos/ahci.o libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o # Devices -qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) +qos-test-obj-y = tests/qtest/qos-test.o $(libqgraph-obj-y) qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) qos-test-obj-y += tests/libqos/e1000e.o qos-test-obj-y += tests/libqos/i2c.o @@ -776,98 +776,98 @@ qos-test-obj-y += tests/libqos/ppc64_pseries-machine.o qos-test-obj-y += tests/libqos/x86_64_pc-machine.o # Tests -qos-test-obj-y += tests/ac97-test.o -qos-test-obj-y += tests/ds1338-test.o -qos-test-obj-y += tests/e1000-test.o -qos-test-obj-y += tests/e1000e-test.o -qos-test-obj-y += tests/eepro100-test.o -qos-test-obj-y += tests/es1370-test.o -qos-test-obj-y += tests/ipoctal232-test.o -qos-test-obj-y += tests/megasas-test.o -qos-test-obj-y += tests/ne2000-test.o -qos-test-obj-y += tests/nvme-test.o -qos-test-obj-y += tests/pca9552-test.o -qos-test-obj-y += tests/pci-test.o -qos-test-obj-y += tests/pcnet-test.o -qos-test-obj-y += tests/sdhci-test.o -qos-test-obj-y += tests/spapr-phb-test.o -qos-test-obj-y += tests/tmp105-test.o -qos-test-obj-y += tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y) -qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) -qos-test-obj-y += tests/virtio-test.o -qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o -qos-test-obj-y += tests/virtio-blk-test.o -qos-test-obj-y += tests/virtio-net-test.o -qos-test-obj-y += tests/virtio-rng-test.o -qos-test-obj-y += tests/virtio-scsi-test.o -qos-test-obj-y += tests/virtio-serial-test.o -qos-test-obj-y += tests/vmxnet3-test.o +qos-test-obj-y += tests/qtest/ac97-test.o +qos-test-obj-y += tests/qtest/ds1338-test.o +qos-test-obj-y += tests/qtest/e1000-test.o +qos-test-obj-y += tests/qtest/e1000e-test.o +qos-test-obj-y += tests/qtest/eepro100-test.o +qos-test-obj-y += tests/qtest/es1370-test.o +qos-test-obj-y += tests/qtest/ipoctal232-test.o +qos-test-obj-y += tests/qtest/megasas-test.o +qos-test-obj-y += tests/qtest/ne2000-test.o +qos-test-obj-y += tests/qtest/nvme-test.o +qos-test-obj-y += tests/qtest/pca9552-test.o +qos-test-obj-y += tests/qtest/pci-test.o +qos-test-obj-y += tests/qtest/pcnet-test.o +qos-test-obj-y += tests/qtest/sdhci-test.o +qos-test-obj-y += tests/qtest/spapr-phb-test.o +qos-test-obj-y += tests/qtest/tmp105-test.o +qos-test-obj-y += tests/qtest/usb-hcd-ohci-test.o $(libqos-usb-obj-y) +qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/qtest/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) +qos-test-obj-y += tests/qtest/virtio-test.o +qos-test-obj-$(CONFIG_VIRTFS) += tests/qtest/virtio-9p-test.o +qos-test-obj-y += tests/qtest/virtio-blk-test.o +qos-test-obj-y += tests/qtest/virtio-net-test.o +qos-test-obj-y += tests/qtest/virtio-rng-test.o +qos-test-obj-y += tests/qtest/virtio-scsi-test.o +qos-test-obj-y += tests/qtest/virtio-serial-test.o +qos-test-obj-y += tests/qtest/vmxnet3-test.o check-unit-y += tests/test-qgraph$(EXESUF) tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) check-qtest-generic-y += qos-test -tests/qos-test$(EXESUF): $(qos-test-obj-y) - -tests/qmp-test$(EXESUF): tests/qmp-test.o -tests/qmp-cmd-test$(EXESUF): tests/qmp-cmd-test.o -tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o -tests/rtc-test$(EXESUF): tests/rtc-test.o -tests/m48t59-test$(EXESUF): tests/m48t59-test.o -tests/hexloader-test$(EXESUF): tests/hexloader-test.o -tests/pflash-cfi02$(EXESUF): tests/pflash-cfi02-test.o -tests/endianness-test$(EXESUF): tests/endianness-test.o -tests/prom-env-test$(EXESUF): tests/prom-env-test.o $(libqos-obj-y) -tests/rtas-test$(EXESUF): tests/rtas-test.o $(libqos-spapr-obj-y) -tests/fdc-test$(EXESUF): tests/fdc-test.o -tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) -tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) qemu-img$(EXESUF) -tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o -tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o -tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o $(libqos-obj-y) -tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) -tests/boot-serial-test$(EXESUF): tests/boot-serial-test.o $(libqos-obj-y) -tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \ - tests/boot-sector.o tests/acpi-utils.o $(libqos-obj-y) -tests/pxe-test$(EXESUF): tests/pxe-test.o tests/boot-sector.o $(libqos-obj-y) -tests/microbit-test$(EXESUF): tests/microbit-test.o -tests/m25p80-test$(EXESUF): tests/m25p80-test.o -tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) -tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y) -tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) -tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) -tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o -tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o -tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) -tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o -tests/display-vga-test$(EXESUF): tests/display-vga-test.o -tests/qom-test$(EXESUF): tests/qom-test.o -tests/test-hmp$(EXESUF): tests/test-hmp.o -tests/machine-none-test$(EXESUF): tests/machine-none-test.o -tests/device-plug-test$(EXESUF): tests/device-plug-test.o -tests/drive_del-test$(EXESUF): tests/drive_del-test.o -tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o -tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o -tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o -tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o -tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y) -tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y) -tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) -tests/cpu-plug-test$(EXESUF): tests/cpu-plug-test.o -tests/migration-test$(EXESUF): tests/migration-test.o tests/migration-helpers.o -tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o -tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y) -tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y) -tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y) -tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y) -tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) -tests/dbus-vmstate-test$(EXESUF): tests/dbus-vmstate-test.o tests/migration-helpers.o tests/dbus-vmstate1.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) -tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a -tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o -tests/numa-test$(EXESUF): tests/numa-test.o -tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o -tests/cdrom-test$(EXESUF): tests/cdrom-test.o tests/boot-sector.o $(libqos-obj-y) -tests/arm-cpu-features$(EXESUF): tests/arm-cpu-features.o +tests/qtest/qos-test$(EXESUF): $(qos-test-obj-y) + +tests/qtest/qmp-test$(EXESUF): tests/qtest/qmp-test.o +tests/qtest/qmp-cmd-test$(EXESUF): tests/qtest/qmp-cmd-test.o +tests/qtest/device-introspect-test$(EXESUF): tests/qtest/device-introspect-test.o +tests/qtest/rtc-test$(EXESUF): tests/qtest/rtc-test.o +tests/qtest/m48t59-test$(EXESUF): tests/qtest/m48t59-test.o +tests/qtest/hexloader-test$(EXESUF): tests/qtest/hexloader-test.o +tests/qtest/pflash-cfi02$(EXESUF): tests/qtest/pflash-cfi02-test.o +tests/qtest/endianness-test$(EXESUF): tests/qtest/endianness-test.o +tests/qtest/prom-env-test$(EXESUF): tests/qtest/prom-env-test.o $(libqos-obj-y) +tests/qtest/rtas-test$(EXESUF): tests/qtest/rtas-test.o $(libqos-spapr-obj-y) +tests/qtest/fdc-test$(EXESUF): tests/qtest/fdc-test.o +tests/qtest/ide-test$(EXESUF): tests/qtest/ide-test.o $(libqos-pc-obj-y) +tests/qtest/ahci-test$(EXESUF): tests/qtest/ahci-test.o $(libqos-pc-obj-y) qemu-img$(EXESUF) +tests/qtest/ipmi-kcs-test$(EXESUF): tests/qtest/ipmi-kcs-test.o +tests/qtest/ipmi-bt-test$(EXESUF): tests/qtest/ipmi-bt-test.o +tests/qtest/hd-geo-test$(EXESUF): tests/qtest/hd-geo-test.o $(libqos-obj-y) +tests/qtest/boot-order-test$(EXESUF): tests/qtest/boot-order-test.o $(libqos-obj-y) +tests/qtest/boot-serial-test$(EXESUF): tests/qtest/boot-serial-test.o $(libqos-obj-y) +tests/qtest/bios-tables-test$(EXESUF): tests/qtest/bios-tables-test.o \ + tests/qtest/boot-sector.o tests/qtest/acpi-utils.o $(libqos-obj-y) +tests/qtest/pxe-test$(EXESUF): tests/qtest/pxe-test.o tests/qtest/boot-sector.o $(libqos-obj-y) +tests/qtest/microbit-test$(EXESUF): tests/qtest/microbit-test.o +tests/qtest/m25p80-test$(EXESUF): tests/qtest/m25p80-test.o +tests/qtest/i440fx-test$(EXESUF): tests/qtest/i440fx-test.o $(libqos-pc-obj-y) +tests/qtest/q35-test$(EXESUF): tests/qtest/q35-test.o $(libqos-pc-obj-y) +tests/qtest/fw_cfg-test$(EXESUF): tests/qtest/fw_cfg-test.o $(libqos-pc-obj-y) +tests/qtest/rtl8139-test$(EXESUF): tests/qtest/rtl8139-test.o $(libqos-pc-obj-y) +tests/qtest/pnv-xscom-test$(EXESUF): tests/qtest/pnv-xscom-test.o +tests/qtest/wdt_ib700-test$(EXESUF): tests/qtest/wdt_ib700-test.o +tests/qtest/tco-test$(EXESUF): tests/qtest/tco-test.o $(libqos-pc-obj-y) +tests/qtest/virtio-ccw-test$(EXESUF): tests/qtest/virtio-ccw-test.o +tests/qtest/display-vga-test$(EXESUF): tests/qtest/display-vga-test.o +tests/qtest/qom-test$(EXESUF): tests/qtest/qom-test.o +tests/qtest/test-hmp$(EXESUF): tests/qtest/test-hmp.o +tests/qtest/machine-none-test$(EXESUF): tests/qtest/machine-none-test.o +tests/qtest/device-plug-test$(EXESUF): tests/qtest/device-plug-test.o +tests/qtest/drive_del-test$(EXESUF): tests/qtest/drive_del-test.o +tests/qtest/pvpanic-test$(EXESUF): tests/qtest/pvpanic-test.o +tests/qtest/i82801b11-test$(EXESUF): tests/qtest/i82801b11-test.o +tests/qtest/intel-hda-test$(EXESUF): tests/qtest/intel-hda-test.o +tests/qtest/ioh3420-test$(EXESUF): tests/qtest/ioh3420-test.o +tests/qtest/usb-hcd-uhci-test$(EXESUF): tests/qtest/usb-hcd-uhci-test.o $(libqos-usb-obj-y) +tests/qtest/usb-hcd-ehci-test$(EXESUF): tests/qtest/usb-hcd-ehci-test.o $(libqos-usb-obj-y) +tests/qtest/usb-hcd-xhci-test$(EXESUF): tests/qtest/usb-hcd-xhci-test.o $(libqos-usb-obj-y) +tests/qtest/cpu-plug-test$(EXESUF): tests/qtest/cpu-plug-test.o +tests/qtest/migration-test$(EXESUF): tests/qtest/migration-test.o tests/qtest/migration-helpers.o +tests/qtest/qemu-iotests/qtest/socket_scm_helper$(EXESUF): tests/qtest/qemu-iotests/qtest/socket_scm_helper.o +tests/qtest/test-netfilter$(EXESUF): tests/qtest/test-netfilter.o $(qtest-obj-y) +tests/qtest/test-filter-mirror$(EXESUF): tests/qtest/test-filter-mirror.o $(qtest-obj-y) +tests/qtest/test-filter-redirector$(EXESUF): tests/qtest/test-filter-redirector.o $(qtest-obj-y) +tests/qtest/test-x86-cpuid-compat$(EXESUF): tests/qtest/test-x86-cpuid-compat.o $(qtest-obj-y) +tests/qtest/ivshmem-test$(EXESUF): tests/qtest/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) +tests/qtest/dbus-vmstate-test$(EXESUF): tests/qtest/dbus-vmstate-test.o tests/qtest/migration-helpers.o tests/qtest/dbus-vmstate1.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) +tests/qtest/vhost-user-bridge$(EXESUF): tests/qtest/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a +tests/qtest/test-arm-mptimer$(EXESUF): tests/qtest/test-arm-mptimer.o +tests/qtest/numa-test$(EXESUF): tests/qtest/numa-test.o +tests/qtest/vmgenid-test$(EXESUF): tests/qtest/vmgenid-test.o tests/qtest/boot-sector.o tests/qtest/acpi-utils.o +tests/qtest/cdrom-test$(EXESUF): tests/qtest/cdrom-test.o tests/qtest/boot-sector.o $(libqos-obj-y) +tests/qtest/arm-cpu-features$(EXESUF): tests/qtest/arm-cpu-features.o tests/migration/stress$(EXESUF): tests/migration/stress.o $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") @@ -886,13 +886,13 @@ tests/migration/initrd-stress.img: tests/migration/stress$(EXESUF) TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS))) ifeq ($(CONFIG_POSIX),y) QTEST_TARGETS = $(TARGETS) -check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y:%=tests/%$(EXESUF))) -check-qtest-y += $(check-qtest-generic-y:%=tests/%$(EXESUF)) +check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y:%=tests/qtest/%$(EXESUF))) +check-qtest-y += $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF)) else QTEST_TARGETS = endif -qtest-obj-y = tests/libqtest.o $(test-util-obj-y) +qtest-obj-y = tests/qtest/libqtest.o $(test-util-obj-y) $(check-qtest-y): $(qtest-obj-y) tests/test-qga$(EXESUF): qemu-ga$(EXESUF) @@ -937,7 +937,7 @@ endef .PHONY: $(patsubst %, check-qtest-%, $(QTEST_TARGETS)) $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: %-softmmu/all $(check-qtest-y) - $(call do_test_human,$(check-qtest-$*-y:%=tests/%$(EXESUF)) $(check-qtest-generic-y:%=tests/%$(EXESUF)), \ + $(call do_test_human,$(check-qtest-$*-y:%=tests/qtest/%$(EXESUF)) $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF)), \ QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ QTEST_QEMU_IMG=qemu-img$(EXESUF)) @@ -950,7 +950,7 @@ check-speed: $(check-speed-y) # gtester tests with TAP output $(patsubst %, check-report-qtest-%.tap, $(QTEST_TARGETS)): check-report-qtest-%.tap: %-softmmu/all $(check-qtest-y) - $(call do_test_tap, $(check-qtest-$*-y:%=tests/%$(EXESUF)) $(check-qtest-generic-y:%=tests/%$(EXESUF)), \ + $(call do_test_tap, $(check-qtest-$*-y:%=tests/qtest/%$(EXESUF)) $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF)), \ QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ QTEST_QEMU_IMG=qemu-img$(EXESUF)) @@ -1215,10 +1215,10 @@ check-block: $(patsubst %,check-%, $(check-block-y)) endif check: check-block check-qapi-schema check-unit check-softfloat check-qtest check-decodetree check-clean: - rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y) - rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y:%=tests/%$(EXESUF))) $(check-qtest-generic-y:%=tests/%$(EXESUF))) + rm -rf $(check-unit-y) tests/*.o tests/*/*.o $(QEMU_IOTESTS_HELPERS-y) + rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y:%=tests/qtest/%$(EXESUF))) $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF))) rm -f tests/test-qapi-gen-timestamp - rm -f tests/dbus-vmstate1-gen-timestamp + rm -f tests/qtest/dbus-vmstate1-gen-timestamp rm -rf $(TESTS_VENV_DIR) $(TESTS_RESULTS_DIR) clean: check-clean @@ -1228,6 +1228,7 @@ clean: check-clean all: $(QEMU_IOTESTS_HELPERS-y) -include $(wildcard tests/*.d) +-include $(wildcard tests/qtest/*.d) -include $(wildcard tests/libqos/*.d) endif diff --git a/tests/ac97-test.c b/tests/ac97-test.c deleted file mode 100644 index b084e31bff..0000000000 --- a/tests/ac97-test.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * QTest testcase for AC97 - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -typedef struct QAC97 QAC97; - -struct QAC97 { - QOSGraphObject obj; - QPCIDevice dev; -}; - -static void *ac97_get_driver(void *obj, const char *interface) -{ - QAC97 *ac97 = obj; - - if (!g_strcmp0(interface, "pci-device")) { - return &ac97->dev; - } - - fprintf(stderr, "%s not present in e1000e\n", interface); - g_assert_not_reached(); -} - -static void *ac97_create(void *pci_bus, QGuestAllocator *alloc, void *addr) -{ - QAC97 *ac97 = g_new0(QAC97, 1); - QPCIBus *bus = pci_bus; - - qpci_device_init(&ac97->dev, bus, addr); - ac97->obj.get_driver = ac97_get_driver; - return &ac97->obj; -} - -static void ac97_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0", - }; - add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); - - qos_node_create_driver("AC97", ac97_create); - qos_node_produces("AC97", "pci-device"); - qos_node_consumes("AC97", "pci-bus", &opts); -} - -libqos_init(ac97_register_nodes); diff --git a/tests/acpi-utils.c b/tests/acpi-utils.c deleted file mode 100644 index d2a202efca..0000000000 --- a/tests/acpi-utils.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * ACPI Utility Functions - * - * Copyright (c) 2013 Red Hat Inc. - * Copyright (c) 2017 Skyport Systems - * - * Authors: - * Michael S. Tsirkin , - * Ben Warren - * - * 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 -#include "qemu-common.h" -#include "qemu/bitmap.h" -#include "acpi-utils.h" -#include "boot-sector.h" - -uint8_t acpi_calc_checksum(const uint8_t *data, int len) -{ - int i; - uint8_t sum = 0; - - for (i = 0; i < len; i++) { - sum += data[i]; - } - - return sum; -} - -uint32_t acpi_find_rsdp_address(QTestState *qts) -{ - uint32_t off; - - /* RSDP location can vary across a narrow range */ - for (off = 0xf0000; off < 0x100000; off += 0x10) { - uint8_t sig[] = "RSD PTR "; - int i; - - for (i = 0; i < sizeof sig - 1; ++i) { - sig[i] = qtest_readb(qts, off + i); - } - - if (!memcmp(sig, "RSD PTR ", sizeof sig)) { - break; - } - } - return off; -} - -void acpi_fetch_rsdp_table(QTestState *qts, uint64_t addr, uint8_t *rsdp_table) -{ - uint8_t revision; - - /* Read mandatory revision 0 table data (20 bytes) first */ - qtest_memread(qts, addr, rsdp_table, 20); - revision = rsdp_table[15 /* Revision offset */]; - - switch (revision) { - case 0: /* ACPI 1.0 RSDP */ - break; - case 2: /* ACPI 2.0+ RSDP */ - /* Read the rest of the RSDP table */ - qtest_memread(qts, addr + 20, rsdp_table + 20, 16); - break; - default: - g_assert_not_reached(); - } - - 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, int addr_size, const char *sig, - bool verify_checksum) -{ - uint32_t len; - uint64_t addr = 0; - - g_assert(addr_size == 4 || addr_size == 8); - memcpy(&addr, addr_ptr , addr_size); - addr = le64_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)); - } -} - -#define GUID_SIZE 16 -static const uint8_t AcpiTestSupportGuid[GUID_SIZE] = { - 0xb1, 0xa6, 0x87, 0xab, - 0x34, 0x20, - 0xa0, 0xbd, - 0x71, 0xbd, 0x37, 0x50, 0x07, 0x75, 0x77, 0x85 }; - -typedef struct { - uint8_t signature_guid[GUID_SIZE]; - uint64_t rsdp10; - uint64_t rsdp20; -} __attribute__((packed)) UefiTestSupport; - -/* Wait at most 600 seconds (test is slow with TCG and --enable-debug) */ -#define TEST_DELAY (1 * G_USEC_PER_SEC / 10) -#define TEST_CYCLES MAX((600 * G_USEC_PER_SEC / TEST_DELAY), 1) -#define MB 0x100000ULL -uint64_t acpi_find_rsdp_address_uefi(QTestState *qts, uint64_t start, - uint64_t size) -{ - int i, j; - uint8_t data[GUID_SIZE]; - - for (i = 0; i < TEST_CYCLES; ++i) { - for (j = 0; j < size / MB; j++) { - /* look for GUID at every 1Mb block */ - uint64_t addr = start + j * MB; - - qtest_memread(qts, addr, data, sizeof(data)); - if (!memcmp(AcpiTestSupportGuid, data, sizeof(data))) { - UefiTestSupport ret; - - qtest_memread(qts, addr, &ret, sizeof(ret)); - ret.rsdp10 = le64_to_cpu(ret.rsdp10); - ret.rsdp20 = le64_to_cpu(ret.rsdp20); - return ret.rsdp20 ? ret.rsdp20 : ret.rsdp10; - } - } - g_usleep(TEST_DELAY); - } - g_assert_not_reached(); - return 0; -} diff --git a/tests/acpi-utils.h b/tests/acpi-utils.h deleted file mode 100644 index 0c86780689..0000000000 --- a/tests/acpi-utils.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Utilities for working with ACPI tables - * - * Copyright (c) 2013 Red Hat Inc. - * - * Authors: - * Michael S. Tsirkin , - * - * 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 TEST_ACPI_UTILS_H -#define TEST_ACPI_UTILS_H - -#include "libqtest.h" - -/* DSDT and SSDTs format */ -typedef struct { - uint8_t *aml; /* aml bytecode from guest */ - uint32_t aml_len; - gchar *aml_file; - gchar *asl; /* asl code generated from aml */ - gsize asl_len; - gchar *asl_file; - bool tmp_files_retain; /* do not delete the temp asl/aml */ -} AcpiSdtTable; - -#define ACPI_ASSERT_CMP(actual, expected) do { \ - char ACPI_ASSERT_CMP_str[5] = {}; \ - memcpy(ACPI_ASSERT_CMP_str, &actual, 4); \ - g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ -} while (0) - -#define ACPI_ASSERT_CMP64(actual, expected) do { \ - char ACPI_ASSERT_CMP_str[9] = {}; \ - memcpy(ACPI_ASSERT_CMP_str, &actual, 8); \ - g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ -} 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); -uint64_t acpi_find_rsdp_address_uefi(QTestState *qts, uint64_t start, - uint64_t size); -void acpi_fetch_rsdp_table(QTestState *qts, uint64_t addr, uint8_t *rsdp_table); -void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len, - const uint8_t *addr_ptr, int addr_size, const char *sig, - bool verify_checksum); - -#endif /* TEST_ACPI_UTILS_H */ diff --git a/tests/ahci-test.c b/tests/ahci-test.c deleted file mode 100644 index c8d42ceea0..0000000000 --- a/tests/ahci-test.c +++ /dev/null @@ -1,1954 +0,0 @@ -/* - * AHCI test cases - * - * Copyright (c) 2014 John Snow - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include - -#include "libqtest.h" -#include "libqos/libqos-pc.h" -#include "libqos/ahci.h" -#include "libqos/pci-pc.h" - -#include "qemu-common.h" -#include "qapi/qmp/qdict.h" -#include "qemu/host-utils.h" - -#include "hw/pci/pci_ids.h" -#include "hw/pci/pci_regs.h" - -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(s, ...) qobject_unref(qtest_qmp(s, __VA_ARGS__)) - -/* Test images sizes in MB */ -#define TEST_IMAGE_SIZE_MB_LARGE (200 * 1024) -#define TEST_IMAGE_SIZE_MB_SMALL 64 - -/*** Globals ***/ -static char tmp_path[] = "/tmp/qtest.XXXXXX"; -static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; -static char mig_socket[] = "/tmp/qtest-migration.XXXXXX"; -static bool ahci_pedantic; -static const char *imgfmt; -static unsigned test_image_size_mb; - -/*** Function Declarations ***/ -static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port); -static void ahci_test_pci_spec(AHCIQState *ahci); -static void ahci_test_pci_caps(AHCIQState *ahci, uint16_t header, - uint8_t offset); -static void ahci_test_satacap(AHCIQState *ahci, uint8_t offset); -static void ahci_test_msicap(AHCIQState *ahci, uint8_t offset); -static void ahci_test_pmcap(AHCIQState *ahci, uint8_t offset); - -/*** Utilities ***/ - -static uint64_t mb_to_sectors(uint64_t image_size_mb) -{ - return (image_size_mb * 1024 * 1024) / AHCI_SECTOR_SIZE; -} - -static void string_bswap16(uint16_t *s, size_t bytes) -{ - g_assert_cmphex((bytes & 1), ==, 0); - bytes /= 2; - - while (bytes--) { - *s = bswap16(*s); - s++; - } -} - -/** - * Verify that the transfer did not corrupt our state at all. - */ -static void verify_state(AHCIQState *ahci, uint64_t hba_old) -{ - int i, j; - uint32_t ahci_fingerprint; - uint64_t hba_base; - AHCICommandHeader cmd; - - ahci_fingerprint = qpci_config_readl(ahci->dev, PCI_VENDOR_ID); - g_assert_cmphex(ahci_fingerprint, ==, ahci->fingerprint); - - /* If we haven't initialized, this is as much as can be validated. */ - if (!ahci->enabled) { - return; - } - - hba_base = (uint64_t)qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); - g_assert_cmphex(hba_base, ==, hba_old); - - g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP), ==, ahci->cap); - g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP2), ==, ahci->cap2); - - for (i = 0; i < 32; i++) { - g_assert_cmphex(ahci_px_rreg(ahci, i, AHCI_PX_FB), ==, - ahci->port[i].fb); - g_assert_cmphex(ahci_px_rreg(ahci, i, AHCI_PX_CLB), ==, - ahci->port[i].clb); - for (j = 0; j < 32; j++) { - ahci_get_command_header(ahci, i, j, &cmd); - g_assert_cmphex(cmd.prdtl, ==, ahci->port[i].prdtl[j]); - g_assert_cmphex(cmd.ctba, ==, ahci->port[i].ctba[j]); - } - } -} - -static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) -{ - QOSState *tmp = to->parent; - QPCIDevice *dev = to->dev; - char *uri_local = NULL; - uint64_t hba_old; - - if (uri == NULL) { - uri_local = g_strdup_printf("%s%s", "unix:", mig_socket); - uri = uri_local; - } - - hba_old = (uint64_t)qpci_config_readl(from->dev, PCI_BASE_ADDRESS_5); - - /* context will be 'to' after completion. */ - migrate(from->parent, to->parent, uri); - - /* We'd like for the AHCIState objects to still point - * to information specific to its specific parent - * instance, but otherwise just inherit the new data. */ - memcpy(to, from, sizeof(AHCIQState)); - to->parent = tmp; - to->dev = dev; - - tmp = from->parent; - dev = from->dev; - memset(from, 0x00, sizeof(AHCIQState)); - from->parent = tmp; - from->dev = dev; - - verify_state(to, hba_old); - g_free(uri_local); -} - -/*** Test Setup & Teardown ***/ - -/** - * Start a Q35 machine and bookmark a handle to the AHCI device. - */ -static AHCIQState *ahci_vboot(const char *cli, va_list ap) -{ - AHCIQState *s; - - s = g_new0(AHCIQState, 1); - s->parent = qtest_pc_vboot(cli, ap); - alloc_set_flags(&s->parent->alloc, ALLOC_LEAK_ASSERT); - - /* Verify that we have an AHCI device present. */ - s->dev = get_ahci_device(s->parent->qts, &s->fingerprint); - - return s; -} - -/** - * Start a Q35 machine and bookmark a handle to the AHCI device. - */ -static AHCIQState *ahci_boot(const char *cli, ...) -{ - AHCIQState *s; - va_list ap; - - if (cli) { - va_start(ap, cli); - s = ahci_vboot(cli, ap); - va_end(ap); - } else { - cli = "-drive if=none,id=drive0,file=%s,cache=writeback,format=%s" - " -M q35 " - "-device ide-hd,drive=drive0 " - "-global ide-hd.serial=%s " - "-global ide-hd.ver=%s"; - s = ahci_boot(cli, tmp_path, imgfmt, "testdisk", "version"); - } - - return s; -} - -/** - * Clean up the PCI device, then terminate the QEMU instance. - */ -static void ahci_shutdown(AHCIQState *ahci) -{ - QOSState *qs = ahci->parent; - - ahci_clean_mem(ahci); - free_ahci_device(ahci->dev); - g_free(ahci); - qtest_shutdown(qs); -} - -/** - * Boot and fully enable the HBA device. - * @see ahci_boot, ahci_pci_enable and ahci_hba_enable. - */ -static AHCIQState *ahci_boot_and_enable(const char *cli, ...) -{ - AHCIQState *ahci; - va_list ap; - uint16_t buff[256]; - uint8_t port; - uint8_t hello; - - if (cli) { - va_start(ap, cli); - ahci = ahci_vboot(cli, ap); - va_end(ap); - } else { - ahci = ahci_boot(NULL); - } - - ahci_pci_enable(ahci); - ahci_hba_enable(ahci); - /* Initialize test device */ - port = ahci_port_select(ahci); - ahci_port_clear(ahci, port); - if (is_atapi(ahci, port)) { - hello = CMD_PACKET_ID; - } else { - hello = CMD_IDENTIFY; - } - ahci_io(ahci, port, hello, &buff, sizeof(buff), 0); - - return ahci; -} - -/*** Specification Adherence Tests ***/ - -/** - * Implementation for test_pci_spec. Ensures PCI configuration space is sane. - */ -static void ahci_test_pci_spec(AHCIQState *ahci) -{ - uint8_t datab; - uint16_t data; - uint32_t datal; - - /* Most of these bits should start cleared until we turn them on. */ - data = qpci_config_readw(ahci->dev, PCI_COMMAND); - ASSERT_BIT_CLEAR(data, PCI_COMMAND_MEMORY); - ASSERT_BIT_CLEAR(data, PCI_COMMAND_MASTER); - ASSERT_BIT_CLEAR(data, PCI_COMMAND_SPECIAL); /* Reserved */ - ASSERT_BIT_CLEAR(data, PCI_COMMAND_VGA_PALETTE); /* Reserved */ - ASSERT_BIT_CLEAR(data, PCI_COMMAND_PARITY); - ASSERT_BIT_CLEAR(data, PCI_COMMAND_WAIT); /* Reserved */ - ASSERT_BIT_CLEAR(data, PCI_COMMAND_SERR); - ASSERT_BIT_CLEAR(data, PCI_COMMAND_FAST_BACK); - ASSERT_BIT_CLEAR(data, PCI_COMMAND_INTX_DISABLE); - ASSERT_BIT_CLEAR(data, 0xF800); /* Reserved */ - - data = qpci_config_readw(ahci->dev, PCI_STATUS); - ASSERT_BIT_CLEAR(data, 0x01 | 0x02 | 0x04); /* Reserved */ - ASSERT_BIT_CLEAR(data, PCI_STATUS_INTERRUPT); - ASSERT_BIT_SET(data, PCI_STATUS_CAP_LIST); /* must be set */ - ASSERT_BIT_CLEAR(data, PCI_STATUS_UDF); /* Reserved */ - ASSERT_BIT_CLEAR(data, PCI_STATUS_PARITY); - ASSERT_BIT_CLEAR(data, PCI_STATUS_SIG_TARGET_ABORT); - ASSERT_BIT_CLEAR(data, PCI_STATUS_REC_TARGET_ABORT); - ASSERT_BIT_CLEAR(data, PCI_STATUS_REC_MASTER_ABORT); - ASSERT_BIT_CLEAR(data, PCI_STATUS_SIG_SYSTEM_ERROR); - ASSERT_BIT_CLEAR(data, PCI_STATUS_DETECTED_PARITY); - - /* RID occupies the low byte, CCs occupy the high three. */ - datal = qpci_config_readl(ahci->dev, PCI_CLASS_REVISION); - if (ahci_pedantic) { - /* AHCI 1.3 specifies that at-boot, the RID should reset to 0x00, - * Though in practice this is likely seldom true. */ - ASSERT_BIT_CLEAR(datal, 0xFF); - } - - /* BCC *must* equal 0x01. */ - g_assert_cmphex(PCI_BCC(datal), ==, 0x01); - if (PCI_SCC(datal) == 0x01) { - /* IDE */ - ASSERT_BIT_SET(0x80000000, datal); - ASSERT_BIT_CLEAR(0x60000000, datal); - } else if (PCI_SCC(datal) == 0x04) { - /* RAID */ - g_assert_cmphex(PCI_PI(datal), ==, 0); - } else if (PCI_SCC(datal) == 0x06) { - /* AHCI */ - g_assert_cmphex(PCI_PI(datal), ==, 0x01); - } else { - g_assert_not_reached(); - } - - datab = qpci_config_readb(ahci->dev, PCI_CACHE_LINE_SIZE); - g_assert_cmphex(datab, ==, 0); - - datab = qpci_config_readb(ahci->dev, PCI_LATENCY_TIMER); - g_assert_cmphex(datab, ==, 0); - - /* Only the bottom 7 bits must be off. */ - datab = qpci_config_readb(ahci->dev, PCI_HEADER_TYPE); - ASSERT_BIT_CLEAR(datab, 0x7F); - - /* BIST is optional, but the low 7 bits must always start off regardless. */ - datab = qpci_config_readb(ahci->dev, PCI_BIST); - ASSERT_BIT_CLEAR(datab, 0x7F); - - /* BARS 0-4 do not have a boot spec, but ABAR/BAR5 must be clean. */ - datal = qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); - g_assert_cmphex(datal, ==, 0); - - qpci_config_writel(ahci->dev, PCI_BASE_ADDRESS_5, 0xFFFFFFFF); - datal = qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); - /* ABAR must be 32-bit, memory mapped, non-prefetchable and - * must be >= 512 bytes. To that end, bits 0-8 must be off. */ - ASSERT_BIT_CLEAR(datal, 0xFF); - - /* Capability list MUST be present, */ - datal = qpci_config_readl(ahci->dev, PCI_CAPABILITY_LIST); - /* But these bits are reserved. */ - ASSERT_BIT_CLEAR(datal, ~0xFF); - g_assert_cmphex(datal, !=, 0); - - /* Check specification adherence for capability extenstions. */ - data = qpci_config_readw(ahci->dev, datal); - - switch (ahci->fingerprint) { - case AHCI_INTEL_ICH9: - /* Intel ICH9 Family Datasheet 14.1.19 p.550 */ - g_assert_cmphex((data & 0xFF), ==, PCI_CAP_ID_MSI); - break; - default: - /* AHCI 1.3, Section 2.1.14 -- CAP must point to PMCAP. */ - g_assert_cmphex((data & 0xFF), ==, PCI_CAP_ID_PM); - } - - ahci_test_pci_caps(ahci, data, (uint8_t)datal); - - /* Reserved. */ - datal = qpci_config_readl(ahci->dev, PCI_CAPABILITY_LIST + 4); - g_assert_cmphex(datal, ==, 0); - - /* IPIN might vary, but ILINE must be off. */ - datab = qpci_config_readb(ahci->dev, PCI_INTERRUPT_LINE); - g_assert_cmphex(datab, ==, 0); -} - -/** - * Test PCI capabilities for AHCI specification adherence. - */ -static void ahci_test_pci_caps(AHCIQState *ahci, uint16_t header, - uint8_t offset) -{ - uint8_t cid = header & 0xFF; - uint8_t next = header >> 8; - - g_test_message("CID: %02x; next: %02x", cid, next); - - switch (cid) { - case PCI_CAP_ID_PM: - ahci_test_pmcap(ahci, offset); - break; - case PCI_CAP_ID_MSI: - ahci_test_msicap(ahci, offset); - break; - case PCI_CAP_ID_SATA: - ahci_test_satacap(ahci, offset); - break; - - default: - g_test_message("Unknown CAP 0x%02x", cid); - } - - if (next) { - ahci_test_pci_caps(ahci, qpci_config_readw(ahci->dev, next), next); - } -} - -/** - * Test SATA PCI capabilitity for AHCI specification adherence. - */ -static void ahci_test_satacap(AHCIQState *ahci, uint8_t offset) -{ - uint16_t dataw; - uint32_t datal; - - g_test_message("Verifying SATACAP"); - - /* Assert that the SATACAP version is 1.0, And reserved bits are empty. */ - dataw = qpci_config_readw(ahci->dev, offset + 2); - g_assert_cmphex(dataw, ==, 0x10); - - /* Grab the SATACR1 register. */ - datal = qpci_config_readw(ahci->dev, offset + 4); - - switch (datal & 0x0F) { - case 0x04: /* BAR0 */ - case 0x05: /* BAR1 */ - case 0x06: - case 0x07: - case 0x08: - case 0x09: /* BAR5 */ - case 0x0F: /* Immediately following SATACR1 in PCI config space. */ - break; - default: - /* Invalid BARLOC for the Index Data Pair. */ - g_assert_not_reached(); - } - - /* Reserved. */ - g_assert_cmphex((datal >> 24), ==, 0x00); -} - -/** - * Test MSI PCI capability for AHCI specification adherence. - */ -static void ahci_test_msicap(AHCIQState *ahci, uint8_t offset) -{ - uint16_t dataw; - uint32_t datal; - - g_test_message("Verifying MSICAP"); - - dataw = qpci_config_readw(ahci->dev, offset + PCI_MSI_FLAGS); - ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_ENABLE); - ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_QSIZE); - ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_RESERVED); - - datal = qpci_config_readl(ahci->dev, offset + PCI_MSI_ADDRESS_LO); - g_assert_cmphex(datal, ==, 0); - - if (dataw & PCI_MSI_FLAGS_64BIT) { - g_test_message("MSICAP is 64bit"); - datal = qpci_config_readl(ahci->dev, offset + PCI_MSI_ADDRESS_HI); - g_assert_cmphex(datal, ==, 0); - dataw = qpci_config_readw(ahci->dev, offset + PCI_MSI_DATA_64); - g_assert_cmphex(dataw, ==, 0); - } else { - g_test_message("MSICAP is 32bit"); - dataw = qpci_config_readw(ahci->dev, offset + PCI_MSI_DATA_32); - g_assert_cmphex(dataw, ==, 0); - } -} - -/** - * Test Power Management PCI capability for AHCI specification adherence. - */ -static void ahci_test_pmcap(AHCIQState *ahci, uint8_t offset) -{ - uint16_t dataw; - - g_test_message("Verifying PMCAP"); - - dataw = qpci_config_readw(ahci->dev, offset + PCI_PM_PMC); - ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_PME_CLOCK); - ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_RESERVED); - ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_D1); - ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_D2); - - dataw = qpci_config_readw(ahci->dev, offset + PCI_PM_CTRL); - ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_STATE_MASK); - ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_RESERVED); - ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_DATA_SEL_MASK); - ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_DATA_SCALE_MASK); -} - -static void ahci_test_hba_spec(AHCIQState *ahci) -{ - unsigned i; - uint32_t reg; - uint32_t ports; - uint8_t nports_impl; - uint8_t maxports; - - g_assert(ahci != NULL); - - /* - * Note that the AHCI spec does expect the BIOS to set up a few things: - * CAP.SSS - Support for staggered spin-up (t/f) - * CAP.SMPS - Support for mechanical presence switches (t/f) - * PI - Ports Implemented (1-32) - * PxCMD.HPCP - Hot Plug Capable Port - * PxCMD.MPSP - Mechanical Presence Switch Present - * PxCMD.CPD - Cold Presence Detection support - * - * Additional items are touched if CAP.SSS is on, see AHCI 10.1.1 p.97: - * Foreach Port Implemented: - * -PxCMD.ST, PxCMD.CR, PxCMD.FRE, PxCMD.FR, PxSCTL.DET are 0 - * -PxCLB/U and PxFB/U are set to valid regions in memory - * -PxSUD is set to 1. - * -PxSSTS.DET is polled for presence; if detected, we continue: - * -PxSERR is cleared with 1's. - * -If PxTFD.STS.BSY, PxTFD.STS.DRQ, and PxTFD.STS.ERR are all zero, - * the device is ready. - */ - - /* 1 CAP - Capabilities Register */ - ahci->cap = ahci_rreg(ahci, AHCI_CAP); - ASSERT_BIT_CLEAR(ahci->cap, AHCI_CAP_RESERVED); - - /* 2 GHC - Global Host Control */ - reg = ahci_rreg(ahci, AHCI_GHC); - ASSERT_BIT_CLEAR(reg, AHCI_GHC_HR); - ASSERT_BIT_CLEAR(reg, AHCI_GHC_IE); - ASSERT_BIT_CLEAR(reg, AHCI_GHC_MRSM); - if (BITSET(ahci->cap, AHCI_CAP_SAM)) { - g_test_message("Supports AHCI-Only Mode: GHC_AE is Read-Only."); - ASSERT_BIT_SET(reg, AHCI_GHC_AE); - } else { - g_test_message("Supports AHCI/Legacy mix."); - ASSERT_BIT_CLEAR(reg, AHCI_GHC_AE); - } - - /* 3 IS - Interrupt Status */ - reg = ahci_rreg(ahci, AHCI_IS); - g_assert_cmphex(reg, ==, 0); - - /* 4 PI - Ports Implemented */ - ports = ahci_rreg(ahci, AHCI_PI); - /* Ports Implemented must be non-zero. */ - g_assert_cmphex(ports, !=, 0); - /* Ports Implemented must be <= Number of Ports. */ - nports_impl = ctpopl(ports); - g_assert_cmpuint(((AHCI_CAP_NP & ahci->cap) + 1), >=, nports_impl); - - /* Ports must be within the proper range. Given a mapping of SIZE, - * 256 bytes are used for global HBA control, and the rest is used - * for ports data, at 0x80 bytes each. */ - g_assert_cmphex(ahci->barsize, >, 0); - maxports = (ahci->barsize - HBA_DATA_REGION_SIZE) / HBA_PORT_DATA_SIZE; - /* e.g, 30 ports for 4K of memory. (4096 - 256) / 128 = 30 */ - g_assert_cmphex((reg >> maxports), ==, 0); - - /* 5 AHCI Version */ - reg = ahci_rreg(ahci, AHCI_VS); - switch (reg) { - case AHCI_VERSION_0_95: - case AHCI_VERSION_1_0: - case AHCI_VERSION_1_1: - case AHCI_VERSION_1_2: - case AHCI_VERSION_1_3: - break; - default: - g_assert_not_reached(); - } - - /* 6 Command Completion Coalescing Control: depends on CAP.CCCS. */ - reg = ahci_rreg(ahci, AHCI_CCCCTL); - if (BITSET(ahci->cap, AHCI_CAP_CCCS)) { - ASSERT_BIT_CLEAR(reg, AHCI_CCCCTL_EN); - ASSERT_BIT_CLEAR(reg, AHCI_CCCCTL_RESERVED); - ASSERT_BIT_SET(reg, AHCI_CCCCTL_CC); - ASSERT_BIT_SET(reg, AHCI_CCCCTL_TV); - } else { - g_assert_cmphex(reg, ==, 0); - } - - /* 7 CCC_PORTS */ - reg = ahci_rreg(ahci, AHCI_CCCPORTS); - /* Must be zeroes initially regardless of CAP.CCCS */ - g_assert_cmphex(reg, ==, 0); - - /* 8 EM_LOC */ - reg = ahci_rreg(ahci, AHCI_EMLOC); - if (BITCLR(ahci->cap, AHCI_CAP_EMS)) { - g_assert_cmphex(reg, ==, 0); - } - - /* 9 EM_CTL */ - reg = ahci_rreg(ahci, AHCI_EMCTL); - if (BITSET(ahci->cap, AHCI_CAP_EMS)) { - ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_STSMR); - ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_CTLTM); - ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_CTLRST); - ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_RESERVED); - } else { - g_assert_cmphex(reg, ==, 0); - } - - /* 10 CAP2 -- Capabilities Extended */ - ahci->cap2 = ahci_rreg(ahci, AHCI_CAP2); - ASSERT_BIT_CLEAR(ahci->cap2, AHCI_CAP2_RESERVED); - - /* 11 BOHC -- Bios/OS Handoff Control */ - reg = ahci_rreg(ahci, AHCI_BOHC); - g_assert_cmphex(reg, ==, 0); - - /* 12 -- 23: Reserved */ - g_test_message("Verifying HBA reserved area is empty."); - for (i = AHCI_RESERVED; i < AHCI_NVMHCI; ++i) { - reg = ahci_rreg(ahci, i); - g_assert_cmphex(reg, ==, 0); - } - - /* 24 -- 39: NVMHCI */ - if (BITCLR(ahci->cap2, AHCI_CAP2_NVMP)) { - g_test_message("Verifying HBA/NVMHCI area is empty."); - for (i = AHCI_NVMHCI; i < AHCI_VENDOR; ++i) { - reg = ahci_rreg(ahci, i); - g_assert_cmphex(reg, ==, 0); - } - } - - /* 40 -- 63: Vendor */ - g_test_message("Verifying HBA/Vendor area is empty."); - for (i = AHCI_VENDOR; i < AHCI_PORTS; ++i) { - reg = ahci_rreg(ahci, i); - g_assert_cmphex(reg, ==, 0); - } - - /* 64 -- XX: Port Space */ - for (i = 0; ports || (i < maxports); ports >>= 1, ++i) { - if (BITSET(ports, 0x1)) { - g_test_message("Testing port %u for spec", i); - ahci_test_port_spec(ahci, i); - } else { - uint16_t j; - uint16_t low = AHCI_PORTS + (32 * i); - uint16_t high = AHCI_PORTS + (32 * (i + 1)); - g_test_message("Asserting unimplemented port %u " - "(reg [%u-%u]) is empty.", - i, low, high - 1); - for (j = low; j < high; ++j) { - reg = ahci_rreg(ahci, j); - g_assert_cmphex(reg, ==, 0); - } - } - } -} - -/** - * Test the memory space for one port for specification adherence. - */ -static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port) -{ - uint32_t reg; - unsigned i; - - /* (0) CLB */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_CLB); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CLB_RESERVED); - - /* (1) CLBU */ - if (BITCLR(ahci->cap, AHCI_CAP_S64A)) { - reg = ahci_px_rreg(ahci, port, AHCI_PX_CLBU); - g_assert_cmphex(reg, ==, 0); - } - - /* (2) FB */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_FB); - ASSERT_BIT_CLEAR(reg, AHCI_PX_FB_RESERVED); - - /* (3) FBU */ - if (BITCLR(ahci->cap, AHCI_CAP_S64A)) { - reg = ahci_px_rreg(ahci, port, AHCI_PX_FBU); - g_assert_cmphex(reg, ==, 0); - } - - /* (4) IS */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); - g_assert_cmphex(reg, ==, 0); - - /* (5) IE */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_IE); - g_assert_cmphex(reg, ==, 0); - - /* (6) CMD */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_CMD); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FRE); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_RESERVED); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CCS); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FR); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_PMA); /* And RW only if CAP.SPM */ - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_APSTE); /* RW only if CAP2.APST */ - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ATAPI); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_DLAE); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ALPE); /* RW only if CAP.SALP */ - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ASP); /* RW only if CAP.SALP */ - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ICC); - /* If CPDetect support does not exist, CPState must be off. */ - if (BITCLR(reg, AHCI_PX_CMD_CPD)) { - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CPS); - } - /* If MPSPresence is not set, MPSState must be off. */ - if (BITCLR(reg, AHCI_PX_CMD_MPSP)) { - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSS); - } - /* If we do not support MPS, MPSS and MPSP must be off. */ - if (BITCLR(ahci->cap, AHCI_CAP_SMPS)) { - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSS); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSP); - } - /* If, via CPD or MPSP we detect a drive, HPCP must be on. */ - if (BITANY(reg, AHCI_PX_CMD_CPD | AHCI_PX_CMD_MPSP)) { - ASSERT_BIT_SET(reg, AHCI_PX_CMD_HPCP); - } - /* HPCP and ESP cannot both be active. */ - g_assert(!BITSET(reg, AHCI_PX_CMD_HPCP | AHCI_PX_CMD_ESP)); - /* If CAP.FBSS is not set, FBSCP must not be set. */ - if (BITCLR(ahci->cap, AHCI_CAP_FBSS)) { - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FBSCP); - } - - /* (7) RESERVED */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_RES1); - g_assert_cmphex(reg, ==, 0); - - /* (8) TFD */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); - /* At boot, prior to an FIS being received, the TFD register should be 0x7F, - * which breaks down as follows, as seen in AHCI 1.3 sec 3.3.8, p. 27. */ - ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR); - ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_CS1); - ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_DRQ); - ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_CS2); - ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_BSY); - ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR); - ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_RESERVED); - - /* (9) SIG */ - /* Though AHCI specifies the boot value should be 0xFFFFFFFF, - * Even when GHC.ST is zero, the AHCI HBA may receive the initial - * D2H register FIS and update the signature asynchronously, - * so we cannot expect a value here. AHCI 1.3, sec 3.3.9, pp 27-28 */ - - /* (10) SSTS / SCR0: SStatus */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_SSTS); - ASSERT_BIT_CLEAR(reg, AHCI_PX_SSTS_RESERVED); - /* Even though the register should be 0 at boot, it is asynchronous and - * prone to change, so we cannot test any well known value. */ - - /* (11) SCTL / SCR2: SControl */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_SCTL); - g_assert_cmphex(reg, ==, 0); - - /* (12) SERR / SCR1: SError */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR); - g_assert_cmphex(reg, ==, 0); - - /* (13) SACT / SCR3: SActive */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_SACT); - g_assert_cmphex(reg, ==, 0); - - /* (14) CI */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_CI); - g_assert_cmphex(reg, ==, 0); - - /* (15) SNTF */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_SNTF); - g_assert_cmphex(reg, ==, 0); - - /* (16) FBS */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_FBS); - ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_EN); - ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DEC); - ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_SDE); - ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DEV); - ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DWE); - ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_RESERVED); - if (BITSET(ahci->cap, AHCI_CAP_FBSS)) { - /* if Port-Multiplier FIS-based switching avail, ADO must >= 2 */ - g_assert((reg & AHCI_PX_FBS_ADO) >> ctzl(AHCI_PX_FBS_ADO) >= 2); - } - - /* [17 -- 27] RESERVED */ - for (i = AHCI_PX_RES2; i < AHCI_PX_VS; ++i) { - reg = ahci_px_rreg(ahci, port, i); - g_assert_cmphex(reg, ==, 0); - } - - /* [28 -- 31] Vendor-Specific */ - for (i = AHCI_PX_VS; i < 32; ++i) { - reg = ahci_px_rreg(ahci, port, i); - if (reg) { - g_test_message("INFO: Vendor register %u non-empty", i); - } - } -} - -/** - * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first - * device we see, then read and check the response. - */ -static void ahci_test_identify(AHCIQState *ahci) -{ - uint16_t buff[256]; - unsigned px; - int rc; - uint16_t sect_size; - const size_t buffsize = 512; - - g_assert(ahci != NULL); - - /** - * This serves as a bit of a tutorial on AHCI device programming: - * - * (1) Create a data buffer for the IDENTIFY response to be sent to - * (2) Create a Command Table buffer, where we will store the - * command and PRDT (Physical Region Descriptor Table) - * (3) Construct an FIS host-to-device command structure, and write it to - * the top of the Command Table buffer. - * (4) Create one or more Physical Region Descriptors (PRDs) that describe - * a location in memory where data may be stored/retrieved. - * (5) Write these PRDTs to the bottom (offset 0x80) of the Command Table. - * (6) Each AHCI port has up to 32 command slots. Each slot contains a - * header that points to a Command Table buffer. Pick an unused slot - * and update it to point to the Command Table we have built. - * (7) Now: Command #n points to our Command Table, and our Command Table - * contains the FIS (that describes our command) and the PRDTL, which - * describes our buffer. - * (8) We inform the HBA via PxCI (Command Issue) that the command in slot - * #n is ready for processing. - */ - - /* Pick the first implemented and running port */ - px = ahci_port_select(ahci); - g_test_message("Selected port %u for test", px); - - /* Clear out the FIS Receive area and any pending interrupts. */ - ahci_port_clear(ahci, px); - - /* "Read" 512 bytes using CMD_IDENTIFY into the host buffer. */ - ahci_io(ahci, px, CMD_IDENTIFY, &buff, buffsize, 0); - - /* Check serial number/version in the buffer */ - /* NB: IDENTIFY strings are packed in 16bit little endian chunks. - * Since we copy byte-for-byte in ahci-test, on both LE and BE, we need to - * unchunk this data. By contrast, ide-test copies 2 bytes at a time, and - * as a consequence, only needs to unchunk the data on LE machines. */ - string_bswap16(&buff[10], 20); - rc = memcmp(&buff[10], "testdisk ", 20); - g_assert_cmphex(rc, ==, 0); - - string_bswap16(&buff[23], 8); - rc = memcmp(&buff[23], "version ", 8); - g_assert_cmphex(rc, ==, 0); - - sect_size = le16_to_cpu(*((uint16_t *)(&buff[5]))); - g_assert_cmphex(sect_size, ==, AHCI_SECTOR_SIZE); -} - -static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, - uint64_t sector, uint8_t read_cmd, - uint8_t write_cmd) -{ - uint64_t ptr; - uint8_t port; - unsigned char *tx = g_malloc(bufsize); - unsigned char *rx = g_malloc0(bufsize); - - g_assert(ahci != NULL); - - /* Pick the first running port and clear it. */ - port = ahci_port_select(ahci); - ahci_port_clear(ahci, port); - - /*** Create pattern and transfer to guest ***/ - /* Data buffer in the guest */ - ptr = ahci_alloc(ahci, bufsize); - g_assert(ptr); - - /* Write some indicative pattern to our buffer. */ - generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); - qtest_bufwrite(ahci->parent->qts, ptr, tx, bufsize); - - /* Write this buffer to disk, then read it back to the DMA buffer. */ - ahci_guest_io(ahci, port, write_cmd, ptr, bufsize, sector); - qtest_memset(ahci->parent->qts, ptr, 0x00, bufsize); - ahci_guest_io(ahci, port, read_cmd, ptr, bufsize, sector); - - /*** Read back the Data ***/ - qtest_bufread(ahci->parent->qts, ptr, rx, bufsize); - g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); - - ahci_free(ahci, ptr); - g_free(tx); - g_free(rx); -} - -static uint8_t ahci_test_nondata(AHCIQState *ahci, uint8_t ide_cmd) -{ - uint8_t port; - - /* Sanitize */ - port = ahci_port_select(ahci); - ahci_port_clear(ahci, port); - - ahci_io(ahci, port, ide_cmd, NULL, 0, 0); - - return port; -} - -static void ahci_test_flush(AHCIQState *ahci) -{ - ahci_test_nondata(ahci, CMD_FLUSH_CACHE); -} - -static void ahci_test_max(AHCIQState *ahci) -{ - RegD2HFIS *d2h = g_malloc0(0x20); - uint64_t nsect; - uint8_t port; - uint8_t cmd; - uint64_t config_sect = mb_to_sectors(test_image_size_mb) - 1; - - if (config_sect > 0xFFFFFF) { - cmd = CMD_READ_MAX_EXT; - } else { - cmd = CMD_READ_MAX; - } - - port = ahci_test_nondata(ahci, cmd); - qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x40, d2h, 0x20); - nsect = (uint64_t)d2h->lba_hi[2] << 40 | - (uint64_t)d2h->lba_hi[1] << 32 | - (uint64_t)d2h->lba_hi[0] << 24 | - (uint64_t)d2h->lba_lo[2] << 16 | - (uint64_t)d2h->lba_lo[1] << 8 | - (uint64_t)d2h->lba_lo[0]; - - g_assert_cmphex(nsect, ==, config_sect); - g_free(d2h); -} - - -/******************************************************************************/ -/* Test Interfaces */ -/******************************************************************************/ - -/** - * Basic sanity test to boot a machine, find an AHCI device, and shutdown. - */ -static void test_sanity(void) -{ - AHCIQState *ahci; - ahci = ahci_boot(NULL); - ahci_shutdown(ahci); -} - -/** - * Ensure that the PCI configuration space for the AHCI device is in-line with - * the AHCI 1.3 specification for initial values. - */ -static void test_pci_spec(void) -{ - AHCIQState *ahci; - ahci = ahci_boot(NULL); - ahci_test_pci_spec(ahci); - ahci_shutdown(ahci); -} - -/** - * Engage the PCI AHCI device and sanity check the response. - * Perform additional PCI config space bringup for the HBA. - */ -static void test_pci_enable(void) -{ - AHCIQState *ahci; - ahci = ahci_boot(NULL); - ahci_pci_enable(ahci); - ahci_shutdown(ahci); -} - -/** - * Investigate the memory mapped regions of the HBA, - * and test them for AHCI specification adherence. - */ -static void test_hba_spec(void) -{ - AHCIQState *ahci; - - ahci = ahci_boot(NULL); - ahci_pci_enable(ahci); - ahci_test_hba_spec(ahci); - ahci_shutdown(ahci); -} - -/** - * Engage the HBA functionality of the AHCI PCI device, - * and bring it into a functional idle state. - */ -static void test_hba_enable(void) -{ - AHCIQState *ahci; - - ahci = ahci_boot(NULL); - ahci_pci_enable(ahci); - ahci_hba_enable(ahci); - ahci_shutdown(ahci); -} - -/** - * Bring up the device and issue an IDENTIFY command. - * Inspect the state of the HBA device and the data returned. - */ -static void test_identify(void) -{ - AHCIQState *ahci; - - ahci = ahci_boot_and_enable(NULL); - ahci_test_identify(ahci); - ahci_shutdown(ahci); -} - -/** - * Fragmented DMA test: Perform a standard 4K DMA read/write - * test, but make sure the physical regions are fragmented to - * be very small, each just 32 bytes, to see how AHCI performs - * with chunks defined to be much less than a sector. - */ -static void test_dma_fragmented(void) -{ - AHCIQState *ahci; - AHCICommand *cmd; - uint8_t px; - size_t bufsize = 4096; - unsigned char *tx = g_malloc(bufsize); - unsigned char *rx = g_malloc0(bufsize); - uint64_t ptr; - - ahci = ahci_boot_and_enable(NULL); - px = ahci_port_select(ahci); - ahci_port_clear(ahci, px); - - /* create pattern */ - generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); - - /* Create a DMA buffer in guest memory, and write our pattern to it. */ - ptr = guest_alloc(&ahci->parent->alloc, bufsize); - g_assert(ptr); - qtest_bufwrite(ahci->parent->qts, ptr, tx, bufsize); - - cmd = ahci_command_create(CMD_WRITE_DMA); - ahci_command_adjust(cmd, 0, ptr, bufsize, 32); - ahci_command_commit(ahci, cmd, px); - ahci_command_issue(ahci, cmd); - ahci_command_verify(ahci, cmd); - ahci_command_free(cmd); - - cmd = ahci_command_create(CMD_READ_DMA); - ahci_command_adjust(cmd, 0, ptr, bufsize, 32); - ahci_command_commit(ahci, cmd, px); - ahci_command_issue(ahci, cmd); - ahci_command_verify(ahci, cmd); - ahci_command_free(cmd); - - /* Read back the guest's receive buffer into local memory */ - qtest_bufread(ahci->parent->qts, ptr, rx, bufsize); - guest_free(&ahci->parent->alloc, ptr); - - g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); - - ahci_shutdown(ahci); - - g_free(rx); - g_free(tx); -} - -/* - * Write sector 1 with random data to make AHCI storage dirty - * Needed for flush tests so that flushes actually go though the block layer - */ -static void make_dirty(AHCIQState* ahci, uint8_t port) -{ - uint64_t ptr; - unsigned bufsize = 512; - - ptr = ahci_alloc(ahci, bufsize); - g_assert(ptr); - - ahci_guest_io(ahci, port, CMD_WRITE_DMA, ptr, bufsize, 1); - ahci_free(ahci, ptr); -} - -static void test_flush(void) -{ - AHCIQState *ahci; - uint8_t port; - - ahci = ahci_boot_and_enable(NULL); - - port = ahci_port_select(ahci); - ahci_port_clear(ahci, port); - - make_dirty(ahci, port); - - ahci_test_flush(ahci); - ahci_shutdown(ahci); -} - -static void test_flush_retry(void) -{ - AHCIQState *ahci; - AHCICommand *cmd; - uint8_t port; - - prepare_blkdebug_script(debug_path, "flush_to_disk"); - ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "format=%s,cache=writeback," - "rerror=stop,werror=stop " - "-M q35 " - "-device ide-hd,drive=drive0 ", - debug_path, - tmp_path, imgfmt); - - port = ahci_port_select(ahci); - ahci_port_clear(ahci, port); - - /* Issue write so that flush actually goes to disk */ - make_dirty(ahci, port); - - /* Issue Flush Command and wait for error */ - cmd = ahci_guest_io_halt(ahci, port, CMD_FLUSH_CACHE, 0, 0, 0); - ahci_guest_io_resume(ahci, cmd); - - ahci_shutdown(ahci); -} - -/** - * Basic sanity test to boot a machine, find an AHCI device, and shutdown. - */ -static void test_migrate_sanity(void) -{ - AHCIQState *src, *dst; - char *uri = g_strdup_printf("unix:%s", mig_socket); - - src = ahci_boot("-m 384 -M q35 " - "-drive if=ide,file=%s,format=%s ", tmp_path, imgfmt); - dst = ahci_boot("-m 384 -M q35 " - "-drive if=ide,file=%s,format=%s " - "-incoming %s", tmp_path, imgfmt, uri); - - ahci_migrate(src, dst, uri); - - ahci_shutdown(src); - ahci_shutdown(dst); - g_free(uri); -} - -/** - * Simple migration test: Write a pattern, migrate, then read. - */ -static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write) -{ - AHCIQState *src, *dst; - uint8_t px; - size_t bufsize = 4096; - unsigned char *tx = g_malloc(bufsize); - unsigned char *rx = g_malloc0(bufsize); - char *uri = g_strdup_printf("unix:%s", mig_socket); - - src = ahci_boot_and_enable("-m 384 -M q35 " - "-drive if=ide,format=%s,file=%s ", - imgfmt, tmp_path); - dst = ahci_boot("-m 384 -M q35 " - "-drive if=ide,format=%s,file=%s " - "-incoming %s", imgfmt, tmp_path, uri); - - /* initialize */ - px = ahci_port_select(src); - ahci_port_clear(src, px); - - /* create pattern */ - generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); - - /* Write, migrate, then read. */ - ahci_io(src, px, cmd_write, tx, bufsize, 0); - ahci_migrate(src, dst, uri); - ahci_io(dst, px, cmd_read, rx, bufsize, 0); - - /* Verify pattern */ - g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); - - ahci_shutdown(src); - ahci_shutdown(dst); - g_free(rx); - g_free(tx); - g_free(uri); -} - -static void test_migrate_dma(void) -{ - ahci_migrate_simple(CMD_READ_DMA, CMD_WRITE_DMA); -} - -static void test_migrate_ncq(void) -{ - ahci_migrate_simple(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED); -} - -/** - * Halted IO Error Test - * - * Simulate an error on first write, Try to write a pattern, - * Confirm the VM has stopped, resume the VM, verify command - * has completed, then read back the data and verify. - */ -static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write) -{ - AHCIQState *ahci; - uint8_t port; - size_t bufsize = 4096; - unsigned char *tx = g_malloc(bufsize); - unsigned char *rx = g_malloc0(bufsize); - uint64_t ptr; - AHCICommand *cmd; - - prepare_blkdebug_script(debug_path, "write_aio"); - - ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "format=%s,cache=writeback," - "rerror=stop,werror=stop " - "-M q35 " - "-device ide-hd,drive=drive0 ", - debug_path, - tmp_path, imgfmt); - - /* Initialize and prepare */ - port = ahci_port_select(ahci); - ahci_port_clear(ahci, port); - - /* create DMA source buffer and write pattern */ - generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); - ptr = ahci_alloc(ahci, bufsize); - g_assert(ptr); - qtest_memwrite(ahci->parent->qts, ptr, tx, bufsize); - - /* Attempt to write (and fail) */ - cmd = ahci_guest_io_halt(ahci, port, cmd_write, - ptr, bufsize, 0); - - /* Attempt to resume the command */ - ahci_guest_io_resume(ahci, cmd); - ahci_free(ahci, ptr); - - /* Read back and verify */ - ahci_io(ahci, port, cmd_read, rx, bufsize, 0); - g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); - - /* Cleanup and go home */ - ahci_shutdown(ahci); - g_free(rx); - g_free(tx); -} - -static void test_halted_dma(void) -{ - ahci_halted_io_test(CMD_READ_DMA, CMD_WRITE_DMA); -} - -static void test_halted_ncq(void) -{ - ahci_halted_io_test(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED); -} - -/** - * IO Error Migration Test - * - * Simulate an error on first write, Try to write a pattern, - * Confirm the VM has stopped, migrate, resume the VM, - * verify command has completed, then read back the data and verify. - */ -static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write) -{ - AHCIQState *src, *dst; - uint8_t port; - size_t bufsize = 4096; - unsigned char *tx = g_malloc(bufsize); - unsigned char *rx = g_malloc0(bufsize); - uint64_t ptr; - AHCICommand *cmd; - char *uri = g_strdup_printf("unix:%s", mig_socket); - - prepare_blkdebug_script(debug_path, "write_aio"); - - src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "format=%s,cache=writeback," - "rerror=stop,werror=stop " - "-M q35 " - "-device ide-hd,drive=drive0 ", - debug_path, - tmp_path, imgfmt); - - dst = ahci_boot("-drive file=%s,if=none,id=drive0," - "format=%s,cache=writeback," - "rerror=stop,werror=stop " - "-M q35 " - "-device ide-hd,drive=drive0 " - "-incoming %s", - tmp_path, imgfmt, uri); - - /* Initialize and prepare */ - port = ahci_port_select(src); - ahci_port_clear(src, port); - generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); - - /* create DMA source buffer and write pattern */ - ptr = ahci_alloc(src, bufsize); - g_assert(ptr); - qtest_memwrite(src->parent->qts, ptr, tx, bufsize); - - /* Write, trigger the VM to stop, migrate, then resume. */ - cmd = ahci_guest_io_halt(src, port, cmd_write, - ptr, bufsize, 0); - ahci_migrate(src, dst, uri); - ahci_guest_io_resume(dst, cmd); - ahci_free(dst, ptr); - - /* Read back */ - ahci_io(dst, port, cmd_read, rx, bufsize, 0); - - /* Verify TX and RX are identical */ - g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); - - /* Cleanup and go home. */ - ahci_shutdown(src); - ahci_shutdown(dst); - g_free(rx); - g_free(tx); - g_free(uri); -} - -static void test_migrate_halted_dma(void) -{ - ahci_migrate_halted_io(CMD_READ_DMA, CMD_WRITE_DMA); -} - -static void test_migrate_halted_ncq(void) -{ - ahci_migrate_halted_io(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED); -} - -/** - * Migration test: Try to flush, migrate, then resume. - */ -static void test_flush_migrate(void) -{ - AHCIQState *src, *dst; - AHCICommand *cmd; - uint8_t px; - char *uri = g_strdup_printf("unix:%s", mig_socket); - - prepare_blkdebug_script(debug_path, "flush_to_disk"); - - src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "cache=writeback,rerror=stop,werror=stop," - "format=%s " - "-M q35 " - "-device ide-hd,drive=drive0 ", - debug_path, tmp_path, imgfmt); - dst = ahci_boot("-drive file=%s,if=none,id=drive0," - "cache=writeback,rerror=stop,werror=stop," - "format=%s " - "-M q35 " - "-device ide-hd,drive=drive0 " - "-incoming %s", tmp_path, imgfmt, uri); - - px = ahci_port_select(src); - ahci_port_clear(src, px); - - /* Dirty device so that flush reaches disk */ - make_dirty(src, px); - - /* Issue Flush Command */ - cmd = ahci_command_create(CMD_FLUSH_CACHE); - ahci_command_commit(src, cmd, px); - ahci_command_issue_async(src, cmd); - qtest_qmp_eventwait(src->parent->qts, "STOP"); - - /* Migrate over */ - ahci_migrate(src, dst, uri); - - /* Complete the command */ - qtest_qmp_send(dst->parent->qts, "{'execute':'cont' }"); - qtest_qmp_eventwait(dst->parent->qts, "RESUME"); - ahci_command_wait(dst, cmd); - ahci_command_verify(dst, cmd); - - ahci_command_free(cmd); - ahci_shutdown(src); - ahci_shutdown(dst); - g_free(uri); -} - -static void test_max(void) -{ - AHCIQState *ahci; - - ahci = ahci_boot_and_enable(NULL); - ahci_test_max(ahci); - ahci_shutdown(ahci); -} - -static void test_reset(void) -{ - AHCIQState *ahci; - int i; - - ahci = ahci_boot(NULL); - ahci_test_pci_spec(ahci); - ahci_pci_enable(ahci); - - for (i = 0; i < 2; i++) { - ahci_test_hba_spec(ahci); - ahci_hba_enable(ahci); - ahci_test_identify(ahci); - ahci_test_io_rw_simple(ahci, 4096, 0, - CMD_READ_DMA_EXT, - CMD_WRITE_DMA_EXT); - ahci_set(ahci, AHCI_GHC, AHCI_GHC_HR); - ahci_clean_mem(ahci); - } - - ahci_shutdown(ahci); -} - -static void test_ncq_simple(void) -{ - AHCIQState *ahci; - - ahci = ahci_boot_and_enable(NULL); - ahci_test_io_rw_simple(ahci, 4096, 0, - READ_FPDMA_QUEUED, - WRITE_FPDMA_QUEUED); - ahci_shutdown(ahci); -} - -static int prepare_iso(size_t size, unsigned char **buf, char **name) -{ - char cdrom_path[] = "/tmp/qtest.iso.XXXXXX"; - unsigned char *patt; - ssize_t ret; - int fd = mkstemp(cdrom_path); - - g_assert(buf); - g_assert(name); - patt = g_malloc(size); - - /* Generate a pattern and build a CDROM image to read from */ - generate_pattern(patt, size, ATAPI_SECTOR_SIZE); - ret = write(fd, patt, size); - g_assert(ret == size); - - *name = g_strdup(cdrom_path); - *buf = patt; - return fd; -} - -static void remove_iso(int fd, char *name) -{ - unlink(name); - g_free(name); - close(fd); -} - -static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd, - const AHCIOpts *opts) -{ - unsigned char *tx = opts->opaque; - unsigned char *rx; - - if (!opts->size) { - return 0; - } - - rx = g_malloc0(opts->size); - qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size); - g_assert_cmphex(memcmp(tx, rx, opts->size), ==, 0); - g_free(rx); - - return 0; -} - -static void ahci_test_cdrom(int nsectors, bool dma, uint8_t cmd, - bool override_bcl, uint16_t bcl) -{ - AHCIQState *ahci; - unsigned char *tx; - char *iso; - int fd; - AHCIOpts opts = { - .size = (ATAPI_SECTOR_SIZE * nsectors), - .atapi = true, - .atapi_dma = dma, - .post_cb = ahci_cb_cmp_buff, - .set_bcl = override_bcl, - .bcl = bcl, - }; - uint64_t iso_size = ATAPI_SECTOR_SIZE * (nsectors + 1); - - /* Prepare ISO and fill 'tx' buffer */ - fd = prepare_iso(iso_size, &tx, &iso); - opts.opaque = tx; - - /* Standard startup wonkery, but use ide-cd and our special iso file */ - ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw " - "-M q35 " - "-device ide-cd,drive=drive0 ", iso); - - /* Build & Send AHCI command */ - ahci_exec(ahci, ahci_port_select(ahci), cmd, &opts); - - /* Cleanup */ - g_free(tx); - ahci_shutdown(ahci); - remove_iso(fd, iso); -} - -static void ahci_test_cdrom_read10(int nsectors, bool dma) -{ - ahci_test_cdrom(nsectors, dma, CMD_ATAPI_READ_10, false, 0); -} - -static void test_cdrom_dma(void) -{ - ahci_test_cdrom_read10(1, true); -} - -static void test_cdrom_dma_multi(void) -{ - ahci_test_cdrom_read10(3, true); -} - -static void test_cdrom_pio(void) -{ - ahci_test_cdrom_read10(1, false); -} - -static void test_cdrom_pio_multi(void) -{ - ahci_test_cdrom_read10(3, false); -} - -/* Regression test: Test that a READ_CD command with a BCL of 0 but a size of 0 - * completes as a NOP instead of erroring out. */ -static void test_atapi_bcl(void) -{ - ahci_test_cdrom(0, false, CMD_ATAPI_READ_CD, true, 0); -} - - -static void atapi_wait_tray(AHCIQState *ahci, bool open) -{ - QDict *rsp = qtest_qmp_eventwait_ref(ahci->parent->qts, - "DEVICE_TRAY_MOVED"); - QDict *data = qdict_get_qdict(rsp, "data"); - if (open) { - g_assert(qdict_get_bool(data, "tray-open")); - } else { - g_assert(!qdict_get_bool(data, "tray-open")); - } - qobject_unref(rsp); -} - -static void test_atapi_tray(void) -{ - AHCIQState *ahci; - unsigned char *tx; - char *iso; - int fd; - uint8_t port, sense, asc; - uint64_t iso_size = ATAPI_SECTOR_SIZE; - QDict *rsp; - - fd = prepare_iso(iso_size, &tx, &iso); - ahci = ahci_boot_and_enable("-blockdev node-name=drive0,driver=file,filename=%s " - "-M q35 " - "-device ide-cd,id=cd0,drive=drive0 ", iso); - port = ahci_port_select(ahci); - - ahci_atapi_eject(ahci, port); - atapi_wait_tray(ahci, true); - - ahci_atapi_load(ahci, port); - atapi_wait_tray(ahci, false); - - /* Remove media */ - qtest_qmp_send(ahci->parent->qts, "{'execute': 'blockdev-open-tray', " - "'arguments': {'id': 'cd0'}}"); - atapi_wait_tray(ahci, true); - rsp = qtest_qmp_receive(ahci->parent->qts); - qobject_unref(rsp); - - qmp_discard_response(ahci->parent->qts, - "{'execute': 'blockdev-remove-medium', " - "'arguments': {'id': 'cd0'}}"); - - /* Test the tray without a medium */ - ahci_atapi_load(ahci, port); - atapi_wait_tray(ahci, false); - - ahci_atapi_eject(ahci, port); - atapi_wait_tray(ahci, true); - - /* Re-insert media */ - qmp_discard_response(ahci->parent->qts, - "{'execute': 'blockdev-add', " - "'arguments': {'node-name': 'node0', " - "'driver': 'raw', " - "'file': { 'driver': 'file', " - "'filename': %s }}}", iso); - qmp_discard_response(ahci->parent->qts, - "{'execute': 'blockdev-insert-medium'," - "'arguments': { 'id': 'cd0', " - "'node-name': 'node0' }}"); - - /* Again, the event shows up first */ - qtest_qmp_send(ahci->parent->qts, "{'execute': 'blockdev-close-tray', " - "'arguments': {'id': 'cd0'}}"); - atapi_wait_tray(ahci, false); - rsp = qtest_qmp_receive(ahci->parent->qts); - qobject_unref(rsp); - - /* Now, to convince ATAPI we understand the media has changed... */ - ahci_atapi_test_ready(ahci, port, false, SENSE_NOT_READY); - ahci_atapi_get_sense(ahci, port, &sense, &asc); - g_assert_cmpuint(sense, ==, SENSE_NOT_READY); - g_assert_cmpuint(asc, ==, ASC_MEDIUM_NOT_PRESENT); - - ahci_atapi_test_ready(ahci, port, false, SENSE_UNIT_ATTENTION); - ahci_atapi_get_sense(ahci, port, &sense, &asc); - g_assert_cmpuint(sense, ==, SENSE_UNIT_ATTENTION); - g_assert_cmpuint(asc, ==, ASC_MEDIUM_MAY_HAVE_CHANGED); - - ahci_atapi_test_ready(ahci, port, true, SENSE_NO_SENSE); - ahci_atapi_get_sense(ahci, port, &sense, &asc); - g_assert_cmpuint(sense, ==, SENSE_NO_SENSE); - - /* Final tray test. */ - ahci_atapi_eject(ahci, port); - atapi_wait_tray(ahci, true); - - ahci_atapi_load(ahci, port); - atapi_wait_tray(ahci, false); - - /* Cleanup */ - g_free(tx); - ahci_shutdown(ahci); - remove_iso(fd, iso); -} - -/******************************************************************************/ -/* AHCI I/O Test Matrix Definitions */ - -enum BuffLen { - LEN_BEGIN = 0, - LEN_SIMPLE = LEN_BEGIN, - LEN_DOUBLE, - LEN_LONG, - LEN_SHORT, - NUM_LENGTHS -}; - -static const char *buff_len_str[NUM_LENGTHS] = { "simple", "double", - "long", "short" }; - -enum AddrMode { - ADDR_MODE_BEGIN = 0, - ADDR_MODE_LBA28 = ADDR_MODE_BEGIN, - ADDR_MODE_LBA48, - NUM_ADDR_MODES -}; - -static const char *addr_mode_str[NUM_ADDR_MODES] = { "lba28", "lba48" }; - -enum IOMode { - MODE_BEGIN = 0, - MODE_PIO = MODE_BEGIN, - MODE_DMA, - NUM_MODES -}; - -static const char *io_mode_str[NUM_MODES] = { "pio", "dma" }; - -enum IOOps { - IO_BEGIN = 0, - IO_READ = IO_BEGIN, - IO_WRITE, - NUM_IO_OPS -}; - -enum OffsetType { - OFFSET_BEGIN = 0, - OFFSET_ZERO = OFFSET_BEGIN, - OFFSET_LOW, - OFFSET_HIGH, - NUM_OFFSETS -}; - -static const char *offset_str[NUM_OFFSETS] = { "zero", "low", "high" }; - -typedef struct AHCIIOTestOptions { - enum BuffLen length; - enum AddrMode address_type; - enum IOMode io_type; - enum OffsetType offset; -} AHCIIOTestOptions; - -static uint64_t offset_sector(enum OffsetType ofst, - enum AddrMode addr_type, - uint64_t buffsize) -{ - uint64_t ceil; - uint64_t nsectors; - - switch (ofst) { - case OFFSET_ZERO: - return 0; - case OFFSET_LOW: - return 1; - case OFFSET_HIGH: - ceil = (addr_type == ADDR_MODE_LBA28) ? 0xfffffff : 0xffffffffffff; - ceil = MIN(ceil, mb_to_sectors(test_image_size_mb) - 1); - nsectors = buffsize / AHCI_SECTOR_SIZE; - return ceil - nsectors + 1; - default: - g_assert_not_reached(); - } -} - -/** - * Table of possible I/O ATA commands given a set of enumerations. - */ -static const uint8_t io_cmds[NUM_MODES][NUM_ADDR_MODES][NUM_IO_OPS] = { - [MODE_PIO] = { - [ADDR_MODE_LBA28] = { - [IO_READ] = CMD_READ_PIO, - [IO_WRITE] = CMD_WRITE_PIO }, - [ADDR_MODE_LBA48] = { - [IO_READ] = CMD_READ_PIO_EXT, - [IO_WRITE] = CMD_WRITE_PIO_EXT } - }, - [MODE_DMA] = { - [ADDR_MODE_LBA28] = { - [IO_READ] = CMD_READ_DMA, - [IO_WRITE] = CMD_WRITE_DMA }, - [ADDR_MODE_LBA48] = { - [IO_READ] = CMD_READ_DMA_EXT, - [IO_WRITE] = CMD_WRITE_DMA_EXT } - } -}; - -/** - * Test a Read/Write pattern using various commands, addressing modes, - * transfer modes, and buffer sizes. - */ -static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma, - unsigned bufsize, uint64_t sector) -{ - AHCIQState *ahci; - - ahci = ahci_boot_and_enable(NULL); - ahci_test_io_rw_simple(ahci, bufsize, sector, - io_cmds[dma][lba48][IO_READ], - io_cmds[dma][lba48][IO_WRITE]); - ahci_shutdown(ahci); -} - -/** - * Demultiplex the test data and invoke the actual test routine. - */ -static void test_io_interface(gconstpointer opaque) -{ - AHCIIOTestOptions *opts = (AHCIIOTestOptions *)opaque; - unsigned bufsize; - uint64_t sector; - - switch (opts->length) { - case LEN_SIMPLE: - bufsize = 4096; - break; - case LEN_DOUBLE: - bufsize = 8192; - break; - case LEN_LONG: - bufsize = 4096 * 64; - break; - case LEN_SHORT: - bufsize = 512; - break; - default: - g_assert_not_reached(); - } - - sector = offset_sector(opts->offset, opts->address_type, bufsize); - test_io_rw_interface(opts->address_type, opts->io_type, bufsize, sector); - g_free(opts); - return; -} - -static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, - enum BuffLen len, enum OffsetType offset) -{ - char *name; - AHCIIOTestOptions *opts; - - opts = g_new(AHCIIOTestOptions, 1); - opts->length = len; - opts->address_type = addr; - opts->io_type = type; - opts->offset = offset; - - name = g_strdup_printf("ahci/io/%s/%s/%s/%s", - io_mode_str[type], - addr_mode_str[addr], - buff_len_str[len], - offset_str[offset]); - - if ((addr == ADDR_MODE_LBA48) && (offset == OFFSET_HIGH) && - (mb_to_sectors(test_image_size_mb) <= 0xFFFFFFF)) { - g_test_message("%s: skipped; test image too small", name); - g_free(opts); - g_free(name); - return; - } - - qtest_add_data_func(name, opts, test_io_interface); - g_free(name); -} - -/******************************************************************************/ - -int main(int argc, char **argv) -{ - const char *arch; - int ret; - int fd; - int c; - int i, j, k, m; - - static struct option long_options[] = { - {"pedantic", no_argument, 0, 'p' }, - {0, 0, 0, 0}, - }; - - /* Should be first to utilize g_test functionality, So we can see errors. */ - g_test_init(&argc, &argv, NULL); - - while (1) { - c = getopt_long(argc, argv, "", long_options, NULL); - if (c == -1) { - break; - } - switch (c) { - case -1: - break; - case 'p': - ahci_pedantic = 1; - break; - default: - fprintf(stderr, "Unrecognized ahci_test option.\n"); - g_assert_not_reached(); - } - } - - /* Check architecture */ - arch = qtest_get_arch(); - if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { - g_test_message("Skipping test for non-x86"); - return 0; - } - - /* Create a temporary image */ - fd = mkstemp(tmp_path); - g_assert(fd >= 0); - if (have_qemu_img()) { - imgfmt = "qcow2"; - test_image_size_mb = TEST_IMAGE_SIZE_MB_LARGE; - mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB_LARGE); - } else { - g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " - "skipping LBA48 high-sector tests"); - imgfmt = "raw"; - test_image_size_mb = TEST_IMAGE_SIZE_MB_SMALL; - ret = ftruncate(fd, test_image_size_mb * 1024 * 1024); - g_assert(ret == 0); - } - close(fd); - - /* Create temporary blkdebug instructions */ - fd = mkstemp(debug_path); - g_assert(fd >= 0); - close(fd); - - /* Reserve a hollow file to use as a socket for migration tests */ - fd = mkstemp(mig_socket); - g_assert(fd >= 0); - close(fd); - - /* Run the tests */ - qtest_add_func("/ahci/sanity", test_sanity); - qtest_add_func("/ahci/pci_spec", test_pci_spec); - qtest_add_func("/ahci/pci_enable", test_pci_enable); - qtest_add_func("/ahci/hba_spec", test_hba_spec); - qtest_add_func("/ahci/hba_enable", test_hba_enable); - qtest_add_func("/ahci/identify", test_identify); - - for (i = MODE_BEGIN; i < NUM_MODES; i++) { - for (j = ADDR_MODE_BEGIN; j < NUM_ADDR_MODES; j++) { - for (k = LEN_BEGIN; k < NUM_LENGTHS; k++) { - for (m = OFFSET_BEGIN; m < NUM_OFFSETS; m++) { - create_ahci_io_test(i, j, k, m); - } - } - } - } - - qtest_add_func("/ahci/io/dma/lba28/fragmented", test_dma_fragmented); - - qtest_add_func("/ahci/flush/simple", test_flush); - qtest_add_func("/ahci/flush/retry", test_flush_retry); - qtest_add_func("/ahci/flush/migrate", test_flush_migrate); - - qtest_add_func("/ahci/migrate/sanity", test_migrate_sanity); - qtest_add_func("/ahci/migrate/dma/simple", test_migrate_dma); - qtest_add_func("/ahci/io/dma/lba28/retry", test_halted_dma); - qtest_add_func("/ahci/migrate/dma/halted", test_migrate_halted_dma); - - qtest_add_func("/ahci/max", test_max); - qtest_add_func("/ahci/reset", test_reset); - - qtest_add_func("/ahci/io/ncq/simple", test_ncq_simple); - qtest_add_func("/ahci/migrate/ncq/simple", test_migrate_ncq); - qtest_add_func("/ahci/io/ncq/retry", test_halted_ncq); - qtest_add_func("/ahci/migrate/ncq/halted", test_migrate_halted_ncq); - - qtest_add_func("/ahci/cdrom/dma/single", test_cdrom_dma); - qtest_add_func("/ahci/cdrom/dma/multi", test_cdrom_dma_multi); - qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio); - qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi); - - qtest_add_func("/ahci/cdrom/pio/bcl", test_atapi_bcl); - qtest_add_func("/ahci/cdrom/eject", test_atapi_tray); - - ret = g_test_run(); - - /* Cleanup */ - unlink(tmp_path); - unlink(debug_path); - unlink(mig_socket); - - return ret; -} diff --git a/tests/arm-cpu-features.c b/tests/arm-cpu-features.c deleted file mode 100644 index bef3ed24b6..0000000000 --- a/tests/arm-cpu-features.c +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Arm CPU feature test cases - * - * Copyright (c) 2019 Red Hat Inc. - * Authors: - * Andrew Jones - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/bitops.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" - -/* - * We expect the SVE max-vq to be 16. Also it must be <= 64 - * for our test code, otherwise 'vls' can't just be a uint64_t. - */ -#define SVE_MAX_VQ 16 - -#define MACHINE "-machine virt,gic-version=max -accel tcg " -#define MACHINE_KVM "-machine virt,gic-version=max -accel kvm -accel tcg " -#define QUERY_HEAD "{ 'execute': 'query-cpu-model-expansion', " \ - " 'arguments': { 'type': 'full', " -#define QUERY_TAIL "}}" - -static bool kvm_enabled(QTestState *qts) -{ - QDict *resp, *qdict; - bool enabled; - - resp = qtest_qmp(qts, "{ 'execute': 'query-kvm' }"); - g_assert(qdict_haskey(resp, "return")); - qdict = qdict_get_qdict(resp, "return"); - g_assert(qdict_haskey(qdict, "enabled")); - enabled = qdict_get_bool(qdict, "enabled"); - qobject_unref(resp); - - return enabled; -} - -static QDict *do_query_no_props(QTestState *qts, const char *cpu_type) -{ - return qtest_qmp(qts, QUERY_HEAD "'model': { 'name': %s }" - QUERY_TAIL, cpu_type); -} - -static QDict *do_query(QTestState *qts, const char *cpu_type, - const char *fmt, ...) -{ - QDict *resp; - - if (fmt) { - QDict *args; - va_list ap; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - resp = qtest_qmp(qts, QUERY_HEAD "'model': { 'name': %s, " - "'props': %p }" - QUERY_TAIL, cpu_type, args); - } else { - resp = do_query_no_props(qts, cpu_type); - } - - return resp; -} - -static const char *resp_get_error(QDict *resp) -{ - QDict *qdict; - - g_assert(resp); - - qdict = qdict_get_qdict(resp, "error"); - if (qdict) { - return qdict_get_str(qdict, "desc"); - } - - return NULL; -} - -#define assert_error(qts, cpu_type, expected_error, fmt, ...) \ -({ \ - QDict *_resp; \ - const char *_error; \ - \ - _resp = do_query(qts, cpu_type, fmt, ##__VA_ARGS__); \ - g_assert(_resp); \ - _error = resp_get_error(_resp); \ - g_assert(_error); \ - g_assert(g_str_equal(_error, expected_error)); \ - qobject_unref(_resp); \ -}) - -static bool resp_has_props(QDict *resp) -{ - QDict *qdict; - - g_assert(resp); - - if (!qdict_haskey(resp, "return")) { - return false; - } - qdict = qdict_get_qdict(resp, "return"); - - if (!qdict_haskey(qdict, "model")) { - return false; - } - qdict = qdict_get_qdict(qdict, "model"); - - return qdict_haskey(qdict, "props"); -} - -static QDict *resp_get_props(QDict *resp) -{ - QDict *qdict; - - g_assert(resp); - g_assert(resp_has_props(resp)); - - qdict = qdict_get_qdict(resp, "return"); - qdict = qdict_get_qdict(qdict, "model"); - qdict = qdict_get_qdict(qdict, "props"); - - return qdict; -} - -static bool resp_get_feature(QDict *resp, const char *feature) -{ - QDict *props; - - g_assert(resp); - g_assert(resp_has_props(resp)); - props = resp_get_props(resp); - g_assert(qdict_get(props, feature)); - return qdict_get_bool(props, feature); -} - -#define assert_has_feature(qts, cpu_type, feature) \ -({ \ - QDict *_resp = do_query_no_props(qts, cpu_type); \ - g_assert(_resp); \ - g_assert(resp_has_props(_resp)); \ - g_assert(qdict_get(resp_get_props(_resp), feature)); \ - qobject_unref(_resp); \ -}) - -#define assert_has_not_feature(qts, cpu_type, feature) \ -({ \ - QDict *_resp = do_query_no_props(qts, cpu_type); \ - g_assert(_resp); \ - g_assert(!resp_has_props(_resp) || \ - !qdict_get(resp_get_props(_resp), feature)); \ - qobject_unref(_resp); \ -}) - -static void assert_type_full(QTestState *qts) -{ - const char *error; - QDict *resp; - - resp = qtest_qmp(qts, "{ 'execute': 'query-cpu-model-expansion', " - "'arguments': { 'type': 'static', " - "'model': { 'name': 'foo' }}}"); - g_assert(resp); - error = resp_get_error(resp); - g_assert(error); - g_assert(g_str_equal(error, - "The requested expansion type is not supported")); - qobject_unref(resp); -} - -static void assert_bad_props(QTestState *qts, const char *cpu_type) -{ - const char *error; - QDict *resp; - - resp = qtest_qmp(qts, "{ 'execute': 'query-cpu-model-expansion', " - "'arguments': { 'type': 'full', " - "'model': { 'name': %s, " - "'props': false }}}", - cpu_type); - g_assert(resp); - error = resp_get_error(resp); - g_assert(error); - g_assert(g_str_equal(error, - "Invalid parameter type for 'props', expected: dict")); - qobject_unref(resp); -} - -static uint64_t resp_get_sve_vls(QDict *resp) -{ - QDict *props; - const QDictEntry *e; - uint64_t vls = 0; - int n = 0; - - g_assert(resp); - g_assert(resp_has_props(resp)); - - props = resp_get_props(resp); - - for (e = qdict_first(props); e; e = qdict_next(props, e)) { - if (strlen(e->key) > 3 && !strncmp(e->key, "sve", 3) && - g_ascii_isdigit(e->key[3])) { - char *endptr; - int bits; - - bits = g_ascii_strtoll(&e->key[3], &endptr, 10); - if (!bits || *endptr != '\0') { - continue; - } - - if (qdict_get_bool(props, e->key)) { - vls |= BIT_ULL((bits / 128) - 1); - } - ++n; - } - } - - g_assert(n == SVE_MAX_VQ); - - return vls; -} - -#define assert_sve_vls(qts, cpu_type, expected_vls, fmt, ...) \ -({ \ - QDict *_resp = do_query(qts, cpu_type, fmt, ##__VA_ARGS__); \ - g_assert(_resp); \ - g_assert(resp_has_props(_resp)); \ - g_assert(resp_get_sve_vls(_resp) == expected_vls); \ - qobject_unref(_resp); \ -}) - -static void sve_tests_default(QTestState *qts, const char *cpu_type) -{ - /* - * With no sve-max-vq or sve properties on the command line - * the default is to have all vector lengths enabled. This also - * tests that 'sve' is 'on' by default. - */ - assert_sve_vls(qts, cpu_type, BIT_ULL(SVE_MAX_VQ) - 1, NULL); - - /* With SVE off, all vector lengths should also be off. */ - assert_sve_vls(qts, cpu_type, 0, "{ 'sve': false }"); - - /* With SVE on, we must have at least one vector length enabled. */ - assert_error(qts, cpu_type, "cannot disable sve128", "{ 'sve128': false }"); - - /* Basic enable/disable tests. */ - assert_sve_vls(qts, cpu_type, 0x7, "{ 'sve384': true }"); - assert_sve_vls(qts, cpu_type, ((BIT_ULL(SVE_MAX_VQ) - 1) & ~BIT_ULL(2)), - "{ 'sve384': false }"); - - /* - * --------------------------------------------------------------------- - * power-of-two(vq) all-power- can can - * of-two(< vq) enable disable - * --------------------------------------------------------------------- - * vq < max_vq no MUST* yes yes - * vq < max_vq yes MUST* yes no - * --------------------------------------------------------------------- - * vq == max_vq n/a MUST* yes** yes** - * --------------------------------------------------------------------- - * vq > max_vq n/a no no yes - * vq > max_vq n/a yes yes yes - * --------------------------------------------------------------------- - * - * [*] "MUST" means this requirement must already be satisfied, - * otherwise 'max_vq' couldn't itself be enabled. - * - * [**] Not testable with the QMP interface, only with the command line. - */ - - /* max_vq := 8 */ - assert_sve_vls(qts, cpu_type, 0x8b, "{ 'sve1024': true }"); - - /* max_vq := 8, vq < max_vq, !power-of-two(vq) */ - assert_sve_vls(qts, cpu_type, 0x8f, - "{ 'sve1024': true, 'sve384': true }"); - assert_sve_vls(qts, cpu_type, 0x8b, - "{ 'sve1024': true, 'sve384': false }"); - - /* max_vq := 8, vq < max_vq, power-of-two(vq) */ - assert_sve_vls(qts, cpu_type, 0x8b, - "{ 'sve1024': true, 'sve256': true }"); - assert_error(qts, cpu_type, "cannot disable sve256", - "{ 'sve1024': true, 'sve256': false }"); - - /* max_vq := 3, vq > max_vq, !all-power-of-two(< vq) */ - assert_error(qts, cpu_type, "cannot disable sve512", - "{ 'sve384': true, 'sve512': false, 'sve640': true }"); - - /* - * We can disable power-of-two vector lengths when all larger lengths - * are also disabled. We only need to disable the power-of-two length, - * as all non-enabled larger lengths will then be auto-disabled. - */ - assert_sve_vls(qts, cpu_type, 0x7, "{ 'sve512': false }"); - - /* max_vq := 3, vq > max_vq, all-power-of-two(< vq) */ - assert_sve_vls(qts, cpu_type, 0x1f, - "{ 'sve384': true, 'sve512': true, 'sve640': true }"); - assert_sve_vls(qts, cpu_type, 0xf, - "{ 'sve384': true, 'sve512': true, 'sve640': false }"); -} - -static void sve_tests_sve_max_vq_8(const void *data) -{ - QTestState *qts; - - qts = qtest_init(MACHINE "-cpu max,sve-max-vq=8"); - - assert_sve_vls(qts, "max", BIT_ULL(8) - 1, NULL); - - /* - * Disabling the max-vq set by sve-max-vq is not allowed, but - * of course enabling it is OK. - */ - assert_error(qts, "max", "cannot disable sve1024", "{ 'sve1024': false }"); - assert_sve_vls(qts, "max", 0xff, "{ 'sve1024': true }"); - - /* - * Enabling anything larger than max-vq set by sve-max-vq is not - * allowed, but of course disabling everything larger is OK. - */ - assert_error(qts, "max", "cannot enable sve1152", "{ 'sve1152': true }"); - assert_sve_vls(qts, "max", 0xff, "{ 'sve1152': false }"); - - /* - * We can enable/disable non power-of-two lengths smaller than the - * max-vq set by sve-max-vq, but, while we can enable power-of-two - * lengths, we can't disable them. - */ - assert_sve_vls(qts, "max", 0xff, "{ 'sve384': true }"); - assert_sve_vls(qts, "max", 0xfb, "{ 'sve384': false }"); - assert_sve_vls(qts, "max", 0xff, "{ 'sve256': true }"); - assert_error(qts, "max", "cannot disable sve256", "{ 'sve256': false }"); - - qtest_quit(qts); -} - -static void sve_tests_sve_off(const void *data) -{ - QTestState *qts; - - qts = qtest_init(MACHINE "-cpu max,sve=off"); - - /* SVE is off, so the map should be empty. */ - assert_sve_vls(qts, "max", 0, NULL); - - /* The map stays empty even if we turn lengths off. */ - assert_sve_vls(qts, "max", 0, "{ 'sve128': false }"); - - /* It's an error to enable lengths when SVE is off. */ - assert_error(qts, "max", "cannot enable sve128", "{ 'sve128': true }"); - - /* With SVE re-enabled we should get all vector lengths enabled. */ - assert_sve_vls(qts, "max", BIT_ULL(SVE_MAX_VQ) - 1, "{ 'sve': true }"); - - /* Or enable SVE with just specific vector lengths. */ - assert_sve_vls(qts, "max", 0x3, - "{ 'sve': true, 'sve128': true, 'sve256': true }"); - - qtest_quit(qts); -} - -static void sve_tests_sve_off_kvm(const void *data) -{ - QTestState *qts; - - qts = qtest_init(MACHINE_KVM "-cpu max,sve=off"); - - /* - * We don't know if this host supports SVE so we don't - * attempt to test enabling anything. We only test that - * everything is disabled (as it should be with sve=off) - * and that using sve=off to explicitly disable vector - * lengths is OK too. - */ - assert_sve_vls(qts, "max", 0, NULL); - assert_sve_vls(qts, "max", 0, "{ 'sve128': false }"); - - qtest_quit(qts); -} - -static void test_query_cpu_model_expansion(const void *data) -{ - QTestState *qts; - - qts = qtest_init(MACHINE "-cpu max"); - - /* Test common query-cpu-model-expansion input validation */ - assert_type_full(qts); - assert_bad_props(qts, "max"); - assert_error(qts, "foo", "The CPU type 'foo' is not a recognized " - "ARM CPU type", NULL); - assert_error(qts, "max", "Parameter 'not-a-prop' is unexpected", - "{ 'not-a-prop': false }"); - assert_error(qts, "host", "The CPU type 'host' requires KVM", NULL); - - /* Test expected feature presence/absence for some cpu types */ - assert_has_feature(qts, "max", "pmu"); - assert_has_feature(qts, "cortex-a15", "pmu"); - assert_has_not_feature(qts, "cortex-a15", "aarch64"); - - if (g_str_equal(qtest_get_arch(), "aarch64")) { - assert_has_feature(qts, "max", "aarch64"); - assert_has_feature(qts, "max", "sve"); - assert_has_feature(qts, "max", "sve128"); - assert_has_feature(qts, "cortex-a57", "pmu"); - assert_has_feature(qts, "cortex-a57", "aarch64"); - - sve_tests_default(qts, "max"); - - /* Test that features that depend on KVM generate errors without. */ - assert_error(qts, "max", - "'aarch64' feature cannot be disabled " - "unless KVM is enabled and 32-bit EL1 " - "is supported", - "{ 'aarch64': false }"); - } - - qtest_quit(qts); -} - -static void test_query_cpu_model_expansion_kvm(const void *data) -{ - QTestState *qts; - - qts = qtest_init(MACHINE_KVM "-cpu max"); - - /* - * These tests target the 'host' CPU type, so KVM must be enabled. - */ - if (!kvm_enabled(qts)) { - qtest_quit(qts); - return; - } - - if (g_str_equal(qtest_get_arch(), "aarch64")) { - bool kvm_supports_sve; - char max_name[8], name[8]; - uint32_t max_vq, vq; - uint64_t vls; - QDict *resp; - char *error; - - assert_has_feature(qts, "host", "aarch64"); - assert_has_feature(qts, "host", "pmu"); - - assert_error(qts, "cortex-a15", - "We cannot guarantee the CPU type 'cortex-a15' works " - "with KVM on this host", NULL); - - assert_has_feature(qts, "host", "sve"); - resp = do_query_no_props(qts, "host"); - kvm_supports_sve = resp_get_feature(resp, "sve"); - vls = resp_get_sve_vls(resp); - qobject_unref(resp); - - if (kvm_supports_sve) { - g_assert(vls != 0); - max_vq = 64 - __builtin_clzll(vls); - sprintf(max_name, "sve%d", max_vq * 128); - - /* Enabling a supported length is of course fine. */ - assert_sve_vls(qts, "host", vls, "{ %s: true }", max_name); - - /* Get the next supported length smaller than max-vq. */ - vq = 64 - __builtin_clzll(vls & ~BIT_ULL(max_vq - 1)); - if (vq) { - /* - * We have at least one length smaller than max-vq, - * so we can disable max-vq. - */ - assert_sve_vls(qts, "host", (vls & ~BIT_ULL(max_vq - 1)), - "{ %s: false }", max_name); - - /* - * Smaller, supported vector lengths cannot be disabled - * unless all larger, supported vector lengths are also - * disabled. - */ - sprintf(name, "sve%d", vq * 128); - error = g_strdup_printf("cannot disable %s", name); - assert_error(qts, "host", error, - "{ %s: true, %s: false }", - max_name, name); - g_free(error); - } - - /* - * The smallest, supported vector length is required, because - * we need at least one vector length enabled. - */ - vq = __builtin_ffsll(vls); - sprintf(name, "sve%d", vq * 128); - error = g_strdup_printf("cannot disable %s", name); - assert_error(qts, "host", error, "{ %s: false }", name); - g_free(error); - - /* Get an unsupported length. */ - for (vq = 1; vq <= max_vq; ++vq) { - if (!(vls & BIT_ULL(vq - 1))) { - break; - } - } - if (vq <= SVE_MAX_VQ) { - sprintf(name, "sve%d", vq * 128); - error = g_strdup_printf("cannot enable %s", name); - assert_error(qts, "host", error, "{ %s: true }", name); - g_free(error); - } - } else { - g_assert(vls == 0); - } - } else { - assert_has_not_feature(qts, "host", "aarch64"); - assert_has_not_feature(qts, "host", "pmu"); - assert_has_not_feature(qts, "host", "sve"); - } - - qtest_quit(qts); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qtest_add_data_func("/arm/query-cpu-model-expansion", - NULL, test_query_cpu_model_expansion); - - /* - * For now we only run KVM specific tests with AArch64 QEMU in - * order avoid attempting to run an AArch32 QEMU with KVM on - * AArch64 hosts. That won't work and isn't easy to detect. - */ - if (g_str_equal(qtest_get_arch(), "aarch64")) { - qtest_add_data_func("/arm/kvm/query-cpu-model-expansion", - NULL, test_query_cpu_model_expansion_kvm); - } - - if (g_str_equal(qtest_get_arch(), "aarch64")) { - qtest_add_data_func("/arm/max/query-cpu-model-expansion/sve-max-vq-8", - NULL, sve_tests_sve_max_vq_8); - qtest_add_data_func("/arm/max/query-cpu-model-expansion/sve-off", - NULL, sve_tests_sve_off); - qtest_add_data_func("/arm/kvm/query-cpu-model-expansion/sve-off", - NULL, sve_tests_sve_off_kvm); - } - - return g_test_run(); -} diff --git a/tests/bios-tables-test-allowed-diff.h b/tests/bios-tables-test-allowed-diff.h deleted file mode 100644 index dfb8523c8b..0000000000 --- a/tests/bios-tables-test-allowed-diff.h +++ /dev/null @@ -1 +0,0 @@ -/* List of comma-separated changed AML files to ignore */ diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c deleted file mode 100644 index f1ac2d7e96..0000000000 --- a/tests/bios-tables-test.c +++ /dev/null @@ -1,1046 +0,0 @@ -/* - * Boot order test cases. - * - * Copyright (c) 2013 Red Hat Inc. - * - * Authors: - * Michael S. Tsirkin , - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -/* - * How to add or update the tests: - * Contributor: - * 1. add empty files for new tables, if any, under tests/data/acpi - * 2. list any changed files in tests/bios-tables-test-allowed-diff.h - * 3. commit the above *before* making changes that affect the tables - * Maintainer: - * After 1-3 above tests will pass but ignore differences with the expected files. - * You will also notice that tests/bios-tables-test-allowed-diff.h lists - * a bunch of files. This is your hint that you need to do the below: - * 4. Run - * make check V=1 - * this will produce a bunch of warnings about differences - * beween actual and expected ACPI tables. If you have IASL installed, - * they will also be disassembled so you can look at the disassembled - * output. If not - disassemble them yourself in any way you like. - * Look at the differences - make sure they make sense and match what the - * changes you are merging are supposed to do. - * - * 5. From build directory, run: - * $(SRC_PATH)/tests/data/acpi/rebuild-expected-aml.sh - * 6. Now commit any changes. - * 7. Before doing a pull request, make sure tests/bios-tables-test-allowed-diff.h - * is empty - this will ensure following changes to ACPI tables will - * be noticed. - */ - -#include "qemu/osdep.h" -#include -#include "qemu-common.h" -#include "hw/firmware/smbios.h" -#include "qemu/bitmap.h" -#include "acpi-utils.h" -#include "boot-sector.h" - -#define MACHINE_PC "pc" -#define MACHINE_Q35 "q35" - -#define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML" - -typedef struct { - bool tcg_only; - const char *machine; - const char *variant; - const char *uefi_fl1; - const char *uefi_fl2; - const char *cd; - const uint64_t ram_start; - const uint64_t scan_len; - uint64_t rsdp_addr; - uint8_t rsdp_table[36 /* ACPI 2.0+ RSDP size */]; - GArray *tables; - uint32_t smbios_ep_addr; - struct smbios_21_entry_point smbios_ep_table; - uint8_t *required_struct_types; - int required_struct_types_len; - QTestState *qts; -} test_data; - -static char disk[] = "tests/acpi-test-disk-XXXXXX"; -static const char *data_dir = "tests/data/acpi"; -#ifdef CONFIG_IASL -static const char *iasl = stringify(CONFIG_IASL); -#else -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) -{ - int i; - - for (i = 0; i < data->tables->len; ++i) { - cleanup_table_descriptor(&g_array_index(data->tables, AcpiSdtTable, i)); - } - - g_array_free(data->tables, true); -} - -static void test_acpi_rsdp_table(test_data *data) -{ - uint8_t *rsdp_table = data->rsdp_table; - - acpi_fetch_rsdp_table(data->qts, data->rsdp_addr, rsdp_table); - - switch (rsdp_table[15 /* Revision offset */]) { - case 0: /* ACPI 1.0 RSDP */ - /* With rev 1, checksum is only for the first 20 bytes */ - g_assert(!acpi_calc_checksum(rsdp_table, 20)); - break; - case 2: /* ACPI 2.0+ RSDP */ - /* With revision 2, we have 2 checksums */ - g_assert(!acpi_calc_checksum(rsdp_table, 20)); - g_assert(!acpi_calc_checksum(rsdp_table, 36)); - break; - default: - g_assert_not_reached(); - } -} - -static void test_acpi_rxsdt_table(test_data *data) -{ - const char *sig = "RSDT"; - AcpiSdtTable rsdt = {}; - int entry_size = 4; - int addr_off = 16 /* RsdtAddress */; - uint8_t *ent; - - if (data->rsdp_table[15 /* Revision offset */] != 0) { - addr_off = 24 /* XsdtAddress */; - entry_size = 8; - sig = "XSDT"; - } - /* read [RX]SDT table */ - acpi_fetch_table(data->qts, &rsdt.aml, &rsdt.aml_len, - &data->rsdp_table[addr_off], entry_size, sig, true); - - /* Load all tables and add to test list directly RSDT referenced tables */ - ACPI_FOREACH_RSDT_ENTRY(rsdt.aml, rsdt.aml_len, ent, entry_size) { - AcpiSdtTable ssdt_table = {}; - - acpi_fetch_table(data->qts, &ssdt_table.aml, &ssdt_table.aml_len, ent, - entry_size, 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_fadt_table(test_data *data) -{ - /* 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; - uint32_t val; - int dsdt_offset = 40 /* DSDT */; - int dsdt_entry_size = 4; - - g_assert(compare_signature(&table, "FACP")); - - /* Since DSDT/FACS isn't in RSDT, add them to ASL test list manually */ - memcpy(&val, fadt_aml + 112 /* Flags */, 4); - val = le32_to_cpu(val); - if (!(val & 1UL << 20 /* HW_REDUCED_ACPI */)) { - acpi_fetch_table(data->qts, &table.aml, &table.aml_len, - fadt_aml + 36 /* FIRMWARE_CTRL */, 4, "FACS", false); - g_array_append_val(data->tables, table); - } - - memcpy(&val, fadt_aml + dsdt_offset, 4); - val = le32_to_cpu(val); - if (!val) { - dsdt_offset = 140 /* X_DSDT */; - dsdt_entry_size = 8; - } - acpi_fetch_table(data->qts, &table.aml, &table.aml_len, - fadt_aml + dsdt_offset, dsdt_entry_size, "DSDT", true); - g_array_append_val(data->tables, 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) -{ - AcpiSdtTable *sdt; - GError *error = NULL; - gchar *aml_file = NULL; - gint fd; - ssize_t ret; - int i; - - for (i = 0; i < data->tables->len; ++i) { - const char *ext = data->variant ? data->variant : ""; - sdt = &g_array_index(data->tables, AcpiSdtTable, i); - g_assert(sdt->aml); - - if (rebuild) { - aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, - sdt->aml, ext); - fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); - if (fd < 0) { - perror(aml_file); - } - g_assert(fd >= 0); - } else { - fd = g_file_open_tmp("aml-XXXXXX", &sdt->aml_file, &error); - g_assert_no_error(error); - } - - ret = qemu_write_full(fd, sdt->aml, sdt->aml_len); - g_assert(ret == sdt->aml_len); - - close(fd); - - g_free(aml_file); - } -} - -static bool load_asl(GArray *sdts, AcpiSdtTable *sdt) -{ - AcpiSdtTable *temp; - GError *error = NULL; - GString *command_line = g_string_new(iasl); - gint fd; - gchar *out, *out_err; - gboolean ret; - int i; - - fd = g_file_open_tmp("asl-XXXXXX.dsl", &sdt->asl_file, &error); - g_assert_no_error(error); - close(fd); - - /* build command line */ - g_string_append_printf(command_line, " -p %s ", sdt->asl_file); - if (compare_signature(sdt, "DSDT") || - compare_signature(sdt, "SSDT")) { - for (i = 0; i < sdts->len; ++i) { - temp = &g_array_index(sdts, AcpiSdtTable, i); - if (compare_signature(temp, "DSDT") || - compare_signature(temp, "SSDT")) { - g_string_append_printf(command_line, "-e %s ", temp->aml_file); - } - } - } - g_string_append_printf(command_line, "-d %s", sdt->aml_file); - - /* pass 'out' and 'out_err' in order to be redirected */ - ret = g_spawn_command_line_sync(command_line->str, &out, &out_err, NULL, &error); - g_assert_no_error(error); - if (ret) { - ret = g_file_get_contents(sdt->asl_file, &sdt->asl, - &sdt->asl_len, &error); - g_assert(ret); - g_assert_no_error(error); - ret = (sdt->asl_len > 0); - } - - g_free(out); - g_free(out_err); - g_string_free(command_line, true); - - return !ret; -} - -#define COMMENT_END "*/" -#define DEF_BLOCK "DefinitionBlock (" -#define BLOCK_NAME_END "," - -static GString *normalize_asl(gchar *asl_code) -{ - GString *asl = g_string_new(asl_code); - gchar *comment, *block_name; - - /* strip comments (different generation days) */ - comment = g_strstr_len(asl->str, asl->len, COMMENT_END); - if (comment) { - comment += strlen(COMMENT_END); - while (*comment == '\n') { - comment++; - } - asl = g_string_erase(asl, 0, comment - asl->str); - } - - /* strip def block name (it has file path in it) */ - if (g_str_has_prefix(asl->str, DEF_BLOCK)) { - block_name = g_strstr_len(asl->str, asl->len, BLOCK_NAME_END); - g_assert(block_name); - asl = g_string_erase(asl, 0, - block_name + sizeof(BLOCK_NAME_END) - asl->str); - } - - return asl; -} - -static GArray *load_expected_aml(test_data *data) -{ - int i; - AcpiSdtTable *sdt; - GError *error = NULL; - gboolean ret; - gsize aml_len; - - GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); - if (getenv("V")) { - fputc('\n', stderr); - } - for (i = 0; i < data->tables->len; ++i) { - AcpiSdtTable exp_sdt; - gchar *aml_file = NULL; - const char *ext = data->variant ? data->variant : ""; - - sdt = &g_array_index(data->tables, AcpiSdtTable, i); - - memset(&exp_sdt, 0, sizeof(exp_sdt)); - -try_again: - aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, - sdt->aml, ext); - if (getenv("V")) { - fprintf(stderr, "Looking for expected file '%s'\n", aml_file); - } - if (g_file_test(aml_file, G_FILE_TEST_EXISTS)) { - exp_sdt.aml_file = aml_file; - } else if (*ext != '\0') { - /* try fallback to generic (extension less) expected file */ - ext = ""; - g_free(aml_file); - goto try_again; - } - g_assert(exp_sdt.aml_file); - if (getenv("V")) { - fprintf(stderr, "Using expected file '%s'\n", aml_file); - } - 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); - if (!exp_sdt.aml_len) { - fprintf(stderr, "Warning! zero length expected file '%s'\n", - aml_file); - } - - g_array_append_val(exp_tables, exp_sdt); - } - - return exp_tables; -} - -static bool test_acpi_find_diff_allowed(AcpiSdtTable *sdt) -{ - const gchar *allowed_diff_file[] = { -#include "bios-tables-test-allowed-diff.h" - NULL - }; - const gchar **f; - - for (f = allowed_diff_file; *f; ++f) { - if (!g_strcmp0(sdt->aml_file, *f)) { - return true; - } - } - return false; -} - -/* test the list of tables in @data->tables against reference tables */ -static void test_acpi_asl(test_data *data) -{ - int i; - AcpiSdtTable *sdt, *exp_sdt; - test_data exp_data; - gboolean exp_err, err, all_tables_match = true; - - memset(&exp_data, 0, sizeof(exp_data)); - exp_data.tables = load_expected_aml(data); - dump_aml_files(data, false); - for (i = 0; i < data->tables->len; ++i) { - GString *asl, *exp_asl; - - sdt = &g_array_index(data->tables, AcpiSdtTable, i); - exp_sdt = &g_array_index(exp_data.tables, AcpiSdtTable, i); - - if (sdt->aml_len == exp_sdt->aml_len && - !memcmp(sdt->aml, exp_sdt->aml, sdt->aml_len)) { - /* Identical table binaries: no need to disassemble. */ - continue; - } - - fprintf(stderr, - "acpi-test: Warning! %.4s binary file mismatch. " - "Actual [aml:%s], Expected [aml:%s].\n", - exp_sdt->aml, sdt->aml_file, exp_sdt->aml_file); - - all_tables_match = all_tables_match && - test_acpi_find_diff_allowed(exp_sdt); - - /* - * don't try to decompile if IASL isn't present, in this case user - * will just 'get binary file mismatch' warnings and test failure - */ - if (!iasl) { - continue; - } - - err = load_asl(data->tables, sdt); - asl = normalize_asl(sdt->asl); - - exp_err = load_asl(exp_data.tables, exp_sdt); - exp_asl = normalize_asl(exp_sdt->asl); - - /* TODO: check for warnings */ - g_assert(!err || exp_err); - - if (g_strcmp0(asl->str, exp_asl->str)) { - sdt->tmp_files_retain = true; - if (exp_err) { - fprintf(stderr, - "Warning! iasl couldn't parse the expected aml\n"); - } else { - exp_sdt->tmp_files_retain = true; - fprintf(stderr, - "acpi-test: Warning! %.4s mismatch. " - "Actual [asl:%s, aml:%s], Expected [asl:%s, aml:%s].\n", - 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"); - if (diff_cmd) { - int ret G_GNUC_UNUSED; - char *diff = g_strdup_printf("%s %s %s", diff_cmd, - exp_sdt->asl_file, sdt->asl_file); - ret = system(diff) ; - g_free(diff); - } else { - fprintf(stderr, "acpi-test: Warning. not showing " - "difference since no diff utility is specified. " - "Set 'DIFF' environment variable to a preferred " - "diff utility and run 'make V=1 check' again to " - "see ASL difference."); - } - } - } - } - g_string_free(asl, true); - g_string_free(exp_asl, true); - } - if (!iasl && !all_tables_match) { - fprintf(stderr, "to see ASL diff between mismatched files install IASL," - " rebuild QEMU from scratch and re-run tests with V=1" - " environment variable set"); - } - g_assert(all_tables_match); - - free_test_data(&exp_data); -} - -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; - - qtest_memread(data->qts, addr, ep_table, sizeof(*ep_table)); - if (memcmp(ep_table->anchor_string, "_SM_", 4)) { - return false; - } - if (memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)) { - return false; - } - if (ep_table->structure_table_length == 0) { - return false; - } - if (ep_table->number_of_structures == 0) { - return false; - } - if (acpi_calc_checksum((uint8_t *)ep_table, sizeof *ep_table) || - acpi_calc_checksum((uint8_t *)ep_table + 0x10, - sizeof *ep_table - 0x10)) { - return false; - } - return true; -} - -static void test_smbios_entry_point(test_data *data) -{ - uint32_t off; - - /* find smbios entry point structure */ - for (off = 0xf0000; off < 0x100000; off += 0x10) { - uint8_t sig[] = "_SM_"; - int i; - - for (i = 0; i < sizeof sig - 1; ++i) { - sig[i] = qtest_readb(data->qts, off + i); - } - - if (!memcmp(sig, "_SM_", sizeof sig)) { - /* signature match, but is this a valid entry point? */ - data->smbios_ep_addr = off; - if (smbios_ep_table_ok(data)) { - break; - } - } - } - - g_assert_cmphex(off, <, 0x100000); -} - -static inline bool smbios_single_instance(uint8_t type) -{ - switch (type) { - case 0: - case 1: - case 2: - case 3: - case 16: - case 32: - case 127: - return true; - default: - return false; - } -} - -static void test_smbios_structs(test_data *data) -{ - DECLARE_BITMAP(struct_bitmap, SMBIOS_MAX_TYPE+1) = { 0 }; - struct smbios_21_entry_point *ep_table = &data->smbios_ep_table; - uint32_t addr = le32_to_cpu(ep_table->structure_table_address); - int i, len, max_len = 0; - uint8_t type, prv, crt; - - /* walk the smbios tables */ - for (i = 0; i < le16_to_cpu(ep_table->number_of_structures); i++) { - - /* grab type and formatted area length from struct header */ - type = qtest_readb(data->qts, addr); - g_assert_cmpuint(type, <=, SMBIOS_MAX_TYPE); - len = qtest_readb(data->qts, addr + 1); - - /* single-instance structs must not have been encountered before */ - if (smbios_single_instance(type)) { - g_assert(!test_bit(type, struct_bitmap)); - } - set_bit(type, struct_bitmap); - - /* seek to end of unformatted string area of this struct ("\0\0") */ - prv = crt = 1; - while (prv || crt) { - prv = crt; - crt = qtest_readb(data->qts, addr + len); - len++; - } - - /* keep track of max. struct size */ - if (max_len < len) { - max_len = len; - g_assert_cmpuint(max_len, <=, ep_table->max_structure_size); - } - - /* start of next structure */ - addr += len; - } - - /* total table length and max struct size must match entry point values */ - g_assert_cmpuint(le16_to_cpu(ep_table->structure_table_length), ==, - addr - le32_to_cpu(ep_table->structure_table_address)); - g_assert_cmpuint(le16_to_cpu(ep_table->max_structure_size), ==, max_len); - - /* required struct types must all be present */ - for (i = 0; i < data->required_struct_types_len; i++) { - g_assert(test_bit(data->required_struct_types[i], struct_bitmap)); - } -} - -static void test_acpi_one(const char *params, test_data *data) -{ - char *args; - bool use_uefi = data->uefi_fl1 && data->uefi_fl2; - - if (use_uefi) { - /* - * TODO: convert '-drive if=pflash' to new syntax (see e33763be7cd3) - * when arm/virt boad starts to support it. - */ - args = g_strdup_printf("-machine %s %s -accel tcg -nodefaults -nographic " - "-drive if=pflash,format=raw,file=%s,readonly " - "-drive if=pflash,format=raw,file=%s,snapshot=on -cdrom %s %s", - data->machine, data->tcg_only ? "" : "-accel kvm", - data->uefi_fl1, data->uefi_fl2, data->cd, params ? params : ""); - - } else { - /* Disable kernel irqchip to be able to override apic irq0. */ - args = g_strdup_printf("-machine %s,kernel-irqchip=off %s -accel tcg " - "-net none -display none %s " - "-drive id=hd0,if=none,file=%s,format=raw " - "-device ide-hd,drive=hd0 ", - data->machine, data->tcg_only ? "" : "-accel kvm", - params ? params : "", disk); - } - - data->qts = qtest_init(args); - - if (use_uefi) { - g_assert(data->scan_len); - data->rsdp_addr = acpi_find_rsdp_address_uefi(data->qts, - data->ram_start, data->scan_len); - } else { - boot_sector_test(data->qts); - data->rsdp_addr = acpi_find_rsdp_address(data->qts); - g_assert_cmphex(data->rsdp_addr, <, 0x100000); - } - - data->tables = g_array_new(false, true, sizeof(AcpiSdtTable)); - test_acpi_rsdp_table(data); - test_acpi_rxsdt_table(data); - test_acpi_fadt_table(data); - - if (getenv(ACPI_REBUILD_EXPECTED_AML)) { - dump_aml_files(data, true); - } else { - test_acpi_asl(data); - } - - /* - * TODO: make SMBIOS tests work with UEFI firmware, - * Bug on uefi-test-tools to provide entry point: - * https://bugs.launchpad.net/qemu/+bug/1821884 - */ - if (!use_uefi) { - test_smbios_entry_point(data); - test_smbios_structs(data); - } - - qtest_quit(data->qts); - g_free(args); -} - -static uint8_t base_required_struct_types[] = { - 0, 1, 3, 4, 16, 17, 19, 32, 127 -}; - -static void test_acpi_piix4_tcg(void) -{ - test_data data; - - /* Supplying -machine accel argument overrides the default (qtest). - * This is to make guest actually run. - */ - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_PC; - data.required_struct_types = base_required_struct_types; - data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - test_acpi_one(NULL, &data); - free_test_data(&data); -} - -static void test_acpi_piix4_tcg_bridge(void) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_PC; - data.variant = ".bridge"; - data.required_struct_types = base_required_struct_types; - data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - test_acpi_one("-device pci-bridge,chassis_nr=1", &data); - free_test_data(&data); -} - -static void test_acpi_q35_tcg(void) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_Q35; - data.required_struct_types = base_required_struct_types; - data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - test_acpi_one(NULL, &data); - free_test_data(&data); -} - -static void test_acpi_q35_tcg_bridge(void) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_Q35; - data.variant = ".bridge"; - data.required_struct_types = base_required_struct_types; - data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - test_acpi_one("-device pci-bridge,chassis_nr=1", - &data); - free_test_data(&data); -} - -static void test_acpi_q35_tcg_mmio64(void) -{ - test_data data = { - .machine = MACHINE_Q35, - .variant = ".mmio64", - .required_struct_types = base_required_struct_types, - .required_struct_types_len = ARRAY_SIZE(base_required_struct_types) - }; - - test_acpi_one("-m 128M,slots=1,maxmem=2G " - "-object memory-backend-ram,id=ram0,size=128M " - "-numa node,memdev=ram0 " - "-device pci-testdev,membar=2G", - &data); - free_test_data(&data); -} - -static void test_acpi_piix4_tcg_cphp(void) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_PC; - data.variant = ".cphp"; - test_acpi_one("-smp 2,cores=3,sockets=2,maxcpus=6" - " -object memory-backend-ram,id=ram0,size=64M" - " -object memory-backend-ram,id=ram1,size=64M" - " -numa node,memdev=ram0 -numa node,memdev=ram1" - " -numa dist,src=0,dst=1,val=21", - &data); - free_test_data(&data); -} - -static void test_acpi_q35_tcg_cphp(void) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_Q35; - data.variant = ".cphp"; - test_acpi_one(" -smp 2,cores=3,sockets=2,maxcpus=6" - " -object memory-backend-ram,id=ram0,size=64M" - " -object memory-backend-ram,id=ram1,size=64M" - " -numa node,memdev=ram0 -numa node,memdev=ram1" - " -numa dist,src=0,dst=1,val=21", - &data); - free_test_data(&data); -} - -static uint8_t ipmi_required_struct_types[] = { - 0, 1, 3, 4, 16, 17, 19, 32, 38, 127 -}; - -static void test_acpi_q35_tcg_ipmi(void) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_Q35; - data.variant = ".ipmibt"; - data.required_struct_types = ipmi_required_struct_types; - data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); - test_acpi_one("-device ipmi-bmc-sim,id=bmc0" - " -device isa-ipmi-bt,bmc=bmc0", - &data); - free_test_data(&data); -} - -static void test_acpi_piix4_tcg_ipmi(void) -{ - test_data data; - - /* Supplying -machine accel argument overrides the default (qtest). - * This is to make guest actually run. - */ - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_PC; - data.variant = ".ipmikcs"; - data.required_struct_types = ipmi_required_struct_types; - data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); - test_acpi_one("-device ipmi-bmc-sim,id=bmc0" - " -device isa-ipmi-kcs,irq=0,bmc=bmc0", - &data); - free_test_data(&data); -} - -static void test_acpi_q35_tcg_memhp(void) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_Q35; - data.variant = ".memhp"; - test_acpi_one(" -m 128,slots=3,maxmem=1G" - " -object memory-backend-ram,id=ram0,size=64M" - " -object memory-backend-ram,id=ram1,size=64M" - " -numa node,memdev=ram0 -numa node,memdev=ram1" - " -numa dist,src=0,dst=1,val=21", - &data); - free_test_data(&data); -} - -static void test_acpi_piix4_tcg_memhp(void) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_PC; - data.variant = ".memhp"; - test_acpi_one(" -m 128,slots=3,maxmem=1G" - " -object memory-backend-ram,id=ram0,size=64M" - " -object memory-backend-ram,id=ram1,size=64M" - " -numa node,memdev=ram0 -numa node,memdev=ram1" - " -numa dist,src=0,dst=1,val=21", - &data); - free_test_data(&data); -} - -static void test_acpi_q35_tcg_numamem(void) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_Q35; - data.variant = ".numamem"; - test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" - " -numa node -numa node,memdev=ram0", &data); - free_test_data(&data); -} - -static void test_acpi_piix4_tcg_numamem(void) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_PC; - data.variant = ".numamem"; - test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" - " -numa node -numa node,memdev=ram0", &data); - free_test_data(&data); -} - -static void test_acpi_tcg_dimm_pxm(const char *machine) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = machine; - data.variant = ".dimmpxm"; - test_acpi_one(" -machine nvdimm=on,nvdimm-persistence=cpu" - " -smp 4,sockets=4" - " -m 128M,slots=3,maxmem=1G" - " -object memory-backend-ram,id=ram0,size=32M" - " -object memory-backend-ram,id=ram1,size=32M" - " -object memory-backend-ram,id=ram2,size=32M" - " -object memory-backend-ram,id=ram3,size=32M" - " -numa node,memdev=ram0,nodeid=0" - " -numa node,memdev=ram1,nodeid=1" - " -numa node,memdev=ram2,nodeid=2" - " -numa node,memdev=ram3,nodeid=3" - " -numa cpu,node-id=0,socket-id=0" - " -numa cpu,node-id=1,socket-id=1" - " -numa cpu,node-id=2,socket-id=2" - " -numa cpu,node-id=3,socket-id=3" - " -object memory-backend-ram,id=ram4,size=128M" - " -object memory-backend-ram,id=nvm0,size=128M" - " -device pc-dimm,id=dimm0,memdev=ram4,node=1" - " -device nvdimm,id=dimm1,memdev=nvm0,node=2", - &data); - free_test_data(&data); -} - -static void test_acpi_q35_tcg_dimm_pxm(void) -{ - test_acpi_tcg_dimm_pxm(MACHINE_Q35); -} - -static void test_acpi_piix4_tcg_dimm_pxm(void) -{ - test_acpi_tcg_dimm_pxm(MACHINE_PC); -} - -static void test_acpi_virt_tcg_memhp(void) -{ - test_data data = { - .machine = "virt", - .tcg_only = true, - .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", - .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", - .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", - .ram_start = 0x40000000ULL, - .scan_len = 256ULL * 1024 * 1024, - }; - - data.variant = ".memhp"; - test_acpi_one(" -cpu cortex-a57" - " -m 256M,slots=3,maxmem=1G" - " -object memory-backend-ram,id=ram0,size=128M" - " -object memory-backend-ram,id=ram1,size=128M" - " -numa node,memdev=ram0 -numa node,memdev=ram1" - " -numa dist,src=0,dst=1,val=21", - &data); - - free_test_data(&data); - -} - -static void test_acpi_virt_tcg_numamem(void) -{ - test_data data = { - .machine = "virt", - .tcg_only = true, - .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", - .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", - .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", - .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, - }; - - data.variant = ".numamem"; - test_acpi_one(" -cpu cortex-a57" - " -object memory-backend-ram,id=ram0,size=128M" - " -numa node,memdev=ram0", - &data); - - free_test_data(&data); - -} - -static void test_acpi_tcg_acpi_hmat(const char *machine) -{ - test_data data; - - memset(&data, 0, sizeof(data)); - data.machine = machine; - data.variant = ".acpihmat"; - test_acpi_one(" -machine hmat=on" - " -smp 2,sockets=2" - " -m 128M,slots=2,maxmem=1G" - " -object memory-backend-ram,size=64M,id=m0" - " -object memory-backend-ram,size=64M,id=m1" - " -numa node,nodeid=0,memdev=m0" - " -numa node,nodeid=1,memdev=m1,initiator=0" - " -numa cpu,node-id=0,socket-id=0" - " -numa cpu,node-id=0,socket-id=1" - " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," - "data-type=access-latency,latency=1" - " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," - "data-type=access-bandwidth,bandwidth=65534M" - " -numa hmat-lb,initiator=0,target=1,hierarchy=memory," - "data-type=access-latency,latency=65534" - " -numa hmat-lb,initiator=0,target=1,hierarchy=memory," - "data-type=access-bandwidth,bandwidth=32767M" - " -numa hmat-cache,node-id=0,size=10K,level=1," - "associativity=direct,policy=write-back,line=8" - " -numa hmat-cache,node-id=1,size=10K,level=1," - "associativity=direct,policy=write-back,line=8", - &data); - free_test_data(&data); -} - -static void test_acpi_q35_tcg_acpi_hmat(void) -{ - test_acpi_tcg_acpi_hmat(MACHINE_Q35); -} - -static void test_acpi_piix4_tcg_acpi_hmat(void) -{ - test_acpi_tcg_acpi_hmat(MACHINE_PC); -} - -static void test_acpi_virt_tcg(void) -{ - test_data data = { - .machine = "virt", - .tcg_only = true, - .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", - .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", - .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", - .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, - }; - - test_acpi_one("-cpu cortex-a57", &data); - free_test_data(&data); -} - -int main(int argc, char *argv[]) -{ - const char *arch = qtest_get_arch(); - int ret; - - g_test_init(&argc, &argv, NULL); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - ret = boot_sector_init(disk); - if (ret) { - return ret; - } - - qtest_add_func("acpi/piix4", test_acpi_piix4_tcg); - qtest_add_func("acpi/piix4/bridge", test_acpi_piix4_tcg_bridge); - qtest_add_func("acpi/q35", test_acpi_q35_tcg); - qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge); - qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64); - qtest_add_func("acpi/piix4/ipmi", test_acpi_piix4_tcg_ipmi); - qtest_add_func("acpi/q35/ipmi", test_acpi_q35_tcg_ipmi); - qtest_add_func("acpi/piix4/cpuhp", test_acpi_piix4_tcg_cphp); - qtest_add_func("acpi/q35/cpuhp", test_acpi_q35_tcg_cphp); - qtest_add_func("acpi/piix4/memhp", test_acpi_piix4_tcg_memhp); - qtest_add_func("acpi/q35/memhp", test_acpi_q35_tcg_memhp); - qtest_add_func("acpi/piix4/numamem", test_acpi_piix4_tcg_numamem); - qtest_add_func("acpi/q35/numamem", test_acpi_q35_tcg_numamem); - qtest_add_func("acpi/piix4/dimmpxm", test_acpi_piix4_tcg_dimm_pxm); - qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm); - qtest_add_func("acpi/piix4/acpihmat", test_acpi_piix4_tcg_acpi_hmat); - qtest_add_func("acpi/q35/acpihmat", test_acpi_q35_tcg_acpi_hmat); - } else if (strcmp(arch, "aarch64") == 0) { - qtest_add_func("acpi/virt", test_acpi_virt_tcg); - qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); - qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp); - } - ret = g_test_run(); - boot_sector_cleanup(disk); - return ret; -} diff --git a/tests/boot-order-test.c b/tests/boot-order-test.c deleted file mode 100644 index a725bce729..0000000000 --- a/tests/boot-order-test.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Boot order test cases. - * - * Copyright (c) 2013 Red Hat Inc. - * - * Authors: - * Markus Armbruster , - * - * 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 "libqos/fw_cfg.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "standard-headers/linux/qemu_fw_cfg.h" - -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) - -typedef struct { - const char *args; - uint64_t expected_boot; - uint64_t expected_reboot; -} boot_order_test; - -static void test_a_boot_order(const char *machine, - const char *test_args, - uint64_t (*read_boot_order)(QTestState *), - uint64_t expected_boot, - uint64_t expected_reboot) -{ - uint64_t actual; - QTestState *qts; - - 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(qts, "{ 'execute': 'system_reset' }"); - /* - * system_reset only requests reset. We get a RESET event after - * the actual reset completes. Need to wait for that. - */ - qtest_qmp_eventwait(qts, "RESET"); - actual = read_boot_order(qts); - g_assert_cmphex(actual, ==, expected_reboot); - qtest_quit(qts); -} - -static void test_boot_orders(const char *machine, - uint64_t (*read_boot_order)(QTestState *), - const boot_order_test *tests) -{ - int i; - - for (i = 0; tests[i].args; i++) { - test_a_boot_order(machine, tests[i].args, - read_boot_order, - tests[i].expected_boot, - tests[i].expected_reboot); - } -} - -static uint8_t read_mc146818(QTestState *qts, uint16_t port, uint8_t reg) -{ - qtest_outb(qts, port, reg); - return qtest_inb(qts, port + 1); -} - -static uint64_t read_boot_order_pc(QTestState *qts) -{ - uint8_t b1 = read_mc146818(qts, 0x70, 0x38); - uint8_t b2 = read_mc146818(qts, 0x70, 0x3d); - - return b1 | (b2 << 8); -} - -static const boot_order_test test_cases_pc[] = { - { "", - 0x1230, 0x1230 }, - { "-no-fd-bootchk", - 0x1231, 0x1231 }, - { "-boot c", - 0x0200, 0x0200 }, - { "-boot nda", - 0x3410, 0x3410 }, - { "-boot order=", - 0, 0 }, - { "-boot order= -boot order=c", - 0x0200, 0x0200 }, - { "-boot once=a", - 0x0100, 0x1230 }, - { "-boot once=a -no-fd-bootchk", - 0x0101, 0x1231 }, - { "-boot once=a,order=c", - 0x0100, 0x0200 }, - { "-boot once=d -boot order=nda", - 0x0300, 0x3410 }, - { "-boot once=a -boot once=b -boot once=c", - 0x0200, 0x1230 }, - {} -}; - -static void test_pc_boot_order(void) -{ - test_boot_orders(NULL, read_boot_order_pc, test_cases_pc); -} - -static uint8_t read_m48t59(QTestState *qts, uint64_t addr, uint16_t reg) -{ - 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(QTestState *qts) -{ - return read_m48t59(qts, 0x80000000 + 0x74, 0x34); -} - -static const boot_order_test test_cases_prep[] = { - { "", 'c', 'c' }, - { "-boot c", 'c', 'c' }, - { "-boot d", 'd', 'd' }, - {} -}; - -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(QTestState *qts) -{ - QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xf0000510); - - return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); -} - -static const boot_order_test test_cases_fw_cfg[] = { - { "", 'c', 'c' }, - { "-boot c", 'c', 'c' }, - { "-boot d", 'd', 'd' }, - { "-boot once=d,order=c", 'd', 'c' }, - {} -}; - -static void test_pmac_oldworld_boot_order(void) -{ - test_boot_orders("g3beige", read_boot_order_pmac, test_cases_fw_cfg); -} - -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(QTestState *qts) -{ - QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xd00000510ULL); - - return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); -} - -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(QTestState *qts) -{ - QFWCFG *fw_cfg = io_fw_cfg_init(qts, 0x510); - - return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); -} - -static void test_sun4u_boot_order(void) -{ - test_boot_orders("sun4u", read_boot_order_sun4u, test_cases_fw_cfg); -} - -int main(int argc, char *argv[]) -{ - const char *arch = qtest_get_arch(); - - g_test_init(&argc, &argv, NULL); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("boot-order/pc", test_pc_boot_order); - } else if (strcmp(arch, "ppc") == 0 || strcmp(arch, "ppc64") == 0) { - qtest_add_func("boot-order/prep", test_prep_boot_order); - qtest_add_func("boot-order/pmac_oldworld", - test_pmac_oldworld_boot_order); - qtest_add_func("boot-order/pmac_newworld", - test_pmac_newworld_boot_order); - } else if (strcmp(arch, "sparc") == 0) { - qtest_add_func("boot-order/sun4m", test_sun4m_boot_order); - } else if (strcmp(arch, "sparc64") == 0) { - qtest_add_func("boot-order/sun4u", test_sun4u_boot_order); - } - - return g_test_run(); -} diff --git a/tests/boot-sector.c b/tests/boot-sector.c deleted file mode 100644 index 9e66c6d013..0000000000 --- a/tests/boot-sector.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * QEMU boot sector testing helpers. - * - * Copyright (c) 2016 Red Hat Inc. - * - * Authors: - * Michael S. Tsirkin - * Victor Kaplansky - * - * 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 "boot-sector.h" -#include "qemu-common.h" -#include "libqtest.h" - -#define LOW(x) ((x) & 0xff) -#define HIGH(x) ((x) >> 8) - -#define SIGNATURE 0xdead -#define SIGNATURE_OFFSET 0x10 -#define BOOT_SECTOR_ADDRESS 0x7c00 -#define SIGNATURE_ADDR (BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET) - -/* x86 boot sector code: write SIGNATURE into memory, - * then halt. - */ -static uint8_t x86_boot_sector[512] = { - /* The first sector will be placed at RAM address 00007C00, and - * the BIOS transfers control to 00007C00 - */ - - /* Data Segment register should be initialized, since pxe - * boot loader can leave it dirty. - */ - - /* 7c00: move $0000,%ax */ - [0x00] = 0xb8, - [0x01] = 0x00, - [0x02] = 0x00, - /* 7c03: move %ax,%ds */ - [0x03] = 0x8e, - [0x04] = 0xd8, - - /* 7c05: mov $0xdead,%ax */ - [0x05] = 0xb8, - [0x06] = LOW(SIGNATURE), - [0x07] = HIGH(SIGNATURE), - /* 7c08: mov %ax,0x7c10 */ - [0x08] = 0xa3, - [0x09] = LOW(SIGNATURE_ADDR), - [0x0a] = HIGH(SIGNATURE_ADDR), - - /* 7c0b cli */ - [0x0b] = 0xfa, - /* 7c0c: hlt */ - [0x0c] = 0xf4, - /* 7c0e: jmp 0x7c07=0x7c0f-3 */ - [0x0d] = 0xeb, - [0x0e] = LOW(-3), - /* We mov 0xdead here: set value to make debugging easier */ - [SIGNATURE_OFFSET] = LOW(0xface), - [SIGNATURE_OFFSET + 1] = HIGH(0xface), - /* End of boot sector marker */ - [0x1FE] = 0x55, - [0x1FF] = 0xAA, -}; - -/* For s390x, use a mini "kernel" with the appropriate signature */ -static const uint8_t s390x_psw_and_magic[] = { - 0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, /* Program status word */ - 0x02, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x50, /* Magic: */ - 0x02, 0x00, 0x00, 0x68, 0x60, 0x00, 0x00, 0x50, /* see linux_s390_magic */ - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* in the s390-ccw bios */ -}; -static const uint8_t s390x_code[] = { - 0xa7, 0xf4, 0x00, 0x08, /* j 0x10010 */ - 0x00, 0x00, 0x00, 0x00, - 'S', '3', '9', '0', - 'E', 'P', 0x00, 0x01, - 0xa7, 0x39, HIGH(SIGNATURE_ADDR), LOW(SIGNATURE_ADDR), /* lghi r3,0x7c10 */ - 0xa7, 0x48, LOW(SIGNATURE), HIGH(SIGNATURE), /* lhi r4,0xadde */ - 0x40, 0x40, 0x30, 0x00, /* sth r4,0(r3) */ - 0xa7, 0xf4, 0xff, 0xfa /* j 0x10010 */ -}; - -/* Create boot disk file. */ -int boot_sector_init(char *fname) -{ - int fd, ret; - size_t len; - char *boot_code; - const char *arch = qtest_get_arch(); - - fd = mkstemp(fname); - if (fd < 0) { - fprintf(stderr, "Couldn't open \"%s\": %s", fname, strerror(errno)); - return 1; - } - - if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64")) { - /* Q35 requires a minimum 0x7e000 bytes disk (bug or feature?) */ - len = MAX(0x7e000, sizeof(x86_boot_sector)); - boot_code = g_malloc0(len); - memcpy(boot_code, x86_boot_sector, sizeof(x86_boot_sector)); - } else if (g_str_equal(arch, "ppc64")) { - /* For Open Firmware based system, use a Forth script */ - boot_code = g_strdup_printf("\\ Bootscript\n%x %x c! %x %x c!\n", - LOW(SIGNATURE), SIGNATURE_ADDR, - HIGH(SIGNATURE), SIGNATURE_ADDR + 1); - len = strlen(boot_code); - } else if (g_str_equal(arch, "s390x")) { - len = 0x10000 + sizeof(s390x_code); - boot_code = g_malloc0(len); - memcpy(boot_code, s390x_psw_and_magic, sizeof(s390x_psw_and_magic)); - memcpy(&boot_code[0x10000], s390x_code, sizeof(s390x_code)); - } else { - g_assert_not_reached(); - } - - ret = write(fd, boot_code, len); - close(fd); - - g_free(boot_code); - - if (ret != len) { - fprintf(stderr, "Could not write \"%s\"", fname); - return 1; - } - - return 0; -} - -/* Loop until signature in memory is OK. */ -void boot_sector_test(QTestState *qts) -{ - uint8_t signature_low; - uint8_t signature_high; - uint16_t signature; - int i; - - /* Wait at most 600 seconds (test is slow with TCI and --enable-debug) */ -#define TEST_DELAY (1 * G_USEC_PER_SEC / 10) -#define TEST_CYCLES MAX((600 * G_USEC_PER_SEC / TEST_DELAY), 1) - - /* Poll until code has run and modified memory. Once it has we know BIOS - * initialization is done. TODO: check that IP reached the halt - * instruction. - */ - for (i = 0; i < TEST_CYCLES; ++i) { - signature_low = qtest_readb(qts, SIGNATURE_ADDR); - signature_high = qtest_readb(qts, SIGNATURE_ADDR + 1); - signature = (signature_high << 8) | signature_low; - if (signature == SIGNATURE) { - break; - } - g_usleep(TEST_DELAY); - } - - g_assert_cmphex(signature, ==, SIGNATURE); -} - -/* unlink boot disk file. */ -void boot_sector_cleanup(const char *fname) -{ - unlink(fname); -} diff --git a/tests/boot-sector.h b/tests/boot-sector.h deleted file mode 100644 index 6ee6bb4d97..0000000000 --- a/tests/boot-sector.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * QEMU boot sector testing helpers. - * - * Copyright (c) 2016 Red Hat Inc. - * - * Authors: - * Michael S. Tsirkin - * Victor Kaplansky - * - * 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 TEST_BOOT_SECTOR_H -#define TEST_BOOT_SECTOR_H - -#include "libqtest.h" - -/* Create boot disk file. fname must be a suitable string for mkstemp() */ -int boot_sector_init(char *fname); - -/* Loop until signature in memory is OK. */ -void boot_sector_test(QTestState *qts); - -/* unlink boot disk file. */ -void boot_sector_cleanup(const char *fname); - -#endif /* TEST_BOOT_SECTOR_H */ diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c deleted file mode 100644 index 05c7f44457..0000000000 --- a/tests/boot-serial-test.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Test serial output of some machines. - * - * Copyright 2016 Thomas Huth, Red Hat Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 - * or later. See the COPYING file in the top-level directory. - * - * This test is used to check that the serial output of the firmware - * (that we provide for some machines) or some small mini-kernels that - * we provide here contains an expected string. Thus we check that the - * firmware/kernel still boots at least to a certain point and so we - * know that the machine is not completely broken. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" - -static const uint8_t kernel_mcf5208[] = { - 0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */ - 0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */ - 0x11, 0x7c, 0x00, 0x04, 0x00, 0x08, /* move.b #4,8(%a0) Enable TX */ - 0x11, 0x40, 0x00, 0x0c, /* move.b %d0,12(%a0) Print 'T' */ - 0x60, 0xfa /* bra.s loop */ -}; - -static const uint8_t bios_nextcube[] = { - 0x06, 0x00, 0x00, 0x00, /* Initial SP */ - 0x01, 0x00, 0x00, 0x08, /* Initial PC */ - 0x41, 0xf9, 0x02, 0x11, 0x80, 0x00, /* lea 0x02118000,%a0 */ - 0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */ - 0x11, 0x7c, 0x00, 0x05, 0x00, 0x01, /* move.b #5,1(%a0) Sel TXCTRL */ - 0x11, 0x7c, 0x00, 0x68, 0x00, 0x01, /* move.b #0x68,1(%a0) Enable TX */ - 0x11, 0x40, 0x00, 0x03, /* move.b %d0,3(%a0) Print 'T' */ - 0x60, 0xfa /* bra.s loop */ -}; - -static const uint8_t kernel_pls3adsp1800[] = { - 0xb0, 0x00, 0x84, 0x00, /* imm 0x8400 */ - 0x30, 0x60, 0x00, 0x04, /* addik r3,r0,4 */ - 0x30, 0x80, 0x00, 0x54, /* addik r4,r0,'T' */ - 0xf0, 0x83, 0x00, 0x00, /* sbi r4,r3,0 */ - 0xb8, 0x00, 0xff, 0xfc /* bri -4 loop */ -}; - -static const uint8_t kernel_plml605[] = { - 0xe0, 0x83, 0x00, 0xb0, /* imm 0x83e0 */ - 0x00, 0x10, 0x60, 0x30, /* addik r3,r0,0x1000 */ - 0x54, 0x00, 0x80, 0x30, /* addik r4,r0,'T' */ - 0x00, 0x00, 0x83, 0xf0, /* sbi r4,r3,0 */ - 0xfc, 0xff, 0x00, 0xb8 /* bri -4 loop */ -}; - -static const uint8_t bios_moxiesim[] = { - 0x20, 0x10, 0x00, 0x00, 0x03, 0xf8, /* ldi.s r1,0x3f8 */ - 0x1b, 0x20, 0x00, 0x00, 0x00, 0x54, /* ldi.b r2,'T' */ - 0x1e, 0x12, /* st.b r1,r2 */ - 0x1a, 0x00, 0x00, 0x00, 0x10, 0x00 /* jmpa 0x1000 */ -}; - -static const uint8_t bios_raspi2[] = { - 0x08, 0x30, 0x9f, 0xe5, /* ldr r3,[pc,#8] Get base */ - 0x54, 0x20, 0xa0, 0xe3, /* mov r2,#'T' */ - 0x00, 0x20, 0xc3, 0xe5, /* strb r2,[r3] */ - 0xfb, 0xff, 0xff, 0xea, /* b loop */ - 0x00, 0x10, 0x20, 0x3f, /* 0x3f201000 = UART0 base addr */ -}; - -static const uint8_t kernel_aarch64[] = { - 0x81, 0x0a, 0x80, 0x52, /* mov w1, #0x54 */ - 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 */ - 0x41, 0x00, 0x00, 0x39, /* strb w1, [x2] */ - 0xfd, 0xff, 0xff, 0x17, /* b -12 (loop) */ -}; - -static const uint8_t kernel_nrf51[] = { - 0x00, 0x00, 0x00, 0x00, /* Stack top address */ - 0x09, 0x00, 0x00, 0x00, /* Reset handler address */ - 0x04, 0x4a, /* ldr r2, [pc, #16] Get ENABLE */ - 0x04, 0x21, /* movs r1, #4 */ - 0x11, 0x60, /* str r1, [r2] */ - 0x04, 0x4a, /* ldr r2, [pc, #16] Get STARTTX */ - 0x01, 0x21, /* movs r1, #1 */ - 0x11, 0x60, /* str r1, [r2] */ - 0x03, 0x4a, /* ldr r2, [pc, #12] Get TXD */ - 0x54, 0x21, /* movs r1, 'T' */ - 0x11, 0x60, /* str r1, [r2] */ - 0xfe, 0xe7, /* b . */ - 0x00, 0x25, 0x00, 0x40, /* 0x40002500 = UART ENABLE */ - 0x08, 0x20, 0x00, 0x40, /* 0x40002008 = UART STARTTX */ - 0x1c, 0x25, 0x00, 0x40 /* 0x4000251c = UART TXD */ -}; - -typedef struct testdef { - const char *arch; /* Target architecture */ - const char *machine; /* Name of the machine */ - const char *extra; /* Additional parameters */ - const char *expect; /* Expected string in the serial output */ - size_t codesize; /* Size of the kernel or bios data */ - const uint8_t *kernel; /* Set in case we use our own mini kernel */ - const uint8_t *bios; /* Set in case we use our own mini bios */ -} testdef_t; - -static testdef_t tests[] = { - { "alpha", "clipper", "", "PCI:" }, - { "ppc", "ppce500", "", "U-Boot" }, - { "ppc", "40p", "-vga none -boot d", "Trying cd:," }, - { "ppc", "g3beige", "", "PowerPC,750" }, - { "ppc", "mac99", "", "PowerPC,G4" }, - { "ppc", "sam460ex", "-m 256", "DRAM: 256 MiB" }, - { "ppc64", "ppce500", "", "U-Boot" }, - { "ppc64", "40p", "-m 192", "Memory: 192M" }, - { "ppc64", "mac99", "", "PowerPC,970FX" }, - { "ppc64", "pseries", - "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken", - "Open Firmware" }, - { "ppc64", "powernv8", "", "OPAL" }, - { "ppc64", "powernv9", "", "OPAL" }, - { "ppc64", "sam460ex", "-device e1000", "8086 100e" }, - { "i386", "isapc", "-cpu qemu32 -device sga", "SGABIOS" }, - { "i386", "pc", "-device sga", "SGABIOS" }, - { "i386", "q35", "-device sga", "SGABIOS" }, - { "x86_64", "isapc", "-cpu qemu32 -device sga", "SGABIOS" }, - { "x86_64", "q35", "-device sga", "SGABIOS" }, - { "sparc", "LX", "", "TMS390S10" }, - { "sparc", "SS-4", "", "MB86904" }, - { "sparc", "SS-600MP", "", "TMS390Z55" }, - { "sparc64", "sun4u", "", "UltraSPARC" }, - { "s390x", "s390-ccw-virtio", "", "device" }, - { "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 }, - { "m68k", "next-cube", "", "TT", sizeof(bios_nextcube), 0, bios_nextcube }, - { "microblaze", "petalogix-s3adsp1800", "", "TT", - sizeof(kernel_pls3adsp1800), kernel_pls3adsp1800 }, - { "microblazeel", "petalogix-ml605", "", "TT", - sizeof(kernel_plml605), kernel_plml605 }, - { "moxie", "moxiesim", "", "TT", sizeof(bios_moxiesim), 0, bios_moxiesim }, - { "arm", "raspi2", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 }, - { "hppa", "hppa", "", "SeaBIOS wants SYSTEM HALT" }, - { "aarch64", "virt", "-cpu cortex-a57", "TT", sizeof(kernel_aarch64), - kernel_aarch64 }, - { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, - - { NULL } -}; - -static bool check_guest_output(QTestState *qts, const testdef_t *test, int fd) -{ - int nbr = 0, pos = 0, ccnt; - time_t now, start = time(NULL); - char ch; - - /* Poll serial output... */ - while (1) { - ccnt = 0; - while (ccnt++ < 512 && (nbr = read(fd, &ch, 1)) == 1) { - if (ch == test->expect[pos]) { - pos += 1; - if (test->expect[pos] == '\0') { - /* We've reached the end of the expected string! */ - return true; - } - } else { - pos = 0; - } - } - g_assert(nbr >= 0); - /* Wait only if the child is still alive. */ - if (!qtest_probe_child(qts)) { - break; - } - /* Wait at most 360 seconds. */ - now = time(NULL); - if (now - start >= 360) { - break; - } - g_usleep(10000); - } - - return false; -} - -static void test_machine(const void *data) -{ - const testdef_t *test = data; - char serialtmp[] = "/tmp/qtest-boot-serial-sXXXXXX"; - char codetmp[] = "/tmp/qtest-boot-serial-cXXXXXX"; - const char *codeparam = ""; - const uint8_t *code = NULL; - QTestState *qts; - int ser_fd; - - ser_fd = mkstemp(serialtmp); - g_assert(ser_fd != -1); - - if (test->kernel) { - code = test->kernel; - codeparam = "-kernel"; - } else if (test->bios) { - code = test->bios; - codeparam = "-bios"; - } - - if (code) { - ssize_t wlen; - int code_fd; - - code_fd = mkstemp(codetmp); - g_assert(code_fd != -1); - wlen = write(code_fd, code, test->codesize); - g_assert(wlen == test->codesize); - close(code_fd); - } - - /* - * Make sure that this test uses tcg if available: It is used as a - * fast-enough smoketest for that. - */ - qts = qtest_initf("%s %s -M %s -no-shutdown " - "-chardev file,id=serial0,path=%s " - "-serial chardev:serial0 -accel tcg -accel kvm %s", - codeparam, code ? codetmp : "", test->machine, - serialtmp, test->extra); - if (code) { - unlink(codetmp); - } - - if (!check_guest_output(qts, test, ser_fd)) { - g_error("Failed to find expected string. Please check '%s'", - serialtmp); - } - unlink(serialtmp); - - qtest_quit(qts); - - close(ser_fd); -} - -int main(int argc, char *argv[]) -{ - const char *arch = qtest_get_arch(); - int i; - - g_test_init(&argc, &argv, NULL); - - for (i = 0; tests[i].arch != NULL; i++) { - if (strcmp(arch, tests[i].arch) == 0) { - char *name = g_strdup_printf("boot-serial/%s", tests[i].machine); - qtest_add_data_func(name, &tests[i], test_machine); - g_free(name); - } - } - - return g_test_run(); -} diff --git a/tests/cdrom-test.c b/tests/cdrom-test.c deleted file mode 100644 index 67635e387a..0000000000 --- a/tests/cdrom-test.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Various tests for emulated CD-ROM drives. - * - * Copyright (c) 2018 Red Hat Inc. - * - * Author: - * Thomas Huth - * - * This work is licensed under the terms of the GNU GPL, version 2 - * or later. See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "boot-sector.h" -#include "qapi/qmp/qdict.h" - -static char isoimage[] = "cdrom-boot-iso-XXXXXX"; - -static int exec_genisoimg(const char **args) -{ - gchar *out_err = NULL; - gint exit_status = -1; - bool success; - - success = g_spawn_sync(NULL, (gchar **)args, NULL, - G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL, - NULL, NULL, NULL, &out_err, &exit_status, NULL); - if (!success) { - return -ENOENT; - } - if (out_err) { - fputs(out_err, stderr); - g_free(out_err); - } - - return exit_status; -} - -static int prepare_image(const char *arch, char *isoimage) -{ - char srcdir[] = "cdrom-test-dir-XXXXXX"; - char *codefile = NULL; - int ifh, ret = -1; - const char *args[] = { - "genisoimage", "-quiet", "-l", "-no-emul-boot", - "-b", NULL, "-o", isoimage, srcdir, NULL - }; - - ifh = mkstemp(isoimage); - if (ifh < 0) { - perror("Error creating temporary iso image file"); - return -1; - } - if (!mkdtemp(srcdir)) { - perror("Error creating temporary directory"); - goto cleanup; - } - - if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") || - g_str_equal(arch, "s390x")) { - codefile = g_strdup_printf("%s/bootcode-XXXXXX", srcdir); - ret = boot_sector_init(codefile); - if (ret) { - goto cleanup; - } - } else { - /* Just create a dummy file */ - char txt[] = "empty disc"; - codefile = g_strdup_printf("%s/readme.txt", srcdir); - if (!g_file_set_contents(codefile, txt, sizeof(txt) - 1, NULL)) { - fprintf(stderr, "Failed to create '%s'\n", codefile); - goto cleanup; - } - } - - args[5] = strchr(codefile, '/') + 1; - ret = exec_genisoimg(args); - if (ret) { - fprintf(stderr, "genisoimage failed: %i\n", ret); - } - - unlink(codefile); - -cleanup: - g_free(codefile); - rmdir(srcdir); - close(ifh); - - return ret; -} - -/** - * Check that at least the -cdrom parameter is basically working, i.e. we can - * see the filename of the ISO image in the output of "info block" afterwards - */ -static void test_cdrom_param(gconstpointer data) -{ - QTestState *qts; - char *resp; - - qts = qtest_initf("-M %s -cdrom %s", (const char *)data, isoimage); - resp = qtest_hmp(qts, "info block"); - g_assert(strstr(resp, isoimage) != 0); - g_free(resp); - qtest_quit(qts); -} - -static void add_cdrom_param_tests(const char **machines) -{ - while (*machines) { - char *testname = g_strdup_printf("cdrom/param/%s", *machines); - qtest_add_data_func(testname, *machines, test_cdrom_param); - g_free(testname); - machines++; - } -} - -static void test_cdboot(gconstpointer data) -{ - QTestState *qts; - - qts = qtest_initf("-accel kvm -accel tcg -no-shutdown %s%s", (const char *)data, - isoimage); - boot_sector_test(qts); - qtest_quit(qts); -} - -static void add_x86_tests(void) -{ - qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); - qtest_add_data_func("cdrom/boot/virtio-scsi", - "-device virtio-scsi -device scsi-cd,drive=cdr " - "-blockdev file,node-name=cdr,filename=", test_cdboot); - /* - * Unstable CI test under load - * See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html - */ - if (g_test_slow()) { - qtest_add_data_func("cdrom/boot/isapc", "-M isapc " - "-drive if=ide,media=cdrom,file=", test_cdboot); - } - qtest_add_data_func("cdrom/boot/am53c974", - "-device am53c974 -device scsi-cd,drive=cd1 " - "-drive if=none,id=cd1,format=raw,file=", test_cdboot); - qtest_add_data_func("cdrom/boot/dc390", - "-device dc390 -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", test_cdboot); - qtest_add_data_func("cdrom/boot/lsi53c895a", - "-device lsi53c895a -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", test_cdboot); - qtest_add_data_func("cdrom/boot/megasas", "-M q35 " - "-device megasas -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", test_cdboot); - qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 " - "-device megasas-gen2 -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", test_cdboot); -} - -static void add_s390x_tests(void) -{ - qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); - qtest_add_data_func("cdrom/boot/virtio-scsi", - "-device virtio-scsi -device scsi-cd,drive=cdr " - "-blockdev file,node-name=cdr,filename=", test_cdboot); -} - -int main(int argc, char **argv) -{ - int ret; - const char *arch = qtest_get_arch(); - const char *genisocheck[] = { "genisoimage", "-version", NULL }; - - g_test_init(&argc, &argv, NULL); - - if (exec_genisoimg(genisocheck)) { - /* genisoimage not available - so can't run tests */ - return g_test_run(); - } - - ret = prepare_image(arch, isoimage); - if (ret) { - return ret; - } - - if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64")) { - add_x86_tests(); - } else if (g_str_equal(arch, "s390x")) { - add_s390x_tests(); - } else if (g_str_equal(arch, "ppc64")) { - const char *ppcmachines[] = { - "pseries", "mac99", "g3beige", "40p", "prep", NULL - }; - add_cdrom_param_tests(ppcmachines); - } else if (g_str_equal(arch, "sparc")) { - const char *sparcmachines[] = { - "LX", "SPARCClassic", "SPARCbook", "SS-10", "SS-20", "SS-4", - "SS-5", "SS-600MP", "Voyager", "leon3_generic", NULL - }; - add_cdrom_param_tests(sparcmachines); - } else if (g_str_equal(arch, "sparc64")) { - const char *sparc64machines[] = { - "niagara", "sun4u", "sun4v", NULL - }; - add_cdrom_param_tests(sparc64machines); - } else if (!strncmp(arch, "mips64", 6)) { - const char *mips64machines[] = { - "magnum", "malta", "mips", "pica61", NULL - }; - add_cdrom_param_tests(mips64machines); - } else if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { - const char *armmachines[] = { - "realview-eb", "realview-eb-mpcore", "realview-pb-a8", - "realview-pbx-a9", "versatileab", "versatilepb", "vexpress-a15", - "vexpress-a9", "virt", NULL - }; - add_cdrom_param_tests(armmachines); - } else { - const char *nonemachine[] = { "none", NULL }; - add_cdrom_param_tests(nonemachine); - } - - ret = g_test_run(); - - unlink(isoimage); - - return ret; -} diff --git a/tests/cpu-plug-test.c b/tests/cpu-plug-test.c deleted file mode 100644 index e8ffbbce4b..0000000000 --- a/tests/cpu-plug-test.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * QTest testcase for CPU plugging - * - * Copyright (c) 2015 SUSE Linux GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qemu-common.h" -#include "libqtest-single.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" - -struct PlugTestData { - char *machine; - const char *cpu_model; - char *device_model; - unsigned sockets; - unsigned cores; - unsigned threads; - unsigned maxcpus; -}; -typedef struct PlugTestData PlugTestData; - -static void test_plug_with_cpu_add(gconstpointer data) -{ - const PlugTestData *s = data; - char *args; - QDict *response; - unsigned int i; - - args = g_strdup_printf("-machine %s -cpu %s " - "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", - s->machine, s->cpu_model, - s->sockets, s->cores, s->threads, s->maxcpus); - qtest_start(args); - - for (i = 1; i < s->maxcpus; i++) { - response = qmp("{ 'execute': 'cpu-add'," - " 'arguments': { 'id': %d } }", i); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - } - - qtest_end(); - g_free(args); -} - -static void test_plug_without_cpu_add(gconstpointer data) -{ - const PlugTestData *s = data; - char *args; - QDict *response; - - args = g_strdup_printf("-machine %s -cpu %s " - "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", - s->machine, s->cpu_model, - s->sockets, s->cores, s->threads, s->maxcpus); - qtest_start(args); - - response = qmp("{ 'execute': 'cpu-add'," - " 'arguments': { 'id': %d } }", - s->sockets * s->cores * s->threads); - g_assert(response); - g_assert(qdict_haskey(response, "error")); - qobject_unref(response); - - qtest_end(); - g_free(args); -} - -static void test_plug_with_device_add(gconstpointer data) -{ - const PlugTestData *td = data; - char *args; - QTestState *qts; - QDict *resp; - QList *cpus; - QObject *e; - int hotplugged = 0; - - args = g_strdup_printf("-machine %s -cpu %s " - "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", - td->machine, td->cpu_model, - td->sockets, td->cores, td->threads, td->maxcpus); - qts = qtest_init(args); - - resp = qtest_qmp(qts, "{ 'execute': 'query-hotpluggable-cpus'}"); - g_assert(qdict_haskey(resp, "return")); - cpus = qdict_get_qlist(resp, "return"); - g_assert(cpus); - - while ((e = qlist_pop(cpus))) { - const QDict *cpu, *props; - - cpu = qobject_to(QDict, e); - if (qdict_haskey(cpu, "qom-path")) { - qobject_unref(e); - continue; - } - - g_assert(qdict_haskey(cpu, "props")); - props = qdict_get_qdict(cpu, "props"); - - qtest_qmp_device_add_qdict(qts, td->device_model, props); - hotplugged++; - qobject_unref(e); - } - - /* make sure that there were hotplugged CPUs */ - g_assert(hotplugged); - qobject_unref(resp); - qtest_quit(qts); - g_free(args); -} - -static void test_data_free(gpointer data) -{ - PlugTestData *pc = data; - - g_free(pc->machine); - g_free(pc->device_model); - g_free(pc); -} - -static void add_pc_test_case(const char *mname) -{ - char *path; - PlugTestData *data; - - if (!g_str_has_prefix(mname, "pc-")) { - return; - } - data = g_new(PlugTestData, 1); - data->machine = g_strdup(mname); - data->cpu_model = "Haswell"; /* 1.3+ theoretically */ - data->device_model = g_strdup_printf("%s-%s-cpu", data->cpu_model, - qtest_get_arch()); - data->sockets = 1; - data->cores = 3; - data->threads = 2; - data->maxcpus = data->sockets * data->cores * data->threads; - if (g_str_has_suffix(mname, "-1.4") || - (strcmp(mname, "pc-1.3") == 0) || - (strcmp(mname, "pc-1.2") == 0) || - (strcmp(mname, "pc-1.1") == 0) || - (strcmp(mname, "pc-1.0") == 0)) { - path = g_strdup_printf("cpu-plug/%s/init/%ux%ux%u&maxcpus=%u", - mname, data->sockets, data->cores, - data->threads, data->maxcpus); - qtest_add_data_func_full(path, data, test_plug_without_cpu_add, - test_data_free); - g_free(path); - } else { - PlugTestData *data2 = g_memdup(data, sizeof(PlugTestData)); - - data2->machine = g_strdup(data->machine); - data2->device_model = g_strdup(data->device_model); - - path = g_strdup_printf("cpu-plug/%s/cpu-add/%ux%ux%u&maxcpus=%u", - mname, data->sockets, data->cores, - data->threads, data->maxcpus); - qtest_add_data_func_full(path, data, test_plug_with_cpu_add, - test_data_free); - g_free(path); - path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", - mname, data2->sockets, data2->cores, - data2->threads, data2->maxcpus); - qtest_add_data_func_full(path, data2, test_plug_with_device_add, - test_data_free); - g_free(path); - } -} - -static void add_pseries_test_case(const char *mname) -{ - char *path; - PlugTestData *data; - - if (!g_str_has_prefix(mname, "pseries-") || - (g_str_has_prefix(mname, "pseries-2.") && atoi(&mname[10]) < 7)) { - return; - } - data = g_new(PlugTestData, 1); - data->machine = g_strdup(mname); - data->cpu_model = "power8_v2.0"; - data->device_model = g_strdup("power8_v2.0-spapr-cpu-core"); - data->sockets = 2; - data->cores = 3; - data->threads = 1; - data->maxcpus = data->sockets * data->cores * data->threads; - - path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", - mname, data->sockets, data->cores, - data->threads, data->maxcpus); - qtest_add_data_func_full(path, data, test_plug_with_device_add, - test_data_free); - g_free(path); -} - -static void add_s390x_test_case(const char *mname) -{ - char *path; - PlugTestData *data, *data2; - - if (!g_str_has_prefix(mname, "s390-ccw-virtio-")) { - return; - } - - data = g_new(PlugTestData, 1); - data->machine = g_strdup(mname); - data->cpu_model = "qemu"; - data->device_model = g_strdup("qemu-s390x-cpu"); - data->sockets = 1; - data->cores = 3; - data->threads = 1; - data->maxcpus = data->sockets * data->cores * data->threads; - - data2 = g_memdup(data, sizeof(PlugTestData)); - data2->machine = g_strdup(data->machine); - data2->device_model = g_strdup(data->device_model); - - path = g_strdup_printf("cpu-plug/%s/cpu-add/%ux%ux%u&maxcpus=%u", - mname, data->sockets, data->cores, - data->threads, data->maxcpus); - qtest_add_data_func_full(path, data, test_plug_with_cpu_add, - test_data_free); - g_free(path); - - path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", - mname, data2->sockets, data2->cores, - data2->threads, data2->maxcpus); - qtest_add_data_func_full(path, data2, test_plug_with_device_add, - test_data_free); - g_free(path); -} - -int main(int argc, char **argv) -{ - const char *arch = qtest_get_arch(); - - g_test_init(&argc, &argv, NULL); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_cb_for_every_machine(add_pc_test_case, g_test_quick()); - } else if (g_str_equal(arch, "ppc64")) { - qtest_cb_for_every_machine(add_pseries_test_case, g_test_quick()); - } else if (g_str_equal(arch, "s390x")) { - qtest_cb_for_every_machine(add_s390x_test_case, g_test_quick()); - } - - return g_test_run(); -} diff --git a/tests/dbus-vmstate-test.c b/tests/dbus-vmstate-test.c deleted file mode 100644 index 2e5e47dec2..0000000000 --- a/tests/dbus-vmstate-test.c +++ /dev/null @@ -1,382 +0,0 @@ -#include "qemu/osdep.h" -#include -#include -#include "libqtest.h" -#include "qemu-common.h" -#include "dbus-vmstate1.h" -#include "migration-helpers.h" - -static char *workdir; - -typedef struct TestServerId { - const char *name; - const char *data; - size_t size; -} TestServerId; - -static const TestServerId idA = { - "idA", "I'am\0idA!", sizeof("I'am\0idA!") -}; - -static const TestServerId idB = { - "idB", "I'am\0idB!", sizeof("I'am\0idB!") -}; - -typedef struct TestServer { - const TestServerId *id; - bool save_called; - bool load_called; -} TestServer; - -typedef struct Test { - const char *id_list; - bool migrate_fail; - bool without_dst_b; - TestServer srcA; - TestServer dstA; - TestServer srcB; - TestServer dstB; - GMainLoop *loop; - QTestState *src_qemu; -} Test; - -static gboolean -vmstate_load(VMState1 *object, GDBusMethodInvocation *invocation, - const gchar *arg_data, gpointer user_data) -{ - TestServer *h = user_data; - g_autoptr(GVariant) var = NULL; - GVariant *args; - const uint8_t *data; - size_t size; - - args = g_dbus_method_invocation_get_parameters(invocation); - var = g_variant_get_child_value(args, 0); - data = g_variant_get_fixed_array(var, &size, sizeof(char)); - g_assert_cmpuint(size, ==, h->id->size); - g_assert(!memcmp(data, h->id->data, h->id->size)); - h->load_called = true; - - g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); - return TRUE; -} - -static gboolean -vmstate_save(VMState1 *object, GDBusMethodInvocation *invocation, - gpointer user_data) -{ - TestServer *h = user_data; - GVariant *var; - - var = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, - h->id->data, h->id->size, sizeof(char)); - g_dbus_method_invocation_return_value(invocation, - g_variant_new("(@ay)", var)); - h->save_called = true; - - return TRUE; -} - -typedef struct WaitNamed { - GMainLoop *loop; - bool named; -} WaitNamed; - -static void -named_cb(GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - WaitNamed *t = user_data; - - t->named = true; - g_main_loop_quit(t->loop); -} - -static GDBusConnection * -get_connection(Test *test, guint *ownid) -{ - g_autofree gchar *addr = NULL; - WaitNamed *wait; - GError *err = NULL; - GDBusConnection *c; - - wait = g_new0(WaitNamed, 1); - wait->loop = test->loop; - addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, NULL, &err); - g_assert_no_error(err); - - c = g_dbus_connection_new_for_address_sync( - addr, - G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, - NULL, NULL, &err); - g_assert_no_error(err); - *ownid = g_bus_own_name_on_connection(c, "org.qemu.VMState1", - G_BUS_NAME_OWNER_FLAGS_NONE, - named_cb, named_cb, wait, g_free); - if (!wait->named) { - g_main_loop_run(wait->loop); - } - - return c; -} - -static GDBusObjectManagerServer * -get_server(GDBusConnection *conn, TestServer *s, const TestServerId *id) -{ - g_autoptr(GDBusObjectSkeleton) sk = NULL; - g_autoptr(VMState1Skeleton) v = NULL; - GDBusObjectManagerServer *os; - - s->id = id; - os = g_dbus_object_manager_server_new("/org/qemu"); - sk = g_dbus_object_skeleton_new("/org/qemu/VMState1"); - - v = VMSTATE1_SKELETON(vmstate1_skeleton_new()); - g_object_set(v, "id", id->name, NULL); - - g_signal_connect(v, "handle-load", G_CALLBACK(vmstate_load), s); - g_signal_connect(v, "handle-save", G_CALLBACK(vmstate_save), s); - - g_dbus_object_skeleton_add_interface(sk, G_DBUS_INTERFACE_SKELETON(v)); - g_dbus_object_manager_server_export(os, sk); - g_dbus_object_manager_server_set_connection(os, conn); - - return os; -} - -static void -set_id_list(Test *test, QTestState *s) -{ - if (!test->id_list) { - return; - } - - g_assert(!qmp_rsp_is_err(qtest_qmp(s, - "{ 'execute': 'qom-set', 'arguments': " - "{ 'path': '/objects/dv', 'property': 'id-list', 'value': %s } }", - test->id_list))); -} - -static gpointer -dbus_vmstate_thread(gpointer data) -{ - GMainLoop *loop = data; - - g_main_loop_run(loop); - - return NULL; -} - -static void -test_dbus_vmstate(Test *test) -{ - g_autofree char *src_qemu_args = NULL; - g_autofree char *dst_qemu_args = NULL; - g_autoptr(GTestDBus) srcbus = NULL; - g_autoptr(GTestDBus) dstbus = NULL; - g_autoptr(GDBusConnection) srcconnA = NULL; - g_autoptr(GDBusConnection) srcconnB = NULL; - g_autoptr(GDBusConnection) dstconnA = NULL; - g_autoptr(GDBusConnection) dstconnB = NULL; - g_autoptr(GDBusObjectManagerServer) srcserverA = NULL; - g_autoptr(GDBusObjectManagerServer) srcserverB = NULL; - g_autoptr(GDBusObjectManagerServer) dstserverA = NULL; - g_autoptr(GDBusObjectManagerServer) dstserverB = NULL; - g_auto(GStrv) srcaddr = NULL; - g_auto(GStrv) dstaddr = NULL; - g_autoptr(GThread) thread = NULL; - g_autoptr(GMainLoop) loop = NULL; - g_autofree char *uri = NULL; - QTestState *src_qemu = NULL, *dst_qemu = NULL; - guint ownsrcA, ownsrcB, owndstA, owndstB; - - uri = g_strdup_printf("unix:%s/migsocket", workdir); - - loop = g_main_loop_new(NULL, FALSE); - test->loop = loop; - - srcbus = g_test_dbus_new(G_TEST_DBUS_NONE); - g_test_dbus_up(srcbus); - srcconnA = get_connection(test, &ownsrcA); - srcserverA = get_server(srcconnA, &test->srcA, &idA); - srcconnB = get_connection(test, &ownsrcB); - srcserverB = get_server(srcconnB, &test->srcB, &idB); - - /* remove ,guid=foo part */ - srcaddr = g_strsplit(g_test_dbus_get_bus_address(srcbus), ",", 2); - src_qemu_args = - g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s", srcaddr[0]); - - dstbus = g_test_dbus_new(G_TEST_DBUS_NONE); - g_test_dbus_up(dstbus); - dstconnA = get_connection(test, &owndstA); - dstserverA = get_server(dstconnA, &test->dstA, &idA); - if (!test->without_dst_b) { - dstconnB = get_connection(test, &owndstB); - dstserverB = get_server(dstconnB, &test->dstB, &idB); - } - - dstaddr = g_strsplit(g_test_dbus_get_bus_address(dstbus), ",", 2); - dst_qemu_args = - g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s -incoming %s", - dstaddr[0], uri); - - src_qemu = qtest_init(src_qemu_args); - dst_qemu = qtest_init(dst_qemu_args); - set_id_list(test, src_qemu); - set_id_list(test, dst_qemu); - - thread = g_thread_new("dbus-vmstate-thread", dbus_vmstate_thread, loop); - - migrate_qmp(src_qemu, uri, "{}"); - test->src_qemu = src_qemu; - if (test->migrate_fail) { - wait_for_migration_fail(src_qemu, true); - qtest_set_expected_status(dst_qemu, 1); - } else { - wait_for_migration_complete(src_qemu); - } - - qtest_quit(dst_qemu); - qtest_quit(src_qemu); - g_bus_unown_name(ownsrcA); - g_bus_unown_name(ownsrcB); - g_bus_unown_name(owndstA); - if (!test->without_dst_b) { - g_bus_unown_name(owndstB); - } - - g_main_loop_quit(test->loop); -} - -static void -check_not_migrated(TestServer *s, TestServer *d) -{ - assert(!s->save_called); - assert(!s->load_called); - assert(!d->save_called); - assert(!d->load_called); -} - -static void -check_migrated(TestServer *s, TestServer *d) -{ - assert(s->save_called); - assert(!s->load_called); - assert(!d->save_called); - assert(d->load_called); -} - -static void -test_dbus_vmstate_without_list(void) -{ - Test test = { 0, }; - - test_dbus_vmstate(&test); - - check_migrated(&test.srcA, &test.dstA); - check_migrated(&test.srcB, &test.dstB); -} - -static void -test_dbus_vmstate_with_list(void) -{ - Test test = { .id_list = "idA,idB" }; - - test_dbus_vmstate(&test); - - check_migrated(&test.srcA, &test.dstA); - check_migrated(&test.srcB, &test.dstB); -} - -static void -test_dbus_vmstate_only_a(void) -{ - Test test = { .id_list = "idA" }; - - test_dbus_vmstate(&test); - - check_migrated(&test.srcA, &test.dstA); - check_not_migrated(&test.srcB, &test.dstB); -} - -static void -test_dbus_vmstate_missing_src(void) -{ - Test test = { .id_list = "idA,idC", .migrate_fail = true }; - - /* run in subprocess to silence QEMU error reporting */ - if (g_test_subprocess()) { - test_dbus_vmstate(&test); - check_not_migrated(&test.srcA, &test.dstA); - check_not_migrated(&test.srcB, &test.dstB); - return; - } - - g_test_trap_subprocess(NULL, 0, 0); - g_test_trap_assert_passed(); -} - -static void -test_dbus_vmstate_missing_dst(void) -{ - Test test = { .id_list = "idA,idB", - .without_dst_b = true, - .migrate_fail = true }; - - /* run in subprocess to silence QEMU error reporting */ - if (g_test_subprocess()) { - test_dbus_vmstate(&test); - assert(test.srcA.save_called); - assert(test.srcB.save_called); - assert(!test.dstB.save_called); - return; - } - - g_test_trap_subprocess(NULL, 0, 0); - g_test_trap_assert_passed(); -} - -int -main(int argc, char **argv) -{ - GError *err = NULL; - g_autofree char *dbus_daemon = NULL; - int ret; - - dbus_daemon = g_build_filename(G_STRINGIFY(SRCDIR), - "tests", - "dbus-vmstate-daemon.sh", - NULL); - g_setenv("G_TEST_DBUS_DAEMON", dbus_daemon, true); - - g_test_init(&argc, &argv, NULL); - - workdir = g_dir_make_tmp("dbus-vmstate-test-XXXXXX", &err); - if (!workdir) { - g_error("Unable to create temporary dir: %s\n", err->message); - exit(1); - } - - g_setenv("DBUS_VMSTATE_TEST_TMPDIR", workdir, true); - - qtest_add_func("/dbus-vmstate/without-list", - test_dbus_vmstate_without_list); - qtest_add_func("/dbus-vmstate/with-list", - test_dbus_vmstate_with_list); - qtest_add_func("/dbus-vmstate/only-a", - test_dbus_vmstate_only_a); - qtest_add_func("/dbus-vmstate/missing-src", - test_dbus_vmstate_missing_src); - qtest_add_func("/dbus-vmstate/missing-dst", - test_dbus_vmstate_missing_dst); - - ret = g_test_run(); - - rmdir(workdir); - g_free(workdir); - - return ret; -} diff --git a/tests/dbus-vmstate1.xml b/tests/dbus-vmstate1.xml deleted file mode 100644 index cc8563be4c..0000000000 --- a/tests/dbus-vmstate1.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c deleted file mode 100644 index 04f22903b0..0000000000 --- a/tests/device-introspect-test.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Device introspection test cases - * - * Copyright (c) 2015 Red Hat Inc. - * - * Authors: - * Markus Armbruster , - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -/* - * Covers QMP device-list-properties and HMP device_add help. We - * currently don't check that their output makes sense, only that QEMU - * survives. Useful since we've had an astounding number of crash - * bugs around here. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "libqtest.h" - -const char common_args[] = "-nodefaults -machine none"; - -static QList *qom_list_types(QTestState * qts, const char *implements, - bool abstract) -{ - QDict *resp; - QList *ret; - QDict *args = qdict_new(); - - qdict_put_bool(args, "abstract", abstract); - if (implements) { - qdict_put_str(args, "implements", implements); - } - resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }", - args); - g_assert(qdict_haskey(resp, "return")); - ret = qdict_get_qlist(resp, "return"); - qobject_ref(ret); - qobject_unref(resp); - return ret; -} - -/* Build a name -> ObjectTypeInfo index from a ObjectTypeInfo list */ -static QDict *qom_type_index(QList *types) -{ - QDict *index = qdict_new(); - QListEntry *e; - - QLIST_FOREACH_ENTRY(types, e) { - QDict *d = qobject_to(QDict, qlist_entry_obj(e)); - const char *name = qdict_get_str(d, "name"); - qobject_ref(d); - qdict_put(index, name, d); - } - return index; -} - -/* Check if @parent is present in the parent chain of @type */ -static bool qom_has_parent(QDict *index, const char *type, const char *parent) -{ - while (type) { - QDict *d = qdict_get_qdict(index, type); - const char *p = d && qdict_haskey(d, "parent") ? - qdict_get_str(d, "parent") : - NULL; - - if (!strcmp(type, parent)) { - return true; - } - - type = p; - } - - return false; -} - -/* Find an entry on a list returned by qom-list-types */ -static QDict *type_list_find(QList *types, const char *name) -{ - QListEntry *e; - - QLIST_FOREACH_ENTRY(types, e) { - QDict *d = qobject_to(QDict, qlist_entry_obj(e)); - const char *ename = qdict_get_str(d, "name"); - if (!strcmp(ename, name)) { - return d; - } - } - - return NULL; -} - -static QList *device_type_list(QTestState *qts, bool abstract) -{ - return qom_list_types(qts, "device", abstract); -} - -static void test_one_device(QTestState *qts, const char *type) -{ - QDict *resp; - char *help; - char *qom_tree_start, *qom_tree_end; - char *qtree_start, *qtree_end; - - g_test_message("Testing device '%s'", type); - - qom_tree_start = qtest_hmp(qts, "info qom-tree"); - qtree_start = qtest_hmp(qts, "info qtree"); - - resp = qtest_qmp(qts, "{'execute': 'device-list-properties'," - " 'arguments': {'typename': %s}}", - type); - qobject_unref(resp); - - help = qtest_hmp(qts, "device_add \"%s,help\"", type); - g_free(help); - - /* - * Some devices leave dangling pointers in QOM behind. - * "info qom-tree" or "info qtree" have a good chance at crashing then. - * Also make sure that the tree did not change. - */ - qom_tree_end = qtest_hmp(qts, "info qom-tree"); - g_assert_cmpstr(qom_tree_start, ==, qom_tree_end); - g_free(qom_tree_start); - g_free(qom_tree_end); - - qtree_end = qtest_hmp(qts, "info qtree"); - g_assert_cmpstr(qtree_start, ==, qtree_end); - g_free(qtree_start); - g_free(qtree_end); -} - -static void test_device_intro_list(void) -{ - QList *types; - char *help; - QTestState *qts; - - qts = qtest_init(common_args); - - types = device_type_list(qts, true); - qobject_unref(types); - - help = qtest_hmp(qts, "device_add help"); - g_free(help); - - qtest_quit(qts); -} - -/* - * Ensure all entries returned by qom-list-types implements= - * have as a parent. - */ -static void test_qom_list_parents(QTestState *qts, const char *parent) -{ - QList *types; - QListEntry *e; - QDict *index; - - types = qom_list_types(qts, parent, true); - index = qom_type_index(types); - - QLIST_FOREACH_ENTRY(types, e) { - QDict *d = qobject_to(QDict, qlist_entry_obj(e)); - const char *name = qdict_get_str(d, "name"); - - g_assert(qom_has_parent(index, name, parent)); - } - - qobject_unref(types); - qobject_unref(index); -} - -static void test_qom_list_fields(void) -{ - QList *all_types; - QList *non_abstract; - QListEntry *e; - QTestState *qts; - - qts = qtest_init(common_args); - - all_types = qom_list_types(qts, NULL, true); - non_abstract = qom_list_types(qts, NULL, false); - - QLIST_FOREACH_ENTRY(all_types, e) { - QDict *d = qobject_to(QDict, qlist_entry_obj(e)); - const char *name = qdict_get_str(d, "name"); - bool abstract = qdict_haskey(d, "abstract") ? - qdict_get_bool(d, "abstract") : - false; - bool expected_abstract = !type_list_find(non_abstract, name); - - g_assert(abstract == expected_abstract); - } - - test_qom_list_parents(qts, "object"); - test_qom_list_parents(qts, "device"); - test_qom_list_parents(qts, "sys-bus-device"); - - qobject_unref(all_types); - qobject_unref(non_abstract); - qtest_quit(qts); -} - -static void test_device_intro_none(void) -{ - QTestState *qts = qtest_init(common_args); - - test_one_device(qts, "nonexistent"); - qtest_quit(qts); -} - -static void test_device_intro_abstract(void) -{ - QTestState *qts = qtest_init(common_args); - - test_one_device(qts, "device"); - qtest_quit(qts); -} - -static void test_device_intro_concrete(const void *args) -{ - QList *types; - QListEntry *entry; - const char *type; - QTestState *qts; - - qts = qtest_init(args); - types = device_type_list(qts, false); - - QLIST_FOREACH_ENTRY(types, entry) { - type = qdict_get_try_str(qobject_to(QDict, qlist_entry_obj(entry)), - "name"); - g_assert(type); - test_one_device(qts, type); - } - - qobject_unref(types); - qtest_quit(qts); - g_free((void *)args); -} - -static void test_abstract_interfaces(void) -{ - QList *all_types; - QListEntry *e; - QDict *index; - QTestState *qts; - - qts = qtest_init(common_args); - - all_types = qom_list_types(qts, "interface", true); - index = qom_type_index(all_types); - - QLIST_FOREACH_ENTRY(all_types, e) { - QDict *d = qobject_to(QDict, qlist_entry_obj(e)); - const char *name = qdict_get_str(d, "name"); - - /* - * qom-list-types implements=interface returns all types - * that implement _any_ interface (not just interface - * types), so skip the ones that don't have "interface" - * on the parent type chain. - */ - if (!qom_has_parent(index, name, "interface")) { - /* Not an interface type */ - continue; - } - - g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract")); - } - - qobject_unref(all_types); - qobject_unref(index); - qtest_quit(qts); -} - -static void add_machine_test_case(const char *mname) -{ - char *path, *args; - - /* Ignore blacklisted machines */ - if (g_str_equal("xenfv", mname) || g_str_equal("xenpv", mname)) { - return; - } - - path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname); - args = g_strdup_printf("-M %s", mname); - qtest_add_data_func(path, args, test_device_intro_concrete); - g_free(path); - - path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname); - args = g_strdup_printf("-nodefaults -M %s", mname); - qtest_add_data_func(path, args, test_device_intro_concrete); - g_free(path); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qtest_add_func("device/introspect/list", test_device_intro_list); - qtest_add_func("device/introspect/list-fields", test_qom_list_fields); - qtest_add_func("device/introspect/none", test_device_intro_none); - qtest_add_func("device/introspect/abstract", test_device_intro_abstract); - qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces); - if (g_test_quick()) { - qtest_add_data_func("device/introspect/concrete/defaults/none", - g_strdup(common_args), test_device_intro_concrete); - } else { - qtest_cb_for_every_machine(add_machine_test_case, true); - } - - return g_test_run(); -} diff --git a/tests/device-plug-test.c b/tests/device-plug-test.c deleted file mode 100644 index 318e422d51..0000000000 --- a/tests/device-plug-test.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * QEMU device plug/unplug handling - * - * Copyright (C) 2019 Red Hat Inc. - * - * Authors: - * David Hildenbrand - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" - -static void device_del_start(QTestState *qtest, const char *id) -{ - qtest_qmp_send(qtest, - "{'execute': 'device_del', 'arguments': { 'id': %s } }", id); -} - -static void device_del_finish(QTestState *qtest) -{ - QDict *resp = qtest_qmp_receive(qtest); - - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); -} - -static void device_del_request(QTestState *qtest, const char *id) -{ - device_del_start(qtest, id); - device_del_finish(qtest); -} - -static void system_reset(QTestState *qtest) -{ - QDict *resp; - - resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); -} - -static void wait_device_deleted_event(QTestState *qtest, const char *id) -{ - QDict *resp, *data; - QString *qstr; - - /* - * Other devices might get removed along with the removed device. Skip - * these. The device of interest will be the last one. - */ - for (;;) { - resp = qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED"); - data = qdict_get_qdict(resp, "data"); - if (!data || !qdict_get(data, "device")) { - qobject_unref(resp); - continue; - } - qstr = qobject_to(QString, qdict_get(data, "device")); - g_assert(qstr); - if (!strcmp(qstring_get_str(qstr), id)) { - qobject_unref(resp); - break; - } - qobject_unref(resp); - } -} - -static void test_pci_unplug_request(void) -{ - QTestState *qtest = qtest_initf("-device virtio-mouse-pci,id=dev0"); - - /* - * Request device removal. As the guest is not running, the request won't - * be processed. However during system reset, the removal will be - * handled, removing the device. - */ - device_del_request(qtest, "dev0"); - system_reset(qtest); - wait_device_deleted_event(qtest, "dev0"); - - qtest_quit(qtest); -} - -static void test_ccw_unplug(void) -{ - QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0"); - - /* - * The DEVICE_DELETED events will be sent before the command - * completes. - */ - device_del_start(qtest, "dev0"); - wait_device_deleted_event(qtest, "dev0"); - device_del_finish(qtest); - - qtest_quit(qtest); -} - -static void test_spapr_cpu_unplug_request(void) -{ - QTestState *qtest; - - qtest = qtest_initf("-cpu power9_v2.0 -smp 1,maxcpus=2 " - "-device power9_v2.0-spapr-cpu-core,core-id=1,id=dev0"); - - /* similar to test_pci_unplug_request */ - device_del_request(qtest, "dev0"); - system_reset(qtest); - wait_device_deleted_event(qtest, "dev0"); - - qtest_quit(qtest); -} - -static void test_spapr_memory_unplug_request(void) -{ - QTestState *qtest; - - qtest = qtest_initf("-m 256M,slots=1,maxmem=768M " - "-object memory-backend-ram,id=mem0,size=512M " - "-device pc-dimm,id=dev0,memdev=mem0"); - - /* similar to test_pci_unplug_request */ - device_del_request(qtest, "dev0"); - system_reset(qtest); - wait_device_deleted_event(qtest, "dev0"); - - qtest_quit(qtest); -} - -static void test_spapr_phb_unplug_request(void) -{ - QTestState *qtest; - - qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0"); - - /* similar to test_pci_unplug_request */ - device_del_request(qtest, "dev0"); - system_reset(qtest); - wait_device_deleted_event(qtest, "dev0"); - - qtest_quit(qtest); -} - -int main(int argc, char **argv) -{ - const char *arch = qtest_get_arch(); - - g_test_init(&argc, &argv, NULL); - - /* - * We need a system that will process unplug requests during system resets - * and does not do PCI surprise removal. This holds for x86 ACPI, - * s390x and spapr. - */ - qtest_add_func("/device-plug/pci-unplug-request", - test_pci_unplug_request); - - if (!strcmp(arch, "s390x")) { - qtest_add_func("/device-plug/ccw-unplug", - test_ccw_unplug); - } - - if (!strcmp(arch, "ppc64")) { - qtest_add_func("/device-plug/spapr-cpu-unplug-request", - test_spapr_cpu_unplug_request); - qtest_add_func("/device-plug/spapr-memory-unplug-request", - test_spapr_memory_unplug_request); - qtest_add_func("/device-plug/spapr-phb-unplug-request", - test_spapr_phb_unplug_request); - } - - return g_test_run(); -} diff --git a/tests/display-vga-test.c b/tests/display-vga-test.c deleted file mode 100644 index ace3bb28e0..0000000000 --- a/tests/display-vga-test.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * QTest testcase for vga cards - * - * Copyright (c) 2014 Red Hat, Inc - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" - -static void pci_cirrus(void) -{ - qtest_start("-vga none -device cirrus-vga"); - qtest_end(); -} - -static void pci_stdvga(void) -{ - qtest_start("-vga none -device VGA"); - qtest_end(); -} - -static void pci_secondary(void) -{ - qtest_start("-vga none -device secondary-vga"); - qtest_end(); -} - -static void pci_multihead(void) -{ - qtest_start("-vga none -device VGA -device secondary-vga"); - qtest_end(); -} - -static void pci_virtio_gpu(void) -{ - qtest_start("-vga none -device virtio-gpu-pci"); - qtest_end(); -} - -static void pci_virtio_vga(void) -{ - qtest_start("-vga none -device virtio-vga"); - qtest_end(); -} - -int main(int argc, char **argv) -{ - const char *arch = qtest_get_arch(); - - g_test_init(&argc, &argv, NULL); - - if (strcmp(arch, "alpha") == 0 || strcmp(arch, "i386") == 0 || - strcmp(arch, "mips") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("/display/pci/cirrus", pci_cirrus); - } - qtest_add_func("/display/pci/stdvga", pci_stdvga); - qtest_add_func("/display/pci/secondary", pci_secondary); - qtest_add_func("/display/pci/multihead", pci_multihead); - qtest_add_func("/display/pci/virtio-gpu", pci_virtio_gpu); - if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") || - g_str_equal(arch, "hppa") || g_str_equal(arch, "ppc64")) { - qtest_add_func("/display/pci/virtio-vga", pci_virtio_vga); - } - - return g_test_run(); -} diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c deleted file mode 100644 index 5f8839b232..0000000000 --- a/tests/drive_del-test.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * blockdev.c test cases - * - * Copyright (C) 2013-2014 Red Hat Inc. - * - * Authors: - * Stefan Hajnoczi - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "libqos/virtio.h" -#include "qapi/qmp/qdict.h" - -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(q, ...) qobject_unref(qtest_qmp(q, __VA_ARGS__)) - -static void drive_add(QTestState *qts) -{ - char *resp = qtest_hmp(qts, "drive_add 0 if=none,id=drive0"); - - g_assert_cmpstr(resp, ==, "OK\r\n"); - g_free(resp); -} - -static void drive_del(QTestState *qts) -{ - char *resp = qtest_hmp(qts, "drive_del drive0"); - - g_assert_cmpstr(resp, ==, ""); - g_free(resp); -} - -static void device_del(QTestState *qts) -{ - QDict *response; - - /* Complication: ignore DEVICE_DELETED event */ - qmp_discard_response(qts, "{'execute': 'device_del'," - " 'arguments': { 'id': 'dev0' } }"); - response = qtest_qmp_receive(qts); - g_assert(response); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); -} - -static void test_drive_without_dev(void) -{ - QTestState *qts; - - /* Start with an empty drive */ - qts = qtest_init("-drive if=none,id=drive0"); - - /* Delete the drive */ - drive_del(qts); - - /* Ensure re-adding the drive works - there should be no duplicate ID error - * because the old drive must be gone. - */ - drive_add(qts); - - qtest_quit(qts); -} - -/* - * qvirtio_get_dev_type: - * Returns: the preferred virtio bus/device type for the current architecture. - * TODO: delete this - */ -static const char *qvirtio_get_dev_type(void) -{ - const char *arch = qtest_get_arch(); - - if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { - return "device"; /* for virtio-mmio */ - } else if (g_str_equal(arch, "s390x")) { - return "ccw"; - } else { - return "pci"; - } -} - -static void test_after_failed_device_add(void) -{ - char driver[32]; - QDict *response; - QTestState *qts; - - snprintf(driver, sizeof(driver), "virtio-blk-%s", - qvirtio_get_dev_type()); - - qts = qtest_init("-drive if=none,id=drive0"); - - /* Make device_add fail. If this leaks the virtio-blk device then a - * reference to drive0 will also be held (via qdev properties). - */ - response = qtest_qmp(qts, "{'execute': 'device_add'," - " 'arguments': {" - " 'driver': %s," - " 'drive': 'drive0'" - "}}", driver); - g_assert(response); - qmp_assert_error_class(response, "GenericError"); - - /* Delete the drive */ - drive_del(qts); - - /* Try to re-add the drive. This fails with duplicate IDs if a leaked - * virtio-blk device exists that holds a reference to the old drive0. - */ - drive_add(qts); - - qtest_quit(qts); -} - -static void test_drive_del_device_del(void) -{ - QTestState *qts; - - /* Start with a drive used by a device that unplugs instantaneously */ - qts = qtest_initf("-drive if=none,id=drive0,file=null-co://," - "file.read-zeroes=on,format=raw" - " -device virtio-scsi-%s" - " -device scsi-hd,drive=drive0,id=dev0", - qvirtio_get_dev_type()); - - /* - * Delete the drive, and then the device - * Doing it in this order takes notoriously tricky special paths - */ - drive_del(qts); - device_del(qts); - - qtest_quit(qts); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/drive_del/without-dev", test_drive_without_dev); - - if (qvirtio_get_dev_type() != NULL) { - qtest_add_func("/drive_del/after_failed_device_add", - test_after_failed_device_add); - qtest_add_func("/blockdev/drive_del_device_del", - test_drive_del_device_del); - } - - return g_test_run(); -} diff --git a/tests/ds1338-test.c b/tests/ds1338-test.c deleted file mode 100644 index f6ade9a050..0000000000 --- a/tests/ds1338-test.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * QTest testcase for the DS1338 RTC - * - * Copyright (c) 2013 Jean-Christophe Dubois - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "libqos/i2c.h" - -#define DS1338_ADDR 0x68 - -static inline uint8_t bcd2bin(uint8_t x) -{ - return ((x) & 0x0f) + ((x) >> 4) * 10; -} - -static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) -{ - QI2CDevice *i2cdev = (QI2CDevice *)obj; - - uint8_t resp[7]; - time_t now = time(NULL); - struct tm *tm_ptr = gmtime(&now); - - i2c_read_block(i2cdev, 0, resp, sizeof(resp)); - - /* check retrieved time againt local time */ - g_assert_cmpuint(bcd2bin(resp[4]), == , tm_ptr->tm_mday); - g_assert_cmpuint(bcd2bin(resp[5]), == , 1 + tm_ptr->tm_mon); - g_assert_cmpuint(2000 + bcd2bin(resp[6]), == , 1900 + tm_ptr->tm_year); -} - -static void ds1338_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "address=0x68" - }; - add_qi2c_address(&opts, &(QI2CAddress) { DS1338_ADDR }); - - qos_node_create_driver("ds1338", i2c_device_create); - qos_node_consumes("ds1338", "i2c-bus", &opts); - qos_add_test("tx-rx", "ds1338", send_and_receive, NULL); -} -libqos_init(ds1338_register_nodes); diff --git a/tests/e1000-test.c b/tests/e1000-test.c deleted file mode 100644 index c387984ef6..0000000000 --- a/tests/e1000-test.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * QTest testcase for e1000 NIC - * - * Copyright (c) 2013-2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -typedef struct QE1000 QE1000; - -struct QE1000 { - QOSGraphObject obj; - QPCIDevice dev; -}; - -static const char *models[] = { - "e1000", - "e1000-82540em", - "e1000-82544gc", - "e1000-82545em", -}; - -static void *e1000_get_driver(void *obj, const char *interface) -{ - QE1000 *e1000 = obj; - - if (!g_strcmp0(interface, "pci-device")) { - return &e1000->dev; - } - - fprintf(stderr, "%s not present in e1000e\n", interface); - g_assert_not_reached(); -} - -static void *e1000_create(void *pci_bus, QGuestAllocator *alloc, void *addr) -{ - QE1000 *e1000 = g_new0(QE1000, 1); - QPCIBus *bus = pci_bus; - - qpci_device_init(&e1000->dev, bus, addr); - e1000->obj.get_driver = e1000_get_driver; - - return &e1000->obj; -} - -static void e1000_register_nodes(void) -{ - int i; - QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0", - }; - add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); - - for (i = 0; i < ARRAY_SIZE(models); i++) { - qos_node_create_driver(models[i], e1000_create); - qos_node_consumes(models[i], "pci-bus", &opts); - qos_node_produces(models[i], "pci-device"); - } -} - -libqos_init(e1000_register_nodes); diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c deleted file mode 100644 index 1a232a663a..0000000000 --- a/tests/e1000e-test.c +++ /dev/null @@ -1,279 +0,0 @@ - /* - * QTest testcase for e1000e NIC - * - * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman - * Leonid Bloch - * Yan Vugenfirer - * - * 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 . - */ - - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "libqtest-single.h" -#include "qemu-common.h" -#include "libqos/pci-pc.h" -#include "qemu/sockets.h" -#include "qemu/iov.h" -#include "qemu/module.h" -#include "qemu/bitops.h" -#include "libqos/malloc.h" -#include "libqos/e1000e.h" - -static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) -{ - struct { - uint64_t buffer_addr; - union { - uint32_t data; - struct { - uint16_t length; - uint8_t cso; - uint8_t cmd; - } flags; - } lower; - union { - uint32_t data; - struct { - uint8_t status; - uint8_t css; - uint16_t special; - } fields; - } upper; - } descr; - - static const uint32_t dtyp_data = BIT(20); - static const uint32_t dtyp_ext = BIT(29); - static const uint32_t dcmd_rs = BIT(27); - static const uint32_t dcmd_eop = BIT(24); - static const uint32_t dsta_dd = BIT(0); - static const int data_len = 64; - char buffer[64]; - int ret; - uint32_t recv_len; - - /* Prepare test data buffer */ - uint64_t data = guest_alloc(alloc, data_len); - memwrite(data, "TEST", 5); - - /* Prepare TX descriptor */ - memset(&descr, 0, sizeof(descr)); - descr.buffer_addr = cpu_to_le64(data); - descr.lower.data = cpu_to_le32(dcmd_rs | - dcmd_eop | - dtyp_ext | - dtyp_data | - data_len); - - /* Put descriptor to the ring */ - e1000e_tx_ring_push(d, &descr); - - /* Wait for TX WB interrupt */ - e1000e_wait_isr(d, E1000E_TX0_MSG_ID); - - /* Check DD bit */ - g_assert_cmphex(le32_to_cpu(descr.upper.data) & dsta_dd, ==, dsta_dd); - - /* Check data sent to the backend */ - ret = qemu_recv(test_sockets[0], &recv_len, sizeof(recv_len), 0); - g_assert_cmpint(ret, == , sizeof(recv_len)); - qemu_recv(test_sockets[0], buffer, 64, 0); - g_assert_cmpstr(buffer, == , "TEST"); - - /* Free test data buffer */ - guest_free(alloc, data); -} - -static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) -{ - union { - struct { - uint64_t buffer_addr; - uint64_t reserved; - } read; - struct { - struct { - uint32_t mrq; - union { - uint32_t rss; - struct { - uint16_t ip_id; - uint16_t csum; - } csum_ip; - } hi_dword; - } lower; - struct { - uint32_t status_error; - uint16_t length; - uint16_t vlan; - } upper; - } wb; - } descr; - - static const uint32_t esta_dd = BIT(0); - - char test[] = "TEST"; - int len = htonl(sizeof(test)); - struct iovec iov[] = { - { - .iov_base = &len, - .iov_len = sizeof(len), - },{ - .iov_base = test, - .iov_len = sizeof(test), - }, - }; - - static const int data_len = 64; - char buffer[64]; - int ret; - - /* Send a dummy packet to device's socket*/ - ret = iov_send(test_sockets[0], iov, 2, 0, sizeof(len) + sizeof(test)); - g_assert_cmpint(ret, == , sizeof(test) + sizeof(len)); - - /* Prepare test data buffer */ - uint64_t data = guest_alloc(alloc, data_len); - - /* Prepare RX descriptor */ - memset(&descr, 0, sizeof(descr)); - descr.read.buffer_addr = cpu_to_le64(data); - - /* Put descriptor to the ring */ - e1000e_rx_ring_push(d, &descr); - - /* Wait for TX WB interrupt */ - e1000e_wait_isr(d, E1000E_RX0_MSG_ID); - - /* Check DD bit */ - g_assert_cmphex(le32_to_cpu(descr.wb.upper.status_error) & - esta_dd, ==, esta_dd); - - /* Check data sent to the backend */ - memread(data, buffer, sizeof(buffer)); - g_assert_cmpstr(buffer, == , "TEST"); - - /* Free test data buffer */ - guest_free(alloc, data); -} - -static void test_e1000e_init(void *obj, void *data, QGuestAllocator * alloc) -{ - /* init does nothing */ -} - -static void test_e1000e_tx(void *obj, void *data, QGuestAllocator * alloc) -{ - QE1000E_PCI *e1000e = obj; - QE1000E *d = &e1000e->e1000e; - QOSGraphObject *e_object = obj; - QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); - - /* FIXME: add spapr support */ - if (qpci_check_buggy_msi(dev)) { - return; - } - - e1000e_send_verify(d, data, alloc); -} - -static void test_e1000e_rx(void *obj, void *data, QGuestAllocator * alloc) -{ - QE1000E_PCI *e1000e = obj; - QE1000E *d = &e1000e->e1000e; - QOSGraphObject *e_object = obj; - QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); - - /* FIXME: add spapr support */ - if (qpci_check_buggy_msi(dev)) { - return; - } - - e1000e_receive_verify(d, data, alloc); -} - -static void test_e1000e_multiple_transfers(void *obj, void *data, - QGuestAllocator *alloc) -{ - static const long iterations = 4 * 1024; - long i; - - QE1000E_PCI *e1000e = obj; - QE1000E *d = &e1000e->e1000e; - QOSGraphObject *e_object = obj; - QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); - - /* FIXME: add spapr support */ - if (qpci_check_buggy_msi(dev)) { - return; - } - - for (i = 0; i < iterations; i++) { - e1000e_send_verify(d, data, alloc); - e1000e_receive_verify(d, data, alloc); - } - -} - -static void test_e1000e_hotplug(void *obj, void *data, QGuestAllocator * alloc) -{ - QTestState *qts = global_qtest; /* TODO: get rid of global_qtest here */ - - qtest_qmp_device_add(qts, "e1000e", "e1000e_net", "{'addr': '0x06'}"); - qpci_unplug_acpi_device_test(qts, "e1000e_net", 0x06); -} - -static void data_test_clear(void *sockets) -{ - int *test_sockets = sockets; - - close(test_sockets[0]); - qos_invalidate_command_line(); - close(test_sockets[1]); - g_free(test_sockets); -} - -static void *data_test_init(GString *cmd_line, void *arg) -{ - int *test_sockets = g_new(int, 2); - int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets); - g_assert_cmpint(ret, != , -1); - - g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", - test_sockets[1]); - - g_test_queue_destroy(data_test_clear, test_sockets); - return test_sockets; -} - -static void register_e1000e_test(void) -{ - QOSGraphTestOptions opts = { - .before = data_test_init, - }; - - qos_add_test("init", "e1000e", test_e1000e_init, &opts); - qos_add_test("tx", "e1000e", test_e1000e_tx, &opts); - qos_add_test("rx", "e1000e", test_e1000e_rx, &opts); - qos_add_test("multiple_transfers", "e1000e", - test_e1000e_multiple_transfers, &opts); - qos_add_test("hotplug", "e1000e", test_e1000e_hotplug, &opts); -} - -libqos_init(register_e1000e_test); diff --git a/tests/eepro100-test.c b/tests/eepro100-test.c deleted file mode 100644 index 8dbffff0b8..0000000000 --- a/tests/eepro100-test.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * QTest testcase for eepro100 NIC - * - * Copyright (c) 2013-2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -typedef struct QEEPRO100 QEEPRO100; - -struct QEEPRO100 { - QOSGraphObject obj; - QPCIDevice dev; -}; - -static const char *models[] = { - "i82550", - "i82551", - "i82557a", - "i82557b", - "i82557c", - "i82558a", - "i82558b", - "i82559a", - "i82559b", - "i82559c", - "i82559er", - "i82562", - "i82801", -}; - -static void *eepro100_get_driver(void *obj, const char *interface) -{ - QEEPRO100 *eepro100 = obj; - - if (!g_strcmp0(interface, "pci-device")) { - return &eepro100->dev; - } - - fprintf(stderr, "%s not present in eepro100\n", interface); - g_assert_not_reached(); -} - -static void *eepro100_create(void *pci_bus, QGuestAllocator *alloc, void *addr) -{ - QEEPRO100 *eepro100 = g_new0(QEEPRO100, 1); - QPCIBus *bus = pci_bus; - - qpci_device_init(&eepro100->dev, bus, addr); - eepro100->obj.get_driver = eepro100_get_driver; - - return &eepro100->obj; -} - -static void eepro100_register_nodes(void) -{ - int i; - QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0", - }; - - add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); - for (i = 0; i < ARRAY_SIZE(models); i++) { - qos_node_create_driver(models[i], eepro100_create); - qos_node_consumes(models[i], "pci-bus", &opts); - qos_node_produces(models[i], "pci-device"); - } -} - -libqos_init(eepro100_register_nodes); diff --git a/tests/endianness-test.c b/tests/endianness-test.c deleted file mode 100644 index 58527952a5..0000000000 --- a/tests/endianness-test.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * QTest testcase for ISA endianness - * - * Copyright Red Hat, Inc. 2012 - * - * Authors: - * Paolo Bonzini - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" - -#include "libqtest.h" -#include "qemu/bswap.h" - -typedef struct TestCase TestCase; -struct TestCase { - const char *arch; - const char *machine; - uint64_t isa_base; - bool bswap; - const char *superio; -}; - -static const TestCase test_cases[] = { - { "i386", "pc", -1 }, - { "mips", "mips", 0x14000000, .bswap = true }, - { "mips", "malta", 0x10000000, .bswap = true }, - { "mips64", "magnum", 0x90000000, .bswap = true }, - { "mips64", "pica61", 0x90000000, .bswap = true }, - { "mips64", "mips", 0x14000000, .bswap = true }, - { "mips64", "malta", 0x10000000, .bswap = true }, - { "mips64el", "fulong2e", 0x1fd00000 }, - { "ppc", "g3beige", 0xfe000000, .bswap = true, .superio = "i82378" }, - { "ppc", "prep", 0x80000000, .bswap = true }, - { "ppc", "bamboo", 0xe8000000, .bswap = true, .superio = "i82378" }, - { "ppc64", "mac99", 0xf2000000, .bswap = true, .superio = "i82378" }, - { "ppc64", "pseries", (1ULL << 45), .bswap = true, .superio = "i82378" }, - { "ppc64", "pseries-2.7", 0x10080000000ULL, - .bswap = true, .superio = "i82378" }, - { "sh4", "r2d", 0xfe240000, .superio = "i82378" }, - { "sh4eb", "r2d", 0xfe240000, .bswap = true, .superio = "i82378" }, - { "sparc64", "sun4u", 0x1fe02000000LL, .bswap = true }, - { "x86_64", "pc", -1 }, - {} -}; - -static uint8_t isa_inb(QTestState *qts, const TestCase *test, uint16_t addr) -{ - uint8_t value; - if (test->isa_base == -1) { - value = qtest_inb(qts, addr); - } else { - value = qtest_readb(qts, test->isa_base + addr); - } - return value; -} - -static uint16_t isa_inw(QTestState *qts, const TestCase *test, uint16_t addr) -{ - uint16_t value; - if (test->isa_base == -1) { - value = qtest_inw(qts, addr); - } else { - value = qtest_readw(qts, test->isa_base + addr); - } - return test->bswap ? bswap16(value) : value; -} - -static uint32_t isa_inl(QTestState *qts, const TestCase *test, uint16_t addr) -{ - uint32_t value; - if (test->isa_base == -1) { - value = qtest_inl(qts, addr); - } else { - value = qtest_readl(qts, test->isa_base + addr); - } - return test->bswap ? bswap32(value) : value; -} - -static void isa_outb(QTestState *qts, const TestCase *test, uint16_t addr, - uint8_t value) -{ - if (test->isa_base == -1) { - qtest_outb(qts, addr, value); - } else { - qtest_writeb(qts, test->isa_base + addr, 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) { - qtest_outw(qts, addr, value); - } else { - qtest_writew(qts, test->isa_base + addr, 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) { - qtest_outl(qts, addr, value); - } else { - qtest_writel(qts, test->isa_base + addr, value); - } -} - - -static void test_endianness(gconstpointer data) -{ - const TestCase *test = data; - 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; - 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; - 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) -{ - const char *arch = qtest_get_arch(); - int i; - - g_test_init(&argc, &argv, NULL); - - for (i = 0; test_cases[i].arch; i++) { - gchar *path; - if (strcmp(test_cases[i].arch, arch) != 0) { - continue; - } - path = g_strdup_printf("endianness/%s", - test_cases[i].machine); - qtest_add_data_func(path, &test_cases[i], test_endianness); - g_free(path); - - path = g_strdup_printf("endianness/split/%s", - test_cases[i].machine); - qtest_add_data_func(path, &test_cases[i], test_endianness_split); - g_free(path); - - path = g_strdup_printf("endianness/combine/%s", - test_cases[i].machine); - qtest_add_data_func(path, &test_cases[i], test_endianness_combine); - g_free(path); - } - - return g_test_run(); -} diff --git a/tests/es1370-test.c b/tests/es1370-test.c deleted file mode 100644 index adccdac1be..0000000000 --- a/tests/es1370-test.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * QTest testcase for ES1370 - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -typedef struct QES1370 QES1370; - -struct QES1370 { - QOSGraphObject obj; - QPCIDevice dev; -}; - -static void *es1370_get_driver(void *obj, const char *interface) -{ - QES1370 *es1370 = obj; - - if (!g_strcmp0(interface, "pci-device")) { - return &es1370->dev; - } - - fprintf(stderr, "%s not present in e1000e\n", interface); - g_assert_not_reached(); -} - -static void *es1370_create(void *pci_bus, QGuestAllocator *alloc, void *addr) -{ - QES1370 *es1370 = g_new0(QES1370, 1); - QPCIBus *bus = pci_bus; - - qpci_device_init(&es1370->dev, bus, addr); - es1370->obj.get_driver = es1370_get_driver; - - return &es1370->obj; -} - -static void es1370_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0", - }; - add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); - - qos_node_create_driver("ES1370", es1370_create); - qos_node_consumes("ES1370", "pci-bus", &opts); - qos_node_produces("ES1370", "pci-device"); -} - -libqos_init(es1370_register_nodes); diff --git a/tests/fdc-test.c b/tests/fdc-test.c deleted file mode 100644 index 26b69f7c5c..0000000000 --- a/tests/fdc-test.c +++ /dev/null @@ -1,587 +0,0 @@ -/* - * Floppy test cases. - * - * Copyright (c) 2012 Kevin Wolf - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - - -#include "libqtest-single.h" -#include "qapi/qmp/qdict.h" -#include "qemu-common.h" - -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) - -#define TEST_IMAGE_SIZE 1440 * 1024 - -#define FLOPPY_BASE 0x3f0 -#define FLOPPY_IRQ 6 - -enum { - reg_sra = 0x0, - reg_srb = 0x1, - reg_dor = 0x2, - reg_msr = 0x4, - reg_dsr = 0x4, - reg_fifo = 0x5, - reg_dir = 0x7, -}; - -enum { - CMD_SENSE_INT = 0x08, - CMD_READ_ID = 0x0a, - CMD_SEEK = 0x0f, - CMD_VERIFY = 0x16, - CMD_READ = 0xe6, - CMD_RELATIVE_SEEK_OUT = 0x8f, - CMD_RELATIVE_SEEK_IN = 0xcf, -}; - -enum { - BUSY = 0x10, - NONDMA = 0x20, - RQM = 0x80, - DIO = 0x40, - - DSKCHG = 0x80, -}; - -static char test_image[] = "/tmp/qtest.XXXXXX"; - -#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) -#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) - -static uint8_t base = 0x70; - -enum { - CMOS_FLOPPY = 0x10, -}; - -static void floppy_send(uint8_t byte) -{ - uint8_t msr; - - msr = inb(FLOPPY_BASE + reg_msr); - assert_bit_set(msr, RQM); - assert_bit_clear(msr, DIO); - - outb(FLOPPY_BASE + reg_fifo, byte); -} - -static uint8_t floppy_recv(void) -{ - uint8_t msr; - - msr = inb(FLOPPY_BASE + reg_msr); - assert_bit_set(msr, RQM | DIO); - - return inb(FLOPPY_BASE + reg_fifo); -} - -/* pcn: Present Cylinder Number */ -static void ack_irq(uint8_t *pcn) -{ - uint8_t ret; - - g_assert(get_irq(FLOPPY_IRQ)); - floppy_send(CMD_SENSE_INT); - floppy_recv(); - - ret = floppy_recv(); - if (pcn != NULL) { - *pcn = ret; - } - - g_assert(!get_irq(FLOPPY_IRQ)); -} - -static uint8_t send_read_command(uint8_t cmd) -{ - uint8_t drive = 0; - uint8_t head = 0; - uint8_t cyl = 0; - uint8_t sect_addr = 1; - uint8_t sect_size = 2; - uint8_t eot = 1; - uint8_t gap = 0x1b; - uint8_t gpl = 0xff; - - uint8_t msr = 0; - uint8_t st0; - - uint8_t ret = 0; - - floppy_send(cmd); - floppy_send(head << 2 | drive); - g_assert(!get_irq(FLOPPY_IRQ)); - floppy_send(cyl); - floppy_send(head); - floppy_send(sect_addr); - floppy_send(sect_size); - floppy_send(eot); - floppy_send(gap); - floppy_send(gpl); - - uint8_t i = 0; - uint8_t n = 2; - for (; i < n; i++) { - msr = inb(FLOPPY_BASE + reg_msr); - if (msr == 0xd0) { - break; - } - sleep(1); - } - - if (i >= n) { - return 1; - } - - st0 = floppy_recv(); - if (st0 != 0x40) { - ret = 1; - } - - floppy_recv(); - floppy_recv(); - floppy_recv(); - floppy_recv(); - floppy_recv(); - floppy_recv(); - - return ret; -} - -static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0) -{ - uint8_t drive = 0; - uint8_t head = 0; - uint8_t cyl = 0; - uint8_t sect_addr = 1; - uint8_t sect_size = 2; - uint8_t eot = nb_sect; - uint8_t gap = 0x1b; - uint8_t gpl = 0xff; - - uint8_t msr = 0; - uint8_t st0; - - uint8_t ret = 0; - - floppy_send(CMD_READ); - floppy_send(head << 2 | drive); - g_assert(!get_irq(FLOPPY_IRQ)); - floppy_send(cyl); - floppy_send(head); - floppy_send(sect_addr); - floppy_send(sect_size); - floppy_send(eot); - floppy_send(gap); - floppy_send(gpl); - - uint16_t i = 0; - uint8_t n = 2; - for (; i < n; i++) { - msr = inb(FLOPPY_BASE + reg_msr); - if (msr == (BUSY | NONDMA | DIO | RQM)) { - break; - } - sleep(1); - } - - if (i >= n) { - return 1; - } - - /* Non-DMA mode */ - for (i = 0; i < 512 * 2 * nb_sect; i++) { - msr = inb(FLOPPY_BASE + reg_msr); - assert_bit_set(msr, BUSY | RQM | DIO); - inb(FLOPPY_BASE + reg_fifo); - } - - msr = inb(FLOPPY_BASE + reg_msr); - assert_bit_set(msr, BUSY | RQM | DIO); - g_assert(get_irq(FLOPPY_IRQ)); - - st0 = floppy_recv(); - if (st0 != expected_st0) { - ret = 1; - } - - floppy_recv(); - floppy_recv(); - floppy_recv(); - floppy_recv(); - floppy_recv(); - g_assert(get_irq(FLOPPY_IRQ)); - floppy_recv(); - - /* Check that we're back in command phase */ - msr = inb(FLOPPY_BASE + reg_msr); - assert_bit_clear(msr, BUSY | DIO); - assert_bit_set(msr, RQM); - g_assert(!get_irq(FLOPPY_IRQ)); - - return ret; -} - -static void send_seek(int cyl) -{ - int drive = 0; - int head = 0; - - floppy_send(CMD_SEEK); - floppy_send(head << 2 | drive); - g_assert(!get_irq(FLOPPY_IRQ)); - floppy_send(cyl); - ack_irq(NULL); -} - -static uint8_t cmos_read(uint8_t reg) -{ - outb(base + 0, reg); - return inb(base + 1); -} - -static void test_cmos(void) -{ - uint8_t cmos; - - cmos = cmos_read(CMOS_FLOPPY); - g_assert(cmos == 0x40 || cmos == 0x50); -} - -static void test_no_media_on_start(void) -{ - uint8_t dir; - - /* Media changed bit must be set all time after start if there is - * no media in drive. */ - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - send_seek(1); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); -} - -static void test_read_without_media(void) -{ - uint8_t ret; - - ret = send_read_command(CMD_READ); - g_assert(ret == 0); -} - -static void test_media_insert(void) -{ - uint8_t dir; - - /* Insert media in drive. DSKCHK should not be reset until a step pulse - * is sent. */ - qmp_discard_response("{'execute':'blockdev-change-medium', 'arguments':{" - " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}", - test_image); - - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - - send_seek(0); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - - /* Step to next track should clear DSKCHG bit. */ - send_seek(1); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_clear(dir, DSKCHG); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_clear(dir, DSKCHG); -} - -static void test_media_change(void) -{ - uint8_t dir; - - test_media_insert(); - - /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't - * reset the bit. */ - qmp_discard_response("{'execute':'eject', 'arguments':{" - " 'id':'floppy0' }}"); - - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - - send_seek(0); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - - send_seek(1); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); - dir = inb(FLOPPY_BASE + reg_dir); - assert_bit_set(dir, DSKCHG); -} - -static void test_sense_interrupt(void) -{ - int drive = 0; - int head = 0; - int cyl = 0; - int ret = 0; - - floppy_send(CMD_SENSE_INT); - ret = floppy_recv(); - g_assert(ret == 0x80); - - floppy_send(CMD_SEEK); - floppy_send(head << 2 | drive); - g_assert(!get_irq(FLOPPY_IRQ)); - floppy_send(cyl); - - floppy_send(CMD_SENSE_INT); - ret = floppy_recv(); - g_assert(ret == 0x20); - floppy_recv(); -} - -static void test_relative_seek(void) -{ - uint8_t drive = 0; - uint8_t head = 0; - uint8_t cyl = 1; - uint8_t pcn; - - /* Send seek to track 0 */ - send_seek(0); - - /* Send relative seek to increase track by 1 */ - floppy_send(CMD_RELATIVE_SEEK_IN); - floppy_send(head << 2 | drive); - g_assert(!get_irq(FLOPPY_IRQ)); - floppy_send(cyl); - - ack_irq(&pcn); - g_assert(pcn == 1); - - /* Send relative seek to decrease track by 1 */ - floppy_send(CMD_RELATIVE_SEEK_OUT); - floppy_send(head << 2 | drive); - g_assert(!get_irq(FLOPPY_IRQ)); - floppy_send(cyl); - - ack_irq(&pcn); - g_assert(pcn == 0); -} - -static void test_read_id(void) -{ - uint8_t drive = 0; - uint8_t head = 0; - uint8_t cyl; - uint8_t st0; - uint8_t msr; - - /* Seek to track 0 and check with READ ID */ - send_seek(0); - - floppy_send(CMD_READ_ID); - g_assert(!get_irq(FLOPPY_IRQ)); - floppy_send(head << 2 | drive); - - msr = inb(FLOPPY_BASE + reg_msr); - if (!get_irq(FLOPPY_IRQ)) { - assert_bit_set(msr, BUSY); - assert_bit_clear(msr, RQM); - } - - while (!get_irq(FLOPPY_IRQ)) { - /* qemu involves a timer with READ ID... */ - clock_step(1000000000LL / 50); - } - - msr = inb(FLOPPY_BASE + reg_msr); - assert_bit_set(msr, BUSY | RQM | DIO); - - st0 = floppy_recv(); - floppy_recv(); - floppy_recv(); - cyl = floppy_recv(); - head = floppy_recv(); - floppy_recv(); - g_assert(get_irq(FLOPPY_IRQ)); - floppy_recv(); - g_assert(!get_irq(FLOPPY_IRQ)); - - g_assert_cmpint(cyl, ==, 0); - g_assert_cmpint(head, ==, 0); - g_assert_cmpint(st0, ==, head << 2); - - /* Seek to track 8 on head 1 and check with READ ID */ - head = 1; - cyl = 8; - - floppy_send(CMD_SEEK); - floppy_send(head << 2 | drive); - g_assert(!get_irq(FLOPPY_IRQ)); - floppy_send(cyl); - g_assert(get_irq(FLOPPY_IRQ)); - ack_irq(NULL); - - floppy_send(CMD_READ_ID); - g_assert(!get_irq(FLOPPY_IRQ)); - floppy_send(head << 2 | drive); - - msr = inb(FLOPPY_BASE + reg_msr); - if (!get_irq(FLOPPY_IRQ)) { - assert_bit_set(msr, BUSY); - assert_bit_clear(msr, RQM); - } - - while (!get_irq(FLOPPY_IRQ)) { - /* qemu involves a timer with READ ID... */ - clock_step(1000000000LL / 50); - } - - msr = inb(FLOPPY_BASE + reg_msr); - assert_bit_set(msr, BUSY | RQM | DIO); - - st0 = floppy_recv(); - floppy_recv(); - floppy_recv(); - cyl = floppy_recv(); - head = floppy_recv(); - floppy_recv(); - g_assert(get_irq(FLOPPY_IRQ)); - floppy_recv(); - g_assert(!get_irq(FLOPPY_IRQ)); - - g_assert_cmpint(cyl, ==, 8); - g_assert_cmpint(head, ==, 1); - g_assert_cmpint(st0, ==, head << 2); -} - -static void test_read_no_dma_1(void) -{ - uint8_t ret; - - outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); - send_seek(0); - ret = send_read_no_dma_command(1, 0x04); - g_assert(ret == 0); -} - -static void test_read_no_dma_18(void) -{ - uint8_t ret; - - outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); - send_seek(0); - ret = send_read_no_dma_command(18, 0x04); - g_assert(ret == 0); -} - -static void test_read_no_dma_19(void) -{ - uint8_t ret; - - outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); - send_seek(0); - ret = send_read_no_dma_command(19, 0x20); - g_assert(ret == 0); -} - -static void test_verify(void) -{ - uint8_t ret; - - ret = send_read_command(CMD_VERIFY); - g_assert(ret == 0); -} - -/* success if no crash or abort */ -static void fuzz_registers(void) -{ - unsigned int i; - - for (i = 0; i < 1000; i++) { - uint8_t reg, val; - - reg = (uint8_t)g_test_rand_int_range(0, 8); - val = (uint8_t)g_test_rand_int_range(0, 256); - - outb(FLOPPY_BASE + reg, val); - inb(FLOPPY_BASE + reg); - } -} - -int main(int argc, char **argv) -{ - int fd; - int ret; - - /* Create a temporary raw image */ - fd = mkstemp(test_image); - g_assert(fd >= 0); - ret = ftruncate(fd, TEST_IMAGE_SIZE); - g_assert(ret == 0); - close(fd); - - /* Run the tests */ - g_test_init(&argc, &argv, NULL); - - qtest_start("-device floppy,id=floppy0"); - qtest_irq_intercept_in(global_qtest, "ioapic"); - qtest_add_func("/fdc/cmos", test_cmos); - qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start); - qtest_add_func("/fdc/read_without_media", test_read_without_media); - qtest_add_func("/fdc/media_change", test_media_change); - qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt); - qtest_add_func("/fdc/relative_seek", test_relative_seek); - qtest_add_func("/fdc/read_id", test_read_id); - qtest_add_func("/fdc/verify", test_verify); - qtest_add_func("/fdc/media_insert", test_media_insert); - qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1); - qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18); - qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19); - qtest_add_func("/fdc/fuzz-registers", fuzz_registers); - - ret = g_test_run(); - - /* Cleanup */ - qtest_end(); - unlink(test_image); - - return ret; -} diff --git a/tests/fw_cfg-test.c b/tests/fw_cfg-test.c deleted file mode 100644 index 5dc807ba23..0000000000 --- a/tests/fw_cfg-test.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * qtest fw_cfg test case - * - * Copyright IBM, Corp. 2012-2013 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "libqtest.h" -#include "standard-headers/linux/qemu_fw_cfg.h" -#include "libqos/fw_cfg.h" -#include "qemu/bswap.h" - -static uint64_t ram_size = 128 << 20; -static uint16_t nb_cpus = 1; -static uint16_t max_cpus = 1; -static uint64_t nb_nodes = 0; -static uint16_t boot_menu = 0; - -static void test_fw_cfg_signature(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - char buf[5]; - - s = qtest_init(""); - fw_cfg = pc_fw_cfg_init(s); - - qfw_cfg_get(fw_cfg, FW_CFG_SIGNATURE, buf, 4); - buf[4] = 0; - - g_assert_cmpstr(buf, ==, "QEMU"); - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -static void test_fw_cfg_id(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - uint32_t id; - - s = qtest_init(""); - fw_cfg = pc_fw_cfg_init(s); - - id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); - g_assert((id == 1) || - (id == 3)); - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -static void test_fw_cfg_uuid(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - - uint8_t buf[16]; - static const uint8_t uuid[16] = { - 0x46, 0x00, 0xcb, 0x32, 0x38, 0xec, 0x4b, 0x2f, - 0x8a, 0xcb, 0x81, 0xc6, 0xea, 0x54, 0xf2, 0xd8, - }; - - s = qtest_init("-uuid 4600cb32-38ec-4b2f-8acb-81c6ea54f2d8"); - fw_cfg = pc_fw_cfg_init(s); - - qfw_cfg_get(fw_cfg, FW_CFG_UUID, buf, 16); - g_assert(memcmp(buf, uuid, sizeof(buf)) == 0); - - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); - -} - -static void test_fw_cfg_ram_size(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - - s = qtest_init(""); - fw_cfg = pc_fw_cfg_init(s); - - g_assert_cmpint(qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE), ==, ram_size); - - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -static void test_fw_cfg_nographic(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - - s = qtest_init(""); - fw_cfg = pc_fw_cfg_init(s); - - g_assert_cmpint(qfw_cfg_get_u16(fw_cfg, FW_CFG_NOGRAPHIC), ==, 0); - - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -static void test_fw_cfg_nb_cpus(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - - s = qtest_init(""); - fw_cfg = pc_fw_cfg_init(s); - - g_assert_cmpint(qfw_cfg_get_u16(fw_cfg, FW_CFG_NB_CPUS), ==, nb_cpus); - - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -static void test_fw_cfg_max_cpus(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - - s = qtest_init(""); - fw_cfg = pc_fw_cfg_init(s); - - g_assert_cmpint(qfw_cfg_get_u16(fw_cfg, FW_CFG_MAX_CPUS), ==, max_cpus); - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -static void test_fw_cfg_numa(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - uint64_t *cpu_mask; - uint64_t *node_mask; - - s = qtest_init(""); - fw_cfg = pc_fw_cfg_init(s); - - g_assert_cmpint(qfw_cfg_get_u64(fw_cfg, FW_CFG_NUMA), ==, nb_nodes); - - cpu_mask = g_new0(uint64_t, max_cpus); - node_mask = g_new0(uint64_t, nb_nodes); - - qfw_cfg_read_data(fw_cfg, cpu_mask, sizeof(uint64_t) * max_cpus); - qfw_cfg_read_data(fw_cfg, node_mask, sizeof(uint64_t) * nb_nodes); - - if (nb_nodes) { - g_assert(cpu_mask[0] & 0x01); - g_assert_cmpint(node_mask[0], ==, ram_size); - } - - g_free(node_mask); - g_free(cpu_mask); - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -static void test_fw_cfg_boot_menu(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - - s = qtest_init(""); - fw_cfg = pc_fw_cfg_init(s); - - g_assert_cmpint(qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_MENU), ==, boot_menu); - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -static void test_fw_cfg_reboot_timeout(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - uint32_t reboot_timeout = 0; - size_t filesize; - - s = qtest_init("-boot reboot-timeout=15"); - fw_cfg = pc_fw_cfg_init(s); - - filesize = qfw_cfg_get_file(fw_cfg, "etc/boot-fail-wait", - &reboot_timeout, sizeof(reboot_timeout)); - g_assert_cmpint(filesize, ==, sizeof(reboot_timeout)); - reboot_timeout = le32_to_cpu(reboot_timeout); - g_assert_cmpint(reboot_timeout, ==, 15); - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -static void test_fw_cfg_no_reboot_timeout(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - uint32_t reboot_timeout = 0; - size_t filesize; - - /* Special value -1 means "don't reboot" */ - s = qtest_init("-boot reboot-timeout=-1"); - fw_cfg = pc_fw_cfg_init(s); - - filesize = qfw_cfg_get_file(fw_cfg, "etc/boot-fail-wait", - &reboot_timeout, sizeof(reboot_timeout)); - g_assert_cmpint(filesize, ==, sizeof(reboot_timeout)); - reboot_timeout = le32_to_cpu(reboot_timeout); - g_assert_cmpint(reboot_timeout, ==, UINT32_MAX); - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -static void test_fw_cfg_splash_time(void) -{ - QFWCFG *fw_cfg; - QTestState *s; - uint16_t splash_time = 0; - size_t filesize; - - s = qtest_init("-boot splash-time=12"); - fw_cfg = pc_fw_cfg_init(s); - - filesize = qfw_cfg_get_file(fw_cfg, "etc/boot-menu-wait", - &splash_time, sizeof(splash_time)); - g_assert_cmpint(filesize, ==, sizeof(splash_time)); - splash_time = le16_to_cpu(splash_time); - g_assert_cmpint(splash_time, ==, 12); - pc_fw_cfg_uninit(fw_cfg); - qtest_quit(s); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qtest_add_func("fw_cfg/signature", test_fw_cfg_signature); - qtest_add_func("fw_cfg/id", test_fw_cfg_id); - qtest_add_func("fw_cfg/uuid", test_fw_cfg_uuid); - qtest_add_func("fw_cfg/ram_size", test_fw_cfg_ram_size); - qtest_add_func("fw_cfg/nographic", test_fw_cfg_nographic); - qtest_add_func("fw_cfg/nb_cpus", test_fw_cfg_nb_cpus); -#if 0 - qtest_add_func("fw_cfg/machine_id", test_fw_cfg_machine_id); - qtest_add_func("fw_cfg/kernel", test_fw_cfg_kernel); - qtest_add_func("fw_cfg/initrd", test_fw_cfg_initrd); - qtest_add_func("fw_cfg/boot_device", test_fw_cfg_boot_device); -#endif - qtest_add_func("fw_cfg/max_cpus", test_fw_cfg_max_cpus); - qtest_add_func("fw_cfg/numa", test_fw_cfg_numa); - qtest_add_func("fw_cfg/boot_menu", test_fw_cfg_boot_menu); - qtest_add_func("fw_cfg/reboot_timeout", test_fw_cfg_reboot_timeout); - qtest_add_func("fw_cfg/no_reboot_timeout", test_fw_cfg_no_reboot_timeout); - qtest_add_func("fw_cfg/splash_time", test_fw_cfg_splash_time); - - return g_test_run(); -} diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c deleted file mode 100644 index a249800544..0000000000 --- a/tests/hd-geo-test.c +++ /dev/null @@ -1,988 +0,0 @@ -/* - * Hard disk geometry test cases. - * - * Copyright (c) 2012 Red Hat Inc. - * - * Authors: - * Markus Armbruster , - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -/* - * Covers only IDE and tests only CMOS contents. Better than nothing. - * Improvements welcome. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/bswap.h" -#include "qapi/qmp/qlist.h" -#include "libqtest.h" -#include "libqos/fw_cfg.h" -#include "libqos/libqos.h" -#include "standard-headers/linux/qemu_fw_cfg.h" - -#define ARGV_SIZE 256 - -static char *create_test_img(int secs) -{ - char *template = strdup("/tmp/qtest.XXXXXX"); - int fd, ret; - - fd = mkstemp(template); - g_assert(fd >= 0); - ret = ftruncate(fd, (off_t)secs * 512); - close(fd); - - if (ret) { - free(template); - template = NULL; - } - - return template; -} - -typedef struct { - int cyls, heads, secs, trans; -} CHST; - -typedef enum { - mbr_blank, mbr_lba, mbr_chs, - mbr_last -} MBRcontents; - -typedef enum { - /* order is relevant */ - backend_small, backend_large, backend_empty, - backend_last -} Backend; - -static const int img_secs[backend_last] = { - [backend_small] = 61440, - [backend_large] = 8388608, - [backend_empty] = -1, -}; - -static const CHST hd_chst[backend_last][mbr_last] = { - [backend_small] = { - [mbr_blank] = { 60, 16, 63, 0 }, - [mbr_lba] = { 60, 16, 63, 2 }, - [mbr_chs] = { 60, 16, 63, 0 } - }, - [backend_large] = { - [mbr_blank] = { 8322, 16, 63, 1 }, - [mbr_lba] = { 8322, 16, 63, 1 }, - [mbr_chs] = { 8322, 16, 63, 0 } - }, -}; - -static char *img_file_name[backend_last]; - -static const CHST *cur_ide[4]; - -static bool is_hd(const CHST *expected_chst) -{ - return expected_chst && expected_chst->cyls; -} - -static void test_cmos_byte(QTestState *qts, int reg, int expected) -{ - enum { cmos_base = 0x70 }; - int actual; - - qtest_outb(qts, cmos_base + 0, reg); - actual = qtest_inb(qts, cmos_base + 1); - g_assert(actual == expected); -} - -static void test_cmos_bytes(QTestState *qts, int reg0, int n, - uint8_t expected[]) -{ - int i; - - for (i = 0; i < 9; i++) { - test_cmos_byte(qts, reg0 + i, expected[i]); - } -} - -static void test_cmos_disk_data(QTestState *qts) -{ - test_cmos_byte(qts, 0x12, - (is_hd(cur_ide[0]) ? 0xf0 : 0) | - (is_hd(cur_ide[1]) ? 0x0f : 0)); -} - -static void test_cmos_drive_cyl(QTestState *qts, int reg0, - const CHST *expected_chst) -{ - if (is_hd(expected_chst)) { - int c = expected_chst->cyls; - int h = expected_chst->heads; - int s = expected_chst->secs; - uint8_t expected_bytes[9] = { - c & 0xff, c >> 8, h, 0xff, 0xff, 0xc0 | ((h > 8) << 3), - c & 0xff, c >> 8, s - }; - test_cmos_bytes(qts, reg0, 9, expected_bytes); - } else { - int i; - - for (i = 0; i < 9; i++) { - test_cmos_byte(qts, reg0 + i, 0); - } - } -} - -static void test_cmos_drive1(QTestState *qts) -{ - test_cmos_byte(qts, 0x19, is_hd(cur_ide[0]) ? 47 : 0); - test_cmos_drive_cyl(qts, 0x1b, cur_ide[0]); -} - -static void test_cmos_drive2(QTestState *qts) -{ - test_cmos_byte(qts, 0x1a, is_hd(cur_ide[1]) ? 47 : 0); - test_cmos_drive_cyl(qts, 0x24, cur_ide[1]); -} - -static void test_cmos_disktransflag(QTestState *qts) -{ - int val, i; - - val = 0; - for (i = 0; i < ARRAY_SIZE(cur_ide); i++) { - if (is_hd(cur_ide[i])) { - val |= cur_ide[i]->trans << (2 * i); - } - } - test_cmos_byte(qts, 0x39, val); -} - -static void test_cmos(QTestState *qts) -{ - test_cmos_disk_data(qts); - test_cmos_drive1(qts); - test_cmos_drive2(qts); - test_cmos_disktransflag(qts); -} - -static int append_arg(int argc, char *argv[], int argv_sz, char *arg) -{ - g_assert(argc + 1 < argv_sz); - argv[argc++] = arg; - argv[argc] = NULL; - return argc; -} - -static int setup_common(char *argv[], int argv_sz) -{ - memset(cur_ide, 0, sizeof(cur_ide)); - return append_arg(0, argv, argv_sz, - g_strdup("-nodefaults")); -} - -static void setup_mbr(int img_idx, MBRcontents mbr) -{ - static const uint8_t part_lba[16] = { - /* chs 0,1,1 (lba 63) to chs 0,127,63 (8001 sectors) */ - 0x80, 1, 1, 0, 6, 127, 63, 0, 63, 0, 0, 0, 0x41, 0x1F, 0, 0, - }; - static const uint8_t part_chs[16] = { - /* chs 0,1,1 (lba 63) to chs 7,15,63 (8001 sectors) */ - 0x80, 1, 1, 0, 6, 15, 63, 7, 63, 0, 0, 0, 0x41, 0x1F, 0, 0, - }; - uint8_t buf[512]; - int fd, ret; - - memset(buf, 0, sizeof(buf)); - - if (mbr != mbr_blank) { - buf[0x1fe] = 0x55; - buf[0x1ff] = 0xAA; - memcpy(buf + 0x1BE, mbr == mbr_lba ? part_lba : part_chs, 16); - } - - fd = open(img_file_name[img_idx], O_WRONLY); - g_assert(fd >= 0); - ret = write(fd, buf, sizeof(buf)); - g_assert(ret == sizeof(buf)); - close(fd); -} - -static int setup_ide(int argc, char *argv[], int argv_sz, - int ide_idx, const char *dev, int img_idx, - MBRcontents mbr) -{ - char *s1, *s2, *s3; - - s1 = g_strdup_printf("-drive id=drive%d,if=%s", - ide_idx, dev ? "none" : "ide"); - s2 = dev ? g_strdup("") : g_strdup_printf(",index=%d", ide_idx); - - if (img_secs[img_idx] >= 0) { - setup_mbr(img_idx, mbr); - s3 = g_strdup_printf(",format=raw,file=%s", img_file_name[img_idx]); - } else { - s3 = g_strdup(",media=cdrom"); - } - argc = append_arg(argc, argv, argv_sz, - g_strdup_printf("%s%s%s", s1, s2, s3)); - g_free(s1); - g_free(s2); - g_free(s3); - - if (dev) { - argc = append_arg(argc, argv, argv_sz, - g_strdup_printf("-device %s,drive=drive%d," - "bus=ide.%d,unit=%d", - dev, ide_idx, - ide_idx / 2, ide_idx % 2)); - } - return argc; -} - -/* - * Test case: no IDE devices - */ -static void test_ide_none(void) -{ - char **argv = g_new0(char *, ARGV_SIZE); - char *args; - QTestState *qts; - - setup_common(argv, ARGV_SIZE); - args = g_strjoinv(" ", argv); - qts = qtest_init(args); - g_strfreev(argv); - g_free(args); - test_cmos(qts); - qtest_quit(qts); -} - -static void test_ide_mbr(bool use_device, MBRcontents mbr) -{ - char **argv = g_new0(char *, ARGV_SIZE); - char *args; - int argc; - Backend i; - const char *dev; - QTestState *qts; - - argc = setup_common(argv, ARGV_SIZE); - for (i = 0; i < backend_last; i++) { - cur_ide[i] = &hd_chst[i][mbr]; - dev = use_device ? (is_hd(cur_ide[i]) ? "ide-hd" : "ide-cd") : NULL; - argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr); - } - args = g_strjoinv(" ", argv); - qts = qtest_init(args); - g_strfreev(argv); - g_free(args); - test_cmos(qts); - qtest_quit(qts); -} - -/* - * Test case: IDE devices (if=ide) with blank MBRs - */ -static void test_ide_drive_mbr_blank(void) -{ - test_ide_mbr(false, mbr_blank); -} - -/* - * Test case: IDE devices (if=ide) with MBRs indicating LBA is in use - */ -static void test_ide_drive_mbr_lba(void) -{ - test_ide_mbr(false, mbr_lba); -} - -/* - * Test case: IDE devices (if=ide) with MBRs indicating CHS is in use - */ -static void test_ide_drive_mbr_chs(void) -{ - test_ide_mbr(false, mbr_chs); -} - -/* - * Test case: IDE devices (if=none) with blank MBRs - */ -static void test_ide_device_mbr_blank(void) -{ - test_ide_mbr(true, mbr_blank); -} - -/* - * Test case: IDE devices (if=none) with MBRs indicating LBA is in use - */ -static void test_ide_device_mbr_lba(void) -{ - test_ide_mbr(true, mbr_lba); -} - -/* - * Test case: IDE devices (if=none) with MBRs indicating CHS is in use - */ -static void test_ide_device_mbr_chs(void) -{ - test_ide_mbr(true, mbr_chs); -} - -static void test_ide_drive_user(const char *dev, bool trans) -{ - char **argv = g_new0(char *, ARGV_SIZE); - char *args, *opts; - int argc; - int secs = img_secs[backend_small]; - const CHST expected_chst = { secs / (4 * 32) , 4, 32, trans }; - QTestState *qts; - - argc = setup_common(argv, ARGV_SIZE); - opts = g_strdup_printf("%s,%scyls=%d,heads=%d,secs=%d", - dev, trans ? "bios-chs-trans=lba," : "", - expected_chst.cyls, expected_chst.heads, - expected_chst.secs); - cur_ide[0] = &expected_chst; - argc = setup_ide(argc, argv, ARGV_SIZE, 0, opts, backend_small, mbr_chs); - g_free(opts); - args = g_strjoinv(" ", argv); - qts = qtest_init(args); - g_strfreev(argv); - g_free(args); - test_cmos(qts); - qtest_quit(qts); -} - -/* - * Test case: IDE device (if=none) with explicit CHS - */ -static void test_ide_device_user_chs(void) -{ - test_ide_drive_user("ide-hd", false); -} - -/* - * Test case: IDE device (if=none) with explicit CHS and translation - */ -static void test_ide_device_user_chst(void) -{ - test_ide_drive_user("ide-hd", true); -} - -/* - * Test case: IDE devices (if=ide), but use index=0 for CD-ROM - */ -static void test_ide_drive_cd_0(void) -{ - char **argv = g_new0(char *, ARGV_SIZE); - char *args; - int argc, ide_idx; - Backend i; - QTestState *qts; - - argc = setup_common(argv, ARGV_SIZE); - for (i = 0; i <= backend_empty; i++) { - ide_idx = backend_empty - i; - cur_ide[ide_idx] = &hd_chst[i][mbr_blank]; - argc = setup_ide(argc, argv, ARGV_SIZE, ide_idx, NULL, i, mbr_blank); - } - args = g_strjoinv(" ", argv); - qts = qtest_init(args); - g_strfreev(argv); - g_free(args); - test_cmos(qts); - qtest_quit(qts); -} - -typedef struct { - bool active; - uint32_t head; - uint32_t sector; - uint32_t cyl; - uint32_t end_head; - uint32_t end_sector; - uint32_t end_cyl; - uint32_t start_sect; - uint32_t nr_sects; -} MBRpartitions[4]; - -static MBRpartitions empty_mbr = { {false, 0, 0, 0, 0, 0, 0, 0, 0}, - {false, 0, 0, 0, 0, 0, 0, 0, 0}, - {false, 0, 0, 0, 0, 0, 0, 0, 0}, - {false, 0, 0, 0, 0, 0, 0, 0, 0} }; - -static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) -{ - const char *template = "/tmp/qtest.XXXXXX"; - char *raw_path = strdup(template); - char *qcow2_path = strdup(template); - char cmd[100 + 2 * PATH_MAX]; - uint8_t buf[512]; - int i, ret, fd, offset; - uint64_t qcow2_size = sectors * 512; - uint8_t status, parttype, head, sector, cyl; - char *qemu_img_path; - char *qemu_img_abs_path; - - offset = 0xbe; - - for (i = 0; i < 4; i++) { - status = mbr[i].active ? 0x80 : 0x00; - g_assert(mbr[i].head < 256); - g_assert(mbr[i].sector < 64); - g_assert(mbr[i].cyl < 1024); - head = mbr[i].head; - sector = mbr[i].sector + ((mbr[i].cyl & 0x300) >> 2); - cyl = mbr[i].cyl & 0xff; - - buf[offset + 0x0] = status; - buf[offset + 0x1] = head; - buf[offset + 0x2] = sector; - buf[offset + 0x3] = cyl; - - parttype = 0; - g_assert(mbr[i].end_head < 256); - g_assert(mbr[i].end_sector < 64); - g_assert(mbr[i].end_cyl < 1024); - head = mbr[i].end_head; - sector = mbr[i].end_sector + ((mbr[i].end_cyl & 0x300) >> 2); - cyl = mbr[i].end_cyl & 0xff; - - buf[offset + 0x4] = parttype; - buf[offset + 0x5] = head; - buf[offset + 0x6] = sector; - buf[offset + 0x7] = cyl; - - (*(uint32_t *)&buf[offset + 0x8]) = cpu_to_le32(mbr[i].start_sect); - (*(uint32_t *)&buf[offset + 0xc]) = cpu_to_le32(mbr[i].nr_sects); - - offset += 0x10; - } - - fd = mkstemp(raw_path); - g_assert(fd); - close(fd); - - fd = open(raw_path, O_WRONLY); - g_assert(fd >= 0); - ret = write(fd, buf, sizeof(buf)); - g_assert(ret == sizeof(buf)); - close(fd); - - fd = mkstemp(qcow2_path); - g_assert(fd); - close(fd); - - qemu_img_path = getenv("QTEST_QEMU_IMG"); - g_assert(qemu_img_path); - qemu_img_abs_path = realpath(qemu_img_path, NULL); - g_assert(qemu_img_abs_path); - - ret = snprintf(cmd, sizeof(cmd), - "%s convert -f raw -O qcow2 %s %s > /dev/null", - qemu_img_abs_path, - raw_path, qcow2_path); - g_assert((0 < ret) && (ret <= sizeof(cmd))); - ret = system(cmd); - g_assert(ret == 0); - - ret = snprintf(cmd, sizeof(cmd), - "%s resize %s %" PRIu64 " > /dev/null", - qemu_img_abs_path, - qcow2_path, qcow2_size); - g_assert((0 < ret) && (ret <= sizeof(cmd))); - ret = system(cmd); - g_assert(ret == 0); - - free(qemu_img_abs_path); - - unlink(raw_path); - free(raw_path); - - return qcow2_path; -} - -#define BIOS_GEOMETRY_MAX_SIZE 10000 - -typedef struct { - uint32_t c; - uint32_t h; - uint32_t s; -} CHS; - -typedef struct { - const char *dev_path; - CHS chs; -} CHSResult; - -static void read_bootdevices(QFWCFG *fw_cfg, CHSResult expected[]) -{ - char *buf = g_malloc0(BIOS_GEOMETRY_MAX_SIZE); - char *cur; - GList *results = NULL, *cur_result; - CHSResult *r; - int i; - int res; - bool found; - - qfw_cfg_get_file(fw_cfg, "bios-geometry", buf, BIOS_GEOMETRY_MAX_SIZE); - - for (cur = buf; *cur; cur++) { - if (*cur == '\n') { - *cur = '\0'; - } - } - cur = buf; - - while (strlen(cur)) { - - r = g_malloc0(sizeof(*r)); - r->dev_path = g_malloc0(strlen(cur) + 1); - res = sscanf(cur, "%s %" PRIu32 " %" PRIu32 " %" PRIu32, - (char *)r->dev_path, - &(r->chs.c), &(r->chs.h), &(r->chs.s)); - - g_assert(res == 4); - - results = g_list_prepend(results, r); - - cur += strlen(cur) + 1; - } - - i = 0; - - while (expected[i].dev_path) { - found = false; - cur_result = results; - while (cur_result) { - r = cur_result->data; - if (!strcmp(r->dev_path, expected[i].dev_path) && - !memcmp(&(r->chs), &(expected[i].chs), sizeof(r->chs))) { - found = true; - break; - } - cur_result = g_list_next(cur_result); - } - g_assert(found); - g_free((char *)((CHSResult *)cur_result->data)->dev_path); - g_free(cur_result->data); - results = g_list_delete_link(results, cur_result); - i++; - } - - g_assert(results == NULL); - - g_free(buf); -} - -#define MAX_DRIVES 30 - -typedef struct { - char **argv; - int argc; - char **drives; - int n_drives; - int n_scsi_disks; - int n_scsi_controllers; - int n_virtio_disks; -} TestArgs; - -static TestArgs *create_args(void) -{ - TestArgs *args = g_malloc0(sizeof(*args)); - args->argv = g_new0(char *, ARGV_SIZE); - args->argc = append_arg(args->argc, args->argv, - ARGV_SIZE, g_strdup("-nodefaults")); - args->drives = g_new0(char *, MAX_DRIVES); - return args; -} - -static void add_drive_with_mbr(TestArgs *args, - MBRpartitions mbr, uint64_t sectors) -{ - char *img_file_name; - char part[300]; - int ret; - - g_assert(args->n_drives < MAX_DRIVES); - - img_file_name = create_qcow2_with_mbr(mbr, sectors); - - args->drives[args->n_drives] = img_file_name; - ret = snprintf(part, sizeof(part), - "-drive file=%s,if=none,format=qcow2,id=disk%d", - img_file_name, args->n_drives); - g_assert((0 < ret) && (ret <= sizeof(part))); - args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part)); - args->n_drives++; -} - -static void add_ide_disk(TestArgs *args, - int drive_idx, int bus, int unit, int c, int h, int s) -{ - char part[300]; - int ret; - - ret = snprintf(part, sizeof(part), - "-device ide-hd,drive=disk%d,bus=ide.%d,unit=%d," - "lcyls=%d,lheads=%d,lsecs=%d", - drive_idx, bus, unit, c, h, s); - g_assert((0 < ret) && (ret <= sizeof(part))); - args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part)); -} - -static void add_scsi_controller(TestArgs *args, - const char *type, - const char *bus, - int addr) -{ - char part[300]; - int ret; - - ret = snprintf(part, sizeof(part), - "-device %s,id=scsi%d,bus=%s,addr=%d", - type, args->n_scsi_controllers, bus, addr); - g_assert((0 < ret) && (ret <= sizeof(part))); - args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part)); - args->n_scsi_controllers++; -} - -static void add_scsi_disk(TestArgs *args, - int drive_idx, int bus, - int channel, int scsi_id, int lun, - int c, int h, int s) -{ - char part[300]; - int ret; - - ret = snprintf(part, sizeof(part), - "-device scsi-hd,id=scsi-disk%d,drive=disk%d," - "bus=scsi%d.0," - "channel=%d,scsi-id=%d,lun=%d," - "lcyls=%d,lheads=%d,lsecs=%d", - args->n_scsi_disks, drive_idx, bus, channel, scsi_id, lun, - c, h, s); - g_assert((0 < ret) && (ret <= sizeof(part))); - args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part)); - args->n_scsi_disks++; -} - -static void add_virtio_disk(TestArgs *args, - int drive_idx, const char *bus, int addr, - int c, int h, int s) -{ - char part[300]; - int ret; - - ret = snprintf(part, sizeof(part), - "-device virtio-blk-pci,id=virtio-disk%d," - "drive=disk%d,bus=%s,addr=%d," - "lcyls=%d,lheads=%d,lsecs=%d", - args->n_virtio_disks, drive_idx, bus, addr, c, h, s); - g_assert((0 < ret) && (ret <= sizeof(part))); - args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part)); - args->n_virtio_disks++; -} - -static void test_override(TestArgs *args, CHSResult expected[]) -{ - QTestState *qts; - char *joined_args; - QFWCFG *fw_cfg; - int i; - - joined_args = g_strjoinv(" ", args->argv); - - qts = qtest_init(joined_args); - fw_cfg = pc_fw_cfg_init(qts); - - read_bootdevices(fw_cfg, expected); - - g_free(joined_args); - qtest_quit(qts); - - g_free(fw_cfg); - - for (i = 0; i < args->n_drives; i++) { - unlink(args->drives[i]); - free(args->drives[i]); - } - g_free(args->drives); - g_strfreev(args->argv); - g_free(args); -} - -static void test_override_ide(void) -{ - TestArgs *args = create_args(); - CHSResult expected[] = { - {"/pci@i0cf8/ide@1,1/drive@0/disk@0", {10000, 120, 30} }, - {"/pci@i0cf8/ide@1,1/drive@0/disk@1", {9000, 120, 30} }, - {"/pci@i0cf8/ide@1,1/drive@1/disk@0", {0, 1, 1} }, - {"/pci@i0cf8/ide@1,1/drive@1/disk@1", {1, 0, 0} }, - {NULL, {0, 0, 0} } - }; - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_ide_disk(args, 0, 0, 0, 10000, 120, 30); - add_ide_disk(args, 1, 0, 1, 9000, 120, 30); - add_ide_disk(args, 2, 1, 0, 0, 1, 1); - add_ide_disk(args, 3, 1, 1, 1, 0, 0); - test_override(args, expected); -} - -static void test_override_scsi(void) -{ - TestArgs *args = create_args(); - CHSResult expected[] = { - {"/pci@i0cf8/scsi@3/channel@0/disk@0,0", {10000, 120, 30} }, - {"/pci@i0cf8/scsi@3/channel@0/disk@1,0", {9000, 120, 30} }, - {"/pci@i0cf8/scsi@3/channel@0/disk@2,0", {1, 0, 0} }, - {"/pci@i0cf8/scsi@3/channel@0/disk@3,0", {0, 1, 0} }, - {NULL, {0, 0, 0} } - }; - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_scsi_controller(args, "lsi53c895a", "pci.0", 3); - add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30); - add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30); - add_scsi_disk(args, 2, 0, 0, 2, 0, 1, 0, 0); - add_scsi_disk(args, 3, 0, 0, 3, 0, 0, 1, 0); - test_override(args, expected); -} - -static void test_override_scsi_2_controllers(void) -{ - TestArgs *args = create_args(); - CHSResult expected[] = { - {"/pci@i0cf8/scsi@3/channel@0/disk@0,0", {10000, 120, 30} }, - {"/pci@i0cf8/scsi@3/channel@0/disk@1,0", {9000, 120, 30} }, - {"/pci@i0cf8/scsi@4/channel@0/disk@0,1", {1, 0, 0} }, - {"/pci@i0cf8/scsi@4/channel@0/disk@1,2", {0, 1, 0} }, - {NULL, {0, 0, 0} } - }; - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_scsi_controller(args, "lsi53c895a", "pci.0", 3); - add_scsi_controller(args, "virtio-scsi-pci", "pci.0", 4); - add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30); - add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30); - add_scsi_disk(args, 2, 1, 0, 0, 1, 1, 0, 0); - add_scsi_disk(args, 3, 1, 0, 1, 2, 0, 1, 0); - test_override(args, expected); -} - -static void test_override_virtio_blk(void) -{ - TestArgs *args = create_args(); - CHSResult expected[] = { - {"/pci@i0cf8/scsi@3/disk@0,0", {10000, 120, 30} }, - {"/pci@i0cf8/scsi@4/disk@0,0", {9000, 120, 30} }, - {NULL, {0, 0, 0} } - }; - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_virtio_disk(args, 0, "pci.0", 3, 10000, 120, 30); - add_virtio_disk(args, 1, "pci.0", 4, 9000, 120, 30); - test_override(args, expected); -} - -static void test_override_zero_chs(void) -{ - TestArgs *args = create_args(); - CHSResult expected[] = { - {NULL, {0, 0, 0} } - }; - add_drive_with_mbr(args, empty_mbr, 1); - add_ide_disk(args, 0, 1, 1, 0, 0, 0); - test_override(args, expected); -} - -static void test_override_scsi_hot_unplug(void) -{ - QTestState *qts; - char *joined_args; - QFWCFG *fw_cfg; - QDict *response; - int i; - TestArgs *args = create_args(); - CHSResult expected[] = { - {"/pci@i0cf8/scsi@2/channel@0/disk@0,0", {10000, 120, 30} }, - {"/pci@i0cf8/scsi@2/channel@0/disk@1,0", {20, 20, 20} }, - {NULL, {0, 0, 0} } - }; - CHSResult expected2[] = { - {"/pci@i0cf8/scsi@2/channel@0/disk@1,0", {20, 20, 20} }, - {NULL, {0, 0, 0} } - }; - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_scsi_controller(args, "virtio-scsi-pci", "pci.0", 2); - add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30); - add_scsi_disk(args, 1, 0, 0, 1, 0, 20, 20, 20); - - joined_args = g_strjoinv(" ", args->argv); - - qts = qtest_init(joined_args); - fw_cfg = pc_fw_cfg_init(qts); - - read_bootdevices(fw_cfg, expected); - - /* unplug device an restart */ - response = qtest_qmp(qts, - "{ 'execute': 'device_del'," - " 'arguments': {'id': 'scsi-disk0' }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - response = qtest_qmp(qts, - "{ 'execute': 'system_reset', 'arguments': { }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - qtest_qmp_eventwait(qts, "RESET"); - - read_bootdevices(fw_cfg, expected2); - - g_free(joined_args); - qtest_quit(qts); - - g_free(fw_cfg); - - for (i = 0; i < args->n_drives; i++) { - unlink(args->drives[i]); - free(args->drives[i]); - } - g_free(args->drives); - g_strfreev(args->argv); - g_free(args); -} - -static void test_override_virtio_hot_unplug(void) -{ - QTestState *qts; - char *joined_args; - QFWCFG *fw_cfg; - QDict *response; - int i; - TestArgs *args = create_args(); - CHSResult expected[] = { - {"/pci@i0cf8/scsi@2/disk@0,0", {10000, 120, 30} }, - {"/pci@i0cf8/scsi@3/disk@0,0", {20, 20, 20} }, - {NULL, {0, 0, 0} } - }; - CHSResult expected2[] = { - {"/pci@i0cf8/scsi@3/disk@0,0", {20, 20, 20} }, - {NULL, {0, 0, 0} } - }; - add_drive_with_mbr(args, empty_mbr, 1); - add_drive_with_mbr(args, empty_mbr, 1); - add_virtio_disk(args, 0, "pci.0", 2, 10000, 120, 30); - add_virtio_disk(args, 1, "pci.0", 3, 20, 20, 20); - - joined_args = g_strjoinv(" ", args->argv); - - qts = qtest_init(joined_args); - fw_cfg = pc_fw_cfg_init(qts); - - read_bootdevices(fw_cfg, expected); - - /* unplug device an restart */ - response = qtest_qmp(qts, - "{ 'execute': 'device_del'," - " 'arguments': {'id': 'virtio-disk0' }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - response = qtest_qmp(qts, - "{ 'execute': 'system_reset', 'arguments': { }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - qtest_qmp_eventwait(qts, "RESET"); - - read_bootdevices(fw_cfg, expected2); - - g_free(joined_args); - qtest_quit(qts); - - g_free(fw_cfg); - - for (i = 0; i < args->n_drives; i++) { - unlink(args->drives[i]); - free(args->drives[i]); - } - g_free(args->drives); - g_strfreev(args->argv); - g_free(args); -} - -int main(int argc, char **argv) -{ - Backend i; - int ret; - - g_test_init(&argc, &argv, NULL); - - for (i = 0; i < backend_last; i++) { - if (img_secs[i] >= 0) { - img_file_name[i] = create_test_img(img_secs[i]); - if (!img_file_name[i]) { - g_test_message("Could not create test images."); - goto test_add_done; - } - } else { - img_file_name[i] = NULL; - } - } - - qtest_add_func("hd-geo/ide/none", test_ide_none); - qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); - qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); - qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); - qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); - qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); - qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); - qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs); - qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs); - qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst); - if (have_qemu_img()) { - qtest_add_func("hd-geo/override/ide", test_override_ide); - qtest_add_func("hd-geo/override/scsi", test_override_scsi); - qtest_add_func("hd-geo/override/scsi_2_controllers", - test_override_scsi_2_controllers); - qtest_add_func("hd-geo/override/virtio_blk", test_override_virtio_blk); - qtest_add_func("hd-geo/override/zero_chs", test_override_zero_chs); - qtest_add_func("hd-geo/override/scsi_hot_unplug", - test_override_scsi_hot_unplug); - qtest_add_func("hd-geo/override/virtio_hot_unplug", - test_override_virtio_hot_unplug); - } else { - g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " - "skipping hd-geo/override/* tests"); - } - -test_add_done: - ret = g_test_run(); - - for (i = 0; i < backend_last; i++) { - if (img_file_name[i]) { - unlink(img_file_name[i]); - free(img_file_name[i]); - } - } - - return ret; -} diff --git a/tests/hexloader-test.c b/tests/hexloader-test.c deleted file mode 100644 index 8b7aa2d72d..0000000000 --- a/tests/hexloader-test.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * QTest testcase for the Intel Hexadecimal Object File Loader - * - * Authors: - * Su Hang 2018 - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "libqtest.h" - -/* Load 'test.hex' and verify that the in-memory contents are as expected. - * 'test.hex' is a memory test pattern stored in Hexadecimal Object - * format. It loads at 0x10000 in RAM and contains values from 0 through - * 255. - */ -static void hex_loader_test(void) -{ - unsigned int i; - const unsigned int base_addr = 0x00010000; - - QTestState *s = qtest_initf( - "-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); - g_assert_cmpuint(i, ==, val); - } - qtest_quit(s); -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/tmp/hex_loader", hex_loader_test); - ret = g_test_run(); - - return ret; -} diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c deleted file mode 100644 index 1f57d9684b..0000000000 --- a/tests/i440fx-test.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * qtest I440FX test case - * - * Copyright IBM, Corp. 2012-2013 - * Copyright Red Hat, Inc. 2013 - * - * Authors: - * Anthony Liguori - * Laszlo Ersek - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "libqtest-single.h" -#include "libqos/pci.h" -#include "libqos/pci-pc.h" -#include "hw/pci/pci_regs.h" - -#define BROKEN 1 - -typedef struct TestData -{ - int num_cpus; -} TestData; - -typedef struct FirmwareTestFixture { - /* decides whether we're testing -bios or -pflash */ - bool is_bios; -} FirmwareTestFixture; - -static QPCIBus *test_start_get_bus(const TestData *s) -{ - char *cmdline; - - cmdline = g_strdup_printf("-smp %d", s->num_cpus); - qtest_start(cmdline); - g_free(cmdline); - return qpci_new_pc(global_qtest, NULL); -} - -static void test_i440fx_defaults(gconstpointer opaque) -{ - const TestData *s = opaque; - QPCIBus *bus; - QPCIDevice *dev; - uint32_t value; - - bus = test_start_get_bus(s); - dev = qpci_device_find(bus, QPCI_DEVFN(0, 0)); - g_assert(dev != NULL); - - /* 3.2.2 */ - g_assert_cmpint(qpci_config_readw(dev, PCI_VENDOR_ID), ==, 0x8086); - /* 3.2.3 */ - g_assert_cmpint(qpci_config_readw(dev, PCI_DEVICE_ID), ==, 0x1237); -#ifndef BROKEN - /* 3.2.4 */ - g_assert_cmpint(qpci_config_readw(dev, PCI_COMMAND), ==, 0x0006); - /* 3.2.5 */ - g_assert_cmpint(qpci_config_readw(dev, PCI_STATUS), ==, 0x0280); -#endif - /* 3.2.7 */ - g_assert_cmpint(qpci_config_readb(dev, PCI_CLASS_PROG), ==, 0x00); - g_assert_cmpint(qpci_config_readw(dev, PCI_CLASS_DEVICE), ==, 0x0600); - /* 3.2.8 */ - g_assert_cmpint(qpci_config_readb(dev, PCI_LATENCY_TIMER), ==, 0x00); - /* 3.2.9 */ - g_assert_cmpint(qpci_config_readb(dev, PCI_HEADER_TYPE), ==, 0x00); - /* 3.2.10 */ - g_assert_cmpint(qpci_config_readb(dev, PCI_BIST), ==, 0x00); - - /* 3.2.11 */ - value = qpci_config_readw(dev, 0x50); /* PMCCFG */ - if (s->num_cpus == 1) { /* WPE */ - g_assert(!(value & (1 << 15))); - } else { - g_assert((value & (1 << 15))); - } - - g_assert(!(value & (1 << 6))); /* EPTE */ - - /* 3.2.12 */ - g_assert_cmpint(qpci_config_readb(dev, 0x52), ==, 0x00); /* DETURBO */ - /* 3.2.13 */ -#ifndef BROKEN - g_assert_cmpint(qpci_config_readb(dev, 0x53), ==, 0x80); /* DBC */ -#endif - /* 3.2.14 */ - g_assert_cmpint(qpci_config_readb(dev, 0x54), ==, 0x00); /* AXC */ - /* 3.2.15 */ - g_assert_cmpint(qpci_config_readw(dev, 0x55), ==, 0x0000); /* DRT */ -#ifndef BROKEN - /* 3.2.16 */ - g_assert_cmpint(qpci_config_readb(dev, 0x57), ==, 0x01); /* DRAMC */ - /* 3.2.17 */ - g_assert_cmpint(qpci_config_readb(dev, 0x58), ==, 0x10); /* DRAMT */ -#endif - /* 3.2.18 */ - g_assert_cmpint(qpci_config_readb(dev, 0x59), ==, 0x00); /* PAM0 */ - g_assert_cmpint(qpci_config_readb(dev, 0x5A), ==, 0x00); /* PAM1 */ - g_assert_cmpint(qpci_config_readb(dev, 0x5B), ==, 0x00); /* PAM2 */ - g_assert_cmpint(qpci_config_readb(dev, 0x5C), ==, 0x00); /* PAM3 */ - g_assert_cmpint(qpci_config_readb(dev, 0x5D), ==, 0x00); /* PAM4 */ - g_assert_cmpint(qpci_config_readb(dev, 0x5E), ==, 0x00); /* PAM5 */ - g_assert_cmpint(qpci_config_readb(dev, 0x5F), ==, 0x00); /* PAM6 */ -#ifndef BROKEN - /* 3.2.19 */ - g_assert_cmpint(qpci_config_readb(dev, 0x60), ==, 0x01); /* DRB0 */ - g_assert_cmpint(qpci_config_readb(dev, 0x61), ==, 0x01); /* DRB1 */ - g_assert_cmpint(qpci_config_readb(dev, 0x62), ==, 0x01); /* DRB2 */ - g_assert_cmpint(qpci_config_readb(dev, 0x63), ==, 0x01); /* DRB3 */ - g_assert_cmpint(qpci_config_readb(dev, 0x64), ==, 0x01); /* DRB4 */ - g_assert_cmpint(qpci_config_readb(dev, 0x65), ==, 0x01); /* DRB5 */ - g_assert_cmpint(qpci_config_readb(dev, 0x66), ==, 0x01); /* DRB6 */ - g_assert_cmpint(qpci_config_readb(dev, 0x67), ==, 0x01); /* DRB7 */ -#endif - /* 3.2.20 */ - g_assert_cmpint(qpci_config_readb(dev, 0x68), ==, 0x00); /* FDHC */ - /* 3.2.21 */ - g_assert_cmpint(qpci_config_readb(dev, 0x70), ==, 0x00); /* MTT */ -#ifndef BROKEN - /* 3.2.22 */ - g_assert_cmpint(qpci_config_readb(dev, 0x71), ==, 0x10); /* CLT */ -#endif - /* 3.2.23 */ - g_assert_cmpint(qpci_config_readb(dev, 0x72), ==, 0x02); /* SMRAM */ - /* 3.2.24 */ - g_assert_cmpint(qpci_config_readb(dev, 0x90), ==, 0x00); /* ERRCMD */ - /* 3.2.25 */ - g_assert_cmpint(qpci_config_readb(dev, 0x91), ==, 0x00); /* ERRSTS */ - /* 3.2.26 */ - g_assert_cmpint(qpci_config_readb(dev, 0x93), ==, 0x00); /* TRC */ - - g_free(dev); - qpci_free_pc(bus); - qtest_end(); -} - -#define PAM_RE 1 -#define PAM_WE 2 - -static void pam_set(QPCIDevice *dev, int index, int flags) -{ - int regno = 0x59 + (index / 2); - uint8_t reg; - - reg = qpci_config_readb(dev, regno); - if (index & 1) { - reg = (reg & 0x0F) | (flags << 4); - } else { - reg = (reg & 0xF0) | flags; - } - qpci_config_writeb(dev, regno, reg); -} - -static gboolean verify_area(uint32_t start, uint32_t end, uint8_t value) -{ - uint32_t size = end - start + 1; - gboolean ret = TRUE; - uint8_t *data; - int i; - - data = g_malloc0(size); - memread(start, data, size); - - g_test_message("verify_area: data[0] = 0x%x", data[0]); - - for (i = 0; i < size; i++) { - if (data[i] != value) { - ret = FALSE; - break; - } - } - - g_free(data); - - return ret; -} - -static void write_area(uint32_t start, uint32_t end, uint8_t value) -{ - uint32_t size = end - start + 1; - uint8_t *data; - - data = g_malloc(size); - memset(data, value, size); - memwrite(start, data, size); - - g_free(data); -} - -static void test_i440fx_pam(gconstpointer opaque) -{ - const TestData *s = opaque; - QPCIBus *bus; - QPCIDevice *dev; - int i; - static struct { - uint32_t start; - uint32_t end; - } pam_area[] = { - { 0, 0 }, /* Reserved */ - { 0xF0000, 0xFFFFF }, /* BIOS Area */ - { 0xC0000, 0xC3FFF }, /* Option ROM */ - { 0xC4000, 0xC7FFF }, /* Option ROM */ - { 0xC8000, 0xCBFFF }, /* Option ROM */ - { 0xCC000, 0xCFFFF }, /* Option ROM */ - { 0xD0000, 0xD3FFF }, /* Option ROM */ - { 0xD4000, 0xD7FFF }, /* Option ROM */ - { 0xD8000, 0xDBFFF }, /* Option ROM */ - { 0xDC000, 0xDFFFF }, /* Option ROM */ - { 0xE0000, 0xE3FFF }, /* BIOS Extension */ - { 0xE4000, 0xE7FFF }, /* BIOS Extension */ - { 0xE8000, 0xEBFFF }, /* BIOS Extension */ - { 0xEC000, 0xEFFFF }, /* BIOS Extension */ - }; - - bus = test_start_get_bus(s); - dev = qpci_device_find(bus, QPCI_DEVFN(0, 0)); - g_assert(dev != NULL); - - for (i = 0; i < ARRAY_SIZE(pam_area); i++) { - if (pam_area[i].start == pam_area[i].end) { - continue; - } - - g_test_message("Checking area 0x%05x..0x%05x", - pam_area[i].start, pam_area[i].end); - /* Switch to RE for the area */ - pam_set(dev, i, PAM_RE); - /* Verify the RAM is all zeros */ - g_assert(verify_area(pam_area[i].start, pam_area[i].end, 0)); - - /* Switch to WE for the area */ - pam_set(dev, i, PAM_RE | PAM_WE); - /* Write out a non-zero mask to the full area */ - write_area(pam_area[i].start, pam_area[i].end, 0x42); - -#ifndef BROKEN - /* QEMU only supports a limited form of PAM */ - - /* Switch to !RE for the area */ - pam_set(dev, i, PAM_WE); - /* Verify the area is not our mask */ - g_assert(!verify_area(pam_area[i].start, pam_area[i].end, 0x42)); -#endif - - /* Verify the area is our new mask */ - g_assert(verify_area(pam_area[i].start, pam_area[i].end, 0x42)); - - /* Write out a new mask */ - write_area(pam_area[i].start, pam_area[i].end, 0x82); - -#ifndef BROKEN - /* QEMU only supports a limited form of PAM */ - - /* Verify the area is not our mask */ - g_assert(!verify_area(pam_area[i].start, pam_area[i].end, 0x82)); - - /* Switch to RE for the area */ - pam_set(dev, i, PAM_RE | PAM_WE); -#endif - /* Verify the area is our new mask */ - g_assert(verify_area(pam_area[i].start, pam_area[i].end, 0x82)); - - /* Reset area */ - pam_set(dev, i, 0); - - /* Verify the area is not our new mask */ - g_assert(!verify_area(pam_area[i].start, pam_area[i].end, 0x82)); - } - - g_free(dev); - qpci_free_pc(bus); - qtest_end(); -} - -#define BLOB_SIZE ((size_t)65536) -#define ISA_BIOS_MAXSZ ((size_t)(128 * 1024)) - -/* Create a blob file, and return its absolute pathname as a dynamically - * allocated string. - * The file is closed before the function returns. - * In case of error, NULL is returned. The function prints the error message. - */ -static char *create_blob_file(void) -{ - int ret, fd; - char *pathname; - GError *error = NULL; - - ret = -1; - fd = g_file_open_tmp("blob_XXXXXX", &pathname, &error); - if (fd == -1) { - fprintf(stderr, "unable to create blob file: %s\n", error->message); - g_error_free(error); - } else { - if (ftruncate(fd, BLOB_SIZE) == -1) { - fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname, - BLOB_SIZE, strerror(errno)); - } else { - void *buf; - - buf = mmap(NULL, BLOB_SIZE, PROT_WRITE, MAP_SHARED, fd, 0); - if (buf == MAP_FAILED) { - fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, BLOB_SIZE, - strerror(errno)); - } else { - size_t i; - - for (i = 0; i < BLOB_SIZE; ++i) { - ((uint8_t *)buf)[i] = i; - } - munmap(buf, BLOB_SIZE); - ret = 0; - } - } - close(fd); - if (ret == -1) { - unlink(pathname); - g_free(pathname); - } - } - - return ret == -1 ? NULL : pathname; -} - -static void test_i440fx_firmware(FirmwareTestFixture *fixture, - gconstpointer user_data) -{ - char *fw_pathname, *cmdline; - uint8_t *buf; - size_t i, isa_bios_size; - - fw_pathname = create_blob_file(); - g_assert(fw_pathname != NULL); - - /* Better hope the user didn't put metacharacters in TMPDIR and co. */ - cmdline = g_strdup_printf("-S %s%s", fixture->is_bios - ? "-bios " - : "-drive if=pflash,format=raw,file=", - fw_pathname); - g_test_message("qemu cmdline: %s", cmdline); - qtest_start(cmdline); - g_free(cmdline); - - /* QEMU has loaded the firmware (because qtest_start() only returns after - * the QMP handshake completes). We must unlink the firmware blob right - * here, because any assertion firing below would leak it in the - * filesystem. This is also the reason why we recreate the blob every time - * this function is invoked. - */ - unlink(fw_pathname); - g_free(fw_pathname); - - /* check below 4G */ - buf = g_malloc0(BLOB_SIZE); - memread(0x100000000ULL - BLOB_SIZE, buf, BLOB_SIZE); - for (i = 0; i < BLOB_SIZE; ++i) { - g_assert_cmphex(buf[i], ==, (uint8_t)i); - } - - /* check in ISA space too */ - memset(buf, 0, BLOB_SIZE); - isa_bios_size = ISA_BIOS_MAXSZ < BLOB_SIZE ? ISA_BIOS_MAXSZ : BLOB_SIZE; - memread(0x100000 - isa_bios_size, buf, isa_bios_size); - for (i = 0; i < isa_bios_size; ++i) { - g_assert_cmphex(buf[i], ==, - (uint8_t)((BLOB_SIZE - isa_bios_size) + i)); - } - - g_free(buf); - qtest_end(); -} - -static void add_firmware_test(const char *testpath, - void (*setup_fixture)(FirmwareTestFixture *f, - gconstpointer test_data)) -{ - qtest_add(testpath, FirmwareTestFixture, NULL, setup_fixture, - test_i440fx_firmware, NULL); -} - -static void request_bios(FirmwareTestFixture *fixture, - gconstpointer user_data) -{ - fixture->is_bios = true; -} - -static void request_pflash(FirmwareTestFixture *fixture, - gconstpointer user_data) -{ - fixture->is_bios = false; -} - -int main(int argc, char **argv) -{ - TestData data; - - g_test_init(&argc, &argv, NULL); - - data.num_cpus = 1; - - qtest_add_data_func("i440fx/defaults", &data, test_i440fx_defaults); - qtest_add_data_func("i440fx/pam", &data, test_i440fx_pam); - add_firmware_test("i440fx/firmware/bios", request_bios); - add_firmware_test("i440fx/firmware/pflash", request_pflash); - - return g_test_run(); -} diff --git a/tests/i82801b11-test.c b/tests/i82801b11-test.c deleted file mode 100644 index 4345da338b..0000000000 --- a/tests/i82801b11-test.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * QTest testcase for i82801b11 - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) -{ -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/i82801b11/nop", nop); - - qtest_start("-machine q35 -device i82801b11-bridge,bus=pcie.0,addr=1e.0"); - ret = g_test_run(); - - qtest_end(); - - return ret; -} diff --git a/tests/ide-test.c b/tests/ide-test.c deleted file mode 100644 index 0277e7d5a9..0000000000 --- a/tests/ide-test.c +++ /dev/null @@ -1,1092 +0,0 @@ -/* - * IDE test cases - * - * Copyright (c) 2013 Kevin Wolf - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - - -#include "libqtest.h" -#include "libqos/libqos.h" -#include "libqos/pci-pc.h" -#include "libqos/malloc-pc.h" -#include "qapi/qmp/qdict.h" -#include "qemu-common.h" -#include "qemu/bswap.h" -#include "hw/pci/pci_ids.h" -#include "hw/pci/pci_regs.h" - -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(q, ...) qobject_unref(qtest_qmp(q, __VA_ARGS__)) - -#define TEST_IMAGE_SIZE 64 * 1024 * 1024 - -#define IDE_PCI_DEV 1 -#define IDE_PCI_FUNC 1 - -#define IDE_BASE 0x1f0 -#define IDE_PRIMARY_IRQ 14 - -#define ATAPI_BLOCK_SIZE 2048 - -/* How many bytes to receive via ATAPI PIO at one time. - * Must be less than 0xFFFF. */ -#define BYTE_COUNT_LIMIT 5120 - -enum { - reg_data = 0x0, - reg_feature = 0x1, - reg_error = 0x1, - reg_nsectors = 0x2, - reg_lba_low = 0x3, - reg_lba_middle = 0x4, - reg_lba_high = 0x5, - reg_device = 0x6, - reg_status = 0x7, - reg_command = 0x7, -}; - -enum { - BSY = 0x80, - DRDY = 0x40, - DF = 0x20, - DRQ = 0x08, - ERR = 0x01, -}; - -/* Error field */ -enum { - ABRT = 0x04, -}; - -enum { - DEV = 0x10, - LBA = 0x40, -}; - -enum { - bmreg_cmd = 0x0, - bmreg_status = 0x2, - bmreg_prdt = 0x4, -}; - -enum { - CMD_DSM = 0x06, - CMD_READ_DMA = 0xc8, - CMD_WRITE_DMA = 0xca, - CMD_FLUSH_CACHE = 0xe7, - CMD_IDENTIFY = 0xec, - CMD_PACKET = 0xa0, - - CMDF_ABORT = 0x100, - CMDF_NO_BM = 0x200, -}; - -enum { - BM_CMD_START = 0x1, - BM_CMD_WRITE = 0x8, /* write = from device to memory */ -}; - -enum { - BM_STS_ACTIVE = 0x1, - BM_STS_ERROR = 0x2, - BM_STS_INTR = 0x4, -}; - -enum { - PRDT_EOT = 0x80000000, -}; - -#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) -#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) - -static QPCIBus *pcibus = NULL; -static QGuestAllocator guest_malloc; - -static char tmp_path[] = "/tmp/qtest.XXXXXX"; -static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; - -static QTestState *ide_test_start(const char *cmdline_fmt, ...) -{ - QTestState *qts; - va_list ap; - - va_start(ap, cmdline_fmt); - qts = qtest_vinitf(cmdline_fmt, ap); - va_end(ap); - - pc_alloc_init(&guest_malloc, qts, 0); - - return qts; -} - -static void ide_test_quit(QTestState *qts) -{ - if (pcibus) { - qpci_free_pc(pcibus); - pcibus = NULL; - } - alloc_destroy(&guest_malloc); - qtest_quit(qts); -} - -static QPCIDevice *get_pci_device(QTestState *qts, QPCIBar *bmdma_bar, - QPCIBar *ide_bar) -{ - QPCIDevice *dev; - uint16_t vendor_id, device_id; - - if (!pcibus) { - pcibus = qpci_new_pc(qts, NULL); - } - - /* Find PCI device and verify it's the right one */ - dev = qpci_device_find(pcibus, QPCI_DEVFN(IDE_PCI_DEV, IDE_PCI_FUNC)); - g_assert(dev != NULL); - - vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID); - device_id = qpci_config_readw(dev, PCI_DEVICE_ID); - g_assert(vendor_id == PCI_VENDOR_ID_INTEL); - g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1); - - /* Map bmdma BAR */ - *bmdma_bar = qpci_iomap(dev, 4, NULL); - - *ide_bar = qpci_legacy_iomap(dev, IDE_BASE); - - qpci_device_enable(dev); - - return dev; -} - -static void free_pci_device(QPCIDevice *dev) -{ - /* libqos doesn't have a function for this, so free it manually */ - g_free(dev); -} - -typedef struct PrdtEntry { - uint32_t addr; - uint32_t size; -} QEMU_PACKED PrdtEntry; - -#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) -#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) - -static uint64_t trim_range_le(uint64_t sector, uint16_t count) -{ - /* 2-byte range, 6-byte LBA */ - return cpu_to_le64(((uint64_t)count << 48) + sector); -} - -static int send_dma_request(QTestState *qts, int cmd, uint64_t sector, - int nb_sectors, PrdtEntry *prdt, int prdt_entries, - void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar, - uint64_t sector, int nb_sectors)) -{ - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uintptr_t guest_prdt; - size_t len; - bool from_dev; - uint8_t status; - int flags; - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - flags = cmd & ~0xff; - cmd &= 0xff; - - switch (cmd) { - case CMD_READ_DMA: - case CMD_PACKET: - /* Assuming we only test data reads w/ ATAPI, otherwise we need to know - * the SCSI command being sent in the packet, too. */ - from_dev = true; - break; - case CMD_DSM: - case CMD_WRITE_DMA: - from_dev = false; - break; - default: - g_assert_not_reached(); - } - - if (flags & CMDF_NO_BM) { - qpci_config_writew(dev, PCI_COMMAND, - PCI_COMMAND_IO | PCI_COMMAND_MEMORY); - } - - /* Select device 0 */ - qpci_io_writeb(dev, ide_bar, reg_device, 0 | LBA); - - /* Stop any running transfer, clear any pending interrupt */ - qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); - qpci_io_writeb(dev, bmdma_bar, bmreg_status, BM_STS_INTR); - - /* Setup PRDT */ - len = sizeof(*prdt) * prdt_entries; - guest_prdt = guest_alloc(&guest_malloc, len); - qtest_memwrite(qts, guest_prdt, prdt, len); - qpci_io_writel(dev, bmdma_bar, bmreg_prdt, guest_prdt); - - /* ATA DMA command */ - if (cmd == CMD_PACKET) { - /* Enables ATAPI DMA; otherwise PIO is attempted */ - qpci_io_writeb(dev, ide_bar, reg_feature, 0x01); - } else { - if (cmd == CMD_DSM) { - /* trim bit */ - qpci_io_writeb(dev, ide_bar, reg_feature, 0x01); - } - qpci_io_writeb(dev, ide_bar, reg_nsectors, nb_sectors); - qpci_io_writeb(dev, ide_bar, reg_lba_low, sector & 0xff); - qpci_io_writeb(dev, ide_bar, reg_lba_middle, (sector >> 8) & 0xff); - qpci_io_writeb(dev, ide_bar, reg_lba_high, (sector >> 16) & 0xff); - } - - qpci_io_writeb(dev, ide_bar, reg_command, cmd); - - if (post_exec) { - post_exec(dev, ide_bar, sector, nb_sectors); - } - - /* Start DMA transfer */ - qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, - BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0)); - - if (flags & CMDF_ABORT) { - qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); - } - - /* Wait for the DMA transfer to complete */ - do { - status = qpci_io_readb(dev, bmdma_bar, bmreg_status); - } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE); - - g_assert_cmpint(qtest_get_irq(qts, IDE_PRIMARY_IRQ), ==, - !!(status & BM_STS_INTR)); - - /* Check IDE status code */ - assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRDY); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), BSY | DRQ); - - /* Reading the status register clears the IRQ */ - g_assert(!qtest_get_irq(qts, IDE_PRIMARY_IRQ)); - - /* Stop DMA transfer if still active */ - if (status & BM_STS_ACTIVE) { - qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); - } - - free_pci_device(dev); - - return status; -} - -static QTestState *test_bmdma_setup(void) -{ - QTestState *qts; - - qts = ide_test_start( - "-drive file=%s,if=ide,cache=writeback,format=raw " - "-global ide-hd.serial=%s -global ide-hd.ver=%s", - tmp_path, "testdisk", "version"); - qtest_irq_intercept_in(qts, "ioapic"); - - return qts; -} - -static void test_bmdma_teardown(QTestState *qts) -{ - ide_test_quit(qts); -} - -static void test_bmdma_simple_rw(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t status; - uint8_t *buf; - uint8_t *cmpbuf; - size_t len = 512; - uintptr_t guest_buf; - PrdtEntry prdt[1]; - - qts = test_bmdma_setup(); - - guest_buf = guest_alloc(&guest_malloc, len); - prdt[0].addr = cpu_to_le32(guest_buf); - prdt[0].size = cpu_to_le32(len | PRDT_EOT); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - buf = g_malloc(len); - cmpbuf = g_malloc(len); - - /* Write 0x55 pattern to sector 0 */ - memset(buf, 0x55, len); - qtest_memwrite(qts, guest_buf, buf, len); - - status = send_dma_request(qts, CMD_WRITE_DMA, 0, 1, prdt, - ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - /* Write 0xaa pattern to sector 1 */ - memset(buf, 0xaa, len); - qtest_memwrite(qts, guest_buf, buf, len); - - status = send_dma_request(qts, CMD_WRITE_DMA, 1, 1, prdt, - ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - /* Read and verify 0x55 pattern in sector 0 */ - memset(cmpbuf, 0x55, len); - - status = send_dma_request(qts, CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), - NULL); - g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - qtest_memread(qts, guest_buf, buf, len); - g_assert(memcmp(buf, cmpbuf, len) == 0); - - /* Read and verify 0xaa pattern in sector 1 */ - memset(cmpbuf, 0xaa, len); - - status = send_dma_request(qts, CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), - NULL); - g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - qtest_memread(qts, guest_buf, buf, len); - g_assert(memcmp(buf, cmpbuf, len) == 0); - - free_pci_device(dev); - g_free(buf); - g_free(cmpbuf); - - test_bmdma_teardown(qts); -} - -static void test_bmdma_trim(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t status; - const uint64_t trim_range[] = { trim_range_le(0, 2), - trim_range_le(6, 8), - trim_range_le(10, 1), - }; - const uint64_t bad_range = trim_range_le(TEST_IMAGE_SIZE / 512 - 1, 2); - size_t len = 512; - uint8_t *buf; - uintptr_t guest_buf; - PrdtEntry prdt[1]; - - qts = test_bmdma_setup(); - - guest_buf = guest_alloc(&guest_malloc, len); - prdt[0].addr = cpu_to_le32(guest_buf), - prdt[0].size = cpu_to_le32(len | PRDT_EOT), - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - buf = g_malloc(len); - - /* Normal request */ - *((uint64_t *)buf) = trim_range[0]; - *((uint64_t *)buf + 1) = trim_range[1]; - - qtest_memwrite(qts, guest_buf, buf, 2 * sizeof(uint64_t)); - - status = send_dma_request(qts, CMD_DSM, 0, 1, prdt, - ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - /* Request contains invalid range */ - *((uint64_t *)buf) = trim_range[2]; - *((uint64_t *)buf + 1) = bad_range; - - qtest_memwrite(qts, guest_buf, buf, 2 * sizeof(uint64_t)); - - status = send_dma_request(qts, CMD_DSM, 0, 1, prdt, - ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), ERR); - assert_bit_set(qpci_io_readb(dev, ide_bar, reg_error), ABRT); - - free_pci_device(dev); - g_free(buf); - test_bmdma_teardown(qts); -} - -static void test_bmdma_short_prdt(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t status; - - PrdtEntry prdt[] = { - { - .addr = 0, - .size = cpu_to_le32(0x10 | PRDT_EOT), - }, - }; - - qts = test_bmdma_setup(); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - /* Normal request */ - status = send_dma_request(qts, CMD_READ_DMA, 0, 1, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, 0); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - /* Abort the request before it completes */ - status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 1, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, 0); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - free_pci_device(dev); - test_bmdma_teardown(qts); -} - -static void test_bmdma_one_sector_short_prdt(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t status; - - /* Read 2 sectors but only give 1 sector in PRDT */ - PrdtEntry prdt[] = { - { - .addr = 0, - .size = cpu_to_le32(0x200 | PRDT_EOT), - }, - }; - - qts = test_bmdma_setup(); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - /* Normal request */ - status = send_dma_request(qts, CMD_READ_DMA, 0, 2, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, 0); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - /* Abort the request before it completes */ - status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 2, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, 0); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - free_pci_device(dev); - test_bmdma_teardown(qts); -} - -static void test_bmdma_long_prdt(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t status; - - PrdtEntry prdt[] = { - { - .addr = 0, - .size = cpu_to_le32(0x1000 | PRDT_EOT), - }, - }; - - qts = test_bmdma_setup(); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - /* Normal request */ - status = send_dma_request(qts, CMD_READ_DMA, 0, 1, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - /* Abort the request before it completes */ - status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 1, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - free_pci_device(dev); - test_bmdma_teardown(qts); -} - -static void test_bmdma_no_busmaster(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t status; - - qts = test_bmdma_setup(); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - /* No PRDT_EOT, each entry addr 0/size 64k, and in theory qemu shouldn't be - * able to access it anyway because the Bus Master bit in the PCI command - * register isn't set. This is complete nonsense, but it used to be pretty - * good at confusing and occasionally crashing qemu. */ - PrdtEntry prdt[4096] = { }; - - status = send_dma_request(qts, CMD_READ_DMA | CMDF_NO_BM, 0, 512, - prdt, ARRAY_SIZE(prdt), NULL); - - /* Not entirely clear what the expected result is, but this is what we get - * in practice. At least we want to be aware of any changes. */ - g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - free_pci_device(dev); - test_bmdma_teardown(qts); -} - -static void string_cpu_to_be16(uint16_t *s, size_t bytes) -{ - g_assert((bytes & 1) == 0); - bytes /= 2; - - while (bytes--) { - *s = cpu_to_be16(*s); - s++; - } -} - -static void test_identify(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t data; - uint16_t buf[256]; - int i; - int ret; - - qts = ide_test_start( - "-drive file=%s,if=ide,cache=writeback,format=raw " - "-global ide-hd.serial=%s -global ide-hd.ver=%s", - tmp_path, "testdisk", "version"); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - /* IDENTIFY command on device 0*/ - qpci_io_writeb(dev, ide_bar, reg_device, 0); - qpci_io_writeb(dev, ide_bar, reg_command, CMD_IDENTIFY); - - /* Read in the IDENTIFY buffer and check registers */ - data = qpci_io_readb(dev, ide_bar, reg_device); - g_assert_cmpint(data & DEV, ==, 0); - - for (i = 0; i < 256; i++) { - data = qpci_io_readb(dev, ide_bar, reg_status); - assert_bit_set(data, DRDY | DRQ); - assert_bit_clear(data, BSY | DF | ERR); - - buf[i] = qpci_io_readw(dev, ide_bar, reg_data); - } - - data = qpci_io_readb(dev, ide_bar, reg_status); - assert_bit_set(data, DRDY); - assert_bit_clear(data, BSY | DF | ERR | DRQ); - - /* Check serial number/version in the buffer */ - string_cpu_to_be16(&buf[10], 20); - ret = memcmp(&buf[10], "testdisk ", 20); - g_assert(ret == 0); - - string_cpu_to_be16(&buf[23], 8); - ret = memcmp(&buf[23], "version ", 8); - g_assert(ret == 0); - - /* Write cache enabled bit */ - assert_bit_set(buf[85], 0x20); - - ide_test_quit(qts); - free_pci_device(dev); -} - -/* - * Write sector 1 with random data to make IDE storage dirty - * Needed for flush tests so that flushes actually go though the block layer - */ -static void make_dirty(QTestState *qts, uint8_t device) -{ - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t status; - size_t len = 512; - uintptr_t guest_buf; - void* buf; - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - guest_buf = guest_alloc(&guest_malloc, len); - buf = g_malloc(len); - memset(buf, rand() % 255 + 1, len); - g_assert(guest_buf); - g_assert(buf); - - qtest_memwrite(qts, guest_buf, buf, len); - - PrdtEntry prdt[] = { - { - .addr = cpu_to_le32(guest_buf), - .size = cpu_to_le32(len | PRDT_EOT), - }, - }; - - status = send_dma_request(qts, CMD_WRITE_DMA, 1, 1, prdt, - ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - g_free(buf); - free_pci_device(dev); -} - -static void test_flush(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t data; - - qts = ide_test_start( - "-drive file=blkdebug::%s,if=ide,cache=writeback,format=raw", - tmp_path); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - qtest_irq_intercept_in(qts, "ioapic"); - - /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */ - make_dirty(qts, 0); - - /* Delay the completion of the flush request until we explicitly do it */ - g_free(qtest_hmp(qts, "qemu-io ide0-hd0 \"break flush_to_os A\"")); - - /* FLUSH CACHE command on device 0*/ - qpci_io_writeb(dev, ide_bar, reg_device, 0); - qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); - - /* Check status while request is in flight*/ - data = qpci_io_readb(dev, ide_bar, reg_status); - assert_bit_set(data, BSY | DRDY); - assert_bit_clear(data, DF | ERR | DRQ); - - /* Complete the command */ - g_free(qtest_hmp(qts, "qemu-io ide0-hd0 \"resume A\"")); - - /* Check registers */ - data = qpci_io_readb(dev, ide_bar, reg_device); - g_assert_cmpint(data & DEV, ==, 0); - - do { - data = qpci_io_readb(dev, ide_bar, reg_status); - } while (data & BSY); - - assert_bit_set(data, DRDY); - assert_bit_clear(data, BSY | DF | ERR | DRQ); - - ide_test_quit(qts); - free_pci_device(dev); -} - -static void test_retry_flush(const char *machine) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t data; - - prepare_blkdebug_script(debug_path, "flush_to_disk"); - - qts = ide_test_start( - "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,format=raw," - "rerror=stop,werror=stop", - debug_path, tmp_path); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - qtest_irq_intercept_in(qts, "ioapic"); - - /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */ - make_dirty(qts, 0); - - /* FLUSH CACHE command on device 0*/ - qpci_io_writeb(dev, ide_bar, reg_device, 0); - qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); - - /* Check status while request is in flight*/ - data = qpci_io_readb(dev, ide_bar, reg_status); - assert_bit_set(data, BSY | DRDY); - assert_bit_clear(data, DF | ERR | DRQ); - - qtest_qmp_eventwait(qts, "STOP"); - - /* Complete the command */ - qmp_discard_response(qts, "{'execute':'cont' }"); - - /* Check registers */ - data = qpci_io_readb(dev, ide_bar, reg_device); - g_assert_cmpint(data & DEV, ==, 0); - - do { - data = qpci_io_readb(dev, ide_bar, reg_status); - } while (data & BSY); - - assert_bit_set(data, DRDY); - assert_bit_clear(data, BSY | DF | ERR | DRQ); - - ide_test_quit(qts); - free_pci_device(dev); -} - -static void test_flush_nodev(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - - qts = ide_test_start(""); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - /* FLUSH CACHE command on device 0*/ - qpci_io_writeb(dev, ide_bar, reg_device, 0); - qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); - - /* Just testing that qemu doesn't crash... */ - - free_pci_device(dev); - ide_test_quit(qts); -} - -static void test_flush_empty_drive(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - - qts = ide_test_start("-device ide-cd,bus=ide.0"); - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - /* FLUSH CACHE command on device 0 */ - qpci_io_writeb(dev, ide_bar, reg_device, 0); - qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); - - /* Just testing that qemu doesn't crash... */ - - free_pci_device(dev); - ide_test_quit(qts); -} - -static void test_pci_retry_flush(void) -{ - test_retry_flush("pc"); -} - -static void test_isa_retry_flush(void) -{ - test_retry_flush("isapc"); -} - -typedef struct Read10CDB { - uint8_t opcode; - uint8_t flags; - uint32_t lba; - uint8_t reserved; - uint16_t nblocks; - uint8_t control; - uint16_t padding; -} __attribute__((__packed__)) Read10CDB; - -static void send_scsi_cdb_read10(QPCIDevice *dev, QPCIBar ide_bar, - uint64_t lba, int nblocks) -{ - Read10CDB pkt = { .padding = 0 }; - int i; - - g_assert_cmpint(lba, <=, UINT32_MAX); - g_assert_cmpint(nblocks, <=, UINT16_MAX); - g_assert_cmpint(nblocks, >=, 0); - - /* Construct SCSI CDB packet */ - pkt.opcode = 0x28; - pkt.lba = cpu_to_be32(lba); - pkt.nblocks = cpu_to_be16(nblocks); - - /* Send Packet */ - for (i = 0; i < sizeof(Read10CDB)/2; i++) { - qpci_io_writew(dev, ide_bar, reg_data, - le16_to_cpu(((uint16_t *)&pkt)[i])); - } -} - -static void nsleep(QTestState *qts, int64_t nsecs) -{ - const struct timespec val = { .tv_nsec = nsecs }; - nanosleep(&val, NULL); - qtest_clock_set(qts, nsecs); -} - -static uint8_t ide_wait_clear(QTestState *qts, uint8_t flag) -{ - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t data; - time_t st; - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - /* Wait with a 5 second timeout */ - time(&st); - while (true) { - data = qpci_io_readb(dev, ide_bar, reg_status); - if (!(data & flag)) { - free_pci_device(dev); - return data; - } - if (difftime(time(NULL), st) > 5.0) { - break; - } - nsleep(qts, 400); - } - g_assert_not_reached(); -} - -static void ide_wait_intr(QTestState *qts, int irq) -{ - time_t st; - bool intr; - - time(&st); - while (true) { - intr = qtest_get_irq(qts, irq); - if (intr) { - return; - } - if (difftime(time(NULL), st) > 5.0) { - break; - } - nsleep(qts, 400); - } - - g_assert_not_reached(); -} - -static void cdrom_pio_impl(int nblocks) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - FILE *fh; - int patt_blocks = MAX(16, nblocks); - size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks; - char *pattern = g_malloc(patt_len); - size_t rxsize = ATAPI_BLOCK_SIZE * nblocks; - uint16_t *rx = g_malloc0(rxsize); - int i, j; - uint8_t data; - uint16_t limit; - size_t ret; - - /* Prepopulate the CDROM with an interesting pattern */ - generate_pattern(pattern, patt_len, ATAPI_BLOCK_SIZE); - fh = fopen(tmp_path, "w+"); - ret = fwrite(pattern, ATAPI_BLOCK_SIZE, patt_blocks, fh); - g_assert_cmpint(ret, ==, patt_blocks); - fclose(fh); - - qts = ide_test_start( - "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " - "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - qtest_irq_intercept_in(qts, "ioapic"); - - /* PACKET command on device 0 */ - qpci_io_writeb(dev, ide_bar, reg_device, 0); - qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); - qpci_io_writeb(dev, ide_bar, reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); - qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); - /* HP0: Check_Status_A State */ - nsleep(qts, 400); - data = ide_wait_clear(qts, BSY); - /* HP1: Send_Packet State */ - assert_bit_set(data, DRQ | DRDY); - assert_bit_clear(data, ERR | DF | BSY); - - /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */ - send_scsi_cdb_read10(dev, ide_bar, 0, nblocks); - - /* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. - * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. - * We allow an odd limit only when the remaining transfer size is - * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only - * request n blocks, so our request size is always even. - * For this reason, we assume there is never a hanging byte to fetch. */ - g_assert(!(rxsize & 1)); - limit = BYTE_COUNT_LIMIT & ~1; - for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) { - size_t offset = i * (limit / 2); - size_t rem = (rxsize / 2) - offset; - - /* HP3: INTRQ_Wait */ - ide_wait_intr(qts, IDE_PRIMARY_IRQ); - - /* HP2: Check_Status_B (and clear IRQ) */ - data = ide_wait_clear(qts, BSY); - assert_bit_set(data, DRQ | DRDY); - assert_bit_clear(data, ERR | DF | BSY); - - /* HP4: Transfer_Data */ - for (j = 0; j < MIN((limit / 2), rem); j++) { - rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar, - reg_data)); - } - } - - /* Check for final completion IRQ */ - ide_wait_intr(qts, IDE_PRIMARY_IRQ); - - /* Sanity check final state */ - data = ide_wait_clear(qts, DRQ); - assert_bit_set(data, DRDY); - assert_bit_clear(data, DRQ | ERR | DF | BSY); - - g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); - g_free(pattern); - g_free(rx); - test_bmdma_teardown(qts); - free_pci_device(dev); -} - -static void test_cdrom_pio(void) -{ - cdrom_pio_impl(1); -} - -static void test_cdrom_pio_large(void) -{ - /* Test a few loops of the PIO DRQ mechanism. */ - cdrom_pio_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE); -} - - -static void test_cdrom_dma(void) -{ - QTestState *qts; - static const size_t len = ATAPI_BLOCK_SIZE; - size_t ret; - char *pattern = g_malloc(ATAPI_BLOCK_SIZE * 16); - char *rx = g_malloc0(len); - uintptr_t guest_buf; - PrdtEntry prdt[1]; - FILE *fh; - - qts = ide_test_start( - "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " - "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); - qtest_irq_intercept_in(qts, "ioapic"); - - guest_buf = guest_alloc(&guest_malloc, len); - prdt[0].addr = cpu_to_le32(guest_buf); - prdt[0].size = cpu_to_le32(len | PRDT_EOT); - - generate_pattern(pattern, ATAPI_BLOCK_SIZE * 16, ATAPI_BLOCK_SIZE); - fh = fopen(tmp_path, "w+"); - ret = fwrite(pattern, ATAPI_BLOCK_SIZE, 16, fh); - g_assert_cmpint(ret, ==, 16); - fclose(fh); - - send_dma_request(qts, CMD_PACKET, 0, 1, prdt, 1, send_scsi_cdb_read10); - - /* Read back data from guest memory into local qtest memory */ - qtest_memread(qts, guest_buf, rx, len); - g_assert_cmpint(memcmp(pattern, rx, len), ==, 0); - - g_free(pattern); - g_free(rx); - test_bmdma_teardown(qts); -} - -int main(int argc, char **argv) -{ - int fd; - int ret; - - /* Create temporary blkdebug instructions */ - fd = mkstemp(debug_path); - g_assert(fd >= 0); - close(fd); - - /* Create a temporary raw image */ - fd = mkstemp(tmp_path); - g_assert(fd >= 0); - ret = ftruncate(fd, TEST_IMAGE_SIZE); - g_assert(ret == 0); - close(fd); - - /* Run the tests */ - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/ide/identify", test_identify); - - qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw); - qtest_add_func("/ide/bmdma/trim", test_bmdma_trim); - qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt); - qtest_add_func("/ide/bmdma/one_sector_short_prdt", - test_bmdma_one_sector_short_prdt); - qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt); - qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster); - - qtest_add_func("/ide/flush", test_flush); - qtest_add_func("/ide/flush/nodev", test_flush_nodev); - qtest_add_func("/ide/flush/empty_drive", test_flush_empty_drive); - qtest_add_func("/ide/flush/retry_pci", test_pci_retry_flush); - qtest_add_func("/ide/flush/retry_isa", test_isa_retry_flush); - - qtest_add_func("/ide/cdrom/pio", test_cdrom_pio); - qtest_add_func("/ide/cdrom/pio_large", test_cdrom_pio_large); - qtest_add_func("/ide/cdrom/dma", test_cdrom_dma); - - ret = g_test_run(); - - /* Cleanup */ - unlink(tmp_path); - unlink(debug_path); - - return ret; -} diff --git a/tests/intel-hda-test.c b/tests/intel-hda-test.c deleted file mode 100644 index fc25ccc33c..0000000000 --- a/tests/intel-hda-test.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * QTest testcase for Intel HDA - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" - -#define HDA_ID "hda0" -#define CODEC_DEVICES " -device hda-output,bus=" HDA_ID ".0" \ - " -device hda-micro,bus=" HDA_ID ".0" \ - " -device hda-duplex,bus=" HDA_ID ".0" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void ich6_test(void) -{ - qtest_start("-device intel-hda,id=" HDA_ID CODEC_DEVICES); - qtest_end(); -} - -static void ich9_test(void) -{ - qtest_start("-machine q35 -device ich9-intel-hda,bus=pcie.0,addr=1b.0,id=" - HDA_ID CODEC_DEVICES); - qtest_end(); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - qtest_add_func("/intel-hda/ich6", ich6_test); - qtest_add_func("/intel-hda/ich9", ich9_test); - - return g_test_run(); -} diff --git a/tests/ioh3420-test.c b/tests/ioh3420-test.c deleted file mode 100644 index f6ca43cca7..0000000000 --- a/tests/ioh3420-test.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * QTest testcase for Intel X58 north bridge IOH - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) -{ -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/ioh3420/nop", nop); - - qtest_start("-machine q35 -device ioh3420,bus=pcie.0,addr=1c.0,port=1," - "chassis=1,multifunction=on"); - ret = g_test_run(); - - qtest_end(); - - return ret; -} diff --git a/tests/ipmi-bt-test.c b/tests/ipmi-bt-test.c deleted file mode 100644 index a42207d416..0000000000 --- a/tests/ipmi-bt-test.c +++ /dev/null @@ -1,425 +0,0 @@ -/* - * IPMI BT test cases, using the external interface for checking - * - * Copyright (c) 2012 Corey Minyard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - -#include -#include -#include -#include - - -#include "libqtest-single.h" -#include "qemu-common.h" - -#define IPMI_IRQ 5 - -#define IPMI_BT_BASE 0xe4 - -#define IPMI_BT_CTLREG_CLR_WR_PTR 0 -#define IPMI_BT_CTLREG_CLR_RD_PTR 1 -#define IPMI_BT_CTLREG_H2B_ATN 2 -#define IPMI_BT_CTLREG_B2H_ATN 3 -#define IPMI_BT_CTLREG_SMS_ATN 4 -#define IPMI_BT_CTLREG_H_BUSY 6 -#define IPMI_BT_CTLREG_B_BUSY 7 - -#define IPMI_BT_CTLREG_GET(b) ((bt_get_ctrlreg() >> (b)) & 1) -#define IPMI_BT_CTLREG_GET_H2B_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H2B_ATN) -#define IPMI_BT_CTLREG_GET_B2H_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B2H_ATN) -#define IPMI_BT_CTLREG_GET_SMS_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_SMS_ATN) -#define IPMI_BT_CTLREG_GET_H_BUSY() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H_BUSY) -#define IPMI_BT_CTLREG_GET_B_BUSY() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B_BUSY) - -#define IPMI_BT_CTLREG_SET(b) bt_write_ctrlreg(1 << (b)) -#define IPMI_BT_CTLREG_SET_CLR_WR_PTR() IPMI_BT_CTLREG_SET( \ - IPMI_BT_CTLREG_CLR_WR_PTR) -#define IPMI_BT_CTLREG_SET_CLR_RD_PTR() IPMI_BT_CTLREG_SET( \ - IPMI_BT_CTLREG_CLR_RD_PTR) -#define IPMI_BT_CTLREG_SET_H2B_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H2B_ATN) -#define IPMI_BT_CTLREG_SET_B2H_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_B2H_ATN) -#define IPMI_BT_CTLREG_SET_SMS_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_SMS_ATN) -#define IPMI_BT_CTLREG_SET_H_BUSY() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H_BUSY) - -static int bt_ints_enabled; - -static uint8_t bt_get_ctrlreg(void) -{ - return inb(IPMI_BT_BASE); -} - -static void bt_write_ctrlreg(uint8_t val) -{ - outb(IPMI_BT_BASE, val); -} - -static uint8_t bt_get_buf(void) -{ - return inb(IPMI_BT_BASE + 1); -} - -static void bt_write_buf(uint8_t val) -{ - outb(IPMI_BT_BASE + 1, val); -} - -static uint8_t bt_get_irqreg(void) -{ - return inb(IPMI_BT_BASE + 2); -} - -static void bt_write_irqreg(uint8_t val) -{ - outb(IPMI_BT_BASE + 2, val); -} - -static void bt_wait_b_busy(void) -{ - unsigned int count = 1000; - while (IPMI_BT_CTLREG_GET_B_BUSY() != 0) { - g_assert(--count != 0); - usleep(100); - } -} - -static void bt_wait_b2h_atn(void) -{ - unsigned int count = 1000; - while (IPMI_BT_CTLREG_GET_B2H_ATN() == 0) { - g_assert(--count != 0); - usleep(100); - } -} - - -static int emu_lfd; -static int emu_fd; -static in_port_t emu_port; -static uint8_t inbuf[100]; -static unsigned int inbuf_len; -static unsigned int inbuf_pos; -static int last_was_aa; - -static void read_emu_data(void) -{ - fd_set readfds; - int rv; - struct timeval tv; - - FD_ZERO(&readfds); - FD_SET(emu_fd, &readfds); - tv.tv_sec = 10; - tv.tv_usec = 0; - rv = select(emu_fd + 1, &readfds, NULL, NULL, &tv); - if (rv == -1) { - perror("select"); - } - g_assert(rv == 1); - rv = read(emu_fd, inbuf, sizeof(inbuf)); - if (rv == -1) { - perror("read"); - } - g_assert(rv > 0); - inbuf_len = rv; - inbuf_pos = 0; -} - -static void write_emu_msg(uint8_t *msg, unsigned int len) -{ - int rv; - -#ifdef DEBUG_TEST - { - unsigned int i; - printf("sending:"); - for (i = 0; i < len; i++) { - printf(" %2.2x", msg[i]); - } - printf("\n"); - } -#endif - rv = write(emu_fd, msg, len); - g_assert(rv == len); -} - -static void get_emu_msg(uint8_t *msg, unsigned int *len) -{ - unsigned int outpos = 0; - - for (;;) { - while (inbuf_pos < inbuf_len) { - uint8_t ch = inbuf[inbuf_pos++]; - - g_assert(outpos < *len); - if (last_was_aa) { - assert(ch & 0x10); - msg[outpos++] = ch & ~0x10; - last_was_aa = 0; - } else if (ch == 0xaa) { - last_was_aa = 1; - } else { - msg[outpos++] = ch; - if ((ch == 0xa0) || (ch == 0xa1)) { - /* Message complete */ - *len = outpos; - goto done; - } - } - } - read_emu_data(); - } - done: -#ifdef DEBUG_TEST - { - unsigned int i; - printf("Msg:"); - for (i = 0; i < outpos; i++) { - printf(" %2.2x", msg[i]); - } - printf("\n"); - } -#endif - return; -} - -static uint8_t -ipmb_checksum(const unsigned char *data, int size, unsigned char start) -{ - unsigned char csum = start; - - for (; size > 0; size--, data++) { - csum += *data; - } - return csum; -} - -static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 }; -static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00 }; - -static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f }; -static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 }; -static uint8_t enable_irq_cmd[] = { 0x05, 0xa1 }; - -static void emu_msg_handler(void) -{ - uint8_t msg[100]; - unsigned int msg_len = sizeof(msg); - - get_emu_msg(msg, &msg_len); - g_assert(msg_len >= 5); - g_assert(msg[msg_len - 1] == 0xa0); - msg_len--; - g_assert(ipmb_checksum(msg, msg_len, 0) == 0); - msg_len--; - if ((msg[1] == get_dev_id_cmd[0]) && (msg[2] == get_dev_id_cmd[1])) { - memcpy(msg + 1, get_dev_id_rsp, sizeof(get_dev_id_rsp)); - msg_len = sizeof(get_dev_id_rsp) + 1; - msg[msg_len] = -ipmb_checksum(msg, msg_len, 0); - msg_len++; - msg[msg_len++] = 0xa0; - write_emu_msg(msg, msg_len); - } else if ((msg[1] == set_bmc_globals_cmd[0]) && - (msg[2] == set_bmc_globals_cmd[1])) { - write_emu_msg(enable_irq_cmd, sizeof(enable_irq_cmd)); - memcpy(msg + 1, set_bmc_globals_rsp, sizeof(set_bmc_globals_rsp)); - msg_len = sizeof(set_bmc_globals_rsp) + 1; - msg[msg_len] = -ipmb_checksum(msg, msg_len, 0); - msg_len++; - msg[msg_len++] = 0xa0; - write_emu_msg(msg, msg_len); - } else { - g_assert(0); - } -} - -static void bt_cmd(uint8_t *cmd, unsigned int cmd_len, - uint8_t *rsp, unsigned int *rsp_len) -{ - unsigned int i, len, j = 0; - uint8_t seq = 5; - - /* Should be idle */ - g_assert(bt_get_ctrlreg() == 0); - - bt_wait_b_busy(); - IPMI_BT_CTLREG_SET_CLR_WR_PTR(); - bt_write_buf(cmd_len + 1); - bt_write_buf(cmd[0]); - bt_write_buf(seq); - for (i = 1; i < cmd_len; i++) { - bt_write_buf(cmd[i]); - } - IPMI_BT_CTLREG_SET_H2B_ATN(); - - emu_msg_handler(); /* We should get a message on the socket here. */ - - bt_wait_b2h_atn(); - if (bt_ints_enabled) { - g_assert((bt_get_irqreg() & 0x02) == 0x02); - g_assert(get_irq(IPMI_IRQ)); - bt_write_irqreg(0x03); - } else { - g_assert(!get_irq(IPMI_IRQ)); - } - IPMI_BT_CTLREG_SET_H_BUSY(); - IPMI_BT_CTLREG_SET_B2H_ATN(); - IPMI_BT_CTLREG_SET_CLR_RD_PTR(); - len = bt_get_buf(); - g_assert(len >= 4); - rsp[0] = bt_get_buf(); - assert(bt_get_buf() == seq); - len--; - for (j = 1; j < len; j++) { - rsp[j] = bt_get_buf(); - } - IPMI_BT_CTLREG_SET_H_BUSY(); - *rsp_len = j; -} - - -/* - * We should get a connect request and a short message with capabilities. - */ -static void test_connect(void) -{ - fd_set readfds; - int rv; - int val; - struct timeval tv; - uint8_t msg[100]; - unsigned int msglen; - static uint8_t exp1[] = { 0xff, 0x01, 0xa1 }; /* A protocol version */ - static uint8_t exp2[] = { 0x08, 0x3f, 0xa1 }; /* A capabilities cmd */ - - FD_ZERO(&readfds); - FD_SET(emu_lfd, &readfds); - tv.tv_sec = 10; - tv.tv_usec = 0; - rv = select(emu_lfd + 1, &readfds, NULL, NULL, &tv); - g_assert(rv == 1); - emu_fd = accept(emu_lfd, NULL, 0); - if (emu_fd < 0) { - perror("accept"); - } - g_assert(emu_fd >= 0); - - val = 1; - rv = setsockopt(emu_fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); - g_assert(rv != -1); - - /* Report our version */ - write_emu_msg(exp1, sizeof(exp1)); - - /* Validate that we get the info we expect. */ - msglen = sizeof(msg); - get_emu_msg(msg, &msglen); - g_assert(msglen == sizeof(exp1)); - g_assert(memcmp(msg, exp1, msglen) == 0); - msglen = sizeof(msg); - get_emu_msg(msg, &msglen); - g_assert(msglen == sizeof(exp2)); - g_assert(memcmp(msg, exp2, msglen) == 0); -} - -/* - * Send a get_device_id to do a basic test. - */ -static void test_bt_base(void) -{ - uint8_t rsp[20]; - unsigned int rsplen = sizeof(rsp); - - bt_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); - g_assert(rsplen == sizeof(get_dev_id_rsp)); - g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0); -} - -/* - * Enable IRQs for the interface. - */ -static void test_enable_irq(void) -{ - uint8_t rsp[20]; - unsigned int rsplen = sizeof(rsp); - - bt_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen); - g_assert(rsplen == sizeof(set_bmc_globals_rsp)); - g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0); - bt_write_irqreg(0x01); - bt_ints_enabled = 1; -} - -/* - * Create a local TCP socket with any port, then save off the port we got. - */ -static void open_socket(void) -{ - struct sockaddr_in myaddr; - socklen_t addrlen; - - myaddr.sin_family = AF_INET; - myaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - myaddr.sin_port = 0; - emu_lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (emu_lfd == -1) { - perror("socket"); - exit(1); - } - if (bind(emu_lfd, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) { - perror("bind"); - exit(1); - } - addrlen = sizeof(myaddr); - if (getsockname(emu_lfd, (struct sockaddr *) &myaddr , &addrlen) == -1) { - perror("getsockname"); - exit(1); - } - emu_port = ntohs(myaddr.sin_port); - assert(listen(emu_lfd, 1) != -1); -} - -int main(int argc, char **argv) -{ - int ret; - - open_socket(); - - /* Run the tests */ - g_test_init(&argc, &argv, NULL); - - global_qtest = qtest_initf( - " -chardev socket,id=ipmi0,host=localhost,port=%d,reconnect=10" - " -device ipmi-bmc-extern,chardev=ipmi0,id=bmc0" - " -device isa-ipmi-bt,bmc=bmc0", emu_port); - qtest_irq_intercept_in(global_qtest, "ioapic"); - qtest_add_func("/ipmi/extern/connect", test_connect); - qtest_add_func("/ipmi/extern/bt_base", test_bt_base); - qtest_add_func("/ipmi/extern/bt_enable_irq", test_enable_irq); - qtest_add_func("/ipmi/extern/bt_base_irq", test_bt_base); - ret = g_test_run(); - qtest_quit(global_qtest); - - return ret; -} diff --git a/tests/ipmi-kcs-test.c b/tests/ipmi-kcs-test.c deleted file mode 100644 index 693a6aacb5..0000000000 --- a/tests/ipmi-kcs-test.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * IPMI KCS test cases, using the local interface. - * - * Copyright (c) 2012 Corey Minyard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - -#include "libqtest-single.h" - -#define IPMI_IRQ 5 - -#define IPMI_KCS_BASE 0xca2 - -#define IPMI_KCS_STATUS_ABORT 0x60 -#define IPMI_KCS_CMD_WRITE_START 0x61 -#define IPMI_KCS_CMD_WRITE_END 0x62 -#define IPMI_KCS_CMD_READ 0x68 - -#define IPMI_KCS_ABORTED_BY_CMD 0x01 - -#define IPMI_KCS_CMDREG_GET_STATE() ((kcs_get_cmdreg() >> 6) & 3) -#define IPMI_KCS_STATE_IDLE 0 -#define IPMI_KCS_STATE_READ 1 -#define IPMI_KCS_STATE_WRITE 2 -#define IPMI_KCS_STATE_ERROR 3 -#define IPMI_KCS_CMDREG_GET_CD() ((kcs_get_cmdreg() >> 3) & 1) -#define IPMI_KCS_CMDREG_GET_ATN() ((kcs_get_cmdreg() >> 2) & 1) -#define IPMI_KCS_CMDREG_GET_IBF() ((kcs_get_cmdreg() >> 1) & 1) -#define IPMI_KCS_CMDREG_GET_OBF() ((kcs_get_cmdreg() >> 0) & 1) - -static int kcs_ints_enabled; - -static uint8_t kcs_get_cmdreg(void) -{ - return inb(IPMI_KCS_BASE + 1); -} - -static void kcs_write_cmdreg(uint8_t val) -{ - outb(IPMI_KCS_BASE + 1, val); -} - -static uint8_t kcs_get_datareg(void) -{ - return inb(IPMI_KCS_BASE); -} - -static void kcs_write_datareg(uint8_t val) -{ - outb(IPMI_KCS_BASE, val); -} - -static void kcs_wait_ibf(void) -{ - unsigned int count = 1000; - while (IPMI_KCS_CMDREG_GET_IBF() != 0) { - g_assert(--count != 0); - } -} - -static void kcs_wait_obf(void) -{ - unsigned int count = 1000; - while (IPMI_KCS_CMDREG_GET_OBF() == 0) { - g_assert(--count != 0); - } -} - -static void kcs_clear_obf(void) -{ - if (kcs_ints_enabled) { - g_assert(get_irq(IPMI_IRQ)); - } else { - g_assert(!get_irq(IPMI_IRQ)); - } - g_assert(IPMI_KCS_CMDREG_GET_OBF() == 1); - kcs_get_datareg(); - g_assert(IPMI_KCS_CMDREG_GET_OBF() == 0); - g_assert(!get_irq(IPMI_IRQ)); -} - -static void kcs_check_state(uint8_t state) -{ - g_assert(IPMI_KCS_CMDREG_GET_STATE() == state); -} - -static void kcs_cmd(uint8_t *cmd, unsigned int cmd_len, - uint8_t *rsp, unsigned int *rsp_len) -{ - unsigned int i, j = 0; - - /* Should be idle */ - g_assert(kcs_get_cmdreg() == 0); - - kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START); - kcs_wait_ibf(); - kcs_check_state(IPMI_KCS_STATE_WRITE); - kcs_clear_obf(); - for (i = 0; i < cmd_len; i++) { - kcs_write_datareg(cmd[i]); - kcs_wait_ibf(); - kcs_check_state(IPMI_KCS_STATE_WRITE); - kcs_clear_obf(); - } - kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END); - kcs_wait_ibf(); - kcs_check_state(IPMI_KCS_STATE_WRITE); - kcs_clear_obf(); - kcs_write_datareg(0); - next_read_byte: - kcs_wait_ibf(); - switch (IPMI_KCS_CMDREG_GET_STATE()) { - case IPMI_KCS_STATE_READ: - kcs_wait_obf(); - g_assert(j < *rsp_len); - rsp[j++] = kcs_get_datareg(); - kcs_write_datareg(IPMI_KCS_CMD_READ); - goto next_read_byte; - break; - - case IPMI_KCS_STATE_IDLE: - kcs_wait_obf(); - kcs_get_datareg(); - break; - - default: - g_assert(0); - } - *rsp_len = j; -} - -static void kcs_abort(uint8_t *cmd, unsigned int cmd_len, - uint8_t *rsp, unsigned int *rsp_len) -{ - unsigned int i, j = 0; - unsigned int retries = 4; - - /* Should be idle */ - g_assert(kcs_get_cmdreg() == 0); - - kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START); - kcs_wait_ibf(); - kcs_check_state(IPMI_KCS_STATE_WRITE); - kcs_clear_obf(); - for (i = 0; i < cmd_len; i++) { - kcs_write_datareg(cmd[i]); - kcs_wait_ibf(); - kcs_check_state(IPMI_KCS_STATE_WRITE); - kcs_clear_obf(); - } - kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END); - kcs_wait_ibf(); - kcs_check_state(IPMI_KCS_STATE_WRITE); - kcs_clear_obf(); - kcs_write_datareg(0); - kcs_wait_ibf(); - switch (IPMI_KCS_CMDREG_GET_STATE()) { - case IPMI_KCS_STATE_READ: - kcs_wait_obf(); - g_assert(j < *rsp_len); - rsp[j++] = kcs_get_datareg(); - kcs_write_datareg(IPMI_KCS_CMD_READ); - break; - - default: - g_assert(0); - } - - /* Start the abort here */ - retry_abort: - g_assert(retries > 0); - - kcs_wait_ibf(); - kcs_write_cmdreg(IPMI_KCS_STATUS_ABORT); - kcs_wait_ibf(); - kcs_clear_obf(); - kcs_write_datareg(0); - kcs_wait_ibf(); - if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_READ) { - retries--; - goto retry_abort; - } - kcs_wait_obf(); - rsp[0] = kcs_get_datareg(); - kcs_write_datareg(IPMI_KCS_CMD_READ); - kcs_wait_ibf(); - if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_IDLE) { - retries--; - goto retry_abort; - } - kcs_wait_obf(); - kcs_clear_obf(); - - *rsp_len = j; -} - - -static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 }; -static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 }; - -/* - * Send a get_device_id to do a basic test. - */ -static void test_kcs_base(void) -{ - uint8_t rsp[20]; - unsigned int rsplen = sizeof(rsp); - - kcs_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); - g_assert(rsplen == sizeof(get_dev_id_rsp)); - g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0); -} - -/* - * Abort a kcs operation while reading - */ -static void test_kcs_abort(void) -{ - uint8_t rsp[20]; - unsigned int rsplen = sizeof(rsp); - - kcs_abort(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); - g_assert(rsp[0] == IPMI_KCS_ABORTED_BY_CMD); -} - -static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f }; -static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 }; - -/* - * Enable interrupts - */ -static void test_enable_irq(void) -{ - uint8_t rsp[20]; - unsigned int rsplen = sizeof(rsp); - - kcs_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen); - g_assert(rsplen == sizeof(set_bmc_globals_rsp)); - g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0); - kcs_ints_enabled = 1; -} - -int main(int argc, char **argv) -{ - char *cmdline; - int ret; - - /* Run the tests */ - g_test_init(&argc, &argv, NULL); - - cmdline = g_strdup_printf("-device ipmi-bmc-sim,id=bmc0" - " -device isa-ipmi-kcs,bmc=bmc0"); - qtest_start(cmdline); - g_free(cmdline); - qtest_irq_intercept_in(global_qtest, "ioapic"); - qtest_add_func("/ipmi/local/kcs_base", test_kcs_base); - qtest_add_func("/ipmi/local/kcs_abort", test_kcs_abort); - qtest_add_func("/ipmi/local/kcs_enable_irq", test_enable_irq); - qtest_add_func("/ipmi/local/kcs_base_irq", test_kcs_base); - qtest_add_func("/ipmi/local/kcs_abort_irq", test_kcs_abort); - ret = g_test_run(); - qtest_quit(global_qtest); - - return ret; -} diff --git a/tests/ipoctal232-test.c b/tests/ipoctal232-test.c deleted file mode 100644 index 53a8c9b13c..0000000000 --- a/tests/ipoctal232-test.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * QTest testcase for IndustryPack Octal-RS232 - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" - -typedef struct QIpoctal232 QIpoctal232; - -struct QIpoctal232 { - QOSGraphObject obj; -}; - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void *obj, void *data, QGuestAllocator *alloc) -{ -} - -static void *ipoctal232_create(void *pci_bus, QGuestAllocator *alloc, - void *addr) -{ - QIpoctal232 *ipoctal232 = g_new0(QIpoctal232, 1); - - return &ipoctal232->obj; -} - -static void ipoctal232_register_nodes(void) -{ - qos_node_create_driver("ipoctal232", ipoctal232_create); - qos_node_consumes("ipoctal232", "ipack", &(QOSGraphEdgeOptions) { - .extra_device_opts = "bus=ipack0.0", - }); -} - -libqos_init(ipoctal232_register_nodes); - -static void register_ipoctal232_test(void) -{ - qos_add_test("nop", "ipoctal232", nop, NULL); -} - -libqos_init(register_ipoctal232_test); diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c deleted file mode 100644 index ecda256472..0000000000 --- a/tests/ivshmem-test.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * QTest testcase for ivshmem - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * Copyright (c) 2015 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include -#include "contrib/ivshmem-server/ivshmem-server.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" -#include "libqtest.h" -#include "qemu-common.h" - -#define TMPSHMSIZE (1 << 20) -static char *tmpshm; -static void *tmpshmem; -static char *tmpdir; -static char *tmpserver; - -static void save_fn(QPCIDevice *dev, int devfn, void *data) -{ - QPCIDevice **pdev = (QPCIDevice **) data; - - *pdev = dev; -} - -static QPCIDevice *get_device(QPCIBus *pcibus) -{ - QPCIDevice *dev; - - dev = NULL; - qpci_device_foreach(pcibus, 0x1af4, 0x1110, save_fn, &dev); - g_assert(dev != NULL); - - return dev; -} - -typedef struct _IVState { - QOSState *qs; - QPCIBar reg_bar, mem_bar; - QPCIDevice *dev; -} IVState; - -enum Reg { - INTRMASK = 0, - INTRSTATUS = 4, - IVPOSITION = 8, - DOORBELL = 12, -}; - -static const char* reg2str(enum Reg reg) { - switch (reg) { - case INTRMASK: - return "IntrMask"; - case INTRSTATUS: - return "IntrStatus"; - case IVPOSITION: - return "IVPosition"; - case DOORBELL: - return "DoorBell"; - default: - return NULL; - } -} - -static inline unsigned in_reg(IVState *s, enum Reg reg) -{ - const char *name = reg2str(reg); - unsigned res; - - res = qpci_io_readl(s->dev, s->reg_bar, reg); - g_test_message("*%s -> %x", name, res); - - return res; -} - -static inline void out_reg(IVState *s, enum Reg reg, unsigned v) -{ - const char *name = reg2str(reg); - - g_test_message("%x -> *%s", v, name); - qpci_io_writel(s->dev, s->reg_bar, reg, v); -} - -static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len) -{ - qpci_memread(s->dev, s->mem_bar, off, buf, len); -} - -static inline void write_mem(IVState *s, uint64_t off, - const void *buf, size_t len) -{ - qpci_memwrite(s->dev, s->mem_bar, off, buf, len); -} - -static void cleanup_vm(IVState *s) -{ - g_free(s->dev); - qtest_shutdown(s->qs); -} - -static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) -{ - uint64_t barsize; - const char *arch = qtest_get_arch(); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - s->qs = qtest_pc_boot(cmd); - } else if (strcmp(arch, "ppc64") == 0) { - s->qs = qtest_spapr_boot(cmd); - } else { - g_printerr("ivshmem-test tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); - } - s->dev = get_device(s->qs->pcibus); - - s->reg_bar = qpci_iomap(s->dev, 0, &barsize); - g_assert_cmpuint(barsize, ==, 256); - - if (msix) { - qpci_msix_enable(s->dev); - } - - s->mem_bar = qpci_iomap(s->dev, 2, &barsize); - g_assert_cmpuint(barsize, ==, TMPSHMSIZE); - - qpci_device_enable(s->dev); -} - -static void setup_vm(IVState *s) -{ - char *cmd = g_strdup_printf("-object memory-backend-file" - ",id=mb1,size=1M,share,mem-path=/dev/shm%s" - " -device ivshmem-plain,memdev=mb1", tmpshm); - - setup_vm_cmd(s, cmd, false); - - g_free(cmd); -} - -static void test_ivshmem_single(void) -{ - IVState state, *s; - uint32_t data[1024]; - int i; - - setup_vm(&state); - s = &state; - - /* initial state of readable registers */ - g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0); - g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0); - g_assert_cmpuint(in_reg(s, IVPOSITION), ==, 0); - - /* trigger interrupt via registers */ - out_reg(s, INTRMASK, 0xffffffff); - g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0xffffffff); - out_reg(s, INTRSTATUS, 1); - /* check interrupt status */ - g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 1); - /* reading clears */ - g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0); - /* TODO intercept actual interrupt (needs qtest work) */ - - /* invalid register access */ - out_reg(s, IVPOSITION, 1); - in_reg(s, DOORBELL); - - /* ring the (non-functional) doorbell */ - out_reg(s, DOORBELL, 8 << 16); - - /* write shared memory */ - for (i = 0; i < G_N_ELEMENTS(data); i++) { - data[i] = i; - } - write_mem(s, 0, data, sizeof(data)); - - /* verify write */ - for (i = 0; i < G_N_ELEMENTS(data); i++) { - g_assert_cmpuint(((uint32_t *)tmpshmem)[i], ==, i); - } - - /* read it back and verify read */ - memset(data, 0, sizeof(data)); - read_mem(s, 0, data, sizeof(data)); - for (i = 0; i < G_N_ELEMENTS(data); i++) { - g_assert_cmpuint(data[i], ==, i); - } - - cleanup_vm(s); -} - -static void test_ivshmem_pair(void) -{ - IVState state1, state2, *s1, *s2; - char *data; - int i; - - setup_vm(&state1); - s1 = &state1; - setup_vm(&state2); - s2 = &state2; - - data = g_malloc0(TMPSHMSIZE); - - /* host write, guest 1 & 2 read */ - memset(tmpshmem, 0x42, TMPSHMSIZE); - read_mem(s1, 0, data, TMPSHMSIZE); - for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x42); - } - read_mem(s2, 0, data, TMPSHMSIZE); - for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x42); - } - - /* guest 1 write, guest 2 read */ - memset(data, 0x43, TMPSHMSIZE); - write_mem(s1, 0, data, TMPSHMSIZE); - memset(data, 0, TMPSHMSIZE); - read_mem(s2, 0, data, TMPSHMSIZE); - for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x43); - } - - /* guest 2 write, guest 1 read */ - memset(data, 0x44, TMPSHMSIZE); - write_mem(s2, 0, data, TMPSHMSIZE); - memset(data, 0, TMPSHMSIZE); - read_mem(s1, 0, data, TMPSHMSIZE); - for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x44); - } - - cleanup_vm(s1); - cleanup_vm(s2); - g_free(data); -} - -typedef struct ServerThread { - GThread *thread; - IvshmemServer *server; - int pipe[2]; /* to handle quit */ -} ServerThread; - -static void *server_thread(void *data) -{ - ServerThread *t = data; - IvshmemServer *server = t->server; - - while (true) { - fd_set fds; - int maxfd, ret; - - FD_ZERO(&fds); - FD_SET(t->pipe[0], &fds); - maxfd = t->pipe[0] + 1; - - ivshmem_server_get_fds(server, &fds, &maxfd); - - ret = select(maxfd, &fds, NULL, NULL, NULL); - - if (ret < 0) { - if (errno == EINTR) { - continue; - } - - g_critical("select error: %s\n", strerror(errno)); - break; - } - if (ret == 0) { - continue; - } - - if (FD_ISSET(t->pipe[0], &fds)) { - break; - } - - if (ivshmem_server_handle_fds(server, &fds, maxfd) < 0) { - g_critical("ivshmem_server_handle_fds() failed\n"); - break; - } - } - - return NULL; -} - -static void setup_vm_with_server(IVState *s, int nvectors) -{ - char *cmd; - - cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s " - "-device ivshmem-doorbell,chardev=chr0,vectors=%d", - tmpserver, nvectors); - - setup_vm_cmd(s, cmd, true); - - g_free(cmd); -} - -static void test_ivshmem_server(void) -{ - IVState state1, state2, *s1, *s2; - ServerThread thread; - IvshmemServer server; - int ret, vm1, vm2; - int nvectors = 2; - guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; - - ret = ivshmem_server_init(&server, tmpserver, tmpshm, true, - TMPSHMSIZE, nvectors, - g_test_verbose()); - g_assert_cmpint(ret, ==, 0); - - ret = ivshmem_server_start(&server); - g_assert_cmpint(ret, ==, 0); - - thread.server = &server; - ret = pipe(thread.pipe); - g_assert_cmpint(ret, ==, 0); - thread.thread = g_thread_new("ivshmem-server", server_thread, &thread); - g_assert(thread.thread != NULL); - - setup_vm_with_server(&state1, nvectors); - s1 = &state1; - setup_vm_with_server(&state2, nvectors); - s2 = &state2; - - /* check got different VM ids */ - vm1 = in_reg(s1, IVPOSITION); - vm2 = in_reg(s2, IVPOSITION); - g_assert_cmpint(vm1, >=, 0); - g_assert_cmpint(vm2, >=, 0); - g_assert_cmpint(vm1, !=, vm2); - - /* check number of MSI-X vectors */ - 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 */ - ret = qpci_msix_pending(s1->dev, 0); - g_assert_cmpuint(ret, ==, 0); - out_reg(s2, DOORBELL, vm1 << 16); - do { - g_usleep(10000); - 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 */ - ret = qpci_msix_pending(s2->dev, 1); - g_assert_cmpuint(ret, ==, 0); - out_reg(s1, DOORBELL, vm2 << 16 | 1); - do { - g_usleep(10000); - ret = qpci_msix_pending(s2->dev, 1); - } while (ret == 0 && g_get_monotonic_time() < end_time); - g_assert_cmpuint(ret, !=, 0); - - cleanup_vm(s2); - cleanup_vm(s1); - - if (qemu_write_full(thread.pipe[1], "q", 1) != 1) { - g_error("qemu_write_full: %s", g_strerror(errno)); - } - - g_thread_join(thread.thread); - - ivshmem_server_close(&server); - close(thread.pipe[1]); - close(thread.pipe[0]); -} - -#define PCI_SLOT_HP 0x06 - -static void test_ivshmem_hotplug(void) -{ - QTestState *qts; - const char *arch = qtest_get_arch(); - - qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1"); - - qtest_qmp_device_add(qts, "ivshmem-plain", "iv1", - "{'addr': %s, 'memdev': 'mb1'}", - stringify(PCI_SLOT_HP)); - if (strcmp(arch, "ppc64") != 0) { - qpci_unplug_acpi_device_test(qts, "iv1", PCI_SLOT_HP); - } - - qtest_quit(qts); -} - -static void test_ivshmem_memdev(void) -{ - IVState state; - - /* just for the sake of checking memory-backend property */ - setup_vm_cmd(&state, "-object memory-backend-ram,size=1M,id=mb1" - " -device ivshmem-plain,memdev=mb1", false); - - cleanup_vm(&state); -} - -static void cleanup(void) -{ - if (tmpshmem) { - munmap(tmpshmem, TMPSHMSIZE); - tmpshmem = NULL; - } - - if (tmpshm) { - shm_unlink(tmpshm); - g_free(tmpshm); - tmpshm = NULL; - } - - if (tmpserver) { - g_unlink(tmpserver); - g_free(tmpserver); - tmpserver = NULL; - } - - if (tmpdir) { - g_rmdir(tmpdir); - tmpdir = NULL; - } -} - -static void abrt_handler(void *data) -{ - cleanup(); -} - -static gchar *mktempshm(int size, int *fd) -{ - while (true) { - gchar *name; - - name = g_strdup_printf("/qtest-%u-%u", getpid(), g_test_rand_int()); - *fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL, - S_IRWXU|S_IRWXG|S_IRWXO); - if (*fd > 0) { - g_assert(ftruncate(*fd, size) == 0); - return name; - } - - g_free(name); - - if (errno != EEXIST) { - perror("shm_open"); - return NULL; - } - } -} - -int main(int argc, char **argv) -{ - int ret, fd; - const char *arch = qtest_get_arch(); - gchar dir[] = "/tmp/ivshmem-test.XXXXXX"; - - g_test_init(&argc, &argv, NULL); - - qtest_add_abrt_handler(abrt_handler, NULL); - /* shm */ - tmpshm = mktempshm(TMPSHMSIZE, &fd); - if (!tmpshm) { - goto out; - } - tmpshmem = mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - g_assert(tmpshmem != MAP_FAILED); - /* server */ - if (mkdtemp(dir) == NULL) { - g_error("mkdtemp: %s", g_strerror(errno)); - } - tmpdir = dir; - tmpserver = g_strconcat(tmpdir, "/server", NULL); - - qtest_add_func("/ivshmem/single", test_ivshmem_single); - qtest_add_func("/ivshmem/hotplug", test_ivshmem_hotplug); - qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev); - if (g_test_slow()) { - qtest_add_func("/ivshmem/pair", test_ivshmem_pair); - if (strcmp(arch, "ppc64") != 0) { - qtest_add_func("/ivshmem/server", test_ivshmem_server); - } - } - -out: - ret = g_test_run(); - cleanup(); - return ret; -} diff --git a/tests/libqtest-single.h b/tests/libqtest-single.h deleted file mode 100644 index 6f1bb1331c..0000000000 --- a/tests/libqtest-single.h +++ /dev/null @@ -1,315 +0,0 @@ -/* - * QTest - wrappers for test with single QEMU instances - * - * Copyright IBM, Corp. 2012 - * Copyright Red Hat, Inc. 2012 - * Copyright SUSE LINUX Products GmbH 2013 - * - * 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 LIBQTEST_SINGLE_H -#define LIBQTEST_SINGLE_H - -#include "libqtest.h" - -QTestState *global_qtest __attribute__((common, weak)); - -/** - * qtest_start: - * @args: other arguments to pass to QEMU - * - * Start QEMU and assign the resulting #QTestState to a global variable. - * The global variable is used by "shortcut" functions documented below. - * - * Returns: #QTestState instance. - */ -static inline QTestState *qtest_start(const char *args) -{ - global_qtest = qtest_init(args); - return global_qtest; -} - -/** - * qtest_end: - * - * Shut down the QEMU process started by qtest_start(). - */ -static inline void qtest_end(void) -{ - if (!global_qtest) { - return; - } - qtest_quit(global_qtest); - global_qtest = NULL; -} - -/** - * qmp: - * @fmt...: QMP message to send to qemu, formatted like - * qobject_from_jsonf_nofail(). See parse_escape() for what's - * supported after '%'. - * - * Sends a QMP message to QEMU and returns the response. - */ -GCC_FMT_ATTR(1, 2) -static inline QDict *qmp(const char *fmt, ...) -{ - va_list ap; - QDict *response; - - va_start(ap, fmt); - response = qtest_vqmp(global_qtest, fmt, ap); - va_end(ap); - return response; -} - -/** - * qmp_eventwait: - * @s: #event event to wait for. - * - * Continuously polls for QMP responses until it receives the desired event. - */ -static inline void qmp_eventwait(const char *event) -{ - return qtest_qmp_eventwait(global_qtest, event); -} - -/** - * get_irq: - * @num: Interrupt to observe. - * - * Returns: The level of the @num interrupt. - */ -static inline bool get_irq(int num) -{ - return qtest_get_irq(global_qtest, num); -} - -/** - * outb: - * @addr: I/O port to write to. - * @value: Value being written. - * - * Write an 8-bit value to an I/O port. - */ -static inline void outb(uint16_t addr, uint8_t value) -{ - qtest_outb(global_qtest, addr, value); -} - -/** - * outw: - * @addr: I/O port to write to. - * @value: Value being written. - * - * Write a 16-bit value to an I/O port. - */ -static inline void outw(uint16_t addr, uint16_t value) -{ - qtest_outw(global_qtest, addr, value); -} - -/** - * outl: - * @addr: I/O port to write to. - * @value: Value being written. - * - * Write a 32-bit value to an I/O port. - */ -static inline void outl(uint16_t addr, uint32_t value) -{ - qtest_outl(global_qtest, addr, value); -} - -/** - * inb: - * @addr: I/O port to read from. - * - * Reads an 8-bit value from an I/O port. - * - * Returns: Value read. - */ -static inline uint8_t inb(uint16_t addr) -{ - return qtest_inb(global_qtest, addr); -} - -/** - * inw: - * @addr: I/O port to read from. - * - * Reads a 16-bit value from an I/O port. - * - * Returns: Value read. - */ -static inline uint16_t inw(uint16_t addr) -{ - return qtest_inw(global_qtest, addr); -} - -/** - * inl: - * @addr: I/O port to read from. - * - * Reads a 32-bit value from an I/O port. - * - * Returns: Value read. - */ -static inline uint32_t inl(uint16_t addr) -{ - return qtest_inl(global_qtest, addr); -} - -/** - * writeb: - * @addr: Guest address to write to. - * @value: Value being written. - * - * Writes an 8-bit value to guest memory. - */ -static inline void writeb(uint64_t addr, uint8_t value) -{ - qtest_writeb(global_qtest, addr, value); -} - -/** - * writew: - * @addr: Guest address to write to. - * @value: Value being written. - * - * Writes a 16-bit value to guest memory. - */ -static inline void writew(uint64_t addr, uint16_t value) -{ - qtest_writew(global_qtest, addr, value); -} - -/** - * writel: - * @addr: Guest address to write to. - * @value: Value being written. - * - * Writes a 32-bit value to guest memory. - */ -static inline void writel(uint64_t addr, uint32_t value) -{ - qtest_writel(global_qtest, addr, value); -} - -/** - * writeq: - * @addr: Guest address to write to. - * @value: Value being written. - * - * Writes a 64-bit value to guest memory. - */ -static inline void writeq(uint64_t addr, uint64_t value) -{ - qtest_writeq(global_qtest, addr, value); -} - -/** - * readb: - * @addr: Guest address to read from. - * - * Reads an 8-bit value from guest memory. - * - * Returns: Value read. - */ -static inline uint8_t readb(uint64_t addr) -{ - return qtest_readb(global_qtest, addr); -} - -/** - * readw: - * @addr: Guest address to read from. - * - * Reads a 16-bit value from guest memory. - * - * Returns: Value read. - */ -static inline uint16_t readw(uint64_t addr) -{ - return qtest_readw(global_qtest, addr); -} - -/** - * readl: - * @addr: Guest address to read from. - * - * Reads a 32-bit value from guest memory. - * - * Returns: Value read. - */ -static inline uint32_t readl(uint64_t addr) -{ - return qtest_readl(global_qtest, addr); -} - -/** - * readq: - * @addr: Guest address to read from. - * - * Reads a 64-bit value from guest memory. - * - * Returns: Value read. - */ -static inline uint64_t readq(uint64_t addr) -{ - return qtest_readq(global_qtest, addr); -} - -/** - * memread: - * @addr: Guest address to read from. - * @data: Pointer to where memory contents will be stored. - * @size: Number of bytes to read. - * - * Read guest memory into a buffer. - */ -static inline void memread(uint64_t addr, void *data, size_t size) -{ - qtest_memread(global_qtest, addr, data, size); -} - -/** - * memwrite: - * @addr: Guest address to write to. - * @data: Pointer to the bytes that will be written to guest memory. - * @size: Number of bytes to write. - * - * Write a buffer to guest memory. - */ -static inline void memwrite(uint64_t addr, const void *data, size_t size) -{ - qtest_memwrite(global_qtest, addr, data, size); -} - -/** - * clock_step_next: - * - * Advance the QEMU_CLOCK_VIRTUAL to the next deadline. - * - * Returns: The current value of the QEMU_CLOCK_VIRTUAL in nanoseconds. - */ -static inline int64_t clock_step_next(void) -{ - return qtest_clock_step_next(global_qtest); -} - -/** - * clock_step: - * @step: Number of nanoseconds to advance the clock by. - * - * Advance the QEMU_CLOCK_VIRTUAL by @step nanoseconds. - * - * Returns: The current value of the QEMU_CLOCK_VIRTUAL in nanoseconds. - */ -static inline int64_t clock_step(int64_t step) -{ - return qtest_clock_step(global_qtest, step); -} - -#endif diff --git a/tests/libqtest.c b/tests/libqtest.c deleted file mode 100644 index 76c9f8eade..0000000000 --- a/tests/libqtest.c +++ /dev/null @@ -1,1339 +0,0 @@ -/* - * QTest - * - * Copyright IBM, Corp. 2012 - * Copyright Red Hat, Inc. 2012 - * Copyright SUSE LINUX Products GmbH 2013 - * - * Authors: - * Anthony Liguori - * Paolo Bonzini - * Andreas Färber - * - * 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 -#include -#include - -#include "libqtest.h" -#include "qemu-common.h" -#include "qemu/ctype.h" -#include "qemu/cutils.h" -#include "qapi/error.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" - -#define MAX_IRQ 256 -#define SOCKET_TIMEOUT 50 -#define SOCKET_MAX_FDS 16 - -struct QTestState -{ - int fd; - int qmp_fd; - pid_t qemu_pid; /* our child QEMU process */ - int wstatus; - int expected_status; - bool big_endian; - bool irq_level[MAX_IRQ]; - GString *rx; -}; - -static GHookList abrt_hooks; -static struct sigaction sigact_old; - -static int qtest_query_target_endianness(QTestState *s); - -static int init_socket(const char *socket_path) -{ - struct sockaddr_un addr; - int sock; - int ret; - - sock = socket(PF_UNIX, SOCK_STREAM, 0); - g_assert_cmpint(sock, !=, -1); - - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); - qemu_set_cloexec(sock); - - do { - ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); - } while (ret == -1 && errno == EINTR); - g_assert_cmpint(ret, !=, -1); - ret = listen(sock, 1); - g_assert_cmpint(ret, !=, -1); - - return sock; -} - -static int socket_accept(int sock) -{ - struct sockaddr_un addr; - socklen_t addrlen; - int ret; - struct timeval timeout = { .tv_sec = SOCKET_TIMEOUT, - .tv_usec = 0 }; - - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, - sizeof(timeout)); - - do { - addrlen = sizeof(addr); - ret = accept(sock, (struct sockaddr *)&addr, &addrlen); - } while (ret == -1 && errno == EINTR); - if (ret == -1) { - fprintf(stderr, "%s failed: %s\n", __func__, strerror(errno)); - } - close(sock); - - return ret; -} - -bool qtest_probe_child(QTestState *s) -{ - pid_t pid = s->qemu_pid; - - if (pid != -1) { - pid = waitpid(pid, &s->wstatus, WNOHANG); - if (pid == 0) { - return true; - } - s->qemu_pid = -1; - } - return false; -} - -void qtest_set_expected_status(QTestState *s, int status) -{ - s->expected_status = status; -} - -static void kill_qemu(QTestState *s) -{ - pid_t pid = s->qemu_pid; - int wstatus; - - /* Skip wait if qtest_probe_child already reaped. */ - if (pid != -1) { - kill(pid, SIGTERM); - TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0)); - assert(pid == s->qemu_pid); - } - - /* - * Check whether qemu exited with expected exit status; anything else is - * fishy and should be logged with as much detail as possible. - */ - wstatus = s->wstatus; - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != s->expected_status) { - fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " - "process but encountered exit status %d (expected %d)\n", - __FILE__, __LINE__, WEXITSTATUS(wstatus), s->expected_status); - abort(); - } else if (WIFSIGNALED(wstatus)) { - int sig = WTERMSIG(wstatus); - const char *signame = strsignal(sig) ?: "unknown ???"; - const char *dump = WCOREDUMP(wstatus) ? " (core dumped)" : ""; - - fprintf(stderr, "%s:%d: kill_qemu() detected QEMU death " - "from signal %d (%s)%s\n", - __FILE__, __LINE__, sig, signame, dump); - abort(); - } -} - -static void kill_qemu_hook_func(void *s) -{ - kill_qemu(s); -} - -static void sigabrt_handler(int signo) -{ - g_hook_list_invoke(&abrt_hooks, FALSE); -} - -static void setup_sigabrt_handler(void) -{ - struct sigaction sigact; - - /* Catch SIGABRT to clean up on g_assert() failure */ - sigact = (struct sigaction){ - .sa_handler = sigabrt_handler, - .sa_flags = SA_RESETHAND, - }; - sigemptyset(&sigact.sa_mask); - sigaction(SIGABRT, &sigact, &sigact_old); -} - -static void cleanup_sigabrt_handler(void) -{ - sigaction(SIGABRT, &sigact_old, NULL); -} - -void qtest_add_abrt_handler(GHookFunc fn, const void *data) -{ - GHook *hook; - - /* Only install SIGABRT handler once */ - if (!abrt_hooks.is_setup) { - g_hook_list_init(&abrt_hooks, sizeof(GHook)); - } - setup_sigabrt_handler(); - - hook = g_hook_alloc(&abrt_hooks); - hook->func = fn; - hook->data = (void *)data; - - g_hook_prepend(&abrt_hooks, hook); -} - -static const char *qtest_qemu_binary(void) -{ - const char *qemu_bin; - - qemu_bin = getenv("QTEST_QEMU_BINARY"); - if (!qemu_bin) { - fprintf(stderr, "Environment variable QTEST_QEMU_BINARY required\n"); - exit(1); - } - - return qemu_bin; -} - -QTestState *qtest_init_without_qmp_handshake(const char *extra_args) -{ - QTestState *s; - int sock, qmpsock, i; - gchar *socket_path; - gchar *qmp_socket_path; - gchar *command; - const char *qemu_binary = qtest_qemu_binary(); - - s = g_new(QTestState, 1); - - socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid()); - qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid()); - - /* It's possible that if an earlier test run crashed it might - * have left a stale unix socket lying around. Delete any - * stale old socket to avoid spurious test failures with - * tests/libqtest.c:70:init_socket: assertion failed (ret != -1): (-1 != -1) - */ - unlink(socket_path); - unlink(qmp_socket_path); - - sock = init_socket(socket_path); - qmpsock = init_socket(qmp_socket_path); - - qtest_add_abrt_handler(kill_qemu_hook_func, s); - - command = g_strdup_printf("exec %s " - "-qtest unix:%s " - "-qtest-log %s " - "-chardev socket,path=%s,id=char0 " - "-mon chardev=char0,mode=control " - "-display none " - "%s" - " -accel qtest", qemu_binary, socket_path, - getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null", - qmp_socket_path, - extra_args ?: ""); - - g_test_message("starting QEMU: %s", command); - - s->wstatus = 0; - s->expected_status = 0; - s->qemu_pid = fork(); - if (s->qemu_pid == 0) { - g_setenv("QEMU_AUDIO_DRV", "none", true); - execlp("/bin/sh", "sh", "-c", command, NULL); - exit(1); - } - - g_free(command); - s->fd = socket_accept(sock); - if (s->fd >= 0) { - s->qmp_fd = socket_accept(qmpsock); - } - unlink(socket_path); - unlink(qmp_socket_path); - g_free(socket_path); - g_free(qmp_socket_path); - - g_assert(s->fd >= 0 && s->qmp_fd >= 0); - - s->rx = g_string_new(""); - for (i = 0; i < MAX_IRQ; i++) { - s->irq_level[i] = false; - } - - if (getenv("QTEST_STOP")) { - kill(s->qemu_pid, SIGSTOP); - } - - /* ask endianness of the target */ - - s->big_endian = qtest_query_target_endianness(s); - - return s; -} - -QTestState *qtest_init(const char *extra_args) -{ - QTestState *s = qtest_init_without_qmp_handshake(extra_args); - QDict *greeting; - - /* Read the QMP greeting and then do the handshake */ - greeting = qtest_qmp_receive(s); - qobject_unref(greeting); - qobject_unref(qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }")); - - return s; -} - -QTestState *qtest_vinitf(const char *fmt, va_list ap) -{ - char *args = g_strdup_vprintf(fmt, ap); - QTestState *s; - - s = qtest_init(args); - g_free(args); - return s; -} - -QTestState *qtest_initf(const char *fmt, ...) -{ - va_list ap; - QTestState *s; - - va_start(ap, fmt); - s = qtest_vinitf(fmt, ap); - va_end(ap); - return s; -} - -QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd) -{ - int sock_fd_init; - char *sock_path, sock_dir[] = "/tmp/qtest-serial-XXXXXX"; - QTestState *qts; - - g_assert_true(mkdtemp(sock_dir) != NULL); - sock_path = g_strdup_printf("%s/sock", sock_dir); - - sock_fd_init = init_socket(sock_path); - - qts = qtest_initf("-chardev socket,id=s0,path=%s -serial chardev:s0 %s", - sock_path, extra_args); - - *sock_fd = socket_accept(sock_fd_init); - - unlink(sock_path); - g_free(sock_path); - rmdir(sock_dir); - - g_assert_true(*sock_fd >= 0); - - return qts; -} - -void qtest_quit(QTestState *s) -{ - g_hook_destroy_link(&abrt_hooks, g_hook_find_data(&abrt_hooks, TRUE, s)); - - /* Uninstall SIGABRT handler on last instance */ - cleanup_sigabrt_handler(); - - kill_qemu(s); - close(s->fd); - close(s->qmp_fd); - g_string_free(s->rx, true); - g_free(s); -} - -static void socket_send(int fd, const char *buf, size_t size) -{ - size_t offset; - - offset = 0; - while (offset < size) { - ssize_t len; - - len = write(fd, buf + offset, size - offset); - if (len == -1 && errno == EINTR) { - continue; - } - - g_assert_cmpint(len, >, 0); - - offset += len; - } -} - -static void socket_sendf(int fd, const char *fmt, va_list ap) -{ - gchar *str = g_strdup_vprintf(fmt, ap); - size_t size = strlen(str); - - socket_send(fd, str, size); - g_free(str); -} - -static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - socket_sendf(s->fd, fmt, ap); - va_end(ap); -} - -/* Sends a message and file descriptors to the socket. - * It's needed for qmp-commands like getfd/add-fd */ -static void socket_send_fds(int socket_fd, int *fds, size_t fds_num, - const char *buf, size_t buf_size) -{ - ssize_t ret; - struct msghdr msg = { 0 }; - char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)] = { 0 }; - size_t fdsize = sizeof(int) * fds_num; - struct cmsghdr *cmsg; - struct iovec iov = { .iov_base = (char *)buf, .iov_len = buf_size }; - - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - if (fds && fds_num > 0) { - g_assert_cmpuint(fds_num, <, SOCKET_MAX_FDS); - - msg.msg_control = control; - msg.msg_controllen = CMSG_SPACE(fdsize); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(fdsize); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), fds, fdsize); - } - - do { - ret = sendmsg(socket_fd, &msg, 0); - } while (ret < 0 && errno == EINTR); - g_assert_cmpint(ret, >, 0); -} - -static GString *qtest_recv_line(QTestState *s) -{ - GString *line; - size_t offset; - char *eol; - - while ((eol = strchr(s->rx->str, '\n')) == NULL) { - ssize_t len; - char buffer[1024]; - - len = read(s->fd, buffer, sizeof(buffer)); - if (len == -1 && errno == EINTR) { - continue; - } - - if (len == -1 || len == 0) { - fprintf(stderr, "Broken pipe\n"); - abort(); - } - - g_string_append_len(s->rx, buffer, len); - } - - offset = eol - s->rx->str; - line = g_string_new_len(s->rx->str, offset); - g_string_erase(s->rx, 0, offset + 1); - - return line; -} - -static gchar **qtest_rsp(QTestState *s, int expected_args) -{ - GString *line; - gchar **words; - int i; - -redo: - line = qtest_recv_line(s); - words = g_strsplit(line->str, " ", 0); - g_string_free(line, TRUE); - - if (strcmp(words[0], "IRQ") == 0) { - long irq; - int ret; - - g_assert(words[1] != NULL); - g_assert(words[2] != NULL); - - ret = qemu_strtol(words[2], NULL, 0, &irq); - g_assert(!ret); - g_assert_cmpint(irq, >=, 0); - g_assert_cmpint(irq, <, MAX_IRQ); - - if (strcmp(words[1], "raise") == 0) { - s->irq_level[irq] = true; - } else { - s->irq_level[irq] = false; - } - - g_strfreev(words); - goto redo; - } - - g_assert(words[0] != NULL); - g_assert_cmpstr(words[0], ==, "OK"); - - if (expected_args) { - for (i = 0; i < expected_args; i++) { - g_assert(words[i] != NULL); - } - } else { - g_strfreev(words); - } - - return words; -} - -static int qtest_query_target_endianness(QTestState *s) -{ - gchar **args; - int big_endian; - - qtest_sendf(s, "endianness\n"); - args = qtest_rsp(s, 1); - g_assert(strcmp(args[1], "big") == 0 || strcmp(args[1], "little") == 0); - big_endian = strcmp(args[1], "big") == 0; - g_strfreev(args); - - return big_endian; -} - -typedef struct { - JSONMessageParser parser; - QDict *response; -} QMPResponseParser; - -static void qmp_response(void *opaque, QObject *obj, Error *err) -{ - QMPResponseParser *qmp = opaque; - - assert(!obj != !err); - - if (err) { - error_prepend(&err, "QMP JSON response parsing failed: "); - error_report_err(err); - abort(); - } - - g_assert(!qmp->response); - qmp->response = qobject_to(QDict, obj); - g_assert(qmp->response); -} - -QDict *qmp_fd_receive(int fd) -{ - QMPResponseParser qmp; - bool log = getenv("QTEST_LOG") != NULL; - - qmp.response = NULL; - json_message_parser_init(&qmp.parser, qmp_response, &qmp, NULL); - while (!qmp.response) { - ssize_t len; - char c; - - len = read(fd, &c, 1); - if (len == -1 && errno == EINTR) { - continue; - } - - if (len == -1 || len == 0) { - fprintf(stderr, "Broken pipe\n"); - abort(); - } - - if (log) { - len = write(2, &c, 1); - } - json_message_parser_feed(&qmp.parser, &c, 1); - } - json_message_parser_destroy(&qmp.parser); - - return qmp.response; -} - -QDict *qtest_qmp_receive(QTestState *s) -{ - return qmp_fd_receive(s->qmp_fd); -} - -/** - * Allow users to send a message without waiting for the reply, - * in the case that they choose to discard all replies up until - * a particular EVENT is received. - */ -void qmp_fd_vsend_fds(int fd, int *fds, size_t fds_num, - const char *fmt, va_list ap) -{ - QObject *qobj; - - /* Going through qobject ensures we escape strings properly */ - qobj = qobject_from_vjsonf_nofail(fmt, ap); - - /* No need to send anything for an empty QObject. */ - if (qobj) { - int log = getenv("QTEST_LOG") != NULL; - QString *qstr = qobject_to_json(qobj); - const char *str; - - /* - * BUG: QMP doesn't react to input until it sees a newline, an - * object, or an array. Work-around: give it a newline. - */ - qstring_append_chr(qstr, '\n'); - str = qstring_get_str(qstr); - - if (log) { - fprintf(stderr, "%s", str); - } - /* Send QMP request */ - if (fds && fds_num > 0) { - socket_send_fds(fd, fds, fds_num, str, qstring_get_length(qstr)); - } else { - socket_send(fd, str, qstring_get_length(qstr)); - } - - qobject_unref(qstr); - qobject_unref(qobj); - } -} - -void qmp_fd_vsend(int fd, const char *fmt, va_list ap) -{ - qmp_fd_vsend_fds(fd, NULL, 0, fmt, ap); -} - -void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, - const char *fmt, va_list ap) -{ - qmp_fd_vsend_fds(s->qmp_fd, fds, fds_num, fmt, ap); -} - -void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) -{ - qmp_fd_vsend_fds(s->qmp_fd, NULL, 0, fmt, ap); -} - -QDict *qmp_fdv(int fd, const char *fmt, va_list ap) -{ - qmp_fd_vsend_fds(fd, NULL, 0, fmt, ap); - - return qmp_fd_receive(fd); -} - -QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, - const char *fmt, va_list ap) -{ - qtest_qmp_vsend_fds(s, fds, fds_num, fmt, ap); - - /* Receive reply */ - return qtest_qmp_receive(s); -} - -QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) -{ - qtest_qmp_vsend(s, fmt, ap); - - /* Receive reply */ - return qtest_qmp_receive(s); -} - -QDict *qmp_fd(int fd, const char *fmt, ...) -{ - va_list ap; - QDict *response; - - va_start(ap, fmt); - response = qmp_fdv(fd, fmt, ap); - va_end(ap); - return response; -} - -void qmp_fd_send(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - qmp_fd_vsend(fd, fmt, ap); - va_end(ap); -} - -QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num, - const char *fmt, ...) -{ - va_list ap; - QDict *response; - - va_start(ap, fmt); - response = qtest_vqmp_fds(s, fds, fds_num, fmt, ap); - va_end(ap); - return response; -} - -QDict *qtest_qmp(QTestState *s, const char *fmt, ...) -{ - va_list ap; - QDict *response; - - va_start(ap, fmt); - response = qtest_vqmp(s, fmt, ap); - va_end(ap); - return response; -} - -void qtest_qmp_send(QTestState *s, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - qtest_qmp_vsend(s, fmt, ap); - va_end(ap); -} - -void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) -{ - bool log = getenv("QTEST_LOG") != NULL; - char *str = g_strdup_vprintf(fmt, ap); - - if (log) { - fprintf(stderr, "%s", str); - } - socket_send(fd, str, strlen(str)); - g_free(str); -} - -void qmp_fd_send_raw(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - qmp_fd_vsend_raw(fd, fmt, ap); - va_end(ap); -} - -void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - qmp_fd_vsend_raw(s->qmp_fd, fmt, ap); - va_end(ap); -} - -QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event) -{ - QDict *response; - - for (;;) { - response = qtest_qmp_receive(s); - if ((qdict_haskey(response, "event")) && - (strcmp(qdict_get_str(response, "event"), event) == 0)) { - return response; - } - qobject_unref(response); - } -} - -void qtest_qmp_eventwait(QTestState *s, const char *event) -{ - QDict *response; - - response = qtest_qmp_eventwait_ref(s, event); - qobject_unref(response); -} - -char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap) -{ - char *cmd; - QDict *resp; - char *ret; - - cmd = g_strdup_vprintf(fmt, ap); - resp = qtest_qmp(s, "{'execute': 'human-monitor-command'," - " 'arguments': {'command-line': %s}}", - cmd); - ret = g_strdup(qdict_get_try_str(resp, "return")); - while (ret == NULL && qdict_get_try_str(resp, "event")) { - /* Ignore asynchronous QMP events */ - qobject_unref(resp); - resp = qtest_qmp_receive(s); - ret = g_strdup(qdict_get_try_str(resp, "return")); - } - g_assert(ret); - qobject_unref(resp); - g_free(cmd); - return ret; -} - -char *qtest_hmp(QTestState *s, const char *fmt, ...) -{ - va_list ap; - char *ret; - - va_start(ap, fmt); - ret = qtest_vhmp(s, fmt, ap); - va_end(ap); - return ret; -} - -const char *qtest_get_arch(void) -{ - const char *qemu = qtest_qemu_binary(); - const char *end = strrchr(qemu, '/'); - - return end + strlen("/qemu-system-"); -} - -bool qtest_get_irq(QTestState *s, int num) -{ - /* dummy operation in order to make sure irq is up to date */ - qtest_inb(s, 0); - - return s->irq_level[num]; -} - -void qtest_module_load(QTestState *s, const char *prefix, const char *libname) -{ - qtest_sendf(s, "module_load %s %s\n", prefix, libname); - qtest_rsp(s, 0); -} - -static int64_t qtest_clock_rsp(QTestState *s) -{ - gchar **words; - int64_t clock; - words = qtest_rsp(s, 2); - clock = g_ascii_strtoll(words[1], NULL, 0); - g_strfreev(words); - return clock; -} - -int64_t qtest_clock_step_next(QTestState *s) -{ - qtest_sendf(s, "clock_step\n"); - return qtest_clock_rsp(s); -} - -int64_t qtest_clock_step(QTestState *s, int64_t step) -{ - qtest_sendf(s, "clock_step %"PRIi64"\n", step); - return qtest_clock_rsp(s); -} - -int64_t qtest_clock_set(QTestState *s, int64_t val) -{ - qtest_sendf(s, "clock_set %"PRIi64"\n", val); - return qtest_clock_rsp(s); -} - -void qtest_irq_intercept_out(QTestState *s, const char *qom_path) -{ - qtest_sendf(s, "irq_intercept_out %s\n", qom_path); - qtest_rsp(s, 0); -} - -void qtest_irq_intercept_in(QTestState *s, const char *qom_path) -{ - qtest_sendf(s, "irq_intercept_in %s\n", qom_path); - qtest_rsp(s, 0); -} - -void qtest_set_irq_in(QTestState *s, const char *qom_path, const char *name, - int num, int level) -{ - if (!name) { - name = "unnamed-gpio-in"; - } - qtest_sendf(s, "set_irq_in %s %s %d %d\n", qom_path, name, num, level); - qtest_rsp(s, 0); -} - -static void qtest_out(QTestState *s, const char *cmd, uint16_t addr, uint32_t value) -{ - qtest_sendf(s, "%s 0x%x 0x%x\n", cmd, addr, value); - qtest_rsp(s, 0); -} - -void qtest_outb(QTestState *s, uint16_t addr, uint8_t value) -{ - qtest_out(s, "outb", addr, value); -} - -void qtest_outw(QTestState *s, uint16_t addr, uint16_t value) -{ - qtest_out(s, "outw", addr, value); -} - -void qtest_outl(QTestState *s, uint16_t addr, uint32_t value) -{ - qtest_out(s, "outl", addr, value); -} - -static uint32_t qtest_in(QTestState *s, const char *cmd, uint16_t addr) -{ - gchar **args; - int ret; - unsigned long value; - - qtest_sendf(s, "%s 0x%x\n", cmd, addr); - args = qtest_rsp(s, 2); - ret = qemu_strtoul(args[1], NULL, 0, &value); - g_assert(!ret && value <= UINT32_MAX); - g_strfreev(args); - - return value; -} - -uint8_t qtest_inb(QTestState *s, uint16_t addr) -{ - return qtest_in(s, "inb", addr); -} - -uint16_t qtest_inw(QTestState *s, uint16_t addr) -{ - return qtest_in(s, "inw", addr); -} - -uint32_t qtest_inl(QTestState *s, uint16_t addr) -{ - return qtest_in(s, "inl", addr); -} - -static void qtest_write(QTestState *s, const char *cmd, uint64_t addr, - uint64_t value) -{ - qtest_sendf(s, "%s 0x%" PRIx64 " 0x%" PRIx64 "\n", cmd, addr, value); - qtest_rsp(s, 0); -} - -void qtest_writeb(QTestState *s, uint64_t addr, uint8_t value) -{ - qtest_write(s, "writeb", addr, value); -} - -void qtest_writew(QTestState *s, uint64_t addr, uint16_t value) -{ - qtest_write(s, "writew", addr, value); -} - -void qtest_writel(QTestState *s, uint64_t addr, uint32_t value) -{ - qtest_write(s, "writel", addr, value); -} - -void qtest_writeq(QTestState *s, uint64_t addr, uint64_t value) -{ - qtest_write(s, "writeq", addr, value); -} - -static uint64_t qtest_read(QTestState *s, const char *cmd, uint64_t addr) -{ - gchar **args; - int ret; - uint64_t value; - - qtest_sendf(s, "%s 0x%" PRIx64 "\n", cmd, addr); - args = qtest_rsp(s, 2); - ret = qemu_strtou64(args[1], NULL, 0, &value); - g_assert(!ret); - g_strfreev(args); - - return value; -} - -uint8_t qtest_readb(QTestState *s, uint64_t addr) -{ - return qtest_read(s, "readb", addr); -} - -uint16_t qtest_readw(QTestState *s, uint64_t addr) -{ - return qtest_read(s, "readw", addr); -} - -uint32_t qtest_readl(QTestState *s, uint64_t addr) -{ - return qtest_read(s, "readl", addr); -} - -uint64_t qtest_readq(QTestState *s, uint64_t addr) -{ - return qtest_read(s, "readq", addr); -} - -static int hex2nib(char ch) -{ - if (ch >= '0' && ch <= '9') { - return ch - '0'; - } else if (ch >= 'a' && ch <= 'f') { - return 10 + (ch - 'a'); - } else if (ch >= 'A' && ch <= 'F') { - return 10 + (ch - 'a'); - } else { - return -1; - } -} - -void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size) -{ - uint8_t *ptr = data; - gchar **args; - size_t i; - - if (!size) { - return; - } - - qtest_sendf(s, "read 0x%" PRIx64 " 0x%zx\n", addr, size); - args = qtest_rsp(s, 2); - - for (i = 0; i < size; i++) { - ptr[i] = hex2nib(args[1][2 + (i * 2)]) << 4; - ptr[i] |= hex2nib(args[1][2 + (i * 2) + 1]); - } - - g_strfreev(args); -} - -uint64_t qtest_rtas_call(QTestState *s, const char *name, - uint32_t nargs, uint64_t args, - uint32_t nret, uint64_t ret) -{ - qtest_sendf(s, "rtas %s %u 0x%"PRIx64" %u 0x%"PRIx64"\n", - name, nargs, args, nret, ret); - qtest_rsp(s, 0); - return 0; -} - -void qtest_add_func(const char *str, void (*fn)(void)) -{ - gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); - g_test_add_func(path, fn); - g_free(path); -} - -void qtest_add_data_func_full(const char *str, void *data, - void (*fn)(const void *), - GDestroyNotify data_free_func) -{ - gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); - g_test_add_data_func_full(path, data, fn, data_free_func); - g_free(path); -} - -void qtest_add_data_func(const char *str, const void *data, - void (*fn)(const void *)) -{ - gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); - g_test_add_data_func(path, data, fn); - g_free(path); -} - -void qtest_bufwrite(QTestState *s, uint64_t addr, const void *data, size_t size) -{ - gchar *bdata; - - bdata = g_base64_encode(data, size); - qtest_sendf(s, "b64write 0x%" PRIx64 " 0x%zx ", addr, size); - socket_send(s->fd, bdata, strlen(bdata)); - socket_send(s->fd, "\n", 1); - qtest_rsp(s, 0); - g_free(bdata); -} - -void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size) -{ - gchar **args; - size_t len; - - qtest_sendf(s, "b64read 0x%" PRIx64 " 0x%zx\n", addr, size); - args = qtest_rsp(s, 2); - - g_base64_decode_inplace(args[1], &len); - if (size != len) { - fprintf(stderr, "bufread: asked for %zu bytes but decoded %zu\n", - size, len); - len = MIN(len, size); - } - - memcpy(data, args[1], len); - g_strfreev(args); -} - -void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size) -{ - const uint8_t *ptr = data; - size_t i; - char *enc; - - if (!size) { - return; - } - - enc = g_malloc(2 * size + 1); - - for (i = 0; i < size; i++) { - sprintf(&enc[i * 2], "%02x", ptr[i]); - } - - qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x%s\n", addr, size, enc); - qtest_rsp(s, 0); - g_free(enc); -} - -void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size) -{ - qtest_sendf(s, "memset 0x%" PRIx64 " 0x%zx 0x%02x\n", addr, size, pattern); - qtest_rsp(s, 0); -} - -void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) -{ - va_list ap; - QDict *response; - - va_start(ap, fmt); - response = qtest_vqmp(qts, fmt, ap); - va_end(ap); - - g_assert(response); - if (!qdict_haskey(response, "return")) { - QString *s = qobject_to_json_pretty(QOBJECT(response)); - g_test_message("%s", qstring_get_str(s)); - qobject_unref(s); - } - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); -} - -bool qtest_big_endian(QTestState *s) -{ - return s->big_endian; -} - -static bool qtest_check_machine_version(const char *mname, const char *basename, - int major, int minor) -{ - char *newname; - bool is_equal; - - newname = g_strdup_printf("%s-%i.%i", basename, major, minor); - is_equal = g_str_equal(mname, newname); - g_free(newname); - - return is_equal; -} - -static bool qtest_is_old_versioned_machine(const char *mname) -{ - const char *dash = strrchr(mname, '-'); - const char *dot = strrchr(mname, '.'); - const char *chr; - char *bname; - const int major = QEMU_VERSION_MAJOR; - const int minor = QEMU_VERSION_MINOR; - bool res = false; - - if (dash && dot && dot > dash) { - for (chr = dash + 1; *chr; chr++) { - if (!qemu_isdigit(*chr) && *chr != '.') { - return false; - } - } - /* - * Now check if it is one of the latest versions. Check major + 1 - * and minor + 1 versions as well, since they might already exist - * in the development branch. - */ - bname = g_strdup(mname); - bname[dash - mname] = 0; - res = !qtest_check_machine_version(mname, bname, major + 1, 0) && - !qtest_check_machine_version(mname, bname, major, minor + 1) && - !qtest_check_machine_version(mname, bname, major, minor); - g_free(bname); - } - - return res; -} - -void qtest_cb_for_every_machine(void (*cb)(const char *machine), - bool skip_old_versioned) -{ - QDict *response, *minfo; - QList *list; - const QListEntry *p; - QObject *qobj; - QString *qstr; - const char *mname; - QTestState *qts; - - qts = qtest_init("-machine none"); - response = qtest_qmp(qts, "{ 'execute': 'query-machines' }"); - g_assert(response); - list = qdict_get_qlist(response, "return"); - g_assert(list); - - for (p = qlist_first(list); p; p = qlist_next(p)) { - minfo = qobject_to(QDict, qlist_entry_obj(p)); - g_assert(minfo); - qobj = qdict_get(minfo, "name"); - g_assert(qobj); - qstr = qobject_to(QString, qobj); - g_assert(qstr); - mname = qstring_get_str(qstr); - if (!skip_old_versioned || !qtest_is_old_versioned_machine(mname)) { - cb(mname); - } - } - - qtest_quit(qts); - qobject_unref(response); -} - -QDict *qtest_qmp_receive_success(QTestState *s, - void (*event_cb)(void *opaque, - const char *event, - QDict *data), - void *opaque) -{ - QDict *response, *ret, *data; - const char *event; - - for (;;) { - response = qtest_qmp_receive(s); - g_assert(!qdict_haskey(response, "error")); - ret = qdict_get_qdict(response, "return"); - if (ret) { - break; - } - event = qdict_get_str(response, "event"); - data = qdict_get_qdict(response, "data"); - if (event_cb) { - event_cb(opaque, event, data); - } - qobject_unref(response); - } - - qobject_ref(ret); - qobject_unref(response); - return ret; -} - -/* - * Generic hot-plugging test via the device_add QMP commands. - */ -void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv, - const QDict *arguments) -{ - QDict *resp; - QDict *args = arguments ? qdict_clone_shallow(arguments) : qdict_new(); - - g_assert(!qdict_haskey(args, "driver")); - qdict_put_str(args, "driver", drv); - resp = qtest_qmp(qts, "{'execute': 'device_add', 'arguments': %p}", args); - g_assert(resp); - g_assert(!qdict_haskey(resp, "event")); /* We don't expect any events */ - g_assert(!qdict_haskey(resp, "error")); - qobject_unref(resp); -} - -void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, - const char *fmt, ...) -{ - QDict *args; - va_list ap; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "id")); - qdict_put_str(args, "id", id); - - qtest_qmp_device_add_qdict(qts, driver, args); - qobject_unref(args); -} - -static void device_deleted_cb(void *opaque, const char *name, QDict *data) -{ - bool *got_event = opaque; - - g_assert_cmpstr(name, ==, "DEVICE_DELETED"); - *got_event = true; -} - -/* - * Generic hot-unplugging test via the device_del QMP command. - * Device deletion will get one response and one event. For example: - * - * {'execute': 'device_del','arguments': { 'id': 'scsi-hd'}} - * - * will get this one: - * - * {"timestamp": {"seconds": 1505289667, "microseconds": 569862}, - * "event": "DEVICE_DELETED", "data": {"device": "scsi-hd", - * "path": "/machine/peripheral/scsi-hd"}} - * - * and this one: - * - * {"return": {}} - * - * But the order of arrival may vary - so we've got to detect both. - */ -void qtest_qmp_device_del(QTestState *qts, const char *id) -{ - bool got_event = false; - QDict *rsp; - - qtest_qmp_send(qts, "{'execute': 'device_del', 'arguments': {'id': %s}}", - id); - rsp = qtest_qmp_receive_success(qts, device_deleted_cb, &got_event); - qobject_unref(rsp); - if (!got_event) { - rsp = qtest_qmp_receive(qts); - g_assert_cmpstr(qdict_get_try_str(rsp, "event"), - ==, "DEVICE_DELETED"); - qobject_unref(rsp); - } -} - -bool qmp_rsp_is_err(QDict *rsp) -{ - QDict *error = qdict_get_qdict(rsp, "error"); - qobject_unref(rsp); - return !!error; -} - -void qmp_assert_error_class(QDict *rsp, const char *class) -{ - QDict *error = qdict_get_qdict(rsp, "error"); - - g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, class); - g_assert_nonnull(qdict_get_try_str(error, "desc")); - g_assert(!qdict_haskey(rsp, "return")); - - qobject_unref(rsp); -} diff --git a/tests/libqtest.h b/tests/libqtest.h deleted file mode 100644 index c9e21e05b3..0000000000 --- a/tests/libqtest.h +++ /dev/null @@ -1,732 +0,0 @@ -/* - * QTest - * - * Copyright IBM, Corp. 2012 - * Copyright Red Hat, Inc. 2012 - * Copyright SUSE LINUX Products GmbH 2013 - * - * Authors: - * Anthony Liguori - * Paolo Bonzini - * Andreas Färber - * - * 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 LIBQTEST_H -#define LIBQTEST_H - -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qdict.h" - -typedef struct QTestState QTestState; - -/** - * qtest_initf: - * @fmt...: Format for creating other arguments to pass to QEMU, formatted - * like sprintf(). - * - * Convenience wrapper around qtest_init(). - * - * Returns: #QTestState instance. - */ -QTestState *qtest_initf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); - -/** - * qtest_vinitf: - * @fmt: Format for creating other arguments to pass to QEMU, formatted - * like vsprintf(). - * @ap: Format arguments. - * - * Convenience wrapper around qtest_init(). - * - * Returns: #QTestState instance. - */ -QTestState *qtest_vinitf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); - -/** - * qtest_init: - * @extra_args: other arguments to pass to QEMU. CAUTION: these - * arguments are subject to word splitting and shell evaluation. - * - * Returns: #QTestState instance. - */ -QTestState *qtest_init(const char *extra_args); - -/** - * qtest_init_without_qmp_handshake: - * @extra_args: other arguments to pass to QEMU. CAUTION: these - * arguments are subject to word splitting and shell evaluation. - * - * Returns: #QTestState instance. - */ -QTestState *qtest_init_without_qmp_handshake(const char *extra_args); - -/** - * qtest_init_with_serial: - * @extra_args: other arguments to pass to QEMU. CAUTION: these - * arguments are subject to word splitting and shell evaluation. - * @sock_fd: pointer to store the socket file descriptor for - * connection with serial. - * - * Returns: #QTestState instance. - */ -QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd); - -/** - * qtest_quit: - * @s: #QTestState instance to operate on. - * - * Shut down the QEMU process associated to @s. - */ -void qtest_quit(QTestState *s); - -/** - * qtest_qmp_fds: - * @s: #QTestState instance to operate on. - * @fds: array of file descriptors - * @fds_num: number of elements in @fds - * @fmt...: QMP message to send to qemu, formatted like - * qobject_from_jsonf_nofail(). See parse_escape() for what's - * supported after '%'. - * - * Sends a QMP message to QEMU with fds and returns the response. - */ -QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num, - const char *fmt, ...) - GCC_FMT_ATTR(4, 5); - -/** - * qtest_qmp: - * @s: #QTestState instance to operate on. - * @fmt...: QMP message to send to qemu, formatted like - * qobject_from_jsonf_nofail(). See parse_escape() for what's - * supported after '%'. - * - * Sends a QMP message to QEMU and returns the response. - */ -QDict *qtest_qmp(QTestState *s, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); - -/** - * qtest_qmp_send: - * @s: #QTestState instance to operate on. - * @fmt...: QMP message to send to qemu, formatted like - * qobject_from_jsonf_nofail(). See parse_escape() for what's - * supported after '%'. - * - * Sends a QMP message to QEMU and leaves the response in the stream. - */ -void qtest_qmp_send(QTestState *s, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); - -/** - * qtest_qmp_send_raw: - * @s: #QTestState instance to operate on. - * @fmt...: text to send, formatted like sprintf() - * - * Sends text to the QMP monitor verbatim. Need not be valid JSON; - * this is useful for negative tests. - */ -void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); - -/** - * qtest_vqmp_fds: - * @s: #QTestState instance to operate on. - * @fds: array of file descriptors - * @fds_num: number of elements in @fds - * @fmt: QMP message to send to QEMU, formatted like - * qobject_from_jsonf_nofail(). See parse_escape() for what's - * supported after '%'. - * @ap: QMP message arguments - * - * Sends a QMP message to QEMU with fds and returns the response. - */ -QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, - const char *fmt, va_list ap) - GCC_FMT_ATTR(4, 0); - -/** - * qtest_vqmp: - * @s: #QTestState instance to operate on. - * @fmt: QMP message to send to QEMU, formatted like - * qobject_from_jsonf_nofail(). See parse_escape() for what's - * supported after '%'. - * @ap: QMP message arguments - * - * Sends a QMP message to QEMU and returns the response. - */ -QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) - GCC_FMT_ATTR(2, 0); - -/** - * qtest_qmp_vsend_fds: - * @s: #QTestState instance to operate on. - * @fds: array of file descriptors - * @fds_num: number of elements in @fds - * @fmt: QMP message to send to QEMU, formatted like - * qobject_from_jsonf_nofail(). See parse_escape() for what's - * supported after '%'. - * @ap: QMP message arguments - * - * Sends a QMP message to QEMU and leaves the response in the stream. - */ -void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, - const char *fmt, va_list ap) - GCC_FMT_ATTR(4, 0); - -/** - * qtest_qmp_vsend: - * @s: #QTestState instance to operate on. - * @fmt: QMP message to send to QEMU, formatted like - * qobject_from_jsonf_nofail(). See parse_escape() for what's - * supported after '%'. - * @ap: QMP message arguments - * - * Sends a QMP message to QEMU and leaves the response in the stream. - */ -void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) - GCC_FMT_ATTR(2, 0); - -/** - * qtest_receive: - * @s: #QTestState instance to operate on. - * - * Reads a QMP message from QEMU and returns the response. - */ -QDict *qtest_qmp_receive(QTestState *s); - -/** - * qtest_qmp_eventwait: - * @s: #QTestState instance to operate on. - * @s: #event event to wait for. - * - * Continuously polls for QMP responses until it receives the desired event. - */ -void qtest_qmp_eventwait(QTestState *s, const char *event); - -/** - * qtest_qmp_eventwait_ref: - * @s: #QTestState instance to operate on. - * @s: #event event to wait for. - * - * Continuously polls for QMP responses until it receives the desired event. - * Returns a copy of the event for further investigation. - */ -QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event); - -/** - * qtest_qmp_receive_success: - * @s: #QTestState instance to operate on - * @event_cb: Event callback - * @opaque: Argument for @event_cb - * - * Poll QMP messages until a command success response is received. - * If @event_cb, call it for each event received, passing @opaque, - * the event's name and data. - * Return the success response's "return" member. - */ -QDict *qtest_qmp_receive_success(QTestState *s, - void (*event_cb)(void *opaque, - const char *name, - QDict *data), - void *opaque); - -/** - * qtest_hmp: - * @s: #QTestState instance to operate on. - * @fmt...: HMP command to send to QEMU, formats arguments like sprintf(). - * - * Send HMP command to QEMU via QMP's human-monitor-command. - * QMP events are discarded. - * - * Returns: the command's output. The caller should g_free() it. - */ -char *qtest_hmp(QTestState *s, const char *fmt, ...) GCC_FMT_ATTR(2, 3); - -/** - * qtest_hmpv: - * @s: #QTestState instance to operate on. - * @fmt: HMP command to send to QEMU, formats arguments like vsprintf(). - * @ap: HMP command arguments - * - * Send HMP command to QEMU via QMP's human-monitor-command. - * QMP events are discarded. - * - * Returns: the command's output. The caller should g_free() it. - */ -char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap) - GCC_FMT_ATTR(2, 0); - -void qtest_module_load(QTestState *s, const char *prefix, const char *libname); - -/** - * qtest_get_irq: - * @s: #QTestState instance to operate on. - * @num: Interrupt to observe. - * - * Returns: The level of the @num interrupt. - */ -bool qtest_get_irq(QTestState *s, int num); - -/** - * qtest_irq_intercept_in: - * @s: #QTestState instance to operate on. - * @string: QOM path of a device. - * - * Associate qtest irqs with the GPIO-in pins of the device - * whose path is specified by @string. - */ -void qtest_irq_intercept_in(QTestState *s, const char *string); - -/** - * qtest_irq_intercept_out: - * @s: #QTestState instance to operate on. - * @string: QOM path of a device. - * - * Associate qtest irqs with the GPIO-out pins of the device - * whose path is specified by @string. - */ -void qtest_irq_intercept_out(QTestState *s, const char *string); - -/** - * qtest_set_irq_in: - * @s: QTestState instance to operate on. - * @string: QOM path of a device - * @name: IRQ name - * @irq: IRQ number - * @level: IRQ level - * - * Force given device/irq GPIO-in pin to the given level. - */ -void qtest_set_irq_in(QTestState *s, const char *string, const char *name, - int irq, int level); - -/** - * qtest_outb: - * @s: #QTestState instance to operate on. - * @addr: I/O port to write to. - * @value: Value being written. - * - * Write an 8-bit value to an I/O port. - */ -void qtest_outb(QTestState *s, uint16_t addr, uint8_t value); - -/** - * qtest_outw: - * @s: #QTestState instance to operate on. - * @addr: I/O port to write to. - * @value: Value being written. - * - * Write a 16-bit value to an I/O port. - */ -void qtest_outw(QTestState *s, uint16_t addr, uint16_t value); - -/** - * qtest_outl: - * @s: #QTestState instance to operate on. - * @addr: I/O port to write to. - * @value: Value being written. - * - * Write a 32-bit value to an I/O port. - */ -void qtest_outl(QTestState *s, uint16_t addr, uint32_t value); - -/** - * qtest_inb: - * @s: #QTestState instance to operate on. - * @addr: I/O port to read from. - * - * Returns an 8-bit value from an I/O port. - */ -uint8_t qtest_inb(QTestState *s, uint16_t addr); - -/** - * qtest_inw: - * @s: #QTestState instance to operate on. - * @addr: I/O port to read from. - * - * Returns a 16-bit value from an I/O port. - */ -uint16_t qtest_inw(QTestState *s, uint16_t addr); - -/** - * qtest_inl: - * @s: #QTestState instance to operate on. - * @addr: I/O port to read from. - * - * Returns a 32-bit value from an I/O port. - */ -uint32_t qtest_inl(QTestState *s, uint16_t addr); - -/** - * qtest_writeb: - * @s: #QTestState instance to operate on. - * @addr: Guest address to write to. - * @value: Value being written. - * - * Writes an 8-bit value to memory. - */ -void qtest_writeb(QTestState *s, uint64_t addr, uint8_t value); - -/** - * qtest_writew: - * @s: #QTestState instance to operate on. - * @addr: Guest address to write to. - * @value: Value being written. - * - * Writes a 16-bit value to memory. - */ -void qtest_writew(QTestState *s, uint64_t addr, uint16_t value); - -/** - * qtest_writel: - * @s: #QTestState instance to operate on. - * @addr: Guest address to write to. - * @value: Value being written. - * - * Writes a 32-bit value to memory. - */ -void qtest_writel(QTestState *s, uint64_t addr, uint32_t value); - -/** - * qtest_writeq: - * @s: #QTestState instance to operate on. - * @addr: Guest address to write to. - * @value: Value being written. - * - * Writes a 64-bit value to memory. - */ -void qtest_writeq(QTestState *s, uint64_t addr, uint64_t value); - -/** - * qtest_readb: - * @s: #QTestState instance to operate on. - * @addr: Guest address to read from. - * - * Reads an 8-bit value from memory. - * - * Returns: Value read. - */ -uint8_t qtest_readb(QTestState *s, uint64_t addr); - -/** - * qtest_readw: - * @s: #QTestState instance to operate on. - * @addr: Guest address to read from. - * - * Reads a 16-bit value from memory. - * - * Returns: Value read. - */ -uint16_t qtest_readw(QTestState *s, uint64_t addr); - -/** - * qtest_readl: - * @s: #QTestState instance to operate on. - * @addr: Guest address to read from. - * - * Reads a 32-bit value from memory. - * - * Returns: Value read. - */ -uint32_t qtest_readl(QTestState *s, uint64_t addr); - -/** - * qtest_readq: - * @s: #QTestState instance to operate on. - * @addr: Guest address to read from. - * - * Reads a 64-bit value from memory. - * - * Returns: Value read. - */ -uint64_t qtest_readq(QTestState *s, uint64_t addr); - -/** - * qtest_memread: - * @s: #QTestState instance to operate on. - * @addr: Guest address to read from. - * @data: Pointer to where memory contents will be stored. - * @size: Number of bytes to read. - * - * Read guest memory into a buffer. - */ -void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size); - -/** - * qtest_rtas_call: - * @s: #QTestState instance to operate on. - * @name: name of the command to call. - * @nargs: Number of args. - * @args: Guest address to read args from. - * @nret: Number of return value. - * @ret: Guest address to write return values to. - * - * Call an RTAS function - */ -uint64_t qtest_rtas_call(QTestState *s, const char *name, - uint32_t nargs, uint64_t args, - uint32_t nret, uint64_t ret); - -/** - * qtest_bufread: - * @s: #QTestState instance to operate on. - * @addr: Guest address to read from. - * @data: Pointer to where memory contents will be stored. - * @size: Number of bytes to read. - * - * Read guest memory into a buffer and receive using a base64 encoding. - */ -void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size); - -/** - * qtest_memwrite: - * @s: #QTestState instance to operate on. - * @addr: Guest address to write to. - * @data: Pointer to the bytes that will be written to guest memory. - * @size: Number of bytes to write. - * - * Write a buffer to guest memory. - */ -void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size); - -/** - * qtest_bufwrite: - * @s: #QTestState instance to operate on. - * @addr: Guest address to write to. - * @data: Pointer to the bytes that will be written to guest memory. - * @size: Number of bytes to write. - * - * Write a buffer to guest memory and transmit using a base64 encoding. - */ -void qtest_bufwrite(QTestState *s, uint64_t addr, - const void *data, size_t size); - -/** - * qtest_memset: - * @s: #QTestState instance to operate on. - * @addr: Guest address to write to. - * @patt: Byte pattern to fill the guest memory region with. - * @size: Number of bytes to write. - * - * Write a pattern to guest memory. - */ -void qtest_memset(QTestState *s, uint64_t addr, uint8_t patt, size_t size); - -/** - * qtest_clock_step_next: - * @s: #QTestState instance to operate on. - * - * Advance the QEMU_CLOCK_VIRTUAL to the next deadline. - * - * Returns: The current value of the QEMU_CLOCK_VIRTUAL in nanoseconds. - */ -int64_t qtest_clock_step_next(QTestState *s); - -/** - * qtest_clock_step: - * @s: QTestState instance to operate on. - * @step: Number of nanoseconds to advance the clock by. - * - * Advance the QEMU_CLOCK_VIRTUAL by @step nanoseconds. - * - * Returns: The current value of the QEMU_CLOCK_VIRTUAL in nanoseconds. - */ -int64_t qtest_clock_step(QTestState *s, int64_t step); - -/** - * qtest_clock_set: - * @s: QTestState instance to operate on. - * @val: Nanoseconds value to advance the clock to. - * - * Advance the QEMU_CLOCK_VIRTUAL to @val nanoseconds since the VM was launched. - * - * Returns: The current value of the QEMU_CLOCK_VIRTUAL in nanoseconds. - */ -int64_t qtest_clock_set(QTestState *s, int64_t val); - -/** - * qtest_big_endian: - * @s: QTestState instance to operate on. - * - * Returns: True if the architecture under test has a big endian configuration. - */ -bool qtest_big_endian(QTestState *s); - -/** - * qtest_get_arch: - * - * Returns: The architecture for the QEMU executable under test. - */ -const char *qtest_get_arch(void); - -/** - * qtest_add_func: - * @str: Test case path. - * @fn: Test case function - * - * Add a GTester testcase with the given name and function. - * The path is prefixed with the architecture under test, as - * returned by qtest_get_arch(). - */ -void qtest_add_func(const char *str, void (*fn)(void)); - -/** - * qtest_add_data_func: - * @str: Test case path. - * @data: Test case data - * @fn: Test case function - * - * Add a GTester testcase with the given name, data and function. - * The path is prefixed with the architecture under test, as - * returned by qtest_get_arch(). - */ -void qtest_add_data_func(const char *str, const void *data, - void (*fn)(const void *)); - -/** - * qtest_add_data_func_full: - * @str: Test case path. - * @data: Test case data - * @fn: Test case function - * @data_free_func: GDestroyNotify for data - * - * Add a GTester testcase with the given name, data and function. - * The path is prefixed with the architecture under test, as - * returned by qtest_get_arch(). - * - * @data is passed to @data_free_func() on test completion. - */ -void qtest_add_data_func_full(const char *str, void *data, - void (*fn)(const void *), - GDestroyNotify data_free_func); - -/** - * qtest_add: - * @testpath: Test case path - * @Fixture: Fixture type - * @tdata: Test case data - * @fsetup: Test case setup function - * @ftest: Test case function - * @fteardown: Test case teardown function - * - * Add a GTester testcase with the given name, data and functions. - * The path is prefixed with the architecture under test, as - * returned by qtest_get_arch(). - */ -#define qtest_add(testpath, Fixture, tdata, fsetup, ftest, fteardown) \ - do { \ - char *path = g_strdup_printf("/%s/%s", qtest_get_arch(), testpath); \ - g_test_add(path, Fixture, tdata, fsetup, ftest, fteardown); \ - g_free(path); \ - } while (0) - -void qtest_add_abrt_handler(GHookFunc fn, const void *data); - -/** - * qtest_qmp_assert_success: - * @qts: QTestState instance to operate on - * @fmt...: QMP message to send to qemu, formatted like - * qobject_from_jsonf_nofail(). See parse_escape() for what's - * supported after '%'. - * - * Sends a QMP message to QEMU and asserts that a 'return' key is present in - * the response. - */ -void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) - GCC_FMT_ATTR(2, 3); - -QDict *qmp_fd_receive(int fd); -void qmp_fd_vsend_fds(int fd, int *fds, size_t fds_num, - const char *fmt, va_list ap) GCC_FMT_ATTR(4, 0); -void qmp_fd_vsend(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); -void qmp_fd_send(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); -void qmp_fd_send_raw(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); -void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); -QDict *qmp_fdv(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); -QDict *qmp_fd(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); - -/** - * qtest_cb_for_every_machine: - * @cb: Pointer to the callback function - * @skip_old_versioned: true if versioned old machine types should be skipped - * - * Call a callback function for every name of all available machines. - */ -void qtest_cb_for_every_machine(void (*cb)(const char *machine), - bool skip_old_versioned); - -/** - * qtest_qmp_device_add_qdict: - * @qts: QTestState instance to operate on - * @drv: Name of the device that should be added - * @arguments: QDict with properties for the device to intialize - * - * Generic hot-plugging test via the device_add QMP command with properties - * supplied in form of QDict. Use NULL for empty properties list. - */ -void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv, - const QDict *arguments); - -/** - * qtest_qmp_device_add: - * @qts: QTestState instance to operate on - * @driver: Name of the device that should be added - * @id: Identification string - * @fmt...: QMP message to send to qemu, formatted like - * qobject_from_jsonf_nofail(). See parse_escape() for what's - * supported after '%'. - * - * Generic hot-plugging test via the device_add QMP command. - */ -void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, - const char *fmt, ...) GCC_FMT_ATTR(4, 5); - -/** - * qtest_qmp_device_del: - * @qts: QTestState instance to operate on - * @id: Identification string - * - * Generic hot-unplugging test via the device_del QMP command. - */ -void qtest_qmp_device_del(QTestState *qts, const char *id); - -/** - * qmp_rsp_is_err: - * @rsp: QMP response to check for error - * - * Test @rsp for error and discard @rsp. - * Returns 'true' if there is error in @rsp and 'false' otherwise. - */ -bool qmp_rsp_is_err(QDict *rsp); - -/** - * qmp_assert_error_class: - * @rsp: QMP response to check for error - * @class: an error class - * - * Assert the response has the given error class and discard @rsp. - */ -void qmp_assert_error_class(QDict *rsp, const char *class); - -/** - * qtest_probe_child: - * @s: QTestState instance to operate on. - * - * Returns: true if the child is still alive. - */ -bool qtest_probe_child(QTestState *s); - -/** - * qtest_set_expected_status: - * @s: QTestState instance to operate on. - * @status: an expected exit status. - * - * Set expected exit status of the child. - */ -void qtest_set_expected_status(QTestState *s, int status); - -#endif diff --git a/tests/m25p80-test.c b/tests/m25p80-test.c deleted file mode 100644 index 50c6b79fb3..0000000000 --- a/tests/m25p80-test.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * QTest testcase for the M25P80 Flash (Using the Aspeed SPI - * Controller) - * - * Copyright (C) 2016 IBM Corp. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/bswap.h" -#include "libqtest-single.h" - -/* - * ASPEED SPI Controller registers - */ -#define R_CONF 0x00 -#define CONF_ENABLE_W0 (1 << 16) -#define R_CE_CTRL 0x04 -#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ -#define R_CTRL0 0x10 -#define CTRL_CE_STOP_ACTIVE (1 << 2) -#define CTRL_READMODE 0x0 -#define CTRL_FREADMODE 0x1 -#define CTRL_WRITEMODE 0x2 -#define CTRL_USERMODE 0x3 - -#define ASPEED_FMC_BASE 0x1E620000 -#define ASPEED_FLASH_BASE 0x20000000 - -/* - * Flash commands - */ -enum { - JEDEC_READ = 0x9f, - BULK_ERASE = 0xc7, - READ = 0x03, - PP = 0x02, - WREN = 0x6, - RESET_ENABLE = 0x66, - RESET_MEMORY = 0x99, - EN_4BYTE_ADDR = 0xB7, - ERASE_SECTOR = 0xd8, -}; - -#define FLASH_JEDEC 0x20ba19 /* n25q256a */ -#define FLASH_SIZE (32 * 1024 * 1024) - -#define PAGE_SIZE 256 - -/* - * Use an explicit bswap for the values read/wrote to the flash region - * as they are BE and the Aspeed CPU is LE. - */ -static inline uint32_t make_be32(uint32_t data) -{ - return bswap32(data); -} - -static void spi_conf(uint32_t value) -{ - uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); - - conf |= value; - writel(ASPEED_FMC_BASE + R_CONF, conf); -} - -static void spi_conf_remove(uint32_t value) -{ - uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); - - conf &= ~value; - writel(ASPEED_FMC_BASE + R_CONF, conf); -} - -static void spi_ce_ctrl(uint32_t value) -{ - uint32_t conf = readl(ASPEED_FMC_BASE + R_CE_CTRL); - - conf |= value; - writel(ASPEED_FMC_BASE + R_CE_CTRL, conf); -} - -static void spi_ctrl_setmode(uint8_t mode, uint8_t cmd) -{ - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); - ctrl &= ~(CTRL_USERMODE | 0xff << 16); - ctrl |= mode | (cmd << 16); - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); -} - -static void spi_ctrl_start_user(void) -{ - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); - - ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); - - ctrl &= ~CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); -} - -static void spi_ctrl_stop_user(void) -{ - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); - - ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); -} - -static void flash_reset(void) -{ - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, RESET_ENABLE); - writeb(ASPEED_FLASH_BASE, RESET_MEMORY); - spi_ctrl_stop_user(); - - spi_conf_remove(CONF_ENABLE_W0); -} - -static void test_read_jedec(void) -{ - uint32_t jedec = 0x0; - - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, JEDEC_READ); - jedec |= readb(ASPEED_FLASH_BASE) << 16; - jedec |= readb(ASPEED_FLASH_BASE) << 8; - jedec |= readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - - flash_reset(); - - g_assert_cmphex(jedec, ==, FLASH_JEDEC); -} - -static void read_page(uint32_t addr, uint32_t *page) -{ - int i; - - spi_ctrl_start_user(); - - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, READ); - writel(ASPEED_FLASH_BASE, make_be32(addr)); - - /* Continuous read are supported */ - for (i = 0; i < PAGE_SIZE / 4; i++) { - page[i] = make_be32(readl(ASPEED_FLASH_BASE)); - } - spi_ctrl_stop_user(); -} - -static void read_page_mem(uint32_t addr, uint32_t *page) -{ - int i; - - /* move out USER mode to use direct reads from the AHB bus */ - spi_ctrl_setmode(CTRL_READMODE, READ); - - for (i = 0; i < PAGE_SIZE / 4; i++) { - page[i] = make_be32(readl(ASPEED_FLASH_BASE + addr + i * 4)); - } -} - -static void test_erase_sector(void) -{ - uint32_t some_page_addr = 0x600 * PAGE_SIZE; - uint32_t page[PAGE_SIZE / 4]; - int i; - - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, ERASE_SECTOR); - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); - spi_ctrl_stop_user(); - - /* Previous page should be full of zeroes as backend is not - * initialized */ - read_page(some_page_addr - PAGE_SIZE, page); - for (i = 0; i < PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0x0); - } - - /* But this one was erased */ - read_page(some_page_addr, page); - for (i = 0; i < PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_erase_all(void) -{ - uint32_t some_page_addr = 0x15000 * PAGE_SIZE; - uint32_t page[PAGE_SIZE / 4]; - int i; - - spi_conf(CONF_ENABLE_W0); - - /* Check some random page. Should be full of zeroes as backend is - * not initialized */ - read_page(some_page_addr, page); - for (i = 0; i < PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0x0); - } - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - spi_ctrl_stop_user(); - - /* Recheck that some random page */ - read_page(some_page_addr, page); - for (i = 0; i < PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_write_page(void) -{ - uint32_t my_page_addr = 0x14000 * PAGE_SIZE; /* beyond 16MB */ - uint32_t some_page_addr = 0x15000 * PAGE_SIZE; - uint32_t page[PAGE_SIZE / 4]; - int i; - - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); - } - spi_ctrl_stop_user(); - - /* Check what was written */ - read_page(my_page_addr, page); - for (i = 0; i < PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - /* Check some other page. It should be full of 0xff */ - read_page(some_page_addr, page); - for (i = 0; i < PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_read_page_mem(void) -{ - uint32_t my_page_addr = 0x14000 * PAGE_SIZE; /* beyond 16MB */ - uint32_t some_page_addr = 0x15000 * PAGE_SIZE; - uint32_t page[PAGE_SIZE / 4]; - int i; - - /* Enable 4BYTE mode for controller. This is should be strapped by - * HW for CE0 anyhow. - */ - spi_ce_ctrl(1 << CRTL_EXTENDED0); - - /* Enable 4BYTE mode for flash. */ - spi_conf(CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - spi_ctrl_stop_user(); - spi_conf_remove(CONF_ENABLE_W0); - - /* Check what was written */ - read_page_mem(my_page_addr, page); - for (i = 0; i < PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - /* Check some other page. It should be full of 0xff */ - read_page_mem(some_page_addr, page); - for (i = 0; i < PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_write_page_mem(void) -{ - uint32_t my_page_addr = 0x15000 * PAGE_SIZE; - uint32_t page[PAGE_SIZE / 4]; - int i; - - /* Enable 4BYTE mode for controller. This is should be strapped by - * HW for CE0 anyhow. - */ - spi_ce_ctrl(1 << CRTL_EXTENDED0); - - /* Enable 4BYTE mode for flash. */ - spi_conf(CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - spi_ctrl_stop_user(); - - /* move out USER mode to use direct writes to the AHB bus */ - spi_ctrl_setmode(CTRL_WRITEMODE, PP); - - for (i = 0; i < PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE + my_page_addr + i * 4, - make_be32(my_page_addr + i * 4)); - } - - /* Check what was written */ - read_page_mem(my_page_addr, page); - for (i = 0; i < PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - flash_reset(); -} - -static char tmp_path[] = "/tmp/qtest.m25p80.XXXXXX"; - -int main(int argc, char **argv) -{ - int ret; - int fd; - - g_test_init(&argc, &argv, NULL); - - fd = mkstemp(tmp_path); - g_assert(fd >= 0); - ret = ftruncate(fd, FLASH_SIZE); - g_assert(ret == 0); - close(fd); - - global_qtest = qtest_initf("-m 256 -machine palmetto-bmc " - "-drive file=%s,format=raw,if=mtd", - tmp_path); - - qtest_add_func("/m25p80/read_jedec", test_read_jedec); - qtest_add_func("/m25p80/erase_sector", test_erase_sector); - qtest_add_func("/m25p80/erase_all", test_erase_all); - qtest_add_func("/m25p80/write_page", test_write_page); - qtest_add_func("/m25p80/read_page_mem", test_read_page_mem); - qtest_add_func("/m25p80/write_page_mem", test_write_page_mem); - - ret = g_test_run(); - - qtest_quit(global_qtest); - unlink(tmp_path); - return ret; -} diff --git a/tests/m48t59-test.c b/tests/m48t59-test.c deleted file mode 100644 index b94a1230f7..0000000000 --- a/tests/m48t59-test.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * QTest testcase for the M48T59 and M48T08 real-time clocks - * - * Based on MC146818 RTC test: - * Copyright IBM, Corp. 2012 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" - -#include "libqtest.h" - -#define RTC_SECONDS 0x9 -#define RTC_MINUTES 0xa -#define RTC_HOURS 0xb - -#define RTC_DAY_OF_WEEK 0xc -#define RTC_DAY_OF_MONTH 0xd -#define RTC_MONTH 0xe -#define RTC_YEAR 0xf - -static uint32_t base; -static uint16_t reg_base = 0x1ff0; /* 0x7f0 for m48t02 */ -static int base_year; -static const char *base_machine; -static bool use_mmio; - -static uint8_t cmos_read_mmio(QTestState *s, uint8_t reg) -{ - return qtest_readb(s, base + (uint32_t)reg_base + (uint32_t)reg); -} - -static void cmos_write_mmio(QTestState *s, uint8_t reg, uint8_t val) -{ - uint8_t data = val; - - qtest_writeb(s, base + (uint32_t)reg_base + (uint32_t)reg, data); -} - -static uint8_t cmos_read_ioio(QTestState *s, uint8_t reg) -{ - qtest_outw(s, base + 0, reg_base + (uint16_t)reg); - return qtest_inb(s, base + 3); -} - -static void cmos_write_ioio(QTestState *s, uint8_t reg, uint8_t val) -{ - qtest_outw(s, base + 0, reg_base + (uint16_t)reg); - qtest_outb(s, base + 3, val); -} - -static uint8_t cmos_read(QTestState *s, uint8_t reg) -{ - if (use_mmio) { - return cmos_read_mmio(s, reg); - } else { - return cmos_read_ioio(s, reg); - } -} - -static void cmos_write(QTestState *s, uint8_t reg, uint8_t val) -{ - if (use_mmio) { - cmos_write_mmio(s, reg, val); - } else { - cmos_write_ioio(s, reg, val); - } -} - -static int bcd2dec(int value) -{ - return (((value >> 4) & 0x0F) * 10) + (value & 0x0F); -} - -static int tm_cmp(struct tm *lhs, struct tm *rhs) -{ - time_t a, b; - struct tm d1, d2; - - memcpy(&d1, lhs, sizeof(d1)); - memcpy(&d2, rhs, sizeof(d2)); - - a = mktime(&d1); - b = mktime(&d2); - - if (a < b) { - return -1; - } else if (a > b) { - return 1; - } - - return 0; -} - -#if 0 -static void print_tm(struct tm *tm) -{ - printf("%04d-%02d-%02d %02d:%02d:%02d %+02ld\n", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff); -} -#endif - -static void cmos_get_date_time(QTestState *s, struct tm *date) -{ - int sec, min, hour, mday, mon, year; - time_t ts; - struct tm dummy; - - sec = cmos_read(s, RTC_SECONDS); - min = cmos_read(s, RTC_MINUTES); - hour = cmos_read(s, RTC_HOURS); - mday = cmos_read(s, RTC_DAY_OF_MONTH); - mon = cmos_read(s, RTC_MONTH); - year = cmos_read(s, RTC_YEAR); - - sec = bcd2dec(sec); - min = bcd2dec(min); - hour = bcd2dec(hour); - mday = bcd2dec(mday); - mon = bcd2dec(mon); - year = bcd2dec(year); - - ts = time(NULL); - localtime_r(&ts, &dummy); - - date->tm_isdst = dummy.tm_isdst; - date->tm_sec = sec; - date->tm_min = min; - date->tm_hour = hour; - date->tm_mday = mday; - date->tm_mon = mon - 1; - date->tm_year = base_year + year - 1900; -#ifndef __sun__ - date->tm_gmtoff = 0; -#endif - - ts = mktime(date); -} - -static QTestState *m48t59_qtest_start(void) -{ - return qtest_initf("-M %s -rtc clock=vm", base_machine); -} - -static void bcd_check_time(void) -{ - struct tm start, date[4], end; - struct tm *datep; - time_t ts; - const int wiggle = 2; - QTestState *s = m48t59_qtest_start(); - - /* - * This check assumes a few things. First, we cannot guarantee that we get - * a consistent reading from the wall clock because we may hit an edge of - * the clock while reading. To work around this, we read four clock readings - * such that at least two of them should match. We need to assume that one - * reading is corrupt so we need four readings to ensure that we have at - * least two consecutive identical readings - * - * It's also possible that we'll cross an edge reading the host clock so - * simply check to make sure that the clock reading is within the period of - * when we expect it to be. - */ - - ts = time(NULL); - gmtime_r(&ts, &start); - - cmos_get_date_time(s, &date[0]); - cmos_get_date_time(s, &date[1]); - cmos_get_date_time(s, &date[2]); - cmos_get_date_time(s, &date[3]); - - ts = time(NULL); - gmtime_r(&ts, &end); - - if (tm_cmp(&date[0], &date[1]) == 0) { - datep = &date[0]; - } else if (tm_cmp(&date[1], &date[2]) == 0) { - datep = &date[1]; - } else if (tm_cmp(&date[2], &date[3]) == 0) { - datep = &date[2]; - } else { - g_assert_not_reached(); - } - - if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) { - long t, s; - - start.tm_isdst = datep->tm_isdst; - - t = (long)mktime(datep); - s = (long)mktime(&start); - if (t < s) { - g_test_message("RTC is %ld second(s) behind wall-clock", (s - t)); - } else { - g_test_message("RTC is %ld second(s) ahead of wall-clock", (t - s)); - } - - g_assert_cmpint(ABS(t - s), <=, wiggle); - } - - qtest_quit(s); -} - -/* success if no crash or abort */ -static void fuzz_registers(void) -{ - unsigned int i; - QTestState *s = m48t59_qtest_start(); - - for (i = 0; i < 1000; i++) { - uint8_t reg, val; - - reg = (uint8_t)g_test_rand_int_range(0, 16); - val = (uint8_t)g_test_rand_int_range(0, 256); - - if (reg == 7) { - /* watchdog setup register, may trigger system reset, skip */ - continue; - } - - cmos_write(s, reg, val); - cmos_read(s, reg); - } - - qtest_quit(s); -} - -static void base_setup(void) -{ - const char *arch = qtest_get_arch(); - - if (g_str_equal(arch, "sparc")) { - /* Note: For sparc64, we'd need to map-in the PCI bridge memory first */ - base = 0x71200000; - base_year = 1968; - base_machine = "SS-5"; - use_mmio = true; - } else if (g_str_equal(arch, "ppc") || g_str_equal(arch, "ppc64")) { - base = 0xF0000000; - base_year = 1968; - base_machine = "ref405ep"; - use_mmio = true; - } else { - g_assert_not_reached(); - } -} - -int main(int argc, char **argv) -{ - base_setup(); - - g_test_init(&argc, &argv, NULL); - - if (g_test_slow()) { - /* Do not run this in timing-sensitive environments */ - qtest_add_func("/rtc/bcd-check-time", bcd_check_time); - } - qtest_add_func("/rtc/fuzz-registers", fuzz_registers); - return g_test_run(); -} diff --git a/tests/machine-none-test.c b/tests/machine-none-test.c deleted file mode 100644 index 5953d31755..0000000000 --- a/tests/machine-none-test.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Machine 'none' tests. - * - * Copyright (c) 2018 Red Hat Inc. - * - * Authors: - * Igor Mammedov , - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qemu-common.h" -#include "qemu/cutils.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" - - -struct arch2cpu { - const char *arch; - const char *cpu_model; -}; - -static struct arch2cpu cpus_map[] = { - /* tested targets list */ - { "arm", "cortex-a15" }, - { "aarch64", "cortex-a57" }, - { "x86_64", "qemu64,apic-id=0" }, - { "i386", "qemu32,apic-id=0" }, - { "alpha", "ev67" }, - { "cris", "crisv32" }, - { "lm32", "lm32-full" }, - { "m68k", "m5206" }, - /* FIXME: { "microblaze", "any" }, doesn't work with -M none -cpu any */ - /* FIXME: { "microblazeel", "any" }, doesn't work with -M none -cpu any */ - { "mips", "4Kc" }, - { "mipsel", "I7200" }, - { "mips64", "20Kc" }, - { "mips64el", "I6500" }, - { "moxie", "MoxieLite" }, - { "nios2", "FIXME" }, - { "or1k", "or1200" }, - { "ppc", "604" }, - { "ppc64", "power8e_v2.1" }, - { "s390x", "qemu" }, - { "sh4", "sh7750r" }, - { "sh4eb", "sh7751r" }, - { "sparc", "LEON2" }, - { "sparc64", "Fujitsu Sparc64" }, - { "tricore", "tc1796" }, - { "unicore32", "UniCore-II" }, - { "xtensa", "dc233c" }, - { "xtensaeb", "fsf" }, - { "hppa", "hppa" }, - { "riscv64", "rv64gcsu-v1.10.0" }, - { "riscv32", "rv32gcsu-v1.9.1" }, -}; - -static const char *get_cpu_model_by_arch(const char *arch) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(cpus_map); i++) { - if (!strcmp(arch, cpus_map[i].arch)) { - return cpus_map[i].cpu_model; - } - } - return NULL; -} - -static void test_machine_cpu_cli(void) -{ - QDict *response; - const char *arch = qtest_get_arch(); - const char *cpu_model = get_cpu_model_by_arch(arch); - QTestState *qts; - - if (!cpu_model) { - if (!(!strcmp(arch, "microblaze") || !strcmp(arch, "microblazeel"))) { - fprintf(stderr, "WARNING: cpu name for target '%s' isn't defined," - " add it to cpus_map\n", arch); - } - return; /* TODO: die here to force all targets have a test */ - } - qts = qtest_initf("-machine none -cpu '%s'", cpu_model); - - response = qtest_qmp(qts, "{ 'execute': 'quit' }"); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); - - qtest_quit(qts); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qtest_add_func("machine/none/cpu_option", test_machine_cpu_cli); - - return g_test_run(); -} diff --git a/tests/megasas-test.c b/tests/megasas-test.c deleted file mode 100644 index d6796b9bd7..0000000000 --- a/tests/megasas-test.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * QTest testcase for LSI MegaRAID - * - * Copyright (c) 2017 Red Hat Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/bswap.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -typedef struct QMegasas QMegasas; - -struct QMegasas { - QOSGraphObject obj; - QPCIDevice dev; -}; - -static void *megasas_get_driver(void *obj, const char *interface) -{ - QMegasas *megasas = obj; - - if (!g_strcmp0(interface, "pci-device")) { - return &megasas->dev; - } - - fprintf(stderr, "%s not present in megasas\n", interface); - g_assert_not_reached(); -} - -static void *megasas_create(void *pci_bus, QGuestAllocator *alloc, void *addr) -{ - QMegasas *megasas = g_new0(QMegasas, 1); - QPCIBus *bus = pci_bus; - - qpci_device_init(&megasas->dev, bus, addr); - megasas->obj.get_driver = megasas_get_driver; - - return &megasas->obj; -} - -/* This used to cause a NULL pointer dereference. */ -static void megasas_pd_get_info_fuzz(void *obj, void *data, QGuestAllocator *alloc) -{ - QMegasas *megasas = obj; - QPCIDevice *dev = &megasas->dev; - QPCIBar bar; - uint32_t context[256]; - uint64_t context_pa; - int i; - - qpci_device_enable(dev); - bar = qpci_iomap(dev, 0, NULL); - - memset(context, 0, sizeof(context)); - context[0] = cpu_to_le32(0x05050505); - context[1] = cpu_to_le32(0x01010101); - for (i = 2; i < ARRAY_SIZE(context); i++) { - context[i] = cpu_to_le32(0x41414141); - } - context[6] = cpu_to_le32(0x02020000); - context[7] = cpu_to_le32(0); - - context_pa = guest_alloc(alloc, sizeof(context)); - qtest_memwrite(dev->bus->qts, context_pa, context, sizeof(context)); - qpci_io_writel(dev, bar, 0x40, context_pa); -} - -static void megasas_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0,id=scsi0", - .before_cmd_line = "-drive id=drv0,if=none,file=null-co://," - "file.read-zeroes=on,format=raw", - .after_cmd_line = "-device scsi-hd,bus=scsi0.0,drive=drv0", - }; - - add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); - - qos_node_create_driver("megasas", megasas_create); - qos_node_consumes("megasas", "pci-bus", &opts); - qos_node_produces("megasas", "pci-device"); - - qos_add_test("dcmd/pd-get-info/fuzz", "megasas", megasas_pd_get_info_fuzz, NULL); -} -libqos_init(megasas_register_nodes); diff --git a/tests/microbit-test.c b/tests/microbit-test.c deleted file mode 100644 index 04e199ec33..0000000000 --- a/tests/microbit-test.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * QTest testcase for Microbit board using the Nordic Semiconductor nRF51 SoC. - * - * nRF51: - * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf - * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf - * - * Microbit Board: http://microbit.org/ - * - * Copyright 2018 Steffen Görtz - * - * This code is licensed under the GPL version 2 or later. See - * the COPYING file in the top-level directory. - */ - - -#include "qemu/osdep.h" -#include "exec/hwaddr.h" -#include "libqtest.h" - -#include "hw/arm/nrf51.h" -#include "hw/char/nrf51_uart.h" -#include "hw/gpio/nrf51_gpio.h" -#include "hw/nvram/nrf51_nvm.h" -#include "hw/timer/nrf51_timer.h" -#include "hw/i2c/microbit_i2c.h" - -static bool uart_wait_for_event(QTestState *qts, uint32_t event_addr) -{ - time_t now, start = time(NULL); - - while (true) { - if (qtest_readl(qts, event_addr) == 1) { - qtest_writel(qts, event_addr, 0x00); - return true; - } - - /* Wait at most 10 minutes */ - now = time(NULL); - if (now - start > 600) { - break; - } - g_usleep(10000); - } - - return false; -} - -static void uart_rw_to_rxd(QTestState *qts, int sock_fd, const char *in, - char *out) -{ - int i, in_len = strlen(in); - - g_assert_true(write(sock_fd, in, in_len) == in_len); - for (i = 0; i < in_len; i++) { - g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + - A_UART_RXDRDY)); - out[i] = qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD); - } - out[i] = '\0'; -} - -static void uart_w_to_txd(QTestState *qts, const char *in) -{ - int i, in_len = strlen(in); - - for (i = 0; i < in_len; i++) { - qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, in[i]); - g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + - A_UART_TXDRDY)); - } -} - -static void test_nrf51_uart(void) -{ - int sock_fd; - char s[10]; - QTestState *qts = qtest_init_with_serial("-M microbit", &sock_fd); - - g_assert_true(write(sock_fd, "c", 1) == 1); - g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 0x00); - - qtest_writel(qts, NRF51_UART_BASE + A_UART_ENABLE, 0x04); - qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTRX, 0x01); - - g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + A_UART_RXDRDY)); - qtest_writel(qts, NRF51_UART_BASE + A_UART_RXDRDY, 0x00); - g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 'c'); - - qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENSET, 0x04); - g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x04); - qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENCLR, 0x04); - g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x00); - - uart_rw_to_rxd(qts, sock_fd, "hello", s); - g_assert_true(memcmp(s, "hello", 5) == 0); - - qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01); - uart_w_to_txd(qts, "d"); - g_assert_true(read(sock_fd, s, 10) == 1); - g_assert_cmphex(s[0], ==, 'd'); - - qtest_writel(qts, NRF51_UART_BASE + A_UART_SUSPEND, 0x01); - qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, 'h'); - qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01); - uart_w_to_txd(qts, "world"); - g_assert_true(read(sock_fd, s, 10) == 5); - g_assert_true(memcmp(s, "world", 5) == 0); - - close(sock_fd); - - qtest_quit(qts); -} - -/* Read a byte from I2C device at @addr from register @reg */ -static uint32_t i2c_read_byte(QTestState *qts, uint32_t addr, uint32_t reg) -{ - uint32_t val; - - qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ADDRESS, addr); - qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTTX, 1); - qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_TXD, reg); - val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_TXDSENT); - g_assert_cmpuint(val, ==, 1); - qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1); - - qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTRX, 1); - val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_RXDREADY); - g_assert_cmpuint(val, ==, 1); - val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_REG_RXD); - qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1); - - return val; -} - -static void test_microbit_i2c(void) -{ - uint32_t val; - QTestState *qts = qtest_init("-M microbit"); - - /* We don't program pins/irqs but at least enable the device */ - qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 5); - - /* MMA8653 magnetometer detection */ - val = i2c_read_byte(qts, 0x3A, 0x0D); - g_assert_cmpuint(val, ==, 0x5A); - - val = i2c_read_byte(qts, 0x3A, 0x0D); - g_assert_cmpuint(val, ==, 0x5A); - - /* LSM303 accelerometer detection */ - val = i2c_read_byte(qts, 0x3C, 0x4F); - g_assert_cmpuint(val, ==, 0x40); - - qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 0); - - qtest_quit(qts); -} - -#define FLASH_SIZE (256 * NRF51_PAGE_SIZE) - -static void fill_and_erase(QTestState *qts, hwaddr base, hwaddr size, - uint32_t address_reg) -{ - hwaddr i; - - /* Erase Page */ - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); - qtest_writel(qts, NRF51_NVMC_BASE + address_reg, base); - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); - - /* Check memory */ - for (i = 0; i < size / 4; i++) { - g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, 0xFFFFFFFF); - } - - /* Fill memory */ - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); - for (i = 0; i < size / 4; i++) { - qtest_writel(qts, base + i * 4, i); - g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, i); - } - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); -} - -static void test_nrf51_nvmc(void) -{ - uint32_t value; - hwaddr i; - QTestState *qts = qtest_init("-M microbit"); - - /* Test always ready */ - value = qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_READY); - g_assert_cmpuint(value & 0x01, ==, 0x01); - - /* Test write-read config register */ - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x03); - g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG), - ==, 0x03); - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); - g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG), - ==, 0x00); - - /* Test PCR0 */ - fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE, - NRF51_NVMC_ERASEPCR0); - fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE, - NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR0); - - /* Test PCR1 */ - fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE, - NRF51_NVMC_ERASEPCR1); - fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE, - NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR1); - - /* Erase all */ - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01); - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); - - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); - for (i = 0; i < FLASH_SIZE / 4; i++) { - qtest_writel(qts, NRF51_FLASH_BASE + i * 4, i); - g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4), ==, i); - } - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); - - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01); - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); - - for (i = 0; i < FLASH_SIZE / 4; i++) { - g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4), - ==, 0xFFFFFFFF); - } - - /* Erase UICR */ - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01); - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); - - for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { - g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), - ==, 0xFFFFFFFF); - } - - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); - for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { - qtest_writel(qts, NRF51_UICR_BASE + i * 4, i); - g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), ==, i); - } - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); - - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01); - qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); - - for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { - g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), - ==, 0xFFFFFFFF); - } - - qtest_quit(qts); -} - -static void test_nrf51_gpio(void) -{ - size_t i; - uint32_t actual, expected; - - struct { - hwaddr addr; - uint32_t expected; - } const reset_state[] = { - {NRF51_GPIO_REG_OUT, 0x00000000}, {NRF51_GPIO_REG_OUTSET, 0x00000000}, - {NRF51_GPIO_REG_OUTCLR, 0x00000000}, {NRF51_GPIO_REG_IN, 0x00000000}, - {NRF51_GPIO_REG_DIR, 0x00000000}, {NRF51_GPIO_REG_DIRSET, 0x00000000}, - {NRF51_GPIO_REG_DIRCLR, 0x00000000} - }; - - QTestState *qts = qtest_init("-M microbit"); - - /* Check reset state */ - for (i = 0; i < ARRAY_SIZE(reset_state); i++) { - expected = reset_state[i].expected; - actual = qtest_readl(qts, NRF51_GPIO_BASE + reset_state[i].addr); - g_assert_cmpuint(actual, ==, expected); - } - - for (i = 0; i < NRF51_GPIO_PINS; i++) { - expected = 0x00000002; - actual = qtest_readl(qts, NRF51_GPIO_BASE + - NRF51_GPIO_REG_CNF_START + i * 4); - g_assert_cmpuint(actual, ==, expected); - } - - /* Check dir bit consistency between dir and cnf */ - /* Check set via DIRSET */ - expected = 0x80000001; - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRSET, expected); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); - g_assert_cmpuint(actual, ==, expected); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) - & 0x01; - g_assert_cmpuint(actual, ==, 0x01); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); - - /* Check clear via DIRCLR */ - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); - g_assert_cmpuint(actual, ==, 0x00000000); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) - & 0x01; - g_assert_cmpuint(actual, ==, 0x00); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); - - /* Check set via DIR */ - expected = 0x80000001; - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, expected); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); - g_assert_cmpuint(actual, ==, expected); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) - & 0x01; - g_assert_cmpuint(actual, ==, 0x01); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); - - /* Reset DIR */ - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000); - - /* Check Input propagates */ - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00); - qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); - qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); - qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); - - /* Check pull-up working */ - qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); - - /* Check pull-down working */ - qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); - qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); - - /* Check Output propagates */ - qtest_irq_intercept_out(qts, "/machine/nrf51"); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0011); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); - g_assert_true(qtest_get_irq(qts, 0)); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); - g_assert_false(qtest_get_irq(qts, 0)); - - /* Check self-stimulation */ - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); - - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); - actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); - - /* - * Check short-circuit - generates an guest_error which must be checked - * manually as long as qtest can not scan qemu_log messages - */ - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); - qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); - qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); - - qtest_quit(qts); -} - -static void timer_task(QTestState *qts, hwaddr task) -{ - qtest_writel(qts, NRF51_TIMER_BASE + task, NRF51_TRIGGER_TASK); -} - -static void timer_clear_event(QTestState *qts, hwaddr event) -{ - qtest_writel(qts, NRF51_TIMER_BASE + event, NRF51_EVENT_CLEAR); -} - -static void timer_set_bitmode(QTestState *qts, uint8_t mode) -{ - qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_BITMODE, mode); -} - -static void timer_set_prescaler(QTestState *qts, uint8_t prescaler) -{ - qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_PRESCALER, prescaler); -} - -static void timer_set_cc(QTestState *qts, size_t idx, uint32_t value) -{ - qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_CC0 + idx * 4, value); -} - -static void timer_assert_events(QTestState *qts, uint32_t ev0, uint32_t ev1, - uint32_t ev2, uint32_t ev3) -{ - g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_0) - == ev0); - g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_1) - == ev1); - g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_2) - == ev2); - g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_3) - == ev3); -} - -static void test_nrf51_timer(void) -{ - uint32_t steps_to_overflow = 408; - QTestState *qts = qtest_init("-M microbit"); - - /* Compare Match */ - timer_task(qts, NRF51_TIMER_TASK_STOP); - timer_task(qts, NRF51_TIMER_TASK_CLEAR); - - timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0); - timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1); - timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2); - timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3); - - timer_set_bitmode(qts, NRF51_TIMER_WIDTH_16); /* 16 MHz Timer */ - timer_set_prescaler(qts, 0); - /* Swept over in first step */ - timer_set_cc(qts, 0, 2); - /* Barely miss on first step */ - timer_set_cc(qts, 1, 162); - /* Spot on on third step */ - timer_set_cc(qts, 2, 480); - - timer_assert_events(qts, 0, 0, 0, 0); - - timer_task(qts, NRF51_TIMER_TASK_START); - qtest_clock_step(qts, 10000); - timer_assert_events(qts, 1, 0, 0, 0); - - /* Swept over on first overflow */ - timer_set_cc(qts, 3, 114); - - qtest_clock_step(qts, 10000); - timer_assert_events(qts, 1, 1, 0, 0); - - qtest_clock_step(qts, 10000); - timer_assert_events(qts, 1, 1, 1, 0); - - /* Wrap time until internal counter overflows */ - while (steps_to_overflow--) { - timer_assert_events(qts, 1, 1, 1, 0); - qtest_clock_step(qts, 10000); - } - - timer_assert_events(qts, 1, 1, 1, 1); - - timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0); - timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1); - timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2); - timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3); - timer_assert_events(qts, 0, 0, 0, 0); - - timer_task(qts, NRF51_TIMER_TASK_STOP); - - /* Test Proposal: Stop/Shutdown */ - /* Test Proposal: Shortcut Compare -> Clear */ - /* Test Proposal: Shortcut Compare -> Stop */ - /* Test Proposal: Counter Mode */ - - qtest_quit(qts); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/microbit/nrf51/uart", test_nrf51_uart); - qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio); - qtest_add_func("/microbit/nrf51/nvmc", test_nrf51_nvmc); - qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer); - qtest_add_func("/microbit/microbit/i2c", test_microbit_i2c); - - return g_test_run(); -} diff --git a/tests/migration-helpers.c b/tests/migration-helpers.c deleted file mode 100644 index 516093b39a..0000000000 --- a/tests/migration-helpers.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * QTest migration helpers - * - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * based on the vhost-user-test.c that is: - * Copyright (c) 2014 Virtual Open Systems Sarl. - * - * 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/qmp/qjson.h" - -#include "migration-helpers.h" - -bool got_stop; - -static void stop_cb(void *opaque, const char *name, QDict *data) -{ - if (!strcmp(name, "STOP")) { - got_stop = true; - } -} - -/* - * Events can get in the way of responses we are actually waiting for. - */ -QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) -{ - va_list ap; - - va_start(ap, command); - qtest_qmp_vsend_fds(who, &fd, 1, command, ap); - va_end(ap); - - return qtest_qmp_receive_success(who, stop_cb, NULL); -} - -/* - * Events can get in the way of responses we are actually waiting for. - */ -QDict *wait_command(QTestState *who, const char *command, ...) -{ - va_list ap; - - va_start(ap, command); - qtest_qmp_vsend(who, command, ap); - va_end(ap); - - return qtest_qmp_receive_success(who, stop_cb, NULL); -} - -/* - * Send QMP command "migrate". - * Arguments are built from @fmt... (formatted like - * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. - */ -void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) -{ - va_list ap; - QDict *args, *rsp; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); - - rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); - - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); -} - -/* - * Note: caller is responsible to free the returned object via - * qobject_unref() after use - */ -QDict *migrate_query(QTestState *who) -{ - return wait_command(who, "{ 'execute': 'query-migrate' }"); -} - -/* - * Note: caller is responsible to free the returned object via - * g_free() after use - */ -static gchar *migrate_query_status(QTestState *who) -{ - QDict *rsp_return = migrate_query(who); - gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); - - g_assert(status); - qobject_unref(rsp_return); - - return status; -} - -static bool check_migration_status(QTestState *who, const char *goal, - const char **ungoals) -{ - bool ready; - char *current_status; - const char **ungoal; - - current_status = migrate_query_status(who); - ready = strcmp(current_status, goal) == 0; - if (!ungoals) { - g_assert_cmpstr(current_status, !=, "failed"); - /* - * If looking for a state other than completed, - * completion of migration would cause the test to - * hang. - */ - if (strcmp(goal, "completed") != 0) { - g_assert_cmpstr(current_status, !=, "completed"); - } - } else { - for (ungoal = ungoals; *ungoal; ungoal++) { - g_assert_cmpstr(current_status, !=, *ungoal); - } - } - g_free(current_status); - return ready; -} - -void wait_for_migration_status(QTestState *who, - const char *goal, const char **ungoals) -{ - while (!check_migration_status(who, goal, ungoals)) { - usleep(1000); - } -} - -void wait_for_migration_complete(QTestState *who) -{ - wait_for_migration_status(who, "completed", NULL); -} - -void wait_for_migration_fail(QTestState *from, bool allow_active) -{ - QDict *rsp_return; - char *status; - bool failed; - - do { - status = migrate_query_status(from); - bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || - (allow_active && !strcmp(status, "active")); - if (!result) { - fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", - __func__, status, allow_active); - } - g_assert(result); - failed = !strcmp(status, "failed"); - g_free(status); - } while (!failed); - - /* Is the machine currently running? */ - rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); - g_assert(qdict_haskey(rsp_return, "running")); - g_assert(qdict_get_bool(rsp_return, "running")); - qobject_unref(rsp_return); -} diff --git a/tests/migration-helpers.h b/tests/migration-helpers.h deleted file mode 100644 index a11808b3b7..0000000000 --- a/tests/migration-helpers.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * QTest migration helpers - * - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * based on the vhost-user-test.c that is: - * Copyright (c) 2014 Virtual Open Systems Sarl. - * - * 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 MIGRATION_HELPERS_H_ -#define MIGRATION_HELPERS_H_ - -#include "libqtest.h" - -extern bool got_stop; - -GCC_FMT_ATTR(3, 4) -QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...); - -GCC_FMT_ATTR(2, 3) -QDict *wait_command(QTestState *who, const char *command, ...); - -GCC_FMT_ATTR(3, 4) -void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); - -QDict *migrate_query(QTestState *who); - -void wait_for_migration_status(QTestState *who, - const char *goal, const char **ungoals); - -void wait_for_migration_complete(QTestState *who); - -void wait_for_migration_fail(QTestState *from, bool allow_active); - -#endif /* MIGRATION_HELPERS_H_ */ diff --git a/tests/migration-test.c b/tests/migration-test.c deleted file mode 100644 index 53afec4395..0000000000 --- a/tests/migration-test.c +++ /dev/null @@ -1,1281 +0,0 @@ -/* - * QTest testcase for migration - * - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * based on the vhost-user-test.c that is: - * Copyright (c) 2014 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" - -#include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qemu/module.h" -#include "qemu/option.h" -#include "qemu/range.h" -#include "qemu/sockets.h" -#include "chardev/char.h" -#include "qapi/qapi-visit-sockets.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/qobject-output-visitor.h" - -#include "migration-helpers.h" -#include "migration/migration-test.h" - -/* TODO actually test the results and get rid of this */ -#define qtest_qmp_discard_response(...) qobject_unref(qtest_qmp(__VA_ARGS__)) - -unsigned start_address; -unsigned end_address; -static bool uffd_feature_thread_id; - -#if defined(__linux__) -#include -#include -#endif - -#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) -#include -#include -#include - -static bool ufd_version_check(void) -{ - struct uffdio_api api_struct; - uint64_t ioctl_mask; - - int ufd = syscall(__NR_userfaultfd, O_CLOEXEC); - - if (ufd == -1) { - g_test_message("Skipping test: userfaultfd not available"); - return false; - } - - api_struct.api = UFFD_API; - api_struct.features = 0; - if (ioctl(ufd, UFFDIO_API, &api_struct)) { - g_test_message("Skipping test: UFFDIO_API failed"); - return false; - } - uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; - - ioctl_mask = (__u64)1 << _UFFDIO_REGISTER | - (__u64)1 << _UFFDIO_UNREGISTER; - if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { - g_test_message("Skipping test: Missing userfault feature"); - return false; - } - - return true; -} - -#else -static bool ufd_version_check(void) -{ - g_test_message("Skipping test: Userfault not available (builtdtime)"); - return false; -} - -#endif - -static const char *tmpfs; - -/* The boot file modifies memory area in [start_address, end_address) - * repeatedly. It outputs a 'B' at a fixed rate while it's still running. - */ -#include "tests/migration/i386/a-b-bootblock.h" -#include "tests/migration/aarch64/a-b-kernel.h" -#include "tests/migration/s390x/a-b-bios.h" - -static void init_bootfile(const char *bootpath, void *content, size_t len) -{ - FILE *bootfile = fopen(bootpath, "wb"); - - g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); - fclose(bootfile); -} - -/* - * Wait for some output in the serial output file, - * we get an 'A' followed by an endless string of 'B's - * but on the destination we won't have the A. - */ -static void wait_for_serial(const char *side) -{ - char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); - FILE *serialfile = fopen(serialpath, "r"); - const char *arch = qtest_get_arch(); - int started = (strcmp(side, "src_serial") == 0 && - strcmp(arch, "ppc64") == 0) ? 0 : 1; - - g_free(serialpath); - do { - int readvalue = fgetc(serialfile); - - if (!started) { - /* SLOF prints its banner before starting test, - * to ignore it, mark the start of the test with '_', - * ignore all characters until this marker - */ - switch (readvalue) { - case '_': - started = 1; - break; - case EOF: - fseek(serialfile, 0, SEEK_SET); - usleep(1000); - break; - } - continue; - } - switch (readvalue) { - case 'A': - /* Fine */ - break; - - case 'B': - /* It's alive! */ - fclose(serialfile); - return; - - case EOF: - started = (strcmp(side, "src_serial") == 0 && - strcmp(arch, "ppc64") == 0) ? 0 : 1; - fseek(serialfile, 0, SEEK_SET); - usleep(1000); - break; - - default: - fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); - g_assert_not_reached(); - } - } while (true); -} - -/* - * It's tricky to use qemu's migration event capability with qtest, - * events suddenly appearing confuse the qmp()/hmp() responses. - */ - -static int64_t read_ram_property_int(QTestState *who, const char *property) -{ - QDict *rsp_return, *rsp_ram; - int64_t result; - - rsp_return = migrate_query(who); - if (!qdict_haskey(rsp_return, "ram")) { - /* Still in setup */ - result = 0; - } else { - rsp_ram = qdict_get_qdict(rsp_return, "ram"); - result = qdict_get_try_int(rsp_ram, property, 0); - } - qobject_unref(rsp_return); - return result; -} - -static int64_t read_migrate_property_int(QTestState *who, const char *property) -{ - QDict *rsp_return; - int64_t result; - - rsp_return = migrate_query(who); - result = qdict_get_try_int(rsp_return, property, 0); - qobject_unref(rsp_return); - return result; -} - -static uint64_t get_migration_pass(QTestState *who) -{ - return read_ram_property_int(who, "dirty-sync-count"); -} - -static void read_blocktime(QTestState *who) -{ - QDict *rsp_return; - - rsp_return = migrate_query(who); - g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); - qobject_unref(rsp_return); -} - -static void wait_for_migration_pass(QTestState *who) -{ - uint64_t initial_pass = get_migration_pass(who); - uint64_t pass; - - /* Wait for the 1st sync */ - while (!got_stop && !initial_pass) { - usleep(1000); - initial_pass = get_migration_pass(who); - } - - do { - usleep(1000); - pass = get_migration_pass(who); - } while (pass == initial_pass && !got_stop); -} - -static void check_guests_ram(QTestState *who) -{ - /* Our ASM test will have been incrementing one byte from each page from - * start_address to < end_address in order. This gives us a constraint - * that any page's byte should be equal or less than the previous pages - * byte (mod 256); and they should all be equal except for one transition - * at the point where we meet the incrementer. (We're running this with - * the guest stopped). - */ - unsigned address; - uint8_t first_byte; - uint8_t last_byte; - bool hit_edge = false; - int bad = 0; - - qtest_memread(who, start_address, &first_byte, 1); - last_byte = first_byte; - - for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; - address += TEST_MEM_PAGE_SIZE) - { - uint8_t b; - qtest_memread(who, address, &b, 1); - if (b != last_byte) { - if (((b + 1) % 256) == last_byte && !hit_edge) { - /* This is OK, the guest stopped at the point of - * incrementing the previous page but didn't get - * to us yet. - */ - hit_edge = true; - last_byte = b; - } else { - bad++; - if (bad <= 10) { - fprintf(stderr, "Memory content inconsistency at %x" - " first_byte = %x last_byte = %x current = %x" - " hit_edge = %x\n", - address, first_byte, last_byte, b, hit_edge); - } - } - } - } - if (bad >= 10) { - fprintf(stderr, "and in another %d pages", bad - 10); - } - g_assert(bad == 0); -} - -static void cleanup(const char *filename) -{ - char *path = g_strdup_printf("%s/%s", tmpfs, filename); - - unlink(path); - g_free(path); -} - -static char *SocketAddress_to_str(SocketAddress *addr) -{ - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - return g_strdup_printf("tcp:%s:%s", - addr->u.inet.host, - addr->u.inet.port); - case SOCKET_ADDRESS_TYPE_UNIX: - return g_strdup_printf("unix:%s", - addr->u.q_unix.path); - case SOCKET_ADDRESS_TYPE_FD: - return g_strdup_printf("fd:%s", addr->u.fd.str); - case SOCKET_ADDRESS_TYPE_VSOCK: - return g_strdup_printf("tcp:%s:%s", - addr->u.vsock.cid, - addr->u.vsock.port); - default: - return g_strdup("unknown address type"); - } -} - -static char *migrate_get_socket_address(QTestState *who, const char *parameter) -{ - QDict *rsp; - char *result; - Error *local_err = NULL; - SocketAddressList *addrs; - Visitor *iv = NULL; - QObject *object; - - rsp = migrate_query(who); - object = qdict_get(rsp, parameter); - - iv = qobject_input_visitor_new(object); - visit_type_SocketAddressList(iv, NULL, &addrs, &local_err); - visit_free(iv); - - /* we are only using a single address */ - result = SocketAddress_to_str(addrs->value); - - qapi_free_SocketAddressList(addrs); - qobject_unref(rsp); - return result; -} - -static long long migrate_get_parameter_int(QTestState *who, - const char *parameter) -{ - QDict *rsp; - long long result; - - rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }"); - result = qdict_get_int(rsp, parameter); - qobject_unref(rsp); - return result; -} - -static void migrate_check_parameter_int(QTestState *who, const char *parameter, - long long value) -{ - long long result; - - result = migrate_get_parameter_int(who, parameter); - g_assert_cmpint(result, ==, value); -} - -static void migrate_set_parameter_int(QTestState *who, const char *parameter, - long long value) -{ - QDict *rsp; - - rsp = qtest_qmp(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %lld } }", - parameter, value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - migrate_check_parameter_int(who, parameter, value); -} - -static void migrate_pause(QTestState *who) -{ - QDict *rsp; - - rsp = wait_command(who, "{ 'execute': 'migrate-pause' }"); - qobject_unref(rsp); -} - -static void migrate_continue(QTestState *who, const char *state) -{ - QDict *rsp; - - rsp = wait_command(who, - "{ 'execute': 'migrate-continue'," - " 'arguments': { 'state': %s } }", - state); - qobject_unref(rsp); -} - -static void migrate_recover(QTestState *who, const char *uri) -{ - QDict *rsp; - - rsp = wait_command(who, - "{ 'execute': 'migrate-recover', " - " 'id': 'recover-cmd', " - " 'arguments': { 'uri': %s } }", - uri); - qobject_unref(rsp); -} - -static void migrate_set_capability(QTestState *who, const char *capability, - bool value) -{ - QDict *rsp; - - rsp = qtest_qmp(who, - "{ 'execute': 'migrate-set-capabilities'," - "'arguments': { " - "'capabilities': [ { " - "'capability': %s, 'state': %i } ] } }", - capability, value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); -} - -static void migrate_postcopy_start(QTestState *from, QTestState *to) -{ - QDict *rsp; - - rsp = wait_command(from, "{ 'execute': 'migrate-start-postcopy' }"); - qobject_unref(rsp); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - - qtest_qmp_eventwait(to, "RESUME"); -} - -typedef struct { - bool hide_stderr; - bool use_shmem; - char *opts_source; - char *opts_target; -} MigrateStart; - -static MigrateStart *migrate_start_new(void) -{ - MigrateStart *args = g_new0(MigrateStart, 1); - - args->opts_source = g_strdup(""); - args->opts_target = g_strdup(""); - return args; -} - -static void migrate_start_destroy(MigrateStart *args) -{ - g_free(args->opts_source); - g_free(args->opts_target); - g_free(args); -} - -static int test_migrate_start(QTestState **from, QTestState **to, - const char *uri, MigrateStart *args) -{ - gchar *arch_source, *arch_target; - gchar *cmd_source, *cmd_target; - const gchar *ignore_stderr; - char *bootpath = NULL; - char *shmem_opts; - char *shmem_path; - const char *arch = qtest_get_arch(); - const char *machine_opts = NULL; - const char *memory_size; - - if (args->use_shmem) { - if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { - g_test_skip("/dev/shm is not supported"); - return -1; - } - } - - got_stop = false; - bootpath = g_strdup_printf("%s/bootsect", tmpfs); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - /* the assembled x86 boot sector should be exactly one sector large */ - assert(sizeof(x86_bootsect) == 512); - init_bootfile(bootpath, x86_bootsect, sizeof(x86_bootsect)); - memory_size = "150M"; - arch_source = g_strdup_printf("-drive file=%s,format=raw", bootpath); - arch_target = g_strdup(arch_source); - start_address = X86_TEST_MEM_START; - end_address = X86_TEST_MEM_END; - } else if (g_str_equal(arch, "s390x")) { - init_bootfile(bootpath, s390x_elf, sizeof(s390x_elf)); - memory_size = "128M"; - arch_source = g_strdup_printf("-bios %s", bootpath); - arch_target = g_strdup(arch_source); - start_address = S390_TEST_MEM_START; - end_address = S390_TEST_MEM_END; - } else if (strcmp(arch, "ppc64") == 0) { - machine_opts = "vsmt=8"; - memory_size = "256M"; - arch_source = g_strdup_printf("-nodefaults " - "-prom-env 'use-nvramrc?=true' -prom-env " - "'nvramrc=hex .\" _\" begin %x %x " - "do i c@ 1 + i c! 1000 +loop .\" B\" 0 " - "until'", end_address, start_address); - arch_target = g_strdup(""); - start_address = PPC_TEST_MEM_START; - end_address = PPC_TEST_MEM_END; - } else if (strcmp(arch, "aarch64") == 0) { - init_bootfile(bootpath, aarch64_kernel, sizeof(aarch64_kernel)); - machine_opts = "virt,gic-version=max"; - memory_size = "150M"; - arch_source = g_strdup_printf("-cpu max " - "-kernel %s", - bootpath); - arch_target = g_strdup(arch_source); - start_address = ARM_TEST_MEM_START; - end_address = ARM_TEST_MEM_END; - - g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); - } else { - g_assert_not_reached(); - } - - g_free(bootpath); - - if (args->hide_stderr) { - ignore_stderr = "2>/dev/null"; - } else { - ignore_stderr = ""; - } - - if (args->use_shmem) { - shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); - shmem_opts = g_strdup_printf( - "-object memory-backend-file,id=mem0,size=%s" - ",mem-path=%s,share=on -numa node,memdev=mem0", - memory_size, shmem_path); - } else { - shmem_path = NULL; - shmem_opts = g_strdup(""); - } - - cmd_source = g_strdup_printf("-accel kvm -accel tcg%s%s " - "-name source,debug-threads=on " - "-m %s " - "-serial file:%s/src_serial " - "%s %s %s %s", - machine_opts ? " -machine " : "", - machine_opts ? machine_opts : "", - memory_size, tmpfs, - arch_source, shmem_opts, args->opts_source, - ignore_stderr); - g_free(arch_source); - *from = qtest_init(cmd_source); - g_free(cmd_source); - - cmd_target = g_strdup_printf("-accel kvm -accel tcg%s%s " - "-name target,debug-threads=on " - "-m %s " - "-serial file:%s/dest_serial " - "-incoming %s " - "%s %s %s %s", - machine_opts ? " -machine " : "", - machine_opts ? machine_opts : "", - memory_size, tmpfs, uri, - arch_target, shmem_opts, - args->opts_target, ignore_stderr); - g_free(arch_target); - *to = qtest_init(cmd_target); - g_free(cmd_target); - - g_free(shmem_opts); - /* - * Remove shmem file immediately to avoid memory leak in test failed case. - * It's valid becase QEMU has already opened this file - */ - if (args->use_shmem) { - unlink(shmem_path); - g_free(shmem_path); - } - - migrate_start_destroy(args); - return 0; -} - -static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) -{ - unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; - - qtest_quit(from); - - if (test_dest) { - qtest_memread(to, start_address, &dest_byte_a, 1); - - /* Destination still running, wait for a byte to change */ - do { - qtest_memread(to, start_address, &dest_byte_b, 1); - usleep(1000 * 10); - } while (dest_byte_a == dest_byte_b); - - qtest_qmp_discard_response(to, "{ 'execute' : 'stop'}"); - - /* With it stopped, check nothing changes */ - qtest_memread(to, start_address, &dest_byte_c, 1); - usleep(1000 * 200); - qtest_memread(to, start_address, &dest_byte_d, 1); - g_assert_cmpint(dest_byte_c, ==, dest_byte_d); - - check_guests_ram(to); - } - - qtest_quit(to); - - cleanup("bootsect"); - cleanup("migsocket"); - cleanup("src_serial"); - cleanup("dest_serial"); -} - -static void deprecated_set_downtime(QTestState *who, const double value) -{ - QDict *rsp; - - rsp = qtest_qmp(who, - "{ 'execute': 'migrate_set_downtime'," - " 'arguments': { 'value': %f } }", value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - migrate_check_parameter_int(who, "downtime-limit", value * 1000); -} - -static void deprecated_set_speed(QTestState *who, long long value) -{ - QDict *rsp; - - rsp = qtest_qmp(who, "{ 'execute': 'migrate_set_speed'," - "'arguments': { 'value': %lld } }", value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - migrate_check_parameter_int(who, "max-bandwidth", value); -} - -static void deprecated_set_cache_size(QTestState *who, long long value) -{ - QDict *rsp; - - rsp = qtest_qmp(who, "{ 'execute': 'migrate-set-cache-size'," - "'arguments': { 'value': %lld } }", value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - migrate_check_parameter_int(who, "xbzrle-cache-size", value); -} - -static void test_deprecated(void) -{ - QTestState *from; - - from = qtest_init("-machine none"); - - deprecated_set_downtime(from, 0.12345); - deprecated_set_speed(from, 12345); - deprecated_set_cache_size(from, 4096); - - qtest_quit(from); -} - -static int migrate_postcopy_prepare(QTestState **from_ptr, - QTestState **to_ptr, - MigrateStart *args) -{ - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - - if (test_migrate_start(&from, &to, uri, args)) { - return -1; - } - - migrate_set_capability(from, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-blocktime", true); - - /* We want to pick a speed slow enough that the test completes - * quickly, but that it doesn't complete precopy even on a slow - * machine, so also set the downtime. - */ - migrate_set_parameter_int(from, "max-bandwidth", 30000000); - migrate_set_parameter_int(from, "downtime-limit", 1); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, uri, "{}"); - g_free(uri); - - wait_for_migration_pass(from); - - *from_ptr = from; - *to_ptr = to; - - return 0; -} - -static void migrate_postcopy_complete(QTestState *from, QTestState *to) -{ - wait_for_migration_complete(from); - - /* Make sure we get at least one "B" on destination */ - wait_for_serial("dest_serial"); - - if (uffd_feature_thread_id) { - read_blocktime(to); - } - - test_migrate_end(from, to, true); -} - -static void test_postcopy(void) -{ - MigrateStart *args = migrate_start_new(); - QTestState *from, *to; - - if (migrate_postcopy_prepare(&from, &to, args)) { - return; - } - migrate_postcopy_start(from, to); - migrate_postcopy_complete(from, to); -} - -static void test_postcopy_recovery(void) -{ - MigrateStart *args = migrate_start_new(); - QTestState *from, *to; - char *uri; - - args->hide_stderr = true; - - if (migrate_postcopy_prepare(&from, &to, args)) { - return; - } - - /* Turn postcopy speed down, 4K/s is slow enough on any machines */ - migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); - - /* Now we start the postcopy */ - migrate_postcopy_start(from, to); - - /* - * Wait until postcopy is really started; we can only run the - * migrate-pause command during a postcopy - */ - wait_for_migration_status(from, "postcopy-active", NULL); - - /* - * Manually stop the postcopy migration. This emulates a network - * failure with the migration socket - */ - migrate_pause(from); - - /* - * Wait for destination side to reach postcopy-paused state. The - * migrate-recover command can only succeed if destination machine - * is in the paused state - */ - wait_for_migration_status(to, "postcopy-paused", - (const char * []) { "failed", "active", - "completed", NULL }); - - /* - * Create a new socket to emulate a new channel that is different - * from the broken migration channel; tell the destination to - * listen to the new port - */ - uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); - migrate_recover(to, uri); - - /* - * Try to rebuild the migration channel using the resume flag and - * the newly created channel - */ - wait_for_migration_status(from, "postcopy-paused", - (const char * []) { "failed", "active", - "completed", NULL }); - migrate_qmp(from, uri, "{'resume': true}"); - g_free(uri); - - /* Restore the postcopy bandwidth to unlimited */ - migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); - - migrate_postcopy_complete(from, to); -} - -static void test_baddest(void) -{ - MigrateStart *args = migrate_start_new(); - QTestState *from, *to; - - args->hide_stderr = true; - - if (test_migrate_start(&from, &to, "tcp:0:0", args)) { - return; - } - migrate_qmp(from, "tcp:0:0", "{}"); - wait_for_migration_fail(from, false); - test_migrate_end(from, to, false); -} - -static void test_precopy_unix(void) -{ - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateStart *args = migrate_start_new(); - QTestState *from, *to; - - if (test_migrate_start(&from, &to, uri, args)) { - return; - } - - /* We want to pick a speed slow enough that the test completes - * quickly, but that it doesn't complete precopy even on a slow - * machine, so also set the downtime. - */ - /* 1 ms should make it not converge*/ - migrate_set_parameter_int(from, "downtime-limit", 1); - /* 1GB/s */ - migrate_set_parameter_int(from, "max-bandwidth", 1000000000); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, uri, "{}"); - - wait_for_migration_pass(from); - - /* 300 ms should converge */ - migrate_set_parameter_int(from, "downtime-limit", 300); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - test_migrate_end(from, to, true); - g_free(uri); -} - -#if 0 -/* Currently upset on aarch64 TCG */ -static void test_ignore_shared(void) -{ - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - - if (test_migrate_start(&from, &to, uri, false, true, NULL, NULL)) { - return; - } - - migrate_set_capability(from, "x-ignore-shared", true); - migrate_set_capability(to, "x-ignore-shared", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, uri, "{}"); - - wait_for_migration_pass(from); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - /* Check whether shared RAM has been really skipped */ - g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); - - test_migrate_end(from, to, true); - g_free(uri); -} -#endif - -static void test_xbzrle(const char *uri) -{ - MigrateStart *args = migrate_start_new(); - QTestState *from, *to; - - if (test_migrate_start(&from, &to, uri, args)) { - return; - } - - /* - * We want to pick a speed slow enough that the test completes - * quickly, but that it doesn't complete precopy even on a slow - * machine, so also set the downtime. - */ - /* 1 ms should make it not converge*/ - migrate_set_parameter_int(from, "downtime-limit", 1); - /* 1GB/s */ - migrate_set_parameter_int(from, "max-bandwidth", 1000000000); - - migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); - - migrate_set_capability(from, "xbzrle", "true"); - migrate_set_capability(to, "xbzrle", "true"); - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, uri, "{}"); - - wait_for_migration_pass(from); - - /* 300ms should converge */ - migrate_set_parameter_int(from, "downtime-limit", 300); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - test_migrate_end(from, to, true); -} - -static void test_xbzrle_unix(void) -{ - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - - test_xbzrle(uri); - g_free(uri); -} - -static void test_precopy_tcp(void) -{ - MigrateStart *args = migrate_start_new(); - char *uri; - QTestState *from, *to; - - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", args)) { - return; - } - - /* - * We want to pick a speed slow enough that the test completes - * quickly, but that it doesn't complete precopy even on a slow - * machine, so also set the downtime. - */ - /* 1 ms should make it not converge*/ - migrate_set_parameter_int(from, "downtime-limit", 1); - /* 1GB/s */ - migrate_set_parameter_int(from, "max-bandwidth", 1000000000); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - uri = migrate_get_socket_address(to, "socket-address"); - - migrate_qmp(from, uri, "{}"); - - wait_for_migration_pass(from); - - /* 300ms should converge */ - migrate_set_parameter_int(from, "downtime-limit", 300); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - test_migrate_end(from, to, true); - g_free(uri); -} - -static void test_migrate_fd_proto(void) -{ - MigrateStart *args = migrate_start_new(); - QTestState *from, *to; - int ret; - int pair[2]; - QDict *rsp; - const char *error_desc; - - if (test_migrate_start(&from, &to, "defer", args)) { - return; - } - - /* - * We want to pick a speed slow enough that the test completes - * quickly, but that it doesn't complete precopy even on a slow - * machine, so also set the downtime. - */ - /* 1 ms should make it not converge */ - migrate_set_parameter_int(from, "downtime-limit", 1); - /* 1GB/s */ - migrate_set_parameter_int(from, "max-bandwidth", 1000000000); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - /* Create two connected sockets for migration */ - ret = socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); - g_assert_cmpint(ret, ==, 0); - - /* Send the 1st socket to the target */ - rsp = wait_command_fd(to, pair[0], - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - qobject_unref(rsp); - close(pair[0]); - - /* Start incoming migration from the 1st socket */ - rsp = wait_command(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'fd:fd-mig' }}"); - qobject_unref(rsp); - - /* Send the 2nd socket to the target */ - rsp = wait_command_fd(from, pair[1], - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - qobject_unref(rsp); - close(pair[1]); - - /* Start migration to the 2nd socket*/ - migrate_qmp(from, "fd:fd-mig", "{}"); - - wait_for_migration_pass(from); - - /* 300ms should converge */ - migrate_set_parameter_int(from, "downtime-limit", 300); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - qtest_qmp_eventwait(to, "RESUME"); - - /* Test closing fds */ - /* We assume, that QEMU removes named fd from its list, - * so this should fail */ - rsp = qtest_qmp(from, "{ 'execute': 'closefd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - g_assert_true(qdict_haskey(rsp, "error")); - error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); - g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); - qobject_unref(rsp); - - rsp = qtest_qmp(to, "{ 'execute': 'closefd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - g_assert_true(qdict_haskey(rsp, "error")); - error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); - g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); - qobject_unref(rsp); - - /* Complete migration */ - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - test_migrate_end(from, to, true); -} - -static void do_test_validate_uuid(MigrateStart *args, bool should_fail) -{ - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - - if (test_migrate_start(&from, &to, uri, args)) { - return; - } - - /* - * UUID validation is at the begin of migration. So, the main process of - * migration is not interesting for us here. Thus, set huge downtime for - * very fast migration. - */ - migrate_set_parameter_int(from, "downtime-limit", 1000000); - migrate_set_capability(from, "validate-uuid", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, uri, "{}"); - - if (should_fail) { - qtest_set_expected_status(to, 1); - wait_for_migration_fail(from, true); - } else { - wait_for_migration_complete(from); - } - - test_migrate_end(from, to, false); - g_free(uri); -} - -static void test_validate_uuid(void) -{ - MigrateStart *args = migrate_start_new(); - - args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); - args->opts_target = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); - do_test_validate_uuid(args, false); -} - -static void test_validate_uuid_error(void) -{ - MigrateStart *args = migrate_start_new(); - - args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); - args->opts_target = g_strdup("-uuid 22222222-2222-2222-2222-222222222222"); - args->hide_stderr = true; - do_test_validate_uuid(args, true); -} - -static void test_validate_uuid_src_not_set(void) -{ - MigrateStart *args = migrate_start_new(); - - args->opts_target = g_strdup("-uuid 22222222-2222-2222-2222-222222222222"); - args->hide_stderr = true; - do_test_validate_uuid(args, false); -} - -static void test_validate_uuid_dst_not_set(void) -{ - MigrateStart *args = migrate_start_new(); - - args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); - args->hide_stderr = true; - do_test_validate_uuid(args, false); -} - -static void test_migrate_auto_converge(void) -{ - char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateStart *args = migrate_start_new(); - QTestState *from, *to; - int64_t remaining, percentage; - - /* - * We want the test to be stable and as fast as possible. - * E.g., with 1Gb/s bandwith migration may pass without throttling, - * so we need to decrease a bandwidth. - */ - const int64_t init_pct = 5, inc_pct = 50, max_pct = 95; - const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ - const int64_t downtime_limit = 250; /* 250ms */ - /* - * We migrate through unix-socket (> 500Mb/s). - * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). - * So, we can predict expected_threshold - */ - const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; - - if (test_migrate_start(&from, &to, uri, args)) { - return; - } - - migrate_set_capability(from, "auto-converge", true); - migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); - migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); - migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); - - /* - * Set the initial parameters so that the migration could not converge - * without throttling. - */ - migrate_set_parameter_int(from, "downtime-limit", 1); - migrate_set_parameter_int(from, "max-bandwidth", 100000000); /* ~100Mb/s */ - - /* To check remaining size after precopy */ - migrate_set_capability(from, "pause-before-switchover", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, uri, "{}"); - - /* Wait for throttling begins */ - percentage = 0; - while (percentage == 0) { - percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - usleep(100); - g_assert_false(got_stop); - } - /* The first percentage of throttling should be equal to init_pct */ - g_assert_cmpint(percentage, ==, init_pct); - /* Now, when we tested that throttling works, let it converge */ - migrate_set_parameter_int(from, "downtime-limit", downtime_limit); - migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); - - /* - * Wait for pre-switchover status to check last throttle percentage - * and remaining. These values will be zeroed later - */ - wait_for_migration_status(from, "pre-switchover", NULL); - - /* The final percentage of throttling shouldn't be greater than max_pct */ - percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - g_assert_cmpint(percentage, <=, max_pct); - - remaining = read_ram_property_int(from, "remaining"); - g_assert_cmpint(remaining, <, expected_threshold); - - migrate_continue(from, "pre-switchover"); - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - g_free(uri); - - test_migrate_end(from, to, true); -} - -int main(int argc, char **argv) -{ - char template[] = "/tmp/migration-test-XXXXXX"; - int ret; - - g_test_init(&argc, &argv, NULL); - - if (!ufd_version_check()) { - return g_test_run(); - } - - /* - * On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG - * is touchy due to race conditions on dirty bits (especially on PPC for - * some reason) - */ - if (g_str_equal(qtest_get_arch(), "ppc64") && - (access("/sys/module/kvm_hv", F_OK) || - access("/dev/kvm", R_OK | W_OK))) { - g_test_message("Skipping test: kvm_hv not available"); - return g_test_run(); - } - - /* - * Similar to ppc64, s390x seems to be touchy with TCG, so disable it - * there until the problems are resolved - */ - if (g_str_equal(qtest_get_arch(), "s390x")) { -#if defined(HOST_S390X) - if (access("/dev/kvm", R_OK | W_OK)) { - g_test_message("Skipping test: kvm not available"); - return g_test_run(); - } -#else - g_test_message("Skipping test: Need s390x host to work properly"); - return g_test_run(); -#endif - } - - tmpfs = mkdtemp(template); - if (!tmpfs) { - g_test_message("mkdtemp on path (%s): %s", template, strerror(errno)); - } - g_assert(tmpfs); - - module_call_init(MODULE_INIT_QOM); - - qtest_add_func("/migration/postcopy/unix", test_postcopy); - qtest_add_func("/migration/postcopy/recovery", test_postcopy_recovery); - qtest_add_func("/migration/deprecated", test_deprecated); - qtest_add_func("/migration/bad_dest", test_baddest); - qtest_add_func("/migration/precopy/unix", test_precopy_unix); - qtest_add_func("/migration/precopy/tcp", test_precopy_tcp); - /* qtest_add_func("/migration/ignore_shared", test_ignore_shared); */ - qtest_add_func("/migration/xbzrle/unix", test_xbzrle_unix); - qtest_add_func("/migration/fd_proto", test_migrate_fd_proto); - qtest_add_func("/migration/validate_uuid", test_validate_uuid); - qtest_add_func("/migration/validate_uuid_error", test_validate_uuid_error); - qtest_add_func("/migration/validate_uuid_src_not_set", - test_validate_uuid_src_not_set); - qtest_add_func("/migration/validate_uuid_dst_not_set", - test_validate_uuid_dst_not_set); - - qtest_add_func("/migration/auto_converge", test_migrate_auto_converge); - - ret = g_test_run(); - - g_assert_cmpint(ret, ==, 0); - - ret = rmdir(tmpfs); - if (ret != 0) { - g_test_message("unable to rmdir: path (%s): %s", - tmpfs, strerror(errno)); - } - - return ret; -} diff --git a/tests/modules-test.c b/tests/modules-test.c deleted file mode 100644 index 88217686e1..0000000000 --- a/tests/modules-test.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "qemu/osdep.h" -#include "libqtest.h" - -const char common_args[] = "-nodefaults -machine none"; - -static void test_modules_load(const void *data) -{ - QTestState *qts; - const char **args = (const char **)data; - - qts = qtest_init(common_args); - qtest_module_load(qts, args[0], args[1]); - qtest_quit(qts); -} - -int main(int argc, char *argv[]) -{ - const char *modules[] = { -#ifdef CONFIG_CURL - "block-", "curl", -#endif -#ifdef CONFIG_GLUSTERFS - "block-", "gluster", -#endif -#ifdef CONFIG_LIBISCSI - "block-", "iscsi", -#endif -#ifdef CONFIG_LIBNFS - "block-", "nfs", -#endif -#ifdef CONFIG_LIBSSH - "block-", "ssh", -#endif -#ifdef CONFIG_RBD - "block-", "rbd", -#endif -#ifdef CONFIG_AUDIO_ALSA - "audio-", "alsa", -#endif -#ifdef CONFIG_AUDIO_OSS - "audio-", "oss", -#endif -#ifdef CONFIG_AUDIO_PA - "audio-", "pa", -#endif -#ifdef CONFIG_AUDIO_SDL - "audio-", "sdl", -#endif -#ifdef CONFIG_CURSES - "ui-", "curses", -#endif -#if defined(CONFIG_GTK) && defined(CONFIG_VTE) - "ui-", "gtk", -#endif -#ifdef CONFIG_SDL - "ui-", "sdl", -#endif -#if defined(CONFIG_SPICE) && defined(CONFIG_GIO) - "ui-", "spice-app", -#endif - }; - int i; - - g_test_init(&argc, &argv, NULL); - - for (i = 0; i < G_N_ELEMENTS(modules); i += 2) { - char *testname = g_strdup_printf("/module/load/%s%s", - modules[i], modules[i + 1]); - qtest_add_data_func(testname, modules + i, test_modules_load); - g_free(testname); - } - - return g_test_run(); -} diff --git a/tests/ne2000-test.c b/tests/ne2000-test.c deleted file mode 100644 index 3fc0e555d5..0000000000 --- a/tests/ne2000-test.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * QTest testcase for ne2000 NIC - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -typedef struct QNe2k_pci QNe2k_pci; - -struct QNe2k_pci { - QOSGraphObject obj; - QPCIDevice dev; -}; - -static void *ne2k_pci_get_driver(void *obj, const char *interface) -{ - QNe2k_pci *ne2k_pci = obj; - - if (!g_strcmp0(interface, "pci-device")) { - return &ne2k_pci->dev; - } - - fprintf(stderr, "%s not present in ne2k_pci\n", interface); - g_assert_not_reached(); -} - -static void *ne2k_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) -{ - QNe2k_pci *ne2k_pci = g_new0(QNe2k_pci, 1); - QPCIBus *bus = pci_bus; - - qpci_device_init(&ne2k_pci->dev, bus, addr); - ne2k_pci->obj.get_driver = ne2k_pci_get_driver; - - return &ne2k_pci->obj; -} - -static void ne2000_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0", - }; - add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); - - qos_node_create_driver("ne2k_pci", ne2k_pci_create); - qos_node_consumes("ne2k_pci", "pci-bus", &opts); - qos_node_produces("ne2k_pci", "pci-device"); -} - -libqos_init(ne2000_register_nodes); diff --git a/tests/numa-test.c b/tests/numa-test.c deleted file mode 100644 index 17dd807d2a..0000000000 --- a/tests/numa-test.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * NUMA configuration test cases - * - * Copyright (c) 2017 Red Hat Inc. - * Authors: - * Igor Mammedov - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" - -static char *make_cli(const char *generic_cli, const char *test_cli) -{ - return g_strdup_printf("%s %s", generic_cli ? generic_cli : "", test_cli); -} - -static void test_mon_explicit(const void *data) -{ - char *s; - char *cli; - QTestState *qts; - - cli = make_cli(data, "-smp 8 " - "-numa node,nodeid=0,cpus=0-3 " - "-numa node,nodeid=1,cpus=4-7 "); - qts = qtest_init(cli); - - s = qtest_hmp(qts, "info numa"); - g_assert(strstr(s, "node 0 cpus: 0 1 2 3")); - g_assert(strstr(s, "node 1 cpus: 4 5 6 7")); - g_free(s); - - qtest_quit(qts); - g_free(cli); -} - -static void test_mon_default(const void *data) -{ - char *s; - char *cli; - QTestState *qts; - - cli = make_cli(data, "-smp 8 -numa node -numa node"); - qts = qtest_init(cli); - - s = qtest_hmp(qts, "info numa"); - g_assert(strstr(s, "node 0 cpus: 0 2 4 6")); - g_assert(strstr(s, "node 1 cpus: 1 3 5 7")); - g_free(s); - - qtest_quit(qts); - g_free(cli); -} - -static void test_mon_partial(const void *data) -{ - char *s; - char *cli; - QTestState *qts; - - cli = make_cli(data, "-smp 8 " - "-numa node,nodeid=0,cpus=0-1 " - "-numa node,nodeid=1,cpus=4-5 "); - qts = qtest_init(cli); - - s = qtest_hmp(qts, "info numa"); - g_assert(strstr(s, "node 0 cpus: 0 1 2 3 6 7")); - g_assert(strstr(s, "node 1 cpus: 4 5")); - g_free(s); - - qtest_quit(qts); - g_free(cli); -} - -static QList *get_cpus(QTestState *qts, QDict **resp) -{ - *resp = qtest_qmp(qts, "{ 'execute': 'query-cpus' }"); - g_assert(*resp); - g_assert(qdict_haskey(*resp, "return")); - return qdict_get_qlist(*resp, "return"); -} - -static void test_query_cpus(const void *data) -{ - char *cli; - QDict *resp; - QList *cpus; - QObject *e; - QTestState *qts; - - cli = make_cli(data, "-smp 8 -numa node,cpus=0-3 -numa node,cpus=4-7"); - qts = qtest_init(cli); - cpus = get_cpus(qts, &resp); - g_assert(cpus); - - while ((e = qlist_pop(cpus))) { - QDict *cpu, *props; - int64_t cpu_idx, node; - - cpu = qobject_to(QDict, e); - g_assert(qdict_haskey(cpu, "CPU")); - g_assert(qdict_haskey(cpu, "props")); - - cpu_idx = qdict_get_int(cpu, "CPU"); - props = qdict_get_qdict(cpu, "props"); - g_assert(qdict_haskey(props, "node-id")); - node = qdict_get_int(props, "node-id"); - if (cpu_idx >= 0 && cpu_idx < 4) { - g_assert_cmpint(node, ==, 0); - } else { - g_assert_cmpint(node, ==, 1); - } - qobject_unref(e); - } - - qobject_unref(resp); - qtest_quit(qts); - g_free(cli); -} - -static void pc_numa_cpu(const void *data) -{ - char *cli; - QDict *resp; - QList *cpus; - QObject *e; - QTestState *qts; - - cli = make_cli(data, "-cpu pentium -smp 8,sockets=2,cores=2,threads=2 " - "-numa node,nodeid=0 -numa node,nodeid=1 " - "-numa cpu,node-id=1,socket-id=0 " - "-numa cpu,node-id=0,socket-id=1,core-id=0 " - "-numa cpu,node-id=0,socket-id=1,core-id=1,thread-id=0 " - "-numa cpu,node-id=1,socket-id=1,core-id=1,thread-id=1"); - qts = qtest_init(cli); - cpus = get_cpus(qts, &resp); - g_assert(cpus); - - while ((e = qlist_pop(cpus))) { - QDict *cpu, *props; - int64_t socket, core, thread, node; - - cpu = qobject_to(QDict, e); - g_assert(qdict_haskey(cpu, "props")); - props = qdict_get_qdict(cpu, "props"); - - g_assert(qdict_haskey(props, "node-id")); - node = qdict_get_int(props, "node-id"); - g_assert(qdict_haskey(props, "socket-id")); - socket = qdict_get_int(props, "socket-id"); - g_assert(qdict_haskey(props, "core-id")); - core = qdict_get_int(props, "core-id"); - g_assert(qdict_haskey(props, "thread-id")); - thread = qdict_get_int(props, "thread-id"); - - if (socket == 0) { - g_assert_cmpint(node, ==, 1); - } else if (socket == 1 && core == 0) { - g_assert_cmpint(node, ==, 0); - } else if (socket == 1 && core == 1 && thread == 0) { - g_assert_cmpint(node, ==, 0); - } else if (socket == 1 && core == 1 && thread == 1) { - g_assert_cmpint(node, ==, 1); - } else { - g_assert(false); - } - qobject_unref(e); - } - - qobject_unref(resp); - qtest_quit(qts); - g_free(cli); -} - -static void spapr_numa_cpu(const void *data) -{ - char *cli; - QDict *resp; - QList *cpus; - QObject *e; - QTestState *qts; - - cli = make_cli(data, "-smp 4,cores=4 " - "-numa node,nodeid=0 -numa node,nodeid=1 " - "-numa cpu,node-id=0,core-id=0 " - "-numa cpu,node-id=0,core-id=1 " - "-numa cpu,node-id=0,core-id=2 " - "-numa cpu,node-id=1,core-id=3"); - qts = qtest_init(cli); - cpus = get_cpus(qts, &resp); - g_assert(cpus); - - while ((e = qlist_pop(cpus))) { - QDict *cpu, *props; - int64_t core, node; - - cpu = qobject_to(QDict, e); - g_assert(qdict_haskey(cpu, "props")); - props = qdict_get_qdict(cpu, "props"); - - g_assert(qdict_haskey(props, "node-id")); - node = qdict_get_int(props, "node-id"); - g_assert(qdict_haskey(props, "core-id")); - core = qdict_get_int(props, "core-id"); - - if (core >= 0 && core < 3) { - g_assert_cmpint(node, ==, 0); - } else if (core == 3) { - g_assert_cmpint(node, ==, 1); - } else { - g_assert(false); - } - qobject_unref(e); - } - - qobject_unref(resp); - qtest_quit(qts); - g_free(cli); -} - -static void aarch64_numa_cpu(const void *data) -{ - char *cli; - QDict *resp; - QList *cpus; - QObject *e; - QTestState *qts; - - cli = make_cli(data, "-smp 2 " - "-numa node,nodeid=0 -numa node,nodeid=1 " - "-numa cpu,node-id=1,thread-id=0 " - "-numa cpu,node-id=0,thread-id=1"); - qts = qtest_init(cli); - cpus = get_cpus(qts, &resp); - g_assert(cpus); - - while ((e = qlist_pop(cpus))) { - QDict *cpu, *props; - int64_t thread, node; - - cpu = qobject_to(QDict, e); - g_assert(qdict_haskey(cpu, "props")); - props = qdict_get_qdict(cpu, "props"); - - g_assert(qdict_haskey(props, "node-id")); - node = qdict_get_int(props, "node-id"); - g_assert(qdict_haskey(props, "thread-id")); - thread = qdict_get_int(props, "thread-id"); - - if (thread == 0) { - g_assert_cmpint(node, ==, 1); - } else if (thread == 1) { - g_assert_cmpint(node, ==, 0); - } else { - g_assert(false); - } - qobject_unref(e); - } - - qobject_unref(resp); - qtest_quit(qts); - g_free(cli); -} - -static void pc_dynamic_cpu_cfg(const void *data) -{ - QObject *e; - QDict *resp; - QList *cpus; - QTestState *qs; - - qs = qtest_initf("%s -nodefaults --preconfig -smp 2", - data ? (char *)data : ""); - - /* create 2 numa nodes */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'node', 'nodeid': 0 } }"))); - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'node', 'nodeid': 1 } }"))); - - /* map 2 cpus in non default reverse order - * i.e socket1->node0, socket0->node1 - */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'cpu', 'node-id': 0, 'socket-id': 1 } }"))); - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'cpu', 'node-id': 1, 'socket-id': 0 } }"))); - - /* let machine initialization to complete and run */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); - qtest_qmp_eventwait(qs, "RESUME"); - - /* check that CPUs are mapped as expected */ - resp = qtest_qmp(qs, "{ 'execute': 'query-hotpluggable-cpus'}"); - g_assert(qdict_haskey(resp, "return")); - cpus = qdict_get_qlist(resp, "return"); - g_assert(cpus); - while ((e = qlist_pop(cpus))) { - const QDict *cpu, *props; - int64_t socket, node; - - cpu = qobject_to(QDict, e); - g_assert(qdict_haskey(cpu, "props")); - props = qdict_get_qdict(cpu, "props"); - - g_assert(qdict_haskey(props, "node-id")); - node = qdict_get_int(props, "node-id"); - g_assert(qdict_haskey(props, "socket-id")); - socket = qdict_get_int(props, "socket-id"); - - if (socket == 0) { - g_assert_cmpint(node, ==, 1); - } else if (socket == 1) { - g_assert_cmpint(node, ==, 0); - } else { - g_assert(false); - } - qobject_unref(e); - } - qobject_unref(resp); - - qtest_quit(qs); -} - -static void pc_hmat_build_cfg(const void *data) -{ - QTestState *qs = qtest_initf("%s -nodefaults --preconfig -machine hmat=on " - "-smp 2,sockets=2 " - "-m 128M,slots=2,maxmem=1G " - "-object memory-backend-ram,size=64M,id=m0 " - "-object memory-backend-ram,size=64M,id=m1 " - "-numa node,nodeid=0,memdev=m0 " - "-numa node,nodeid=1,memdev=m1,initiator=0 " - "-numa cpu,node-id=0,socket-id=0 " - "-numa cpu,node-id=0,socket-id=1", - data ? (char *)data : ""); - - /* Fail: Initiator should be less than the number of nodes */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 2, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"access-latency\" } }"))); - - /* Fail: Target should be less than the number of nodes */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 2," - " 'hierarchy': \"memory\", 'data-type': \"access-latency\" } }"))); - - /* Fail: Initiator should contain cpu */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 1, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"access-latency\" } }"))); - - /* Fail: Data-type mismatch */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"write-latency\"," - " 'bandwidth': 524288000 } }"))); - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"read-bandwidth\"," - " 'latency': 5 } }"))); - - /* Fail: Bandwidth should be 1MB (1048576) aligned */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," - " 'bandwidth': 1048575 } }"))); - - /* Configuring HMAT bandwidth and latency details */ - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," - " 'latency': 1 } }"))); /* 1 ns */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," - " 'latency': 5 } }"))); /* Fail: Duplicate configuration */ - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," - " 'bandwidth': 68717379584 } }"))); /* 65534 MB/s */ - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," - " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," - " 'latency': 65534 } }"))); /* 65534 ns */ - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," - " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," - " 'bandwidth': 34358689792 } }"))); /* 32767 MB/s */ - - /* Fail: node_id should be less than the number of nodes */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 2, 'size': 10240," - " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," - " 'line': 8 } }"))); - - /* Fail: level should be less than HMAT_LB_LEVELS (4) */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," - " 'level': 4, 'associativity': \"direct\", 'policy': \"write-back\"," - " 'line': 8 } }"))); - - /* Fail: associativity option should be 'none', if level is 0 */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," - " 'level': 0, 'associativity': \"direct\", 'policy': \"none\"," - " 'line': 0 } }"))); - /* Fail: policy option should be 'none', if level is 0 */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," - " 'level': 0, 'associativity': \"none\", 'policy': \"write-back\"," - " 'line': 0 } }"))); - /* Fail: line option should be 0, if level is 0 */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," - " 'level': 0, 'associativity': \"none\", 'policy': \"none\"," - " 'line': 8 } }"))); - - /* Configuring HMAT memory side cache attributes */ - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," - " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," - " 'line': 8 } }"))); - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," - " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," - " 'line': 8 } }"))); /* Fail: Duplicate configuration */ - /* Fail: The size of level 2 size should be small than level 1 */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," - " 'level': 2, 'associativity': \"direct\", 'policy': \"write-back\"," - " 'line': 8 } }"))); - /* Fail: The size of level 0 size should be larger than level 1 */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," - " 'level': 0, 'associativity': \"direct\", 'policy': \"write-back\"," - " 'line': 8 } }"))); - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 1, 'size': 10240," - " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," - " 'line': 8 } }"))); - - /* let machine initialization to complete and run */ - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, - "{ 'execute': 'x-exit-preconfig' }"))); - qtest_qmp_eventwait(qs, "RESUME"); - - qtest_quit(qs); -} - -static void pc_hmat_off_cfg(const void *data) -{ - QTestState *qs = qtest_initf("%s -nodefaults --preconfig " - "-smp 2,sockets=2 " - "-m 128M,slots=2,maxmem=1G " - "-object memory-backend-ram,size=64M,id=m0 " - "-object memory-backend-ram,size=64M,id=m1 " - "-numa node,nodeid=0,memdev=m0", - data ? (char *)data : ""); - - /* - * Fail: Enable HMAT with -machine hmat=on - * before using any of hmat specific options - */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'node', 'nodeid': 1, 'memdev': \"m1\"," - " 'initiator': 0 } }"))); - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'node', 'nodeid': 1, 'memdev': \"m1\" } }"))); - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," - " 'latency': 1 } }"))); - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," - " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," - " 'line': 8 } }"))); - - /* let machine initialization to complete and run */ - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, - "{ 'execute': 'x-exit-preconfig' }"))); - qtest_qmp_eventwait(qs, "RESUME"); - - qtest_quit(qs); -} - -static void pc_hmat_erange_cfg(const void *data) -{ - QTestState *qs = qtest_initf("%s -nodefaults --preconfig -machine hmat=on " - "-smp 2,sockets=2 " - "-m 128M,slots=2,maxmem=1G " - "-object memory-backend-ram,size=64M,id=m0 " - "-object memory-backend-ram,size=64M,id=m1 " - "-numa node,nodeid=0,memdev=m0 " - "-numa node,nodeid=1,memdev=m1,initiator=0 " - "-numa cpu,node-id=0,socket-id=0 " - "-numa cpu,node-id=0,socket-id=1", - data ? (char *)data : ""); - - /* Can't store the compressed latency */ - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," - " 'latency': 1 } }"))); /* 1 ns */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," - " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," - " 'latency': 65535 } }"))); /* 65535 ns */ - - /* Test the 0 input (bandwidth not provided) */ - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," - " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," - " 'bandwidth': 0 } }"))); /* 0 MB/s */ - /* Fail: bandwidth should be provided before memory side cache attributes */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," - " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," - " 'line': 8 } }"))); - - /* Can't store the compressed bandwidth */ - g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," - " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," - " 'bandwidth': 68718428160 } }"))); /* 65535 MB/s */ - - /* let machine initialization to complete and run */ - g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, - "{ 'execute': 'x-exit-preconfig' }"))); - qtest_qmp_eventwait(qs, "RESUME"); - - qtest_quit(qs); -} - -int main(int argc, char **argv) -{ - const char *args = NULL; - const char *arch = qtest_get_arch(); - - if (strcmp(arch, "aarch64") == 0) { - args = "-machine virt"; - } - - g_test_init(&argc, &argv, NULL); - - qtest_add_data_func("/numa/mon/default", args, test_mon_default); - qtest_add_data_func("/numa/mon/cpus/explicit", args, test_mon_explicit); - qtest_add_data_func("/numa/mon/cpus/partial", args, test_mon_partial); - qtest_add_data_func("/numa/qmp/cpus/query-cpus", args, test_query_cpus); - - if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) { - qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu); - qtest_add_data_func("/numa/pc/dynamic/cpu", args, pc_dynamic_cpu_cfg); - qtest_add_data_func("/numa/pc/hmat/build", args, pc_hmat_build_cfg); - qtest_add_data_func("/numa/pc/hmat/off", args, pc_hmat_off_cfg); - qtest_add_data_func("/numa/pc/hmat/erange", args, pc_hmat_erange_cfg); - } - - if (!strcmp(arch, "ppc64")) { - qtest_add_data_func("/numa/spapr/cpu/explicit", args, spapr_numa_cpu); - } - - if (!strcmp(arch, "aarch64")) { - qtest_add_data_func("/numa/aarch64/cpu/explicit", args, - aarch64_numa_cpu); - } - - return g_test_run(); -} diff --git a/tests/nvme-test.c b/tests/nvme-test.c deleted file mode 100644 index ff0442150c..0000000000 --- a/tests/nvme-test.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * QTest testcase for NVMe - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu/module.h" -#include "qemu/units.h" -#include "libqtest.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -typedef struct QNvme QNvme; - -struct QNvme { - QOSGraphObject obj; - QPCIDevice dev; -}; - -static void *nvme_get_driver(void *obj, const char *interface) -{ - QNvme *nvme = obj; - - if (!g_strcmp0(interface, "pci-device")) { - return &nvme->dev; - } - - fprintf(stderr, "%s not present in nvme\n", interface); - g_assert_not_reached(); -} - -static void *nvme_create(void *pci_bus, QGuestAllocator *alloc, void *addr) -{ - QNvme *nvme = g_new0(QNvme, 1); - QPCIBus *bus = pci_bus; - - qpci_device_init(&nvme->dev, bus, addr); - nvme->obj.get_driver = nvme_get_driver; - - return &nvme->obj; -} - -/* This used to cause a NULL pointer dereference. */ -static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc) -{ - const int cmb_bar_size = 2 * MiB; - QNvme *nvme = obj; - QPCIDevice *pdev = &nvme->dev; - QPCIBar bar; - - qpci_device_enable(pdev); - bar = qpci_iomap(pdev, 2, NULL); - - qpci_io_writel(pdev, bar, 0, 0xccbbaa99); - g_assert_cmpint(qpci_io_readb(pdev, bar, 0), ==, 0x99); - g_assert_cmpint(qpci_io_readw(pdev, bar, 0), ==, 0xaa99); - - /* Test partially out-of-bounds accesses. */ - qpci_io_writel(pdev, bar, cmb_bar_size - 1, 0x44332211); - g_assert_cmpint(qpci_io_readb(pdev, bar, cmb_bar_size - 1), ==, 0x11); - g_assert_cmpint(qpci_io_readw(pdev, bar, cmb_bar_size - 1), !=, 0x2211); - g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211); -} - -static void nvme_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0,drive=drv0,serial=foo", - .before_cmd_line = "-drive id=drv0,if=none,file=null-co://," - "file.read-zeroes=on,format=raw", - }; - - add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); - - qos_node_create_driver("nvme", nvme_create); - qos_node_consumes("nvme", "pci-bus", &opts); - qos_node_produces("nvme", "pci-device"); - - qos_add_test("oob-cmb-access", "nvme", nvmetest_oob_cmb_test, &(QOSGraphTestOptions) { - .edge.extra_device_opts = "cmb_size_mb=2" - }); -} - -libqos_init(nvme_register_nodes); diff --git a/tests/pca9552-test.c b/tests/pca9552-test.c deleted file mode 100644 index 4b800d3c3e..0000000000 --- a/tests/pca9552-test.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * QTest testcase for the PCA9552 LED blinker - * - * Copyright (c) 2017-2018, IBM Corporation. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "libqtest.h" -#include "libqos/qgraph.h" -#include "libqos/i2c.h" -#include "hw/misc/pca9552_regs.h" - -#define PCA9552_TEST_ID "pca9552-test" -#define PCA9552_TEST_ADDR 0x60 - -static void pca9552_init(QI2CDevice *i2cdev) -{ - /* Switch on LEDs 0 and 12 */ - i2c_set8(i2cdev, PCA9552_LS0, 0x54); - i2c_set8(i2cdev, PCA9552_LS3, 0x54); -} - -static void receive_autoinc(void *obj, void *data, QGuestAllocator *alloc) -{ - QI2CDevice *i2cdev = (QI2CDevice *)obj; - uint8_t resp; - uint8_t reg = PCA9552_LS0 | PCA9552_AUTOINC; - - pca9552_init(i2cdev); - - i2c_send(i2cdev, ®, 1); - - /* PCA9552_LS0 */ - i2c_recv(i2cdev, &resp, 1); - g_assert_cmphex(resp, ==, 0x54); - - /* PCA9552_LS1 */ - i2c_recv(i2cdev, &resp, 1); - g_assert_cmphex(resp, ==, 0x55); - - /* PCA9552_LS2 */ - i2c_recv(i2cdev, &resp, 1); - g_assert_cmphex(resp, ==, 0x55); - - /* PCA9552_LS3 */ - i2c_recv(i2cdev, &resp, 1); - g_assert_cmphex(resp, ==, 0x54); -} - -static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) -{ - QI2CDevice *i2cdev = (QI2CDevice *)obj; - uint8_t value; - - value = i2c_get8(i2cdev, PCA9552_LS0); - g_assert_cmphex(value, ==, 0x55); - - value = i2c_get8(i2cdev, PCA9552_INPUT0); - g_assert_cmphex(value, ==, 0x0); - - pca9552_init(i2cdev); - - value = i2c_get8(i2cdev, PCA9552_LS0); - g_assert_cmphex(value, ==, 0x54); - - value = i2c_get8(i2cdev, PCA9552_INPUT0); - g_assert_cmphex(value, ==, 0x01); - - value = i2c_get8(i2cdev, PCA9552_LS3); - g_assert_cmphex(value, ==, 0x54); - - value = i2c_get8(i2cdev, PCA9552_INPUT1); - g_assert_cmphex(value, ==, 0x10); -} - -static void pca9552_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "address=0x60" - }; - add_qi2c_address(&opts, &(QI2CAddress) { 0x60 }); - - qos_node_create_driver("pca9552", i2c_device_create); - qos_node_consumes("pca9552", "i2c-bus", &opts); - - qos_add_test("tx-rx", "pca9552", send_and_receive, NULL); - qos_add_test("rx-autoinc", "pca9552", receive_autoinc, NULL); -} -libqos_init(pca9552_register_nodes); diff --git a/tests/pci-test.c b/tests/pci-test.c deleted file mode 100644 index 4b2092b949..0000000000 --- a/tests/pci-test.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * QTest testcase for PCI - * - * Copyright (c) 2018 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void *obj, void *data, QGuestAllocator *alloc) -{ -} - -static void register_pci_test(void) -{ - qos_add_test("nop", "pci-device", nop, NULL); -} - -libqos_init(register_pci_test); diff --git a/tests/pcnet-test.c b/tests/pcnet-test.c deleted file mode 100644 index 900944fa7e..0000000000 --- a/tests/pcnet-test.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * QTest testcase for PC-Net NIC - * - * Copyright (c) 2013-2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -typedef struct QPCNet QPCNet; - -struct QPCNet { - QOSGraphObject obj; - QPCIDevice dev; -}; - -static void *pcnet_get_driver(void *obj, const char *interface) -{ - QPCNet *pcnet = obj; - - if (!g_strcmp0(interface, "pci-device")) { - return &pcnet->dev; - } - - fprintf(stderr, "%s not present in pcnet\n", interface); - g_assert_not_reached(); -} - -static void *pcnet_create(void *pci_bus, QGuestAllocator *alloc, void *addr) -{ - QPCNet *pcnet = g_new0(QPCNet, 1); - QPCIBus *bus = pci_bus; - - qpci_device_init(&pcnet->dev, bus, addr); - pcnet->obj.get_driver = pcnet_get_driver; - - return &pcnet->obj; -} - -static void pcnet_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0", - }; - add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); - - qos_node_create_driver("pcnet", pcnet_create); - qos_node_consumes("pcnet", "pci-bus", &opts); - qos_node_produces("pcnet", "pci-device"); -} - -libqos_init(pcnet_register_nodes); diff --git a/tests/pflash-cfi02-test.c b/tests/pflash-cfi02-test.c deleted file mode 100644 index 17aa669b2e..0000000000 --- a/tests/pflash-cfi02-test.c +++ /dev/null @@ -1,681 +0,0 @@ -/* - * QTest testcase for parallel flash with AMD command set - * - * Copyright (c) 2019 Stephen Checkoway - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" - -/* - * To test the pflash_cfi02 device, we run QEMU with the musicpal machine with - * a pflash drive. This enables us to test some flash configurations, but not - * all. In particular, we're limited to a 16-bit wide flash device. - */ - -#define MP_FLASH_SIZE_MAX (32 * 1024 * 1024) -#define BASE_ADDR (0x100000000ULL - MP_FLASH_SIZE_MAX) - -#define UNIFORM_FLASH_SIZE (8 * 1024 * 1024) -#define UNIFORM_FLASH_SECTOR_SIZE (64 * 1024) - -/* Use a newtype to keep flash addresses separate from byte addresses. */ -typedef struct { - uint64_t addr; -} faddr; -#define FLASH_ADDR(x) ((faddr) { .addr = (x) }) - -#define CFI_ADDR FLASH_ADDR(0x55) -#define UNLOCK0_ADDR FLASH_ADDR(0x555) -#define UNLOCK1_ADDR FLASH_ADDR(0x2AA) - -#define CFI_CMD 0x98 -#define UNLOCK0_CMD 0xAA -#define UNLOCK1_CMD 0x55 -#define SECOND_UNLOCK_CMD 0x80 -#define AUTOSELECT_CMD 0x90 -#define RESET_CMD 0xF0 -#define PROGRAM_CMD 0xA0 -#define SECTOR_ERASE_CMD 0x30 -#define CHIP_ERASE_CMD 0x10 -#define UNLOCK_BYPASS_CMD 0x20 -#define UNLOCK_BYPASS_RESET_CMD 0x00 -#define ERASE_SUSPEND_CMD 0xB0 -#define ERASE_RESUME_CMD SECTOR_ERASE_CMD - -typedef struct { - int bank_width; - - /* Nonuniform block size. */ - int nb_blocs[4]; - int sector_len[4]; - - QTestState *qtest; -} FlashConfig; - -static char image_path[] = "/tmp/qtest.XXXXXX"; - -/* - * The pflash implementation allows some parameters to be unspecified. We want - * to test those configurations but we also need to know the real values in - * our testing code. So after we launch qemu, we'll need a new FlashConfig - * with the correct values filled in. - */ -static FlashConfig expand_config_defaults(const FlashConfig *c) -{ - FlashConfig ret = *c; - - if (ret.bank_width == 0) { - ret.bank_width = 2; - } - if (ret.nb_blocs[0] == 0 && ret.sector_len[0] == 0) { - ret.sector_len[0] = UNIFORM_FLASH_SECTOR_SIZE; - ret.nb_blocs[0] = UNIFORM_FLASH_SIZE / UNIFORM_FLASH_SECTOR_SIZE; - } - - /* XXX: Limitations of test harness. */ - assert(ret.bank_width == 2); - return ret; -} - -/* - * Return a bit mask suitable for extracting the least significant - * status/query response from an interleaved response. - */ -static inline uint64_t device_mask(const FlashConfig *c) -{ - return (uint64_t)-1; -} - -/* - * Return a bit mask exactly as long as the bank_width. - */ -static inline uint64_t bank_mask(const FlashConfig *c) -{ - if (c->bank_width == 8) { - return (uint64_t)-1; - } - return (1ULL << (c->bank_width * 8)) - 1ULL; -} - -static inline void flash_write(const FlashConfig *c, uint64_t byte_addr, - uint64_t data) -{ - /* Sanity check our tests. */ - assert((data & ~bank_mask(c)) == 0); - uint64_t addr = BASE_ADDR + byte_addr; - switch (c->bank_width) { - case 1: - qtest_writeb(c->qtest, addr, data); - break; - case 2: - qtest_writew(c->qtest, addr, data); - break; - case 4: - qtest_writel(c->qtest, addr, data); - break; - case 8: - qtest_writeq(c->qtest, addr, data); - break; - default: - abort(); - } -} - -static inline uint64_t flash_read(const FlashConfig *c, uint64_t byte_addr) -{ - uint64_t addr = BASE_ADDR + byte_addr; - switch (c->bank_width) { - case 1: - return qtest_readb(c->qtest, addr); - case 2: - return qtest_readw(c->qtest, addr); - case 4: - return qtest_readl(c->qtest, addr); - case 8: - return qtest_readq(c->qtest, addr); - default: - abort(); - } -} - -/* - * Convert a flash address expressed in the maximum width of the device as a - * byte address. - */ -static inline uint64_t as_byte_addr(const FlashConfig *c, faddr flash_addr) -{ - /* - * Command addresses are always given as addresses in the maximum - * supported bus size for the flash chip. So an x8/x16 chip in x8 mode - * uses addresses 0xAAA and 0x555 to unlock because the least significant - * bit is ignored. (0x555 rather than 0x554 is traditional.) - * - * In general we need to multiply by the maximum device width. - */ - return flash_addr.addr * c->bank_width; -} - -/* - * Return the command value or expected status replicated across all devices. - */ -static inline uint64_t replicate(const FlashConfig *c, uint64_t data) -{ - /* Sanity check our tests. */ - assert((data & ~device_mask(c)) == 0); - return data; -} - -static inline void flash_cmd(const FlashConfig *c, faddr cmd_addr, - uint8_t cmd) -{ - flash_write(c, as_byte_addr(c, cmd_addr), replicate(c, cmd)); -} - -static inline uint64_t flash_query(const FlashConfig *c, faddr query_addr) -{ - return flash_read(c, as_byte_addr(c, query_addr)); -} - -static inline uint64_t flash_query_1(const FlashConfig *c, faddr query_addr) -{ - return flash_query(c, query_addr) & device_mask(c); -} - -static void unlock(const FlashConfig *c) -{ - flash_cmd(c, UNLOCK0_ADDR, UNLOCK0_CMD); - flash_cmd(c, UNLOCK1_ADDR, UNLOCK1_CMD); -} - -static void reset(const FlashConfig *c) -{ - flash_cmd(c, FLASH_ADDR(0), RESET_CMD); -} - -static void sector_erase(const FlashConfig *c, uint64_t byte_addr) -{ - unlock(c); - flash_cmd(c, UNLOCK0_ADDR, SECOND_UNLOCK_CMD); - unlock(c); - flash_write(c, byte_addr, replicate(c, SECTOR_ERASE_CMD)); -} - -static void wait_for_completion(const FlashConfig *c, uint64_t byte_addr) -{ - /* If DQ6 is toggling, step the clock and ensure the toggle stops. */ - const uint64_t dq6 = replicate(c, 0x40); - if ((flash_read(c, byte_addr) & dq6) ^ (flash_read(c, byte_addr) & dq6)) { - /* Wait for erase or program to finish. */ - qtest_clock_step_next(c->qtest); - /* Ensure that DQ6 has stopped toggling. */ - g_assert_cmphex(flash_read(c, byte_addr), ==, flash_read(c, byte_addr)); - } -} - -static void bypass_program(const FlashConfig *c, uint64_t byte_addr, - uint16_t data) -{ - flash_cmd(c, UNLOCK0_ADDR, PROGRAM_CMD); - flash_write(c, byte_addr, data); - /* - * Data isn't valid until DQ6 stops toggling. We don't model this as - * writes are immediate, but if this changes in the future, we can wait - * until the program is complete. - */ - wait_for_completion(c, byte_addr); -} - -static void program(const FlashConfig *c, uint64_t byte_addr, uint16_t data) -{ - unlock(c); - bypass_program(c, byte_addr, data); -} - -static void chip_erase(const FlashConfig *c) -{ - unlock(c); - flash_cmd(c, UNLOCK0_ADDR, SECOND_UNLOCK_CMD); - unlock(c); - flash_cmd(c, UNLOCK0_ADDR, CHIP_ERASE_CMD); -} - -static void erase_suspend(const FlashConfig *c) -{ - flash_cmd(c, FLASH_ADDR(0), ERASE_SUSPEND_CMD); -} - -static void erase_resume(const FlashConfig *c) -{ - flash_cmd(c, FLASH_ADDR(0), ERASE_RESUME_CMD); -} - -/* - * Test flash commands with a variety of device geometry. - */ -static void test_geometry(const void *opaque) -{ - const FlashConfig *config = opaque; - QTestState *qtest; - qtest = qtest_initf("-M musicpal" - " -drive if=pflash,file=%s,format=raw,copy-on-read" - /* Device geometry properties. */ - " -global driver=cfi.pflash02," - "property=num-blocks0,value=%d" - " -global driver=cfi.pflash02," - "property=sector-length0,value=%d" - " -global driver=cfi.pflash02," - "property=num-blocks1,value=%d" - " -global driver=cfi.pflash02," - "property=sector-length1,value=%d" - " -global driver=cfi.pflash02," - "property=num-blocks2,value=%d" - " -global driver=cfi.pflash02," - "property=sector-length2,value=%d" - " -global driver=cfi.pflash02," - "property=num-blocks3,value=%d" - " -global driver=cfi.pflash02," - "property=sector-length3,value=%d", - image_path, - config->nb_blocs[0], - config->sector_len[0], - config->nb_blocs[1], - config->sector_len[1], - config->nb_blocs[2], - config->sector_len[2], - config->nb_blocs[3], - config->sector_len[3]); - FlashConfig explicit_config = expand_config_defaults(config); - explicit_config.qtest = qtest; - const FlashConfig *c = &explicit_config; - - /* Check the IDs. */ - unlock(c); - flash_cmd(c, UNLOCK0_ADDR, AUTOSELECT_CMD); - g_assert_cmphex(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF)); - if (c->bank_width >= 2) { - /* - * XXX: The ID returned by the musicpal flash chip is 16 bits which - * wouldn't happen with an 8-bit device. It would probably be best to - * prohibit addresses larger than the device width in pflash_cfi02.c, - * but then we couldn't test smaller device widths at all. - */ - g_assert_cmphex(flash_query(c, FLASH_ADDR(1)), ==, - replicate(c, 0x236D)); - } - reset(c); - - /* Check the erase blocks. */ - flash_cmd(c, CFI_ADDR, CFI_CMD); - g_assert_cmphex(flash_query(c, FLASH_ADDR(0x10)), ==, replicate(c, 'Q')); - g_assert_cmphex(flash_query(c, FLASH_ADDR(0x11)), ==, replicate(c, 'R')); - g_assert_cmphex(flash_query(c, FLASH_ADDR(0x12)), ==, replicate(c, 'Y')); - - /* Num erase regions. */ - int nb_erase_regions = flash_query_1(c, FLASH_ADDR(0x2C)); - g_assert_cmphex(nb_erase_regions, ==, - !!c->nb_blocs[0] + !!c->nb_blocs[1] + !!c->nb_blocs[2] + - !!c->nb_blocs[3]); - - /* Check device length. */ - uint32_t device_len = 1 << flash_query_1(c, FLASH_ADDR(0x27)); - g_assert_cmphex(device_len, ==, UNIFORM_FLASH_SIZE); - - /* Check that erase suspend to read/write is supported. */ - uint16_t pri = flash_query_1(c, FLASH_ADDR(0x15)) + - (flash_query_1(c, FLASH_ADDR(0x16)) << 8); - g_assert_cmpint(pri, >=, 0x2D + 4 * nb_erase_regions); - g_assert_cmpint(flash_query(c, FLASH_ADDR(pri + 0)), ==, replicate(c, 'P')); - g_assert_cmpint(flash_query(c, FLASH_ADDR(pri + 1)), ==, replicate(c, 'R')); - g_assert_cmpint(flash_query(c, FLASH_ADDR(pri + 2)), ==, replicate(c, 'I')); - g_assert_cmpint(flash_query_1(c, FLASH_ADDR(pri + 6)), ==, 2); /* R/W */ - reset(c); - - const uint64_t dq7 = replicate(c, 0x80); - const uint64_t dq6 = replicate(c, 0x40); - const uint64_t dq3 = replicate(c, 0x08); - const uint64_t dq2 = replicate(c, 0x04); - - uint64_t byte_addr = 0; - for (int region = 0; region < nb_erase_regions; ++region) { - uint64_t base = 0x2D + 4 * region; - flash_cmd(c, CFI_ADDR, CFI_CMD); - uint32_t nb_sectors = flash_query_1(c, FLASH_ADDR(base + 0)) + - (flash_query_1(c, FLASH_ADDR(base + 1)) << 8) + 1; - uint32_t sector_len = (flash_query_1(c, FLASH_ADDR(base + 2)) << 8) + - (flash_query_1(c, FLASH_ADDR(base + 3)) << 16); - g_assert_cmphex(nb_sectors, ==, c->nb_blocs[region]); - g_assert_cmphex(sector_len, ==, c->sector_len[region]); - reset(c); - - /* Erase and program sector. */ - for (uint32_t i = 0; i < nb_sectors; ++i) { - sector_erase(c, byte_addr); - - /* Check that DQ3 is 0. */ - g_assert_cmphex(flash_read(c, byte_addr) & dq3, ==, 0); - qtest_clock_step_next(c->qtest); /* Step over the 50 us timeout. */ - - /* Check that DQ3 is 1. */ - uint64_t status0 = flash_read(c, byte_addr); - g_assert_cmphex(status0 & dq3, ==, dq3); - - /* DQ7 is 0 during an erase. */ - g_assert_cmphex(status0 & dq7, ==, 0); - uint64_t status1 = flash_read(c, byte_addr); - - /* DQ6 toggles during an erase. */ - g_assert_cmphex(status0 & dq6, ==, ~status1 & dq6); - - /* Wait for erase to complete. */ - wait_for_completion(c, byte_addr); - - /* Ensure DQ6 has stopped toggling. */ - g_assert_cmphex(flash_read(c, byte_addr), ==, - flash_read(c, byte_addr)); - - /* Now the data should be valid. */ - g_assert_cmphex(flash_read(c, byte_addr), ==, bank_mask(c)); - - /* Program a bit pattern. */ - program(c, byte_addr, 0x55); - g_assert_cmphex(flash_read(c, byte_addr) & 0xFF, ==, 0x55); - program(c, byte_addr, 0xA5); - g_assert_cmphex(flash_read(c, byte_addr) & 0xFF, ==, 0x05); - byte_addr += sector_len; - } - } - - /* Erase the chip. */ - chip_erase(c); - /* Read toggle. */ - uint64_t status0 = flash_read(c, 0); - /* DQ7 is 0 during an erase. */ - g_assert_cmphex(status0 & dq7, ==, 0); - uint64_t status1 = flash_read(c, 0); - /* DQ6 toggles during an erase. */ - g_assert_cmphex(status0 & dq6, ==, ~status1 & dq6); - /* Wait for erase to complete. */ - qtest_clock_step_next(c->qtest); - /* Ensure DQ6 has stopped toggling. */ - g_assert_cmphex(flash_read(c, 0), ==, flash_read(c, 0)); - /* Now the data should be valid. */ - - for (int region = 0; region < nb_erase_regions; ++region) { - for (uint32_t i = 0; i < c->nb_blocs[region]; ++i) { - uint64_t byte_addr = i * c->sector_len[region]; - g_assert_cmphex(flash_read(c, byte_addr), ==, bank_mask(c)); - } - } - - /* Unlock bypass */ - unlock(c); - flash_cmd(c, UNLOCK0_ADDR, UNLOCK_BYPASS_CMD); - bypass_program(c, 0 * c->bank_width, 0x01); - bypass_program(c, 1 * c->bank_width, 0x23); - bypass_program(c, 2 * c->bank_width, 0x45); - /* - * Test that bypass programming, unlike normal programming can use any - * address for the PROGRAM_CMD. - */ - flash_cmd(c, FLASH_ADDR(3 * c->bank_width), PROGRAM_CMD); - flash_write(c, 3 * c->bank_width, 0x67); - wait_for_completion(c, 3 * c->bank_width); - flash_cmd(c, FLASH_ADDR(0), UNLOCK_BYPASS_RESET_CMD); - bypass_program(c, 4 * c->bank_width, 0x89); /* Should fail. */ - g_assert_cmphex(flash_read(c, 0 * c->bank_width), ==, 0x01); - g_assert_cmphex(flash_read(c, 1 * c->bank_width), ==, 0x23); - g_assert_cmphex(flash_read(c, 2 * c->bank_width), ==, 0x45); - g_assert_cmphex(flash_read(c, 3 * c->bank_width), ==, 0x67); - g_assert_cmphex(flash_read(c, 4 * c->bank_width), ==, bank_mask(c)); - - /* Test ignored high order bits of address. */ - flash_cmd(c, FLASH_ADDR(0x5555), UNLOCK0_CMD); - flash_cmd(c, FLASH_ADDR(0x2AAA), UNLOCK1_CMD); - flash_cmd(c, FLASH_ADDR(0x5555), AUTOSELECT_CMD); - g_assert_cmphex(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF)); - reset(c); - - /* - * Program a word on each sector, erase one or two sectors per region, and - * verify that all of those, and only those, are erased. - */ - byte_addr = 0; - for (int region = 0; region < nb_erase_regions; ++region) { - for (int i = 0; i < config->nb_blocs[region]; ++i) { - program(c, byte_addr, 0); - byte_addr += config->sector_len[region]; - } - } - unlock(c); - flash_cmd(c, UNLOCK0_ADDR, SECOND_UNLOCK_CMD); - unlock(c); - byte_addr = 0; - const uint64_t erase_cmd = replicate(c, SECTOR_ERASE_CMD); - for (int region = 0; region < nb_erase_regions; ++region) { - flash_write(c, byte_addr, erase_cmd); - if (c->nb_blocs[region] > 1) { - flash_write(c, byte_addr + c->sector_len[region], erase_cmd); - } - byte_addr += c->sector_len[region] * c->nb_blocs[region]; - } - - qtest_clock_step_next(c->qtest); /* Step over the 50 us timeout. */ - wait_for_completion(c, 0); - byte_addr = 0; - for (int region = 0; region < nb_erase_regions; ++region) { - for (int i = 0; i < config->nb_blocs[region]; ++i) { - if (i < 2) { - g_assert_cmphex(flash_read(c, byte_addr), ==, bank_mask(c)); - } else { - g_assert_cmphex(flash_read(c, byte_addr), ==, 0); - } - byte_addr += config->sector_len[region]; - } - } - - /* Test erase suspend/resume during erase timeout. */ - sector_erase(c, 0); - /* - * Check that DQ 3 is 0 and DQ6 and DQ2 are toggling in the sector being - * erased as well as in a sector not being erased. - */ - byte_addr = c->sector_len[0]; - status0 = flash_read(c, 0); - status1 = flash_read(c, 0); - g_assert_cmpint(status0 & dq3, ==, 0); - g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); - g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); - status0 = flash_read(c, byte_addr); - status1 = flash_read(c, byte_addr); - g_assert_cmpint(status0 & dq3, ==, 0); - g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); - g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); - - /* - * Check that after suspending, DQ6 does not toggle but DQ2 does toggle in - * an erase suspended sector but that neither toggle (we should be - * getting data) in a sector not being erased. - */ - erase_suspend(c); - status0 = flash_read(c, 0); - status1 = flash_read(c, 0); - g_assert_cmpint(status0 & dq6, ==, status1 & dq6); - g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); - g_assert_cmpint(flash_read(c, byte_addr), ==, flash_read(c, byte_addr)); - - /* Check that after resuming, DQ3 is 1 and DQ6 and DQ2 toggle. */ - erase_resume(c); - status0 = flash_read(c, 0); - status1 = flash_read(c, 0); - g_assert_cmpint(status0 & dq3, ==, dq3); - g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); - g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); - status0 = flash_read(c, byte_addr); - status1 = flash_read(c, byte_addr); - g_assert_cmpint(status0 & dq3, ==, dq3); - g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); - g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); - wait_for_completion(c, 0); - - /* Repeat this process but this time suspend after the timeout. */ - sector_erase(c, 0); - qtest_clock_step_next(c->qtest); - /* - * Check that DQ 3 is 1 and DQ6 and DQ2 are toggling in the sector being - * erased as well as in a sector not being erased. - */ - byte_addr = c->sector_len[0]; - status0 = flash_read(c, 0); - status1 = flash_read(c, 0); - g_assert_cmpint(status0 & dq3, ==, dq3); - g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); - g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); - status0 = flash_read(c, byte_addr); - status1 = flash_read(c, byte_addr); - g_assert_cmpint(status0 & dq3, ==, dq3); - g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); - g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); - - /* - * Check that after suspending, DQ6 does not toggle but DQ2 does toggle in - * an erase suspended sector but that neither toggle (we should be - * getting data) in a sector not being erased. - */ - erase_suspend(c); - status0 = flash_read(c, 0); - status1 = flash_read(c, 0); - g_assert_cmpint(status0 & dq6, ==, status1 & dq6); - g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); - g_assert_cmpint(flash_read(c, byte_addr), ==, flash_read(c, byte_addr)); - - /* Check that after resuming, DQ3 is 1 and DQ6 and DQ2 toggle. */ - erase_resume(c); - status0 = flash_read(c, 0); - status1 = flash_read(c, 0); - g_assert_cmpint(status0 & dq3, ==, dq3); - g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); - g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); - status0 = flash_read(c, byte_addr); - status1 = flash_read(c, byte_addr); - g_assert_cmpint(status0 & dq3, ==, dq3); - g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); - g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); - wait_for_completion(c, 0); - - qtest_quit(qtest); -} - -/* - * Test that - * 1. enter autoselect mode; - * 2. enter CFI mode; and then - * 3. exit CFI mode - * leaves the flash device in autoselect mode. - */ -static void test_cfi_in_autoselect(const void *opaque) -{ - const FlashConfig *config = opaque; - QTestState *qtest; - qtest = qtest_initf("-M musicpal" - " -drive if=pflash,file=%s,format=raw,copy-on-read", - image_path); - FlashConfig explicit_config = expand_config_defaults(config); - explicit_config.qtest = qtest; - const FlashConfig *c = &explicit_config; - - /* 1. Enter autoselect. */ - unlock(c); - flash_cmd(c, UNLOCK0_ADDR, AUTOSELECT_CMD); - g_assert_cmphex(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF)); - - /* 2. Enter CFI. */ - flash_cmd(c, CFI_ADDR, CFI_CMD); - g_assert_cmphex(flash_query(c, FLASH_ADDR(0x10)), ==, replicate(c, 'Q')); - g_assert_cmphex(flash_query(c, FLASH_ADDR(0x11)), ==, replicate(c, 'R')); - g_assert_cmphex(flash_query(c, FLASH_ADDR(0x12)), ==, replicate(c, 'Y')); - - /* 3. Exit CFI. */ - reset(c); - g_assert_cmphex(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF)); - - qtest_quit(qtest); -} - -static void cleanup(void *opaque) -{ - unlink(image_path); -} - -/* - * XXX: Tests are limited to bank_width = 2 for now because that's what - * hw/arm/musicpal.c has. - */ -static const FlashConfig configuration[] = { - /* One x16 device. */ - { - .bank_width = 2, - }, - /* Nonuniform sectors (top boot). */ - { - .bank_width = 2, - .nb_blocs = { 127, 1, 2, 1 }, - .sector_len = { 0x10000, 0x08000, 0x02000, 0x04000 }, - }, - /* Nonuniform sectors (bottom boot). */ - { - .bank_width = 2, - .nb_blocs = { 1, 2, 1, 127 }, - .sector_len = { 0x04000, 0x02000, 0x08000, 0x10000 }, - }, -}; - -int main(int argc, char **argv) -{ - int fd = mkstemp(image_path); - if (fd == -1) { - g_printerr("Failed to create temporary file %s: %s\n", image_path, - strerror(errno)); - exit(EXIT_FAILURE); - } - if (ftruncate(fd, UNIFORM_FLASH_SIZE) < 0) { - int error_code = errno; - close(fd); - unlink(image_path); - g_printerr("Failed to truncate file %s to %u MB: %s\n", image_path, - UNIFORM_FLASH_SIZE, strerror(error_code)); - exit(EXIT_FAILURE); - } - close(fd); - - qtest_add_abrt_handler(cleanup, NULL); - g_test_init(&argc, &argv, NULL); - - size_t nb_configurations = sizeof configuration / sizeof configuration[0]; - for (size_t i = 0; i < nb_configurations; ++i) { - const FlashConfig *config = &configuration[i]; - char *path = g_strdup_printf("pflash-cfi02" - "/geometry/%dx%x-%dx%x-%dx%x-%dx%x" - "/%d", - config->nb_blocs[0], - config->sector_len[0], - config->nb_blocs[1], - config->sector_len[1], - config->nb_blocs[2], - config->sector_len[2], - config->nb_blocs[3], - config->sector_len[3], - config->bank_width); - qtest_add_data_func(path, config, test_geometry); - g_free(path); - } - - qtest_add_data_func("pflash-cfi02/cfi-in-autoselect", &configuration[0], - test_cfi_in_autoselect); - int result = g_test_run(); - cleanup(NULL); - return result; -} diff --git a/tests/pnv-xscom-test.c b/tests/pnv-xscom-test.c deleted file mode 100644 index 2c46d5cf6d..0000000000 --- a/tests/pnv-xscom-test.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * QTest testcase for PowerNV XSCOM bus - * - * Copyright (c) 2016, IBM Corporation. - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * later. See the COPYING file in the top-level directory. - */ -#include "qemu/osdep.h" - -#include "libqtest.h" - -typedef enum PnvChipType { - PNV_CHIP_POWER8E, /* AKA Murano (default) */ - PNV_CHIP_POWER8, /* AKA Venice */ - PNV_CHIP_POWER8NVL, /* AKA Naples */ - PNV_CHIP_POWER9, /* AKA Nimbus */ -} PnvChipType; - -typedef struct PnvChip { - PnvChipType chip_type; - const char *cpu_model; - uint64_t xscom_base; - uint64_t cfam_id; - uint32_t first_core; -} PnvChip; - -static const PnvChip pnv_chips[] = { - { - .chip_type = PNV_CHIP_POWER8, - .cpu_model = "POWER8", - .xscom_base = 0x0003fc0000000000ull, - .cfam_id = 0x220ea04980000000ull, - .first_core = 0x1, - }, { - .chip_type = PNV_CHIP_POWER8NVL, - .cpu_model = "POWER8NVL", - .xscom_base = 0x0003fc0000000000ull, - .cfam_id = 0x120d304980000000ull, - .first_core = 0x1, - }, - { - .chip_type = PNV_CHIP_POWER9, - .cpu_model = "POWER9", - .xscom_base = 0x000603fc00000000ull, - .cfam_id = 0x220d104900008000ull, - .first_core = 0x0, - }, -}; - -static uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba) -{ - uint64_t addr = chip->xscom_base; - - if (chip->chip_type == PNV_CHIP_POWER9) { - addr |= ((uint64_t) pcba << 3); - } else { - addr |= (((uint64_t) pcba << 4) & ~0xffull) | - (((uint64_t) pcba << 3) & 0x78); - } - return addr; -} - -static uint64_t pnv_xscom_read(QTestState *qts, const PnvChip *chip, - uint32_t pcba) -{ - return qtest_readq(qts, pnv_xscom_addr(chip, pcba)); -} - -static void test_xscom_cfam_id(QTestState *qts, const PnvChip *chip) -{ - uint64_t f000f = pnv_xscom_read(qts, chip, 0xf000f); - - g_assert_cmphex(f000f, ==, chip->cfam_id); -} - -static void test_cfam_id(const void *data) -{ - const PnvChip *chip = data; - const char *machine = "powernv8"; - QTestState *qts; - - if (chip->chip_type == PNV_CHIP_POWER9) { - machine = "powernv9"; - } - - qts = qtest_initf("-M %s -accel tcg -cpu %s", - machine, chip->cpu_model); - test_xscom_cfam_id(qts, chip); - qtest_quit(qts); -} - - -#define PNV_XSCOM_EX_CORE_BASE 0x10000000ull -#define PNV_XSCOM_EX_BASE(core) \ - (PNV_XSCOM_EX_CORE_BASE | ((uint64_t)(core) << 24)) -#define PNV_XSCOM_P9_EC_BASE(core) \ - ((uint64_t)(((core) & 0x1F) + 0x20) << 24) - -#define PNV_XSCOM_EX_DTS_RESULT0 0x50000 - -static void test_xscom_core(QTestState *qts, const PnvChip *chip) -{ - uint32_t first_core_dts0 = PNV_XSCOM_EX_DTS_RESULT0; - uint64_t dts0; - - if (chip->chip_type != PNV_CHIP_POWER9) { - first_core_dts0 |= PNV_XSCOM_EX_BASE(chip->first_core); - } else { - first_core_dts0 |= PNV_XSCOM_P9_EC_BASE(chip->first_core); - } - - dts0 = pnv_xscom_read(qts, chip, first_core_dts0); - - g_assert_cmphex(dts0, ==, 0x26f024f023f0000ull); -} - -static void test_core(const void *data) -{ - const PnvChip *chip = data; - QTestState *qts; - const char *machine = "powernv8"; - - if (chip->chip_type == PNV_CHIP_POWER9) { - machine = "powernv9"; - } - - qts = qtest_initf("-M %s -accel tcg -cpu %s", - machine, chip->cpu_model); - test_xscom_core(qts, chip); - qtest_quit(qts); -} - -static void add_test(const char *name, void (*test)(const void *data)) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pnv_chips); i++) { - char *tname = g_strdup_printf("pnv-xscom/%s/%s", name, - pnv_chips[i].cpu_model); - qtest_add_data_func(tname, &pnv_chips[i], test); - g_free(tname); - } -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - add_test("cfam_id", test_cfam_id); - add_test("core", test_core); - return g_test_run(); -} diff --git a/tests/prom-env-test.c b/tests/prom-env-test.c deleted file mode 100644 index 9be52c766f..0000000000 --- a/tests/prom-env-test.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Test Open-Firmware-based machines. - * - * Copyright (c) 2016, 2017 Red Hat Inc. - * - * Author: - * Thomas Huth - * - * This work is licensed under the terms of the GNU GPL, version 2 - * or later. See the COPYING file in the top-level directory. - * - * This test is used to check that some Open Firmware based machines (i.e. - * OpenBIOS or SLOF) can be started successfully in TCG mode. To do this, we - * first put some Forth code into the "boot-command" Open Firmware environment - * variable. This Forth code writes a well-known magic value to a known location - * in memory. Then we start the guest so that the firmware can boot and finally - * run the Forth code. - * The testing code here then can finally check whether the value has been - * successfully written into the guest memory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" - -#define MAGIC 0xcafec0de -#define ADDRESS 0x4000 - -static void check_guest_memory(QTestState *qts) -{ - uint32_t signature; - int i; - - /* Poll until code has run and modified memory. Wait at most 600 seconds */ - for (i = 0; i < 60000; ++i) { - signature = qtest_readl(qts, ADDRESS); - if (signature == MAGIC) { - break; - } - g_usleep(10000); - } - - g_assert_cmphex(signature, ==, MAGIC); -} - -static void test_machine(const void *machine) -{ - const char *extra_args = ""; - QTestState *qts; - - /* - * The pseries firmware boots much faster without the default - * devices, it also needs Spectre/Meltdown workarounds disabled to - * avoid warnings with TCG - */ - if (strcmp(machine, "pseries") == 0) { - extra_args = "-nodefaults" - " -machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken"; - } - - qts = qtest_initf("-M %s -accel tcg %s -prom-env 'use-nvramrc?=true' " - "-prom-env 'nvramrc=%x %x l!' ", (const char *)machine, - extra_args, MAGIC, ADDRESS); - check_guest_memory(qts); - qtest_quit(qts); -} - -static void add_tests(const char *machines[]) -{ - int i; - char *name; - - for (i = 0; machines[i] != NULL; i++) { - name = g_strdup_printf("prom-env/%s", machines[i]); - qtest_add_data_func(name, machines[i], test_machine); - g_free(name); - } -} - -int main(int argc, char *argv[]) -{ - const char *sparc_machines[] = { "SPARCbook", "Voyager", "SS-20", NULL }; - const char *sparc64_machines[] = { "sun4u", NULL }; - const char *ppc_machines[] = { "mac99", "g3beige", NULL }; - const char *arch = qtest_get_arch(); - - g_test_init(&argc, &argv, NULL); - - if (!strcmp(arch, "ppc")) { - add_tests(ppc_machines); - } else if (!strcmp(arch, "ppc64")) { - add_tests(ppc_machines); - if (g_test_slow()) { - qtest_add_data_func("prom-env/pseries", "pseries", test_machine); - } - } else if (!strcmp(arch, "sparc")) { - add_tests(sparc_machines); - } else if (!strcmp(arch, "sparc64")) { - add_tests(sparc64_machines); - } else { - g_assert_not_reached(); - } - - return g_test_run(); -} diff --git a/tests/pvpanic-test.c b/tests/pvpanic-test.c deleted file mode 100644 index ff9176adf3..0000000000 --- a/tests/pvpanic-test.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * QTest testcase for PV Panic - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" - -static void test_panic(void) -{ - uint8_t val; - QDict *response, *data; - QTestState *qts; - - qts = qtest_init("-device pvpanic"); - - val = qtest_inb(qts, 0x505); - g_assert_cmpuint(val, ==, 1); - - qtest_outb(qts, 0x505, 0x1); - - response = qtest_qmp_receive(qts); - g_assert(qdict_haskey(response, "event")); - g_assert_cmpstr(qdict_get_str(response, "event"), ==, "GUEST_PANICKED"); - g_assert(qdict_haskey(response, "data")); - data = qdict_get_qdict(response, "data"); - g_assert(qdict_haskey(data, "action")); - g_assert_cmpstr(qdict_get_str(data, "action"), ==, "pause"); - qobject_unref(response); - - qtest_quit(qts); -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/pvpanic/panic", test_panic); - - ret = g_test_run(); - - return ret; -} diff --git a/tests/pxe-test.c b/tests/pxe-test.c deleted file mode 100644 index f68d0aadbb..0000000000 --- a/tests/pxe-test.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * PXE test cases. - * - * Copyright (c) 2016, 2017 Red Hat Inc. - * - * Authors: - * Michael S. Tsirkin , - * Victor Kaplansky - * Thomas Huth - * - * 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 -#include "qemu-common.h" -#include "libqtest.h" -#include "boot-sector.h" - -#define NETNAME "net0" - -static char disk[] = "tests/pxe-test-disk-XXXXXX"; - -typedef struct testdef { - const char *machine; /* Machine type */ - const char *model; /* NIC device model */ - const char *extra; /* Any additional parameters */ -} testdef_t; - -static testdef_t x86_tests[] = { - { "pc", "e1000" }, - { "pc", "virtio-net-pci" }, - { "q35", "e1000e" }, - { "q35", "virtio-net-pci", }, - { NULL }, -}; - -static testdef_t x86_tests_slow[] = { - { "pc", "ne2k_pci", }, - { "pc", "i82550", }, - { "pc", "rtl8139" }, - { "pc", "vmxnet3" }, - { NULL }, -}; - -static testdef_t ppc64_tests[] = { - { "pseries", "spapr-vlan", - "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" }, - { "pseries", "virtio-net-pci", - "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" }, - { NULL }, -}; - -static testdef_t ppc64_tests_slow[] = { - { "pseries", "e1000", - "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" }, - { NULL }, -}; - -static testdef_t s390x_tests[] = { - { "s390-ccw-virtio", "virtio-net-ccw" }, - { NULL }, -}; - -static void test_pxe_one(const testdef_t *test, bool ipv6) -{ - QTestState *qts; - char *args; - const char *extra = test->extra; - - if (!extra) { - extra = ""; - } - - args = g_strdup_printf( - "-accel kvm -accel tcg -machine %s -nodefaults -boot order=n " - "-netdev user,id=" NETNAME ",tftp=./,bootfile=%s,ipv4=%s,ipv6=%s " - "-device %s,bootindex=1,netdev=" NETNAME " %s", - test->machine, disk, ipv6 ? "off" : "on", ipv6 ? "on" : "off", - test->model, extra); - - qts = qtest_init(args); - boot_sector_test(qts); - qtest_quit(qts); - g_free(args); -} - -static void test_pxe_ipv4(gconstpointer data) -{ - const testdef_t *test = data; - - test_pxe_one(test, false); -} - -static void test_pxe_ipv6(gconstpointer data) -{ - const testdef_t *test = data; - - test_pxe_one(test, true); -} - -static void test_batch(const testdef_t *tests, bool ipv6) -{ - int i; - - for (i = 0; tests[i].machine; i++) { - const testdef_t *test = &tests[i]; - char *testname; - - testname = g_strdup_printf("pxe/ipv4/%s/%s", - test->machine, test->model); - qtest_add_data_func(testname, test, test_pxe_ipv4); - g_free(testname); - - if (ipv6) { - testname = g_strdup_printf("pxe/ipv6/%s/%s", - test->machine, test->model); - qtest_add_data_func(testname, test, test_pxe_ipv6); - g_free(testname); - } - } -} - -int main(int argc, char *argv[]) -{ - int ret; - const char *arch = qtest_get_arch(); - - ret = boot_sector_init(disk); - if(ret) - return ret; - - g_test_init(&argc, &argv, NULL); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - test_batch(x86_tests, false); - if (g_test_slow()) { - test_batch(x86_tests_slow, false); - } - } else if (strcmp(arch, "ppc64") == 0) { - test_batch(ppc64_tests, g_test_slow()); - if (g_test_slow()) { - test_batch(ppc64_tests_slow, true); - } - } else if (g_str_equal(arch, "s390x")) { - test_batch(s390x_tests, g_test_slow()); - } - ret = g_test_run(); - boot_sector_cleanup(disk); - return ret; -} diff --git a/tests/q35-test.c b/tests/q35-test.c deleted file mode 100644 index a68183d513..0000000000 --- a/tests/q35-test.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * QTest testcase for Q35 northbridge - * - * Copyright (c) 2015 Red Hat, Inc. - * - * Author: Gerd Hoffmann - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "libqos/pci.h" -#include "libqos/pci-pc.h" -#include "hw/pci-host/q35.h" -#include "qapi/qmp/qdict.h" - -#define TSEG_SIZE_TEST_GUEST_RAM_MBYTES 128 - -/* @esmramc_tseg_sz: ESMRAMC.TSEG_SZ bitmask for selecting the requested TSEG - * size. Must be a subset of - * MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK. - * - * @extended_tseg_mbytes: Size of the extended TSEG. Only consulted if - * @esmramc_tseg_sz equals - * MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK precisely. - * - * @expected_tseg_mbytes: Expected guest-visible TSEG size in megabytes, - * matching @esmramc_tseg_sz and @extended_tseg_mbytes - * above. - */ -struct TsegSizeArgs { - uint8_t esmramc_tseg_sz; - uint16_t extended_tseg_mbytes; - uint16_t expected_tseg_mbytes; -}; -typedef struct TsegSizeArgs TsegSizeArgs; - -static const TsegSizeArgs tseg_1mb = { - .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB, - .extended_tseg_mbytes = 0, - .expected_tseg_mbytes = 1, -}; -static const TsegSizeArgs tseg_2mb = { - .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_2MB, - .extended_tseg_mbytes = 0, - .expected_tseg_mbytes = 2, -}; -static const TsegSizeArgs tseg_8mb = { - .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_8MB, - .extended_tseg_mbytes = 0, - .expected_tseg_mbytes = 8, -}; -static const TsegSizeArgs tseg_ext_16mb = { - .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK, - .extended_tseg_mbytes = 16, - .expected_tseg_mbytes = 16, -}; - -static void smram_set_bit(QPCIDevice *pcidev, uint8_t mask, bool enabled) -{ - uint8_t smram; - - smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); - if (enabled) { - smram |= mask; - } else { - smram &= ~mask; - } - qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram); -} - -static bool smram_test_bit(QPCIDevice *pcidev, uint8_t mask) -{ - uint8_t smram; - - smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); - return smram & mask; -} - -static void test_smram_lock(void) -{ - QPCIBus *pcibus; - QPCIDevice *pcidev; - QDict *response; - QTestState *qts; - - qts = qtest_init("-M q35"); - - pcibus = qpci_new_pc(qts, NULL); - g_assert(pcibus != NULL); - - pcidev = qpci_device_find(pcibus, 0); - g_assert(pcidev != NULL); - - /* check open is settable */ - smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); - g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); - smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); - g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true); - - /* lock, check open is cleared & not settable */ - smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_LCK, true); - g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); - smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); - g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); - - /* reset */ - response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - /* check open is settable again */ - smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); - g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); - smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); - g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true); - - g_free(pcidev); - qpci_free_pc(pcibus); - - qtest_quit(qts); -} - -static void test_tseg_size(const void *data) -{ - const TsegSizeArgs *args = data; - QPCIBus *pcibus; - QPCIDevice *pcidev; - uint8_t smram_val; - uint8_t esmramc_val; - uint32_t ram_offs; - QTestState *qts; - - if (args->esmramc_tseg_sz == MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) { - qts = qtest_initf("-M q35 -m %uM -global mch.extended-tseg-mbytes=%u", - TSEG_SIZE_TEST_GUEST_RAM_MBYTES, - args->extended_tseg_mbytes); - } else { - qts = qtest_initf("-M q35 -m %uM", TSEG_SIZE_TEST_GUEST_RAM_MBYTES); - } - - /* locate the DRAM controller */ - pcibus = qpci_new_pc(qts, NULL); - g_assert(pcibus != NULL); - pcidev = qpci_device_find(pcibus, 0); - g_assert(pcidev != NULL); - - /* Set TSEG size. Restrict TSEG visibility to SMM by setting T_EN. */ - esmramc_val = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_ESMRAMC); - esmramc_val &= ~MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK; - esmramc_val |= args->esmramc_tseg_sz; - esmramc_val |= MCH_HOST_BRIDGE_ESMRAMC_T_EN; - qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_ESMRAMC, esmramc_val); - - /* Enable TSEG by setting G_SMRAME. Close TSEG by setting D_CLS. */ - smram_val = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); - smram_val &= ~(MCH_HOST_BRIDGE_SMRAM_D_OPEN | - MCH_HOST_BRIDGE_SMRAM_D_LCK); - smram_val |= (MCH_HOST_BRIDGE_SMRAM_D_CLS | - MCH_HOST_BRIDGE_SMRAM_G_SMRAME); - qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram_val); - - /* lock TSEG */ - smram_val |= MCH_HOST_BRIDGE_SMRAM_D_LCK; - qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram_val); - - /* Now check that the byte right before the TSEG is r/w, and that the first - * byte in the TSEG always reads as 0xff. - */ - ram_offs = (TSEG_SIZE_TEST_GUEST_RAM_MBYTES - args->expected_tseg_mbytes) * - 1024 * 1024 - 1; - g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 0); - qtest_writeb(qts, ram_offs, 1); - g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 1); - - ram_offs++; - g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 0xff); - qtest_writeb(qts, ram_offs, 1); - g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 0xff); - - g_free(pcidev); - qpci_free_pc(pcibus); - qtest_quit(qts); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/q35/smram/lock", test_smram_lock); - - qtest_add_data_func("/q35/tseg-size/1mb", &tseg_1mb, test_tseg_size); - qtest_add_data_func("/q35/tseg-size/2mb", &tseg_2mb, test_tseg_size); - qtest_add_data_func("/q35/tseg-size/8mb", &tseg_8mb, test_tseg_size); - qtest_add_data_func("/q35/tseg-size/ext/16mb", &tseg_ext_16mb, - test_tseg_size); - return g_test_run(); -} diff --git a/tests/qmp-cmd-test.c b/tests/qmp-cmd-test.c deleted file mode 100644 index 9f5228cd99..0000000000 --- a/tests/qmp-cmd-test.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * QMP command test cases - * - * Copyright (c) 2017 Red Hat Inc. - * - * Authors: - * Markus Armbruster - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qapi/error.h" -#include "qapi/qapi-visit-introspect.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qobject-input-visitor.h" - -const char common_args[] = "-nodefaults -machine none"; - -/* Query smoke tests */ - -static int query_error_class(const char *cmd) -{ - static struct { - const char *cmd; - int err_class; - } fails[] = { - /* Success depends on build configuration: */ -#ifndef CONFIG_SPICE - { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, -#endif -#ifndef CONFIG_VNC - { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, - { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, -#endif -#ifndef CONFIG_REPLICATION - { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, -#endif - /* Likewise, and require special QEMU command-line arguments: */ - { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, - { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, - { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, - { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, - { NULL, -1 } - }; - int i; - - for (i = 0; fails[i].cmd; i++) { - if (!strcmp(cmd, fails[i].cmd)) { - return fails[i].err_class; - } - } - return -1; -} - -static void test_query(const void *data) -{ - const char *cmd = data; - int expected_error_class = query_error_class(cmd); - QDict *resp, *error; - const char *error_class; - QTestState *qts; - - qts = qtest_init(common_args); - - resp = qtest_qmp(qts, "{ 'execute': %s }", cmd); - error = qdict_get_qdict(resp, "error"); - error_class = error ? qdict_get_str(error, "class") : NULL; - - if (expected_error_class < 0) { - g_assert(qdict_haskey(resp, "return")); - } else { - g_assert(error); - g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class, - -1, &error_abort), - ==, expected_error_class); - } - qobject_unref(resp); - - qtest_quit(qts); -} - -static bool query_is_blacklisted(const char *cmd) -{ - const char *blacklist[] = { - /* Not actually queries: */ - "add-fd", - /* Success depends on target arch: */ - "query-cpu-definitions", /* arm, i386, ppc, s390x */ - "query-gic-capabilities", /* arm */ - /* Success depends on target-specific build configuration: */ - "query-pci", /* CONFIG_PCI */ - /* Success depends on launching SEV guest */ - "query-sev-launch-measure", - /* Success depends on Host or Hypervisor SEV support */ - "query-sev", - "query-sev-capabilities", - NULL - }; - int i; - - for (i = 0; blacklist[i]; i++) { - if (!strcmp(cmd, blacklist[i])) { - return true; - } - } - return false; -} - -typedef struct { - SchemaInfoList *list; - GHashTable *hash; -} QmpSchema; - -static void qmp_schema_init(QmpSchema *schema) -{ - QDict *resp; - Visitor *qiv; - SchemaInfoList *tail; - QTestState *qts; - - qts = qtest_init(common_args); - - resp = qtest_qmp(qts, "{ 'execute': 'query-qmp-schema' }"); - - qiv = qobject_input_visitor_new(qdict_get(resp, "return")); - visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); - visit_free(qiv); - - qobject_unref(resp); - qtest_quit(qts); - - schema->hash = g_hash_table_new(g_str_hash, g_str_equal); - - /* Build @schema: hash table mapping entity name to SchemaInfo */ - for (tail = schema->list; tail; tail = tail->next) { - g_hash_table_insert(schema->hash, tail->value->name, tail->value); - } -} - -static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) -{ - return g_hash_table_lookup(schema->hash, name); -} - -static void qmp_schema_cleanup(QmpSchema *schema) -{ - qapi_free_SchemaInfoList(schema->list); - g_hash_table_destroy(schema->hash); -} - -static bool object_type_has_mandatory_members(SchemaInfo *type) -{ - SchemaInfoObjectMemberList *tail; - - g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); - - for (tail = type->u.object.members; tail; tail = tail->next) { - if (!tail->value->has_q_default) { - return true; - } - } - - return false; -} - -static void add_query_tests(QmpSchema *schema) -{ - SchemaInfoList *tail; - SchemaInfo *si, *arg_type, *ret_type; - char *test_name; - - /* Test the query-like commands */ - for (tail = schema->list; tail; tail = tail->next) { - si = tail->value; - if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { - continue; - } - - if (query_is_blacklisted(si->name)) { - continue; - } - - arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); - if (object_type_has_mandatory_members(arg_type)) { - continue; - } - - ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); - if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT - && !ret_type->u.object.members) { - continue; - } - - test_name = g_strdup_printf("qmp/%s", si->name); - qtest_add_data_func(test_name, si->name, test_query); - g_free(test_name); - } -} - -static void test_object_add_without_props(void) -{ - QTestState *qts; - QDict *resp; - - qts = qtest_init(common_args); - resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':" - " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }"); - g_assert_nonnull(resp); - qmp_assert_error_class(resp, "GenericError"); - qtest_quit(qts); -} - -int main(int argc, char *argv[]) -{ - QmpSchema schema; - int ret; - - g_test_init(&argc, &argv, NULL); - - qmp_schema_init(&schema); - add_query_tests(&schema); - - qtest_add_func("qmp/object-add-without-props", - test_object_add_without_props); - /* TODO: add coverage of generic object-add failure modes */ - - ret = g_test_run(); - - qmp_schema_cleanup(&schema); - return ret; -} diff --git a/tests/qmp-test.c b/tests/qmp-test.c deleted file mode 100644 index 1b0eb69832..0000000000 --- a/tests/qmp-test.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * QMP protocol test cases - * - * Copyright (c) 2017-2018 Red Hat Inc. - * - * Authors: - * Markus Armbruster - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qapi/error.h" -#include "qapi/qapi-visit-misc.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qstring.h" - -const char common_args[] = "-nodefaults -machine none"; - -static void test_version(QObject *version) -{ - Visitor *v; - VersionInfo *vinfo; - - g_assert(version); - v = qobject_input_visitor_new(version); - visit_type_VersionInfo(v, "version", &vinfo, &error_abort); - qapi_free_VersionInfo(vinfo); - visit_free(v); -} - -static void assert_recovered(QTestState *qts) -{ - QDict *resp; - - resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd' }"); - qmp_assert_error_class(resp, "CommandNotFound"); -} - -static void test_malformed(QTestState *qts) -{ - QDict *resp; - - /* syntax error */ - qtest_qmp_send_raw(qts, "{]\n"); - resp = qtest_qmp_receive(qts); - qmp_assert_error_class(resp, "GenericError"); - assert_recovered(qts); - - /* lexical error: impossible byte outside string */ - qtest_qmp_send_raw(qts, "{\xFF"); - resp = qtest_qmp_receive(qts); - qmp_assert_error_class(resp, "GenericError"); - assert_recovered(qts); - - /* lexical error: funny control character outside string */ - qtest_qmp_send_raw(qts, "{\x01"); - resp = qtest_qmp_receive(qts); - qmp_assert_error_class(resp, "GenericError"); - assert_recovered(qts); - - /* lexical error: impossible byte in string */ - qtest_qmp_send_raw(qts, "{'bad \xFF"); - resp = qtest_qmp_receive(qts); - qmp_assert_error_class(resp, "GenericError"); - assert_recovered(qts); - - /* lexical error: control character in string */ - qtest_qmp_send_raw(qts, "{'execute': 'nonexistent', 'id':'\n"); - resp = qtest_qmp_receive(qts); - qmp_assert_error_class(resp, "GenericError"); - assert_recovered(qts); - - /* lexical error: interpolation */ - qtest_qmp_send_raw(qts, "%%p"); - resp = qtest_qmp_receive(qts); - qmp_assert_error_class(resp, "GenericError"); - assert_recovered(qts); - - /* Not even a dictionary */ - resp = qtest_qmp(qts, "null"); - qmp_assert_error_class(resp, "GenericError"); - - /* No "execute" key */ - resp = qtest_qmp(qts, "{}"); - qmp_assert_error_class(resp, "GenericError"); - - /* "execute" isn't a string */ - resp = qtest_qmp(qts, "{ 'execute': true }"); - qmp_assert_error_class(resp, "GenericError"); - - /* "arguments" isn't a dictionary */ - resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }"); - qmp_assert_error_class(resp, "GenericError"); - - /* extra key */ - resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }"); - qmp_assert_error_class(resp, "GenericError"); -} - -static void test_qmp_protocol(void) -{ - QDict *resp, *q, *ret; - QList *capabilities; - QTestState *qts; - - qts = qtest_init_without_qmp_handshake(common_args); - - /* Test greeting */ - resp = qtest_qmp_receive(qts); - q = qdict_get_qdict(resp, "QMP"); - g_assert(q); - test_version(qdict_get(q, "version")); - capabilities = qdict_get_qlist(q, "capabilities"); - g_assert(capabilities); - qobject_unref(resp); - - /* Test valid command before handshake */ - resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); - qmp_assert_error_class(resp, "CommandNotFound"); - - /* Test malformed commands before handshake */ - test_malformed(qts); - - /* Test handshake */ - resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); - ret = qdict_get_qdict(resp, "return"); - g_assert(ret && !qdict_size(ret)); - qobject_unref(resp); - - /* Test repeated handshake */ - resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); - qmp_assert_error_class(resp, "CommandNotFound"); - - /* Test valid command */ - resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); - test_version(qdict_get(resp, "return")); - qobject_unref(resp); - - /* Test malformed commands */ - test_malformed(qts); - - /* Test 'id' */ - resp = qtest_qmp(qts, "{ 'execute': 'query-name', 'id': 'cookie#1' }"); - ret = qdict_get_qdict(resp, "return"); - g_assert(ret); - g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1"); - qobject_unref(resp); - - /* Test command failure with 'id' */ - resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }"); - g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); - qmp_assert_error_class(resp, "GenericError"); - - qtest_quit(qts); -} - -/* Out-of-band tests */ - -char tmpdir[] = "/tmp/qmp-test-XXXXXX"; -char *fifo_name; - -static void setup_blocking_cmd(void) -{ - if (!mkdtemp(tmpdir)) { - g_error("mkdtemp: %s", strerror(errno)); - } - fifo_name = g_strdup_printf("%s/fifo", tmpdir); - if (mkfifo(fifo_name, 0666)) { - g_error("mkfifo: %s", strerror(errno)); - } -} - -static void cleanup_blocking_cmd(void) -{ - unlink(fifo_name); - rmdir(tmpdir); -} - -static void send_cmd_that_blocks(QTestState *s, const char *id) -{ - qtest_qmp_send(s, "{ 'execute': 'blockdev-add', 'id': %s," - " 'arguments': {" - " 'driver': 'blkdebug', 'node-name': %s," - " 'config': %s," - " 'image': { 'driver': 'null-co', 'read-zeroes': true } } }", - id, id, fifo_name); -} - -static void unblock_blocked_cmd(void) -{ - int fd = open(fifo_name, O_WRONLY); - g_assert(fd >= 0); - close(fd); -} - -static void send_oob_cmd_that_fails(QTestState *s, const char *id) -{ - qtest_qmp_send(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id); -} - -static void recv_cmd_id(QTestState *s, const char *id) -{ - QDict *resp = qtest_qmp_receive(s); - - g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id); - qobject_unref(resp); -} - -static void test_qmp_oob(void) -{ - QTestState *qts; - QDict *resp, *q; - const QListEntry *entry; - QList *capabilities; - QString *qstr; - - qts = qtest_init_without_qmp_handshake(common_args); - - /* Check the greeting message. */ - resp = qtest_qmp_receive(qts); - q = qdict_get_qdict(resp, "QMP"); - g_assert(q); - capabilities = qdict_get_qlist(q, "capabilities"); - g_assert(capabilities && !qlist_empty(capabilities)); - entry = qlist_first(capabilities); - g_assert(entry); - qstr = qobject_to(QString, entry->value); - g_assert(qstr); - g_assert_cmpstr(qstring_get_str(qstr), ==, "oob"); - qobject_unref(resp); - - /* Try a fake capability, it should fail. */ - resp = qtest_qmp(qts, - "{ 'execute': 'qmp_capabilities', " - " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }"); - g_assert(qdict_haskey(resp, "error")); - qobject_unref(resp); - - /* Now, enable OOB in current QMP session, it should succeed. */ - resp = qtest_qmp(qts, - "{ 'execute': 'qmp_capabilities', " - " 'arguments': { 'enable': [ 'oob' ] } }"); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); - - /* - * Try any command that does not support OOB but with OOB flag. We - * should get failure. - */ - resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }"); - g_assert(qdict_haskey(resp, "error")); - qobject_unref(resp); - - /* OOB command overtakes slow in-band command */ - setup_blocking_cmd(); - send_cmd_that_blocks(qts, "ib-blocks-1"); - qtest_qmp_send(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }"); - send_oob_cmd_that_fails(qts, "oob-1"); - recv_cmd_id(qts, "oob-1"); - unblock_blocked_cmd(); - recv_cmd_id(qts, "ib-blocks-1"); - recv_cmd_id(qts, "ib-quick-1"); - - /* Even malformed in-band command fails in-band */ - send_cmd_that_blocks(qts, "blocks-2"); - qtest_qmp_send(qts, "{ 'id': 'err-2' }"); - unblock_blocked_cmd(); - recv_cmd_id(qts, "blocks-2"); - recv_cmd_id(qts, "err-2"); - cleanup_blocking_cmd(); - - qtest_quit(qts); -} - -/* Preconfig tests */ - -static void test_qmp_preconfig(void) -{ - QDict *rsp, *ret; - QTestState *qs = qtest_initf("%s --preconfig", common_args); - - /* preconfig state */ - /* enabled commands, no error expected */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-commands' }"))); - - /* forbidden commands, expected error */ - g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); - - /* check that query-status returns preconfig state */ - rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); - ret = qdict_get_qdict(rsp, "return"); - g_assert(ret); - g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "preconfig"); - qobject_unref(rsp); - - /* exit preconfig state */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); - qtest_qmp_eventwait(qs, "RESUME"); - - /* check that query-status returns running state */ - rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); - ret = qdict_get_qdict(rsp, "return"); - g_assert(ret); - g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running"); - qobject_unref(rsp); - - /* check that x-exit-preconfig returns error after exiting preconfig */ - g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); - - /* enabled commands, no error expected */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); - - qtest_quit(qs); -} - -static void test_qmp_missing_any_arg(void) -{ - QTestState *qts; - QDict *resp; - - qts = qtest_init(common_args); - resp = qtest_qmp(qts, "{'execute': 'qom-set', 'arguments':" - " { 'path': '/machine', 'property': 'rtc-time' } }"); - g_assert_nonnull(resp); - qmp_assert_error_class(resp, "GenericError"); - qtest_quit(qts); -} - -int main(int argc, char *argv[]) -{ - g_test_init(&argc, &argv, NULL); - - qtest_add_func("qmp/protocol", test_qmp_protocol); - qtest_add_func("qmp/oob", test_qmp_oob); - qtest_add_func("qmp/preconfig", test_qmp_preconfig); - qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg); - - return g_test_run(); -} diff --git a/tests/qom-test.c b/tests/qom-test.c deleted file mode 100644 index 4f94cc678c..0000000000 --- a/tests/qom-test.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * QTest testcase for QOM - * - * Copyright (c) 2013 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qemu-common.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qemu/cutils.h" -#include "libqtest.h" - -static const char *blacklist_x86[] = { - "xenfv", "xenpv", NULL -}; - -static const struct { - const char *arch; - const char **machine; -} blacklists[] = { - { "i386", blacklist_x86 }, - { "x86_64", blacklist_x86 }, -}; - -static bool is_blacklisted(const char *arch, const char *mach) -{ - int i; - const char **p; - - for (i = 0; i < ARRAY_SIZE(blacklists); i++) { - if (!strcmp(blacklists[i].arch, arch)) { - for (p = blacklists[i].machine; *p; p++) { - if (!strcmp(*p, mach)) { - return true; - } - } - } - } - return false; -} - -static void test_properties(QTestState *qts, const char *path, bool recurse) -{ - char *child_path; - QDict *response, *tuple, *tmp; - QList *list; - QListEntry *entry; - - g_test_message("Obtaining properties of %s", path); - response = qtest_qmp(qts, "{ 'execute': 'qom-list'," - " 'arguments': { 'path': %s } }", path); - g_assert(response); - - if (!recurse) { - qobject_unref(response); - return; - } - - g_assert(qdict_haskey(response, "return")); - list = qobject_to(QList, qdict_get(response, "return")); - QLIST_FOREACH_ENTRY(list, entry) { - tuple = qobject_to(QDict, qlist_entry_obj(entry)); - bool is_child = strstart(qdict_get_str(tuple, "type"), "child<", NULL); - bool is_link = strstart(qdict_get_str(tuple, "type"), "link<", NULL); - - if (is_child || is_link) { - child_path = g_strdup_printf("%s/%s", - path, qdict_get_str(tuple, "name")); - test_properties(qts, child_path, is_child); - g_free(child_path); - } else { - const char *prop = qdict_get_str(tuple, "name"); - g_test_message("Testing property %s.%s", path, prop); - tmp = qtest_qmp(qts, - "{ 'execute': 'qom-get'," - " 'arguments': { 'path': %s, 'property': %s } }", - path, prop); - /* qom-get may fail but should not, e.g., segfault. */ - g_assert(tmp); - qobject_unref(tmp); - } - } - qobject_unref(response); -} - -static void test_machine(gconstpointer data) -{ - const char *machine = data; - QDict *response; - QTestState *qts; - - qts = qtest_initf("-machine %s", machine); - - test_properties(qts, "/machine", true); - - response = qtest_qmp(qts, "{ 'execute': 'quit' }"); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); - - qtest_quit(qts); - g_free((void *)machine); -} - -static void add_machine_test_case(const char *mname) -{ - const char *arch = qtest_get_arch(); - - if (!is_blacklisted(arch, mname)) { - char *path = g_strdup_printf("qom/%s", mname); - qtest_add_data_func(path, g_strdup(mname), test_machine); - g_free(path); - } -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qtest_cb_for_every_machine(add_machine_test_case, g_test_quick()); - - return g_test_run(); -} diff --git a/tests/qos-test.c b/tests/qos-test.c deleted file mode 100644 index fd70d73ea5..0000000000 --- a/tests/qos-test.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * libqos driver framework - * - * Copyright (c) 2018 Emanuele Giuseppe Esposito - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2 as published by the Free Software Foundation. - * - * 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 - */ - -#include "qemu/osdep.h" -#include -#include "libqtest-single.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qstring.h" -#include "qemu/module.h" -#include "qapi/qmp/qlist.h" -#include "libqos/malloc.h" -#include "libqos/qgraph.h" -#include "libqos/qgraph_internal.h" - -static char *old_path; - -static void apply_to_node(const char *name, bool is_machine, bool is_abstract) -{ - char *machine_name = NULL; - if (is_machine) { - const char *arch = qtest_get_arch(); - machine_name = g_strconcat(arch, "/", name, NULL); - name = machine_name; - } - qos_graph_node_set_availability(name, true); - if (is_abstract) { - qos_delete_cmd_line(name); - } - g_free(machine_name); -} - -/** - * apply_to_qlist(): using QMP queries QEMU for a list of - * machines and devices available, and sets the respective node - * as true. If a node is found, also all its produced and contained - * child are marked available. - * - * See qos_graph_node_set_availability() for more info - */ -static void apply_to_qlist(QList *list, bool is_machine) -{ - const QListEntry *p; - const char *name; - bool abstract; - QDict *minfo; - QObject *qobj; - QString *qstr; - QBool *qbool; - - for (p = qlist_first(list); p; p = qlist_next(p)) { - minfo = qobject_to(QDict, qlist_entry_obj(p)); - qobj = qdict_get(minfo, "name"); - qstr = qobject_to(QString, qobj); - name = qstring_get_str(qstr); - - qobj = qdict_get(minfo, "abstract"); - if (qobj) { - qbool = qobject_to(QBool, qobj); - abstract = qbool_get_bool(qbool); - } else { - abstract = false; - } - - apply_to_node(name, is_machine, abstract); - qobj = qdict_get(minfo, "alias"); - if (qobj) { - qstr = qobject_to(QString, qobj); - name = qstring_get_str(qstr); - apply_to_node(name, is_machine, abstract); - } - } -} - -/** - * qos_set_machines_devices_available(): sets availability of qgraph - * machines and devices. - * - * This function firstly starts QEMU with "-machine none" option, - * and then executes the QMP protocol asking for the list of devices - * and machines available. - * - * for each of these items, it looks up the corresponding qgraph node, - * setting it as available. The list currently returns all devices that - * are either machines or QEDGE_CONSUMED_BY other nodes. - * Therefore, in order to mark all other nodes, it recursively sets - * all its QEDGE_CONTAINS and QEDGE_PRODUCES child as available too. - */ -static void qos_set_machines_devices_available(void) -{ - QDict *response; - QDict *args = qdict_new(); - QList *list; - - qtest_start("-machine none"); - response = qmp("{ 'execute': 'query-machines' }"); - list = qdict_get_qlist(response, "return"); - - apply_to_qlist(list, true); - - qobject_unref(response); - - qdict_put_bool(args, "abstract", true); - qdict_put_str(args, "implements", "device"); - - response = qmp("{'execute': 'qom-list-types'," - " 'arguments': %p }", args); - g_assert(qdict_haskey(response, "return")); - list = qdict_get_qlist(response, "return"); - - apply_to_qlist(list, false); - - qtest_end(); - qobject_unref(response); -} - -static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj) -{ - return obj->get_driver(obj, "memory"); -} - -static void restart_qemu_or_continue(char *path) -{ - /* compares the current command line with the - * one previously executed: if they are the same, - * don't restart QEMU, if they differ, stop previous - * QEMU subprocess (if active) and start over with - * the new command line - */ - if (g_strcmp0(old_path, path)) { - qtest_end(); - qos_invalidate_command_line(); - old_path = g_strdup(path); - qtest_start(path); - } else { /* if cmd line is the same, reset the guest */ - qobject_unref(qmp("{ 'execute': 'system_reset' }")); - qmp_eventwait("RESET"); - } -} - -void qos_invalidate_command_line(void) -{ - g_free(old_path); - old_path = NULL; -} - -/** - * allocate_objects(): given an array of nodes @arg, - * walks the path invoking all constructors and - * passing the corresponding parameter in order to - * continue the objects allocation. - * Once the test is reached, return the object it consumes. - * - * Since the machine and QEDGE_CONSUMED_BY nodes allocate - * memory in the constructor, g_test_queue_destroy is used so - * that after execution they can be safely free'd. (The test's - * ->before callback is also welcome to use g_test_queue_destroy). - * - * Note: as specified in walk_path() too, @arg is an array of - * char *, where arg[0] is a pointer to the command line - * string that will be used to properly start QEMU when executing - * the test, and the remaining elements represent the actual objects - * that will be allocated. - */ -static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc) -{ - int current = 0; - QGuestAllocator *alloc; - QOSGraphObject *parent = NULL; - QOSGraphEdge *edge; - QOSGraphNode *node; - void *edge_arg; - void *obj; - - node = qos_graph_get_node(path[current]); - g_assert(node->type == QNODE_MACHINE); - - obj = qos_machine_new(node, qts); - qos_object_queue_destroy(obj); - - alloc = get_machine_allocator(obj); - if (p_alloc) { - *p_alloc = alloc; - } - - for (;;) { - if (node->type != QNODE_INTERFACE) { - qos_object_start_hw(obj); - parent = obj; - } - - /* follow edge and get object for next node constructor */ - current++; - edge = qos_graph_get_edge(path[current - 1], path[current]); - node = qos_graph_get_node(path[current]); - - if (node->type == QNODE_TEST) { - g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY); - return obj; - } - - switch (qos_graph_edge_get_type(edge)) { - case QEDGE_PRODUCES: - obj = parent->get_driver(parent, path[current]); - break; - - case QEDGE_CONSUMED_BY: - edge_arg = qos_graph_edge_get_arg(edge); - obj = qos_driver_new(node, obj, alloc, edge_arg); - qos_object_queue_destroy(obj); - break; - - case QEDGE_CONTAINS: - obj = parent->get_device(parent, path[current]); - break; - } - } -} - -/* The argument to run_one_test, which is the test function that is registered - * with GTest, is a vector of strings. The first item is the initial command - * line (before it is modified by the test's "before" function), the remaining - * items are node names forming the path to the test node. - */ -static char **current_path; - -const char *qos_get_current_command_line(void) -{ - return current_path[0]; -} - -void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc) -{ - return allocate_objects(qts, current_path + 1, p_alloc); -} - -/** - * run_one_test(): given an array of nodes @arg, - * walks the path invoking all constructors and - * passing the corresponding parameter in order to - * continue the objects allocation. - * Once the test is reached, its function is executed. - * - * Since the machine and QEDGE_CONSUMED_BY nodes allocate - * memory in the constructor, g_test_queue_destroy is used so - * that after execution they can be safely free'd. The test's - * ->before callback is also welcome to use g_test_queue_destroy. - * - * Note: as specified in walk_path() too, @arg is an array of - * char *, where arg[0] is a pointer to the command line - * string that will be used to properly start QEMU when executing - * the test, and the remaining elements represent the actual objects - * that will be allocated. - * - * The order of execution is the following: - * 1) @before test function as defined in the given QOSGraphTestOptions - * 2) start QEMU - * 3) call all nodes constructor and get_driver/get_device depending on edge, - * start the hardware (*_device_enable functions) - * 4) start test - */ -static void run_one_test(const void *arg) -{ - QOSGraphNode *test_node; - QGuestAllocator *alloc = NULL; - void *obj; - char **path = (char **) arg; - GString *cmd_line = g_string_new(path[0]); - void *test_arg; - - /* Before test */ - current_path = path; - test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]); - test_arg = test_node->u.test.arg; - if (test_node->u.test.before) { - test_arg = test_node->u.test.before(cmd_line, test_arg); - } - - restart_qemu_or_continue(cmd_line->str); - g_string_free(cmd_line, true); - - obj = qos_allocate_objects(global_qtest, &alloc); - test_node->u.test.function(obj, test_arg, alloc); -} - -static void subprocess_run_one_test(const void *arg) -{ - const gchar *path = arg; - g_test_trap_subprocess(path, 0, 0); - g_test_trap_assert_passed(); -} - -/* - * in this function, 2 path will be built: - * path_str, a one-string path (ex "pc/i440FX-pcihost/...") - * path_vec, a string-array path (ex [0] = "pc", [1] = "i440FX-pcihost"). - * - * path_str will be only used to build the test name, and won't need the - * architecture name at beginning, since it will be added by qtest_add_func(). - * - * path_vec is used to allocate all constructors of the path nodes. - * Each name in this array except position 0 must correspond to a valid - * QOSGraphNode name. - * Position 0 is special, initially contains just the name of - * the node, (ex for "x86_64/pc" it will be "pc"), used to build the test - * path (see below). After it will contain the command line used to start - * qemu with all required devices. - * - * Note that the machine node name must be with format / - * (ex "x86_64/pc"), because it will identify the node "x86_64/pc" - * and start QEMU with "-M pc". For this reason, - * when building path_str, path_vec - * initially contains the at position 0 ("pc"), - * and the node name at position 1 (/) - * ("x86_64/pc"), followed by the rest of the nodes. - */ -static void walk_path(QOSGraphNode *orig_path, int len) -{ - QOSGraphNode *path; - QOSGraphEdge *edge; - - /* etype set to QEDGE_CONSUMED_BY so that machine can add to the command line */ - QOSEdgeType etype = QEDGE_CONSUMED_BY; - - /* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */ - char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2)); - int path_vec_size = 0; - - char *after_cmd, *before_cmd, *after_device; - GString *after_device_str = g_string_new(""); - char *node_name = orig_path->name, *path_str; - - GString *cmd_line = g_string_new(""); - GString *cmd_line2 = g_string_new(""); - - path = qos_graph_get_node(node_name); /* root */ - node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */ - - path_vec[path_vec_size++] = node_name; - path_vec[path_vec_size++] = qos_get_machine_type(node_name); - - for (;;) { - path = qos_graph_get_node(node_name); - if (!path->path_edge) { - break; - } - - node_name = qos_graph_edge_get_dest(path->path_edge); - - /* append node command line + previous edge command line */ - if (path->command_line && etype == QEDGE_CONSUMED_BY) { - g_string_append(cmd_line, path->command_line); - g_string_append(cmd_line, after_device_str->str); - g_string_truncate(after_device_str, 0); - } - - path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge); - /* detect if edge has command line args */ - after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge); - after_device = qos_graph_edge_get_extra_device_opts(path->path_edge); - before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge); - edge = qos_graph_get_edge(path->name, node_name); - etype = qos_graph_edge_get_type(edge); - - if (before_cmd) { - g_string_append(cmd_line, before_cmd); - } - if (after_cmd) { - g_string_append(cmd_line2, after_cmd); - } - if (after_device) { - g_string_append(after_device_str, after_device); - } - } - - path_vec[path_vec_size++] = NULL; - g_string_append(cmd_line, after_device_str->str); - g_string_free(after_device_str, true); - - g_string_append(cmd_line, cmd_line2->str); - g_string_free(cmd_line2, true); - - /* here position 0 has /, position 1 has . - * The path must not have the , qtest_add_data_func adds it. - */ - path_str = g_strjoinv("/", path_vec + 1); - - /* put arch/machine in position 1 so run_one_test can do its work - * and add the command line at position 0. - */ - path_vec[1] = path_vec[0]; - path_vec[0] = g_string_free(cmd_line, false); - - if (path->u.test.subprocess) { - gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess", - qtest_get_arch(), path_str); - qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test); - g_test_add_data_func(subprocess_path, path_vec, run_one_test); - } else { - qtest_add_data_func(path_str, path_vec, run_one_test); - } - - g_free(path_str); -} - - - -/** - * main(): heart of the qgraph framework. - * - * - Initializes the glib test framework - * - Creates the graph by invoking the various _init constructors - * - Starts QEMU to mark the available devices - * - Walks the graph, and each path is added to - * the glib test framework (walk_path) - * - Runs the tests, calling allocate_object() and allocating the - * machine/drivers/test objects - * - Cleans up everything - */ -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - qos_graph_init(); - module_call_init(MODULE_INIT_QOM); - module_call_init(MODULE_INIT_LIBQOS); - qos_set_machines_devices_available(); - - qos_graph_foreach_test_path(walk_path); - g_test_run(); - qtest_end(); - qos_graph_destroy(); - g_free(old_path); - return 0; -} diff --git a/tests/qtest/ac97-test.c b/tests/qtest/ac97-test.c new file mode 100644 index 0000000000..b084e31bff --- /dev/null +++ b/tests/qtest/ac97-test.c @@ -0,0 +1,57 @@ +/* + * QTest testcase for AC97 + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QAC97 QAC97; + +struct QAC97 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *ac97_get_driver(void *obj, const char *interface) +{ + QAC97 *ac97 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &ac97->dev; + } + + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); +} + +static void *ac97_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QAC97 *ac97 = g_new0(QAC97, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&ac97->dev, bus, addr); + ac97->obj.get_driver = ac97_get_driver; + return &ac97->obj; +} + +static void ac97_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("AC97", ac97_create); + qos_node_produces("AC97", "pci-device"); + qos_node_consumes("AC97", "pci-bus", &opts); +} + +libqos_init(ac97_register_nodes); diff --git a/tests/qtest/acpi-utils.c b/tests/qtest/acpi-utils.c new file mode 100644 index 0000000000..d2a202efca --- /dev/null +++ b/tests/qtest/acpi-utils.c @@ -0,0 +1,147 @@ +/* + * ACPI Utility Functions + * + * Copyright (c) 2013 Red Hat Inc. + * Copyright (c) 2017 Skyport Systems + * + * Authors: + * Michael S. Tsirkin , + * Ben Warren + * + * 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 +#include "qemu-common.h" +#include "qemu/bitmap.h" +#include "acpi-utils.h" +#include "boot-sector.h" + +uint8_t acpi_calc_checksum(const uint8_t *data, int len) +{ + int i; + uint8_t sum = 0; + + for (i = 0; i < len; i++) { + sum += data[i]; + } + + return sum; +} + +uint32_t acpi_find_rsdp_address(QTestState *qts) +{ + uint32_t off; + + /* RSDP location can vary across a narrow range */ + for (off = 0xf0000; off < 0x100000; off += 0x10) { + uint8_t sig[] = "RSD PTR "; + int i; + + for (i = 0; i < sizeof sig - 1; ++i) { + sig[i] = qtest_readb(qts, off + i); + } + + if (!memcmp(sig, "RSD PTR ", sizeof sig)) { + break; + } + } + return off; +} + +void acpi_fetch_rsdp_table(QTestState *qts, uint64_t addr, uint8_t *rsdp_table) +{ + uint8_t revision; + + /* Read mandatory revision 0 table data (20 bytes) first */ + qtest_memread(qts, addr, rsdp_table, 20); + revision = rsdp_table[15 /* Revision offset */]; + + switch (revision) { + case 0: /* ACPI 1.0 RSDP */ + break; + case 2: /* ACPI 2.0+ RSDP */ + /* Read the rest of the RSDP table */ + qtest_memread(qts, addr + 20, rsdp_table + 20, 16); + break; + default: + g_assert_not_reached(); + } + + 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, int addr_size, const char *sig, + bool verify_checksum) +{ + uint32_t len; + uint64_t addr = 0; + + g_assert(addr_size == 4 || addr_size == 8); + memcpy(&addr, addr_ptr , addr_size); + addr = le64_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)); + } +} + +#define GUID_SIZE 16 +static const uint8_t AcpiTestSupportGuid[GUID_SIZE] = { + 0xb1, 0xa6, 0x87, 0xab, + 0x34, 0x20, + 0xa0, 0xbd, + 0x71, 0xbd, 0x37, 0x50, 0x07, 0x75, 0x77, 0x85 }; + +typedef struct { + uint8_t signature_guid[GUID_SIZE]; + uint64_t rsdp10; + uint64_t rsdp20; +} __attribute__((packed)) UefiTestSupport; + +/* Wait at most 600 seconds (test is slow with TCG and --enable-debug) */ +#define TEST_DELAY (1 * G_USEC_PER_SEC / 10) +#define TEST_CYCLES MAX((600 * G_USEC_PER_SEC / TEST_DELAY), 1) +#define MB 0x100000ULL +uint64_t acpi_find_rsdp_address_uefi(QTestState *qts, uint64_t start, + uint64_t size) +{ + int i, j; + uint8_t data[GUID_SIZE]; + + for (i = 0; i < TEST_CYCLES; ++i) { + for (j = 0; j < size / MB; j++) { + /* look for GUID at every 1Mb block */ + uint64_t addr = start + j * MB; + + qtest_memread(qts, addr, data, sizeof(data)); + if (!memcmp(AcpiTestSupportGuid, data, sizeof(data))) { + UefiTestSupport ret; + + qtest_memread(qts, addr, &ret, sizeof(ret)); + ret.rsdp10 = le64_to_cpu(ret.rsdp10); + ret.rsdp20 = le64_to_cpu(ret.rsdp20); + return ret.rsdp20 ? ret.rsdp20 : ret.rsdp10; + } + } + g_usleep(TEST_DELAY); + } + g_assert_not_reached(); + return 0; +} diff --git a/tests/qtest/acpi-utils.h b/tests/qtest/acpi-utils.h new file mode 100644 index 0000000000..0c86780689 --- /dev/null +++ b/tests/qtest/acpi-utils.h @@ -0,0 +1,56 @@ +/* + * Utilities for working with ACPI tables + * + * Copyright (c) 2013 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin , + * + * 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 TEST_ACPI_UTILS_H +#define TEST_ACPI_UTILS_H + +#include "libqtest.h" + +/* DSDT and SSDTs format */ +typedef struct { + uint8_t *aml; /* aml bytecode from guest */ + uint32_t aml_len; + gchar *aml_file; + gchar *asl; /* asl code generated from aml */ + gsize asl_len; + gchar *asl_file; + bool tmp_files_retain; /* do not delete the temp asl/aml */ +} AcpiSdtTable; + +#define ACPI_ASSERT_CMP(actual, expected) do { \ + char ACPI_ASSERT_CMP_str[5] = {}; \ + memcpy(ACPI_ASSERT_CMP_str, &actual, 4); \ + g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ +} while (0) + +#define ACPI_ASSERT_CMP64(actual, expected) do { \ + char ACPI_ASSERT_CMP_str[9] = {}; \ + memcpy(ACPI_ASSERT_CMP_str, &actual, 8); \ + g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ +} 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); +uint64_t acpi_find_rsdp_address_uefi(QTestState *qts, uint64_t start, + uint64_t size); +void acpi_fetch_rsdp_table(QTestState *qts, uint64_t addr, uint8_t *rsdp_table); +void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len, + const uint8_t *addr_ptr, int addr_size, const char *sig, + bool verify_checksum); + +#endif /* TEST_ACPI_UTILS_H */ diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c new file mode 100644 index 0000000000..c8d42ceea0 --- /dev/null +++ b/tests/qtest/ahci-test.c @@ -0,0 +1,1954 @@ +/* + * AHCI test cases + * + * Copyright (c) 2014 John Snow + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include + +#include "libqtest.h" +#include "libqos/libqos-pc.h" +#include "libqos/ahci.h" +#include "libqos/pci-pc.h" + +#include "qemu-common.h" +#include "qapi/qmp/qdict.h" +#include "qemu/host-utils.h" + +#include "hw/pci/pci_ids.h" +#include "hw/pci/pci_regs.h" + +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(s, ...) qobject_unref(qtest_qmp(s, __VA_ARGS__)) + +/* Test images sizes in MB */ +#define TEST_IMAGE_SIZE_MB_LARGE (200 * 1024) +#define TEST_IMAGE_SIZE_MB_SMALL 64 + +/*** Globals ***/ +static char tmp_path[] = "/tmp/qtest.XXXXXX"; +static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; +static char mig_socket[] = "/tmp/qtest-migration.XXXXXX"; +static bool ahci_pedantic; +static const char *imgfmt; +static unsigned test_image_size_mb; + +/*** Function Declarations ***/ +static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port); +static void ahci_test_pci_spec(AHCIQState *ahci); +static void ahci_test_pci_caps(AHCIQState *ahci, uint16_t header, + uint8_t offset); +static void ahci_test_satacap(AHCIQState *ahci, uint8_t offset); +static void ahci_test_msicap(AHCIQState *ahci, uint8_t offset); +static void ahci_test_pmcap(AHCIQState *ahci, uint8_t offset); + +/*** Utilities ***/ + +static uint64_t mb_to_sectors(uint64_t image_size_mb) +{ + return (image_size_mb * 1024 * 1024) / AHCI_SECTOR_SIZE; +} + +static void string_bswap16(uint16_t *s, size_t bytes) +{ + g_assert_cmphex((bytes & 1), ==, 0); + bytes /= 2; + + while (bytes--) { + *s = bswap16(*s); + s++; + } +} + +/** + * Verify that the transfer did not corrupt our state at all. + */ +static void verify_state(AHCIQState *ahci, uint64_t hba_old) +{ + int i, j; + uint32_t ahci_fingerprint; + uint64_t hba_base; + AHCICommandHeader cmd; + + ahci_fingerprint = qpci_config_readl(ahci->dev, PCI_VENDOR_ID); + g_assert_cmphex(ahci_fingerprint, ==, ahci->fingerprint); + + /* If we haven't initialized, this is as much as can be validated. */ + if (!ahci->enabled) { + return; + } + + hba_base = (uint64_t)qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); + g_assert_cmphex(hba_base, ==, hba_old); + + g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP), ==, ahci->cap); + g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP2), ==, ahci->cap2); + + for (i = 0; i < 32; i++) { + g_assert_cmphex(ahci_px_rreg(ahci, i, AHCI_PX_FB), ==, + ahci->port[i].fb); + g_assert_cmphex(ahci_px_rreg(ahci, i, AHCI_PX_CLB), ==, + ahci->port[i].clb); + for (j = 0; j < 32; j++) { + ahci_get_command_header(ahci, i, j, &cmd); + g_assert_cmphex(cmd.prdtl, ==, ahci->port[i].prdtl[j]); + g_assert_cmphex(cmd.ctba, ==, ahci->port[i].ctba[j]); + } + } +} + +static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) +{ + QOSState *tmp = to->parent; + QPCIDevice *dev = to->dev; + char *uri_local = NULL; + uint64_t hba_old; + + if (uri == NULL) { + uri_local = g_strdup_printf("%s%s", "unix:", mig_socket); + uri = uri_local; + } + + hba_old = (uint64_t)qpci_config_readl(from->dev, PCI_BASE_ADDRESS_5); + + /* context will be 'to' after completion. */ + migrate(from->parent, to->parent, uri); + + /* We'd like for the AHCIState objects to still point + * to information specific to its specific parent + * instance, but otherwise just inherit the new data. */ + memcpy(to, from, sizeof(AHCIQState)); + to->parent = tmp; + to->dev = dev; + + tmp = from->parent; + dev = from->dev; + memset(from, 0x00, sizeof(AHCIQState)); + from->parent = tmp; + from->dev = dev; + + verify_state(to, hba_old); + g_free(uri_local); +} + +/*** Test Setup & Teardown ***/ + +/** + * Start a Q35 machine and bookmark a handle to the AHCI device. + */ +static AHCIQState *ahci_vboot(const char *cli, va_list ap) +{ + AHCIQState *s; + + s = g_new0(AHCIQState, 1); + s->parent = qtest_pc_vboot(cli, ap); + alloc_set_flags(&s->parent->alloc, ALLOC_LEAK_ASSERT); + + /* Verify that we have an AHCI device present. */ + s->dev = get_ahci_device(s->parent->qts, &s->fingerprint); + + return s; +} + +/** + * Start a Q35 machine and bookmark a handle to the AHCI device. + */ +static AHCIQState *ahci_boot(const char *cli, ...) +{ + AHCIQState *s; + va_list ap; + + if (cli) { + va_start(ap, cli); + s = ahci_vboot(cli, ap); + va_end(ap); + } else { + cli = "-drive if=none,id=drive0,file=%s,cache=writeback,format=%s" + " -M q35 " + "-device ide-hd,drive=drive0 " + "-global ide-hd.serial=%s " + "-global ide-hd.ver=%s"; + s = ahci_boot(cli, tmp_path, imgfmt, "testdisk", "version"); + } + + return s; +} + +/** + * Clean up the PCI device, then terminate the QEMU instance. + */ +static void ahci_shutdown(AHCIQState *ahci) +{ + QOSState *qs = ahci->parent; + + ahci_clean_mem(ahci); + free_ahci_device(ahci->dev); + g_free(ahci); + qtest_shutdown(qs); +} + +/** + * Boot and fully enable the HBA device. + * @see ahci_boot, ahci_pci_enable and ahci_hba_enable. + */ +static AHCIQState *ahci_boot_and_enable(const char *cli, ...) +{ + AHCIQState *ahci; + va_list ap; + uint16_t buff[256]; + uint8_t port; + uint8_t hello; + + if (cli) { + va_start(ap, cli); + ahci = ahci_vboot(cli, ap); + va_end(ap); + } else { + ahci = ahci_boot(NULL); + } + + ahci_pci_enable(ahci); + ahci_hba_enable(ahci); + /* Initialize test device */ + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + if (is_atapi(ahci, port)) { + hello = CMD_PACKET_ID; + } else { + hello = CMD_IDENTIFY; + } + ahci_io(ahci, port, hello, &buff, sizeof(buff), 0); + + return ahci; +} + +/*** Specification Adherence Tests ***/ + +/** + * Implementation for test_pci_spec. Ensures PCI configuration space is sane. + */ +static void ahci_test_pci_spec(AHCIQState *ahci) +{ + uint8_t datab; + uint16_t data; + uint32_t datal; + + /* Most of these bits should start cleared until we turn them on. */ + data = qpci_config_readw(ahci->dev, PCI_COMMAND); + ASSERT_BIT_CLEAR(data, PCI_COMMAND_MEMORY); + ASSERT_BIT_CLEAR(data, PCI_COMMAND_MASTER); + ASSERT_BIT_CLEAR(data, PCI_COMMAND_SPECIAL); /* Reserved */ + ASSERT_BIT_CLEAR(data, PCI_COMMAND_VGA_PALETTE); /* Reserved */ + ASSERT_BIT_CLEAR(data, PCI_COMMAND_PARITY); + ASSERT_BIT_CLEAR(data, PCI_COMMAND_WAIT); /* Reserved */ + ASSERT_BIT_CLEAR(data, PCI_COMMAND_SERR); + ASSERT_BIT_CLEAR(data, PCI_COMMAND_FAST_BACK); + ASSERT_BIT_CLEAR(data, PCI_COMMAND_INTX_DISABLE); + ASSERT_BIT_CLEAR(data, 0xF800); /* Reserved */ + + data = qpci_config_readw(ahci->dev, PCI_STATUS); + ASSERT_BIT_CLEAR(data, 0x01 | 0x02 | 0x04); /* Reserved */ + ASSERT_BIT_CLEAR(data, PCI_STATUS_INTERRUPT); + ASSERT_BIT_SET(data, PCI_STATUS_CAP_LIST); /* must be set */ + ASSERT_BIT_CLEAR(data, PCI_STATUS_UDF); /* Reserved */ + ASSERT_BIT_CLEAR(data, PCI_STATUS_PARITY); + ASSERT_BIT_CLEAR(data, PCI_STATUS_SIG_TARGET_ABORT); + ASSERT_BIT_CLEAR(data, PCI_STATUS_REC_TARGET_ABORT); + ASSERT_BIT_CLEAR(data, PCI_STATUS_REC_MASTER_ABORT); + ASSERT_BIT_CLEAR(data, PCI_STATUS_SIG_SYSTEM_ERROR); + ASSERT_BIT_CLEAR(data, PCI_STATUS_DETECTED_PARITY); + + /* RID occupies the low byte, CCs occupy the high three. */ + datal = qpci_config_readl(ahci->dev, PCI_CLASS_REVISION); + if (ahci_pedantic) { + /* AHCI 1.3 specifies that at-boot, the RID should reset to 0x00, + * Though in practice this is likely seldom true. */ + ASSERT_BIT_CLEAR(datal, 0xFF); + } + + /* BCC *must* equal 0x01. */ + g_assert_cmphex(PCI_BCC(datal), ==, 0x01); + if (PCI_SCC(datal) == 0x01) { + /* IDE */ + ASSERT_BIT_SET(0x80000000, datal); + ASSERT_BIT_CLEAR(0x60000000, datal); + } else if (PCI_SCC(datal) == 0x04) { + /* RAID */ + g_assert_cmphex(PCI_PI(datal), ==, 0); + } else if (PCI_SCC(datal) == 0x06) { + /* AHCI */ + g_assert_cmphex(PCI_PI(datal), ==, 0x01); + } else { + g_assert_not_reached(); + } + + datab = qpci_config_readb(ahci->dev, PCI_CACHE_LINE_SIZE); + g_assert_cmphex(datab, ==, 0); + + datab = qpci_config_readb(ahci->dev, PCI_LATENCY_TIMER); + g_assert_cmphex(datab, ==, 0); + + /* Only the bottom 7 bits must be off. */ + datab = qpci_config_readb(ahci->dev, PCI_HEADER_TYPE); + ASSERT_BIT_CLEAR(datab, 0x7F); + + /* BIST is optional, but the low 7 bits must always start off regardless. */ + datab = qpci_config_readb(ahci->dev, PCI_BIST); + ASSERT_BIT_CLEAR(datab, 0x7F); + + /* BARS 0-4 do not have a boot spec, but ABAR/BAR5 must be clean. */ + datal = qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); + g_assert_cmphex(datal, ==, 0); + + qpci_config_writel(ahci->dev, PCI_BASE_ADDRESS_5, 0xFFFFFFFF); + datal = qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); + /* ABAR must be 32-bit, memory mapped, non-prefetchable and + * must be >= 512 bytes. To that end, bits 0-8 must be off. */ + ASSERT_BIT_CLEAR(datal, 0xFF); + + /* Capability list MUST be present, */ + datal = qpci_config_readl(ahci->dev, PCI_CAPABILITY_LIST); + /* But these bits are reserved. */ + ASSERT_BIT_CLEAR(datal, ~0xFF); + g_assert_cmphex(datal, !=, 0); + + /* Check specification adherence for capability extenstions. */ + data = qpci_config_readw(ahci->dev, datal); + + switch (ahci->fingerprint) { + case AHCI_INTEL_ICH9: + /* Intel ICH9 Family Datasheet 14.1.19 p.550 */ + g_assert_cmphex((data & 0xFF), ==, PCI_CAP_ID_MSI); + break; + default: + /* AHCI 1.3, Section 2.1.14 -- CAP must point to PMCAP. */ + g_assert_cmphex((data & 0xFF), ==, PCI_CAP_ID_PM); + } + + ahci_test_pci_caps(ahci, data, (uint8_t)datal); + + /* Reserved. */ + datal = qpci_config_readl(ahci->dev, PCI_CAPABILITY_LIST + 4); + g_assert_cmphex(datal, ==, 0); + + /* IPIN might vary, but ILINE must be off. */ + datab = qpci_config_readb(ahci->dev, PCI_INTERRUPT_LINE); + g_assert_cmphex(datab, ==, 0); +} + +/** + * Test PCI capabilities for AHCI specification adherence. + */ +static void ahci_test_pci_caps(AHCIQState *ahci, uint16_t header, + uint8_t offset) +{ + uint8_t cid = header & 0xFF; + uint8_t next = header >> 8; + + g_test_message("CID: %02x; next: %02x", cid, next); + + switch (cid) { + case PCI_CAP_ID_PM: + ahci_test_pmcap(ahci, offset); + break; + case PCI_CAP_ID_MSI: + ahci_test_msicap(ahci, offset); + break; + case PCI_CAP_ID_SATA: + ahci_test_satacap(ahci, offset); + break; + + default: + g_test_message("Unknown CAP 0x%02x", cid); + } + + if (next) { + ahci_test_pci_caps(ahci, qpci_config_readw(ahci->dev, next), next); + } +} + +/** + * Test SATA PCI capabilitity for AHCI specification adherence. + */ +static void ahci_test_satacap(AHCIQState *ahci, uint8_t offset) +{ + uint16_t dataw; + uint32_t datal; + + g_test_message("Verifying SATACAP"); + + /* Assert that the SATACAP version is 1.0, And reserved bits are empty. */ + dataw = qpci_config_readw(ahci->dev, offset + 2); + g_assert_cmphex(dataw, ==, 0x10); + + /* Grab the SATACR1 register. */ + datal = qpci_config_readw(ahci->dev, offset + 4); + + switch (datal & 0x0F) { + case 0x04: /* BAR0 */ + case 0x05: /* BAR1 */ + case 0x06: + case 0x07: + case 0x08: + case 0x09: /* BAR5 */ + case 0x0F: /* Immediately following SATACR1 in PCI config space. */ + break; + default: + /* Invalid BARLOC for the Index Data Pair. */ + g_assert_not_reached(); + } + + /* Reserved. */ + g_assert_cmphex((datal >> 24), ==, 0x00); +} + +/** + * Test MSI PCI capability for AHCI specification adherence. + */ +static void ahci_test_msicap(AHCIQState *ahci, uint8_t offset) +{ + uint16_t dataw; + uint32_t datal; + + g_test_message("Verifying MSICAP"); + + dataw = qpci_config_readw(ahci->dev, offset + PCI_MSI_FLAGS); + ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_ENABLE); + ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_QSIZE); + ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_RESERVED); + + datal = qpci_config_readl(ahci->dev, offset + PCI_MSI_ADDRESS_LO); + g_assert_cmphex(datal, ==, 0); + + if (dataw & PCI_MSI_FLAGS_64BIT) { + g_test_message("MSICAP is 64bit"); + datal = qpci_config_readl(ahci->dev, offset + PCI_MSI_ADDRESS_HI); + g_assert_cmphex(datal, ==, 0); + dataw = qpci_config_readw(ahci->dev, offset + PCI_MSI_DATA_64); + g_assert_cmphex(dataw, ==, 0); + } else { + g_test_message("MSICAP is 32bit"); + dataw = qpci_config_readw(ahci->dev, offset + PCI_MSI_DATA_32); + g_assert_cmphex(dataw, ==, 0); + } +} + +/** + * Test Power Management PCI capability for AHCI specification adherence. + */ +static void ahci_test_pmcap(AHCIQState *ahci, uint8_t offset) +{ + uint16_t dataw; + + g_test_message("Verifying PMCAP"); + + dataw = qpci_config_readw(ahci->dev, offset + PCI_PM_PMC); + ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_PME_CLOCK); + ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_RESERVED); + ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_D1); + ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_D2); + + dataw = qpci_config_readw(ahci->dev, offset + PCI_PM_CTRL); + ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_STATE_MASK); + ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_RESERVED); + ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_DATA_SEL_MASK); + ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_DATA_SCALE_MASK); +} + +static void ahci_test_hba_spec(AHCIQState *ahci) +{ + unsigned i; + uint32_t reg; + uint32_t ports; + uint8_t nports_impl; + uint8_t maxports; + + g_assert(ahci != NULL); + + /* + * Note that the AHCI spec does expect the BIOS to set up a few things: + * CAP.SSS - Support for staggered spin-up (t/f) + * CAP.SMPS - Support for mechanical presence switches (t/f) + * PI - Ports Implemented (1-32) + * PxCMD.HPCP - Hot Plug Capable Port + * PxCMD.MPSP - Mechanical Presence Switch Present + * PxCMD.CPD - Cold Presence Detection support + * + * Additional items are touched if CAP.SSS is on, see AHCI 10.1.1 p.97: + * Foreach Port Implemented: + * -PxCMD.ST, PxCMD.CR, PxCMD.FRE, PxCMD.FR, PxSCTL.DET are 0 + * -PxCLB/U and PxFB/U are set to valid regions in memory + * -PxSUD is set to 1. + * -PxSSTS.DET is polled for presence; if detected, we continue: + * -PxSERR is cleared with 1's. + * -If PxTFD.STS.BSY, PxTFD.STS.DRQ, and PxTFD.STS.ERR are all zero, + * the device is ready. + */ + + /* 1 CAP - Capabilities Register */ + ahci->cap = ahci_rreg(ahci, AHCI_CAP); + ASSERT_BIT_CLEAR(ahci->cap, AHCI_CAP_RESERVED); + + /* 2 GHC - Global Host Control */ + reg = ahci_rreg(ahci, AHCI_GHC); + ASSERT_BIT_CLEAR(reg, AHCI_GHC_HR); + ASSERT_BIT_CLEAR(reg, AHCI_GHC_IE); + ASSERT_BIT_CLEAR(reg, AHCI_GHC_MRSM); + if (BITSET(ahci->cap, AHCI_CAP_SAM)) { + g_test_message("Supports AHCI-Only Mode: GHC_AE is Read-Only."); + ASSERT_BIT_SET(reg, AHCI_GHC_AE); + } else { + g_test_message("Supports AHCI/Legacy mix."); + ASSERT_BIT_CLEAR(reg, AHCI_GHC_AE); + } + + /* 3 IS - Interrupt Status */ + reg = ahci_rreg(ahci, AHCI_IS); + g_assert_cmphex(reg, ==, 0); + + /* 4 PI - Ports Implemented */ + ports = ahci_rreg(ahci, AHCI_PI); + /* Ports Implemented must be non-zero. */ + g_assert_cmphex(ports, !=, 0); + /* Ports Implemented must be <= Number of Ports. */ + nports_impl = ctpopl(ports); + g_assert_cmpuint(((AHCI_CAP_NP & ahci->cap) + 1), >=, nports_impl); + + /* Ports must be within the proper range. Given a mapping of SIZE, + * 256 bytes are used for global HBA control, and the rest is used + * for ports data, at 0x80 bytes each. */ + g_assert_cmphex(ahci->barsize, >, 0); + maxports = (ahci->barsize - HBA_DATA_REGION_SIZE) / HBA_PORT_DATA_SIZE; + /* e.g, 30 ports for 4K of memory. (4096 - 256) / 128 = 30 */ + g_assert_cmphex((reg >> maxports), ==, 0); + + /* 5 AHCI Version */ + reg = ahci_rreg(ahci, AHCI_VS); + switch (reg) { + case AHCI_VERSION_0_95: + case AHCI_VERSION_1_0: + case AHCI_VERSION_1_1: + case AHCI_VERSION_1_2: + case AHCI_VERSION_1_3: + break; + default: + g_assert_not_reached(); + } + + /* 6 Command Completion Coalescing Control: depends on CAP.CCCS. */ + reg = ahci_rreg(ahci, AHCI_CCCCTL); + if (BITSET(ahci->cap, AHCI_CAP_CCCS)) { + ASSERT_BIT_CLEAR(reg, AHCI_CCCCTL_EN); + ASSERT_BIT_CLEAR(reg, AHCI_CCCCTL_RESERVED); + ASSERT_BIT_SET(reg, AHCI_CCCCTL_CC); + ASSERT_BIT_SET(reg, AHCI_CCCCTL_TV); + } else { + g_assert_cmphex(reg, ==, 0); + } + + /* 7 CCC_PORTS */ + reg = ahci_rreg(ahci, AHCI_CCCPORTS); + /* Must be zeroes initially regardless of CAP.CCCS */ + g_assert_cmphex(reg, ==, 0); + + /* 8 EM_LOC */ + reg = ahci_rreg(ahci, AHCI_EMLOC); + if (BITCLR(ahci->cap, AHCI_CAP_EMS)) { + g_assert_cmphex(reg, ==, 0); + } + + /* 9 EM_CTL */ + reg = ahci_rreg(ahci, AHCI_EMCTL); + if (BITSET(ahci->cap, AHCI_CAP_EMS)) { + ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_STSMR); + ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_CTLTM); + ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_CTLRST); + ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_RESERVED); + } else { + g_assert_cmphex(reg, ==, 0); + } + + /* 10 CAP2 -- Capabilities Extended */ + ahci->cap2 = ahci_rreg(ahci, AHCI_CAP2); + ASSERT_BIT_CLEAR(ahci->cap2, AHCI_CAP2_RESERVED); + + /* 11 BOHC -- Bios/OS Handoff Control */ + reg = ahci_rreg(ahci, AHCI_BOHC); + g_assert_cmphex(reg, ==, 0); + + /* 12 -- 23: Reserved */ + g_test_message("Verifying HBA reserved area is empty."); + for (i = AHCI_RESERVED; i < AHCI_NVMHCI; ++i) { + reg = ahci_rreg(ahci, i); + g_assert_cmphex(reg, ==, 0); + } + + /* 24 -- 39: NVMHCI */ + if (BITCLR(ahci->cap2, AHCI_CAP2_NVMP)) { + g_test_message("Verifying HBA/NVMHCI area is empty."); + for (i = AHCI_NVMHCI; i < AHCI_VENDOR; ++i) { + reg = ahci_rreg(ahci, i); + g_assert_cmphex(reg, ==, 0); + } + } + + /* 40 -- 63: Vendor */ + g_test_message("Verifying HBA/Vendor area is empty."); + for (i = AHCI_VENDOR; i < AHCI_PORTS; ++i) { + reg = ahci_rreg(ahci, i); + g_assert_cmphex(reg, ==, 0); + } + + /* 64 -- XX: Port Space */ + for (i = 0; ports || (i < maxports); ports >>= 1, ++i) { + if (BITSET(ports, 0x1)) { + g_test_message("Testing port %u for spec", i); + ahci_test_port_spec(ahci, i); + } else { + uint16_t j; + uint16_t low = AHCI_PORTS + (32 * i); + uint16_t high = AHCI_PORTS + (32 * (i + 1)); + g_test_message("Asserting unimplemented port %u " + "(reg [%u-%u]) is empty.", + i, low, high - 1); + for (j = low; j < high; ++j) { + reg = ahci_rreg(ahci, j); + g_assert_cmphex(reg, ==, 0); + } + } + } +} + +/** + * Test the memory space for one port for specification adherence. + */ +static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port) +{ + uint32_t reg; + unsigned i; + + /* (0) CLB */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_CLB); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CLB_RESERVED); + + /* (1) CLBU */ + if (BITCLR(ahci->cap, AHCI_CAP_S64A)) { + reg = ahci_px_rreg(ahci, port, AHCI_PX_CLBU); + g_assert_cmphex(reg, ==, 0); + } + + /* (2) FB */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_FB); + ASSERT_BIT_CLEAR(reg, AHCI_PX_FB_RESERVED); + + /* (3) FBU */ + if (BITCLR(ahci->cap, AHCI_CAP_S64A)) { + reg = ahci_px_rreg(ahci, port, AHCI_PX_FBU); + g_assert_cmphex(reg, ==, 0); + } + + /* (4) IS */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); + g_assert_cmphex(reg, ==, 0); + + /* (5) IE */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_IE); + g_assert_cmphex(reg, ==, 0); + + /* (6) CMD */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_CMD); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FRE); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_RESERVED); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CCS); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FR); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_PMA); /* And RW only if CAP.SPM */ + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_APSTE); /* RW only if CAP2.APST */ + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ATAPI); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_DLAE); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ALPE); /* RW only if CAP.SALP */ + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ASP); /* RW only if CAP.SALP */ + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_ICC); + /* If CPDetect support does not exist, CPState must be off. */ + if (BITCLR(reg, AHCI_PX_CMD_CPD)) { + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CPS); + } + /* If MPSPresence is not set, MPSState must be off. */ + if (BITCLR(reg, AHCI_PX_CMD_MPSP)) { + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSS); + } + /* If we do not support MPS, MPSS and MPSP must be off. */ + if (BITCLR(ahci->cap, AHCI_CAP_SMPS)) { + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSS); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSP); + } + /* If, via CPD or MPSP we detect a drive, HPCP must be on. */ + if (BITANY(reg, AHCI_PX_CMD_CPD | AHCI_PX_CMD_MPSP)) { + ASSERT_BIT_SET(reg, AHCI_PX_CMD_HPCP); + } + /* HPCP and ESP cannot both be active. */ + g_assert(!BITSET(reg, AHCI_PX_CMD_HPCP | AHCI_PX_CMD_ESP)); + /* If CAP.FBSS is not set, FBSCP must not be set. */ + if (BITCLR(ahci->cap, AHCI_CAP_FBSS)) { + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FBSCP); + } + + /* (7) RESERVED */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_RES1); + g_assert_cmphex(reg, ==, 0); + + /* (8) TFD */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); + /* At boot, prior to an FIS being received, the TFD register should be 0x7F, + * which breaks down as follows, as seen in AHCI 1.3 sec 3.3.8, p. 27. */ + ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR); + ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_CS1); + ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_DRQ); + ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_CS2); + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_BSY); + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR); + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_RESERVED); + + /* (9) SIG */ + /* Though AHCI specifies the boot value should be 0xFFFFFFFF, + * Even when GHC.ST is zero, the AHCI HBA may receive the initial + * D2H register FIS and update the signature asynchronously, + * so we cannot expect a value here. AHCI 1.3, sec 3.3.9, pp 27-28 */ + + /* (10) SSTS / SCR0: SStatus */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_SSTS); + ASSERT_BIT_CLEAR(reg, AHCI_PX_SSTS_RESERVED); + /* Even though the register should be 0 at boot, it is asynchronous and + * prone to change, so we cannot test any well known value. */ + + /* (11) SCTL / SCR2: SControl */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_SCTL); + g_assert_cmphex(reg, ==, 0); + + /* (12) SERR / SCR1: SError */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR); + g_assert_cmphex(reg, ==, 0); + + /* (13) SACT / SCR3: SActive */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_SACT); + g_assert_cmphex(reg, ==, 0); + + /* (14) CI */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_CI); + g_assert_cmphex(reg, ==, 0); + + /* (15) SNTF */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_SNTF); + g_assert_cmphex(reg, ==, 0); + + /* (16) FBS */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_FBS); + ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_EN); + ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DEC); + ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_SDE); + ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DEV); + ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DWE); + ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_RESERVED); + if (BITSET(ahci->cap, AHCI_CAP_FBSS)) { + /* if Port-Multiplier FIS-based switching avail, ADO must >= 2 */ + g_assert((reg & AHCI_PX_FBS_ADO) >> ctzl(AHCI_PX_FBS_ADO) >= 2); + } + + /* [17 -- 27] RESERVED */ + for (i = AHCI_PX_RES2; i < AHCI_PX_VS; ++i) { + reg = ahci_px_rreg(ahci, port, i); + g_assert_cmphex(reg, ==, 0); + } + + /* [28 -- 31] Vendor-Specific */ + for (i = AHCI_PX_VS; i < 32; ++i) { + reg = ahci_px_rreg(ahci, port, i); + if (reg) { + g_test_message("INFO: Vendor register %u non-empty", i); + } + } +} + +/** + * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first + * device we see, then read and check the response. + */ +static void ahci_test_identify(AHCIQState *ahci) +{ + uint16_t buff[256]; + unsigned px; + int rc; + uint16_t sect_size; + const size_t buffsize = 512; + + g_assert(ahci != NULL); + + /** + * This serves as a bit of a tutorial on AHCI device programming: + * + * (1) Create a data buffer for the IDENTIFY response to be sent to + * (2) Create a Command Table buffer, where we will store the + * command and PRDT (Physical Region Descriptor Table) + * (3) Construct an FIS host-to-device command structure, and write it to + * the top of the Command Table buffer. + * (4) Create one or more Physical Region Descriptors (PRDs) that describe + * a location in memory where data may be stored/retrieved. + * (5) Write these PRDTs to the bottom (offset 0x80) of the Command Table. + * (6) Each AHCI port has up to 32 command slots. Each slot contains a + * header that points to a Command Table buffer. Pick an unused slot + * and update it to point to the Command Table we have built. + * (7) Now: Command #n points to our Command Table, and our Command Table + * contains the FIS (that describes our command) and the PRDTL, which + * describes our buffer. + * (8) We inform the HBA via PxCI (Command Issue) that the command in slot + * #n is ready for processing. + */ + + /* Pick the first implemented and running port */ + px = ahci_port_select(ahci); + g_test_message("Selected port %u for test", px); + + /* Clear out the FIS Receive area and any pending interrupts. */ + ahci_port_clear(ahci, px); + + /* "Read" 512 bytes using CMD_IDENTIFY into the host buffer. */ + ahci_io(ahci, px, CMD_IDENTIFY, &buff, buffsize, 0); + + /* Check serial number/version in the buffer */ + /* NB: IDENTIFY strings are packed in 16bit little endian chunks. + * Since we copy byte-for-byte in ahci-test, on both LE and BE, we need to + * unchunk this data. By contrast, ide-test copies 2 bytes at a time, and + * as a consequence, only needs to unchunk the data on LE machines. */ + string_bswap16(&buff[10], 20); + rc = memcmp(&buff[10], "testdisk ", 20); + g_assert_cmphex(rc, ==, 0); + + string_bswap16(&buff[23], 8); + rc = memcmp(&buff[23], "version ", 8); + g_assert_cmphex(rc, ==, 0); + + sect_size = le16_to_cpu(*((uint16_t *)(&buff[5]))); + g_assert_cmphex(sect_size, ==, AHCI_SECTOR_SIZE); +} + +static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, + uint64_t sector, uint8_t read_cmd, + uint8_t write_cmd) +{ + uint64_t ptr; + uint8_t port; + unsigned char *tx = g_malloc(bufsize); + unsigned char *rx = g_malloc0(bufsize); + + g_assert(ahci != NULL); + + /* Pick the first running port and clear it. */ + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + /*** Create pattern and transfer to guest ***/ + /* Data buffer in the guest */ + ptr = ahci_alloc(ahci, bufsize); + g_assert(ptr); + + /* Write some indicative pattern to our buffer. */ + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); + qtest_bufwrite(ahci->parent->qts, ptr, tx, bufsize); + + /* Write this buffer to disk, then read it back to the DMA buffer. */ + ahci_guest_io(ahci, port, write_cmd, ptr, bufsize, sector); + qtest_memset(ahci->parent->qts, ptr, 0x00, bufsize); + ahci_guest_io(ahci, port, read_cmd, ptr, bufsize, sector); + + /*** Read back the Data ***/ + qtest_bufread(ahci->parent->qts, ptr, rx, bufsize); + g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); + + ahci_free(ahci, ptr); + g_free(tx); + g_free(rx); +} + +static uint8_t ahci_test_nondata(AHCIQState *ahci, uint8_t ide_cmd) +{ + uint8_t port; + + /* Sanitize */ + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + ahci_io(ahci, port, ide_cmd, NULL, 0, 0); + + return port; +} + +static void ahci_test_flush(AHCIQState *ahci) +{ + ahci_test_nondata(ahci, CMD_FLUSH_CACHE); +} + +static void ahci_test_max(AHCIQState *ahci) +{ + RegD2HFIS *d2h = g_malloc0(0x20); + uint64_t nsect; + uint8_t port; + uint8_t cmd; + uint64_t config_sect = mb_to_sectors(test_image_size_mb) - 1; + + if (config_sect > 0xFFFFFF) { + cmd = CMD_READ_MAX_EXT; + } else { + cmd = CMD_READ_MAX; + } + + port = ahci_test_nondata(ahci, cmd); + qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x40, d2h, 0x20); + nsect = (uint64_t)d2h->lba_hi[2] << 40 | + (uint64_t)d2h->lba_hi[1] << 32 | + (uint64_t)d2h->lba_hi[0] << 24 | + (uint64_t)d2h->lba_lo[2] << 16 | + (uint64_t)d2h->lba_lo[1] << 8 | + (uint64_t)d2h->lba_lo[0]; + + g_assert_cmphex(nsect, ==, config_sect); + g_free(d2h); +} + + +/******************************************************************************/ +/* Test Interfaces */ +/******************************************************************************/ + +/** + * Basic sanity test to boot a machine, find an AHCI device, and shutdown. + */ +static void test_sanity(void) +{ + AHCIQState *ahci; + ahci = ahci_boot(NULL); + ahci_shutdown(ahci); +} + +/** + * Ensure that the PCI configuration space for the AHCI device is in-line with + * the AHCI 1.3 specification for initial values. + */ +static void test_pci_spec(void) +{ + AHCIQState *ahci; + ahci = ahci_boot(NULL); + ahci_test_pci_spec(ahci); + ahci_shutdown(ahci); +} + +/** + * Engage the PCI AHCI device and sanity check the response. + * Perform additional PCI config space bringup for the HBA. + */ +static void test_pci_enable(void) +{ + AHCIQState *ahci; + ahci = ahci_boot(NULL); + ahci_pci_enable(ahci); + ahci_shutdown(ahci); +} + +/** + * Investigate the memory mapped regions of the HBA, + * and test them for AHCI specification adherence. + */ +static void test_hba_spec(void) +{ + AHCIQState *ahci; + + ahci = ahci_boot(NULL); + ahci_pci_enable(ahci); + ahci_test_hba_spec(ahci); + ahci_shutdown(ahci); +} + +/** + * Engage the HBA functionality of the AHCI PCI device, + * and bring it into a functional idle state. + */ +static void test_hba_enable(void) +{ + AHCIQState *ahci; + + ahci = ahci_boot(NULL); + ahci_pci_enable(ahci); + ahci_hba_enable(ahci); + ahci_shutdown(ahci); +} + +/** + * Bring up the device and issue an IDENTIFY command. + * Inspect the state of the HBA device and the data returned. + */ +static void test_identify(void) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(NULL); + ahci_test_identify(ahci); + ahci_shutdown(ahci); +} + +/** + * Fragmented DMA test: Perform a standard 4K DMA read/write + * test, but make sure the physical regions are fragmented to + * be very small, each just 32 bytes, to see how AHCI performs + * with chunks defined to be much less than a sector. + */ +static void test_dma_fragmented(void) +{ + AHCIQState *ahci; + AHCICommand *cmd; + uint8_t px; + size_t bufsize = 4096; + unsigned char *tx = g_malloc(bufsize); + unsigned char *rx = g_malloc0(bufsize); + uint64_t ptr; + + ahci = ahci_boot_and_enable(NULL); + px = ahci_port_select(ahci); + ahci_port_clear(ahci, px); + + /* create pattern */ + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); + + /* Create a DMA buffer in guest memory, and write our pattern to it. */ + ptr = guest_alloc(&ahci->parent->alloc, bufsize); + g_assert(ptr); + qtest_bufwrite(ahci->parent->qts, ptr, tx, bufsize); + + cmd = ahci_command_create(CMD_WRITE_DMA); + ahci_command_adjust(cmd, 0, ptr, bufsize, 32); + ahci_command_commit(ahci, cmd, px); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + ahci_command_free(cmd); + + cmd = ahci_command_create(CMD_READ_DMA); + ahci_command_adjust(cmd, 0, ptr, bufsize, 32); + ahci_command_commit(ahci, cmd, px); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + ahci_command_free(cmd); + + /* Read back the guest's receive buffer into local memory */ + qtest_bufread(ahci->parent->qts, ptr, rx, bufsize); + guest_free(&ahci->parent->alloc, ptr); + + g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); + + ahci_shutdown(ahci); + + g_free(rx); + g_free(tx); +} + +/* + * Write sector 1 with random data to make AHCI storage dirty + * Needed for flush tests so that flushes actually go though the block layer + */ +static void make_dirty(AHCIQState* ahci, uint8_t port) +{ + uint64_t ptr; + unsigned bufsize = 512; + + ptr = ahci_alloc(ahci, bufsize); + g_assert(ptr); + + ahci_guest_io(ahci, port, CMD_WRITE_DMA, ptr, bufsize, 1); + ahci_free(ahci, ptr); +} + +static void test_flush(void) +{ + AHCIQState *ahci; + uint8_t port; + + ahci = ahci_boot_and_enable(NULL); + + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + make_dirty(ahci, port); + + ahci_test_flush(ahci); + ahci_shutdown(ahci); +} + +static void test_flush_retry(void) +{ + AHCIQState *ahci; + AHCICommand *cmd; + uint8_t port; + + prepare_blkdebug_script(debug_path, "flush_to_disk"); + ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," + "format=%s,cache=writeback," + "rerror=stop,werror=stop " + "-M q35 " + "-device ide-hd,drive=drive0 ", + debug_path, + tmp_path, imgfmt); + + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + /* Issue write so that flush actually goes to disk */ + make_dirty(ahci, port); + + /* Issue Flush Command and wait for error */ + cmd = ahci_guest_io_halt(ahci, port, CMD_FLUSH_CACHE, 0, 0, 0); + ahci_guest_io_resume(ahci, cmd); + + ahci_shutdown(ahci); +} + +/** + * Basic sanity test to boot a machine, find an AHCI device, and shutdown. + */ +static void test_migrate_sanity(void) +{ + AHCIQState *src, *dst; + char *uri = g_strdup_printf("unix:%s", mig_socket); + + src = ahci_boot("-m 384 -M q35 " + "-drive if=ide,file=%s,format=%s ", tmp_path, imgfmt); + dst = ahci_boot("-m 384 -M q35 " + "-drive if=ide,file=%s,format=%s " + "-incoming %s", tmp_path, imgfmt, uri); + + ahci_migrate(src, dst, uri); + + ahci_shutdown(src); + ahci_shutdown(dst); + g_free(uri); +} + +/** + * Simple migration test: Write a pattern, migrate, then read. + */ +static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write) +{ + AHCIQState *src, *dst; + uint8_t px; + size_t bufsize = 4096; + unsigned char *tx = g_malloc(bufsize); + unsigned char *rx = g_malloc0(bufsize); + char *uri = g_strdup_printf("unix:%s", mig_socket); + + src = ahci_boot_and_enable("-m 384 -M q35 " + "-drive if=ide,format=%s,file=%s ", + imgfmt, tmp_path); + dst = ahci_boot("-m 384 -M q35 " + "-drive if=ide,format=%s,file=%s " + "-incoming %s", imgfmt, tmp_path, uri); + + /* initialize */ + px = ahci_port_select(src); + ahci_port_clear(src, px); + + /* create pattern */ + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); + + /* Write, migrate, then read. */ + ahci_io(src, px, cmd_write, tx, bufsize, 0); + ahci_migrate(src, dst, uri); + ahci_io(dst, px, cmd_read, rx, bufsize, 0); + + /* Verify pattern */ + g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); + + ahci_shutdown(src); + ahci_shutdown(dst); + g_free(rx); + g_free(tx); + g_free(uri); +} + +static void test_migrate_dma(void) +{ + ahci_migrate_simple(CMD_READ_DMA, CMD_WRITE_DMA); +} + +static void test_migrate_ncq(void) +{ + ahci_migrate_simple(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED); +} + +/** + * Halted IO Error Test + * + * Simulate an error on first write, Try to write a pattern, + * Confirm the VM has stopped, resume the VM, verify command + * has completed, then read back the data and verify. + */ +static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write) +{ + AHCIQState *ahci; + uint8_t port; + size_t bufsize = 4096; + unsigned char *tx = g_malloc(bufsize); + unsigned char *rx = g_malloc0(bufsize); + uint64_t ptr; + AHCICommand *cmd; + + prepare_blkdebug_script(debug_path, "write_aio"); + + ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," + "format=%s,cache=writeback," + "rerror=stop,werror=stop " + "-M q35 " + "-device ide-hd,drive=drive0 ", + debug_path, + tmp_path, imgfmt); + + /* Initialize and prepare */ + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + /* create DMA source buffer and write pattern */ + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); + ptr = ahci_alloc(ahci, bufsize); + g_assert(ptr); + qtest_memwrite(ahci->parent->qts, ptr, tx, bufsize); + + /* Attempt to write (and fail) */ + cmd = ahci_guest_io_halt(ahci, port, cmd_write, + ptr, bufsize, 0); + + /* Attempt to resume the command */ + ahci_guest_io_resume(ahci, cmd); + ahci_free(ahci, ptr); + + /* Read back and verify */ + ahci_io(ahci, port, cmd_read, rx, bufsize, 0); + g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); + + /* Cleanup and go home */ + ahci_shutdown(ahci); + g_free(rx); + g_free(tx); +} + +static void test_halted_dma(void) +{ + ahci_halted_io_test(CMD_READ_DMA, CMD_WRITE_DMA); +} + +static void test_halted_ncq(void) +{ + ahci_halted_io_test(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED); +} + +/** + * IO Error Migration Test + * + * Simulate an error on first write, Try to write a pattern, + * Confirm the VM has stopped, migrate, resume the VM, + * verify command has completed, then read back the data and verify. + */ +static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write) +{ + AHCIQState *src, *dst; + uint8_t port; + size_t bufsize = 4096; + unsigned char *tx = g_malloc(bufsize); + unsigned char *rx = g_malloc0(bufsize); + uint64_t ptr; + AHCICommand *cmd; + char *uri = g_strdup_printf("unix:%s", mig_socket); + + prepare_blkdebug_script(debug_path, "write_aio"); + + src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," + "format=%s,cache=writeback," + "rerror=stop,werror=stop " + "-M q35 " + "-device ide-hd,drive=drive0 ", + debug_path, + tmp_path, imgfmt); + + dst = ahci_boot("-drive file=%s,if=none,id=drive0," + "format=%s,cache=writeback," + "rerror=stop,werror=stop " + "-M q35 " + "-device ide-hd,drive=drive0 " + "-incoming %s", + tmp_path, imgfmt, uri); + + /* Initialize and prepare */ + port = ahci_port_select(src); + ahci_port_clear(src, port); + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); + + /* create DMA source buffer and write pattern */ + ptr = ahci_alloc(src, bufsize); + g_assert(ptr); + qtest_memwrite(src->parent->qts, ptr, tx, bufsize); + + /* Write, trigger the VM to stop, migrate, then resume. */ + cmd = ahci_guest_io_halt(src, port, cmd_write, + ptr, bufsize, 0); + ahci_migrate(src, dst, uri); + ahci_guest_io_resume(dst, cmd); + ahci_free(dst, ptr); + + /* Read back */ + ahci_io(dst, port, cmd_read, rx, bufsize, 0); + + /* Verify TX and RX are identical */ + g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); + + /* Cleanup and go home. */ + ahci_shutdown(src); + ahci_shutdown(dst); + g_free(rx); + g_free(tx); + g_free(uri); +} + +static void test_migrate_halted_dma(void) +{ + ahci_migrate_halted_io(CMD_READ_DMA, CMD_WRITE_DMA); +} + +static void test_migrate_halted_ncq(void) +{ + ahci_migrate_halted_io(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED); +} + +/** + * Migration test: Try to flush, migrate, then resume. + */ +static void test_flush_migrate(void) +{ + AHCIQState *src, *dst; + AHCICommand *cmd; + uint8_t px; + char *uri = g_strdup_printf("unix:%s", mig_socket); + + prepare_blkdebug_script(debug_path, "flush_to_disk"); + + src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," + "cache=writeback,rerror=stop,werror=stop," + "format=%s " + "-M q35 " + "-device ide-hd,drive=drive0 ", + debug_path, tmp_path, imgfmt); + dst = ahci_boot("-drive file=%s,if=none,id=drive0," + "cache=writeback,rerror=stop,werror=stop," + "format=%s " + "-M q35 " + "-device ide-hd,drive=drive0 " + "-incoming %s", tmp_path, imgfmt, uri); + + px = ahci_port_select(src); + ahci_port_clear(src, px); + + /* Dirty device so that flush reaches disk */ + make_dirty(src, px); + + /* Issue Flush Command */ + cmd = ahci_command_create(CMD_FLUSH_CACHE); + ahci_command_commit(src, cmd, px); + ahci_command_issue_async(src, cmd); + qtest_qmp_eventwait(src->parent->qts, "STOP"); + + /* Migrate over */ + ahci_migrate(src, dst, uri); + + /* Complete the command */ + qtest_qmp_send(dst->parent->qts, "{'execute':'cont' }"); + qtest_qmp_eventwait(dst->parent->qts, "RESUME"); + ahci_command_wait(dst, cmd); + ahci_command_verify(dst, cmd); + + ahci_command_free(cmd); + ahci_shutdown(src); + ahci_shutdown(dst); + g_free(uri); +} + +static void test_max(void) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(NULL); + ahci_test_max(ahci); + ahci_shutdown(ahci); +} + +static void test_reset(void) +{ + AHCIQState *ahci; + int i; + + ahci = ahci_boot(NULL); + ahci_test_pci_spec(ahci); + ahci_pci_enable(ahci); + + for (i = 0; i < 2; i++) { + ahci_test_hba_spec(ahci); + ahci_hba_enable(ahci); + ahci_test_identify(ahci); + ahci_test_io_rw_simple(ahci, 4096, 0, + CMD_READ_DMA_EXT, + CMD_WRITE_DMA_EXT); + ahci_set(ahci, AHCI_GHC, AHCI_GHC_HR); + ahci_clean_mem(ahci); + } + + ahci_shutdown(ahci); +} + +static void test_ncq_simple(void) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(NULL); + ahci_test_io_rw_simple(ahci, 4096, 0, + READ_FPDMA_QUEUED, + WRITE_FPDMA_QUEUED); + ahci_shutdown(ahci); +} + +static int prepare_iso(size_t size, unsigned char **buf, char **name) +{ + char cdrom_path[] = "/tmp/qtest.iso.XXXXXX"; + unsigned char *patt; + ssize_t ret; + int fd = mkstemp(cdrom_path); + + g_assert(buf); + g_assert(name); + patt = g_malloc(size); + + /* Generate a pattern and build a CDROM image to read from */ + generate_pattern(patt, size, ATAPI_SECTOR_SIZE); + ret = write(fd, patt, size); + g_assert(ret == size); + + *name = g_strdup(cdrom_path); + *buf = patt; + return fd; +} + +static void remove_iso(int fd, char *name) +{ + unlink(name); + g_free(name); + close(fd); +} + +static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd, + const AHCIOpts *opts) +{ + unsigned char *tx = opts->opaque; + unsigned char *rx; + + if (!opts->size) { + return 0; + } + + rx = g_malloc0(opts->size); + qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size); + g_assert_cmphex(memcmp(tx, rx, opts->size), ==, 0); + g_free(rx); + + return 0; +} + +static void ahci_test_cdrom(int nsectors, bool dma, uint8_t cmd, + bool override_bcl, uint16_t bcl) +{ + AHCIQState *ahci; + unsigned char *tx; + char *iso; + int fd; + AHCIOpts opts = { + .size = (ATAPI_SECTOR_SIZE * nsectors), + .atapi = true, + .atapi_dma = dma, + .post_cb = ahci_cb_cmp_buff, + .set_bcl = override_bcl, + .bcl = bcl, + }; + uint64_t iso_size = ATAPI_SECTOR_SIZE * (nsectors + 1); + + /* Prepare ISO and fill 'tx' buffer */ + fd = prepare_iso(iso_size, &tx, &iso); + opts.opaque = tx; + + /* Standard startup wonkery, but use ide-cd and our special iso file */ + ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw " + "-M q35 " + "-device ide-cd,drive=drive0 ", iso); + + /* Build & Send AHCI command */ + ahci_exec(ahci, ahci_port_select(ahci), cmd, &opts); + + /* Cleanup */ + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +static void ahci_test_cdrom_read10(int nsectors, bool dma) +{ + ahci_test_cdrom(nsectors, dma, CMD_ATAPI_READ_10, false, 0); +} + +static void test_cdrom_dma(void) +{ + ahci_test_cdrom_read10(1, true); +} + +static void test_cdrom_dma_multi(void) +{ + ahci_test_cdrom_read10(3, true); +} + +static void test_cdrom_pio(void) +{ + ahci_test_cdrom_read10(1, false); +} + +static void test_cdrom_pio_multi(void) +{ + ahci_test_cdrom_read10(3, false); +} + +/* Regression test: Test that a READ_CD command with a BCL of 0 but a size of 0 + * completes as a NOP instead of erroring out. */ +static void test_atapi_bcl(void) +{ + ahci_test_cdrom(0, false, CMD_ATAPI_READ_CD, true, 0); +} + + +static void atapi_wait_tray(AHCIQState *ahci, bool open) +{ + QDict *rsp = qtest_qmp_eventwait_ref(ahci->parent->qts, + "DEVICE_TRAY_MOVED"); + QDict *data = qdict_get_qdict(rsp, "data"); + if (open) { + g_assert(qdict_get_bool(data, "tray-open")); + } else { + g_assert(!qdict_get_bool(data, "tray-open")); + } + qobject_unref(rsp); +} + +static void test_atapi_tray(void) +{ + AHCIQState *ahci; + unsigned char *tx; + char *iso; + int fd; + uint8_t port, sense, asc; + uint64_t iso_size = ATAPI_SECTOR_SIZE; + QDict *rsp; + + fd = prepare_iso(iso_size, &tx, &iso); + ahci = ahci_boot_and_enable("-blockdev node-name=drive0,driver=file,filename=%s " + "-M q35 " + "-device ide-cd,id=cd0,drive=drive0 ", iso); + port = ahci_port_select(ahci); + + ahci_atapi_eject(ahci, port); + atapi_wait_tray(ahci, true); + + ahci_atapi_load(ahci, port); + atapi_wait_tray(ahci, false); + + /* Remove media */ + qtest_qmp_send(ahci->parent->qts, "{'execute': 'blockdev-open-tray', " + "'arguments': {'id': 'cd0'}}"); + atapi_wait_tray(ahci, true); + rsp = qtest_qmp_receive(ahci->parent->qts); + qobject_unref(rsp); + + qmp_discard_response(ahci->parent->qts, + "{'execute': 'blockdev-remove-medium', " + "'arguments': {'id': 'cd0'}}"); + + /* Test the tray without a medium */ + ahci_atapi_load(ahci, port); + atapi_wait_tray(ahci, false); + + ahci_atapi_eject(ahci, port); + atapi_wait_tray(ahci, true); + + /* Re-insert media */ + qmp_discard_response(ahci->parent->qts, + "{'execute': 'blockdev-add', " + "'arguments': {'node-name': 'node0', " + "'driver': 'raw', " + "'file': { 'driver': 'file', " + "'filename': %s }}}", iso); + qmp_discard_response(ahci->parent->qts, + "{'execute': 'blockdev-insert-medium'," + "'arguments': { 'id': 'cd0', " + "'node-name': 'node0' }}"); + + /* Again, the event shows up first */ + qtest_qmp_send(ahci->parent->qts, "{'execute': 'blockdev-close-tray', " + "'arguments': {'id': 'cd0'}}"); + atapi_wait_tray(ahci, false); + rsp = qtest_qmp_receive(ahci->parent->qts); + qobject_unref(rsp); + + /* Now, to convince ATAPI we understand the media has changed... */ + ahci_atapi_test_ready(ahci, port, false, SENSE_NOT_READY); + ahci_atapi_get_sense(ahci, port, &sense, &asc); + g_assert_cmpuint(sense, ==, SENSE_NOT_READY); + g_assert_cmpuint(asc, ==, ASC_MEDIUM_NOT_PRESENT); + + ahci_atapi_test_ready(ahci, port, false, SENSE_UNIT_ATTENTION); + ahci_atapi_get_sense(ahci, port, &sense, &asc); + g_assert_cmpuint(sense, ==, SENSE_UNIT_ATTENTION); + g_assert_cmpuint(asc, ==, ASC_MEDIUM_MAY_HAVE_CHANGED); + + ahci_atapi_test_ready(ahci, port, true, SENSE_NO_SENSE); + ahci_atapi_get_sense(ahci, port, &sense, &asc); + g_assert_cmpuint(sense, ==, SENSE_NO_SENSE); + + /* Final tray test. */ + ahci_atapi_eject(ahci, port); + atapi_wait_tray(ahci, true); + + ahci_atapi_load(ahci, port); + atapi_wait_tray(ahci, false); + + /* Cleanup */ + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +/******************************************************************************/ +/* AHCI I/O Test Matrix Definitions */ + +enum BuffLen { + LEN_BEGIN = 0, + LEN_SIMPLE = LEN_BEGIN, + LEN_DOUBLE, + LEN_LONG, + LEN_SHORT, + NUM_LENGTHS +}; + +static const char *buff_len_str[NUM_LENGTHS] = { "simple", "double", + "long", "short" }; + +enum AddrMode { + ADDR_MODE_BEGIN = 0, + ADDR_MODE_LBA28 = ADDR_MODE_BEGIN, + ADDR_MODE_LBA48, + NUM_ADDR_MODES +}; + +static const char *addr_mode_str[NUM_ADDR_MODES] = { "lba28", "lba48" }; + +enum IOMode { + MODE_BEGIN = 0, + MODE_PIO = MODE_BEGIN, + MODE_DMA, + NUM_MODES +}; + +static const char *io_mode_str[NUM_MODES] = { "pio", "dma" }; + +enum IOOps { + IO_BEGIN = 0, + IO_READ = IO_BEGIN, + IO_WRITE, + NUM_IO_OPS +}; + +enum OffsetType { + OFFSET_BEGIN = 0, + OFFSET_ZERO = OFFSET_BEGIN, + OFFSET_LOW, + OFFSET_HIGH, + NUM_OFFSETS +}; + +static const char *offset_str[NUM_OFFSETS] = { "zero", "low", "high" }; + +typedef struct AHCIIOTestOptions { + enum BuffLen length; + enum AddrMode address_type; + enum IOMode io_type; + enum OffsetType offset; +} AHCIIOTestOptions; + +static uint64_t offset_sector(enum OffsetType ofst, + enum AddrMode addr_type, + uint64_t buffsize) +{ + uint64_t ceil; + uint64_t nsectors; + + switch (ofst) { + case OFFSET_ZERO: + return 0; + case OFFSET_LOW: + return 1; + case OFFSET_HIGH: + ceil = (addr_type == ADDR_MODE_LBA28) ? 0xfffffff : 0xffffffffffff; + ceil = MIN(ceil, mb_to_sectors(test_image_size_mb) - 1); + nsectors = buffsize / AHCI_SECTOR_SIZE; + return ceil - nsectors + 1; + default: + g_assert_not_reached(); + } +} + +/** + * Table of possible I/O ATA commands given a set of enumerations. + */ +static const uint8_t io_cmds[NUM_MODES][NUM_ADDR_MODES][NUM_IO_OPS] = { + [MODE_PIO] = { + [ADDR_MODE_LBA28] = { + [IO_READ] = CMD_READ_PIO, + [IO_WRITE] = CMD_WRITE_PIO }, + [ADDR_MODE_LBA48] = { + [IO_READ] = CMD_READ_PIO_EXT, + [IO_WRITE] = CMD_WRITE_PIO_EXT } + }, + [MODE_DMA] = { + [ADDR_MODE_LBA28] = { + [IO_READ] = CMD_READ_DMA, + [IO_WRITE] = CMD_WRITE_DMA }, + [ADDR_MODE_LBA48] = { + [IO_READ] = CMD_READ_DMA_EXT, + [IO_WRITE] = CMD_WRITE_DMA_EXT } + } +}; + +/** + * Test a Read/Write pattern using various commands, addressing modes, + * transfer modes, and buffer sizes. + */ +static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma, + unsigned bufsize, uint64_t sector) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(NULL); + ahci_test_io_rw_simple(ahci, bufsize, sector, + io_cmds[dma][lba48][IO_READ], + io_cmds[dma][lba48][IO_WRITE]); + ahci_shutdown(ahci); +} + +/** + * Demultiplex the test data and invoke the actual test routine. + */ +static void test_io_interface(gconstpointer opaque) +{ + AHCIIOTestOptions *opts = (AHCIIOTestOptions *)opaque; + unsigned bufsize; + uint64_t sector; + + switch (opts->length) { + case LEN_SIMPLE: + bufsize = 4096; + break; + case LEN_DOUBLE: + bufsize = 8192; + break; + case LEN_LONG: + bufsize = 4096 * 64; + break; + case LEN_SHORT: + bufsize = 512; + break; + default: + g_assert_not_reached(); + } + + sector = offset_sector(opts->offset, opts->address_type, bufsize); + test_io_rw_interface(opts->address_type, opts->io_type, bufsize, sector); + g_free(opts); + return; +} + +static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, + enum BuffLen len, enum OffsetType offset) +{ + char *name; + AHCIIOTestOptions *opts; + + opts = g_new(AHCIIOTestOptions, 1); + opts->length = len; + opts->address_type = addr; + opts->io_type = type; + opts->offset = offset; + + name = g_strdup_printf("ahci/io/%s/%s/%s/%s", + io_mode_str[type], + addr_mode_str[addr], + buff_len_str[len], + offset_str[offset]); + + if ((addr == ADDR_MODE_LBA48) && (offset == OFFSET_HIGH) && + (mb_to_sectors(test_image_size_mb) <= 0xFFFFFFF)) { + g_test_message("%s: skipped; test image too small", name); + g_free(opts); + g_free(name); + return; + } + + qtest_add_data_func(name, opts, test_io_interface); + g_free(name); +} + +/******************************************************************************/ + +int main(int argc, char **argv) +{ + const char *arch; + int ret; + int fd; + int c; + int i, j, k, m; + + static struct option long_options[] = { + {"pedantic", no_argument, 0, 'p' }, + {0, 0, 0, 0}, + }; + + /* Should be first to utilize g_test functionality, So we can see errors. */ + g_test_init(&argc, &argv, NULL); + + while (1) { + c = getopt_long(argc, argv, "", long_options, NULL); + if (c == -1) { + break; + } + switch (c) { + case -1: + break; + case 'p': + ahci_pedantic = 1; + break; + default: + fprintf(stderr, "Unrecognized ahci_test option.\n"); + g_assert_not_reached(); + } + } + + /* Check architecture */ + arch = qtest_get_arch(); + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { + g_test_message("Skipping test for non-x86"); + return 0; + } + + /* Create a temporary image */ + fd = mkstemp(tmp_path); + g_assert(fd >= 0); + if (have_qemu_img()) { + imgfmt = "qcow2"; + test_image_size_mb = TEST_IMAGE_SIZE_MB_LARGE; + mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB_LARGE); + } else { + g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " + "skipping LBA48 high-sector tests"); + imgfmt = "raw"; + test_image_size_mb = TEST_IMAGE_SIZE_MB_SMALL; + ret = ftruncate(fd, test_image_size_mb * 1024 * 1024); + g_assert(ret == 0); + } + close(fd); + + /* Create temporary blkdebug instructions */ + fd = mkstemp(debug_path); + g_assert(fd >= 0); + close(fd); + + /* Reserve a hollow file to use as a socket for migration tests */ + fd = mkstemp(mig_socket); + g_assert(fd >= 0); + close(fd); + + /* Run the tests */ + qtest_add_func("/ahci/sanity", test_sanity); + qtest_add_func("/ahci/pci_spec", test_pci_spec); + qtest_add_func("/ahci/pci_enable", test_pci_enable); + qtest_add_func("/ahci/hba_spec", test_hba_spec); + qtest_add_func("/ahci/hba_enable", test_hba_enable); + qtest_add_func("/ahci/identify", test_identify); + + for (i = MODE_BEGIN; i < NUM_MODES; i++) { + for (j = ADDR_MODE_BEGIN; j < NUM_ADDR_MODES; j++) { + for (k = LEN_BEGIN; k < NUM_LENGTHS; k++) { + for (m = OFFSET_BEGIN; m < NUM_OFFSETS; m++) { + create_ahci_io_test(i, j, k, m); + } + } + } + } + + qtest_add_func("/ahci/io/dma/lba28/fragmented", test_dma_fragmented); + + qtest_add_func("/ahci/flush/simple", test_flush); + qtest_add_func("/ahci/flush/retry", test_flush_retry); + qtest_add_func("/ahci/flush/migrate", test_flush_migrate); + + qtest_add_func("/ahci/migrate/sanity", test_migrate_sanity); + qtest_add_func("/ahci/migrate/dma/simple", test_migrate_dma); + qtest_add_func("/ahci/io/dma/lba28/retry", test_halted_dma); + qtest_add_func("/ahci/migrate/dma/halted", test_migrate_halted_dma); + + qtest_add_func("/ahci/max", test_max); + qtest_add_func("/ahci/reset", test_reset); + + qtest_add_func("/ahci/io/ncq/simple", test_ncq_simple); + qtest_add_func("/ahci/migrate/ncq/simple", test_migrate_ncq); + qtest_add_func("/ahci/io/ncq/retry", test_halted_ncq); + qtest_add_func("/ahci/migrate/ncq/halted", test_migrate_halted_ncq); + + qtest_add_func("/ahci/cdrom/dma/single", test_cdrom_dma); + qtest_add_func("/ahci/cdrom/dma/multi", test_cdrom_dma_multi); + qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio); + qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi); + + qtest_add_func("/ahci/cdrom/pio/bcl", test_atapi_bcl); + qtest_add_func("/ahci/cdrom/eject", test_atapi_tray); + + ret = g_test_run(); + + /* Cleanup */ + unlink(tmp_path); + unlink(debug_path); + unlink(mig_socket); + + return ret; +} diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c new file mode 100644 index 0000000000..bef3ed24b6 --- /dev/null +++ b/tests/qtest/arm-cpu-features.c @@ -0,0 +1,559 @@ +/* + * Arm CPU feature test cases + * + * Copyright (c) 2019 Red Hat Inc. + * Authors: + * Andrew Jones + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" + +/* + * We expect the SVE max-vq to be 16. Also it must be <= 64 + * for our test code, otherwise 'vls' can't just be a uint64_t. + */ +#define SVE_MAX_VQ 16 + +#define MACHINE "-machine virt,gic-version=max -accel tcg " +#define MACHINE_KVM "-machine virt,gic-version=max -accel kvm -accel tcg " +#define QUERY_HEAD "{ 'execute': 'query-cpu-model-expansion', " \ + " 'arguments': { 'type': 'full', " +#define QUERY_TAIL "}}" + +static bool kvm_enabled(QTestState *qts) +{ + QDict *resp, *qdict; + bool enabled; + + resp = qtest_qmp(qts, "{ 'execute': 'query-kvm' }"); + g_assert(qdict_haskey(resp, "return")); + qdict = qdict_get_qdict(resp, "return"); + g_assert(qdict_haskey(qdict, "enabled")); + enabled = qdict_get_bool(qdict, "enabled"); + qobject_unref(resp); + + return enabled; +} + +static QDict *do_query_no_props(QTestState *qts, const char *cpu_type) +{ + return qtest_qmp(qts, QUERY_HEAD "'model': { 'name': %s }" + QUERY_TAIL, cpu_type); +} + +static QDict *do_query(QTestState *qts, const char *cpu_type, + const char *fmt, ...) +{ + QDict *resp; + + if (fmt) { + QDict *args; + va_list ap; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + resp = qtest_qmp(qts, QUERY_HEAD "'model': { 'name': %s, " + "'props': %p }" + QUERY_TAIL, cpu_type, args); + } else { + resp = do_query_no_props(qts, cpu_type); + } + + return resp; +} + +static const char *resp_get_error(QDict *resp) +{ + QDict *qdict; + + g_assert(resp); + + qdict = qdict_get_qdict(resp, "error"); + if (qdict) { + return qdict_get_str(qdict, "desc"); + } + + return NULL; +} + +#define assert_error(qts, cpu_type, expected_error, fmt, ...) \ +({ \ + QDict *_resp; \ + const char *_error; \ + \ + _resp = do_query(qts, cpu_type, fmt, ##__VA_ARGS__); \ + g_assert(_resp); \ + _error = resp_get_error(_resp); \ + g_assert(_error); \ + g_assert(g_str_equal(_error, expected_error)); \ + qobject_unref(_resp); \ +}) + +static bool resp_has_props(QDict *resp) +{ + QDict *qdict; + + g_assert(resp); + + if (!qdict_haskey(resp, "return")) { + return false; + } + qdict = qdict_get_qdict(resp, "return"); + + if (!qdict_haskey(qdict, "model")) { + return false; + } + qdict = qdict_get_qdict(qdict, "model"); + + return qdict_haskey(qdict, "props"); +} + +static QDict *resp_get_props(QDict *resp) +{ + QDict *qdict; + + g_assert(resp); + g_assert(resp_has_props(resp)); + + qdict = qdict_get_qdict(resp, "return"); + qdict = qdict_get_qdict(qdict, "model"); + qdict = qdict_get_qdict(qdict, "props"); + + return qdict; +} + +static bool resp_get_feature(QDict *resp, const char *feature) +{ + QDict *props; + + g_assert(resp); + g_assert(resp_has_props(resp)); + props = resp_get_props(resp); + g_assert(qdict_get(props, feature)); + return qdict_get_bool(props, feature); +} + +#define assert_has_feature(qts, cpu_type, feature) \ +({ \ + QDict *_resp = do_query_no_props(qts, cpu_type); \ + g_assert(_resp); \ + g_assert(resp_has_props(_resp)); \ + g_assert(qdict_get(resp_get_props(_resp), feature)); \ + qobject_unref(_resp); \ +}) + +#define assert_has_not_feature(qts, cpu_type, feature) \ +({ \ + QDict *_resp = do_query_no_props(qts, cpu_type); \ + g_assert(_resp); \ + g_assert(!resp_has_props(_resp) || \ + !qdict_get(resp_get_props(_resp), feature)); \ + qobject_unref(_resp); \ +}) + +static void assert_type_full(QTestState *qts) +{ + const char *error; + QDict *resp; + + resp = qtest_qmp(qts, "{ 'execute': 'query-cpu-model-expansion', " + "'arguments': { 'type': 'static', " + "'model': { 'name': 'foo' }}}"); + g_assert(resp); + error = resp_get_error(resp); + g_assert(error); + g_assert(g_str_equal(error, + "The requested expansion type is not supported")); + qobject_unref(resp); +} + +static void assert_bad_props(QTestState *qts, const char *cpu_type) +{ + const char *error; + QDict *resp; + + resp = qtest_qmp(qts, "{ 'execute': 'query-cpu-model-expansion', " + "'arguments': { 'type': 'full', " + "'model': { 'name': %s, " + "'props': false }}}", + cpu_type); + g_assert(resp); + error = resp_get_error(resp); + g_assert(error); + g_assert(g_str_equal(error, + "Invalid parameter type for 'props', expected: dict")); + qobject_unref(resp); +} + +static uint64_t resp_get_sve_vls(QDict *resp) +{ + QDict *props; + const QDictEntry *e; + uint64_t vls = 0; + int n = 0; + + g_assert(resp); + g_assert(resp_has_props(resp)); + + props = resp_get_props(resp); + + for (e = qdict_first(props); e; e = qdict_next(props, e)) { + if (strlen(e->key) > 3 && !strncmp(e->key, "sve", 3) && + g_ascii_isdigit(e->key[3])) { + char *endptr; + int bits; + + bits = g_ascii_strtoll(&e->key[3], &endptr, 10); + if (!bits || *endptr != '\0') { + continue; + } + + if (qdict_get_bool(props, e->key)) { + vls |= BIT_ULL((bits / 128) - 1); + } + ++n; + } + } + + g_assert(n == SVE_MAX_VQ); + + return vls; +} + +#define assert_sve_vls(qts, cpu_type, expected_vls, fmt, ...) \ +({ \ + QDict *_resp = do_query(qts, cpu_type, fmt, ##__VA_ARGS__); \ + g_assert(_resp); \ + g_assert(resp_has_props(_resp)); \ + g_assert(resp_get_sve_vls(_resp) == expected_vls); \ + qobject_unref(_resp); \ +}) + +static void sve_tests_default(QTestState *qts, const char *cpu_type) +{ + /* + * With no sve-max-vq or sve properties on the command line + * the default is to have all vector lengths enabled. This also + * tests that 'sve' is 'on' by default. + */ + assert_sve_vls(qts, cpu_type, BIT_ULL(SVE_MAX_VQ) - 1, NULL); + + /* With SVE off, all vector lengths should also be off. */ + assert_sve_vls(qts, cpu_type, 0, "{ 'sve': false }"); + + /* With SVE on, we must have at least one vector length enabled. */ + assert_error(qts, cpu_type, "cannot disable sve128", "{ 'sve128': false }"); + + /* Basic enable/disable tests. */ + assert_sve_vls(qts, cpu_type, 0x7, "{ 'sve384': true }"); + assert_sve_vls(qts, cpu_type, ((BIT_ULL(SVE_MAX_VQ) - 1) & ~BIT_ULL(2)), + "{ 'sve384': false }"); + + /* + * --------------------------------------------------------------------- + * power-of-two(vq) all-power- can can + * of-two(< vq) enable disable + * --------------------------------------------------------------------- + * vq < max_vq no MUST* yes yes + * vq < max_vq yes MUST* yes no + * --------------------------------------------------------------------- + * vq == max_vq n/a MUST* yes** yes** + * --------------------------------------------------------------------- + * vq > max_vq n/a no no yes + * vq > max_vq n/a yes yes yes + * --------------------------------------------------------------------- + * + * [*] "MUST" means this requirement must already be satisfied, + * otherwise 'max_vq' couldn't itself be enabled. + * + * [**] Not testable with the QMP interface, only with the command line. + */ + + /* max_vq := 8 */ + assert_sve_vls(qts, cpu_type, 0x8b, "{ 'sve1024': true }"); + + /* max_vq := 8, vq < max_vq, !power-of-two(vq) */ + assert_sve_vls(qts, cpu_type, 0x8f, + "{ 'sve1024': true, 'sve384': true }"); + assert_sve_vls(qts, cpu_type, 0x8b, + "{ 'sve1024': true, 'sve384': false }"); + + /* max_vq := 8, vq < max_vq, power-of-two(vq) */ + assert_sve_vls(qts, cpu_type, 0x8b, + "{ 'sve1024': true, 'sve256': true }"); + assert_error(qts, cpu_type, "cannot disable sve256", + "{ 'sve1024': true, 'sve256': false }"); + + /* max_vq := 3, vq > max_vq, !all-power-of-two(< vq) */ + assert_error(qts, cpu_type, "cannot disable sve512", + "{ 'sve384': true, 'sve512': false, 'sve640': true }"); + + /* + * We can disable power-of-two vector lengths when all larger lengths + * are also disabled. We only need to disable the power-of-two length, + * as all non-enabled larger lengths will then be auto-disabled. + */ + assert_sve_vls(qts, cpu_type, 0x7, "{ 'sve512': false }"); + + /* max_vq := 3, vq > max_vq, all-power-of-two(< vq) */ + assert_sve_vls(qts, cpu_type, 0x1f, + "{ 'sve384': true, 'sve512': true, 'sve640': true }"); + assert_sve_vls(qts, cpu_type, 0xf, + "{ 'sve384': true, 'sve512': true, 'sve640': false }"); +} + +static void sve_tests_sve_max_vq_8(const void *data) +{ + QTestState *qts; + + qts = qtest_init(MACHINE "-cpu max,sve-max-vq=8"); + + assert_sve_vls(qts, "max", BIT_ULL(8) - 1, NULL); + + /* + * Disabling the max-vq set by sve-max-vq is not allowed, but + * of course enabling it is OK. + */ + assert_error(qts, "max", "cannot disable sve1024", "{ 'sve1024': false }"); + assert_sve_vls(qts, "max", 0xff, "{ 'sve1024': true }"); + + /* + * Enabling anything larger than max-vq set by sve-max-vq is not + * allowed, but of course disabling everything larger is OK. + */ + assert_error(qts, "max", "cannot enable sve1152", "{ 'sve1152': true }"); + assert_sve_vls(qts, "max", 0xff, "{ 'sve1152': false }"); + + /* + * We can enable/disable non power-of-two lengths smaller than the + * max-vq set by sve-max-vq, but, while we can enable power-of-two + * lengths, we can't disable them. + */ + assert_sve_vls(qts, "max", 0xff, "{ 'sve384': true }"); + assert_sve_vls(qts, "max", 0xfb, "{ 'sve384': false }"); + assert_sve_vls(qts, "max", 0xff, "{ 'sve256': true }"); + assert_error(qts, "max", "cannot disable sve256", "{ 'sve256': false }"); + + qtest_quit(qts); +} + +static void sve_tests_sve_off(const void *data) +{ + QTestState *qts; + + qts = qtest_init(MACHINE "-cpu max,sve=off"); + + /* SVE is off, so the map should be empty. */ + assert_sve_vls(qts, "max", 0, NULL); + + /* The map stays empty even if we turn lengths off. */ + assert_sve_vls(qts, "max", 0, "{ 'sve128': false }"); + + /* It's an error to enable lengths when SVE is off. */ + assert_error(qts, "max", "cannot enable sve128", "{ 'sve128': true }"); + + /* With SVE re-enabled we should get all vector lengths enabled. */ + assert_sve_vls(qts, "max", BIT_ULL(SVE_MAX_VQ) - 1, "{ 'sve': true }"); + + /* Or enable SVE with just specific vector lengths. */ + assert_sve_vls(qts, "max", 0x3, + "{ 'sve': true, 'sve128': true, 'sve256': true }"); + + qtest_quit(qts); +} + +static void sve_tests_sve_off_kvm(const void *data) +{ + QTestState *qts; + + qts = qtest_init(MACHINE_KVM "-cpu max,sve=off"); + + /* + * We don't know if this host supports SVE so we don't + * attempt to test enabling anything. We only test that + * everything is disabled (as it should be with sve=off) + * and that using sve=off to explicitly disable vector + * lengths is OK too. + */ + assert_sve_vls(qts, "max", 0, NULL); + assert_sve_vls(qts, "max", 0, "{ 'sve128': false }"); + + qtest_quit(qts); +} + +static void test_query_cpu_model_expansion(const void *data) +{ + QTestState *qts; + + qts = qtest_init(MACHINE "-cpu max"); + + /* Test common query-cpu-model-expansion input validation */ + assert_type_full(qts); + assert_bad_props(qts, "max"); + assert_error(qts, "foo", "The CPU type 'foo' is not a recognized " + "ARM CPU type", NULL); + assert_error(qts, "max", "Parameter 'not-a-prop' is unexpected", + "{ 'not-a-prop': false }"); + assert_error(qts, "host", "The CPU type 'host' requires KVM", NULL); + + /* Test expected feature presence/absence for some cpu types */ + assert_has_feature(qts, "max", "pmu"); + assert_has_feature(qts, "cortex-a15", "pmu"); + assert_has_not_feature(qts, "cortex-a15", "aarch64"); + + if (g_str_equal(qtest_get_arch(), "aarch64")) { + assert_has_feature(qts, "max", "aarch64"); + assert_has_feature(qts, "max", "sve"); + assert_has_feature(qts, "max", "sve128"); + assert_has_feature(qts, "cortex-a57", "pmu"); + assert_has_feature(qts, "cortex-a57", "aarch64"); + + sve_tests_default(qts, "max"); + + /* Test that features that depend on KVM generate errors without. */ + assert_error(qts, "max", + "'aarch64' feature cannot be disabled " + "unless KVM is enabled and 32-bit EL1 " + "is supported", + "{ 'aarch64': false }"); + } + + qtest_quit(qts); +} + +static void test_query_cpu_model_expansion_kvm(const void *data) +{ + QTestState *qts; + + qts = qtest_init(MACHINE_KVM "-cpu max"); + + /* + * These tests target the 'host' CPU type, so KVM must be enabled. + */ + if (!kvm_enabled(qts)) { + qtest_quit(qts); + return; + } + + if (g_str_equal(qtest_get_arch(), "aarch64")) { + bool kvm_supports_sve; + char max_name[8], name[8]; + uint32_t max_vq, vq; + uint64_t vls; + QDict *resp; + char *error; + + assert_has_feature(qts, "host", "aarch64"); + assert_has_feature(qts, "host", "pmu"); + + assert_error(qts, "cortex-a15", + "We cannot guarantee the CPU type 'cortex-a15' works " + "with KVM on this host", NULL); + + assert_has_feature(qts, "host", "sve"); + resp = do_query_no_props(qts, "host"); + kvm_supports_sve = resp_get_feature(resp, "sve"); + vls = resp_get_sve_vls(resp); + qobject_unref(resp); + + if (kvm_supports_sve) { + g_assert(vls != 0); + max_vq = 64 - __builtin_clzll(vls); + sprintf(max_name, "sve%d", max_vq * 128); + + /* Enabling a supported length is of course fine. */ + assert_sve_vls(qts, "host", vls, "{ %s: true }", max_name); + + /* Get the next supported length smaller than max-vq. */ + vq = 64 - __builtin_clzll(vls & ~BIT_ULL(max_vq - 1)); + if (vq) { + /* + * We have at least one length smaller than max-vq, + * so we can disable max-vq. + */ + assert_sve_vls(qts, "host", (vls & ~BIT_ULL(max_vq - 1)), + "{ %s: false }", max_name); + + /* + * Smaller, supported vector lengths cannot be disabled + * unless all larger, supported vector lengths are also + * disabled. + */ + sprintf(name, "sve%d", vq * 128); + error = g_strdup_printf("cannot disable %s", name); + assert_error(qts, "host", error, + "{ %s: true, %s: false }", + max_name, name); + g_free(error); + } + + /* + * The smallest, supported vector length is required, because + * we need at least one vector length enabled. + */ + vq = __builtin_ffsll(vls); + sprintf(name, "sve%d", vq * 128); + error = g_strdup_printf("cannot disable %s", name); + assert_error(qts, "host", error, "{ %s: false }", name); + g_free(error); + + /* Get an unsupported length. */ + for (vq = 1; vq <= max_vq; ++vq) { + if (!(vls & BIT_ULL(vq - 1))) { + break; + } + } + if (vq <= SVE_MAX_VQ) { + sprintf(name, "sve%d", vq * 128); + error = g_strdup_printf("cannot enable %s", name); + assert_error(qts, "host", error, "{ %s: true }", name); + g_free(error); + } + } else { + g_assert(vls == 0); + } + } else { + assert_has_not_feature(qts, "host", "aarch64"); + assert_has_not_feature(qts, "host", "pmu"); + assert_has_not_feature(qts, "host", "sve"); + } + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/arm/query-cpu-model-expansion", + NULL, test_query_cpu_model_expansion); + + /* + * For now we only run KVM specific tests with AArch64 QEMU in + * order avoid attempting to run an AArch32 QEMU with KVM on + * AArch64 hosts. That won't work and isn't easy to detect. + */ + if (g_str_equal(qtest_get_arch(), "aarch64")) { + qtest_add_data_func("/arm/kvm/query-cpu-model-expansion", + NULL, test_query_cpu_model_expansion_kvm); + } + + if (g_str_equal(qtest_get_arch(), "aarch64")) { + qtest_add_data_func("/arm/max/query-cpu-model-expansion/sve-max-vq-8", + NULL, sve_tests_sve_max_vq_8); + qtest_add_data_func("/arm/max/query-cpu-model-expansion/sve-off", + NULL, sve_tests_sve_off); + qtest_add_data_func("/arm/kvm/query-cpu-model-expansion/sve-off", + NULL, sve_tests_sve_off_kvm); + } + + return g_test_run(); +} diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h new file mode 100644 index 0000000000..dfb8523c8b --- /dev/null +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -0,0 +1 @@ +/* List of comma-separated changed AML files to ignore */ diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c new file mode 100644 index 0000000000..f1ac2d7e96 --- /dev/null +++ b/tests/qtest/bios-tables-test.c @@ -0,0 +1,1046 @@ +/* + * Boot order test cases. + * + * Copyright (c) 2013 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * How to add or update the tests: + * Contributor: + * 1. add empty files for new tables, if any, under tests/data/acpi + * 2. list any changed files in tests/bios-tables-test-allowed-diff.h + * 3. commit the above *before* making changes that affect the tables + * Maintainer: + * After 1-3 above tests will pass but ignore differences with the expected files. + * You will also notice that tests/bios-tables-test-allowed-diff.h lists + * a bunch of files. This is your hint that you need to do the below: + * 4. Run + * make check V=1 + * this will produce a bunch of warnings about differences + * beween actual and expected ACPI tables. If you have IASL installed, + * they will also be disassembled so you can look at the disassembled + * output. If not - disassemble them yourself in any way you like. + * Look at the differences - make sure they make sense and match what the + * changes you are merging are supposed to do. + * + * 5. From build directory, run: + * $(SRC_PATH)/tests/data/acpi/rebuild-expected-aml.sh + * 6. Now commit any changes. + * 7. Before doing a pull request, make sure tests/bios-tables-test-allowed-diff.h + * is empty - this will ensure following changes to ACPI tables will + * be noticed. + */ + +#include "qemu/osdep.h" +#include +#include "qemu-common.h" +#include "hw/firmware/smbios.h" +#include "qemu/bitmap.h" +#include "acpi-utils.h" +#include "boot-sector.h" + +#define MACHINE_PC "pc" +#define MACHINE_Q35 "q35" + +#define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML" + +typedef struct { + bool tcg_only; + const char *machine; + const char *variant; + const char *uefi_fl1; + const char *uefi_fl2; + const char *cd; + const uint64_t ram_start; + const uint64_t scan_len; + uint64_t rsdp_addr; + uint8_t rsdp_table[36 /* ACPI 2.0+ RSDP size */]; + GArray *tables; + uint32_t smbios_ep_addr; + struct smbios_21_entry_point smbios_ep_table; + uint8_t *required_struct_types; + int required_struct_types_len; + QTestState *qts; +} test_data; + +static char disk[] = "tests/acpi-test-disk-XXXXXX"; +static const char *data_dir = "tests/data/acpi"; +#ifdef CONFIG_IASL +static const char *iasl = stringify(CONFIG_IASL); +#else +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) +{ + int i; + + for (i = 0; i < data->tables->len; ++i) { + cleanup_table_descriptor(&g_array_index(data->tables, AcpiSdtTable, i)); + } + + g_array_free(data->tables, true); +} + +static void test_acpi_rsdp_table(test_data *data) +{ + uint8_t *rsdp_table = data->rsdp_table; + + acpi_fetch_rsdp_table(data->qts, data->rsdp_addr, rsdp_table); + + switch (rsdp_table[15 /* Revision offset */]) { + case 0: /* ACPI 1.0 RSDP */ + /* With rev 1, checksum is only for the first 20 bytes */ + g_assert(!acpi_calc_checksum(rsdp_table, 20)); + break; + case 2: /* ACPI 2.0+ RSDP */ + /* With revision 2, we have 2 checksums */ + g_assert(!acpi_calc_checksum(rsdp_table, 20)); + g_assert(!acpi_calc_checksum(rsdp_table, 36)); + break; + default: + g_assert_not_reached(); + } +} + +static void test_acpi_rxsdt_table(test_data *data) +{ + const char *sig = "RSDT"; + AcpiSdtTable rsdt = {}; + int entry_size = 4; + int addr_off = 16 /* RsdtAddress */; + uint8_t *ent; + + if (data->rsdp_table[15 /* Revision offset */] != 0) { + addr_off = 24 /* XsdtAddress */; + entry_size = 8; + sig = "XSDT"; + } + /* read [RX]SDT table */ + acpi_fetch_table(data->qts, &rsdt.aml, &rsdt.aml_len, + &data->rsdp_table[addr_off], entry_size, sig, true); + + /* Load all tables and add to test list directly RSDT referenced tables */ + ACPI_FOREACH_RSDT_ENTRY(rsdt.aml, rsdt.aml_len, ent, entry_size) { + AcpiSdtTable ssdt_table = {}; + + acpi_fetch_table(data->qts, &ssdt_table.aml, &ssdt_table.aml_len, ent, + entry_size, 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_fadt_table(test_data *data) +{ + /* 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; + uint32_t val; + int dsdt_offset = 40 /* DSDT */; + int dsdt_entry_size = 4; + + g_assert(compare_signature(&table, "FACP")); + + /* Since DSDT/FACS isn't in RSDT, add them to ASL test list manually */ + memcpy(&val, fadt_aml + 112 /* Flags */, 4); + val = le32_to_cpu(val); + if (!(val & 1UL << 20 /* HW_REDUCED_ACPI */)) { + acpi_fetch_table(data->qts, &table.aml, &table.aml_len, + fadt_aml + 36 /* FIRMWARE_CTRL */, 4, "FACS", false); + g_array_append_val(data->tables, table); + } + + memcpy(&val, fadt_aml + dsdt_offset, 4); + val = le32_to_cpu(val); + if (!val) { + dsdt_offset = 140 /* X_DSDT */; + dsdt_entry_size = 8; + } + acpi_fetch_table(data->qts, &table.aml, &table.aml_len, + fadt_aml + dsdt_offset, dsdt_entry_size, "DSDT", true); + g_array_append_val(data->tables, 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) +{ + AcpiSdtTable *sdt; + GError *error = NULL; + gchar *aml_file = NULL; + gint fd; + ssize_t ret; + int i; + + for (i = 0; i < data->tables->len; ++i) { + const char *ext = data->variant ? data->variant : ""; + sdt = &g_array_index(data->tables, AcpiSdtTable, i); + g_assert(sdt->aml); + + if (rebuild) { + aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, + sdt->aml, ext); + fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); + if (fd < 0) { + perror(aml_file); + } + g_assert(fd >= 0); + } else { + fd = g_file_open_tmp("aml-XXXXXX", &sdt->aml_file, &error); + g_assert_no_error(error); + } + + ret = qemu_write_full(fd, sdt->aml, sdt->aml_len); + g_assert(ret == sdt->aml_len); + + close(fd); + + g_free(aml_file); + } +} + +static bool load_asl(GArray *sdts, AcpiSdtTable *sdt) +{ + AcpiSdtTable *temp; + GError *error = NULL; + GString *command_line = g_string_new(iasl); + gint fd; + gchar *out, *out_err; + gboolean ret; + int i; + + fd = g_file_open_tmp("asl-XXXXXX.dsl", &sdt->asl_file, &error); + g_assert_no_error(error); + close(fd); + + /* build command line */ + g_string_append_printf(command_line, " -p %s ", sdt->asl_file); + if (compare_signature(sdt, "DSDT") || + compare_signature(sdt, "SSDT")) { + for (i = 0; i < sdts->len; ++i) { + temp = &g_array_index(sdts, AcpiSdtTable, i); + if (compare_signature(temp, "DSDT") || + compare_signature(temp, "SSDT")) { + g_string_append_printf(command_line, "-e %s ", temp->aml_file); + } + } + } + g_string_append_printf(command_line, "-d %s", sdt->aml_file); + + /* pass 'out' and 'out_err' in order to be redirected */ + ret = g_spawn_command_line_sync(command_line->str, &out, &out_err, NULL, &error); + g_assert_no_error(error); + if (ret) { + ret = g_file_get_contents(sdt->asl_file, &sdt->asl, + &sdt->asl_len, &error); + g_assert(ret); + g_assert_no_error(error); + ret = (sdt->asl_len > 0); + } + + g_free(out); + g_free(out_err); + g_string_free(command_line, true); + + return !ret; +} + +#define COMMENT_END "*/" +#define DEF_BLOCK "DefinitionBlock (" +#define BLOCK_NAME_END "," + +static GString *normalize_asl(gchar *asl_code) +{ + GString *asl = g_string_new(asl_code); + gchar *comment, *block_name; + + /* strip comments (different generation days) */ + comment = g_strstr_len(asl->str, asl->len, COMMENT_END); + if (comment) { + comment += strlen(COMMENT_END); + while (*comment == '\n') { + comment++; + } + asl = g_string_erase(asl, 0, comment - asl->str); + } + + /* strip def block name (it has file path in it) */ + if (g_str_has_prefix(asl->str, DEF_BLOCK)) { + block_name = g_strstr_len(asl->str, asl->len, BLOCK_NAME_END); + g_assert(block_name); + asl = g_string_erase(asl, 0, + block_name + sizeof(BLOCK_NAME_END) - asl->str); + } + + return asl; +} + +static GArray *load_expected_aml(test_data *data) +{ + int i; + AcpiSdtTable *sdt; + GError *error = NULL; + gboolean ret; + gsize aml_len; + + GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); + if (getenv("V")) { + fputc('\n', stderr); + } + for (i = 0; i < data->tables->len; ++i) { + AcpiSdtTable exp_sdt; + gchar *aml_file = NULL; + const char *ext = data->variant ? data->variant : ""; + + sdt = &g_array_index(data->tables, AcpiSdtTable, i); + + memset(&exp_sdt, 0, sizeof(exp_sdt)); + +try_again: + aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, + sdt->aml, ext); + if (getenv("V")) { + fprintf(stderr, "Looking for expected file '%s'\n", aml_file); + } + if (g_file_test(aml_file, G_FILE_TEST_EXISTS)) { + exp_sdt.aml_file = aml_file; + } else if (*ext != '\0') { + /* try fallback to generic (extension less) expected file */ + ext = ""; + g_free(aml_file); + goto try_again; + } + g_assert(exp_sdt.aml_file); + if (getenv("V")) { + fprintf(stderr, "Using expected file '%s'\n", aml_file); + } + 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); + if (!exp_sdt.aml_len) { + fprintf(stderr, "Warning! zero length expected file '%s'\n", + aml_file); + } + + g_array_append_val(exp_tables, exp_sdt); + } + + return exp_tables; +} + +static bool test_acpi_find_diff_allowed(AcpiSdtTable *sdt) +{ + const gchar *allowed_diff_file[] = { +#include "bios-tables-test-allowed-diff.h" + NULL + }; + const gchar **f; + + for (f = allowed_diff_file; *f; ++f) { + if (!g_strcmp0(sdt->aml_file, *f)) { + return true; + } + } + return false; +} + +/* test the list of tables in @data->tables against reference tables */ +static void test_acpi_asl(test_data *data) +{ + int i; + AcpiSdtTable *sdt, *exp_sdt; + test_data exp_data; + gboolean exp_err, err, all_tables_match = true; + + memset(&exp_data, 0, sizeof(exp_data)); + exp_data.tables = load_expected_aml(data); + dump_aml_files(data, false); + for (i = 0; i < data->tables->len; ++i) { + GString *asl, *exp_asl; + + sdt = &g_array_index(data->tables, AcpiSdtTable, i); + exp_sdt = &g_array_index(exp_data.tables, AcpiSdtTable, i); + + if (sdt->aml_len == exp_sdt->aml_len && + !memcmp(sdt->aml, exp_sdt->aml, sdt->aml_len)) { + /* Identical table binaries: no need to disassemble. */ + continue; + } + + fprintf(stderr, + "acpi-test: Warning! %.4s binary file mismatch. " + "Actual [aml:%s], Expected [aml:%s].\n", + exp_sdt->aml, sdt->aml_file, exp_sdt->aml_file); + + all_tables_match = all_tables_match && + test_acpi_find_diff_allowed(exp_sdt); + + /* + * don't try to decompile if IASL isn't present, in this case user + * will just 'get binary file mismatch' warnings and test failure + */ + if (!iasl) { + continue; + } + + err = load_asl(data->tables, sdt); + asl = normalize_asl(sdt->asl); + + exp_err = load_asl(exp_data.tables, exp_sdt); + exp_asl = normalize_asl(exp_sdt->asl); + + /* TODO: check for warnings */ + g_assert(!err || exp_err); + + if (g_strcmp0(asl->str, exp_asl->str)) { + sdt->tmp_files_retain = true; + if (exp_err) { + fprintf(stderr, + "Warning! iasl couldn't parse the expected aml\n"); + } else { + exp_sdt->tmp_files_retain = true; + fprintf(stderr, + "acpi-test: Warning! %.4s mismatch. " + "Actual [asl:%s, aml:%s], Expected [asl:%s, aml:%s].\n", + 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"); + if (diff_cmd) { + int ret G_GNUC_UNUSED; + char *diff = g_strdup_printf("%s %s %s", diff_cmd, + exp_sdt->asl_file, sdt->asl_file); + ret = system(diff) ; + g_free(diff); + } else { + fprintf(stderr, "acpi-test: Warning. not showing " + "difference since no diff utility is specified. " + "Set 'DIFF' environment variable to a preferred " + "diff utility and run 'make V=1 check' again to " + "see ASL difference."); + } + } + } + } + g_string_free(asl, true); + g_string_free(exp_asl, true); + } + if (!iasl && !all_tables_match) { + fprintf(stderr, "to see ASL diff between mismatched files install IASL," + " rebuild QEMU from scratch and re-run tests with V=1" + " environment variable set"); + } + g_assert(all_tables_match); + + free_test_data(&exp_data); +} + +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; + + qtest_memread(data->qts, addr, ep_table, sizeof(*ep_table)); + if (memcmp(ep_table->anchor_string, "_SM_", 4)) { + return false; + } + if (memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)) { + return false; + } + if (ep_table->structure_table_length == 0) { + return false; + } + if (ep_table->number_of_structures == 0) { + return false; + } + if (acpi_calc_checksum((uint8_t *)ep_table, sizeof *ep_table) || + acpi_calc_checksum((uint8_t *)ep_table + 0x10, + sizeof *ep_table - 0x10)) { + return false; + } + return true; +} + +static void test_smbios_entry_point(test_data *data) +{ + uint32_t off; + + /* find smbios entry point structure */ + for (off = 0xf0000; off < 0x100000; off += 0x10) { + uint8_t sig[] = "_SM_"; + int i; + + for (i = 0; i < sizeof sig - 1; ++i) { + sig[i] = qtest_readb(data->qts, off + i); + } + + if (!memcmp(sig, "_SM_", sizeof sig)) { + /* signature match, but is this a valid entry point? */ + data->smbios_ep_addr = off; + if (smbios_ep_table_ok(data)) { + break; + } + } + } + + g_assert_cmphex(off, <, 0x100000); +} + +static inline bool smbios_single_instance(uint8_t type) +{ + switch (type) { + case 0: + case 1: + case 2: + case 3: + case 16: + case 32: + case 127: + return true; + default: + return false; + } +} + +static void test_smbios_structs(test_data *data) +{ + DECLARE_BITMAP(struct_bitmap, SMBIOS_MAX_TYPE+1) = { 0 }; + struct smbios_21_entry_point *ep_table = &data->smbios_ep_table; + uint32_t addr = le32_to_cpu(ep_table->structure_table_address); + int i, len, max_len = 0; + uint8_t type, prv, crt; + + /* walk the smbios tables */ + for (i = 0; i < le16_to_cpu(ep_table->number_of_structures); i++) { + + /* grab type and formatted area length from struct header */ + type = qtest_readb(data->qts, addr); + g_assert_cmpuint(type, <=, SMBIOS_MAX_TYPE); + len = qtest_readb(data->qts, addr + 1); + + /* single-instance structs must not have been encountered before */ + if (smbios_single_instance(type)) { + g_assert(!test_bit(type, struct_bitmap)); + } + set_bit(type, struct_bitmap); + + /* seek to end of unformatted string area of this struct ("\0\0") */ + prv = crt = 1; + while (prv || crt) { + prv = crt; + crt = qtest_readb(data->qts, addr + len); + len++; + } + + /* keep track of max. struct size */ + if (max_len < len) { + max_len = len; + g_assert_cmpuint(max_len, <=, ep_table->max_structure_size); + } + + /* start of next structure */ + addr += len; + } + + /* total table length and max struct size must match entry point values */ + g_assert_cmpuint(le16_to_cpu(ep_table->structure_table_length), ==, + addr - le32_to_cpu(ep_table->structure_table_address)); + g_assert_cmpuint(le16_to_cpu(ep_table->max_structure_size), ==, max_len); + + /* required struct types must all be present */ + for (i = 0; i < data->required_struct_types_len; i++) { + g_assert(test_bit(data->required_struct_types[i], struct_bitmap)); + } +} + +static void test_acpi_one(const char *params, test_data *data) +{ + char *args; + bool use_uefi = data->uefi_fl1 && data->uefi_fl2; + + if (use_uefi) { + /* + * TODO: convert '-drive if=pflash' to new syntax (see e33763be7cd3) + * when arm/virt boad starts to support it. + */ + args = g_strdup_printf("-machine %s %s -accel tcg -nodefaults -nographic " + "-drive if=pflash,format=raw,file=%s,readonly " + "-drive if=pflash,format=raw,file=%s,snapshot=on -cdrom %s %s", + data->machine, data->tcg_only ? "" : "-accel kvm", + data->uefi_fl1, data->uefi_fl2, data->cd, params ? params : ""); + + } else { + /* Disable kernel irqchip to be able to override apic irq0. */ + args = g_strdup_printf("-machine %s,kernel-irqchip=off %s -accel tcg " + "-net none -display none %s " + "-drive id=hd0,if=none,file=%s,format=raw " + "-device ide-hd,drive=hd0 ", + data->machine, data->tcg_only ? "" : "-accel kvm", + params ? params : "", disk); + } + + data->qts = qtest_init(args); + + if (use_uefi) { + g_assert(data->scan_len); + data->rsdp_addr = acpi_find_rsdp_address_uefi(data->qts, + data->ram_start, data->scan_len); + } else { + boot_sector_test(data->qts); + data->rsdp_addr = acpi_find_rsdp_address(data->qts); + g_assert_cmphex(data->rsdp_addr, <, 0x100000); + } + + data->tables = g_array_new(false, true, sizeof(AcpiSdtTable)); + test_acpi_rsdp_table(data); + test_acpi_rxsdt_table(data); + test_acpi_fadt_table(data); + + if (getenv(ACPI_REBUILD_EXPECTED_AML)) { + dump_aml_files(data, true); + } else { + test_acpi_asl(data); + } + + /* + * TODO: make SMBIOS tests work with UEFI firmware, + * Bug on uefi-test-tools to provide entry point: + * https://bugs.launchpad.net/qemu/+bug/1821884 + */ + if (!use_uefi) { + test_smbios_entry_point(data); + test_smbios_structs(data); + } + + qtest_quit(data->qts); + g_free(args); +} + +static uint8_t base_required_struct_types[] = { + 0, 1, 3, 4, 16, 17, 19, 32, 127 +}; + +static void test_acpi_piix4_tcg(void) +{ + test_data data; + + /* Supplying -machine accel argument overrides the default (qtest). + * This is to make guest actually run. + */ + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_PC; + data.required_struct_types = base_required_struct_types; + data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); + test_acpi_one(NULL, &data); + free_test_data(&data); +} + +static void test_acpi_piix4_tcg_bridge(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_PC; + data.variant = ".bridge"; + data.required_struct_types = base_required_struct_types; + data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); + test_acpi_one("-device pci-bridge,chassis_nr=1", &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.required_struct_types = base_required_struct_types; + data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); + test_acpi_one(NULL, &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_bridge(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".bridge"; + data.required_struct_types = base_required_struct_types; + data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); + test_acpi_one("-device pci-bridge,chassis_nr=1", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_mmio64(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".mmio64", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types) + }; + + test_acpi_one("-m 128M,slots=1,maxmem=2G " + "-object memory-backend-ram,id=ram0,size=128M " + "-numa node,memdev=ram0 " + "-device pci-testdev,membar=2G", + &data); + free_test_data(&data); +} + +static void test_acpi_piix4_tcg_cphp(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_PC; + data.variant = ".cphp"; + test_acpi_one("-smp 2,cores=3,sockets=2,maxcpus=6" + " -object memory-backend-ram,id=ram0,size=64M" + " -object memory-backend-ram,id=ram1,size=64M" + " -numa node,memdev=ram0 -numa node,memdev=ram1" + " -numa dist,src=0,dst=1,val=21", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_cphp(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".cphp"; + test_acpi_one(" -smp 2,cores=3,sockets=2,maxcpus=6" + " -object memory-backend-ram,id=ram0,size=64M" + " -object memory-backend-ram,id=ram1,size=64M" + " -numa node,memdev=ram0 -numa node,memdev=ram1" + " -numa dist,src=0,dst=1,val=21", + &data); + free_test_data(&data); +} + +static uint8_t ipmi_required_struct_types[] = { + 0, 1, 3, 4, 16, 17, 19, 32, 38, 127 +}; + +static void test_acpi_q35_tcg_ipmi(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".ipmibt"; + data.required_struct_types = ipmi_required_struct_types; + data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); + test_acpi_one("-device ipmi-bmc-sim,id=bmc0" + " -device isa-ipmi-bt,bmc=bmc0", + &data); + free_test_data(&data); +} + +static void test_acpi_piix4_tcg_ipmi(void) +{ + test_data data; + + /* Supplying -machine accel argument overrides the default (qtest). + * This is to make guest actually run. + */ + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_PC; + data.variant = ".ipmikcs"; + data.required_struct_types = ipmi_required_struct_types; + data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); + test_acpi_one("-device ipmi-bmc-sim,id=bmc0" + " -device isa-ipmi-kcs,irq=0,bmc=bmc0", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_memhp(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".memhp"; + test_acpi_one(" -m 128,slots=3,maxmem=1G" + " -object memory-backend-ram,id=ram0,size=64M" + " -object memory-backend-ram,id=ram1,size=64M" + " -numa node,memdev=ram0 -numa node,memdev=ram1" + " -numa dist,src=0,dst=1,val=21", + &data); + free_test_data(&data); +} + +static void test_acpi_piix4_tcg_memhp(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_PC; + data.variant = ".memhp"; + test_acpi_one(" -m 128,slots=3,maxmem=1G" + " -object memory-backend-ram,id=ram0,size=64M" + " -object memory-backend-ram,id=ram1,size=64M" + " -numa node,memdev=ram0 -numa node,memdev=ram1" + " -numa dist,src=0,dst=1,val=21", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_numamem(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".numamem"; + test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" + " -numa node -numa node,memdev=ram0", &data); + free_test_data(&data); +} + +static void test_acpi_piix4_tcg_numamem(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_PC; + data.variant = ".numamem"; + test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" + " -numa node -numa node,memdev=ram0", &data); + free_test_data(&data); +} + +static void test_acpi_tcg_dimm_pxm(const char *machine) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = machine; + data.variant = ".dimmpxm"; + test_acpi_one(" -machine nvdimm=on,nvdimm-persistence=cpu" + " -smp 4,sockets=4" + " -m 128M,slots=3,maxmem=1G" + " -object memory-backend-ram,id=ram0,size=32M" + " -object memory-backend-ram,id=ram1,size=32M" + " -object memory-backend-ram,id=ram2,size=32M" + " -object memory-backend-ram,id=ram3,size=32M" + " -numa node,memdev=ram0,nodeid=0" + " -numa node,memdev=ram1,nodeid=1" + " -numa node,memdev=ram2,nodeid=2" + " -numa node,memdev=ram3,nodeid=3" + " -numa cpu,node-id=0,socket-id=0" + " -numa cpu,node-id=1,socket-id=1" + " -numa cpu,node-id=2,socket-id=2" + " -numa cpu,node-id=3,socket-id=3" + " -object memory-backend-ram,id=ram4,size=128M" + " -object memory-backend-ram,id=nvm0,size=128M" + " -device pc-dimm,id=dimm0,memdev=ram4,node=1" + " -device nvdimm,id=dimm1,memdev=nvm0,node=2", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_dimm_pxm(void) +{ + test_acpi_tcg_dimm_pxm(MACHINE_Q35); +} + +static void test_acpi_piix4_tcg_dimm_pxm(void) +{ + test_acpi_tcg_dimm_pxm(MACHINE_PC); +} + +static void test_acpi_virt_tcg_memhp(void) +{ + test_data data = { + .machine = "virt", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 256ULL * 1024 * 1024, + }; + + data.variant = ".memhp"; + test_acpi_one(" -cpu cortex-a57" + " -m 256M,slots=3,maxmem=1G" + " -object memory-backend-ram,id=ram0,size=128M" + " -object memory-backend-ram,id=ram1,size=128M" + " -numa node,memdev=ram0 -numa node,memdev=ram1" + " -numa dist,src=0,dst=1,val=21", + &data); + + free_test_data(&data); + +} + +static void test_acpi_virt_tcg_numamem(void) +{ + test_data data = { + .machine = "virt", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * 1024 * 1024, + }; + + data.variant = ".numamem"; + test_acpi_one(" -cpu cortex-a57" + " -object memory-backend-ram,id=ram0,size=128M" + " -numa node,memdev=ram0", + &data); + + free_test_data(&data); + +} + +static void test_acpi_tcg_acpi_hmat(const char *machine) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = machine; + data.variant = ".acpihmat"; + test_acpi_one(" -machine hmat=on" + " -smp 2,sockets=2" + " -m 128M,slots=2,maxmem=1G" + " -object memory-backend-ram,size=64M,id=m0" + " -object memory-backend-ram,size=64M,id=m1" + " -numa node,nodeid=0,memdev=m0" + " -numa node,nodeid=1,memdev=m1,initiator=0" + " -numa cpu,node-id=0,socket-id=0" + " -numa cpu,node-id=0,socket-id=1" + " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," + "data-type=access-latency,latency=1" + " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=65534M" + " -numa hmat-lb,initiator=0,target=1,hierarchy=memory," + "data-type=access-latency,latency=65534" + " -numa hmat-lb,initiator=0,target=1,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=32767M" + " -numa hmat-cache,node-id=0,size=10K,level=1," + "associativity=direct,policy=write-back,line=8" + " -numa hmat-cache,node-id=1,size=10K,level=1," + "associativity=direct,policy=write-back,line=8", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_acpi_hmat(void) +{ + test_acpi_tcg_acpi_hmat(MACHINE_Q35); +} + +static void test_acpi_piix4_tcg_acpi_hmat(void) +{ + test_acpi_tcg_acpi_hmat(MACHINE_PC); +} + +static void test_acpi_virt_tcg(void) +{ + test_data data = { + .machine = "virt", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * 1024 * 1024, + }; + + test_acpi_one("-cpu cortex-a57", &data); + free_test_data(&data); +} + +int main(int argc, char *argv[]) +{ + const char *arch = qtest_get_arch(); + int ret; + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + ret = boot_sector_init(disk); + if (ret) { + return ret; + } + + qtest_add_func("acpi/piix4", test_acpi_piix4_tcg); + qtest_add_func("acpi/piix4/bridge", test_acpi_piix4_tcg_bridge); + qtest_add_func("acpi/q35", test_acpi_q35_tcg); + qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge); + qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64); + qtest_add_func("acpi/piix4/ipmi", test_acpi_piix4_tcg_ipmi); + qtest_add_func("acpi/q35/ipmi", test_acpi_q35_tcg_ipmi); + qtest_add_func("acpi/piix4/cpuhp", test_acpi_piix4_tcg_cphp); + qtest_add_func("acpi/q35/cpuhp", test_acpi_q35_tcg_cphp); + qtest_add_func("acpi/piix4/memhp", test_acpi_piix4_tcg_memhp); + qtest_add_func("acpi/q35/memhp", test_acpi_q35_tcg_memhp); + qtest_add_func("acpi/piix4/numamem", test_acpi_piix4_tcg_numamem); + qtest_add_func("acpi/q35/numamem", test_acpi_q35_tcg_numamem); + qtest_add_func("acpi/piix4/dimmpxm", test_acpi_piix4_tcg_dimm_pxm); + qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm); + qtest_add_func("acpi/piix4/acpihmat", test_acpi_piix4_tcg_acpi_hmat); + qtest_add_func("acpi/q35/acpihmat", test_acpi_q35_tcg_acpi_hmat); + } else if (strcmp(arch, "aarch64") == 0) { + qtest_add_func("acpi/virt", test_acpi_virt_tcg); + qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); + qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp); + } + ret = g_test_run(); + boot_sector_cleanup(disk); + return ret; +} diff --git a/tests/qtest/boot-order-test.c b/tests/qtest/boot-order-test.c new file mode 100644 index 0000000000..a725bce729 --- /dev/null +++ b/tests/qtest/boot-order-test.c @@ -0,0 +1,205 @@ +/* + * Boot order test cases. + * + * Copyright (c) 2013 Red Hat Inc. + * + * Authors: + * Markus Armbruster , + * + * 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 "libqos/fw_cfg.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "standard-headers/linux/qemu_fw_cfg.h" + +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) + +typedef struct { + const char *args; + uint64_t expected_boot; + uint64_t expected_reboot; +} boot_order_test; + +static void test_a_boot_order(const char *machine, + const char *test_args, + uint64_t (*read_boot_order)(QTestState *), + uint64_t expected_boot, + uint64_t expected_reboot) +{ + uint64_t actual; + QTestState *qts; + + 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(qts, "{ 'execute': 'system_reset' }"); + /* + * system_reset only requests reset. We get a RESET event after + * the actual reset completes. Need to wait for that. + */ + qtest_qmp_eventwait(qts, "RESET"); + actual = read_boot_order(qts); + g_assert_cmphex(actual, ==, expected_reboot); + qtest_quit(qts); +} + +static void test_boot_orders(const char *machine, + uint64_t (*read_boot_order)(QTestState *), + const boot_order_test *tests) +{ + int i; + + for (i = 0; tests[i].args; i++) { + test_a_boot_order(machine, tests[i].args, + read_boot_order, + tests[i].expected_boot, + tests[i].expected_reboot); + } +} + +static uint8_t read_mc146818(QTestState *qts, uint16_t port, uint8_t reg) +{ + qtest_outb(qts, port, reg); + return qtest_inb(qts, port + 1); +} + +static uint64_t read_boot_order_pc(QTestState *qts) +{ + uint8_t b1 = read_mc146818(qts, 0x70, 0x38); + uint8_t b2 = read_mc146818(qts, 0x70, 0x3d); + + return b1 | (b2 << 8); +} + +static const boot_order_test test_cases_pc[] = { + { "", + 0x1230, 0x1230 }, + { "-no-fd-bootchk", + 0x1231, 0x1231 }, + { "-boot c", + 0x0200, 0x0200 }, + { "-boot nda", + 0x3410, 0x3410 }, + { "-boot order=", + 0, 0 }, + { "-boot order= -boot order=c", + 0x0200, 0x0200 }, + { "-boot once=a", + 0x0100, 0x1230 }, + { "-boot once=a -no-fd-bootchk", + 0x0101, 0x1231 }, + { "-boot once=a,order=c", + 0x0100, 0x0200 }, + { "-boot once=d -boot order=nda", + 0x0300, 0x3410 }, + { "-boot once=a -boot once=b -boot once=c", + 0x0200, 0x1230 }, + {} +}; + +static void test_pc_boot_order(void) +{ + test_boot_orders(NULL, read_boot_order_pc, test_cases_pc); +} + +static uint8_t read_m48t59(QTestState *qts, uint64_t addr, uint16_t reg) +{ + 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(QTestState *qts) +{ + return read_m48t59(qts, 0x80000000 + 0x74, 0x34); +} + +static const boot_order_test test_cases_prep[] = { + { "", 'c', 'c' }, + { "-boot c", 'c', 'c' }, + { "-boot d", 'd', 'd' }, + {} +}; + +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(QTestState *qts) +{ + QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xf0000510); + + return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); +} + +static const boot_order_test test_cases_fw_cfg[] = { + { "", 'c', 'c' }, + { "-boot c", 'c', 'c' }, + { "-boot d", 'd', 'd' }, + { "-boot once=d,order=c", 'd', 'c' }, + {} +}; + +static void test_pmac_oldworld_boot_order(void) +{ + test_boot_orders("g3beige", read_boot_order_pmac, test_cases_fw_cfg); +} + +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(QTestState *qts) +{ + QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xd00000510ULL); + + return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); +} + +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(QTestState *qts) +{ + QFWCFG *fw_cfg = io_fw_cfg_init(qts, 0x510); + + return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); +} + +static void test_sun4u_boot_order(void) +{ + test_boot_orders("sun4u", read_boot_order_sun4u, test_cases_fw_cfg); +} + +int main(int argc, char *argv[]) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("boot-order/pc", test_pc_boot_order); + } else if (strcmp(arch, "ppc") == 0 || strcmp(arch, "ppc64") == 0) { + qtest_add_func("boot-order/prep", test_prep_boot_order); + qtest_add_func("boot-order/pmac_oldworld", + test_pmac_oldworld_boot_order); + qtest_add_func("boot-order/pmac_newworld", + test_pmac_newworld_boot_order); + } else if (strcmp(arch, "sparc") == 0) { + qtest_add_func("boot-order/sun4m", test_sun4m_boot_order); + } else if (strcmp(arch, "sparc64") == 0) { + qtest_add_func("boot-order/sun4u", test_sun4u_boot_order); + } + + return g_test_run(); +} diff --git a/tests/qtest/boot-sector.c b/tests/qtest/boot-sector.c new file mode 100644 index 0000000000..9e66c6d013 --- /dev/null +++ b/tests/qtest/boot-sector.c @@ -0,0 +1,168 @@ +/* + * QEMU boot sector testing helpers. + * + * Copyright (c) 2016 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin + * Victor Kaplansky + * + * 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 "boot-sector.h" +#include "qemu-common.h" +#include "libqtest.h" + +#define LOW(x) ((x) & 0xff) +#define HIGH(x) ((x) >> 8) + +#define SIGNATURE 0xdead +#define SIGNATURE_OFFSET 0x10 +#define BOOT_SECTOR_ADDRESS 0x7c00 +#define SIGNATURE_ADDR (BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET) + +/* x86 boot sector code: write SIGNATURE into memory, + * then halt. + */ +static uint8_t x86_boot_sector[512] = { + /* The first sector will be placed at RAM address 00007C00, and + * the BIOS transfers control to 00007C00 + */ + + /* Data Segment register should be initialized, since pxe + * boot loader can leave it dirty. + */ + + /* 7c00: move $0000,%ax */ + [0x00] = 0xb8, + [0x01] = 0x00, + [0x02] = 0x00, + /* 7c03: move %ax,%ds */ + [0x03] = 0x8e, + [0x04] = 0xd8, + + /* 7c05: mov $0xdead,%ax */ + [0x05] = 0xb8, + [0x06] = LOW(SIGNATURE), + [0x07] = HIGH(SIGNATURE), + /* 7c08: mov %ax,0x7c10 */ + [0x08] = 0xa3, + [0x09] = LOW(SIGNATURE_ADDR), + [0x0a] = HIGH(SIGNATURE_ADDR), + + /* 7c0b cli */ + [0x0b] = 0xfa, + /* 7c0c: hlt */ + [0x0c] = 0xf4, + /* 7c0e: jmp 0x7c07=0x7c0f-3 */ + [0x0d] = 0xeb, + [0x0e] = LOW(-3), + /* We mov 0xdead here: set value to make debugging easier */ + [SIGNATURE_OFFSET] = LOW(0xface), + [SIGNATURE_OFFSET + 1] = HIGH(0xface), + /* End of boot sector marker */ + [0x1FE] = 0x55, + [0x1FF] = 0xAA, +}; + +/* For s390x, use a mini "kernel" with the appropriate signature */ +static const uint8_t s390x_psw_and_magic[] = { + 0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, /* Program status word */ + 0x02, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x50, /* Magic: */ + 0x02, 0x00, 0x00, 0x68, 0x60, 0x00, 0x00, 0x50, /* see linux_s390_magic */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* in the s390-ccw bios */ +}; +static const uint8_t s390x_code[] = { + 0xa7, 0xf4, 0x00, 0x08, /* j 0x10010 */ + 0x00, 0x00, 0x00, 0x00, + 'S', '3', '9', '0', + 'E', 'P', 0x00, 0x01, + 0xa7, 0x39, HIGH(SIGNATURE_ADDR), LOW(SIGNATURE_ADDR), /* lghi r3,0x7c10 */ + 0xa7, 0x48, LOW(SIGNATURE), HIGH(SIGNATURE), /* lhi r4,0xadde */ + 0x40, 0x40, 0x30, 0x00, /* sth r4,0(r3) */ + 0xa7, 0xf4, 0xff, 0xfa /* j 0x10010 */ +}; + +/* Create boot disk file. */ +int boot_sector_init(char *fname) +{ + int fd, ret; + size_t len; + char *boot_code; + const char *arch = qtest_get_arch(); + + fd = mkstemp(fname); + if (fd < 0) { + fprintf(stderr, "Couldn't open \"%s\": %s", fname, strerror(errno)); + return 1; + } + + if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64")) { + /* Q35 requires a minimum 0x7e000 bytes disk (bug or feature?) */ + len = MAX(0x7e000, sizeof(x86_boot_sector)); + boot_code = g_malloc0(len); + memcpy(boot_code, x86_boot_sector, sizeof(x86_boot_sector)); + } else if (g_str_equal(arch, "ppc64")) { + /* For Open Firmware based system, use a Forth script */ + boot_code = g_strdup_printf("\\ Bootscript\n%x %x c! %x %x c!\n", + LOW(SIGNATURE), SIGNATURE_ADDR, + HIGH(SIGNATURE), SIGNATURE_ADDR + 1); + len = strlen(boot_code); + } else if (g_str_equal(arch, "s390x")) { + len = 0x10000 + sizeof(s390x_code); + boot_code = g_malloc0(len); + memcpy(boot_code, s390x_psw_and_magic, sizeof(s390x_psw_and_magic)); + memcpy(&boot_code[0x10000], s390x_code, sizeof(s390x_code)); + } else { + g_assert_not_reached(); + } + + ret = write(fd, boot_code, len); + close(fd); + + g_free(boot_code); + + if (ret != len) { + fprintf(stderr, "Could not write \"%s\"", fname); + return 1; + } + + return 0; +} + +/* Loop until signature in memory is OK. */ +void boot_sector_test(QTestState *qts) +{ + uint8_t signature_low; + uint8_t signature_high; + uint16_t signature; + int i; + + /* Wait at most 600 seconds (test is slow with TCI and --enable-debug) */ +#define TEST_DELAY (1 * G_USEC_PER_SEC / 10) +#define TEST_CYCLES MAX((600 * G_USEC_PER_SEC / TEST_DELAY), 1) + + /* Poll until code has run and modified memory. Once it has we know BIOS + * initialization is done. TODO: check that IP reached the halt + * instruction. + */ + for (i = 0; i < TEST_CYCLES; ++i) { + signature_low = qtest_readb(qts, SIGNATURE_ADDR); + signature_high = qtest_readb(qts, SIGNATURE_ADDR + 1); + signature = (signature_high << 8) | signature_low; + if (signature == SIGNATURE) { + break; + } + g_usleep(TEST_DELAY); + } + + g_assert_cmphex(signature, ==, SIGNATURE); +} + +/* unlink boot disk file. */ +void boot_sector_cleanup(const char *fname) +{ + unlink(fname); +} diff --git a/tests/qtest/boot-sector.h b/tests/qtest/boot-sector.h new file mode 100644 index 0000000000..6ee6bb4d97 --- /dev/null +++ b/tests/qtest/boot-sector.h @@ -0,0 +1,28 @@ +/* + * QEMU boot sector testing helpers. + * + * Copyright (c) 2016 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin + * Victor Kaplansky + * + * 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 TEST_BOOT_SECTOR_H +#define TEST_BOOT_SECTOR_H + +#include "libqtest.h" + +/* Create boot disk file. fname must be a suitable string for mkstemp() */ +int boot_sector_init(char *fname); + +/* Loop until signature in memory is OK. */ +void boot_sector_test(QTestState *qts); + +/* unlink boot disk file. */ +void boot_sector_cleanup(const char *fname); + +#endif /* TEST_BOOT_SECTOR_H */ diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c new file mode 100644 index 0000000000..05c7f44457 --- /dev/null +++ b/tests/qtest/boot-serial-test.c @@ -0,0 +1,254 @@ +/* + * Test serial output of some machines. + * + * Copyright 2016 Thomas Huth, Red Hat Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 + * or later. See the COPYING file in the top-level directory. + * + * This test is used to check that the serial output of the firmware + * (that we provide for some machines) or some small mini-kernels that + * we provide here contains an expected string. Thus we check that the + * firmware/kernel still boots at least to a certain point and so we + * know that the machine is not completely broken. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +static const uint8_t kernel_mcf5208[] = { + 0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */ + 0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */ + 0x11, 0x7c, 0x00, 0x04, 0x00, 0x08, /* move.b #4,8(%a0) Enable TX */ + 0x11, 0x40, 0x00, 0x0c, /* move.b %d0,12(%a0) Print 'T' */ + 0x60, 0xfa /* bra.s loop */ +}; + +static const uint8_t bios_nextcube[] = { + 0x06, 0x00, 0x00, 0x00, /* Initial SP */ + 0x01, 0x00, 0x00, 0x08, /* Initial PC */ + 0x41, 0xf9, 0x02, 0x11, 0x80, 0x00, /* lea 0x02118000,%a0 */ + 0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */ + 0x11, 0x7c, 0x00, 0x05, 0x00, 0x01, /* move.b #5,1(%a0) Sel TXCTRL */ + 0x11, 0x7c, 0x00, 0x68, 0x00, 0x01, /* move.b #0x68,1(%a0) Enable TX */ + 0x11, 0x40, 0x00, 0x03, /* move.b %d0,3(%a0) Print 'T' */ + 0x60, 0xfa /* bra.s loop */ +}; + +static const uint8_t kernel_pls3adsp1800[] = { + 0xb0, 0x00, 0x84, 0x00, /* imm 0x8400 */ + 0x30, 0x60, 0x00, 0x04, /* addik r3,r0,4 */ + 0x30, 0x80, 0x00, 0x54, /* addik r4,r0,'T' */ + 0xf0, 0x83, 0x00, 0x00, /* sbi r4,r3,0 */ + 0xb8, 0x00, 0xff, 0xfc /* bri -4 loop */ +}; + +static const uint8_t kernel_plml605[] = { + 0xe0, 0x83, 0x00, 0xb0, /* imm 0x83e0 */ + 0x00, 0x10, 0x60, 0x30, /* addik r3,r0,0x1000 */ + 0x54, 0x00, 0x80, 0x30, /* addik r4,r0,'T' */ + 0x00, 0x00, 0x83, 0xf0, /* sbi r4,r3,0 */ + 0xfc, 0xff, 0x00, 0xb8 /* bri -4 loop */ +}; + +static const uint8_t bios_moxiesim[] = { + 0x20, 0x10, 0x00, 0x00, 0x03, 0xf8, /* ldi.s r1,0x3f8 */ + 0x1b, 0x20, 0x00, 0x00, 0x00, 0x54, /* ldi.b r2,'T' */ + 0x1e, 0x12, /* st.b r1,r2 */ + 0x1a, 0x00, 0x00, 0x00, 0x10, 0x00 /* jmpa 0x1000 */ +}; + +static const uint8_t bios_raspi2[] = { + 0x08, 0x30, 0x9f, 0xe5, /* ldr r3,[pc,#8] Get base */ + 0x54, 0x20, 0xa0, 0xe3, /* mov r2,#'T' */ + 0x00, 0x20, 0xc3, 0xe5, /* strb r2,[r3] */ + 0xfb, 0xff, 0xff, 0xea, /* b loop */ + 0x00, 0x10, 0x20, 0x3f, /* 0x3f201000 = UART0 base addr */ +}; + +static const uint8_t kernel_aarch64[] = { + 0x81, 0x0a, 0x80, 0x52, /* mov w1, #0x54 */ + 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 */ + 0x41, 0x00, 0x00, 0x39, /* strb w1, [x2] */ + 0xfd, 0xff, 0xff, 0x17, /* b -12 (loop) */ +}; + +static const uint8_t kernel_nrf51[] = { + 0x00, 0x00, 0x00, 0x00, /* Stack top address */ + 0x09, 0x00, 0x00, 0x00, /* Reset handler address */ + 0x04, 0x4a, /* ldr r2, [pc, #16] Get ENABLE */ + 0x04, 0x21, /* movs r1, #4 */ + 0x11, 0x60, /* str r1, [r2] */ + 0x04, 0x4a, /* ldr r2, [pc, #16] Get STARTTX */ + 0x01, 0x21, /* movs r1, #1 */ + 0x11, 0x60, /* str r1, [r2] */ + 0x03, 0x4a, /* ldr r2, [pc, #12] Get TXD */ + 0x54, 0x21, /* movs r1, 'T' */ + 0x11, 0x60, /* str r1, [r2] */ + 0xfe, 0xe7, /* b . */ + 0x00, 0x25, 0x00, 0x40, /* 0x40002500 = UART ENABLE */ + 0x08, 0x20, 0x00, 0x40, /* 0x40002008 = UART STARTTX */ + 0x1c, 0x25, 0x00, 0x40 /* 0x4000251c = UART TXD */ +}; + +typedef struct testdef { + const char *arch; /* Target architecture */ + const char *machine; /* Name of the machine */ + const char *extra; /* Additional parameters */ + const char *expect; /* Expected string in the serial output */ + size_t codesize; /* Size of the kernel or bios data */ + const uint8_t *kernel; /* Set in case we use our own mini kernel */ + const uint8_t *bios; /* Set in case we use our own mini bios */ +} testdef_t; + +static testdef_t tests[] = { + { "alpha", "clipper", "", "PCI:" }, + { "ppc", "ppce500", "", "U-Boot" }, + { "ppc", "40p", "-vga none -boot d", "Trying cd:," }, + { "ppc", "g3beige", "", "PowerPC,750" }, + { "ppc", "mac99", "", "PowerPC,G4" }, + { "ppc", "sam460ex", "-m 256", "DRAM: 256 MiB" }, + { "ppc64", "ppce500", "", "U-Boot" }, + { "ppc64", "40p", "-m 192", "Memory: 192M" }, + { "ppc64", "mac99", "", "PowerPC,970FX" }, + { "ppc64", "pseries", + "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken", + "Open Firmware" }, + { "ppc64", "powernv8", "", "OPAL" }, + { "ppc64", "powernv9", "", "OPAL" }, + { "ppc64", "sam460ex", "-device e1000", "8086 100e" }, + { "i386", "isapc", "-cpu qemu32 -device sga", "SGABIOS" }, + { "i386", "pc", "-device sga", "SGABIOS" }, + { "i386", "q35", "-device sga", "SGABIOS" }, + { "x86_64", "isapc", "-cpu qemu32 -device sga", "SGABIOS" }, + { "x86_64", "q35", "-device sga", "SGABIOS" }, + { "sparc", "LX", "", "TMS390S10" }, + { "sparc", "SS-4", "", "MB86904" }, + { "sparc", "SS-600MP", "", "TMS390Z55" }, + { "sparc64", "sun4u", "", "UltraSPARC" }, + { "s390x", "s390-ccw-virtio", "", "device" }, + { "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 }, + { "m68k", "next-cube", "", "TT", sizeof(bios_nextcube), 0, bios_nextcube }, + { "microblaze", "petalogix-s3adsp1800", "", "TT", + sizeof(kernel_pls3adsp1800), kernel_pls3adsp1800 }, + { "microblazeel", "petalogix-ml605", "", "TT", + sizeof(kernel_plml605), kernel_plml605 }, + { "moxie", "moxiesim", "", "TT", sizeof(bios_moxiesim), 0, bios_moxiesim }, + { "arm", "raspi2", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 }, + { "hppa", "hppa", "", "SeaBIOS wants SYSTEM HALT" }, + { "aarch64", "virt", "-cpu cortex-a57", "TT", sizeof(kernel_aarch64), + kernel_aarch64 }, + { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, + + { NULL } +}; + +static bool check_guest_output(QTestState *qts, const testdef_t *test, int fd) +{ + int nbr = 0, pos = 0, ccnt; + time_t now, start = time(NULL); + char ch; + + /* Poll serial output... */ + while (1) { + ccnt = 0; + while (ccnt++ < 512 && (nbr = read(fd, &ch, 1)) == 1) { + if (ch == test->expect[pos]) { + pos += 1; + if (test->expect[pos] == '\0') { + /* We've reached the end of the expected string! */ + return true; + } + } else { + pos = 0; + } + } + g_assert(nbr >= 0); + /* Wait only if the child is still alive. */ + if (!qtest_probe_child(qts)) { + break; + } + /* Wait at most 360 seconds. */ + now = time(NULL); + if (now - start >= 360) { + break; + } + g_usleep(10000); + } + + return false; +} + +static void test_machine(const void *data) +{ + const testdef_t *test = data; + char serialtmp[] = "/tmp/qtest-boot-serial-sXXXXXX"; + char codetmp[] = "/tmp/qtest-boot-serial-cXXXXXX"; + const char *codeparam = ""; + const uint8_t *code = NULL; + QTestState *qts; + int ser_fd; + + ser_fd = mkstemp(serialtmp); + g_assert(ser_fd != -1); + + if (test->kernel) { + code = test->kernel; + codeparam = "-kernel"; + } else if (test->bios) { + code = test->bios; + codeparam = "-bios"; + } + + if (code) { + ssize_t wlen; + int code_fd; + + code_fd = mkstemp(codetmp); + g_assert(code_fd != -1); + wlen = write(code_fd, code, test->codesize); + g_assert(wlen == test->codesize); + close(code_fd); + } + + /* + * Make sure that this test uses tcg if available: It is used as a + * fast-enough smoketest for that. + */ + qts = qtest_initf("%s %s -M %s -no-shutdown " + "-chardev file,id=serial0,path=%s " + "-serial chardev:serial0 -accel tcg -accel kvm %s", + codeparam, code ? codetmp : "", test->machine, + serialtmp, test->extra); + if (code) { + unlink(codetmp); + } + + if (!check_guest_output(qts, test, ser_fd)) { + g_error("Failed to find expected string. Please check '%s'", + serialtmp); + } + unlink(serialtmp); + + qtest_quit(qts); + + close(ser_fd); +} + +int main(int argc, char *argv[]) +{ + const char *arch = qtest_get_arch(); + int i; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; tests[i].arch != NULL; i++) { + if (strcmp(arch, tests[i].arch) == 0) { + char *name = g_strdup_printf("boot-serial/%s", tests[i].machine); + qtest_add_data_func(name, &tests[i], test_machine); + g_free(name); + } + } + + return g_test_run(); +} diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c new file mode 100644 index 0000000000..67635e387a --- /dev/null +++ b/tests/qtest/cdrom-test.c @@ -0,0 +1,228 @@ +/* + * Various tests for emulated CD-ROM drives. + * + * Copyright (c) 2018 Red Hat Inc. + * + * Author: + * Thomas Huth + * + * This work is licensed under the terms of the GNU GPL, version 2 + * or later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "boot-sector.h" +#include "qapi/qmp/qdict.h" + +static char isoimage[] = "cdrom-boot-iso-XXXXXX"; + +static int exec_genisoimg(const char **args) +{ + gchar *out_err = NULL; + gint exit_status = -1; + bool success; + + success = g_spawn_sync(NULL, (gchar **)args, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL, + NULL, NULL, NULL, &out_err, &exit_status, NULL); + if (!success) { + return -ENOENT; + } + if (out_err) { + fputs(out_err, stderr); + g_free(out_err); + } + + return exit_status; +} + +static int prepare_image(const char *arch, char *isoimage) +{ + char srcdir[] = "cdrom-test-dir-XXXXXX"; + char *codefile = NULL; + int ifh, ret = -1; + const char *args[] = { + "genisoimage", "-quiet", "-l", "-no-emul-boot", + "-b", NULL, "-o", isoimage, srcdir, NULL + }; + + ifh = mkstemp(isoimage); + if (ifh < 0) { + perror("Error creating temporary iso image file"); + return -1; + } + if (!mkdtemp(srcdir)) { + perror("Error creating temporary directory"); + goto cleanup; + } + + if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") || + g_str_equal(arch, "s390x")) { + codefile = g_strdup_printf("%s/bootcode-XXXXXX", srcdir); + ret = boot_sector_init(codefile); + if (ret) { + goto cleanup; + } + } else { + /* Just create a dummy file */ + char txt[] = "empty disc"; + codefile = g_strdup_printf("%s/readme.txt", srcdir); + if (!g_file_set_contents(codefile, txt, sizeof(txt) - 1, NULL)) { + fprintf(stderr, "Failed to create '%s'\n", codefile); + goto cleanup; + } + } + + args[5] = strchr(codefile, '/') + 1; + ret = exec_genisoimg(args); + if (ret) { + fprintf(stderr, "genisoimage failed: %i\n", ret); + } + + unlink(codefile); + +cleanup: + g_free(codefile); + rmdir(srcdir); + close(ifh); + + return ret; +} + +/** + * Check that at least the -cdrom parameter is basically working, i.e. we can + * see the filename of the ISO image in the output of "info block" afterwards + */ +static void test_cdrom_param(gconstpointer data) +{ + QTestState *qts; + char *resp; + + qts = qtest_initf("-M %s -cdrom %s", (const char *)data, isoimage); + resp = qtest_hmp(qts, "info block"); + g_assert(strstr(resp, isoimage) != 0); + g_free(resp); + qtest_quit(qts); +} + +static void add_cdrom_param_tests(const char **machines) +{ + while (*machines) { + char *testname = g_strdup_printf("cdrom/param/%s", *machines); + qtest_add_data_func(testname, *machines, test_cdrom_param); + g_free(testname); + machines++; + } +} + +static void test_cdboot(gconstpointer data) +{ + QTestState *qts; + + qts = qtest_initf("-accel kvm -accel tcg -no-shutdown %s%s", (const char *)data, + isoimage); + boot_sector_test(qts); + qtest_quit(qts); +} + +static void add_x86_tests(void) +{ + qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); + qtest_add_data_func("cdrom/boot/virtio-scsi", + "-device virtio-scsi -device scsi-cd,drive=cdr " + "-blockdev file,node-name=cdr,filename=", test_cdboot); + /* + * Unstable CI test under load + * See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html + */ + if (g_test_slow()) { + qtest_add_data_func("cdrom/boot/isapc", "-M isapc " + "-drive if=ide,media=cdrom,file=", test_cdboot); + } + qtest_add_data_func("cdrom/boot/am53c974", + "-device am53c974 -device scsi-cd,drive=cd1 " + "-drive if=none,id=cd1,format=raw,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/dc390", + "-device dc390 -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/lsi53c895a", + "-device lsi53c895a -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/megasas", "-M q35 " + "-device megasas -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 " + "-device megasas-gen2 -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); +} + +static void add_s390x_tests(void) +{ + qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); + qtest_add_data_func("cdrom/boot/virtio-scsi", + "-device virtio-scsi -device scsi-cd,drive=cdr " + "-blockdev file,node-name=cdr,filename=", test_cdboot); +} + +int main(int argc, char **argv) +{ + int ret; + const char *arch = qtest_get_arch(); + const char *genisocheck[] = { "genisoimage", "-version", NULL }; + + g_test_init(&argc, &argv, NULL); + + if (exec_genisoimg(genisocheck)) { + /* genisoimage not available - so can't run tests */ + return g_test_run(); + } + + ret = prepare_image(arch, isoimage); + if (ret) { + return ret; + } + + if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64")) { + add_x86_tests(); + } else if (g_str_equal(arch, "s390x")) { + add_s390x_tests(); + } else if (g_str_equal(arch, "ppc64")) { + const char *ppcmachines[] = { + "pseries", "mac99", "g3beige", "40p", "prep", NULL + }; + add_cdrom_param_tests(ppcmachines); + } else if (g_str_equal(arch, "sparc")) { + const char *sparcmachines[] = { + "LX", "SPARCClassic", "SPARCbook", "SS-10", "SS-20", "SS-4", + "SS-5", "SS-600MP", "Voyager", "leon3_generic", NULL + }; + add_cdrom_param_tests(sparcmachines); + } else if (g_str_equal(arch, "sparc64")) { + const char *sparc64machines[] = { + "niagara", "sun4u", "sun4v", NULL + }; + add_cdrom_param_tests(sparc64machines); + } else if (!strncmp(arch, "mips64", 6)) { + const char *mips64machines[] = { + "magnum", "malta", "mips", "pica61", NULL + }; + add_cdrom_param_tests(mips64machines); + } else if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { + const char *armmachines[] = { + "realview-eb", "realview-eb-mpcore", "realview-pb-a8", + "realview-pbx-a9", "versatileab", "versatilepb", "vexpress-a15", + "vexpress-a9", "virt", NULL + }; + add_cdrom_param_tests(armmachines); + } else { + const char *nonemachine[] = { "none", NULL }; + add_cdrom_param_tests(nonemachine); + } + + ret = g_test_run(); + + unlink(isoimage); + + return ret; +} diff --git a/tests/qtest/cpu-plug-test.c b/tests/qtest/cpu-plug-test.c new file mode 100644 index 0000000000..e8ffbbce4b --- /dev/null +++ b/tests/qtest/cpu-plug-test.c @@ -0,0 +1,257 @@ +/* + * QTest testcase for CPU plugging + * + * Copyright (c) 2015 SUSE Linux GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "libqtest-single.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" + +struct PlugTestData { + char *machine; + const char *cpu_model; + char *device_model; + unsigned sockets; + unsigned cores; + unsigned threads; + unsigned maxcpus; +}; +typedef struct PlugTestData PlugTestData; + +static void test_plug_with_cpu_add(gconstpointer data) +{ + const PlugTestData *s = data; + char *args; + QDict *response; + unsigned int i; + + args = g_strdup_printf("-machine %s -cpu %s " + "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", + s->machine, s->cpu_model, + s->sockets, s->cores, s->threads, s->maxcpus); + qtest_start(args); + + for (i = 1; i < s->maxcpus; i++) { + response = qmp("{ 'execute': 'cpu-add'," + " 'arguments': { 'id': %d } }", i); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + } + + qtest_end(); + g_free(args); +} + +static void test_plug_without_cpu_add(gconstpointer data) +{ + const PlugTestData *s = data; + char *args; + QDict *response; + + args = g_strdup_printf("-machine %s -cpu %s " + "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", + s->machine, s->cpu_model, + s->sockets, s->cores, s->threads, s->maxcpus); + qtest_start(args); + + response = qmp("{ 'execute': 'cpu-add'," + " 'arguments': { 'id': %d } }", + s->sockets * s->cores * s->threads); + g_assert(response); + g_assert(qdict_haskey(response, "error")); + qobject_unref(response); + + qtest_end(); + g_free(args); +} + +static void test_plug_with_device_add(gconstpointer data) +{ + const PlugTestData *td = data; + char *args; + QTestState *qts; + QDict *resp; + QList *cpus; + QObject *e; + int hotplugged = 0; + + args = g_strdup_printf("-machine %s -cpu %s " + "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", + td->machine, td->cpu_model, + td->sockets, td->cores, td->threads, td->maxcpus); + qts = qtest_init(args); + + resp = qtest_qmp(qts, "{ 'execute': 'query-hotpluggable-cpus'}"); + g_assert(qdict_haskey(resp, "return")); + cpus = qdict_get_qlist(resp, "return"); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + const QDict *cpu, *props; + + cpu = qobject_to(QDict, e); + if (qdict_haskey(cpu, "qom-path")) { + qobject_unref(e); + continue; + } + + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + qtest_qmp_device_add_qdict(qts, td->device_model, props); + hotplugged++; + qobject_unref(e); + } + + /* make sure that there were hotplugged CPUs */ + g_assert(hotplugged); + qobject_unref(resp); + qtest_quit(qts); + g_free(args); +} + +static void test_data_free(gpointer data) +{ + PlugTestData *pc = data; + + g_free(pc->machine); + g_free(pc->device_model); + g_free(pc); +} + +static void add_pc_test_case(const char *mname) +{ + char *path; + PlugTestData *data; + + if (!g_str_has_prefix(mname, "pc-")) { + return; + } + data = g_new(PlugTestData, 1); + data->machine = g_strdup(mname); + data->cpu_model = "Haswell"; /* 1.3+ theoretically */ + data->device_model = g_strdup_printf("%s-%s-cpu", data->cpu_model, + qtest_get_arch()); + data->sockets = 1; + data->cores = 3; + data->threads = 2; + data->maxcpus = data->sockets * data->cores * data->threads; + if (g_str_has_suffix(mname, "-1.4") || + (strcmp(mname, "pc-1.3") == 0) || + (strcmp(mname, "pc-1.2") == 0) || + (strcmp(mname, "pc-1.1") == 0) || + (strcmp(mname, "pc-1.0") == 0)) { + path = g_strdup_printf("cpu-plug/%s/init/%ux%ux%u&maxcpus=%u", + mname, data->sockets, data->cores, + data->threads, data->maxcpus); + qtest_add_data_func_full(path, data, test_plug_without_cpu_add, + test_data_free); + g_free(path); + } else { + PlugTestData *data2 = g_memdup(data, sizeof(PlugTestData)); + + data2->machine = g_strdup(data->machine); + data2->device_model = g_strdup(data->device_model); + + path = g_strdup_printf("cpu-plug/%s/cpu-add/%ux%ux%u&maxcpus=%u", + mname, data->sockets, data->cores, + data->threads, data->maxcpus); + qtest_add_data_func_full(path, data, test_plug_with_cpu_add, + test_data_free); + g_free(path); + path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", + mname, data2->sockets, data2->cores, + data2->threads, data2->maxcpus); + qtest_add_data_func_full(path, data2, test_plug_with_device_add, + test_data_free); + g_free(path); + } +} + +static void add_pseries_test_case(const char *mname) +{ + char *path; + PlugTestData *data; + + if (!g_str_has_prefix(mname, "pseries-") || + (g_str_has_prefix(mname, "pseries-2.") && atoi(&mname[10]) < 7)) { + return; + } + data = g_new(PlugTestData, 1); + data->machine = g_strdup(mname); + data->cpu_model = "power8_v2.0"; + data->device_model = g_strdup("power8_v2.0-spapr-cpu-core"); + data->sockets = 2; + data->cores = 3; + data->threads = 1; + data->maxcpus = data->sockets * data->cores * data->threads; + + path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", + mname, data->sockets, data->cores, + data->threads, data->maxcpus); + qtest_add_data_func_full(path, data, test_plug_with_device_add, + test_data_free); + g_free(path); +} + +static void add_s390x_test_case(const char *mname) +{ + char *path; + PlugTestData *data, *data2; + + if (!g_str_has_prefix(mname, "s390-ccw-virtio-")) { + return; + } + + data = g_new(PlugTestData, 1); + data->machine = g_strdup(mname); + data->cpu_model = "qemu"; + data->device_model = g_strdup("qemu-s390x-cpu"); + data->sockets = 1; + data->cores = 3; + data->threads = 1; + data->maxcpus = data->sockets * data->cores * data->threads; + + data2 = g_memdup(data, sizeof(PlugTestData)); + data2->machine = g_strdup(data->machine); + data2->device_model = g_strdup(data->device_model); + + path = g_strdup_printf("cpu-plug/%s/cpu-add/%ux%ux%u&maxcpus=%u", + mname, data->sockets, data->cores, + data->threads, data->maxcpus); + qtest_add_data_func_full(path, data, test_plug_with_cpu_add, + test_data_free); + g_free(path); + + path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", + mname, data2->sockets, data2->cores, + data2->threads, data2->maxcpus); + qtest_add_data_func_full(path, data2, test_plug_with_device_add, + test_data_free); + g_free(path); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_cb_for_every_machine(add_pc_test_case, g_test_quick()); + } else if (g_str_equal(arch, "ppc64")) { + qtest_cb_for_every_machine(add_pseries_test_case, g_test_quick()); + } else if (g_str_equal(arch, "s390x")) { + qtest_cb_for_every_machine(add_s390x_test_case, g_test_quick()); + } + + return g_test_run(); +} diff --git a/tests/qtest/dbus-vmstate-test.c b/tests/qtest/dbus-vmstate-test.c new file mode 100644 index 0000000000..2e5e47dec2 --- /dev/null +++ b/tests/qtest/dbus-vmstate-test.c @@ -0,0 +1,382 @@ +#include "qemu/osdep.h" +#include +#include +#include "libqtest.h" +#include "qemu-common.h" +#include "dbus-vmstate1.h" +#include "migration-helpers.h" + +static char *workdir; + +typedef struct TestServerId { + const char *name; + const char *data; + size_t size; +} TestServerId; + +static const TestServerId idA = { + "idA", "I'am\0idA!", sizeof("I'am\0idA!") +}; + +static const TestServerId idB = { + "idB", "I'am\0idB!", sizeof("I'am\0idB!") +}; + +typedef struct TestServer { + const TestServerId *id; + bool save_called; + bool load_called; +} TestServer; + +typedef struct Test { + const char *id_list; + bool migrate_fail; + bool without_dst_b; + TestServer srcA; + TestServer dstA; + TestServer srcB; + TestServer dstB; + GMainLoop *loop; + QTestState *src_qemu; +} Test; + +static gboolean +vmstate_load(VMState1 *object, GDBusMethodInvocation *invocation, + const gchar *arg_data, gpointer user_data) +{ + TestServer *h = user_data; + g_autoptr(GVariant) var = NULL; + GVariant *args; + const uint8_t *data; + size_t size; + + args = g_dbus_method_invocation_get_parameters(invocation); + var = g_variant_get_child_value(args, 0); + data = g_variant_get_fixed_array(var, &size, sizeof(char)); + g_assert_cmpuint(size, ==, h->id->size); + g_assert(!memcmp(data, h->id->data, h->id->size)); + h->load_called = true; + + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); + return TRUE; +} + +static gboolean +vmstate_save(VMState1 *object, GDBusMethodInvocation *invocation, + gpointer user_data) +{ + TestServer *h = user_data; + GVariant *var; + + var = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + h->id->data, h->id->size, sizeof(char)); + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(@ay)", var)); + h->save_called = true; + + return TRUE; +} + +typedef struct WaitNamed { + GMainLoop *loop; + bool named; +} WaitNamed; + +static void +named_cb(GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + WaitNamed *t = user_data; + + t->named = true; + g_main_loop_quit(t->loop); +} + +static GDBusConnection * +get_connection(Test *test, guint *ownid) +{ + g_autofree gchar *addr = NULL; + WaitNamed *wait; + GError *err = NULL; + GDBusConnection *c; + + wait = g_new0(WaitNamed, 1); + wait->loop = test->loop; + addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, NULL, &err); + g_assert_no_error(err); + + c = g_dbus_connection_new_for_address_sync( + addr, + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, NULL, &err); + g_assert_no_error(err); + *ownid = g_bus_own_name_on_connection(c, "org.qemu.VMState1", + G_BUS_NAME_OWNER_FLAGS_NONE, + named_cb, named_cb, wait, g_free); + if (!wait->named) { + g_main_loop_run(wait->loop); + } + + return c; +} + +static GDBusObjectManagerServer * +get_server(GDBusConnection *conn, TestServer *s, const TestServerId *id) +{ + g_autoptr(GDBusObjectSkeleton) sk = NULL; + g_autoptr(VMState1Skeleton) v = NULL; + GDBusObjectManagerServer *os; + + s->id = id; + os = g_dbus_object_manager_server_new("/org/qemu"); + sk = g_dbus_object_skeleton_new("/org/qemu/VMState1"); + + v = VMSTATE1_SKELETON(vmstate1_skeleton_new()); + g_object_set(v, "id", id->name, NULL); + + g_signal_connect(v, "handle-load", G_CALLBACK(vmstate_load), s); + g_signal_connect(v, "handle-save", G_CALLBACK(vmstate_save), s); + + g_dbus_object_skeleton_add_interface(sk, G_DBUS_INTERFACE_SKELETON(v)); + g_dbus_object_manager_server_export(os, sk); + g_dbus_object_manager_server_set_connection(os, conn); + + return os; +} + +static void +set_id_list(Test *test, QTestState *s) +{ + if (!test->id_list) { + return; + } + + g_assert(!qmp_rsp_is_err(qtest_qmp(s, + "{ 'execute': 'qom-set', 'arguments': " + "{ 'path': '/objects/dv', 'property': 'id-list', 'value': %s } }", + test->id_list))); +} + +static gpointer +dbus_vmstate_thread(gpointer data) +{ + GMainLoop *loop = data; + + g_main_loop_run(loop); + + return NULL; +} + +static void +test_dbus_vmstate(Test *test) +{ + g_autofree char *src_qemu_args = NULL; + g_autofree char *dst_qemu_args = NULL; + g_autoptr(GTestDBus) srcbus = NULL; + g_autoptr(GTestDBus) dstbus = NULL; + g_autoptr(GDBusConnection) srcconnA = NULL; + g_autoptr(GDBusConnection) srcconnB = NULL; + g_autoptr(GDBusConnection) dstconnA = NULL; + g_autoptr(GDBusConnection) dstconnB = NULL; + g_autoptr(GDBusObjectManagerServer) srcserverA = NULL; + g_autoptr(GDBusObjectManagerServer) srcserverB = NULL; + g_autoptr(GDBusObjectManagerServer) dstserverA = NULL; + g_autoptr(GDBusObjectManagerServer) dstserverB = NULL; + g_auto(GStrv) srcaddr = NULL; + g_auto(GStrv) dstaddr = NULL; + g_autoptr(GThread) thread = NULL; + g_autoptr(GMainLoop) loop = NULL; + g_autofree char *uri = NULL; + QTestState *src_qemu = NULL, *dst_qemu = NULL; + guint ownsrcA, ownsrcB, owndstA, owndstB; + + uri = g_strdup_printf("unix:%s/migsocket", workdir); + + loop = g_main_loop_new(NULL, FALSE); + test->loop = loop; + + srcbus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(srcbus); + srcconnA = get_connection(test, &ownsrcA); + srcserverA = get_server(srcconnA, &test->srcA, &idA); + srcconnB = get_connection(test, &ownsrcB); + srcserverB = get_server(srcconnB, &test->srcB, &idB); + + /* remove ,guid=foo part */ + srcaddr = g_strsplit(g_test_dbus_get_bus_address(srcbus), ",", 2); + src_qemu_args = + g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s", srcaddr[0]); + + dstbus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(dstbus); + dstconnA = get_connection(test, &owndstA); + dstserverA = get_server(dstconnA, &test->dstA, &idA); + if (!test->without_dst_b) { + dstconnB = get_connection(test, &owndstB); + dstserverB = get_server(dstconnB, &test->dstB, &idB); + } + + dstaddr = g_strsplit(g_test_dbus_get_bus_address(dstbus), ",", 2); + dst_qemu_args = + g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s -incoming %s", + dstaddr[0], uri); + + src_qemu = qtest_init(src_qemu_args); + dst_qemu = qtest_init(dst_qemu_args); + set_id_list(test, src_qemu); + set_id_list(test, dst_qemu); + + thread = g_thread_new("dbus-vmstate-thread", dbus_vmstate_thread, loop); + + migrate_qmp(src_qemu, uri, "{}"); + test->src_qemu = src_qemu; + if (test->migrate_fail) { + wait_for_migration_fail(src_qemu, true); + qtest_set_expected_status(dst_qemu, 1); + } else { + wait_for_migration_complete(src_qemu); + } + + qtest_quit(dst_qemu); + qtest_quit(src_qemu); + g_bus_unown_name(ownsrcA); + g_bus_unown_name(ownsrcB); + g_bus_unown_name(owndstA); + if (!test->without_dst_b) { + g_bus_unown_name(owndstB); + } + + g_main_loop_quit(test->loop); +} + +static void +check_not_migrated(TestServer *s, TestServer *d) +{ + assert(!s->save_called); + assert(!s->load_called); + assert(!d->save_called); + assert(!d->load_called); +} + +static void +check_migrated(TestServer *s, TestServer *d) +{ + assert(s->save_called); + assert(!s->load_called); + assert(!d->save_called); + assert(d->load_called); +} + +static void +test_dbus_vmstate_without_list(void) +{ + Test test = { 0, }; + + test_dbus_vmstate(&test); + + check_migrated(&test.srcA, &test.dstA); + check_migrated(&test.srcB, &test.dstB); +} + +static void +test_dbus_vmstate_with_list(void) +{ + Test test = { .id_list = "idA,idB" }; + + test_dbus_vmstate(&test); + + check_migrated(&test.srcA, &test.dstA); + check_migrated(&test.srcB, &test.dstB); +} + +static void +test_dbus_vmstate_only_a(void) +{ + Test test = { .id_list = "idA" }; + + test_dbus_vmstate(&test); + + check_migrated(&test.srcA, &test.dstA); + check_not_migrated(&test.srcB, &test.dstB); +} + +static void +test_dbus_vmstate_missing_src(void) +{ + Test test = { .id_list = "idA,idC", .migrate_fail = true }; + + /* run in subprocess to silence QEMU error reporting */ + if (g_test_subprocess()) { + test_dbus_vmstate(&test); + check_not_migrated(&test.srcA, &test.dstA); + check_not_migrated(&test.srcB, &test.dstB); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); +} + +static void +test_dbus_vmstate_missing_dst(void) +{ + Test test = { .id_list = "idA,idB", + .without_dst_b = true, + .migrate_fail = true }; + + /* run in subprocess to silence QEMU error reporting */ + if (g_test_subprocess()) { + test_dbus_vmstate(&test); + assert(test.srcA.save_called); + assert(test.srcB.save_called); + assert(!test.dstB.save_called); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); +} + +int +main(int argc, char **argv) +{ + GError *err = NULL; + g_autofree char *dbus_daemon = NULL; + int ret; + + dbus_daemon = g_build_filename(G_STRINGIFY(SRCDIR), + "tests", + "dbus-vmstate-daemon.sh", + NULL); + g_setenv("G_TEST_DBUS_DAEMON", dbus_daemon, true); + + g_test_init(&argc, &argv, NULL); + + workdir = g_dir_make_tmp("dbus-vmstate-test-XXXXXX", &err); + if (!workdir) { + g_error("Unable to create temporary dir: %s\n", err->message); + exit(1); + } + + g_setenv("DBUS_VMSTATE_TEST_TMPDIR", workdir, true); + + qtest_add_func("/dbus-vmstate/without-list", + test_dbus_vmstate_without_list); + qtest_add_func("/dbus-vmstate/with-list", + test_dbus_vmstate_with_list); + qtest_add_func("/dbus-vmstate/only-a", + test_dbus_vmstate_only_a); + qtest_add_func("/dbus-vmstate/missing-src", + test_dbus_vmstate_missing_src); + qtest_add_func("/dbus-vmstate/missing-dst", + test_dbus_vmstate_missing_dst); + + ret = g_test_run(); + + rmdir(workdir); + g_free(workdir); + + return ret; +} diff --git a/tests/qtest/dbus-vmstate1.xml b/tests/qtest/dbus-vmstate1.xml new file mode 100644 index 0000000000..cc8563be4c --- /dev/null +++ b/tests/qtest/dbus-vmstate1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/qtest/device-introspect-test.c b/tests/qtest/device-introspect-test.c new file mode 100644 index 0000000000..04f22903b0 --- /dev/null +++ b/tests/qtest/device-introspect-test.c @@ -0,0 +1,323 @@ +/* + * Device introspection test cases + * + * Copyright (c) 2015 Red Hat Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Covers QMP device-list-properties and HMP device_add help. We + * currently don't check that their output makes sense, only that QEMU + * survives. Useful since we've had an astounding number of crash + * bugs around here. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "libqtest.h" + +const char common_args[] = "-nodefaults -machine none"; + +static QList *qom_list_types(QTestState * qts, const char *implements, + bool abstract) +{ + QDict *resp; + QList *ret; + QDict *args = qdict_new(); + + qdict_put_bool(args, "abstract", abstract); + if (implements) { + qdict_put_str(args, "implements", implements); + } + resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }", + args); + g_assert(qdict_haskey(resp, "return")); + ret = qdict_get_qlist(resp, "return"); + qobject_ref(ret); + qobject_unref(resp); + return ret; +} + +/* Build a name -> ObjectTypeInfo index from a ObjectTypeInfo list */ +static QDict *qom_type_index(QList *types) +{ + QDict *index = qdict_new(); + QListEntry *e; + + QLIST_FOREACH_ENTRY(types, e) { + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); + const char *name = qdict_get_str(d, "name"); + qobject_ref(d); + qdict_put(index, name, d); + } + return index; +} + +/* Check if @parent is present in the parent chain of @type */ +static bool qom_has_parent(QDict *index, const char *type, const char *parent) +{ + while (type) { + QDict *d = qdict_get_qdict(index, type); + const char *p = d && qdict_haskey(d, "parent") ? + qdict_get_str(d, "parent") : + NULL; + + if (!strcmp(type, parent)) { + return true; + } + + type = p; + } + + return false; +} + +/* Find an entry on a list returned by qom-list-types */ +static QDict *type_list_find(QList *types, const char *name) +{ + QListEntry *e; + + QLIST_FOREACH_ENTRY(types, e) { + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); + const char *ename = qdict_get_str(d, "name"); + if (!strcmp(ename, name)) { + return d; + } + } + + return NULL; +} + +static QList *device_type_list(QTestState *qts, bool abstract) +{ + return qom_list_types(qts, "device", abstract); +} + +static void test_one_device(QTestState *qts, const char *type) +{ + QDict *resp; + char *help; + char *qom_tree_start, *qom_tree_end; + char *qtree_start, *qtree_end; + + g_test_message("Testing device '%s'", type); + + qom_tree_start = qtest_hmp(qts, "info qom-tree"); + qtree_start = qtest_hmp(qts, "info qtree"); + + resp = qtest_qmp(qts, "{'execute': 'device-list-properties'," + " 'arguments': {'typename': %s}}", + type); + qobject_unref(resp); + + help = qtest_hmp(qts, "device_add \"%s,help\"", type); + g_free(help); + + /* + * Some devices leave dangling pointers in QOM behind. + * "info qom-tree" or "info qtree" have a good chance at crashing then. + * Also make sure that the tree did not change. + */ + qom_tree_end = qtest_hmp(qts, "info qom-tree"); + g_assert_cmpstr(qom_tree_start, ==, qom_tree_end); + g_free(qom_tree_start); + g_free(qom_tree_end); + + qtree_end = qtest_hmp(qts, "info qtree"); + g_assert_cmpstr(qtree_start, ==, qtree_end); + g_free(qtree_start); + g_free(qtree_end); +} + +static void test_device_intro_list(void) +{ + QList *types; + char *help; + QTestState *qts; + + qts = qtest_init(common_args); + + types = device_type_list(qts, true); + qobject_unref(types); + + help = qtest_hmp(qts, "device_add help"); + g_free(help); + + qtest_quit(qts); +} + +/* + * Ensure all entries returned by qom-list-types implements= + * have as a parent. + */ +static void test_qom_list_parents(QTestState *qts, const char *parent) +{ + QList *types; + QListEntry *e; + QDict *index; + + types = qom_list_types(qts, parent, true); + index = qom_type_index(types); + + QLIST_FOREACH_ENTRY(types, e) { + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); + const char *name = qdict_get_str(d, "name"); + + g_assert(qom_has_parent(index, name, parent)); + } + + qobject_unref(types); + qobject_unref(index); +} + +static void test_qom_list_fields(void) +{ + QList *all_types; + QList *non_abstract; + QListEntry *e; + QTestState *qts; + + qts = qtest_init(common_args); + + all_types = qom_list_types(qts, NULL, true); + non_abstract = qom_list_types(qts, NULL, false); + + QLIST_FOREACH_ENTRY(all_types, e) { + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); + const char *name = qdict_get_str(d, "name"); + bool abstract = qdict_haskey(d, "abstract") ? + qdict_get_bool(d, "abstract") : + false; + bool expected_abstract = !type_list_find(non_abstract, name); + + g_assert(abstract == expected_abstract); + } + + test_qom_list_parents(qts, "object"); + test_qom_list_parents(qts, "device"); + test_qom_list_parents(qts, "sys-bus-device"); + + qobject_unref(all_types); + qobject_unref(non_abstract); + qtest_quit(qts); +} + +static void test_device_intro_none(void) +{ + QTestState *qts = qtest_init(common_args); + + test_one_device(qts, "nonexistent"); + qtest_quit(qts); +} + +static void test_device_intro_abstract(void) +{ + QTestState *qts = qtest_init(common_args); + + test_one_device(qts, "device"); + qtest_quit(qts); +} + +static void test_device_intro_concrete(const void *args) +{ + QList *types; + QListEntry *entry; + const char *type; + QTestState *qts; + + qts = qtest_init(args); + types = device_type_list(qts, false); + + QLIST_FOREACH_ENTRY(types, entry) { + type = qdict_get_try_str(qobject_to(QDict, qlist_entry_obj(entry)), + "name"); + g_assert(type); + test_one_device(qts, type); + } + + qobject_unref(types); + qtest_quit(qts); + g_free((void *)args); +} + +static void test_abstract_interfaces(void) +{ + QList *all_types; + QListEntry *e; + QDict *index; + QTestState *qts; + + qts = qtest_init(common_args); + + all_types = qom_list_types(qts, "interface", true); + index = qom_type_index(all_types); + + QLIST_FOREACH_ENTRY(all_types, e) { + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); + const char *name = qdict_get_str(d, "name"); + + /* + * qom-list-types implements=interface returns all types + * that implement _any_ interface (not just interface + * types), so skip the ones that don't have "interface" + * on the parent type chain. + */ + if (!qom_has_parent(index, name, "interface")) { + /* Not an interface type */ + continue; + } + + g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract")); + } + + qobject_unref(all_types); + qobject_unref(index); + qtest_quit(qts); +} + +static void add_machine_test_case(const char *mname) +{ + char *path, *args; + + /* Ignore blacklisted machines */ + if (g_str_equal("xenfv", mname) || g_str_equal("xenpv", mname)) { + return; + } + + path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname); + args = g_strdup_printf("-M %s", mname); + qtest_add_data_func(path, args, test_device_intro_concrete); + g_free(path); + + path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname); + args = g_strdup_printf("-nodefaults -M %s", mname); + qtest_add_data_func(path, args, test_device_intro_concrete); + g_free(path); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("device/introspect/list", test_device_intro_list); + qtest_add_func("device/introspect/list-fields", test_qom_list_fields); + qtest_add_func("device/introspect/none", test_device_intro_none); + qtest_add_func("device/introspect/abstract", test_device_intro_abstract); + qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces); + if (g_test_quick()) { + qtest_add_data_func("device/introspect/concrete/defaults/none", + g_strdup(common_args), test_device_intro_concrete); + } else { + qtest_cb_for_every_machine(add_machine_test_case, true); + } + + return g_test_run(); +} diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c new file mode 100644 index 0000000000..318e422d51 --- /dev/null +++ b/tests/qtest/device-plug-test.c @@ -0,0 +1,178 @@ +/* + * QEMU device plug/unplug handling + * + * Copyright (C) 2019 Red Hat Inc. + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" + +static void device_del_start(QTestState *qtest, const char *id) +{ + qtest_qmp_send(qtest, + "{'execute': 'device_del', 'arguments': { 'id': %s } }", id); +} + +static void device_del_finish(QTestState *qtest) +{ + QDict *resp = qtest_qmp_receive(qtest); + + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); +} + +static void device_del_request(QTestState *qtest, const char *id) +{ + device_del_start(qtest, id); + device_del_finish(qtest); +} + +static void system_reset(QTestState *qtest) +{ + QDict *resp; + + resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); +} + +static void wait_device_deleted_event(QTestState *qtest, const char *id) +{ + QDict *resp, *data; + QString *qstr; + + /* + * Other devices might get removed along with the removed device. Skip + * these. The device of interest will be the last one. + */ + for (;;) { + resp = qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED"); + data = qdict_get_qdict(resp, "data"); + if (!data || !qdict_get(data, "device")) { + qobject_unref(resp); + continue; + } + qstr = qobject_to(QString, qdict_get(data, "device")); + g_assert(qstr); + if (!strcmp(qstring_get_str(qstr), id)) { + qobject_unref(resp); + break; + } + qobject_unref(resp); + } +} + +static void test_pci_unplug_request(void) +{ + QTestState *qtest = qtest_initf("-device virtio-mouse-pci,id=dev0"); + + /* + * Request device removal. As the guest is not running, the request won't + * be processed. However during system reset, the removal will be + * handled, removing the device. + */ + device_del_request(qtest, "dev0"); + system_reset(qtest); + wait_device_deleted_event(qtest, "dev0"); + + qtest_quit(qtest); +} + +static void test_ccw_unplug(void) +{ + QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0"); + + /* + * The DEVICE_DELETED events will be sent before the command + * completes. + */ + device_del_start(qtest, "dev0"); + wait_device_deleted_event(qtest, "dev0"); + device_del_finish(qtest); + + qtest_quit(qtest); +} + +static void test_spapr_cpu_unplug_request(void) +{ + QTestState *qtest; + + qtest = qtest_initf("-cpu power9_v2.0 -smp 1,maxcpus=2 " + "-device power9_v2.0-spapr-cpu-core,core-id=1,id=dev0"); + + /* similar to test_pci_unplug_request */ + device_del_request(qtest, "dev0"); + system_reset(qtest); + wait_device_deleted_event(qtest, "dev0"); + + qtest_quit(qtest); +} + +static void test_spapr_memory_unplug_request(void) +{ + QTestState *qtest; + + qtest = qtest_initf("-m 256M,slots=1,maxmem=768M " + "-object memory-backend-ram,id=mem0,size=512M " + "-device pc-dimm,id=dev0,memdev=mem0"); + + /* similar to test_pci_unplug_request */ + device_del_request(qtest, "dev0"); + system_reset(qtest); + wait_device_deleted_event(qtest, "dev0"); + + qtest_quit(qtest); +} + +static void test_spapr_phb_unplug_request(void) +{ + QTestState *qtest; + + qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0"); + + /* similar to test_pci_unplug_request */ + device_del_request(qtest, "dev0"); + system_reset(qtest); + wait_device_deleted_event(qtest, "dev0"); + + qtest_quit(qtest); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + /* + * We need a system that will process unplug requests during system resets + * and does not do PCI surprise removal. This holds for x86 ACPI, + * s390x and spapr. + */ + qtest_add_func("/device-plug/pci-unplug-request", + test_pci_unplug_request); + + if (!strcmp(arch, "s390x")) { + qtest_add_func("/device-plug/ccw-unplug", + test_ccw_unplug); + } + + if (!strcmp(arch, "ppc64")) { + qtest_add_func("/device-plug/spapr-cpu-unplug-request", + test_spapr_cpu_unplug_request); + qtest_add_func("/device-plug/spapr-memory-unplug-request", + test_spapr_memory_unplug_request); + qtest_add_func("/device-plug/spapr-phb-unplug-request", + test_spapr_phb_unplug_request); + } + + return g_test_run(); +} diff --git a/tests/qtest/display-vga-test.c b/tests/qtest/display-vga-test.c new file mode 100644 index 0000000000..ace3bb28e0 --- /dev/null +++ b/tests/qtest/display-vga-test.c @@ -0,0 +1,69 @@ +/* + * QTest testcase for vga cards + * + * Copyright (c) 2014 Red Hat, Inc + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +static void pci_cirrus(void) +{ + qtest_start("-vga none -device cirrus-vga"); + qtest_end(); +} + +static void pci_stdvga(void) +{ + qtest_start("-vga none -device VGA"); + qtest_end(); +} + +static void pci_secondary(void) +{ + qtest_start("-vga none -device secondary-vga"); + qtest_end(); +} + +static void pci_multihead(void) +{ + qtest_start("-vga none -device VGA -device secondary-vga"); + qtest_end(); +} + +static void pci_virtio_gpu(void) +{ + qtest_start("-vga none -device virtio-gpu-pci"); + qtest_end(); +} + +static void pci_virtio_vga(void) +{ + qtest_start("-vga none -device virtio-vga"); + qtest_end(); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "alpha") == 0 || strcmp(arch, "i386") == 0 || + strcmp(arch, "mips") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("/display/pci/cirrus", pci_cirrus); + } + qtest_add_func("/display/pci/stdvga", pci_stdvga); + qtest_add_func("/display/pci/secondary", pci_secondary); + qtest_add_func("/display/pci/multihead", pci_multihead); + qtest_add_func("/display/pci/virtio-gpu", pci_virtio_gpu); + if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") || + g_str_equal(arch, "hppa") || g_str_equal(arch, "ppc64")) { + qtest_add_func("/display/pci/virtio-vga", pci_virtio_vga); + } + + return g_test_run(); +} diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c new file mode 100644 index 0000000000..5f8839b232 --- /dev/null +++ b/tests/qtest/drive_del-test.c @@ -0,0 +1,154 @@ +/* + * blockdev.c test cases + * + * Copyright (C) 2013-2014 Red Hat Inc. + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/virtio.h" +#include "qapi/qmp/qdict.h" + +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(q, ...) qobject_unref(qtest_qmp(q, __VA_ARGS__)) + +static void drive_add(QTestState *qts) +{ + char *resp = qtest_hmp(qts, "drive_add 0 if=none,id=drive0"); + + g_assert_cmpstr(resp, ==, "OK\r\n"); + g_free(resp); +} + +static void drive_del(QTestState *qts) +{ + char *resp = qtest_hmp(qts, "drive_del drive0"); + + g_assert_cmpstr(resp, ==, ""); + g_free(resp); +} + +static void device_del(QTestState *qts) +{ + QDict *response; + + /* Complication: ignore DEVICE_DELETED event */ + qmp_discard_response(qts, "{'execute': 'device_del'," + " 'arguments': { 'id': 'dev0' } }"); + response = qtest_qmp_receive(qts); + g_assert(response); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +static void test_drive_without_dev(void) +{ + QTestState *qts; + + /* Start with an empty drive */ + qts = qtest_init("-drive if=none,id=drive0"); + + /* Delete the drive */ + drive_del(qts); + + /* Ensure re-adding the drive works - there should be no duplicate ID error + * because the old drive must be gone. + */ + drive_add(qts); + + qtest_quit(qts); +} + +/* + * qvirtio_get_dev_type: + * Returns: the preferred virtio bus/device type for the current architecture. + * TODO: delete this + */ +static const char *qvirtio_get_dev_type(void) +{ + const char *arch = qtest_get_arch(); + + if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { + return "device"; /* for virtio-mmio */ + } else if (g_str_equal(arch, "s390x")) { + return "ccw"; + } else { + return "pci"; + } +} + +static void test_after_failed_device_add(void) +{ + char driver[32]; + QDict *response; + QTestState *qts; + + snprintf(driver, sizeof(driver), "virtio-blk-%s", + qvirtio_get_dev_type()); + + qts = qtest_init("-drive if=none,id=drive0"); + + /* Make device_add fail. If this leaks the virtio-blk device then a + * reference to drive0 will also be held (via qdev properties). + */ + response = qtest_qmp(qts, "{'execute': 'device_add'," + " 'arguments': {" + " 'driver': %s," + " 'drive': 'drive0'" + "}}", driver); + g_assert(response); + qmp_assert_error_class(response, "GenericError"); + + /* Delete the drive */ + drive_del(qts); + + /* Try to re-add the drive. This fails with duplicate IDs if a leaked + * virtio-blk device exists that holds a reference to the old drive0. + */ + drive_add(qts); + + qtest_quit(qts); +} + +static void test_drive_del_device_del(void) +{ + QTestState *qts; + + /* Start with a drive used by a device that unplugs instantaneously */ + qts = qtest_initf("-drive if=none,id=drive0,file=null-co://," + "file.read-zeroes=on,format=raw" + " -device virtio-scsi-%s" + " -device scsi-hd,drive=drive0,id=dev0", + qvirtio_get_dev_type()); + + /* + * Delete the drive, and then the device + * Doing it in this order takes notoriously tricky special paths + */ + drive_del(qts); + device_del(qts); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/drive_del/without-dev", test_drive_without_dev); + + if (qvirtio_get_dev_type() != NULL) { + qtest_add_func("/drive_del/after_failed_device_add", + test_after_failed_device_add); + qtest_add_func("/blockdev/drive_del_device_del", + test_drive_del_device_del); + } + + return g_test_run(); +} diff --git a/tests/qtest/ds1338-test.c b/tests/qtest/ds1338-test.c new file mode 100644 index 0000000000..f6ade9a050 --- /dev/null +++ b/tests/qtest/ds1338-test.c @@ -0,0 +1,58 @@ +/* + * QTest testcase for the DS1338 RTC + * + * Copyright (c) 2013 Jean-Christophe Dubois + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/i2c.h" + +#define DS1338_ADDR 0x68 + +static inline uint8_t bcd2bin(uint8_t x) +{ + return ((x) & 0x0f) + ((x) >> 4) * 10; +} + +static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + uint8_t resp[7]; + time_t now = time(NULL); + struct tm *tm_ptr = gmtime(&now); + + i2c_read_block(i2cdev, 0, resp, sizeof(resp)); + + /* check retrieved time againt local time */ + g_assert_cmpuint(bcd2bin(resp[4]), == , tm_ptr->tm_mday); + g_assert_cmpuint(bcd2bin(resp[5]), == , 1 + tm_ptr->tm_mon); + g_assert_cmpuint(2000 + bcd2bin(resp[6]), == , 1900 + tm_ptr->tm_year); +} + +static void ds1338_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "address=0x68" + }; + add_qi2c_address(&opts, &(QI2CAddress) { DS1338_ADDR }); + + qos_node_create_driver("ds1338", i2c_device_create); + qos_node_consumes("ds1338", "i2c-bus", &opts); + qos_add_test("tx-rx", "ds1338", send_and_receive, NULL); +} +libqos_init(ds1338_register_nodes); diff --git a/tests/qtest/e1000-test.c b/tests/qtest/e1000-test.c new file mode 100644 index 0000000000..c387984ef6 --- /dev/null +++ b/tests/qtest/e1000-test.c @@ -0,0 +1,68 @@ +/* + * QTest testcase for e1000 NIC + * + * Copyright (c) 2013-2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QE1000 QE1000; + +struct QE1000 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static const char *models[] = { + "e1000", + "e1000-82540em", + "e1000-82544gc", + "e1000-82545em", +}; + +static void *e1000_get_driver(void *obj, const char *interface) +{ + QE1000 *e1000 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &e1000->dev; + } + + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); +} + +static void *e1000_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QE1000 *e1000 = g_new0(QE1000, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&e1000->dev, bus, addr); + e1000->obj.get_driver = e1000_get_driver; + + return &e1000->obj; +} + +static void e1000_register_nodes(void) +{ + int i; + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + for (i = 0; i < ARRAY_SIZE(models); i++) { + qos_node_create_driver(models[i], e1000_create); + qos_node_consumes(models[i], "pci-bus", &opts); + qos_node_produces(models[i], "pci-device"); + } +} + +libqos_init(e1000_register_nodes); diff --git a/tests/qtest/e1000e-test.c b/tests/qtest/e1000e-test.c new file mode 100644 index 0000000000..1a232a663a --- /dev/null +++ b/tests/qtest/e1000e-test.c @@ -0,0 +1,279 @@ + /* + * QTest testcase for e1000e NIC + * + * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman + * Leonid Bloch + * Yan Vugenfirer + * + * 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 . + */ + + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "libqtest-single.h" +#include "qemu-common.h" +#include "libqos/pci-pc.h" +#include "qemu/sockets.h" +#include "qemu/iov.h" +#include "qemu/module.h" +#include "qemu/bitops.h" +#include "libqos/malloc.h" +#include "libqos/e1000e.h" + +static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) +{ + struct { + uint64_t buffer_addr; + union { + uint32_t data; + struct { + uint16_t length; + uint8_t cso; + uint8_t cmd; + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; + uint8_t css; + uint16_t special; + } fields; + } upper; + } descr; + + static const uint32_t dtyp_data = BIT(20); + static const uint32_t dtyp_ext = BIT(29); + static const uint32_t dcmd_rs = BIT(27); + static const uint32_t dcmd_eop = BIT(24); + static const uint32_t dsta_dd = BIT(0); + static const int data_len = 64; + char buffer[64]; + int ret; + uint32_t recv_len; + + /* Prepare test data buffer */ + uint64_t data = guest_alloc(alloc, data_len); + memwrite(data, "TEST", 5); + + /* Prepare TX descriptor */ + memset(&descr, 0, sizeof(descr)); + descr.buffer_addr = cpu_to_le64(data); + descr.lower.data = cpu_to_le32(dcmd_rs | + dcmd_eop | + dtyp_ext | + dtyp_data | + data_len); + + /* Put descriptor to the ring */ + e1000e_tx_ring_push(d, &descr); + + /* Wait for TX WB interrupt */ + e1000e_wait_isr(d, E1000E_TX0_MSG_ID); + + /* Check DD bit */ + g_assert_cmphex(le32_to_cpu(descr.upper.data) & dsta_dd, ==, dsta_dd); + + /* Check data sent to the backend */ + ret = qemu_recv(test_sockets[0], &recv_len, sizeof(recv_len), 0); + g_assert_cmpint(ret, == , sizeof(recv_len)); + qemu_recv(test_sockets[0], buffer, 64, 0); + g_assert_cmpstr(buffer, == , "TEST"); + + /* Free test data buffer */ + guest_free(alloc, data); +} + +static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) +{ + union { + struct { + uint64_t buffer_addr; + uint64_t reserved; + } read; + struct { + struct { + uint32_t mrq; + union { + uint32_t rss; + struct { + uint16_t ip_id; + uint16_t csum; + } csum_ip; + } hi_dword; + } lower; + struct { + uint32_t status_error; + uint16_t length; + uint16_t vlan; + } upper; + } wb; + } descr; + + static const uint32_t esta_dd = BIT(0); + + char test[] = "TEST"; + int len = htonl(sizeof(test)); + struct iovec iov[] = { + { + .iov_base = &len, + .iov_len = sizeof(len), + },{ + .iov_base = test, + .iov_len = sizeof(test), + }, + }; + + static const int data_len = 64; + char buffer[64]; + int ret; + + /* Send a dummy packet to device's socket*/ + ret = iov_send(test_sockets[0], iov, 2, 0, sizeof(len) + sizeof(test)); + g_assert_cmpint(ret, == , sizeof(test) + sizeof(len)); + + /* Prepare test data buffer */ + uint64_t data = guest_alloc(alloc, data_len); + + /* Prepare RX descriptor */ + memset(&descr, 0, sizeof(descr)); + descr.read.buffer_addr = cpu_to_le64(data); + + /* Put descriptor to the ring */ + e1000e_rx_ring_push(d, &descr); + + /* Wait for TX WB interrupt */ + e1000e_wait_isr(d, E1000E_RX0_MSG_ID); + + /* Check DD bit */ + g_assert_cmphex(le32_to_cpu(descr.wb.upper.status_error) & + esta_dd, ==, esta_dd); + + /* Check data sent to the backend */ + memread(data, buffer, sizeof(buffer)); + g_assert_cmpstr(buffer, == , "TEST"); + + /* Free test data buffer */ + guest_free(alloc, data); +} + +static void test_e1000e_init(void *obj, void *data, QGuestAllocator * alloc) +{ + /* init does nothing */ +} + +static void test_e1000e_tx(void *obj, void *data, QGuestAllocator * alloc) +{ + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); + + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } + + e1000e_send_verify(d, data, alloc); +} + +static void test_e1000e_rx(void *obj, void *data, QGuestAllocator * alloc) +{ + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); + + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } + + e1000e_receive_verify(d, data, alloc); +} + +static void test_e1000e_multiple_transfers(void *obj, void *data, + QGuestAllocator *alloc) +{ + static const long iterations = 4 * 1024; + long i; + + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); + + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } + + for (i = 0; i < iterations; i++) { + e1000e_send_verify(d, data, alloc); + e1000e_receive_verify(d, data, alloc); + } + +} + +static void test_e1000e_hotplug(void *obj, void *data, QGuestAllocator * alloc) +{ + QTestState *qts = global_qtest; /* TODO: get rid of global_qtest here */ + + qtest_qmp_device_add(qts, "e1000e", "e1000e_net", "{'addr': '0x06'}"); + qpci_unplug_acpi_device_test(qts, "e1000e_net", 0x06); +} + +static void data_test_clear(void *sockets) +{ + int *test_sockets = sockets; + + close(test_sockets[0]); + qos_invalidate_command_line(); + close(test_sockets[1]); + g_free(test_sockets); +} + +static void *data_test_init(GString *cmd_line, void *arg) +{ + int *test_sockets = g_new(int, 2); + int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets); + g_assert_cmpint(ret, != , -1); + + g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", + test_sockets[1]); + + g_test_queue_destroy(data_test_clear, test_sockets); + return test_sockets; +} + +static void register_e1000e_test(void) +{ + QOSGraphTestOptions opts = { + .before = data_test_init, + }; + + qos_add_test("init", "e1000e", test_e1000e_init, &opts); + qos_add_test("tx", "e1000e", test_e1000e_tx, &opts); + qos_add_test("rx", "e1000e", test_e1000e_rx, &opts); + qos_add_test("multiple_transfers", "e1000e", + test_e1000e_multiple_transfers, &opts); + qos_add_test("hotplug", "e1000e", test_e1000e_hotplug, &opts); +} + +libqos_init(register_e1000e_test); diff --git a/tests/qtest/eepro100-test.c b/tests/qtest/eepro100-test.c new file mode 100644 index 0000000000..8dbffff0b8 --- /dev/null +++ b/tests/qtest/eepro100-test.c @@ -0,0 +1,77 @@ +/* + * QTest testcase for eepro100 NIC + * + * Copyright (c) 2013-2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QEEPRO100 QEEPRO100; + +struct QEEPRO100 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static const char *models[] = { + "i82550", + "i82551", + "i82557a", + "i82557b", + "i82557c", + "i82558a", + "i82558b", + "i82559a", + "i82559b", + "i82559c", + "i82559er", + "i82562", + "i82801", +}; + +static void *eepro100_get_driver(void *obj, const char *interface) +{ + QEEPRO100 *eepro100 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &eepro100->dev; + } + + fprintf(stderr, "%s not present in eepro100\n", interface); + g_assert_not_reached(); +} + +static void *eepro100_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QEEPRO100 *eepro100 = g_new0(QEEPRO100, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&eepro100->dev, bus, addr); + eepro100->obj.get_driver = eepro100_get_driver; + + return &eepro100->obj; +} + +static void eepro100_register_nodes(void) +{ + int i; + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + for (i = 0; i < ARRAY_SIZE(models); i++) { + qos_node_create_driver(models[i], eepro100_create); + qos_node_consumes(models[i], "pci-bus", &opts); + qos_node_produces(models[i], "pci-device"); + } +} + +libqos_init(eepro100_register_nodes); diff --git a/tests/qtest/endianness-test.c b/tests/qtest/endianness-test.c new file mode 100644 index 0000000000..58527952a5 --- /dev/null +++ b/tests/qtest/endianness-test.c @@ -0,0 +1,306 @@ +/* + * QTest testcase for ISA endianness + * + * Copyright Red Hat, Inc. 2012 + * + * Authors: + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "libqtest.h" +#include "qemu/bswap.h" + +typedef struct TestCase TestCase; +struct TestCase { + const char *arch; + const char *machine; + uint64_t isa_base; + bool bswap; + const char *superio; +}; + +static const TestCase test_cases[] = { + { "i386", "pc", -1 }, + { "mips", "mips", 0x14000000, .bswap = true }, + { "mips", "malta", 0x10000000, .bswap = true }, + { "mips64", "magnum", 0x90000000, .bswap = true }, + { "mips64", "pica61", 0x90000000, .bswap = true }, + { "mips64", "mips", 0x14000000, .bswap = true }, + { "mips64", "malta", 0x10000000, .bswap = true }, + { "mips64el", "fulong2e", 0x1fd00000 }, + { "ppc", "g3beige", 0xfe000000, .bswap = true, .superio = "i82378" }, + { "ppc", "prep", 0x80000000, .bswap = true }, + { "ppc", "bamboo", 0xe8000000, .bswap = true, .superio = "i82378" }, + { "ppc64", "mac99", 0xf2000000, .bswap = true, .superio = "i82378" }, + { "ppc64", "pseries", (1ULL << 45), .bswap = true, .superio = "i82378" }, + { "ppc64", "pseries-2.7", 0x10080000000ULL, + .bswap = true, .superio = "i82378" }, + { "sh4", "r2d", 0xfe240000, .superio = "i82378" }, + { "sh4eb", "r2d", 0xfe240000, .bswap = true, .superio = "i82378" }, + { "sparc64", "sun4u", 0x1fe02000000LL, .bswap = true }, + { "x86_64", "pc", -1 }, + {} +}; + +static uint8_t isa_inb(QTestState *qts, const TestCase *test, uint16_t addr) +{ + uint8_t value; + if (test->isa_base == -1) { + value = qtest_inb(qts, addr); + } else { + value = qtest_readb(qts, test->isa_base + addr); + } + return value; +} + +static uint16_t isa_inw(QTestState *qts, const TestCase *test, uint16_t addr) +{ + uint16_t value; + if (test->isa_base == -1) { + value = qtest_inw(qts, addr); + } else { + value = qtest_readw(qts, test->isa_base + addr); + } + return test->bswap ? bswap16(value) : value; +} + +static uint32_t isa_inl(QTestState *qts, const TestCase *test, uint16_t addr) +{ + uint32_t value; + if (test->isa_base == -1) { + value = qtest_inl(qts, addr); + } else { + value = qtest_readl(qts, test->isa_base + addr); + } + return test->bswap ? bswap32(value) : value; +} + +static void isa_outb(QTestState *qts, const TestCase *test, uint16_t addr, + uint8_t value) +{ + if (test->isa_base == -1) { + qtest_outb(qts, addr, value); + } else { + qtest_writeb(qts, test->isa_base + addr, 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) { + qtest_outw(qts, addr, value); + } else { + qtest_writew(qts, test->isa_base + addr, 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) { + qtest_outl(qts, addr, value); + } else { + qtest_writel(qts, test->isa_base + addr, value); + } +} + + +static void test_endianness(gconstpointer data) +{ + const TestCase *test = data; + 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; + 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; + 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) +{ + const char *arch = qtest_get_arch(); + int i; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; test_cases[i].arch; i++) { + gchar *path; + if (strcmp(test_cases[i].arch, arch) != 0) { + continue; + } + path = g_strdup_printf("endianness/%s", + test_cases[i].machine); + qtest_add_data_func(path, &test_cases[i], test_endianness); + g_free(path); + + path = g_strdup_printf("endianness/split/%s", + test_cases[i].machine); + qtest_add_data_func(path, &test_cases[i], test_endianness_split); + g_free(path); + + path = g_strdup_printf("endianness/combine/%s", + test_cases[i].machine); + qtest_add_data_func(path, &test_cases[i], test_endianness_combine); + g_free(path); + } + + return g_test_run(); +} diff --git a/tests/qtest/es1370-test.c b/tests/qtest/es1370-test.c new file mode 100644 index 0000000000..adccdac1be --- /dev/null +++ b/tests/qtest/es1370-test.c @@ -0,0 +1,58 @@ +/* + * QTest testcase for ES1370 + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QES1370 QES1370; + +struct QES1370 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *es1370_get_driver(void *obj, const char *interface) +{ + QES1370 *es1370 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &es1370->dev; + } + + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); +} + +static void *es1370_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QES1370 *es1370 = g_new0(QES1370, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&es1370->dev, bus, addr); + es1370->obj.get_driver = es1370_get_driver; + + return &es1370->obj; +} + +static void es1370_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("ES1370", es1370_create); + qos_node_consumes("ES1370", "pci-bus", &opts); + qos_node_produces("ES1370", "pci-device"); +} + +libqos_init(es1370_register_nodes); diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c new file mode 100644 index 0000000000..26b69f7c5c --- /dev/null +++ b/tests/qtest/fdc-test.c @@ -0,0 +1,587 @@ +/* + * Floppy test cases. + * + * Copyright (c) 2012 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + + +#include "libqtest-single.h" +#include "qapi/qmp/qdict.h" +#include "qemu-common.h" + +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) + +#define TEST_IMAGE_SIZE 1440 * 1024 + +#define FLOPPY_BASE 0x3f0 +#define FLOPPY_IRQ 6 + +enum { + reg_sra = 0x0, + reg_srb = 0x1, + reg_dor = 0x2, + reg_msr = 0x4, + reg_dsr = 0x4, + reg_fifo = 0x5, + reg_dir = 0x7, +}; + +enum { + CMD_SENSE_INT = 0x08, + CMD_READ_ID = 0x0a, + CMD_SEEK = 0x0f, + CMD_VERIFY = 0x16, + CMD_READ = 0xe6, + CMD_RELATIVE_SEEK_OUT = 0x8f, + CMD_RELATIVE_SEEK_IN = 0xcf, +}; + +enum { + BUSY = 0x10, + NONDMA = 0x20, + RQM = 0x80, + DIO = 0x40, + + DSKCHG = 0x80, +}; + +static char test_image[] = "/tmp/qtest.XXXXXX"; + +#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) +#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) + +static uint8_t base = 0x70; + +enum { + CMOS_FLOPPY = 0x10, +}; + +static void floppy_send(uint8_t byte) +{ + uint8_t msr; + + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_set(msr, RQM); + assert_bit_clear(msr, DIO); + + outb(FLOPPY_BASE + reg_fifo, byte); +} + +static uint8_t floppy_recv(void) +{ + uint8_t msr; + + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_set(msr, RQM | DIO); + + return inb(FLOPPY_BASE + reg_fifo); +} + +/* pcn: Present Cylinder Number */ +static void ack_irq(uint8_t *pcn) +{ + uint8_t ret; + + g_assert(get_irq(FLOPPY_IRQ)); + floppy_send(CMD_SENSE_INT); + floppy_recv(); + + ret = floppy_recv(); + if (pcn != NULL) { + *pcn = ret; + } + + g_assert(!get_irq(FLOPPY_IRQ)); +} + +static uint8_t send_read_command(uint8_t cmd) +{ + uint8_t drive = 0; + uint8_t head = 0; + uint8_t cyl = 0; + uint8_t sect_addr = 1; + uint8_t sect_size = 2; + uint8_t eot = 1; + uint8_t gap = 0x1b; + uint8_t gpl = 0xff; + + uint8_t msr = 0; + uint8_t st0; + + uint8_t ret = 0; + + floppy_send(cmd); + floppy_send(head << 2 | drive); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(cyl); + floppy_send(head); + floppy_send(sect_addr); + floppy_send(sect_size); + floppy_send(eot); + floppy_send(gap); + floppy_send(gpl); + + uint8_t i = 0; + uint8_t n = 2; + for (; i < n; i++) { + msr = inb(FLOPPY_BASE + reg_msr); + if (msr == 0xd0) { + break; + } + sleep(1); + } + + if (i >= n) { + return 1; + } + + st0 = floppy_recv(); + if (st0 != 0x40) { + ret = 1; + } + + floppy_recv(); + floppy_recv(); + floppy_recv(); + floppy_recv(); + floppy_recv(); + floppy_recv(); + + return ret; +} + +static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0) +{ + uint8_t drive = 0; + uint8_t head = 0; + uint8_t cyl = 0; + uint8_t sect_addr = 1; + uint8_t sect_size = 2; + uint8_t eot = nb_sect; + uint8_t gap = 0x1b; + uint8_t gpl = 0xff; + + uint8_t msr = 0; + uint8_t st0; + + uint8_t ret = 0; + + floppy_send(CMD_READ); + floppy_send(head << 2 | drive); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(cyl); + floppy_send(head); + floppy_send(sect_addr); + floppy_send(sect_size); + floppy_send(eot); + floppy_send(gap); + floppy_send(gpl); + + uint16_t i = 0; + uint8_t n = 2; + for (; i < n; i++) { + msr = inb(FLOPPY_BASE + reg_msr); + if (msr == (BUSY | NONDMA | DIO | RQM)) { + break; + } + sleep(1); + } + + if (i >= n) { + return 1; + } + + /* Non-DMA mode */ + for (i = 0; i < 512 * 2 * nb_sect; i++) { + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_set(msr, BUSY | RQM | DIO); + inb(FLOPPY_BASE + reg_fifo); + } + + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_set(msr, BUSY | RQM | DIO); + g_assert(get_irq(FLOPPY_IRQ)); + + st0 = floppy_recv(); + if (st0 != expected_st0) { + ret = 1; + } + + floppy_recv(); + floppy_recv(); + floppy_recv(); + floppy_recv(); + floppy_recv(); + g_assert(get_irq(FLOPPY_IRQ)); + floppy_recv(); + + /* Check that we're back in command phase */ + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_clear(msr, BUSY | DIO); + assert_bit_set(msr, RQM); + g_assert(!get_irq(FLOPPY_IRQ)); + + return ret; +} + +static void send_seek(int cyl) +{ + int drive = 0; + int head = 0; + + floppy_send(CMD_SEEK); + floppy_send(head << 2 | drive); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(cyl); + ack_irq(NULL); +} + +static uint8_t cmos_read(uint8_t reg) +{ + outb(base + 0, reg); + return inb(base + 1); +} + +static void test_cmos(void) +{ + uint8_t cmos; + + cmos = cmos_read(CMOS_FLOPPY); + g_assert(cmos == 0x40 || cmos == 0x50); +} + +static void test_no_media_on_start(void) +{ + uint8_t dir; + + /* Media changed bit must be set all time after start if there is + * no media in drive. */ + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + send_seek(1); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); +} + +static void test_read_without_media(void) +{ + uint8_t ret; + + ret = send_read_command(CMD_READ); + g_assert(ret == 0); +} + +static void test_media_insert(void) +{ + uint8_t dir; + + /* Insert media in drive. DSKCHK should not be reset until a step pulse + * is sent. */ + qmp_discard_response("{'execute':'blockdev-change-medium', 'arguments':{" + " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}", + test_image); + + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + + send_seek(0); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + + /* Step to next track should clear DSKCHG bit. */ + send_seek(1); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_clear(dir, DSKCHG); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_clear(dir, DSKCHG); +} + +static void test_media_change(void) +{ + uint8_t dir; + + test_media_insert(); + + /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't + * reset the bit. */ + qmp_discard_response("{'execute':'eject', 'arguments':{" + " 'id':'floppy0' }}"); + + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + + send_seek(0); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + + send_seek(1); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); + dir = inb(FLOPPY_BASE + reg_dir); + assert_bit_set(dir, DSKCHG); +} + +static void test_sense_interrupt(void) +{ + int drive = 0; + int head = 0; + int cyl = 0; + int ret = 0; + + floppy_send(CMD_SENSE_INT); + ret = floppy_recv(); + g_assert(ret == 0x80); + + floppy_send(CMD_SEEK); + floppy_send(head << 2 | drive); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(cyl); + + floppy_send(CMD_SENSE_INT); + ret = floppy_recv(); + g_assert(ret == 0x20); + floppy_recv(); +} + +static void test_relative_seek(void) +{ + uint8_t drive = 0; + uint8_t head = 0; + uint8_t cyl = 1; + uint8_t pcn; + + /* Send seek to track 0 */ + send_seek(0); + + /* Send relative seek to increase track by 1 */ + floppy_send(CMD_RELATIVE_SEEK_IN); + floppy_send(head << 2 | drive); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(cyl); + + ack_irq(&pcn); + g_assert(pcn == 1); + + /* Send relative seek to decrease track by 1 */ + floppy_send(CMD_RELATIVE_SEEK_OUT); + floppy_send(head << 2 | drive); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(cyl); + + ack_irq(&pcn); + g_assert(pcn == 0); +} + +static void test_read_id(void) +{ + uint8_t drive = 0; + uint8_t head = 0; + uint8_t cyl; + uint8_t st0; + uint8_t msr; + + /* Seek to track 0 and check with READ ID */ + send_seek(0); + + floppy_send(CMD_READ_ID); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(head << 2 | drive); + + msr = inb(FLOPPY_BASE + reg_msr); + if (!get_irq(FLOPPY_IRQ)) { + assert_bit_set(msr, BUSY); + assert_bit_clear(msr, RQM); + } + + while (!get_irq(FLOPPY_IRQ)) { + /* qemu involves a timer with READ ID... */ + clock_step(1000000000LL / 50); + } + + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_set(msr, BUSY | RQM | DIO); + + st0 = floppy_recv(); + floppy_recv(); + floppy_recv(); + cyl = floppy_recv(); + head = floppy_recv(); + floppy_recv(); + g_assert(get_irq(FLOPPY_IRQ)); + floppy_recv(); + g_assert(!get_irq(FLOPPY_IRQ)); + + g_assert_cmpint(cyl, ==, 0); + g_assert_cmpint(head, ==, 0); + g_assert_cmpint(st0, ==, head << 2); + + /* Seek to track 8 on head 1 and check with READ ID */ + head = 1; + cyl = 8; + + floppy_send(CMD_SEEK); + floppy_send(head << 2 | drive); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(cyl); + g_assert(get_irq(FLOPPY_IRQ)); + ack_irq(NULL); + + floppy_send(CMD_READ_ID); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(head << 2 | drive); + + msr = inb(FLOPPY_BASE + reg_msr); + if (!get_irq(FLOPPY_IRQ)) { + assert_bit_set(msr, BUSY); + assert_bit_clear(msr, RQM); + } + + while (!get_irq(FLOPPY_IRQ)) { + /* qemu involves a timer with READ ID... */ + clock_step(1000000000LL / 50); + } + + msr = inb(FLOPPY_BASE + reg_msr); + assert_bit_set(msr, BUSY | RQM | DIO); + + st0 = floppy_recv(); + floppy_recv(); + floppy_recv(); + cyl = floppy_recv(); + head = floppy_recv(); + floppy_recv(); + g_assert(get_irq(FLOPPY_IRQ)); + floppy_recv(); + g_assert(!get_irq(FLOPPY_IRQ)); + + g_assert_cmpint(cyl, ==, 8); + g_assert_cmpint(head, ==, 1); + g_assert_cmpint(st0, ==, head << 2); +} + +static void test_read_no_dma_1(void) +{ + uint8_t ret; + + outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); + send_seek(0); + ret = send_read_no_dma_command(1, 0x04); + g_assert(ret == 0); +} + +static void test_read_no_dma_18(void) +{ + uint8_t ret; + + outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); + send_seek(0); + ret = send_read_no_dma_command(18, 0x04); + g_assert(ret == 0); +} + +static void test_read_no_dma_19(void) +{ + uint8_t ret; + + outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); + send_seek(0); + ret = send_read_no_dma_command(19, 0x20); + g_assert(ret == 0); +} + +static void test_verify(void) +{ + uint8_t ret; + + ret = send_read_command(CMD_VERIFY); + g_assert(ret == 0); +} + +/* success if no crash or abort */ +static void fuzz_registers(void) +{ + unsigned int i; + + for (i = 0; i < 1000; i++) { + uint8_t reg, val; + + reg = (uint8_t)g_test_rand_int_range(0, 8); + val = (uint8_t)g_test_rand_int_range(0, 256); + + outb(FLOPPY_BASE + reg, val); + inb(FLOPPY_BASE + reg); + } +} + +int main(int argc, char **argv) +{ + int fd; + int ret; + + /* Create a temporary raw image */ + fd = mkstemp(test_image); + g_assert(fd >= 0); + ret = ftruncate(fd, TEST_IMAGE_SIZE); + g_assert(ret == 0); + close(fd); + + /* Run the tests */ + g_test_init(&argc, &argv, NULL); + + qtest_start("-device floppy,id=floppy0"); + qtest_irq_intercept_in(global_qtest, "ioapic"); + qtest_add_func("/fdc/cmos", test_cmos); + qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start); + qtest_add_func("/fdc/read_without_media", test_read_without_media); + qtest_add_func("/fdc/media_change", test_media_change); + qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt); + qtest_add_func("/fdc/relative_seek", test_relative_seek); + qtest_add_func("/fdc/read_id", test_read_id); + qtest_add_func("/fdc/verify", test_verify); + qtest_add_func("/fdc/media_insert", test_media_insert); + qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1); + qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18); + qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19); + qtest_add_func("/fdc/fuzz-registers", fuzz_registers); + + ret = g_test_run(); + + /* Cleanup */ + qtest_end(); + unlink(test_image); + + return ret; +} diff --git a/tests/qtest/fw_cfg-test.c b/tests/qtest/fw_cfg-test.c new file mode 100644 index 0000000000..5dc807ba23 --- /dev/null +++ b/tests/qtest/fw_cfg-test.c @@ -0,0 +1,260 @@ +/* + * qtest fw_cfg test case + * + * Copyright IBM, Corp. 2012-2013 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "libqtest.h" +#include "standard-headers/linux/qemu_fw_cfg.h" +#include "libqos/fw_cfg.h" +#include "qemu/bswap.h" + +static uint64_t ram_size = 128 << 20; +static uint16_t nb_cpus = 1; +static uint16_t max_cpus = 1; +static uint64_t nb_nodes = 0; +static uint16_t boot_menu = 0; + +static void test_fw_cfg_signature(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + char buf[5]; + + s = qtest_init(""); + fw_cfg = pc_fw_cfg_init(s); + + qfw_cfg_get(fw_cfg, FW_CFG_SIGNATURE, buf, 4); + buf[4] = 0; + + g_assert_cmpstr(buf, ==, "QEMU"); + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +static void test_fw_cfg_id(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + uint32_t id; + + s = qtest_init(""); + fw_cfg = pc_fw_cfg_init(s); + + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); + g_assert((id == 1) || + (id == 3)); + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +static void test_fw_cfg_uuid(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + + uint8_t buf[16]; + static const uint8_t uuid[16] = { + 0x46, 0x00, 0xcb, 0x32, 0x38, 0xec, 0x4b, 0x2f, + 0x8a, 0xcb, 0x81, 0xc6, 0xea, 0x54, 0xf2, 0xd8, + }; + + s = qtest_init("-uuid 4600cb32-38ec-4b2f-8acb-81c6ea54f2d8"); + fw_cfg = pc_fw_cfg_init(s); + + qfw_cfg_get(fw_cfg, FW_CFG_UUID, buf, 16); + g_assert(memcmp(buf, uuid, sizeof(buf)) == 0); + + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); + +} + +static void test_fw_cfg_ram_size(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + + s = qtest_init(""); + fw_cfg = pc_fw_cfg_init(s); + + g_assert_cmpint(qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE), ==, ram_size); + + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +static void test_fw_cfg_nographic(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + + s = qtest_init(""); + fw_cfg = pc_fw_cfg_init(s); + + g_assert_cmpint(qfw_cfg_get_u16(fw_cfg, FW_CFG_NOGRAPHIC), ==, 0); + + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +static void test_fw_cfg_nb_cpus(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + + s = qtest_init(""); + fw_cfg = pc_fw_cfg_init(s); + + g_assert_cmpint(qfw_cfg_get_u16(fw_cfg, FW_CFG_NB_CPUS), ==, nb_cpus); + + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +static void test_fw_cfg_max_cpus(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + + s = qtest_init(""); + fw_cfg = pc_fw_cfg_init(s); + + g_assert_cmpint(qfw_cfg_get_u16(fw_cfg, FW_CFG_MAX_CPUS), ==, max_cpus); + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +static void test_fw_cfg_numa(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + uint64_t *cpu_mask; + uint64_t *node_mask; + + s = qtest_init(""); + fw_cfg = pc_fw_cfg_init(s); + + g_assert_cmpint(qfw_cfg_get_u64(fw_cfg, FW_CFG_NUMA), ==, nb_nodes); + + cpu_mask = g_new0(uint64_t, max_cpus); + node_mask = g_new0(uint64_t, nb_nodes); + + qfw_cfg_read_data(fw_cfg, cpu_mask, sizeof(uint64_t) * max_cpus); + qfw_cfg_read_data(fw_cfg, node_mask, sizeof(uint64_t) * nb_nodes); + + if (nb_nodes) { + g_assert(cpu_mask[0] & 0x01); + g_assert_cmpint(node_mask[0], ==, ram_size); + } + + g_free(node_mask); + g_free(cpu_mask); + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +static void test_fw_cfg_boot_menu(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + + s = qtest_init(""); + fw_cfg = pc_fw_cfg_init(s); + + g_assert_cmpint(qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_MENU), ==, boot_menu); + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +static void test_fw_cfg_reboot_timeout(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + uint32_t reboot_timeout = 0; + size_t filesize; + + s = qtest_init("-boot reboot-timeout=15"); + fw_cfg = pc_fw_cfg_init(s); + + filesize = qfw_cfg_get_file(fw_cfg, "etc/boot-fail-wait", + &reboot_timeout, sizeof(reboot_timeout)); + g_assert_cmpint(filesize, ==, sizeof(reboot_timeout)); + reboot_timeout = le32_to_cpu(reboot_timeout); + g_assert_cmpint(reboot_timeout, ==, 15); + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +static void test_fw_cfg_no_reboot_timeout(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + uint32_t reboot_timeout = 0; + size_t filesize; + + /* Special value -1 means "don't reboot" */ + s = qtest_init("-boot reboot-timeout=-1"); + fw_cfg = pc_fw_cfg_init(s); + + filesize = qfw_cfg_get_file(fw_cfg, "etc/boot-fail-wait", + &reboot_timeout, sizeof(reboot_timeout)); + g_assert_cmpint(filesize, ==, sizeof(reboot_timeout)); + reboot_timeout = le32_to_cpu(reboot_timeout); + g_assert_cmpint(reboot_timeout, ==, UINT32_MAX); + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +static void test_fw_cfg_splash_time(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + uint16_t splash_time = 0; + size_t filesize; + + s = qtest_init("-boot splash-time=12"); + fw_cfg = pc_fw_cfg_init(s); + + filesize = qfw_cfg_get_file(fw_cfg, "etc/boot-menu-wait", + &splash_time, sizeof(splash_time)); + g_assert_cmpint(filesize, ==, sizeof(splash_time)); + splash_time = le16_to_cpu(splash_time); + g_assert_cmpint(splash_time, ==, 12); + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("fw_cfg/signature", test_fw_cfg_signature); + qtest_add_func("fw_cfg/id", test_fw_cfg_id); + qtest_add_func("fw_cfg/uuid", test_fw_cfg_uuid); + qtest_add_func("fw_cfg/ram_size", test_fw_cfg_ram_size); + qtest_add_func("fw_cfg/nographic", test_fw_cfg_nographic); + qtest_add_func("fw_cfg/nb_cpus", test_fw_cfg_nb_cpus); +#if 0 + qtest_add_func("fw_cfg/machine_id", test_fw_cfg_machine_id); + qtest_add_func("fw_cfg/kernel", test_fw_cfg_kernel); + qtest_add_func("fw_cfg/initrd", test_fw_cfg_initrd); + qtest_add_func("fw_cfg/boot_device", test_fw_cfg_boot_device); +#endif + qtest_add_func("fw_cfg/max_cpus", test_fw_cfg_max_cpus); + qtest_add_func("fw_cfg/numa", test_fw_cfg_numa); + qtest_add_func("fw_cfg/boot_menu", test_fw_cfg_boot_menu); + qtest_add_func("fw_cfg/reboot_timeout", test_fw_cfg_reboot_timeout); + qtest_add_func("fw_cfg/no_reboot_timeout", test_fw_cfg_no_reboot_timeout); + qtest_add_func("fw_cfg/splash_time", test_fw_cfg_splash_time); + + return g_test_run(); +} diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c new file mode 100644 index 0000000000..a249800544 --- /dev/null +++ b/tests/qtest/hd-geo-test.c @@ -0,0 +1,988 @@ +/* + * Hard disk geometry test cases. + * + * Copyright (c) 2012 Red Hat Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Covers only IDE and tests only CMOS contents. Better than nothing. + * Improvements welcome. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/bswap.h" +#include "qapi/qmp/qlist.h" +#include "libqtest.h" +#include "libqos/fw_cfg.h" +#include "libqos/libqos.h" +#include "standard-headers/linux/qemu_fw_cfg.h" + +#define ARGV_SIZE 256 + +static char *create_test_img(int secs) +{ + char *template = strdup("/tmp/qtest.XXXXXX"); + int fd, ret; + + fd = mkstemp(template); + g_assert(fd >= 0); + ret = ftruncate(fd, (off_t)secs * 512); + close(fd); + + if (ret) { + free(template); + template = NULL; + } + + return template; +} + +typedef struct { + int cyls, heads, secs, trans; +} CHST; + +typedef enum { + mbr_blank, mbr_lba, mbr_chs, + mbr_last +} MBRcontents; + +typedef enum { + /* order is relevant */ + backend_small, backend_large, backend_empty, + backend_last +} Backend; + +static const int img_secs[backend_last] = { + [backend_small] = 61440, + [backend_large] = 8388608, + [backend_empty] = -1, +}; + +static const CHST hd_chst[backend_last][mbr_last] = { + [backend_small] = { + [mbr_blank] = { 60, 16, 63, 0 }, + [mbr_lba] = { 60, 16, 63, 2 }, + [mbr_chs] = { 60, 16, 63, 0 } + }, + [backend_large] = { + [mbr_blank] = { 8322, 16, 63, 1 }, + [mbr_lba] = { 8322, 16, 63, 1 }, + [mbr_chs] = { 8322, 16, 63, 0 } + }, +}; + +static char *img_file_name[backend_last]; + +static const CHST *cur_ide[4]; + +static bool is_hd(const CHST *expected_chst) +{ + return expected_chst && expected_chst->cyls; +} + +static void test_cmos_byte(QTestState *qts, int reg, int expected) +{ + enum { cmos_base = 0x70 }; + int actual; + + qtest_outb(qts, cmos_base + 0, reg); + actual = qtest_inb(qts, cmos_base + 1); + g_assert(actual == expected); +} + +static void test_cmos_bytes(QTestState *qts, int reg0, int n, + uint8_t expected[]) +{ + int i; + + for (i = 0; i < 9; i++) { + test_cmos_byte(qts, reg0 + i, expected[i]); + } +} + +static void test_cmos_disk_data(QTestState *qts) +{ + test_cmos_byte(qts, 0x12, + (is_hd(cur_ide[0]) ? 0xf0 : 0) | + (is_hd(cur_ide[1]) ? 0x0f : 0)); +} + +static void test_cmos_drive_cyl(QTestState *qts, int reg0, + const CHST *expected_chst) +{ + if (is_hd(expected_chst)) { + int c = expected_chst->cyls; + int h = expected_chst->heads; + int s = expected_chst->secs; + uint8_t expected_bytes[9] = { + c & 0xff, c >> 8, h, 0xff, 0xff, 0xc0 | ((h > 8) << 3), + c & 0xff, c >> 8, s + }; + test_cmos_bytes(qts, reg0, 9, expected_bytes); + } else { + int i; + + for (i = 0; i < 9; i++) { + test_cmos_byte(qts, reg0 + i, 0); + } + } +} + +static void test_cmos_drive1(QTestState *qts) +{ + test_cmos_byte(qts, 0x19, is_hd(cur_ide[0]) ? 47 : 0); + test_cmos_drive_cyl(qts, 0x1b, cur_ide[0]); +} + +static void test_cmos_drive2(QTestState *qts) +{ + test_cmos_byte(qts, 0x1a, is_hd(cur_ide[1]) ? 47 : 0); + test_cmos_drive_cyl(qts, 0x24, cur_ide[1]); +} + +static void test_cmos_disktransflag(QTestState *qts) +{ + int val, i; + + val = 0; + for (i = 0; i < ARRAY_SIZE(cur_ide); i++) { + if (is_hd(cur_ide[i])) { + val |= cur_ide[i]->trans << (2 * i); + } + } + test_cmos_byte(qts, 0x39, val); +} + +static void test_cmos(QTestState *qts) +{ + test_cmos_disk_data(qts); + test_cmos_drive1(qts); + test_cmos_drive2(qts); + test_cmos_disktransflag(qts); +} + +static int append_arg(int argc, char *argv[], int argv_sz, char *arg) +{ + g_assert(argc + 1 < argv_sz); + argv[argc++] = arg; + argv[argc] = NULL; + return argc; +} + +static int setup_common(char *argv[], int argv_sz) +{ + memset(cur_ide, 0, sizeof(cur_ide)); + return append_arg(0, argv, argv_sz, + g_strdup("-nodefaults")); +} + +static void setup_mbr(int img_idx, MBRcontents mbr) +{ + static const uint8_t part_lba[16] = { + /* chs 0,1,1 (lba 63) to chs 0,127,63 (8001 sectors) */ + 0x80, 1, 1, 0, 6, 127, 63, 0, 63, 0, 0, 0, 0x41, 0x1F, 0, 0, + }; + static const uint8_t part_chs[16] = { + /* chs 0,1,1 (lba 63) to chs 7,15,63 (8001 sectors) */ + 0x80, 1, 1, 0, 6, 15, 63, 7, 63, 0, 0, 0, 0x41, 0x1F, 0, 0, + }; + uint8_t buf[512]; + int fd, ret; + + memset(buf, 0, sizeof(buf)); + + if (mbr != mbr_blank) { + buf[0x1fe] = 0x55; + buf[0x1ff] = 0xAA; + memcpy(buf + 0x1BE, mbr == mbr_lba ? part_lba : part_chs, 16); + } + + fd = open(img_file_name[img_idx], O_WRONLY); + g_assert(fd >= 0); + ret = write(fd, buf, sizeof(buf)); + g_assert(ret == sizeof(buf)); + close(fd); +} + +static int setup_ide(int argc, char *argv[], int argv_sz, + int ide_idx, const char *dev, int img_idx, + MBRcontents mbr) +{ + char *s1, *s2, *s3; + + s1 = g_strdup_printf("-drive id=drive%d,if=%s", + ide_idx, dev ? "none" : "ide"); + s2 = dev ? g_strdup("") : g_strdup_printf(",index=%d", ide_idx); + + if (img_secs[img_idx] >= 0) { + setup_mbr(img_idx, mbr); + s3 = g_strdup_printf(",format=raw,file=%s", img_file_name[img_idx]); + } else { + s3 = g_strdup(",media=cdrom"); + } + argc = append_arg(argc, argv, argv_sz, + g_strdup_printf("%s%s%s", s1, s2, s3)); + g_free(s1); + g_free(s2); + g_free(s3); + + if (dev) { + argc = append_arg(argc, argv, argv_sz, + g_strdup_printf("-device %s,drive=drive%d," + "bus=ide.%d,unit=%d", + dev, ide_idx, + ide_idx / 2, ide_idx % 2)); + } + return argc; +} + +/* + * Test case: no IDE devices + */ +static void test_ide_none(void) +{ + char **argv = g_new0(char *, ARGV_SIZE); + char *args; + QTestState *qts; + + setup_common(argv, ARGV_SIZE); + args = g_strjoinv(" ", argv); + qts = qtest_init(args); + g_strfreev(argv); + g_free(args); + test_cmos(qts); + qtest_quit(qts); +} + +static void test_ide_mbr(bool use_device, MBRcontents mbr) +{ + char **argv = g_new0(char *, ARGV_SIZE); + char *args; + int argc; + Backend i; + const char *dev; + QTestState *qts; + + argc = setup_common(argv, ARGV_SIZE); + for (i = 0; i < backend_last; i++) { + cur_ide[i] = &hd_chst[i][mbr]; + dev = use_device ? (is_hd(cur_ide[i]) ? "ide-hd" : "ide-cd") : NULL; + argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr); + } + args = g_strjoinv(" ", argv); + qts = qtest_init(args); + g_strfreev(argv); + g_free(args); + test_cmos(qts); + qtest_quit(qts); +} + +/* + * Test case: IDE devices (if=ide) with blank MBRs + */ +static void test_ide_drive_mbr_blank(void) +{ + test_ide_mbr(false, mbr_blank); +} + +/* + * Test case: IDE devices (if=ide) with MBRs indicating LBA is in use + */ +static void test_ide_drive_mbr_lba(void) +{ + test_ide_mbr(false, mbr_lba); +} + +/* + * Test case: IDE devices (if=ide) with MBRs indicating CHS is in use + */ +static void test_ide_drive_mbr_chs(void) +{ + test_ide_mbr(false, mbr_chs); +} + +/* + * Test case: IDE devices (if=none) with blank MBRs + */ +static void test_ide_device_mbr_blank(void) +{ + test_ide_mbr(true, mbr_blank); +} + +/* + * Test case: IDE devices (if=none) with MBRs indicating LBA is in use + */ +static void test_ide_device_mbr_lba(void) +{ + test_ide_mbr(true, mbr_lba); +} + +/* + * Test case: IDE devices (if=none) with MBRs indicating CHS is in use + */ +static void test_ide_device_mbr_chs(void) +{ + test_ide_mbr(true, mbr_chs); +} + +static void test_ide_drive_user(const char *dev, bool trans) +{ + char **argv = g_new0(char *, ARGV_SIZE); + char *args, *opts; + int argc; + int secs = img_secs[backend_small]; + const CHST expected_chst = { secs / (4 * 32) , 4, 32, trans }; + QTestState *qts; + + argc = setup_common(argv, ARGV_SIZE); + opts = g_strdup_printf("%s,%scyls=%d,heads=%d,secs=%d", + dev, trans ? "bios-chs-trans=lba," : "", + expected_chst.cyls, expected_chst.heads, + expected_chst.secs); + cur_ide[0] = &expected_chst; + argc = setup_ide(argc, argv, ARGV_SIZE, 0, opts, backend_small, mbr_chs); + g_free(opts); + args = g_strjoinv(" ", argv); + qts = qtest_init(args); + g_strfreev(argv); + g_free(args); + test_cmos(qts); + qtest_quit(qts); +} + +/* + * Test case: IDE device (if=none) with explicit CHS + */ +static void test_ide_device_user_chs(void) +{ + test_ide_drive_user("ide-hd", false); +} + +/* + * Test case: IDE device (if=none) with explicit CHS and translation + */ +static void test_ide_device_user_chst(void) +{ + test_ide_drive_user("ide-hd", true); +} + +/* + * Test case: IDE devices (if=ide), but use index=0 for CD-ROM + */ +static void test_ide_drive_cd_0(void) +{ + char **argv = g_new0(char *, ARGV_SIZE); + char *args; + int argc, ide_idx; + Backend i; + QTestState *qts; + + argc = setup_common(argv, ARGV_SIZE); + for (i = 0; i <= backend_empty; i++) { + ide_idx = backend_empty - i; + cur_ide[ide_idx] = &hd_chst[i][mbr_blank]; + argc = setup_ide(argc, argv, ARGV_SIZE, ide_idx, NULL, i, mbr_blank); + } + args = g_strjoinv(" ", argv); + qts = qtest_init(args); + g_strfreev(argv); + g_free(args); + test_cmos(qts); + qtest_quit(qts); +} + +typedef struct { + bool active; + uint32_t head; + uint32_t sector; + uint32_t cyl; + uint32_t end_head; + uint32_t end_sector; + uint32_t end_cyl; + uint32_t start_sect; + uint32_t nr_sects; +} MBRpartitions[4]; + +static MBRpartitions empty_mbr = { {false, 0, 0, 0, 0, 0, 0, 0, 0}, + {false, 0, 0, 0, 0, 0, 0, 0, 0}, + {false, 0, 0, 0, 0, 0, 0, 0, 0}, + {false, 0, 0, 0, 0, 0, 0, 0, 0} }; + +static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) +{ + const char *template = "/tmp/qtest.XXXXXX"; + char *raw_path = strdup(template); + char *qcow2_path = strdup(template); + char cmd[100 + 2 * PATH_MAX]; + uint8_t buf[512]; + int i, ret, fd, offset; + uint64_t qcow2_size = sectors * 512; + uint8_t status, parttype, head, sector, cyl; + char *qemu_img_path; + char *qemu_img_abs_path; + + offset = 0xbe; + + for (i = 0; i < 4; i++) { + status = mbr[i].active ? 0x80 : 0x00; + g_assert(mbr[i].head < 256); + g_assert(mbr[i].sector < 64); + g_assert(mbr[i].cyl < 1024); + head = mbr[i].head; + sector = mbr[i].sector + ((mbr[i].cyl & 0x300) >> 2); + cyl = mbr[i].cyl & 0xff; + + buf[offset + 0x0] = status; + buf[offset + 0x1] = head; + buf[offset + 0x2] = sector; + buf[offset + 0x3] = cyl; + + parttype = 0; + g_assert(mbr[i].end_head < 256); + g_assert(mbr[i].end_sector < 64); + g_assert(mbr[i].end_cyl < 1024); + head = mbr[i].end_head; + sector = mbr[i].end_sector + ((mbr[i].end_cyl & 0x300) >> 2); + cyl = mbr[i].end_cyl & 0xff; + + buf[offset + 0x4] = parttype; + buf[offset + 0x5] = head; + buf[offset + 0x6] = sector; + buf[offset + 0x7] = cyl; + + (*(uint32_t *)&buf[offset + 0x8]) = cpu_to_le32(mbr[i].start_sect); + (*(uint32_t *)&buf[offset + 0xc]) = cpu_to_le32(mbr[i].nr_sects); + + offset += 0x10; + } + + fd = mkstemp(raw_path); + g_assert(fd); + close(fd); + + fd = open(raw_path, O_WRONLY); + g_assert(fd >= 0); + ret = write(fd, buf, sizeof(buf)); + g_assert(ret == sizeof(buf)); + close(fd); + + fd = mkstemp(qcow2_path); + g_assert(fd); + close(fd); + + qemu_img_path = getenv("QTEST_QEMU_IMG"); + g_assert(qemu_img_path); + qemu_img_abs_path = realpath(qemu_img_path, NULL); + g_assert(qemu_img_abs_path); + + ret = snprintf(cmd, sizeof(cmd), + "%s convert -f raw -O qcow2 %s %s > /dev/null", + qemu_img_abs_path, + raw_path, qcow2_path); + g_assert((0 < ret) && (ret <= sizeof(cmd))); + ret = system(cmd); + g_assert(ret == 0); + + ret = snprintf(cmd, sizeof(cmd), + "%s resize %s %" PRIu64 " > /dev/null", + qemu_img_abs_path, + qcow2_path, qcow2_size); + g_assert((0 < ret) && (ret <= sizeof(cmd))); + ret = system(cmd); + g_assert(ret == 0); + + free(qemu_img_abs_path); + + unlink(raw_path); + free(raw_path); + + return qcow2_path; +} + +#define BIOS_GEOMETRY_MAX_SIZE 10000 + +typedef struct { + uint32_t c; + uint32_t h; + uint32_t s; +} CHS; + +typedef struct { + const char *dev_path; + CHS chs; +} CHSResult; + +static void read_bootdevices(QFWCFG *fw_cfg, CHSResult expected[]) +{ + char *buf = g_malloc0(BIOS_GEOMETRY_MAX_SIZE); + char *cur; + GList *results = NULL, *cur_result; + CHSResult *r; + int i; + int res; + bool found; + + qfw_cfg_get_file(fw_cfg, "bios-geometry", buf, BIOS_GEOMETRY_MAX_SIZE); + + for (cur = buf; *cur; cur++) { + if (*cur == '\n') { + *cur = '\0'; + } + } + cur = buf; + + while (strlen(cur)) { + + r = g_malloc0(sizeof(*r)); + r->dev_path = g_malloc0(strlen(cur) + 1); + res = sscanf(cur, "%s %" PRIu32 " %" PRIu32 " %" PRIu32, + (char *)r->dev_path, + &(r->chs.c), &(r->chs.h), &(r->chs.s)); + + g_assert(res == 4); + + results = g_list_prepend(results, r); + + cur += strlen(cur) + 1; + } + + i = 0; + + while (expected[i].dev_path) { + found = false; + cur_result = results; + while (cur_result) { + r = cur_result->data; + if (!strcmp(r->dev_path, expected[i].dev_path) && + !memcmp(&(r->chs), &(expected[i].chs), sizeof(r->chs))) { + found = true; + break; + } + cur_result = g_list_next(cur_result); + } + g_assert(found); + g_free((char *)((CHSResult *)cur_result->data)->dev_path); + g_free(cur_result->data); + results = g_list_delete_link(results, cur_result); + i++; + } + + g_assert(results == NULL); + + g_free(buf); +} + +#define MAX_DRIVES 30 + +typedef struct { + char **argv; + int argc; + char **drives; + int n_drives; + int n_scsi_disks; + int n_scsi_controllers; + int n_virtio_disks; +} TestArgs; + +static TestArgs *create_args(void) +{ + TestArgs *args = g_malloc0(sizeof(*args)); + args->argv = g_new0(char *, ARGV_SIZE); + args->argc = append_arg(args->argc, args->argv, + ARGV_SIZE, g_strdup("-nodefaults")); + args->drives = g_new0(char *, MAX_DRIVES); + return args; +} + +static void add_drive_with_mbr(TestArgs *args, + MBRpartitions mbr, uint64_t sectors) +{ + char *img_file_name; + char part[300]; + int ret; + + g_assert(args->n_drives < MAX_DRIVES); + + img_file_name = create_qcow2_with_mbr(mbr, sectors); + + args->drives[args->n_drives] = img_file_name; + ret = snprintf(part, sizeof(part), + "-drive file=%s,if=none,format=qcow2,id=disk%d", + img_file_name, args->n_drives); + g_assert((0 < ret) && (ret <= sizeof(part))); + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part)); + args->n_drives++; +} + +static void add_ide_disk(TestArgs *args, + int drive_idx, int bus, int unit, int c, int h, int s) +{ + char part[300]; + int ret; + + ret = snprintf(part, sizeof(part), + "-device ide-hd,drive=disk%d,bus=ide.%d,unit=%d," + "lcyls=%d,lheads=%d,lsecs=%d", + drive_idx, bus, unit, c, h, s); + g_assert((0 < ret) && (ret <= sizeof(part))); + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part)); +} + +static void add_scsi_controller(TestArgs *args, + const char *type, + const char *bus, + int addr) +{ + char part[300]; + int ret; + + ret = snprintf(part, sizeof(part), + "-device %s,id=scsi%d,bus=%s,addr=%d", + type, args->n_scsi_controllers, bus, addr); + g_assert((0 < ret) && (ret <= sizeof(part))); + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part)); + args->n_scsi_controllers++; +} + +static void add_scsi_disk(TestArgs *args, + int drive_idx, int bus, + int channel, int scsi_id, int lun, + int c, int h, int s) +{ + char part[300]; + int ret; + + ret = snprintf(part, sizeof(part), + "-device scsi-hd,id=scsi-disk%d,drive=disk%d," + "bus=scsi%d.0," + "channel=%d,scsi-id=%d,lun=%d," + "lcyls=%d,lheads=%d,lsecs=%d", + args->n_scsi_disks, drive_idx, bus, channel, scsi_id, lun, + c, h, s); + g_assert((0 < ret) && (ret <= sizeof(part))); + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part)); + args->n_scsi_disks++; +} + +static void add_virtio_disk(TestArgs *args, + int drive_idx, const char *bus, int addr, + int c, int h, int s) +{ + char part[300]; + int ret; + + ret = snprintf(part, sizeof(part), + "-device virtio-blk-pci,id=virtio-disk%d," + "drive=disk%d,bus=%s,addr=%d," + "lcyls=%d,lheads=%d,lsecs=%d", + args->n_virtio_disks, drive_idx, bus, addr, c, h, s); + g_assert((0 < ret) && (ret <= sizeof(part))); + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part)); + args->n_virtio_disks++; +} + +static void test_override(TestArgs *args, CHSResult expected[]) +{ + QTestState *qts; + char *joined_args; + QFWCFG *fw_cfg; + int i; + + joined_args = g_strjoinv(" ", args->argv); + + qts = qtest_init(joined_args); + fw_cfg = pc_fw_cfg_init(qts); + + read_bootdevices(fw_cfg, expected); + + g_free(joined_args); + qtest_quit(qts); + + g_free(fw_cfg); + + for (i = 0; i < args->n_drives; i++) { + unlink(args->drives[i]); + free(args->drives[i]); + } + g_free(args->drives); + g_strfreev(args->argv); + g_free(args); +} + +static void test_override_ide(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + {"/pci@i0cf8/ide@1,1/drive@0/disk@0", {10000, 120, 30} }, + {"/pci@i0cf8/ide@1,1/drive@0/disk@1", {9000, 120, 30} }, + {"/pci@i0cf8/ide@1,1/drive@1/disk@0", {0, 1, 1} }, + {"/pci@i0cf8/ide@1,1/drive@1/disk@1", {1, 0, 0} }, + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_ide_disk(args, 0, 0, 0, 10000, 120, 30); + add_ide_disk(args, 1, 0, 1, 9000, 120, 30); + add_ide_disk(args, 2, 1, 0, 0, 1, 1); + add_ide_disk(args, 3, 1, 1, 1, 0, 0); + test_override(args, expected); +} + +static void test_override_scsi(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + {"/pci@i0cf8/scsi@3/channel@0/disk@0,0", {10000, 120, 30} }, + {"/pci@i0cf8/scsi@3/channel@0/disk@1,0", {9000, 120, 30} }, + {"/pci@i0cf8/scsi@3/channel@0/disk@2,0", {1, 0, 0} }, + {"/pci@i0cf8/scsi@3/channel@0/disk@3,0", {0, 1, 0} }, + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_scsi_controller(args, "lsi53c895a", "pci.0", 3); + add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30); + add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30); + add_scsi_disk(args, 2, 0, 0, 2, 0, 1, 0, 0); + add_scsi_disk(args, 3, 0, 0, 3, 0, 0, 1, 0); + test_override(args, expected); +} + +static void test_override_scsi_2_controllers(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + {"/pci@i0cf8/scsi@3/channel@0/disk@0,0", {10000, 120, 30} }, + {"/pci@i0cf8/scsi@3/channel@0/disk@1,0", {9000, 120, 30} }, + {"/pci@i0cf8/scsi@4/channel@0/disk@0,1", {1, 0, 0} }, + {"/pci@i0cf8/scsi@4/channel@0/disk@1,2", {0, 1, 0} }, + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_scsi_controller(args, "lsi53c895a", "pci.0", 3); + add_scsi_controller(args, "virtio-scsi-pci", "pci.0", 4); + add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30); + add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30); + add_scsi_disk(args, 2, 1, 0, 0, 1, 1, 0, 0); + add_scsi_disk(args, 3, 1, 0, 1, 2, 0, 1, 0); + test_override(args, expected); +} + +static void test_override_virtio_blk(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + {"/pci@i0cf8/scsi@3/disk@0,0", {10000, 120, 30} }, + {"/pci@i0cf8/scsi@4/disk@0,0", {9000, 120, 30} }, + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_virtio_disk(args, 0, "pci.0", 3, 10000, 120, 30); + add_virtio_disk(args, 1, "pci.0", 4, 9000, 120, 30); + test_override(args, expected); +} + +static void test_override_zero_chs(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_ide_disk(args, 0, 1, 1, 0, 0, 0); + test_override(args, expected); +} + +static void test_override_scsi_hot_unplug(void) +{ + QTestState *qts; + char *joined_args; + QFWCFG *fw_cfg; + QDict *response; + int i; + TestArgs *args = create_args(); + CHSResult expected[] = { + {"/pci@i0cf8/scsi@2/channel@0/disk@0,0", {10000, 120, 30} }, + {"/pci@i0cf8/scsi@2/channel@0/disk@1,0", {20, 20, 20} }, + {NULL, {0, 0, 0} } + }; + CHSResult expected2[] = { + {"/pci@i0cf8/scsi@2/channel@0/disk@1,0", {20, 20, 20} }, + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_scsi_controller(args, "virtio-scsi-pci", "pci.0", 2); + add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30); + add_scsi_disk(args, 1, 0, 0, 1, 0, 20, 20, 20); + + joined_args = g_strjoinv(" ", args->argv); + + qts = qtest_init(joined_args); + fw_cfg = pc_fw_cfg_init(qts); + + read_bootdevices(fw_cfg, expected); + + /* unplug device an restart */ + response = qtest_qmp(qts, + "{ 'execute': 'device_del'," + " 'arguments': {'id': 'scsi-disk0' }}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + response = qtest_qmp(qts, + "{ 'execute': 'system_reset', 'arguments': { }}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + qtest_qmp_eventwait(qts, "RESET"); + + read_bootdevices(fw_cfg, expected2); + + g_free(joined_args); + qtest_quit(qts); + + g_free(fw_cfg); + + for (i = 0; i < args->n_drives; i++) { + unlink(args->drives[i]); + free(args->drives[i]); + } + g_free(args->drives); + g_strfreev(args->argv); + g_free(args); +} + +static void test_override_virtio_hot_unplug(void) +{ + QTestState *qts; + char *joined_args; + QFWCFG *fw_cfg; + QDict *response; + int i; + TestArgs *args = create_args(); + CHSResult expected[] = { + {"/pci@i0cf8/scsi@2/disk@0,0", {10000, 120, 30} }, + {"/pci@i0cf8/scsi@3/disk@0,0", {20, 20, 20} }, + {NULL, {0, 0, 0} } + }; + CHSResult expected2[] = { + {"/pci@i0cf8/scsi@3/disk@0,0", {20, 20, 20} }, + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_virtio_disk(args, 0, "pci.0", 2, 10000, 120, 30); + add_virtio_disk(args, 1, "pci.0", 3, 20, 20, 20); + + joined_args = g_strjoinv(" ", args->argv); + + qts = qtest_init(joined_args); + fw_cfg = pc_fw_cfg_init(qts); + + read_bootdevices(fw_cfg, expected); + + /* unplug device an restart */ + response = qtest_qmp(qts, + "{ 'execute': 'device_del'," + " 'arguments': {'id': 'virtio-disk0' }}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + response = qtest_qmp(qts, + "{ 'execute': 'system_reset', 'arguments': { }}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + qtest_qmp_eventwait(qts, "RESET"); + + read_bootdevices(fw_cfg, expected2); + + g_free(joined_args); + qtest_quit(qts); + + g_free(fw_cfg); + + for (i = 0; i < args->n_drives; i++) { + unlink(args->drives[i]); + free(args->drives[i]); + } + g_free(args->drives); + g_strfreev(args->argv); + g_free(args); +} + +int main(int argc, char **argv) +{ + Backend i; + int ret; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < backend_last; i++) { + if (img_secs[i] >= 0) { + img_file_name[i] = create_test_img(img_secs[i]); + if (!img_file_name[i]) { + g_test_message("Could not create test images."); + goto test_add_done; + } + } else { + img_file_name[i] = NULL; + } + } + + qtest_add_func("hd-geo/ide/none", test_ide_none); + qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); + qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); + qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); + qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); + qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); + qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); + qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs); + qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs); + qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst); + if (have_qemu_img()) { + qtest_add_func("hd-geo/override/ide", test_override_ide); + qtest_add_func("hd-geo/override/scsi", test_override_scsi); + qtest_add_func("hd-geo/override/scsi_2_controllers", + test_override_scsi_2_controllers); + qtest_add_func("hd-geo/override/virtio_blk", test_override_virtio_blk); + qtest_add_func("hd-geo/override/zero_chs", test_override_zero_chs); + qtest_add_func("hd-geo/override/scsi_hot_unplug", + test_override_scsi_hot_unplug); + qtest_add_func("hd-geo/override/virtio_hot_unplug", + test_override_virtio_hot_unplug); + } else { + g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " + "skipping hd-geo/override/* tests"); + } + +test_add_done: + ret = g_test_run(); + + for (i = 0; i < backend_last; i++) { + if (img_file_name[i]) { + unlink(img_file_name[i]); + free(img_file_name[i]); + } + } + + return ret; +} diff --git a/tests/qtest/hexloader-test.c b/tests/qtest/hexloader-test.c new file mode 100644 index 0000000000..8b7aa2d72d --- /dev/null +++ b/tests/qtest/hexloader-test.c @@ -0,0 +1,45 @@ +/* + * QTest testcase for the Intel Hexadecimal Object File Loader + * + * Authors: + * Su Hang 2018 + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +/* Load 'test.hex' and verify that the in-memory contents are as expected. + * 'test.hex' is a memory test pattern stored in Hexadecimal Object + * format. It loads at 0x10000 in RAM and contains values from 0 through + * 255. + */ +static void hex_loader_test(void) +{ + unsigned int i; + const unsigned int base_addr = 0x00010000; + + QTestState *s = qtest_initf( + "-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); + g_assert_cmpuint(i, ==, val); + } + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/tmp/hex_loader", hex_loader_test); + ret = g_test_run(); + + return ret; +} diff --git a/tests/qtest/i440fx-test.c b/tests/qtest/i440fx-test.c new file mode 100644 index 0000000000..1f57d9684b --- /dev/null +++ b/tests/qtest/i440fx-test.c @@ -0,0 +1,413 @@ +/* + * qtest I440FX test case + * + * Copyright IBM, Corp. 2012-2013 + * Copyright Red Hat, Inc. 2013 + * + * Authors: + * Anthony Liguori + * Laszlo Ersek + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "libqtest-single.h" +#include "libqos/pci.h" +#include "libqos/pci-pc.h" +#include "hw/pci/pci_regs.h" + +#define BROKEN 1 + +typedef struct TestData +{ + int num_cpus; +} TestData; + +typedef struct FirmwareTestFixture { + /* decides whether we're testing -bios or -pflash */ + bool is_bios; +} FirmwareTestFixture; + +static QPCIBus *test_start_get_bus(const TestData *s) +{ + char *cmdline; + + cmdline = g_strdup_printf("-smp %d", s->num_cpus); + qtest_start(cmdline); + g_free(cmdline); + return qpci_new_pc(global_qtest, NULL); +} + +static void test_i440fx_defaults(gconstpointer opaque) +{ + const TestData *s = opaque; + QPCIBus *bus; + QPCIDevice *dev; + uint32_t value; + + bus = test_start_get_bus(s); + dev = qpci_device_find(bus, QPCI_DEVFN(0, 0)); + g_assert(dev != NULL); + + /* 3.2.2 */ + g_assert_cmpint(qpci_config_readw(dev, PCI_VENDOR_ID), ==, 0x8086); + /* 3.2.3 */ + g_assert_cmpint(qpci_config_readw(dev, PCI_DEVICE_ID), ==, 0x1237); +#ifndef BROKEN + /* 3.2.4 */ + g_assert_cmpint(qpci_config_readw(dev, PCI_COMMAND), ==, 0x0006); + /* 3.2.5 */ + g_assert_cmpint(qpci_config_readw(dev, PCI_STATUS), ==, 0x0280); +#endif + /* 3.2.7 */ + g_assert_cmpint(qpci_config_readb(dev, PCI_CLASS_PROG), ==, 0x00); + g_assert_cmpint(qpci_config_readw(dev, PCI_CLASS_DEVICE), ==, 0x0600); + /* 3.2.8 */ + g_assert_cmpint(qpci_config_readb(dev, PCI_LATENCY_TIMER), ==, 0x00); + /* 3.2.9 */ + g_assert_cmpint(qpci_config_readb(dev, PCI_HEADER_TYPE), ==, 0x00); + /* 3.2.10 */ + g_assert_cmpint(qpci_config_readb(dev, PCI_BIST), ==, 0x00); + + /* 3.2.11 */ + value = qpci_config_readw(dev, 0x50); /* PMCCFG */ + if (s->num_cpus == 1) { /* WPE */ + g_assert(!(value & (1 << 15))); + } else { + g_assert((value & (1 << 15))); + } + + g_assert(!(value & (1 << 6))); /* EPTE */ + + /* 3.2.12 */ + g_assert_cmpint(qpci_config_readb(dev, 0x52), ==, 0x00); /* DETURBO */ + /* 3.2.13 */ +#ifndef BROKEN + g_assert_cmpint(qpci_config_readb(dev, 0x53), ==, 0x80); /* DBC */ +#endif + /* 3.2.14 */ + g_assert_cmpint(qpci_config_readb(dev, 0x54), ==, 0x00); /* AXC */ + /* 3.2.15 */ + g_assert_cmpint(qpci_config_readw(dev, 0x55), ==, 0x0000); /* DRT */ +#ifndef BROKEN + /* 3.2.16 */ + g_assert_cmpint(qpci_config_readb(dev, 0x57), ==, 0x01); /* DRAMC */ + /* 3.2.17 */ + g_assert_cmpint(qpci_config_readb(dev, 0x58), ==, 0x10); /* DRAMT */ +#endif + /* 3.2.18 */ + g_assert_cmpint(qpci_config_readb(dev, 0x59), ==, 0x00); /* PAM0 */ + g_assert_cmpint(qpci_config_readb(dev, 0x5A), ==, 0x00); /* PAM1 */ + g_assert_cmpint(qpci_config_readb(dev, 0x5B), ==, 0x00); /* PAM2 */ + g_assert_cmpint(qpci_config_readb(dev, 0x5C), ==, 0x00); /* PAM3 */ + g_assert_cmpint(qpci_config_readb(dev, 0x5D), ==, 0x00); /* PAM4 */ + g_assert_cmpint(qpci_config_readb(dev, 0x5E), ==, 0x00); /* PAM5 */ + g_assert_cmpint(qpci_config_readb(dev, 0x5F), ==, 0x00); /* PAM6 */ +#ifndef BROKEN + /* 3.2.19 */ + g_assert_cmpint(qpci_config_readb(dev, 0x60), ==, 0x01); /* DRB0 */ + g_assert_cmpint(qpci_config_readb(dev, 0x61), ==, 0x01); /* DRB1 */ + g_assert_cmpint(qpci_config_readb(dev, 0x62), ==, 0x01); /* DRB2 */ + g_assert_cmpint(qpci_config_readb(dev, 0x63), ==, 0x01); /* DRB3 */ + g_assert_cmpint(qpci_config_readb(dev, 0x64), ==, 0x01); /* DRB4 */ + g_assert_cmpint(qpci_config_readb(dev, 0x65), ==, 0x01); /* DRB5 */ + g_assert_cmpint(qpci_config_readb(dev, 0x66), ==, 0x01); /* DRB6 */ + g_assert_cmpint(qpci_config_readb(dev, 0x67), ==, 0x01); /* DRB7 */ +#endif + /* 3.2.20 */ + g_assert_cmpint(qpci_config_readb(dev, 0x68), ==, 0x00); /* FDHC */ + /* 3.2.21 */ + g_assert_cmpint(qpci_config_readb(dev, 0x70), ==, 0x00); /* MTT */ +#ifndef BROKEN + /* 3.2.22 */ + g_assert_cmpint(qpci_config_readb(dev, 0x71), ==, 0x10); /* CLT */ +#endif + /* 3.2.23 */ + g_assert_cmpint(qpci_config_readb(dev, 0x72), ==, 0x02); /* SMRAM */ + /* 3.2.24 */ + g_assert_cmpint(qpci_config_readb(dev, 0x90), ==, 0x00); /* ERRCMD */ + /* 3.2.25 */ + g_assert_cmpint(qpci_config_readb(dev, 0x91), ==, 0x00); /* ERRSTS */ + /* 3.2.26 */ + g_assert_cmpint(qpci_config_readb(dev, 0x93), ==, 0x00); /* TRC */ + + g_free(dev); + qpci_free_pc(bus); + qtest_end(); +} + +#define PAM_RE 1 +#define PAM_WE 2 + +static void pam_set(QPCIDevice *dev, int index, int flags) +{ + int regno = 0x59 + (index / 2); + uint8_t reg; + + reg = qpci_config_readb(dev, regno); + if (index & 1) { + reg = (reg & 0x0F) | (flags << 4); + } else { + reg = (reg & 0xF0) | flags; + } + qpci_config_writeb(dev, regno, reg); +} + +static gboolean verify_area(uint32_t start, uint32_t end, uint8_t value) +{ + uint32_t size = end - start + 1; + gboolean ret = TRUE; + uint8_t *data; + int i; + + data = g_malloc0(size); + memread(start, data, size); + + g_test_message("verify_area: data[0] = 0x%x", data[0]); + + for (i = 0; i < size; i++) { + if (data[i] != value) { + ret = FALSE; + break; + } + } + + g_free(data); + + return ret; +} + +static void write_area(uint32_t start, uint32_t end, uint8_t value) +{ + uint32_t size = end - start + 1; + uint8_t *data; + + data = g_malloc(size); + memset(data, value, size); + memwrite(start, data, size); + + g_free(data); +} + +static void test_i440fx_pam(gconstpointer opaque) +{ + const TestData *s = opaque; + QPCIBus *bus; + QPCIDevice *dev; + int i; + static struct { + uint32_t start; + uint32_t end; + } pam_area[] = { + { 0, 0 }, /* Reserved */ + { 0xF0000, 0xFFFFF }, /* BIOS Area */ + { 0xC0000, 0xC3FFF }, /* Option ROM */ + { 0xC4000, 0xC7FFF }, /* Option ROM */ + { 0xC8000, 0xCBFFF }, /* Option ROM */ + { 0xCC000, 0xCFFFF }, /* Option ROM */ + { 0xD0000, 0xD3FFF }, /* Option ROM */ + { 0xD4000, 0xD7FFF }, /* Option ROM */ + { 0xD8000, 0xDBFFF }, /* Option ROM */ + { 0xDC000, 0xDFFFF }, /* Option ROM */ + { 0xE0000, 0xE3FFF }, /* BIOS Extension */ + { 0xE4000, 0xE7FFF }, /* BIOS Extension */ + { 0xE8000, 0xEBFFF }, /* BIOS Extension */ + { 0xEC000, 0xEFFFF }, /* BIOS Extension */ + }; + + bus = test_start_get_bus(s); + dev = qpci_device_find(bus, QPCI_DEVFN(0, 0)); + g_assert(dev != NULL); + + for (i = 0; i < ARRAY_SIZE(pam_area); i++) { + if (pam_area[i].start == pam_area[i].end) { + continue; + } + + g_test_message("Checking area 0x%05x..0x%05x", + pam_area[i].start, pam_area[i].end); + /* Switch to RE for the area */ + pam_set(dev, i, PAM_RE); + /* Verify the RAM is all zeros */ + g_assert(verify_area(pam_area[i].start, pam_area[i].end, 0)); + + /* Switch to WE for the area */ + pam_set(dev, i, PAM_RE | PAM_WE); + /* Write out a non-zero mask to the full area */ + write_area(pam_area[i].start, pam_area[i].end, 0x42); + +#ifndef BROKEN + /* QEMU only supports a limited form of PAM */ + + /* Switch to !RE for the area */ + pam_set(dev, i, PAM_WE); + /* Verify the area is not our mask */ + g_assert(!verify_area(pam_area[i].start, pam_area[i].end, 0x42)); +#endif + + /* Verify the area is our new mask */ + g_assert(verify_area(pam_area[i].start, pam_area[i].end, 0x42)); + + /* Write out a new mask */ + write_area(pam_area[i].start, pam_area[i].end, 0x82); + +#ifndef BROKEN + /* QEMU only supports a limited form of PAM */ + + /* Verify the area is not our mask */ + g_assert(!verify_area(pam_area[i].start, pam_area[i].end, 0x82)); + + /* Switch to RE for the area */ + pam_set(dev, i, PAM_RE | PAM_WE); +#endif + /* Verify the area is our new mask */ + g_assert(verify_area(pam_area[i].start, pam_area[i].end, 0x82)); + + /* Reset area */ + pam_set(dev, i, 0); + + /* Verify the area is not our new mask */ + g_assert(!verify_area(pam_area[i].start, pam_area[i].end, 0x82)); + } + + g_free(dev); + qpci_free_pc(bus); + qtest_end(); +} + +#define BLOB_SIZE ((size_t)65536) +#define ISA_BIOS_MAXSZ ((size_t)(128 * 1024)) + +/* Create a blob file, and return its absolute pathname as a dynamically + * allocated string. + * The file is closed before the function returns. + * In case of error, NULL is returned. The function prints the error message. + */ +static char *create_blob_file(void) +{ + int ret, fd; + char *pathname; + GError *error = NULL; + + ret = -1; + fd = g_file_open_tmp("blob_XXXXXX", &pathname, &error); + if (fd == -1) { + fprintf(stderr, "unable to create blob file: %s\n", error->message); + g_error_free(error); + } else { + if (ftruncate(fd, BLOB_SIZE) == -1) { + fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname, + BLOB_SIZE, strerror(errno)); + } else { + void *buf; + + buf = mmap(NULL, BLOB_SIZE, PROT_WRITE, MAP_SHARED, fd, 0); + if (buf == MAP_FAILED) { + fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, BLOB_SIZE, + strerror(errno)); + } else { + size_t i; + + for (i = 0; i < BLOB_SIZE; ++i) { + ((uint8_t *)buf)[i] = i; + } + munmap(buf, BLOB_SIZE); + ret = 0; + } + } + close(fd); + if (ret == -1) { + unlink(pathname); + g_free(pathname); + } + } + + return ret == -1 ? NULL : pathname; +} + +static void test_i440fx_firmware(FirmwareTestFixture *fixture, + gconstpointer user_data) +{ + char *fw_pathname, *cmdline; + uint8_t *buf; + size_t i, isa_bios_size; + + fw_pathname = create_blob_file(); + g_assert(fw_pathname != NULL); + + /* Better hope the user didn't put metacharacters in TMPDIR and co. */ + cmdline = g_strdup_printf("-S %s%s", fixture->is_bios + ? "-bios " + : "-drive if=pflash,format=raw,file=", + fw_pathname); + g_test_message("qemu cmdline: %s", cmdline); + qtest_start(cmdline); + g_free(cmdline); + + /* QEMU has loaded the firmware (because qtest_start() only returns after + * the QMP handshake completes). We must unlink the firmware blob right + * here, because any assertion firing below would leak it in the + * filesystem. This is also the reason why we recreate the blob every time + * this function is invoked. + */ + unlink(fw_pathname); + g_free(fw_pathname); + + /* check below 4G */ + buf = g_malloc0(BLOB_SIZE); + memread(0x100000000ULL - BLOB_SIZE, buf, BLOB_SIZE); + for (i = 0; i < BLOB_SIZE; ++i) { + g_assert_cmphex(buf[i], ==, (uint8_t)i); + } + + /* check in ISA space too */ + memset(buf, 0, BLOB_SIZE); + isa_bios_size = ISA_BIOS_MAXSZ < BLOB_SIZE ? ISA_BIOS_MAXSZ : BLOB_SIZE; + memread(0x100000 - isa_bios_size, buf, isa_bios_size); + for (i = 0; i < isa_bios_size; ++i) { + g_assert_cmphex(buf[i], ==, + (uint8_t)((BLOB_SIZE - isa_bios_size) + i)); + } + + g_free(buf); + qtest_end(); +} + +static void add_firmware_test(const char *testpath, + void (*setup_fixture)(FirmwareTestFixture *f, + gconstpointer test_data)) +{ + qtest_add(testpath, FirmwareTestFixture, NULL, setup_fixture, + test_i440fx_firmware, NULL); +} + +static void request_bios(FirmwareTestFixture *fixture, + gconstpointer user_data) +{ + fixture->is_bios = true; +} + +static void request_pflash(FirmwareTestFixture *fixture, + gconstpointer user_data) +{ + fixture->is_bios = false; +} + +int main(int argc, char **argv) +{ + TestData data; + + g_test_init(&argc, &argv, NULL); + + data.num_cpus = 1; + + qtest_add_data_func("i440fx/defaults", &data, test_i440fx_defaults); + qtest_add_data_func("i440fx/pam", &data, test_i440fx_pam); + add_firmware_test("i440fx/firmware/bios", request_bios); + add_firmware_test("i440fx/firmware/pflash", request_pflash); + + return g_test_run(); +} diff --git a/tests/qtest/i82801b11-test.c b/tests/qtest/i82801b11-test.c new file mode 100644 index 0000000000..4345da338b --- /dev/null +++ b/tests/qtest/i82801b11-test.c @@ -0,0 +1,31 @@ +/* + * QTest testcase for i82801b11 + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void nop(void) +{ +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/i82801b11/nop", nop); + + qtest_start("-machine q35 -device i82801b11-bridge,bus=pcie.0,addr=1e.0"); + ret = g_test_run(); + + qtest_end(); + + return ret; +} diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c new file mode 100644 index 0000000000..0277e7d5a9 --- /dev/null +++ b/tests/qtest/ide-test.c @@ -0,0 +1,1092 @@ +/* + * IDE test cases + * + * Copyright (c) 2013 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + + +#include "libqtest.h" +#include "libqos/libqos.h" +#include "libqos/pci-pc.h" +#include "libqos/malloc-pc.h" +#include "qapi/qmp/qdict.h" +#include "qemu-common.h" +#include "qemu/bswap.h" +#include "hw/pci/pci_ids.h" +#include "hw/pci/pci_regs.h" + +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(q, ...) qobject_unref(qtest_qmp(q, __VA_ARGS__)) + +#define TEST_IMAGE_SIZE 64 * 1024 * 1024 + +#define IDE_PCI_DEV 1 +#define IDE_PCI_FUNC 1 + +#define IDE_BASE 0x1f0 +#define IDE_PRIMARY_IRQ 14 + +#define ATAPI_BLOCK_SIZE 2048 + +/* How many bytes to receive via ATAPI PIO at one time. + * Must be less than 0xFFFF. */ +#define BYTE_COUNT_LIMIT 5120 + +enum { + reg_data = 0x0, + reg_feature = 0x1, + reg_error = 0x1, + reg_nsectors = 0x2, + reg_lba_low = 0x3, + reg_lba_middle = 0x4, + reg_lba_high = 0x5, + reg_device = 0x6, + reg_status = 0x7, + reg_command = 0x7, +}; + +enum { + BSY = 0x80, + DRDY = 0x40, + DF = 0x20, + DRQ = 0x08, + ERR = 0x01, +}; + +/* Error field */ +enum { + ABRT = 0x04, +}; + +enum { + DEV = 0x10, + LBA = 0x40, +}; + +enum { + bmreg_cmd = 0x0, + bmreg_status = 0x2, + bmreg_prdt = 0x4, +}; + +enum { + CMD_DSM = 0x06, + CMD_READ_DMA = 0xc8, + CMD_WRITE_DMA = 0xca, + CMD_FLUSH_CACHE = 0xe7, + CMD_IDENTIFY = 0xec, + CMD_PACKET = 0xa0, + + CMDF_ABORT = 0x100, + CMDF_NO_BM = 0x200, +}; + +enum { + BM_CMD_START = 0x1, + BM_CMD_WRITE = 0x8, /* write = from device to memory */ +}; + +enum { + BM_STS_ACTIVE = 0x1, + BM_STS_ERROR = 0x2, + BM_STS_INTR = 0x4, +}; + +enum { + PRDT_EOT = 0x80000000, +}; + +#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) +#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) + +static QPCIBus *pcibus = NULL; +static QGuestAllocator guest_malloc; + +static char tmp_path[] = "/tmp/qtest.XXXXXX"; +static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; + +static QTestState *ide_test_start(const char *cmdline_fmt, ...) +{ + QTestState *qts; + va_list ap; + + va_start(ap, cmdline_fmt); + qts = qtest_vinitf(cmdline_fmt, ap); + va_end(ap); + + pc_alloc_init(&guest_malloc, qts, 0); + + return qts; +} + +static void ide_test_quit(QTestState *qts) +{ + if (pcibus) { + qpci_free_pc(pcibus); + pcibus = NULL; + } + alloc_destroy(&guest_malloc); + qtest_quit(qts); +} + +static QPCIDevice *get_pci_device(QTestState *qts, QPCIBar *bmdma_bar, + QPCIBar *ide_bar) +{ + QPCIDevice *dev; + uint16_t vendor_id, device_id; + + if (!pcibus) { + pcibus = qpci_new_pc(qts, NULL); + } + + /* Find PCI device and verify it's the right one */ + dev = qpci_device_find(pcibus, QPCI_DEVFN(IDE_PCI_DEV, IDE_PCI_FUNC)); + g_assert(dev != NULL); + + vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID); + device_id = qpci_config_readw(dev, PCI_DEVICE_ID); + g_assert(vendor_id == PCI_VENDOR_ID_INTEL); + g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1); + + /* Map bmdma BAR */ + *bmdma_bar = qpci_iomap(dev, 4, NULL); + + *ide_bar = qpci_legacy_iomap(dev, IDE_BASE); + + qpci_device_enable(dev); + + return dev; +} + +static void free_pci_device(QPCIDevice *dev) +{ + /* libqos doesn't have a function for this, so free it manually */ + g_free(dev); +} + +typedef struct PrdtEntry { + uint32_t addr; + uint32_t size; +} QEMU_PACKED PrdtEntry; + +#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) +#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) + +static uint64_t trim_range_le(uint64_t sector, uint16_t count) +{ + /* 2-byte range, 6-byte LBA */ + return cpu_to_le64(((uint64_t)count << 48) + sector); +} + +static int send_dma_request(QTestState *qts, int cmd, uint64_t sector, + int nb_sectors, PrdtEntry *prdt, int prdt_entries, + void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar, + uint64_t sector, int nb_sectors)) +{ + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uintptr_t guest_prdt; + size_t len; + bool from_dev; + uint8_t status; + int flags; + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + flags = cmd & ~0xff; + cmd &= 0xff; + + switch (cmd) { + case CMD_READ_DMA: + case CMD_PACKET: + /* Assuming we only test data reads w/ ATAPI, otherwise we need to know + * the SCSI command being sent in the packet, too. */ + from_dev = true; + break; + case CMD_DSM: + case CMD_WRITE_DMA: + from_dev = false; + break; + default: + g_assert_not_reached(); + } + + if (flags & CMDF_NO_BM) { + qpci_config_writew(dev, PCI_COMMAND, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + } + + /* Select device 0 */ + qpci_io_writeb(dev, ide_bar, reg_device, 0 | LBA); + + /* Stop any running transfer, clear any pending interrupt */ + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); + qpci_io_writeb(dev, bmdma_bar, bmreg_status, BM_STS_INTR); + + /* Setup PRDT */ + len = sizeof(*prdt) * prdt_entries; + guest_prdt = guest_alloc(&guest_malloc, len); + qtest_memwrite(qts, guest_prdt, prdt, len); + qpci_io_writel(dev, bmdma_bar, bmreg_prdt, guest_prdt); + + /* ATA DMA command */ + if (cmd == CMD_PACKET) { + /* Enables ATAPI DMA; otherwise PIO is attempted */ + qpci_io_writeb(dev, ide_bar, reg_feature, 0x01); + } else { + if (cmd == CMD_DSM) { + /* trim bit */ + qpci_io_writeb(dev, ide_bar, reg_feature, 0x01); + } + qpci_io_writeb(dev, ide_bar, reg_nsectors, nb_sectors); + qpci_io_writeb(dev, ide_bar, reg_lba_low, sector & 0xff); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, (sector >> 8) & 0xff); + qpci_io_writeb(dev, ide_bar, reg_lba_high, (sector >> 16) & 0xff); + } + + qpci_io_writeb(dev, ide_bar, reg_command, cmd); + + if (post_exec) { + post_exec(dev, ide_bar, sector, nb_sectors); + } + + /* Start DMA transfer */ + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, + BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0)); + + if (flags & CMDF_ABORT) { + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); + } + + /* Wait for the DMA transfer to complete */ + do { + status = qpci_io_readb(dev, bmdma_bar, bmreg_status); + } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE); + + g_assert_cmpint(qtest_get_irq(qts, IDE_PRIMARY_IRQ), ==, + !!(status & BM_STS_INTR)); + + /* Check IDE status code */ + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRDY); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), BSY | DRQ); + + /* Reading the status register clears the IRQ */ + g_assert(!qtest_get_irq(qts, IDE_PRIMARY_IRQ)); + + /* Stop DMA transfer if still active */ + if (status & BM_STS_ACTIVE) { + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); + } + + free_pci_device(dev); + + return status; +} + +static QTestState *test_bmdma_setup(void) +{ + QTestState *qts; + + qts = ide_test_start( + "-drive file=%s,if=ide,cache=writeback,format=raw " + "-global ide-hd.serial=%s -global ide-hd.ver=%s", + tmp_path, "testdisk", "version"); + qtest_irq_intercept_in(qts, "ioapic"); + + return qts; +} + +static void test_bmdma_teardown(QTestState *qts) +{ + ide_test_quit(qts); +} + +static void test_bmdma_simple_rw(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t status; + uint8_t *buf; + uint8_t *cmpbuf; + size_t len = 512; + uintptr_t guest_buf; + PrdtEntry prdt[1]; + + qts = test_bmdma_setup(); + + guest_buf = guest_alloc(&guest_malloc, len); + prdt[0].addr = cpu_to_le32(guest_buf); + prdt[0].size = cpu_to_le32(len | PRDT_EOT); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + buf = g_malloc(len); + cmpbuf = g_malloc(len); + + /* Write 0x55 pattern to sector 0 */ + memset(buf, 0x55, len); + qtest_memwrite(qts, guest_buf, buf, len); + + status = send_dma_request(qts, CMD_WRITE_DMA, 0, 1, prdt, + ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, BM_STS_INTR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + + /* Write 0xaa pattern to sector 1 */ + memset(buf, 0xaa, len); + qtest_memwrite(qts, guest_buf, buf, len); + + status = send_dma_request(qts, CMD_WRITE_DMA, 1, 1, prdt, + ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, BM_STS_INTR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + + /* Read and verify 0x55 pattern in sector 0 */ + memset(cmpbuf, 0x55, len); + + status = send_dma_request(qts, CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), + NULL); + g_assert_cmphex(status, ==, BM_STS_INTR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + + qtest_memread(qts, guest_buf, buf, len); + g_assert(memcmp(buf, cmpbuf, len) == 0); + + /* Read and verify 0xaa pattern in sector 1 */ + memset(cmpbuf, 0xaa, len); + + status = send_dma_request(qts, CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), + NULL); + g_assert_cmphex(status, ==, BM_STS_INTR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + + qtest_memread(qts, guest_buf, buf, len); + g_assert(memcmp(buf, cmpbuf, len) == 0); + + free_pci_device(dev); + g_free(buf); + g_free(cmpbuf); + + test_bmdma_teardown(qts); +} + +static void test_bmdma_trim(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t status; + const uint64_t trim_range[] = { trim_range_le(0, 2), + trim_range_le(6, 8), + trim_range_le(10, 1), + }; + const uint64_t bad_range = trim_range_le(TEST_IMAGE_SIZE / 512 - 1, 2); + size_t len = 512; + uint8_t *buf; + uintptr_t guest_buf; + PrdtEntry prdt[1]; + + qts = test_bmdma_setup(); + + guest_buf = guest_alloc(&guest_malloc, len); + prdt[0].addr = cpu_to_le32(guest_buf), + prdt[0].size = cpu_to_le32(len | PRDT_EOT), + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + buf = g_malloc(len); + + /* Normal request */ + *((uint64_t *)buf) = trim_range[0]; + *((uint64_t *)buf + 1) = trim_range[1]; + + qtest_memwrite(qts, guest_buf, buf, 2 * sizeof(uint64_t)); + + status = send_dma_request(qts, CMD_DSM, 0, 1, prdt, + ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, BM_STS_INTR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + + /* Request contains invalid range */ + *((uint64_t *)buf) = trim_range[2]; + *((uint64_t *)buf + 1) = bad_range; + + qtest_memwrite(qts, guest_buf, buf, 2 * sizeof(uint64_t)); + + status = send_dma_request(qts, CMD_DSM, 0, 1, prdt, + ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, BM_STS_INTR); + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), ERR); + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_error), ABRT); + + free_pci_device(dev); + g_free(buf); + test_bmdma_teardown(qts); +} + +static void test_bmdma_short_prdt(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t status; + + PrdtEntry prdt[] = { + { + .addr = 0, + .size = cpu_to_le32(0x10 | PRDT_EOT), + }, + }; + + qts = test_bmdma_setup(); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* Normal request */ + status = send_dma_request(qts, CMD_READ_DMA, 0, 1, + prdt, ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, 0); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + + /* Abort the request before it completes */ + status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 1, + prdt, ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, 0); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + free_pci_device(dev); + test_bmdma_teardown(qts); +} + +static void test_bmdma_one_sector_short_prdt(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t status; + + /* Read 2 sectors but only give 1 sector in PRDT */ + PrdtEntry prdt[] = { + { + .addr = 0, + .size = cpu_to_le32(0x200 | PRDT_EOT), + }, + }; + + qts = test_bmdma_setup(); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* Normal request */ + status = send_dma_request(qts, CMD_READ_DMA, 0, 2, + prdt, ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, 0); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + + /* Abort the request before it completes */ + status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 2, + prdt, ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, 0); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + free_pci_device(dev); + test_bmdma_teardown(qts); +} + +static void test_bmdma_long_prdt(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t status; + + PrdtEntry prdt[] = { + { + .addr = 0, + .size = cpu_to_le32(0x1000 | PRDT_EOT), + }, + }; + + qts = test_bmdma_setup(); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* Normal request */ + status = send_dma_request(qts, CMD_READ_DMA, 0, 1, + prdt, ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + + /* Abort the request before it completes */ + status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 1, + prdt, ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, BM_STS_INTR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + free_pci_device(dev); + test_bmdma_teardown(qts); +} + +static void test_bmdma_no_busmaster(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t status; + + qts = test_bmdma_setup(); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* No PRDT_EOT, each entry addr 0/size 64k, and in theory qemu shouldn't be + * able to access it anyway because the Bus Master bit in the PCI command + * register isn't set. This is complete nonsense, but it used to be pretty + * good at confusing and occasionally crashing qemu. */ + PrdtEntry prdt[4096] = { }; + + status = send_dma_request(qts, CMD_READ_DMA | CMDF_NO_BM, 0, 512, + prdt, ARRAY_SIZE(prdt), NULL); + + /* Not entirely clear what the expected result is, but this is what we get + * in practice. At least we want to be aware of any changes. */ + g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + free_pci_device(dev); + test_bmdma_teardown(qts); +} + +static void string_cpu_to_be16(uint16_t *s, size_t bytes) +{ + g_assert((bytes & 1) == 0); + bytes /= 2; + + while (bytes--) { + *s = cpu_to_be16(*s); + s++; + } +} + +static void test_identify(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t data; + uint16_t buf[256]; + int i; + int ret; + + qts = ide_test_start( + "-drive file=%s,if=ide,cache=writeback,format=raw " + "-global ide-hd.serial=%s -global ide-hd.ver=%s", + tmp_path, "testdisk", "version"); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* IDENTIFY command on device 0*/ + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_IDENTIFY); + + /* Read in the IDENTIFY buffer and check registers */ + data = qpci_io_readb(dev, ide_bar, reg_device); + g_assert_cmpint(data & DEV, ==, 0); + + for (i = 0; i < 256; i++) { + data = qpci_io_readb(dev, ide_bar, reg_status); + assert_bit_set(data, DRDY | DRQ); + assert_bit_clear(data, BSY | DF | ERR); + + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); + } + + data = qpci_io_readb(dev, ide_bar, reg_status); + assert_bit_set(data, DRDY); + assert_bit_clear(data, BSY | DF | ERR | DRQ); + + /* Check serial number/version in the buffer */ + string_cpu_to_be16(&buf[10], 20); + ret = memcmp(&buf[10], "testdisk ", 20); + g_assert(ret == 0); + + string_cpu_to_be16(&buf[23], 8); + ret = memcmp(&buf[23], "version ", 8); + g_assert(ret == 0); + + /* Write cache enabled bit */ + assert_bit_set(buf[85], 0x20); + + ide_test_quit(qts); + free_pci_device(dev); +} + +/* + * Write sector 1 with random data to make IDE storage dirty + * Needed for flush tests so that flushes actually go though the block layer + */ +static void make_dirty(QTestState *qts, uint8_t device) +{ + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t status; + size_t len = 512; + uintptr_t guest_buf; + void* buf; + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + guest_buf = guest_alloc(&guest_malloc, len); + buf = g_malloc(len); + memset(buf, rand() % 255 + 1, len); + g_assert(guest_buf); + g_assert(buf); + + qtest_memwrite(qts, guest_buf, buf, len); + + PrdtEntry prdt[] = { + { + .addr = cpu_to_le32(guest_buf), + .size = cpu_to_le32(len | PRDT_EOT), + }, + }; + + status = send_dma_request(qts, CMD_WRITE_DMA, 1, 1, prdt, + ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(status, ==, BM_STS_INTR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + + g_free(buf); + free_pci_device(dev); +} + +static void test_flush(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t data; + + qts = ide_test_start( + "-drive file=blkdebug::%s,if=ide,cache=writeback,format=raw", + tmp_path); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + qtest_irq_intercept_in(qts, "ioapic"); + + /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */ + make_dirty(qts, 0); + + /* Delay the completion of the flush request until we explicitly do it */ + g_free(qtest_hmp(qts, "qemu-io ide0-hd0 \"break flush_to_os A\"")); + + /* FLUSH CACHE command on device 0*/ + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); + + /* Check status while request is in flight*/ + data = qpci_io_readb(dev, ide_bar, reg_status); + assert_bit_set(data, BSY | DRDY); + assert_bit_clear(data, DF | ERR | DRQ); + + /* Complete the command */ + g_free(qtest_hmp(qts, "qemu-io ide0-hd0 \"resume A\"")); + + /* Check registers */ + data = qpci_io_readb(dev, ide_bar, reg_device); + g_assert_cmpint(data & DEV, ==, 0); + + do { + data = qpci_io_readb(dev, ide_bar, reg_status); + } while (data & BSY); + + assert_bit_set(data, DRDY); + assert_bit_clear(data, BSY | DF | ERR | DRQ); + + ide_test_quit(qts); + free_pci_device(dev); +} + +static void test_retry_flush(const char *machine) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t data; + + prepare_blkdebug_script(debug_path, "flush_to_disk"); + + qts = ide_test_start( + "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,format=raw," + "rerror=stop,werror=stop", + debug_path, tmp_path); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + qtest_irq_intercept_in(qts, "ioapic"); + + /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */ + make_dirty(qts, 0); + + /* FLUSH CACHE command on device 0*/ + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); + + /* Check status while request is in flight*/ + data = qpci_io_readb(dev, ide_bar, reg_status); + assert_bit_set(data, BSY | DRDY); + assert_bit_clear(data, DF | ERR | DRQ); + + qtest_qmp_eventwait(qts, "STOP"); + + /* Complete the command */ + qmp_discard_response(qts, "{'execute':'cont' }"); + + /* Check registers */ + data = qpci_io_readb(dev, ide_bar, reg_device); + g_assert_cmpint(data & DEV, ==, 0); + + do { + data = qpci_io_readb(dev, ide_bar, reg_status); + } while (data & BSY); + + assert_bit_set(data, DRDY); + assert_bit_clear(data, BSY | DF | ERR | DRQ); + + ide_test_quit(qts); + free_pci_device(dev); +} + +static void test_flush_nodev(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + + qts = ide_test_start(""); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* FLUSH CACHE command on device 0*/ + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); + + /* Just testing that qemu doesn't crash... */ + + free_pci_device(dev); + ide_test_quit(qts); +} + +static void test_flush_empty_drive(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + + qts = ide_test_start("-device ide-cd,bus=ide.0"); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* FLUSH CACHE command on device 0 */ + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); + + /* Just testing that qemu doesn't crash... */ + + free_pci_device(dev); + ide_test_quit(qts); +} + +static void test_pci_retry_flush(void) +{ + test_retry_flush("pc"); +} + +static void test_isa_retry_flush(void) +{ + test_retry_flush("isapc"); +} + +typedef struct Read10CDB { + uint8_t opcode; + uint8_t flags; + uint32_t lba; + uint8_t reserved; + uint16_t nblocks; + uint8_t control; + uint16_t padding; +} __attribute__((__packed__)) Read10CDB; + +static void send_scsi_cdb_read10(QPCIDevice *dev, QPCIBar ide_bar, + uint64_t lba, int nblocks) +{ + Read10CDB pkt = { .padding = 0 }; + int i; + + g_assert_cmpint(lba, <=, UINT32_MAX); + g_assert_cmpint(nblocks, <=, UINT16_MAX); + g_assert_cmpint(nblocks, >=, 0); + + /* Construct SCSI CDB packet */ + pkt.opcode = 0x28; + pkt.lba = cpu_to_be32(lba); + pkt.nblocks = cpu_to_be16(nblocks); + + /* Send Packet */ + for (i = 0; i < sizeof(Read10CDB)/2; i++) { + qpci_io_writew(dev, ide_bar, reg_data, + le16_to_cpu(((uint16_t *)&pkt)[i])); + } +} + +static void nsleep(QTestState *qts, int64_t nsecs) +{ + const struct timespec val = { .tv_nsec = nsecs }; + nanosleep(&val, NULL); + qtest_clock_set(qts, nsecs); +} + +static uint8_t ide_wait_clear(QTestState *qts, uint8_t flag) +{ + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t data; + time_t st; + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* Wait with a 5 second timeout */ + time(&st); + while (true) { + data = qpci_io_readb(dev, ide_bar, reg_status); + if (!(data & flag)) { + free_pci_device(dev); + return data; + } + if (difftime(time(NULL), st) > 5.0) { + break; + } + nsleep(qts, 400); + } + g_assert_not_reached(); +} + +static void ide_wait_intr(QTestState *qts, int irq) +{ + time_t st; + bool intr; + + time(&st); + while (true) { + intr = qtest_get_irq(qts, irq); + if (intr) { + return; + } + if (difftime(time(NULL), st) > 5.0) { + break; + } + nsleep(qts, 400); + } + + g_assert_not_reached(); +} + +static void cdrom_pio_impl(int nblocks) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + FILE *fh; + int patt_blocks = MAX(16, nblocks); + size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks; + char *pattern = g_malloc(patt_len); + size_t rxsize = ATAPI_BLOCK_SIZE * nblocks; + uint16_t *rx = g_malloc0(rxsize); + int i, j; + uint8_t data; + uint16_t limit; + size_t ret; + + /* Prepopulate the CDROM with an interesting pattern */ + generate_pattern(pattern, patt_len, ATAPI_BLOCK_SIZE); + fh = fopen(tmp_path, "w+"); + ret = fwrite(pattern, ATAPI_BLOCK_SIZE, patt_blocks, fh); + g_assert_cmpint(ret, ==, patt_blocks); + fclose(fh); + + qts = ide_test_start( + "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " + "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + qtest_irq_intercept_in(qts, "ioapic"); + + /* PACKET command on device 0 */ + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); + qpci_io_writeb(dev, ide_bar, reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); + /* HP0: Check_Status_A State */ + nsleep(qts, 400); + data = ide_wait_clear(qts, BSY); + /* HP1: Send_Packet State */ + assert_bit_set(data, DRQ | DRDY); + assert_bit_clear(data, ERR | DF | BSY); + + /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */ + send_scsi_cdb_read10(dev, ide_bar, 0, nblocks); + + /* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. + * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. + * We allow an odd limit only when the remaining transfer size is + * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only + * request n blocks, so our request size is always even. + * For this reason, we assume there is never a hanging byte to fetch. */ + g_assert(!(rxsize & 1)); + limit = BYTE_COUNT_LIMIT & ~1; + for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) { + size_t offset = i * (limit / 2); + size_t rem = (rxsize / 2) - offset; + + /* HP3: INTRQ_Wait */ + ide_wait_intr(qts, IDE_PRIMARY_IRQ); + + /* HP2: Check_Status_B (and clear IRQ) */ + data = ide_wait_clear(qts, BSY); + assert_bit_set(data, DRQ | DRDY); + assert_bit_clear(data, ERR | DF | BSY); + + /* HP4: Transfer_Data */ + for (j = 0; j < MIN((limit / 2), rem); j++) { + rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar, + reg_data)); + } + } + + /* Check for final completion IRQ */ + ide_wait_intr(qts, IDE_PRIMARY_IRQ); + + /* Sanity check final state */ + data = ide_wait_clear(qts, DRQ); + assert_bit_set(data, DRDY); + assert_bit_clear(data, DRQ | ERR | DF | BSY); + + g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); + g_free(pattern); + g_free(rx); + test_bmdma_teardown(qts); + free_pci_device(dev); +} + +static void test_cdrom_pio(void) +{ + cdrom_pio_impl(1); +} + +static void test_cdrom_pio_large(void) +{ + /* Test a few loops of the PIO DRQ mechanism. */ + cdrom_pio_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE); +} + + +static void test_cdrom_dma(void) +{ + QTestState *qts; + static const size_t len = ATAPI_BLOCK_SIZE; + size_t ret; + char *pattern = g_malloc(ATAPI_BLOCK_SIZE * 16); + char *rx = g_malloc0(len); + uintptr_t guest_buf; + PrdtEntry prdt[1]; + FILE *fh; + + qts = ide_test_start( + "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " + "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); + qtest_irq_intercept_in(qts, "ioapic"); + + guest_buf = guest_alloc(&guest_malloc, len); + prdt[0].addr = cpu_to_le32(guest_buf); + prdt[0].size = cpu_to_le32(len | PRDT_EOT); + + generate_pattern(pattern, ATAPI_BLOCK_SIZE * 16, ATAPI_BLOCK_SIZE); + fh = fopen(tmp_path, "w+"); + ret = fwrite(pattern, ATAPI_BLOCK_SIZE, 16, fh); + g_assert_cmpint(ret, ==, 16); + fclose(fh); + + send_dma_request(qts, CMD_PACKET, 0, 1, prdt, 1, send_scsi_cdb_read10); + + /* Read back data from guest memory into local qtest memory */ + qtest_memread(qts, guest_buf, rx, len); + g_assert_cmpint(memcmp(pattern, rx, len), ==, 0); + + g_free(pattern); + g_free(rx); + test_bmdma_teardown(qts); +} + +int main(int argc, char **argv) +{ + int fd; + int ret; + + /* Create temporary blkdebug instructions */ + fd = mkstemp(debug_path); + g_assert(fd >= 0); + close(fd); + + /* Create a temporary raw image */ + fd = mkstemp(tmp_path); + g_assert(fd >= 0); + ret = ftruncate(fd, TEST_IMAGE_SIZE); + g_assert(ret == 0); + close(fd); + + /* Run the tests */ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/ide/identify", test_identify); + + qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw); + qtest_add_func("/ide/bmdma/trim", test_bmdma_trim); + qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt); + qtest_add_func("/ide/bmdma/one_sector_short_prdt", + test_bmdma_one_sector_short_prdt); + qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt); + qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster); + + qtest_add_func("/ide/flush", test_flush); + qtest_add_func("/ide/flush/nodev", test_flush_nodev); + qtest_add_func("/ide/flush/empty_drive", test_flush_empty_drive); + qtest_add_func("/ide/flush/retry_pci", test_pci_retry_flush); + qtest_add_func("/ide/flush/retry_isa", test_isa_retry_flush); + + qtest_add_func("/ide/cdrom/pio", test_cdrom_pio); + qtest_add_func("/ide/cdrom/pio_large", test_cdrom_pio_large); + qtest_add_func("/ide/cdrom/dma", test_cdrom_dma); + + ret = g_test_run(); + + /* Cleanup */ + unlink(tmp_path); + unlink(debug_path); + + return ret; +} diff --git a/tests/qtest/intel-hda-test.c b/tests/qtest/intel-hda-test.c new file mode 100644 index 0000000000..fc25ccc33c --- /dev/null +++ b/tests/qtest/intel-hda-test.c @@ -0,0 +1,39 @@ +/* + * QTest testcase for Intel HDA + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +#define HDA_ID "hda0" +#define CODEC_DEVICES " -device hda-output,bus=" HDA_ID ".0" \ + " -device hda-micro,bus=" HDA_ID ".0" \ + " -device hda-duplex,bus=" HDA_ID ".0" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void ich6_test(void) +{ + qtest_start("-device intel-hda,id=" HDA_ID CODEC_DEVICES); + qtest_end(); +} + +static void ich9_test(void) +{ + qtest_start("-machine q35 -device ich9-intel-hda,bus=pcie.0,addr=1b.0,id=" + HDA_ID CODEC_DEVICES); + qtest_end(); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/intel-hda/ich6", ich6_test); + qtest_add_func("/intel-hda/ich9", ich9_test); + + return g_test_run(); +} diff --git a/tests/qtest/ioh3420-test.c b/tests/qtest/ioh3420-test.c new file mode 100644 index 0000000000..f6ca43cca7 --- /dev/null +++ b/tests/qtest/ioh3420-test.c @@ -0,0 +1,32 @@ +/* + * QTest testcase for Intel X58 north bridge IOH + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void nop(void) +{ +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/ioh3420/nop", nop); + + qtest_start("-machine q35 -device ioh3420,bus=pcie.0,addr=1c.0,port=1," + "chassis=1,multifunction=on"); + ret = g_test_run(); + + qtest_end(); + + return ret; +} diff --git a/tests/qtest/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c new file mode 100644 index 0000000000..a42207d416 --- /dev/null +++ b/tests/qtest/ipmi-bt-test.c @@ -0,0 +1,425 @@ +/* + * IPMI BT test cases, using the external interface for checking + * + * Copyright (c) 2012 Corey Minyard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include +#include +#include +#include + + +#include "libqtest-single.h" +#include "qemu-common.h" + +#define IPMI_IRQ 5 + +#define IPMI_BT_BASE 0xe4 + +#define IPMI_BT_CTLREG_CLR_WR_PTR 0 +#define IPMI_BT_CTLREG_CLR_RD_PTR 1 +#define IPMI_BT_CTLREG_H2B_ATN 2 +#define IPMI_BT_CTLREG_B2H_ATN 3 +#define IPMI_BT_CTLREG_SMS_ATN 4 +#define IPMI_BT_CTLREG_H_BUSY 6 +#define IPMI_BT_CTLREG_B_BUSY 7 + +#define IPMI_BT_CTLREG_GET(b) ((bt_get_ctrlreg() >> (b)) & 1) +#define IPMI_BT_CTLREG_GET_H2B_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H2B_ATN) +#define IPMI_BT_CTLREG_GET_B2H_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B2H_ATN) +#define IPMI_BT_CTLREG_GET_SMS_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_SMS_ATN) +#define IPMI_BT_CTLREG_GET_H_BUSY() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H_BUSY) +#define IPMI_BT_CTLREG_GET_B_BUSY() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B_BUSY) + +#define IPMI_BT_CTLREG_SET(b) bt_write_ctrlreg(1 << (b)) +#define IPMI_BT_CTLREG_SET_CLR_WR_PTR() IPMI_BT_CTLREG_SET( \ + IPMI_BT_CTLREG_CLR_WR_PTR) +#define IPMI_BT_CTLREG_SET_CLR_RD_PTR() IPMI_BT_CTLREG_SET( \ + IPMI_BT_CTLREG_CLR_RD_PTR) +#define IPMI_BT_CTLREG_SET_H2B_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H2B_ATN) +#define IPMI_BT_CTLREG_SET_B2H_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_B2H_ATN) +#define IPMI_BT_CTLREG_SET_SMS_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_SMS_ATN) +#define IPMI_BT_CTLREG_SET_H_BUSY() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H_BUSY) + +static int bt_ints_enabled; + +static uint8_t bt_get_ctrlreg(void) +{ + return inb(IPMI_BT_BASE); +} + +static void bt_write_ctrlreg(uint8_t val) +{ + outb(IPMI_BT_BASE, val); +} + +static uint8_t bt_get_buf(void) +{ + return inb(IPMI_BT_BASE + 1); +} + +static void bt_write_buf(uint8_t val) +{ + outb(IPMI_BT_BASE + 1, val); +} + +static uint8_t bt_get_irqreg(void) +{ + return inb(IPMI_BT_BASE + 2); +} + +static void bt_write_irqreg(uint8_t val) +{ + outb(IPMI_BT_BASE + 2, val); +} + +static void bt_wait_b_busy(void) +{ + unsigned int count = 1000; + while (IPMI_BT_CTLREG_GET_B_BUSY() != 0) { + g_assert(--count != 0); + usleep(100); + } +} + +static void bt_wait_b2h_atn(void) +{ + unsigned int count = 1000; + while (IPMI_BT_CTLREG_GET_B2H_ATN() == 0) { + g_assert(--count != 0); + usleep(100); + } +} + + +static int emu_lfd; +static int emu_fd; +static in_port_t emu_port; +static uint8_t inbuf[100]; +static unsigned int inbuf_len; +static unsigned int inbuf_pos; +static int last_was_aa; + +static void read_emu_data(void) +{ + fd_set readfds; + int rv; + struct timeval tv; + + FD_ZERO(&readfds); + FD_SET(emu_fd, &readfds); + tv.tv_sec = 10; + tv.tv_usec = 0; + rv = select(emu_fd + 1, &readfds, NULL, NULL, &tv); + if (rv == -1) { + perror("select"); + } + g_assert(rv == 1); + rv = read(emu_fd, inbuf, sizeof(inbuf)); + if (rv == -1) { + perror("read"); + } + g_assert(rv > 0); + inbuf_len = rv; + inbuf_pos = 0; +} + +static void write_emu_msg(uint8_t *msg, unsigned int len) +{ + int rv; + +#ifdef DEBUG_TEST + { + unsigned int i; + printf("sending:"); + for (i = 0; i < len; i++) { + printf(" %2.2x", msg[i]); + } + printf("\n"); + } +#endif + rv = write(emu_fd, msg, len); + g_assert(rv == len); +} + +static void get_emu_msg(uint8_t *msg, unsigned int *len) +{ + unsigned int outpos = 0; + + for (;;) { + while (inbuf_pos < inbuf_len) { + uint8_t ch = inbuf[inbuf_pos++]; + + g_assert(outpos < *len); + if (last_was_aa) { + assert(ch & 0x10); + msg[outpos++] = ch & ~0x10; + last_was_aa = 0; + } else if (ch == 0xaa) { + last_was_aa = 1; + } else { + msg[outpos++] = ch; + if ((ch == 0xa0) || (ch == 0xa1)) { + /* Message complete */ + *len = outpos; + goto done; + } + } + } + read_emu_data(); + } + done: +#ifdef DEBUG_TEST + { + unsigned int i; + printf("Msg:"); + for (i = 0; i < outpos; i++) { + printf(" %2.2x", msg[i]); + } + printf("\n"); + } +#endif + return; +} + +static uint8_t +ipmb_checksum(const unsigned char *data, int size, unsigned char start) +{ + unsigned char csum = start; + + for (; size > 0; size--, data++) { + csum += *data; + } + return csum; +} + +static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 }; +static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f }; +static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 }; +static uint8_t enable_irq_cmd[] = { 0x05, 0xa1 }; + +static void emu_msg_handler(void) +{ + uint8_t msg[100]; + unsigned int msg_len = sizeof(msg); + + get_emu_msg(msg, &msg_len); + g_assert(msg_len >= 5); + g_assert(msg[msg_len - 1] == 0xa0); + msg_len--; + g_assert(ipmb_checksum(msg, msg_len, 0) == 0); + msg_len--; + if ((msg[1] == get_dev_id_cmd[0]) && (msg[2] == get_dev_id_cmd[1])) { + memcpy(msg + 1, get_dev_id_rsp, sizeof(get_dev_id_rsp)); + msg_len = sizeof(get_dev_id_rsp) + 1; + msg[msg_len] = -ipmb_checksum(msg, msg_len, 0); + msg_len++; + msg[msg_len++] = 0xa0; + write_emu_msg(msg, msg_len); + } else if ((msg[1] == set_bmc_globals_cmd[0]) && + (msg[2] == set_bmc_globals_cmd[1])) { + write_emu_msg(enable_irq_cmd, sizeof(enable_irq_cmd)); + memcpy(msg + 1, set_bmc_globals_rsp, sizeof(set_bmc_globals_rsp)); + msg_len = sizeof(set_bmc_globals_rsp) + 1; + msg[msg_len] = -ipmb_checksum(msg, msg_len, 0); + msg_len++; + msg[msg_len++] = 0xa0; + write_emu_msg(msg, msg_len); + } else { + g_assert(0); + } +} + +static void bt_cmd(uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len) +{ + unsigned int i, len, j = 0; + uint8_t seq = 5; + + /* Should be idle */ + g_assert(bt_get_ctrlreg() == 0); + + bt_wait_b_busy(); + IPMI_BT_CTLREG_SET_CLR_WR_PTR(); + bt_write_buf(cmd_len + 1); + bt_write_buf(cmd[0]); + bt_write_buf(seq); + for (i = 1; i < cmd_len; i++) { + bt_write_buf(cmd[i]); + } + IPMI_BT_CTLREG_SET_H2B_ATN(); + + emu_msg_handler(); /* We should get a message on the socket here. */ + + bt_wait_b2h_atn(); + if (bt_ints_enabled) { + g_assert((bt_get_irqreg() & 0x02) == 0x02); + g_assert(get_irq(IPMI_IRQ)); + bt_write_irqreg(0x03); + } else { + g_assert(!get_irq(IPMI_IRQ)); + } + IPMI_BT_CTLREG_SET_H_BUSY(); + IPMI_BT_CTLREG_SET_B2H_ATN(); + IPMI_BT_CTLREG_SET_CLR_RD_PTR(); + len = bt_get_buf(); + g_assert(len >= 4); + rsp[0] = bt_get_buf(); + assert(bt_get_buf() == seq); + len--; + for (j = 1; j < len; j++) { + rsp[j] = bt_get_buf(); + } + IPMI_BT_CTLREG_SET_H_BUSY(); + *rsp_len = j; +} + + +/* + * We should get a connect request and a short message with capabilities. + */ +static void test_connect(void) +{ + fd_set readfds; + int rv; + int val; + struct timeval tv; + uint8_t msg[100]; + unsigned int msglen; + static uint8_t exp1[] = { 0xff, 0x01, 0xa1 }; /* A protocol version */ + static uint8_t exp2[] = { 0x08, 0x3f, 0xa1 }; /* A capabilities cmd */ + + FD_ZERO(&readfds); + FD_SET(emu_lfd, &readfds); + tv.tv_sec = 10; + tv.tv_usec = 0; + rv = select(emu_lfd + 1, &readfds, NULL, NULL, &tv); + g_assert(rv == 1); + emu_fd = accept(emu_lfd, NULL, 0); + if (emu_fd < 0) { + perror("accept"); + } + g_assert(emu_fd >= 0); + + val = 1; + rv = setsockopt(emu_fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); + g_assert(rv != -1); + + /* Report our version */ + write_emu_msg(exp1, sizeof(exp1)); + + /* Validate that we get the info we expect. */ + msglen = sizeof(msg); + get_emu_msg(msg, &msglen); + g_assert(msglen == sizeof(exp1)); + g_assert(memcmp(msg, exp1, msglen) == 0); + msglen = sizeof(msg); + get_emu_msg(msg, &msglen); + g_assert(msglen == sizeof(exp2)); + g_assert(memcmp(msg, exp2, msglen) == 0); +} + +/* + * Send a get_device_id to do a basic test. + */ +static void test_bt_base(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + bt_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(get_dev_id_rsp)); + g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0); +} + +/* + * Enable IRQs for the interface. + */ +static void test_enable_irq(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + bt_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(set_bmc_globals_rsp)); + g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0); + bt_write_irqreg(0x01); + bt_ints_enabled = 1; +} + +/* + * Create a local TCP socket with any port, then save off the port we got. + */ +static void open_socket(void) +{ + struct sockaddr_in myaddr; + socklen_t addrlen; + + myaddr.sin_family = AF_INET; + myaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + myaddr.sin_port = 0; + emu_lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (emu_lfd == -1) { + perror("socket"); + exit(1); + } + if (bind(emu_lfd, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) { + perror("bind"); + exit(1); + } + addrlen = sizeof(myaddr); + if (getsockname(emu_lfd, (struct sockaddr *) &myaddr , &addrlen) == -1) { + perror("getsockname"); + exit(1); + } + emu_port = ntohs(myaddr.sin_port); + assert(listen(emu_lfd, 1) != -1); +} + +int main(int argc, char **argv) +{ + int ret; + + open_socket(); + + /* Run the tests */ + g_test_init(&argc, &argv, NULL); + + global_qtest = qtest_initf( + " -chardev socket,id=ipmi0,host=localhost,port=%d,reconnect=10" + " -device ipmi-bmc-extern,chardev=ipmi0,id=bmc0" + " -device isa-ipmi-bt,bmc=bmc0", emu_port); + qtest_irq_intercept_in(global_qtest, "ioapic"); + qtest_add_func("/ipmi/extern/connect", test_connect); + qtest_add_func("/ipmi/extern/bt_base", test_bt_base); + qtest_add_func("/ipmi/extern/bt_enable_irq", test_enable_irq); + qtest_add_func("/ipmi/extern/bt_base_irq", test_bt_base); + ret = g_test_run(); + qtest_quit(global_qtest); + + return ret; +} diff --git a/tests/qtest/ipmi-kcs-test.c b/tests/qtest/ipmi-kcs-test.c new file mode 100644 index 0000000000..693a6aacb5 --- /dev/null +++ b/tests/qtest/ipmi-kcs-test.c @@ -0,0 +1,285 @@ +/* + * IPMI KCS test cases, using the local interface. + * + * Copyright (c) 2012 Corey Minyard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include "libqtest-single.h" + +#define IPMI_IRQ 5 + +#define IPMI_KCS_BASE 0xca2 + +#define IPMI_KCS_STATUS_ABORT 0x60 +#define IPMI_KCS_CMD_WRITE_START 0x61 +#define IPMI_KCS_CMD_WRITE_END 0x62 +#define IPMI_KCS_CMD_READ 0x68 + +#define IPMI_KCS_ABORTED_BY_CMD 0x01 + +#define IPMI_KCS_CMDREG_GET_STATE() ((kcs_get_cmdreg() >> 6) & 3) +#define IPMI_KCS_STATE_IDLE 0 +#define IPMI_KCS_STATE_READ 1 +#define IPMI_KCS_STATE_WRITE 2 +#define IPMI_KCS_STATE_ERROR 3 +#define IPMI_KCS_CMDREG_GET_CD() ((kcs_get_cmdreg() >> 3) & 1) +#define IPMI_KCS_CMDREG_GET_ATN() ((kcs_get_cmdreg() >> 2) & 1) +#define IPMI_KCS_CMDREG_GET_IBF() ((kcs_get_cmdreg() >> 1) & 1) +#define IPMI_KCS_CMDREG_GET_OBF() ((kcs_get_cmdreg() >> 0) & 1) + +static int kcs_ints_enabled; + +static uint8_t kcs_get_cmdreg(void) +{ + return inb(IPMI_KCS_BASE + 1); +} + +static void kcs_write_cmdreg(uint8_t val) +{ + outb(IPMI_KCS_BASE + 1, val); +} + +static uint8_t kcs_get_datareg(void) +{ + return inb(IPMI_KCS_BASE); +} + +static void kcs_write_datareg(uint8_t val) +{ + outb(IPMI_KCS_BASE, val); +} + +static void kcs_wait_ibf(void) +{ + unsigned int count = 1000; + while (IPMI_KCS_CMDREG_GET_IBF() != 0) { + g_assert(--count != 0); + } +} + +static void kcs_wait_obf(void) +{ + unsigned int count = 1000; + while (IPMI_KCS_CMDREG_GET_OBF() == 0) { + g_assert(--count != 0); + } +} + +static void kcs_clear_obf(void) +{ + if (kcs_ints_enabled) { + g_assert(get_irq(IPMI_IRQ)); + } else { + g_assert(!get_irq(IPMI_IRQ)); + } + g_assert(IPMI_KCS_CMDREG_GET_OBF() == 1); + kcs_get_datareg(); + g_assert(IPMI_KCS_CMDREG_GET_OBF() == 0); + g_assert(!get_irq(IPMI_IRQ)); +} + +static void kcs_check_state(uint8_t state) +{ + g_assert(IPMI_KCS_CMDREG_GET_STATE() == state); +} + +static void kcs_cmd(uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len) +{ + unsigned int i, j = 0; + + /* Should be idle */ + g_assert(kcs_get_cmdreg() == 0); + + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + for (i = 0; i < cmd_len; i++) { + kcs_write_datareg(cmd[i]); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + } + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + kcs_write_datareg(0); + next_read_byte: + kcs_wait_ibf(); + switch (IPMI_KCS_CMDREG_GET_STATE()) { + case IPMI_KCS_STATE_READ: + kcs_wait_obf(); + g_assert(j < *rsp_len); + rsp[j++] = kcs_get_datareg(); + kcs_write_datareg(IPMI_KCS_CMD_READ); + goto next_read_byte; + break; + + case IPMI_KCS_STATE_IDLE: + kcs_wait_obf(); + kcs_get_datareg(); + break; + + default: + g_assert(0); + } + *rsp_len = j; +} + +static void kcs_abort(uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len) +{ + unsigned int i, j = 0; + unsigned int retries = 4; + + /* Should be idle */ + g_assert(kcs_get_cmdreg() == 0); + + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + for (i = 0; i < cmd_len; i++) { + kcs_write_datareg(cmd[i]); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + } + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + kcs_write_datareg(0); + kcs_wait_ibf(); + switch (IPMI_KCS_CMDREG_GET_STATE()) { + case IPMI_KCS_STATE_READ: + kcs_wait_obf(); + g_assert(j < *rsp_len); + rsp[j++] = kcs_get_datareg(); + kcs_write_datareg(IPMI_KCS_CMD_READ); + break; + + default: + g_assert(0); + } + + /* Start the abort here */ + retry_abort: + g_assert(retries > 0); + + kcs_wait_ibf(); + kcs_write_cmdreg(IPMI_KCS_STATUS_ABORT); + kcs_wait_ibf(); + kcs_clear_obf(); + kcs_write_datareg(0); + kcs_wait_ibf(); + if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_READ) { + retries--; + goto retry_abort; + } + kcs_wait_obf(); + rsp[0] = kcs_get_datareg(); + kcs_write_datareg(IPMI_KCS_CMD_READ); + kcs_wait_ibf(); + if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_IDLE) { + retries--; + goto retry_abort; + } + kcs_wait_obf(); + kcs_clear_obf(); + + *rsp_len = j; +} + + +static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 }; +static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* + * Send a get_device_id to do a basic test. + */ +static void test_kcs_base(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + kcs_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(get_dev_id_rsp)); + g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0); +} + +/* + * Abort a kcs operation while reading + */ +static void test_kcs_abort(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + kcs_abort(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); + g_assert(rsp[0] == IPMI_KCS_ABORTED_BY_CMD); +} + +static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f }; +static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 }; + +/* + * Enable interrupts + */ +static void test_enable_irq(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + kcs_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(set_bmc_globals_rsp)); + g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0); + kcs_ints_enabled = 1; +} + +int main(int argc, char **argv) +{ + char *cmdline; + int ret; + + /* Run the tests */ + g_test_init(&argc, &argv, NULL); + + cmdline = g_strdup_printf("-device ipmi-bmc-sim,id=bmc0" + " -device isa-ipmi-kcs,bmc=bmc0"); + qtest_start(cmdline); + g_free(cmdline); + qtest_irq_intercept_in(global_qtest, "ioapic"); + qtest_add_func("/ipmi/local/kcs_base", test_kcs_base); + qtest_add_func("/ipmi/local/kcs_abort", test_kcs_abort); + qtest_add_func("/ipmi/local/kcs_enable_irq", test_enable_irq); + qtest_add_func("/ipmi/local/kcs_base_irq", test_kcs_base); + qtest_add_func("/ipmi/local/kcs_abort_irq", test_kcs_abort); + ret = g_test_run(); + qtest_quit(global_qtest); + + return ret; +} diff --git a/tests/qtest/ipoctal232-test.c b/tests/qtest/ipoctal232-test.c new file mode 100644 index 0000000000..53a8c9b13c --- /dev/null +++ b/tests/qtest/ipoctal232-test.c @@ -0,0 +1,49 @@ +/* + * QTest testcase for IndustryPack Octal-RS232 + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" + +typedef struct QIpoctal232 QIpoctal232; + +struct QIpoctal232 { + QOSGraphObject obj; +}; + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void nop(void *obj, void *data, QGuestAllocator *alloc) +{ +} + +static void *ipoctal232_create(void *pci_bus, QGuestAllocator *alloc, + void *addr) +{ + QIpoctal232 *ipoctal232 = g_new0(QIpoctal232, 1); + + return &ipoctal232->obj; +} + +static void ipoctal232_register_nodes(void) +{ + qos_node_create_driver("ipoctal232", ipoctal232_create); + qos_node_consumes("ipoctal232", "ipack", &(QOSGraphEdgeOptions) { + .extra_device_opts = "bus=ipack0.0", + }); +} + +libqos_init(ipoctal232_register_nodes); + +static void register_ipoctal232_test(void) +{ + qos_add_test("nop", "ipoctal232", nop, NULL); +} + +libqos_init(register_ipoctal232_test); diff --git a/tests/qtest/ivshmem-test.c b/tests/qtest/ivshmem-test.c new file mode 100644 index 0000000000..ecda256472 --- /dev/null +++ b/tests/qtest/ivshmem-test.c @@ -0,0 +1,500 @@ +/* + * QTest testcase for ivshmem + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * Copyright (c) 2015 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include +#include "contrib/ivshmem-server/ivshmem-server.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" +#include "libqtest.h" +#include "qemu-common.h" + +#define TMPSHMSIZE (1 << 20) +static char *tmpshm; +static void *tmpshmem; +static char *tmpdir; +static char *tmpserver; + +static void save_fn(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice **pdev = (QPCIDevice **) data; + + *pdev = dev; +} + +static QPCIDevice *get_device(QPCIBus *pcibus) +{ + QPCIDevice *dev; + + dev = NULL; + qpci_device_foreach(pcibus, 0x1af4, 0x1110, save_fn, &dev); + g_assert(dev != NULL); + + return dev; +} + +typedef struct _IVState { + QOSState *qs; + QPCIBar reg_bar, mem_bar; + QPCIDevice *dev; +} IVState; + +enum Reg { + INTRMASK = 0, + INTRSTATUS = 4, + IVPOSITION = 8, + DOORBELL = 12, +}; + +static const char* reg2str(enum Reg reg) { + switch (reg) { + case INTRMASK: + return "IntrMask"; + case INTRSTATUS: + return "IntrStatus"; + case IVPOSITION: + return "IVPosition"; + case DOORBELL: + return "DoorBell"; + default: + return NULL; + } +} + +static inline unsigned in_reg(IVState *s, enum Reg reg) +{ + const char *name = reg2str(reg); + unsigned res; + + res = qpci_io_readl(s->dev, s->reg_bar, reg); + g_test_message("*%s -> %x", name, res); + + return res; +} + +static inline void out_reg(IVState *s, enum Reg reg, unsigned v) +{ + const char *name = reg2str(reg); + + g_test_message("%x -> *%s", v, name); + qpci_io_writel(s->dev, s->reg_bar, reg, v); +} + +static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len) +{ + qpci_memread(s->dev, s->mem_bar, off, buf, len); +} + +static inline void write_mem(IVState *s, uint64_t off, + const void *buf, size_t len) +{ + qpci_memwrite(s->dev, s->mem_bar, off, buf, len); +} + +static void cleanup_vm(IVState *s) +{ + g_free(s->dev); + qtest_shutdown(s->qs); +} + +static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) +{ + uint64_t barsize; + const char *arch = qtest_get_arch(); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + s->qs = qtest_pc_boot(cmd); + } else if (strcmp(arch, "ppc64") == 0) { + s->qs = qtest_spapr_boot(cmd); + } else { + g_printerr("ivshmem-test tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); + } + s->dev = get_device(s->qs->pcibus); + + s->reg_bar = qpci_iomap(s->dev, 0, &barsize); + g_assert_cmpuint(barsize, ==, 256); + + if (msix) { + qpci_msix_enable(s->dev); + } + + s->mem_bar = qpci_iomap(s->dev, 2, &barsize); + g_assert_cmpuint(barsize, ==, TMPSHMSIZE); + + qpci_device_enable(s->dev); +} + +static void setup_vm(IVState *s) +{ + char *cmd = g_strdup_printf("-object memory-backend-file" + ",id=mb1,size=1M,share,mem-path=/dev/shm%s" + " -device ivshmem-plain,memdev=mb1", tmpshm); + + setup_vm_cmd(s, cmd, false); + + g_free(cmd); +} + +static void test_ivshmem_single(void) +{ + IVState state, *s; + uint32_t data[1024]; + int i; + + setup_vm(&state); + s = &state; + + /* initial state of readable registers */ + g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0); + g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0); + g_assert_cmpuint(in_reg(s, IVPOSITION), ==, 0); + + /* trigger interrupt via registers */ + out_reg(s, INTRMASK, 0xffffffff); + g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0xffffffff); + out_reg(s, INTRSTATUS, 1); + /* check interrupt status */ + g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 1); + /* reading clears */ + g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0); + /* TODO intercept actual interrupt (needs qtest work) */ + + /* invalid register access */ + out_reg(s, IVPOSITION, 1); + in_reg(s, DOORBELL); + + /* ring the (non-functional) doorbell */ + out_reg(s, DOORBELL, 8 << 16); + + /* write shared memory */ + for (i = 0; i < G_N_ELEMENTS(data); i++) { + data[i] = i; + } + write_mem(s, 0, data, sizeof(data)); + + /* verify write */ + for (i = 0; i < G_N_ELEMENTS(data); i++) { + g_assert_cmpuint(((uint32_t *)tmpshmem)[i], ==, i); + } + + /* read it back and verify read */ + memset(data, 0, sizeof(data)); + read_mem(s, 0, data, sizeof(data)); + for (i = 0; i < G_N_ELEMENTS(data); i++) { + g_assert_cmpuint(data[i], ==, i); + } + + cleanup_vm(s); +} + +static void test_ivshmem_pair(void) +{ + IVState state1, state2, *s1, *s2; + char *data; + int i; + + setup_vm(&state1); + s1 = &state1; + setup_vm(&state2); + s2 = &state2; + + data = g_malloc0(TMPSHMSIZE); + + /* host write, guest 1 & 2 read */ + memset(tmpshmem, 0x42, TMPSHMSIZE); + read_mem(s1, 0, data, TMPSHMSIZE); + for (i = 0; i < TMPSHMSIZE; i++) { + g_assert_cmpuint(data[i], ==, 0x42); + } + read_mem(s2, 0, data, TMPSHMSIZE); + for (i = 0; i < TMPSHMSIZE; i++) { + g_assert_cmpuint(data[i], ==, 0x42); + } + + /* guest 1 write, guest 2 read */ + memset(data, 0x43, TMPSHMSIZE); + write_mem(s1, 0, data, TMPSHMSIZE); + memset(data, 0, TMPSHMSIZE); + read_mem(s2, 0, data, TMPSHMSIZE); + for (i = 0; i < TMPSHMSIZE; i++) { + g_assert_cmpuint(data[i], ==, 0x43); + } + + /* guest 2 write, guest 1 read */ + memset(data, 0x44, TMPSHMSIZE); + write_mem(s2, 0, data, TMPSHMSIZE); + memset(data, 0, TMPSHMSIZE); + read_mem(s1, 0, data, TMPSHMSIZE); + for (i = 0; i < TMPSHMSIZE; i++) { + g_assert_cmpuint(data[i], ==, 0x44); + } + + cleanup_vm(s1); + cleanup_vm(s2); + g_free(data); +} + +typedef struct ServerThread { + GThread *thread; + IvshmemServer *server; + int pipe[2]; /* to handle quit */ +} ServerThread; + +static void *server_thread(void *data) +{ + ServerThread *t = data; + IvshmemServer *server = t->server; + + while (true) { + fd_set fds; + int maxfd, ret; + + FD_ZERO(&fds); + FD_SET(t->pipe[0], &fds); + maxfd = t->pipe[0] + 1; + + ivshmem_server_get_fds(server, &fds, &maxfd); + + ret = select(maxfd, &fds, NULL, NULL, NULL); + + if (ret < 0) { + if (errno == EINTR) { + continue; + } + + g_critical("select error: %s\n", strerror(errno)); + break; + } + if (ret == 0) { + continue; + } + + if (FD_ISSET(t->pipe[0], &fds)) { + break; + } + + if (ivshmem_server_handle_fds(server, &fds, maxfd) < 0) { + g_critical("ivshmem_server_handle_fds() failed\n"); + break; + } + } + + return NULL; +} + +static void setup_vm_with_server(IVState *s, int nvectors) +{ + char *cmd; + + cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s " + "-device ivshmem-doorbell,chardev=chr0,vectors=%d", + tmpserver, nvectors); + + setup_vm_cmd(s, cmd, true); + + g_free(cmd); +} + +static void test_ivshmem_server(void) +{ + IVState state1, state2, *s1, *s2; + ServerThread thread; + IvshmemServer server; + int ret, vm1, vm2; + int nvectors = 2; + guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + + ret = ivshmem_server_init(&server, tmpserver, tmpshm, true, + TMPSHMSIZE, nvectors, + g_test_verbose()); + g_assert_cmpint(ret, ==, 0); + + ret = ivshmem_server_start(&server); + g_assert_cmpint(ret, ==, 0); + + thread.server = &server; + ret = pipe(thread.pipe); + g_assert_cmpint(ret, ==, 0); + thread.thread = g_thread_new("ivshmem-server", server_thread, &thread); + g_assert(thread.thread != NULL); + + setup_vm_with_server(&state1, nvectors); + s1 = &state1; + setup_vm_with_server(&state2, nvectors); + s2 = &state2; + + /* check got different VM ids */ + vm1 = in_reg(s1, IVPOSITION); + vm2 = in_reg(s2, IVPOSITION); + g_assert_cmpint(vm1, >=, 0); + g_assert_cmpint(vm2, >=, 0); + g_assert_cmpint(vm1, !=, vm2); + + /* check number of MSI-X vectors */ + 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 */ + ret = qpci_msix_pending(s1->dev, 0); + g_assert_cmpuint(ret, ==, 0); + out_reg(s2, DOORBELL, vm1 << 16); + do { + g_usleep(10000); + 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 */ + ret = qpci_msix_pending(s2->dev, 1); + g_assert_cmpuint(ret, ==, 0); + out_reg(s1, DOORBELL, vm2 << 16 | 1); + do { + g_usleep(10000); + ret = qpci_msix_pending(s2->dev, 1); + } while (ret == 0 && g_get_monotonic_time() < end_time); + g_assert_cmpuint(ret, !=, 0); + + cleanup_vm(s2); + cleanup_vm(s1); + + if (qemu_write_full(thread.pipe[1], "q", 1) != 1) { + g_error("qemu_write_full: %s", g_strerror(errno)); + } + + g_thread_join(thread.thread); + + ivshmem_server_close(&server); + close(thread.pipe[1]); + close(thread.pipe[0]); +} + +#define PCI_SLOT_HP 0x06 + +static void test_ivshmem_hotplug(void) +{ + QTestState *qts; + const char *arch = qtest_get_arch(); + + qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1"); + + qtest_qmp_device_add(qts, "ivshmem-plain", "iv1", + "{'addr': %s, 'memdev': 'mb1'}", + stringify(PCI_SLOT_HP)); + if (strcmp(arch, "ppc64") != 0) { + qpci_unplug_acpi_device_test(qts, "iv1", PCI_SLOT_HP); + } + + qtest_quit(qts); +} + +static void test_ivshmem_memdev(void) +{ + IVState state; + + /* just for the sake of checking memory-backend property */ + setup_vm_cmd(&state, "-object memory-backend-ram,size=1M,id=mb1" + " -device ivshmem-plain,memdev=mb1", false); + + cleanup_vm(&state); +} + +static void cleanup(void) +{ + if (tmpshmem) { + munmap(tmpshmem, TMPSHMSIZE); + tmpshmem = NULL; + } + + if (tmpshm) { + shm_unlink(tmpshm); + g_free(tmpshm); + tmpshm = NULL; + } + + if (tmpserver) { + g_unlink(tmpserver); + g_free(tmpserver); + tmpserver = NULL; + } + + if (tmpdir) { + g_rmdir(tmpdir); + tmpdir = NULL; + } +} + +static void abrt_handler(void *data) +{ + cleanup(); +} + +static gchar *mktempshm(int size, int *fd) +{ + while (true) { + gchar *name; + + name = g_strdup_printf("/qtest-%u-%u", getpid(), g_test_rand_int()); + *fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL, + S_IRWXU|S_IRWXG|S_IRWXO); + if (*fd > 0) { + g_assert(ftruncate(*fd, size) == 0); + return name; + } + + g_free(name); + + if (errno != EEXIST) { + perror("shm_open"); + return NULL; + } + } +} + +int main(int argc, char **argv) +{ + int ret, fd; + const char *arch = qtest_get_arch(); + gchar dir[] = "/tmp/ivshmem-test.XXXXXX"; + + g_test_init(&argc, &argv, NULL); + + qtest_add_abrt_handler(abrt_handler, NULL); + /* shm */ + tmpshm = mktempshm(TMPSHMSIZE, &fd); + if (!tmpshm) { + goto out; + } + tmpshmem = mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + g_assert(tmpshmem != MAP_FAILED); + /* server */ + if (mkdtemp(dir) == NULL) { + g_error("mkdtemp: %s", g_strerror(errno)); + } + tmpdir = dir; + tmpserver = g_strconcat(tmpdir, "/server", NULL); + + qtest_add_func("/ivshmem/single", test_ivshmem_single); + qtest_add_func("/ivshmem/hotplug", test_ivshmem_hotplug); + qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev); + if (g_test_slow()) { + qtest_add_func("/ivshmem/pair", test_ivshmem_pair); + if (strcmp(arch, "ppc64") != 0) { + qtest_add_func("/ivshmem/server", test_ivshmem_server); + } + } + +out: + ret = g_test_run(); + cleanup(); + return ret; +} diff --git a/tests/qtest/libqtest-single.h b/tests/qtest/libqtest-single.h new file mode 100644 index 0000000000..6f1bb1331c --- /dev/null +++ b/tests/qtest/libqtest-single.h @@ -0,0 +1,315 @@ +/* + * QTest - wrappers for test with single QEMU instances + * + * Copyright IBM, Corp. 2012 + * Copyright Red Hat, Inc. 2012 + * Copyright SUSE LINUX Products GmbH 2013 + * + * 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 LIBQTEST_SINGLE_H +#define LIBQTEST_SINGLE_H + +#include "libqtest.h" + +QTestState *global_qtest __attribute__((common, weak)); + +/** + * qtest_start: + * @args: other arguments to pass to QEMU + * + * Start QEMU and assign the resulting #QTestState to a global variable. + * The global variable is used by "shortcut" functions documented below. + * + * Returns: #QTestState instance. + */ +static inline QTestState *qtest_start(const char *args) +{ + global_qtest = qtest_init(args); + return global_qtest; +} + +/** + * qtest_end: + * + * Shut down the QEMU process started by qtest_start(). + */ +static inline void qtest_end(void) +{ + if (!global_qtest) { + return; + } + qtest_quit(global_qtest); + global_qtest = NULL; +} + +/** + * qmp: + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. + * + * Sends a QMP message to QEMU and returns the response. + */ +GCC_FMT_ATTR(1, 2) +static inline QDict *qmp(const char *fmt, ...) +{ + va_list ap; + QDict *response; + + va_start(ap, fmt); + response = qtest_vqmp(global_qtest, fmt, ap); + va_end(ap); + return response; +} + +/** + * qmp_eventwait: + * @s: #event event to wait for. + * + * Continuously polls for QMP responses until it receives the desired event. + */ +static inline void qmp_eventwait(const char *event) +{ + return qtest_qmp_eventwait(global_qtest, event); +} + +/** + * get_irq: + * @num: Interrupt to observe. + * + * Returns: The level of the @num interrupt. + */ +static inline bool get_irq(int num) +{ + return qtest_get_irq(global_qtest, num); +} + +/** + * outb: + * @addr: I/O port to write to. + * @value: Value being written. + * + * Write an 8-bit value to an I/O port. + */ +static inline void outb(uint16_t addr, uint8_t value) +{ + qtest_outb(global_qtest, addr, value); +} + +/** + * outw: + * @addr: I/O port to write to. + * @value: Value being written. + * + * Write a 16-bit value to an I/O port. + */ +static inline void outw(uint16_t addr, uint16_t value) +{ + qtest_outw(global_qtest, addr, value); +} + +/** + * outl: + * @addr: I/O port to write to. + * @value: Value being written. + * + * Write a 32-bit value to an I/O port. + */ +static inline void outl(uint16_t addr, uint32_t value) +{ + qtest_outl(global_qtest, addr, value); +} + +/** + * inb: + * @addr: I/O port to read from. + * + * Reads an 8-bit value from an I/O port. + * + * Returns: Value read. + */ +static inline uint8_t inb(uint16_t addr) +{ + return qtest_inb(global_qtest, addr); +} + +/** + * inw: + * @addr: I/O port to read from. + * + * Reads a 16-bit value from an I/O port. + * + * Returns: Value read. + */ +static inline uint16_t inw(uint16_t addr) +{ + return qtest_inw(global_qtest, addr); +} + +/** + * inl: + * @addr: I/O port to read from. + * + * Reads a 32-bit value from an I/O port. + * + * Returns: Value read. + */ +static inline uint32_t inl(uint16_t addr) +{ + return qtest_inl(global_qtest, addr); +} + +/** + * writeb: + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes an 8-bit value to guest memory. + */ +static inline void writeb(uint64_t addr, uint8_t value) +{ + qtest_writeb(global_qtest, addr, value); +} + +/** + * writew: + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 16-bit value to guest memory. + */ +static inline void writew(uint64_t addr, uint16_t value) +{ + qtest_writew(global_qtest, addr, value); +} + +/** + * writel: + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 32-bit value to guest memory. + */ +static inline void writel(uint64_t addr, uint32_t value) +{ + qtest_writel(global_qtest, addr, value); +} + +/** + * writeq: + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 64-bit value to guest memory. + */ +static inline void writeq(uint64_t addr, uint64_t value) +{ + qtest_writeq(global_qtest, addr, value); +} + +/** + * readb: + * @addr: Guest address to read from. + * + * Reads an 8-bit value from guest memory. + * + * Returns: Value read. + */ +static inline uint8_t readb(uint64_t addr) +{ + return qtest_readb(global_qtest, addr); +} + +/** + * readw: + * @addr: Guest address to read from. + * + * Reads a 16-bit value from guest memory. + * + * Returns: Value read. + */ +static inline uint16_t readw(uint64_t addr) +{ + return qtest_readw(global_qtest, addr); +} + +/** + * readl: + * @addr: Guest address to read from. + * + * Reads a 32-bit value from guest memory. + * + * Returns: Value read. + */ +static inline uint32_t readl(uint64_t addr) +{ + return qtest_readl(global_qtest, addr); +} + +/** + * readq: + * @addr: Guest address to read from. + * + * Reads a 64-bit value from guest memory. + * + * Returns: Value read. + */ +static inline uint64_t readq(uint64_t addr) +{ + return qtest_readq(global_qtest, addr); +} + +/** + * memread: + * @addr: Guest address to read from. + * @data: Pointer to where memory contents will be stored. + * @size: Number of bytes to read. + * + * Read guest memory into a buffer. + */ +static inline void memread(uint64_t addr, void *data, size_t size) +{ + qtest_memread(global_qtest, addr, data, size); +} + +/** + * memwrite: + * @addr: Guest address to write to. + * @data: Pointer to the bytes that will be written to guest memory. + * @size: Number of bytes to write. + * + * Write a buffer to guest memory. + */ +static inline void memwrite(uint64_t addr, const void *data, size_t size) +{ + qtest_memwrite(global_qtest, addr, data, size); +} + +/** + * clock_step_next: + * + * Advance the QEMU_CLOCK_VIRTUAL to the next deadline. + * + * Returns: The current value of the QEMU_CLOCK_VIRTUAL in nanoseconds. + */ +static inline int64_t clock_step_next(void) +{ + return qtest_clock_step_next(global_qtest); +} + +/** + * clock_step: + * @step: Number of nanoseconds to advance the clock by. + * + * Advance the QEMU_CLOCK_VIRTUAL by @step nanoseconds. + * + * Returns: The current value of the QEMU_CLOCK_VIRTUAL in nanoseconds. + */ +static inline int64_t clock_step(int64_t step) +{ + return qtest_clock_step(global_qtest, step); +} + +#endif diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c new file mode 100644 index 0000000000..76c9f8eade --- /dev/null +++ b/tests/qtest/libqtest.c @@ -0,0 +1,1339 @@ +/* + * QTest + * + * Copyright IBM, Corp. 2012 + * Copyright Red Hat, Inc. 2012 + * Copyright SUSE LINUX Products GmbH 2013 + * + * Authors: + * Anthony Liguori + * Paolo Bonzini + * Andreas Färber + * + * 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 +#include +#include + +#include "libqtest.h" +#include "qemu-common.h" +#include "qemu/ctype.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "qapi/qmp/json-parser.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qstring.h" + +#define MAX_IRQ 256 +#define SOCKET_TIMEOUT 50 +#define SOCKET_MAX_FDS 16 + +struct QTestState +{ + int fd; + int qmp_fd; + pid_t qemu_pid; /* our child QEMU process */ + int wstatus; + int expected_status; + bool big_endian; + bool irq_level[MAX_IRQ]; + GString *rx; +}; + +static GHookList abrt_hooks; +static struct sigaction sigact_old; + +static int qtest_query_target_endianness(QTestState *s); + +static int init_socket(const char *socket_path) +{ + struct sockaddr_un addr; + int sock; + int ret; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + g_assert_cmpint(sock, !=, -1); + + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); + qemu_set_cloexec(sock); + + do { + ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); + } while (ret == -1 && errno == EINTR); + g_assert_cmpint(ret, !=, -1); + ret = listen(sock, 1); + g_assert_cmpint(ret, !=, -1); + + return sock; +} + +static int socket_accept(int sock) +{ + struct sockaddr_un addr; + socklen_t addrlen; + int ret; + struct timeval timeout = { .tv_sec = SOCKET_TIMEOUT, + .tv_usec = 0 }; + + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, + sizeof(timeout)); + + do { + addrlen = sizeof(addr); + ret = accept(sock, (struct sockaddr *)&addr, &addrlen); + } while (ret == -1 && errno == EINTR); + if (ret == -1) { + fprintf(stderr, "%s failed: %s\n", __func__, strerror(errno)); + } + close(sock); + + return ret; +} + +bool qtest_probe_child(QTestState *s) +{ + pid_t pid = s->qemu_pid; + + if (pid != -1) { + pid = waitpid(pid, &s->wstatus, WNOHANG); + if (pid == 0) { + return true; + } + s->qemu_pid = -1; + } + return false; +} + +void qtest_set_expected_status(QTestState *s, int status) +{ + s->expected_status = status; +} + +static void kill_qemu(QTestState *s) +{ + pid_t pid = s->qemu_pid; + int wstatus; + + /* Skip wait if qtest_probe_child already reaped. */ + if (pid != -1) { + kill(pid, SIGTERM); + TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0)); + assert(pid == s->qemu_pid); + } + + /* + * Check whether qemu exited with expected exit status; anything else is + * fishy and should be logged with as much detail as possible. + */ + wstatus = s->wstatus; + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != s->expected_status) { + fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " + "process but encountered exit status %d (expected %d)\n", + __FILE__, __LINE__, WEXITSTATUS(wstatus), s->expected_status); + abort(); + } else if (WIFSIGNALED(wstatus)) { + int sig = WTERMSIG(wstatus); + const char *signame = strsignal(sig) ?: "unknown ???"; + const char *dump = WCOREDUMP(wstatus) ? " (core dumped)" : ""; + + fprintf(stderr, "%s:%d: kill_qemu() detected QEMU death " + "from signal %d (%s)%s\n", + __FILE__, __LINE__, sig, signame, dump); + abort(); + } +} + +static void kill_qemu_hook_func(void *s) +{ + kill_qemu(s); +} + +static void sigabrt_handler(int signo) +{ + g_hook_list_invoke(&abrt_hooks, FALSE); +} + +static void setup_sigabrt_handler(void) +{ + struct sigaction sigact; + + /* Catch SIGABRT to clean up on g_assert() failure */ + sigact = (struct sigaction){ + .sa_handler = sigabrt_handler, + .sa_flags = SA_RESETHAND, + }; + sigemptyset(&sigact.sa_mask); + sigaction(SIGABRT, &sigact, &sigact_old); +} + +static void cleanup_sigabrt_handler(void) +{ + sigaction(SIGABRT, &sigact_old, NULL); +} + +void qtest_add_abrt_handler(GHookFunc fn, const void *data) +{ + GHook *hook; + + /* Only install SIGABRT handler once */ + if (!abrt_hooks.is_setup) { + g_hook_list_init(&abrt_hooks, sizeof(GHook)); + } + setup_sigabrt_handler(); + + hook = g_hook_alloc(&abrt_hooks); + hook->func = fn; + hook->data = (void *)data; + + g_hook_prepend(&abrt_hooks, hook); +} + +static const char *qtest_qemu_binary(void) +{ + const char *qemu_bin; + + qemu_bin = getenv("QTEST_QEMU_BINARY"); + if (!qemu_bin) { + fprintf(stderr, "Environment variable QTEST_QEMU_BINARY required\n"); + exit(1); + } + + return qemu_bin; +} + +QTestState *qtest_init_without_qmp_handshake(const char *extra_args) +{ + QTestState *s; + int sock, qmpsock, i; + gchar *socket_path; + gchar *qmp_socket_path; + gchar *command; + const char *qemu_binary = qtest_qemu_binary(); + + s = g_new(QTestState, 1); + + socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid()); + qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid()); + + /* It's possible that if an earlier test run crashed it might + * have left a stale unix socket lying around. Delete any + * stale old socket to avoid spurious test failures with + * tests/libqtest.c:70:init_socket: assertion failed (ret != -1): (-1 != -1) + */ + unlink(socket_path); + unlink(qmp_socket_path); + + sock = init_socket(socket_path); + qmpsock = init_socket(qmp_socket_path); + + qtest_add_abrt_handler(kill_qemu_hook_func, s); + + command = g_strdup_printf("exec %s " + "-qtest unix:%s " + "-qtest-log %s " + "-chardev socket,path=%s,id=char0 " + "-mon chardev=char0,mode=control " + "-display none " + "%s" + " -accel qtest", qemu_binary, socket_path, + getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null", + qmp_socket_path, + extra_args ?: ""); + + g_test_message("starting QEMU: %s", command); + + s->wstatus = 0; + s->expected_status = 0; + s->qemu_pid = fork(); + if (s->qemu_pid == 0) { + g_setenv("QEMU_AUDIO_DRV", "none", true); + execlp("/bin/sh", "sh", "-c", command, NULL); + exit(1); + } + + g_free(command); + s->fd = socket_accept(sock); + if (s->fd >= 0) { + s->qmp_fd = socket_accept(qmpsock); + } + unlink(socket_path); + unlink(qmp_socket_path); + g_free(socket_path); + g_free(qmp_socket_path); + + g_assert(s->fd >= 0 && s->qmp_fd >= 0); + + s->rx = g_string_new(""); + for (i = 0; i < MAX_IRQ; i++) { + s->irq_level[i] = false; + } + + if (getenv("QTEST_STOP")) { + kill(s->qemu_pid, SIGSTOP); + } + + /* ask endianness of the target */ + + s->big_endian = qtest_query_target_endianness(s); + + return s; +} + +QTestState *qtest_init(const char *extra_args) +{ + QTestState *s = qtest_init_without_qmp_handshake(extra_args); + QDict *greeting; + + /* Read the QMP greeting and then do the handshake */ + greeting = qtest_qmp_receive(s); + qobject_unref(greeting); + qobject_unref(qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }")); + + return s; +} + +QTestState *qtest_vinitf(const char *fmt, va_list ap) +{ + char *args = g_strdup_vprintf(fmt, ap); + QTestState *s; + + s = qtest_init(args); + g_free(args); + return s; +} + +QTestState *qtest_initf(const char *fmt, ...) +{ + va_list ap; + QTestState *s; + + va_start(ap, fmt); + s = qtest_vinitf(fmt, ap); + va_end(ap); + return s; +} + +QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd) +{ + int sock_fd_init; + char *sock_path, sock_dir[] = "/tmp/qtest-serial-XXXXXX"; + QTestState *qts; + + g_assert_true(mkdtemp(sock_dir) != NULL); + sock_path = g_strdup_printf("%s/sock", sock_dir); + + sock_fd_init = init_socket(sock_path); + + qts = qtest_initf("-chardev socket,id=s0,path=%s -serial chardev:s0 %s", + sock_path, extra_args); + + *sock_fd = socket_accept(sock_fd_init); + + unlink(sock_path); + g_free(sock_path); + rmdir(sock_dir); + + g_assert_true(*sock_fd >= 0); + + return qts; +} + +void qtest_quit(QTestState *s) +{ + g_hook_destroy_link(&abrt_hooks, g_hook_find_data(&abrt_hooks, TRUE, s)); + + /* Uninstall SIGABRT handler on last instance */ + cleanup_sigabrt_handler(); + + kill_qemu(s); + close(s->fd); + close(s->qmp_fd); + g_string_free(s->rx, true); + g_free(s); +} + +static void socket_send(int fd, const char *buf, size_t size) +{ + size_t offset; + + offset = 0; + while (offset < size) { + ssize_t len; + + len = write(fd, buf + offset, size - offset); + if (len == -1 && errno == EINTR) { + continue; + } + + g_assert_cmpint(len, >, 0); + + offset += len; + } +} + +static void socket_sendf(int fd, const char *fmt, va_list ap) +{ + gchar *str = g_strdup_vprintf(fmt, ap); + size_t size = strlen(str); + + socket_send(fd, str, size); + g_free(str); +} + +static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + socket_sendf(s->fd, fmt, ap); + va_end(ap); +} + +/* Sends a message and file descriptors to the socket. + * It's needed for qmp-commands like getfd/add-fd */ +static void socket_send_fds(int socket_fd, int *fds, size_t fds_num, + const char *buf, size_t buf_size) +{ + ssize_t ret; + struct msghdr msg = { 0 }; + char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)] = { 0 }; + size_t fdsize = sizeof(int) * fds_num; + struct cmsghdr *cmsg; + struct iovec iov = { .iov_base = (char *)buf, .iov_len = buf_size }; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + if (fds && fds_num > 0) { + g_assert_cmpuint(fds_num, <, SOCKET_MAX_FDS); + + msg.msg_control = control; + msg.msg_controllen = CMSG_SPACE(fdsize); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(fdsize); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), fds, fdsize); + } + + do { + ret = sendmsg(socket_fd, &msg, 0); + } while (ret < 0 && errno == EINTR); + g_assert_cmpint(ret, >, 0); +} + +static GString *qtest_recv_line(QTestState *s) +{ + GString *line; + size_t offset; + char *eol; + + while ((eol = strchr(s->rx->str, '\n')) == NULL) { + ssize_t len; + char buffer[1024]; + + len = read(s->fd, buffer, sizeof(buffer)); + if (len == -1 && errno == EINTR) { + continue; + } + + if (len == -1 || len == 0) { + fprintf(stderr, "Broken pipe\n"); + abort(); + } + + g_string_append_len(s->rx, buffer, len); + } + + offset = eol - s->rx->str; + line = g_string_new_len(s->rx->str, offset); + g_string_erase(s->rx, 0, offset + 1); + + return line; +} + +static gchar **qtest_rsp(QTestState *s, int expected_args) +{ + GString *line; + gchar **words; + int i; + +redo: + line = qtest_recv_line(s); + words = g_strsplit(line->str, " ", 0); + g_string_free(line, TRUE); + + if (strcmp(words[0], "IRQ") == 0) { + long irq; + int ret; + + g_assert(words[1] != NULL); + g_assert(words[2] != NULL); + + ret = qemu_strtol(words[2], NULL, 0, &irq); + g_assert(!ret); + g_assert_cmpint(irq, >=, 0); + g_assert_cmpint(irq, <, MAX_IRQ); + + if (strcmp(words[1], "raise") == 0) { + s->irq_level[irq] = true; + } else { + s->irq_level[irq] = false; + } + + g_strfreev(words); + goto redo; + } + + g_assert(words[0] != NULL); + g_assert_cmpstr(words[0], ==, "OK"); + + if (expected_args) { + for (i = 0; i < expected_args; i++) { + g_assert(words[i] != NULL); + } + } else { + g_strfreev(words); + } + + return words; +} + +static int qtest_query_target_endianness(QTestState *s) +{ + gchar **args; + int big_endian; + + qtest_sendf(s, "endianness\n"); + args = qtest_rsp(s, 1); + g_assert(strcmp(args[1], "big") == 0 || strcmp(args[1], "little") == 0); + big_endian = strcmp(args[1], "big") == 0; + g_strfreev(args); + + return big_endian; +} + +typedef struct { + JSONMessageParser parser; + QDict *response; +} QMPResponseParser; + +static void qmp_response(void *opaque, QObject *obj, Error *err) +{ + QMPResponseParser *qmp = opaque; + + assert(!obj != !err); + + if (err) { + error_prepend(&err, "QMP JSON response parsing failed: "); + error_report_err(err); + abort(); + } + + g_assert(!qmp->response); + qmp->response = qobject_to(QDict, obj); + g_assert(qmp->response); +} + +QDict *qmp_fd_receive(int fd) +{ + QMPResponseParser qmp; + bool log = getenv("QTEST_LOG") != NULL; + + qmp.response = NULL; + json_message_parser_init(&qmp.parser, qmp_response, &qmp, NULL); + while (!qmp.response) { + ssize_t len; + char c; + + len = read(fd, &c, 1); + if (len == -1 && errno == EINTR) { + continue; + } + + if (len == -1 || len == 0) { + fprintf(stderr, "Broken pipe\n"); + abort(); + } + + if (log) { + len = write(2, &c, 1); + } + json_message_parser_feed(&qmp.parser, &c, 1); + } + json_message_parser_destroy(&qmp.parser); + + return qmp.response; +} + +QDict *qtest_qmp_receive(QTestState *s) +{ + return qmp_fd_receive(s->qmp_fd); +} + +/** + * Allow users to send a message without waiting for the reply, + * in the case that they choose to discard all replies up until + * a particular EVENT is received. + */ +void qmp_fd_vsend_fds(int fd, int *fds, size_t fds_num, + const char *fmt, va_list ap) +{ + QObject *qobj; + + /* Going through qobject ensures we escape strings properly */ + qobj = qobject_from_vjsonf_nofail(fmt, ap); + + /* No need to send anything for an empty QObject. */ + if (qobj) { + int log = getenv("QTEST_LOG") != NULL; + QString *qstr = qobject_to_json(qobj); + const char *str; + + /* + * BUG: QMP doesn't react to input until it sees a newline, an + * object, or an array. Work-around: give it a newline. + */ + qstring_append_chr(qstr, '\n'); + str = qstring_get_str(qstr); + + if (log) { + fprintf(stderr, "%s", str); + } + /* Send QMP request */ + if (fds && fds_num > 0) { + socket_send_fds(fd, fds, fds_num, str, qstring_get_length(qstr)); + } else { + socket_send(fd, str, qstring_get_length(qstr)); + } + + qobject_unref(qstr); + qobject_unref(qobj); + } +} + +void qmp_fd_vsend(int fd, const char *fmt, va_list ap) +{ + qmp_fd_vsend_fds(fd, NULL, 0, fmt, ap); +} + +void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, + const char *fmt, va_list ap) +{ + qmp_fd_vsend_fds(s->qmp_fd, fds, fds_num, fmt, ap); +} + +void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) +{ + qmp_fd_vsend_fds(s->qmp_fd, NULL, 0, fmt, ap); +} + +QDict *qmp_fdv(int fd, const char *fmt, va_list ap) +{ + qmp_fd_vsend_fds(fd, NULL, 0, fmt, ap); + + return qmp_fd_receive(fd); +} + +QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, + const char *fmt, va_list ap) +{ + qtest_qmp_vsend_fds(s, fds, fds_num, fmt, ap); + + /* Receive reply */ + return qtest_qmp_receive(s); +} + +QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) +{ + qtest_qmp_vsend(s, fmt, ap); + + /* Receive reply */ + return qtest_qmp_receive(s); +} + +QDict *qmp_fd(int fd, const char *fmt, ...) +{ + va_list ap; + QDict *response; + + va_start(ap, fmt); + response = qmp_fdv(fd, fmt, ap); + va_end(ap); + return response; +} + +void qmp_fd_send(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qmp_fd_vsend(fd, fmt, ap); + va_end(ap); +} + +QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num, + const char *fmt, ...) +{ + va_list ap; + QDict *response; + + va_start(ap, fmt); + response = qtest_vqmp_fds(s, fds, fds_num, fmt, ap); + va_end(ap); + return response; +} + +QDict *qtest_qmp(QTestState *s, const char *fmt, ...) +{ + va_list ap; + QDict *response; + + va_start(ap, fmt); + response = qtest_vqmp(s, fmt, ap); + va_end(ap); + return response; +} + +void qtest_qmp_send(QTestState *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qtest_qmp_vsend(s, fmt, ap); + va_end(ap); +} + +void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) +{ + bool log = getenv("QTEST_LOG") != NULL; + char *str = g_strdup_vprintf(fmt, ap); + + if (log) { + fprintf(stderr, "%s", str); + } + socket_send(fd, str, strlen(str)); + g_free(str); +} + +void qmp_fd_send_raw(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qmp_fd_vsend_raw(fd, fmt, ap); + va_end(ap); +} + +void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qmp_fd_vsend_raw(s->qmp_fd, fmt, ap); + va_end(ap); +} + +QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event) +{ + QDict *response; + + for (;;) { + response = qtest_qmp_receive(s); + if ((qdict_haskey(response, "event")) && + (strcmp(qdict_get_str(response, "event"), event) == 0)) { + return response; + } + qobject_unref(response); + } +} + +void qtest_qmp_eventwait(QTestState *s, const char *event) +{ + QDict *response; + + response = qtest_qmp_eventwait_ref(s, event); + qobject_unref(response); +} + +char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap) +{ + char *cmd; + QDict *resp; + char *ret; + + cmd = g_strdup_vprintf(fmt, ap); + resp = qtest_qmp(s, "{'execute': 'human-monitor-command'," + " 'arguments': {'command-line': %s}}", + cmd); + ret = g_strdup(qdict_get_try_str(resp, "return")); + while (ret == NULL && qdict_get_try_str(resp, "event")) { + /* Ignore asynchronous QMP events */ + qobject_unref(resp); + resp = qtest_qmp_receive(s); + ret = g_strdup(qdict_get_try_str(resp, "return")); + } + g_assert(ret); + qobject_unref(resp); + g_free(cmd); + return ret; +} + +char *qtest_hmp(QTestState *s, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = qtest_vhmp(s, fmt, ap); + va_end(ap); + return ret; +} + +const char *qtest_get_arch(void) +{ + const char *qemu = qtest_qemu_binary(); + const char *end = strrchr(qemu, '/'); + + return end + strlen("/qemu-system-"); +} + +bool qtest_get_irq(QTestState *s, int num) +{ + /* dummy operation in order to make sure irq is up to date */ + qtest_inb(s, 0); + + return s->irq_level[num]; +} + +void qtest_module_load(QTestState *s, const char *prefix, const char *libname) +{ + qtest_sendf(s, "module_load %s %s\n", prefix, libname); + qtest_rsp(s, 0); +} + +static int64_t qtest_clock_rsp(QTestState *s) +{ + gchar **words; + int64_t clock; + words = qtest_rsp(s, 2); + clock = g_ascii_strtoll(words[1], NULL, 0); + g_strfreev(words); + return clock; +} + +int64_t qtest_clock_step_next(QTestState *s) +{ + qtest_sendf(s, "clock_step\n"); + return qtest_clock_rsp(s); +} + +int64_t qtest_clock_step(QTestState *s, int64_t step) +{ + qtest_sendf(s, "clock_step %"PRIi64"\n", step); + return qtest_clock_rsp(s); +} + +int64_t qtest_clock_set(QTestState *s, int64_t val) +{ + qtest_sendf(s, "clock_set %"PRIi64"\n", val); + return qtest_clock_rsp(s); +} + +void qtest_irq_intercept_out(QTestState *s, const char *qom_path) +{ + qtest_sendf(s, "irq_intercept_out %s\n", qom_path); + qtest_rsp(s, 0); +} + +void qtest_irq_intercept_in(QTestState *s, const char *qom_path) +{ + qtest_sendf(s, "irq_intercept_in %s\n", qom_path); + qtest_rsp(s, 0); +} + +void qtest_set_irq_in(QTestState *s, const char *qom_path, const char *name, + int num, int level) +{ + if (!name) { + name = "unnamed-gpio-in"; + } + qtest_sendf(s, "set_irq_in %s %s %d %d\n", qom_path, name, num, level); + qtest_rsp(s, 0); +} + +static void qtest_out(QTestState *s, const char *cmd, uint16_t addr, uint32_t value) +{ + qtest_sendf(s, "%s 0x%x 0x%x\n", cmd, addr, value); + qtest_rsp(s, 0); +} + +void qtest_outb(QTestState *s, uint16_t addr, uint8_t value) +{ + qtest_out(s, "outb", addr, value); +} + +void qtest_outw(QTestState *s, uint16_t addr, uint16_t value) +{ + qtest_out(s, "outw", addr, value); +} + +void qtest_outl(QTestState *s, uint16_t addr, uint32_t value) +{ + qtest_out(s, "outl", addr, value); +} + +static uint32_t qtest_in(QTestState *s, const char *cmd, uint16_t addr) +{ + gchar **args; + int ret; + unsigned long value; + + qtest_sendf(s, "%s 0x%x\n", cmd, addr); + args = qtest_rsp(s, 2); + ret = qemu_strtoul(args[1], NULL, 0, &value); + g_assert(!ret && value <= UINT32_MAX); + g_strfreev(args); + + return value; +} + +uint8_t qtest_inb(QTestState *s, uint16_t addr) +{ + return qtest_in(s, "inb", addr); +} + +uint16_t qtest_inw(QTestState *s, uint16_t addr) +{ + return qtest_in(s, "inw", addr); +} + +uint32_t qtest_inl(QTestState *s, uint16_t addr) +{ + return qtest_in(s, "inl", addr); +} + +static void qtest_write(QTestState *s, const char *cmd, uint64_t addr, + uint64_t value) +{ + qtest_sendf(s, "%s 0x%" PRIx64 " 0x%" PRIx64 "\n", cmd, addr, value); + qtest_rsp(s, 0); +} + +void qtest_writeb(QTestState *s, uint64_t addr, uint8_t value) +{ + qtest_write(s, "writeb", addr, value); +} + +void qtest_writew(QTestState *s, uint64_t addr, uint16_t value) +{ + qtest_write(s, "writew", addr, value); +} + +void qtest_writel(QTestState *s, uint64_t addr, uint32_t value) +{ + qtest_write(s, "writel", addr, value); +} + +void qtest_writeq(QTestState *s, uint64_t addr, uint64_t value) +{ + qtest_write(s, "writeq", addr, value); +} + +static uint64_t qtest_read(QTestState *s, const char *cmd, uint64_t addr) +{ + gchar **args; + int ret; + uint64_t value; + + qtest_sendf(s, "%s 0x%" PRIx64 "\n", cmd, addr); + args = qtest_rsp(s, 2); + ret = qemu_strtou64(args[1], NULL, 0, &value); + g_assert(!ret); + g_strfreev(args); + + return value; +} + +uint8_t qtest_readb(QTestState *s, uint64_t addr) +{ + return qtest_read(s, "readb", addr); +} + +uint16_t qtest_readw(QTestState *s, uint64_t addr) +{ + return qtest_read(s, "readw", addr); +} + +uint32_t qtest_readl(QTestState *s, uint64_t addr) +{ + return qtest_read(s, "readl", addr); +} + +uint64_t qtest_readq(QTestState *s, uint64_t addr) +{ + return qtest_read(s, "readq", addr); +} + +static int hex2nib(char ch) +{ + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } else if (ch >= 'a' && ch <= 'f') { + return 10 + (ch - 'a'); + } else if (ch >= 'A' && ch <= 'F') { + return 10 + (ch - 'a'); + } else { + return -1; + } +} + +void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size) +{ + uint8_t *ptr = data; + gchar **args; + size_t i; + + if (!size) { + return; + } + + qtest_sendf(s, "read 0x%" PRIx64 " 0x%zx\n", addr, size); + args = qtest_rsp(s, 2); + + for (i = 0; i < size; i++) { + ptr[i] = hex2nib(args[1][2 + (i * 2)]) << 4; + ptr[i] |= hex2nib(args[1][2 + (i * 2) + 1]); + } + + g_strfreev(args); +} + +uint64_t qtest_rtas_call(QTestState *s, const char *name, + uint32_t nargs, uint64_t args, + uint32_t nret, uint64_t ret) +{ + qtest_sendf(s, "rtas %s %u 0x%"PRIx64" %u 0x%"PRIx64"\n", + name, nargs, args, nret, ret); + qtest_rsp(s, 0); + return 0; +} + +void qtest_add_func(const char *str, void (*fn)(void)) +{ + gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); + g_test_add_func(path, fn); + g_free(path); +} + +void qtest_add_data_func_full(const char *str, void *data, + void (*fn)(const void *), + GDestroyNotify data_free_func) +{ + gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); + g_test_add_data_func_full(path, data, fn, data_free_func); + g_free(path); +} + +void qtest_add_data_func(const char *str, const void *data, + void (*fn)(const void *)) +{ + gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); + g_test_add_data_func(path, data, fn); + g_free(path); +} + +void qtest_bufwrite(QTestState *s, uint64_t addr, const void *data, size_t size) +{ + gchar *bdata; + + bdata = g_base64_encode(data, size); + qtest_sendf(s, "b64write 0x%" PRIx64 " 0x%zx ", addr, size); + socket_send(s->fd, bdata, strlen(bdata)); + socket_send(s->fd, "\n", 1); + qtest_rsp(s, 0); + g_free(bdata); +} + +void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size) +{ + gchar **args; + size_t len; + + qtest_sendf(s, "b64read 0x%" PRIx64 " 0x%zx\n", addr, size); + args = qtest_rsp(s, 2); + + g_base64_decode_inplace(args[1], &len); + if (size != len) { + fprintf(stderr, "bufread: asked for %zu bytes but decoded %zu\n", + size, len); + len = MIN(len, size); + } + + memcpy(data, args[1], len); + g_strfreev(args); +} + +void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size) +{ + const uint8_t *ptr = data; + size_t i; + char *enc; + + if (!size) { + return; + } + + enc = g_malloc(2 * size + 1); + + for (i = 0; i < size; i++) { + sprintf(&enc[i * 2], "%02x", ptr[i]); + } + + qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x%s\n", addr, size, enc); + qtest_rsp(s, 0); + g_free(enc); +} + +void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size) +{ + qtest_sendf(s, "memset 0x%" PRIx64 " 0x%zx 0x%02x\n", addr, size, pattern); + qtest_rsp(s, 0); +} + +void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) +{ + va_list ap; + QDict *response; + + va_start(ap, fmt); + response = qtest_vqmp(qts, fmt, ap); + va_end(ap); + + g_assert(response); + if (!qdict_haskey(response, "return")) { + QString *s = qobject_to_json_pretty(QOBJECT(response)); + g_test_message("%s", qstring_get_str(s)); + qobject_unref(s); + } + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +bool qtest_big_endian(QTestState *s) +{ + return s->big_endian; +} + +static bool qtest_check_machine_version(const char *mname, const char *basename, + int major, int minor) +{ + char *newname; + bool is_equal; + + newname = g_strdup_printf("%s-%i.%i", basename, major, minor); + is_equal = g_str_equal(mname, newname); + g_free(newname); + + return is_equal; +} + +static bool qtest_is_old_versioned_machine(const char *mname) +{ + const char *dash = strrchr(mname, '-'); + const char *dot = strrchr(mname, '.'); + const char *chr; + char *bname; + const int major = QEMU_VERSION_MAJOR; + const int minor = QEMU_VERSION_MINOR; + bool res = false; + + if (dash && dot && dot > dash) { + for (chr = dash + 1; *chr; chr++) { + if (!qemu_isdigit(*chr) && *chr != '.') { + return false; + } + } + /* + * Now check if it is one of the latest versions. Check major + 1 + * and minor + 1 versions as well, since they might already exist + * in the development branch. + */ + bname = g_strdup(mname); + bname[dash - mname] = 0; + res = !qtest_check_machine_version(mname, bname, major + 1, 0) && + !qtest_check_machine_version(mname, bname, major, minor + 1) && + !qtest_check_machine_version(mname, bname, major, minor); + g_free(bname); + } + + return res; +} + +void qtest_cb_for_every_machine(void (*cb)(const char *machine), + bool skip_old_versioned) +{ + QDict *response, *minfo; + QList *list; + const QListEntry *p; + QObject *qobj; + QString *qstr; + const char *mname; + QTestState *qts; + + qts = qtest_init("-machine none"); + response = qtest_qmp(qts, "{ 'execute': 'query-machines' }"); + g_assert(response); + list = qdict_get_qlist(response, "return"); + g_assert(list); + + for (p = qlist_first(list); p; p = qlist_next(p)) { + minfo = qobject_to(QDict, qlist_entry_obj(p)); + g_assert(minfo); + qobj = qdict_get(minfo, "name"); + g_assert(qobj); + qstr = qobject_to(QString, qobj); + g_assert(qstr); + mname = qstring_get_str(qstr); + if (!skip_old_versioned || !qtest_is_old_versioned_machine(mname)) { + cb(mname); + } + } + + qtest_quit(qts); + qobject_unref(response); +} + +QDict *qtest_qmp_receive_success(QTestState *s, + void (*event_cb)(void *opaque, + const char *event, + QDict *data), + void *opaque) +{ + QDict *response, *ret, *data; + const char *event; + + for (;;) { + response = qtest_qmp_receive(s); + g_assert(!qdict_haskey(response, "error")); + ret = qdict_get_qdict(response, "return"); + if (ret) { + break; + } + event = qdict_get_str(response, "event"); + data = qdict_get_qdict(response, "data"); + if (event_cb) { + event_cb(opaque, event, data); + } + qobject_unref(response); + } + + qobject_ref(ret); + qobject_unref(response); + return ret; +} + +/* + * Generic hot-plugging test via the device_add QMP commands. + */ +void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv, + const QDict *arguments) +{ + QDict *resp; + QDict *args = arguments ? qdict_clone_shallow(arguments) : qdict_new(); + + g_assert(!qdict_haskey(args, "driver")); + qdict_put_str(args, "driver", drv); + resp = qtest_qmp(qts, "{'execute': 'device_add', 'arguments': %p}", args); + g_assert(resp); + g_assert(!qdict_haskey(resp, "event")); /* We don't expect any events */ + g_assert(!qdict_haskey(resp, "error")); + qobject_unref(resp); +} + +void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, + const char *fmt, ...) +{ + QDict *args; + va_list ap; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "id")); + qdict_put_str(args, "id", id); + + qtest_qmp_device_add_qdict(qts, driver, args); + qobject_unref(args); +} + +static void device_deleted_cb(void *opaque, const char *name, QDict *data) +{ + bool *got_event = opaque; + + g_assert_cmpstr(name, ==, "DEVICE_DELETED"); + *got_event = true; +} + +/* + * Generic hot-unplugging test via the device_del QMP command. + * Device deletion will get one response and one event. For example: + * + * {'execute': 'device_del','arguments': { 'id': 'scsi-hd'}} + * + * will get this one: + * + * {"timestamp": {"seconds": 1505289667, "microseconds": 569862}, + * "event": "DEVICE_DELETED", "data": {"device": "scsi-hd", + * "path": "/machine/peripheral/scsi-hd"}} + * + * and this one: + * + * {"return": {}} + * + * But the order of arrival may vary - so we've got to detect both. + */ +void qtest_qmp_device_del(QTestState *qts, const char *id) +{ + bool got_event = false; + QDict *rsp; + + qtest_qmp_send(qts, "{'execute': 'device_del', 'arguments': {'id': %s}}", + id); + rsp = qtest_qmp_receive_success(qts, device_deleted_cb, &got_event); + qobject_unref(rsp); + if (!got_event) { + rsp = qtest_qmp_receive(qts); + g_assert_cmpstr(qdict_get_try_str(rsp, "event"), + ==, "DEVICE_DELETED"); + qobject_unref(rsp); + } +} + +bool qmp_rsp_is_err(QDict *rsp) +{ + QDict *error = qdict_get_qdict(rsp, "error"); + qobject_unref(rsp); + return !!error; +} + +void qmp_assert_error_class(QDict *rsp, const char *class) +{ + QDict *error = qdict_get_qdict(rsp, "error"); + + g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, class); + g_assert_nonnull(qdict_get_try_str(error, "desc")); + g_assert(!qdict_haskey(rsp, "return")); + + qobject_unref(rsp); +} diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h new file mode 100644 index 0000000000..c9e21e05b3 --- /dev/null +++ b/tests/qtest/libqtest.h @@ -0,0 +1,732 @@ +/* + * QTest + * + * Copyright IBM, Corp. 2012 + * Copyright Red Hat, Inc. 2012 + * Copyright SUSE LINUX Products GmbH 2013 + * + * Authors: + * Anthony Liguori + * Paolo Bonzini + * Andreas Färber + * + * 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 LIBQTEST_H +#define LIBQTEST_H + +#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qdict.h" + +typedef struct QTestState QTestState; + +/** + * qtest_initf: + * @fmt...: Format for creating other arguments to pass to QEMU, formatted + * like sprintf(). + * + * Convenience wrapper around qtest_init(). + * + * Returns: #QTestState instance. + */ +QTestState *qtest_initf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); + +/** + * qtest_vinitf: + * @fmt: Format for creating other arguments to pass to QEMU, formatted + * like vsprintf(). + * @ap: Format arguments. + * + * Convenience wrapper around qtest_init(). + * + * Returns: #QTestState instance. + */ +QTestState *qtest_vinitf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); + +/** + * qtest_init: + * @extra_args: other arguments to pass to QEMU. CAUTION: these + * arguments are subject to word splitting and shell evaluation. + * + * Returns: #QTestState instance. + */ +QTestState *qtest_init(const char *extra_args); + +/** + * qtest_init_without_qmp_handshake: + * @extra_args: other arguments to pass to QEMU. CAUTION: these + * arguments are subject to word splitting and shell evaluation. + * + * Returns: #QTestState instance. + */ +QTestState *qtest_init_without_qmp_handshake(const char *extra_args); + +/** + * qtest_init_with_serial: + * @extra_args: other arguments to pass to QEMU. CAUTION: these + * arguments are subject to word splitting and shell evaluation. + * @sock_fd: pointer to store the socket file descriptor for + * connection with serial. + * + * Returns: #QTestState instance. + */ +QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd); + +/** + * qtest_quit: + * @s: #QTestState instance to operate on. + * + * Shut down the QEMU process associated to @s. + */ +void qtest_quit(QTestState *s); + +/** + * qtest_qmp_fds: + * @s: #QTestState instance to operate on. + * @fds: array of file descriptors + * @fds_num: number of elements in @fds + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. + * + * Sends a QMP message to QEMU with fds and returns the response. + */ +QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num, + const char *fmt, ...) + GCC_FMT_ATTR(4, 5); + +/** + * qtest_qmp: + * @s: #QTestState instance to operate on. + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. + * + * Sends a QMP message to QEMU and returns the response. + */ +QDict *qtest_qmp(QTestState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + +/** + * qtest_qmp_send: + * @s: #QTestState instance to operate on. + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. + * + * Sends a QMP message to QEMU and leaves the response in the stream. + */ +void qtest_qmp_send(QTestState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + +/** + * qtest_qmp_send_raw: + * @s: #QTestState instance to operate on. + * @fmt...: text to send, formatted like sprintf() + * + * Sends text to the QMP monitor verbatim. Need not be valid JSON; + * this is useful for negative tests. + */ +void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + +/** + * qtest_vqmp_fds: + * @s: #QTestState instance to operate on. + * @fds: array of file descriptors + * @fds_num: number of elements in @fds + * @fmt: QMP message to send to QEMU, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. + * @ap: QMP message arguments + * + * Sends a QMP message to QEMU with fds and returns the response. + */ +QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, + const char *fmt, va_list ap) + GCC_FMT_ATTR(4, 0); + +/** + * qtest_vqmp: + * @s: #QTestState instance to operate on. + * @fmt: QMP message to send to QEMU, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. + * @ap: QMP message arguments + * + * Sends a QMP message to QEMU and returns the response. + */ +QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) + GCC_FMT_ATTR(2, 0); + +/** + * qtest_qmp_vsend_fds: + * @s: #QTestState instance to operate on. + * @fds: array of file descriptors + * @fds_num: number of elements in @fds + * @fmt: QMP message to send to QEMU, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. + * @ap: QMP message arguments + * + * Sends a QMP message to QEMU and leaves the response in the stream. + */ +void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, + const char *fmt, va_list ap) + GCC_FMT_ATTR(4, 0); + +/** + * qtest_qmp_vsend: + * @s: #QTestState instance to operate on. + * @fmt: QMP message to send to QEMU, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. + * @ap: QMP message arguments + * + * Sends a QMP message to QEMU and leaves the response in the stream. + */ +void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) + GCC_FMT_ATTR(2, 0); + +/** + * qtest_receive: + * @s: #QTestState instance to operate on. + * + * Reads a QMP message from QEMU and returns the response. + */ +QDict *qtest_qmp_receive(QTestState *s); + +/** + * qtest_qmp_eventwait: + * @s: #QTestState instance to operate on. + * @s: #event event to wait for. + * + * Continuously polls for QMP responses until it receives the desired event. + */ +void qtest_qmp_eventwait(QTestState *s, const char *event); + +/** + * qtest_qmp_eventwait_ref: + * @s: #QTestState instance to operate on. + * @s: #event event to wait for. + * + * Continuously polls for QMP responses until it receives the desired event. + * Returns a copy of the event for further investigation. + */ +QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event); + +/** + * qtest_qmp_receive_success: + * @s: #QTestState instance to operate on + * @event_cb: Event callback + * @opaque: Argument for @event_cb + * + * Poll QMP messages until a command success response is received. + * If @event_cb, call it for each event received, passing @opaque, + * the event's name and data. + * Return the success response's "return" member. + */ +QDict *qtest_qmp_receive_success(QTestState *s, + void (*event_cb)(void *opaque, + const char *name, + QDict *data), + void *opaque); + +/** + * qtest_hmp: + * @s: #QTestState instance to operate on. + * @fmt...: HMP command to send to QEMU, formats arguments like sprintf(). + * + * Send HMP command to QEMU via QMP's human-monitor-command. + * QMP events are discarded. + * + * Returns: the command's output. The caller should g_free() it. + */ +char *qtest_hmp(QTestState *s, const char *fmt, ...) GCC_FMT_ATTR(2, 3); + +/** + * qtest_hmpv: + * @s: #QTestState instance to operate on. + * @fmt: HMP command to send to QEMU, formats arguments like vsprintf(). + * @ap: HMP command arguments + * + * Send HMP command to QEMU via QMP's human-monitor-command. + * QMP events are discarded. + * + * Returns: the command's output. The caller should g_free() it. + */ +char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap) + GCC_FMT_ATTR(2, 0); + +void qtest_module_load(QTestState *s, const char *prefix, const char *libname); + +/** + * qtest_get_irq: + * @s: #QTestState instance to operate on. + * @num: Interrupt to observe. + * + * Returns: The level of the @num interrupt. + */ +bool qtest_get_irq(QTestState *s, int num); + +/** + * qtest_irq_intercept_in: + * @s: #QTestState instance to operate on. + * @string: QOM path of a device. + * + * Associate qtest irqs with the GPIO-in pins of the device + * whose path is specified by @string. + */ +void qtest_irq_intercept_in(QTestState *s, const char *string); + +/** + * qtest_irq_intercept_out: + * @s: #QTestState instance to operate on. + * @string: QOM path of a device. + * + * Associate qtest irqs with the GPIO-out pins of the device + * whose path is specified by @string. + */ +void qtest_irq_intercept_out(QTestState *s, const char *string); + +/** + * qtest_set_irq_in: + * @s: QTestState instance to operate on. + * @string: QOM path of a device + * @name: IRQ name + * @irq: IRQ number + * @level: IRQ level + * + * Force given device/irq GPIO-in pin to the given level. + */ +void qtest_set_irq_in(QTestState *s, const char *string, const char *name, + int irq, int level); + +/** + * qtest_outb: + * @s: #QTestState instance to operate on. + * @addr: I/O port to write to. + * @value: Value being written. + * + * Write an 8-bit value to an I/O port. + */ +void qtest_outb(QTestState *s, uint16_t addr, uint8_t value); + +/** + * qtest_outw: + * @s: #QTestState instance to operate on. + * @addr: I/O port to write to. + * @value: Value being written. + * + * Write a 16-bit value to an I/O port. + */ +void qtest_outw(QTestState *s, uint16_t addr, uint16_t value); + +/** + * qtest_outl: + * @s: #QTestState instance to operate on. + * @addr: I/O port to write to. + * @value: Value being written. + * + * Write a 32-bit value to an I/O port. + */ +void qtest_outl(QTestState *s, uint16_t addr, uint32_t value); + +/** + * qtest_inb: + * @s: #QTestState instance to operate on. + * @addr: I/O port to read from. + * + * Returns an 8-bit value from an I/O port. + */ +uint8_t qtest_inb(QTestState *s, uint16_t addr); + +/** + * qtest_inw: + * @s: #QTestState instance to operate on. + * @addr: I/O port to read from. + * + * Returns a 16-bit value from an I/O port. + */ +uint16_t qtest_inw(QTestState *s, uint16_t addr); + +/** + * qtest_inl: + * @s: #QTestState instance to operate on. + * @addr: I/O port to read from. + * + * Returns a 32-bit value from an I/O port. + */ +uint32_t qtest_inl(QTestState *s, uint16_t addr); + +/** + * qtest_writeb: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes an 8-bit value to memory. + */ +void qtest_writeb(QTestState *s, uint64_t addr, uint8_t value); + +/** + * qtest_writew: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 16-bit value to memory. + */ +void qtest_writew(QTestState *s, uint64_t addr, uint16_t value); + +/** + * qtest_writel: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 32-bit value to memory. + */ +void qtest_writel(QTestState *s, uint64_t addr, uint32_t value); + +/** + * qtest_writeq: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 64-bit value to memory. + */ +void qtest_writeq(QTestState *s, uint64_t addr, uint64_t value); + +/** + * qtest_readb: + * @s: #QTestState instance to operate on. + * @addr: Guest address to read from. + * + * Reads an 8-bit value from memory. + * + * Returns: Value read. + */ +uint8_t qtest_readb(QTestState *s, uint64_t addr); + +/** + * qtest_readw: + * @s: #QTestState instance to operate on. + * @addr: Guest address to read from. + * + * Reads a 16-bit value from memory. + * + * Returns: Value read. + */ +uint16_t qtest_readw(QTestState *s, uint64_t addr); + +/** + * qtest_readl: + * @s: #QTestState instance to operate on. + * @addr: Guest address to read from. + * + * Reads a 32-bit value from memory. + * + * Returns: Value read. + */ +uint32_t qtest_readl(QTestState *s, uint64_t addr); + +/** + * qtest_readq: + * @s: #QTestState instance to operate on. + * @addr: Guest address to read from. + * + * Reads a 64-bit value from memory. + * + * Returns: Value read. + */ +uint64_t qtest_readq(QTestState *s, uint64_t addr); + +/** + * qtest_memread: + * @s: #QTestState instance to operate on. + * @addr: Guest address to read from. + * @data: Pointer to where memory contents will be stored. + * @size: Number of bytes to read. + * + * Read guest memory into a buffer. + */ +void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size); + +/** + * qtest_rtas_call: + * @s: #QTestState instance to operate on. + * @name: name of the command to call. + * @nargs: Number of args. + * @args: Guest address to read args from. + * @nret: Number of return value. + * @ret: Guest address to write return values to. + * + * Call an RTAS function + */ +uint64_t qtest_rtas_call(QTestState *s, const char *name, + uint32_t nargs, uint64_t args, + uint32_t nret, uint64_t ret); + +/** + * qtest_bufread: + * @s: #QTestState instance to operate on. + * @addr: Guest address to read from. + * @data: Pointer to where memory contents will be stored. + * @size: Number of bytes to read. + * + * Read guest memory into a buffer and receive using a base64 encoding. + */ +void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size); + +/** + * qtest_memwrite: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @data: Pointer to the bytes that will be written to guest memory. + * @size: Number of bytes to write. + * + * Write a buffer to guest memory. + */ +void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size); + +/** + * qtest_bufwrite: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @data: Pointer to the bytes that will be written to guest memory. + * @size: Number of bytes to write. + * + * Write a buffer to guest memory and transmit using a base64 encoding. + */ +void qtest_bufwrite(QTestState *s, uint64_t addr, + const void *data, size_t size); + +/** + * qtest_memset: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @patt: Byte pattern to fill the guest memory region with. + * @size: Number of bytes to write. + * + * Write a pattern to guest memory. + */ +void qtest_memset(QTestState *s, uint64_t addr, uint8_t patt, size_t size); + +/** + * qtest_clock_step_next: + * @s: #QTestState instance to operate on. + * + * Advance the QEMU_CLOCK_VIRTUAL to the next deadline. + * + * Returns: The current value of the QEMU_CLOCK_VIRTUAL in nanoseconds. + */ +int64_t qtest_clock_step_next(QTestState *s); + +/** + * qtest_clock_step: + * @s: QTestState instance to operate on. + * @step: Number of nanoseconds to advance the clock by. + * + * Advance the QEMU_CLOCK_VIRTUAL by @step nanoseconds. + * + * Returns: The current value of the QEMU_CLOCK_VIRTUAL in nanoseconds. + */ +int64_t qtest_clock_step(QTestState *s, int64_t step); + +/** + * qtest_clock_set: + * @s: QTestState instance to operate on. + * @val: Nanoseconds value to advance the clock to. + * + * Advance the QEMU_CLOCK_VIRTUAL to @val nanoseconds since the VM was launched. + * + * Returns: The current value of the QEMU_CLOCK_VIRTUAL in nanoseconds. + */ +int64_t qtest_clock_set(QTestState *s, int64_t val); + +/** + * qtest_big_endian: + * @s: QTestState instance to operate on. + * + * Returns: True if the architecture under test has a big endian configuration. + */ +bool qtest_big_endian(QTestState *s); + +/** + * qtest_get_arch: + * + * Returns: The architecture for the QEMU executable under test. + */ +const char *qtest_get_arch(void); + +/** + * qtest_add_func: + * @str: Test case path. + * @fn: Test case function + * + * Add a GTester testcase with the given name and function. + * The path is prefixed with the architecture under test, as + * returned by qtest_get_arch(). + */ +void qtest_add_func(const char *str, void (*fn)(void)); + +/** + * qtest_add_data_func: + * @str: Test case path. + * @data: Test case data + * @fn: Test case function + * + * Add a GTester testcase with the given name, data and function. + * The path is prefixed with the architecture under test, as + * returned by qtest_get_arch(). + */ +void qtest_add_data_func(const char *str, const void *data, + void (*fn)(const void *)); + +/** + * qtest_add_data_func_full: + * @str: Test case path. + * @data: Test case data + * @fn: Test case function + * @data_free_func: GDestroyNotify for data + * + * Add a GTester testcase with the given name, data and function. + * The path is prefixed with the architecture under test, as + * returned by qtest_get_arch(). + * + * @data is passed to @data_free_func() on test completion. + */ +void qtest_add_data_func_full(const char *str, void *data, + void (*fn)(const void *), + GDestroyNotify data_free_func); + +/** + * qtest_add: + * @testpath: Test case path + * @Fixture: Fixture type + * @tdata: Test case data + * @fsetup: Test case setup function + * @ftest: Test case function + * @fteardown: Test case teardown function + * + * Add a GTester testcase with the given name, data and functions. + * The path is prefixed with the architecture under test, as + * returned by qtest_get_arch(). + */ +#define qtest_add(testpath, Fixture, tdata, fsetup, ftest, fteardown) \ + do { \ + char *path = g_strdup_printf("/%s/%s", qtest_get_arch(), testpath); \ + g_test_add(path, Fixture, tdata, fsetup, ftest, fteardown); \ + g_free(path); \ + } while (0) + +void qtest_add_abrt_handler(GHookFunc fn, const void *data); + +/** + * qtest_qmp_assert_success: + * @qts: QTestState instance to operate on + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. + * + * Sends a QMP message to QEMU and asserts that a 'return' key is present in + * the response. + */ +void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + +QDict *qmp_fd_receive(int fd); +void qmp_fd_vsend_fds(int fd, int *fds, size_t fds_num, + const char *fmt, va_list ap) GCC_FMT_ATTR(4, 0); +void qmp_fd_vsend(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); +void qmp_fd_send(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +void qmp_fd_send_raw(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); +QDict *qmp_fdv(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); +QDict *qmp_fd(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); + +/** + * qtest_cb_for_every_machine: + * @cb: Pointer to the callback function + * @skip_old_versioned: true if versioned old machine types should be skipped + * + * Call a callback function for every name of all available machines. + */ +void qtest_cb_for_every_machine(void (*cb)(const char *machine), + bool skip_old_versioned); + +/** + * qtest_qmp_device_add_qdict: + * @qts: QTestState instance to operate on + * @drv: Name of the device that should be added + * @arguments: QDict with properties for the device to intialize + * + * Generic hot-plugging test via the device_add QMP command with properties + * supplied in form of QDict. Use NULL for empty properties list. + */ +void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv, + const QDict *arguments); + +/** + * qtest_qmp_device_add: + * @qts: QTestState instance to operate on + * @driver: Name of the device that should be added + * @id: Identification string + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. + * + * Generic hot-plugging test via the device_add QMP command. + */ +void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, + const char *fmt, ...) GCC_FMT_ATTR(4, 5); + +/** + * qtest_qmp_device_del: + * @qts: QTestState instance to operate on + * @id: Identification string + * + * Generic hot-unplugging test via the device_del QMP command. + */ +void qtest_qmp_device_del(QTestState *qts, const char *id); + +/** + * qmp_rsp_is_err: + * @rsp: QMP response to check for error + * + * Test @rsp for error and discard @rsp. + * Returns 'true' if there is error in @rsp and 'false' otherwise. + */ +bool qmp_rsp_is_err(QDict *rsp); + +/** + * qmp_assert_error_class: + * @rsp: QMP response to check for error + * @class: an error class + * + * Assert the response has the given error class and discard @rsp. + */ +void qmp_assert_error_class(QDict *rsp, const char *class); + +/** + * qtest_probe_child: + * @s: QTestState instance to operate on. + * + * Returns: true if the child is still alive. + */ +bool qtest_probe_child(QTestState *s); + +/** + * qtest_set_expected_status: + * @s: QTestState instance to operate on. + * @status: an expected exit status. + * + * Set expected exit status of the child. + */ +void qtest_set_expected_status(QTestState *s, int status); + +#endif diff --git a/tests/qtest/m25p80-test.c b/tests/qtest/m25p80-test.c new file mode 100644 index 0000000000..50c6b79fb3 --- /dev/null +++ b/tests/qtest/m25p80-test.c @@ -0,0 +1,382 @@ +/* + * QTest testcase for the M25P80 Flash (Using the Aspeed SPI + * Controller) + * + * Copyright (C) 2016 IBM Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "libqtest-single.h" + +/* + * ASPEED SPI Controller registers + */ +#define R_CONF 0x00 +#define CONF_ENABLE_W0 (1 << 16) +#define R_CE_CTRL 0x04 +#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ +#define R_CTRL0 0x10 +#define CTRL_CE_STOP_ACTIVE (1 << 2) +#define CTRL_READMODE 0x0 +#define CTRL_FREADMODE 0x1 +#define CTRL_WRITEMODE 0x2 +#define CTRL_USERMODE 0x3 + +#define ASPEED_FMC_BASE 0x1E620000 +#define ASPEED_FLASH_BASE 0x20000000 + +/* + * Flash commands + */ +enum { + JEDEC_READ = 0x9f, + BULK_ERASE = 0xc7, + READ = 0x03, + PP = 0x02, + WREN = 0x6, + RESET_ENABLE = 0x66, + RESET_MEMORY = 0x99, + EN_4BYTE_ADDR = 0xB7, + ERASE_SECTOR = 0xd8, +}; + +#define FLASH_JEDEC 0x20ba19 /* n25q256a */ +#define FLASH_SIZE (32 * 1024 * 1024) + +#define PAGE_SIZE 256 + +/* + * Use an explicit bswap for the values read/wrote to the flash region + * as they are BE and the Aspeed CPU is LE. + */ +static inline uint32_t make_be32(uint32_t data) +{ + return bswap32(data); +} + +static void spi_conf(uint32_t value) +{ + uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); + + conf |= value; + writel(ASPEED_FMC_BASE + R_CONF, conf); +} + +static void spi_conf_remove(uint32_t value) +{ + uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); + + conf &= ~value; + writel(ASPEED_FMC_BASE + R_CONF, conf); +} + +static void spi_ce_ctrl(uint32_t value) +{ + uint32_t conf = readl(ASPEED_FMC_BASE + R_CE_CTRL); + + conf |= value; + writel(ASPEED_FMC_BASE + R_CE_CTRL, conf); +} + +static void spi_ctrl_setmode(uint8_t mode, uint8_t cmd) +{ + uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); + ctrl &= ~(CTRL_USERMODE | 0xff << 16); + ctrl |= mode | (cmd << 16); + writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); +} + +static void spi_ctrl_start_user(void) +{ + uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); + + ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; + writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); + + ctrl &= ~CTRL_CE_STOP_ACTIVE; + writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); +} + +static void spi_ctrl_stop_user(void) +{ + uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); + + ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; + writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); +} + +static void flash_reset(void) +{ + spi_conf(CONF_ENABLE_W0); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, RESET_ENABLE); + writeb(ASPEED_FLASH_BASE, RESET_MEMORY); + spi_ctrl_stop_user(); + + spi_conf_remove(CONF_ENABLE_W0); +} + +static void test_read_jedec(void) +{ + uint32_t jedec = 0x0; + + spi_conf(CONF_ENABLE_W0); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, JEDEC_READ); + jedec |= readb(ASPEED_FLASH_BASE) << 16; + jedec |= readb(ASPEED_FLASH_BASE) << 8; + jedec |= readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + + flash_reset(); + + g_assert_cmphex(jedec, ==, FLASH_JEDEC); +} + +static void read_page(uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_start_user(); + + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, READ); + writel(ASPEED_FLASH_BASE, make_be32(addr)); + + /* Continuous read are supported */ + for (i = 0; i < PAGE_SIZE / 4; i++) { + page[i] = make_be32(readl(ASPEED_FLASH_BASE)); + } + spi_ctrl_stop_user(); +} + +static void read_page_mem(uint32_t addr, uint32_t *page) +{ + int i; + + /* move out USER mode to use direct reads from the AHB bus */ + spi_ctrl_setmode(CTRL_READMODE, READ); + + for (i = 0; i < PAGE_SIZE / 4; i++) { + page[i] = make_be32(readl(ASPEED_FLASH_BASE + addr + i * 4)); + } +} + +static void test_erase_sector(void) +{ + uint32_t some_page_addr = 0x600 * PAGE_SIZE; + uint32_t page[PAGE_SIZE / 4]; + int i; + + spi_conf(CONF_ENABLE_W0); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, ERASE_SECTOR); + writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); + spi_ctrl_stop_user(); + + /* Previous page should be full of zeroes as backend is not + * initialized */ + read_page(some_page_addr - PAGE_SIZE, page); + for (i = 0; i < PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0x0); + } + + /* But this one was erased */ + read_page(some_page_addr, page); + for (i = 0; i < PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(); +} + +static void test_erase_all(void) +{ + uint32_t some_page_addr = 0x15000 * PAGE_SIZE; + uint32_t page[PAGE_SIZE / 4]; + int i; + + spi_conf(CONF_ENABLE_W0); + + /* Check some random page. Should be full of zeroes as backend is + * not initialized */ + read_page(some_page_addr, page); + for (i = 0; i < PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0x0); + } + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, BULK_ERASE); + spi_ctrl_stop_user(); + + /* Recheck that some random page */ + read_page(some_page_addr, page); + for (i = 0; i < PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(); +} + +static void test_write_page(void) +{ + uint32_t my_page_addr = 0x14000 * PAGE_SIZE; /* beyond 16MB */ + uint32_t some_page_addr = 0x15000 * PAGE_SIZE; + uint32_t page[PAGE_SIZE / 4]; + int i; + + spi_conf(CONF_ENABLE_W0); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, PP); + writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < PAGE_SIZE / 4; i++) { + writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(); + + /* Check what was written */ + read_page(my_page_addr, page); + for (i = 0; i < PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page(some_page_addr, page); + for (i = 0; i < PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(); +} + +static void test_read_page_mem(void) +{ + uint32_t my_page_addr = 0x14000 * PAGE_SIZE; /* beyond 16MB */ + uint32_t some_page_addr = 0x15000 * PAGE_SIZE; + uint32_t page[PAGE_SIZE / 4]; + int i; + + /* Enable 4BYTE mode for controller. This is should be strapped by + * HW for CE0 anyhow. + */ + spi_ce_ctrl(1 << CRTL_EXTENDED0); + + /* Enable 4BYTE mode for flash. */ + spi_conf(CONF_ENABLE_W0); + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + spi_ctrl_stop_user(); + spi_conf_remove(CONF_ENABLE_W0); + + /* Check what was written */ + read_page_mem(my_page_addr, page); + for (i = 0; i < PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page_mem(some_page_addr, page); + for (i = 0; i < PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(); +} + +static void test_write_page_mem(void) +{ + uint32_t my_page_addr = 0x15000 * PAGE_SIZE; + uint32_t page[PAGE_SIZE / 4]; + int i; + + /* Enable 4BYTE mode for controller. This is should be strapped by + * HW for CE0 anyhow. + */ + spi_ce_ctrl(1 << CRTL_EXTENDED0); + + /* Enable 4BYTE mode for flash. */ + spi_conf(CONF_ENABLE_W0); + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + spi_ctrl_stop_user(); + + /* move out USER mode to use direct writes to the AHB bus */ + spi_ctrl_setmode(CTRL_WRITEMODE, PP); + + for (i = 0; i < PAGE_SIZE / 4; i++) { + writel(ASPEED_FLASH_BASE + my_page_addr + i * 4, + make_be32(my_page_addr + i * 4)); + } + + /* Check what was written */ + read_page_mem(my_page_addr, page); + for (i = 0; i < PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + flash_reset(); +} + +static char tmp_path[] = "/tmp/qtest.m25p80.XXXXXX"; + +int main(int argc, char **argv) +{ + int ret; + int fd; + + g_test_init(&argc, &argv, NULL); + + fd = mkstemp(tmp_path); + g_assert(fd >= 0); + ret = ftruncate(fd, FLASH_SIZE); + g_assert(ret == 0); + close(fd); + + global_qtest = qtest_initf("-m 256 -machine palmetto-bmc " + "-drive file=%s,format=raw,if=mtd", + tmp_path); + + qtest_add_func("/m25p80/read_jedec", test_read_jedec); + qtest_add_func("/m25p80/erase_sector", test_erase_sector); + qtest_add_func("/m25p80/erase_all", test_erase_all); + qtest_add_func("/m25p80/write_page", test_write_page); + qtest_add_func("/m25p80/read_page_mem", test_read_page_mem); + qtest_add_func("/m25p80/write_page_mem", test_write_page_mem); + + ret = g_test_run(); + + qtest_quit(global_qtest); + unlink(tmp_path); + return ret; +} diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c new file mode 100644 index 0000000000..b94a1230f7 --- /dev/null +++ b/tests/qtest/m48t59-test.c @@ -0,0 +1,269 @@ +/* + * QTest testcase for the M48T59 and M48T08 real-time clocks + * + * Based on MC146818 RTC test: + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "libqtest.h" + +#define RTC_SECONDS 0x9 +#define RTC_MINUTES 0xa +#define RTC_HOURS 0xb + +#define RTC_DAY_OF_WEEK 0xc +#define RTC_DAY_OF_MONTH 0xd +#define RTC_MONTH 0xe +#define RTC_YEAR 0xf + +static uint32_t base; +static uint16_t reg_base = 0x1ff0; /* 0x7f0 for m48t02 */ +static int base_year; +static const char *base_machine; +static bool use_mmio; + +static uint8_t cmos_read_mmio(QTestState *s, uint8_t reg) +{ + return qtest_readb(s, base + (uint32_t)reg_base + (uint32_t)reg); +} + +static void cmos_write_mmio(QTestState *s, uint8_t reg, uint8_t val) +{ + uint8_t data = val; + + qtest_writeb(s, base + (uint32_t)reg_base + (uint32_t)reg, data); +} + +static uint8_t cmos_read_ioio(QTestState *s, uint8_t reg) +{ + qtest_outw(s, base + 0, reg_base + (uint16_t)reg); + return qtest_inb(s, base + 3); +} + +static void cmos_write_ioio(QTestState *s, uint8_t reg, uint8_t val) +{ + qtest_outw(s, base + 0, reg_base + (uint16_t)reg); + qtest_outb(s, base + 3, val); +} + +static uint8_t cmos_read(QTestState *s, uint8_t reg) +{ + if (use_mmio) { + return cmos_read_mmio(s, reg); + } else { + return cmos_read_ioio(s, reg); + } +} + +static void cmos_write(QTestState *s, uint8_t reg, uint8_t val) +{ + if (use_mmio) { + cmos_write_mmio(s, reg, val); + } else { + cmos_write_ioio(s, reg, val); + } +} + +static int bcd2dec(int value) +{ + return (((value >> 4) & 0x0F) * 10) + (value & 0x0F); +} + +static int tm_cmp(struct tm *lhs, struct tm *rhs) +{ + time_t a, b; + struct tm d1, d2; + + memcpy(&d1, lhs, sizeof(d1)); + memcpy(&d2, rhs, sizeof(d2)); + + a = mktime(&d1); + b = mktime(&d2); + + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } + + return 0; +} + +#if 0 +static void print_tm(struct tm *tm) +{ + printf("%04d-%02d-%02d %02d:%02d:%02d %+02ld\n", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff); +} +#endif + +static void cmos_get_date_time(QTestState *s, struct tm *date) +{ + int sec, min, hour, mday, mon, year; + time_t ts; + struct tm dummy; + + sec = cmos_read(s, RTC_SECONDS); + min = cmos_read(s, RTC_MINUTES); + hour = cmos_read(s, RTC_HOURS); + mday = cmos_read(s, RTC_DAY_OF_MONTH); + mon = cmos_read(s, RTC_MONTH); + year = cmos_read(s, RTC_YEAR); + + sec = bcd2dec(sec); + min = bcd2dec(min); + hour = bcd2dec(hour); + mday = bcd2dec(mday); + mon = bcd2dec(mon); + year = bcd2dec(year); + + ts = time(NULL); + localtime_r(&ts, &dummy); + + date->tm_isdst = dummy.tm_isdst; + date->tm_sec = sec; + date->tm_min = min; + date->tm_hour = hour; + date->tm_mday = mday; + date->tm_mon = mon - 1; + date->tm_year = base_year + year - 1900; +#ifndef __sun__ + date->tm_gmtoff = 0; +#endif + + ts = mktime(date); +} + +static QTestState *m48t59_qtest_start(void) +{ + return qtest_initf("-M %s -rtc clock=vm", base_machine); +} + +static void bcd_check_time(void) +{ + struct tm start, date[4], end; + struct tm *datep; + time_t ts; + const int wiggle = 2; + QTestState *s = m48t59_qtest_start(); + + /* + * This check assumes a few things. First, we cannot guarantee that we get + * a consistent reading from the wall clock because we may hit an edge of + * the clock while reading. To work around this, we read four clock readings + * such that at least two of them should match. We need to assume that one + * reading is corrupt so we need four readings to ensure that we have at + * least two consecutive identical readings + * + * It's also possible that we'll cross an edge reading the host clock so + * simply check to make sure that the clock reading is within the period of + * when we expect it to be. + */ + + ts = time(NULL); + gmtime_r(&ts, &start); + + cmos_get_date_time(s, &date[0]); + cmos_get_date_time(s, &date[1]); + cmos_get_date_time(s, &date[2]); + cmos_get_date_time(s, &date[3]); + + ts = time(NULL); + gmtime_r(&ts, &end); + + if (tm_cmp(&date[0], &date[1]) == 0) { + datep = &date[0]; + } else if (tm_cmp(&date[1], &date[2]) == 0) { + datep = &date[1]; + } else if (tm_cmp(&date[2], &date[3]) == 0) { + datep = &date[2]; + } else { + g_assert_not_reached(); + } + + if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) { + long t, s; + + start.tm_isdst = datep->tm_isdst; + + t = (long)mktime(datep); + s = (long)mktime(&start); + if (t < s) { + g_test_message("RTC is %ld second(s) behind wall-clock", (s - t)); + } else { + g_test_message("RTC is %ld second(s) ahead of wall-clock", (t - s)); + } + + g_assert_cmpint(ABS(t - s), <=, wiggle); + } + + qtest_quit(s); +} + +/* success if no crash or abort */ +static void fuzz_registers(void) +{ + unsigned int i; + QTestState *s = m48t59_qtest_start(); + + for (i = 0; i < 1000; i++) { + uint8_t reg, val; + + reg = (uint8_t)g_test_rand_int_range(0, 16); + val = (uint8_t)g_test_rand_int_range(0, 256); + + if (reg == 7) { + /* watchdog setup register, may trigger system reset, skip */ + continue; + } + + cmos_write(s, reg, val); + cmos_read(s, reg); + } + + qtest_quit(s); +} + +static void base_setup(void) +{ + const char *arch = qtest_get_arch(); + + if (g_str_equal(arch, "sparc")) { + /* Note: For sparc64, we'd need to map-in the PCI bridge memory first */ + base = 0x71200000; + base_year = 1968; + base_machine = "SS-5"; + use_mmio = true; + } else if (g_str_equal(arch, "ppc") || g_str_equal(arch, "ppc64")) { + base = 0xF0000000; + base_year = 1968; + base_machine = "ref405ep"; + use_mmio = true; + } else { + g_assert_not_reached(); + } +} + +int main(int argc, char **argv) +{ + base_setup(); + + g_test_init(&argc, &argv, NULL); + + if (g_test_slow()) { + /* Do not run this in timing-sensitive environments */ + qtest_add_func("/rtc/bcd-check-time", bcd_check_time); + } + qtest_add_func("/rtc/fuzz-registers", fuzz_registers); + return g_test_run(); +} diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c new file mode 100644 index 0000000000..5953d31755 --- /dev/null +++ b/tests/qtest/machine-none-test.c @@ -0,0 +1,103 @@ +/* + * Machine 'none' tests. + * + * Copyright (c) 2018 Red Hat Inc. + * + * Authors: + * Igor Mammedov , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qemu/cutils.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" + + +struct arch2cpu { + const char *arch; + const char *cpu_model; +}; + +static struct arch2cpu cpus_map[] = { + /* tested targets list */ + { "arm", "cortex-a15" }, + { "aarch64", "cortex-a57" }, + { "x86_64", "qemu64,apic-id=0" }, + { "i386", "qemu32,apic-id=0" }, + { "alpha", "ev67" }, + { "cris", "crisv32" }, + { "lm32", "lm32-full" }, + { "m68k", "m5206" }, + /* FIXME: { "microblaze", "any" }, doesn't work with -M none -cpu any */ + /* FIXME: { "microblazeel", "any" }, doesn't work with -M none -cpu any */ + { "mips", "4Kc" }, + { "mipsel", "I7200" }, + { "mips64", "20Kc" }, + { "mips64el", "I6500" }, + { "moxie", "MoxieLite" }, + { "nios2", "FIXME" }, + { "or1k", "or1200" }, + { "ppc", "604" }, + { "ppc64", "power8e_v2.1" }, + { "s390x", "qemu" }, + { "sh4", "sh7750r" }, + { "sh4eb", "sh7751r" }, + { "sparc", "LEON2" }, + { "sparc64", "Fujitsu Sparc64" }, + { "tricore", "tc1796" }, + { "unicore32", "UniCore-II" }, + { "xtensa", "dc233c" }, + { "xtensaeb", "fsf" }, + { "hppa", "hppa" }, + { "riscv64", "rv64gcsu-v1.10.0" }, + { "riscv32", "rv32gcsu-v1.9.1" }, +}; + +static const char *get_cpu_model_by_arch(const char *arch) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cpus_map); i++) { + if (!strcmp(arch, cpus_map[i].arch)) { + return cpus_map[i].cpu_model; + } + } + return NULL; +} + +static void test_machine_cpu_cli(void) +{ + QDict *response; + const char *arch = qtest_get_arch(); + const char *cpu_model = get_cpu_model_by_arch(arch); + QTestState *qts; + + if (!cpu_model) { + if (!(!strcmp(arch, "microblaze") || !strcmp(arch, "microblazeel"))) { + fprintf(stderr, "WARNING: cpu name for target '%s' isn't defined," + " add it to cpus_map\n", arch); + } + return; /* TODO: die here to force all targets have a test */ + } + qts = qtest_initf("-machine none -cpu '%s'", cpu_model); + + response = qtest_qmp(qts, "{ 'execute': 'quit' }"); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("machine/none/cpu_option", test_machine_cpu_cli); + + return g_test_run(); +} diff --git a/tests/qtest/megasas-test.c b/tests/qtest/megasas-test.c new file mode 100644 index 0000000000..d6796b9bd7 --- /dev/null +++ b/tests/qtest/megasas-test.c @@ -0,0 +1,91 @@ +/* + * QTest testcase for LSI MegaRAID + * + * Copyright (c) 2017 Red Hat Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bswap.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QMegasas QMegasas; + +struct QMegasas { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *megasas_get_driver(void *obj, const char *interface) +{ + QMegasas *megasas = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &megasas->dev; + } + + fprintf(stderr, "%s not present in megasas\n", interface); + g_assert_not_reached(); +} + +static void *megasas_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QMegasas *megasas = g_new0(QMegasas, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&megasas->dev, bus, addr); + megasas->obj.get_driver = megasas_get_driver; + + return &megasas->obj; +} + +/* This used to cause a NULL pointer dereference. */ +static void megasas_pd_get_info_fuzz(void *obj, void *data, QGuestAllocator *alloc) +{ + QMegasas *megasas = obj; + QPCIDevice *dev = &megasas->dev; + QPCIBar bar; + uint32_t context[256]; + uint64_t context_pa; + int i; + + qpci_device_enable(dev); + bar = qpci_iomap(dev, 0, NULL); + + memset(context, 0, sizeof(context)); + context[0] = cpu_to_le32(0x05050505); + context[1] = cpu_to_le32(0x01010101); + for (i = 2; i < ARRAY_SIZE(context); i++) { + context[i] = cpu_to_le32(0x41414141); + } + context[6] = cpu_to_le32(0x02020000); + context[7] = cpu_to_le32(0); + + context_pa = guest_alloc(alloc, sizeof(context)); + qtest_memwrite(dev->bus->qts, context_pa, context, sizeof(context)); + qpci_io_writel(dev, bar, 0x40, context_pa); +} + +static void megasas_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,id=scsi0", + .before_cmd_line = "-drive id=drv0,if=none,file=null-co://," + "file.read-zeroes=on,format=raw", + .after_cmd_line = "-device scsi-hd,bus=scsi0.0,drive=drv0", + }; + + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("megasas", megasas_create); + qos_node_consumes("megasas", "pci-bus", &opts); + qos_node_produces("megasas", "pci-device"); + + qos_add_test("dcmd/pd-get-info/fuzz", "megasas", megasas_pd_get_info_fuzz, NULL); +} +libqos_init(megasas_register_nodes); diff --git a/tests/qtest/microbit-test.c b/tests/qtest/microbit-test.c new file mode 100644 index 0000000000..04e199ec33 --- /dev/null +++ b/tests/qtest/microbit-test.c @@ -0,0 +1,507 @@ +/* + * QTest testcase for Microbit board using the Nordic Semiconductor nRF51 SoC. + * + * nRF51: + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf + * + * Microbit Board: http://microbit.org/ + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + + +#include "qemu/osdep.h" +#include "exec/hwaddr.h" +#include "libqtest.h" + +#include "hw/arm/nrf51.h" +#include "hw/char/nrf51_uart.h" +#include "hw/gpio/nrf51_gpio.h" +#include "hw/nvram/nrf51_nvm.h" +#include "hw/timer/nrf51_timer.h" +#include "hw/i2c/microbit_i2c.h" + +static bool uart_wait_for_event(QTestState *qts, uint32_t event_addr) +{ + time_t now, start = time(NULL); + + while (true) { + if (qtest_readl(qts, event_addr) == 1) { + qtest_writel(qts, event_addr, 0x00); + return true; + } + + /* Wait at most 10 minutes */ + now = time(NULL); + if (now - start > 600) { + break; + } + g_usleep(10000); + } + + return false; +} + +static void uart_rw_to_rxd(QTestState *qts, int sock_fd, const char *in, + char *out) +{ + int i, in_len = strlen(in); + + g_assert_true(write(sock_fd, in, in_len) == in_len); + for (i = 0; i < in_len; i++) { + g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + + A_UART_RXDRDY)); + out[i] = qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD); + } + out[i] = '\0'; +} + +static void uart_w_to_txd(QTestState *qts, const char *in) +{ + int i, in_len = strlen(in); + + for (i = 0; i < in_len; i++) { + qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, in[i]); + g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + + A_UART_TXDRDY)); + } +} + +static void test_nrf51_uart(void) +{ + int sock_fd; + char s[10]; + QTestState *qts = qtest_init_with_serial("-M microbit", &sock_fd); + + g_assert_true(write(sock_fd, "c", 1) == 1); + g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 0x00); + + qtest_writel(qts, NRF51_UART_BASE + A_UART_ENABLE, 0x04); + qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTRX, 0x01); + + g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + A_UART_RXDRDY)); + qtest_writel(qts, NRF51_UART_BASE + A_UART_RXDRDY, 0x00); + g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 'c'); + + qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENSET, 0x04); + g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x04); + qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENCLR, 0x04); + g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x00); + + uart_rw_to_rxd(qts, sock_fd, "hello", s); + g_assert_true(memcmp(s, "hello", 5) == 0); + + qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01); + uart_w_to_txd(qts, "d"); + g_assert_true(read(sock_fd, s, 10) == 1); + g_assert_cmphex(s[0], ==, 'd'); + + qtest_writel(qts, NRF51_UART_BASE + A_UART_SUSPEND, 0x01); + qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, 'h'); + qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01); + uart_w_to_txd(qts, "world"); + g_assert_true(read(sock_fd, s, 10) == 5); + g_assert_true(memcmp(s, "world", 5) == 0); + + close(sock_fd); + + qtest_quit(qts); +} + +/* Read a byte from I2C device at @addr from register @reg */ +static uint32_t i2c_read_byte(QTestState *qts, uint32_t addr, uint32_t reg) +{ + uint32_t val; + + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ADDRESS, addr); + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTTX, 1); + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_TXD, reg); + val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_TXDSENT); + g_assert_cmpuint(val, ==, 1); + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1); + + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTRX, 1); + val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_RXDREADY); + g_assert_cmpuint(val, ==, 1); + val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_REG_RXD); + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1); + + return val; +} + +static void test_microbit_i2c(void) +{ + uint32_t val; + QTestState *qts = qtest_init("-M microbit"); + + /* We don't program pins/irqs but at least enable the device */ + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 5); + + /* MMA8653 magnetometer detection */ + val = i2c_read_byte(qts, 0x3A, 0x0D); + g_assert_cmpuint(val, ==, 0x5A); + + val = i2c_read_byte(qts, 0x3A, 0x0D); + g_assert_cmpuint(val, ==, 0x5A); + + /* LSM303 accelerometer detection */ + val = i2c_read_byte(qts, 0x3C, 0x4F); + g_assert_cmpuint(val, ==, 0x40); + + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 0); + + qtest_quit(qts); +} + +#define FLASH_SIZE (256 * NRF51_PAGE_SIZE) + +static void fill_and_erase(QTestState *qts, hwaddr base, hwaddr size, + uint32_t address_reg) +{ + hwaddr i; + + /* Erase Page */ + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); + qtest_writel(qts, NRF51_NVMC_BASE + address_reg, base); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + /* Check memory */ + for (i = 0; i < size / 4; i++) { + g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, 0xFFFFFFFF); + } + + /* Fill memory */ + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); + for (i = 0; i < size / 4; i++) { + qtest_writel(qts, base + i * 4, i); + g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, i); + } + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); +} + +static void test_nrf51_nvmc(void) +{ + uint32_t value; + hwaddr i; + QTestState *qts = qtest_init("-M microbit"); + + /* Test always ready */ + value = qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_READY); + g_assert_cmpuint(value & 0x01, ==, 0x01); + + /* Test write-read config register */ + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x03); + g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG), + ==, 0x03); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG), + ==, 0x00); + + /* Test PCR0 */ + fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE, + NRF51_NVMC_ERASEPCR0); + fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE, + NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR0); + + /* Test PCR1 */ + fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE, + NRF51_NVMC_ERASEPCR1); + fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE, + NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR1); + + /* Erase all */ + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); + for (i = 0; i < FLASH_SIZE / 4; i++) { + qtest_writel(qts, NRF51_FLASH_BASE + i * 4, i); + g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4), ==, i); + } + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + for (i = 0; i < FLASH_SIZE / 4; i++) { + g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4), + ==, 0xFFFFFFFF); + } + + /* Erase UICR */ + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { + g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), + ==, 0xFFFFFFFF); + } + + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); + for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { + qtest_writel(qts, NRF51_UICR_BASE + i * 4, i); + g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), ==, i); + } + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { + g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), + ==, 0xFFFFFFFF); + } + + qtest_quit(qts); +} + +static void test_nrf51_gpio(void) +{ + size_t i; + uint32_t actual, expected; + + struct { + hwaddr addr; + uint32_t expected; + } const reset_state[] = { + {NRF51_GPIO_REG_OUT, 0x00000000}, {NRF51_GPIO_REG_OUTSET, 0x00000000}, + {NRF51_GPIO_REG_OUTCLR, 0x00000000}, {NRF51_GPIO_REG_IN, 0x00000000}, + {NRF51_GPIO_REG_DIR, 0x00000000}, {NRF51_GPIO_REG_DIRSET, 0x00000000}, + {NRF51_GPIO_REG_DIRCLR, 0x00000000} + }; + + QTestState *qts = qtest_init("-M microbit"); + + /* Check reset state */ + for (i = 0; i < ARRAY_SIZE(reset_state); i++) { + expected = reset_state[i].expected; + actual = qtest_readl(qts, NRF51_GPIO_BASE + reset_state[i].addr); + g_assert_cmpuint(actual, ==, expected); + } + + for (i = 0; i < NRF51_GPIO_PINS; i++) { + expected = 0x00000002; + actual = qtest_readl(qts, NRF51_GPIO_BASE + + NRF51_GPIO_REG_CNF_START + i * 4); + g_assert_cmpuint(actual, ==, expected); + } + + /* Check dir bit consistency between dir and cnf */ + /* Check set via DIRSET */ + expected = 0x80000001; + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRSET, expected); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); + g_assert_cmpuint(actual, ==, expected); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) + & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + + /* Check clear via DIRCLR */ + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); + g_assert_cmpuint(actual, ==, 0x00000000); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) + & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + + /* Check set via DIR */ + expected = 0x80000001; + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, expected); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); + g_assert_cmpuint(actual, ==, expected); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) + & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + + /* Reset DIR */ + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000); + + /* Check Input propagates */ + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); + + /* Check pull-up working */ + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); + + /* Check pull-down working */ + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); + + /* Check Output propagates */ + qtest_irq_intercept_out(qts, "/machine/nrf51"); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0011); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); + g_assert_true(qtest_get_irq(qts, 0)); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); + g_assert_false(qtest_get_irq(qts, 0)); + + /* Check self-stimulation */ + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + + /* + * Check short-circuit - generates an guest_error which must be checked + * manually as long as qtest can not scan qemu_log messages + */ + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); + + qtest_quit(qts); +} + +static void timer_task(QTestState *qts, hwaddr task) +{ + qtest_writel(qts, NRF51_TIMER_BASE + task, NRF51_TRIGGER_TASK); +} + +static void timer_clear_event(QTestState *qts, hwaddr event) +{ + qtest_writel(qts, NRF51_TIMER_BASE + event, NRF51_EVENT_CLEAR); +} + +static void timer_set_bitmode(QTestState *qts, uint8_t mode) +{ + qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_BITMODE, mode); +} + +static void timer_set_prescaler(QTestState *qts, uint8_t prescaler) +{ + qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_PRESCALER, prescaler); +} + +static void timer_set_cc(QTestState *qts, size_t idx, uint32_t value) +{ + qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_CC0 + idx * 4, value); +} + +static void timer_assert_events(QTestState *qts, uint32_t ev0, uint32_t ev1, + uint32_t ev2, uint32_t ev3) +{ + g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_0) + == ev0); + g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_1) + == ev1); + g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_2) + == ev2); + g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_3) + == ev3); +} + +static void test_nrf51_timer(void) +{ + uint32_t steps_to_overflow = 408; + QTestState *qts = qtest_init("-M microbit"); + + /* Compare Match */ + timer_task(qts, NRF51_TIMER_TASK_STOP); + timer_task(qts, NRF51_TIMER_TASK_CLEAR); + + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3); + + timer_set_bitmode(qts, NRF51_TIMER_WIDTH_16); /* 16 MHz Timer */ + timer_set_prescaler(qts, 0); + /* Swept over in first step */ + timer_set_cc(qts, 0, 2); + /* Barely miss on first step */ + timer_set_cc(qts, 1, 162); + /* Spot on on third step */ + timer_set_cc(qts, 2, 480); + + timer_assert_events(qts, 0, 0, 0, 0); + + timer_task(qts, NRF51_TIMER_TASK_START); + qtest_clock_step(qts, 10000); + timer_assert_events(qts, 1, 0, 0, 0); + + /* Swept over on first overflow */ + timer_set_cc(qts, 3, 114); + + qtest_clock_step(qts, 10000); + timer_assert_events(qts, 1, 1, 0, 0); + + qtest_clock_step(qts, 10000); + timer_assert_events(qts, 1, 1, 1, 0); + + /* Wrap time until internal counter overflows */ + while (steps_to_overflow--) { + timer_assert_events(qts, 1, 1, 1, 0); + qtest_clock_step(qts, 10000); + } + + timer_assert_events(qts, 1, 1, 1, 1); + + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3); + timer_assert_events(qts, 0, 0, 0, 0); + + timer_task(qts, NRF51_TIMER_TASK_STOP); + + /* Test Proposal: Stop/Shutdown */ + /* Test Proposal: Shortcut Compare -> Clear */ + /* Test Proposal: Shortcut Compare -> Stop */ + /* Test Proposal: Counter Mode */ + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/microbit/nrf51/uart", test_nrf51_uart); + qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio); + qtest_add_func("/microbit/nrf51/nvmc", test_nrf51_nvmc); + qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer); + qtest_add_func("/microbit/microbit/i2c", test_microbit_i2c); + + return g_test_run(); +} diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c new file mode 100644 index 0000000000..516093b39a --- /dev/null +++ b/tests/qtest/migration-helpers.c @@ -0,0 +1,167 @@ +/* + * QTest migration helpers + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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/qmp/qjson.h" + +#include "migration-helpers.h" + +bool got_stop; + +static void stop_cb(void *opaque, const char *name, QDict *data) +{ + if (!strcmp(name, "STOP")) { + got_stop = true; + } +} + +/* + * Events can get in the way of responses we are actually waiting for. + */ +QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) +{ + va_list ap; + + va_start(ap, command); + qtest_qmp_vsend_fds(who, &fd, 1, command, ap); + va_end(ap); + + return qtest_qmp_receive_success(who, stop_cb, NULL); +} + +/* + * Events can get in the way of responses we are actually waiting for. + */ +QDict *wait_command(QTestState *who, const char *command, ...) +{ + va_list ap; + + va_start(ap, command); + qtest_qmp_vsend(who, command, ap); + va_end(ap); + + return qtest_qmp_receive_success(who, stop_cb, NULL); +} + +/* + * Send QMP command "migrate". + * Arguments are built from @fmt... (formatted like + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. + */ +void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) +{ + va_list ap; + QDict *args, *rsp; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + qdict_put_str(args, "uri", uri); + + rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); + + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +/* + * Note: caller is responsible to free the returned object via + * qobject_unref() after use + */ +QDict *migrate_query(QTestState *who) +{ + return wait_command(who, "{ 'execute': 'query-migrate' }"); +} + +/* + * Note: caller is responsible to free the returned object via + * g_free() after use + */ +static gchar *migrate_query_status(QTestState *who) +{ + QDict *rsp_return = migrate_query(who); + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); + + g_assert(status); + qobject_unref(rsp_return); + + return status; +} + +static bool check_migration_status(QTestState *who, const char *goal, + const char **ungoals) +{ + bool ready; + char *current_status; + const char **ungoal; + + current_status = migrate_query_status(who); + ready = strcmp(current_status, goal) == 0; + if (!ungoals) { + g_assert_cmpstr(current_status, !=, "failed"); + /* + * If looking for a state other than completed, + * completion of migration would cause the test to + * hang. + */ + if (strcmp(goal, "completed") != 0) { + g_assert_cmpstr(current_status, !=, "completed"); + } + } else { + for (ungoal = ungoals; *ungoal; ungoal++) { + g_assert_cmpstr(current_status, !=, *ungoal); + } + } + g_free(current_status); + return ready; +} + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals) +{ + while (!check_migration_status(who, goal, ungoals)) { + usleep(1000); + } +} + +void wait_for_migration_complete(QTestState *who) +{ + wait_for_migration_status(who, "completed", NULL); +} + +void wait_for_migration_fail(QTestState *from, bool allow_active) +{ + QDict *rsp_return; + char *status; + bool failed; + + do { + status = migrate_query_status(from); + bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || + (allow_active && !strcmp(status, "active")); + if (!result) { + fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", + __func__, status, allow_active); + } + g_assert(result); + failed = !strcmp(status, "failed"); + g_free(status); + } while (!failed); + + /* Is the machine currently running? */ + rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); + g_assert(qdict_haskey(rsp_return, "running")); + g_assert(qdict_get_bool(rsp_return, "running")); + qobject_unref(rsp_return); +} diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h new file mode 100644 index 0000000000..a11808b3b7 --- /dev/null +++ b/tests/qtest/migration-helpers.h @@ -0,0 +1,37 @@ +/* + * QTest migration helpers + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * 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 MIGRATION_HELPERS_H_ +#define MIGRATION_HELPERS_H_ + +#include "libqtest.h" + +extern bool got_stop; + +GCC_FMT_ATTR(3, 4) +QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...); + +GCC_FMT_ATTR(2, 3) +QDict *wait_command(QTestState *who, const char *command, ...); + +GCC_FMT_ATTR(3, 4) +void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); + +QDict *migrate_query(QTestState *who); + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals); + +void wait_for_migration_complete(QTestState *who); + +void wait_for_migration_fail(QTestState *from, bool allow_active); + +#endif /* MIGRATION_HELPERS_H_ */ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c new file mode 100644 index 0000000000..53afec4395 --- /dev/null +++ b/tests/qtest/migration-test.c @@ -0,0 +1,1281 @@ +/* + * QTest testcase for migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" +#include "chardev/char.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" + +#include "migration-helpers.h" +#include "migration/migration-test.h" + +/* TODO actually test the results and get rid of this */ +#define qtest_qmp_discard_response(...) qobject_unref(qtest_qmp(__VA_ARGS__)) + +unsigned start_address; +unsigned end_address; +static bool uffd_feature_thread_id; + +#if defined(__linux__) +#include +#include +#endif + +#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) +#include +#include +#include + +static bool ufd_version_check(void) +{ + struct uffdio_api api_struct; + uint64_t ioctl_mask; + + int ufd = syscall(__NR_userfaultfd, O_CLOEXEC); + + if (ufd == -1) { + g_test_message("Skipping test: userfaultfd not available"); + return false; + } + + api_struct.api = UFFD_API; + api_struct.features = 0; + if (ioctl(ufd, UFFDIO_API, &api_struct)) { + g_test_message("Skipping test: UFFDIO_API failed"); + return false; + } + uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; + + ioctl_mask = (__u64)1 << _UFFDIO_REGISTER | + (__u64)1 << _UFFDIO_UNREGISTER; + if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { + g_test_message("Skipping test: Missing userfault feature"); + return false; + } + + return true; +} + +#else +static bool ufd_version_check(void) +{ + g_test_message("Skipping test: Userfault not available (builtdtime)"); + return false; +} + +#endif + +static const char *tmpfs; + +/* The boot file modifies memory area in [start_address, end_address) + * repeatedly. It outputs a 'B' at a fixed rate while it's still running. + */ +#include "tests/migration/i386/a-b-bootblock.h" +#include "tests/migration/aarch64/a-b-kernel.h" +#include "tests/migration/s390x/a-b-bios.h" + +static void init_bootfile(const char *bootpath, void *content, size_t len) +{ + FILE *bootfile = fopen(bootpath, "wb"); + + g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); + fclose(bootfile); +} + +/* + * Wait for some output in the serial output file, + * we get an 'A' followed by an endless string of 'B's + * but on the destination we won't have the A. + */ +static void wait_for_serial(const char *side) +{ + char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); + FILE *serialfile = fopen(serialpath, "r"); + const char *arch = qtest_get_arch(); + int started = (strcmp(side, "src_serial") == 0 && + strcmp(arch, "ppc64") == 0) ? 0 : 1; + + g_free(serialpath); + do { + int readvalue = fgetc(serialfile); + + if (!started) { + /* SLOF prints its banner before starting test, + * to ignore it, mark the start of the test with '_', + * ignore all characters until this marker + */ + switch (readvalue) { + case '_': + started = 1; + break; + case EOF: + fseek(serialfile, 0, SEEK_SET); + usleep(1000); + break; + } + continue; + } + switch (readvalue) { + case 'A': + /* Fine */ + break; + + case 'B': + /* It's alive! */ + fclose(serialfile); + return; + + case EOF: + started = (strcmp(side, "src_serial") == 0 && + strcmp(arch, "ppc64") == 0) ? 0 : 1; + fseek(serialfile, 0, SEEK_SET); + usleep(1000); + break; + + default: + fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); + g_assert_not_reached(); + } + } while (true); +} + +/* + * It's tricky to use qemu's migration event capability with qtest, + * events suddenly appearing confuse the qmp()/hmp() responses. + */ + +static int64_t read_ram_property_int(QTestState *who, const char *property) +{ + QDict *rsp_return, *rsp_ram; + int64_t result; + + rsp_return = migrate_query(who); + if (!qdict_haskey(rsp_return, "ram")) { + /* Still in setup */ + result = 0; + } else { + rsp_ram = qdict_get_qdict(rsp_return, "ram"); + result = qdict_get_try_int(rsp_ram, property, 0); + } + qobject_unref(rsp_return); + return result; +} + +static int64_t read_migrate_property_int(QTestState *who, const char *property) +{ + QDict *rsp_return; + int64_t result; + + rsp_return = migrate_query(who); + result = qdict_get_try_int(rsp_return, property, 0); + qobject_unref(rsp_return); + return result; +} + +static uint64_t get_migration_pass(QTestState *who) +{ + return read_ram_property_int(who, "dirty-sync-count"); +} + +static void read_blocktime(QTestState *who) +{ + QDict *rsp_return; + + rsp_return = migrate_query(who); + g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); + qobject_unref(rsp_return); +} + +static void wait_for_migration_pass(QTestState *who) +{ + uint64_t initial_pass = get_migration_pass(who); + uint64_t pass; + + /* Wait for the 1st sync */ + while (!got_stop && !initial_pass) { + usleep(1000); + initial_pass = get_migration_pass(who); + } + + do { + usleep(1000); + pass = get_migration_pass(who); + } while (pass == initial_pass && !got_stop); +} + +static void check_guests_ram(QTestState *who) +{ + /* Our ASM test will have been incrementing one byte from each page from + * start_address to < end_address in order. This gives us a constraint + * that any page's byte should be equal or less than the previous pages + * byte (mod 256); and they should all be equal except for one transition + * at the point where we meet the incrementer. (We're running this with + * the guest stopped). + */ + unsigned address; + uint8_t first_byte; + uint8_t last_byte; + bool hit_edge = false; + int bad = 0; + + qtest_memread(who, start_address, &first_byte, 1); + last_byte = first_byte; + + for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; + address += TEST_MEM_PAGE_SIZE) + { + uint8_t b; + qtest_memread(who, address, &b, 1); + if (b != last_byte) { + if (((b + 1) % 256) == last_byte && !hit_edge) { + /* This is OK, the guest stopped at the point of + * incrementing the previous page but didn't get + * to us yet. + */ + hit_edge = true; + last_byte = b; + } else { + bad++; + if (bad <= 10) { + fprintf(stderr, "Memory content inconsistency at %x" + " first_byte = %x last_byte = %x current = %x" + " hit_edge = %x\n", + address, first_byte, last_byte, b, hit_edge); + } + } + } + } + if (bad >= 10) { + fprintf(stderr, "and in another %d pages", bad - 10); + } + g_assert(bad == 0); +} + +static void cleanup(const char *filename) +{ + char *path = g_strdup_printf("%s/%s", tmpfs, filename); + + unlink(path); + g_free(path); +} + +static char *SocketAddress_to_str(SocketAddress *addr) +{ + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("tcp:%s:%s", + addr->u.inet.host, + addr->u.inet.port); + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("unix:%s", + addr->u.q_unix.path); + case SOCKET_ADDRESS_TYPE_FD: + return g_strdup_printf("fd:%s", addr->u.fd.str); + case SOCKET_ADDRESS_TYPE_VSOCK: + return g_strdup_printf("tcp:%s:%s", + addr->u.vsock.cid, + addr->u.vsock.port); + default: + return g_strdup("unknown address type"); + } +} + +static char *migrate_get_socket_address(QTestState *who, const char *parameter) +{ + QDict *rsp; + char *result; + Error *local_err = NULL; + SocketAddressList *addrs; + Visitor *iv = NULL; + QObject *object; + + rsp = migrate_query(who); + object = qdict_get(rsp, parameter); + + iv = qobject_input_visitor_new(object); + visit_type_SocketAddressList(iv, NULL, &addrs, &local_err); + visit_free(iv); + + /* we are only using a single address */ + result = SocketAddress_to_str(addrs->value); + + qapi_free_SocketAddressList(addrs); + qobject_unref(rsp); + return result; +} + +static long long migrate_get_parameter_int(QTestState *who, + const char *parameter) +{ + QDict *rsp; + long long result; + + rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }"); + result = qdict_get_int(rsp, parameter); + qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter_int(QTestState *who, const char *parameter, + long long value) +{ + long long result; + + result = migrate_get_parameter_int(who, parameter); + g_assert_cmpint(result, ==, value); +} + +static void migrate_set_parameter_int(QTestState *who, const char *parameter, + long long value) +{ + QDict *rsp; + + rsp = qtest_qmp(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %lld } }", + parameter, value); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + migrate_check_parameter_int(who, parameter, value); +} + +static void migrate_pause(QTestState *who) +{ + QDict *rsp; + + rsp = wait_command(who, "{ 'execute': 'migrate-pause' }"); + qobject_unref(rsp); +} + +static void migrate_continue(QTestState *who, const char *state) +{ + QDict *rsp; + + rsp = wait_command(who, + "{ 'execute': 'migrate-continue'," + " 'arguments': { 'state': %s } }", + state); + qobject_unref(rsp); +} + +static void migrate_recover(QTestState *who, const char *uri) +{ + QDict *rsp; + + rsp = wait_command(who, + "{ 'execute': 'migrate-recover', " + " 'id': 'recover-cmd', " + " 'arguments': { 'uri': %s } }", + uri); + qobject_unref(rsp); +} + +static void migrate_set_capability(QTestState *who, const char *capability, + bool value) +{ + QDict *rsp; + + rsp = qtest_qmp(who, + "{ 'execute': 'migrate-set-capabilities'," + "'arguments': { " + "'capabilities': [ { " + "'capability': %s, 'state': %i } ] } }", + capability, value); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +static void migrate_postcopy_start(QTestState *from, QTestState *to) +{ + QDict *rsp; + + rsp = wait_command(from, "{ 'execute': 'migrate-start-postcopy' }"); + qobject_unref(rsp); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + + qtest_qmp_eventwait(to, "RESUME"); +} + +typedef struct { + bool hide_stderr; + bool use_shmem; + char *opts_source; + char *opts_target; +} MigrateStart; + +static MigrateStart *migrate_start_new(void) +{ + MigrateStart *args = g_new0(MigrateStart, 1); + + args->opts_source = g_strdup(""); + args->opts_target = g_strdup(""); + return args; +} + +static void migrate_start_destroy(MigrateStart *args) +{ + g_free(args->opts_source); + g_free(args->opts_target); + g_free(args); +} + +static int test_migrate_start(QTestState **from, QTestState **to, + const char *uri, MigrateStart *args) +{ + gchar *arch_source, *arch_target; + gchar *cmd_source, *cmd_target; + const gchar *ignore_stderr; + char *bootpath = NULL; + char *shmem_opts; + char *shmem_path; + const char *arch = qtest_get_arch(); + const char *machine_opts = NULL; + const char *memory_size; + + if (args->use_shmem) { + if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { + g_test_skip("/dev/shm is not supported"); + return -1; + } + } + + got_stop = false; + bootpath = g_strdup_printf("%s/bootsect", tmpfs); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + /* the assembled x86 boot sector should be exactly one sector large */ + assert(sizeof(x86_bootsect) == 512); + init_bootfile(bootpath, x86_bootsect, sizeof(x86_bootsect)); + memory_size = "150M"; + arch_source = g_strdup_printf("-drive file=%s,format=raw", bootpath); + arch_target = g_strdup(arch_source); + start_address = X86_TEST_MEM_START; + end_address = X86_TEST_MEM_END; + } else if (g_str_equal(arch, "s390x")) { + init_bootfile(bootpath, s390x_elf, sizeof(s390x_elf)); + memory_size = "128M"; + arch_source = g_strdup_printf("-bios %s", bootpath); + arch_target = g_strdup(arch_source); + start_address = S390_TEST_MEM_START; + end_address = S390_TEST_MEM_END; + } else if (strcmp(arch, "ppc64") == 0) { + machine_opts = "vsmt=8"; + memory_size = "256M"; + arch_source = g_strdup_printf("-nodefaults " + "-prom-env 'use-nvramrc?=true' -prom-env " + "'nvramrc=hex .\" _\" begin %x %x " + "do i c@ 1 + i c! 1000 +loop .\" B\" 0 " + "until'", end_address, start_address); + arch_target = g_strdup(""); + start_address = PPC_TEST_MEM_START; + end_address = PPC_TEST_MEM_END; + } else if (strcmp(arch, "aarch64") == 0) { + init_bootfile(bootpath, aarch64_kernel, sizeof(aarch64_kernel)); + machine_opts = "virt,gic-version=max"; + memory_size = "150M"; + arch_source = g_strdup_printf("-cpu max " + "-kernel %s", + bootpath); + arch_target = g_strdup(arch_source); + start_address = ARM_TEST_MEM_START; + end_address = ARM_TEST_MEM_END; + + g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); + } else { + g_assert_not_reached(); + } + + g_free(bootpath); + + if (args->hide_stderr) { + ignore_stderr = "2>/dev/null"; + } else { + ignore_stderr = ""; + } + + if (args->use_shmem) { + shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); + shmem_opts = g_strdup_printf( + "-object memory-backend-file,id=mem0,size=%s" + ",mem-path=%s,share=on -numa node,memdev=mem0", + memory_size, shmem_path); + } else { + shmem_path = NULL; + shmem_opts = g_strdup(""); + } + + cmd_source = g_strdup_printf("-accel kvm -accel tcg%s%s " + "-name source,debug-threads=on " + "-m %s " + "-serial file:%s/src_serial " + "%s %s %s %s", + machine_opts ? " -machine " : "", + machine_opts ? machine_opts : "", + memory_size, tmpfs, + arch_source, shmem_opts, args->opts_source, + ignore_stderr); + g_free(arch_source); + *from = qtest_init(cmd_source); + g_free(cmd_source); + + cmd_target = g_strdup_printf("-accel kvm -accel tcg%s%s " + "-name target,debug-threads=on " + "-m %s " + "-serial file:%s/dest_serial " + "-incoming %s " + "%s %s %s %s", + machine_opts ? " -machine " : "", + machine_opts ? machine_opts : "", + memory_size, tmpfs, uri, + arch_target, shmem_opts, + args->opts_target, ignore_stderr); + g_free(arch_target); + *to = qtest_init(cmd_target); + g_free(cmd_target); + + g_free(shmem_opts); + /* + * Remove shmem file immediately to avoid memory leak in test failed case. + * It's valid becase QEMU has already opened this file + */ + if (args->use_shmem) { + unlink(shmem_path); + g_free(shmem_path); + } + + migrate_start_destroy(args); + return 0; +} + +static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) +{ + unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; + + qtest_quit(from); + + if (test_dest) { + qtest_memread(to, start_address, &dest_byte_a, 1); + + /* Destination still running, wait for a byte to change */ + do { + qtest_memread(to, start_address, &dest_byte_b, 1); + usleep(1000 * 10); + } while (dest_byte_a == dest_byte_b); + + qtest_qmp_discard_response(to, "{ 'execute' : 'stop'}"); + + /* With it stopped, check nothing changes */ + qtest_memread(to, start_address, &dest_byte_c, 1); + usleep(1000 * 200); + qtest_memread(to, start_address, &dest_byte_d, 1); + g_assert_cmpint(dest_byte_c, ==, dest_byte_d); + + check_guests_ram(to); + } + + qtest_quit(to); + + cleanup("bootsect"); + cleanup("migsocket"); + cleanup("src_serial"); + cleanup("dest_serial"); +} + +static void deprecated_set_downtime(QTestState *who, const double value) +{ + QDict *rsp; + + rsp = qtest_qmp(who, + "{ 'execute': 'migrate_set_downtime'," + " 'arguments': { 'value': %f } }", value); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + migrate_check_parameter_int(who, "downtime-limit", value * 1000); +} + +static void deprecated_set_speed(QTestState *who, long long value) +{ + QDict *rsp; + + rsp = qtest_qmp(who, "{ 'execute': 'migrate_set_speed'," + "'arguments': { 'value': %lld } }", value); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + migrate_check_parameter_int(who, "max-bandwidth", value); +} + +static void deprecated_set_cache_size(QTestState *who, long long value) +{ + QDict *rsp; + + rsp = qtest_qmp(who, "{ 'execute': 'migrate-set-cache-size'," + "'arguments': { 'value': %lld } }", value); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + migrate_check_parameter_int(who, "xbzrle-cache-size", value); +} + +static void test_deprecated(void) +{ + QTestState *from; + + from = qtest_init("-machine none"); + + deprecated_set_downtime(from, 0.12345); + deprecated_set_speed(from, 12345); + deprecated_set_cache_size(from, 4096); + + qtest_quit(from); +} + +static int migrate_postcopy_prepare(QTestState **from_ptr, + QTestState **to_ptr, + MigrateStart *args) +{ + char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + + if (test_migrate_start(&from, &to, uri, args)) { + return -1; + } + + migrate_set_capability(from, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-blocktime", true); + + /* We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + migrate_set_parameter_int(from, "max-bandwidth", 30000000); + migrate_set_parameter_int(from, "downtime-limit", 1); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, uri, "{}"); + g_free(uri); + + wait_for_migration_pass(from); + + *from_ptr = from; + *to_ptr = to; + + return 0; +} + +static void migrate_postcopy_complete(QTestState *from, QTestState *to) +{ + wait_for_migration_complete(from); + + /* Make sure we get at least one "B" on destination */ + wait_for_serial("dest_serial"); + + if (uffd_feature_thread_id) { + read_blocktime(to); + } + + test_migrate_end(from, to, true); +} + +static void test_postcopy(void) +{ + MigrateStart *args = migrate_start_new(); + QTestState *from, *to; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + migrate_postcopy_start(from, to); + migrate_postcopy_complete(from, to); +} + +static void test_postcopy_recovery(void) +{ + MigrateStart *args = migrate_start_new(); + QTestState *from, *to; + char *uri; + + args->hide_stderr = true; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + + /* Turn postcopy speed down, 4K/s is slow enough on any machines */ + migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); + + /* Now we start the postcopy */ + migrate_postcopy_start(from, to); + + /* + * Wait until postcopy is really started; we can only run the + * migrate-pause command during a postcopy + */ + wait_for_migration_status(from, "postcopy-active", NULL); + + /* + * Manually stop the postcopy migration. This emulates a network + * failure with the migration socket + */ + migrate_pause(from); + + /* + * Wait for destination side to reach postcopy-paused state. The + * migrate-recover command can only succeed if destination machine + * is in the paused state + */ + wait_for_migration_status(to, "postcopy-paused", + (const char * []) { "failed", "active", + "completed", NULL }); + + /* + * Create a new socket to emulate a new channel that is different + * from the broken migration channel; tell the destination to + * listen to the new port + */ + uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); + migrate_recover(to, uri); + + /* + * Try to rebuild the migration channel using the resume flag and + * the newly created channel + */ + wait_for_migration_status(from, "postcopy-paused", + (const char * []) { "failed", "active", + "completed", NULL }); + migrate_qmp(from, uri, "{'resume': true}"); + g_free(uri); + + /* Restore the postcopy bandwidth to unlimited */ + migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); + + migrate_postcopy_complete(from, to); +} + +static void test_baddest(void) +{ + MigrateStart *args = migrate_start_new(); + QTestState *from, *to; + + args->hide_stderr = true; + + if (test_migrate_start(&from, &to, "tcp:0:0", args)) { + return; + } + migrate_qmp(from, "tcp:0:0", "{}"); + wait_for_migration_fail(from, false); + test_migrate_end(from, to, false); +} + +static void test_precopy_unix(void) +{ + char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateStart *args = migrate_start_new(); + QTestState *from, *to; + + if (test_migrate_start(&from, &to, uri, args)) { + return; + } + + /* We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + /* 1 ms should make it not converge*/ + migrate_set_parameter_int(from, "downtime-limit", 1); + /* 1GB/s */ + migrate_set_parameter_int(from, "max-bandwidth", 1000000000); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, uri, "{}"); + + wait_for_migration_pass(from); + + /* 300 ms should converge */ + migrate_set_parameter_int(from, "downtime-limit", 300); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + test_migrate_end(from, to, true); + g_free(uri); +} + +#if 0 +/* Currently upset on aarch64 TCG */ +static void test_ignore_shared(void) +{ + char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + + if (test_migrate_start(&from, &to, uri, false, true, NULL, NULL)) { + return; + } + + migrate_set_capability(from, "x-ignore-shared", true); + migrate_set_capability(to, "x-ignore-shared", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, uri, "{}"); + + wait_for_migration_pass(from); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + /* Check whether shared RAM has been really skipped */ + g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); + + test_migrate_end(from, to, true); + g_free(uri); +} +#endif + +static void test_xbzrle(const char *uri) +{ + MigrateStart *args = migrate_start_new(); + QTestState *from, *to; + + if (test_migrate_start(&from, &to, uri, args)) { + return; + } + + /* + * We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + /* 1 ms should make it not converge*/ + migrate_set_parameter_int(from, "downtime-limit", 1); + /* 1GB/s */ + migrate_set_parameter_int(from, "max-bandwidth", 1000000000); + + migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); + + migrate_set_capability(from, "xbzrle", "true"); + migrate_set_capability(to, "xbzrle", "true"); + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, uri, "{}"); + + wait_for_migration_pass(from); + + /* 300ms should converge */ + migrate_set_parameter_int(from, "downtime-limit", 300); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + test_migrate_end(from, to, true); +} + +static void test_xbzrle_unix(void) +{ + char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + + test_xbzrle(uri); + g_free(uri); +} + +static void test_precopy_tcp(void) +{ + MigrateStart *args = migrate_start_new(); + char *uri; + QTestState *from, *to; + + if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", args)) { + return; + } + + /* + * We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + /* 1 ms should make it not converge*/ + migrate_set_parameter_int(from, "downtime-limit", 1); + /* 1GB/s */ + migrate_set_parameter_int(from, "max-bandwidth", 1000000000); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + uri = migrate_get_socket_address(to, "socket-address"); + + migrate_qmp(from, uri, "{}"); + + wait_for_migration_pass(from); + + /* 300ms should converge */ + migrate_set_parameter_int(from, "downtime-limit", 300); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + test_migrate_end(from, to, true); + g_free(uri); +} + +static void test_migrate_fd_proto(void) +{ + MigrateStart *args = migrate_start_new(); + QTestState *from, *to; + int ret; + int pair[2]; + QDict *rsp; + const char *error_desc; + + if (test_migrate_start(&from, &to, "defer", args)) { + return; + } + + /* + * We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + /* 1 ms should make it not converge */ + migrate_set_parameter_int(from, "downtime-limit", 1); + /* 1GB/s */ + migrate_set_parameter_int(from, "max-bandwidth", 1000000000); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + /* Create two connected sockets for migration */ + ret = socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); + g_assert_cmpint(ret, ==, 0); + + /* Send the 1st socket to the target */ + rsp = wait_command_fd(to, pair[0], + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + qobject_unref(rsp); + close(pair[0]); + + /* Start incoming migration from the 1st socket */ + rsp = wait_command(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': 'fd:fd-mig' }}"); + qobject_unref(rsp); + + /* Send the 2nd socket to the target */ + rsp = wait_command_fd(from, pair[1], + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + qobject_unref(rsp); + close(pair[1]); + + /* Start migration to the 2nd socket*/ + migrate_qmp(from, "fd:fd-mig", "{}"); + + wait_for_migration_pass(from); + + /* 300ms should converge */ + migrate_set_parameter_int(from, "downtime-limit", 300); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + qtest_qmp_eventwait(to, "RESUME"); + + /* Test closing fds */ + /* We assume, that QEMU removes named fd from its list, + * so this should fail */ + rsp = qtest_qmp(from, "{ 'execute': 'closefd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + g_assert_true(qdict_haskey(rsp, "error")); + error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); + g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); + qobject_unref(rsp); + + rsp = qtest_qmp(to, "{ 'execute': 'closefd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + g_assert_true(qdict_haskey(rsp, "error")); + error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); + g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); + qobject_unref(rsp); + + /* Complete migration */ + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + test_migrate_end(from, to, true); +} + +static void do_test_validate_uuid(MigrateStart *args, bool should_fail) +{ + char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + + if (test_migrate_start(&from, &to, uri, args)) { + return; + } + + /* + * UUID validation is at the begin of migration. So, the main process of + * migration is not interesting for us here. Thus, set huge downtime for + * very fast migration. + */ + migrate_set_parameter_int(from, "downtime-limit", 1000000); + migrate_set_capability(from, "validate-uuid", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, uri, "{}"); + + if (should_fail) { + qtest_set_expected_status(to, 1); + wait_for_migration_fail(from, true); + } else { + wait_for_migration_complete(from); + } + + test_migrate_end(from, to, false); + g_free(uri); +} + +static void test_validate_uuid(void) +{ + MigrateStart *args = migrate_start_new(); + + args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); + args->opts_target = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); + do_test_validate_uuid(args, false); +} + +static void test_validate_uuid_error(void) +{ + MigrateStart *args = migrate_start_new(); + + args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); + args->opts_target = g_strdup("-uuid 22222222-2222-2222-2222-222222222222"); + args->hide_stderr = true; + do_test_validate_uuid(args, true); +} + +static void test_validate_uuid_src_not_set(void) +{ + MigrateStart *args = migrate_start_new(); + + args->opts_target = g_strdup("-uuid 22222222-2222-2222-2222-222222222222"); + args->hide_stderr = true; + do_test_validate_uuid(args, false); +} + +static void test_validate_uuid_dst_not_set(void) +{ + MigrateStart *args = migrate_start_new(); + + args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); + args->hide_stderr = true; + do_test_validate_uuid(args, false); +} + +static void test_migrate_auto_converge(void) +{ + char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateStart *args = migrate_start_new(); + QTestState *from, *to; + int64_t remaining, percentage; + + /* + * We want the test to be stable and as fast as possible. + * E.g., with 1Gb/s bandwith migration may pass without throttling, + * so we need to decrease a bandwidth. + */ + const int64_t init_pct = 5, inc_pct = 50, max_pct = 95; + const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ + const int64_t downtime_limit = 250; /* 250ms */ + /* + * We migrate through unix-socket (> 500Mb/s). + * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). + * So, we can predict expected_threshold + */ + const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; + + if (test_migrate_start(&from, &to, uri, args)) { + return; + } + + migrate_set_capability(from, "auto-converge", true); + migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); + migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); + migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); + + /* + * Set the initial parameters so that the migration could not converge + * without throttling. + */ + migrate_set_parameter_int(from, "downtime-limit", 1); + migrate_set_parameter_int(from, "max-bandwidth", 100000000); /* ~100Mb/s */ + + /* To check remaining size after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, uri, "{}"); + + /* Wait for throttling begins */ + percentage = 0; + while (percentage == 0) { + percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); + usleep(100); + g_assert_false(got_stop); + } + /* The first percentage of throttling should be equal to init_pct */ + g_assert_cmpint(percentage, ==, init_pct); + /* Now, when we tested that throttling works, let it converge */ + migrate_set_parameter_int(from, "downtime-limit", downtime_limit); + migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); + + /* + * Wait for pre-switchover status to check last throttle percentage + * and remaining. These values will be zeroed later + */ + wait_for_migration_status(from, "pre-switchover", NULL); + + /* The final percentage of throttling shouldn't be greater than max_pct */ + percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); + g_assert_cmpint(percentage, <=, max_pct); + + remaining = read_ram_property_int(from, "remaining"); + g_assert_cmpint(remaining, <, expected_threshold); + + migrate_continue(from, "pre-switchover"); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + g_free(uri); + + test_migrate_end(from, to, true); +} + +int main(int argc, char **argv) +{ + char template[] = "/tmp/migration-test-XXXXXX"; + int ret; + + g_test_init(&argc, &argv, NULL); + + if (!ufd_version_check()) { + return g_test_run(); + } + + /* + * On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG + * is touchy due to race conditions on dirty bits (especially on PPC for + * some reason) + */ + if (g_str_equal(qtest_get_arch(), "ppc64") && + (access("/sys/module/kvm_hv", F_OK) || + access("/dev/kvm", R_OK | W_OK))) { + g_test_message("Skipping test: kvm_hv not available"); + return g_test_run(); + } + + /* + * Similar to ppc64, s390x seems to be touchy with TCG, so disable it + * there until the problems are resolved + */ + if (g_str_equal(qtest_get_arch(), "s390x")) { +#if defined(HOST_S390X) + if (access("/dev/kvm", R_OK | W_OK)) { + g_test_message("Skipping test: kvm not available"); + return g_test_run(); + } +#else + g_test_message("Skipping test: Need s390x host to work properly"); + return g_test_run(); +#endif + } + + tmpfs = mkdtemp(template); + if (!tmpfs) { + g_test_message("mkdtemp on path (%s): %s", template, strerror(errno)); + } + g_assert(tmpfs); + + module_call_init(MODULE_INIT_QOM); + + qtest_add_func("/migration/postcopy/unix", test_postcopy); + qtest_add_func("/migration/postcopy/recovery", test_postcopy_recovery); + qtest_add_func("/migration/deprecated", test_deprecated); + qtest_add_func("/migration/bad_dest", test_baddest); + qtest_add_func("/migration/precopy/unix", test_precopy_unix); + qtest_add_func("/migration/precopy/tcp", test_precopy_tcp); + /* qtest_add_func("/migration/ignore_shared", test_ignore_shared); */ + qtest_add_func("/migration/xbzrle/unix", test_xbzrle_unix); + qtest_add_func("/migration/fd_proto", test_migrate_fd_proto); + qtest_add_func("/migration/validate_uuid", test_validate_uuid); + qtest_add_func("/migration/validate_uuid_error", test_validate_uuid_error); + qtest_add_func("/migration/validate_uuid_src_not_set", + test_validate_uuid_src_not_set); + qtest_add_func("/migration/validate_uuid_dst_not_set", + test_validate_uuid_dst_not_set); + + qtest_add_func("/migration/auto_converge", test_migrate_auto_converge); + + ret = g_test_run(); + + g_assert_cmpint(ret, ==, 0); + + ret = rmdir(tmpfs); + if (ret != 0) { + g_test_message("unable to rmdir: path (%s): %s", + tmpfs, strerror(errno)); + } + + return ret; +} diff --git a/tests/qtest/modules-test.c b/tests/qtest/modules-test.c new file mode 100644 index 0000000000..88217686e1 --- /dev/null +++ b/tests/qtest/modules-test.c @@ -0,0 +1,74 @@ +#include "qemu/osdep.h" +#include "libqtest.h" + +const char common_args[] = "-nodefaults -machine none"; + +static void test_modules_load(const void *data) +{ + QTestState *qts; + const char **args = (const char **)data; + + qts = qtest_init(common_args); + qtest_module_load(qts, args[0], args[1]); + qtest_quit(qts); +} + +int main(int argc, char *argv[]) +{ + const char *modules[] = { +#ifdef CONFIG_CURL + "block-", "curl", +#endif +#ifdef CONFIG_GLUSTERFS + "block-", "gluster", +#endif +#ifdef CONFIG_LIBISCSI + "block-", "iscsi", +#endif +#ifdef CONFIG_LIBNFS + "block-", "nfs", +#endif +#ifdef CONFIG_LIBSSH + "block-", "ssh", +#endif +#ifdef CONFIG_RBD + "block-", "rbd", +#endif +#ifdef CONFIG_AUDIO_ALSA + "audio-", "alsa", +#endif +#ifdef CONFIG_AUDIO_OSS + "audio-", "oss", +#endif +#ifdef CONFIG_AUDIO_PA + "audio-", "pa", +#endif +#ifdef CONFIG_AUDIO_SDL + "audio-", "sdl", +#endif +#ifdef CONFIG_CURSES + "ui-", "curses", +#endif +#if defined(CONFIG_GTK) && defined(CONFIG_VTE) + "ui-", "gtk", +#endif +#ifdef CONFIG_SDL + "ui-", "sdl", +#endif +#if defined(CONFIG_SPICE) && defined(CONFIG_GIO) + "ui-", "spice-app", +#endif + }; + int i; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < G_N_ELEMENTS(modules); i += 2) { + char *testname = g_strdup_printf("/module/load/%s%s", + modules[i], modules[i + 1]); + qtest_add_data_func(testname, modules + i, test_modules_load); + g_free(testname); + } + + return g_test_run(); +} diff --git a/tests/qtest/ne2000-test.c b/tests/qtest/ne2000-test.c new file mode 100644 index 0000000000..3fc0e555d5 --- /dev/null +++ b/tests/qtest/ne2000-test.c @@ -0,0 +1,58 @@ +/* + * QTest testcase for ne2000 NIC + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QNe2k_pci QNe2k_pci; + +struct QNe2k_pci { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *ne2k_pci_get_driver(void *obj, const char *interface) +{ + QNe2k_pci *ne2k_pci = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &ne2k_pci->dev; + } + + fprintf(stderr, "%s not present in ne2k_pci\n", interface); + g_assert_not_reached(); +} + +static void *ne2k_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QNe2k_pci *ne2k_pci = g_new0(QNe2k_pci, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&ne2k_pci->dev, bus, addr); + ne2k_pci->obj.get_driver = ne2k_pci_get_driver; + + return &ne2k_pci->obj; +} + +static void ne2000_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("ne2k_pci", ne2k_pci_create); + qos_node_consumes("ne2k_pci", "pci-bus", &opts); + qos_node_produces("ne2k_pci", "pci-device"); +} + +libqos_init(ne2000_register_nodes); diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c new file mode 100644 index 0000000000..17dd807d2a --- /dev/null +++ b/tests/qtest/numa-test.c @@ -0,0 +1,574 @@ +/* + * NUMA configuration test cases + * + * Copyright (c) 2017 Red Hat Inc. + * Authors: + * Igor Mammedov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" + +static char *make_cli(const char *generic_cli, const char *test_cli) +{ + return g_strdup_printf("%s %s", generic_cli ? generic_cli : "", test_cli); +} + +static void test_mon_explicit(const void *data) +{ + char *s; + char *cli; + QTestState *qts; + + cli = make_cli(data, "-smp 8 " + "-numa node,nodeid=0,cpus=0-3 " + "-numa node,nodeid=1,cpus=4-7 "); + qts = qtest_init(cli); + + s = qtest_hmp(qts, "info numa"); + g_assert(strstr(s, "node 0 cpus: 0 1 2 3")); + g_assert(strstr(s, "node 1 cpus: 4 5 6 7")); + g_free(s); + + qtest_quit(qts); + g_free(cli); +} + +static void test_mon_default(const void *data) +{ + char *s; + char *cli; + QTestState *qts; + + cli = make_cli(data, "-smp 8 -numa node -numa node"); + qts = qtest_init(cli); + + s = qtest_hmp(qts, "info numa"); + g_assert(strstr(s, "node 0 cpus: 0 2 4 6")); + g_assert(strstr(s, "node 1 cpus: 1 3 5 7")); + g_free(s); + + qtest_quit(qts); + g_free(cli); +} + +static void test_mon_partial(const void *data) +{ + char *s; + char *cli; + QTestState *qts; + + cli = make_cli(data, "-smp 8 " + "-numa node,nodeid=0,cpus=0-1 " + "-numa node,nodeid=1,cpus=4-5 "); + qts = qtest_init(cli); + + s = qtest_hmp(qts, "info numa"); + g_assert(strstr(s, "node 0 cpus: 0 1 2 3 6 7")); + g_assert(strstr(s, "node 1 cpus: 4 5")); + g_free(s); + + qtest_quit(qts); + g_free(cli); +} + +static QList *get_cpus(QTestState *qts, QDict **resp) +{ + *resp = qtest_qmp(qts, "{ 'execute': 'query-cpus' }"); + g_assert(*resp); + g_assert(qdict_haskey(*resp, "return")); + return qdict_get_qlist(*resp, "return"); +} + +static void test_query_cpus(const void *data) +{ + char *cli; + QDict *resp; + QList *cpus; + QObject *e; + QTestState *qts; + + cli = make_cli(data, "-smp 8 -numa node,cpus=0-3 -numa node,cpus=4-7"); + qts = qtest_init(cli); + cpus = get_cpus(qts, &resp); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + QDict *cpu, *props; + int64_t cpu_idx, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "CPU")); + g_assert(qdict_haskey(cpu, "props")); + + cpu_idx = qdict_get_int(cpu, "CPU"); + props = qdict_get_qdict(cpu, "props"); + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + if (cpu_idx >= 0 && cpu_idx < 4) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert_cmpint(node, ==, 1); + } + qobject_unref(e); + } + + qobject_unref(resp); + qtest_quit(qts); + g_free(cli); +} + +static void pc_numa_cpu(const void *data) +{ + char *cli; + QDict *resp; + QList *cpus; + QObject *e; + QTestState *qts; + + cli = make_cli(data, "-cpu pentium -smp 8,sockets=2,cores=2,threads=2 " + "-numa node,nodeid=0 -numa node,nodeid=1 " + "-numa cpu,node-id=1,socket-id=0 " + "-numa cpu,node-id=0,socket-id=1,core-id=0 " + "-numa cpu,node-id=0,socket-id=1,core-id=1,thread-id=0 " + "-numa cpu,node-id=1,socket-id=1,core-id=1,thread-id=1"); + qts = qtest_init(cli); + cpus = get_cpus(qts, &resp); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + QDict *cpu, *props; + int64_t socket, core, thread, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "socket-id")); + socket = qdict_get_int(props, "socket-id"); + g_assert(qdict_haskey(props, "core-id")); + core = qdict_get_int(props, "core-id"); + g_assert(qdict_haskey(props, "thread-id")); + thread = qdict_get_int(props, "thread-id"); + + if (socket == 0) { + g_assert_cmpint(node, ==, 1); + } else if (socket == 1 && core == 0) { + g_assert_cmpint(node, ==, 0); + } else if (socket == 1 && core == 1 && thread == 0) { + g_assert_cmpint(node, ==, 0); + } else if (socket == 1 && core == 1 && thread == 1) { + g_assert_cmpint(node, ==, 1); + } else { + g_assert(false); + } + qobject_unref(e); + } + + qobject_unref(resp); + qtest_quit(qts); + g_free(cli); +} + +static void spapr_numa_cpu(const void *data) +{ + char *cli; + QDict *resp; + QList *cpus; + QObject *e; + QTestState *qts; + + cli = make_cli(data, "-smp 4,cores=4 " + "-numa node,nodeid=0 -numa node,nodeid=1 " + "-numa cpu,node-id=0,core-id=0 " + "-numa cpu,node-id=0,core-id=1 " + "-numa cpu,node-id=0,core-id=2 " + "-numa cpu,node-id=1,core-id=3"); + qts = qtest_init(cli); + cpus = get_cpus(qts, &resp); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + QDict *cpu, *props; + int64_t core, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "core-id")); + core = qdict_get_int(props, "core-id"); + + if (core >= 0 && core < 3) { + g_assert_cmpint(node, ==, 0); + } else if (core == 3) { + g_assert_cmpint(node, ==, 1); + } else { + g_assert(false); + } + qobject_unref(e); + } + + qobject_unref(resp); + qtest_quit(qts); + g_free(cli); +} + +static void aarch64_numa_cpu(const void *data) +{ + char *cli; + QDict *resp; + QList *cpus; + QObject *e; + QTestState *qts; + + cli = make_cli(data, "-smp 2 " + "-numa node,nodeid=0 -numa node,nodeid=1 " + "-numa cpu,node-id=1,thread-id=0 " + "-numa cpu,node-id=0,thread-id=1"); + qts = qtest_init(cli); + cpus = get_cpus(qts, &resp); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + QDict *cpu, *props; + int64_t thread, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "thread-id")); + thread = qdict_get_int(props, "thread-id"); + + if (thread == 0) { + g_assert_cmpint(node, ==, 1); + } else if (thread == 1) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert(false); + } + qobject_unref(e); + } + + qobject_unref(resp); + qtest_quit(qts); + g_free(cli); +} + +static void pc_dynamic_cpu_cfg(const void *data) +{ + QObject *e; + QDict *resp; + QList *cpus; + QTestState *qs; + + qs = qtest_initf("%s -nodefaults --preconfig -smp 2", + data ? (char *)data : ""); + + /* create 2 numa nodes */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 0 } }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 1 } }"))); + + /* map 2 cpus in non default reverse order + * i.e socket1->node0, socket0->node1 + */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'cpu', 'node-id': 0, 'socket-id': 1 } }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'cpu', 'node-id': 1, 'socket-id': 0 } }"))); + + /* let machine initialization to complete and run */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + /* check that CPUs are mapped as expected */ + resp = qtest_qmp(qs, "{ 'execute': 'query-hotpluggable-cpus'}"); + g_assert(qdict_haskey(resp, "return")); + cpus = qdict_get_qlist(resp, "return"); + g_assert(cpus); + while ((e = qlist_pop(cpus))) { + const QDict *cpu, *props; + int64_t socket, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "socket-id")); + socket = qdict_get_int(props, "socket-id"); + + if (socket == 0) { + g_assert_cmpint(node, ==, 1); + } else if (socket == 1) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert(false); + } + qobject_unref(e); + } + qobject_unref(resp); + + qtest_quit(qs); +} + +static void pc_hmat_build_cfg(const void *data) +{ + QTestState *qs = qtest_initf("%s -nodefaults --preconfig -machine hmat=on " + "-smp 2,sockets=2 " + "-m 128M,slots=2,maxmem=1G " + "-object memory-backend-ram,size=64M,id=m0 " + "-object memory-backend-ram,size=64M,id=m1 " + "-numa node,nodeid=0,memdev=m0 " + "-numa node,nodeid=1,memdev=m1,initiator=0 " + "-numa cpu,node-id=0,socket-id=0 " + "-numa cpu,node-id=0,socket-id=1", + data ? (char *)data : ""); + + /* Fail: Initiator should be less than the number of nodes */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 2, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\" } }"))); + + /* Fail: Target should be less than the number of nodes */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 2," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\" } }"))); + + /* Fail: Initiator should contain cpu */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 1, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\" } }"))); + + /* Fail: Data-type mismatch */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"write-latency\"," + " 'bandwidth': 524288000 } }"))); + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"read-bandwidth\"," + " 'latency': 5 } }"))); + + /* Fail: Bandwidth should be 1MB (1048576) aligned */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," + " 'bandwidth': 1048575 } }"))); + + /* Configuring HMAT bandwidth and latency details */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 1 } }"))); /* 1 ns */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 5 } }"))); /* Fail: Duplicate configuration */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," + " 'bandwidth': 68717379584 } }"))); /* 65534 MB/s */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 65534 } }"))); /* 65534 ns */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," + " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," + " 'bandwidth': 34358689792 } }"))); /* 32767 MB/s */ + + /* Fail: node_id should be less than the number of nodes */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 2, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + + /* Fail: level should be less than HMAT_LB_LEVELS (4) */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 4, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + + /* Fail: associativity option should be 'none', if level is 0 */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 0, 'associativity': \"direct\", 'policy': \"none\"," + " 'line': 0 } }"))); + /* Fail: policy option should be 'none', if level is 0 */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 0, 'associativity': \"none\", 'policy': \"write-back\"," + " 'line': 0 } }"))); + /* Fail: line option should be 0, if level is 0 */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 0, 'associativity': \"none\", 'policy': \"none\"," + " 'line': 8 } }"))); + + /* Configuring HMAT memory side cache attributes */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); /* Fail: Duplicate configuration */ + /* Fail: The size of level 2 size should be small than level 1 */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 2, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + /* Fail: The size of level 0 size should be larger than level 1 */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 0, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 1, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + + /* let machine initialization to complete and run */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, + "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + qtest_quit(qs); +} + +static void pc_hmat_off_cfg(const void *data) +{ + QTestState *qs = qtest_initf("%s -nodefaults --preconfig " + "-smp 2,sockets=2 " + "-m 128M,slots=2,maxmem=1G " + "-object memory-backend-ram,size=64M,id=m0 " + "-object memory-backend-ram,size=64M,id=m1 " + "-numa node,nodeid=0,memdev=m0", + data ? (char *)data : ""); + + /* + * Fail: Enable HMAT with -machine hmat=on + * before using any of hmat specific options + */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 1, 'memdev': \"m1\"," + " 'initiator': 0 } }"))); + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 1, 'memdev': \"m1\" } }"))); + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 1 } }"))); + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + + /* let machine initialization to complete and run */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, + "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + qtest_quit(qs); +} + +static void pc_hmat_erange_cfg(const void *data) +{ + QTestState *qs = qtest_initf("%s -nodefaults --preconfig -machine hmat=on " + "-smp 2,sockets=2 " + "-m 128M,slots=2,maxmem=1G " + "-object memory-backend-ram,size=64M,id=m0 " + "-object memory-backend-ram,size=64M,id=m1 " + "-numa node,nodeid=0,memdev=m0 " + "-numa node,nodeid=1,memdev=m1,initiator=0 " + "-numa cpu,node-id=0,socket-id=0 " + "-numa cpu,node-id=0,socket-id=1", + data ? (char *)data : ""); + + /* Can't store the compressed latency */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 1 } }"))); /* 1 ns */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 65535 } }"))); /* 65535 ns */ + + /* Test the 0 input (bandwidth not provided) */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," + " 'bandwidth': 0 } }"))); /* 0 MB/s */ + /* Fail: bandwidth should be provided before memory side cache attributes */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + + /* Can't store the compressed bandwidth */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," + " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," + " 'bandwidth': 68718428160 } }"))); /* 65535 MB/s */ + + /* let machine initialization to complete and run */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, + "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + qtest_quit(qs); +} + +int main(int argc, char **argv) +{ + const char *args = NULL; + const char *arch = qtest_get_arch(); + + if (strcmp(arch, "aarch64") == 0) { + args = "-machine virt"; + } + + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/numa/mon/default", args, test_mon_default); + qtest_add_data_func("/numa/mon/cpus/explicit", args, test_mon_explicit); + qtest_add_data_func("/numa/mon/cpus/partial", args, test_mon_partial); + qtest_add_data_func("/numa/qmp/cpus/query-cpus", args, test_query_cpus); + + if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) { + qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu); + qtest_add_data_func("/numa/pc/dynamic/cpu", args, pc_dynamic_cpu_cfg); + qtest_add_data_func("/numa/pc/hmat/build", args, pc_hmat_build_cfg); + qtest_add_data_func("/numa/pc/hmat/off", args, pc_hmat_off_cfg); + qtest_add_data_func("/numa/pc/hmat/erange", args, pc_hmat_erange_cfg); + } + + if (!strcmp(arch, "ppc64")) { + qtest_add_data_func("/numa/spapr/cpu/explicit", args, spapr_numa_cpu); + } + + if (!strcmp(arch, "aarch64")) { + qtest_add_data_func("/numa/aarch64/cpu/explicit", args, + aarch64_numa_cpu); + } + + return g_test_run(); +} diff --git a/tests/qtest/nvme-test.c b/tests/qtest/nvme-test.c new file mode 100644 index 0000000000..ff0442150c --- /dev/null +++ b/tests/qtest/nvme-test.c @@ -0,0 +1,88 @@ +/* + * QTest testcase for NVMe + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QNvme QNvme; + +struct QNvme { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *nvme_get_driver(void *obj, const char *interface) +{ + QNvme *nvme = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &nvme->dev; + } + + fprintf(stderr, "%s not present in nvme\n", interface); + g_assert_not_reached(); +} + +static void *nvme_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QNvme *nvme = g_new0(QNvme, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&nvme->dev, bus, addr); + nvme->obj.get_driver = nvme_get_driver; + + return &nvme->obj; +} + +/* This used to cause a NULL pointer dereference. */ +static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc) +{ + const int cmb_bar_size = 2 * MiB; + QNvme *nvme = obj; + QPCIDevice *pdev = &nvme->dev; + QPCIBar bar; + + qpci_device_enable(pdev); + bar = qpci_iomap(pdev, 2, NULL); + + qpci_io_writel(pdev, bar, 0, 0xccbbaa99); + g_assert_cmpint(qpci_io_readb(pdev, bar, 0), ==, 0x99); + g_assert_cmpint(qpci_io_readw(pdev, bar, 0), ==, 0xaa99); + + /* Test partially out-of-bounds accesses. */ + qpci_io_writel(pdev, bar, cmb_bar_size - 1, 0x44332211); + g_assert_cmpint(qpci_io_readb(pdev, bar, cmb_bar_size - 1), ==, 0x11); + g_assert_cmpint(qpci_io_readw(pdev, bar, cmb_bar_size - 1), !=, 0x2211); + g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211); +} + +static void nvme_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,drive=drv0,serial=foo", + .before_cmd_line = "-drive id=drv0,if=none,file=null-co://," + "file.read-zeroes=on,format=raw", + }; + + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("nvme", nvme_create); + qos_node_consumes("nvme", "pci-bus", &opts); + qos_node_produces("nvme", "pci-device"); + + qos_add_test("oob-cmb-access", "nvme", nvmetest_oob_cmb_test, &(QOSGraphTestOptions) { + .edge.extra_device_opts = "cmb_size_mb=2" + }); +} + +libqos_init(nvme_register_nodes); diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c new file mode 100644 index 0000000000..4b800d3c3e --- /dev/null +++ b/tests/qtest/pca9552-test.c @@ -0,0 +1,93 @@ +/* + * QTest testcase for the PCA9552 LED blinker + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/i2c.h" +#include "hw/misc/pca9552_regs.h" + +#define PCA9552_TEST_ID "pca9552-test" +#define PCA9552_TEST_ADDR 0x60 + +static void pca9552_init(QI2CDevice *i2cdev) +{ + /* Switch on LEDs 0 and 12 */ + i2c_set8(i2cdev, PCA9552_LS0, 0x54); + i2c_set8(i2cdev, PCA9552_LS3, 0x54); +} + +static void receive_autoinc(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + uint8_t resp; + uint8_t reg = PCA9552_LS0 | PCA9552_AUTOINC; + + pca9552_init(i2cdev); + + i2c_send(i2cdev, ®, 1); + + /* PCA9552_LS0 */ + i2c_recv(i2cdev, &resp, 1); + g_assert_cmphex(resp, ==, 0x54); + + /* PCA9552_LS1 */ + i2c_recv(i2cdev, &resp, 1); + g_assert_cmphex(resp, ==, 0x55); + + /* PCA9552_LS2 */ + i2c_recv(i2cdev, &resp, 1); + g_assert_cmphex(resp, ==, 0x55); + + /* PCA9552_LS3 */ + i2c_recv(i2cdev, &resp, 1); + g_assert_cmphex(resp, ==, 0x54); +} + +static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + uint8_t value; + + value = i2c_get8(i2cdev, PCA9552_LS0); + g_assert_cmphex(value, ==, 0x55); + + value = i2c_get8(i2cdev, PCA9552_INPUT0); + g_assert_cmphex(value, ==, 0x0); + + pca9552_init(i2cdev); + + value = i2c_get8(i2cdev, PCA9552_LS0); + g_assert_cmphex(value, ==, 0x54); + + value = i2c_get8(i2cdev, PCA9552_INPUT0); + g_assert_cmphex(value, ==, 0x01); + + value = i2c_get8(i2cdev, PCA9552_LS3); + g_assert_cmphex(value, ==, 0x54); + + value = i2c_get8(i2cdev, PCA9552_INPUT1); + g_assert_cmphex(value, ==, 0x10); +} + +static void pca9552_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "address=0x60" + }; + add_qi2c_address(&opts, &(QI2CAddress) { 0x60 }); + + qos_node_create_driver("pca9552", i2c_device_create); + qos_node_consumes("pca9552", "i2c-bus", &opts); + + qos_add_test("tx-rx", "pca9552", send_and_receive, NULL); + qos_add_test("rx-autoinc", "pca9552", receive_autoinc, NULL); +} +libqos_init(pca9552_register_nodes); diff --git a/tests/qtest/pci-test.c b/tests/qtest/pci-test.c new file mode 100644 index 0000000000..4b2092b949 --- /dev/null +++ b/tests/qtest/pci-test.c @@ -0,0 +1,26 @@ +/* + * QTest testcase for PCI + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void nop(void *obj, void *data, QGuestAllocator *alloc) +{ +} + +static void register_pci_test(void) +{ + qos_add_test("nop", "pci-device", nop, NULL); +} + +libqos_init(register_pci_test); diff --git a/tests/qtest/pcnet-test.c b/tests/qtest/pcnet-test.c new file mode 100644 index 0000000000..900944fa7e --- /dev/null +++ b/tests/qtest/pcnet-test.c @@ -0,0 +1,58 @@ +/* + * QTest testcase for PC-Net NIC + * + * Copyright (c) 2013-2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QPCNet QPCNet; + +struct QPCNet { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *pcnet_get_driver(void *obj, const char *interface) +{ + QPCNet *pcnet = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &pcnet->dev; + } + + fprintf(stderr, "%s not present in pcnet\n", interface); + g_assert_not_reached(); +} + +static void *pcnet_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QPCNet *pcnet = g_new0(QPCNet, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&pcnet->dev, bus, addr); + pcnet->obj.get_driver = pcnet_get_driver; + + return &pcnet->obj; +} + +static void pcnet_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("pcnet", pcnet_create); + qos_node_consumes("pcnet", "pci-bus", &opts); + qos_node_produces("pcnet", "pci-device"); +} + +libqos_init(pcnet_register_nodes); diff --git a/tests/qtest/pflash-cfi02-test.c b/tests/qtest/pflash-cfi02-test.c new file mode 100644 index 0000000000..17aa669b2e --- /dev/null +++ b/tests/qtest/pflash-cfi02-test.c @@ -0,0 +1,681 @@ +/* + * QTest testcase for parallel flash with AMD command set + * + * Copyright (c) 2019 Stephen Checkoway + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +/* + * To test the pflash_cfi02 device, we run QEMU with the musicpal machine with + * a pflash drive. This enables us to test some flash configurations, but not + * all. In particular, we're limited to a 16-bit wide flash device. + */ + +#define MP_FLASH_SIZE_MAX (32 * 1024 * 1024) +#define BASE_ADDR (0x100000000ULL - MP_FLASH_SIZE_MAX) + +#define UNIFORM_FLASH_SIZE (8 * 1024 * 1024) +#define UNIFORM_FLASH_SECTOR_SIZE (64 * 1024) + +/* Use a newtype to keep flash addresses separate from byte addresses. */ +typedef struct { + uint64_t addr; +} faddr; +#define FLASH_ADDR(x) ((faddr) { .addr = (x) }) + +#define CFI_ADDR FLASH_ADDR(0x55) +#define UNLOCK0_ADDR FLASH_ADDR(0x555) +#define UNLOCK1_ADDR FLASH_ADDR(0x2AA) + +#define CFI_CMD 0x98 +#define UNLOCK0_CMD 0xAA +#define UNLOCK1_CMD 0x55 +#define SECOND_UNLOCK_CMD 0x80 +#define AUTOSELECT_CMD 0x90 +#define RESET_CMD 0xF0 +#define PROGRAM_CMD 0xA0 +#define SECTOR_ERASE_CMD 0x30 +#define CHIP_ERASE_CMD 0x10 +#define UNLOCK_BYPASS_CMD 0x20 +#define UNLOCK_BYPASS_RESET_CMD 0x00 +#define ERASE_SUSPEND_CMD 0xB0 +#define ERASE_RESUME_CMD SECTOR_ERASE_CMD + +typedef struct { + int bank_width; + + /* Nonuniform block size. */ + int nb_blocs[4]; + int sector_len[4]; + + QTestState *qtest; +} FlashConfig; + +static char image_path[] = "/tmp/qtest.XXXXXX"; + +/* + * The pflash implementation allows some parameters to be unspecified. We want + * to test those configurations but we also need to know the real values in + * our testing code. So after we launch qemu, we'll need a new FlashConfig + * with the correct values filled in. + */ +static FlashConfig expand_config_defaults(const FlashConfig *c) +{ + FlashConfig ret = *c; + + if (ret.bank_width == 0) { + ret.bank_width = 2; + } + if (ret.nb_blocs[0] == 0 && ret.sector_len[0] == 0) { + ret.sector_len[0] = UNIFORM_FLASH_SECTOR_SIZE; + ret.nb_blocs[0] = UNIFORM_FLASH_SIZE / UNIFORM_FLASH_SECTOR_SIZE; + } + + /* XXX: Limitations of test harness. */ + assert(ret.bank_width == 2); + return ret; +} + +/* + * Return a bit mask suitable for extracting the least significant + * status/query response from an interleaved response. + */ +static inline uint64_t device_mask(const FlashConfig *c) +{ + return (uint64_t)-1; +} + +/* + * Return a bit mask exactly as long as the bank_width. + */ +static inline uint64_t bank_mask(const FlashConfig *c) +{ + if (c->bank_width == 8) { + return (uint64_t)-1; + } + return (1ULL << (c->bank_width * 8)) - 1ULL; +} + +static inline void flash_write(const FlashConfig *c, uint64_t byte_addr, + uint64_t data) +{ + /* Sanity check our tests. */ + assert((data & ~bank_mask(c)) == 0); + uint64_t addr = BASE_ADDR + byte_addr; + switch (c->bank_width) { + case 1: + qtest_writeb(c->qtest, addr, data); + break; + case 2: + qtest_writew(c->qtest, addr, data); + break; + case 4: + qtest_writel(c->qtest, addr, data); + break; + case 8: + qtest_writeq(c->qtest, addr, data); + break; + default: + abort(); + } +} + +static inline uint64_t flash_read(const FlashConfig *c, uint64_t byte_addr) +{ + uint64_t addr = BASE_ADDR + byte_addr; + switch (c->bank_width) { + case 1: + return qtest_readb(c->qtest, addr); + case 2: + return qtest_readw(c->qtest, addr); + case 4: + return qtest_readl(c->qtest, addr); + case 8: + return qtest_readq(c->qtest, addr); + default: + abort(); + } +} + +/* + * Convert a flash address expressed in the maximum width of the device as a + * byte address. + */ +static inline uint64_t as_byte_addr(const FlashConfig *c, faddr flash_addr) +{ + /* + * Command addresses are always given as addresses in the maximum + * supported bus size for the flash chip. So an x8/x16 chip in x8 mode + * uses addresses 0xAAA and 0x555 to unlock because the least significant + * bit is ignored. (0x555 rather than 0x554 is traditional.) + * + * In general we need to multiply by the maximum device width. + */ + return flash_addr.addr * c->bank_width; +} + +/* + * Return the command value or expected status replicated across all devices. + */ +static inline uint64_t replicate(const FlashConfig *c, uint64_t data) +{ + /* Sanity check our tests. */ + assert((data & ~device_mask(c)) == 0); + return data; +} + +static inline void flash_cmd(const FlashConfig *c, faddr cmd_addr, + uint8_t cmd) +{ + flash_write(c, as_byte_addr(c, cmd_addr), replicate(c, cmd)); +} + +static inline uint64_t flash_query(const FlashConfig *c, faddr query_addr) +{ + return flash_read(c, as_byte_addr(c, query_addr)); +} + +static inline uint64_t flash_query_1(const FlashConfig *c, faddr query_addr) +{ + return flash_query(c, query_addr) & device_mask(c); +} + +static void unlock(const FlashConfig *c) +{ + flash_cmd(c, UNLOCK0_ADDR, UNLOCK0_CMD); + flash_cmd(c, UNLOCK1_ADDR, UNLOCK1_CMD); +} + +static void reset(const FlashConfig *c) +{ + flash_cmd(c, FLASH_ADDR(0), RESET_CMD); +} + +static void sector_erase(const FlashConfig *c, uint64_t byte_addr) +{ + unlock(c); + flash_cmd(c, UNLOCK0_ADDR, SECOND_UNLOCK_CMD); + unlock(c); + flash_write(c, byte_addr, replicate(c, SECTOR_ERASE_CMD)); +} + +static void wait_for_completion(const FlashConfig *c, uint64_t byte_addr) +{ + /* If DQ6 is toggling, step the clock and ensure the toggle stops. */ + const uint64_t dq6 = replicate(c, 0x40); + if ((flash_read(c, byte_addr) & dq6) ^ (flash_read(c, byte_addr) & dq6)) { + /* Wait for erase or program to finish. */ + qtest_clock_step_next(c->qtest); + /* Ensure that DQ6 has stopped toggling. */ + g_assert_cmphex(flash_read(c, byte_addr), ==, flash_read(c, byte_addr)); + } +} + +static void bypass_program(const FlashConfig *c, uint64_t byte_addr, + uint16_t data) +{ + flash_cmd(c, UNLOCK0_ADDR, PROGRAM_CMD); + flash_write(c, byte_addr, data); + /* + * Data isn't valid until DQ6 stops toggling. We don't model this as + * writes are immediate, but if this changes in the future, we can wait + * until the program is complete. + */ + wait_for_completion(c, byte_addr); +} + +static void program(const FlashConfig *c, uint64_t byte_addr, uint16_t data) +{ + unlock(c); + bypass_program(c, byte_addr, data); +} + +static void chip_erase(const FlashConfig *c) +{ + unlock(c); + flash_cmd(c, UNLOCK0_ADDR, SECOND_UNLOCK_CMD); + unlock(c); + flash_cmd(c, UNLOCK0_ADDR, CHIP_ERASE_CMD); +} + +static void erase_suspend(const FlashConfig *c) +{ + flash_cmd(c, FLASH_ADDR(0), ERASE_SUSPEND_CMD); +} + +static void erase_resume(const FlashConfig *c) +{ + flash_cmd(c, FLASH_ADDR(0), ERASE_RESUME_CMD); +} + +/* + * Test flash commands with a variety of device geometry. + */ +static void test_geometry(const void *opaque) +{ + const FlashConfig *config = opaque; + QTestState *qtest; + qtest = qtest_initf("-M musicpal" + " -drive if=pflash,file=%s,format=raw,copy-on-read" + /* Device geometry properties. */ + " -global driver=cfi.pflash02," + "property=num-blocks0,value=%d" + " -global driver=cfi.pflash02," + "property=sector-length0,value=%d" + " -global driver=cfi.pflash02," + "property=num-blocks1,value=%d" + " -global driver=cfi.pflash02," + "property=sector-length1,value=%d" + " -global driver=cfi.pflash02," + "property=num-blocks2,value=%d" + " -global driver=cfi.pflash02," + "property=sector-length2,value=%d" + " -global driver=cfi.pflash02," + "property=num-blocks3,value=%d" + " -global driver=cfi.pflash02," + "property=sector-length3,value=%d", + image_path, + config->nb_blocs[0], + config->sector_len[0], + config->nb_blocs[1], + config->sector_len[1], + config->nb_blocs[2], + config->sector_len[2], + config->nb_blocs[3], + config->sector_len[3]); + FlashConfig explicit_config = expand_config_defaults(config); + explicit_config.qtest = qtest; + const FlashConfig *c = &explicit_config; + + /* Check the IDs. */ + unlock(c); + flash_cmd(c, UNLOCK0_ADDR, AUTOSELECT_CMD); + g_assert_cmphex(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF)); + if (c->bank_width >= 2) { + /* + * XXX: The ID returned by the musicpal flash chip is 16 bits which + * wouldn't happen with an 8-bit device. It would probably be best to + * prohibit addresses larger than the device width in pflash_cfi02.c, + * but then we couldn't test smaller device widths at all. + */ + g_assert_cmphex(flash_query(c, FLASH_ADDR(1)), ==, + replicate(c, 0x236D)); + } + reset(c); + + /* Check the erase blocks. */ + flash_cmd(c, CFI_ADDR, CFI_CMD); + g_assert_cmphex(flash_query(c, FLASH_ADDR(0x10)), ==, replicate(c, 'Q')); + g_assert_cmphex(flash_query(c, FLASH_ADDR(0x11)), ==, replicate(c, 'R')); + g_assert_cmphex(flash_query(c, FLASH_ADDR(0x12)), ==, replicate(c, 'Y')); + + /* Num erase regions. */ + int nb_erase_regions = flash_query_1(c, FLASH_ADDR(0x2C)); + g_assert_cmphex(nb_erase_regions, ==, + !!c->nb_blocs[0] + !!c->nb_blocs[1] + !!c->nb_blocs[2] + + !!c->nb_blocs[3]); + + /* Check device length. */ + uint32_t device_len = 1 << flash_query_1(c, FLASH_ADDR(0x27)); + g_assert_cmphex(device_len, ==, UNIFORM_FLASH_SIZE); + + /* Check that erase suspend to read/write is supported. */ + uint16_t pri = flash_query_1(c, FLASH_ADDR(0x15)) + + (flash_query_1(c, FLASH_ADDR(0x16)) << 8); + g_assert_cmpint(pri, >=, 0x2D + 4 * nb_erase_regions); + g_assert_cmpint(flash_query(c, FLASH_ADDR(pri + 0)), ==, replicate(c, 'P')); + g_assert_cmpint(flash_query(c, FLASH_ADDR(pri + 1)), ==, replicate(c, 'R')); + g_assert_cmpint(flash_query(c, FLASH_ADDR(pri + 2)), ==, replicate(c, 'I')); + g_assert_cmpint(flash_query_1(c, FLASH_ADDR(pri + 6)), ==, 2); /* R/W */ + reset(c); + + const uint64_t dq7 = replicate(c, 0x80); + const uint64_t dq6 = replicate(c, 0x40); + const uint64_t dq3 = replicate(c, 0x08); + const uint64_t dq2 = replicate(c, 0x04); + + uint64_t byte_addr = 0; + for (int region = 0; region < nb_erase_regions; ++region) { + uint64_t base = 0x2D + 4 * region; + flash_cmd(c, CFI_ADDR, CFI_CMD); + uint32_t nb_sectors = flash_query_1(c, FLASH_ADDR(base + 0)) + + (flash_query_1(c, FLASH_ADDR(base + 1)) << 8) + 1; + uint32_t sector_len = (flash_query_1(c, FLASH_ADDR(base + 2)) << 8) + + (flash_query_1(c, FLASH_ADDR(base + 3)) << 16); + g_assert_cmphex(nb_sectors, ==, c->nb_blocs[region]); + g_assert_cmphex(sector_len, ==, c->sector_len[region]); + reset(c); + + /* Erase and program sector. */ + for (uint32_t i = 0; i < nb_sectors; ++i) { + sector_erase(c, byte_addr); + + /* Check that DQ3 is 0. */ + g_assert_cmphex(flash_read(c, byte_addr) & dq3, ==, 0); + qtest_clock_step_next(c->qtest); /* Step over the 50 us timeout. */ + + /* Check that DQ3 is 1. */ + uint64_t status0 = flash_read(c, byte_addr); + g_assert_cmphex(status0 & dq3, ==, dq3); + + /* DQ7 is 0 during an erase. */ + g_assert_cmphex(status0 & dq7, ==, 0); + uint64_t status1 = flash_read(c, byte_addr); + + /* DQ6 toggles during an erase. */ + g_assert_cmphex(status0 & dq6, ==, ~status1 & dq6); + + /* Wait for erase to complete. */ + wait_for_completion(c, byte_addr); + + /* Ensure DQ6 has stopped toggling. */ + g_assert_cmphex(flash_read(c, byte_addr), ==, + flash_read(c, byte_addr)); + + /* Now the data should be valid. */ + g_assert_cmphex(flash_read(c, byte_addr), ==, bank_mask(c)); + + /* Program a bit pattern. */ + program(c, byte_addr, 0x55); + g_assert_cmphex(flash_read(c, byte_addr) & 0xFF, ==, 0x55); + program(c, byte_addr, 0xA5); + g_assert_cmphex(flash_read(c, byte_addr) & 0xFF, ==, 0x05); + byte_addr += sector_len; + } + } + + /* Erase the chip. */ + chip_erase(c); + /* Read toggle. */ + uint64_t status0 = flash_read(c, 0); + /* DQ7 is 0 during an erase. */ + g_assert_cmphex(status0 & dq7, ==, 0); + uint64_t status1 = flash_read(c, 0); + /* DQ6 toggles during an erase. */ + g_assert_cmphex(status0 & dq6, ==, ~status1 & dq6); + /* Wait for erase to complete. */ + qtest_clock_step_next(c->qtest); + /* Ensure DQ6 has stopped toggling. */ + g_assert_cmphex(flash_read(c, 0), ==, flash_read(c, 0)); + /* Now the data should be valid. */ + + for (int region = 0; region < nb_erase_regions; ++region) { + for (uint32_t i = 0; i < c->nb_blocs[region]; ++i) { + uint64_t byte_addr = i * c->sector_len[region]; + g_assert_cmphex(flash_read(c, byte_addr), ==, bank_mask(c)); + } + } + + /* Unlock bypass */ + unlock(c); + flash_cmd(c, UNLOCK0_ADDR, UNLOCK_BYPASS_CMD); + bypass_program(c, 0 * c->bank_width, 0x01); + bypass_program(c, 1 * c->bank_width, 0x23); + bypass_program(c, 2 * c->bank_width, 0x45); + /* + * Test that bypass programming, unlike normal programming can use any + * address for the PROGRAM_CMD. + */ + flash_cmd(c, FLASH_ADDR(3 * c->bank_width), PROGRAM_CMD); + flash_write(c, 3 * c->bank_width, 0x67); + wait_for_completion(c, 3 * c->bank_width); + flash_cmd(c, FLASH_ADDR(0), UNLOCK_BYPASS_RESET_CMD); + bypass_program(c, 4 * c->bank_width, 0x89); /* Should fail. */ + g_assert_cmphex(flash_read(c, 0 * c->bank_width), ==, 0x01); + g_assert_cmphex(flash_read(c, 1 * c->bank_width), ==, 0x23); + g_assert_cmphex(flash_read(c, 2 * c->bank_width), ==, 0x45); + g_assert_cmphex(flash_read(c, 3 * c->bank_width), ==, 0x67); + g_assert_cmphex(flash_read(c, 4 * c->bank_width), ==, bank_mask(c)); + + /* Test ignored high order bits of address. */ + flash_cmd(c, FLASH_ADDR(0x5555), UNLOCK0_CMD); + flash_cmd(c, FLASH_ADDR(0x2AAA), UNLOCK1_CMD); + flash_cmd(c, FLASH_ADDR(0x5555), AUTOSELECT_CMD); + g_assert_cmphex(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF)); + reset(c); + + /* + * Program a word on each sector, erase one or two sectors per region, and + * verify that all of those, and only those, are erased. + */ + byte_addr = 0; + for (int region = 0; region < nb_erase_regions; ++region) { + for (int i = 0; i < config->nb_blocs[region]; ++i) { + program(c, byte_addr, 0); + byte_addr += config->sector_len[region]; + } + } + unlock(c); + flash_cmd(c, UNLOCK0_ADDR, SECOND_UNLOCK_CMD); + unlock(c); + byte_addr = 0; + const uint64_t erase_cmd = replicate(c, SECTOR_ERASE_CMD); + for (int region = 0; region < nb_erase_regions; ++region) { + flash_write(c, byte_addr, erase_cmd); + if (c->nb_blocs[region] > 1) { + flash_write(c, byte_addr + c->sector_len[region], erase_cmd); + } + byte_addr += c->sector_len[region] * c->nb_blocs[region]; + } + + qtest_clock_step_next(c->qtest); /* Step over the 50 us timeout. */ + wait_for_completion(c, 0); + byte_addr = 0; + for (int region = 0; region < nb_erase_regions; ++region) { + for (int i = 0; i < config->nb_blocs[region]; ++i) { + if (i < 2) { + g_assert_cmphex(flash_read(c, byte_addr), ==, bank_mask(c)); + } else { + g_assert_cmphex(flash_read(c, byte_addr), ==, 0); + } + byte_addr += config->sector_len[region]; + } + } + + /* Test erase suspend/resume during erase timeout. */ + sector_erase(c, 0); + /* + * Check that DQ 3 is 0 and DQ6 and DQ2 are toggling in the sector being + * erased as well as in a sector not being erased. + */ + byte_addr = c->sector_len[0]; + status0 = flash_read(c, 0); + status1 = flash_read(c, 0); + g_assert_cmpint(status0 & dq3, ==, 0); + g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); + g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); + status0 = flash_read(c, byte_addr); + status1 = flash_read(c, byte_addr); + g_assert_cmpint(status0 & dq3, ==, 0); + g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); + g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); + + /* + * Check that after suspending, DQ6 does not toggle but DQ2 does toggle in + * an erase suspended sector but that neither toggle (we should be + * getting data) in a sector not being erased. + */ + erase_suspend(c); + status0 = flash_read(c, 0); + status1 = flash_read(c, 0); + g_assert_cmpint(status0 & dq6, ==, status1 & dq6); + g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); + g_assert_cmpint(flash_read(c, byte_addr), ==, flash_read(c, byte_addr)); + + /* Check that after resuming, DQ3 is 1 and DQ6 and DQ2 toggle. */ + erase_resume(c); + status0 = flash_read(c, 0); + status1 = flash_read(c, 0); + g_assert_cmpint(status0 & dq3, ==, dq3); + g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); + g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); + status0 = flash_read(c, byte_addr); + status1 = flash_read(c, byte_addr); + g_assert_cmpint(status0 & dq3, ==, dq3); + g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); + g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); + wait_for_completion(c, 0); + + /* Repeat this process but this time suspend after the timeout. */ + sector_erase(c, 0); + qtest_clock_step_next(c->qtest); + /* + * Check that DQ 3 is 1 and DQ6 and DQ2 are toggling in the sector being + * erased as well as in a sector not being erased. + */ + byte_addr = c->sector_len[0]; + status0 = flash_read(c, 0); + status1 = flash_read(c, 0); + g_assert_cmpint(status0 & dq3, ==, dq3); + g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); + g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); + status0 = flash_read(c, byte_addr); + status1 = flash_read(c, byte_addr); + g_assert_cmpint(status0 & dq3, ==, dq3); + g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); + g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); + + /* + * Check that after suspending, DQ6 does not toggle but DQ2 does toggle in + * an erase suspended sector but that neither toggle (we should be + * getting data) in a sector not being erased. + */ + erase_suspend(c); + status0 = flash_read(c, 0); + status1 = flash_read(c, 0); + g_assert_cmpint(status0 & dq6, ==, status1 & dq6); + g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); + g_assert_cmpint(flash_read(c, byte_addr), ==, flash_read(c, byte_addr)); + + /* Check that after resuming, DQ3 is 1 and DQ6 and DQ2 toggle. */ + erase_resume(c); + status0 = flash_read(c, 0); + status1 = flash_read(c, 0); + g_assert_cmpint(status0 & dq3, ==, dq3); + g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); + g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); + status0 = flash_read(c, byte_addr); + status1 = flash_read(c, byte_addr); + g_assert_cmpint(status0 & dq3, ==, dq3); + g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6); + g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2); + wait_for_completion(c, 0); + + qtest_quit(qtest); +} + +/* + * Test that + * 1. enter autoselect mode; + * 2. enter CFI mode; and then + * 3. exit CFI mode + * leaves the flash device in autoselect mode. + */ +static void test_cfi_in_autoselect(const void *opaque) +{ + const FlashConfig *config = opaque; + QTestState *qtest; + qtest = qtest_initf("-M musicpal" + " -drive if=pflash,file=%s,format=raw,copy-on-read", + image_path); + FlashConfig explicit_config = expand_config_defaults(config); + explicit_config.qtest = qtest; + const FlashConfig *c = &explicit_config; + + /* 1. Enter autoselect. */ + unlock(c); + flash_cmd(c, UNLOCK0_ADDR, AUTOSELECT_CMD); + g_assert_cmphex(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF)); + + /* 2. Enter CFI. */ + flash_cmd(c, CFI_ADDR, CFI_CMD); + g_assert_cmphex(flash_query(c, FLASH_ADDR(0x10)), ==, replicate(c, 'Q')); + g_assert_cmphex(flash_query(c, FLASH_ADDR(0x11)), ==, replicate(c, 'R')); + g_assert_cmphex(flash_query(c, FLASH_ADDR(0x12)), ==, replicate(c, 'Y')); + + /* 3. Exit CFI. */ + reset(c); + g_assert_cmphex(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF)); + + qtest_quit(qtest); +} + +static void cleanup(void *opaque) +{ + unlink(image_path); +} + +/* + * XXX: Tests are limited to bank_width = 2 for now because that's what + * hw/arm/musicpal.c has. + */ +static const FlashConfig configuration[] = { + /* One x16 device. */ + { + .bank_width = 2, + }, + /* Nonuniform sectors (top boot). */ + { + .bank_width = 2, + .nb_blocs = { 127, 1, 2, 1 }, + .sector_len = { 0x10000, 0x08000, 0x02000, 0x04000 }, + }, + /* Nonuniform sectors (bottom boot). */ + { + .bank_width = 2, + .nb_blocs = { 1, 2, 1, 127 }, + .sector_len = { 0x04000, 0x02000, 0x08000, 0x10000 }, + }, +}; + +int main(int argc, char **argv) +{ + int fd = mkstemp(image_path); + if (fd == -1) { + g_printerr("Failed to create temporary file %s: %s\n", image_path, + strerror(errno)); + exit(EXIT_FAILURE); + } + if (ftruncate(fd, UNIFORM_FLASH_SIZE) < 0) { + int error_code = errno; + close(fd); + unlink(image_path); + g_printerr("Failed to truncate file %s to %u MB: %s\n", image_path, + UNIFORM_FLASH_SIZE, strerror(error_code)); + exit(EXIT_FAILURE); + } + close(fd); + + qtest_add_abrt_handler(cleanup, NULL); + g_test_init(&argc, &argv, NULL); + + size_t nb_configurations = sizeof configuration / sizeof configuration[0]; + for (size_t i = 0; i < nb_configurations; ++i) { + const FlashConfig *config = &configuration[i]; + char *path = g_strdup_printf("pflash-cfi02" + "/geometry/%dx%x-%dx%x-%dx%x-%dx%x" + "/%d", + config->nb_blocs[0], + config->sector_len[0], + config->nb_blocs[1], + config->sector_len[1], + config->nb_blocs[2], + config->sector_len[2], + config->nb_blocs[3], + config->sector_len[3], + config->bank_width); + qtest_add_data_func(path, config, test_geometry); + g_free(path); + } + + qtest_add_data_func("pflash-cfi02/cfi-in-autoselect", &configuration[0], + test_cfi_in_autoselect); + int result = g_test_run(); + cleanup(NULL); + return result; +} diff --git a/tests/qtest/pnv-xscom-test.c b/tests/qtest/pnv-xscom-test.c new file mode 100644 index 0000000000..2c46d5cf6d --- /dev/null +++ b/tests/qtest/pnv-xscom-test.c @@ -0,0 +1,153 @@ +/* + * QTest testcase for PowerNV XSCOM bus + * + * Copyright (c) 2016, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" + +#include "libqtest.h" + +typedef enum PnvChipType { + PNV_CHIP_POWER8E, /* AKA Murano (default) */ + PNV_CHIP_POWER8, /* AKA Venice */ + PNV_CHIP_POWER8NVL, /* AKA Naples */ + PNV_CHIP_POWER9, /* AKA Nimbus */ +} PnvChipType; + +typedef struct PnvChip { + PnvChipType chip_type; + const char *cpu_model; + uint64_t xscom_base; + uint64_t cfam_id; + uint32_t first_core; +} PnvChip; + +static const PnvChip pnv_chips[] = { + { + .chip_type = PNV_CHIP_POWER8, + .cpu_model = "POWER8", + .xscom_base = 0x0003fc0000000000ull, + .cfam_id = 0x220ea04980000000ull, + .first_core = 0x1, + }, { + .chip_type = PNV_CHIP_POWER8NVL, + .cpu_model = "POWER8NVL", + .xscom_base = 0x0003fc0000000000ull, + .cfam_id = 0x120d304980000000ull, + .first_core = 0x1, + }, + { + .chip_type = PNV_CHIP_POWER9, + .cpu_model = "POWER9", + .xscom_base = 0x000603fc00000000ull, + .cfam_id = 0x220d104900008000ull, + .first_core = 0x0, + }, +}; + +static uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba) +{ + uint64_t addr = chip->xscom_base; + + if (chip->chip_type == PNV_CHIP_POWER9) { + addr |= ((uint64_t) pcba << 3); + } else { + addr |= (((uint64_t) pcba << 4) & ~0xffull) | + (((uint64_t) pcba << 3) & 0x78); + } + return addr; +} + +static uint64_t pnv_xscom_read(QTestState *qts, const PnvChip *chip, + uint32_t pcba) +{ + return qtest_readq(qts, pnv_xscom_addr(chip, pcba)); +} + +static void test_xscom_cfam_id(QTestState *qts, const PnvChip *chip) +{ + uint64_t f000f = pnv_xscom_read(qts, chip, 0xf000f); + + g_assert_cmphex(f000f, ==, chip->cfam_id); +} + +static void test_cfam_id(const void *data) +{ + const PnvChip *chip = data; + const char *machine = "powernv8"; + QTestState *qts; + + if (chip->chip_type == PNV_CHIP_POWER9) { + machine = "powernv9"; + } + + qts = qtest_initf("-M %s -accel tcg -cpu %s", + machine, chip->cpu_model); + test_xscom_cfam_id(qts, chip); + qtest_quit(qts); +} + + +#define PNV_XSCOM_EX_CORE_BASE 0x10000000ull +#define PNV_XSCOM_EX_BASE(core) \ + (PNV_XSCOM_EX_CORE_BASE | ((uint64_t)(core) << 24)) +#define PNV_XSCOM_P9_EC_BASE(core) \ + ((uint64_t)(((core) & 0x1F) + 0x20) << 24) + +#define PNV_XSCOM_EX_DTS_RESULT0 0x50000 + +static void test_xscom_core(QTestState *qts, const PnvChip *chip) +{ + uint32_t first_core_dts0 = PNV_XSCOM_EX_DTS_RESULT0; + uint64_t dts0; + + if (chip->chip_type != PNV_CHIP_POWER9) { + first_core_dts0 |= PNV_XSCOM_EX_BASE(chip->first_core); + } else { + first_core_dts0 |= PNV_XSCOM_P9_EC_BASE(chip->first_core); + } + + dts0 = pnv_xscom_read(qts, chip, first_core_dts0); + + g_assert_cmphex(dts0, ==, 0x26f024f023f0000ull); +} + +static void test_core(const void *data) +{ + const PnvChip *chip = data; + QTestState *qts; + const char *machine = "powernv8"; + + if (chip->chip_type == PNV_CHIP_POWER9) { + machine = "powernv9"; + } + + qts = qtest_initf("-M %s -accel tcg -cpu %s", + machine, chip->cpu_model); + test_xscom_core(qts, chip); + qtest_quit(qts); +} + +static void add_test(const char *name, void (*test)(const void *data)) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pnv_chips); i++) { + char *tname = g_strdup_printf("pnv-xscom/%s/%s", name, + pnv_chips[i].cpu_model); + qtest_add_data_func(tname, &pnv_chips[i], test); + g_free(tname); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + add_test("cfam_id", test_cfam_id); + add_test("core", test_core); + return g_test_run(); +} diff --git a/tests/qtest/prom-env-test.c b/tests/qtest/prom-env-test.c new file mode 100644 index 0000000000..9be52c766f --- /dev/null +++ b/tests/qtest/prom-env-test.c @@ -0,0 +1,104 @@ +/* + * Test Open-Firmware-based machines. + * + * Copyright (c) 2016, 2017 Red Hat Inc. + * + * Author: + * Thomas Huth + * + * This work is licensed under the terms of the GNU GPL, version 2 + * or later. See the COPYING file in the top-level directory. + * + * This test is used to check that some Open Firmware based machines (i.e. + * OpenBIOS or SLOF) can be started successfully in TCG mode. To do this, we + * first put some Forth code into the "boot-command" Open Firmware environment + * variable. This Forth code writes a well-known magic value to a known location + * in memory. Then we start the guest so that the firmware can boot and finally + * run the Forth code. + * The testing code here then can finally check whether the value has been + * successfully written into the guest memory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +#define MAGIC 0xcafec0de +#define ADDRESS 0x4000 + +static void check_guest_memory(QTestState *qts) +{ + uint32_t signature; + int i; + + /* Poll until code has run and modified memory. Wait at most 600 seconds */ + for (i = 0; i < 60000; ++i) { + signature = qtest_readl(qts, ADDRESS); + if (signature == MAGIC) { + break; + } + g_usleep(10000); + } + + g_assert_cmphex(signature, ==, MAGIC); +} + +static void test_machine(const void *machine) +{ + const char *extra_args = ""; + QTestState *qts; + + /* + * The pseries firmware boots much faster without the default + * devices, it also needs Spectre/Meltdown workarounds disabled to + * avoid warnings with TCG + */ + if (strcmp(machine, "pseries") == 0) { + extra_args = "-nodefaults" + " -machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken"; + } + + qts = qtest_initf("-M %s -accel tcg %s -prom-env 'use-nvramrc?=true' " + "-prom-env 'nvramrc=%x %x l!' ", (const char *)machine, + extra_args, MAGIC, ADDRESS); + check_guest_memory(qts); + qtest_quit(qts); +} + +static void add_tests(const char *machines[]) +{ + int i; + char *name; + + for (i = 0; machines[i] != NULL; i++) { + name = g_strdup_printf("prom-env/%s", machines[i]); + qtest_add_data_func(name, machines[i], test_machine); + g_free(name); + } +} + +int main(int argc, char *argv[]) +{ + const char *sparc_machines[] = { "SPARCbook", "Voyager", "SS-20", NULL }; + const char *sparc64_machines[] = { "sun4u", NULL }; + const char *ppc_machines[] = { "mac99", "g3beige", NULL }; + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (!strcmp(arch, "ppc")) { + add_tests(ppc_machines); + } else if (!strcmp(arch, "ppc64")) { + add_tests(ppc_machines); + if (g_test_slow()) { + qtest_add_data_func("prom-env/pseries", "pseries", test_machine); + } + } else if (!strcmp(arch, "sparc")) { + add_tests(sparc_machines); + } else if (!strcmp(arch, "sparc64")) { + add_tests(sparc64_machines); + } else { + g_assert_not_reached(); + } + + return g_test_run(); +} diff --git a/tests/qtest/pvpanic-test.c b/tests/qtest/pvpanic-test.c new file mode 100644 index 0000000000..ff9176adf3 --- /dev/null +++ b/tests/qtest/pvpanic-test.c @@ -0,0 +1,49 @@ +/* + * QTest testcase for PV Panic + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" + +static void test_panic(void) +{ + uint8_t val; + QDict *response, *data; + QTestState *qts; + + qts = qtest_init("-device pvpanic"); + + val = qtest_inb(qts, 0x505); + g_assert_cmpuint(val, ==, 1); + + qtest_outb(qts, 0x505, 0x1); + + response = qtest_qmp_receive(qts); + g_assert(qdict_haskey(response, "event")); + g_assert_cmpstr(qdict_get_str(response, "event"), ==, "GUEST_PANICKED"); + g_assert(qdict_haskey(response, "data")); + data = qdict_get_qdict(response, "data"); + g_assert(qdict_haskey(data, "action")); + g_assert_cmpstr(qdict_get_str(data, "action"), ==, "pause"); + qobject_unref(response); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/pvpanic/panic", test_panic); + + ret = g_test_run(); + + return ret; +} diff --git a/tests/qtest/pxe-test.c b/tests/qtest/pxe-test.c new file mode 100644 index 0000000000..f68d0aadbb --- /dev/null +++ b/tests/qtest/pxe-test.c @@ -0,0 +1,152 @@ +/* + * PXE test cases. + * + * Copyright (c) 2016, 2017 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin , + * Victor Kaplansky + * Thomas Huth + * + * 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 +#include "qemu-common.h" +#include "libqtest.h" +#include "boot-sector.h" + +#define NETNAME "net0" + +static char disk[] = "tests/pxe-test-disk-XXXXXX"; + +typedef struct testdef { + const char *machine; /* Machine type */ + const char *model; /* NIC device model */ + const char *extra; /* Any additional parameters */ +} testdef_t; + +static testdef_t x86_tests[] = { + { "pc", "e1000" }, + { "pc", "virtio-net-pci" }, + { "q35", "e1000e" }, + { "q35", "virtio-net-pci", }, + { NULL }, +}; + +static testdef_t x86_tests_slow[] = { + { "pc", "ne2k_pci", }, + { "pc", "i82550", }, + { "pc", "rtl8139" }, + { "pc", "vmxnet3" }, + { NULL }, +}; + +static testdef_t ppc64_tests[] = { + { "pseries", "spapr-vlan", + "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" }, + { "pseries", "virtio-net-pci", + "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" }, + { NULL }, +}; + +static testdef_t ppc64_tests_slow[] = { + { "pseries", "e1000", + "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" }, + { NULL }, +}; + +static testdef_t s390x_tests[] = { + { "s390-ccw-virtio", "virtio-net-ccw" }, + { NULL }, +}; + +static void test_pxe_one(const testdef_t *test, bool ipv6) +{ + QTestState *qts; + char *args; + const char *extra = test->extra; + + if (!extra) { + extra = ""; + } + + args = g_strdup_printf( + "-accel kvm -accel tcg -machine %s -nodefaults -boot order=n " + "-netdev user,id=" NETNAME ",tftp=./,bootfile=%s,ipv4=%s,ipv6=%s " + "-device %s,bootindex=1,netdev=" NETNAME " %s", + test->machine, disk, ipv6 ? "off" : "on", ipv6 ? "on" : "off", + test->model, extra); + + qts = qtest_init(args); + boot_sector_test(qts); + qtest_quit(qts); + g_free(args); +} + +static void test_pxe_ipv4(gconstpointer data) +{ + const testdef_t *test = data; + + test_pxe_one(test, false); +} + +static void test_pxe_ipv6(gconstpointer data) +{ + const testdef_t *test = data; + + test_pxe_one(test, true); +} + +static void test_batch(const testdef_t *tests, bool ipv6) +{ + int i; + + for (i = 0; tests[i].machine; i++) { + const testdef_t *test = &tests[i]; + char *testname; + + testname = g_strdup_printf("pxe/ipv4/%s/%s", + test->machine, test->model); + qtest_add_data_func(testname, test, test_pxe_ipv4); + g_free(testname); + + if (ipv6) { + testname = g_strdup_printf("pxe/ipv6/%s/%s", + test->machine, test->model); + qtest_add_data_func(testname, test, test_pxe_ipv6); + g_free(testname); + } + } +} + +int main(int argc, char *argv[]) +{ + int ret; + const char *arch = qtest_get_arch(); + + ret = boot_sector_init(disk); + if(ret) + return ret; + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + test_batch(x86_tests, false); + if (g_test_slow()) { + test_batch(x86_tests_slow, false); + } + } else if (strcmp(arch, "ppc64") == 0) { + test_batch(ppc64_tests, g_test_slow()); + if (g_test_slow()) { + test_batch(ppc64_tests_slow, true); + } + } else if (g_str_equal(arch, "s390x")) { + test_batch(s390x_tests, g_test_slow()); + } + ret = g_test_run(); + boot_sector_cleanup(disk); + return ret; +} diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c new file mode 100644 index 0000000000..a68183d513 --- /dev/null +++ b/tests/qtest/q35-test.c @@ -0,0 +1,201 @@ +/* + * QTest testcase for Q35 northbridge + * + * Copyright (c) 2015 Red Hat, Inc. + * + * Author: Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/pci.h" +#include "libqos/pci-pc.h" +#include "hw/pci-host/q35.h" +#include "qapi/qmp/qdict.h" + +#define TSEG_SIZE_TEST_GUEST_RAM_MBYTES 128 + +/* @esmramc_tseg_sz: ESMRAMC.TSEG_SZ bitmask for selecting the requested TSEG + * size. Must be a subset of + * MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK. + * + * @extended_tseg_mbytes: Size of the extended TSEG. Only consulted if + * @esmramc_tseg_sz equals + * MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK precisely. + * + * @expected_tseg_mbytes: Expected guest-visible TSEG size in megabytes, + * matching @esmramc_tseg_sz and @extended_tseg_mbytes + * above. + */ +struct TsegSizeArgs { + uint8_t esmramc_tseg_sz; + uint16_t extended_tseg_mbytes; + uint16_t expected_tseg_mbytes; +}; +typedef struct TsegSizeArgs TsegSizeArgs; + +static const TsegSizeArgs tseg_1mb = { + .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB, + .extended_tseg_mbytes = 0, + .expected_tseg_mbytes = 1, +}; +static const TsegSizeArgs tseg_2mb = { + .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_2MB, + .extended_tseg_mbytes = 0, + .expected_tseg_mbytes = 2, +}; +static const TsegSizeArgs tseg_8mb = { + .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_8MB, + .extended_tseg_mbytes = 0, + .expected_tseg_mbytes = 8, +}; +static const TsegSizeArgs tseg_ext_16mb = { + .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK, + .extended_tseg_mbytes = 16, + .expected_tseg_mbytes = 16, +}; + +static void smram_set_bit(QPCIDevice *pcidev, uint8_t mask, bool enabled) +{ + uint8_t smram; + + smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); + if (enabled) { + smram |= mask; + } else { + smram &= ~mask; + } + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram); +} + +static bool smram_test_bit(QPCIDevice *pcidev, uint8_t mask) +{ + uint8_t smram; + + smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); + return smram & mask; +} + +static void test_smram_lock(void) +{ + QPCIBus *pcibus; + QPCIDevice *pcidev; + QDict *response; + QTestState *qts; + + qts = qtest_init("-M q35"); + + pcibus = qpci_new_pc(qts, NULL); + g_assert(pcibus != NULL); + + pcidev = qpci_device_find(pcibus, 0); + g_assert(pcidev != NULL); + + /* check open is settable */ + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true); + + /* lock, check open is cleared & not settable */ + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_LCK, true); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); + + /* reset */ + response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + /* check open is settable again */ + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); + smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); + g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true); + + g_free(pcidev); + qpci_free_pc(pcibus); + + qtest_quit(qts); +} + +static void test_tseg_size(const void *data) +{ + const TsegSizeArgs *args = data; + QPCIBus *pcibus; + QPCIDevice *pcidev; + uint8_t smram_val; + uint8_t esmramc_val; + uint32_t ram_offs; + QTestState *qts; + + if (args->esmramc_tseg_sz == MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) { + qts = qtest_initf("-M q35 -m %uM -global mch.extended-tseg-mbytes=%u", + TSEG_SIZE_TEST_GUEST_RAM_MBYTES, + args->extended_tseg_mbytes); + } else { + qts = qtest_initf("-M q35 -m %uM", TSEG_SIZE_TEST_GUEST_RAM_MBYTES); + } + + /* locate the DRAM controller */ + pcibus = qpci_new_pc(qts, NULL); + g_assert(pcibus != NULL); + pcidev = qpci_device_find(pcibus, 0); + g_assert(pcidev != NULL); + + /* Set TSEG size. Restrict TSEG visibility to SMM by setting T_EN. */ + esmramc_val = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_ESMRAMC); + esmramc_val &= ~MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK; + esmramc_val |= args->esmramc_tseg_sz; + esmramc_val |= MCH_HOST_BRIDGE_ESMRAMC_T_EN; + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_ESMRAMC, esmramc_val); + + /* Enable TSEG by setting G_SMRAME. Close TSEG by setting D_CLS. */ + smram_val = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); + smram_val &= ~(MCH_HOST_BRIDGE_SMRAM_D_OPEN | + MCH_HOST_BRIDGE_SMRAM_D_LCK); + smram_val |= (MCH_HOST_BRIDGE_SMRAM_D_CLS | + MCH_HOST_BRIDGE_SMRAM_G_SMRAME); + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram_val); + + /* lock TSEG */ + smram_val |= MCH_HOST_BRIDGE_SMRAM_D_LCK; + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram_val); + + /* Now check that the byte right before the TSEG is r/w, and that the first + * byte in the TSEG always reads as 0xff. + */ + ram_offs = (TSEG_SIZE_TEST_GUEST_RAM_MBYTES - args->expected_tseg_mbytes) * + 1024 * 1024 - 1; + g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 0); + qtest_writeb(qts, ram_offs, 1); + g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 1); + + ram_offs++; + g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 0xff); + qtest_writeb(qts, ram_offs, 1); + g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 0xff); + + g_free(pcidev); + qpci_free_pc(pcibus); + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/q35/smram/lock", test_smram_lock); + + qtest_add_data_func("/q35/tseg-size/1mb", &tseg_1mb, test_tseg_size); + qtest_add_data_func("/q35/tseg-size/2mb", &tseg_2mb, test_tseg_size); + qtest_add_data_func("/q35/tseg-size/8mb", &tseg_8mb, test_tseg_size); + qtest_add_data_func("/q35/tseg-size/ext/16mb", &tseg_ext_16mb, + test_tseg_size); + return g_test_run(); +} diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c new file mode 100644 index 0000000000..9f5228cd99 --- /dev/null +++ b/tests/qtest/qmp-cmd-test.c @@ -0,0 +1,234 @@ +/* + * QMP command test cases + * + * Copyright (c) 2017 Red Hat Inc. + * + * Authors: + * Markus Armbruster + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-introspect.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qobject-input-visitor.h" + +const char common_args[] = "-nodefaults -machine none"; + +/* Query smoke tests */ + +static int query_error_class(const char *cmd) +{ + static struct { + const char *cmd; + int err_class; + } fails[] = { + /* Success depends on build configuration: */ +#ifndef CONFIG_SPICE + { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, +#endif +#ifndef CONFIG_VNC + { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, + { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, +#endif +#ifndef CONFIG_REPLICATION + { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, +#endif + /* Likewise, and require special QEMU command-line arguments: */ + { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, + { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, + { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, + { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, + { NULL, -1 } + }; + int i; + + for (i = 0; fails[i].cmd; i++) { + if (!strcmp(cmd, fails[i].cmd)) { + return fails[i].err_class; + } + } + return -1; +} + +static void test_query(const void *data) +{ + const char *cmd = data; + int expected_error_class = query_error_class(cmd); + QDict *resp, *error; + const char *error_class; + QTestState *qts; + + qts = qtest_init(common_args); + + resp = qtest_qmp(qts, "{ 'execute': %s }", cmd); + error = qdict_get_qdict(resp, "error"); + error_class = error ? qdict_get_str(error, "class") : NULL; + + if (expected_error_class < 0) { + g_assert(qdict_haskey(resp, "return")); + } else { + g_assert(error); + g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class, + -1, &error_abort), + ==, expected_error_class); + } + qobject_unref(resp); + + qtest_quit(qts); +} + +static bool query_is_blacklisted(const char *cmd) +{ + const char *blacklist[] = { + /* Not actually queries: */ + "add-fd", + /* Success depends on target arch: */ + "query-cpu-definitions", /* arm, i386, ppc, s390x */ + "query-gic-capabilities", /* arm */ + /* Success depends on target-specific build configuration: */ + "query-pci", /* CONFIG_PCI */ + /* Success depends on launching SEV guest */ + "query-sev-launch-measure", + /* Success depends on Host or Hypervisor SEV support */ + "query-sev", + "query-sev-capabilities", + NULL + }; + int i; + + for (i = 0; blacklist[i]; i++) { + if (!strcmp(cmd, blacklist[i])) { + return true; + } + } + return false; +} + +typedef struct { + SchemaInfoList *list; + GHashTable *hash; +} QmpSchema; + +static void qmp_schema_init(QmpSchema *schema) +{ + QDict *resp; + Visitor *qiv; + SchemaInfoList *tail; + QTestState *qts; + + qts = qtest_init(common_args); + + resp = qtest_qmp(qts, "{ 'execute': 'query-qmp-schema' }"); + + qiv = qobject_input_visitor_new(qdict_get(resp, "return")); + visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); + visit_free(qiv); + + qobject_unref(resp); + qtest_quit(qts); + + schema->hash = g_hash_table_new(g_str_hash, g_str_equal); + + /* Build @schema: hash table mapping entity name to SchemaInfo */ + for (tail = schema->list; tail; tail = tail->next) { + g_hash_table_insert(schema->hash, tail->value->name, tail->value); + } +} + +static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) +{ + return g_hash_table_lookup(schema->hash, name); +} + +static void qmp_schema_cleanup(QmpSchema *schema) +{ + qapi_free_SchemaInfoList(schema->list); + g_hash_table_destroy(schema->hash); +} + +static bool object_type_has_mandatory_members(SchemaInfo *type) +{ + SchemaInfoObjectMemberList *tail; + + g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); + + for (tail = type->u.object.members; tail; tail = tail->next) { + if (!tail->value->has_q_default) { + return true; + } + } + + return false; +} + +static void add_query_tests(QmpSchema *schema) +{ + SchemaInfoList *tail; + SchemaInfo *si, *arg_type, *ret_type; + char *test_name; + + /* Test the query-like commands */ + for (tail = schema->list; tail; tail = tail->next) { + si = tail->value; + if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { + continue; + } + + if (query_is_blacklisted(si->name)) { + continue; + } + + arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); + if (object_type_has_mandatory_members(arg_type)) { + continue; + } + + ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); + if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT + && !ret_type->u.object.members) { + continue; + } + + test_name = g_strdup_printf("qmp/%s", si->name); + qtest_add_data_func(test_name, si->name, test_query); + g_free(test_name); + } +} + +static void test_object_add_without_props(void) +{ + QTestState *qts; + QDict *resp; + + qts = qtest_init(common_args); + resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':" + " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }"); + g_assert_nonnull(resp); + qmp_assert_error_class(resp, "GenericError"); + qtest_quit(qts); +} + +int main(int argc, char *argv[]) +{ + QmpSchema schema; + int ret; + + g_test_init(&argc, &argv, NULL); + + qmp_schema_init(&schema); + add_query_tests(&schema); + + qtest_add_func("qmp/object-add-without-props", + test_object_add_without_props); + /* TODO: add coverage of generic object-add failure modes */ + + ret = g_test_run(); + + qmp_schema_cleanup(&schema); + return ret; +} diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c new file mode 100644 index 0000000000..1b0eb69832 --- /dev/null +++ b/tests/qtest/qmp-test.c @@ -0,0 +1,344 @@ +/* + * QMP protocol test cases + * + * Copyright (c) 2017-2018 Red Hat Inc. + * + * Authors: + * Markus Armbruster + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-misc.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qmp/qstring.h" + +const char common_args[] = "-nodefaults -machine none"; + +static void test_version(QObject *version) +{ + Visitor *v; + VersionInfo *vinfo; + + g_assert(version); + v = qobject_input_visitor_new(version); + visit_type_VersionInfo(v, "version", &vinfo, &error_abort); + qapi_free_VersionInfo(vinfo); + visit_free(v); +} + +static void assert_recovered(QTestState *qts) +{ + QDict *resp; + + resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd' }"); + qmp_assert_error_class(resp, "CommandNotFound"); +} + +static void test_malformed(QTestState *qts) +{ + QDict *resp; + + /* syntax error */ + qtest_qmp_send_raw(qts, "{]\n"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* lexical error: impossible byte outside string */ + qtest_qmp_send_raw(qts, "{\xFF"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* lexical error: funny control character outside string */ + qtest_qmp_send_raw(qts, "{\x01"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* lexical error: impossible byte in string */ + qtest_qmp_send_raw(qts, "{'bad \xFF"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* lexical error: control character in string */ + qtest_qmp_send_raw(qts, "{'execute': 'nonexistent', 'id':'\n"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* lexical error: interpolation */ + qtest_qmp_send_raw(qts, "%%p"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* Not even a dictionary */ + resp = qtest_qmp(qts, "null"); + qmp_assert_error_class(resp, "GenericError"); + + /* No "execute" key */ + resp = qtest_qmp(qts, "{}"); + qmp_assert_error_class(resp, "GenericError"); + + /* "execute" isn't a string */ + resp = qtest_qmp(qts, "{ 'execute': true }"); + qmp_assert_error_class(resp, "GenericError"); + + /* "arguments" isn't a dictionary */ + resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }"); + qmp_assert_error_class(resp, "GenericError"); + + /* extra key */ + resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }"); + qmp_assert_error_class(resp, "GenericError"); +} + +static void test_qmp_protocol(void) +{ + QDict *resp, *q, *ret; + QList *capabilities; + QTestState *qts; + + qts = qtest_init_without_qmp_handshake(common_args); + + /* Test greeting */ + resp = qtest_qmp_receive(qts); + q = qdict_get_qdict(resp, "QMP"); + g_assert(q); + test_version(qdict_get(q, "version")); + capabilities = qdict_get_qlist(q, "capabilities"); + g_assert(capabilities); + qobject_unref(resp); + + /* Test valid command before handshake */ + resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); + qmp_assert_error_class(resp, "CommandNotFound"); + + /* Test malformed commands before handshake */ + test_malformed(qts); + + /* Test handshake */ + resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); + ret = qdict_get_qdict(resp, "return"); + g_assert(ret && !qdict_size(ret)); + qobject_unref(resp); + + /* Test repeated handshake */ + resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); + qmp_assert_error_class(resp, "CommandNotFound"); + + /* Test valid command */ + resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); + test_version(qdict_get(resp, "return")); + qobject_unref(resp); + + /* Test malformed commands */ + test_malformed(qts); + + /* Test 'id' */ + resp = qtest_qmp(qts, "{ 'execute': 'query-name', 'id': 'cookie#1' }"); + ret = qdict_get_qdict(resp, "return"); + g_assert(ret); + g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1"); + qobject_unref(resp); + + /* Test command failure with 'id' */ + resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }"); + g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); + qmp_assert_error_class(resp, "GenericError"); + + qtest_quit(qts); +} + +/* Out-of-band tests */ + +char tmpdir[] = "/tmp/qmp-test-XXXXXX"; +char *fifo_name; + +static void setup_blocking_cmd(void) +{ + if (!mkdtemp(tmpdir)) { + g_error("mkdtemp: %s", strerror(errno)); + } + fifo_name = g_strdup_printf("%s/fifo", tmpdir); + if (mkfifo(fifo_name, 0666)) { + g_error("mkfifo: %s", strerror(errno)); + } +} + +static void cleanup_blocking_cmd(void) +{ + unlink(fifo_name); + rmdir(tmpdir); +} + +static void send_cmd_that_blocks(QTestState *s, const char *id) +{ + qtest_qmp_send(s, "{ 'execute': 'blockdev-add', 'id': %s," + " 'arguments': {" + " 'driver': 'blkdebug', 'node-name': %s," + " 'config': %s," + " 'image': { 'driver': 'null-co', 'read-zeroes': true } } }", + id, id, fifo_name); +} + +static void unblock_blocked_cmd(void) +{ + int fd = open(fifo_name, O_WRONLY); + g_assert(fd >= 0); + close(fd); +} + +static void send_oob_cmd_that_fails(QTestState *s, const char *id) +{ + qtest_qmp_send(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id); +} + +static void recv_cmd_id(QTestState *s, const char *id) +{ + QDict *resp = qtest_qmp_receive(s); + + g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id); + qobject_unref(resp); +} + +static void test_qmp_oob(void) +{ + QTestState *qts; + QDict *resp, *q; + const QListEntry *entry; + QList *capabilities; + QString *qstr; + + qts = qtest_init_without_qmp_handshake(common_args); + + /* Check the greeting message. */ + resp = qtest_qmp_receive(qts); + q = qdict_get_qdict(resp, "QMP"); + g_assert(q); + capabilities = qdict_get_qlist(q, "capabilities"); + g_assert(capabilities && !qlist_empty(capabilities)); + entry = qlist_first(capabilities); + g_assert(entry); + qstr = qobject_to(QString, entry->value); + g_assert(qstr); + g_assert_cmpstr(qstring_get_str(qstr), ==, "oob"); + qobject_unref(resp); + + /* Try a fake capability, it should fail. */ + resp = qtest_qmp(qts, + "{ 'execute': 'qmp_capabilities', " + " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }"); + g_assert(qdict_haskey(resp, "error")); + qobject_unref(resp); + + /* Now, enable OOB in current QMP session, it should succeed. */ + resp = qtest_qmp(qts, + "{ 'execute': 'qmp_capabilities', " + " 'arguments': { 'enable': [ 'oob' ] } }"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + /* + * Try any command that does not support OOB but with OOB flag. We + * should get failure. + */ + resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }"); + g_assert(qdict_haskey(resp, "error")); + qobject_unref(resp); + + /* OOB command overtakes slow in-band command */ + setup_blocking_cmd(); + send_cmd_that_blocks(qts, "ib-blocks-1"); + qtest_qmp_send(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }"); + send_oob_cmd_that_fails(qts, "oob-1"); + recv_cmd_id(qts, "oob-1"); + unblock_blocked_cmd(); + recv_cmd_id(qts, "ib-blocks-1"); + recv_cmd_id(qts, "ib-quick-1"); + + /* Even malformed in-band command fails in-band */ + send_cmd_that_blocks(qts, "blocks-2"); + qtest_qmp_send(qts, "{ 'id': 'err-2' }"); + unblock_blocked_cmd(); + recv_cmd_id(qts, "blocks-2"); + recv_cmd_id(qts, "err-2"); + cleanup_blocking_cmd(); + + qtest_quit(qts); +} + +/* Preconfig tests */ + +static void test_qmp_preconfig(void) +{ + QDict *rsp, *ret; + QTestState *qs = qtest_initf("%s --preconfig", common_args); + + /* preconfig state */ + /* enabled commands, no error expected */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-commands' }"))); + + /* forbidden commands, expected error */ + g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); + + /* check that query-status returns preconfig state */ + rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); + ret = qdict_get_qdict(rsp, "return"); + g_assert(ret); + g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "preconfig"); + qobject_unref(rsp); + + /* exit preconfig state */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + /* check that query-status returns running state */ + rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); + ret = qdict_get_qdict(rsp, "return"); + g_assert(ret); + g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running"); + qobject_unref(rsp); + + /* check that x-exit-preconfig returns error after exiting preconfig */ + g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); + + /* enabled commands, no error expected */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); + + qtest_quit(qs); +} + +static void test_qmp_missing_any_arg(void) +{ + QTestState *qts; + QDict *resp; + + qts = qtest_init(common_args); + resp = qtest_qmp(qts, "{'execute': 'qom-set', 'arguments':" + " { 'path': '/machine', 'property': 'rtc-time' } }"); + g_assert_nonnull(resp); + qmp_assert_error_class(resp, "GenericError"); + qtest_quit(qts); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("qmp/protocol", test_qmp_protocol); + qtest_add_func("qmp/oob", test_qmp_oob); + qtest_add_func("qmp/preconfig", test_qmp_preconfig); + qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg); + + return g_test_run(); +} diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c new file mode 100644 index 0000000000..4f94cc678c --- /dev/null +++ b/tests/qtest/qom-test.c @@ -0,0 +1,127 @@ +/* + * QTest testcase for QOM + * + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qemu/cutils.h" +#include "libqtest.h" + +static const char *blacklist_x86[] = { + "xenfv", "xenpv", NULL +}; + +static const struct { + const char *arch; + const char **machine; +} blacklists[] = { + { "i386", blacklist_x86 }, + { "x86_64", blacklist_x86 }, +}; + +static bool is_blacklisted(const char *arch, const char *mach) +{ + int i; + const char **p; + + for (i = 0; i < ARRAY_SIZE(blacklists); i++) { + if (!strcmp(blacklists[i].arch, arch)) { + for (p = blacklists[i].machine; *p; p++) { + if (!strcmp(*p, mach)) { + return true; + } + } + } + } + return false; +} + +static void test_properties(QTestState *qts, const char *path, bool recurse) +{ + char *child_path; + QDict *response, *tuple, *tmp; + QList *list; + QListEntry *entry; + + g_test_message("Obtaining properties of %s", path); + response = qtest_qmp(qts, "{ 'execute': 'qom-list'," + " 'arguments': { 'path': %s } }", path); + g_assert(response); + + if (!recurse) { + qobject_unref(response); + return; + } + + g_assert(qdict_haskey(response, "return")); + list = qobject_to(QList, qdict_get(response, "return")); + QLIST_FOREACH_ENTRY(list, entry) { + tuple = qobject_to(QDict, qlist_entry_obj(entry)); + bool is_child = strstart(qdict_get_str(tuple, "type"), "child<", NULL); + bool is_link = strstart(qdict_get_str(tuple, "type"), "link<", NULL); + + if (is_child || is_link) { + child_path = g_strdup_printf("%s/%s", + path, qdict_get_str(tuple, "name")); + test_properties(qts, child_path, is_child); + g_free(child_path); + } else { + const char *prop = qdict_get_str(tuple, "name"); + g_test_message("Testing property %s.%s", path, prop); + tmp = qtest_qmp(qts, + "{ 'execute': 'qom-get'," + " 'arguments': { 'path': %s, 'property': %s } }", + path, prop); + /* qom-get may fail but should not, e.g., segfault. */ + g_assert(tmp); + qobject_unref(tmp); + } + } + qobject_unref(response); +} + +static void test_machine(gconstpointer data) +{ + const char *machine = data; + QDict *response; + QTestState *qts; + + qts = qtest_initf("-machine %s", machine); + + test_properties(qts, "/machine", true); + + response = qtest_qmp(qts, "{ 'execute': 'quit' }"); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); + + qtest_quit(qts); + g_free((void *)machine); +} + +static void add_machine_test_case(const char *mname) +{ + const char *arch = qtest_get_arch(); + + if (!is_blacklisted(arch, mname)) { + char *path = g_strdup_printf("qom/%s", mname); + qtest_add_data_func(path, g_strdup(mname), test_machine); + g_free(path); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_cb_for_every_machine(add_machine_test_case, g_test_quick()); + + return g_test_run(); +} diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c new file mode 100644 index 0000000000..fd70d73ea5 --- /dev/null +++ b/tests/qtest/qos-test.c @@ -0,0 +1,449 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * 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 + */ + +#include "qemu/osdep.h" +#include +#include "libqtest-single.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qstring.h" +#include "qemu/module.h" +#include "qapi/qmp/qlist.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "libqos/qgraph_internal.h" + +static char *old_path; + +static void apply_to_node(const char *name, bool is_machine, bool is_abstract) +{ + char *machine_name = NULL; + if (is_machine) { + const char *arch = qtest_get_arch(); + machine_name = g_strconcat(arch, "/", name, NULL); + name = machine_name; + } + qos_graph_node_set_availability(name, true); + if (is_abstract) { + qos_delete_cmd_line(name); + } + g_free(machine_name); +} + +/** + * apply_to_qlist(): using QMP queries QEMU for a list of + * machines and devices available, and sets the respective node + * as true. If a node is found, also all its produced and contained + * child are marked available. + * + * See qos_graph_node_set_availability() for more info + */ +static void apply_to_qlist(QList *list, bool is_machine) +{ + const QListEntry *p; + const char *name; + bool abstract; + QDict *minfo; + QObject *qobj; + QString *qstr; + QBool *qbool; + + for (p = qlist_first(list); p; p = qlist_next(p)) { + minfo = qobject_to(QDict, qlist_entry_obj(p)); + qobj = qdict_get(minfo, "name"); + qstr = qobject_to(QString, qobj); + name = qstring_get_str(qstr); + + qobj = qdict_get(minfo, "abstract"); + if (qobj) { + qbool = qobject_to(QBool, qobj); + abstract = qbool_get_bool(qbool); + } else { + abstract = false; + } + + apply_to_node(name, is_machine, abstract); + qobj = qdict_get(minfo, "alias"); + if (qobj) { + qstr = qobject_to(QString, qobj); + name = qstring_get_str(qstr); + apply_to_node(name, is_machine, abstract); + } + } +} + +/** + * qos_set_machines_devices_available(): sets availability of qgraph + * machines and devices. + * + * This function firstly starts QEMU with "-machine none" option, + * and then executes the QMP protocol asking for the list of devices + * and machines available. + * + * for each of these items, it looks up the corresponding qgraph node, + * setting it as available. The list currently returns all devices that + * are either machines or QEDGE_CONSUMED_BY other nodes. + * Therefore, in order to mark all other nodes, it recursively sets + * all its QEDGE_CONTAINS and QEDGE_PRODUCES child as available too. + */ +static void qos_set_machines_devices_available(void) +{ + QDict *response; + QDict *args = qdict_new(); + QList *list; + + qtest_start("-machine none"); + response = qmp("{ 'execute': 'query-machines' }"); + list = qdict_get_qlist(response, "return"); + + apply_to_qlist(list, true); + + qobject_unref(response); + + qdict_put_bool(args, "abstract", true); + qdict_put_str(args, "implements", "device"); + + response = qmp("{'execute': 'qom-list-types'," + " 'arguments': %p }", args); + g_assert(qdict_haskey(response, "return")); + list = qdict_get_qlist(response, "return"); + + apply_to_qlist(list, false); + + qtest_end(); + qobject_unref(response); +} + +static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj) +{ + return obj->get_driver(obj, "memory"); +} + +static void restart_qemu_or_continue(char *path) +{ + /* compares the current command line with the + * one previously executed: if they are the same, + * don't restart QEMU, if they differ, stop previous + * QEMU subprocess (if active) and start over with + * the new command line + */ + if (g_strcmp0(old_path, path)) { + qtest_end(); + qos_invalidate_command_line(); + old_path = g_strdup(path); + qtest_start(path); + } else { /* if cmd line is the same, reset the guest */ + qobject_unref(qmp("{ 'execute': 'system_reset' }")); + qmp_eventwait("RESET"); + } +} + +void qos_invalidate_command_line(void) +{ + g_free(old_path); + old_path = NULL; +} + +/** + * allocate_objects(): given an array of nodes @arg, + * walks the path invoking all constructors and + * passing the corresponding parameter in order to + * continue the objects allocation. + * Once the test is reached, return the object it consumes. + * + * Since the machine and QEDGE_CONSUMED_BY nodes allocate + * memory in the constructor, g_test_queue_destroy is used so + * that after execution they can be safely free'd. (The test's + * ->before callback is also welcome to use g_test_queue_destroy). + * + * Note: as specified in walk_path() too, @arg is an array of + * char *, where arg[0] is a pointer to the command line + * string that will be used to properly start QEMU when executing + * the test, and the remaining elements represent the actual objects + * that will be allocated. + */ +static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc) +{ + int current = 0; + QGuestAllocator *alloc; + QOSGraphObject *parent = NULL; + QOSGraphEdge *edge; + QOSGraphNode *node; + void *edge_arg; + void *obj; + + node = qos_graph_get_node(path[current]); + g_assert(node->type == QNODE_MACHINE); + + obj = qos_machine_new(node, qts); + qos_object_queue_destroy(obj); + + alloc = get_machine_allocator(obj); + if (p_alloc) { + *p_alloc = alloc; + } + + for (;;) { + if (node->type != QNODE_INTERFACE) { + qos_object_start_hw(obj); + parent = obj; + } + + /* follow edge and get object for next node constructor */ + current++; + edge = qos_graph_get_edge(path[current - 1], path[current]); + node = qos_graph_get_node(path[current]); + + if (node->type == QNODE_TEST) { + g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY); + return obj; + } + + switch (qos_graph_edge_get_type(edge)) { + case QEDGE_PRODUCES: + obj = parent->get_driver(parent, path[current]); + break; + + case QEDGE_CONSUMED_BY: + edge_arg = qos_graph_edge_get_arg(edge); + obj = qos_driver_new(node, obj, alloc, edge_arg); + qos_object_queue_destroy(obj); + break; + + case QEDGE_CONTAINS: + obj = parent->get_device(parent, path[current]); + break; + } + } +} + +/* The argument to run_one_test, which is the test function that is registered + * with GTest, is a vector of strings. The first item is the initial command + * line (before it is modified by the test's "before" function), the remaining + * items are node names forming the path to the test node. + */ +static char **current_path; + +const char *qos_get_current_command_line(void) +{ + return current_path[0]; +} + +void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc) +{ + return allocate_objects(qts, current_path + 1, p_alloc); +} + +/** + * run_one_test(): given an array of nodes @arg, + * walks the path invoking all constructors and + * passing the corresponding parameter in order to + * continue the objects allocation. + * Once the test is reached, its function is executed. + * + * Since the machine and QEDGE_CONSUMED_BY nodes allocate + * memory in the constructor, g_test_queue_destroy is used so + * that after execution they can be safely free'd. The test's + * ->before callback is also welcome to use g_test_queue_destroy. + * + * Note: as specified in walk_path() too, @arg is an array of + * char *, where arg[0] is a pointer to the command line + * string that will be used to properly start QEMU when executing + * the test, and the remaining elements represent the actual objects + * that will be allocated. + * + * The order of execution is the following: + * 1) @before test function as defined in the given QOSGraphTestOptions + * 2) start QEMU + * 3) call all nodes constructor and get_driver/get_device depending on edge, + * start the hardware (*_device_enable functions) + * 4) start test + */ +static void run_one_test(const void *arg) +{ + QOSGraphNode *test_node; + QGuestAllocator *alloc = NULL; + void *obj; + char **path = (char **) arg; + GString *cmd_line = g_string_new(path[0]); + void *test_arg; + + /* Before test */ + current_path = path; + test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]); + test_arg = test_node->u.test.arg; + if (test_node->u.test.before) { + test_arg = test_node->u.test.before(cmd_line, test_arg); + } + + restart_qemu_or_continue(cmd_line->str); + g_string_free(cmd_line, true); + + obj = qos_allocate_objects(global_qtest, &alloc); + test_node->u.test.function(obj, test_arg, alloc); +} + +static void subprocess_run_one_test(const void *arg) +{ + const gchar *path = arg; + g_test_trap_subprocess(path, 0, 0); + g_test_trap_assert_passed(); +} + +/* + * in this function, 2 path will be built: + * path_str, a one-string path (ex "pc/i440FX-pcihost/...") + * path_vec, a string-array path (ex [0] = "pc", [1] = "i440FX-pcihost"). + * + * path_str will be only used to build the test name, and won't need the + * architecture name at beginning, since it will be added by qtest_add_func(). + * + * path_vec is used to allocate all constructors of the path nodes. + * Each name in this array except position 0 must correspond to a valid + * QOSGraphNode name. + * Position 0 is special, initially contains just the name of + * the node, (ex for "x86_64/pc" it will be "pc"), used to build the test + * path (see below). After it will contain the command line used to start + * qemu with all required devices. + * + * Note that the machine node name must be with format / + * (ex "x86_64/pc"), because it will identify the node "x86_64/pc" + * and start QEMU with "-M pc". For this reason, + * when building path_str, path_vec + * initially contains the at position 0 ("pc"), + * and the node name at position 1 (/) + * ("x86_64/pc"), followed by the rest of the nodes. + */ +static void walk_path(QOSGraphNode *orig_path, int len) +{ + QOSGraphNode *path; + QOSGraphEdge *edge; + + /* etype set to QEDGE_CONSUMED_BY so that machine can add to the command line */ + QOSEdgeType etype = QEDGE_CONSUMED_BY; + + /* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */ + char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2)); + int path_vec_size = 0; + + char *after_cmd, *before_cmd, *after_device; + GString *after_device_str = g_string_new(""); + char *node_name = orig_path->name, *path_str; + + GString *cmd_line = g_string_new(""); + GString *cmd_line2 = g_string_new(""); + + path = qos_graph_get_node(node_name); /* root */ + node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */ + + path_vec[path_vec_size++] = node_name; + path_vec[path_vec_size++] = qos_get_machine_type(node_name); + + for (;;) { + path = qos_graph_get_node(node_name); + if (!path->path_edge) { + break; + } + + node_name = qos_graph_edge_get_dest(path->path_edge); + + /* append node command line + previous edge command line */ + if (path->command_line && etype == QEDGE_CONSUMED_BY) { + g_string_append(cmd_line, path->command_line); + g_string_append(cmd_line, after_device_str->str); + g_string_truncate(after_device_str, 0); + } + + path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge); + /* detect if edge has command line args */ + after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge); + after_device = qos_graph_edge_get_extra_device_opts(path->path_edge); + before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge); + edge = qos_graph_get_edge(path->name, node_name); + etype = qos_graph_edge_get_type(edge); + + if (before_cmd) { + g_string_append(cmd_line, before_cmd); + } + if (after_cmd) { + g_string_append(cmd_line2, after_cmd); + } + if (after_device) { + g_string_append(after_device_str, after_device); + } + } + + path_vec[path_vec_size++] = NULL; + g_string_append(cmd_line, after_device_str->str); + g_string_free(after_device_str, true); + + g_string_append(cmd_line, cmd_line2->str); + g_string_free(cmd_line2, true); + + /* here position 0 has /, position 1 has . + * The path must not have the , qtest_add_data_func adds it. + */ + path_str = g_strjoinv("/", path_vec + 1); + + /* put arch/machine in position 1 so run_one_test can do its work + * and add the command line at position 0. + */ + path_vec[1] = path_vec[0]; + path_vec[0] = g_string_free(cmd_line, false); + + if (path->u.test.subprocess) { + gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess", + qtest_get_arch(), path_str); + qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test); + g_test_add_data_func(subprocess_path, path_vec, run_one_test); + } else { + qtest_add_data_func(path_str, path_vec, run_one_test); + } + + g_free(path_str); +} + + + +/** + * main(): heart of the qgraph framework. + * + * - Initializes the glib test framework + * - Creates the graph by invoking the various _init constructors + * - Starts QEMU to mark the available devices + * - Walks the graph, and each path is added to + * the glib test framework (walk_path) + * - Runs the tests, calling allocate_object() and allocating the + * machine/drivers/test objects + * - Cleans up everything + */ +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qos_graph_init(); + module_call_init(MODULE_INIT_QOM); + module_call_init(MODULE_INIT_LIBQOS); + qos_set_machines_devices_available(); + + qos_graph_foreach_test_path(walk_path); + g_test_run(); + qtest_end(); + qos_graph_destroy(); + g_free(old_path); + return 0; +} diff --git a/tests/qtest/rtas-test.c b/tests/qtest/rtas-test.c new file mode 100644 index 0000000000..167b42db38 --- /dev/null +++ b/tests/qtest/rtas-test.c @@ -0,0 +1,40 @@ +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "libqtest.h" + +#include "libqos/libqos-spapr.h" +#include "libqos/rtas.h" + +static void test_rtas_get_time_of_day(void) +{ + QOSState *qs; + struct tm tm; + uint32_t ns; + uint64_t ret; + time_t t1, t2; + + qs = qtest_spapr_boot("-machine pseries"); + + t1 = time(NULL); + ret = qrtas_get_time_of_day(qs->qts, &qs->alloc, &tm, &ns); + g_assert_cmpint(ret, ==, 0); + t2 = mktimegm(&tm); + g_assert(t2 - t1 < 5); /* 5 sec max to run the test */ + + qtest_shutdown(qs); +} + +int main(int argc, char *argv[]) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "ppc64")) { + g_printerr("RTAS requires ppc64-softmmu/qemu-system-ppc64\n"); + exit(EXIT_FAILURE); + } + qtest_add_func("rtas/get-time-of-day", test_rtas_get_time_of_day); + + return g_test_run(); +} diff --git a/tests/qtest/rtc-test.c b/tests/qtest/rtc-test.c new file mode 100644 index 0000000000..c7af34f6b1 --- /dev/null +++ b/tests/qtest/rtc-test.c @@ -0,0 +1,720 @@ +/* + * QTest testcase for the MC146818 real-time clock + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "libqtest-single.h" +#include "qemu/timer.h" +#include "hw/rtc/mc146818rtc.h" +#include "hw/rtc/mc146818rtc_regs.h" + +#define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768) + +static uint8_t base = 0x70; + +static int bcd2dec(int value) +{ + return (((value >> 4) & 0x0F) * 10) + (value & 0x0F); +} + +static uint8_t cmos_read(uint8_t reg) +{ + outb(base + 0, reg); + return inb(base + 1); +} + +static void cmos_write(uint8_t reg, uint8_t val) +{ + outb(base + 0, reg); + outb(base + 1, val); +} + +static int tm_cmp(struct tm *lhs, struct tm *rhs) +{ + time_t a, b; + struct tm d1, d2; + + memcpy(&d1, lhs, sizeof(d1)); + memcpy(&d2, rhs, sizeof(d2)); + + a = mktime(&d1); + b = mktime(&d2); + + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } + + return 0; +} + +#if 0 +static void print_tm(struct tm *tm) +{ + printf("%04d-%02d-%02d %02d:%02d:%02d\n", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff); +} +#endif + +static void cmos_get_date_time(struct tm *date) +{ + int base_year = 2000, hour_offset; + int sec, min, hour, mday, mon, year; + time_t ts; + struct tm dummy; + + sec = cmos_read(RTC_SECONDS); + min = cmos_read(RTC_MINUTES); + hour = cmos_read(RTC_HOURS); + mday = cmos_read(RTC_DAY_OF_MONTH); + mon = cmos_read(RTC_MONTH); + year = cmos_read(RTC_YEAR); + + if ((cmos_read(RTC_REG_B) & REG_B_DM) == 0) { + sec = bcd2dec(sec); + min = bcd2dec(min); + hour = bcd2dec(hour); + mday = bcd2dec(mday); + mon = bcd2dec(mon); + year = bcd2dec(year); + hour_offset = 80; + } else { + hour_offset = 0x80; + } + + if ((cmos_read(0x0B) & REG_B_24H) == 0) { + if (hour >= hour_offset) { + hour -= hour_offset; + hour += 12; + } + } + + ts = time(NULL); + localtime_r(&ts, &dummy); + + date->tm_isdst = dummy.tm_isdst; + date->tm_sec = sec; + date->tm_min = min; + date->tm_hour = hour; + date->tm_mday = mday; + date->tm_mon = mon - 1; + date->tm_year = base_year + year - 1900; +#ifndef __sun__ + date->tm_gmtoff = 0; +#endif + + ts = mktime(date); +} + +static void check_time(int wiggle) +{ + struct tm start, date[4], end; + struct tm *datep; + time_t ts; + + /* + * This check assumes a few things. First, we cannot guarantee that we get + * a consistent reading from the wall clock because we may hit an edge of + * the clock while reading. To work around this, we read four clock readings + * such that at least two of them should match. We need to assume that one + * reading is corrupt so we need four readings to ensure that we have at + * least two consecutive identical readings + * + * It's also possible that we'll cross an edge reading the host clock so + * simply check to make sure that the clock reading is within the period of + * when we expect it to be. + */ + + ts = time(NULL); + gmtime_r(&ts, &start); + + cmos_get_date_time(&date[0]); + cmos_get_date_time(&date[1]); + cmos_get_date_time(&date[2]); + cmos_get_date_time(&date[3]); + + ts = time(NULL); + gmtime_r(&ts, &end); + + if (tm_cmp(&date[0], &date[1]) == 0) { + datep = &date[0]; + } else if (tm_cmp(&date[1], &date[2]) == 0) { + datep = &date[1]; + } else if (tm_cmp(&date[2], &date[3]) == 0) { + datep = &date[2]; + } else { + g_assert_not_reached(); + } + + if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) { + long t, s; + + start.tm_isdst = datep->tm_isdst; + + t = (long)mktime(datep); + s = (long)mktime(&start); + if (t < s) { + g_test_message("RTC is %ld second(s) behind wall-clock", (s - t)); + } else { + g_test_message("RTC is %ld second(s) ahead of wall-clock", (t - s)); + } + + g_assert_cmpint(ABS(t - s), <=, wiggle); + } +} + +static int wiggle = 2; + +static void set_year_20xx(void) +{ + /* Set BCD mode */ + cmos_write(RTC_REG_B, REG_B_24H); + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_YEAR, 0x11); + cmos_write(RTC_CENTURY, 0x20); + cmos_write(RTC_MONTH, 0x02); + cmos_write(RTC_DAY_OF_MONTH, 0x02); + cmos_write(RTC_HOURS, 0x02); + cmos_write(RTC_MINUTES, 0x04); + cmos_write(RTC_SECONDS, 0x58); + cmos_write(RTC_REG_A, 0x26); + + g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); + g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); + g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11); + g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20); + + if (sizeof(time_t) == 4) { + return; + } + + /* Set a date in 2080 to ensure there is no year-2038 overflow. */ + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_YEAR, 0x80); + cmos_write(RTC_REG_A, 0x26); + + g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); + g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); + g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80); + g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20); + + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_YEAR, 0x11); + cmos_write(RTC_REG_A, 0x26); + + g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); + g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); + g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11); + g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20); +} + +static void set_year_1980(void) +{ + /* Set BCD mode */ + cmos_write(RTC_REG_B, REG_B_24H); + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_YEAR, 0x80); + cmos_write(RTC_CENTURY, 0x19); + cmos_write(RTC_MONTH, 0x02); + cmos_write(RTC_DAY_OF_MONTH, 0x02); + cmos_write(RTC_HOURS, 0x02); + cmos_write(RTC_MINUTES, 0x04); + cmos_write(RTC_SECONDS, 0x58); + cmos_write(RTC_REG_A, 0x26); + + g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); + g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); + g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80); + g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x19); +} + +static void bcd_check_time(void) +{ + /* Set BCD mode */ + cmos_write(RTC_REG_B, REG_B_24H); + check_time(wiggle); +} + +static void dec_check_time(void) +{ + /* Set DEC mode */ + cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM); + check_time(wiggle); +} + +static void alarm_time(void) +{ + struct tm now; + time_t ts; + int i; + + ts = time(NULL); + gmtime_r(&ts, &now); + + /* set DEC mode */ + cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM); + + g_assert(!get_irq(RTC_ISA_IRQ)); + cmos_read(RTC_REG_C); + + now.tm_sec = (now.tm_sec + 2) % 60; + cmos_write(RTC_SECONDS_ALARM, now.tm_sec); + cmos_write(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE); + cmos_write(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE); + cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_AIE); + + for (i = 0; i < 2 + wiggle; i++) { + if (get_irq(RTC_ISA_IRQ)) { + break; + } + + clock_step(1000000000); + } + + g_assert(get_irq(RTC_ISA_IRQ)); + g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0); + g_assert(cmos_read(RTC_REG_C) == 0); +} + +static void set_time_regs(int h, int m, int s) +{ + cmos_write(RTC_HOURS, h); + cmos_write(RTC_MINUTES, m); + cmos_write(RTC_SECONDS, s); +} + +static void set_time(int mode, int h, int m, int s) +{ + cmos_write(RTC_REG_B, mode); + cmos_write(RTC_REG_A, 0x76); + set_time_regs(h, m, s); + cmos_write(RTC_REG_A, 0x26); +} + +static void set_datetime_bcd(int h, int min, int s, int d, int m, int y) +{ + cmos_write(RTC_HOURS, h); + cmos_write(RTC_MINUTES, min); + cmos_write(RTC_SECONDS, s); + cmos_write(RTC_YEAR, y & 0xFF); + cmos_write(RTC_CENTURY, y >> 8); + cmos_write(RTC_MONTH, m); + cmos_write(RTC_DAY_OF_MONTH, d); +} + +static void set_datetime_dec(int h, int min, int s, int d, int m, int y) +{ + cmos_write(RTC_HOURS, h); + cmos_write(RTC_MINUTES, min); + cmos_write(RTC_SECONDS, s); + cmos_write(RTC_YEAR, y % 100); + cmos_write(RTC_CENTURY, y / 100); + cmos_write(RTC_MONTH, m); + cmos_write(RTC_DAY_OF_MONTH, d); +} + +static void set_datetime(int mode, int h, int min, int s, int d, int m, int y) +{ + cmos_write(RTC_REG_B, mode); + + cmos_write(RTC_REG_A, 0x76); + if (mode & REG_B_DM) { + set_datetime_dec(h, min, s, d, m, y); + } else { + set_datetime_bcd(h, min, s, d, m, y); + } + cmos_write(RTC_REG_A, 0x26); +} + +#define assert_time(h, m, s) \ + do { \ + g_assert_cmpint(cmos_read(RTC_HOURS), ==, h); \ + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, m); \ + g_assert_cmpint(cmos_read(RTC_SECONDS), ==, s); \ + } while(0) + +#define assert_datetime_bcd(h, min, s, d, m, y) \ + do { \ + g_assert_cmpint(cmos_read(RTC_HOURS), ==, h); \ + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, min); \ + g_assert_cmpint(cmos_read(RTC_SECONDS), ==, s); \ + g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, d); \ + g_assert_cmpint(cmos_read(RTC_MONTH), ==, m); \ + g_assert_cmpint(cmos_read(RTC_YEAR), ==, (y & 0xFF)); \ + g_assert_cmpint(cmos_read(RTC_CENTURY), ==, (y >> 8)); \ + } while(0) + +static void basic_12h_bcd(void) +{ + /* set BCD 12 hour mode */ + set_time(0, 0x81, 0x59, 0x00); + clock_step(1000000000LL); + assert_time(0x81, 0x59, 0x01); + clock_step(59000000000LL); + assert_time(0x82, 0x00, 0x00); + + /* test BCD wraparound */ + set_time(0, 0x09, 0x59, 0x59); + clock_step(60000000000LL); + assert_time(0x10, 0x00, 0x59); + + /* 12 AM -> 1 AM */ + set_time(0, 0x12, 0x59, 0x59); + clock_step(1000000000LL); + assert_time(0x01, 0x00, 0x00); + + /* 12 PM -> 1 PM */ + set_time(0, 0x92, 0x59, 0x59); + clock_step(1000000000LL); + assert_time(0x81, 0x00, 0x00); + + /* 11 AM -> 12 PM */ + set_time(0, 0x11, 0x59, 0x59); + clock_step(1000000000LL); + assert_time(0x92, 0x00, 0x00); + /* TODO: test day wraparound */ + + /* 11 PM -> 12 AM */ + set_time(0, 0x91, 0x59, 0x59); + clock_step(1000000000LL); + assert_time(0x12, 0x00, 0x00); + /* TODO: test day wraparound */ +} + +static void basic_12h_dec(void) +{ + /* set decimal 12 hour mode */ + set_time(REG_B_DM, 0x81, 59, 0); + clock_step(1000000000LL); + assert_time(0x81, 59, 1); + clock_step(59000000000LL); + assert_time(0x82, 0, 0); + + /* 12 PM -> 1 PM */ + set_time(REG_B_DM, 0x8c, 59, 59); + clock_step(1000000000LL); + assert_time(0x81, 0, 0); + + /* 12 AM -> 1 AM */ + set_time(REG_B_DM, 0x0c, 59, 59); + clock_step(1000000000LL); + assert_time(0x01, 0, 0); + + /* 11 AM -> 12 PM */ + set_time(REG_B_DM, 0x0b, 59, 59); + clock_step(1000000000LL); + assert_time(0x8c, 0, 0); + + /* 11 PM -> 12 AM */ + set_time(REG_B_DM, 0x8b, 59, 59); + clock_step(1000000000LL); + assert_time(0x0c, 0, 0); + /* TODO: test day wraparound */ +} + +static void basic_24h_bcd(void) +{ + /* set BCD 24 hour mode */ + set_time(REG_B_24H, 0x09, 0x59, 0x00); + clock_step(1000000000LL); + assert_time(0x09, 0x59, 0x01); + clock_step(59000000000LL); + assert_time(0x10, 0x00, 0x00); + + /* test BCD wraparound */ + set_time(REG_B_24H, 0x09, 0x59, 0x00); + clock_step(60000000000LL); + assert_time(0x10, 0x00, 0x00); + + /* TODO: test day wraparound */ + set_time(REG_B_24H, 0x23, 0x59, 0x00); + clock_step(60000000000LL); + assert_time(0x00, 0x00, 0x00); +} + +static void basic_24h_dec(void) +{ + /* set decimal 24 hour mode */ + set_time(REG_B_24H | REG_B_DM, 9, 59, 0); + clock_step(1000000000LL); + assert_time(9, 59, 1); + clock_step(59000000000LL); + assert_time(10, 0, 0); + + /* test BCD wraparound */ + set_time(REG_B_24H | REG_B_DM, 9, 59, 0); + clock_step(60000000000LL); + assert_time(10, 0, 0); + + /* TODO: test day wraparound */ + set_time(REG_B_24H | REG_B_DM, 23, 59, 0); + clock_step(60000000000LL); + assert_time(0, 0, 0); +} + +static void am_pm_alarm(void) +{ + cmos_write(RTC_MINUTES_ALARM, 0xC0); + cmos_write(RTC_SECONDS_ALARM, 0xC0); + + /* set BCD 12 hour mode */ + cmos_write(RTC_REG_B, 0); + + /* Set time and alarm hour. */ + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_HOURS_ALARM, 0x82); + cmos_write(RTC_HOURS, 0x81); + cmos_write(RTC_MINUTES, 0x59); + cmos_write(RTC_SECONDS, 0x00); + cmos_read(RTC_REG_C); + cmos_write(RTC_REG_A, 0x26); + + /* Check that alarm triggers when AM/PM is set. */ + clock_step(60000000000LL); + g_assert(cmos_read(RTC_HOURS) == 0x82); + g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0); + + /* + * Each of the following two tests takes over 60 seconds due to the time + * needed to report the PIT interrupts. Unfortunately, our PIT device + * model keeps counting even when GATE=0, so we cannot simply disable + * it in main(). + */ + if (g_test_quick()) { + return; + } + + /* set DEC 12 hour mode */ + cmos_write(RTC_REG_B, REG_B_DM); + + /* Set time and alarm hour. */ + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_HOURS_ALARM, 0x82); + cmos_write(RTC_HOURS, 3); + cmos_write(RTC_MINUTES, 0); + cmos_write(RTC_SECONDS, 0); + cmos_read(RTC_REG_C); + cmos_write(RTC_REG_A, 0x26); + + /* Check that alarm triggers. */ + clock_step(3600 * 11 * 1000000000LL); + g_assert(cmos_read(RTC_HOURS) == 0x82); + g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0); + + /* Same as above, with inverted HOURS and HOURS_ALARM. */ + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_HOURS_ALARM, 2); + cmos_write(RTC_HOURS, 3); + cmos_write(RTC_MINUTES, 0); + cmos_write(RTC_SECONDS, 0); + cmos_read(RTC_REG_C); + cmos_write(RTC_REG_A, 0x26); + + /* Check that alarm does not trigger if hours differ only by AM/PM. */ + clock_step(3600 * 11 * 1000000000LL); + g_assert(cmos_read(RTC_HOURS) == 0x82); + g_assert((cmos_read(RTC_REG_C) & REG_C_AF) == 0); +} + +/* success if no crash or abort */ +static void fuzz_registers(void) +{ + unsigned int i; + + for (i = 0; i < 1000; i++) { + uint8_t reg, val; + + reg = (uint8_t)g_test_rand_int_range(0, 16); + val = (uint8_t)g_test_rand_int_range(0, 256); + + cmos_write(reg, val); + cmos_read(reg); + } +} + +static void register_b_set_flag(void) +{ + if (cmos_read(RTC_REG_A) & REG_A_UIP) { + clock_step(UIP_HOLD_LENGTH + NANOSECONDS_PER_SECOND / 5); + } + g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0); + + /* Enable binary-coded decimal (BCD) mode and SET flag in Register B*/ + cmos_write(RTC_REG_B, REG_B_24H | REG_B_SET); + + set_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); + + assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); + + /* Since SET flag is still enabled, time does not advance. */ + clock_step(1000000000LL); + assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); + + /* Disable SET flag in Register B */ + cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_SET); + + assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); + + /* Since SET flag is disabled, the clock now advances. */ + clock_step(1000000000LL); + assert_datetime_bcd(0x02, 0x04, 0x59, 0x02, 0x02, 0x2011); +} + +static void divider_reset(void) +{ + /* Enable binary-coded decimal (BCD) mode in Register B*/ + cmos_write(RTC_REG_B, REG_B_24H); + + /* Enter divider reset */ + cmos_write(RTC_REG_A, 0x76); + set_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); + + assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); + + /* Since divider reset flag is still enabled, these are equality checks. */ + clock_step(1000000000LL); + assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); + + /* The first update ends 500 ms after divider reset */ + cmos_write(RTC_REG_A, 0x26); + clock_step(500000000LL - UIP_HOLD_LENGTH - 1); + g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0); + assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); + + clock_step(1); + g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, !=, 0); + clock_step(UIP_HOLD_LENGTH); + g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0); + + assert_datetime_bcd(0x02, 0x04, 0x59, 0x02, 0x02, 0x2011); +} + +static void uip_stuck(void) +{ + set_datetime(REG_B_24H, 0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); + + /* The first update ends 500 ms after divider reset */ + (void)cmos_read(RTC_REG_C); + clock_step(500000000LL); + g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0); + assert_datetime_bcd(0x02, 0x04, 0x59, 0x02, 0x02, 0x2011); + + /* UF is now set. */ + cmos_write(RTC_HOURS_ALARM, 0x02); + cmos_write(RTC_MINUTES_ALARM, 0xC0); + cmos_write(RTC_SECONDS_ALARM, 0xC0); + + /* Because the alarm will fire soon, reading register A will latch UIP. */ + clock_step(1000000000LL - UIP_HOLD_LENGTH / 2); + g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, !=, 0); + + /* Move the alarm far away. This must not cause UIP to remain stuck! */ + cmos_write(RTC_HOURS_ALARM, 0x03); + clock_step(UIP_HOLD_LENGTH); + g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0); +} + +#define RTC_PERIOD_CODE1 13 /* 8 Hz */ +#define RTC_PERIOD_CODE2 15 /* 2 Hz */ + +#define RTC_PERIOD_TEST_NR 50 + +static uint64_t wait_periodic_interrupt(uint64_t real_time) +{ + while (!get_irq(RTC_ISA_IRQ)) { + real_time = clock_step_next(); + } + + g_assert((cmos_read(RTC_REG_C) & REG_C_PF) != 0); + return real_time; +} + +static void periodic_timer(void) +{ + int i; + uint64_t period_clocks, period_time, start_time, real_time; + + /* disable all interrupts. */ + cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & + ~(REG_B_PIE | REG_B_AIE | REG_B_UIE)); + cmos_write(RTC_REG_A, RTC_PERIOD_CODE1); + /* enable periodic interrupt after properly configure the period. */ + cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_PIE); + + start_time = real_time = clock_step_next(); + + for (i = 0; i < RTC_PERIOD_TEST_NR; i++) { + cmos_write(RTC_REG_A, RTC_PERIOD_CODE1); + real_time = wait_periodic_interrupt(real_time); + cmos_write(RTC_REG_A, RTC_PERIOD_CODE2); + real_time = wait_periodic_interrupt(real_time); + } + + period_clocks = periodic_period_to_clock(RTC_PERIOD_CODE1) + + periodic_period_to_clock(RTC_PERIOD_CODE2); + period_clocks *= RTC_PERIOD_TEST_NR; + period_time = periodic_clock_to_ns(period_clocks); + + real_time -= start_time; + g_assert_cmpint(ABS((int64_t)(real_time - period_time)), <=, + NANOSECONDS_PER_SECOND * 0.5); +} + +int main(int argc, char **argv) +{ + QTestState *s = NULL; + int ret; + + g_test_init(&argc, &argv, NULL); + + s = qtest_start("-rtc clock=vm"); + qtest_irq_intercept_in(s, "ioapic"); + + qtest_add_func("/rtc/check-time/bcd", bcd_check_time); + qtest_add_func("/rtc/check-time/dec", dec_check_time); + qtest_add_func("/rtc/alarm/interrupt", alarm_time); + qtest_add_func("/rtc/alarm/am-pm", am_pm_alarm); + qtest_add_func("/rtc/basic/dec-24h", basic_24h_dec); + qtest_add_func("/rtc/basic/bcd-24h", basic_24h_bcd); + qtest_add_func("/rtc/basic/dec-12h", basic_12h_dec); + qtest_add_func("/rtc/basic/bcd-12h", basic_12h_bcd); + qtest_add_func("/rtc/set-year/20xx", set_year_20xx); + qtest_add_func("/rtc/set-year/1980", set_year_1980); + qtest_add_func("/rtc/update/register_b_set_flag", register_b_set_flag); + qtest_add_func("/rtc/update/divider-reset", divider_reset); + qtest_add_func("/rtc/update/uip-stuck", uip_stuck); + qtest_add_func("/rtc/misc/fuzz-registers", fuzz_registers); + qtest_add_func("/rtc/periodic/interrupt", periodic_timer); + + ret = g_test_run(); + + if (s) { + qtest_quit(s); + } + + return ret; +} diff --git a/tests/qtest/rtl8139-test.c b/tests/qtest/rtl8139-test.c new file mode 100644 index 0000000000..4506049264 --- /dev/null +++ b/tests/qtest/rtl8139-test.c @@ -0,0 +1,211 @@ +/* + * QTest testcase for Realtek 8139 NIC + * + * Copyright (c) 2013-2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "libqos/pci-pc.h" +#include "qemu/timer.h" +#include "qemu-common.h" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void nop(void) +{ +} + +#define CLK 33333333 + +static QPCIBus *pcibus; +static QPCIDevice *dev; +static QPCIBar dev_bar; + +static void save_fn(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice **pdev = (QPCIDevice **) data; + + *pdev = dev; +} + +static QPCIDevice *get_device(void) +{ + QPCIDevice *dev; + + pcibus = qpci_new_pc(global_qtest, NULL); + qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev); + g_assert(dev != NULL); + + return dev; +} + +#define PORT(name, len, val) \ +static unsigned __attribute__((unused)) in_##name(void) \ +{ \ + unsigned res = qpci_io_read##len(dev, dev_bar, (val)); \ + g_test_message("*%s -> %x", #name, res); \ + return res; \ +} \ +static void out_##name(unsigned v) \ +{ \ + g_test_message("%x -> *%s", v, #name); \ + qpci_io_write##len(dev, dev_bar, (val), v); \ +} + +PORT(Timer, l, 0x48) +PORT(IntrMask, w, 0x3c) +PORT(IntrStatus, w, 0x3E) +PORT(TimerInt, l, 0x54) + +#define fatal(...) do { g_test_message(__VA_ARGS__); g_assert(0); } while (0) + +static void test_timer(void) +{ + const unsigned from = 0.95 * CLK; + const unsigned to = 1.6 * CLK; + unsigned prev, curr, next; + unsigned cnt, diff; + + out_IntrMask(0); + + in_IntrStatus(); + in_Timer(); + in_Timer(); + + /* Test 1. test counter continue and continue */ + out_TimerInt(0); /* disable timer */ + out_IntrStatus(0x4000); + out_Timer(12345); /* reset timer to 0 */ + curr = in_Timer(); + if (curr > 0.1 * CLK) { + fatal("time too big %u\n", curr); + } + for (cnt = 0; ; ) { + clock_step(1 * NANOSECONDS_PER_SECOND); + prev = curr; + curr = in_Timer(); + + /* test skip is in a specific range */ + diff = (curr-prev) & 0xffffffffu; + if (diff < from || diff > to) { + fatal("Invalid diff %u (%u-%u)\n", diff, from, to); + } + if (curr < prev && ++cnt == 3) { + break; + } + } + + /* Test 2. Check we didn't get an interrupt with TimerInt == 0 */ + if (in_IntrStatus() & 0x4000) { + fatal("got an interrupt\n"); + } + + /* Test 3. Setting TimerInt to 1 and Timer to 0 get interrupt */ + out_TimerInt(1); + out_Timer(0); + clock_step(40); + if ((in_IntrStatus() & 0x4000) == 0) { + fatal("we should have an interrupt here!\n"); + } + + /* Test 3. Check acknowledge */ + out_IntrStatus(0x4000); + if (in_IntrStatus() & 0x4000) { + fatal("got an interrupt\n"); + } + + /* Test. Status set after Timer reset */ + out_Timer(0); + out_TimerInt(0); + out_IntrStatus(0x4000); + curr = in_Timer(); + out_TimerInt(curr + 0.5 * CLK); + clock_step(1 * NANOSECONDS_PER_SECOND); + out_Timer(0); + if ((in_IntrStatus() & 0x4000) == 0) { + fatal("we should have an interrupt here!\n"); + } + + /* Test. Status set after TimerInt reset */ + out_Timer(0); + out_TimerInt(0); + out_IntrStatus(0x4000); + curr = in_Timer(); + out_TimerInt(curr + 0.5 * CLK); + clock_step(1 * NANOSECONDS_PER_SECOND); + out_TimerInt(0); + if ((in_IntrStatus() & 0x4000) == 0) { + fatal("we should have an interrupt here!\n"); + } + + /* Test 4. Increment TimerInt we should see an interrupt */ + curr = in_Timer(); + next = curr + 5.0 * CLK; + out_TimerInt(next); + for (cnt = 0; ; ) { + clock_step(1 * NANOSECONDS_PER_SECOND); + prev = curr; + curr = in_Timer(); + diff = (curr-prev) & 0xffffffffu; + if (diff < from || diff > to) { + fatal("Invalid diff %u (%u-%u)\n", diff, from, to); + } + if (cnt < 3 && curr > next) { + if ((in_IntrStatus() & 0x4000) == 0) { + fatal("we should have an interrupt here!\n"); + } + out_IntrStatus(0x4000); + next = curr + 5.0 * CLK; + out_TimerInt(next); + if (++cnt == 3) { + out_TimerInt(1); + } + /* Test 5. Second time we pass from 0 should see an interrupt */ + } else if (cnt >= 3 && curr < prev) { + /* here we should have an interrupt */ + if ((in_IntrStatus() & 0x4000) == 0) { + fatal("we should have an interrupt here!\n"); + } + out_IntrStatus(0x4000); + if (++cnt == 5) { + break; + } + } + } + + g_test_message("Everythink is ok!"); +} + + +static void test_init(void) +{ + uint64_t barsize; + + dev = get_device(); + + dev_bar = qpci_iomap(dev, 0, &barsize); + + qpci_device_enable(dev); + + test_timer(); +} + +int main(int argc, char **argv) +{ + int ret; + + qtest_start("-device rtl8139"); + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/rtl8139/nop", nop); + qtest_add_func("/rtl8139/timer", test_init); + + ret = g_test_run(); + + qtest_end(); + + return ret; +} diff --git a/tests/qtest/sdhci-test.c b/tests/qtest/sdhci-test.c new file mode 100644 index 0000000000..6275e7626c --- /dev/null +++ b/tests/qtest/sdhci-test.c @@ -0,0 +1,111 @@ +/* + * QTest testcase for SDHCI controllers + * + * Written by Philippe Mathieu-Daudé + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/registerfields.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/pci-pc.h" +#include "hw/pci/pci.h" +#include "libqos/qgraph.h" +#include "libqos/sdhci.h" + +#define SDHC_CAPAB 0x40 +FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8); /* since v2 */ +FIELD(SDHC_CAPAB, SDMA, 22, 1); +FIELD(SDHC_CAPAB, SDR, 32, 3); /* since v3 */ +FIELD(SDHC_CAPAB, DRIVER, 36, 3); /* since v3 */ +#define SDHC_HCVER 0xFE + +static void check_specs_version(QSDHCI *s, uint8_t version) +{ + uint32_t v; + + v = s->readw(s, SDHC_HCVER); + v &= 0xff; + v += 1; + g_assert_cmpuint(v, ==, version); +} + +static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab) +{ + uint64_t capab; + + capab = s->readq(s, SDHC_CAPAB); + g_assert_cmphex(capab, ==, expec_capab); +} + +static void check_capab_readonly(QSDHCI *s) +{ + const uint64_t vrand = 0x123456789abcdef; + uint64_t capab0, capab1; + + capab0 = s->readq(s, SDHC_CAPAB); + g_assert_cmpuint(capab0, !=, vrand); + + s->writeq(s, SDHC_CAPAB, vrand); + capab1 = s->readq(s, SDHC_CAPAB); + g_assert_cmpuint(capab1, !=, vrand); + g_assert_cmpuint(capab1, ==, capab0); +} + +static void check_capab_baseclock(QSDHCI *s, uint8_t expec_freq) +{ + uint64_t capab, capab_freq; + + if (!expec_freq) { + return; + } + capab = s->readq(s, SDHC_CAPAB); + capab_freq = FIELD_EX64(capab, SDHC_CAPAB, BASECLKFREQ); + g_assert_cmpuint(capab_freq, ==, expec_freq); +} + +static void check_capab_sdma(QSDHCI *s, bool supported) +{ + uint64_t capab, capab_sdma; + + capab = s->readq(s, SDHC_CAPAB); + capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA); + g_assert_cmpuint(capab_sdma, ==, supported); +} + +static void check_capab_v3(QSDHCI *s, uint8_t version) +{ + uint64_t capab, capab_v3; + + if (version < 3) { + /* before v3 those fields are RESERVED */ + capab = s->readq(s, SDHC_CAPAB); + capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, SDR); + g_assert_cmpuint(capab_v3, ==, 0); + capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, DRIVER); + g_assert_cmpuint(capab_v3, ==, 0); + } +} + +static void test_registers(void *obj, void *data, QGuestAllocator *alloc) +{ + QSDHCI *s = obj; + + check_specs_version(s, s->props.version); + check_capab_capareg(s, s->props.capab.reg); + check_capab_readonly(s); + check_capab_v3(s, s->props.version); + check_capab_sdma(s, s->props.capab.sdma); + check_capab_baseclock(s, s->props.baseclock); +} + +static void register_sdhci_test(void) +{ + qos_add_test("registers", "sdhci", test_registers, NULL); +} + +libqos_init(register_sdhci_test); diff --git a/tests/qtest/spapr-phb-test.c b/tests/qtest/spapr-phb-test.c new file mode 100644 index 0000000000..093dc22f2f --- /dev/null +++ b/tests/qtest/spapr-phb-test.c @@ -0,0 +1,32 @@ +/* + * QTest testcase for SPAPR PHB + * + * Authors: + * Alexey Kardashevskiy + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" + +/* Tests only initialization so far. TODO: Replace with functional tests, + * for example by producing pci-bus. + */ +static void test_phb_device(void *obj, void *data, QGuestAllocator *alloc) +{ +} + +static void register_phb_test(void) +{ + qos_add_test("spapr-phb-test", "ppc64/pseries", + test_phb_device, &(QOSGraphTestOptions) { + .edge.before_cmd_line = "-device spapr-pci-host-bridge" + ",index=30", + }); +} + +libqos_init(register_phb_test); diff --git a/tests/qtest/tco-test.c b/tests/qtest/tco-test.c new file mode 100644 index 0000000000..254f735370 --- /dev/null +++ b/tests/qtest/tco-test.c @@ -0,0 +1,469 @@ +/* + * QEMU ICH9 TCO emulation tests + * + * Copyright (c) 2015 Paulo Alcantara + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "libqtest.h" +#include "libqos/pci.h" +#include "libqos/pci-pc.h" +#include "qapi/qmp/qdict.h" +#include "hw/pci/pci_regs.h" +#include "hw/i386/ich9.h" +#include "hw/acpi/ich9.h" +#include "hw/acpi/tco.h" + +#define RCBA_BASE_ADDR 0xfed1c000 +#define PM_IO_BASE_ADDR 0xb000 + +enum { + TCO_RLD_DEFAULT = 0x0000, + TCO_DAT_IN_DEFAULT = 0x00, + TCO_DAT_OUT_DEFAULT = 0x00, + TCO1_STS_DEFAULT = 0x0000, + TCO2_STS_DEFAULT = 0x0000, + TCO1_CNT_DEFAULT = 0x0000, + TCO2_CNT_DEFAULT = 0x0008, + TCO_MESSAGE1_DEFAULT = 0x00, + TCO_MESSAGE2_DEFAULT = 0x00, + TCO_WDCNT_DEFAULT = 0x00, + TCO_TMR_DEFAULT = 0x0004, + SW_IRQ_GEN_DEFAULT = 0x03, +}; + +#define TCO_SECS_TO_TICKS(secs) (((secs) * 10) / 6) +#define TCO_TICKS_TO_SECS(ticks) (((ticks) * 6) / 10) + +typedef struct { + const char *args; + bool noreboot; + QPCIDevice *dev; + QPCIBar tco_io_bar; + QPCIBus *bus; + QTestState *qts; +} TestData; + +static void test_end(TestData *d) +{ + g_free(d->dev); + qpci_free_pc(d->bus); + qtest_quit(d->qts); +} + +static void test_init(TestData *d) +{ + QTestState *qs; + + qs = qtest_initf("-machine q35 %s %s", + d->noreboot ? "" : "-global ICH9-LPC.noreboot=false", + !d->args ? "" : d->args); + qtest_irq_intercept_in(qs, "ioapic"); + + d->bus = qpci_new_pc(qs, NULL); + d->dev = qpci_device_find(d->bus, QPCI_DEVFN(0x1f, 0x00)); + g_assert(d->dev != NULL); + + qpci_device_enable(d->dev); + + /* set ACPI PM I/O space base address */ + qpci_config_writel(d->dev, ICH9_LPC_PMBASE, PM_IO_BASE_ADDR | 0x1); + /* enable ACPI I/O */ + qpci_config_writeb(d->dev, ICH9_LPC_ACPI_CTRL, 0x80); + /* set Root Complex BAR */ + qpci_config_writel(d->dev, ICH9_LPC_RCBA, RCBA_BASE_ADDR | 0x1); + + d->tco_io_bar = qpci_legacy_iomap(d->dev, PM_IO_BASE_ADDR + 0x60); + d->qts = qs; +} + +static void stop_tco(const TestData *d) +{ + uint32_t val; + + val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT); + val |= TCO_TMR_HLT; + qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val); +} + +static void start_tco(const TestData *d) +{ + uint32_t val; + + val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT); + val &= ~TCO_TMR_HLT; + qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val); +} + +static void load_tco(const TestData *d) +{ + qpci_io_writew(d->dev, d->tco_io_bar, TCO_RLD, 4); +} + +static void set_tco_timeout(const TestData *d, uint16_t ticks) +{ + qpci_io_writew(d->dev, d->tco_io_bar, TCO_TMR, ticks); +} + +static void clear_tco_status(const TestData *d) +{ + qpci_io_writew(d->dev, d->tco_io_bar, TCO1_STS, 0x0008); + qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0002); + qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0004); +} + +static void reset_on_second_timeout(const TestData *td, bool enable) +{ + uint32_t val; + + val = qtest_readl(td->qts, RCBA_BASE_ADDR + ICH9_CC_GCS); + if (enable) { + val &= ~ICH9_CC_GCS_NO_REBOOT; + } else { + val |= ICH9_CC_GCS_NO_REBOOT; + } + qtest_writel(td->qts, RCBA_BASE_ADDR + ICH9_CC_GCS, val); +} + +static void test_tco_defaults(void) +{ + TestData d; + + d.args = NULL; + d.noreboot = true; + test_init(&d); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD), ==, + TCO_RLD_DEFAULT); + /* TCO_DAT_IN & TCO_DAT_OUT */ + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_DAT_IN), ==, + (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT); + /* TCO1_STS & TCO2_STS */ + g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_STS), ==, + (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT); + /* TCO1_CNT & TCO2_CNT */ + g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_CNT), ==, + (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT); + /* TCO_MESSAGE1 & TCO_MESSAGE2 */ + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_MESSAGE1), ==, + (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT); + g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, TCO_WDCNT), ==, + TCO_WDCNT_DEFAULT); + g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, SW_IRQ_GEN), ==, + SW_IRQ_GEN_DEFAULT); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_TMR), ==, + TCO_TMR_DEFAULT); + test_end(&d); +} + +static void test_tco_timeout(void) +{ + TestData d; + const uint16_t ticks = TCO_SECS_TO_TICKS(4); + uint32_t val; + int ret; + + d.args = NULL; + d.noreboot = true; + test_init(&d); + + stop_tco(&d); + clear_tco_status(&d); + reset_on_second_timeout(&d, false); + set_tco_timeout(&d, ticks); + load_tco(&d); + start_tco(&d); + qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC); + + /* test first timeout */ + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); + ret = val & TCO_TIMEOUT ? 1 : 0; + g_assert(ret == 1); + + /* test clearing timeout bit */ + val |= TCO_TIMEOUT; + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); + ret = val & TCO_TIMEOUT ? 1 : 0; + g_assert(ret == 0); + + /* test second timeout */ + qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); + ret = val & TCO_TIMEOUT ? 1 : 0; + g_assert(ret == 1); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); + ret = val & TCO_SECOND_TO_STS ? 1 : 0; + g_assert(ret == 1); + + stop_tco(&d); + test_end(&d); +} + +static void test_tco_max_timeout(void) +{ + TestData d; + const uint16_t ticks = 0xffff; + uint32_t val; + int ret; + + d.args = NULL; + d.noreboot = true; + test_init(&d); + + stop_tco(&d); + clear_tco_status(&d); + reset_on_second_timeout(&d, false); + set_tco_timeout(&d, ticks); + load_tco(&d); + start_tco(&d); + qtest_clock_step(d.qts, ((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC); + + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD); + g_assert_cmpint(val & TCO_RLD_MASK, ==, 1); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); + ret = val & TCO_TIMEOUT ? 1 : 0; + g_assert(ret == 0); + qtest_clock_step(d.qts, TCO_TICK_NSEC); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); + ret = val & TCO_TIMEOUT ? 1 : 0; + g_assert(ret == 1); + + stop_tco(&d); + test_end(&d); +} + +static QDict *get_watchdog_action(const TestData *td) +{ + QDict *ev = qtest_qmp_eventwait_ref(td->qts, "WATCHDOG"); + QDict *data; + + data = qdict_get_qdict(ev, "data"); + qobject_ref(data); + qobject_unref(ev); + return data; +} + +static void test_tco_second_timeout_pause(void) +{ + TestData td; + const uint16_t ticks = TCO_SECS_TO_TICKS(32); + QDict *ad; + + td.args = "-watchdog-action pause"; + td.noreboot = false; + test_init(&td); + + stop_tco(&td); + clear_tco_status(&td); + reset_on_second_timeout(&td, true); + set_tco_timeout(&td, TCO_SECS_TO_TICKS(16)); + load_tco(&td); + start_tco(&td); + qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(&td); + g_assert(!strcmp(qdict_get_str(ad, "action"), "pause")); + qobject_unref(ad); + + stop_tco(&td); + test_end(&td); +} + +static void test_tco_second_timeout_reset(void) +{ + TestData td; + const uint16_t ticks = TCO_SECS_TO_TICKS(16); + QDict *ad; + + td.args = "-watchdog-action reset"; + td.noreboot = false; + test_init(&td); + + stop_tco(&td); + clear_tco_status(&td); + reset_on_second_timeout(&td, true); + set_tco_timeout(&td, TCO_SECS_TO_TICKS(16)); + load_tco(&td); + start_tco(&td); + qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(&td); + g_assert(!strcmp(qdict_get_str(ad, "action"), "reset")); + qobject_unref(ad); + + stop_tco(&td); + test_end(&td); +} + +static void test_tco_second_timeout_shutdown(void) +{ + TestData td; + const uint16_t ticks = TCO_SECS_TO_TICKS(128); + QDict *ad; + + td.args = "-watchdog-action shutdown"; + td.noreboot = false; + test_init(&td); + + stop_tco(&td); + clear_tco_status(&td); + reset_on_second_timeout(&td, true); + set_tco_timeout(&td, ticks); + load_tco(&td); + start_tco(&td); + qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(&td); + g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown")); + qobject_unref(ad); + + stop_tco(&td); + test_end(&td); +} + +static void test_tco_second_timeout_none(void) +{ + TestData td; + const uint16_t ticks = TCO_SECS_TO_TICKS(256); + QDict *ad; + + td.args = "-watchdog-action none"; + td.noreboot = false; + test_init(&td); + + stop_tco(&td); + clear_tco_status(&td); + reset_on_second_timeout(&td, true); + set_tco_timeout(&td, ticks); + load_tco(&td); + start_tco(&td); + qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); + ad = get_watchdog_action(&td); + g_assert(!strcmp(qdict_get_str(ad, "action"), "none")); + qobject_unref(ad); + + stop_tco(&td); + test_end(&td); +} + +static void test_tco_ticks_counter(void) +{ + TestData d; + uint16_t ticks = TCO_SECS_TO_TICKS(8); + uint16_t rld; + + d.args = NULL; + d.noreboot = true; + test_init(&d); + + stop_tco(&d); + clear_tco_status(&d); + reset_on_second_timeout(&d, false); + set_tco_timeout(&d, ticks); + load_tco(&d); + start_tco(&d); + + do { + rld = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD) & TCO_RLD_MASK; + g_assert_cmpint(rld, ==, ticks); + qtest_clock_step(d.qts, TCO_TICK_NSEC); + ticks--; + } while (!(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS) & TCO_TIMEOUT)); + + stop_tco(&d); + test_end(&d); +} + +static void test_tco1_control_bits(void) +{ + TestData d; + uint16_t val; + + d.args = NULL; + d.noreboot = true; + test_init(&d); + + val = TCO_LOCK; + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val); + val &= ~TCO_LOCK; + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_CNT), ==, + TCO_LOCK); + test_end(&d); +} + +static void test_tco1_status_bits(void) +{ + TestData d; + uint16_t ticks = 8; + uint16_t val; + int ret; + + d.args = NULL; + d.noreboot = true; + test_init(&d); + + stop_tco(&d); + clear_tco_status(&d); + reset_on_second_timeout(&d, false); + set_tco_timeout(&d, ticks); + load_tco(&d); + start_tco(&d); + qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC); + + qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_IN, 0); + qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_OUT, 0); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); + ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0; + g_assert(ret == 1); + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS), ==, 0); + test_end(&d); +} + +static void test_tco2_status_bits(void) +{ + TestData d; + uint16_t ticks = 8; + uint16_t val; + int ret; + + d.args = NULL; + d.noreboot = true; + test_init(&d); + + stop_tco(&d); + clear_tco_status(&d); + reset_on_second_timeout(&d, true); + set_tco_timeout(&d, ticks); + load_tco(&d); + start_tco(&d); + qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC * 2); + + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); + ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0; + g_assert(ret == 1); + qpci_io_writew(d.dev, d.tco_io_bar, TCO2_STS, val); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS), ==, 0); + test_end(&d); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("tco/defaults", test_tco_defaults); + qtest_add_func("tco/timeout/no_action", test_tco_timeout); + qtest_add_func("tco/timeout/no_action/max", test_tco_max_timeout); + qtest_add_func("tco/second_timeout/pause", test_tco_second_timeout_pause); + qtest_add_func("tco/second_timeout/reset", test_tco_second_timeout_reset); + qtest_add_func("tco/second_timeout/shutdown", + test_tco_second_timeout_shutdown); + qtest_add_func("tco/second_timeout/none", test_tco_second_timeout_none); + qtest_add_func("tco/counter", test_tco_ticks_counter); + qtest_add_func("tco/tco1_control/bits", test_tco1_control_bits); + qtest_add_func("tco/tco1_status/bits", test_tco1_status_bits); + qtest_add_func("tco/tco2_status/bits", test_tco2_status_bits); + return g_test_run(); +} diff --git a/tests/qtest/test-arm-mptimer.c b/tests/qtest/test-arm-mptimer.c new file mode 100644 index 0000000000..7a56d56da9 --- /dev/null +++ b/tests/qtest/test-arm-mptimer.c @@ -0,0 +1,1090 @@ +/* + * QTest testcase for the ARM MPTimer + * + * Copyright (c) 2016 Dmitry Osipenko + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "libqtest-single.h" + +#define TIMER_BLOCK_SCALE(s) ((((s) & 0xff) + 1) * 10) + +#define TIMER_BLOCK_STEP(scaler, steps_nb) \ + clock_step(TIMER_BLOCK_SCALE(scaler) * (int64_t)(steps_nb) + 1) + +#define TIMER_BASE_PHYS 0x1e000600 + +#define TIMER_LOAD 0x00 +#define TIMER_COUNTER 0x04 +#define TIMER_CONTROL 0x08 +#define TIMER_INTSTAT 0x0C + +#define TIMER_CONTROL_ENABLE (1 << 0) +#define TIMER_CONTROL_PERIODIC (1 << 1) +#define TIMER_CONTROL_IT_ENABLE (1 << 2) +#define TIMER_CONTROL_PRESCALER(p) (((p) & 0xff) << 8) + +#define PERIODIC 1 +#define ONESHOT 0 +#define NOSCALE 0 + +static int nonscaled = NOSCALE; +static int scaled = 122; + +static void timer_load(uint32_t load) +{ + writel(TIMER_BASE_PHYS + TIMER_LOAD, load); +} + +static void timer_start(int periodic, uint32_t scale) +{ + uint32_t ctl = TIMER_CONTROL_ENABLE | TIMER_CONTROL_PRESCALER(scale); + + if (periodic) { + ctl |= TIMER_CONTROL_PERIODIC; + } + + writel(TIMER_BASE_PHYS + TIMER_CONTROL, ctl); +} + +static void timer_stop(void) +{ + writel(TIMER_BASE_PHYS + TIMER_CONTROL, 0); +} + +static void timer_int_clr(void) +{ + writel(TIMER_BASE_PHYS + TIMER_INTSTAT, 1); +} + +static void timer_reset(void) +{ + timer_stop(); + timer_load(0); + timer_int_clr(); +} + +static uint32_t timer_get_and_clr_int_sts(void) +{ + uint32_t int_sts = readl(TIMER_BASE_PHYS + TIMER_INTSTAT); + + if (int_sts) { + timer_int_clr(); + } + + return int_sts; +} + +static uint32_t timer_counter(void) +{ + return readl(TIMER_BASE_PHYS + TIMER_COUNTER); +} + +static void timer_set_counter(uint32_t value) +{ + writel(TIMER_BASE_PHYS + TIMER_COUNTER, value); +} + +static void test_timer_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 9999); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + g_assert_cmpuint(timer_counter(), ==, 9990000); + + TIMER_BLOCK_STEP(scaler, 9990000); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + TIMER_BLOCK_STEP(scaler, 9990000); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_pause(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(999999999); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 999); + + g_assert_cmpuint(timer_counter(), ==, 999999000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 9000); + + g_assert_cmpuint(timer_counter(), ==, 999990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_stop(); + + g_assert_cmpuint(timer_counter(), ==, 999990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 90000); + + g_assert_cmpuint(timer_counter(), ==, 999990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 999990000); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_counter(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 999990000); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + g_assert_cmpuint(timer_counter(), ==, 0); +} + +static void test_timer_reload(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 90000); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 90000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(UINT32_MAX); + + TIMER_BLOCK_STEP(scaler, 90000); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 90000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + int repeat = 10; + + timer_reset(); + timer_load(100); + timer_start(PERIODIC, scaler); + + while (repeat--) { + clock_step(TIMER_BLOCK_SCALE(scaler) * (101 + repeat) + 1); + + g_assert_cmpuint(timer_counter(), ==, 100 - repeat); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + clock_step(TIMER_BLOCK_SCALE(scaler) * (101 - repeat) - 1); + } +} + +static void test_timer_oneshot_to_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(10000); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1000); + + g_assert_cmpuint(timer_counter(), ==, 9000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 14001); + + g_assert_cmpuint(timer_counter(), ==, 5000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_periodic_to_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(99999999); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 999); + + g_assert_cmpuint(timer_counter(), ==, 99999000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 99999009); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_prescaler(void) +{ + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 9999998); + + g_assert_cmpuint(timer_counter(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, 0xAB); + + TIMER_BLOCK_STEP(0xAB, 9999998); + + g_assert_cmpuint(timer_counter(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(0xAB, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_prescaler_on_the_fly(void) +{ + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 999); + + g_assert_cmpuint(timer_counter(), ==, 9999000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, 0xAB); + + TIMER_BLOCK_STEP(0xAB, 9000); + + g_assert_cmpuint(timer_counter(), ==, 9990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_oneshot_counter_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + TIMER_BLOCK_STEP(scaler, 10); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_set_periodic_counter_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - (scaler ? 0 : 1)); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_reset(); + timer_set_counter(UINT32_MAX); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_noload_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_noload_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_zero_load_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_zero_load_oneshot_to_nonzero(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(999); + + TIMER_BLOCK_STEP(scaler, 1001); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_zero_load_periodic_to_nonzero(gconstpointer arg) +{ + int scaler = *((int *) arg); + int i; + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_load(1999999); + + for (i = 1; i < 10; i++) { + TIMER_BLOCK_STEP(scaler, 2000001); + + g_assert_cmpuint(timer_counter(), ==, 1999999 - i); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + } +} + +static void test_timer_nonzero_load_oneshot_to_zero(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(UINT32_MAX); + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_nonzero_load_periodic_to_zero(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_load(UINT32_MAX); + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_set_periodic_counter_on_the_fly(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX / 2); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX / 2 - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(UINT32_MAX); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_enable_and_set_counter(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_set_counter(UINT32_MAX); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_counter_and_enable(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_set_counter(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_counter_disabled(void) +{ + timer_reset(); + timer_set_counter(999999999); + + TIMER_BLOCK_STEP(NOSCALE, 100); + + g_assert_cmpuint(timer_counter(), ==, 999999999); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_load_disabled(void) +{ + timer_reset(); + timer_load(999999999); + + TIMER_BLOCK_STEP(NOSCALE, 100); + + g_assert_cmpuint(timer_counter(), ==, 999999999); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_oneshot_with_counter_0_on_start(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(999); + timer_set_counter(0); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_periodic_with_counter_0_on_start(gconstpointer arg) +{ + int scaler = *((int *) arg); + int i; + + timer_reset(); + timer_load(UINT32_MAX); + timer_set_counter(0); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX + (scaler ? 1 : 0) - 100); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX + (scaler ? 1 : 0) - 200); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_reset(); + timer_load(1999999); + timer_set_counter(0); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + for (i = 2 - (!!scaler ? 1 : 0); i < 10; i++) { + TIMER_BLOCK_STEP(scaler, 2000001); + + g_assert_cmpuint(timer_counter(), ==, 1999999 - i); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + } +} + +static void test_periodic_counter(gconstpointer arg) +{ + const int test_load = 10; + int scaler = *((int *) arg); + int test_val; + + timer_reset(); + timer_load(test_load); + timer_start(PERIODIC, scaler); + + clock_step(1); + + for (test_val = 0; test_val <= test_load; test_val++) { + clock_step(TIMER_BLOCK_SCALE(scaler) * test_load); + g_assert_cmpint(timer_counter(), ==, test_val); + } +} + +static void test_timer_set_counter_periodic_with_zero_load(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_set_counter(999); + + TIMER_BLOCK_STEP(scaler, 999); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_set_oneshot_load_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_periodic_load_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); +} + +static void test_deferred_trigger(void) +{ + int mode = ONESHOT; + +again: + timer_reset(); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_counter(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(2); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(0); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + if (mode == ONESHOT) { + mode = PERIODIC; + goto again; + } +} + +static void test_timer_zero_load_mode_switch(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(0); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 1); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_zero_load_prescaled_periodic_to_nonscaled_oneshot(void) +{ + timer_reset(); + timer_load(0); + timer_start(PERIODIC, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_prescaled_oneshot_to_nonscaled_periodic(void) +{ + timer_reset(); + timer_load(0); + timer_start(ONESHOT, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_nonscaled_oneshot_to_prescaled_periodic(void) +{ + timer_reset(); + timer_load(0); + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot(void) +{ + timer_reset(); + timer_load(0); + timer_start(PERIODIC, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +/* + * Add a qtest test that comes in two versions: one with + * a timer scaler setting, and one with the timer nonscaled. + */ +static void add_scaler_test(const char *str, bool scale, + void (*fn)(const void *)) +{ + char *name; + int *scaler = scale ? &scaled : &nonscaled; + + name = g_strdup_printf("%s=%d", str, *scaler); + qtest_add_data_func(name, scaler, fn); + g_free(name); +} + +int main(int argc, char **argv) +{ + int ret; + int scale; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("mptimer/deferred_trigger", test_deferred_trigger); + qtest_add_func("mptimer/load_disabled", test_timer_load_disabled); + qtest_add_func("mptimer/set_counter_disabled", test_timer_set_counter_disabled); + qtest_add_func("mptimer/zero_load_prescaled_periodic_to_nonscaled_oneshot", + test_timer_zero_load_prescaled_periodic_to_nonscaled_oneshot); + qtest_add_func("mptimer/zero_load_prescaled_oneshot_to_nonscaled_periodic", + test_timer_zero_load_prescaled_oneshot_to_nonscaled_periodic); + qtest_add_func("mptimer/zero_load_nonscaled_oneshot_to_prescaled_periodic", + test_timer_zero_load_nonscaled_oneshot_to_prescaled_periodic); + qtest_add_func("mptimer/zero_load_nonscaled_periodic_to_prescaled_oneshot", + test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot); + qtest_add_func("mptimer/prescaler", test_timer_prescaler); + qtest_add_func("mptimer/prescaler_on_the_fly", test_timer_prescaler_on_the_fly); + + for (scale = 0; scale < 2; scale++) { + add_scaler_test("mptimer/oneshot scaler", + scale, test_timer_oneshot); + add_scaler_test("mptimer/pause scaler", + scale, test_timer_pause); + add_scaler_test("mptimer/reload scaler", + scale, test_timer_reload); + add_scaler_test("mptimer/periodic scaler", + scale, test_timer_periodic); + add_scaler_test("mptimer/oneshot_to_periodic scaler", + scale, test_timer_oneshot_to_periodic); + add_scaler_test("mptimer/periodic_to_oneshot scaler", + scale, test_timer_periodic_to_oneshot); + add_scaler_test("mptimer/set_oneshot_counter_to_0 scaler", + scale, test_timer_set_oneshot_counter_to_0); + add_scaler_test("mptimer/set_periodic_counter_to_0 scaler", + scale, test_timer_set_periodic_counter_to_0); + add_scaler_test("mptimer/noload_oneshot scaler", + scale, test_timer_noload_oneshot); + add_scaler_test("mptimer/noload_periodic scaler", + scale, test_timer_noload_periodic); + add_scaler_test("mptimer/zero_load_oneshot scaler", + scale, test_timer_zero_load_oneshot); + add_scaler_test("mptimer/zero_load_periodic scaler", + scale, test_timer_zero_load_periodic); + add_scaler_test("mptimer/zero_load_oneshot_to_nonzero scaler", + scale, test_timer_zero_load_oneshot_to_nonzero); + add_scaler_test("mptimer/zero_load_periodic_to_nonzero scaler", + scale, test_timer_zero_load_periodic_to_nonzero); + add_scaler_test("mptimer/nonzero_load_oneshot_to_zero scaler", + scale, test_timer_nonzero_load_oneshot_to_zero); + add_scaler_test("mptimer/nonzero_load_periodic_to_zero scaler", + scale, test_timer_nonzero_load_periodic_to_zero); + add_scaler_test("mptimer/set_periodic_counter_on_the_fly scaler", + scale, test_timer_set_periodic_counter_on_the_fly); + add_scaler_test("mptimer/enable_and_set_counter scaler", + scale, test_timer_enable_and_set_counter); + add_scaler_test("mptimer/set_counter_and_enable scaler", + scale, test_timer_set_counter_and_enable); + add_scaler_test("mptimer/oneshot_with_counter_0_on_start scaler", + scale, test_timer_oneshot_with_counter_0_on_start); + add_scaler_test("mptimer/periodic_with_counter_0_on_start scaler", + scale, test_timer_periodic_with_counter_0_on_start); + add_scaler_test("mptimer/periodic_counter scaler", + scale, test_periodic_counter); + add_scaler_test("mptimer/set_counter_periodic_with_zero_load scaler", + scale, test_timer_set_counter_periodic_with_zero_load); + add_scaler_test("mptimer/set_oneshot_load_to_0 scaler", + scale, test_timer_set_oneshot_load_to_0); + add_scaler_test("mptimer/set_periodic_load_to_0 scaler", + scale, test_timer_set_periodic_load_to_0); + add_scaler_test("mptimer/zero_load_mode_switch scaler", + scale, test_timer_zero_load_mode_switch); + } + + qtest_start("-machine vexpress-a9"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/test-filter-mirror.c b/tests/qtest/test-filter-mirror.c new file mode 100644 index 0000000000..1e3ced84a9 --- /dev/null +++ b/tests/qtest/test-filter-mirror.c @@ -0,0 +1,94 @@ +/* + * QTest testcase for filter-mirror + * + * Copyright (c) 2016 FUJITSU LIMITED + * Author: Zhang Chen + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" + +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) + +static void test_mirror(void) +{ + int send_sock[2], recv_sock[2]; + uint32_t ret = 0, len = 0; + char send_buf[] = "Hello! filter-mirror~"; + char *recv_buf; + uint32_t size = sizeof(send_buf); + size = htonl(size); + const char *devstr = "e1000"; + QTestState *qts; + + if (g_str_equal(qtest_get_arch(), "s390x")) { + devstr = "virtio-net-ccw"; + } + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, send_sock); + g_assert_cmpint(ret, !=, -1); + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, recv_sock); + g_assert_cmpint(ret, !=, -1); + + qts = qtest_initf( + "-netdev socket,id=qtest-bn0,fd=%d " + "-device %s,netdev=qtest-bn0,id=qtest-e0 " + "-chardev socket,id=mirror0,fd=%d " + "-object filter-mirror,id=qtest-f0,netdev=qtest-bn0,queue=tx,outdev=mirror0 " + , send_sock[1], devstr, recv_sock[1]); + + struct iovec iov[] = { + { + .iov_base = &size, + .iov_len = sizeof(size), + }, { + .iov_base = send_buf, + .iov_len = sizeof(send_buf), + }, + }; + + /* send a qmp command to guarantee that 'connected' is setting to true. */ + qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); + ret = iov_send(send_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf)); + g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); + close(send_sock[0]); + + ret = qemu_recv(recv_sock[0], &len, sizeof(len), 0); + g_assert_cmpint(ret, ==, sizeof(len)); + len = ntohl(len); + + g_assert_cmpint(len, ==, sizeof(send_buf)); + recv_buf = g_malloc(len); + ret = qemu_recv(recv_sock[0], recv_buf, len, 0); + g_assert_cmpstr(recv_buf, ==, send_buf); + + g_free(recv_buf); + close(send_sock[0]); + close(send_sock[1]); + close(recv_sock[0]); + close(recv_sock[1]); + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/netfilter/mirror", test_mirror); + ret = g_test_run(); + + return ret; +} diff --git a/tests/qtest/test-filter-redirector.c b/tests/qtest/test-filter-redirector.c new file mode 100644 index 0000000000..e4d53220fd --- /dev/null +++ b/tests/qtest/test-filter-redirector.c @@ -0,0 +1,219 @@ +/* + * QTest testcase for filter-redirector + * + * Copyright (c) 2016 FUJITSU LIMITED + * Author: Zhang Chen + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + * Case 1, tx traffic flow: + * + * qemu side | test side + * | + * +---------+ | +-------+ + * | backend <---------------+ sock0 | + * +----+----+ | +-------+ + * | | + * +----v----+ +-------+ | + * | rd0 +->+chardev| | + * +---------+ +---+---+ | + * | | + * +---------+ | | + * | rd1 <------+ | + * +----+----+ | + * | | + * +----v----+ | +-------+ + * | rd2 +--------------->sock1 | + * +---------+ | +-------+ + * + + * + * -------------------------------------- + * Case 2, rx traffic flow + * qemu side | test side + * | + * +---------+ | +-------+ + * | backend +---------------> sock1 | + * +----^----+ | +-------+ + * | | + * +----+----+ +-------+ | + * | rd0 +<-+chardev| | + * +---------+ +---+---+ | + * ^ | + * +---------+ | | + * | rd1 +------+ | + * +----^----+ | + * | | + * +----+----+ | +-------+ + * | rd2 <---------------+sock0 | + * +---------+ | +-------+ + * + + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" + +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) + +static const char *get_devstr(void) +{ + if (g_str_equal(qtest_get_arch(), "s390x")) { + return "virtio-net-ccw"; + } + + return "rtl8139"; +} + + +static void test_redirector_tx(void) +{ + int backend_sock[2], recv_sock; + uint32_t ret = 0, len = 0; + char send_buf[] = "Hello!!"; + char sock_path0[] = "filter-redirector0.XXXXXX"; + char sock_path1[] = "filter-redirector1.XXXXXX"; + char *recv_buf; + uint32_t size = sizeof(send_buf); + size = htonl(size); + QTestState *qts; + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock); + g_assert_cmpint(ret, !=, -1); + + ret = mkstemp(sock_path0); + g_assert_cmpint(ret, !=, -1); + ret = mkstemp(sock_path1); + g_assert_cmpint(ret, !=, -1); + + qts = qtest_initf( + "-netdev socket,id=qtest-bn0,fd=%d " + "-device %s,netdev=qtest-bn0,id=qtest-e0 " + "-chardev socket,id=redirector0,path=%s,server,nowait " + "-chardev socket,id=redirector1,path=%s,server,nowait " + "-chardev socket,id=redirector2,path=%s " + "-object filter-redirector,id=qtest-f0,netdev=qtest-bn0," + "queue=tx,outdev=redirector0 " + "-object filter-redirector,id=qtest-f1,netdev=qtest-bn0," + "queue=tx,indev=redirector2 " + "-object filter-redirector,id=qtest-f2,netdev=qtest-bn0," + "queue=tx,outdev=redirector1 ", backend_sock[1], get_devstr(), + sock_path0, sock_path1, sock_path0); + + recv_sock = unix_connect(sock_path1, NULL); + g_assert_cmpint(recv_sock, !=, -1); + + /* send a qmp command to guarantee that 'connected' is setting to true. */ + qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); + + struct iovec iov[] = { + { + .iov_base = &size, + .iov_len = sizeof(size), + }, { + .iov_base = send_buf, + .iov_len = sizeof(send_buf), + }, + }; + + ret = iov_send(backend_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf)); + g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); + close(backend_sock[0]); + + ret = qemu_recv(recv_sock, &len, sizeof(len), 0); + g_assert_cmpint(ret, ==, sizeof(len)); + len = ntohl(len); + + g_assert_cmpint(len, ==, sizeof(send_buf)); + recv_buf = g_malloc(len); + ret = qemu_recv(recv_sock, recv_buf, len, 0); + g_assert_cmpstr(recv_buf, ==, send_buf); + + g_free(recv_buf); + close(recv_sock); + unlink(sock_path0); + unlink(sock_path1); + qtest_quit(qts); +} + +static void test_redirector_rx(void) +{ + int backend_sock[2], send_sock; + uint32_t ret = 0, len = 0; + char send_buf[] = "Hello!!"; + char sock_path0[] = "filter-redirector0.XXXXXX"; + char sock_path1[] = "filter-redirector1.XXXXXX"; + char *recv_buf; + uint32_t size = sizeof(send_buf); + size = htonl(size); + QTestState *qts; + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock); + g_assert_cmpint(ret, !=, -1); + + ret = mkstemp(sock_path0); + g_assert_cmpint(ret, !=, -1); + ret = mkstemp(sock_path1); + g_assert_cmpint(ret, !=, -1); + + qts = qtest_initf( + "-netdev socket,id=qtest-bn0,fd=%d " + "-device %s,netdev=qtest-bn0,id=qtest-e0 " + "-chardev socket,id=redirector0,path=%s,server,nowait " + "-chardev socket,id=redirector1,path=%s,server,nowait " + "-chardev socket,id=redirector2,path=%s " + "-object filter-redirector,id=qtest-f0,netdev=qtest-bn0," + "queue=rx,indev=redirector0 " + "-object filter-redirector,id=qtest-f1,netdev=qtest-bn0," + "queue=rx,outdev=redirector2 " + "-object filter-redirector,id=qtest-f2,netdev=qtest-bn0," + "queue=rx,indev=redirector1 ", backend_sock[1], get_devstr(), + sock_path0, sock_path1, sock_path0); + + struct iovec iov[] = { + { + .iov_base = &size, + .iov_len = sizeof(size), + }, { + .iov_base = send_buf, + .iov_len = sizeof(send_buf), + }, + }; + + send_sock = unix_connect(sock_path1, NULL); + g_assert_cmpint(send_sock, !=, -1); + /* send a qmp command to guarantee that 'connected' is setting to true. */ + qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); + + ret = iov_send(send_sock, iov, 2, 0, sizeof(size) + sizeof(send_buf)); + g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); + + ret = qemu_recv(backend_sock[0], &len, sizeof(len), 0); + g_assert_cmpint(ret, ==, sizeof(len)); + len = ntohl(len); + + g_assert_cmpint(len, ==, sizeof(send_buf)); + recv_buf = g_malloc(len); + ret = qemu_recv(backend_sock[0], recv_buf, len, 0); + g_assert_cmpstr(recv_buf, ==, send_buf); + + close(send_sock); + g_free(recv_buf); + unlink(sock_path0); + unlink(sock_path1); + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/netfilter/redirector_tx", test_redirector_tx); + qtest_add_func("/netfilter/redirector_rx", test_redirector_rx); + return g_test_run(); +} diff --git a/tests/qtest/test-hmp.c b/tests/qtest/test-hmp.c new file mode 100644 index 0000000000..5029c4d2c9 --- /dev/null +++ b/tests/qtest/test-hmp.c @@ -0,0 +1,171 @@ +/* + * Test HMP commands. + * + * Copyright (c) 2017 Red Hat Inc. + * + * Author: + * Thomas Huth + * + * This work is licensed under the terms of the GNU GPL, version 2 + * or later. See the COPYING file in the top-level directory. + * + * This test calls some HMP commands for all machines that the current + * QEMU binary provides, to check whether they terminate successfully + * (i.e. do not crash QEMU). + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +static int verbose; + +static const char *hmp_cmds[] = { + "announce_self", + "boot_set ndc", + "chardev-add null,id=testchardev1", + "chardev-send-break testchardev1", + "chardev-change testchardev1 ringbuf", + "chardev-remove testchardev1", + "commit all", + "cpu-add 1", + "cpu 0", + "device_add ?", + "device_add usb-mouse,id=mouse1", + "drive_add ignored format=help", + "mouse_button 7", + "mouse_move 10 10", + "mouse_button 0", + "device_del mouse1", + "dump-guest-memory /dev/null 0 4096", + "dump-guest-memory /dev/null", + "gdbserver", + "gva2gpa 0", + "hostfwd_add tcp::43210-:43210", + "hostfwd_remove tcp::43210-:43210", + "i /w 0", + "log all", + "log none", + "memsave 0 4096 \"/dev/null\"", + "migrate_set_cache_size 1", + "migrate_set_downtime 1", + "migrate_set_speed 1", + "netdev_add user,id=net1", + "set_link net1 off", + "set_link net1 on", + "netdev_del net1", + "nmi", + "o /w 0 0x1234", + "object_add memory-backend-ram,id=mem1,size=256M", + "object_del mem1", + "pmemsave 0 4096 \"/dev/null\"", + "p $pc + 8", + "qom-list /", + "qom-set /machine initrd test", + "screendump /dev/null", + "sendkey x", + "singlestep on", + "wavcapture /dev/null", + "stopcapture 0", + "sum 0 512", + "x /8i 0x100", + "xp /16x 0", + NULL +}; + +/* Run through the list of pre-defined commands */ +static void test_commands(QTestState *qts) +{ + char *response; + int i; + + for (i = 0; hmp_cmds[i] != NULL; i++) { + response = qtest_hmp(qts, "%s", hmp_cmds[i]); + if (verbose) { + fprintf(stderr, + "\texecute HMP command: %s\n" + "\tresult : %s\n", + hmp_cmds[i], response); + } + g_free(response); + } + +} + +/* Run through all info commands and call them blindly (without arguments) */ +static void test_info_commands(QTestState *qts) +{ + char *resp, *info, *info_buf, *endp; + + info_buf = info = qtest_hmp(qts, "help info"); + + while (*info) { + /* Extract the info command, ignore parameters and description */ + g_assert(strncmp(info, "info ", 5) == 0); + endp = strchr(&info[5], ' '); + g_assert(endp != NULL); + *endp = '\0'; + /* Now run the info command */ + if (verbose) { + fprintf(stderr, "\t%s\n", info); + } + resp = qtest_hmp(qts, "%s", info); + g_free(resp); + /* And move forward to the next line */ + info = strchr(endp + 1, '\n'); + if (!info) { + break; + } + info += 1; + } + + g_free(info_buf); +} + +static void test_machine(gconstpointer data) +{ + const char *machine = data; + char *args; + QTestState *qts; + + args = g_strdup_printf("-S -M %s", machine); + qts = qtest_init(args); + + test_info_commands(qts); + test_commands(qts); + + qtest_quit(qts); + g_free(args); + g_free((void *)data); +} + +static void add_machine_test_case(const char *mname) +{ + char *path; + + /* Ignore blacklisted machines that have known problems */ + if (!strcmp("xenfv", mname) || !strcmp("xenpv", mname)) { + return; + } + + path = g_strdup_printf("hmp/%s", mname); + qtest_add_data_func(path, g_strdup(mname), test_machine); + g_free(path); +} + +int main(int argc, char **argv) +{ + char *v_env = getenv("V"); + + if (v_env && *v_env >= '2') { + verbose = true; + } + + g_test_init(&argc, &argv, NULL); + + qtest_cb_for_every_machine(add_machine_test_case, g_test_quick()); + + /* as none machine has no memory by default, add a test case with memory */ + qtest_add_data_func("hmp/none+2MB", g_strdup("none -m 2"), test_machine); + + return g_test_run(); +} diff --git a/tests/qtest/test-netfilter.c b/tests/qtest/test-netfilter.c new file mode 100644 index 0000000000..22927ee6ab --- /dev/null +++ b/tests/qtest/test-netfilter.c @@ -0,0 +1,210 @@ +/* + * QTest testcase for netfilter + * + * Copyright (c) 2015 FUJITSU LIMITED + * Author: Yang Hongyang + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "qapi/qmp/qdict.h" + +/* add a netfilter to a netdev and then remove it */ +static void add_one_netfilter(void) +{ + QDict *response; + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f0'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + response = qmp("{'execute': 'object-del'," + " 'arguments': {" + " 'id': 'qtest-f0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); +} + +/* add a netfilter to a netdev and then remove the netdev */ +static void remove_netdev_with_one_netfilter(void) +{ + QDict *response; + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f0'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + response = qmp("{'execute': 'netdev_del'," + " 'arguments': {" + " 'id': 'qtest-bn0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + /* add back the netdev */ + response = qmp("{'execute': 'netdev_add'," + " 'arguments': {" + " 'type': 'user'," + " 'id': 'qtest-bn0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); +} + +/* add multi(2) netfilters to a netdev and then remove them */ +static void add_multi_netfilter(void) +{ + QDict *response; + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f0'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f1'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + response = qmp("{'execute': 'object-del'," + " 'arguments': {" + " 'id': 'qtest-f0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + response = qmp("{'execute': 'object-del'," + " 'arguments': {" + " 'id': 'qtest-f1'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); +} + +/* add multi(2) netfilters to a netdev and then remove the netdev */ +static void remove_netdev_with_multi_netfilter(void) +{ + QDict *response; + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f0'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + response = qmp("{'execute': 'object-add'," + " 'arguments': {" + " 'qom-type': 'filter-buffer'," + " 'id': 'qtest-f1'," + " 'props': {" + " 'netdev': 'qtest-bn0'," + " 'queue': 'rx'," + " 'interval': 1000" + "}}}"); + + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + response = qmp("{'execute': 'netdev_del'," + " 'arguments': {" + " 'id': 'qtest-bn0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + /* add back the netdev */ + response = qmp("{'execute': 'netdev_add'," + " 'arguments': {" + " 'type': 'user'," + " 'id': 'qtest-bn0'" + "}}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); +} + +int main(int argc, char **argv) +{ + int ret; + char *args; + const char *devstr = "e1000"; + + if (g_str_equal(qtest_get_arch(), "s390x")) { + devstr = "virtio-net-ccw"; + } + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/netfilter/addremove_one", add_one_netfilter); + qtest_add_func("/netfilter/remove_netdev_one", + remove_netdev_with_one_netfilter); + qtest_add_func("/netfilter/addremove_multi", add_multi_netfilter); + qtest_add_func("/netfilter/remove_netdev_multi", + remove_netdev_with_multi_netfilter); + + args = g_strdup_printf("-netdev user,id=qtest-bn0 " + "-device %s,netdev=qtest-bn0", devstr); + qtest_start(args); + ret = g_test_run(); + + qtest_end(); + g_free(args); + + return ret; +} diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c new file mode 100644 index 0000000000..772287bdb4 --- /dev/null +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -0,0 +1,381 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qbool.h" +#include "libqtest-single.h" + +static char *get_cpu0_qom_path(void) +{ + QDict *resp; + QList *ret; + QDict *cpu0; + char *path; + + resp = qmp("{'execute': 'query-cpus', 'arguments': {}}"); + g_assert(qdict_haskey(resp, "return")); + ret = qdict_get_qlist(resp, "return"); + + cpu0 = qobject_to(QDict, qlist_peek(ret)); + path = g_strdup(qdict_get_str(cpu0, "qom_path")); + qobject_unref(resp); + return path; +} + +static QObject *qom_get(const char *path, const char *prop) +{ + QDict *resp = qmp("{ 'execute': 'qom-get'," + " 'arguments': { 'path': %s," + " 'property': %s } }", + path, prop); + QObject *ret = qdict_get(resp, "return"); + qobject_ref(ret); + qobject_unref(resp); + return ret; +} + +static bool qom_get_bool(const char *path, const char *prop) +{ + QBool *value = qobject_to(QBool, qom_get(path, prop)); + bool b = qbool_get_bool(value); + + qobject_unref(value); + return b; +} + +typedef struct CpuidTestArgs { + const char *cmdline; + const char *property; + int64_t expected_value; +} CpuidTestArgs; + +static void test_cpuid_prop(const void *data) +{ + const CpuidTestArgs *args = data; + char *path; + QNum *value; + int64_t val; + + qtest_start(args->cmdline); + path = get_cpu0_qom_path(); + value = qobject_to(QNum, qom_get(path, args->property)); + g_assert(qnum_get_try_int(value, &val)); + g_assert_cmpint(val, ==, args->expected_value); + qtest_end(); + + qobject_unref(value); + g_free(path); +} + +static void add_cpuid_test(const char *name, const char *cmdline, + const char *property, int64_t expected_value) +{ + CpuidTestArgs *args = g_new0(CpuidTestArgs, 1); + args->cmdline = cmdline; + args->property = property; + args->expected_value = expected_value; + qtest_add_data_func(name, args, test_cpuid_prop); +} + + +/* Parameters to a add_feature_test() test case */ +typedef struct FeatureTestArgs { + /* cmdline to start QEMU */ + const char *cmdline; + /* + * cpuid-input-eax and cpuid-input-ecx values to look for, + * in "feature-words" and "filtered-features" properties. + */ + uint32_t in_eax, in_ecx; + /* The register name to look for, in the X86CPUFeatureWordInfo array */ + const char *reg; + /* The bit to check in X86CPUFeatureWordInfo.features */ + int bitnr; + /* The expected value for the bit in (X86CPUFeatureWordInfo.features) */ + bool expected_value; +} FeatureTestArgs; + +/* Get the value for a feature word in a X86CPUFeatureWordInfo list */ +static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx, + const char *reg) +{ + const QListEntry *e; + + for (e = qlist_first(features); e; e = qlist_next(e)) { + QDict *w = qobject_to(QDict, qlist_entry_obj(e)); + const char *rreg = qdict_get_str(w, "cpuid-register"); + uint32_t reax = qdict_get_int(w, "cpuid-input-eax"); + bool has_ecx = qdict_haskey(w, "cpuid-input-ecx"); + uint32_t recx = 0; + int64_t val; + + if (has_ecx) { + recx = qdict_get_int(w, "cpuid-input-ecx"); + } + if (eax == reax && (!has_ecx || ecx == recx) && !strcmp(rreg, reg)) { + g_assert(qnum_get_try_int(qobject_to(QNum, + qdict_get(w, "features")), + &val)); + return val; + } + } + return 0; +} + +static void test_feature_flag(const void *data) +{ + const FeatureTestArgs *args = data; + char *path; + QList *present, *filtered; + uint32_t value; + + qtest_start(args->cmdline); + path = get_cpu0_qom_path(); + present = qobject_to(QList, qom_get(path, "feature-words")); + filtered = qobject_to(QList, qom_get(path, "filtered-features")); + value = get_feature_word(present, args->in_eax, args->in_ecx, args->reg); + value |= get_feature_word(filtered, args->in_eax, args->in_ecx, args->reg); + qtest_end(); + + g_assert(!!(value & (1U << args->bitnr)) == args->expected_value); + + qobject_unref(present); + qobject_unref(filtered); + g_free(path); +} + +/* + * Add test case to ensure that a given feature flag is set in + * either "feature-words" or "filtered-features", when running QEMU + * using cmdline + */ +static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline, + uint32_t eax, uint32_t ecx, + const char *reg, int bitnr, + bool expected_value) +{ + FeatureTestArgs *args = g_new0(FeatureTestArgs, 1); + args->cmdline = cmdline; + args->in_eax = eax; + args->in_ecx = ecx; + args->reg = reg; + args->bitnr = bitnr; + args->expected_value = expected_value; + qtest_add_data_func(name, args, test_feature_flag); + return args; +} + +static void test_plus_minus_subprocess(void) +{ + char *path; + + /* Rules: + * 1)"-foo" overrides "+foo" + * 2) "[+-]foo" overrides "foo=..." + * 3) Old feature names with underscores (e.g. "sse4_2") + * should keep working + * + * Note: rules 1 and 2 are planned to be removed soon, and + * should generate a warning. + */ + qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on"); + path = get_cpu0_qom_path(); + + g_assert_false(qom_get_bool(path, "fpu")); + g_assert_false(qom_get_bool(path, "mce")); + g_assert_true(qom_get_bool(path, "cx8")); + + /* Test both the original and the alias feature names: */ + g_assert_true(qom_get_bool(path, "sse4-1")); + g_assert_true(qom_get_bool(path, "sse4.1")); + + g_assert_true(qom_get_bool(path, "sse4-2")); + g_assert_true(qom_get_bool(path, "sse4.2")); + + qtest_end(); + g_free(path); +} + +static void test_plus_minus(void) +{ + g_test_trap_subprocess("/x86/cpuid/parsing-plus-minus/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("*Ambiguous CPU model string. " + "Don't mix both \"-mce\" and \"mce=on\"*"); + g_test_trap_assert_stderr("*Ambiguous CPU model string. " + "Don't mix both \"+cx8\" and \"cx8=off\"*"); + g_test_trap_assert_stdout(""); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/x86/cpuid/parsing-plus-minus/subprocess", + test_plus_minus_subprocess); + g_test_add_func("/x86/cpuid/parsing-plus-minus", test_plus_minus); + + /* Original level values for CPU models: */ + add_cpuid_test("x86/cpuid/phenom/level", + "-cpu phenom", "level", 5); + add_cpuid_test("x86/cpuid/Conroe/level", + "-cpu Conroe", "level", 10); + add_cpuid_test("x86/cpuid/SandyBridge/level", + "-cpu SandyBridge", "level", 0xd); + add_cpuid_test("x86/cpuid/486/xlevel", + "-cpu 486", "xlevel", 0); + add_cpuid_test("x86/cpuid/core2duo/xlevel", + "-cpu core2duo", "xlevel", 0x80000008); + add_cpuid_test("x86/cpuid/phenom/xlevel", + "-cpu phenom", "xlevel", 0x8000001A); + add_cpuid_test("x86/cpuid/athlon/xlevel", + "-cpu athlon", "xlevel", 0x80000008); + + /* If level is not large enough, it should increase automatically: */ + /* CPUID[6].EAX: */ + add_cpuid_test("x86/cpuid/auto-level/phenom/arat", + "-cpu 486,+arat", "level", 6); + /* CPUID[EAX=7,ECX=0].EBX: */ + add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase", + "-cpu phenom,+fsgsbase", "level", 7); + /* CPUID[EAX=7,ECX=0].ECX: */ + add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi", + "-cpu phenom,+avx512vbmi", "level", 7); + /* CPUID[EAX=0xd,ECX=1].EAX: */ + add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt", + "-cpu phenom,+xsaveopt", "level", 0xd); + /* CPUID[8000_0001].EDX: */ + add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow", + "-cpu 486,+3dnow", "xlevel", 0x80000001); + /* CPUID[8000_0001].ECX: */ + add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a", + "-cpu 486,+sse4a", "xlevel", 0x80000001); + /* CPUID[8000_0007].EDX: */ + add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc", + "-cpu 486,+invtsc", "xlevel", 0x80000007); + /* CPUID[8000_000A].EDX: */ + add_cpuid_test("x86/cpuid/auto-xlevel/486/npt", + "-cpu 486,+npt", "xlevel", 0x8000000A); + /* CPUID[C000_0001].EDX: */ + add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore", + "-cpu phenom,+xstore", "xlevel2", 0xC0000001); + /* SVM needs CPUID[0x8000000A] */ + add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm", + "-cpu athlon,+svm", "xlevel", 0x8000000A); + + + /* If level is already large enough, it shouldn't change: */ + add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple", + "-cpu SandyBridge,+arat,+fsgsbase,+avx512vbmi", + "level", 0xd); + /* If level is explicitly set, it shouldn't change: */ + add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF", + "-cpu 486,level=0xF,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", + "level", 0xF); + add_cpuid_test("x86/cpuid/auto-level/486/fixed/2", + "-cpu 486,level=2,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", + "level", 2); + add_cpuid_test("x86/cpuid/auto-level/486/fixed/0", + "-cpu 486,level=0,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", + "level", 0); + + /* if xlevel is already large enough, it shouldn't change: */ + add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow", + "-cpu phenom,+3dnow,+sse4a,+invtsc,+npt,+svm", + "xlevel", 0x8000001A); + /* If xlevel is explicitly set, it shouldn't change: */ + add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002", + "-cpu 486,xlevel=0x80000002,+3dnow,+sse4a,+invtsc,+npt,+svm", + "xlevel", 0x80000002); + add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A", + "-cpu 486,xlevel=0x8000001A,+3dnow,+sse4a,+invtsc,+npt,+svm", + "xlevel", 0x8000001A); + add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0", + "-cpu 486,xlevel=0,+3dnow,+sse4a,+invtsc,+npt,+svm", + "xlevel", 0); + + /* if xlevel2 is already large enough, it shouldn't change: */ + add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed", + "-cpu 486,xlevel2=0xC0000002,+xstore", + "xlevel2", 0xC0000002); + + /* Check compatibility of old machine-types that didn't + * auto-increase level/xlevel/xlevel2: */ + + add_cpuid_test("x86/cpuid/auto-level/pc-2.7", + "-machine pc-i440fx-2.7 -cpu 486,+arat,+avx512vbmi,+xsaveopt", + "level", 1); + add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7", + "-machine pc-i440fx-2.7 -cpu 486,+3dnow,+sse4a,+invtsc,+npt,+svm", + "xlevel", 0); + add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7", + "-machine pc-i440fx-2.7 -cpu 486,+xstore", + "xlevel2", 0); + /* + * QEMU 1.4.0 had auto-level enabled for CPUID[7], already, + * and the compat code that sets default level shouldn't + * disable the auto-level=7 code: + */ + add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.4/off", + "-machine pc-i440fx-1.4 -cpu Nehalem", + "level", 2); + add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.5/on", + "-machine pc-i440fx-1.4 -cpu Nehalem,+smap", + "level", 7); + add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off", + "-machine pc-i440fx-2.3 -cpu Penryn", + "level", 4); + add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on", + "-machine pc-i440fx-2.3 -cpu Penryn,+erms", + "level", 7); + add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off", + "-machine pc-i440fx-2.9 -cpu Conroe", + "level", 10); + add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/on", + "-machine pc-i440fx-2.9 -cpu Conroe,+erms", + "level", 10); + + /* + * xlevel doesn't have any feature that triggers auto-level + * code on old machine-types. Just check that the compat code + * is working correctly: + */ + add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3", + "-machine pc-i440fx-2.3 -cpu SandyBridge", + "xlevel", 0x8000000a); + add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off", + "-machine pc-i440fx-2.4 -cpu SandyBridge,", + "xlevel", 0x80000008); + add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on", + "-machine pc-i440fx-2.4 -cpu SandyBridge,+npt", + "xlevel", 0x80000008); + + /* Test feature parsing */ + add_feature_test("x86/cpuid/features/plus", + "-cpu 486,+arat", + 6, 0, "EAX", 2, true); + add_feature_test("x86/cpuid/features/minus", + "-cpu pentium,-mmx", + 1, 0, "EDX", 23, false); + add_feature_test("x86/cpuid/features/on", + "-cpu 486,arat=on", + 6, 0, "EAX", 2, true); + add_feature_test("x86/cpuid/features/off", + "-cpu pentium,mmx=off", + 1, 0, "EDX", 23, false); + add_feature_test("x86/cpuid/features/max-plus-invtsc", + "-cpu max,+invtsc", + 0x80000007, 0, "EDX", 8, true); + add_feature_test("x86/cpuid/features/max-invtsc-on", + "-cpu max,invtsc=on", + 0x80000007, 0, "EDX", 8, true); + add_feature_test("x86/cpuid/features/max-minus-mmx", + "-cpu max,-mmx", + 1, 0, "EDX", 23, false); + add_feature_test("x86/cpuid/features/max-invtsc-on,mmx=off", + "-cpu max,mmx=off", + 1, 0, "EDX", 23, false); + + return g_test_run(); +} diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c new file mode 100644 index 0000000000..f930a96b83 --- /dev/null +++ b/tests/qtest/tmp105-test.c @@ -0,0 +1,120 @@ +/* + * QTest testcase for the TMP105 temperature sensor + * + * Copyright (c) 2012 Andreas Färber + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "libqtest-single.h" +#include "libqos/qgraph.h" +#include "libqos/i2c.h" +#include "qapi/qmp/qdict.h" +#include "hw/misc/tmp105_regs.h" + +#define TMP105_TEST_ID "tmp105-test" +#define TMP105_TEST_ADDR 0x49 + +static int qmp_tmp105_get_temperature(const char *id) +{ + QDict *response; + int ret; + + response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, " + "'property': 'temperature' } }", id); + g_assert(qdict_haskey(response, "return")); + ret = qdict_get_int(response, "return"); + qobject_unref(response); + return ret; +} + +static void qmp_tmp105_set_temperature(const char *id, int value) +{ + QDict *response; + + response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, " + "'property': 'temperature', 'value': %d } }", id, value); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +#define TMP105_PRECISION (1000/16) +static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + value = qmp_tmp105_get_temperature(TMP105_TEST_ID); + g_assert_cmpuint(value, ==, 0); + + value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); + g_assert_cmphex(value, ==, 0); + + qmp_tmp105_set_temperature(TMP105_TEST_ID, 20000); + value = qmp_tmp105_get_temperature(TMP105_TEST_ID); + g_assert_cmpuint(value, ==, 20000); + + value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); + g_assert_cmphex(value, ==, 0x1400); + + qmp_tmp105_set_temperature(TMP105_TEST_ID, 20938); /* 20 + 15/16 */ + value = qmp_tmp105_get_temperature(TMP105_TEST_ID); + g_assert_cmpuint(value, >=, 20938 - TMP105_PRECISION/2); + g_assert_cmpuint(value, <, 20938 + TMP105_PRECISION/2); + + /* Set config */ + i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x60); + value = i2c_get8(i2cdev, TMP105_REG_CONFIG); + g_assert_cmphex(value, ==, 0x60); + + value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); + g_assert_cmphex(value, ==, 0x14f0); + + /* Set precision to 9, 10, 11 bits. */ + i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x00); + g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x00); + value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); + g_assert_cmphex(value, ==, 0x1480); + + i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x20); + g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x20); + value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); + g_assert_cmphex(value, ==, 0x14c0); + + i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x40); + g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x40); + value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); + g_assert_cmphex(value, ==, 0x14e0); + + /* stored precision remains the same */ + value = qmp_tmp105_get_temperature(TMP105_TEST_ID); + g_assert_cmpuint(value, >=, 20938 - TMP105_PRECISION/2); + g_assert_cmpuint(value, <, 20938 + TMP105_PRECISION/2); + + i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x60); + g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x60); + value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); + g_assert_cmphex(value, ==, 0x14f0); + + i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x1234); + g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1234); + i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x4231); + g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4231); +} + +static void tmp105_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "id=" TMP105_TEST_ID ",address=0x49" + }; + add_qi2c_address(&opts, &(QI2CAddress) { 0x49 }); + + qos_node_create_driver("tmp105", i2c_device_create); + qos_node_consumes("tmp105", "i2c-bus", &opts); + + qos_add_test("tx-rx", "tmp105", send_and_receive, NULL); +} +libqos_init(tmp105_register_nodes); diff --git a/tests/qtest/tpm-crb-swtpm-test.c b/tests/qtest/tpm-crb-swtpm-test.c new file mode 100644 index 0000000000..2c4fb8ae29 --- /dev/null +++ b/tests/qtest/tpm-crb-swtpm-test.c @@ -0,0 +1,67 @@ +/* + * QTest testcase for TPM CRB talking to external swtpm and swtpm migration + * + * Copyright (c) 2018 IBM Corporation + * with parts borrowed from migration-test.c that is: + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Berger + * + * 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 + +#include "libqtest.h" +#include "qemu/module.h" +#include "tpm-tests.h" + +typedef struct TestState { + char *src_tpm_path; + char *dst_tpm_path; + char *uri; +} TestState; + +static void tpm_crb_swtpm_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_transfer, "tpm-crb"); +} + +static void tpm_crb_swtpm_migration_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, + tpm_util_crb_transfer, "tpm-crb"); +} + +int main(int argc, char **argv) +{ + int ret; + TestState ts = { 0 }; + + ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); + ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); + ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/tpm/crb-swtpm/test", &ts, tpm_crb_swtpm_test); + qtest_add_data_func("/tpm/crb-swtpm-migration/test", &ts, + tpm_crb_swtpm_migration_test); + ret = g_test_run(); + + g_rmdir(ts.dst_tpm_path); + g_free(ts.dst_tpm_path); + g_rmdir(ts.src_tpm_path); + g_free(ts.src_tpm_path); + g_free(ts.uri); + + return ret; +} diff --git a/tests/qtest/tpm-crb-test.c b/tests/qtest/tpm-crb-test.c new file mode 100644 index 0000000000..632fb7fbd8 --- /dev/null +++ b/tests/qtest/tpm-crb-test.c @@ -0,0 +1,179 @@ +/* + * QTest testcase for TPM CRB + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Marc-André Lureau + * + * 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 + +#include "hw/acpi/tpm.h" +#include "io/channel-socket.h" +#include "libqtest-single.h" +#include "qemu/module.h" +#include "tpm-emu.h" + +#define TPM_CMD "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00" + +static void tpm_crb_test(const void *data) +{ + const TestState *s = data; + uint32_t intfid = readl(TPM_CRB_ADDR_BASE + A_CRB_INTF_ID); + uint32_t csize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE); + uint64_t caddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); + uint32_t rsize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_SIZE); + uint64_t raddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); + uint8_t locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); + uint32_t locctrl = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL); + uint32_t locsts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); + uint32_t sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); + + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceType), ==, 1); + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceVersion), ==, 1); + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapLocality), ==, 0); + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRBIdleBypass), ==, 0); + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapDataXferSizeSupport), + ==, 3); + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapFIFO), ==, 0); + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRB), ==, 1); + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceSelector), ==, 1); + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, RID), ==, 0); + + g_assert_cmpint(csize, >=, 128); + g_assert_cmpint(rsize, >=, 128); + g_assert_cmpint(caddr, >, TPM_CRB_ADDR_BASE); + g_assert_cmpint(raddr, >, TPM_CRB_ADDR_BASE); + + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); + + g_assert_cmpint(locctrl, ==, 0); + + g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, Granted), ==, 0); + g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, beenSeized), ==, 0); + + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 1); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); + + /* request access to locality 0 */ + writeb(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); + + /* granted bit must be set now */ + locsts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); + g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, Granted), ==, 1); + g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, beenSeized), ==, 0); + + /* we must have an assigned locality */ + locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 1); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); + + /* set into ready state */ + writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ, 1); + + /* TPM must not be in the idle state */ + sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 0); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); + + memwrite(caddr, TPM_CMD, sizeof(TPM_CMD)); + + uint32_t start = 1; + uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); + do { + start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); + if ((start & 1) == 0) { + break; + } + } while (g_get_monotonic_time() < end_time); + start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); + g_assert_cmpint(start & 1, ==, 0); + + /* TPM must still not be in the idle state */ + sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 0); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); + + struct tpm_hdr tpm_msg; + memread(raddr, &tpm_msg, sizeof(tpm_msg)); + g_assert_cmpmem(&tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg)); + + /* set TPM into idle state */ + writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ, 2); + + /* idle state must be indicated now */ + sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 1); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); + + /* relinquish locality */ + writel(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 2); + + /* Granted flag must be cleared */ + sts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); + g_assert_cmpint(FIELD_EX32(sts, CRB_LOC_STS, Granted), ==, 0); + g_assert_cmpint(FIELD_EX32(sts, CRB_LOC_STS, beenSeized), ==, 0); + + /* no locality may be assigned */ + locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); + +} + +int main(int argc, char **argv) +{ + int ret; + char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-crb-test.XXXXXX", NULL); + GThread *thread; + TestState test; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + test.addr = g_new0(SocketAddress, 1); + test.addr->type = SOCKET_ADDRESS_TYPE_UNIX; + test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); + g_mutex_init(&test.data_mutex); + g_cond_init(&test.data_cond); + test.data_cond_signal = false; + + thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); + tpm_emu_test_wait_cond(&test); + + args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-crb,tpmdev=dev", + test.addr->u.q_unix.path); + qtest_start(args); + + qtest_add_data_func("/tpm-crb/test", &test, tpm_crb_test); + ret = g_test_run(); + + qtest_end(); + + g_thread_join(thread); + g_unlink(test.addr->u.q_unix.path); + qapi_free_SocketAddress(test.addr); + g_rmdir(tmp_path); + g_free(tmp_path); + g_free(args); + return ret; +} diff --git a/tests/qtest/tpm-emu.c b/tests/qtest/tpm-emu.c new file mode 100644 index 0000000000..c43ac4aef8 --- /dev/null +++ b/tests/qtest/tpm-emu.c @@ -0,0 +1,183 @@ +/* + * Minimal TPM emulator for TPM test cases + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Marc-André Lureau + * + * 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 + +#include "hw/tpm/tpm_ioctl.h" +#include "io/channel-socket.h" +#include "qapi/error.h" +#include "tpm-emu.h" + +void tpm_emu_test_wait_cond(TestState *s) +{ + gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + + g_mutex_lock(&s->data_mutex); + + if (!s->data_cond_signal && + !g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { + g_assert_not_reached(); + } + + s->data_cond_signal = false; + + g_mutex_unlock(&s->data_mutex); +} + +static void *tpm_emu_tpm_thread(void *data) +{ + TestState *s = data; + QIOChannel *ioc = s->tpm_ioc; + + s->tpm_msg = g_new(struct tpm_hdr, 1); + while (true) { + int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len); + + if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) { + break; + } + s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag); + s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len); + g_assert_cmpint(s->tpm_msg->len, >=, minhlen); + g_assert_cmpint(s->tpm_msg->tag, ==, TPM2_ST_NO_SESSIONS); + + s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len); + qio_channel_read(ioc, (char *)&s->tpm_msg->code, + s->tpm_msg->len - minhlen, &error_abort); + s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code); + + /* reply error */ + s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); + s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr)); + s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE); + qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len), + &error_abort); + } + + g_free(s->tpm_msg); + s->tpm_msg = NULL; + object_unref(OBJECT(s->tpm_ioc)); + return NULL; +} + +void *tpm_emu_ctrl_thread(void *data) +{ + TestState *s = data; + QIOChannelSocket *lioc = qio_channel_socket_new(); + QIOChannel *ioc; + + qio_channel_socket_listen_sync(lioc, s->addr, 1, &error_abort); + + g_mutex_lock(&s->data_mutex); + s->data_cond_signal = true; + g_mutex_unlock(&s->data_mutex); + g_cond_signal(&s->data_cond); + + qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); + ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); + g_assert(ioc); + + { + uint32_t cmd = 0; + struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) }; + int *pfd = NULL; + size_t nfd = 0; + + qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort); + cmd = be32_to_cpu(cmd); + g_assert_cmpint(cmd, ==, CMD_SET_DATAFD); + g_assert_cmpint(nfd, ==, 1); + s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort)); + g_free(pfd); + + cmd = 0; + qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort); + + s->emu_tpm_thread = g_thread_new(NULL, tpm_emu_tpm_thread, s); + } + + while (true) { + uint32_t cmd; + ssize_t ret; + + ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL); + if (ret <= 0) { + break; + } + + cmd = be32_to_cpu(cmd); + switch (cmd) { + case CMD_GET_CAPABILITY: { + ptm_cap cap = cpu_to_be64(0x3fff); + qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort); + break; + } + case CMD_INIT: { + ptm_init init; + qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req), + &error_abort); + init.u.resp.tpm_result = 0; + qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp), + &error_abort); + break; + } + case CMD_SHUTDOWN: { + ptm_res res = 0; + qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); + /* the tpm data thread is expected to finish now */ + g_thread_join(s->emu_tpm_thread); + break; + } + case CMD_STOP: { + ptm_res res = 0; + qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); + break; + } + case CMD_SET_BUFFERSIZE: { + ptm_setbuffersize sbs; + qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req), + &error_abort); + sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096); + sbs.u.resp.tpm_result = 0; + sbs.u.resp.minsize = cpu_to_be32(128); + sbs.u.resp.maxsize = cpu_to_be32(4096); + qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp), + &error_abort); + break; + } + case CMD_SET_LOCALITY: { + ptm_loc loc; + /* Note: this time it's not u.req / u.resp... */ + qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort); + g_assert_cmpint(loc.u.req.loc, ==, 0); + loc.u.resp.tpm_result = 0; + qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort); + break; + } + case CMD_GET_TPMESTABLISHED: { + ptm_est est = { + .u.resp.bit = 0, + }; + qio_channel_write(ioc, (char *)&est, sizeof(est), &error_abort); + break; + } + default: + g_debug("unimplemented %u", cmd); + g_assert_not_reached(); + } + } + + object_unref(OBJECT(ioc)); + object_unref(OBJECT(lioc)); + return NULL; +} diff --git a/tests/qtest/tpm-emu.h b/tests/qtest/tpm-emu.h new file mode 100644 index 0000000000..a4f1d64226 --- /dev/null +++ b/tests/qtest/tpm-emu.h @@ -0,0 +1,39 @@ +/* + * Minimal TPM emulator for TPM test cases + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Marc-André Lureau + * + * 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 TESTS_TPM_EMU_H +#define TESTS_TPM_EMU_H + +#define TPM_RC_FAILURE 0x101 +#define TPM2_ST_NO_SESSIONS 0x8001 + +struct tpm_hdr { + uint16_t tag; + uint32_t len; + uint32_t code; /*ordinal/error */ + char buffer[]; +} QEMU_PACKED; + +typedef struct TestState { + GMutex data_mutex; + GCond data_cond; + bool data_cond_signal; + SocketAddress *addr; + QIOChannel *tpm_ioc; + GThread *emu_tpm_thread; + struct tpm_hdr *tpm_msg; +} TestState; + +void tpm_emu_test_wait_cond(TestState *s); +void *tpm_emu_ctrl_thread(void *data); + +#endif /* TESTS_TPM_EMU_H */ diff --git a/tests/qtest/tpm-tests.c b/tests/qtest/tpm-tests.c new file mode 100644 index 0000000000..6e45a0ba85 --- /dev/null +++ b/tests/qtest/tpm-tests.c @@ -0,0 +1,136 @@ +/* + * QTest TPM commont test code + * + * Copyright (c) 2018 IBM Corporation + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Stefan Berger + * Marc-André Lureau + * + * 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 + +#include "libqtest-single.h" +#include "tpm-tests.h" + +static bool +tpm_test_swtpm_skip(void) +{ + if (!tpm_util_swtpm_has_tpm2()) { + g_test_skip("swtpm not in PATH or missing --tpm2 support"); + return true; + } + + return false; +} + +void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, + const char *ifmodel) +{ + char *args = NULL; + QTestState *s; + SocketAddress *addr = NULL; + gboolean succ; + GPid swtpm_pid; + GError *error = NULL; + + if (tpm_test_swtpm_skip()) { + return; + } + + succ = tpm_util_swtpm_start(src_tpm_path, &swtpm_pid, &addr, &error); + g_assert_true(succ); + + args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device %s,tpmdev=dev", + addr->u.q_unix.path, ifmodel); + + s = qtest_start(args); + g_free(args); + + tpm_util_startup(s, tx); + tpm_util_pcrextend(s, tx); + + unsigned char tpm_pcrread_resp[] = + "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" + "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" + "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" + "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; + tpm_util_pcrread(s, tx, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + qtest_end(); + tpm_util_swtpm_kill(swtpm_pid); + + if (addr) { + g_unlink(addr->u.q_unix.path); + qapi_free_SocketAddress(addr); + } +} + +void tpm_test_swtpm_migration_test(const char *src_tpm_path, + const char *dst_tpm_path, + const char *uri, tx_func *tx, + const char *ifmodel) +{ + gboolean succ; + GPid src_tpm_pid, dst_tpm_pid; + SocketAddress *src_tpm_addr = NULL, *dst_tpm_addr = NULL; + GError *error = NULL; + QTestState *src_qemu, *dst_qemu; + + if (tpm_test_swtpm_skip()) { + return; + } + + succ = tpm_util_swtpm_start(src_tpm_path, &src_tpm_pid, + &src_tpm_addr, &error); + g_assert_true(succ); + + succ = tpm_util_swtpm_start(dst_tpm_path, &dst_tpm_pid, + &dst_tpm_addr, &error); + g_assert_true(succ); + + tpm_util_migration_start_qemu(&src_qemu, &dst_qemu, + src_tpm_addr, dst_tpm_addr, uri, + ifmodel); + + tpm_util_startup(src_qemu, tx); + tpm_util_pcrextend(src_qemu, tx); + + unsigned char tpm_pcrread_resp[] = + "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" + "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" + "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" + "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; + tpm_util_pcrread(src_qemu, tx, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + tpm_util_migrate(src_qemu, uri); + tpm_util_wait_for_migration_complete(src_qemu); + + tpm_util_pcrread(dst_qemu, tx, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + qtest_quit(dst_qemu); + qtest_quit(src_qemu); + + tpm_util_swtpm_kill(dst_tpm_pid); + if (dst_tpm_addr) { + g_unlink(dst_tpm_addr->u.q_unix.path); + qapi_free_SocketAddress(dst_tpm_addr); + } + + tpm_util_swtpm_kill(src_tpm_pid); + if (src_tpm_addr) { + g_unlink(src_tpm_addr->u.q_unix.path); + qapi_free_SocketAddress(src_tpm_addr); + } +} diff --git a/tests/qtest/tpm-tests.h b/tests/qtest/tpm-tests.h new file mode 100644 index 0000000000..b97688fe75 --- /dev/null +++ b/tests/qtest/tpm-tests.h @@ -0,0 +1,26 @@ +/* + * QTest TPM commont test code + * + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Stefan Berger + * + * 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 TESTS_TPM_TESTS_H +#define TESTS_TPM_TESTS_H + +#include "tpm-util.h" + +void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, + const char *ifmodel); + +void tpm_test_swtpm_migration_test(const char *src_tpm_path, + const char *dst_tpm_path, + const char *uri, tx_func *tx, + const char *ifmodel); + +#endif /* TESTS_TPM_TESTS_H */ diff --git a/tests/qtest/tpm-tis-swtpm-test.c b/tests/qtest/tpm-tis-swtpm-test.c new file mode 100644 index 0000000000..9f58a3a92b --- /dev/null +++ b/tests/qtest/tpm-tis-swtpm-test.c @@ -0,0 +1,67 @@ +/* + * QTest testcase for TPM TIS talking to external swtpm and swtpm migration + * + * Copyright (c) 2018 IBM Corporation + * with parts borrowed from migration-test.c that is: + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Berger + * + * 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 + +#include "libqtest.h" +#include "qemu/module.h" +#include "tpm-tests.h" + +typedef struct TestState { + char *src_tpm_path; + char *dst_tpm_path; + char *uri; +} TestState; + +static void tpm_tis_swtpm_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_tis_transfer, "tpm-tis"); +} + +static void tpm_tis_swtpm_migration_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, + tpm_util_tis_transfer, "tpm-tis"); +} + +int main(int argc, char **argv) +{ + int ret; + TestState ts = { 0 }; + + ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-tis-swtpm-test.XXXXXX", NULL); + ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-tis-swtpm-test.XXXXXX", NULL); + ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/tpm/tis-swtpm/test", &ts, tpm_tis_swtpm_test); + qtest_add_data_func("/tpm/tis-swtpm-migration/test", &ts, + tpm_tis_swtpm_migration_test); + ret = g_test_run(); + + g_rmdir(ts.dst_tpm_path); + g_free(ts.dst_tpm_path); + g_rmdir(ts.src_tpm_path); + g_free(ts.src_tpm_path); + g_free(ts.uri); + + return ret; +} diff --git a/tests/qtest/tpm-tis-test.c b/tests/qtest/tpm-tis-test.c new file mode 100644 index 0000000000..dcf30e05b7 --- /dev/null +++ b/tests/qtest/tpm-tis-test.c @@ -0,0 +1,488 @@ +/* + * QTest testcase for TPM TIS + * + * Copyright (c) 2018 Red Hat, Inc. + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Marc-André Lureau + * Stefan Berger + * + * 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 + +#include "hw/acpi/tpm.h" +#include "io/channel-socket.h" +#include "libqtest-single.h" +#include "qemu/module.h" +#include "tpm-emu.h" + +#define TIS_REG(LOCTY, REG) \ + (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG) + +#define DEBUG_TIS_TEST 0 + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_TIS_TEST) { \ + printf(fmt, ## __VA_ARGS__); \ + } \ +} while (0) + +#define DPRINTF_ACCESS \ + DPRINTF("%s: %d: locty=%d l=%d access=0x%02x pending_request_flag=0x%x\n", \ + __func__, __LINE__, locty, l, access, pending_request_flag) + +#define DPRINTF_STS \ + DPRINTF("%s: %d: sts = 0x%08x\n", __func__, __LINE__, sts) + +static const uint8_t TPM_CMD[12] = + "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; + +static void tpm_tis_test_check_localities(const void *data) +{ + uint8_t locty; + uint8_t access; + uint32_t ifaceid; + uint32_t capability; + uint32_t didvid; + uint32_t rid; + + for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES; locty++) { + access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + capability = readl(TIS_REG(locty, TPM_TIS_REG_INTF_CAPABILITY)); + g_assert_cmpint(capability, ==, TPM_TIS_CAPABILITIES_SUPPORTED2_0); + + ifaceid = readl(TIS_REG(locty, TPM_TIS_REG_INTERFACE_ID)); + g_assert_cmpint(ifaceid, ==, TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0); + + didvid = readl(TIS_REG(locty, TPM_TIS_REG_DID_VID)); + g_assert_cmpint(didvid, !=, 0); + g_assert_cmpint(didvid, !=, 0xffffffff); + + rid = readl(TIS_REG(locty, TPM_TIS_REG_RID)); + g_assert_cmpint(rid, !=, 0); + g_assert_cmpint(rid, !=, 0xffffffff); + } +} + +static void tpm_tis_test_check_access_reg(const void *data) +{ + uint8_t locty; + uint8_t access; + + /* do not test locality 4 (hw only) */ + for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) { + access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* request use of locality */ + writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + + access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* release access */ + writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + } +} + +/* + * Test case for seizing access by a higher number locality + */ +static void tpm_tis_test_check_access_reg_seize(const void *data) +{ + int locty, l; + uint8_t access; + uint8_t pending_request_flag; + + /* do not test locality 4 (hw only) */ + for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) { + pending_request_flag = 0; + + access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* request use of locality */ + writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* lower localities cannot seize access */ + for (l = 0; l < locty; l++) { + /* lower locality is not active */ + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* try to request use from 'l' */ + writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + + /* requesting use from 'l' was not possible; + we must see REQUEST_USE and possibly PENDING_REQUEST */ + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_REQUEST_USE | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* locality 'locty' must be unchanged; + we must see PENDING_REQUEST */ + access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_PENDING_REQUEST | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* try to seize from 'l' */ + writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_SEIZE); + /* seize from 'l' was not possible */ + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_REQUEST_USE | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* locality 'locty' must be unchanged */ + access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_PENDING_REQUEST | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* on the next loop we will have a PENDING_REQUEST flag + set for locality 'l' */ + pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST; + } + + /* higher localities can 'seize' access but not 'request use'; + note: this will activate first l+1, then l+2 etc. */ + for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { + /* try to 'request use' from 'l' */ + writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + + /* requesting use from 'l' was not possible; we should see + REQUEST_USE and may see PENDING_REQUEST */ + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_REQUEST_USE | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* locality 'l-1' must be unchanged; we should always + see PENDING_REQUEST from 'l' requesting access */ + access = readb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_PENDING_REQUEST | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* try to seize from 'l' */ + writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_SEIZE); + + /* seize from 'l' was possible */ + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* l - 1 should show that it has BEEN_SEIZED */ + access = readb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_BEEN_SEIZED | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* clear the BEEN_SEIZED flag and make sure it's gone */ + writeb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS), + TPM_TIS_ACCESS_BEEN_SEIZED); + + access = readb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + } + + /* PENDING_REQUEST will not be set if locty = 0 since all localities + were active; in case of locty = 1, locality 0 will be active + but no PENDING_REQUEST anywhere */ + if (locty <= 1) { + pending_request_flag = 0; + } + + /* release access from l - 1; this activates locty - 1 */ + l--; + + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + + DPRINTF("%s: %d: relinquishing control on l = %d\n", + __func__, __LINE__, l); + writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + for (l = locty - 1; l >= 0; l--) { + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* release this locality */ + writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + + if (l == 1) { + pending_request_flag = 0; + } + } + + /* no locality may be active now */ + for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + } + } +} + +/* + * Test case for getting access when higher number locality relinquishes access + */ +static void tpm_tis_test_check_access_reg_release(const void *data) +{ + int locty, l; + uint8_t access; + uint8_t pending_request_flag; + + /* do not test locality 4 (hw only) */ + for (locty = TPM_TIS_NUM_LOCALITIES - 2; locty >= 0; locty--) { + pending_request_flag = 0; + + access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* request use of locality */ + writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* request use of all other localities */ + for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { + if (l == locty) { + continue; + } + /* request use of locality 'l' -- we MUST see REQUEST USE and + may see PENDING_REQUEST */ + writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_REQUEST_USE | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST; + } + /* release locality 'locty' */ + writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + /* highest locality should now be active; release it and make sure the + next higest locality is active afterwards */ + for (l = TPM_TIS_NUM_LOCALITIES - 2; l >= 0; l--) { + if (l == locty) { + continue; + } + /* 'l' should be active now */ + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + /* 'l' relinquishes access */ + writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); + DPRINTF_ACCESS; + if (l == 1 || (locty <= 1 && l == 2)) { + pending_request_flag = 0; + } + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + } + } +} + +/* + * Test case for transmitting packets + */ +static void tpm_tis_test_check_transmit(const void *data) +{ + const TestState *s = data; + uint8_t access; + uint32_t sts; + uint16_t bcount; + size_t i; + + /* request use of locality 0 */ + writeb(TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); + DPRINTF_STS; + + g_assert_cmpint(sts & 0xff, ==, 0); + g_assert_cmpint(sts & TPM_TIS_STS_TPM_FAMILY_MASK, ==, + TPM_TIS_STS_TPM_FAMILY2_0); + + bcount = (sts >> 8) & 0xffff; + g_assert_cmpint(bcount, >=, 128); + + writel(TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY); + sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); + DPRINTF_STS; + g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_COMMAND_READY); + + /* transmit command */ + for (i = 0; i < sizeof(TPM_CMD); i++) { + writeb(TIS_REG(0, TPM_TIS_REG_DATA_FIFO), TPM_CMD[i]); + sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); + DPRINTF_STS; + if (i < sizeof(TPM_CMD) - 1) { + g_assert_cmpint(sts & 0xff, ==, + TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); + } else { + g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_VALID); + } + g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount); + } + /* start processing */ + writeb(TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO); + + uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; + do { + sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); + if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { + break; + } + } while (g_get_monotonic_time() < end_time); + + sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); + DPRINTF_STS; + g_assert_cmpint(sts & 0xff, == , + TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); + bcount = (sts >> 8) & 0xffff; + + /* read response */ + uint8_t tpm_msg[sizeof(struct tpm_hdr)]; + g_assert_cmpint(sizeof(tpm_msg), ==, bcount); + + for (i = 0; i < sizeof(tpm_msg); i++) { + tpm_msg[i] = readb(TIS_REG(0, TPM_TIS_REG_DATA_FIFO)); + sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); + DPRINTF_STS; + if (sts & TPM_TIS_STS_DATA_AVAILABLE) { + g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount); + } + } + g_assert_cmpmem(tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg)); + + /* relinquish use of locality 0 */ + writeb(TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_ACTIVE_LOCALITY); + access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); +} + +int main(int argc, char **argv) +{ + int ret; + char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-tis-test.XXXXXX", NULL); + GThread *thread; + TestState test; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + test.addr = g_new0(SocketAddress, 1); + test.addr->type = SOCKET_ADDRESS_TYPE_UNIX; + test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); + g_mutex_init(&test.data_mutex); + g_cond_init(&test.data_cond); + test.data_cond_signal = false; + + thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); + tpm_emu_test_wait_cond(&test); + + args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-tis,tpmdev=dev", + test.addr->u.q_unix.path); + qtest_start(args); + + qtest_add_data_func("/tpm-tis/test_check_localities", &test, + tpm_tis_test_check_localities); + + qtest_add_data_func("/tpm-tis/test_check_access_reg", &test, + tpm_tis_test_check_access_reg); + + qtest_add_data_func("/tpm-tis/test_check_access_reg_seize", &test, + tpm_tis_test_check_access_reg_seize); + + qtest_add_data_func("/tpm-tis/test_check_access_reg_release", &test, + tpm_tis_test_check_access_reg_release); + + qtest_add_data_func("/tpm-tis/test_check_transmit", &test, + tpm_tis_test_check_transmit); + + ret = g_test_run(); + + qtest_end(); + + g_thread_join(thread); + g_unlink(test.addr->u.q_unix.path); + qapi_free_SocketAddress(test.addr); + g_rmdir(tmp_path); + g_free(tmp_path); + g_free(args); + return ret; +} diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c new file mode 100644 index 0000000000..e08b137651 --- /dev/null +++ b/tests/qtest/tpm-util.c @@ -0,0 +1,285 @@ +/* + * QTest TPM utilities + * + * Copyright (c) 2018 IBM Corporation + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Stefan Berger + * Marc-André Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "hw/acpi/tpm.h" +#include "libqtest.h" +#include "tpm-util.h" +#include "qapi/qmp/qdict.h" + +#define TIS_REG(LOCTY, REG) \ + (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG) + +void tpm_util_crb_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size) +{ + uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); + uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); + + qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); + + qtest_memwrite(s, caddr, req, req_size); + + uint32_t sts, start = 1; + uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); + while (true) { + start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); + if ((start & 1) == 0) { + break; + } + if (g_get_monotonic_time() >= end_time) { + break; + } + }; + start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); + g_assert_cmpint(start & 1, ==, 0); + sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); + g_assert_cmpint(sts & 1, ==, 0); + + qtest_memread(s, raddr, rsp, rsp_size); +} + +void tpm_util_tis_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size) +{ + uint32_t sts; + uint16_t bcount; + size_t i; + + /* request use of locality 0 */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + qtest_writel(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY); + + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + bcount = (sts >> 8) & 0xffff; + g_assert_cmpint(bcount, >=, req_size); + + /* transmit command */ + for (i = 0; i < req_size; i++) { + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO), req[i]); + } + + /* start processing */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO); + + uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; + do { + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { + break; + } + } while (g_get_monotonic_time() < end_time); + + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + bcount = (sts >> 8) & 0xffff; + + memset(rsp, 0, rsp_size); + for (i = 0; i < bcount; i++) { + rsp[i] = qtest_readb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO)); + } + + /* relinquish use of locality 0 */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), + TPM_TIS_ACCESS_ACTIVE_LOCALITY); +} + +void tpm_util_startup(QTestState *s, tx_func *tx) +{ + unsigned char buffer[1024]; + unsigned char tpm_startup[] = + "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; + unsigned char tpm_startup_resp[] = + "\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00"; + + tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, sizeof(tpm_startup_resp), + tpm_startup_resp, sizeof(tpm_startup_resp)); +} + +void tpm_util_pcrextend(QTestState *s, tx_func *tx) +{ + unsigned char buffer[1024]; + unsigned char tpm_pcrextend[] = + "\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00" + "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + "\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00"; + + unsigned char tpm_pcrextend_resp[] = + "\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x01\x00\x00"; + + tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp), + tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp)); +} + +void tpm_util_pcrread(QTestState *s, tx_func *tx, + const unsigned char *exp_resp, size_t exp_resp_size) +{ + unsigned char buffer[1024]; + unsigned char tpm_pcrread[] = + "\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b" + "\x03\x00\x04\x00"; + + tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size); +} + +bool tpm_util_swtpm_has_tpm2(void) +{ + bool has_tpm2 = false; + char *out = NULL; + static const char *argv[] = { + "swtpm", "socket", "--help", NULL + }; + + if (!g_spawn_sync(NULL /* working_dir */, + (char **)argv, + NULL /* envp */, + G_SPAWN_SEARCH_PATH, + NULL /* child_setup */, + NULL /* user_data */, + &out, + NULL /* err */, + NULL /* exit_status */, + NULL)) { + return false; + } + + if (strstr(out, "--tpm2")) { + has_tpm2 = true; + } + + g_free(out); + return has_tpm2; +} + +gboolean tpm_util_swtpm_start(const char *path, GPid *pid, + SocketAddress **addr, GError **error) +{ + char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path); + char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock", + path); + gchar *swtpm_argv[] = { + g_strdup("swtpm"), g_strdup("socket"), + g_strdup("--tpmstate"), swtpm_argv_tpmstate, + g_strdup("--ctrl"), swtpm_argv_ctrl, + g_strdup("--tpm2"), + NULL + }; + gboolean succ; + unsigned i; + + *addr = g_new0(SocketAddress, 1); + (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX; + (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL); + + succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, pid, error); + + for (i = 0; swtpm_argv[i]; i++) { + g_free(swtpm_argv[i]); + } + + return succ; +} + +void tpm_util_swtpm_kill(GPid pid) +{ + int n; + + if (!pid) { + return; + } + + g_spawn_close_pid(pid); + + n = kill(pid, 0); + if (n < 0) { + return; + } + + kill(pid, SIGKILL); +} + +void tpm_util_migrate(QTestState *who, const char *uri) +{ + QDict *rsp; + + rsp = qtest_qmp(who, + "{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", + uri); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +void tpm_util_wait_for_migration_complete(QTestState *who) +{ + while (true) { + QDict *rsp_return; + bool completed; + const char *status; + + qtest_qmp_send(who, "{ 'execute': 'query-migrate' }"); + rsp_return = qtest_qmp_receive_success(who, NULL, NULL); + status = qdict_get_str(rsp_return, "status"); + completed = strcmp(status, "completed") == 0; + g_assert_cmpstr(status, !=, "failed"); + qobject_unref(rsp_return); + if (completed) { + return; + } + usleep(1000); + } +} + +void tpm_util_migration_start_qemu(QTestState **src_qemu, + QTestState **dst_qemu, + SocketAddress *src_tpm_addr, + SocketAddress *dst_tpm_addr, + const char *miguri, + const char *ifmodel) +{ + char *src_qemu_args, *dst_qemu_args; + + src_qemu_args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device %s,tpmdev=dev ", + src_tpm_addr->u.q_unix.path, ifmodel); + + *src_qemu = qtest_init(src_qemu_args); + + dst_qemu_args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device %s,tpmdev=dev " + "-incoming %s", + dst_tpm_addr->u.q_unix.path, + ifmodel, miguri); + + *dst_qemu = qtest_init(dst_qemu_args); + + free(src_qemu_args); + free(dst_qemu_args); +} diff --git a/tests/qtest/tpm-util.h b/tests/qtest/tpm-util.h new file mode 100644 index 0000000000..5755698ad2 --- /dev/null +++ b/tests/qtest/tpm-util.h @@ -0,0 +1,51 @@ +/* + * QTest TPM utilities + * + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Stefan Berger + * + * 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 TESTS_TPM_UTIL_H +#define TESTS_TPM_UTIL_H + +#include "io/channel-socket.h" + +typedef void (tx_func)(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); + +void tpm_util_crb_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); +void tpm_util_tis_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); + +void tpm_util_startup(QTestState *s, tx_func *tx); +void tpm_util_pcrextend(QTestState *s, tx_func *tx); +void tpm_util_pcrread(QTestState *s, tx_func *tx, + const unsigned char *exp_resp, size_t exp_resp_size); + +bool tpm_util_swtpm_has_tpm2(void); + +gboolean tpm_util_swtpm_start(const char *path, GPid *pid, + SocketAddress **addr, GError **error); +void tpm_util_swtpm_kill(GPid pid); + +void tpm_util_migrate(QTestState *who, const char *uri); + +void tpm_util_migration_start_qemu(QTestState **src_qemu, + QTestState **dst_qemu, + SocketAddress *src_tpm_addr, + SocketAddress *dst_tpm_addr, + const char *miguri, + const char *ifmodel); + +void tpm_util_wait_for_migration_complete(QTestState *who); + +#endif /* TESTS_TPM_UTIL_H */ diff --git a/tests/qtest/usb-hcd-ehci-test.c b/tests/qtest/usb-hcd-ehci-test.c new file mode 100644 index 0000000000..5251d539e9 --- /dev/null +++ b/tests/qtest/usb-hcd-ehci-test.c @@ -0,0 +1,178 @@ +/* + * QTest testcase for USB EHCI + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "libqos/pci-pc.h" +#include "hw/usb/uhci-regs.h" +#include "hw/usb/ehci-regs.h" +#include "libqos/usb.h" + +static QPCIBus *pcibus; +static struct qhc uhci1; +static struct qhc uhci2; +static struct qhc uhci3; +static struct qhc ehci1; + +/* helpers */ + +#if 0 +static void uhci_port_update(struct qhc *hc, int port, + uint16_t set, uint16_t clear) +{ + void *addr = hc->base + 0x10 + 2 * port; + uint16_t value; + + value = qpci_io_readw(hc->dev, addr); + value |= set; + value &= ~clear; + qpci_io_writew(hc->dev, addr, value); +} +#endif + +static void ehci_port_test(struct qhc *hc, int port, uint32_t expect) +{ + uint32_t value = qpci_io_readl(hc->dev, hc->bar, 0x64 + 4 * port); + uint16_t mask = ~(PORTSC_CSC | PORTSC_PEDC | PORTSC_OCC); + +#if 0 + fprintf(stderr, "%s: %d, have 0x%08x, want 0x%08x\n", + __func__, port, value & mask, expect & mask); +#endif + g_assert((value & mask) == (expect & mask)); +} + +/* tests */ + +static void test_init(void) +{ + pcibus = qpci_new_pc(global_qtest, NULL); + g_assert(pcibus != NULL); + + qusb_pci_init_one(pcibus, &uhci1, QPCI_DEVFN(0x1d, 0), 4); + qusb_pci_init_one(pcibus, &uhci2, QPCI_DEVFN(0x1d, 1), 4); + qusb_pci_init_one(pcibus, &uhci3, QPCI_DEVFN(0x1d, 2), 4); + qusb_pci_init_one(pcibus, &ehci1, QPCI_DEVFN(0x1d, 7), 0); +} + +static void test_deinit(void) +{ + uhci_deinit(&uhci1); + uhci_deinit(&uhci2); + uhci_deinit(&uhci3); + uhci_deinit(&ehci1); + qpci_free_pc(pcibus); +} + +static void pci_uhci_port_1(void) +{ + g_assert(pcibus != NULL); + + uhci_port_test(&uhci1, 0, UHCI_PORT_CCS); /* usb-tablet */ + uhci_port_test(&uhci1, 1, UHCI_PORT_CCS); /* usb-storage */ + uhci_port_test(&uhci2, 0, 0); + uhci_port_test(&uhci2, 1, 0); + uhci_port_test(&uhci3, 0, 0); + uhci_port_test(&uhci3, 1, 0); +} + +static void pci_ehci_port_1(void) +{ + int i; + + g_assert(pcibus != NULL); + + for (i = 0; i < 6; i++) { + ehci_port_test(&ehci1, i, PORTSC_POWNER | PORTSC_PPOWER); + } +} + +static void pci_ehci_config(void) +{ + /* hands over all ports from companion uhci to ehci */ + qpci_io_writew(ehci1.dev, ehci1.bar, 0x60, 1); +} + +static void pci_uhci_port_2(void) +{ + g_assert(pcibus != NULL); + + uhci_port_test(&uhci1, 0, 0); /* usb-tablet, @ehci */ + uhci_port_test(&uhci1, 1, 0); /* usb-storage, @ehci */ + uhci_port_test(&uhci2, 0, 0); + uhci_port_test(&uhci2, 1, 0); + uhci_port_test(&uhci3, 0, 0); + uhci_port_test(&uhci3, 1, 0); +} + +static void pci_ehci_port_2(void) +{ + static uint32_t expect[] = { + PORTSC_PPOWER | PORTSC_CONNECT, /* usb-tablet */ + PORTSC_PPOWER | PORTSC_CONNECT, /* usb-storage */ + PORTSC_PPOWER, + PORTSC_PPOWER, + PORTSC_PPOWER, + PORTSC_PPOWER, + }; + int i; + + g_assert(pcibus != NULL); + + for (i = 0; i < 6; i++) { + ehci_port_test(&ehci1, i, expect[i]); + } +} + +static void pci_ehci_port_3_hotplug(void) +{ + /* check for presence of hotplugged usb-tablet */ + g_assert(pcibus != NULL); + ehci_port_test(&ehci1, 2, PORTSC_PPOWER | PORTSC_CONNECT); +} + +static void pci_ehci_port_hotplug(void) +{ + usb_test_hotplug(global_qtest, "ich9-ehci-1", "3", pci_ehci_port_3_hotplug); +} + + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/ehci/pci/uhci-port-1", pci_uhci_port_1); + qtest_add_func("/ehci/pci/ehci-port-1", pci_ehci_port_1); + qtest_add_func("/ehci/pci/ehci-config", pci_ehci_config); + qtest_add_func("/ehci/pci/uhci-port-2", pci_uhci_port_2); + qtest_add_func("/ehci/pci/ehci-port-2", pci_ehci_port_2); + qtest_add_func("/ehci/pci/ehci-port-3-hotplug", pci_ehci_port_hotplug); + + qtest_start("-machine q35 -device ich9-usb-ehci1,bus=pcie.0,addr=1d.7," + "multifunction=on,id=ich9-ehci-1 " + "-device ich9-usb-uhci1,bus=pcie.0,addr=1d.0," + "multifunction=on,masterbus=ich9-ehci-1.0,firstport=0 " + "-device ich9-usb-uhci2,bus=pcie.0,addr=1d.1," + "multifunction=on,masterbus=ich9-ehci-1.0,firstport=2 " + "-device ich9-usb-uhci3,bus=pcie.0,addr=1d.2," + "multifunction=on,masterbus=ich9-ehci-1.0,firstport=4 " + "-drive if=none,id=usbcdrom,media=cdrom " + "-device usb-tablet,bus=ich9-ehci-1.0,port=1,usb_version=1 " + "-device usb-storage,bus=ich9-ehci-1.0,port=2,drive=usbcdrom "); + + test_init(); + ret = g_test_run(); + test_deinit(); + + qtest_end(); + + return ret; +} diff --git a/tests/qtest/usb-hcd-ohci-test.c b/tests/qtest/usb-hcd-ohci-test.c new file mode 100644 index 0000000000..19d760f3fb --- /dev/null +++ b/tests/qtest/usb-hcd-ohci-test.c @@ -0,0 +1,68 @@ +/* + * QTest testcase for USB OHCI controller + * + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "qemu/module.h" +#include "libqos/usb.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QOHCI_PCI QOHCI_PCI; + +struct QOHCI_PCI { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void test_ohci_hotplug(void *obj, void *data, QGuestAllocator *alloc) +{ + usb_test_hotplug(global_qtest, "ohci", "1", NULL); +} + +static void *ohci_pci_get_driver(void *obj, const char *interface) +{ + QOHCI_PCI *ohci_pci = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &ohci_pci->dev; + } + + fprintf(stderr, "%s not present in pci-ohci\n", interface); + g_assert_not_reached(); +} + +static void *ohci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QOHCI_PCI *ohci_pci = g_new0(QOHCI_PCI, 1); + ohci_pci->obj.get_driver = ohci_pci_get_driver; + + return &ohci_pci->obj; +} + +static void ohci_pci_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,id=ohci", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("pci-ohci", ohci_pci_create); + qos_node_consumes("pci-ohci", "pci-bus", &opts); + qos_node_produces("pci-ohci", "pci-device"); +} + +libqos_init(ohci_pci_register_nodes); + +static void register_ohci_pci_test(void) +{ + qos_add_test("ohci_pci-test-hotplug", "pci-ohci", test_ohci_hotplug, NULL); +} + +libqos_init(register_ohci_pci_test); diff --git a/tests/qtest/usb-hcd-uhci-test.c b/tests/qtest/usb-hcd-uhci-test.c new file mode 100644 index 0000000000..7a117b64d9 --- /dev/null +++ b/tests/qtest/usb-hcd-uhci-test.c @@ -0,0 +1,88 @@ +/* + * QTest testcase for USB UHCI controller + * + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "libqos/libqos.h" +#include "libqos/usb.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" +#include "hw/usb/uhci-regs.h" + +static QOSState *qs; + +static void test_uhci_init(void) +{ +} + +static void test_port(int port) +{ + struct qhc uhci; + + g_assert(port > 0); + qusb_pci_init_one(qs->pcibus, &uhci, QPCI_DEVFN(0x1d, 0), 4); + uhci_port_test(&uhci, port - 1, UHCI_PORT_CCS); + uhci_deinit(&uhci); +} + +static void test_port_1(void) +{ + test_port(1); +} + +static void test_port_2(void) +{ + test_port(2); +} + +static void test_uhci_hotplug(void) +{ + usb_test_hotplug(global_qtest, "uhci", "2", test_port_2); +} + +static void test_usb_storage_hotplug(void) +{ + QTestState *qts = global_qtest; + + qtest_qmp_device_add(qts, "usb-storage", "usbdev0", "{'drive': 'drive0'}"); + + qtest_qmp_device_del(qts, "usbdev0"); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + const char *cmd = "-device piix3-usb-uhci,id=uhci,addr=1d.0" + " -drive id=drive0,if=none,file=null-co://," + "file.read-zeroes=on,format=raw" + " -device usb-tablet,bus=uhci.0,port=1"; + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/uhci/pci/init", test_uhci_init); + qtest_add_func("/uhci/pci/port1", test_port_1); + qtest_add_func("/uhci/pci/hotplug", test_uhci_hotplug); + qtest_add_func("/uhci/pci/hotplug/usb-storage", test_usb_storage_hotplug); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qs = qtest_pc_boot(cmd); + } else if (strcmp(arch, "ppc64") == 0) { + qs = qtest_spapr_boot(cmd); + } else { + g_printerr("usb-hcd-uhci-test tests are only " + "available on x86 or ppc64\n"); + exit(EXIT_FAILURE); + } + global_qtest = qs->qts; + ret = g_test_run(); + qtest_shutdown(qs); + + return ret; +} diff --git a/tests/qtest/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-test.c new file mode 100644 index 0000000000..10ef9d2a91 --- /dev/null +++ b/tests/qtest/usb-hcd-xhci-test.c @@ -0,0 +1,69 @@ +/* + * QTest testcase for USB xHCI controller + * + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "libqos/usb.h" + + +static void test_xhci_init(void) +{ +} + +static void test_xhci_hotplug(void) +{ + usb_test_hotplug(global_qtest, "xhci", "1", NULL); +} + +static void test_usb_uas_hotplug(void) +{ + QTestState *qts = global_qtest; + + qtest_qmp_device_add(qts, "usb-uas", "uas", "{}"); + qtest_qmp_device_add(qts, "scsi-hd", "scsihd", "{'drive': 'drive0'}"); + + /* TODO: + UAS HBA driver in libqos, to check that + added disk is visible after BUS rescan + */ + + qtest_qmp_device_del(qts, "scsihd"); + qtest_qmp_device_del(qts, "uas"); +} + +static void test_usb_ccid_hotplug(void) +{ + QTestState *qts = global_qtest; + + qtest_qmp_device_add(qts, "usb-ccid", "ccid", "{}"); + qtest_qmp_device_del(qts, "ccid"); + /* check the device can be added again */ + qtest_qmp_device_add(qts, "usb-ccid", "ccid", "{}"); + qtest_qmp_device_del(qts, "ccid"); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/xhci/pci/init", test_xhci_init); + qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); + qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); + qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug); + + qtest_start("-device nec-usb-xhci,id=xhci" + " -drive id=drive0,if=none,file=null-co://," + "file.read-zeroes=on,format=raw"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c new file mode 100644 index 0000000000..91ea373ba5 --- /dev/null +++ b/tests/qtest/vhost-user-test.c @@ -0,0 +1,967 @@ +/* + * QTest testcase for the vhost-user + * + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "libqtest-single.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "qemu/config-file.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" +#include "chardev/char-fe.h" +#include "qemu/memfd.h" +#include "qemu/module.h" +#include "sysemu/sysemu.h" +#include "libqos/libqos.h" +#include "libqos/pci-pc.h" +#include "libqos/virtio-pci.h" + +#include "libqos/malloc-pc.h" +#include "hw/virtio/virtio-net.h" + +#include "standard-headers/linux/vhost_types.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_net.h" + +#ifdef CONFIG_LINUX +#include +#endif + + +#define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM," \ + "mem-path=%s,share=on -numa node,memdev=mem" +#define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \ + " -numa node,memdev=mem" +#define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s" +#define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce" + +#define HUGETLBFS_MAGIC 0x958458f6 + +/*********** FROM hw/virtio/vhost-user.c *************************************/ + +#define VHOST_MEMORY_MAX_NREGIONS 8 +#define VHOST_MAX_VIRTQUEUES 0x100 + +#define VHOST_USER_F_PROTOCOL_FEATURES 30 +#define VHOST_USER_PROTOCOL_F_MQ 0 +#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1 +#define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6 + +#define VHOST_LOG_PAGE 0x1000 + +typedef enum VhostUserRequest { + VHOST_USER_NONE = 0, + VHOST_USER_GET_FEATURES = 1, + VHOST_USER_SET_FEATURES = 2, + VHOST_USER_SET_OWNER = 3, + VHOST_USER_RESET_OWNER = 4, + VHOST_USER_SET_MEM_TABLE = 5, + VHOST_USER_SET_LOG_BASE = 6, + VHOST_USER_SET_LOG_FD = 7, + VHOST_USER_SET_VRING_NUM = 8, + VHOST_USER_SET_VRING_ADDR = 9, + VHOST_USER_SET_VRING_BASE = 10, + VHOST_USER_GET_VRING_BASE = 11, + VHOST_USER_SET_VRING_KICK = 12, + VHOST_USER_SET_VRING_CALL = 13, + VHOST_USER_SET_VRING_ERR = 14, + VHOST_USER_GET_PROTOCOL_FEATURES = 15, + VHOST_USER_SET_PROTOCOL_FEATURES = 16, + VHOST_USER_GET_QUEUE_NUM = 17, + VHOST_USER_SET_VRING_ENABLE = 18, + VHOST_USER_MAX +} VhostUserRequest; + +typedef struct VhostUserMemoryRegion { + uint64_t guest_phys_addr; + uint64_t memory_size; + uint64_t userspace_addr; + uint64_t mmap_offset; +} VhostUserMemoryRegion; + +typedef struct VhostUserMemory { + uint32_t nregions; + uint32_t padding; + VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; +} VhostUserMemory; + +typedef struct VhostUserLog { + uint64_t mmap_size; + uint64_t mmap_offset; +} VhostUserLog; + +typedef struct VhostUserMsg { + VhostUserRequest request; + +#define VHOST_USER_VERSION_MASK (0x3) +#define VHOST_USER_REPLY_MASK (0x1<<2) + uint32_t flags; + uint32_t size; /* the following payload size */ + union { +#define VHOST_USER_VRING_IDX_MASK (0xff) +#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) + uint64_t u64; + struct vhost_vring_state state; + struct vhost_vring_addr addr; + VhostUserMemory memory; + VhostUserLog log; + } payload; +} QEMU_PACKED VhostUserMsg; + +static VhostUserMsg m __attribute__ ((unused)); +#define VHOST_USER_HDR_SIZE (sizeof(m.request) \ + + sizeof(m.flags) \ + + sizeof(m.size)) + +#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) + +/* The version of the protocol we support */ +#define VHOST_USER_VERSION (0x1) +/*****************************************************************************/ + +enum { + TEST_FLAGS_OK, + TEST_FLAGS_DISCONNECT, + TEST_FLAGS_BAD, + TEST_FLAGS_END, +}; + +typedef struct TestServer { + gchar *socket_path; + gchar *mig_path; + gchar *chr_name; + gchar *tmpfs; + CharBackend chr; + int fds_num; + int fds[VHOST_MEMORY_MAX_NREGIONS]; + VhostUserMemory memory; + GMainContext *context; + GMainLoop *loop; + GThread *thread; + GMutex data_mutex; + GCond data_cond; + int log_fd; + uint64_t rings; + bool test_fail; + int test_flags; + int queues; +} TestServer; + +static const char *init_hugepagefs(void); +static TestServer *test_server_new(const gchar *name); +static void test_server_free(TestServer *server); +static void test_server_listen(TestServer *server); + +enum test_memfd { + TEST_MEMFD_AUTO, + TEST_MEMFD_YES, + TEST_MEMFD_NO, +}; + +static void append_vhost_opts(TestServer *s, GString *cmd_line, + const char *chr_opts) +{ + g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV, + s->chr_name, s->socket_path, + chr_opts, s->chr_name); +} + +static void append_mem_opts(TestServer *server, GString *cmd_line, + int size, enum test_memfd memfd) +{ + if (memfd == TEST_MEMFD_AUTO) { + memfd = qemu_memfd_check(MFD_ALLOW_SEALING) ? TEST_MEMFD_YES + : TEST_MEMFD_NO; + } + + if (memfd == TEST_MEMFD_YES) { + g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size); + } else { + const char *root = init_hugepagefs() ? : server->tmpfs; + + g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root); + } +} + +static bool wait_for_fds(TestServer *s) +{ + gint64 end_time; + bool got_region; + int i; + + g_mutex_lock(&s->data_mutex); + + end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + while (!s->fds_num) { + if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { + /* timeout has passed */ + g_assert(s->fds_num); + break; + } + } + + /* check for sanity */ + g_assert_cmpint(s->fds_num, >, 0); + g_assert_cmpint(s->fds_num, ==, s->memory.nregions); + + g_mutex_unlock(&s->data_mutex); + + got_region = false; + for (i = 0; i < s->memory.nregions; ++i) { + VhostUserMemoryRegion *reg = &s->memory.regions[i]; + if (reg->guest_phys_addr == 0) { + got_region = true; + break; + } + } + if (!got_region) { + g_test_skip("No memory at address 0x0"); + } + return got_region; +} + +static void read_guest_mem_server(QTestState *qts, TestServer *s) +{ + uint8_t *guest_mem; + int i, j; + size_t size; + + g_mutex_lock(&s->data_mutex); + + /* iterate all regions */ + for (i = 0; i < s->fds_num; i++) { + + /* We'll check only the region statring at 0x0*/ + if (s->memory.regions[i].guest_phys_addr != 0x0) { + continue; + } + + g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024); + + size = s->memory.regions[i].memory_size + + s->memory.regions[i].mmap_offset; + + guest_mem = mmap(0, size, PROT_READ | PROT_WRITE, + MAP_SHARED, s->fds[i], 0); + + g_assert(guest_mem != MAP_FAILED); + guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem)); + + for (j = 0; j < 1024; j++) { + uint32_t a = qtest_readb(qts, s->memory.regions[i].guest_phys_addr + j); + uint32_t b = guest_mem[j]; + + g_assert_cmpint(a, ==, b); + } + + munmap(guest_mem, s->memory.regions[i].memory_size); + } + + g_mutex_unlock(&s->data_mutex); +} + +static void *thread_function(void *data) +{ + GMainLoop *loop = data; + g_main_loop_run(loop); + return NULL; +} + +static int chr_can_read(void *opaque) +{ + return VHOST_USER_HDR_SIZE; +} + +static void chr_read(void *opaque, const uint8_t *buf, int size) +{ + TestServer *s = opaque; + CharBackend *chr = &s->chr; + VhostUserMsg msg; + uint8_t *p = (uint8_t *) &msg; + int fd = -1; + + if (s->test_fail) { + qemu_chr_fe_disconnect(chr); + /* now switch to non-failure */ + s->test_fail = false; + } + + if (size != VHOST_USER_HDR_SIZE) { + g_test_message("Wrong message size received %d", size); + return; + } + + g_mutex_lock(&s->data_mutex); + memcpy(p, buf, VHOST_USER_HDR_SIZE); + + if (msg.size) { + p += VHOST_USER_HDR_SIZE; + size = qemu_chr_fe_read_all(chr, p, msg.size); + if (size != msg.size) { + g_test_message("Wrong message size received %d != %d", + size, msg.size); + return; + } + } + + switch (msg.request) { + case VHOST_USER_GET_FEATURES: + /* send back features to qemu */ + msg.flags |= VHOST_USER_REPLY_MASK; + msg.size = sizeof(m.payload.u64); + msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL | + 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; + if (s->queues > 1) { + msg.payload.u64 |= 0x1ULL << VIRTIO_NET_F_MQ; + } + if (s->test_flags >= TEST_FLAGS_BAD) { + msg.payload.u64 = 0; + s->test_flags = TEST_FLAGS_END; + } + p = (uint8_t *) &msg; + qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); + break; + + case VHOST_USER_SET_FEATURES: + g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES), + !=, 0ULL); + if (s->test_flags == TEST_FLAGS_DISCONNECT) { + qemu_chr_fe_disconnect(chr); + s->test_flags = TEST_FLAGS_BAD; + } + break; + + case VHOST_USER_GET_PROTOCOL_FEATURES: + /* send back features to qemu */ + msg.flags |= VHOST_USER_REPLY_MASK; + msg.size = sizeof(m.payload.u64); + msg.payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD; + msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_CROSS_ENDIAN; + if (s->queues > 1) { + msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ; + } + p = (uint8_t *) &msg; + qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); + break; + + case VHOST_USER_GET_VRING_BASE: + /* send back vring base to qemu */ + msg.flags |= VHOST_USER_REPLY_MASK; + msg.size = sizeof(m.payload.state); + msg.payload.state.num = 0; + p = (uint8_t *) &msg; + qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); + + assert(msg.payload.state.index < s->queues * 2); + s->rings &= ~(0x1ULL << msg.payload.state.index); + g_cond_broadcast(&s->data_cond); + break; + + case VHOST_USER_SET_MEM_TABLE: + /* received the mem table */ + memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory)); + s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, + G_N_ELEMENTS(s->fds)); + + /* signal the test that it can continue */ + g_cond_broadcast(&s->data_cond); + break; + + case VHOST_USER_SET_VRING_KICK: + case VHOST_USER_SET_VRING_CALL: + /* consume the fd */ + qemu_chr_fe_get_msgfds(chr, &fd, 1); + /* + * This is a non-blocking eventfd. + * The receive function forces it to be blocking, + * so revert it back to non-blocking. + */ + qemu_set_nonblock(fd); + break; + + case VHOST_USER_SET_LOG_BASE: + if (s->log_fd != -1) { + close(s->log_fd); + s->log_fd = -1; + } + qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1); + msg.flags |= VHOST_USER_REPLY_MASK; + msg.size = 0; + p = (uint8_t *) &msg; + qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE); + + g_cond_broadcast(&s->data_cond); + break; + + case VHOST_USER_SET_VRING_BASE: + assert(msg.payload.state.index < s->queues * 2); + s->rings |= 0x1ULL << msg.payload.state.index; + g_cond_broadcast(&s->data_cond); + break; + + case VHOST_USER_GET_QUEUE_NUM: + msg.flags |= VHOST_USER_REPLY_MASK; + msg.size = sizeof(m.payload.u64); + msg.payload.u64 = s->queues; + p = (uint8_t *) &msg; + qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); + break; + + default: + break; + } + + g_mutex_unlock(&s->data_mutex); +} + +static const char *init_hugepagefs(void) +{ +#ifdef CONFIG_LINUX + static const char *hugepagefs; + const char *path = getenv("QTEST_HUGETLBFS_PATH"); + struct statfs fs; + int ret; + + if (hugepagefs) { + return hugepagefs; + } + if (!path) { + return NULL; + } + + if (access(path, R_OK | W_OK | X_OK)) { + g_test_message("access on path (%s): %s", path, strerror(errno)); + g_test_fail(); + return NULL; + } + + do { + ret = statfs(path, &fs); + } while (ret != 0 && errno == EINTR); + + if (ret != 0) { + g_test_message("statfs on path (%s): %s", path, strerror(errno)); + g_test_fail(); + return NULL; + } + + if (fs.f_type != HUGETLBFS_MAGIC) { + g_test_message("Warning: path not on HugeTLBFS: %s", path); + g_test_fail(); + return NULL; + } + + hugepagefs = path; + return hugepagefs; +#else + return NULL; +#endif +} + +static TestServer *test_server_new(const gchar *name) +{ + TestServer *server = g_new0(TestServer, 1); + char template[] = "/tmp/vhost-test-XXXXXX"; + const char *tmpfs; + + server->context = g_main_context_new(); + server->loop = g_main_loop_new(server->context, FALSE); + + /* run the main loop thread so the chardev may operate */ + server->thread = g_thread_new(NULL, thread_function, server->loop); + + tmpfs = mkdtemp(template); + if (!tmpfs) { + g_test_message("mkdtemp on path (%s): %s", template, strerror(errno)); + } + g_assert(tmpfs); + + server->tmpfs = g_strdup(tmpfs); + server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name); + server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name); + server->chr_name = g_strdup_printf("chr-%s", name); + + g_mutex_init(&server->data_mutex); + g_cond_init(&server->data_cond); + + server->log_fd = -1; + server->queues = 1; + + return server; +} + +static void chr_event(void *opaque, int event) +{ + TestServer *s = opaque; + + if (s->test_flags == TEST_FLAGS_END && + event == CHR_EVENT_CLOSED) { + s->test_flags = TEST_FLAGS_OK; + } +} + +static void test_server_create_chr(TestServer *server, const gchar *opt) +{ + gchar *chr_path; + Chardev *chr; + + chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt); + chr = qemu_chr_new(server->chr_name, chr_path, server->context); + g_free(chr_path); + + g_assert_nonnull(chr); + qemu_chr_fe_init(&server->chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read, + chr_event, NULL, server, server->context, true); +} + +static void test_server_listen(TestServer *server) +{ + test_server_create_chr(server, ",server,nowait"); +} + +static void test_server_free(TestServer *server) +{ + int i, ret; + + /* finish the helper thread and dispatch pending sources */ + g_main_loop_quit(server->loop); + g_thread_join(server->thread); + while (g_main_context_pending(NULL)) { + g_main_context_iteration(NULL, TRUE); + } + + unlink(server->socket_path); + g_free(server->socket_path); + + unlink(server->mig_path); + g_free(server->mig_path); + + ret = rmdir(server->tmpfs); + if (ret != 0) { + g_test_message("unable to rmdir: path (%s): %s", + server->tmpfs, strerror(errno)); + } + g_free(server->tmpfs); + + qemu_chr_fe_deinit(&server->chr, true); + + for (i = 0; i < server->fds_num; i++) { + close(server->fds[i]); + } + + if (server->log_fd != -1) { + close(server->log_fd); + } + + g_free(server->chr_name); + + g_main_loop_unref(server->loop); + g_main_context_unref(server->context); + g_cond_clear(&server->data_cond); + g_mutex_clear(&server->data_mutex); + g_free(server); +} + +static void wait_for_log_fd(TestServer *s) +{ + gint64 end_time; + + g_mutex_lock(&s->data_mutex); + end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + while (s->log_fd == -1) { + if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { + /* timeout has passed */ + g_assert(s->log_fd != -1); + break; + } + } + + g_mutex_unlock(&s->data_mutex); +} + +static void write_guest_mem(TestServer *s, uint32_t seed) +{ + uint32_t *guest_mem; + int i, j; + size_t size; + + /* iterate all regions */ + for (i = 0; i < s->fds_num; i++) { + + /* We'll write only the region statring at 0x0 */ + if (s->memory.regions[i].guest_phys_addr != 0x0) { + continue; + } + + g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024); + + size = s->memory.regions[i].memory_size + + s->memory.regions[i].mmap_offset; + + guest_mem = mmap(0, size, PROT_READ | PROT_WRITE, + MAP_SHARED, s->fds[i], 0); + + g_assert(guest_mem != MAP_FAILED); + guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem)); + + for (j = 0; j < 256; j++) { + guest_mem[j] = seed + j; + } + + munmap(guest_mem, s->memory.regions[i].memory_size); + break; + } +} + +static guint64 get_log_size(TestServer *s) +{ + guint64 log_size = 0; + int i; + + for (i = 0; i < s->memory.nregions; ++i) { + VhostUserMemoryRegion *reg = &s->memory.regions[i]; + guint64 last = range_get_last(reg->guest_phys_addr, + reg->memory_size); + log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1); + } + + return log_size; +} + +typedef struct TestMigrateSource { + GSource source; + TestServer *src; + TestServer *dest; +} TestMigrateSource; + +static gboolean +test_migrate_source_check(GSource *source) +{ + TestMigrateSource *t = (TestMigrateSource *)source; + gboolean overlap = t->src->rings && t->dest->rings; + + g_assert(!overlap); + + return FALSE; +} + +GSourceFuncs test_migrate_source_funcs = { + .check = test_migrate_source_check, +}; + +static void vhost_user_test_cleanup(void *s) +{ + TestServer *server = s; + + qos_invalidate_command_line(); + test_server_free(server); +} + +static void *vhost_user_test_setup(GString *cmd_line, void *arg) +{ + TestServer *server = test_server_new("vhost-user-test"); + test_server_listen(server); + + append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(server, cmd_line, ""); + + g_test_queue_destroy(vhost_user_test_cleanup, server); + + return server; +} + +static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg) +{ + TestServer *server = test_server_new("vhost-user-test"); + test_server_listen(server); + + append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES); + append_vhost_opts(server, cmd_line, ""); + + g_test_queue_destroy(vhost_user_test_cleanup, server); + + return server; +} + +static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *server = arg; + + if (!wait_for_fds(server)) { + return; + } + + read_guest_mem_server(global_qtest, server); +} + +static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; + TestServer *dest = test_server_new("dest"); + GString *dest_cmdline = g_string_new(qos_get_current_command_line()); + char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path); + QTestState *to; + GSource *source; + QDict *rsp; + guint8 *log; + guint64 size; + + if (!wait_for_fds(s)) { + return; + } + + size = get_log_size(s); + g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); + + test_server_listen(dest); + g_string_append_printf(dest_cmdline, " -incoming %s", uri); + append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO); + append_vhost_opts(dest, dest_cmdline, ""); + to = qtest_init(dest_cmdline->str); + + /* This would be where you call qos_allocate_objects(to, NULL), if you want + * to talk to the QVirtioNet object on the destination. + */ + + source = g_source_new(&test_migrate_source_funcs, + sizeof(TestMigrateSource)); + ((TestMigrateSource *)source)->src = s; + ((TestMigrateSource *)source)->dest = dest; + g_source_attach(source, s->context); + + /* slow down migration to have time to fiddle with log */ + /* TODO: qtest could learn to break on some places */ + rsp = qmp("{ 'execute': 'migrate_set_speed'," + "'arguments': { 'value': 10 } }"); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + + rsp = qmp("{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", uri); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + + wait_for_log_fd(s); + + log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0); + g_assert(log != MAP_FAILED); + + /* modify first page */ + write_guest_mem(s, 0x42); + log[0] = 1; + munmap(log, size); + + /* speed things up */ + rsp = qmp("{ 'execute': 'migrate_set_speed'," + "'arguments': { 'value': 0 } }"); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + + qmp_eventwait("STOP"); + qtest_qmp_eventwait(to, "RESUME"); + + g_assert(wait_for_fds(dest)); + read_guest_mem_server(to, dest); + + g_source_destroy(source); + g_source_unref(source); + + qtest_quit(to); + test_server_free(dest); + g_free(uri); +} + +static void wait_for_rings_started(TestServer *s, size_t count) +{ + gint64 end_time; + + g_mutex_lock(&s->data_mutex); + end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + while (ctpop64(s->rings) != count) { + if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { + /* timeout has passed */ + g_assert_cmpint(ctpop64(s->rings), ==, count); + break; + } + } + + g_mutex_unlock(&s->data_mutex); +} + +static inline void test_server_connect(TestServer *server) +{ + test_server_create_chr(server, ",reconnect=1"); +} + +static gboolean +reconnect_cb(gpointer user_data) +{ + TestServer *s = user_data; + + qemu_chr_fe_disconnect(&s->chr); + + return FALSE; +} + +static gpointer +connect_thread(gpointer data) +{ + TestServer *s = data; + + /* wait for qemu to start before first try, to avoid extra warnings */ + g_usleep(G_USEC_PER_SEC); + test_server_connect(s); + + return NULL; +} + +static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg) +{ + TestServer *s = test_server_new("reconnect"); + + g_thread_new("connect", connect_thread, s); + append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(s, cmd_line, ",server"); + + g_test_queue_destroy(vhost_user_test_cleanup, s); + + return s; +} + +static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; + GSource *src; + + if (!wait_for_fds(s)) { + return; + } + + wait_for_rings_started(s, 2); + + /* reconnect */ + s->fds_num = 0; + s->rings = 0; + src = g_idle_source_new(); + g_source_set_callback(src, reconnect_cb, s, NULL); + g_source_attach(src, s->context); + g_source_unref(src); + g_assert(wait_for_fds(s)); + wait_for_rings_started(s, 2); +} + +static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg) +{ + TestServer *s = test_server_new("connect-fail"); + + s->test_fail = true; + + g_thread_new("connect", connect_thread, s); + append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(s, cmd_line, ",server"); + + g_test_queue_destroy(vhost_user_test_cleanup, s); + + return s; +} + +static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg) +{ + TestServer *s = test_server_new("flags-mismatch"); + + s->test_flags = TEST_FLAGS_DISCONNECT; + + g_thread_new("connect", connect_thread, s); + append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(s, cmd_line, ",server"); + + g_test_queue_destroy(vhost_user_test_cleanup, s); + + return s; +} + +static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; + + if (!wait_for_fds(s)) { + return; + } + wait_for_rings_started(s, 2); +} + +static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg) +{ + TestServer *s = vhost_user_test_setup(cmd_line, arg); + + s->queues = 2; + g_string_append_printf(cmd_line, + " -set netdev.hs0.queues=%d" + " -global virtio-net-pci.vectors=%d", + s->queues, s->queues * 2 + 2); + + return s; +} + +static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; + + wait_for_rings_started(s, s->queues * 2); +} + +static void register_vhost_user_test(void) +{ + QOSGraphTestOptions opts = { + .before = vhost_user_test_setup, + .subprocess = true, + }; + + qemu_add_opts(&qemu_chardev_opts); + + qos_add_test("vhost-user/read-guest-mem/memfile", + "virtio-net", + test_read_guest_mem, &opts); + + if (qemu_memfd_check(MFD_ALLOW_SEALING)) { + opts.before = vhost_user_test_setup_memfd; + qos_add_test("vhost-user/read-guest-mem/memfd", + "virtio-net", + test_read_guest_mem, &opts); + } + + qos_add_test("vhost-user/migrate", + "virtio-net", + test_migrate, &opts); + + /* keeps failing on build-system since Aug 15 2017 */ + if (getenv("QTEST_VHOST_USER_FIXME")) { + opts.before = vhost_user_test_setup_reconnect; + qos_add_test("vhost-user/reconnect", "virtio-net", + test_reconnect, &opts); + + opts.before = vhost_user_test_setup_connect_fail; + qos_add_test("vhost-user/connect-fail", "virtio-net", + test_vhost_user_started, &opts); + + opts.before = vhost_user_test_setup_flags_mismatch; + qos_add_test("vhost-user/flags-mismatch", "virtio-net", + test_vhost_user_started, &opts); + } + + opts.before = vhost_user_test_setup_multiqueue; + opts.edge.extra_device_opts = "mq=on"; + qos_add_test("vhost-user/multiqueue", + "virtio-net", + test_multiqueue, &opts); +} +libqos_init(register_vhost_user_test); diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c new file mode 100644 index 0000000000..e7b58e3a0c --- /dev/null +++ b/tests/qtest/virtio-9p-test.c @@ -0,0 +1,662 @@ +/* + * QTest testcase for VirtIO 9P + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "qemu/module.h" +#include "hw/9pfs/9p.h" +#include "hw/9pfs/9p-synth.h" +#include "libqos/virtio-9p.h" +#include "libqos/qgraph.h" + +#define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000) +static QGuestAllocator *alloc; + +static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + size_t tag_len = qvirtio_config_readw(v9p->vdev, 0); + char *tag; + int i; + + g_assert_cmpint(tag_len, ==, strlen(MOUNT_TAG)); + + tag = g_malloc(tag_len); + for (i = 0; i < tag_len; i++) { + tag[i] = qvirtio_config_readb(v9p->vdev, i + 2); + } + g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len); + g_free(tag); +} + +#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */ + +typedef struct { + QTestState *qts; + QVirtio9P *v9p; + uint16_t tag; + uint64_t t_msg; + uint32_t t_size; + uint64_t r_msg; + /* No r_size, it is hardcoded to P9_MAX_SIZE */ + size_t t_off; + size_t r_off; + uint32_t free_head; +} P9Req; + +static void v9fs_memwrite(P9Req *req, const void *addr, size_t len) +{ + qtest_memwrite(req->qts, req->t_msg + req->t_off, addr, len); + req->t_off += len; +} + +static void v9fs_memskip(P9Req *req, size_t len) +{ + req->r_off += len; +} + +static void v9fs_memread(P9Req *req, void *addr, size_t len) +{ + qtest_memread(req->qts, req->r_msg + req->r_off, addr, len); + req->r_off += len; +} + +static void v9fs_uint16_write(P9Req *req, uint16_t val) +{ + uint16_t le_val = cpu_to_le16(val); + + v9fs_memwrite(req, &le_val, 2); +} + +static void v9fs_uint16_read(P9Req *req, uint16_t *val) +{ + v9fs_memread(req, val, 2); + le16_to_cpus(val); +} + +static void v9fs_uint32_write(P9Req *req, uint32_t val) +{ + uint32_t le_val = cpu_to_le32(val); + + v9fs_memwrite(req, &le_val, 4); +} + +static void v9fs_uint64_write(P9Req *req, uint64_t val) +{ + uint64_t le_val = cpu_to_le64(val); + + v9fs_memwrite(req, &le_val, 8); +} + +static void v9fs_uint32_read(P9Req *req, uint32_t *val) +{ + v9fs_memread(req, val, 4); + le32_to_cpus(val); +} + +/* len[2] string[len] */ +static uint16_t v9fs_string_size(const char *string) +{ + size_t len = strlen(string); + + g_assert_cmpint(len, <=, UINT16_MAX - 2); + + return 2 + len; +} + +static void v9fs_string_write(P9Req *req, const char *string) +{ + int len = strlen(string); + + g_assert_cmpint(len, <=, UINT16_MAX); + + v9fs_uint16_write(req, (uint16_t) len); + v9fs_memwrite(req, string, len); +} + +static void v9fs_string_read(P9Req *req, uint16_t *len, char **string) +{ + uint16_t local_len; + + v9fs_uint16_read(req, &local_len); + if (len) { + *len = local_len; + } + if (string) { + *string = g_malloc(local_len); + v9fs_memread(req, *string, local_len); + } else { + v9fs_memskip(req, local_len); + } +} + + typedef struct { + uint32_t size; + uint8_t id; + uint16_t tag; +} QEMU_PACKED P9Hdr; + +static P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id, + uint16_t tag) +{ + P9Req *req = g_new0(P9Req, 1); + uint32_t total_size = 7; /* 9P header has well-known size of 7 bytes */ + P9Hdr hdr = { + .id = id, + .tag = cpu_to_le16(tag) + }; + + g_assert_cmpint(total_size, <=, UINT32_MAX - size); + total_size += size; + hdr.size = cpu_to_le32(total_size); + + g_assert_cmpint(total_size, <=, P9_MAX_SIZE); + + req->qts = global_qtest; + req->v9p = v9p; + req->t_size = total_size; + req->t_msg = guest_alloc(alloc, req->t_size); + v9fs_memwrite(req, &hdr, 7); + req->tag = tag; + return req; +} + +static void v9fs_req_send(P9Req *req) +{ + QVirtio9P *v9p = req->v9p; + + req->r_msg = guest_alloc(alloc, P9_MAX_SIZE); + req->free_head = qvirtqueue_add(req->qts, v9p->vq, req->t_msg, req->t_size, + false, true); + qvirtqueue_add(req->qts, v9p->vq, req->r_msg, P9_MAX_SIZE, true, false); + qvirtqueue_kick(req->qts, v9p->vdev, v9p->vq, req->free_head); + req->t_off = 0; +} + +static const char *rmessage_name(uint8_t id) +{ + return + id == P9_RLERROR ? "RLERROR" : + id == P9_RVERSION ? "RVERSION" : + id == P9_RATTACH ? "RATTACH" : + id == P9_RWALK ? "RWALK" : + id == P9_RLOPEN ? "RLOPEN" : + id == P9_RWRITE ? "RWRITE" : + id == P9_RFLUSH ? "RFLUSH" : + ""; +} + +static void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len) +{ + QVirtio9P *v9p = req->v9p; + + qvirtio_wait_used_elem(req->qts, v9p->vdev, v9p->vq, req->free_head, len, + QVIRTIO_9P_TIMEOUT_US); +} + +static void v9fs_req_recv(P9Req *req, uint8_t id) +{ + P9Hdr hdr; + + v9fs_memread(req, &hdr, 7); + hdr.size = ldl_le_p(&hdr.size); + hdr.tag = lduw_le_p(&hdr.tag); + + g_assert_cmpint(hdr.size, >=, 7); + g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE); + g_assert_cmpint(hdr.tag, ==, req->tag); + + if (hdr.id != id) { + g_printerr("Received response %d (%s) instead of %d (%s)\n", + hdr.id, rmessage_name(hdr.id), id, rmessage_name(id)); + + if (hdr.id == P9_RLERROR) { + uint32_t err; + v9fs_uint32_read(req, &err); + g_printerr("Rlerror has errno %d (%s)\n", err, strerror(err)); + } + } + g_assert_cmpint(hdr.id, ==, id); +} + +static void v9fs_req_free(P9Req *req) +{ + guest_free(alloc, req->t_msg); + guest_free(alloc, req->r_msg); + g_free(req); +} + +/* size[4] Rlerror tag[2] ecode[4] */ +static void v9fs_rlerror(P9Req *req, uint32_t *err) +{ + v9fs_req_recv(req, P9_RLERROR); + v9fs_uint32_read(req, err); + v9fs_req_free(req); +} + +/* size[4] Tversion tag[2] msize[4] version[s] */ +static P9Req *v9fs_tversion(QVirtio9P *v9p, uint32_t msize, const char *version, + uint16_t tag) +{ + P9Req *req; + uint32_t body_size = 4; + uint16_t string_size = v9fs_string_size(version); + + g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); + body_size += string_size; + req = v9fs_req_init(v9p, body_size, P9_TVERSION, tag); + + v9fs_uint32_write(req, msize); + v9fs_string_write(req, version); + v9fs_req_send(req); + return req; +} + +/* size[4] Rversion tag[2] msize[4] version[s] */ +static void v9fs_rversion(P9Req *req, uint16_t *len, char **version) +{ + uint32_t msize; + + v9fs_req_recv(req, P9_RVERSION); + v9fs_uint32_read(req, &msize); + + g_assert_cmpint(msize, ==, P9_MAX_SIZE); + + if (len || version) { + v9fs_string_read(req, len, version); + } + + v9fs_req_free(req); +} + +/* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */ +static P9Req *v9fs_tattach(QVirtio9P *v9p, uint32_t fid, uint32_t n_uname, + uint16_t tag) +{ + const char *uname = ""; /* ignored by QEMU */ + const char *aname = ""; /* ignored by QEMU */ + P9Req *req = v9fs_req_init(v9p, 4 + 4 + 2 + 2 + 4, P9_TATTACH, tag); + + v9fs_uint32_write(req, fid); + v9fs_uint32_write(req, P9_NOFID); + v9fs_string_write(req, uname); + v9fs_string_write(req, aname); + v9fs_uint32_write(req, n_uname); + v9fs_req_send(req); + return req; +} + +typedef char v9fs_qid[13]; + +/* size[4] Rattach tag[2] qid[13] */ +static void v9fs_rattach(P9Req *req, v9fs_qid *qid) +{ + v9fs_req_recv(req, P9_RATTACH); + if (qid) { + v9fs_memread(req, qid, 13); + } + v9fs_req_free(req); +} + +/* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ +static P9Req *v9fs_twalk(QVirtio9P *v9p, uint32_t fid, uint32_t newfid, + uint16_t nwname, char *const wnames[], uint16_t tag) +{ + P9Req *req; + int i; + uint32_t body_size = 4 + 4 + 2; + + for (i = 0; i < nwname; i++) { + uint16_t wname_size = v9fs_string_size(wnames[i]); + + g_assert_cmpint(body_size, <=, UINT32_MAX - wname_size); + body_size += wname_size; + } + req = v9fs_req_init(v9p, body_size, P9_TWALK, tag); + v9fs_uint32_write(req, fid); + v9fs_uint32_write(req, newfid); + v9fs_uint16_write(req, nwname); + for (i = 0; i < nwname; i++) { + v9fs_string_write(req, wnames[i]); + } + v9fs_req_send(req); + return req; +} + +/* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */ +static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid) +{ + uint16_t local_nwqid; + + v9fs_req_recv(req, P9_RWALK); + v9fs_uint16_read(req, &local_nwqid); + if (nwqid) { + *nwqid = local_nwqid; + } + if (wqid) { + *wqid = g_malloc(local_nwqid * 13); + v9fs_memread(req, *wqid, local_nwqid * 13); + } + v9fs_req_free(req); +} + +/* size[4] Tlopen tag[2] fid[4] flags[4] */ +static P9Req *v9fs_tlopen(QVirtio9P *v9p, uint32_t fid, uint32_t flags, + uint16_t tag) +{ + P9Req *req; + + req = v9fs_req_init(v9p, 4 + 4, P9_TLOPEN, tag); + v9fs_uint32_write(req, fid); + v9fs_uint32_write(req, flags); + v9fs_req_send(req); + return req; +} + +/* size[4] Rlopen tag[2] qid[13] iounit[4] */ +static void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit) +{ + v9fs_req_recv(req, P9_RLOPEN); + if (qid) { + v9fs_memread(req, qid, 13); + } else { + v9fs_memskip(req, 13); + } + if (iounit) { + v9fs_uint32_read(req, iounit); + } + v9fs_req_free(req); +} + +/* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */ +static P9Req *v9fs_twrite(QVirtio9P *v9p, uint32_t fid, uint64_t offset, + uint32_t count, const void *data, uint16_t tag) +{ + P9Req *req; + uint32_t body_size = 4 + 8 + 4; + + g_assert_cmpint(body_size, <=, UINT32_MAX - count); + body_size += count; + req = v9fs_req_init(v9p, body_size, P9_TWRITE, tag); + v9fs_uint32_write(req, fid); + v9fs_uint64_write(req, offset); + v9fs_uint32_write(req, count); + v9fs_memwrite(req, data, count); + v9fs_req_send(req); + return req; +} + +/* size[4] Rwrite tag[2] count[4] */ +static void v9fs_rwrite(P9Req *req, uint32_t *count) +{ + v9fs_req_recv(req, P9_RWRITE); + if (count) { + v9fs_uint32_read(req, count); + } + v9fs_req_free(req); +} + +/* size[4] Tflush tag[2] oldtag[2] */ +static P9Req *v9fs_tflush(QVirtio9P *v9p, uint16_t oldtag, uint16_t tag) +{ + P9Req *req; + + req = v9fs_req_init(v9p, 2, P9_TFLUSH, tag); + v9fs_uint32_write(req, oldtag); + v9fs_req_send(req); + return req; +} + +/* size[4] Rflush tag[2] */ +static void v9fs_rflush(P9Req *req) +{ + v9fs_req_recv(req, P9_RFLUSH); + v9fs_req_free(req); +} + +static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + const char *version = "9P2000.L"; + uint16_t server_len; + char *server_version; + P9Req *req; + + req = v9fs_tversion(v9p, P9_MAX_SIZE, version, P9_NOTAG); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rversion(req, &server_len, &server_version); + + g_assert_cmpmem(server_version, server_len, version, strlen(version)); + + g_free(server_version); +} + +static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + P9Req *req; + + fs_version(v9p, NULL, t_alloc); + req = v9fs_tattach(v9p, 0, getuid(), 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rattach(req, NULL); +} + +static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + char *wnames[P9_MAXWELEM]; + uint16_t nwqid; + v9fs_qid *wqid; + int i; + P9Req *req; + + for (i = 0; i < P9_MAXWELEM; i++) { + wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i); + } + + fs_attach(v9p, NULL, t_alloc); + req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwalk(req, &nwqid, &wqid); + + g_assert_cmpint(nwqid, ==, P9_MAXWELEM); + + for (i = 0; i < P9_MAXWELEM; i++) { + g_free(wnames[i]); + } + + g_free(wqid); +} + +static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + char *const wnames[] = { g_strdup(" /") }; + P9Req *req; + uint32_t err; + + fs_attach(v9p, NULL, t_alloc); + req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rlerror(req, &err); + + g_assert_cmpint(err, ==, ENOENT); + + g_free(wnames[0]); +} + +static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + char *const wnames[] = { g_strdup("..") }; + v9fs_qid root_qid, *wqid; + P9Req *req; + + fs_version(v9p, NULL, t_alloc); + req = v9fs_tattach(v9p, 0, getuid(), 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rattach(req, &root_qid); + + req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwalk(req, NULL, &wqid); /* We now we'll get one qid */ + + g_assert_cmpmem(&root_qid, 13, wqid[0], 13); + + g_free(wqid); + g_free(wnames[0]); +} + +static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) }; + P9Req *req; + + fs_attach(v9p, NULL, t_alloc); + req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwalk(req, NULL, NULL); + + req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rlopen(req, NULL, NULL); + + g_free(wnames[0]); +} + +static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + static const uint32_t write_count = P9_MAX_SIZE / 2; + char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) }; + char *buf = g_malloc0(write_count); + uint32_t count; + P9Req *req; + + fs_attach(v9p, NULL, t_alloc); + req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwalk(req, NULL, NULL); + + req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rlopen(req, NULL, NULL); + + req = v9fs_twrite(v9p, 1, 0, write_count, buf, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwrite(req, &count); + g_assert_cmpint(count, ==, write_count); + + g_free(buf); + g_free(wnames[0]); +} + +static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; + P9Req *req, *flush_req; + uint32_t reply_len; + uint8_t should_block; + + fs_attach(v9p, NULL, t_alloc); + req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwalk(req, NULL, NULL); + + req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rlopen(req, NULL, NULL); + + /* This will cause the 9p server to try to write data to the backend, + * until the write request gets cancelled. + */ + should_block = 1; + req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0); + + flush_req = v9fs_tflush(v9p, req->tag, 1); + + /* The write request is supposed to be flushed: the server should just + * mark the write request as used and reply to the flush request. + */ + v9fs_req_wait_for_reply(req, &reply_len); + g_assert_cmpint(reply_len, ==, 0); + v9fs_req_free(req); + v9fs_rflush(flush_req); + + g_free(wnames[0]); +} + +static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + alloc = t_alloc; + char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; + P9Req *req, *flush_req; + uint32_t count; + uint8_t should_block; + + fs_attach(v9p, NULL, t_alloc); + req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwalk(req, NULL, NULL); + + req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); + v9fs_req_wait_for_reply(req, NULL); + v9fs_rlopen(req, NULL, NULL); + + /* This will cause the write request to complete right away, before it + * could be actually cancelled. + */ + should_block = 0; + req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0); + + flush_req = v9fs_tflush(v9p, req->tag, 1); + + /* The write request is supposed to complete. The server should + * reply to the write request and the flush request. + */ + v9fs_req_wait_for_reply(req, NULL); + v9fs_rwrite(req, &count); + g_assert_cmpint(count, ==, sizeof(should_block)); + v9fs_rflush(flush_req); + + g_free(wnames[0]); +} + +static void register_virtio_9p_test(void) +{ + qos_add_test("config", "virtio-9p", pci_config, NULL); + qos_add_test("fs/version/basic", "virtio-9p", fs_version, NULL); + qos_add_test("fs/attach/basic", "virtio-9p", fs_attach, NULL); + qos_add_test("fs/walk/basic", "virtio-9p", fs_walk, NULL); + qos_add_test("fs/walk/no_slash", "virtio-9p", fs_walk_no_slash, + NULL); + qos_add_test("fs/walk/dotdot_from_root", "virtio-9p", + fs_walk_dotdot, NULL); + qos_add_test("fs/lopen/basic", "virtio-9p", fs_lopen, NULL); + qos_add_test("fs/write/basic", "virtio-9p", fs_write, NULL); + qos_add_test("fs/flush/success", "virtio-9p", fs_flush_success, + NULL); + qos_add_test("fs/flush/ignored", "virtio-9p", fs_flush_ignored, + NULL); +} + +libqos_init(register_virtio_9p_test); diff --git a/tests/qtest/virtio-blk-test.c b/tests/qtest/virtio-blk-test.c new file mode 100644 index 0000000000..2a23698211 --- /dev/null +++ b/tests/qtest/virtio-blk-test.c @@ -0,0 +1,802 @@ +/* + * QTest testcase for VirtIO Block Device + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * Copyright (c) 2014 Marc Marí + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "qemu/bswap.h" +#include "qemu/module.h" +#include "standard-headers/linux/virtio_blk.h" +#include "standard-headers/linux/virtio_pci.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-blk.h" + +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) + +#define TEST_IMAGE_SIZE (64 * 1024 * 1024) +#define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000) +#define PCI_SLOT_HP 0x06 + +typedef struct QVirtioBlkReq { + uint32_t type; + uint32_t ioprio; + uint64_t sector; + char *data; + uint8_t status; +} QVirtioBlkReq; + + +#ifdef HOST_WORDS_BIGENDIAN +const bool host_is_big_endian = true; +#else +const bool host_is_big_endian; /* false */ +#endif + +static void drive_destroy(void *path) +{ + unlink(path); + g_free(path); + qos_invalidate_command_line(); +} + +static char *drive_create(void) +{ + int fd, ret; + char *t_path = g_strdup("/tmp/qtest.XXXXXX"); + + /* Create a temporary raw image */ + fd = mkstemp(t_path); + g_assert_cmpint(fd, >=, 0); + ret = ftruncate(fd, TEST_IMAGE_SIZE); + g_assert_cmpint(ret, ==, 0); + close(fd); + + g_test_queue_destroy(drive_destroy, t_path); + return t_path; +} + +static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req) +{ + if (qvirtio_is_big_endian(d) != host_is_big_endian) { + req->type = bswap32(req->type); + req->ioprio = bswap32(req->ioprio); + req->sector = bswap64(req->sector); + } +} + + +static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d, + struct virtio_blk_discard_write_zeroes *dwz_hdr) +{ + if (qvirtio_is_big_endian(d) != host_is_big_endian) { + dwz_hdr->sector = bswap64(dwz_hdr->sector); + dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors); + dwz_hdr->flags = bswap32(dwz_hdr->flags); + } +} + +static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d, + QVirtioBlkReq *req, uint64_t data_size) +{ + uint64_t addr; + uint8_t status = 0xFF; + + switch (req->type) { + case VIRTIO_BLK_T_IN: + case VIRTIO_BLK_T_OUT: + g_assert_cmpuint(data_size % 512, ==, 0); + break; + case VIRTIO_BLK_T_DISCARD: + case VIRTIO_BLK_T_WRITE_ZEROES: + g_assert_cmpuint(data_size % + sizeof(struct virtio_blk_discard_write_zeroes), ==, 0); + break; + default: + g_assert_cmpuint(data_size, ==, 0); + } + + addr = guest_alloc(alloc, sizeof(*req) + data_size); + + virtio_blk_fix_request(d, req); + + memwrite(addr, req, 16); + memwrite(addr + 16, req->data, data_size); + memwrite(addr + 16 + data_size, &status, sizeof(status)); + + return addr; +} + +/* Returns the request virtqueue so the caller can perform further tests */ +static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc) +{ + QVirtioBlkReq req; + uint64_t req_addr; + uint64_t capacity; + uint64_t features; + uint32_t free_head; + uint8_t status; + char *data; + QTestState *qts = global_qtest; + QVirtQueue *vq; + + features = qvirtio_get_features(dev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_RING_F_EVENT_IDX) | + (1u << VIRTIO_BLK_F_SCSI)); + qvirtio_set_features(dev, features); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + vq = qvirtqueue_setup(dev, alloc, 0); + + qvirtio_set_driver_ok(dev); + + /* Write and read with 3 descriptor layout */ + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(alloc, req_addr); + + if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) { + struct virtio_blk_discard_write_zeroes dwz_hdr; + void *expected; + + /* + * WRITE_ZEROES request on the same sector of previous test where + * we wrote "TEST". + */ + req.type = VIRTIO_BLK_T_WRITE_ZEROES; + req.data = (char *) &dwz_hdr; + dwz_hdr.sector = 0; + dwz_hdr.num_sectors = 1; + dwz_hdr.flags = 0; + + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); + + req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true); + qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, + false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 16 + sizeof(dwz_hdr)); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request to check if the sector contains all zeroes */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc(512); + expected = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpmem(data, 512, expected, 512); + g_free(expected); + g_free(data); + + guest_free(alloc, req_addr); + } + + if (features & (1u << VIRTIO_BLK_F_DISCARD)) { + struct virtio_blk_discard_write_zeroes dwz_hdr; + + req.type = VIRTIO_BLK_T_DISCARD; + req.data = (char *) &dwz_hdr; + dwz_hdr.sector = 0; + dwz_hdr.num_sectors = 1; + dwz_hdr.flags = 0; + + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); + + req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true); + qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 16 + sizeof(dwz_hdr)); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + } + + if (features & (1u << VIRTIO_F_ANY_LAYOUT)) { + /* Write and read with 2 descriptor layout */ + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 528, false, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 513, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(alloc, req_addr); + } + + return vq; +} + +static void basic(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioBlk *blk_if = obj; + QVirtQueue *vq; + + vq = test_basic(blk_if->vdev, t_alloc); + qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc); + +} + +static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc) +{ + QVirtQueue *vq; + QVirtioBlk *blk_if = obj; + QVirtioDevice *dev = blk_if->vdev; + QVirtioBlkReq req; + QVRingIndirectDesc *indirect; + uint64_t req_addr; + uint64_t capacity; + uint64_t features; + uint32_t free_head; + uint8_t status; + char *data; + QTestState *qts = global_qtest; + + features = qvirtio_get_features(dev); + g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_EVENT_IDX) | + (1u << VIRTIO_BLK_F_SCSI)); + qvirtio_set_features(dev, features); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtio_set_driver_ok(dev); + + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2); + qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false); + qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true); + free_head = qvirtqueue_add_indirect(qts, vq, indirect); + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + g_free(indirect); + guest_free(t_alloc, req_addr); + + /* Read request */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2); + qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false); + qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true); + free_head = qvirtqueue_add_indirect(qts, vq, indirect); + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + g_free(indirect); + guest_free(t_alloc, req_addr); + qvirtqueue_cleanup(dev->bus, vq, t_alloc); +} + +static void config(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioBlk *blk_if = obj; + QVirtioDevice *dev = blk_if->vdev; + int n_size = TEST_IMAGE_SIZE / 2; + uint64_t features; + uint64_t capacity; + + features = qvirtio_get_features(dev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_RING_F_EVENT_IDX) | + (1u << VIRTIO_BLK_F_SCSI)); + qvirtio_set_features(dev, features); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + qvirtio_set_driver_ok(dev); + + qmp_discard_response("{ 'execute': 'block_resize', " + " 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); + qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, n_size / 512); +} + +static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc) +{ + QVirtQueue *vq; + QVirtioBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; + QVirtioDevice *dev = &pdev->vdev; + QVirtioBlkReq req; + int n_size = TEST_IMAGE_SIZE / 2; + uint64_t req_addr; + uint64_t capacity; + uint64_t features; + uint32_t free_head; + uint8_t status; + char *data; + QOSGraphObject *blk_object = obj; + QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device"); + QTestState *qts = global_qtest; + + if (qpci_check_buggy_msi(pci_dev)) { + return; + } + + qpci_msix_enable(pdev->pdev); + qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); + + features = qvirtio_get_features(dev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_RING_F_EVENT_IDX) | + (1u << VIRTIO_BLK_F_SCSI)); + qvirtio_set_features(dev, features); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); + + qvirtio_set_driver_ok(dev); + + qmp_discard_response("{ 'execute': 'block_resize', " + " 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); + + qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, n_size / 512); + + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + guest_free(t_alloc, req_addr); + + /* Read request */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(t_alloc, req_addr); + + /* End test */ + qpci_msix_disable(pdev->pdev); + qvirtqueue_cleanup(dev->bus, vq, t_alloc); +} + +static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc) +{ + QVirtQueue *vq; + QVirtioBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; + QVirtioDevice *dev = &pdev->vdev; + QVirtioBlkReq req; + uint64_t req_addr; + uint64_t capacity; + uint64_t features; + uint32_t free_head; + uint32_t write_head; + uint32_t desc_idx; + uint8_t status; + char *data; + QOSGraphObject *blk_object = obj; + QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device"); + QTestState *qts = global_qtest; + + if (qpci_check_buggy_msi(pci_dev)) { + return; + } + + qpci_msix_enable(pdev->pdev); + qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); + + features = qvirtio_get_features(dev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | + (1u << VIRTIO_BLK_F_SCSI)); + qvirtio_set_features(dev, features); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); + + qvirtio_set_driver_ok(dev); + + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + /* Notify after processing the third request */ + qvirtqueue_set_used_event(qts, vq, 2); + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(qts, dev, vq, free_head); + write_head = free_head; + + /* No notification expected */ + status = qvirtio_wait_status_byte_no_isr(qts, dev, + vq, req_addr + 528, + QVIRTIO_BLK_TIMEOUT_US); + g_assert_cmpint(status, ==, 0); + + guest_free(t_alloc, req_addr); + + /* Read request */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + /* We get just one notification for both requests */ + qvirtio_wait_used_elem(qts, dev, vq, write_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + g_assert(qvirtqueue_get_buf(qts, vq, &desc_idx, NULL)); + g_assert_cmpint(desc_idx, ==, free_head); + + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(t_alloc, req_addr); + + /* End test */ + qpci_msix_disable(pdev->pdev); + + qvirtqueue_cleanup(dev->bus, vq, t_alloc); +} + +static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioPCIDevice *dev1 = obj; + QVirtioPCIDevice *dev; + QTestState *qts = dev1->pdev->bus->qts; + + /* plug secondary disk */ + qtest_qmp_device_add(qts, "virtio-blk-pci", "drv1", + "{'addr': %s, 'drive': 'drive1'}", + stringify(PCI_SLOT_HP) ".0"); + + dev = virtio_pci_new(dev1->pdev->bus, + &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) }); + g_assert_nonnull(dev); + g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); + qvirtio_pci_device_disable(dev); + qos_object_destroy((QOSGraphObject *)dev); + + /* unplug secondary disk */ + qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP); +} + +/* + * Check that setting the vring addr on a non-existent virtqueue does + * not crash. + */ +static void test_nonexistent_virtqueue(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QVirtioBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; + QPCIBar bar0; + QPCIDevice *dev; + + dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0)); + g_assert(dev != NULL); + qpci_device_enable(dev); + + bar0 = qpci_iomap(dev, 0, NULL); + + qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2); + qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1); + + + g_free(dev); +} + +static void resize(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioBlk *blk_if = obj; + QVirtioDevice *dev = blk_if->vdev; + int n_size = TEST_IMAGE_SIZE / 2; + uint64_t capacity; + QVirtQueue *vq; + QTestState *qts = global_qtest; + + vq = test_basic(dev, t_alloc); + + qmp_discard_response("{ 'execute': 'block_resize', " + " 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); + + qvirtio_wait_queue_isr(qts, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, n_size / 512); + + qvirtqueue_cleanup(dev->bus, vq, t_alloc); + +} + +static void *virtio_blk_test_setup(GString *cmd_line, void *arg) +{ + char *tmp_path = drive_create(); + + g_string_append_printf(cmd_line, + " -drive if=none,id=drive0,file=%s," + "format=raw,auto-read-only=off " + "-drive if=none,id=drive1,file=null-co://," + "file.read-zeroes=on,format=raw ", + tmp_path); + + return arg; +} + +static void register_virtio_blk_test(void) +{ + QOSGraphTestOptions opts = { + .before = virtio_blk_test_setup, + }; + + qos_add_test("indirect", "virtio-blk", indirect, &opts); + qos_add_test("config", "virtio-blk", config, &opts); + qos_add_test("basic", "virtio-blk", basic, &opts); + qos_add_test("resize", "virtio-blk", resize, &opts); + + /* tests just for virtio-blk-pci */ + qos_add_test("msix", "virtio-blk-pci", msix, &opts); + qos_add_test("idx", "virtio-blk-pci", idx, &opts); + qos_add_test("nxvirtq", "virtio-blk-pci", + test_nonexistent_virtqueue, &opts); + qos_add_test("hotplug", "virtio-blk-pci", pci_hotplug, &opts); +} + +libqos_init(register_virtio_blk_test); diff --git a/tests/qtest/virtio-ccw-test.c b/tests/qtest/virtio-ccw-test.c new file mode 100644 index 0000000000..d05236407b --- /dev/null +++ b/tests/qtest/virtio-ccw-test.c @@ -0,0 +1,115 @@ +/* + * QTest testcase for VirtIO CCW + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * Copyright (c) 2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* Until we have a full libqos implementation of virtio-ccw (which requires + * also to add support for I/O channels to qtest), we can only do simple + * tests that initialize the devices. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "libqos/virtio.h" + +static void virtio_balloon_nop(void) +{ + global_qtest = qtest_initf("-device virtio-balloon-ccw"); + qtest_end(); +} + +static void virtconsole_nop(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw,id=vser0 " + "-device virtconsole,bus=vser0.0"); + qtest_end(); +} + +static void virtserialport_nop(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw,id=vser0 " + "-device virtserialport,bus=vser0.0"); + qtest_end(); +} + +static void virtio_serial_nop(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw"); + qtest_end(); +} + +static void virtio_serial_hotplug(void) +{ + QTestState *qts = qtest_initf("-device virtio-serial-ccw"); + + qtest_qmp_device_add(qts, "virtserialport", "hp-port", "{}"); + qtest_qmp_device_del(qts, "hp-port"); + + qtest_quit(qts); +} + +static void virtio_blk_nop(void) +{ + global_qtest = qtest_initf("-drive if=none,id=drv0,file=null-co://," + "file.read-zeroes=on,format=raw " + "-device virtio-blk-ccw,drive=drv0"); + qtest_end(); +} + +static void virtio_net_nop(void) +{ + global_qtest = qtest_initf("-device virtio-net-ccw"); + qtest_end(); +} + +static void virtio_rng_nop(void) +{ + global_qtest = qtest_initf("-device virtio-rng-ccw"); + qtest_end(); +} + +static void virtio_scsi_nop(void) +{ + global_qtest = qtest_initf("-device virtio-scsi-ccw"); + qtest_end(); +} + +static void virtio_scsi_hotplug(void) +{ + QTestState *s = qtest_initf("-drive if=none,id=drv0,file=null-co://," + "file.read-zeroes=on,format=raw " + "-drive if=none,id=drv1,file=null-co://," + "file.read-zeroes=on,format=raw " + "-device virtio-scsi-ccw " + "-device scsi-hd,drive=drv0"); + qtest_qmp_device_add(s, "scsi-hd", "scsihd", "{'drive': 'drv1'}"); + qtest_qmp_device_del(s, "scsihd"); + + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/virtio/balloon/nop", virtio_balloon_nop); + qtest_add_func("/virtio/console/nop", virtconsole_nop); + qtest_add_func("/virtio/serialport/nop", virtserialport_nop); + qtest_add_func("/virtio/serial/nop", virtio_serial_nop); + qtest_add_func("/virtio/serial/hotplug", virtio_serial_hotplug); + qtest_add_func("/virtio/block/nop", virtio_blk_nop); + qtest_add_func("/virtio/net/nop", virtio_net_nop); + qtest_add_func("/virtio/rng/nop", virtio_rng_nop); + qtest_add_func("/virtio/scsi/nop", virtio_scsi_nop); + qtest_add_func("/virtio/scsi/hotplug", virtio_scsi_hotplug); + + ret = g_test_run(); + + return ret; +} diff --git a/tests/qtest/virtio-net-test.c b/tests/qtest/virtio-net-test.c new file mode 100644 index 0000000000..a08e2ffe12 --- /dev/null +++ b/tests/qtest/virtio-net-test.c @@ -0,0 +1,337 @@ +/* + * QTest testcase for VirtIO NIC + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "libqtest-single.h" +#include "qemu/iov.h" +#include "qemu/module.h" +#include "qapi/qmp/qdict.h" +#include "hw/virtio/virtio-net.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-net.h" + +#ifndef ETH_P_RARP +#define ETH_P_RARP 0x8035 +#endif + +#define PCI_SLOT_HP 0x06 +#define PCI_SLOT 0x04 + +#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000) +#define VNET_HDR_SIZE sizeof(struct virtio_net_hdr_mrg_rxbuf) + +#ifndef _WIN32 + +static void rx_test(QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, + int socket) +{ + QTestState *qts = global_qtest; + uint64_t req_addr; + uint32_t free_head; + char test[] = "TEST"; + char buffer[64]; + int len = htonl(sizeof(test)); + struct iovec iov[] = { + { + .iov_base = &len, + .iov_len = sizeof(len), + }, { + .iov_base = test, + .iov_len = sizeof(test), + }, + }; + int ret; + + req_addr = guest_alloc(alloc, 64); + + free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false); + qvirtqueue_kick(qts, dev, vq, free_head); + + ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); + g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_NET_TIMEOUT_US); + memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); + g_assert_cmpstr(buffer, ==, "TEST"); + + guest_free(alloc, req_addr); +} + +static void tx_test(QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, + int socket) +{ + QTestState *qts = global_qtest; + uint64_t req_addr; + uint32_t free_head; + uint32_t len; + char buffer[64]; + int ret; + + req_addr = guest_alloc(alloc, 64); + memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4); + + free_head = qvirtqueue_add(qts, vq, req_addr, 64, false, false); + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_NET_TIMEOUT_US); + guest_free(alloc, req_addr); + + ret = qemu_recv(socket, &len, sizeof(len), 0); + g_assert_cmpint(ret, ==, sizeof(len)); + len = ntohl(len); + + ret = qemu_recv(socket, buffer, len, 0); + g_assert_cmpstr(buffer, ==, "TEST"); +} + +static void rx_stop_cont_test(QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, + int socket) +{ + QTestState *qts = global_qtest; + uint64_t req_addr; + uint32_t free_head; + char test[] = "TEST"; + char buffer[64]; + int len = htonl(sizeof(test)); + QDict *rsp; + struct iovec iov[] = { + { + .iov_base = &len, + .iov_len = sizeof(len), + }, { + .iov_base = test, + .iov_len = sizeof(test), + }, + }; + int ret; + + req_addr = guest_alloc(alloc, 64); + + free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false); + qvirtqueue_kick(qts, dev, vq, free_head); + + rsp = qmp("{ 'execute' : 'stop'}"); + qobject_unref(rsp); + + ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); + g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); + + /* We could check the status, but this command is more importantly to + * ensure the packet data gets queued in QEMU, before we do 'cont'. + */ + rsp = qmp("{ 'execute' : 'query-status'}"); + qobject_unref(rsp); + rsp = qmp("{ 'execute' : 'cont'}"); + qobject_unref(rsp); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_NET_TIMEOUT_US); + memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); + g_assert_cmpstr(buffer, ==, "TEST"); + + guest_free(alloc, req_addr); +} + +static void send_recv_test(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioNet *net_if = obj; + QVirtioDevice *dev = net_if->vdev; + QVirtQueue *rx = net_if->queues[0]; + QVirtQueue *tx = net_if->queues[1]; + int *sv = data; + + rx_test(dev, t_alloc, rx, sv[0]); + tx_test(dev, t_alloc, tx, sv[0]); +} + +static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioNet *net_if = obj; + QVirtioDevice *dev = net_if->vdev; + QVirtQueue *rx = net_if->queues[0]; + int *sv = data; + + rx_stop_cont_test(dev, t_alloc, rx, sv[0]); +} + +#endif + +static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioPCIDevice *dev = obj; + QTestState *qts = dev->pdev->bus->qts; + const char *arch = qtest_get_arch(); + + qtest_qmp_device_add(qts, "virtio-net-pci", "net1", + "{'addr': %s}", stringify(PCI_SLOT_HP)); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qpci_unplug_acpi_device_test(qts, "net1", PCI_SLOT_HP); + } +} + +static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc) +{ + int *sv = data; + char buffer[60]; + int len; + QDict *rsp; + int ret; + uint16_t *proto = (uint16_t *)&buffer[12]; + size_t total_received = 0; + uint64_t start, now, last_rxt, deadline; + + /* Send a set of packets over a few second period */ + rsp = qmp("{ 'execute' : 'announce-self', " + " 'arguments': {" + " 'initial': 20, 'max': 100," + " 'rounds': 300, 'step': 10, 'id': 'bob' } }"); + assert(!qdict_haskey(rsp, "error")); + qobject_unref(rsp); + + /* Catch the first packet and make sure it's a RARP */ + ret = qemu_recv(sv[0], &len, sizeof(len), 0); + g_assert_cmpint(ret, ==, sizeof(len)); + len = ntohl(len); + + ret = qemu_recv(sv[0], buffer, len, 0); + g_assert_cmpint(*proto, ==, htons(ETH_P_RARP)); + + /* + * Stop the announcment by settings rounds to 0 on the + * existing timer. + */ + rsp = qmp("{ 'execute' : 'announce-self', " + " 'arguments': {" + " 'initial': 20, 'max': 100," + " 'rounds': 0, 'step': 10, 'id': 'bob' } }"); + assert(!qdict_haskey(rsp, "error")); + qobject_unref(rsp); + + /* Now make sure the packets stop */ + + /* Times are in us */ + start = g_get_monotonic_time(); + /* 30 packets, max gap 100ms, * 4 for wiggle */ + deadline = start + 1000 * (100 * 30 * 4); + last_rxt = start; + + while (true) { + int saved_err; + ret = qemu_recv(sv[0], buffer, 60, MSG_DONTWAIT); + saved_err = errno; + now = g_get_monotonic_time(); + g_assert_cmpint(now, <, deadline); + + if (ret >= 0) { + if (ret) { + last_rxt = now; + } + total_received += ret; + + /* Check it's not spewing loads */ + g_assert_cmpint(total_received, <, 60 * 30 * 2); + } else { + g_assert_cmpint(saved_err, ==, EAGAIN); + + /* 400ms, i.e. 4 worst case gaps */ + if ((now - last_rxt) > (1000 * 100 * 4)) { + /* Nothings arrived for a while - must have stopped */ + break; + }; + + /* 100ms */ + g_usleep(1000 * 100); + } + }; +} + +static void virtio_net_test_cleanup(void *sockets) +{ + int *sv = sockets; + + close(sv[0]); + qos_invalidate_command_line(); + close(sv[1]); + g_free(sv); +} + +static void *virtio_net_test_setup(GString *cmd_line, void *arg) +{ + int ret; + int *sv = g_new(int, 2); + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); + g_assert_cmpint(ret, !=, -1); + + g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sv[1]); + + g_test_queue_destroy(virtio_net_test_cleanup, sv); + return sv; +} + +static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioNet *dev = obj; + QVirtQueue *vq = dev->queues[1]; + uint64_t req_addr; + uint32_t free_head; + size_t alloc_size = (size_t)data / 64; + QTestState *qts = global_qtest; + int i; + + /* Bypass the limitation by pointing several descriptors to a single + * smaller area */ + req_addr = guest_alloc(t_alloc, alloc_size); + free_head = qvirtqueue_add(qts, vq, req_addr, alloc_size, false, true); + + for (i = 0; i < 64; i++) { + qvirtqueue_add(qts, vq, req_addr, alloc_size, false, i != 63); + } + qvirtqueue_kick(qts, dev->vdev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev->vdev, vq, free_head, NULL, + QVIRTIO_NET_TIMEOUT_US); + guest_free(t_alloc, req_addr); +} + +static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 "); + return arg; +} + +static void register_virtio_net_test(void) +{ + QOSGraphTestOptions opts = { + .before = virtio_net_test_setup, + }; + + qos_add_test("hotplug", "virtio-pci", hotplug, &opts); +#ifndef _WIN32 + qos_add_test("basic", "virtio-net", send_recv_test, &opts); + qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts); +#endif + qos_add_test("announce-self", "virtio-net", announce_self, &opts); + + /* These tests do not need a loopback backend. */ + opts.before = virtio_net_test_setup_nosocket; + opts.arg = (gpointer)UINT_MAX; + qos_add_test("large_tx/uint_max", "virtio-net", large_tx, &opts); + opts.arg = (gpointer)NET_BUFSIZE; + qos_add_test("large_tx/net_bufsize", "virtio-net", large_tx, &opts); +} + +libqos_init(register_virtio_net_test); diff --git a/tests/qtest/virtio-rng-test.c b/tests/qtest/virtio-rng-test.c new file mode 100644 index 0000000000..092ba13068 --- /dev/null +++ b/tests/qtest/virtio-rng-test.c @@ -0,0 +1,38 @@ +/* + * QTest testcase for VirtIO RNG + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-rng.h" + +#define PCI_SLOT_HP 0x06 + +static void rng_hotplug(void *obj, void *data, QGuestAllocator *alloc) +{ + QVirtioPCIDevice *dev = obj; + QTestState *qts = dev->pdev->bus->qts; + + const char *arch = qtest_get_arch(); + + qtest_qmp_device_add(qts, "virtio-rng-pci", "rng1", + "{'addr': %s}", stringify(PCI_SLOT_HP)); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qpci_unplug_acpi_device_test(qts, "rng1", PCI_SLOT_HP); + } +} + +static void register_virtio_rng_test(void) +{ + qos_add_test("hotplug", "virtio-rng-pci", rng_hotplug, NULL); +} + +libqos_init(register_virtio_rng_test); diff --git a/tests/qtest/virtio-scsi-test.c b/tests/qtest/virtio-scsi-test.c new file mode 100644 index 0000000000..0415e75876 --- /dev/null +++ b/tests/qtest/virtio-scsi-test.c @@ -0,0 +1,298 @@ +/* + * QTest testcase for VirtIO SCSI + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * Copyright (c) 2015 Red Hat Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "qemu/module.h" +#include "scsi/constants.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_pci.h" +#include "standard-headers/linux/virtio_scsi.h" +#include "libqos/virtio-scsi.h" +#include "libqos/qgraph.h" + +#define PCI_SLOT 0x02 +#define PCI_FN 0x00 +#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000) + +#define MAX_NUM_QUEUES 64 + +typedef struct { + QVirtioDevice *dev; + int num_queues; + QVirtQueue *vq[MAX_NUM_QUEUES + 2]; +} QVirtioSCSIQueues; + +static QGuestAllocator *alloc; + +static void qvirtio_scsi_pci_free(QVirtioSCSIQueues *vs) +{ + int i; + + for (i = 0; i < vs->num_queues + 2; i++) { + qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], alloc); + } + g_free(vs); +} + +static uint64_t qvirtio_scsi_alloc(QVirtioSCSIQueues *vs, size_t alloc_size, + const void *data) +{ + uint64_t addr; + + addr = guest_alloc(alloc, alloc_size); + if (data) { + memwrite(addr, data, alloc_size); + } + + return addr; +} + +static uint8_t virtio_scsi_do_command(QVirtioSCSIQueues *vs, + const uint8_t *cdb, + const uint8_t *data_in, + size_t data_in_len, + uint8_t *data_out, size_t data_out_len, + struct virtio_scsi_cmd_resp *resp_out) +{ + QVirtQueue *vq; + struct virtio_scsi_cmd_req req = { { 0 } }; + struct virtio_scsi_cmd_resp resp = { .response = 0xff, .status = 0xff }; + uint64_t req_addr, resp_addr, data_in_addr = 0, data_out_addr = 0; + uint8_t response; + uint32_t free_head; + QTestState *qts = global_qtest; + + vq = vs->vq[2]; + + req.lun[0] = 1; /* Select LUN */ + req.lun[1] = 1; /* Select target 1 */ + memcpy(req.cdb, cdb, VIRTIO_SCSI_CDB_SIZE); + + /* XXX: Fix endian if any multi-byte field in req/resp is used */ + + /* Add request header */ + req_addr = qvirtio_scsi_alloc(vs, sizeof(req), &req); + free_head = qvirtqueue_add(qts, vq, req_addr, sizeof(req), false, true); + + if (data_out_len) { + data_out_addr = qvirtio_scsi_alloc(vs, data_out_len, data_out); + qvirtqueue_add(qts, vq, data_out_addr, data_out_len, false, true); + } + + /* Add response header */ + resp_addr = qvirtio_scsi_alloc(vs, sizeof(resp), &resp); + qvirtqueue_add(qts, vq, resp_addr, sizeof(resp), true, !!data_in_len); + + if (data_in_len) { + data_in_addr = qvirtio_scsi_alloc(vs, data_in_len, data_in); + qvirtqueue_add(qts, vq, data_in_addr, data_in_len, true, false); + } + + qvirtqueue_kick(qts, vs->dev, vq, free_head); + qvirtio_wait_used_elem(qts, vs->dev, vq, free_head, NULL, + QVIRTIO_SCSI_TIMEOUT_US); + + response = readb(resp_addr + + offsetof(struct virtio_scsi_cmd_resp, response)); + + if (resp_out) { + memread(resp_addr, resp_out, sizeof(*resp_out)); + } + + guest_free(alloc, req_addr); + guest_free(alloc, resp_addr); + guest_free(alloc, data_in_addr); + guest_free(alloc, data_out_addr); + return response; +} + +static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev) +{ + QVirtioSCSIQueues *vs; + const uint8_t test_unit_ready_cdb[VIRTIO_SCSI_CDB_SIZE] = {}; + struct virtio_scsi_cmd_resp resp; + uint64_t features; + int i; + + vs = g_new0(QVirtioSCSIQueues, 1); + vs->dev = dev; + + features = qvirtio_get_features(dev); + features &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX)); + qvirtio_set_features(dev, features); + + vs->num_queues = qvirtio_config_readl(dev, 0); + + g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); + + for (i = 0; i < vs->num_queues + 2; i++) { + vs->vq[i] = qvirtqueue_setup(dev, alloc, i); + } + + qvirtio_set_driver_ok(dev); + + /* Clear the POWER ON OCCURRED unit attention */ + g_assert_cmpint(virtio_scsi_do_command(vs, test_unit_ready_cdb, + NULL, 0, NULL, 0, &resp), + ==, 0); + g_assert_cmpint(resp.status, ==, CHECK_CONDITION); + g_assert_cmpint(resp.sense[0], ==, 0x70); /* Fixed format sense buffer */ + g_assert_cmpint(resp.sense[2], ==, UNIT_ATTENTION); + g_assert_cmpint(resp.sense[12], ==, 0x29); /* POWER ON */ + g_assert_cmpint(resp.sense[13], ==, 0x00); + + return vs; +} + +static void hotplug(void *obj, void *data, QGuestAllocator *alloc) +{ + QTestState *qts = global_qtest; + + qtest_qmp_device_add(qts, "scsi-hd", "scsihd", "{'drive': 'drv1'}"); + qtest_qmp_device_del(qts, "scsihd"); +} + +/* Test WRITE SAME with the lba not aligned */ +static void test_unaligned_write_same(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QVirtioSCSI *scsi = obj; + QVirtioSCSIQueues *vs; + uint8_t buf1[512] = { 0 }; + uint8_t buf2[512] = { 1 }; + const uint8_t write_same_cdb_1[VIRTIO_SCSI_CDB_SIZE] = { + 0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00 + }; + const uint8_t write_same_cdb_2[VIRTIO_SCSI_CDB_SIZE] = { + 0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 + }; + const uint8_t write_same_cdb_ndob[VIRTIO_SCSI_CDB_SIZE] = { + 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 + }; + + alloc = t_alloc; + vs = qvirtio_scsi_init(scsi->vdev); + + g_assert_cmphex(0, ==, + virtio_scsi_do_command(vs, write_same_cdb_1, NULL, 0, buf1, 512, + NULL)); + + g_assert_cmphex(0, ==, + virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, + NULL)); + + g_assert_cmphex(0, ==, + virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, + NULL)); + + qvirtio_scsi_pci_free(vs); +} + +static void test_iothread_attach_node(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QVirtioSCSIPCI *scsi_pci = obj; + QVirtioSCSI *scsi = &scsi_pci->scsi; + QVirtioSCSIQueues *vs; + char tmp_path[] = "/tmp/qtest.XXXXXX"; + int fd; + int ret; + + uint8_t buf[512] = { 0 }; + const uint8_t write_cdb[VIRTIO_SCSI_CDB_SIZE] = { + /* WRITE(10) to LBA 0, transfer length 1 */ + 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 + }; + + alloc = t_alloc; + vs = qvirtio_scsi_init(scsi->vdev); + + /* Create a temporary qcow2 overlay*/ + fd = mkstemp(tmp_path); + g_assert(fd >= 0); + close(fd); + + if (!have_qemu_img()) { + g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " + "skipping snapshot test"); + goto fail; + } + + mkqcow2(tmp_path, 64); + + /* Attach the overlay to the null0 node */ + qtest_qmp_assert_success(scsi_pci->pci_vdev.pdev->bus->qts, + "{'execute': 'blockdev-add', 'arguments': {" + " 'driver': 'qcow2', 'node-name': 'overlay'," + " 'backing': 'null0', 'file': {" + " 'driver': 'file', 'filename': %s}}}", + tmp_path); + + /* Send a request to see if the AioContext is still right */ + ret = virtio_scsi_do_command(vs, write_cdb, NULL, 0, buf, 512, NULL); + g_assert_cmphex(ret, ==, 0); + +fail: + qvirtio_scsi_pci_free(vs); + unlink(tmp_path); +} + +static void *virtio_scsi_hotplug_setup(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, + " -drive id=drv1,if=none,file=null-co://," + "file.read-zeroes=on,format=raw"); + return arg; +} + +static void *virtio_scsi_setup(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, + " -drive file=blkdebug::null-co://," + "file.image.read-zeroes=on," + "if=none,id=dr1,format=raw,file.align=4k " + "-device scsi-hd,drive=dr1,lun=0,scsi-id=1"); + return arg; +} + +static void *virtio_scsi_setup_iothread(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, + " -object iothread,id=thread0" + " -blockdev driver=null-co,read-zeroes=on,node-name=null0" + " -device scsi-hd,drive=null0"); + return arg; +} + +static void register_virtio_scsi_test(void) +{ + QOSGraphTestOptions opts = { }; + + opts.before = virtio_scsi_hotplug_setup; + qos_add_test("hotplug", "virtio-scsi", hotplug, &opts); + + opts.before = virtio_scsi_setup; + qos_add_test("unaligned-write-same", "virtio-scsi", + test_unaligned_write_same, &opts); + + opts.before = virtio_scsi_setup_iothread; + opts.edge = (QOSGraphEdgeOptions) { + .extra_device_opts = "iothread=thread0", + }; + qos_add_test("iothread-attach-node", "virtio-scsi-pci", + test_iothread_attach_node, &opts); +} + +libqos_init(register_virtio_scsi_test); diff --git a/tests/qtest/virtio-serial-test.c b/tests/qtest/virtio-serial-test.c new file mode 100644 index 0000000000..2541034822 --- /dev/null +++ b/tests/qtest/virtio-serial-test.c @@ -0,0 +1,39 @@ +/* + * QTest testcase for VirtIO Serial + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "qemu/module.h" +#include "libqos/virtio-serial.h" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void virtio_serial_nop(void *obj, void *data, QGuestAllocator *alloc) +{ + /* no operation */ +} + +static void serial_hotplug(void *obj, void *data, QGuestAllocator *alloc) +{ + qtest_qmp_device_add(global_qtest, "virtserialport", "hp-port", "{}"); + qtest_qmp_device_del(global_qtest, "hp-port"); +} + +static void register_virtio_serial_test(void) +{ + QOSGraphTestOptions opts = { }; + + opts.edge.before_cmd_line = "-device virtconsole,bus=vser0.0"; + qos_add_test("console-nop", "virtio-serial", virtio_serial_nop, &opts); + + opts.edge.before_cmd_line = "-device virtserialport,bus=vser0.0"; + qos_add_test("serialport-nop", "virtio-serial", virtio_serial_nop, &opts); + + qos_add_test("hotplug", "virtio-serial", serial_hotplug, NULL); +} +libqos_init(register_virtio_serial_test); diff --git a/tests/qtest/virtio-test.c b/tests/qtest/virtio-test.c new file mode 100644 index 0000000000..f7c6afdcf1 --- /dev/null +++ b/tests/qtest/virtio-test.c @@ -0,0 +1,26 @@ +/* + * QTest testcase for virtio + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void nop(void *obj, void *data, QGuestAllocator *alloc) +{ +} + +static void register_virtio_test(void) +{ + qos_add_test("nop", "virtio", nop, NULL); +} + +libqos_init(register_virtio_test); diff --git a/tests/qtest/vmgenid-test.c b/tests/qtest/vmgenid-test.c new file mode 100644 index 0000000000..efba76e716 --- /dev/null +++ b/tests/qtest/vmgenid-test.c @@ -0,0 +1,185 @@ +/* + * QTest testcase for VM Generation ID + * + * Copyright (c) 2016 Red Hat, Inc. + * Copyright (c) 2017 Skyport Systems + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/bitmap.h" +#include "qemu/uuid.h" +#include "hw/acpi/acpi-defs.h" +#include "boot-sector.h" +#include "acpi-utils.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" + +#define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" +#define VMGENID_GUID_OFFSET 40 /* allow space for + * OVMF SDT Header Probe Supressor + */ +#define RSDP_ADDR_INVALID 0x100000 /* RSDP must be below this address */ + +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_len, table_length; + uint8_t *rsdt, *ent; + + /* Wait for guest firmware to finish and start the payload. */ + boot_sector_test(qts); + + /* Tables should be initialized now. */ + rsdp_offset = acpi_find_rsdp_address(qts); + + g_assert_cmphex(rsdp_offset, <, RSDP_ADDR_INVALID); + + + acpi_fetch_rsdp_table(qts, rsdp_offset, rsdp_table); + acpi_fetch_table(qts, &rsdt, &rsdt_len, &rsdp_table[16 /* RsdtAddress */], + 4, "RSDT", true); + + ACPI_FOREACH_RSDT_ENTRY(rsdt, rsdt_len, ent, 4 /* Entry size */) { + uint8_t *table_aml; + + acpi_fetch_table(qts, &table_aml, &table_length, ent, 4, 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 + */ + 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(vgia_val) + VMGENID_GUID_OFFSET; + g_free(table_aml); + break; + } + g_free(table_aml); + } + g_free(rsdt); + return guid_offset; +} + +static void read_guid_from_memory(QTestState *qts, QemuUUID *guid) +{ + uint32_t vmgenid_addr; + int i; + + vmgenid_addr = acpi_find_vgia(qts); + g_assert(vmgenid_addr); + + /* Read the GUID directly from guest memory */ + for (i = 0; i < 16; i++) { + guid->data[i] = qtest_readb(qts, vmgenid_addr + i); + } + /* The GUID is in little-endian format in the guest, while QEMU + * uses big-endian. Swap after reading. + */ + *guid = qemu_uuid_bswap(*guid); +} + +static void read_guid_from_monitor(QTestState *qts, QemuUUID *guid) +{ + QDict *rsp, *rsp_ret; + const char *guid_str; + + rsp = qtest_qmp(qts, "{ 'execute': 'query-vm-generation-id' }"); + if (qdict_haskey(rsp, "return")) { + rsp_ret = qdict_get_qdict(rsp, "return"); + g_assert(qdict_haskey(rsp_ret, "guid")); + guid_str = qdict_get_str(rsp_ret, "guid"); + g_assert(qemu_uuid_parse(guid_str, guid) == 0); + } + qobject_unref(rsp); +} + +static char disk[] = "tests/vmgenid-test-disk-XXXXXX"; + +#define GUID_CMD(guid) \ + "-accel kvm -accel tcg " \ + "-device vmgenid,id=testvgid,guid=%s " \ + "-drive id=hd0,if=none,file=%s,format=raw " \ + "-device ide-hd,drive=hd0 ", guid, disk + +static void vmgenid_set_guid_test(void) +{ + QemuUUID expected, measured; + QTestState *qts; + + g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0); + + qts = qtest_initf(GUID_CMD(VGID_GUID)); + + /* Read the GUID from accessing guest memory */ + read_guid_from_memory(qts, &measured); + g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0); + + qtest_quit(qts); +} + +static void vmgenid_set_guid_auto_test(void) +{ + QemuUUID measured; + QTestState *qts; + + qts = qtest_initf(GUID_CMD("auto")); + + read_guid_from_memory(qts, &measured); + + /* Just check that the GUID is non-null */ + g_assert(!qemu_uuid_is_null(&measured)); + + qtest_quit(qts); +} + +static void vmgenid_query_monitor_test(void) +{ + QemuUUID expected, measured; + QTestState *qts; + + g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0); + + qts = qtest_initf(GUID_CMD(VGID_GUID)); + + /* Read the GUID via the monitor */ + read_guid_from_monitor(qts, &measured); + g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + int ret; + + ret = boot_sector_init(disk); + if (ret) { + return ret; + } + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/vmgenid/vmgenid/set-guid", + vmgenid_set_guid_test); + qtest_add_func("/vmgenid/vmgenid/set-guid-auto", + vmgenid_set_guid_auto_test); + qtest_add_func("/vmgenid/vmgenid/query-monitor", + vmgenid_query_monitor_test); + ret = g_test_run(); + boot_sector_cleanup(disk); + + return ret; +} diff --git a/tests/qtest/vmxnet3-test.c b/tests/qtest/vmxnet3-test.c new file mode 100644 index 0000000000..a81025252c --- /dev/null +++ b/tests/qtest/vmxnet3-test.c @@ -0,0 +1,58 @@ +/* + * QTest testcase for vmxnet3 NIC + * + * Copyright (c) 2013-2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QVmxnet3 QVmxnet3; + +struct QVmxnet3 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *vmxnet3_get_driver(void *obj, const char *interface) +{ + QVmxnet3 *vmxnet3 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &vmxnet3->dev; + } + + fprintf(stderr, "%s not present in vmxnet3\n", interface); + g_assert_not_reached(); +} + +static void *vmxnet3_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QVmxnet3 *vmxnet3 = g_new0(QVmxnet3, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&vmxnet3->dev, bus, addr); + vmxnet3->obj.get_driver = vmxnet3_get_driver; + + return &vmxnet3->obj; +} + +static void vmxnet3_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("vmxnet3", vmxnet3_create); + qos_node_consumes("vmxnet3", "pci-bus", &opts); + qos_node_produces("vmxnet3", "pci-device"); +} + +libqos_init(vmxnet3_register_nodes); diff --git a/tests/qtest/wdt_ib700-test.c b/tests/qtest/wdt_ib700-test.c new file mode 100644 index 0000000000..797288d939 --- /dev/null +++ b/tests/qtest/wdt_ib700-test.c @@ -0,0 +1,118 @@ +/* + * QTest testcase for the IB700 watchdog + * + * Copyright (c) 2014 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qemu/timer.h" + +static void qmp_check_no_event(QTestState *s) +{ + QDict *resp = qtest_qmp(s, "{'execute':'query-status'}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); +} + +static QDict *ib700_program_and_wait(QTestState *s) +{ + QDict *event, *data; + + qtest_clock_step(s, NANOSECONDS_PER_SECOND * 40); + qmp_check_no_event(s); + + /* 2 second limit */ + qtest_outb(s, 0x443, 14); + + /* Ping */ + qtest_clock_step(s, NANOSECONDS_PER_SECOND); + qmp_check_no_event(s); + qtest_outb(s, 0x443, 14); + + /* Disable */ + qtest_clock_step(s, NANOSECONDS_PER_SECOND); + qmp_check_no_event(s); + qtest_outb(s, 0x441, 1); + qtest_clock_step(s, 3 * NANOSECONDS_PER_SECOND); + qmp_check_no_event(s); + + /* Enable and let it fire */ + qtest_outb(s, 0x443, 13); + qtest_clock_step(s, 3 * NANOSECONDS_PER_SECOND); + qmp_check_no_event(s); + qtest_clock_step(s, 2 * NANOSECONDS_PER_SECOND); + event = qtest_qmp_eventwait_ref(s, "WATCHDOG"); + data = qdict_get_qdict(event, "data"); + qobject_ref(data); + qobject_unref(event); + return data; +} + + +static void ib700_pause(void) +{ + QDict *d; + QTestState *s = qtest_init("-watchdog-action pause -device ib700"); + + qtest_irq_intercept_in(s, "ioapic"); + d = ib700_program_and_wait(s); + g_assert(!strcmp(qdict_get_str(d, "action"), "pause")); + qobject_unref(d); + qtest_qmp_eventwait(s, "STOP"); + qtest_quit(s); +} + +static void ib700_reset(void) +{ + QDict *d; + QTestState *s = qtest_init("-watchdog-action reset -device ib700"); + + qtest_irq_intercept_in(s, "ioapic"); + d = ib700_program_and_wait(s); + g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); + qobject_unref(d); + qtest_qmp_eventwait(s, "RESET"); + qtest_quit(s); +} + +static void ib700_shutdown(void) +{ + QDict *d; + QTestState *s; + + s = qtest_init("-watchdog-action reset -no-reboot -device ib700"); + qtest_irq_intercept_in(s, "ioapic"); + d = ib700_program_and_wait(s); + g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); + qobject_unref(d); + qtest_qmp_eventwait(s, "SHUTDOWN"); + qtest_quit(s); +} + +static void ib700_none(void) +{ + QDict *d; + QTestState *s = qtest_init("-watchdog-action none -device ib700"); + + qtest_irq_intercept_in(s, "ioapic"); + d = ib700_program_and_wait(s); + g_assert(!strcmp(qdict_get_str(d, "action"), "none")); + qobject_unref(d); + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/wdt_ib700/pause", ib700_pause); + qtest_add_func("/wdt_ib700/reset", ib700_reset); + qtest_add_func("/wdt_ib700/shutdown", ib700_shutdown); + qtest_add_func("/wdt_ib700/none", ib700_none); + + return g_test_run(); +} diff --git a/tests/rtas-test.c b/tests/rtas-test.c deleted file mode 100644 index 167b42db38..0000000000 --- a/tests/rtas-test.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/cutils.h" -#include "libqtest.h" - -#include "libqos/libqos-spapr.h" -#include "libqos/rtas.h" - -static void test_rtas_get_time_of_day(void) -{ - QOSState *qs; - struct tm tm; - uint32_t ns; - uint64_t ret; - time_t t1, t2; - - qs = qtest_spapr_boot("-machine pseries"); - - t1 = time(NULL); - ret = qrtas_get_time_of_day(qs->qts, &qs->alloc, &tm, &ns); - g_assert_cmpint(ret, ==, 0); - t2 = mktimegm(&tm); - g_assert(t2 - t1 < 5); /* 5 sec max to run the test */ - - qtest_shutdown(qs); -} - -int main(int argc, char *argv[]) -{ - const char *arch = qtest_get_arch(); - - g_test_init(&argc, &argv, NULL); - - if (strcmp(arch, "ppc64")) { - g_printerr("RTAS requires ppc64-softmmu/qemu-system-ppc64\n"); - exit(EXIT_FAILURE); - } - qtest_add_func("rtas/get-time-of-day", test_rtas_get_time_of_day); - - return g_test_run(); -} diff --git a/tests/rtc-test.c b/tests/rtc-test.c deleted file mode 100644 index c7af34f6b1..0000000000 --- a/tests/rtc-test.c +++ /dev/null @@ -1,720 +0,0 @@ -/* - * QTest testcase for the MC146818 real-time clock - * - * Copyright IBM, Corp. 2012 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" - -#include "libqtest-single.h" -#include "qemu/timer.h" -#include "hw/rtc/mc146818rtc.h" -#include "hw/rtc/mc146818rtc_regs.h" - -#define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768) - -static uint8_t base = 0x70; - -static int bcd2dec(int value) -{ - return (((value >> 4) & 0x0F) * 10) + (value & 0x0F); -} - -static uint8_t cmos_read(uint8_t reg) -{ - outb(base + 0, reg); - return inb(base + 1); -} - -static void cmos_write(uint8_t reg, uint8_t val) -{ - outb(base + 0, reg); - outb(base + 1, val); -} - -static int tm_cmp(struct tm *lhs, struct tm *rhs) -{ - time_t a, b; - struct tm d1, d2; - - memcpy(&d1, lhs, sizeof(d1)); - memcpy(&d2, rhs, sizeof(d2)); - - a = mktime(&d1); - b = mktime(&d2); - - if (a < b) { - return -1; - } else if (a > b) { - return 1; - } - - return 0; -} - -#if 0 -static void print_tm(struct tm *tm) -{ - printf("%04d-%02d-%02d %02d:%02d:%02d\n", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff); -} -#endif - -static void cmos_get_date_time(struct tm *date) -{ - int base_year = 2000, hour_offset; - int sec, min, hour, mday, mon, year; - time_t ts; - struct tm dummy; - - sec = cmos_read(RTC_SECONDS); - min = cmos_read(RTC_MINUTES); - hour = cmos_read(RTC_HOURS); - mday = cmos_read(RTC_DAY_OF_MONTH); - mon = cmos_read(RTC_MONTH); - year = cmos_read(RTC_YEAR); - - if ((cmos_read(RTC_REG_B) & REG_B_DM) == 0) { - sec = bcd2dec(sec); - min = bcd2dec(min); - hour = bcd2dec(hour); - mday = bcd2dec(mday); - mon = bcd2dec(mon); - year = bcd2dec(year); - hour_offset = 80; - } else { - hour_offset = 0x80; - } - - if ((cmos_read(0x0B) & REG_B_24H) == 0) { - if (hour >= hour_offset) { - hour -= hour_offset; - hour += 12; - } - } - - ts = time(NULL); - localtime_r(&ts, &dummy); - - date->tm_isdst = dummy.tm_isdst; - date->tm_sec = sec; - date->tm_min = min; - date->tm_hour = hour; - date->tm_mday = mday; - date->tm_mon = mon - 1; - date->tm_year = base_year + year - 1900; -#ifndef __sun__ - date->tm_gmtoff = 0; -#endif - - ts = mktime(date); -} - -static void check_time(int wiggle) -{ - struct tm start, date[4], end; - struct tm *datep; - time_t ts; - - /* - * This check assumes a few things. First, we cannot guarantee that we get - * a consistent reading from the wall clock because we may hit an edge of - * the clock while reading. To work around this, we read four clock readings - * such that at least two of them should match. We need to assume that one - * reading is corrupt so we need four readings to ensure that we have at - * least two consecutive identical readings - * - * It's also possible that we'll cross an edge reading the host clock so - * simply check to make sure that the clock reading is within the period of - * when we expect it to be. - */ - - ts = time(NULL); - gmtime_r(&ts, &start); - - cmos_get_date_time(&date[0]); - cmos_get_date_time(&date[1]); - cmos_get_date_time(&date[2]); - cmos_get_date_time(&date[3]); - - ts = time(NULL); - gmtime_r(&ts, &end); - - if (tm_cmp(&date[0], &date[1]) == 0) { - datep = &date[0]; - } else if (tm_cmp(&date[1], &date[2]) == 0) { - datep = &date[1]; - } else if (tm_cmp(&date[2], &date[3]) == 0) { - datep = &date[2]; - } else { - g_assert_not_reached(); - } - - if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) { - long t, s; - - start.tm_isdst = datep->tm_isdst; - - t = (long)mktime(datep); - s = (long)mktime(&start); - if (t < s) { - g_test_message("RTC is %ld second(s) behind wall-clock", (s - t)); - } else { - g_test_message("RTC is %ld second(s) ahead of wall-clock", (t - s)); - } - - g_assert_cmpint(ABS(t - s), <=, wiggle); - } -} - -static int wiggle = 2; - -static void set_year_20xx(void) -{ - /* Set BCD mode */ - cmos_write(RTC_REG_B, REG_B_24H); - cmos_write(RTC_REG_A, 0x76); - cmos_write(RTC_YEAR, 0x11); - cmos_write(RTC_CENTURY, 0x20); - cmos_write(RTC_MONTH, 0x02); - cmos_write(RTC_DAY_OF_MONTH, 0x02); - cmos_write(RTC_HOURS, 0x02); - cmos_write(RTC_MINUTES, 0x04); - cmos_write(RTC_SECONDS, 0x58); - cmos_write(RTC_REG_A, 0x26); - - g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); - g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); - g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11); - g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20); - - if (sizeof(time_t) == 4) { - return; - } - - /* Set a date in 2080 to ensure there is no year-2038 overflow. */ - cmos_write(RTC_REG_A, 0x76); - cmos_write(RTC_YEAR, 0x80); - cmos_write(RTC_REG_A, 0x26); - - g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); - g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); - g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80); - g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20); - - cmos_write(RTC_REG_A, 0x76); - cmos_write(RTC_YEAR, 0x11); - cmos_write(RTC_REG_A, 0x26); - - g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); - g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); - g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11); - g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20); -} - -static void set_year_1980(void) -{ - /* Set BCD mode */ - cmos_write(RTC_REG_B, REG_B_24H); - cmos_write(RTC_REG_A, 0x76); - cmos_write(RTC_YEAR, 0x80); - cmos_write(RTC_CENTURY, 0x19); - cmos_write(RTC_MONTH, 0x02); - cmos_write(RTC_DAY_OF_MONTH, 0x02); - cmos_write(RTC_HOURS, 0x02); - cmos_write(RTC_MINUTES, 0x04); - cmos_write(RTC_SECONDS, 0x58); - cmos_write(RTC_REG_A, 0x26); - - g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); - g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); - g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); - g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80); - g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x19); -} - -static void bcd_check_time(void) -{ - /* Set BCD mode */ - cmos_write(RTC_REG_B, REG_B_24H); - check_time(wiggle); -} - -static void dec_check_time(void) -{ - /* Set DEC mode */ - cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM); - check_time(wiggle); -} - -static void alarm_time(void) -{ - struct tm now; - time_t ts; - int i; - - ts = time(NULL); - gmtime_r(&ts, &now); - - /* set DEC mode */ - cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM); - - g_assert(!get_irq(RTC_ISA_IRQ)); - cmos_read(RTC_REG_C); - - now.tm_sec = (now.tm_sec + 2) % 60; - cmos_write(RTC_SECONDS_ALARM, now.tm_sec); - cmos_write(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE); - cmos_write(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE); - cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_AIE); - - for (i = 0; i < 2 + wiggle; i++) { - if (get_irq(RTC_ISA_IRQ)) { - break; - } - - clock_step(1000000000); - } - - g_assert(get_irq(RTC_ISA_IRQ)); - g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0); - g_assert(cmos_read(RTC_REG_C) == 0); -} - -static void set_time_regs(int h, int m, int s) -{ - cmos_write(RTC_HOURS, h); - cmos_write(RTC_MINUTES, m); - cmos_write(RTC_SECONDS, s); -} - -static void set_time(int mode, int h, int m, int s) -{ - cmos_write(RTC_REG_B, mode); - cmos_write(RTC_REG_A, 0x76); - set_time_regs(h, m, s); - cmos_write(RTC_REG_A, 0x26); -} - -static void set_datetime_bcd(int h, int min, int s, int d, int m, int y) -{ - cmos_write(RTC_HOURS, h); - cmos_write(RTC_MINUTES, min); - cmos_write(RTC_SECONDS, s); - cmos_write(RTC_YEAR, y & 0xFF); - cmos_write(RTC_CENTURY, y >> 8); - cmos_write(RTC_MONTH, m); - cmos_write(RTC_DAY_OF_MONTH, d); -} - -static void set_datetime_dec(int h, int min, int s, int d, int m, int y) -{ - cmos_write(RTC_HOURS, h); - cmos_write(RTC_MINUTES, min); - cmos_write(RTC_SECONDS, s); - cmos_write(RTC_YEAR, y % 100); - cmos_write(RTC_CENTURY, y / 100); - cmos_write(RTC_MONTH, m); - cmos_write(RTC_DAY_OF_MONTH, d); -} - -static void set_datetime(int mode, int h, int min, int s, int d, int m, int y) -{ - cmos_write(RTC_REG_B, mode); - - cmos_write(RTC_REG_A, 0x76); - if (mode & REG_B_DM) { - set_datetime_dec(h, min, s, d, m, y); - } else { - set_datetime_bcd(h, min, s, d, m, y); - } - cmos_write(RTC_REG_A, 0x26); -} - -#define assert_time(h, m, s) \ - do { \ - g_assert_cmpint(cmos_read(RTC_HOURS), ==, h); \ - g_assert_cmpint(cmos_read(RTC_MINUTES), ==, m); \ - g_assert_cmpint(cmos_read(RTC_SECONDS), ==, s); \ - } while(0) - -#define assert_datetime_bcd(h, min, s, d, m, y) \ - do { \ - g_assert_cmpint(cmos_read(RTC_HOURS), ==, h); \ - g_assert_cmpint(cmos_read(RTC_MINUTES), ==, min); \ - g_assert_cmpint(cmos_read(RTC_SECONDS), ==, s); \ - g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, d); \ - g_assert_cmpint(cmos_read(RTC_MONTH), ==, m); \ - g_assert_cmpint(cmos_read(RTC_YEAR), ==, (y & 0xFF)); \ - g_assert_cmpint(cmos_read(RTC_CENTURY), ==, (y >> 8)); \ - } while(0) - -static void basic_12h_bcd(void) -{ - /* set BCD 12 hour mode */ - set_time(0, 0x81, 0x59, 0x00); - clock_step(1000000000LL); - assert_time(0x81, 0x59, 0x01); - clock_step(59000000000LL); - assert_time(0x82, 0x00, 0x00); - - /* test BCD wraparound */ - set_time(0, 0x09, 0x59, 0x59); - clock_step(60000000000LL); - assert_time(0x10, 0x00, 0x59); - - /* 12 AM -> 1 AM */ - set_time(0, 0x12, 0x59, 0x59); - clock_step(1000000000LL); - assert_time(0x01, 0x00, 0x00); - - /* 12 PM -> 1 PM */ - set_time(0, 0x92, 0x59, 0x59); - clock_step(1000000000LL); - assert_time(0x81, 0x00, 0x00); - - /* 11 AM -> 12 PM */ - set_time(0, 0x11, 0x59, 0x59); - clock_step(1000000000LL); - assert_time(0x92, 0x00, 0x00); - /* TODO: test day wraparound */ - - /* 11 PM -> 12 AM */ - set_time(0, 0x91, 0x59, 0x59); - clock_step(1000000000LL); - assert_time(0x12, 0x00, 0x00); - /* TODO: test day wraparound */ -} - -static void basic_12h_dec(void) -{ - /* set decimal 12 hour mode */ - set_time(REG_B_DM, 0x81, 59, 0); - clock_step(1000000000LL); - assert_time(0x81, 59, 1); - clock_step(59000000000LL); - assert_time(0x82, 0, 0); - - /* 12 PM -> 1 PM */ - set_time(REG_B_DM, 0x8c, 59, 59); - clock_step(1000000000LL); - assert_time(0x81, 0, 0); - - /* 12 AM -> 1 AM */ - set_time(REG_B_DM, 0x0c, 59, 59); - clock_step(1000000000LL); - assert_time(0x01, 0, 0); - - /* 11 AM -> 12 PM */ - set_time(REG_B_DM, 0x0b, 59, 59); - clock_step(1000000000LL); - assert_time(0x8c, 0, 0); - - /* 11 PM -> 12 AM */ - set_time(REG_B_DM, 0x8b, 59, 59); - clock_step(1000000000LL); - assert_time(0x0c, 0, 0); - /* TODO: test day wraparound */ -} - -static void basic_24h_bcd(void) -{ - /* set BCD 24 hour mode */ - set_time(REG_B_24H, 0x09, 0x59, 0x00); - clock_step(1000000000LL); - assert_time(0x09, 0x59, 0x01); - clock_step(59000000000LL); - assert_time(0x10, 0x00, 0x00); - - /* test BCD wraparound */ - set_time(REG_B_24H, 0x09, 0x59, 0x00); - clock_step(60000000000LL); - assert_time(0x10, 0x00, 0x00); - - /* TODO: test day wraparound */ - set_time(REG_B_24H, 0x23, 0x59, 0x00); - clock_step(60000000000LL); - assert_time(0x00, 0x00, 0x00); -} - -static void basic_24h_dec(void) -{ - /* set decimal 24 hour mode */ - set_time(REG_B_24H | REG_B_DM, 9, 59, 0); - clock_step(1000000000LL); - assert_time(9, 59, 1); - clock_step(59000000000LL); - assert_time(10, 0, 0); - - /* test BCD wraparound */ - set_time(REG_B_24H | REG_B_DM, 9, 59, 0); - clock_step(60000000000LL); - assert_time(10, 0, 0); - - /* TODO: test day wraparound */ - set_time(REG_B_24H | REG_B_DM, 23, 59, 0); - clock_step(60000000000LL); - assert_time(0, 0, 0); -} - -static void am_pm_alarm(void) -{ - cmos_write(RTC_MINUTES_ALARM, 0xC0); - cmos_write(RTC_SECONDS_ALARM, 0xC0); - - /* set BCD 12 hour mode */ - cmos_write(RTC_REG_B, 0); - - /* Set time and alarm hour. */ - cmos_write(RTC_REG_A, 0x76); - cmos_write(RTC_HOURS_ALARM, 0x82); - cmos_write(RTC_HOURS, 0x81); - cmos_write(RTC_MINUTES, 0x59); - cmos_write(RTC_SECONDS, 0x00); - cmos_read(RTC_REG_C); - cmos_write(RTC_REG_A, 0x26); - - /* Check that alarm triggers when AM/PM is set. */ - clock_step(60000000000LL); - g_assert(cmos_read(RTC_HOURS) == 0x82); - g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0); - - /* - * Each of the following two tests takes over 60 seconds due to the time - * needed to report the PIT interrupts. Unfortunately, our PIT device - * model keeps counting even when GATE=0, so we cannot simply disable - * it in main(). - */ - if (g_test_quick()) { - return; - } - - /* set DEC 12 hour mode */ - cmos_write(RTC_REG_B, REG_B_DM); - - /* Set time and alarm hour. */ - cmos_write(RTC_REG_A, 0x76); - cmos_write(RTC_HOURS_ALARM, 0x82); - cmos_write(RTC_HOURS, 3); - cmos_write(RTC_MINUTES, 0); - cmos_write(RTC_SECONDS, 0); - cmos_read(RTC_REG_C); - cmos_write(RTC_REG_A, 0x26); - - /* Check that alarm triggers. */ - clock_step(3600 * 11 * 1000000000LL); - g_assert(cmos_read(RTC_HOURS) == 0x82); - g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0); - - /* Same as above, with inverted HOURS and HOURS_ALARM. */ - cmos_write(RTC_REG_A, 0x76); - cmos_write(RTC_HOURS_ALARM, 2); - cmos_write(RTC_HOURS, 3); - cmos_write(RTC_MINUTES, 0); - cmos_write(RTC_SECONDS, 0); - cmos_read(RTC_REG_C); - cmos_write(RTC_REG_A, 0x26); - - /* Check that alarm does not trigger if hours differ only by AM/PM. */ - clock_step(3600 * 11 * 1000000000LL); - g_assert(cmos_read(RTC_HOURS) == 0x82); - g_assert((cmos_read(RTC_REG_C) & REG_C_AF) == 0); -} - -/* success if no crash or abort */ -static void fuzz_registers(void) -{ - unsigned int i; - - for (i = 0; i < 1000; i++) { - uint8_t reg, val; - - reg = (uint8_t)g_test_rand_int_range(0, 16); - val = (uint8_t)g_test_rand_int_range(0, 256); - - cmos_write(reg, val); - cmos_read(reg); - } -} - -static void register_b_set_flag(void) -{ - if (cmos_read(RTC_REG_A) & REG_A_UIP) { - clock_step(UIP_HOLD_LENGTH + NANOSECONDS_PER_SECOND / 5); - } - g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0); - - /* Enable binary-coded decimal (BCD) mode and SET flag in Register B*/ - cmos_write(RTC_REG_B, REG_B_24H | REG_B_SET); - - set_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); - - assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); - - /* Since SET flag is still enabled, time does not advance. */ - clock_step(1000000000LL); - assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); - - /* Disable SET flag in Register B */ - cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_SET); - - assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); - - /* Since SET flag is disabled, the clock now advances. */ - clock_step(1000000000LL); - assert_datetime_bcd(0x02, 0x04, 0x59, 0x02, 0x02, 0x2011); -} - -static void divider_reset(void) -{ - /* Enable binary-coded decimal (BCD) mode in Register B*/ - cmos_write(RTC_REG_B, REG_B_24H); - - /* Enter divider reset */ - cmos_write(RTC_REG_A, 0x76); - set_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); - - assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); - - /* Since divider reset flag is still enabled, these are equality checks. */ - clock_step(1000000000LL); - assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); - - /* The first update ends 500 ms after divider reset */ - cmos_write(RTC_REG_A, 0x26); - clock_step(500000000LL - UIP_HOLD_LENGTH - 1); - g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0); - assert_datetime_bcd(0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); - - clock_step(1); - g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, !=, 0); - clock_step(UIP_HOLD_LENGTH); - g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0); - - assert_datetime_bcd(0x02, 0x04, 0x59, 0x02, 0x02, 0x2011); -} - -static void uip_stuck(void) -{ - set_datetime(REG_B_24H, 0x02, 0x04, 0x58, 0x02, 0x02, 0x2011); - - /* The first update ends 500 ms after divider reset */ - (void)cmos_read(RTC_REG_C); - clock_step(500000000LL); - g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0); - assert_datetime_bcd(0x02, 0x04, 0x59, 0x02, 0x02, 0x2011); - - /* UF is now set. */ - cmos_write(RTC_HOURS_ALARM, 0x02); - cmos_write(RTC_MINUTES_ALARM, 0xC0); - cmos_write(RTC_SECONDS_ALARM, 0xC0); - - /* Because the alarm will fire soon, reading register A will latch UIP. */ - clock_step(1000000000LL - UIP_HOLD_LENGTH / 2); - g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, !=, 0); - - /* Move the alarm far away. This must not cause UIP to remain stuck! */ - cmos_write(RTC_HOURS_ALARM, 0x03); - clock_step(UIP_HOLD_LENGTH); - g_assert_cmpint(cmos_read(RTC_REG_A) & REG_A_UIP, ==, 0); -} - -#define RTC_PERIOD_CODE1 13 /* 8 Hz */ -#define RTC_PERIOD_CODE2 15 /* 2 Hz */ - -#define RTC_PERIOD_TEST_NR 50 - -static uint64_t wait_periodic_interrupt(uint64_t real_time) -{ - while (!get_irq(RTC_ISA_IRQ)) { - real_time = clock_step_next(); - } - - g_assert((cmos_read(RTC_REG_C) & REG_C_PF) != 0); - return real_time; -} - -static void periodic_timer(void) -{ - int i; - uint64_t period_clocks, period_time, start_time, real_time; - - /* disable all interrupts. */ - cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & - ~(REG_B_PIE | REG_B_AIE | REG_B_UIE)); - cmos_write(RTC_REG_A, RTC_PERIOD_CODE1); - /* enable periodic interrupt after properly configure the period. */ - cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_PIE); - - start_time = real_time = clock_step_next(); - - for (i = 0; i < RTC_PERIOD_TEST_NR; i++) { - cmos_write(RTC_REG_A, RTC_PERIOD_CODE1); - real_time = wait_periodic_interrupt(real_time); - cmos_write(RTC_REG_A, RTC_PERIOD_CODE2); - real_time = wait_periodic_interrupt(real_time); - } - - period_clocks = periodic_period_to_clock(RTC_PERIOD_CODE1) + - periodic_period_to_clock(RTC_PERIOD_CODE2); - period_clocks *= RTC_PERIOD_TEST_NR; - period_time = periodic_clock_to_ns(period_clocks); - - real_time -= start_time; - g_assert_cmpint(ABS((int64_t)(real_time - period_time)), <=, - NANOSECONDS_PER_SECOND * 0.5); -} - -int main(int argc, char **argv) -{ - QTestState *s = NULL; - int ret; - - g_test_init(&argc, &argv, NULL); - - s = qtest_start("-rtc clock=vm"); - qtest_irq_intercept_in(s, "ioapic"); - - qtest_add_func("/rtc/check-time/bcd", bcd_check_time); - qtest_add_func("/rtc/check-time/dec", dec_check_time); - qtest_add_func("/rtc/alarm/interrupt", alarm_time); - qtest_add_func("/rtc/alarm/am-pm", am_pm_alarm); - qtest_add_func("/rtc/basic/dec-24h", basic_24h_dec); - qtest_add_func("/rtc/basic/bcd-24h", basic_24h_bcd); - qtest_add_func("/rtc/basic/dec-12h", basic_12h_dec); - qtest_add_func("/rtc/basic/bcd-12h", basic_12h_bcd); - qtest_add_func("/rtc/set-year/20xx", set_year_20xx); - qtest_add_func("/rtc/set-year/1980", set_year_1980); - qtest_add_func("/rtc/update/register_b_set_flag", register_b_set_flag); - qtest_add_func("/rtc/update/divider-reset", divider_reset); - qtest_add_func("/rtc/update/uip-stuck", uip_stuck); - qtest_add_func("/rtc/misc/fuzz-registers", fuzz_registers); - qtest_add_func("/rtc/periodic/interrupt", periodic_timer); - - ret = g_test_run(); - - if (s) { - qtest_quit(s); - } - - return ret; -} diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c deleted file mode 100644 index 4506049264..0000000000 --- a/tests/rtl8139-test.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * QTest testcase for Realtek 8139 NIC - * - * Copyright (c) 2013-2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "libqos/pci-pc.h" -#include "qemu/timer.h" -#include "qemu-common.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) -{ -} - -#define CLK 33333333 - -static QPCIBus *pcibus; -static QPCIDevice *dev; -static QPCIBar dev_bar; - -static void save_fn(QPCIDevice *dev, int devfn, void *data) -{ - QPCIDevice **pdev = (QPCIDevice **) data; - - *pdev = dev; -} - -static QPCIDevice *get_device(void) -{ - QPCIDevice *dev; - - pcibus = qpci_new_pc(global_qtest, NULL); - qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev); - g_assert(dev != NULL); - - return dev; -} - -#define PORT(name, len, val) \ -static unsigned __attribute__((unused)) in_##name(void) \ -{ \ - unsigned res = qpci_io_read##len(dev, dev_bar, (val)); \ - g_test_message("*%s -> %x", #name, res); \ - return res; \ -} \ -static void out_##name(unsigned v) \ -{ \ - g_test_message("%x -> *%s", v, #name); \ - qpci_io_write##len(dev, dev_bar, (val), v); \ -} - -PORT(Timer, l, 0x48) -PORT(IntrMask, w, 0x3c) -PORT(IntrStatus, w, 0x3E) -PORT(TimerInt, l, 0x54) - -#define fatal(...) do { g_test_message(__VA_ARGS__); g_assert(0); } while (0) - -static void test_timer(void) -{ - const unsigned from = 0.95 * CLK; - const unsigned to = 1.6 * CLK; - unsigned prev, curr, next; - unsigned cnt, diff; - - out_IntrMask(0); - - in_IntrStatus(); - in_Timer(); - in_Timer(); - - /* Test 1. test counter continue and continue */ - out_TimerInt(0); /* disable timer */ - out_IntrStatus(0x4000); - out_Timer(12345); /* reset timer to 0 */ - curr = in_Timer(); - if (curr > 0.1 * CLK) { - fatal("time too big %u\n", curr); - } - for (cnt = 0; ; ) { - clock_step(1 * NANOSECONDS_PER_SECOND); - prev = curr; - curr = in_Timer(); - - /* test skip is in a specific range */ - diff = (curr-prev) & 0xffffffffu; - if (diff < from || diff > to) { - fatal("Invalid diff %u (%u-%u)\n", diff, from, to); - } - if (curr < prev && ++cnt == 3) { - break; - } - } - - /* Test 2. Check we didn't get an interrupt with TimerInt == 0 */ - if (in_IntrStatus() & 0x4000) { - fatal("got an interrupt\n"); - } - - /* Test 3. Setting TimerInt to 1 and Timer to 0 get interrupt */ - out_TimerInt(1); - out_Timer(0); - clock_step(40); - if ((in_IntrStatus() & 0x4000) == 0) { - fatal("we should have an interrupt here!\n"); - } - - /* Test 3. Check acknowledge */ - out_IntrStatus(0x4000); - if (in_IntrStatus() & 0x4000) { - fatal("got an interrupt\n"); - } - - /* Test. Status set after Timer reset */ - out_Timer(0); - out_TimerInt(0); - out_IntrStatus(0x4000); - curr = in_Timer(); - out_TimerInt(curr + 0.5 * CLK); - clock_step(1 * NANOSECONDS_PER_SECOND); - out_Timer(0); - if ((in_IntrStatus() & 0x4000) == 0) { - fatal("we should have an interrupt here!\n"); - } - - /* Test. Status set after TimerInt reset */ - out_Timer(0); - out_TimerInt(0); - out_IntrStatus(0x4000); - curr = in_Timer(); - out_TimerInt(curr + 0.5 * CLK); - clock_step(1 * NANOSECONDS_PER_SECOND); - out_TimerInt(0); - if ((in_IntrStatus() & 0x4000) == 0) { - fatal("we should have an interrupt here!\n"); - } - - /* Test 4. Increment TimerInt we should see an interrupt */ - curr = in_Timer(); - next = curr + 5.0 * CLK; - out_TimerInt(next); - for (cnt = 0; ; ) { - clock_step(1 * NANOSECONDS_PER_SECOND); - prev = curr; - curr = in_Timer(); - diff = (curr-prev) & 0xffffffffu; - if (diff < from || diff > to) { - fatal("Invalid diff %u (%u-%u)\n", diff, from, to); - } - if (cnt < 3 && curr > next) { - if ((in_IntrStatus() & 0x4000) == 0) { - fatal("we should have an interrupt here!\n"); - } - out_IntrStatus(0x4000); - next = curr + 5.0 * CLK; - out_TimerInt(next); - if (++cnt == 3) { - out_TimerInt(1); - } - /* Test 5. Second time we pass from 0 should see an interrupt */ - } else if (cnt >= 3 && curr < prev) { - /* here we should have an interrupt */ - if ((in_IntrStatus() & 0x4000) == 0) { - fatal("we should have an interrupt here!\n"); - } - out_IntrStatus(0x4000); - if (++cnt == 5) { - break; - } - } - } - - g_test_message("Everythink is ok!"); -} - - -static void test_init(void) -{ - uint64_t barsize; - - dev = get_device(); - - dev_bar = qpci_iomap(dev, 0, &barsize); - - qpci_device_enable(dev); - - test_timer(); -} - -int main(int argc, char **argv) -{ - int ret; - - qtest_start("-device rtl8139"); - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/rtl8139/nop", nop); - qtest_add_func("/rtl8139/timer", test_init); - - ret = g_test_run(); - - qtest_end(); - - return ret; -} diff --git a/tests/sdhci-test.c b/tests/sdhci-test.c deleted file mode 100644 index 6275e7626c..0000000000 --- a/tests/sdhci-test.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * QTest testcase for SDHCI controllers - * - * Written by Philippe Mathieu-Daudé - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "hw/registerfields.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/pci-pc.h" -#include "hw/pci/pci.h" -#include "libqos/qgraph.h" -#include "libqos/sdhci.h" - -#define SDHC_CAPAB 0x40 -FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8); /* since v2 */ -FIELD(SDHC_CAPAB, SDMA, 22, 1); -FIELD(SDHC_CAPAB, SDR, 32, 3); /* since v3 */ -FIELD(SDHC_CAPAB, DRIVER, 36, 3); /* since v3 */ -#define SDHC_HCVER 0xFE - -static void check_specs_version(QSDHCI *s, uint8_t version) -{ - uint32_t v; - - v = s->readw(s, SDHC_HCVER); - v &= 0xff; - v += 1; - g_assert_cmpuint(v, ==, version); -} - -static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab) -{ - uint64_t capab; - - capab = s->readq(s, SDHC_CAPAB); - g_assert_cmphex(capab, ==, expec_capab); -} - -static void check_capab_readonly(QSDHCI *s) -{ - const uint64_t vrand = 0x123456789abcdef; - uint64_t capab0, capab1; - - capab0 = s->readq(s, SDHC_CAPAB); - g_assert_cmpuint(capab0, !=, vrand); - - s->writeq(s, SDHC_CAPAB, vrand); - capab1 = s->readq(s, SDHC_CAPAB); - g_assert_cmpuint(capab1, !=, vrand); - g_assert_cmpuint(capab1, ==, capab0); -} - -static void check_capab_baseclock(QSDHCI *s, uint8_t expec_freq) -{ - uint64_t capab, capab_freq; - - if (!expec_freq) { - return; - } - capab = s->readq(s, SDHC_CAPAB); - capab_freq = FIELD_EX64(capab, SDHC_CAPAB, BASECLKFREQ); - g_assert_cmpuint(capab_freq, ==, expec_freq); -} - -static void check_capab_sdma(QSDHCI *s, bool supported) -{ - uint64_t capab, capab_sdma; - - capab = s->readq(s, SDHC_CAPAB); - capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA); - g_assert_cmpuint(capab_sdma, ==, supported); -} - -static void check_capab_v3(QSDHCI *s, uint8_t version) -{ - uint64_t capab, capab_v3; - - if (version < 3) { - /* before v3 those fields are RESERVED */ - capab = s->readq(s, SDHC_CAPAB); - capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, SDR); - g_assert_cmpuint(capab_v3, ==, 0); - capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, DRIVER); - g_assert_cmpuint(capab_v3, ==, 0); - } -} - -static void test_registers(void *obj, void *data, QGuestAllocator *alloc) -{ - QSDHCI *s = obj; - - check_specs_version(s, s->props.version); - check_capab_capareg(s, s->props.capab.reg); - check_capab_readonly(s); - check_capab_v3(s, s->props.version); - check_capab_sdma(s, s->props.capab.sdma); - check_capab_baseclock(s, s->props.baseclock); -} - -static void register_sdhci_test(void) -{ - qos_add_test("registers", "sdhci", test_registers, NULL); -} - -libqos_init(register_sdhci_test); diff --git a/tests/spapr-phb-test.c b/tests/spapr-phb-test.c deleted file mode 100644 index 093dc22f2f..0000000000 --- a/tests/spapr-phb-test.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * QTest testcase for SPAPR PHB - * - * Authors: - * Alexey Kardashevskiy - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" - -/* Tests only initialization so far. TODO: Replace with functional tests, - * for example by producing pci-bus. - */ -static void test_phb_device(void *obj, void *data, QGuestAllocator *alloc) -{ -} - -static void register_phb_test(void) -{ - qos_add_test("spapr-phb-test", "ppc64/pseries", - test_phb_device, &(QOSGraphTestOptions) { - .edge.before_cmd_line = "-device spapr-pci-host-bridge" - ",index=30", - }); -} - -libqos_init(register_phb_test); diff --git a/tests/tco-test.c b/tests/tco-test.c deleted file mode 100644 index 254f735370..0000000000 --- a/tests/tco-test.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * QEMU ICH9 TCO emulation tests - * - * Copyright (c) 2015 Paulo Alcantara - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "libqtest.h" -#include "libqos/pci.h" -#include "libqos/pci-pc.h" -#include "qapi/qmp/qdict.h" -#include "hw/pci/pci_regs.h" -#include "hw/i386/ich9.h" -#include "hw/acpi/ich9.h" -#include "hw/acpi/tco.h" - -#define RCBA_BASE_ADDR 0xfed1c000 -#define PM_IO_BASE_ADDR 0xb000 - -enum { - TCO_RLD_DEFAULT = 0x0000, - TCO_DAT_IN_DEFAULT = 0x00, - TCO_DAT_OUT_DEFAULT = 0x00, - TCO1_STS_DEFAULT = 0x0000, - TCO2_STS_DEFAULT = 0x0000, - TCO1_CNT_DEFAULT = 0x0000, - TCO2_CNT_DEFAULT = 0x0008, - TCO_MESSAGE1_DEFAULT = 0x00, - TCO_MESSAGE2_DEFAULT = 0x00, - TCO_WDCNT_DEFAULT = 0x00, - TCO_TMR_DEFAULT = 0x0004, - SW_IRQ_GEN_DEFAULT = 0x03, -}; - -#define TCO_SECS_TO_TICKS(secs) (((secs) * 10) / 6) -#define TCO_TICKS_TO_SECS(ticks) (((ticks) * 6) / 10) - -typedef struct { - const char *args; - bool noreboot; - QPCIDevice *dev; - QPCIBar tco_io_bar; - QPCIBus *bus; - QTestState *qts; -} TestData; - -static void test_end(TestData *d) -{ - g_free(d->dev); - qpci_free_pc(d->bus); - qtest_quit(d->qts); -} - -static void test_init(TestData *d) -{ - QTestState *qs; - - qs = qtest_initf("-machine q35 %s %s", - d->noreboot ? "" : "-global ICH9-LPC.noreboot=false", - !d->args ? "" : d->args); - qtest_irq_intercept_in(qs, "ioapic"); - - d->bus = qpci_new_pc(qs, NULL); - d->dev = qpci_device_find(d->bus, QPCI_DEVFN(0x1f, 0x00)); - g_assert(d->dev != NULL); - - qpci_device_enable(d->dev); - - /* set ACPI PM I/O space base address */ - qpci_config_writel(d->dev, ICH9_LPC_PMBASE, PM_IO_BASE_ADDR | 0x1); - /* enable ACPI I/O */ - qpci_config_writeb(d->dev, ICH9_LPC_ACPI_CTRL, 0x80); - /* set Root Complex BAR */ - qpci_config_writel(d->dev, ICH9_LPC_RCBA, RCBA_BASE_ADDR | 0x1); - - d->tco_io_bar = qpci_legacy_iomap(d->dev, PM_IO_BASE_ADDR + 0x60); - d->qts = qs; -} - -static void stop_tco(const TestData *d) -{ - uint32_t val; - - val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT); - val |= TCO_TMR_HLT; - qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val); -} - -static void start_tco(const TestData *d) -{ - uint32_t val; - - val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT); - val &= ~TCO_TMR_HLT; - qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val); -} - -static void load_tco(const TestData *d) -{ - qpci_io_writew(d->dev, d->tco_io_bar, TCO_RLD, 4); -} - -static void set_tco_timeout(const TestData *d, uint16_t ticks) -{ - qpci_io_writew(d->dev, d->tco_io_bar, TCO_TMR, ticks); -} - -static void clear_tco_status(const TestData *d) -{ - qpci_io_writew(d->dev, d->tco_io_bar, TCO1_STS, 0x0008); - qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0002); - qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0004); -} - -static void reset_on_second_timeout(const TestData *td, bool enable) -{ - uint32_t val; - - val = qtest_readl(td->qts, RCBA_BASE_ADDR + ICH9_CC_GCS); - if (enable) { - val &= ~ICH9_CC_GCS_NO_REBOOT; - } else { - val |= ICH9_CC_GCS_NO_REBOOT; - } - qtest_writel(td->qts, RCBA_BASE_ADDR + ICH9_CC_GCS, val); -} - -static void test_tco_defaults(void) -{ - TestData d; - - d.args = NULL; - d.noreboot = true; - test_init(&d); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD), ==, - TCO_RLD_DEFAULT); - /* TCO_DAT_IN & TCO_DAT_OUT */ - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_DAT_IN), ==, - (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT); - /* TCO1_STS & TCO2_STS */ - g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_STS), ==, - (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT); - /* TCO1_CNT & TCO2_CNT */ - g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_CNT), ==, - (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT); - /* TCO_MESSAGE1 & TCO_MESSAGE2 */ - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_MESSAGE1), ==, - (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT); - g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, TCO_WDCNT), ==, - TCO_WDCNT_DEFAULT); - g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, SW_IRQ_GEN), ==, - SW_IRQ_GEN_DEFAULT); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_TMR), ==, - TCO_TMR_DEFAULT); - test_end(&d); -} - -static void test_tco_timeout(void) -{ - TestData d; - const uint16_t ticks = TCO_SECS_TO_TICKS(4); - uint32_t val; - int ret; - - d.args = NULL; - d.noreboot = true; - test_init(&d); - - stop_tco(&d); - clear_tco_status(&d); - reset_on_second_timeout(&d, false); - set_tco_timeout(&d, ticks); - load_tco(&d); - start_tco(&d); - qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC); - - /* test first timeout */ - val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); - ret = val & TCO_TIMEOUT ? 1 : 0; - g_assert(ret == 1); - - /* test clearing timeout bit */ - val |= TCO_TIMEOUT; - qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val); - val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); - ret = val & TCO_TIMEOUT ? 1 : 0; - g_assert(ret == 0); - - /* test second timeout */ - qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC); - val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); - ret = val & TCO_TIMEOUT ? 1 : 0; - g_assert(ret == 1); - val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); - ret = val & TCO_SECOND_TO_STS ? 1 : 0; - g_assert(ret == 1); - - stop_tco(&d); - test_end(&d); -} - -static void test_tco_max_timeout(void) -{ - TestData d; - const uint16_t ticks = 0xffff; - uint32_t val; - int ret; - - d.args = NULL; - d.noreboot = true; - test_init(&d); - - stop_tco(&d); - clear_tco_status(&d); - reset_on_second_timeout(&d, false); - set_tco_timeout(&d, ticks); - load_tco(&d); - start_tco(&d); - qtest_clock_step(d.qts, ((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC); - - val = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD); - g_assert_cmpint(val & TCO_RLD_MASK, ==, 1); - val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); - ret = val & TCO_TIMEOUT ? 1 : 0; - g_assert(ret == 0); - qtest_clock_step(d.qts, TCO_TICK_NSEC); - val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); - ret = val & TCO_TIMEOUT ? 1 : 0; - g_assert(ret == 1); - - stop_tco(&d); - test_end(&d); -} - -static QDict *get_watchdog_action(const TestData *td) -{ - QDict *ev = qtest_qmp_eventwait_ref(td->qts, "WATCHDOG"); - QDict *data; - - data = qdict_get_qdict(ev, "data"); - qobject_ref(data); - qobject_unref(ev); - return data; -} - -static void test_tco_second_timeout_pause(void) -{ - TestData td; - const uint16_t ticks = TCO_SECS_TO_TICKS(32); - QDict *ad; - - td.args = "-watchdog-action pause"; - td.noreboot = false; - test_init(&td); - - stop_tco(&td); - clear_tco_status(&td); - reset_on_second_timeout(&td, true); - set_tco_timeout(&td, TCO_SECS_TO_TICKS(16)); - load_tco(&td); - start_tco(&td); - qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); - ad = get_watchdog_action(&td); - g_assert(!strcmp(qdict_get_str(ad, "action"), "pause")); - qobject_unref(ad); - - stop_tco(&td); - test_end(&td); -} - -static void test_tco_second_timeout_reset(void) -{ - TestData td; - const uint16_t ticks = TCO_SECS_TO_TICKS(16); - QDict *ad; - - td.args = "-watchdog-action reset"; - td.noreboot = false; - test_init(&td); - - stop_tco(&td); - clear_tco_status(&td); - reset_on_second_timeout(&td, true); - set_tco_timeout(&td, TCO_SECS_TO_TICKS(16)); - load_tco(&td); - start_tco(&td); - qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); - ad = get_watchdog_action(&td); - g_assert(!strcmp(qdict_get_str(ad, "action"), "reset")); - qobject_unref(ad); - - stop_tco(&td); - test_end(&td); -} - -static void test_tco_second_timeout_shutdown(void) -{ - TestData td; - const uint16_t ticks = TCO_SECS_TO_TICKS(128); - QDict *ad; - - td.args = "-watchdog-action shutdown"; - td.noreboot = false; - test_init(&td); - - stop_tco(&td); - clear_tco_status(&td); - reset_on_second_timeout(&td, true); - set_tco_timeout(&td, ticks); - load_tco(&td); - start_tco(&td); - qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); - ad = get_watchdog_action(&td); - g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown")); - qobject_unref(ad); - - stop_tco(&td); - test_end(&td); -} - -static void test_tco_second_timeout_none(void) -{ - TestData td; - const uint16_t ticks = TCO_SECS_TO_TICKS(256); - QDict *ad; - - td.args = "-watchdog-action none"; - td.noreboot = false; - test_init(&td); - - stop_tco(&td); - clear_tco_status(&td); - reset_on_second_timeout(&td, true); - set_tco_timeout(&td, ticks); - load_tco(&td); - start_tco(&td); - qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2); - ad = get_watchdog_action(&td); - g_assert(!strcmp(qdict_get_str(ad, "action"), "none")); - qobject_unref(ad); - - stop_tco(&td); - test_end(&td); -} - -static void test_tco_ticks_counter(void) -{ - TestData d; - uint16_t ticks = TCO_SECS_TO_TICKS(8); - uint16_t rld; - - d.args = NULL; - d.noreboot = true; - test_init(&d); - - stop_tco(&d); - clear_tco_status(&d); - reset_on_second_timeout(&d, false); - set_tco_timeout(&d, ticks); - load_tco(&d); - start_tco(&d); - - do { - rld = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD) & TCO_RLD_MASK; - g_assert_cmpint(rld, ==, ticks); - qtest_clock_step(d.qts, TCO_TICK_NSEC); - ticks--; - } while (!(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS) & TCO_TIMEOUT)); - - stop_tco(&d); - test_end(&d); -} - -static void test_tco1_control_bits(void) -{ - TestData d; - uint16_t val; - - d.args = NULL; - d.noreboot = true; - test_init(&d); - - val = TCO_LOCK; - qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val); - val &= ~TCO_LOCK; - qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_CNT), ==, - TCO_LOCK); - test_end(&d); -} - -static void test_tco1_status_bits(void) -{ - TestData d; - uint16_t ticks = 8; - uint16_t val; - int ret; - - d.args = NULL; - d.noreboot = true; - test_init(&d); - - stop_tco(&d); - clear_tco_status(&d); - reset_on_second_timeout(&d, false); - set_tco_timeout(&d, ticks); - load_tco(&d); - start_tco(&d); - qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC); - - qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_IN, 0); - qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_OUT, 0); - val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); - ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0; - g_assert(ret == 1); - qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS), ==, 0); - test_end(&d); -} - -static void test_tco2_status_bits(void) -{ - TestData d; - uint16_t ticks = 8; - uint16_t val; - int ret; - - d.args = NULL; - d.noreboot = true; - test_init(&d); - - stop_tco(&d); - clear_tco_status(&d); - reset_on_second_timeout(&d, true); - set_tco_timeout(&d, ticks); - load_tco(&d); - start_tco(&d); - qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC * 2); - - val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); - ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0; - g_assert(ret == 1); - qpci_io_writew(d.dev, d.tco_io_bar, TCO2_STS, val); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS), ==, 0); - test_end(&d); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qtest_add_func("tco/defaults", test_tco_defaults); - qtest_add_func("tco/timeout/no_action", test_tco_timeout); - qtest_add_func("tco/timeout/no_action/max", test_tco_max_timeout); - qtest_add_func("tco/second_timeout/pause", test_tco_second_timeout_pause); - qtest_add_func("tco/second_timeout/reset", test_tco_second_timeout_reset); - qtest_add_func("tco/second_timeout/shutdown", - test_tco_second_timeout_shutdown); - qtest_add_func("tco/second_timeout/none", test_tco_second_timeout_none); - qtest_add_func("tco/counter", test_tco_ticks_counter); - qtest_add_func("tco/tco1_control/bits", test_tco1_control_bits); - qtest_add_func("tco/tco1_status/bits", test_tco1_status_bits); - qtest_add_func("tco/tco2_status/bits", test_tco2_status_bits); - return g_test_run(); -} diff --git a/tests/test-arm-mptimer.c b/tests/test-arm-mptimer.c deleted file mode 100644 index 7a56d56da9..0000000000 --- a/tests/test-arm-mptimer.c +++ /dev/null @@ -1,1090 +0,0 @@ -/* - * QTest testcase for the ARM MPTimer - * - * Copyright (c) 2016 Dmitry Osipenko - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu/timer.h" -#include "libqtest-single.h" - -#define TIMER_BLOCK_SCALE(s) ((((s) & 0xff) + 1) * 10) - -#define TIMER_BLOCK_STEP(scaler, steps_nb) \ - clock_step(TIMER_BLOCK_SCALE(scaler) * (int64_t)(steps_nb) + 1) - -#define TIMER_BASE_PHYS 0x1e000600 - -#define TIMER_LOAD 0x00 -#define TIMER_COUNTER 0x04 -#define TIMER_CONTROL 0x08 -#define TIMER_INTSTAT 0x0C - -#define TIMER_CONTROL_ENABLE (1 << 0) -#define TIMER_CONTROL_PERIODIC (1 << 1) -#define TIMER_CONTROL_IT_ENABLE (1 << 2) -#define TIMER_CONTROL_PRESCALER(p) (((p) & 0xff) << 8) - -#define PERIODIC 1 -#define ONESHOT 0 -#define NOSCALE 0 - -static int nonscaled = NOSCALE; -static int scaled = 122; - -static void timer_load(uint32_t load) -{ - writel(TIMER_BASE_PHYS + TIMER_LOAD, load); -} - -static void timer_start(int periodic, uint32_t scale) -{ - uint32_t ctl = TIMER_CONTROL_ENABLE | TIMER_CONTROL_PRESCALER(scale); - - if (periodic) { - ctl |= TIMER_CONTROL_PERIODIC; - } - - writel(TIMER_BASE_PHYS + TIMER_CONTROL, ctl); -} - -static void timer_stop(void) -{ - writel(TIMER_BASE_PHYS + TIMER_CONTROL, 0); -} - -static void timer_int_clr(void) -{ - writel(TIMER_BASE_PHYS + TIMER_INTSTAT, 1); -} - -static void timer_reset(void) -{ - timer_stop(); - timer_load(0); - timer_int_clr(); -} - -static uint32_t timer_get_and_clr_int_sts(void) -{ - uint32_t int_sts = readl(TIMER_BASE_PHYS + TIMER_INTSTAT); - - if (int_sts) { - timer_int_clr(); - } - - return int_sts; -} - -static uint32_t timer_counter(void) -{ - return readl(TIMER_BASE_PHYS + TIMER_COUNTER); -} - -static void timer_set_counter(uint32_t value) -{ - writel(TIMER_BASE_PHYS + TIMER_COUNTER, value); -} - -static void test_timer_oneshot(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(9999999); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 9999); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - g_assert_cmpuint(timer_counter(), ==, 9990000); - - TIMER_BLOCK_STEP(scaler, 9990000); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - - TIMER_BLOCK_STEP(scaler, 9990000); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_pause(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(999999999); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 999); - - g_assert_cmpuint(timer_counter(), ==, 999999000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(scaler, 9000); - - g_assert_cmpuint(timer_counter(), ==, 999990000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_stop(); - - g_assert_cmpuint(timer_counter(), ==, 999990000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(scaler, 90000); - - g_assert_cmpuint(timer_counter(), ==, 999990000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 999990000); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - g_assert_cmpuint(timer_counter(), ==, 0); - - TIMER_BLOCK_STEP(scaler, 999990000); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - g_assert_cmpuint(timer_counter(), ==, 0); -} - -static void test_timer_reload(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(UINT32_MAX); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 90000); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 90000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_load(UINT32_MAX); - - TIMER_BLOCK_STEP(scaler, 90000); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 90000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_periodic(gconstpointer arg) -{ - int scaler = *((int *) arg); - int repeat = 10; - - timer_reset(); - timer_load(100); - timer_start(PERIODIC, scaler); - - while (repeat--) { - clock_step(TIMER_BLOCK_SCALE(scaler) * (101 + repeat) + 1); - - g_assert_cmpuint(timer_counter(), ==, 100 - repeat); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - - clock_step(TIMER_BLOCK_SCALE(scaler) * (101 - repeat) - 1); - } -} - -static void test_timer_oneshot_to_periodic(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(10000); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 1000); - - g_assert_cmpuint(timer_counter(), ==, 9000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 14001); - - g_assert_cmpuint(timer_counter(), ==, 5000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); -} - -static void test_timer_periodic_to_oneshot(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(99999999); - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 999); - - g_assert_cmpuint(timer_counter(), ==, 99999000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 99999009); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); -} - -static void test_timer_prescaler(void) -{ - timer_reset(); - timer_load(9999999); - timer_start(ONESHOT, NOSCALE); - - TIMER_BLOCK_STEP(NOSCALE, 9999998); - - g_assert_cmpuint(timer_counter(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(NOSCALE, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - - timer_reset(); - timer_load(9999999); - timer_start(ONESHOT, 0xAB); - - TIMER_BLOCK_STEP(0xAB, 9999998); - - g_assert_cmpuint(timer_counter(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(0xAB, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); -} - -static void test_timer_prescaler_on_the_fly(void) -{ - timer_reset(); - timer_load(9999999); - timer_start(ONESHOT, NOSCALE); - - TIMER_BLOCK_STEP(NOSCALE, 999); - - g_assert_cmpuint(timer_counter(), ==, 9999000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_start(ONESHOT, 0xAB); - - TIMER_BLOCK_STEP(0xAB, 9000); - - g_assert_cmpuint(timer_counter(), ==, 9990000); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_set_oneshot_counter_to_0(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(UINT32_MAX); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_set_counter(0); - - TIMER_BLOCK_STEP(scaler, 10); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); -} - -static void test_timer_set_periodic_counter_to_0(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(UINT32_MAX); - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_set_counter(0); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - (scaler ? 0 : 1)); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - timer_reset(); - timer_set_counter(UINT32_MAX); - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_set_counter(0); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); -} - -static void test_timer_noload_oneshot(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_noload_periodic(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); -} - -static void test_timer_zero_load_oneshot(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - g_assert_cmpuint(timer_counter(), ==, 0); - - timer_load(0); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_zero_load_periodic(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - g_assert_cmpuint(timer_counter(), ==, 0); - - timer_load(0); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); -} - -static void test_timer_zero_load_oneshot_to_nonzero(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - timer_load(0); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - g_assert_cmpuint(timer_counter(), ==, 0); - - timer_load(999); - - TIMER_BLOCK_STEP(scaler, 1001); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); -} - -static void test_timer_zero_load_periodic_to_nonzero(gconstpointer arg) -{ - int scaler = *((int *) arg); - int i; - - timer_reset(); - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - g_assert_cmpuint(timer_counter(), ==, 0); - - timer_load(0); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - timer_load(1999999); - - for (i = 1; i < 10; i++) { - TIMER_BLOCK_STEP(scaler, 2000001); - - g_assert_cmpuint(timer_counter(), ==, 1999999 - i); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - } -} - -static void test_timer_nonzero_load_oneshot_to_zero(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - g_assert_cmpuint(timer_counter(), ==, 0); - - timer_load(UINT32_MAX); - timer_load(0); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); -} - -static void test_timer_nonzero_load_periodic_to_zero(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - timer_load(UINT32_MAX); - timer_load(0); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); -} - -static void test_timer_set_periodic_counter_on_the_fly(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(UINT32_MAX / 2); - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX / 2 - 100); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_set_counter(UINT32_MAX); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_enable_and_set_counter(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - timer_set_counter(UINT32_MAX); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_set_counter_and_enable(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_set_counter(UINT32_MAX); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_set_counter_disabled(void) -{ - timer_reset(); - timer_set_counter(999999999); - - TIMER_BLOCK_STEP(NOSCALE, 100); - - g_assert_cmpuint(timer_counter(), ==, 999999999); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_load_disabled(void) -{ - timer_reset(); - timer_load(999999999); - - TIMER_BLOCK_STEP(NOSCALE, 100); - - g_assert_cmpuint(timer_counter(), ==, 999999999); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_oneshot_with_counter_0_on_start(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(999); - timer_set_counter(0); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_periodic_with_counter_0_on_start(gconstpointer arg) -{ - int scaler = *((int *) arg); - int i; - - timer_reset(); - timer_load(UINT32_MAX); - timer_set_counter(0); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - g_assert_cmpuint(timer_counter(), ==, 0); - - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX + (scaler ? 1 : 0) - 100); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX + (scaler ? 1 : 0) - 200); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_reset(); - timer_load(1999999); - timer_set_counter(0); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - for (i = 2 - (!!scaler ? 1 : 0); i < 10; i++) { - TIMER_BLOCK_STEP(scaler, 2000001); - - g_assert_cmpuint(timer_counter(), ==, 1999999 - i); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - } -} - -static void test_periodic_counter(gconstpointer arg) -{ - const int test_load = 10; - int scaler = *((int *) arg); - int test_val; - - timer_reset(); - timer_load(test_load); - timer_start(PERIODIC, scaler); - - clock_step(1); - - for (test_val = 0; test_val <= test_load; test_val++) { - clock_step(TIMER_BLOCK_SCALE(scaler) * test_load); - g_assert_cmpint(timer_counter(), ==, test_val); - } -} - -static void test_timer_set_counter_periodic_with_zero_load(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_start(PERIODIC, scaler); - timer_load(0); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - timer_set_counter(999); - - TIMER_BLOCK_STEP(scaler, 999); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); -} - -static void test_timer_set_oneshot_load_to_0(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(UINT32_MAX); - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_load(0); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_set_periodic_load_to_0(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(UINT32_MAX); - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_load(0); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - TIMER_BLOCK_STEP(scaler, 100); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - g_assert_cmpuint(timer_counter(), ==, 0); -} - -static void test_deferred_trigger(void) -{ - int mode = ONESHOT; - -again: - timer_reset(); - timer_start(mode, 255); - - clock_step(100); - - g_assert_cmpuint(timer_counter(), ==, 0); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - - timer_reset(); - timer_load(2); - timer_start(mode, 255); - - clock_step(100); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - - timer_reset(); - timer_load(UINT32_MAX); - timer_start(mode, 255); - - clock_step(100); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_set_counter(0); - - clock_step(100); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - - timer_reset(); - timer_load(UINT32_MAX); - timer_start(mode, 255); - - clock_step(100); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_load(0); - - clock_step(100); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - - if (mode == ONESHOT) { - mode = PERIODIC; - goto again; - } -} - -static void test_timer_zero_load_mode_switch(gconstpointer arg) -{ - int scaler = *((int *) arg); - - timer_reset(); - timer_load(0); - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - timer_start(ONESHOT, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(scaler, 1); - - timer_start(PERIODIC, scaler); - - TIMER_BLOCK_STEP(scaler, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); -} - -static void test_timer_zero_load_prescaled_periodic_to_nonscaled_oneshot(void) -{ - timer_reset(); - timer_load(0); - timer_start(PERIODIC, 255); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(255, 1); - - timer_start(ONESHOT, NOSCALE); - - TIMER_BLOCK_STEP(NOSCALE, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(NOSCALE, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_zero_load_prescaled_oneshot_to_nonscaled_periodic(void) -{ - timer_reset(); - timer_load(0); - timer_start(ONESHOT, 255); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_start(PERIODIC, NOSCALE); - - TIMER_BLOCK_STEP(NOSCALE, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_zero_load_nonscaled_oneshot_to_prescaled_periodic(void) -{ - timer_reset(); - timer_load(0); - timer_start(ONESHOT, NOSCALE); - - TIMER_BLOCK_STEP(NOSCALE, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_start(PERIODIC, 255); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -static void test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot(void) -{ - timer_reset(); - timer_load(0); - timer_start(PERIODIC, NOSCALE); - - TIMER_BLOCK_STEP(NOSCALE, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - timer_start(ONESHOT, 255); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); - - TIMER_BLOCK_STEP(255, 1); - - g_assert_cmpuint(timer_counter(), ==, 0); - g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); -} - -/* - * Add a qtest test that comes in two versions: one with - * a timer scaler setting, and one with the timer nonscaled. - */ -static void add_scaler_test(const char *str, bool scale, - void (*fn)(const void *)) -{ - char *name; - int *scaler = scale ? &scaled : &nonscaled; - - name = g_strdup_printf("%s=%d", str, *scaler); - qtest_add_data_func(name, scaler, fn); - g_free(name); -} - -int main(int argc, char **argv) -{ - int ret; - int scale; - - g_test_init(&argc, &argv, NULL); - - qtest_add_func("mptimer/deferred_trigger", test_deferred_trigger); - qtest_add_func("mptimer/load_disabled", test_timer_load_disabled); - qtest_add_func("mptimer/set_counter_disabled", test_timer_set_counter_disabled); - qtest_add_func("mptimer/zero_load_prescaled_periodic_to_nonscaled_oneshot", - test_timer_zero_load_prescaled_periodic_to_nonscaled_oneshot); - qtest_add_func("mptimer/zero_load_prescaled_oneshot_to_nonscaled_periodic", - test_timer_zero_load_prescaled_oneshot_to_nonscaled_periodic); - qtest_add_func("mptimer/zero_load_nonscaled_oneshot_to_prescaled_periodic", - test_timer_zero_load_nonscaled_oneshot_to_prescaled_periodic); - qtest_add_func("mptimer/zero_load_nonscaled_periodic_to_prescaled_oneshot", - test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot); - qtest_add_func("mptimer/prescaler", test_timer_prescaler); - qtest_add_func("mptimer/prescaler_on_the_fly", test_timer_prescaler_on_the_fly); - - for (scale = 0; scale < 2; scale++) { - add_scaler_test("mptimer/oneshot scaler", - scale, test_timer_oneshot); - add_scaler_test("mptimer/pause scaler", - scale, test_timer_pause); - add_scaler_test("mptimer/reload scaler", - scale, test_timer_reload); - add_scaler_test("mptimer/periodic scaler", - scale, test_timer_periodic); - add_scaler_test("mptimer/oneshot_to_periodic scaler", - scale, test_timer_oneshot_to_periodic); - add_scaler_test("mptimer/periodic_to_oneshot scaler", - scale, test_timer_periodic_to_oneshot); - add_scaler_test("mptimer/set_oneshot_counter_to_0 scaler", - scale, test_timer_set_oneshot_counter_to_0); - add_scaler_test("mptimer/set_periodic_counter_to_0 scaler", - scale, test_timer_set_periodic_counter_to_0); - add_scaler_test("mptimer/noload_oneshot scaler", - scale, test_timer_noload_oneshot); - add_scaler_test("mptimer/noload_periodic scaler", - scale, test_timer_noload_periodic); - add_scaler_test("mptimer/zero_load_oneshot scaler", - scale, test_timer_zero_load_oneshot); - add_scaler_test("mptimer/zero_load_periodic scaler", - scale, test_timer_zero_load_periodic); - add_scaler_test("mptimer/zero_load_oneshot_to_nonzero scaler", - scale, test_timer_zero_load_oneshot_to_nonzero); - add_scaler_test("mptimer/zero_load_periodic_to_nonzero scaler", - scale, test_timer_zero_load_periodic_to_nonzero); - add_scaler_test("mptimer/nonzero_load_oneshot_to_zero scaler", - scale, test_timer_nonzero_load_oneshot_to_zero); - add_scaler_test("mptimer/nonzero_load_periodic_to_zero scaler", - scale, test_timer_nonzero_load_periodic_to_zero); - add_scaler_test("mptimer/set_periodic_counter_on_the_fly scaler", - scale, test_timer_set_periodic_counter_on_the_fly); - add_scaler_test("mptimer/enable_and_set_counter scaler", - scale, test_timer_enable_and_set_counter); - add_scaler_test("mptimer/set_counter_and_enable scaler", - scale, test_timer_set_counter_and_enable); - add_scaler_test("mptimer/oneshot_with_counter_0_on_start scaler", - scale, test_timer_oneshot_with_counter_0_on_start); - add_scaler_test("mptimer/periodic_with_counter_0_on_start scaler", - scale, test_timer_periodic_with_counter_0_on_start); - add_scaler_test("mptimer/periodic_counter scaler", - scale, test_periodic_counter); - add_scaler_test("mptimer/set_counter_periodic_with_zero_load scaler", - scale, test_timer_set_counter_periodic_with_zero_load); - add_scaler_test("mptimer/set_oneshot_load_to_0 scaler", - scale, test_timer_set_oneshot_load_to_0); - add_scaler_test("mptimer/set_periodic_load_to_0 scaler", - scale, test_timer_set_periodic_load_to_0); - add_scaler_test("mptimer/zero_load_mode_switch scaler", - scale, test_timer_zero_load_mode_switch); - } - - qtest_start("-machine vexpress-a9"); - ret = g_test_run(); - qtest_end(); - - return ret; -} diff --git a/tests/test-filter-mirror.c b/tests/test-filter-mirror.c deleted file mode 100644 index 1e3ced84a9..0000000000 --- a/tests/test-filter-mirror.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * QTest testcase for filter-mirror - * - * Copyright (c) 2016 FUJITSU LIMITED - * Author: Zhang Chen - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * later. See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qemu/iov.h" -#include "qemu/sockets.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" - -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) - -static void test_mirror(void) -{ - int send_sock[2], recv_sock[2]; - uint32_t ret = 0, len = 0; - char send_buf[] = "Hello! filter-mirror~"; - char *recv_buf; - uint32_t size = sizeof(send_buf); - size = htonl(size); - const char *devstr = "e1000"; - QTestState *qts; - - if (g_str_equal(qtest_get_arch(), "s390x")) { - devstr = "virtio-net-ccw"; - } - - ret = socketpair(PF_UNIX, SOCK_STREAM, 0, send_sock); - g_assert_cmpint(ret, !=, -1); - - ret = socketpair(PF_UNIX, SOCK_STREAM, 0, recv_sock); - g_assert_cmpint(ret, !=, -1); - - qts = qtest_initf( - "-netdev socket,id=qtest-bn0,fd=%d " - "-device %s,netdev=qtest-bn0,id=qtest-e0 " - "-chardev socket,id=mirror0,fd=%d " - "-object filter-mirror,id=qtest-f0,netdev=qtest-bn0,queue=tx,outdev=mirror0 " - , send_sock[1], devstr, recv_sock[1]); - - struct iovec iov[] = { - { - .iov_base = &size, - .iov_len = sizeof(size), - }, { - .iov_base = send_buf, - .iov_len = sizeof(send_buf), - }, - }; - - /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); - ret = iov_send(send_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf)); - g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); - close(send_sock[0]); - - ret = qemu_recv(recv_sock[0], &len, sizeof(len), 0); - g_assert_cmpint(ret, ==, sizeof(len)); - len = ntohl(len); - - g_assert_cmpint(len, ==, sizeof(send_buf)); - recv_buf = g_malloc(len); - ret = qemu_recv(recv_sock[0], recv_buf, len, 0); - g_assert_cmpstr(recv_buf, ==, send_buf); - - g_free(recv_buf); - close(send_sock[0]); - close(send_sock[1]); - close(recv_sock[0]); - close(recv_sock[1]); - qtest_quit(qts); -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/netfilter/mirror", test_mirror); - ret = g_test_run(); - - return ret; -} diff --git a/tests/test-filter-redirector.c b/tests/test-filter-redirector.c deleted file mode 100644 index e4d53220fd..0000000000 --- a/tests/test-filter-redirector.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * QTest testcase for filter-redirector - * - * Copyright (c) 2016 FUJITSU LIMITED - * Author: Zhang Chen - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * later. See the COPYING file in the top-level directory. - * - * Case 1, tx traffic flow: - * - * qemu side | test side - * | - * +---------+ | +-------+ - * | backend <---------------+ sock0 | - * +----+----+ | +-------+ - * | | - * +----v----+ +-------+ | - * | rd0 +->+chardev| | - * +---------+ +---+---+ | - * | | - * +---------+ | | - * | rd1 <------+ | - * +----+----+ | - * | | - * +----v----+ | +-------+ - * | rd2 +--------------->sock1 | - * +---------+ | +-------+ - * + - * - * -------------------------------------- - * Case 2, rx traffic flow - * qemu side | test side - * | - * +---------+ | +-------+ - * | backend +---------------> sock1 | - * +----^----+ | +-------+ - * | | - * +----+----+ +-------+ | - * | rd0 +<-+chardev| | - * +---------+ +---+---+ | - * ^ | - * +---------+ | | - * | rd1 +------+ | - * +----^----+ | - * | | - * +----+----+ | +-------+ - * | rd2 <---------------+sock0 | - * +---------+ | +-------+ - * + - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qemu/iov.h" -#include "qemu/sockets.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" - -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) - -static const char *get_devstr(void) -{ - if (g_str_equal(qtest_get_arch(), "s390x")) { - return "virtio-net-ccw"; - } - - return "rtl8139"; -} - - -static void test_redirector_tx(void) -{ - int backend_sock[2], recv_sock; - uint32_t ret = 0, len = 0; - char send_buf[] = "Hello!!"; - char sock_path0[] = "filter-redirector0.XXXXXX"; - char sock_path1[] = "filter-redirector1.XXXXXX"; - char *recv_buf; - uint32_t size = sizeof(send_buf); - size = htonl(size); - QTestState *qts; - - ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock); - g_assert_cmpint(ret, !=, -1); - - ret = mkstemp(sock_path0); - g_assert_cmpint(ret, !=, -1); - ret = mkstemp(sock_path1); - g_assert_cmpint(ret, !=, -1); - - qts = qtest_initf( - "-netdev socket,id=qtest-bn0,fd=%d " - "-device %s,netdev=qtest-bn0,id=qtest-e0 " - "-chardev socket,id=redirector0,path=%s,server,nowait " - "-chardev socket,id=redirector1,path=%s,server,nowait " - "-chardev socket,id=redirector2,path=%s " - "-object filter-redirector,id=qtest-f0,netdev=qtest-bn0," - "queue=tx,outdev=redirector0 " - "-object filter-redirector,id=qtest-f1,netdev=qtest-bn0," - "queue=tx,indev=redirector2 " - "-object filter-redirector,id=qtest-f2,netdev=qtest-bn0," - "queue=tx,outdev=redirector1 ", backend_sock[1], get_devstr(), - sock_path0, sock_path1, sock_path0); - - recv_sock = unix_connect(sock_path1, NULL); - g_assert_cmpint(recv_sock, !=, -1); - - /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); - - struct iovec iov[] = { - { - .iov_base = &size, - .iov_len = sizeof(size), - }, { - .iov_base = send_buf, - .iov_len = sizeof(send_buf), - }, - }; - - ret = iov_send(backend_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf)); - g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); - close(backend_sock[0]); - - ret = qemu_recv(recv_sock, &len, sizeof(len), 0); - g_assert_cmpint(ret, ==, sizeof(len)); - len = ntohl(len); - - g_assert_cmpint(len, ==, sizeof(send_buf)); - recv_buf = g_malloc(len); - ret = qemu_recv(recv_sock, recv_buf, len, 0); - g_assert_cmpstr(recv_buf, ==, send_buf); - - g_free(recv_buf); - close(recv_sock); - unlink(sock_path0); - unlink(sock_path1); - qtest_quit(qts); -} - -static void test_redirector_rx(void) -{ - int backend_sock[2], send_sock; - uint32_t ret = 0, len = 0; - char send_buf[] = "Hello!!"; - char sock_path0[] = "filter-redirector0.XXXXXX"; - char sock_path1[] = "filter-redirector1.XXXXXX"; - char *recv_buf; - uint32_t size = sizeof(send_buf); - size = htonl(size); - QTestState *qts; - - ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock); - g_assert_cmpint(ret, !=, -1); - - ret = mkstemp(sock_path0); - g_assert_cmpint(ret, !=, -1); - ret = mkstemp(sock_path1); - g_assert_cmpint(ret, !=, -1); - - qts = qtest_initf( - "-netdev socket,id=qtest-bn0,fd=%d " - "-device %s,netdev=qtest-bn0,id=qtest-e0 " - "-chardev socket,id=redirector0,path=%s,server,nowait " - "-chardev socket,id=redirector1,path=%s,server,nowait " - "-chardev socket,id=redirector2,path=%s " - "-object filter-redirector,id=qtest-f0,netdev=qtest-bn0," - "queue=rx,indev=redirector0 " - "-object filter-redirector,id=qtest-f1,netdev=qtest-bn0," - "queue=rx,outdev=redirector2 " - "-object filter-redirector,id=qtest-f2,netdev=qtest-bn0," - "queue=rx,indev=redirector1 ", backend_sock[1], get_devstr(), - sock_path0, sock_path1, sock_path0); - - struct iovec iov[] = { - { - .iov_base = &size, - .iov_len = sizeof(size), - }, { - .iov_base = send_buf, - .iov_len = sizeof(send_buf), - }, - }; - - send_sock = unix_connect(sock_path1, NULL); - g_assert_cmpint(send_sock, !=, -1); - /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); - - ret = iov_send(send_sock, iov, 2, 0, sizeof(size) + sizeof(send_buf)); - g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); - - ret = qemu_recv(backend_sock[0], &len, sizeof(len), 0); - g_assert_cmpint(ret, ==, sizeof(len)); - len = ntohl(len); - - g_assert_cmpint(len, ==, sizeof(send_buf)); - recv_buf = g_malloc(len); - ret = qemu_recv(backend_sock[0], recv_buf, len, 0); - g_assert_cmpstr(recv_buf, ==, send_buf); - - close(send_sock); - g_free(recv_buf); - unlink(sock_path0); - unlink(sock_path1); - qtest_quit(qts); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - qtest_add_func("/netfilter/redirector_tx", test_redirector_tx); - qtest_add_func("/netfilter/redirector_rx", test_redirector_rx); - return g_test_run(); -} diff --git a/tests/test-hmp.c b/tests/test-hmp.c deleted file mode 100644 index 5029c4d2c9..0000000000 --- a/tests/test-hmp.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Test HMP commands. - * - * Copyright (c) 2017 Red Hat Inc. - * - * Author: - * Thomas Huth - * - * This work is licensed under the terms of the GNU GPL, version 2 - * or later. See the COPYING file in the top-level directory. - * - * This test calls some HMP commands for all machines that the current - * QEMU binary provides, to check whether they terminate successfully - * (i.e. do not crash QEMU). - */ - -#include "qemu/osdep.h" -#include "libqtest.h" - -static int verbose; - -static const char *hmp_cmds[] = { - "announce_self", - "boot_set ndc", - "chardev-add null,id=testchardev1", - "chardev-send-break testchardev1", - "chardev-change testchardev1 ringbuf", - "chardev-remove testchardev1", - "commit all", - "cpu-add 1", - "cpu 0", - "device_add ?", - "device_add usb-mouse,id=mouse1", - "drive_add ignored format=help", - "mouse_button 7", - "mouse_move 10 10", - "mouse_button 0", - "device_del mouse1", - "dump-guest-memory /dev/null 0 4096", - "dump-guest-memory /dev/null", - "gdbserver", - "gva2gpa 0", - "hostfwd_add tcp::43210-:43210", - "hostfwd_remove tcp::43210-:43210", - "i /w 0", - "log all", - "log none", - "memsave 0 4096 \"/dev/null\"", - "migrate_set_cache_size 1", - "migrate_set_downtime 1", - "migrate_set_speed 1", - "netdev_add user,id=net1", - "set_link net1 off", - "set_link net1 on", - "netdev_del net1", - "nmi", - "o /w 0 0x1234", - "object_add memory-backend-ram,id=mem1,size=256M", - "object_del mem1", - "pmemsave 0 4096 \"/dev/null\"", - "p $pc + 8", - "qom-list /", - "qom-set /machine initrd test", - "screendump /dev/null", - "sendkey x", - "singlestep on", - "wavcapture /dev/null", - "stopcapture 0", - "sum 0 512", - "x /8i 0x100", - "xp /16x 0", - NULL -}; - -/* Run through the list of pre-defined commands */ -static void test_commands(QTestState *qts) -{ - char *response; - int i; - - for (i = 0; hmp_cmds[i] != NULL; i++) { - response = qtest_hmp(qts, "%s", hmp_cmds[i]); - if (verbose) { - fprintf(stderr, - "\texecute HMP command: %s\n" - "\tresult : %s\n", - hmp_cmds[i], response); - } - g_free(response); - } - -} - -/* Run through all info commands and call them blindly (without arguments) */ -static void test_info_commands(QTestState *qts) -{ - char *resp, *info, *info_buf, *endp; - - info_buf = info = qtest_hmp(qts, "help info"); - - while (*info) { - /* Extract the info command, ignore parameters and description */ - g_assert(strncmp(info, "info ", 5) == 0); - endp = strchr(&info[5], ' '); - g_assert(endp != NULL); - *endp = '\0'; - /* Now run the info command */ - if (verbose) { - fprintf(stderr, "\t%s\n", info); - } - resp = qtest_hmp(qts, "%s", info); - g_free(resp); - /* And move forward to the next line */ - info = strchr(endp + 1, '\n'); - if (!info) { - break; - } - info += 1; - } - - g_free(info_buf); -} - -static void test_machine(gconstpointer data) -{ - const char *machine = data; - char *args; - QTestState *qts; - - args = g_strdup_printf("-S -M %s", machine); - qts = qtest_init(args); - - test_info_commands(qts); - test_commands(qts); - - qtest_quit(qts); - g_free(args); - g_free((void *)data); -} - -static void add_machine_test_case(const char *mname) -{ - char *path; - - /* Ignore blacklisted machines that have known problems */ - if (!strcmp("xenfv", mname) || !strcmp("xenpv", mname)) { - return; - } - - path = g_strdup_printf("hmp/%s", mname); - qtest_add_data_func(path, g_strdup(mname), test_machine); - g_free(path); -} - -int main(int argc, char **argv) -{ - char *v_env = getenv("V"); - - if (v_env && *v_env >= '2') { - verbose = true; - } - - g_test_init(&argc, &argv, NULL); - - qtest_cb_for_every_machine(add_machine_test_case, g_test_quick()); - - /* as none machine has no memory by default, add a test case with memory */ - qtest_add_data_func("hmp/none+2MB", g_strdup("none -m 2"), test_machine); - - return g_test_run(); -} diff --git a/tests/test-netfilter.c b/tests/test-netfilter.c deleted file mode 100644 index 22927ee6ab..0000000000 --- a/tests/test-netfilter.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * QTest testcase for netfilter - * - * Copyright (c) 2015 FUJITSU LIMITED - * Author: Yang Hongyang - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * later. See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "qapi/qmp/qdict.h" - -/* add a netfilter to a netdev and then remove it */ -static void add_one_netfilter(void) -{ - QDict *response; - - response = qmp("{'execute': 'object-add'," - " 'arguments': {" - " 'qom-type': 'filter-buffer'," - " 'id': 'qtest-f0'," - " 'props': {" - " 'netdev': 'qtest-bn0'," - " 'queue': 'rx'," - " 'interval': 1000" - "}}}"); - - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - response = qmp("{'execute': 'object-del'," - " 'arguments': {" - " 'id': 'qtest-f0'" - "}}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); -} - -/* add a netfilter to a netdev and then remove the netdev */ -static void remove_netdev_with_one_netfilter(void) -{ - QDict *response; - - response = qmp("{'execute': 'object-add'," - " 'arguments': {" - " 'qom-type': 'filter-buffer'," - " 'id': 'qtest-f0'," - " 'props': {" - " 'netdev': 'qtest-bn0'," - " 'queue': 'rx'," - " 'interval': 1000" - "}}}"); - - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - response = qmp("{'execute': 'netdev_del'," - " 'arguments': {" - " 'id': 'qtest-bn0'" - "}}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - /* add back the netdev */ - response = qmp("{'execute': 'netdev_add'," - " 'arguments': {" - " 'type': 'user'," - " 'id': 'qtest-bn0'" - "}}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); -} - -/* add multi(2) netfilters to a netdev and then remove them */ -static void add_multi_netfilter(void) -{ - QDict *response; - - response = qmp("{'execute': 'object-add'," - " 'arguments': {" - " 'qom-type': 'filter-buffer'," - " 'id': 'qtest-f0'," - " 'props': {" - " 'netdev': 'qtest-bn0'," - " 'queue': 'rx'," - " 'interval': 1000" - "}}}"); - - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - response = qmp("{'execute': 'object-add'," - " 'arguments': {" - " 'qom-type': 'filter-buffer'," - " 'id': 'qtest-f1'," - " 'props': {" - " 'netdev': 'qtest-bn0'," - " 'queue': 'rx'," - " 'interval': 1000" - "}}}"); - - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - response = qmp("{'execute': 'object-del'," - " 'arguments': {" - " 'id': 'qtest-f0'" - "}}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - response = qmp("{'execute': 'object-del'," - " 'arguments': {" - " 'id': 'qtest-f1'" - "}}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); -} - -/* add multi(2) netfilters to a netdev and then remove the netdev */ -static void remove_netdev_with_multi_netfilter(void) -{ - QDict *response; - - response = qmp("{'execute': 'object-add'," - " 'arguments': {" - " 'qom-type': 'filter-buffer'," - " 'id': 'qtest-f0'," - " 'props': {" - " 'netdev': 'qtest-bn0'," - " 'queue': 'rx'," - " 'interval': 1000" - "}}}"); - - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - response = qmp("{'execute': 'object-add'," - " 'arguments': {" - " 'qom-type': 'filter-buffer'," - " 'id': 'qtest-f1'," - " 'props': {" - " 'netdev': 'qtest-bn0'," - " 'queue': 'rx'," - " 'interval': 1000" - "}}}"); - - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - response = qmp("{'execute': 'netdev_del'," - " 'arguments': {" - " 'id': 'qtest-bn0'" - "}}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - /* add back the netdev */ - response = qmp("{'execute': 'netdev_add'," - " 'arguments': {" - " 'type': 'user'," - " 'id': 'qtest-bn0'" - "}}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); -} - -int main(int argc, char **argv) -{ - int ret; - char *args; - const char *devstr = "e1000"; - - if (g_str_equal(qtest_get_arch(), "s390x")) { - devstr = "virtio-net-ccw"; - } - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/netfilter/addremove_one", add_one_netfilter); - qtest_add_func("/netfilter/remove_netdev_one", - remove_netdev_with_one_netfilter); - qtest_add_func("/netfilter/addremove_multi", add_multi_netfilter); - qtest_add_func("/netfilter/remove_netdev_multi", - remove_netdev_with_multi_netfilter); - - args = g_strdup_printf("-netdev user,id=qtest-bn0 " - "-device %s,netdev=qtest-bn0", devstr); - qtest_start(args); - ret = g_test_run(); - - qtest_end(); - g_free(args); - - return ret; -} diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c deleted file mode 100644 index 772287bdb4..0000000000 --- a/tests/test-x86-cpuid-compat.c +++ /dev/null @@ -1,381 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qbool.h" -#include "libqtest-single.h" - -static char *get_cpu0_qom_path(void) -{ - QDict *resp; - QList *ret; - QDict *cpu0; - char *path; - - resp = qmp("{'execute': 'query-cpus', 'arguments': {}}"); - g_assert(qdict_haskey(resp, "return")); - ret = qdict_get_qlist(resp, "return"); - - cpu0 = qobject_to(QDict, qlist_peek(ret)); - path = g_strdup(qdict_get_str(cpu0, "qom_path")); - qobject_unref(resp); - return path; -} - -static QObject *qom_get(const char *path, const char *prop) -{ - QDict *resp = qmp("{ 'execute': 'qom-get'," - " 'arguments': { 'path': %s," - " 'property': %s } }", - path, prop); - QObject *ret = qdict_get(resp, "return"); - qobject_ref(ret); - qobject_unref(resp); - return ret; -} - -static bool qom_get_bool(const char *path, const char *prop) -{ - QBool *value = qobject_to(QBool, qom_get(path, prop)); - bool b = qbool_get_bool(value); - - qobject_unref(value); - return b; -} - -typedef struct CpuidTestArgs { - const char *cmdline; - const char *property; - int64_t expected_value; -} CpuidTestArgs; - -static void test_cpuid_prop(const void *data) -{ - const CpuidTestArgs *args = data; - char *path; - QNum *value; - int64_t val; - - qtest_start(args->cmdline); - path = get_cpu0_qom_path(); - value = qobject_to(QNum, qom_get(path, args->property)); - g_assert(qnum_get_try_int(value, &val)); - g_assert_cmpint(val, ==, args->expected_value); - qtest_end(); - - qobject_unref(value); - g_free(path); -} - -static void add_cpuid_test(const char *name, const char *cmdline, - const char *property, int64_t expected_value) -{ - CpuidTestArgs *args = g_new0(CpuidTestArgs, 1); - args->cmdline = cmdline; - args->property = property; - args->expected_value = expected_value; - qtest_add_data_func(name, args, test_cpuid_prop); -} - - -/* Parameters to a add_feature_test() test case */ -typedef struct FeatureTestArgs { - /* cmdline to start QEMU */ - const char *cmdline; - /* - * cpuid-input-eax and cpuid-input-ecx values to look for, - * in "feature-words" and "filtered-features" properties. - */ - uint32_t in_eax, in_ecx; - /* The register name to look for, in the X86CPUFeatureWordInfo array */ - const char *reg; - /* The bit to check in X86CPUFeatureWordInfo.features */ - int bitnr; - /* The expected value for the bit in (X86CPUFeatureWordInfo.features) */ - bool expected_value; -} FeatureTestArgs; - -/* Get the value for a feature word in a X86CPUFeatureWordInfo list */ -static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx, - const char *reg) -{ - const QListEntry *e; - - for (e = qlist_first(features); e; e = qlist_next(e)) { - QDict *w = qobject_to(QDict, qlist_entry_obj(e)); - const char *rreg = qdict_get_str(w, "cpuid-register"); - uint32_t reax = qdict_get_int(w, "cpuid-input-eax"); - bool has_ecx = qdict_haskey(w, "cpuid-input-ecx"); - uint32_t recx = 0; - int64_t val; - - if (has_ecx) { - recx = qdict_get_int(w, "cpuid-input-ecx"); - } - if (eax == reax && (!has_ecx || ecx == recx) && !strcmp(rreg, reg)) { - g_assert(qnum_get_try_int(qobject_to(QNum, - qdict_get(w, "features")), - &val)); - return val; - } - } - return 0; -} - -static void test_feature_flag(const void *data) -{ - const FeatureTestArgs *args = data; - char *path; - QList *present, *filtered; - uint32_t value; - - qtest_start(args->cmdline); - path = get_cpu0_qom_path(); - present = qobject_to(QList, qom_get(path, "feature-words")); - filtered = qobject_to(QList, qom_get(path, "filtered-features")); - value = get_feature_word(present, args->in_eax, args->in_ecx, args->reg); - value |= get_feature_word(filtered, args->in_eax, args->in_ecx, args->reg); - qtest_end(); - - g_assert(!!(value & (1U << args->bitnr)) == args->expected_value); - - qobject_unref(present); - qobject_unref(filtered); - g_free(path); -} - -/* - * Add test case to ensure that a given feature flag is set in - * either "feature-words" or "filtered-features", when running QEMU - * using cmdline - */ -static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline, - uint32_t eax, uint32_t ecx, - const char *reg, int bitnr, - bool expected_value) -{ - FeatureTestArgs *args = g_new0(FeatureTestArgs, 1); - args->cmdline = cmdline; - args->in_eax = eax; - args->in_ecx = ecx; - args->reg = reg; - args->bitnr = bitnr; - args->expected_value = expected_value; - qtest_add_data_func(name, args, test_feature_flag); - return args; -} - -static void test_plus_minus_subprocess(void) -{ - char *path; - - /* Rules: - * 1)"-foo" overrides "+foo" - * 2) "[+-]foo" overrides "foo=..." - * 3) Old feature names with underscores (e.g. "sse4_2") - * should keep working - * - * Note: rules 1 and 2 are planned to be removed soon, and - * should generate a warning. - */ - qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on"); - path = get_cpu0_qom_path(); - - g_assert_false(qom_get_bool(path, "fpu")); - g_assert_false(qom_get_bool(path, "mce")); - g_assert_true(qom_get_bool(path, "cx8")); - - /* Test both the original and the alias feature names: */ - g_assert_true(qom_get_bool(path, "sse4-1")); - g_assert_true(qom_get_bool(path, "sse4.1")); - - g_assert_true(qom_get_bool(path, "sse4-2")); - g_assert_true(qom_get_bool(path, "sse4.2")); - - qtest_end(); - g_free(path); -} - -static void test_plus_minus(void) -{ - g_test_trap_subprocess("/x86/cpuid/parsing-plus-minus/subprocess", 0, 0); - g_test_trap_assert_passed(); - g_test_trap_assert_stderr("*Ambiguous CPU model string. " - "Don't mix both \"-mce\" and \"mce=on\"*"); - g_test_trap_assert_stderr("*Ambiguous CPU model string. " - "Don't mix both \"+cx8\" and \"cx8=off\"*"); - g_test_trap_assert_stdout(""); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/x86/cpuid/parsing-plus-minus/subprocess", - test_plus_minus_subprocess); - g_test_add_func("/x86/cpuid/parsing-plus-minus", test_plus_minus); - - /* Original level values for CPU models: */ - add_cpuid_test("x86/cpuid/phenom/level", - "-cpu phenom", "level", 5); - add_cpuid_test("x86/cpuid/Conroe/level", - "-cpu Conroe", "level", 10); - add_cpuid_test("x86/cpuid/SandyBridge/level", - "-cpu SandyBridge", "level", 0xd); - add_cpuid_test("x86/cpuid/486/xlevel", - "-cpu 486", "xlevel", 0); - add_cpuid_test("x86/cpuid/core2duo/xlevel", - "-cpu core2duo", "xlevel", 0x80000008); - add_cpuid_test("x86/cpuid/phenom/xlevel", - "-cpu phenom", "xlevel", 0x8000001A); - add_cpuid_test("x86/cpuid/athlon/xlevel", - "-cpu athlon", "xlevel", 0x80000008); - - /* If level is not large enough, it should increase automatically: */ - /* CPUID[6].EAX: */ - add_cpuid_test("x86/cpuid/auto-level/phenom/arat", - "-cpu 486,+arat", "level", 6); - /* CPUID[EAX=7,ECX=0].EBX: */ - add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase", - "-cpu phenom,+fsgsbase", "level", 7); - /* CPUID[EAX=7,ECX=0].ECX: */ - add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi", - "-cpu phenom,+avx512vbmi", "level", 7); - /* CPUID[EAX=0xd,ECX=1].EAX: */ - add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt", - "-cpu phenom,+xsaveopt", "level", 0xd); - /* CPUID[8000_0001].EDX: */ - add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow", - "-cpu 486,+3dnow", "xlevel", 0x80000001); - /* CPUID[8000_0001].ECX: */ - add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a", - "-cpu 486,+sse4a", "xlevel", 0x80000001); - /* CPUID[8000_0007].EDX: */ - add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc", - "-cpu 486,+invtsc", "xlevel", 0x80000007); - /* CPUID[8000_000A].EDX: */ - add_cpuid_test("x86/cpuid/auto-xlevel/486/npt", - "-cpu 486,+npt", "xlevel", 0x8000000A); - /* CPUID[C000_0001].EDX: */ - add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore", - "-cpu phenom,+xstore", "xlevel2", 0xC0000001); - /* SVM needs CPUID[0x8000000A] */ - add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm", - "-cpu athlon,+svm", "xlevel", 0x8000000A); - - - /* If level is already large enough, it shouldn't change: */ - add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple", - "-cpu SandyBridge,+arat,+fsgsbase,+avx512vbmi", - "level", 0xd); - /* If level is explicitly set, it shouldn't change: */ - add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF", - "-cpu 486,level=0xF,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", - "level", 0xF); - add_cpuid_test("x86/cpuid/auto-level/486/fixed/2", - "-cpu 486,level=2,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", - "level", 2); - add_cpuid_test("x86/cpuid/auto-level/486/fixed/0", - "-cpu 486,level=0,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", - "level", 0); - - /* if xlevel is already large enough, it shouldn't change: */ - add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow", - "-cpu phenom,+3dnow,+sse4a,+invtsc,+npt,+svm", - "xlevel", 0x8000001A); - /* If xlevel is explicitly set, it shouldn't change: */ - add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002", - "-cpu 486,xlevel=0x80000002,+3dnow,+sse4a,+invtsc,+npt,+svm", - "xlevel", 0x80000002); - add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A", - "-cpu 486,xlevel=0x8000001A,+3dnow,+sse4a,+invtsc,+npt,+svm", - "xlevel", 0x8000001A); - add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0", - "-cpu 486,xlevel=0,+3dnow,+sse4a,+invtsc,+npt,+svm", - "xlevel", 0); - - /* if xlevel2 is already large enough, it shouldn't change: */ - add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed", - "-cpu 486,xlevel2=0xC0000002,+xstore", - "xlevel2", 0xC0000002); - - /* Check compatibility of old machine-types that didn't - * auto-increase level/xlevel/xlevel2: */ - - add_cpuid_test("x86/cpuid/auto-level/pc-2.7", - "-machine pc-i440fx-2.7 -cpu 486,+arat,+avx512vbmi,+xsaveopt", - "level", 1); - add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7", - "-machine pc-i440fx-2.7 -cpu 486,+3dnow,+sse4a,+invtsc,+npt,+svm", - "xlevel", 0); - add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7", - "-machine pc-i440fx-2.7 -cpu 486,+xstore", - "xlevel2", 0); - /* - * QEMU 1.4.0 had auto-level enabled for CPUID[7], already, - * and the compat code that sets default level shouldn't - * disable the auto-level=7 code: - */ - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.4/off", - "-machine pc-i440fx-1.4 -cpu Nehalem", - "level", 2); - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.5/on", - "-machine pc-i440fx-1.4 -cpu Nehalem,+smap", - "level", 7); - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off", - "-machine pc-i440fx-2.3 -cpu Penryn", - "level", 4); - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on", - "-machine pc-i440fx-2.3 -cpu Penryn,+erms", - "level", 7); - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off", - "-machine pc-i440fx-2.9 -cpu Conroe", - "level", 10); - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/on", - "-machine pc-i440fx-2.9 -cpu Conroe,+erms", - "level", 10); - - /* - * xlevel doesn't have any feature that triggers auto-level - * code on old machine-types. Just check that the compat code - * is working correctly: - */ - add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3", - "-machine pc-i440fx-2.3 -cpu SandyBridge", - "xlevel", 0x8000000a); - add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off", - "-machine pc-i440fx-2.4 -cpu SandyBridge,", - "xlevel", 0x80000008); - add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on", - "-machine pc-i440fx-2.4 -cpu SandyBridge,+npt", - "xlevel", 0x80000008); - - /* Test feature parsing */ - add_feature_test("x86/cpuid/features/plus", - "-cpu 486,+arat", - 6, 0, "EAX", 2, true); - add_feature_test("x86/cpuid/features/minus", - "-cpu pentium,-mmx", - 1, 0, "EDX", 23, false); - add_feature_test("x86/cpuid/features/on", - "-cpu 486,arat=on", - 6, 0, "EAX", 2, true); - add_feature_test("x86/cpuid/features/off", - "-cpu pentium,mmx=off", - 1, 0, "EDX", 23, false); - add_feature_test("x86/cpuid/features/max-plus-invtsc", - "-cpu max,+invtsc", - 0x80000007, 0, "EDX", 8, true); - add_feature_test("x86/cpuid/features/max-invtsc-on", - "-cpu max,invtsc=on", - 0x80000007, 0, "EDX", 8, true); - add_feature_test("x86/cpuid/features/max-minus-mmx", - "-cpu max,-mmx", - 1, 0, "EDX", 23, false); - add_feature_test("x86/cpuid/features/max-invtsc-on,mmx=off", - "-cpu max,mmx=off", - 1, 0, "EDX", 23, false); - - return g_test_run(); -} diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c deleted file mode 100644 index f930a96b83..0000000000 --- a/tests/tmp105-test.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * QTest testcase for the TMP105 temperature sensor - * - * Copyright (c) 2012 Andreas Färber - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "libqtest-single.h" -#include "libqos/qgraph.h" -#include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "hw/misc/tmp105_regs.h" - -#define TMP105_TEST_ID "tmp105-test" -#define TMP105_TEST_ADDR 0x49 - -static int qmp_tmp105_get_temperature(const char *id) -{ - QDict *response; - int ret; - - response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, " - "'property': 'temperature' } }", id); - g_assert(qdict_haskey(response, "return")); - ret = qdict_get_int(response, "return"); - qobject_unref(response); - return ret; -} - -static void qmp_tmp105_set_temperature(const char *id, int value) -{ - QDict *response; - - response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, " - "'property': 'temperature', 'value': %d } }", id, value); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); -} - -#define TMP105_PRECISION (1000/16) -static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) -{ - uint16_t value; - QI2CDevice *i2cdev = (QI2CDevice *)obj; - - value = qmp_tmp105_get_temperature(TMP105_TEST_ID); - g_assert_cmpuint(value, ==, 0); - - value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); - g_assert_cmphex(value, ==, 0); - - qmp_tmp105_set_temperature(TMP105_TEST_ID, 20000); - value = qmp_tmp105_get_temperature(TMP105_TEST_ID); - g_assert_cmpuint(value, ==, 20000); - - value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); - g_assert_cmphex(value, ==, 0x1400); - - qmp_tmp105_set_temperature(TMP105_TEST_ID, 20938); /* 20 + 15/16 */ - value = qmp_tmp105_get_temperature(TMP105_TEST_ID); - g_assert_cmpuint(value, >=, 20938 - TMP105_PRECISION/2); - g_assert_cmpuint(value, <, 20938 + TMP105_PRECISION/2); - - /* Set config */ - i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x60); - value = i2c_get8(i2cdev, TMP105_REG_CONFIG); - g_assert_cmphex(value, ==, 0x60); - - value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); - g_assert_cmphex(value, ==, 0x14f0); - - /* Set precision to 9, 10, 11 bits. */ - i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x00); - g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x00); - value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); - g_assert_cmphex(value, ==, 0x1480); - - i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x20); - g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x20); - value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); - g_assert_cmphex(value, ==, 0x14c0); - - i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x40); - g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x40); - value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); - g_assert_cmphex(value, ==, 0x14e0); - - /* stored precision remains the same */ - value = qmp_tmp105_get_temperature(TMP105_TEST_ID); - g_assert_cmpuint(value, >=, 20938 - TMP105_PRECISION/2); - g_assert_cmpuint(value, <, 20938 + TMP105_PRECISION/2); - - i2c_set8(i2cdev, TMP105_REG_CONFIG, 0x60); - g_assert_cmphex(i2c_get8(i2cdev, TMP105_REG_CONFIG), ==, 0x60); - value = i2c_get16(i2cdev, TMP105_REG_TEMPERATURE); - g_assert_cmphex(value, ==, 0x14f0); - - i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x1234); - g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1234); - i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x4231); - g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4231); -} - -static void tmp105_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "id=" TMP105_TEST_ID ",address=0x49" - }; - add_qi2c_address(&opts, &(QI2CAddress) { 0x49 }); - - qos_node_create_driver("tmp105", i2c_device_create); - qos_node_consumes("tmp105", "i2c-bus", &opts); - - qos_add_test("tx-rx", "tmp105", send_and_receive, NULL); -} -libqos_init(tmp105_register_nodes); diff --git a/tests/tpm-crb-swtpm-test.c b/tests/tpm-crb-swtpm-test.c deleted file mode 100644 index 2c4fb8ae29..0000000000 --- a/tests/tpm-crb-swtpm-test.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * QTest testcase for TPM CRB talking to external swtpm and swtpm migration - * - * Copyright (c) 2018 IBM Corporation - * with parts borrowed from migration-test.c that is: - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Stefan Berger - * - * 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 - -#include "libqtest.h" -#include "qemu/module.h" -#include "tpm-tests.h" - -typedef struct TestState { - char *src_tpm_path; - char *dst_tpm_path; - char *uri; -} TestState; - -static void tpm_crb_swtpm_test(const void *data) -{ - const TestState *ts = data; - - tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_transfer, "tpm-crb"); -} - -static void tpm_crb_swtpm_migration_test(const void *data) -{ - const TestState *ts = data; - - tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, - tpm_util_crb_transfer, "tpm-crb"); -} - -int main(int argc, char **argv) -{ - int ret; - TestState ts = { 0 }; - - ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); - ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); - ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); - - module_call_init(MODULE_INIT_QOM); - g_test_init(&argc, &argv, NULL); - - qtest_add_data_func("/tpm/crb-swtpm/test", &ts, tpm_crb_swtpm_test); - qtest_add_data_func("/tpm/crb-swtpm-migration/test", &ts, - tpm_crb_swtpm_migration_test); - ret = g_test_run(); - - g_rmdir(ts.dst_tpm_path); - g_free(ts.dst_tpm_path); - g_rmdir(ts.src_tpm_path); - g_free(ts.src_tpm_path); - g_free(ts.uri); - - return ret; -} diff --git a/tests/tpm-crb-test.c b/tests/tpm-crb-test.c deleted file mode 100644 index 632fb7fbd8..0000000000 --- a/tests/tpm-crb-test.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * QTest testcase for TPM CRB - * - * Copyright (c) 2018 Red Hat, Inc. - * - * Authors: - * Marc-André Lureau - * - * 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 - -#include "hw/acpi/tpm.h" -#include "io/channel-socket.h" -#include "libqtest-single.h" -#include "qemu/module.h" -#include "tpm-emu.h" - -#define TPM_CMD "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00" - -static void tpm_crb_test(const void *data) -{ - const TestState *s = data; - uint32_t intfid = readl(TPM_CRB_ADDR_BASE + A_CRB_INTF_ID); - uint32_t csize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE); - uint64_t caddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); - uint32_t rsize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_SIZE); - uint64_t raddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); - uint8_t locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); - uint32_t locctrl = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL); - uint32_t locsts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); - uint32_t sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); - - g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceType), ==, 1); - g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceVersion), ==, 1); - g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapLocality), ==, 0); - g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRBIdleBypass), ==, 0); - g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapDataXferSizeSupport), - ==, 3); - g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapFIFO), ==, 0); - g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRB), ==, 1); - g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceSelector), ==, 1); - g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, RID), ==, 0); - - g_assert_cmpint(csize, >=, 128); - g_assert_cmpint(rsize, >=, 128); - g_assert_cmpint(caddr, >, TPM_CRB_ADDR_BASE); - g_assert_cmpint(raddr, >, TPM_CRB_ADDR_BASE); - - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 0); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); - - g_assert_cmpint(locctrl, ==, 0); - - g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, Granted), ==, 0); - g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, beenSeized), ==, 0); - - g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 1); - g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); - - /* request access to locality 0 */ - writeb(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); - - /* granted bit must be set now */ - locsts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); - g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, Granted), ==, 1); - g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, beenSeized), ==, 0); - - /* we must have an assigned locality */ - locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 1); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); - - /* set into ready state */ - writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ, 1); - - /* TPM must not be in the idle state */ - sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); - g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 0); - g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); - - memwrite(caddr, TPM_CMD, sizeof(TPM_CMD)); - - uint32_t start = 1; - uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; - writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); - do { - start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); - if ((start & 1) == 0) { - break; - } - } while (g_get_monotonic_time() < end_time); - start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); - g_assert_cmpint(start & 1, ==, 0); - - /* TPM must still not be in the idle state */ - sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); - g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 0); - g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); - - struct tpm_hdr tpm_msg; - memread(raddr, &tpm_msg, sizeof(tpm_msg)); - g_assert_cmpmem(&tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg)); - - /* set TPM into idle state */ - writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ, 2); - - /* idle state must be indicated now */ - sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); - g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 1); - g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); - - /* relinquish locality */ - writel(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 2); - - /* Granted flag must be cleared */ - sts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); - g_assert_cmpint(FIELD_EX32(sts, CRB_LOC_STS, Granted), ==, 0); - g_assert_cmpint(FIELD_EX32(sts, CRB_LOC_STS, beenSeized), ==, 0); - - /* no locality may be assigned */ - locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 0); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); - g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); - -} - -int main(int argc, char **argv) -{ - int ret; - char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-crb-test.XXXXXX", NULL); - GThread *thread; - TestState test; - - module_call_init(MODULE_INIT_QOM); - g_test_init(&argc, &argv, NULL); - - test.addr = g_new0(SocketAddress, 1); - test.addr->type = SOCKET_ADDRESS_TYPE_UNIX; - test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); - g_mutex_init(&test.data_mutex); - g_cond_init(&test.data_cond); - test.data_cond_signal = false; - - thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); - tpm_emu_test_wait_cond(&test); - - args = g_strdup_printf( - "-chardev socket,id=chr,path=%s " - "-tpmdev emulator,id=dev,chardev=chr " - "-device tpm-crb,tpmdev=dev", - test.addr->u.q_unix.path); - qtest_start(args); - - qtest_add_data_func("/tpm-crb/test", &test, tpm_crb_test); - ret = g_test_run(); - - qtest_end(); - - g_thread_join(thread); - g_unlink(test.addr->u.q_unix.path); - qapi_free_SocketAddress(test.addr); - g_rmdir(tmp_path); - g_free(tmp_path); - g_free(args); - return ret; -} diff --git a/tests/tpm-emu.c b/tests/tpm-emu.c deleted file mode 100644 index c43ac4aef8..0000000000 --- a/tests/tpm-emu.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Minimal TPM emulator for TPM test cases - * - * Copyright (c) 2018 Red Hat, Inc. - * - * Authors: - * Marc-André Lureau - * - * 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 - -#include "hw/tpm/tpm_ioctl.h" -#include "io/channel-socket.h" -#include "qapi/error.h" -#include "tpm-emu.h" - -void tpm_emu_test_wait_cond(TestState *s) -{ - gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; - - g_mutex_lock(&s->data_mutex); - - if (!s->data_cond_signal && - !g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { - g_assert_not_reached(); - } - - s->data_cond_signal = false; - - g_mutex_unlock(&s->data_mutex); -} - -static void *tpm_emu_tpm_thread(void *data) -{ - TestState *s = data; - QIOChannel *ioc = s->tpm_ioc; - - s->tpm_msg = g_new(struct tpm_hdr, 1); - while (true) { - int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len); - - if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) { - break; - } - s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag); - s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len); - g_assert_cmpint(s->tpm_msg->len, >=, minhlen); - g_assert_cmpint(s->tpm_msg->tag, ==, TPM2_ST_NO_SESSIONS); - - s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len); - qio_channel_read(ioc, (char *)&s->tpm_msg->code, - s->tpm_msg->len - minhlen, &error_abort); - s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code); - - /* reply error */ - s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); - s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr)); - s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE); - qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len), - &error_abort); - } - - g_free(s->tpm_msg); - s->tpm_msg = NULL; - object_unref(OBJECT(s->tpm_ioc)); - return NULL; -} - -void *tpm_emu_ctrl_thread(void *data) -{ - TestState *s = data; - QIOChannelSocket *lioc = qio_channel_socket_new(); - QIOChannel *ioc; - - qio_channel_socket_listen_sync(lioc, s->addr, 1, &error_abort); - - g_mutex_lock(&s->data_mutex); - s->data_cond_signal = true; - g_mutex_unlock(&s->data_mutex); - g_cond_signal(&s->data_cond); - - qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); - ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); - g_assert(ioc); - - { - uint32_t cmd = 0; - struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) }; - int *pfd = NULL; - size_t nfd = 0; - - qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort); - cmd = be32_to_cpu(cmd); - g_assert_cmpint(cmd, ==, CMD_SET_DATAFD); - g_assert_cmpint(nfd, ==, 1); - s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort)); - g_free(pfd); - - cmd = 0; - qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort); - - s->emu_tpm_thread = g_thread_new(NULL, tpm_emu_tpm_thread, s); - } - - while (true) { - uint32_t cmd; - ssize_t ret; - - ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL); - if (ret <= 0) { - break; - } - - cmd = be32_to_cpu(cmd); - switch (cmd) { - case CMD_GET_CAPABILITY: { - ptm_cap cap = cpu_to_be64(0x3fff); - qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort); - break; - } - case CMD_INIT: { - ptm_init init; - qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req), - &error_abort); - init.u.resp.tpm_result = 0; - qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp), - &error_abort); - break; - } - case CMD_SHUTDOWN: { - ptm_res res = 0; - qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); - /* the tpm data thread is expected to finish now */ - g_thread_join(s->emu_tpm_thread); - break; - } - case CMD_STOP: { - ptm_res res = 0; - qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); - break; - } - case CMD_SET_BUFFERSIZE: { - ptm_setbuffersize sbs; - qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req), - &error_abort); - sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096); - sbs.u.resp.tpm_result = 0; - sbs.u.resp.minsize = cpu_to_be32(128); - sbs.u.resp.maxsize = cpu_to_be32(4096); - qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp), - &error_abort); - break; - } - case CMD_SET_LOCALITY: { - ptm_loc loc; - /* Note: this time it's not u.req / u.resp... */ - qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort); - g_assert_cmpint(loc.u.req.loc, ==, 0); - loc.u.resp.tpm_result = 0; - qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort); - break; - } - case CMD_GET_TPMESTABLISHED: { - ptm_est est = { - .u.resp.bit = 0, - }; - qio_channel_write(ioc, (char *)&est, sizeof(est), &error_abort); - break; - } - default: - g_debug("unimplemented %u", cmd); - g_assert_not_reached(); - } - } - - object_unref(OBJECT(ioc)); - object_unref(OBJECT(lioc)); - return NULL; -} diff --git a/tests/tpm-emu.h b/tests/tpm-emu.h deleted file mode 100644 index a4f1d64226..0000000000 --- a/tests/tpm-emu.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Minimal TPM emulator for TPM test cases - * - * Copyright (c) 2018 Red Hat, Inc. - * - * Authors: - * Marc-André Lureau - * - * 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 TESTS_TPM_EMU_H -#define TESTS_TPM_EMU_H - -#define TPM_RC_FAILURE 0x101 -#define TPM2_ST_NO_SESSIONS 0x8001 - -struct tpm_hdr { - uint16_t tag; - uint32_t len; - uint32_t code; /*ordinal/error */ - char buffer[]; -} QEMU_PACKED; - -typedef struct TestState { - GMutex data_mutex; - GCond data_cond; - bool data_cond_signal; - SocketAddress *addr; - QIOChannel *tpm_ioc; - GThread *emu_tpm_thread; - struct tpm_hdr *tpm_msg; -} TestState; - -void tpm_emu_test_wait_cond(TestState *s); -void *tpm_emu_ctrl_thread(void *data); - -#endif /* TESTS_TPM_EMU_H */ diff --git a/tests/tpm-tests.c b/tests/tpm-tests.c deleted file mode 100644 index 6e45a0ba85..0000000000 --- a/tests/tpm-tests.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * QTest TPM commont test code - * - * Copyright (c) 2018 IBM Corporation - * Copyright (c) 2018 Red Hat, Inc. - * - * Authors: - * Stefan Berger - * Marc-André Lureau - * - * 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 - -#include "libqtest-single.h" -#include "tpm-tests.h" - -static bool -tpm_test_swtpm_skip(void) -{ - if (!tpm_util_swtpm_has_tpm2()) { - g_test_skip("swtpm not in PATH or missing --tpm2 support"); - return true; - } - - return false; -} - -void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, - const char *ifmodel) -{ - char *args = NULL; - QTestState *s; - SocketAddress *addr = NULL; - gboolean succ; - GPid swtpm_pid; - GError *error = NULL; - - if (tpm_test_swtpm_skip()) { - return; - } - - succ = tpm_util_swtpm_start(src_tpm_path, &swtpm_pid, &addr, &error); - g_assert_true(succ); - - args = g_strdup_printf( - "-chardev socket,id=chr,path=%s " - "-tpmdev emulator,id=dev,chardev=chr " - "-device %s,tpmdev=dev", - addr->u.q_unix.path, ifmodel); - - s = qtest_start(args); - g_free(args); - - tpm_util_startup(s, tx); - tpm_util_pcrextend(s, tx); - - unsigned char tpm_pcrread_resp[] = - "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" - "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" - "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" - "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; - tpm_util_pcrread(s, tx, tpm_pcrread_resp, - sizeof(tpm_pcrread_resp)); - - qtest_end(); - tpm_util_swtpm_kill(swtpm_pid); - - if (addr) { - g_unlink(addr->u.q_unix.path); - qapi_free_SocketAddress(addr); - } -} - -void tpm_test_swtpm_migration_test(const char *src_tpm_path, - const char *dst_tpm_path, - const char *uri, tx_func *tx, - const char *ifmodel) -{ - gboolean succ; - GPid src_tpm_pid, dst_tpm_pid; - SocketAddress *src_tpm_addr = NULL, *dst_tpm_addr = NULL; - GError *error = NULL; - QTestState *src_qemu, *dst_qemu; - - if (tpm_test_swtpm_skip()) { - return; - } - - succ = tpm_util_swtpm_start(src_tpm_path, &src_tpm_pid, - &src_tpm_addr, &error); - g_assert_true(succ); - - succ = tpm_util_swtpm_start(dst_tpm_path, &dst_tpm_pid, - &dst_tpm_addr, &error); - g_assert_true(succ); - - tpm_util_migration_start_qemu(&src_qemu, &dst_qemu, - src_tpm_addr, dst_tpm_addr, uri, - ifmodel); - - tpm_util_startup(src_qemu, tx); - tpm_util_pcrextend(src_qemu, tx); - - unsigned char tpm_pcrread_resp[] = - "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" - "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" - "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" - "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; - tpm_util_pcrread(src_qemu, tx, tpm_pcrread_resp, - sizeof(tpm_pcrread_resp)); - - tpm_util_migrate(src_qemu, uri); - tpm_util_wait_for_migration_complete(src_qemu); - - tpm_util_pcrread(dst_qemu, tx, tpm_pcrread_resp, - sizeof(tpm_pcrread_resp)); - - qtest_quit(dst_qemu); - qtest_quit(src_qemu); - - tpm_util_swtpm_kill(dst_tpm_pid); - if (dst_tpm_addr) { - g_unlink(dst_tpm_addr->u.q_unix.path); - qapi_free_SocketAddress(dst_tpm_addr); - } - - tpm_util_swtpm_kill(src_tpm_pid); - if (src_tpm_addr) { - g_unlink(src_tpm_addr->u.q_unix.path); - qapi_free_SocketAddress(src_tpm_addr); - } -} diff --git a/tests/tpm-tests.h b/tests/tpm-tests.h deleted file mode 100644 index b97688fe75..0000000000 --- a/tests/tpm-tests.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * QTest TPM commont test code - * - * Copyright (c) 2018 IBM Corporation - * - * Authors: - * Stefan Berger - * - * 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 TESTS_TPM_TESTS_H -#define TESTS_TPM_TESTS_H - -#include "tpm-util.h" - -void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, - const char *ifmodel); - -void tpm_test_swtpm_migration_test(const char *src_tpm_path, - const char *dst_tpm_path, - const char *uri, tx_func *tx, - const char *ifmodel); - -#endif /* TESTS_TPM_TESTS_H */ diff --git a/tests/tpm-tis-swtpm-test.c b/tests/tpm-tis-swtpm-test.c deleted file mode 100644 index 9f58a3a92b..0000000000 --- a/tests/tpm-tis-swtpm-test.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * QTest testcase for TPM TIS talking to external swtpm and swtpm migration - * - * Copyright (c) 2018 IBM Corporation - * with parts borrowed from migration-test.c that is: - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Stefan Berger - * - * 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 - -#include "libqtest.h" -#include "qemu/module.h" -#include "tpm-tests.h" - -typedef struct TestState { - char *src_tpm_path; - char *dst_tpm_path; - char *uri; -} TestState; - -static void tpm_tis_swtpm_test(const void *data) -{ - const TestState *ts = data; - - tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_tis_transfer, "tpm-tis"); -} - -static void tpm_tis_swtpm_migration_test(const void *data) -{ - const TestState *ts = data; - - tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, - tpm_util_tis_transfer, "tpm-tis"); -} - -int main(int argc, char **argv) -{ - int ret; - TestState ts = { 0 }; - - ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-tis-swtpm-test.XXXXXX", NULL); - ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-tis-swtpm-test.XXXXXX", NULL); - ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); - - module_call_init(MODULE_INIT_QOM); - g_test_init(&argc, &argv, NULL); - - qtest_add_data_func("/tpm/tis-swtpm/test", &ts, tpm_tis_swtpm_test); - qtest_add_data_func("/tpm/tis-swtpm-migration/test", &ts, - tpm_tis_swtpm_migration_test); - ret = g_test_run(); - - g_rmdir(ts.dst_tpm_path); - g_free(ts.dst_tpm_path); - g_rmdir(ts.src_tpm_path); - g_free(ts.src_tpm_path); - g_free(ts.uri); - - return ret; -} diff --git a/tests/tpm-tis-test.c b/tests/tpm-tis-test.c deleted file mode 100644 index dcf30e05b7..0000000000 --- a/tests/tpm-tis-test.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * QTest testcase for TPM TIS - * - * Copyright (c) 2018 Red Hat, Inc. - * Copyright (c) 2018 IBM Corporation - * - * Authors: - * Marc-André Lureau - * Stefan Berger - * - * 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 - -#include "hw/acpi/tpm.h" -#include "io/channel-socket.h" -#include "libqtest-single.h" -#include "qemu/module.h" -#include "tpm-emu.h" - -#define TIS_REG(LOCTY, REG) \ - (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG) - -#define DEBUG_TIS_TEST 0 - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_TIS_TEST) { \ - printf(fmt, ## __VA_ARGS__); \ - } \ -} while (0) - -#define DPRINTF_ACCESS \ - DPRINTF("%s: %d: locty=%d l=%d access=0x%02x pending_request_flag=0x%x\n", \ - __func__, __LINE__, locty, l, access, pending_request_flag) - -#define DPRINTF_STS \ - DPRINTF("%s: %d: sts = 0x%08x\n", __func__, __LINE__, sts) - -static const uint8_t TPM_CMD[12] = - "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; - -static void tpm_tis_test_check_localities(const void *data) -{ - uint8_t locty; - uint8_t access; - uint32_t ifaceid; - uint32_t capability; - uint32_t didvid; - uint32_t rid; - - for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES; locty++) { - access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - capability = readl(TIS_REG(locty, TPM_TIS_REG_INTF_CAPABILITY)); - g_assert_cmpint(capability, ==, TPM_TIS_CAPABILITIES_SUPPORTED2_0); - - ifaceid = readl(TIS_REG(locty, TPM_TIS_REG_INTERFACE_ID)); - g_assert_cmpint(ifaceid, ==, TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0); - - didvid = readl(TIS_REG(locty, TPM_TIS_REG_DID_VID)); - g_assert_cmpint(didvid, !=, 0); - g_assert_cmpint(didvid, !=, 0xffffffff); - - rid = readl(TIS_REG(locty, TPM_TIS_REG_RID)); - g_assert_cmpint(rid, !=, 0); - g_assert_cmpint(rid, !=, 0xffffffff); - } -} - -static void tpm_tis_test_check_access_reg(const void *data) -{ - uint8_t locty; - uint8_t access; - - /* do not test locality 4 (hw only) */ - for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) { - access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* request use of locality */ - writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - - access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_ACTIVE_LOCALITY | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* release access */ - writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), - TPM_TIS_ACCESS_ACTIVE_LOCALITY); - access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - } -} - -/* - * Test case for seizing access by a higher number locality - */ -static void tpm_tis_test_check_access_reg_seize(const void *data) -{ - int locty, l; - uint8_t access; - uint8_t pending_request_flag; - - /* do not test locality 4 (hw only) */ - for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) { - pending_request_flag = 0; - - access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* request use of locality */ - writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_ACTIVE_LOCALITY | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* lower localities cannot seize access */ - for (l = 0; l < locty; l++) { - /* lower locality is not active */ - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* try to request use from 'l' */ - writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - - /* requesting use from 'l' was not possible; - we must see REQUEST_USE and possibly PENDING_REQUEST */ - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_REQUEST_USE | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* locality 'locty' must be unchanged; - we must see PENDING_REQUEST */ - access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_ACTIVE_LOCALITY | - TPM_TIS_ACCESS_PENDING_REQUEST | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* try to seize from 'l' */ - writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_SEIZE); - /* seize from 'l' was not possible */ - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_REQUEST_USE | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* locality 'locty' must be unchanged */ - access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_ACTIVE_LOCALITY | - TPM_TIS_ACCESS_PENDING_REQUEST | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* on the next loop we will have a PENDING_REQUEST flag - set for locality 'l' */ - pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST; - } - - /* higher localities can 'seize' access but not 'request use'; - note: this will activate first l+1, then l+2 etc. */ - for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { - /* try to 'request use' from 'l' */ - writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - - /* requesting use from 'l' was not possible; we should see - REQUEST_USE and may see PENDING_REQUEST */ - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_REQUEST_USE | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* locality 'l-1' must be unchanged; we should always - see PENDING_REQUEST from 'l' requesting access */ - access = readb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_ACTIVE_LOCALITY | - TPM_TIS_ACCESS_PENDING_REQUEST | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* try to seize from 'l' */ - writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_SEIZE); - - /* seize from 'l' was possible */ - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_ACTIVE_LOCALITY | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* l - 1 should show that it has BEEN_SEIZED */ - access = readb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_BEEN_SEIZED | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* clear the BEEN_SEIZED flag and make sure it's gone */ - writeb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS), - TPM_TIS_ACCESS_BEEN_SEIZED); - - access = readb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - } - - /* PENDING_REQUEST will not be set if locty = 0 since all localities - were active; in case of locty = 1, locality 0 will be active - but no PENDING_REQUEST anywhere */ - if (locty <= 1) { - pending_request_flag = 0; - } - - /* release access from l - 1; this activates locty - 1 */ - l--; - - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - - DPRINTF("%s: %d: relinquishing control on l = %d\n", - __func__, __LINE__, l); - writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), - TPM_TIS_ACCESS_ACTIVE_LOCALITY); - - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - for (l = locty - 1; l >= 0; l--) { - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_ACTIVE_LOCALITY | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* release this locality */ - writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), - TPM_TIS_ACCESS_ACTIVE_LOCALITY); - - if (l == 1) { - pending_request_flag = 0; - } - } - - /* no locality may be active now */ - for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - } - } -} - -/* - * Test case for getting access when higher number locality relinquishes access - */ -static void tpm_tis_test_check_access_reg_release(const void *data) -{ - int locty, l; - uint8_t access; - uint8_t pending_request_flag; - - /* do not test locality 4 (hw only) */ - for (locty = TPM_TIS_NUM_LOCALITIES - 2; locty >= 0; locty--) { - pending_request_flag = 0; - - access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* request use of locality */ - writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_ACTIVE_LOCALITY | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - /* request use of all other localities */ - for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { - if (l == locty) { - continue; - } - /* request use of locality 'l' -- we MUST see REQUEST USE and - may see PENDING_REQUEST */ - writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_REQUEST_USE | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST; - } - /* release locality 'locty' */ - writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), - TPM_TIS_ACCESS_ACTIVE_LOCALITY); - /* highest locality should now be active; release it and make sure the - next higest locality is active afterwards */ - for (l = TPM_TIS_NUM_LOCALITIES - 2; l >= 0; l--) { - if (l == locty) { - continue; - } - /* 'l' should be active now */ - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_ACTIVE_LOCALITY | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - /* 'l' relinquishes access */ - writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), - TPM_TIS_ACCESS_ACTIVE_LOCALITY); - access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); - DPRINTF_ACCESS; - if (l == 1 || (locty <= 1 && l == 2)) { - pending_request_flag = 0; - } - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - pending_request_flag | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - } - } -} - -/* - * Test case for transmitting packets - */ -static void tpm_tis_test_check_transmit(const void *data) -{ - const TestState *s = data; - uint8_t access; - uint32_t sts; - uint16_t bcount; - size_t i; - - /* request use of locality 0 */ - writeb(TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); - g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | - TPM_TIS_ACCESS_ACTIVE_LOCALITY | - TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - - sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); - DPRINTF_STS; - - g_assert_cmpint(sts & 0xff, ==, 0); - g_assert_cmpint(sts & TPM_TIS_STS_TPM_FAMILY_MASK, ==, - TPM_TIS_STS_TPM_FAMILY2_0); - - bcount = (sts >> 8) & 0xffff; - g_assert_cmpint(bcount, >=, 128); - - writel(TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY); - sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); - DPRINTF_STS; - g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_COMMAND_READY); - - /* transmit command */ - for (i = 0; i < sizeof(TPM_CMD); i++) { - writeb(TIS_REG(0, TPM_TIS_REG_DATA_FIFO), TPM_CMD[i]); - sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); - DPRINTF_STS; - if (i < sizeof(TPM_CMD) - 1) { - g_assert_cmpint(sts & 0xff, ==, - TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); - } else { - g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_VALID); - } - g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount); - } - /* start processing */ - writeb(TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO); - - uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; - do { - sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); - if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { - break; - } - } while (g_get_monotonic_time() < end_time); - - sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); - DPRINTF_STS; - g_assert_cmpint(sts & 0xff, == , - TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); - bcount = (sts >> 8) & 0xffff; - - /* read response */ - uint8_t tpm_msg[sizeof(struct tpm_hdr)]; - g_assert_cmpint(sizeof(tpm_msg), ==, bcount); - - for (i = 0; i < sizeof(tpm_msg); i++) { - tpm_msg[i] = readb(TIS_REG(0, TPM_TIS_REG_DATA_FIFO)); - sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); - DPRINTF_STS; - if (sts & TPM_TIS_STS_DATA_AVAILABLE) { - g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount); - } - } - g_assert_cmpmem(tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg)); - - /* relinquish use of locality 0 */ - writeb(TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_ACTIVE_LOCALITY); - access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); -} - -int main(int argc, char **argv) -{ - int ret; - char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-tis-test.XXXXXX", NULL); - GThread *thread; - TestState test; - - module_call_init(MODULE_INIT_QOM); - g_test_init(&argc, &argv, NULL); - - test.addr = g_new0(SocketAddress, 1); - test.addr->type = SOCKET_ADDRESS_TYPE_UNIX; - test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); - g_mutex_init(&test.data_mutex); - g_cond_init(&test.data_cond); - test.data_cond_signal = false; - - thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); - tpm_emu_test_wait_cond(&test); - - args = g_strdup_printf( - "-chardev socket,id=chr,path=%s " - "-tpmdev emulator,id=dev,chardev=chr " - "-device tpm-tis,tpmdev=dev", - test.addr->u.q_unix.path); - qtest_start(args); - - qtest_add_data_func("/tpm-tis/test_check_localities", &test, - tpm_tis_test_check_localities); - - qtest_add_data_func("/tpm-tis/test_check_access_reg", &test, - tpm_tis_test_check_access_reg); - - qtest_add_data_func("/tpm-tis/test_check_access_reg_seize", &test, - tpm_tis_test_check_access_reg_seize); - - qtest_add_data_func("/tpm-tis/test_check_access_reg_release", &test, - tpm_tis_test_check_access_reg_release); - - qtest_add_data_func("/tpm-tis/test_check_transmit", &test, - tpm_tis_test_check_transmit); - - ret = g_test_run(); - - qtest_end(); - - g_thread_join(thread); - g_unlink(test.addr->u.q_unix.path); - qapi_free_SocketAddress(test.addr); - g_rmdir(tmp_path); - g_free(tmp_path); - g_free(args); - return ret; -} diff --git a/tests/tpm-util.c b/tests/tpm-util.c deleted file mode 100644 index e08b137651..0000000000 --- a/tests/tpm-util.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * QTest TPM utilities - * - * Copyright (c) 2018 IBM Corporation - * Copyright (c) 2018 Red Hat, Inc. - * - * Authors: - * Stefan Berger - * Marc-André Lureau - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "hw/acpi/tpm.h" -#include "libqtest.h" -#include "tpm-util.h" -#include "qapi/qmp/qdict.h" - -#define TIS_REG(LOCTY, REG) \ - (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG) - -void tpm_util_crb_transfer(QTestState *s, - const unsigned char *req, size_t req_size, - unsigned char *rsp, size_t rsp_size) -{ - uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); - uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); - - qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); - - qtest_memwrite(s, caddr, req, req_size); - - uint32_t sts, start = 1; - uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; - qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); - while (true) { - start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); - if ((start & 1) == 0) { - break; - } - if (g_get_monotonic_time() >= end_time) { - break; - } - }; - start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); - g_assert_cmpint(start & 1, ==, 0); - sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); - g_assert_cmpint(sts & 1, ==, 0); - - qtest_memread(s, raddr, rsp, rsp_size); -} - -void tpm_util_tis_transfer(QTestState *s, - const unsigned char *req, size_t req_size, - unsigned char *rsp, size_t rsp_size) -{ - uint32_t sts; - uint16_t bcount; - size_t i; - - /* request use of locality 0 */ - qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - qtest_writel(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY); - - sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); - bcount = (sts >> 8) & 0xffff; - g_assert_cmpint(bcount, >=, req_size); - - /* transmit command */ - for (i = 0; i < req_size; i++) { - qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO), req[i]); - } - - /* start processing */ - qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO); - - uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; - do { - sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); - if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { - break; - } - } while (g_get_monotonic_time() < end_time); - - sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); - bcount = (sts >> 8) & 0xffff; - - memset(rsp, 0, rsp_size); - for (i = 0; i < bcount; i++) { - rsp[i] = qtest_readb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO)); - } - - /* relinquish use of locality 0 */ - qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), - TPM_TIS_ACCESS_ACTIVE_LOCALITY); -} - -void tpm_util_startup(QTestState *s, tx_func *tx) -{ - unsigned char buffer[1024]; - unsigned char tpm_startup[] = - "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; - unsigned char tpm_startup_resp[] = - "\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00"; - - tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer)); - - g_assert_cmpmem(buffer, sizeof(tpm_startup_resp), - tpm_startup_resp, sizeof(tpm_startup_resp)); -} - -void tpm_util_pcrextend(QTestState *s, tx_func *tx) -{ - unsigned char buffer[1024]; - unsigned char tpm_pcrextend[] = - "\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00" - "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" - "\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00"; - - unsigned char tpm_pcrextend_resp[] = - "\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x01\x00\x00"; - - tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer)); - - g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp), - tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp)); -} - -void tpm_util_pcrread(QTestState *s, tx_func *tx, - const unsigned char *exp_resp, size_t exp_resp_size) -{ - unsigned char buffer[1024]; - unsigned char tpm_pcrread[] = - "\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b" - "\x03\x00\x04\x00"; - - tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer)); - - g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size); -} - -bool tpm_util_swtpm_has_tpm2(void) -{ - bool has_tpm2 = false; - char *out = NULL; - static const char *argv[] = { - "swtpm", "socket", "--help", NULL - }; - - if (!g_spawn_sync(NULL /* working_dir */, - (char **)argv, - NULL /* envp */, - G_SPAWN_SEARCH_PATH, - NULL /* child_setup */, - NULL /* user_data */, - &out, - NULL /* err */, - NULL /* exit_status */, - NULL)) { - return false; - } - - if (strstr(out, "--tpm2")) { - has_tpm2 = true; - } - - g_free(out); - return has_tpm2; -} - -gboolean tpm_util_swtpm_start(const char *path, GPid *pid, - SocketAddress **addr, GError **error) -{ - char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path); - char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock", - path); - gchar *swtpm_argv[] = { - g_strdup("swtpm"), g_strdup("socket"), - g_strdup("--tpmstate"), swtpm_argv_tpmstate, - g_strdup("--ctrl"), swtpm_argv_ctrl, - g_strdup("--tpm2"), - NULL - }; - gboolean succ; - unsigned i; - - *addr = g_new0(SocketAddress, 1); - (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX; - (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL); - - succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH, - NULL, NULL, pid, error); - - for (i = 0; swtpm_argv[i]; i++) { - g_free(swtpm_argv[i]); - } - - return succ; -} - -void tpm_util_swtpm_kill(GPid pid) -{ - int n; - - if (!pid) { - return; - } - - g_spawn_close_pid(pid); - - n = kill(pid, 0); - if (n < 0) { - return; - } - - kill(pid, SIGKILL); -} - -void tpm_util_migrate(QTestState *who, const char *uri) -{ - QDict *rsp; - - rsp = qtest_qmp(who, - "{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", - uri); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); -} - -void tpm_util_wait_for_migration_complete(QTestState *who) -{ - while (true) { - QDict *rsp_return; - bool completed; - const char *status; - - qtest_qmp_send(who, "{ 'execute': 'query-migrate' }"); - rsp_return = qtest_qmp_receive_success(who, NULL, NULL); - status = qdict_get_str(rsp_return, "status"); - completed = strcmp(status, "completed") == 0; - g_assert_cmpstr(status, !=, "failed"); - qobject_unref(rsp_return); - if (completed) { - return; - } - usleep(1000); - } -} - -void tpm_util_migration_start_qemu(QTestState **src_qemu, - QTestState **dst_qemu, - SocketAddress *src_tpm_addr, - SocketAddress *dst_tpm_addr, - const char *miguri, - const char *ifmodel) -{ - char *src_qemu_args, *dst_qemu_args; - - src_qemu_args = g_strdup_printf( - "-chardev socket,id=chr,path=%s " - "-tpmdev emulator,id=dev,chardev=chr " - "-device %s,tpmdev=dev ", - src_tpm_addr->u.q_unix.path, ifmodel); - - *src_qemu = qtest_init(src_qemu_args); - - dst_qemu_args = g_strdup_printf( - "-chardev socket,id=chr,path=%s " - "-tpmdev emulator,id=dev,chardev=chr " - "-device %s,tpmdev=dev " - "-incoming %s", - dst_tpm_addr->u.q_unix.path, - ifmodel, miguri); - - *dst_qemu = qtest_init(dst_qemu_args); - - free(src_qemu_args); - free(dst_qemu_args); -} diff --git a/tests/tpm-util.h b/tests/tpm-util.h deleted file mode 100644 index 5755698ad2..0000000000 --- a/tests/tpm-util.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * QTest TPM utilities - * - * Copyright (c) 2018 IBM Corporation - * - * Authors: - * Stefan Berger - * - * 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 TESTS_TPM_UTIL_H -#define TESTS_TPM_UTIL_H - -#include "io/channel-socket.h" - -typedef void (tx_func)(QTestState *s, - const unsigned char *req, size_t req_size, - unsigned char *rsp, size_t rsp_size); - -void tpm_util_crb_transfer(QTestState *s, - const unsigned char *req, size_t req_size, - unsigned char *rsp, size_t rsp_size); -void tpm_util_tis_transfer(QTestState *s, - const unsigned char *req, size_t req_size, - unsigned char *rsp, size_t rsp_size); - -void tpm_util_startup(QTestState *s, tx_func *tx); -void tpm_util_pcrextend(QTestState *s, tx_func *tx); -void tpm_util_pcrread(QTestState *s, tx_func *tx, - const unsigned char *exp_resp, size_t exp_resp_size); - -bool tpm_util_swtpm_has_tpm2(void); - -gboolean tpm_util_swtpm_start(const char *path, GPid *pid, - SocketAddress **addr, GError **error); -void tpm_util_swtpm_kill(GPid pid); - -void tpm_util_migrate(QTestState *who, const char *uri); - -void tpm_util_migration_start_qemu(QTestState **src_qemu, - QTestState **dst_qemu, - SocketAddress *src_tpm_addr, - SocketAddress *dst_tpm_addr, - const char *miguri, - const char *ifmodel); - -void tpm_util_wait_for_migration_complete(QTestState *who); - -#endif /* TESTS_TPM_UTIL_H */ diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c deleted file mode 100644 index 5251d539e9..0000000000 --- a/tests/usb-hcd-ehci-test.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * QTest testcase for USB EHCI - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "libqos/pci-pc.h" -#include "hw/usb/uhci-regs.h" -#include "hw/usb/ehci-regs.h" -#include "libqos/usb.h" - -static QPCIBus *pcibus; -static struct qhc uhci1; -static struct qhc uhci2; -static struct qhc uhci3; -static struct qhc ehci1; - -/* helpers */ - -#if 0 -static void uhci_port_update(struct qhc *hc, int port, - uint16_t set, uint16_t clear) -{ - void *addr = hc->base + 0x10 + 2 * port; - uint16_t value; - - value = qpci_io_readw(hc->dev, addr); - value |= set; - value &= ~clear; - qpci_io_writew(hc->dev, addr, value); -} -#endif - -static void ehci_port_test(struct qhc *hc, int port, uint32_t expect) -{ - uint32_t value = qpci_io_readl(hc->dev, hc->bar, 0x64 + 4 * port); - uint16_t mask = ~(PORTSC_CSC | PORTSC_PEDC | PORTSC_OCC); - -#if 0 - fprintf(stderr, "%s: %d, have 0x%08x, want 0x%08x\n", - __func__, port, value & mask, expect & mask); -#endif - g_assert((value & mask) == (expect & mask)); -} - -/* tests */ - -static void test_init(void) -{ - pcibus = qpci_new_pc(global_qtest, NULL); - g_assert(pcibus != NULL); - - qusb_pci_init_one(pcibus, &uhci1, QPCI_DEVFN(0x1d, 0), 4); - qusb_pci_init_one(pcibus, &uhci2, QPCI_DEVFN(0x1d, 1), 4); - qusb_pci_init_one(pcibus, &uhci3, QPCI_DEVFN(0x1d, 2), 4); - qusb_pci_init_one(pcibus, &ehci1, QPCI_DEVFN(0x1d, 7), 0); -} - -static void test_deinit(void) -{ - uhci_deinit(&uhci1); - uhci_deinit(&uhci2); - uhci_deinit(&uhci3); - uhci_deinit(&ehci1); - qpci_free_pc(pcibus); -} - -static void pci_uhci_port_1(void) -{ - g_assert(pcibus != NULL); - - uhci_port_test(&uhci1, 0, UHCI_PORT_CCS); /* usb-tablet */ - uhci_port_test(&uhci1, 1, UHCI_PORT_CCS); /* usb-storage */ - uhci_port_test(&uhci2, 0, 0); - uhci_port_test(&uhci2, 1, 0); - uhci_port_test(&uhci3, 0, 0); - uhci_port_test(&uhci3, 1, 0); -} - -static void pci_ehci_port_1(void) -{ - int i; - - g_assert(pcibus != NULL); - - for (i = 0; i < 6; i++) { - ehci_port_test(&ehci1, i, PORTSC_POWNER | PORTSC_PPOWER); - } -} - -static void pci_ehci_config(void) -{ - /* hands over all ports from companion uhci to ehci */ - qpci_io_writew(ehci1.dev, ehci1.bar, 0x60, 1); -} - -static void pci_uhci_port_2(void) -{ - g_assert(pcibus != NULL); - - uhci_port_test(&uhci1, 0, 0); /* usb-tablet, @ehci */ - uhci_port_test(&uhci1, 1, 0); /* usb-storage, @ehci */ - uhci_port_test(&uhci2, 0, 0); - uhci_port_test(&uhci2, 1, 0); - uhci_port_test(&uhci3, 0, 0); - uhci_port_test(&uhci3, 1, 0); -} - -static void pci_ehci_port_2(void) -{ - static uint32_t expect[] = { - PORTSC_PPOWER | PORTSC_CONNECT, /* usb-tablet */ - PORTSC_PPOWER | PORTSC_CONNECT, /* usb-storage */ - PORTSC_PPOWER, - PORTSC_PPOWER, - PORTSC_PPOWER, - PORTSC_PPOWER, - }; - int i; - - g_assert(pcibus != NULL); - - for (i = 0; i < 6; i++) { - ehci_port_test(&ehci1, i, expect[i]); - } -} - -static void pci_ehci_port_3_hotplug(void) -{ - /* check for presence of hotplugged usb-tablet */ - g_assert(pcibus != NULL); - ehci_port_test(&ehci1, 2, PORTSC_PPOWER | PORTSC_CONNECT); -} - -static void pci_ehci_port_hotplug(void) -{ - usb_test_hotplug(global_qtest, "ich9-ehci-1", "3", pci_ehci_port_3_hotplug); -} - - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/ehci/pci/uhci-port-1", pci_uhci_port_1); - qtest_add_func("/ehci/pci/ehci-port-1", pci_ehci_port_1); - qtest_add_func("/ehci/pci/ehci-config", pci_ehci_config); - qtest_add_func("/ehci/pci/uhci-port-2", pci_uhci_port_2); - qtest_add_func("/ehci/pci/ehci-port-2", pci_ehci_port_2); - qtest_add_func("/ehci/pci/ehci-port-3-hotplug", pci_ehci_port_hotplug); - - qtest_start("-machine q35 -device ich9-usb-ehci1,bus=pcie.0,addr=1d.7," - "multifunction=on,id=ich9-ehci-1 " - "-device ich9-usb-uhci1,bus=pcie.0,addr=1d.0," - "multifunction=on,masterbus=ich9-ehci-1.0,firstport=0 " - "-device ich9-usb-uhci2,bus=pcie.0,addr=1d.1," - "multifunction=on,masterbus=ich9-ehci-1.0,firstport=2 " - "-device ich9-usb-uhci3,bus=pcie.0,addr=1d.2," - "multifunction=on,masterbus=ich9-ehci-1.0,firstport=4 " - "-drive if=none,id=usbcdrom,media=cdrom " - "-device usb-tablet,bus=ich9-ehci-1.0,port=1,usb_version=1 " - "-device usb-storage,bus=ich9-ehci-1.0,port=2,drive=usbcdrom "); - - test_init(); - ret = g_test_run(); - test_deinit(); - - qtest_end(); - - return ret; -} diff --git a/tests/usb-hcd-ohci-test.c b/tests/usb-hcd-ohci-test.c deleted file mode 100644 index 19d760f3fb..0000000000 --- a/tests/usb-hcd-ohci-test.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * QTest testcase for USB OHCI controller - * - * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "qemu/module.h" -#include "libqos/usb.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -typedef struct QOHCI_PCI QOHCI_PCI; - -struct QOHCI_PCI { - QOSGraphObject obj; - QPCIDevice dev; -}; - -static void test_ohci_hotplug(void *obj, void *data, QGuestAllocator *alloc) -{ - usb_test_hotplug(global_qtest, "ohci", "1", NULL); -} - -static void *ohci_pci_get_driver(void *obj, const char *interface) -{ - QOHCI_PCI *ohci_pci = obj; - - if (!g_strcmp0(interface, "pci-device")) { - return &ohci_pci->dev; - } - - fprintf(stderr, "%s not present in pci-ohci\n", interface); - g_assert_not_reached(); -} - -static void *ohci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) -{ - QOHCI_PCI *ohci_pci = g_new0(QOHCI_PCI, 1); - ohci_pci->obj.get_driver = ohci_pci_get_driver; - - return &ohci_pci->obj; -} - -static void ohci_pci_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0,id=ohci", - }; - add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); - - qos_node_create_driver("pci-ohci", ohci_pci_create); - qos_node_consumes("pci-ohci", "pci-bus", &opts); - qos_node_produces("pci-ohci", "pci-device"); -} - -libqos_init(ohci_pci_register_nodes); - -static void register_ohci_pci_test(void) -{ - qos_add_test("ohci_pci-test-hotplug", "pci-ohci", test_ohci_hotplug, NULL); -} - -libqos_init(register_ohci_pci_test); diff --git a/tests/usb-hcd-uhci-test.c b/tests/usb-hcd-uhci-test.c deleted file mode 100644 index 7a117b64d9..0000000000 --- a/tests/usb-hcd-uhci-test.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * QTest testcase for USB UHCI controller - * - * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "libqos/libqos.h" -#include "libqos/usb.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" -#include "hw/usb/uhci-regs.h" - -static QOSState *qs; - -static void test_uhci_init(void) -{ -} - -static void test_port(int port) -{ - struct qhc uhci; - - g_assert(port > 0); - qusb_pci_init_one(qs->pcibus, &uhci, QPCI_DEVFN(0x1d, 0), 4); - uhci_port_test(&uhci, port - 1, UHCI_PORT_CCS); - uhci_deinit(&uhci); -} - -static void test_port_1(void) -{ - test_port(1); -} - -static void test_port_2(void) -{ - test_port(2); -} - -static void test_uhci_hotplug(void) -{ - usb_test_hotplug(global_qtest, "uhci", "2", test_port_2); -} - -static void test_usb_storage_hotplug(void) -{ - QTestState *qts = global_qtest; - - qtest_qmp_device_add(qts, "usb-storage", "usbdev0", "{'drive': 'drive0'}"); - - qtest_qmp_device_del(qts, "usbdev0"); -} - -int main(int argc, char **argv) -{ - const char *arch = qtest_get_arch(); - const char *cmd = "-device piix3-usb-uhci,id=uhci,addr=1d.0" - " -drive id=drive0,if=none,file=null-co://," - "file.read-zeroes=on,format=raw" - " -device usb-tablet,bus=uhci.0,port=1"; - int ret; - - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/uhci/pci/init", test_uhci_init); - qtest_add_func("/uhci/pci/port1", test_port_1); - qtest_add_func("/uhci/pci/hotplug", test_uhci_hotplug); - qtest_add_func("/uhci/pci/hotplug/usb-storage", test_usb_storage_hotplug); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qs = qtest_pc_boot(cmd); - } else if (strcmp(arch, "ppc64") == 0) { - qs = qtest_spapr_boot(cmd); - } else { - g_printerr("usb-hcd-uhci-test tests are only " - "available on x86 or ppc64\n"); - exit(EXIT_FAILURE); - } - global_qtest = qs->qts; - ret = g_test_run(); - qtest_shutdown(qs); - - return ret; -} diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c deleted file mode 100644 index 10ef9d2a91..0000000000 --- a/tests/usb-hcd-xhci-test.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * QTest testcase for USB xHCI controller - * - * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "libqos/usb.h" - - -static void test_xhci_init(void) -{ -} - -static void test_xhci_hotplug(void) -{ - usb_test_hotplug(global_qtest, "xhci", "1", NULL); -} - -static void test_usb_uas_hotplug(void) -{ - QTestState *qts = global_qtest; - - qtest_qmp_device_add(qts, "usb-uas", "uas", "{}"); - qtest_qmp_device_add(qts, "scsi-hd", "scsihd", "{'drive': 'drive0'}"); - - /* TODO: - UAS HBA driver in libqos, to check that - added disk is visible after BUS rescan - */ - - qtest_qmp_device_del(qts, "scsihd"); - qtest_qmp_device_del(qts, "uas"); -} - -static void test_usb_ccid_hotplug(void) -{ - QTestState *qts = global_qtest; - - qtest_qmp_device_add(qts, "usb-ccid", "ccid", "{}"); - qtest_qmp_device_del(qts, "ccid"); - /* check the device can be added again */ - qtest_qmp_device_add(qts, "usb-ccid", "ccid", "{}"); - qtest_qmp_device_del(qts, "ccid"); -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/xhci/pci/init", test_xhci_init); - qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); - qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); - qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug); - - qtest_start("-device nec-usb-xhci,id=xhci" - " -drive id=drive0,if=none,file=null-co://," - "file.read-zeroes=on,format=raw"); - ret = g_test_run(); - qtest_end(); - - return ret; -} diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c deleted file mode 100644 index 91ea373ba5..0000000000 --- a/tests/vhost-user-test.c +++ /dev/null @@ -1,967 +0,0 @@ -/* - * QTest testcase for the vhost-user - * - * Copyright (c) 2014 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" - -#include "libqtest-single.h" -#include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qemu/config-file.h" -#include "qemu/option.h" -#include "qemu/range.h" -#include "qemu/sockets.h" -#include "chardev/char-fe.h" -#include "qemu/memfd.h" -#include "qemu/module.h" -#include "sysemu/sysemu.h" -#include "libqos/libqos.h" -#include "libqos/pci-pc.h" -#include "libqos/virtio-pci.h" - -#include "libqos/malloc-pc.h" -#include "hw/virtio/virtio-net.h" - -#include "standard-headers/linux/vhost_types.h" -#include "standard-headers/linux/virtio_ids.h" -#include "standard-headers/linux/virtio_net.h" - -#ifdef CONFIG_LINUX -#include -#endif - - -#define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM," \ - "mem-path=%s,share=on -numa node,memdev=mem" -#define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \ - " -numa node,memdev=mem" -#define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s" -#define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce" - -#define HUGETLBFS_MAGIC 0x958458f6 - -/*********** FROM hw/virtio/vhost-user.c *************************************/ - -#define VHOST_MEMORY_MAX_NREGIONS 8 -#define VHOST_MAX_VIRTQUEUES 0x100 - -#define VHOST_USER_F_PROTOCOL_FEATURES 30 -#define VHOST_USER_PROTOCOL_F_MQ 0 -#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1 -#define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6 - -#define VHOST_LOG_PAGE 0x1000 - -typedef enum VhostUserRequest { - VHOST_USER_NONE = 0, - VHOST_USER_GET_FEATURES = 1, - VHOST_USER_SET_FEATURES = 2, - VHOST_USER_SET_OWNER = 3, - VHOST_USER_RESET_OWNER = 4, - VHOST_USER_SET_MEM_TABLE = 5, - VHOST_USER_SET_LOG_BASE = 6, - VHOST_USER_SET_LOG_FD = 7, - VHOST_USER_SET_VRING_NUM = 8, - VHOST_USER_SET_VRING_ADDR = 9, - VHOST_USER_SET_VRING_BASE = 10, - VHOST_USER_GET_VRING_BASE = 11, - VHOST_USER_SET_VRING_KICK = 12, - VHOST_USER_SET_VRING_CALL = 13, - VHOST_USER_SET_VRING_ERR = 14, - VHOST_USER_GET_PROTOCOL_FEATURES = 15, - VHOST_USER_SET_PROTOCOL_FEATURES = 16, - VHOST_USER_GET_QUEUE_NUM = 17, - VHOST_USER_SET_VRING_ENABLE = 18, - VHOST_USER_MAX -} VhostUserRequest; - -typedef struct VhostUserMemoryRegion { - uint64_t guest_phys_addr; - uint64_t memory_size; - uint64_t userspace_addr; - uint64_t mmap_offset; -} VhostUserMemoryRegion; - -typedef struct VhostUserMemory { - uint32_t nregions; - uint32_t padding; - VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; -} VhostUserMemory; - -typedef struct VhostUserLog { - uint64_t mmap_size; - uint64_t mmap_offset; -} VhostUserLog; - -typedef struct VhostUserMsg { - VhostUserRequest request; - -#define VHOST_USER_VERSION_MASK (0x3) -#define VHOST_USER_REPLY_MASK (0x1<<2) - uint32_t flags; - uint32_t size; /* the following payload size */ - union { -#define VHOST_USER_VRING_IDX_MASK (0xff) -#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) - uint64_t u64; - struct vhost_vring_state state; - struct vhost_vring_addr addr; - VhostUserMemory memory; - VhostUserLog log; - } payload; -} QEMU_PACKED VhostUserMsg; - -static VhostUserMsg m __attribute__ ((unused)); -#define VHOST_USER_HDR_SIZE (sizeof(m.request) \ - + sizeof(m.flags) \ - + sizeof(m.size)) - -#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) - -/* The version of the protocol we support */ -#define VHOST_USER_VERSION (0x1) -/*****************************************************************************/ - -enum { - TEST_FLAGS_OK, - TEST_FLAGS_DISCONNECT, - TEST_FLAGS_BAD, - TEST_FLAGS_END, -}; - -typedef struct TestServer { - gchar *socket_path; - gchar *mig_path; - gchar *chr_name; - gchar *tmpfs; - CharBackend chr; - int fds_num; - int fds[VHOST_MEMORY_MAX_NREGIONS]; - VhostUserMemory memory; - GMainContext *context; - GMainLoop *loop; - GThread *thread; - GMutex data_mutex; - GCond data_cond; - int log_fd; - uint64_t rings; - bool test_fail; - int test_flags; - int queues; -} TestServer; - -static const char *init_hugepagefs(void); -static TestServer *test_server_new(const gchar *name); -static void test_server_free(TestServer *server); -static void test_server_listen(TestServer *server); - -enum test_memfd { - TEST_MEMFD_AUTO, - TEST_MEMFD_YES, - TEST_MEMFD_NO, -}; - -static void append_vhost_opts(TestServer *s, GString *cmd_line, - const char *chr_opts) -{ - g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV, - s->chr_name, s->socket_path, - chr_opts, s->chr_name); -} - -static void append_mem_opts(TestServer *server, GString *cmd_line, - int size, enum test_memfd memfd) -{ - if (memfd == TEST_MEMFD_AUTO) { - memfd = qemu_memfd_check(MFD_ALLOW_SEALING) ? TEST_MEMFD_YES - : TEST_MEMFD_NO; - } - - if (memfd == TEST_MEMFD_YES) { - g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size); - } else { - const char *root = init_hugepagefs() ? : server->tmpfs; - - g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root); - } -} - -static bool wait_for_fds(TestServer *s) -{ - gint64 end_time; - bool got_region; - int i; - - g_mutex_lock(&s->data_mutex); - - end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; - while (!s->fds_num) { - if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { - /* timeout has passed */ - g_assert(s->fds_num); - break; - } - } - - /* check for sanity */ - g_assert_cmpint(s->fds_num, >, 0); - g_assert_cmpint(s->fds_num, ==, s->memory.nregions); - - g_mutex_unlock(&s->data_mutex); - - got_region = false; - for (i = 0; i < s->memory.nregions; ++i) { - VhostUserMemoryRegion *reg = &s->memory.regions[i]; - if (reg->guest_phys_addr == 0) { - got_region = true; - break; - } - } - if (!got_region) { - g_test_skip("No memory at address 0x0"); - } - return got_region; -} - -static void read_guest_mem_server(QTestState *qts, TestServer *s) -{ - uint8_t *guest_mem; - int i, j; - size_t size; - - g_mutex_lock(&s->data_mutex); - - /* iterate all regions */ - for (i = 0; i < s->fds_num; i++) { - - /* We'll check only the region statring at 0x0*/ - if (s->memory.regions[i].guest_phys_addr != 0x0) { - continue; - } - - g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024); - - size = s->memory.regions[i].memory_size + - s->memory.regions[i].mmap_offset; - - guest_mem = mmap(0, size, PROT_READ | PROT_WRITE, - MAP_SHARED, s->fds[i], 0); - - g_assert(guest_mem != MAP_FAILED); - guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem)); - - for (j = 0; j < 1024; j++) { - uint32_t a = qtest_readb(qts, s->memory.regions[i].guest_phys_addr + j); - uint32_t b = guest_mem[j]; - - g_assert_cmpint(a, ==, b); - } - - munmap(guest_mem, s->memory.regions[i].memory_size); - } - - g_mutex_unlock(&s->data_mutex); -} - -static void *thread_function(void *data) -{ - GMainLoop *loop = data; - g_main_loop_run(loop); - return NULL; -} - -static int chr_can_read(void *opaque) -{ - return VHOST_USER_HDR_SIZE; -} - -static void chr_read(void *opaque, const uint8_t *buf, int size) -{ - TestServer *s = opaque; - CharBackend *chr = &s->chr; - VhostUserMsg msg; - uint8_t *p = (uint8_t *) &msg; - int fd = -1; - - if (s->test_fail) { - qemu_chr_fe_disconnect(chr); - /* now switch to non-failure */ - s->test_fail = false; - } - - if (size != VHOST_USER_HDR_SIZE) { - g_test_message("Wrong message size received %d", size); - return; - } - - g_mutex_lock(&s->data_mutex); - memcpy(p, buf, VHOST_USER_HDR_SIZE); - - if (msg.size) { - p += VHOST_USER_HDR_SIZE; - size = qemu_chr_fe_read_all(chr, p, msg.size); - if (size != msg.size) { - g_test_message("Wrong message size received %d != %d", - size, msg.size); - return; - } - } - - switch (msg.request) { - case VHOST_USER_GET_FEATURES: - /* send back features to qemu */ - msg.flags |= VHOST_USER_REPLY_MASK; - msg.size = sizeof(m.payload.u64); - msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL | - 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; - if (s->queues > 1) { - msg.payload.u64 |= 0x1ULL << VIRTIO_NET_F_MQ; - } - if (s->test_flags >= TEST_FLAGS_BAD) { - msg.payload.u64 = 0; - s->test_flags = TEST_FLAGS_END; - } - p = (uint8_t *) &msg; - qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); - break; - - case VHOST_USER_SET_FEATURES: - g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES), - !=, 0ULL); - if (s->test_flags == TEST_FLAGS_DISCONNECT) { - qemu_chr_fe_disconnect(chr); - s->test_flags = TEST_FLAGS_BAD; - } - break; - - case VHOST_USER_GET_PROTOCOL_FEATURES: - /* send back features to qemu */ - msg.flags |= VHOST_USER_REPLY_MASK; - msg.size = sizeof(m.payload.u64); - msg.payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD; - msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_CROSS_ENDIAN; - if (s->queues > 1) { - msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ; - } - p = (uint8_t *) &msg; - qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); - break; - - case VHOST_USER_GET_VRING_BASE: - /* send back vring base to qemu */ - msg.flags |= VHOST_USER_REPLY_MASK; - msg.size = sizeof(m.payload.state); - msg.payload.state.num = 0; - p = (uint8_t *) &msg; - qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); - - assert(msg.payload.state.index < s->queues * 2); - s->rings &= ~(0x1ULL << msg.payload.state.index); - g_cond_broadcast(&s->data_cond); - break; - - case VHOST_USER_SET_MEM_TABLE: - /* received the mem table */ - memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory)); - s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, - G_N_ELEMENTS(s->fds)); - - /* signal the test that it can continue */ - g_cond_broadcast(&s->data_cond); - break; - - case VHOST_USER_SET_VRING_KICK: - case VHOST_USER_SET_VRING_CALL: - /* consume the fd */ - qemu_chr_fe_get_msgfds(chr, &fd, 1); - /* - * This is a non-blocking eventfd. - * The receive function forces it to be blocking, - * so revert it back to non-blocking. - */ - qemu_set_nonblock(fd); - break; - - case VHOST_USER_SET_LOG_BASE: - if (s->log_fd != -1) { - close(s->log_fd); - s->log_fd = -1; - } - qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1); - msg.flags |= VHOST_USER_REPLY_MASK; - msg.size = 0; - p = (uint8_t *) &msg; - qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE); - - g_cond_broadcast(&s->data_cond); - break; - - case VHOST_USER_SET_VRING_BASE: - assert(msg.payload.state.index < s->queues * 2); - s->rings |= 0x1ULL << msg.payload.state.index; - g_cond_broadcast(&s->data_cond); - break; - - case VHOST_USER_GET_QUEUE_NUM: - msg.flags |= VHOST_USER_REPLY_MASK; - msg.size = sizeof(m.payload.u64); - msg.payload.u64 = s->queues; - p = (uint8_t *) &msg; - qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); - break; - - default: - break; - } - - g_mutex_unlock(&s->data_mutex); -} - -static const char *init_hugepagefs(void) -{ -#ifdef CONFIG_LINUX - static const char *hugepagefs; - const char *path = getenv("QTEST_HUGETLBFS_PATH"); - struct statfs fs; - int ret; - - if (hugepagefs) { - return hugepagefs; - } - if (!path) { - return NULL; - } - - if (access(path, R_OK | W_OK | X_OK)) { - g_test_message("access on path (%s): %s", path, strerror(errno)); - g_test_fail(); - return NULL; - } - - do { - ret = statfs(path, &fs); - } while (ret != 0 && errno == EINTR); - - if (ret != 0) { - g_test_message("statfs on path (%s): %s", path, strerror(errno)); - g_test_fail(); - return NULL; - } - - if (fs.f_type != HUGETLBFS_MAGIC) { - g_test_message("Warning: path not on HugeTLBFS: %s", path); - g_test_fail(); - return NULL; - } - - hugepagefs = path; - return hugepagefs; -#else - return NULL; -#endif -} - -static TestServer *test_server_new(const gchar *name) -{ - TestServer *server = g_new0(TestServer, 1); - char template[] = "/tmp/vhost-test-XXXXXX"; - const char *tmpfs; - - server->context = g_main_context_new(); - server->loop = g_main_loop_new(server->context, FALSE); - - /* run the main loop thread so the chardev may operate */ - server->thread = g_thread_new(NULL, thread_function, server->loop); - - tmpfs = mkdtemp(template); - if (!tmpfs) { - g_test_message("mkdtemp on path (%s): %s", template, strerror(errno)); - } - g_assert(tmpfs); - - server->tmpfs = g_strdup(tmpfs); - server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name); - server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name); - server->chr_name = g_strdup_printf("chr-%s", name); - - g_mutex_init(&server->data_mutex); - g_cond_init(&server->data_cond); - - server->log_fd = -1; - server->queues = 1; - - return server; -} - -static void chr_event(void *opaque, int event) -{ - TestServer *s = opaque; - - if (s->test_flags == TEST_FLAGS_END && - event == CHR_EVENT_CLOSED) { - s->test_flags = TEST_FLAGS_OK; - } -} - -static void test_server_create_chr(TestServer *server, const gchar *opt) -{ - gchar *chr_path; - Chardev *chr; - - chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt); - chr = qemu_chr_new(server->chr_name, chr_path, server->context); - g_free(chr_path); - - g_assert_nonnull(chr); - qemu_chr_fe_init(&server->chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read, - chr_event, NULL, server, server->context, true); -} - -static void test_server_listen(TestServer *server) -{ - test_server_create_chr(server, ",server,nowait"); -} - -static void test_server_free(TestServer *server) -{ - int i, ret; - - /* finish the helper thread and dispatch pending sources */ - g_main_loop_quit(server->loop); - g_thread_join(server->thread); - while (g_main_context_pending(NULL)) { - g_main_context_iteration(NULL, TRUE); - } - - unlink(server->socket_path); - g_free(server->socket_path); - - unlink(server->mig_path); - g_free(server->mig_path); - - ret = rmdir(server->tmpfs); - if (ret != 0) { - g_test_message("unable to rmdir: path (%s): %s", - server->tmpfs, strerror(errno)); - } - g_free(server->tmpfs); - - qemu_chr_fe_deinit(&server->chr, true); - - for (i = 0; i < server->fds_num; i++) { - close(server->fds[i]); - } - - if (server->log_fd != -1) { - close(server->log_fd); - } - - g_free(server->chr_name); - - g_main_loop_unref(server->loop); - g_main_context_unref(server->context); - g_cond_clear(&server->data_cond); - g_mutex_clear(&server->data_mutex); - g_free(server); -} - -static void wait_for_log_fd(TestServer *s) -{ - gint64 end_time; - - g_mutex_lock(&s->data_mutex); - end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; - while (s->log_fd == -1) { - if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { - /* timeout has passed */ - g_assert(s->log_fd != -1); - break; - } - } - - g_mutex_unlock(&s->data_mutex); -} - -static void write_guest_mem(TestServer *s, uint32_t seed) -{ - uint32_t *guest_mem; - int i, j; - size_t size; - - /* iterate all regions */ - for (i = 0; i < s->fds_num; i++) { - - /* We'll write only the region statring at 0x0 */ - if (s->memory.regions[i].guest_phys_addr != 0x0) { - continue; - } - - g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024); - - size = s->memory.regions[i].memory_size + - s->memory.regions[i].mmap_offset; - - guest_mem = mmap(0, size, PROT_READ | PROT_WRITE, - MAP_SHARED, s->fds[i], 0); - - g_assert(guest_mem != MAP_FAILED); - guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem)); - - for (j = 0; j < 256; j++) { - guest_mem[j] = seed + j; - } - - munmap(guest_mem, s->memory.regions[i].memory_size); - break; - } -} - -static guint64 get_log_size(TestServer *s) -{ - guint64 log_size = 0; - int i; - - for (i = 0; i < s->memory.nregions; ++i) { - VhostUserMemoryRegion *reg = &s->memory.regions[i]; - guint64 last = range_get_last(reg->guest_phys_addr, - reg->memory_size); - log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1); - } - - return log_size; -} - -typedef struct TestMigrateSource { - GSource source; - TestServer *src; - TestServer *dest; -} TestMigrateSource; - -static gboolean -test_migrate_source_check(GSource *source) -{ - TestMigrateSource *t = (TestMigrateSource *)source; - gboolean overlap = t->src->rings && t->dest->rings; - - g_assert(!overlap); - - return FALSE; -} - -GSourceFuncs test_migrate_source_funcs = { - .check = test_migrate_source_check, -}; - -static void vhost_user_test_cleanup(void *s) -{ - TestServer *server = s; - - qos_invalidate_command_line(); - test_server_free(server); -} - -static void *vhost_user_test_setup(GString *cmd_line, void *arg) -{ - TestServer *server = test_server_new("vhost-user-test"); - test_server_listen(server); - - append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO); - append_vhost_opts(server, cmd_line, ""); - - g_test_queue_destroy(vhost_user_test_cleanup, server); - - return server; -} - -static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg) -{ - TestServer *server = test_server_new("vhost-user-test"); - test_server_listen(server); - - append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES); - append_vhost_opts(server, cmd_line, ""); - - g_test_queue_destroy(vhost_user_test_cleanup, server); - - return server; -} - -static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc) -{ - TestServer *server = arg; - - if (!wait_for_fds(server)) { - return; - } - - read_guest_mem_server(global_qtest, server); -} - -static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc) -{ - TestServer *s = arg; - TestServer *dest = test_server_new("dest"); - GString *dest_cmdline = g_string_new(qos_get_current_command_line()); - char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path); - QTestState *to; - GSource *source; - QDict *rsp; - guint8 *log; - guint64 size; - - if (!wait_for_fds(s)) { - return; - } - - size = get_log_size(s); - g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); - - test_server_listen(dest); - g_string_append_printf(dest_cmdline, " -incoming %s", uri); - append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO); - append_vhost_opts(dest, dest_cmdline, ""); - to = qtest_init(dest_cmdline->str); - - /* This would be where you call qos_allocate_objects(to, NULL), if you want - * to talk to the QVirtioNet object on the destination. - */ - - source = g_source_new(&test_migrate_source_funcs, - sizeof(TestMigrateSource)); - ((TestMigrateSource *)source)->src = s; - ((TestMigrateSource *)source)->dest = dest; - g_source_attach(source, s->context); - - /* slow down migration to have time to fiddle with log */ - /* TODO: qtest could learn to break on some places */ - rsp = qmp("{ 'execute': 'migrate_set_speed'," - "'arguments': { 'value': 10 } }"); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - - rsp = qmp("{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", uri); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - - wait_for_log_fd(s); - - log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0); - g_assert(log != MAP_FAILED); - - /* modify first page */ - write_guest_mem(s, 0x42); - log[0] = 1; - munmap(log, size); - - /* speed things up */ - rsp = qmp("{ 'execute': 'migrate_set_speed'," - "'arguments': { 'value': 0 } }"); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - - qmp_eventwait("STOP"); - qtest_qmp_eventwait(to, "RESUME"); - - g_assert(wait_for_fds(dest)); - read_guest_mem_server(to, dest); - - g_source_destroy(source); - g_source_unref(source); - - qtest_quit(to); - test_server_free(dest); - g_free(uri); -} - -static void wait_for_rings_started(TestServer *s, size_t count) -{ - gint64 end_time; - - g_mutex_lock(&s->data_mutex); - end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; - while (ctpop64(s->rings) != count) { - if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { - /* timeout has passed */ - g_assert_cmpint(ctpop64(s->rings), ==, count); - break; - } - } - - g_mutex_unlock(&s->data_mutex); -} - -static inline void test_server_connect(TestServer *server) -{ - test_server_create_chr(server, ",reconnect=1"); -} - -static gboolean -reconnect_cb(gpointer user_data) -{ - TestServer *s = user_data; - - qemu_chr_fe_disconnect(&s->chr); - - return FALSE; -} - -static gpointer -connect_thread(gpointer data) -{ - TestServer *s = data; - - /* wait for qemu to start before first try, to avoid extra warnings */ - g_usleep(G_USEC_PER_SEC); - test_server_connect(s); - - return NULL; -} - -static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg) -{ - TestServer *s = test_server_new("reconnect"); - - g_thread_new("connect", connect_thread, s); - append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); - append_vhost_opts(s, cmd_line, ",server"); - - g_test_queue_destroy(vhost_user_test_cleanup, s); - - return s; -} - -static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc) -{ - TestServer *s = arg; - GSource *src; - - if (!wait_for_fds(s)) { - return; - } - - wait_for_rings_started(s, 2); - - /* reconnect */ - s->fds_num = 0; - s->rings = 0; - src = g_idle_source_new(); - g_source_set_callback(src, reconnect_cb, s, NULL); - g_source_attach(src, s->context); - g_source_unref(src); - g_assert(wait_for_fds(s)); - wait_for_rings_started(s, 2); -} - -static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg) -{ - TestServer *s = test_server_new("connect-fail"); - - s->test_fail = true; - - g_thread_new("connect", connect_thread, s); - append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); - append_vhost_opts(s, cmd_line, ",server"); - - g_test_queue_destroy(vhost_user_test_cleanup, s); - - return s; -} - -static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg) -{ - TestServer *s = test_server_new("flags-mismatch"); - - s->test_flags = TEST_FLAGS_DISCONNECT; - - g_thread_new("connect", connect_thread, s); - append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); - append_vhost_opts(s, cmd_line, ",server"); - - g_test_queue_destroy(vhost_user_test_cleanup, s); - - return s; -} - -static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc) -{ - TestServer *s = arg; - - if (!wait_for_fds(s)) { - return; - } - wait_for_rings_started(s, 2); -} - -static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg) -{ - TestServer *s = vhost_user_test_setup(cmd_line, arg); - - s->queues = 2; - g_string_append_printf(cmd_line, - " -set netdev.hs0.queues=%d" - " -global virtio-net-pci.vectors=%d", - s->queues, s->queues * 2 + 2); - - return s; -} - -static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc) -{ - TestServer *s = arg; - - wait_for_rings_started(s, s->queues * 2); -} - -static void register_vhost_user_test(void) -{ - QOSGraphTestOptions opts = { - .before = vhost_user_test_setup, - .subprocess = true, - }; - - qemu_add_opts(&qemu_chardev_opts); - - qos_add_test("vhost-user/read-guest-mem/memfile", - "virtio-net", - test_read_guest_mem, &opts); - - if (qemu_memfd_check(MFD_ALLOW_SEALING)) { - opts.before = vhost_user_test_setup_memfd; - qos_add_test("vhost-user/read-guest-mem/memfd", - "virtio-net", - test_read_guest_mem, &opts); - } - - qos_add_test("vhost-user/migrate", - "virtio-net", - test_migrate, &opts); - - /* keeps failing on build-system since Aug 15 2017 */ - if (getenv("QTEST_VHOST_USER_FIXME")) { - opts.before = vhost_user_test_setup_reconnect; - qos_add_test("vhost-user/reconnect", "virtio-net", - test_reconnect, &opts); - - opts.before = vhost_user_test_setup_connect_fail; - qos_add_test("vhost-user/connect-fail", "virtio-net", - test_vhost_user_started, &opts); - - opts.before = vhost_user_test_setup_flags_mismatch; - qos_add_test("vhost-user/flags-mismatch", "virtio-net", - test_vhost_user_started, &opts); - } - - opts.before = vhost_user_test_setup_multiqueue; - opts.edge.extra_device_opts = "mq=on"; - qos_add_test("vhost-user/multiqueue", - "virtio-net", - test_multiqueue, &opts); -} -libqos_init(register_vhost_user_test); diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c deleted file mode 100644 index e7b58e3a0c..0000000000 --- a/tests/virtio-9p-test.c +++ /dev/null @@ -1,662 +0,0 @@ -/* - * QTest testcase for VirtIO 9P - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "qemu/module.h" -#include "hw/9pfs/9p.h" -#include "hw/9pfs/9p-synth.h" -#include "libqos/virtio-9p.h" -#include "libqos/qgraph.h" - -#define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000) -static QGuestAllocator *alloc; - -static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtio9P *v9p = obj; - alloc = t_alloc; - size_t tag_len = qvirtio_config_readw(v9p->vdev, 0); - char *tag; - int i; - - g_assert_cmpint(tag_len, ==, strlen(MOUNT_TAG)); - - tag = g_malloc(tag_len); - for (i = 0; i < tag_len; i++) { - tag[i] = qvirtio_config_readb(v9p->vdev, i + 2); - } - g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len); - g_free(tag); -} - -#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */ - -typedef struct { - QTestState *qts; - QVirtio9P *v9p; - uint16_t tag; - uint64_t t_msg; - uint32_t t_size; - uint64_t r_msg; - /* No r_size, it is hardcoded to P9_MAX_SIZE */ - size_t t_off; - size_t r_off; - uint32_t free_head; -} P9Req; - -static void v9fs_memwrite(P9Req *req, const void *addr, size_t len) -{ - qtest_memwrite(req->qts, req->t_msg + req->t_off, addr, len); - req->t_off += len; -} - -static void v9fs_memskip(P9Req *req, size_t len) -{ - req->r_off += len; -} - -static void v9fs_memread(P9Req *req, void *addr, size_t len) -{ - qtest_memread(req->qts, req->r_msg + req->r_off, addr, len); - req->r_off += len; -} - -static void v9fs_uint16_write(P9Req *req, uint16_t val) -{ - uint16_t le_val = cpu_to_le16(val); - - v9fs_memwrite(req, &le_val, 2); -} - -static void v9fs_uint16_read(P9Req *req, uint16_t *val) -{ - v9fs_memread(req, val, 2); - le16_to_cpus(val); -} - -static void v9fs_uint32_write(P9Req *req, uint32_t val) -{ - uint32_t le_val = cpu_to_le32(val); - - v9fs_memwrite(req, &le_val, 4); -} - -static void v9fs_uint64_write(P9Req *req, uint64_t val) -{ - uint64_t le_val = cpu_to_le64(val); - - v9fs_memwrite(req, &le_val, 8); -} - -static void v9fs_uint32_read(P9Req *req, uint32_t *val) -{ - v9fs_memread(req, val, 4); - le32_to_cpus(val); -} - -/* len[2] string[len] */ -static uint16_t v9fs_string_size(const char *string) -{ - size_t len = strlen(string); - - g_assert_cmpint(len, <=, UINT16_MAX - 2); - - return 2 + len; -} - -static void v9fs_string_write(P9Req *req, const char *string) -{ - int len = strlen(string); - - g_assert_cmpint(len, <=, UINT16_MAX); - - v9fs_uint16_write(req, (uint16_t) len); - v9fs_memwrite(req, string, len); -} - -static void v9fs_string_read(P9Req *req, uint16_t *len, char **string) -{ - uint16_t local_len; - - v9fs_uint16_read(req, &local_len); - if (len) { - *len = local_len; - } - if (string) { - *string = g_malloc(local_len); - v9fs_memread(req, *string, local_len); - } else { - v9fs_memskip(req, local_len); - } -} - - typedef struct { - uint32_t size; - uint8_t id; - uint16_t tag; -} QEMU_PACKED P9Hdr; - -static P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id, - uint16_t tag) -{ - P9Req *req = g_new0(P9Req, 1); - uint32_t total_size = 7; /* 9P header has well-known size of 7 bytes */ - P9Hdr hdr = { - .id = id, - .tag = cpu_to_le16(tag) - }; - - g_assert_cmpint(total_size, <=, UINT32_MAX - size); - total_size += size; - hdr.size = cpu_to_le32(total_size); - - g_assert_cmpint(total_size, <=, P9_MAX_SIZE); - - req->qts = global_qtest; - req->v9p = v9p; - req->t_size = total_size; - req->t_msg = guest_alloc(alloc, req->t_size); - v9fs_memwrite(req, &hdr, 7); - req->tag = tag; - return req; -} - -static void v9fs_req_send(P9Req *req) -{ - QVirtio9P *v9p = req->v9p; - - req->r_msg = guest_alloc(alloc, P9_MAX_SIZE); - req->free_head = qvirtqueue_add(req->qts, v9p->vq, req->t_msg, req->t_size, - false, true); - qvirtqueue_add(req->qts, v9p->vq, req->r_msg, P9_MAX_SIZE, true, false); - qvirtqueue_kick(req->qts, v9p->vdev, v9p->vq, req->free_head); - req->t_off = 0; -} - -static const char *rmessage_name(uint8_t id) -{ - return - id == P9_RLERROR ? "RLERROR" : - id == P9_RVERSION ? "RVERSION" : - id == P9_RATTACH ? "RATTACH" : - id == P9_RWALK ? "RWALK" : - id == P9_RLOPEN ? "RLOPEN" : - id == P9_RWRITE ? "RWRITE" : - id == P9_RFLUSH ? "RFLUSH" : - ""; -} - -static void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len) -{ - QVirtio9P *v9p = req->v9p; - - qvirtio_wait_used_elem(req->qts, v9p->vdev, v9p->vq, req->free_head, len, - QVIRTIO_9P_TIMEOUT_US); -} - -static void v9fs_req_recv(P9Req *req, uint8_t id) -{ - P9Hdr hdr; - - v9fs_memread(req, &hdr, 7); - hdr.size = ldl_le_p(&hdr.size); - hdr.tag = lduw_le_p(&hdr.tag); - - g_assert_cmpint(hdr.size, >=, 7); - g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE); - g_assert_cmpint(hdr.tag, ==, req->tag); - - if (hdr.id != id) { - g_printerr("Received response %d (%s) instead of %d (%s)\n", - hdr.id, rmessage_name(hdr.id), id, rmessage_name(id)); - - if (hdr.id == P9_RLERROR) { - uint32_t err; - v9fs_uint32_read(req, &err); - g_printerr("Rlerror has errno %d (%s)\n", err, strerror(err)); - } - } - g_assert_cmpint(hdr.id, ==, id); -} - -static void v9fs_req_free(P9Req *req) -{ - guest_free(alloc, req->t_msg); - guest_free(alloc, req->r_msg); - g_free(req); -} - -/* size[4] Rlerror tag[2] ecode[4] */ -static void v9fs_rlerror(P9Req *req, uint32_t *err) -{ - v9fs_req_recv(req, P9_RLERROR); - v9fs_uint32_read(req, err); - v9fs_req_free(req); -} - -/* size[4] Tversion tag[2] msize[4] version[s] */ -static P9Req *v9fs_tversion(QVirtio9P *v9p, uint32_t msize, const char *version, - uint16_t tag) -{ - P9Req *req; - uint32_t body_size = 4; - uint16_t string_size = v9fs_string_size(version); - - g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); - body_size += string_size; - req = v9fs_req_init(v9p, body_size, P9_TVERSION, tag); - - v9fs_uint32_write(req, msize); - v9fs_string_write(req, version); - v9fs_req_send(req); - return req; -} - -/* size[4] Rversion tag[2] msize[4] version[s] */ -static void v9fs_rversion(P9Req *req, uint16_t *len, char **version) -{ - uint32_t msize; - - v9fs_req_recv(req, P9_RVERSION); - v9fs_uint32_read(req, &msize); - - g_assert_cmpint(msize, ==, P9_MAX_SIZE); - - if (len || version) { - v9fs_string_read(req, len, version); - } - - v9fs_req_free(req); -} - -/* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */ -static P9Req *v9fs_tattach(QVirtio9P *v9p, uint32_t fid, uint32_t n_uname, - uint16_t tag) -{ - const char *uname = ""; /* ignored by QEMU */ - const char *aname = ""; /* ignored by QEMU */ - P9Req *req = v9fs_req_init(v9p, 4 + 4 + 2 + 2 + 4, P9_TATTACH, tag); - - v9fs_uint32_write(req, fid); - v9fs_uint32_write(req, P9_NOFID); - v9fs_string_write(req, uname); - v9fs_string_write(req, aname); - v9fs_uint32_write(req, n_uname); - v9fs_req_send(req); - return req; -} - -typedef char v9fs_qid[13]; - -/* size[4] Rattach tag[2] qid[13] */ -static void v9fs_rattach(P9Req *req, v9fs_qid *qid) -{ - v9fs_req_recv(req, P9_RATTACH); - if (qid) { - v9fs_memread(req, qid, 13); - } - v9fs_req_free(req); -} - -/* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ -static P9Req *v9fs_twalk(QVirtio9P *v9p, uint32_t fid, uint32_t newfid, - uint16_t nwname, char *const wnames[], uint16_t tag) -{ - P9Req *req; - int i; - uint32_t body_size = 4 + 4 + 2; - - for (i = 0; i < nwname; i++) { - uint16_t wname_size = v9fs_string_size(wnames[i]); - - g_assert_cmpint(body_size, <=, UINT32_MAX - wname_size); - body_size += wname_size; - } - req = v9fs_req_init(v9p, body_size, P9_TWALK, tag); - v9fs_uint32_write(req, fid); - v9fs_uint32_write(req, newfid); - v9fs_uint16_write(req, nwname); - for (i = 0; i < nwname; i++) { - v9fs_string_write(req, wnames[i]); - } - v9fs_req_send(req); - return req; -} - -/* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */ -static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid) -{ - uint16_t local_nwqid; - - v9fs_req_recv(req, P9_RWALK); - v9fs_uint16_read(req, &local_nwqid); - if (nwqid) { - *nwqid = local_nwqid; - } - if (wqid) { - *wqid = g_malloc(local_nwqid * 13); - v9fs_memread(req, *wqid, local_nwqid * 13); - } - v9fs_req_free(req); -} - -/* size[4] Tlopen tag[2] fid[4] flags[4] */ -static P9Req *v9fs_tlopen(QVirtio9P *v9p, uint32_t fid, uint32_t flags, - uint16_t tag) -{ - P9Req *req; - - req = v9fs_req_init(v9p, 4 + 4, P9_TLOPEN, tag); - v9fs_uint32_write(req, fid); - v9fs_uint32_write(req, flags); - v9fs_req_send(req); - return req; -} - -/* size[4] Rlopen tag[2] qid[13] iounit[4] */ -static void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit) -{ - v9fs_req_recv(req, P9_RLOPEN); - if (qid) { - v9fs_memread(req, qid, 13); - } else { - v9fs_memskip(req, 13); - } - if (iounit) { - v9fs_uint32_read(req, iounit); - } - v9fs_req_free(req); -} - -/* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */ -static P9Req *v9fs_twrite(QVirtio9P *v9p, uint32_t fid, uint64_t offset, - uint32_t count, const void *data, uint16_t tag) -{ - P9Req *req; - uint32_t body_size = 4 + 8 + 4; - - g_assert_cmpint(body_size, <=, UINT32_MAX - count); - body_size += count; - req = v9fs_req_init(v9p, body_size, P9_TWRITE, tag); - v9fs_uint32_write(req, fid); - v9fs_uint64_write(req, offset); - v9fs_uint32_write(req, count); - v9fs_memwrite(req, data, count); - v9fs_req_send(req); - return req; -} - -/* size[4] Rwrite tag[2] count[4] */ -static void v9fs_rwrite(P9Req *req, uint32_t *count) -{ - v9fs_req_recv(req, P9_RWRITE); - if (count) { - v9fs_uint32_read(req, count); - } - v9fs_req_free(req); -} - -/* size[4] Tflush tag[2] oldtag[2] */ -static P9Req *v9fs_tflush(QVirtio9P *v9p, uint16_t oldtag, uint16_t tag) -{ - P9Req *req; - - req = v9fs_req_init(v9p, 2, P9_TFLUSH, tag); - v9fs_uint32_write(req, oldtag); - v9fs_req_send(req); - return req; -} - -/* size[4] Rflush tag[2] */ -static void v9fs_rflush(P9Req *req) -{ - v9fs_req_recv(req, P9_RFLUSH); - v9fs_req_free(req); -} - -static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtio9P *v9p = obj; - alloc = t_alloc; - const char *version = "9P2000.L"; - uint16_t server_len; - char *server_version; - P9Req *req; - - req = v9fs_tversion(v9p, P9_MAX_SIZE, version, P9_NOTAG); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rversion(req, &server_len, &server_version); - - g_assert_cmpmem(server_version, server_len, version, strlen(version)); - - g_free(server_version); -} - -static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtio9P *v9p = obj; - alloc = t_alloc; - P9Req *req; - - fs_version(v9p, NULL, t_alloc); - req = v9fs_tattach(v9p, 0, getuid(), 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rattach(req, NULL); -} - -static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtio9P *v9p = obj; - alloc = t_alloc; - char *wnames[P9_MAXWELEM]; - uint16_t nwqid; - v9fs_qid *wqid; - int i; - P9Req *req; - - for (i = 0; i < P9_MAXWELEM; i++) { - wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i); - } - - fs_attach(v9p, NULL, t_alloc); - req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, &nwqid, &wqid); - - g_assert_cmpint(nwqid, ==, P9_MAXWELEM); - - for (i = 0; i < P9_MAXWELEM; i++) { - g_free(wnames[i]); - } - - g_free(wqid); -} - -static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtio9P *v9p = obj; - alloc = t_alloc; - char *const wnames[] = { g_strdup(" /") }; - P9Req *req; - uint32_t err; - - fs_attach(v9p, NULL, t_alloc); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlerror(req, &err); - - g_assert_cmpint(err, ==, ENOENT); - - g_free(wnames[0]); -} - -static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtio9P *v9p = obj; - alloc = t_alloc; - char *const wnames[] = { g_strdup("..") }; - v9fs_qid root_qid, *wqid; - P9Req *req; - - fs_version(v9p, NULL, t_alloc); - req = v9fs_tattach(v9p, 0, getuid(), 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rattach(req, &root_qid); - - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, &wqid); /* We now we'll get one qid */ - - g_assert_cmpmem(&root_qid, 13, wqid[0], 13); - - g_free(wqid); - g_free(wnames[0]); -} - -static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtio9P *v9p = obj; - alloc = t_alloc; - char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) }; - P9Req *req; - - fs_attach(v9p, NULL, t_alloc); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, NULL); - - req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlopen(req, NULL, NULL); - - g_free(wnames[0]); -} - -static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtio9P *v9p = obj; - alloc = t_alloc; - static const uint32_t write_count = P9_MAX_SIZE / 2; - char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) }; - char *buf = g_malloc0(write_count); - uint32_t count; - P9Req *req; - - fs_attach(v9p, NULL, t_alloc); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, NULL); - - req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlopen(req, NULL, NULL); - - req = v9fs_twrite(v9p, 1, 0, write_count, buf, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwrite(req, &count); - g_assert_cmpint(count, ==, write_count); - - g_free(buf); - g_free(wnames[0]); -} - -static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtio9P *v9p = obj; - alloc = t_alloc; - char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; - P9Req *req, *flush_req; - uint32_t reply_len; - uint8_t should_block; - - fs_attach(v9p, NULL, t_alloc); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, NULL); - - req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlopen(req, NULL, NULL); - - /* This will cause the 9p server to try to write data to the backend, - * until the write request gets cancelled. - */ - should_block = 1; - req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0); - - flush_req = v9fs_tflush(v9p, req->tag, 1); - - /* The write request is supposed to be flushed: the server should just - * mark the write request as used and reply to the flush request. - */ - v9fs_req_wait_for_reply(req, &reply_len); - g_assert_cmpint(reply_len, ==, 0); - v9fs_req_free(req); - v9fs_rflush(flush_req); - - g_free(wnames[0]); -} - -static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtio9P *v9p = obj; - alloc = t_alloc; - char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; - P9Req *req, *flush_req; - uint32_t count; - uint8_t should_block; - - fs_attach(v9p, NULL, t_alloc); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, NULL); - - req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlopen(req, NULL, NULL); - - /* This will cause the write request to complete right away, before it - * could be actually cancelled. - */ - should_block = 0; - req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0); - - flush_req = v9fs_tflush(v9p, req->tag, 1); - - /* The write request is supposed to complete. The server should - * reply to the write request and the flush request. - */ - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwrite(req, &count); - g_assert_cmpint(count, ==, sizeof(should_block)); - v9fs_rflush(flush_req); - - g_free(wnames[0]); -} - -static void register_virtio_9p_test(void) -{ - qos_add_test("config", "virtio-9p", pci_config, NULL); - qos_add_test("fs/version/basic", "virtio-9p", fs_version, NULL); - qos_add_test("fs/attach/basic", "virtio-9p", fs_attach, NULL); - qos_add_test("fs/walk/basic", "virtio-9p", fs_walk, NULL); - qos_add_test("fs/walk/no_slash", "virtio-9p", fs_walk_no_slash, - NULL); - qos_add_test("fs/walk/dotdot_from_root", "virtio-9p", - fs_walk_dotdot, NULL); - qos_add_test("fs/lopen/basic", "virtio-9p", fs_lopen, NULL); - qos_add_test("fs/write/basic", "virtio-9p", fs_write, NULL); - qos_add_test("fs/flush/success", "virtio-9p", fs_flush_success, - NULL); - qos_add_test("fs/flush/ignored", "virtio-9p", fs_flush_ignored, - NULL); -} - -libqos_init(register_virtio_9p_test); diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c deleted file mode 100644 index 2a23698211..0000000000 --- a/tests/virtio-blk-test.c +++ /dev/null @@ -1,802 +0,0 @@ -/* - * QTest testcase for VirtIO Block Device - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * Copyright (c) 2014 Marc Marí - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "qemu/bswap.h" -#include "qemu/module.h" -#include "standard-headers/linux/virtio_blk.h" -#include "standard-headers/linux/virtio_pci.h" -#include "libqos/qgraph.h" -#include "libqos/virtio-blk.h" - -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) - -#define TEST_IMAGE_SIZE (64 * 1024 * 1024) -#define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000) -#define PCI_SLOT_HP 0x06 - -typedef struct QVirtioBlkReq { - uint32_t type; - uint32_t ioprio; - uint64_t sector; - char *data; - uint8_t status; -} QVirtioBlkReq; - - -#ifdef HOST_WORDS_BIGENDIAN -const bool host_is_big_endian = true; -#else -const bool host_is_big_endian; /* false */ -#endif - -static void drive_destroy(void *path) -{ - unlink(path); - g_free(path); - qos_invalidate_command_line(); -} - -static char *drive_create(void) -{ - int fd, ret; - char *t_path = g_strdup("/tmp/qtest.XXXXXX"); - - /* Create a temporary raw image */ - fd = mkstemp(t_path); - g_assert_cmpint(fd, >=, 0); - ret = ftruncate(fd, TEST_IMAGE_SIZE); - g_assert_cmpint(ret, ==, 0); - close(fd); - - g_test_queue_destroy(drive_destroy, t_path); - return t_path; -} - -static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req) -{ - if (qvirtio_is_big_endian(d) != host_is_big_endian) { - req->type = bswap32(req->type); - req->ioprio = bswap32(req->ioprio); - req->sector = bswap64(req->sector); - } -} - - -static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d, - struct virtio_blk_discard_write_zeroes *dwz_hdr) -{ - if (qvirtio_is_big_endian(d) != host_is_big_endian) { - dwz_hdr->sector = bswap64(dwz_hdr->sector); - dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors); - dwz_hdr->flags = bswap32(dwz_hdr->flags); - } -} - -static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d, - QVirtioBlkReq *req, uint64_t data_size) -{ - uint64_t addr; - uint8_t status = 0xFF; - - switch (req->type) { - case VIRTIO_BLK_T_IN: - case VIRTIO_BLK_T_OUT: - g_assert_cmpuint(data_size % 512, ==, 0); - break; - case VIRTIO_BLK_T_DISCARD: - case VIRTIO_BLK_T_WRITE_ZEROES: - g_assert_cmpuint(data_size % - sizeof(struct virtio_blk_discard_write_zeroes), ==, 0); - break; - default: - g_assert_cmpuint(data_size, ==, 0); - } - - addr = guest_alloc(alloc, sizeof(*req) + data_size); - - virtio_blk_fix_request(d, req); - - memwrite(addr, req, 16); - memwrite(addr + 16, req->data, data_size); - memwrite(addr + 16 + data_size, &status, sizeof(status)); - - return addr; -} - -/* Returns the request virtqueue so the caller can perform further tests */ -static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc) -{ - QVirtioBlkReq req; - uint64_t req_addr; - uint64_t capacity; - uint64_t features; - uint32_t free_head; - uint8_t status; - char *data; - QTestState *qts = global_qtest; - QVirtQueue *vq; - - features = qvirtio_get_features(dev); - features = features & ~(QVIRTIO_F_BAD_FEATURE | - (1u << VIRTIO_RING_F_INDIRECT_DESC) | - (1u << VIRTIO_RING_F_EVENT_IDX) | - (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(dev, features); - - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - - vq = qvirtqueue_setup(dev, alloc, 0); - - qvirtio_set_driver_ok(dev); - - /* Write and read with 3 descriptor layout */ - /* Write request */ - req.type = VIRTIO_BLK_T_OUT; - req.ioprio = 1; - req.sector = 0; - req.data = g_malloc0(512); - strcpy(req.data, "TEST"); - - req_addr = virtio_blk_request(alloc, dev, &req, 512); - - g_free(req.data); - - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); - qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); - - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); - - guest_free(alloc, req_addr); - - /* Read request */ - req.type = VIRTIO_BLK_T_IN; - req.ioprio = 1; - req.sector = 0; - req.data = g_malloc0(512); - - req_addr = virtio_blk_request(alloc, dev, &req, 512); - - g_free(req.data); - - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); - qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); - - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); - - data = g_malloc0(512); - memread(req_addr + 16, data, 512); - g_assert_cmpstr(data, ==, "TEST"); - g_free(data); - - guest_free(alloc, req_addr); - - if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) { - struct virtio_blk_discard_write_zeroes dwz_hdr; - void *expected; - - /* - * WRITE_ZEROES request on the same sector of previous test where - * we wrote "TEST". - */ - req.type = VIRTIO_BLK_T_WRITE_ZEROES; - req.data = (char *) &dwz_hdr; - dwz_hdr.sector = 0; - dwz_hdr.num_sectors = 1; - dwz_hdr.flags = 0; - - virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); - - req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); - - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true); - qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, - false); - - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 16 + sizeof(dwz_hdr)); - g_assert_cmpint(status, ==, 0); - - guest_free(alloc, req_addr); - - /* Read request to check if the sector contains all zeroes */ - req.type = VIRTIO_BLK_T_IN; - req.ioprio = 1; - req.sector = 0; - req.data = g_malloc0(512); - - req_addr = virtio_blk_request(alloc, dev, &req, 512); - - g_free(req.data); - - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); - qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); - - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); - - data = g_malloc(512); - expected = g_malloc0(512); - memread(req_addr + 16, data, 512); - g_assert_cmpmem(data, 512, expected, 512); - g_free(expected); - g_free(data); - - guest_free(alloc, req_addr); - } - - if (features & (1u << VIRTIO_BLK_F_DISCARD)) { - struct virtio_blk_discard_write_zeroes dwz_hdr; - - req.type = VIRTIO_BLK_T_DISCARD; - req.data = (char *) &dwz_hdr; - dwz_hdr.sector = 0; - dwz_hdr.num_sectors = 1; - dwz_hdr.flags = 0; - - virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); - - req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); - - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true); - qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false); - - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 16 + sizeof(dwz_hdr)); - g_assert_cmpint(status, ==, 0); - - guest_free(alloc, req_addr); - } - - if (features & (1u << VIRTIO_F_ANY_LAYOUT)) { - /* Write and read with 2 descriptor layout */ - /* Write request */ - req.type = VIRTIO_BLK_T_OUT; - req.ioprio = 1; - req.sector = 1; - req.data = g_malloc0(512); - strcpy(req.data, "TEST"); - - req_addr = virtio_blk_request(alloc, dev, &req, 512); - - g_free(req.data); - - free_head = qvirtqueue_add(qts, vq, req_addr, 528, false, true); - qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); - - guest_free(alloc, req_addr); - - /* Read request */ - req.type = VIRTIO_BLK_T_IN; - req.ioprio = 1; - req.sector = 1; - req.data = g_malloc0(512); - - req_addr = virtio_blk_request(alloc, dev, &req, 512); - - g_free(req.data); - - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, 513, true, false); - - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); - - data = g_malloc0(512); - memread(req_addr + 16, data, 512); - g_assert_cmpstr(data, ==, "TEST"); - g_free(data); - - guest_free(alloc, req_addr); - } - - return vq; -} - -static void basic(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtioBlk *blk_if = obj; - QVirtQueue *vq; - - vq = test_basic(blk_if->vdev, t_alloc); - qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc); - -} - -static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc) -{ - QVirtQueue *vq; - QVirtioBlk *blk_if = obj; - QVirtioDevice *dev = blk_if->vdev; - QVirtioBlkReq req; - QVRingIndirectDesc *indirect; - uint64_t req_addr; - uint64_t capacity; - uint64_t features; - uint32_t free_head; - uint8_t status; - char *data; - QTestState *qts = global_qtest; - - features = qvirtio_get_features(dev); - g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); - features = features & ~(QVIRTIO_F_BAD_FEATURE | - (1u << VIRTIO_RING_F_EVENT_IDX) | - (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(dev, features); - - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - - vq = qvirtqueue_setup(dev, t_alloc, 0); - qvirtio_set_driver_ok(dev); - - /* Write request */ - req.type = VIRTIO_BLK_T_OUT; - req.ioprio = 1; - req.sector = 0; - req.data = g_malloc0(512); - strcpy(req.data, "TEST"); - - req_addr = virtio_blk_request(t_alloc, dev, &req, 512); - - g_free(req.data); - - indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2); - qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false); - qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true); - free_head = qvirtqueue_add_indirect(qts, vq, indirect); - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); - - g_free(indirect); - guest_free(t_alloc, req_addr); - - /* Read request */ - req.type = VIRTIO_BLK_T_IN; - req.ioprio = 1; - req.sector = 0; - req.data = g_malloc0(512); - strcpy(req.data, "TEST"); - - req_addr = virtio_blk_request(t_alloc, dev, &req, 512); - - g_free(req.data); - - indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2); - qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false); - qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true); - free_head = qvirtqueue_add_indirect(qts, vq, indirect); - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); - - data = g_malloc0(512); - memread(req_addr + 16, data, 512); - g_assert_cmpstr(data, ==, "TEST"); - g_free(data); - - g_free(indirect); - guest_free(t_alloc, req_addr); - qvirtqueue_cleanup(dev->bus, vq, t_alloc); -} - -static void config(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtioBlk *blk_if = obj; - QVirtioDevice *dev = blk_if->vdev; - int n_size = TEST_IMAGE_SIZE / 2; - uint64_t features; - uint64_t capacity; - - features = qvirtio_get_features(dev); - features = features & ~(QVIRTIO_F_BAD_FEATURE | - (1u << VIRTIO_RING_F_INDIRECT_DESC) | - (1u << VIRTIO_RING_F_EVENT_IDX) | - (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(dev, features); - - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - - qvirtio_set_driver_ok(dev); - - qmp_discard_response("{ 'execute': 'block_resize', " - " 'arguments': { 'device': 'drive0', " - " 'size': %d } }", n_size); - qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US); - - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, n_size / 512); -} - -static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc) -{ - QVirtQueue *vq; - QVirtioBlkPCI *blk = obj; - QVirtioPCIDevice *pdev = &blk->pci_vdev; - QVirtioDevice *dev = &pdev->vdev; - QVirtioBlkReq req; - int n_size = TEST_IMAGE_SIZE / 2; - uint64_t req_addr; - uint64_t capacity; - uint64_t features; - uint32_t free_head; - uint8_t status; - char *data; - QOSGraphObject *blk_object = obj; - QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device"); - QTestState *qts = global_qtest; - - if (qpci_check_buggy_msi(pci_dev)) { - return; - } - - qpci_msix_enable(pdev->pdev); - qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); - - features = qvirtio_get_features(dev); - features = features & ~(QVIRTIO_F_BAD_FEATURE | - (1u << VIRTIO_RING_F_INDIRECT_DESC) | - (1u << VIRTIO_RING_F_EVENT_IDX) | - (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(dev, features); - - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - - vq = qvirtqueue_setup(dev, t_alloc, 0); - qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); - - qvirtio_set_driver_ok(dev); - - qmp_discard_response("{ 'execute': 'block_resize', " - " 'arguments': { 'device': 'drive0', " - " 'size': %d } }", n_size); - - qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US); - - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, n_size / 512); - - /* Write request */ - req.type = VIRTIO_BLK_T_OUT; - req.ioprio = 1; - req.sector = 0; - req.data = g_malloc0(512); - strcpy(req.data, "TEST"); - - req_addr = virtio_blk_request(t_alloc, dev, &req, 512); - - g_free(req.data); - - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); - qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); - - guest_free(t_alloc, req_addr); - - /* Read request */ - req.type = VIRTIO_BLK_T_IN; - req.ioprio = 1; - req.sector = 0; - req.data = g_malloc0(512); - - req_addr = virtio_blk_request(t_alloc, dev, &req, 512); - - g_free(req.data); - - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); - qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); - - qvirtqueue_kick(qts, dev, vq, free_head); - - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); - - data = g_malloc0(512); - memread(req_addr + 16, data, 512); - g_assert_cmpstr(data, ==, "TEST"); - g_free(data); - - guest_free(t_alloc, req_addr); - - /* End test */ - qpci_msix_disable(pdev->pdev); - qvirtqueue_cleanup(dev->bus, vq, t_alloc); -} - -static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc) -{ - QVirtQueue *vq; - QVirtioBlkPCI *blk = obj; - QVirtioPCIDevice *pdev = &blk->pci_vdev; - QVirtioDevice *dev = &pdev->vdev; - QVirtioBlkReq req; - uint64_t req_addr; - uint64_t capacity; - uint64_t features; - uint32_t free_head; - uint32_t write_head; - uint32_t desc_idx; - uint8_t status; - char *data; - QOSGraphObject *blk_object = obj; - QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device"); - QTestState *qts = global_qtest; - - if (qpci_check_buggy_msi(pci_dev)) { - return; - } - - qpci_msix_enable(pdev->pdev); - qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); - - features = qvirtio_get_features(dev); - features = features & ~(QVIRTIO_F_BAD_FEATURE | - (1u << VIRTIO_RING_F_INDIRECT_DESC) | - (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | - (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(dev, features); - - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - - vq = qvirtqueue_setup(dev, t_alloc, 0); - qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); - - qvirtio_set_driver_ok(dev); - - /* Write request */ - req.type = VIRTIO_BLK_T_OUT; - req.ioprio = 1; - req.sector = 0; - req.data = g_malloc0(512); - strcpy(req.data, "TEST"); - - req_addr = virtio_blk_request(t_alloc, dev, &req, 512); - - g_free(req.data); - - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); - qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - - /* Write request */ - req.type = VIRTIO_BLK_T_OUT; - req.ioprio = 1; - req.sector = 1; - req.data = g_malloc0(512); - strcpy(req.data, "TEST"); - - req_addr = virtio_blk_request(t_alloc, dev, &req, 512); - - g_free(req.data); - - /* Notify after processing the third request */ - qvirtqueue_set_used_event(qts, vq, 2); - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); - qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(qts, dev, vq, free_head); - write_head = free_head; - - /* No notification expected */ - status = qvirtio_wait_status_byte_no_isr(qts, dev, - vq, req_addr + 528, - QVIRTIO_BLK_TIMEOUT_US); - g_assert_cmpint(status, ==, 0); - - guest_free(t_alloc, req_addr); - - /* Read request */ - req.type = VIRTIO_BLK_T_IN; - req.ioprio = 1; - req.sector = 1; - req.data = g_malloc0(512); - - req_addr = virtio_blk_request(t_alloc, dev, &req, 512); - - g_free(req.data); - - free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); - qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); - qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); - - qvirtqueue_kick(qts, dev, vq, free_head); - - /* We get just one notification for both requests */ - qvirtio_wait_used_elem(qts, dev, vq, write_head, NULL, - QVIRTIO_BLK_TIMEOUT_US); - g_assert(qvirtqueue_get_buf(qts, vq, &desc_idx, NULL)); - g_assert_cmpint(desc_idx, ==, free_head); - - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); - - data = g_malloc0(512); - memread(req_addr + 16, data, 512); - g_assert_cmpstr(data, ==, "TEST"); - g_free(data); - - guest_free(t_alloc, req_addr); - - /* End test */ - qpci_msix_disable(pdev->pdev); - - qvirtqueue_cleanup(dev->bus, vq, t_alloc); -} - -static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtioPCIDevice *dev1 = obj; - QVirtioPCIDevice *dev; - QTestState *qts = dev1->pdev->bus->qts; - - /* plug secondary disk */ - qtest_qmp_device_add(qts, "virtio-blk-pci", "drv1", - "{'addr': %s, 'drive': 'drive1'}", - stringify(PCI_SLOT_HP) ".0"); - - dev = virtio_pci_new(dev1->pdev->bus, - &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) }); - g_assert_nonnull(dev); - g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); - qvirtio_pci_device_disable(dev); - qos_object_destroy((QOSGraphObject *)dev); - - /* unplug secondary disk */ - qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP); -} - -/* - * Check that setting the vring addr on a non-existent virtqueue does - * not crash. - */ -static void test_nonexistent_virtqueue(void *obj, void *data, - QGuestAllocator *t_alloc) -{ - QVirtioBlkPCI *blk = obj; - QVirtioPCIDevice *pdev = &blk->pci_vdev; - QPCIBar bar0; - QPCIDevice *dev; - - dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0)); - g_assert(dev != NULL); - qpci_device_enable(dev); - - bar0 = qpci_iomap(dev, 0, NULL); - - qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2); - qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1); - - - g_free(dev); -} - -static void resize(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtioBlk *blk_if = obj; - QVirtioDevice *dev = blk_if->vdev; - int n_size = TEST_IMAGE_SIZE / 2; - uint64_t capacity; - QVirtQueue *vq; - QTestState *qts = global_qtest; - - vq = test_basic(dev, t_alloc); - - qmp_discard_response("{ 'execute': 'block_resize', " - " 'arguments': { 'device': 'drive0', " - " 'size': %d } }", n_size); - - qvirtio_wait_queue_isr(qts, dev, vq, QVIRTIO_BLK_TIMEOUT_US); - - capacity = qvirtio_config_readq(dev, 0); - g_assert_cmpint(capacity, ==, n_size / 512); - - qvirtqueue_cleanup(dev->bus, vq, t_alloc); - -} - -static void *virtio_blk_test_setup(GString *cmd_line, void *arg) -{ - char *tmp_path = drive_create(); - - g_string_append_printf(cmd_line, - " -drive if=none,id=drive0,file=%s," - "format=raw,auto-read-only=off " - "-drive if=none,id=drive1,file=null-co://," - "file.read-zeroes=on,format=raw ", - tmp_path); - - return arg; -} - -static void register_virtio_blk_test(void) -{ - QOSGraphTestOptions opts = { - .before = virtio_blk_test_setup, - }; - - qos_add_test("indirect", "virtio-blk", indirect, &opts); - qos_add_test("config", "virtio-blk", config, &opts); - qos_add_test("basic", "virtio-blk", basic, &opts); - qos_add_test("resize", "virtio-blk", resize, &opts); - - /* tests just for virtio-blk-pci */ - qos_add_test("msix", "virtio-blk-pci", msix, &opts); - qos_add_test("idx", "virtio-blk-pci", idx, &opts); - qos_add_test("nxvirtq", "virtio-blk-pci", - test_nonexistent_virtqueue, &opts); - qos_add_test("hotplug", "virtio-blk-pci", pci_hotplug, &opts); -} - -libqos_init(register_virtio_blk_test); diff --git a/tests/virtio-ccw-test.c b/tests/virtio-ccw-test.c deleted file mode 100644 index d05236407b..0000000000 --- a/tests/virtio-ccw-test.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * QTest testcase for VirtIO CCW - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * Copyright (c) 2018 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -/* Until we have a full libqos implementation of virtio-ccw (which requires - * also to add support for I/O channels to qtest), we can only do simple - * tests that initialize the devices. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "libqos/virtio.h" - -static void virtio_balloon_nop(void) -{ - global_qtest = qtest_initf("-device virtio-balloon-ccw"); - qtest_end(); -} - -static void virtconsole_nop(void) -{ - global_qtest = qtest_initf("-device virtio-serial-ccw,id=vser0 " - "-device virtconsole,bus=vser0.0"); - qtest_end(); -} - -static void virtserialport_nop(void) -{ - global_qtest = qtest_initf("-device virtio-serial-ccw,id=vser0 " - "-device virtserialport,bus=vser0.0"); - qtest_end(); -} - -static void virtio_serial_nop(void) -{ - global_qtest = qtest_initf("-device virtio-serial-ccw"); - qtest_end(); -} - -static void virtio_serial_hotplug(void) -{ - QTestState *qts = qtest_initf("-device virtio-serial-ccw"); - - qtest_qmp_device_add(qts, "virtserialport", "hp-port", "{}"); - qtest_qmp_device_del(qts, "hp-port"); - - qtest_quit(qts); -} - -static void virtio_blk_nop(void) -{ - global_qtest = qtest_initf("-drive if=none,id=drv0,file=null-co://," - "file.read-zeroes=on,format=raw " - "-device virtio-blk-ccw,drive=drv0"); - qtest_end(); -} - -static void virtio_net_nop(void) -{ - global_qtest = qtest_initf("-device virtio-net-ccw"); - qtest_end(); -} - -static void virtio_rng_nop(void) -{ - global_qtest = qtest_initf("-device virtio-rng-ccw"); - qtest_end(); -} - -static void virtio_scsi_nop(void) -{ - global_qtest = qtest_initf("-device virtio-scsi-ccw"); - qtest_end(); -} - -static void virtio_scsi_hotplug(void) -{ - QTestState *s = qtest_initf("-drive if=none,id=drv0,file=null-co://," - "file.read-zeroes=on,format=raw " - "-drive if=none,id=drv1,file=null-co://," - "file.read-zeroes=on,format=raw " - "-device virtio-scsi-ccw " - "-device scsi-hd,drive=drv0"); - qtest_qmp_device_add(s, "scsi-hd", "scsihd", "{'drive': 'drv1'}"); - qtest_qmp_device_del(s, "scsihd"); - - qtest_quit(s); -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/balloon/nop", virtio_balloon_nop); - qtest_add_func("/virtio/console/nop", virtconsole_nop); - qtest_add_func("/virtio/serialport/nop", virtserialport_nop); - qtest_add_func("/virtio/serial/nop", virtio_serial_nop); - qtest_add_func("/virtio/serial/hotplug", virtio_serial_hotplug); - qtest_add_func("/virtio/block/nop", virtio_blk_nop); - qtest_add_func("/virtio/net/nop", virtio_net_nop); - qtest_add_func("/virtio/rng/nop", virtio_rng_nop); - qtest_add_func("/virtio/scsi/nop", virtio_scsi_nop); - qtest_add_func("/virtio/scsi/hotplug", virtio_scsi_hotplug); - - ret = g_test_run(); - - return ret; -} diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c deleted file mode 100644 index a08e2ffe12..0000000000 --- a/tests/virtio-net-test.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * QTest testcase for VirtIO NIC - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "libqtest-single.h" -#include "qemu/iov.h" -#include "qemu/module.h" -#include "qapi/qmp/qdict.h" -#include "hw/virtio/virtio-net.h" -#include "libqos/qgraph.h" -#include "libqos/virtio-net.h" - -#ifndef ETH_P_RARP -#define ETH_P_RARP 0x8035 -#endif - -#define PCI_SLOT_HP 0x06 -#define PCI_SLOT 0x04 - -#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000) -#define VNET_HDR_SIZE sizeof(struct virtio_net_hdr_mrg_rxbuf) - -#ifndef _WIN32 - -static void rx_test(QVirtioDevice *dev, - QGuestAllocator *alloc, QVirtQueue *vq, - int socket) -{ - QTestState *qts = global_qtest; - uint64_t req_addr; - uint32_t free_head; - char test[] = "TEST"; - char buffer[64]; - int len = htonl(sizeof(test)); - struct iovec iov[] = { - { - .iov_base = &len, - .iov_len = sizeof(len), - }, { - .iov_base = test, - .iov_len = sizeof(test), - }, - }; - int ret; - - req_addr = guest_alloc(alloc, 64); - - free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false); - qvirtqueue_kick(qts, dev, vq, free_head); - - ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); - g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_NET_TIMEOUT_US); - memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); - g_assert_cmpstr(buffer, ==, "TEST"); - - guest_free(alloc, req_addr); -} - -static void tx_test(QVirtioDevice *dev, - QGuestAllocator *alloc, QVirtQueue *vq, - int socket) -{ - QTestState *qts = global_qtest; - uint64_t req_addr; - uint32_t free_head; - uint32_t len; - char buffer[64]; - int ret; - - req_addr = guest_alloc(alloc, 64); - memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4); - - free_head = qvirtqueue_add(qts, vq, req_addr, 64, false, false); - qvirtqueue_kick(qts, dev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_NET_TIMEOUT_US); - guest_free(alloc, req_addr); - - ret = qemu_recv(socket, &len, sizeof(len), 0); - g_assert_cmpint(ret, ==, sizeof(len)); - len = ntohl(len); - - ret = qemu_recv(socket, buffer, len, 0); - g_assert_cmpstr(buffer, ==, "TEST"); -} - -static void rx_stop_cont_test(QVirtioDevice *dev, - QGuestAllocator *alloc, QVirtQueue *vq, - int socket) -{ - QTestState *qts = global_qtest; - uint64_t req_addr; - uint32_t free_head; - char test[] = "TEST"; - char buffer[64]; - int len = htonl(sizeof(test)); - QDict *rsp; - struct iovec iov[] = { - { - .iov_base = &len, - .iov_len = sizeof(len), - }, { - .iov_base = test, - .iov_len = sizeof(test), - }, - }; - int ret; - - req_addr = guest_alloc(alloc, 64); - - free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false); - qvirtqueue_kick(qts, dev, vq, free_head); - - rsp = qmp("{ 'execute' : 'stop'}"); - qobject_unref(rsp); - - ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); - g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); - - /* We could check the status, but this command is more importantly to - * ensure the packet data gets queued in QEMU, before we do 'cont'. - */ - rsp = qmp("{ 'execute' : 'query-status'}"); - qobject_unref(rsp); - rsp = qmp("{ 'execute' : 'cont'}"); - qobject_unref(rsp); - - qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, - QVIRTIO_NET_TIMEOUT_US); - memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); - g_assert_cmpstr(buffer, ==, "TEST"); - - guest_free(alloc, req_addr); -} - -static void send_recv_test(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtioNet *net_if = obj; - QVirtioDevice *dev = net_if->vdev; - QVirtQueue *rx = net_if->queues[0]; - QVirtQueue *tx = net_if->queues[1]; - int *sv = data; - - rx_test(dev, t_alloc, rx, sv[0]); - tx_test(dev, t_alloc, tx, sv[0]); -} - -static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtioNet *net_if = obj; - QVirtioDevice *dev = net_if->vdev; - QVirtQueue *rx = net_if->queues[0]; - int *sv = data; - - rx_stop_cont_test(dev, t_alloc, rx, sv[0]); -} - -#endif - -static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtioPCIDevice *dev = obj; - QTestState *qts = dev->pdev->bus->qts; - const char *arch = qtest_get_arch(); - - qtest_qmp_device_add(qts, "virtio-net-pci", "net1", - "{'addr': %s}", stringify(PCI_SLOT_HP)); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qpci_unplug_acpi_device_test(qts, "net1", PCI_SLOT_HP); - } -} - -static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc) -{ - int *sv = data; - char buffer[60]; - int len; - QDict *rsp; - int ret; - uint16_t *proto = (uint16_t *)&buffer[12]; - size_t total_received = 0; - uint64_t start, now, last_rxt, deadline; - - /* Send a set of packets over a few second period */ - rsp = qmp("{ 'execute' : 'announce-self', " - " 'arguments': {" - " 'initial': 20, 'max': 100," - " 'rounds': 300, 'step': 10, 'id': 'bob' } }"); - assert(!qdict_haskey(rsp, "error")); - qobject_unref(rsp); - - /* Catch the first packet and make sure it's a RARP */ - ret = qemu_recv(sv[0], &len, sizeof(len), 0); - g_assert_cmpint(ret, ==, sizeof(len)); - len = ntohl(len); - - ret = qemu_recv(sv[0], buffer, len, 0); - g_assert_cmpint(*proto, ==, htons(ETH_P_RARP)); - - /* - * Stop the announcment by settings rounds to 0 on the - * existing timer. - */ - rsp = qmp("{ 'execute' : 'announce-self', " - " 'arguments': {" - " 'initial': 20, 'max': 100," - " 'rounds': 0, 'step': 10, 'id': 'bob' } }"); - assert(!qdict_haskey(rsp, "error")); - qobject_unref(rsp); - - /* Now make sure the packets stop */ - - /* Times are in us */ - start = g_get_monotonic_time(); - /* 30 packets, max gap 100ms, * 4 for wiggle */ - deadline = start + 1000 * (100 * 30 * 4); - last_rxt = start; - - while (true) { - int saved_err; - ret = qemu_recv(sv[0], buffer, 60, MSG_DONTWAIT); - saved_err = errno; - now = g_get_monotonic_time(); - g_assert_cmpint(now, <, deadline); - - if (ret >= 0) { - if (ret) { - last_rxt = now; - } - total_received += ret; - - /* Check it's not spewing loads */ - g_assert_cmpint(total_received, <, 60 * 30 * 2); - } else { - g_assert_cmpint(saved_err, ==, EAGAIN); - - /* 400ms, i.e. 4 worst case gaps */ - if ((now - last_rxt) > (1000 * 100 * 4)) { - /* Nothings arrived for a while - must have stopped */ - break; - }; - - /* 100ms */ - g_usleep(1000 * 100); - } - }; -} - -static void virtio_net_test_cleanup(void *sockets) -{ - int *sv = sockets; - - close(sv[0]); - qos_invalidate_command_line(); - close(sv[1]); - g_free(sv); -} - -static void *virtio_net_test_setup(GString *cmd_line, void *arg) -{ - int ret; - int *sv = g_new(int, 2); - - ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); - g_assert_cmpint(ret, !=, -1); - - g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sv[1]); - - g_test_queue_destroy(virtio_net_test_cleanup, sv); - return sv; -} - -static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc) -{ - QVirtioNet *dev = obj; - QVirtQueue *vq = dev->queues[1]; - uint64_t req_addr; - uint32_t free_head; - size_t alloc_size = (size_t)data / 64; - QTestState *qts = global_qtest; - int i; - - /* Bypass the limitation by pointing several descriptors to a single - * smaller area */ - req_addr = guest_alloc(t_alloc, alloc_size); - free_head = qvirtqueue_add(qts, vq, req_addr, alloc_size, false, true); - - for (i = 0; i < 64; i++) { - qvirtqueue_add(qts, vq, req_addr, alloc_size, false, i != 63); - } - qvirtqueue_kick(qts, dev->vdev, vq, free_head); - - qvirtio_wait_used_elem(qts, dev->vdev, vq, free_head, NULL, - QVIRTIO_NET_TIMEOUT_US); - guest_free(t_alloc, req_addr); -} - -static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg) -{ - g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 "); - return arg; -} - -static void register_virtio_net_test(void) -{ - QOSGraphTestOptions opts = { - .before = virtio_net_test_setup, - }; - - qos_add_test("hotplug", "virtio-pci", hotplug, &opts); -#ifndef _WIN32 - qos_add_test("basic", "virtio-net", send_recv_test, &opts); - qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts); -#endif - qos_add_test("announce-self", "virtio-net", announce_self, &opts); - - /* These tests do not need a loopback backend. */ - opts.before = virtio_net_test_setup_nosocket; - opts.arg = (gpointer)UINT_MAX; - qos_add_test("large_tx/uint_max", "virtio-net", large_tx, &opts); - opts.arg = (gpointer)NET_BUFSIZE; - qos_add_test("large_tx/net_bufsize", "virtio-net", large_tx, &opts); -} - -libqos_init(register_virtio_net_test); diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c deleted file mode 100644 index 092ba13068..0000000000 --- a/tests/virtio-rng-test.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * QTest testcase for VirtIO RNG - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/virtio-rng.h" - -#define PCI_SLOT_HP 0x06 - -static void rng_hotplug(void *obj, void *data, QGuestAllocator *alloc) -{ - QVirtioPCIDevice *dev = obj; - QTestState *qts = dev->pdev->bus->qts; - - const char *arch = qtest_get_arch(); - - qtest_qmp_device_add(qts, "virtio-rng-pci", "rng1", - "{'addr': %s}", stringify(PCI_SLOT_HP)); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qpci_unplug_acpi_device_test(qts, "rng1", PCI_SLOT_HP); - } -} - -static void register_virtio_rng_test(void) -{ - qos_add_test("hotplug", "virtio-rng-pci", rng_hotplug, NULL); -} - -libqos_init(register_virtio_rng_test); diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c deleted file mode 100644 index 0415e75876..0000000000 --- a/tests/virtio-scsi-test.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * QTest testcase for VirtIO SCSI - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * Copyright (c) 2015 Red Hat Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "qemu/module.h" -#include "scsi/constants.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" -#include "libqos/virtio.h" -#include "libqos/virtio-pci.h" -#include "standard-headers/linux/virtio_ids.h" -#include "standard-headers/linux/virtio_pci.h" -#include "standard-headers/linux/virtio_scsi.h" -#include "libqos/virtio-scsi.h" -#include "libqos/qgraph.h" - -#define PCI_SLOT 0x02 -#define PCI_FN 0x00 -#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000) - -#define MAX_NUM_QUEUES 64 - -typedef struct { - QVirtioDevice *dev; - int num_queues; - QVirtQueue *vq[MAX_NUM_QUEUES + 2]; -} QVirtioSCSIQueues; - -static QGuestAllocator *alloc; - -static void qvirtio_scsi_pci_free(QVirtioSCSIQueues *vs) -{ - int i; - - for (i = 0; i < vs->num_queues + 2; i++) { - qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], alloc); - } - g_free(vs); -} - -static uint64_t qvirtio_scsi_alloc(QVirtioSCSIQueues *vs, size_t alloc_size, - const void *data) -{ - uint64_t addr; - - addr = guest_alloc(alloc, alloc_size); - if (data) { - memwrite(addr, data, alloc_size); - } - - return addr; -} - -static uint8_t virtio_scsi_do_command(QVirtioSCSIQueues *vs, - const uint8_t *cdb, - const uint8_t *data_in, - size_t data_in_len, - uint8_t *data_out, size_t data_out_len, - struct virtio_scsi_cmd_resp *resp_out) -{ - QVirtQueue *vq; - struct virtio_scsi_cmd_req req = { { 0 } }; - struct virtio_scsi_cmd_resp resp = { .response = 0xff, .status = 0xff }; - uint64_t req_addr, resp_addr, data_in_addr = 0, data_out_addr = 0; - uint8_t response; - uint32_t free_head; - QTestState *qts = global_qtest; - - vq = vs->vq[2]; - - req.lun[0] = 1; /* Select LUN */ - req.lun[1] = 1; /* Select target 1 */ - memcpy(req.cdb, cdb, VIRTIO_SCSI_CDB_SIZE); - - /* XXX: Fix endian if any multi-byte field in req/resp is used */ - - /* Add request header */ - req_addr = qvirtio_scsi_alloc(vs, sizeof(req), &req); - free_head = qvirtqueue_add(qts, vq, req_addr, sizeof(req), false, true); - - if (data_out_len) { - data_out_addr = qvirtio_scsi_alloc(vs, data_out_len, data_out); - qvirtqueue_add(qts, vq, data_out_addr, data_out_len, false, true); - } - - /* Add response header */ - resp_addr = qvirtio_scsi_alloc(vs, sizeof(resp), &resp); - qvirtqueue_add(qts, vq, resp_addr, sizeof(resp), true, !!data_in_len); - - if (data_in_len) { - data_in_addr = qvirtio_scsi_alloc(vs, data_in_len, data_in); - qvirtqueue_add(qts, vq, data_in_addr, data_in_len, true, false); - } - - qvirtqueue_kick(qts, vs->dev, vq, free_head); - qvirtio_wait_used_elem(qts, vs->dev, vq, free_head, NULL, - QVIRTIO_SCSI_TIMEOUT_US); - - response = readb(resp_addr + - offsetof(struct virtio_scsi_cmd_resp, response)); - - if (resp_out) { - memread(resp_addr, resp_out, sizeof(*resp_out)); - } - - guest_free(alloc, req_addr); - guest_free(alloc, resp_addr); - guest_free(alloc, data_in_addr); - guest_free(alloc, data_out_addr); - return response; -} - -static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev) -{ - QVirtioSCSIQueues *vs; - const uint8_t test_unit_ready_cdb[VIRTIO_SCSI_CDB_SIZE] = {}; - struct virtio_scsi_cmd_resp resp; - uint64_t features; - int i; - - vs = g_new0(QVirtioSCSIQueues, 1); - vs->dev = dev; - - features = qvirtio_get_features(dev); - features &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX)); - qvirtio_set_features(dev, features); - - vs->num_queues = qvirtio_config_readl(dev, 0); - - g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); - - for (i = 0; i < vs->num_queues + 2; i++) { - vs->vq[i] = qvirtqueue_setup(dev, alloc, i); - } - - qvirtio_set_driver_ok(dev); - - /* Clear the POWER ON OCCURRED unit attention */ - g_assert_cmpint(virtio_scsi_do_command(vs, test_unit_ready_cdb, - NULL, 0, NULL, 0, &resp), - ==, 0); - g_assert_cmpint(resp.status, ==, CHECK_CONDITION); - g_assert_cmpint(resp.sense[0], ==, 0x70); /* Fixed format sense buffer */ - g_assert_cmpint(resp.sense[2], ==, UNIT_ATTENTION); - g_assert_cmpint(resp.sense[12], ==, 0x29); /* POWER ON */ - g_assert_cmpint(resp.sense[13], ==, 0x00); - - return vs; -} - -static void hotplug(void *obj, void *data, QGuestAllocator *alloc) -{ - QTestState *qts = global_qtest; - - qtest_qmp_device_add(qts, "scsi-hd", "scsihd", "{'drive': 'drv1'}"); - qtest_qmp_device_del(qts, "scsihd"); -} - -/* Test WRITE SAME with the lba not aligned */ -static void test_unaligned_write_same(void *obj, void *data, - QGuestAllocator *t_alloc) -{ - QVirtioSCSI *scsi = obj; - QVirtioSCSIQueues *vs; - uint8_t buf1[512] = { 0 }; - uint8_t buf2[512] = { 1 }; - const uint8_t write_same_cdb_1[VIRTIO_SCSI_CDB_SIZE] = { - 0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00 - }; - const uint8_t write_same_cdb_2[VIRTIO_SCSI_CDB_SIZE] = { - 0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 - }; - const uint8_t write_same_cdb_ndob[VIRTIO_SCSI_CDB_SIZE] = { - 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 - }; - - alloc = t_alloc; - vs = qvirtio_scsi_init(scsi->vdev); - - g_assert_cmphex(0, ==, - virtio_scsi_do_command(vs, write_same_cdb_1, NULL, 0, buf1, 512, - NULL)); - - g_assert_cmphex(0, ==, - virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, - NULL)); - - g_assert_cmphex(0, ==, - virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, - NULL)); - - qvirtio_scsi_pci_free(vs); -} - -static void test_iothread_attach_node(void *obj, void *data, - QGuestAllocator *t_alloc) -{ - QVirtioSCSIPCI *scsi_pci = obj; - QVirtioSCSI *scsi = &scsi_pci->scsi; - QVirtioSCSIQueues *vs; - char tmp_path[] = "/tmp/qtest.XXXXXX"; - int fd; - int ret; - - uint8_t buf[512] = { 0 }; - const uint8_t write_cdb[VIRTIO_SCSI_CDB_SIZE] = { - /* WRITE(10) to LBA 0, transfer length 1 */ - 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 - }; - - alloc = t_alloc; - vs = qvirtio_scsi_init(scsi->vdev); - - /* Create a temporary qcow2 overlay*/ - fd = mkstemp(tmp_path); - g_assert(fd >= 0); - close(fd); - - if (!have_qemu_img()) { - g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " - "skipping snapshot test"); - goto fail; - } - - mkqcow2(tmp_path, 64); - - /* Attach the overlay to the null0 node */ - qtest_qmp_assert_success(scsi_pci->pci_vdev.pdev->bus->qts, - "{'execute': 'blockdev-add', 'arguments': {" - " 'driver': 'qcow2', 'node-name': 'overlay'," - " 'backing': 'null0', 'file': {" - " 'driver': 'file', 'filename': %s}}}", - tmp_path); - - /* Send a request to see if the AioContext is still right */ - ret = virtio_scsi_do_command(vs, write_cdb, NULL, 0, buf, 512, NULL); - g_assert_cmphex(ret, ==, 0); - -fail: - qvirtio_scsi_pci_free(vs); - unlink(tmp_path); -} - -static void *virtio_scsi_hotplug_setup(GString *cmd_line, void *arg) -{ - g_string_append(cmd_line, - " -drive id=drv1,if=none,file=null-co://," - "file.read-zeroes=on,format=raw"); - return arg; -} - -static void *virtio_scsi_setup(GString *cmd_line, void *arg) -{ - g_string_append(cmd_line, - " -drive file=blkdebug::null-co://," - "file.image.read-zeroes=on," - "if=none,id=dr1,format=raw,file.align=4k " - "-device scsi-hd,drive=dr1,lun=0,scsi-id=1"); - return arg; -} - -static void *virtio_scsi_setup_iothread(GString *cmd_line, void *arg) -{ - g_string_append(cmd_line, - " -object iothread,id=thread0" - " -blockdev driver=null-co,read-zeroes=on,node-name=null0" - " -device scsi-hd,drive=null0"); - return arg; -} - -static void register_virtio_scsi_test(void) -{ - QOSGraphTestOptions opts = { }; - - opts.before = virtio_scsi_hotplug_setup; - qos_add_test("hotplug", "virtio-scsi", hotplug, &opts); - - opts.before = virtio_scsi_setup; - qos_add_test("unaligned-write-same", "virtio-scsi", - test_unaligned_write_same, &opts); - - opts.before = virtio_scsi_setup_iothread; - opts.edge = (QOSGraphEdgeOptions) { - .extra_device_opts = "iothread=thread0", - }; - qos_add_test("iothread-attach-node", "virtio-scsi-pci", - test_iothread_attach_node, &opts); -} - -libqos_init(register_virtio_scsi_test); diff --git a/tests/virtio-serial-test.c b/tests/virtio-serial-test.c deleted file mode 100644 index 2541034822..0000000000 --- a/tests/virtio-serial-test.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * QTest testcase for VirtIO Serial - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest-single.h" -#include "qemu/module.h" -#include "libqos/virtio-serial.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void virtio_serial_nop(void *obj, void *data, QGuestAllocator *alloc) -{ - /* no operation */ -} - -static void serial_hotplug(void *obj, void *data, QGuestAllocator *alloc) -{ - qtest_qmp_device_add(global_qtest, "virtserialport", "hp-port", "{}"); - qtest_qmp_device_del(global_qtest, "hp-port"); -} - -static void register_virtio_serial_test(void) -{ - QOSGraphTestOptions opts = { }; - - opts.edge.before_cmd_line = "-device virtconsole,bus=vser0.0"; - qos_add_test("console-nop", "virtio-serial", virtio_serial_nop, &opts); - - opts.edge.before_cmd_line = "-device virtserialport,bus=vser0.0"; - qos_add_test("serialport-nop", "virtio-serial", virtio_serial_nop, &opts); - - qos_add_test("hotplug", "virtio-serial", serial_hotplug, NULL); -} -libqos_init(register_virtio_serial_test); diff --git a/tests/virtio-test.c b/tests/virtio-test.c deleted file mode 100644 index f7c6afdcf1..0000000000 --- a/tests/virtio-test.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * QTest testcase for virtio - * - * Copyright (c) 2018 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void *obj, void *data, QGuestAllocator *alloc) -{ -} - -static void register_virtio_test(void) -{ - qos_add_test("nop", "virtio", nop, NULL); -} - -libqos_init(register_virtio_test); diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c deleted file mode 100644 index efba76e716..0000000000 --- a/tests/vmgenid-test.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * QTest testcase for VM Generation ID - * - * Copyright (c) 2016 Red Hat, Inc. - * Copyright (c) 2017 Skyport Systems - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu/bitmap.h" -#include "qemu/uuid.h" -#include "hw/acpi/acpi-defs.h" -#include "boot-sector.h" -#include "acpi-utils.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" - -#define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" -#define VMGENID_GUID_OFFSET 40 /* allow space for - * OVMF SDT Header Probe Supressor - */ -#define RSDP_ADDR_INVALID 0x100000 /* RSDP must be below this address */ - -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_len, table_length; - uint8_t *rsdt, *ent; - - /* Wait for guest firmware to finish and start the payload. */ - boot_sector_test(qts); - - /* Tables should be initialized now. */ - rsdp_offset = acpi_find_rsdp_address(qts); - - g_assert_cmphex(rsdp_offset, <, RSDP_ADDR_INVALID); - - - acpi_fetch_rsdp_table(qts, rsdp_offset, rsdp_table); - acpi_fetch_table(qts, &rsdt, &rsdt_len, &rsdp_table[16 /* RsdtAddress */], - 4, "RSDT", true); - - ACPI_FOREACH_RSDT_ENTRY(rsdt, rsdt_len, ent, 4 /* Entry size */) { - uint8_t *table_aml; - - acpi_fetch_table(qts, &table_aml, &table_length, ent, 4, 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 - */ - 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(vgia_val) + VMGENID_GUID_OFFSET; - g_free(table_aml); - break; - } - g_free(table_aml); - } - g_free(rsdt); - return guid_offset; -} - -static void read_guid_from_memory(QTestState *qts, QemuUUID *guid) -{ - uint32_t vmgenid_addr; - int i; - - vmgenid_addr = acpi_find_vgia(qts); - g_assert(vmgenid_addr); - - /* Read the GUID directly from guest memory */ - for (i = 0; i < 16; i++) { - guid->data[i] = qtest_readb(qts, vmgenid_addr + i); - } - /* The GUID is in little-endian format in the guest, while QEMU - * uses big-endian. Swap after reading. - */ - *guid = qemu_uuid_bswap(*guid); -} - -static void read_guid_from_monitor(QTestState *qts, QemuUUID *guid) -{ - QDict *rsp, *rsp_ret; - const char *guid_str; - - rsp = qtest_qmp(qts, "{ 'execute': 'query-vm-generation-id' }"); - if (qdict_haskey(rsp, "return")) { - rsp_ret = qdict_get_qdict(rsp, "return"); - g_assert(qdict_haskey(rsp_ret, "guid")); - guid_str = qdict_get_str(rsp_ret, "guid"); - g_assert(qemu_uuid_parse(guid_str, guid) == 0); - } - qobject_unref(rsp); -} - -static char disk[] = "tests/vmgenid-test-disk-XXXXXX"; - -#define GUID_CMD(guid) \ - "-accel kvm -accel tcg " \ - "-device vmgenid,id=testvgid,guid=%s " \ - "-drive id=hd0,if=none,file=%s,format=raw " \ - "-device ide-hd,drive=hd0 ", guid, disk - -static void vmgenid_set_guid_test(void) -{ - QemuUUID expected, measured; - QTestState *qts; - - g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0); - - qts = qtest_initf(GUID_CMD(VGID_GUID)); - - /* Read the GUID from accessing guest memory */ - read_guid_from_memory(qts, &measured); - g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0); - - qtest_quit(qts); -} - -static void vmgenid_set_guid_auto_test(void) -{ - QemuUUID measured; - QTestState *qts; - - qts = qtest_initf(GUID_CMD("auto")); - - read_guid_from_memory(qts, &measured); - - /* Just check that the GUID is non-null */ - g_assert(!qemu_uuid_is_null(&measured)); - - qtest_quit(qts); -} - -static void vmgenid_query_monitor_test(void) -{ - QemuUUID expected, measured; - QTestState *qts; - - g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0); - - qts = qtest_initf(GUID_CMD(VGID_GUID)); - - /* Read the GUID via the monitor */ - read_guid_from_monitor(qts, &measured); - g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0); - - qtest_quit(qts); -} - -int main(int argc, char **argv) -{ - int ret; - - ret = boot_sector_init(disk); - if (ret) { - return ret; - } - - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/vmgenid/vmgenid/set-guid", - vmgenid_set_guid_test); - qtest_add_func("/vmgenid/vmgenid/set-guid-auto", - vmgenid_set_guid_auto_test); - qtest_add_func("/vmgenid/vmgenid/query-monitor", - vmgenid_query_monitor_test); - ret = g_test_run(); - boot_sector_cleanup(disk); - - return ret; -} diff --git a/tests/vmxnet3-test.c b/tests/vmxnet3-test.c deleted file mode 100644 index a81025252c..0000000000 --- a/tests/vmxnet3-test.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * QTest testcase for vmxnet3 NIC - * - * Copyright (c) 2013-2014 SUSE LINUX Products GmbH - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qemu/module.h" -#include "libqos/qgraph.h" -#include "libqos/pci.h" - -typedef struct QVmxnet3 QVmxnet3; - -struct QVmxnet3 { - QOSGraphObject obj; - QPCIDevice dev; -}; - -static void *vmxnet3_get_driver(void *obj, const char *interface) -{ - QVmxnet3 *vmxnet3 = obj; - - if (!g_strcmp0(interface, "pci-device")) { - return &vmxnet3->dev; - } - - fprintf(stderr, "%s not present in vmxnet3\n", interface); - g_assert_not_reached(); -} - -static void *vmxnet3_create(void *pci_bus, QGuestAllocator *alloc, void *addr) -{ - QVmxnet3 *vmxnet3 = g_new0(QVmxnet3, 1); - QPCIBus *bus = pci_bus; - - qpci_device_init(&vmxnet3->dev, bus, addr); - vmxnet3->obj.get_driver = vmxnet3_get_driver; - - return &vmxnet3->obj; -} - -static void vmxnet3_register_nodes(void) -{ - QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0", - }; - add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); - - qos_node_create_driver("vmxnet3", vmxnet3_create); - qos_node_consumes("vmxnet3", "pci-bus", &opts); - qos_node_produces("vmxnet3", "pci-device"); -} - -libqos_init(vmxnet3_register_nodes); diff --git a/tests/wdt_ib700-test.c b/tests/wdt_ib700-test.c deleted file mode 100644 index 797288d939..0000000000 --- a/tests/wdt_ib700-test.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * QTest testcase for the IB700 watchdog - * - * Copyright (c) 2014 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qemu/timer.h" - -static void qmp_check_no_event(QTestState *s) -{ - QDict *resp = qtest_qmp(s, "{'execute':'query-status'}"); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); -} - -static QDict *ib700_program_and_wait(QTestState *s) -{ - QDict *event, *data; - - qtest_clock_step(s, NANOSECONDS_PER_SECOND * 40); - qmp_check_no_event(s); - - /* 2 second limit */ - qtest_outb(s, 0x443, 14); - - /* Ping */ - qtest_clock_step(s, NANOSECONDS_PER_SECOND); - qmp_check_no_event(s); - qtest_outb(s, 0x443, 14); - - /* Disable */ - qtest_clock_step(s, NANOSECONDS_PER_SECOND); - qmp_check_no_event(s); - qtest_outb(s, 0x441, 1); - qtest_clock_step(s, 3 * NANOSECONDS_PER_SECOND); - qmp_check_no_event(s); - - /* Enable and let it fire */ - qtest_outb(s, 0x443, 13); - qtest_clock_step(s, 3 * NANOSECONDS_PER_SECOND); - qmp_check_no_event(s); - qtest_clock_step(s, 2 * NANOSECONDS_PER_SECOND); - event = qtest_qmp_eventwait_ref(s, "WATCHDOG"); - data = qdict_get_qdict(event, "data"); - qobject_ref(data); - qobject_unref(event); - return data; -} - - -static void ib700_pause(void) -{ - QDict *d; - QTestState *s = qtest_init("-watchdog-action pause -device ib700"); - - qtest_irq_intercept_in(s, "ioapic"); - d = ib700_program_and_wait(s); - g_assert(!strcmp(qdict_get_str(d, "action"), "pause")); - qobject_unref(d); - qtest_qmp_eventwait(s, "STOP"); - qtest_quit(s); -} - -static void ib700_reset(void) -{ - QDict *d; - QTestState *s = qtest_init("-watchdog-action reset -device ib700"); - - qtest_irq_intercept_in(s, "ioapic"); - d = ib700_program_and_wait(s); - g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); - qobject_unref(d); - qtest_qmp_eventwait(s, "RESET"); - qtest_quit(s); -} - -static void ib700_shutdown(void) -{ - QDict *d; - QTestState *s; - - s = qtest_init("-watchdog-action reset -no-reboot -device ib700"); - qtest_irq_intercept_in(s, "ioapic"); - d = ib700_program_and_wait(s); - g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); - qobject_unref(d); - qtest_qmp_eventwait(s, "SHUTDOWN"); - qtest_quit(s); -} - -static void ib700_none(void) -{ - QDict *d; - QTestState *s = qtest_init("-watchdog-action none -device ib700"); - - qtest_irq_intercept_in(s, "ioapic"); - d = ib700_program_and_wait(s); - g_assert(!strcmp(qdict_get_str(d, "action"), "none")); - qobject_unref(d); - qtest_quit(s); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - qtest_add_func("/wdt_ib700/pause", ib700_pause); - qtest_add_func("/wdt_ib700/reset", ib700_reset); - qtest_add_func("/wdt_ib700/shutdown", ib700_shutdown); - qtest_add_func("/wdt_ib700/none", ib700_none); - - return g_test_run(); -}