diff options
Diffstat (limited to 'tests')
112 files changed, 3131 insertions, 544 deletions
diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index a313e88c07..cb71f50db9 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -309,6 +309,16 @@ class QemuSystemTest(QemuBaseTest): if netdevhelp.find('\n' + netdevname + '\n') < 0: self.cancel('no support for user networking') + def require_multiprocess(self): + """ + Test for the presence of the x-pci-proxy-dev which is required + to support multiprocess. + """ + devhelp = run_cmd([self.qemu_bin, + '-M', 'none', '-device', 'help'])[0]; + if devhelp.find('x-pci-proxy-dev') < 0: + self.cancel('no support for multiprocess device emulation') + def _new_vm(self, name, *args): self._sd = tempfile.TemporaryDirectory(prefix="qemu_") vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir, diff --git a/tests/avocado/machine_aarch64_virt.py b/tests/avocado/machine_aarch64_virt.py index 25dab8dc00..a90dc6ff4b 100644 --- a/tests/avocado/machine_aarch64_virt.py +++ b/tests/avocado/machine_aarch64_virt.py @@ -38,11 +38,11 @@ class Aarch64VirtMachine(QemuSystemTest): :avocado: tags=accel:tcg """ iso_url = ('https://dl-cdn.alpinelinux.org/' - 'alpine/v3.16/releases/aarch64/' - 'alpine-virt-3.16.3-aarch64.iso') + 'alpine/v3.17/releases/aarch64/' + 'alpine-standard-3.17.2-aarch64.iso') # Alpine use sha256 so I recalculated this myself - iso_sha1 = '0683bc089486d55c91bf6607d5ecb93925769bc0' + iso_sha1 = '76284fcd7b41fe899b0c2375ceb8470803eea839' iso_path = self.fetch_asset(iso_url, asset_hash=iso_sha1) self.vm.set_console() @@ -65,7 +65,7 @@ class Aarch64VirtMachine(QemuSystemTest): self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') self.vm.launch() - self.wait_for_console_pattern('Welcome to Alpine Linux 3.16') + self.wait_for_console_pattern('Welcome to Alpine Linux 3.17') def common_aarch64_virt(self, machine): diff --git a/tests/avocado/multiprocess.py b/tests/avocado/multiprocess.py index 80a3b8f442..9112a4cacc 100644 --- a/tests/avocado/multiprocess.py +++ b/tests/avocado/multiprocess.py @@ -22,6 +22,7 @@ class Multiprocess(QemuSystemTest): machine_type): """Main test method""" self.require_accelerator('kvm') + self.require_multiprocess() # Create socketpair to connect proxy and remote processes proxy_sock, remote_sock = socket.socketpair(socket.AF_UNIX, diff --git a/tests/avocado/netdev-ethtool.py b/tests/avocado/netdev-ethtool.py new file mode 100644 index 0000000000..f7e9464184 --- /dev/null +++ b/tests/avocado/netdev-ethtool.py @@ -0,0 +1,116 @@ +# ethtool tests for emulated network devices +# +# This test leverages ethtool's --test sequence to validate network +# device behaviour. +# +# SPDX-License-Identifier: GPL-2.0-or-late + +from avocado import skip +from avocado_qemu import QemuSystemTest +from avocado_qemu import exec_command, exec_command_and_wait_for_pattern +from avocado_qemu import wait_for_console_pattern + +class NetDevEthtool(QemuSystemTest): + """ + :avocado: tags=arch:x86_64 + :avocado: tags=machine:q35 + """ + + # Runs in about 17s under KVM, 19s under TCG, 25s under GCOV + timeout = 45 + + # Fetch assets from the netdev-ethtool subdir of my shared test + # images directory on fileserver.linaro.org. + def get_asset(self, name, sha1): + base_url = ('https://fileserver.linaro.org/s/' + 'kE4nCFLdQcoBF9t/download?' + 'path=%2Fnetdev-ethtool&files=' ) + url = base_url + name + # use explicit name rather than failing to neatly parse the + # URL into a unique one + return self.fetch_asset(name=name, locations=(url), asset_hash=sha1) + + def common_test_code(self, netdev, extra_args=None, kvm=False): + + # This custom kernel has drivers for all the supported network + # devices we can emulate in QEMU + kernel = self.get_asset("bzImage", + "33469d7802732d5815226166581442395cb289e2") + + rootfs = self.get_asset("rootfs.squashfs", + "9793cea7021414ae844bda51f558bd6565b50cdc") + + append = 'printk.time=0 console=ttyS0 ' + append += 'root=/dev/sr0 rootfstype=squashfs ' + + # any additional kernel tweaks for the test + if extra_args: + append += extra_args + + # finally invoke ethtool directly + append += ' init=/usr/sbin/ethtool -- -t eth1 offline' + + # add the rootfs via a readonly cdrom image + drive = f"file={rootfs},if=ide,index=0,media=cdrom" + + self.vm.add_args('-kernel', kernel, + '-append', append, + '-drive', drive, + '-device', netdev) + + if kvm: + self.vm.add_args('-accel', 'kvm') + + self.vm.set_console(console_index=0) + self.vm.launch() + + wait_for_console_pattern(self, + "The test result is PASS", + "The test result is FAIL", + vm=None) + # no need to gracefully shutdown, just finish + self.vm.kill() + + # Skip testing for MSI for now. Allegedly it was fixed by: + # 28e96556ba (igb: Allocate MSI-X vector when testing) + # but I'm seeing oops in the kernel + @skip("Kernel bug with MSI enabled") + def test_igb(self): + """ + :avocado: tags=device:igb + """ + self.common_test_code("igb") + + def test_igb_nomsi(self): + """ + :avocado: tags=device:igb + """ + self.common_test_code("igb", "pci=nomsi") + + def test_igb_nomsi_kvm(self): + """ + :avocado: tags=device:igb + """ + self.require_accelerator('kvm') + self.common_test_code("igb", "pci=nomsi", True) + + # It seems the other popular cards we model in QEMU currently fail + # the pattern test with: + # + # pattern test failed (reg 0x00178): got 0x00000000 expected 0x00005A5A + # + # So for now we skip them. + + @skip("Incomplete reg 0x00178 support") + def test_e1000(self): + """ + :avocado: tags=device:e1000 + """ + self.common_test_code("e1000") + + @skip("Incomplete reg 0x00178 support") + def test_i82550(self): + """ + :avocado: tags=device:i82550 + """ + self.common_test_code("i82550") diff --git a/tests/avocado/tuxrun_baselines.py b/tests/avocado/tuxrun_baselines.py index 30aaefc1d3..c3fb67f5dc 100644 --- a/tests/avocado/tuxrun_baselines.py +++ b/tests/avocado/tuxrun_baselines.py @@ -67,9 +67,6 @@ class TuxRunBaselineTest(QemuSystemTest): # The name of the kernel Image file self.image = self.get_tag('image', "Image") - # The block device drive type - self.drive = self.get_tag('drive', "virtio-blk-device") - self.root = self.get_tag('root', "vda") # Occasionally we need extra devices to hook things up @@ -99,7 +96,7 @@ class TuxRunBaselineTest(QemuSystemTest): return (kernel_image, self.workdir + "/rootfs.ext4", dtb) - def prepare_run(self, kernel, disk, dtb=None, console_index=0): + def prepare_run(self, kernel, disk, drive, dtb=None, console_index=0): """ Setup to run and add the common parameters to the system """ @@ -121,10 +118,8 @@ class TuxRunBaselineTest(QemuSystemTest): if self.extradev: self.vm.add_args('-device', self.extradev) - # Some machines already define a drive device - if self.drive != "none": - self.vm.add_args('-device', - f"{self.drive},drive=hd0") + self.vm.add_args('-device', + f"{drive},drive=hd0") # Some machines need an explicit DTB if dtb: @@ -154,7 +149,9 @@ class TuxRunBaselineTest(QemuSystemTest): else: self.vm.wait() - def common_tuxrun(self, dt=None, haltmsg="reboot: System halted", + def common_tuxrun(self, dt=None, + drive="virtio-blk-device", + haltmsg="reboot: System halted", console_index=0): """ Common path for LKFT tests. Unless we need to do something @@ -163,7 +160,7 @@ class TuxRunBaselineTest(QemuSystemTest): """ (kernel, disk, dtb) = self.fetch_tuxrun_assets(dt) - self.prepare_run(kernel, disk, dtb, console_index) + self.prepare_run(kernel, disk, drive, dtb, console_index) self.vm.launch() self.run_tuxtest_tests(haltmsg) @@ -206,11 +203,11 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=machine:versatilepb :avocado: tags=tuxboot:armv5 :avocado: tags=image:zImage - :avocado: tags=drive:virtio-blk-pci :avocado: tags=console:ttyAMA0 :avocado: tags=shutdown:nowait """ - self.common_tuxrun(dt="versatile-pb.dtb") + self.common_tuxrun(drive="virtio-blk-pci", + dt="versatile-pb.dtb") def test_armv7(self): """ @@ -244,10 +241,9 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=machine:q35 :avocado: tags=tuxboot:i386 :avocado: tags=image:bzImage - :avocado: tags=drive:virtio-blk-pci :avocado: tags=shutdown:nowait """ - self.common_tuxrun() + self.common_tuxrun(drive="virtio-blk-pci") def test_mips32(self): """ @@ -257,11 +253,10 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=endian:big :avocado: tags=tuxboot:mips32 :avocado: tags=image:vmlinux - :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 :avocado: tags=root:sda :avocado: tags=shutdown:nowait """ - self.common_tuxrun() + self.common_tuxrun(drive="driver=ide-hd,bus=ide.0,unit=0") def test_mips32el(self): """ @@ -270,11 +265,10 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=cpu:mips32r6-generic :avocado: tags=tuxboot:mips32el :avocado: tags=image:vmlinux - :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 :avocado: tags=root:sda :avocado: tags=shutdown:nowait """ - self.common_tuxrun() + self.common_tuxrun(drive="driver=ide-hd,bus=ide.0,unit=0") @skip("QEMU currently broken") # regression against stable QEMU def test_mips64(self): @@ -284,11 +278,10 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=tuxboot:mips64 :avocado: tags=endian:big :avocado: tags=image:vmlinux - :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 :avocado: tags=root:sda :avocado: tags=shutdown:nowait """ - self.common_tuxrun() + self.common_tuxrun(drive="driver=ide-hd,bus=ide.0,unit=0") def test_mips64el(self): """ @@ -296,11 +289,10 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=machine:malta :avocado: tags=tuxboot:mips64el :avocado: tags=image:vmlinux - :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 :avocado: tags=root:sda :avocado: tags=shutdown:nowait """ - self.common_tuxrun() + self.common_tuxrun(drive="driver=ide-hd,bus=ide.0,unit=0") def test_ppc32(self): """ @@ -309,10 +301,9 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=cpu:e500mc :avocado: tags=tuxboot:ppc32 :avocado: tags=image:uImage - :avocado: tags=drive:virtio-blk-pci :avocado: tags=shutdown:nowait """ - self.common_tuxrun() + self.common_tuxrun(drive="virtio-blk-pci") def test_ppc64(self): """ @@ -324,10 +315,9 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=tuxboot:ppc64 :avocado: tags=image:vmlinux :avocado: tags=extradev:driver=spapr-vscsi - :avocado: tags=drive:scsi-hd :avocado: tags=root:sda """ - self.common_tuxrun() + self.common_tuxrun(drive="scsi-hd") def test_ppc64le(self): """ @@ -338,10 +328,9 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=tuxboot:ppc64le :avocado: tags=image:vmlinux :avocado: tags=extradev:driver=spapr-vscsi - :avocado: tags=drive:scsi-hd :avocado: tags=root:sda """ - self.common_tuxrun() + self.common_tuxrun(drive="scsi-hd") def test_riscv32(self): """ @@ -365,10 +354,10 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=endian:big :avocado: tags=tuxboot:s390 :avocado: tags=image:bzImage - :avocado: tags=drive:virtio-blk-ccw :avocado: tags=shutdown:nowait """ - self.common_tuxrun(haltmsg="Requesting system halt") + self.common_tuxrun(drive="virtio-blk-ccw", + haltmsg="Requesting system halt") # Note: some segfaults caused by unaligned userspace access @skipIf(os.getenv('GITLAB_CI'), 'Skipping unstable test on GitLab') @@ -380,7 +369,6 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=tuxboot:sh4 :avocado: tags=image:zImage :avocado: tags=root:sda - :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 :avocado: tags=console:ttySC1 """ # The test is currently too unstable to do much in userspace @@ -388,7 +376,9 @@ class TuxRunBaselineTest(QemuSystemTest): (kernel, disk, dtb) = self.fetch_tuxrun_assets() # the console comes on the second serial port - self.prepare_run(kernel, disk, console_index=1) + self.prepare_run(kernel, disk, + "driver=ide-hd,bus=ide.0,unit=0", + console_index=1) self.vm.launch() self.wait_for_console_pattern("Welcome to TuxTest") @@ -404,10 +394,9 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=tuxboot:sparc64 :avocado: tags=image:vmlinux :avocado: tags=root:sda - :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 :avocado: tags=shutdown:nowait """ - self.common_tuxrun() + self.common_tuxrun(drive="driver=ide-hd,bus=ide.0,unit=0") def test_x86_64(self): """ @@ -417,7 +406,6 @@ class TuxRunBaselineTest(QemuSystemTest): :avocado: tags=tuxboot:x86_64 :avocado: tags=image:bzImage :avocado: tags=root:sda - :avocado: tags=drive:driver=ide-hd,bus=ide.0,unit=0 :avocado: tags=shutdown:nowait """ - self.common_tuxrun() + self.common_tuxrun(drive="driver=ide-hd,bus=ide.0,unit=0") diff --git a/tests/check-block.sh b/tests/check-block.sh deleted file mode 100755 index 5de2c1ba0b..0000000000 --- a/tests/check-block.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh - -if [ "$#" -eq 0 ]; then - echo "Usage: $0 fmt..." >&2 - exit 99 -fi - -# Honor the SPEED environment variable, just like we do it for "meson test" -format_list="$@" -if [ "$SPEED" = "slow" ] || [ "$SPEED" = "thorough" ]; then - group= -else - group="-g auto" -fi - -skip() { - echo "1..0 #SKIP $*" - exit 0 -} - -if [ -z "$(find . -name 'qemu-system-*' -print)" ]; then - skip "No qemu-system binary available ==> Not running the qemu-iotests." -fi - -cd tests/qemu-iotests - -# QEMU_CHECK_BLOCK_AUTO is used to disable some unstable sub-tests -export QEMU_CHECK_BLOCK_AUTO=1 -export PYTHONUTF8=1 -# If make was called with -jN we want to call ./check with -j N. Extract the -# flag from MAKEFLAGS, so that if it absent (or MAKEFLAGS is not defined), JOBS -# would be an empty line otherwise JOBS is prepared string of flag with value: -# "-j N" -# Note, that the following works even if make was called with "-j N" or even -# "--jobs N", as all these variants becomes simply "-jN" in MAKEFLAGS variable. -JOBS=$(echo "$MAKEFLAGS" | sed -n 's/\(^\|.* \)-j\([0-9]\+\)\( .*\|$\)/-j \2/p') - -ret=0 -for fmt in $format_list ; do - ${PYTHON} ./check $JOBS -tap -$fmt $group || ret=1 -done - -exit $ret diff --git a/tests/data/acpi/pc/DSDT b/tests/data/acpi/pc/DSDT index 0b475fb5a9..32d255cfc0 100644 --- a/tests/data/acpi/pc/DSDT +++ b/tests/data/acpi/pc/DSDT Binary files differdiff --git a/tests/data/acpi/pc/DSDT.acpierst b/tests/data/acpi/pc/DSDT.acpierst index 17ef7caeb6..33e872b2fa 100644 --- a/tests/data/acpi/pc/DSDT.acpierst +++ b/tests/data/acpi/pc/DSDT.acpierst Binary files differdiff --git a/tests/data/acpi/pc/DSDT.acpihmat b/tests/data/acpi/pc/DSDT.acpihmat index 675b674eaa..cd84abc1b1 100644 --- a/tests/data/acpi/pc/DSDT.acpihmat +++ b/tests/data/acpi/pc/DSDT.acpihmat Binary files differdiff --git a/tests/data/acpi/pc/DSDT.bridge b/tests/data/acpi/pc/DSDT.bridge index c1ce061366..69a73ea2a6 100644 --- a/tests/data/acpi/pc/DSDT.bridge +++ b/tests/data/acpi/pc/DSDT.bridge Binary files differdiff --git a/tests/data/acpi/pc/DSDT.cphp b/tests/data/acpi/pc/DSDT.cphp index 754ab854dc..20379056b3 100644 --- a/tests/data/acpi/pc/DSDT.cphp +++ b/tests/data/acpi/pc/DSDT.cphp Binary files differdiff --git a/tests/data/acpi/pc/DSDT.dimmpxm b/tests/data/acpi/pc/DSDT.dimmpxm index 170503336b..435496e836 100644 --- a/tests/data/acpi/pc/DSDT.dimmpxm +++ b/tests/data/acpi/pc/DSDT.dimmpxm Binary files differdiff --git a/tests/data/acpi/pc/DSDT.hpbridge b/tests/data/acpi/pc/DSDT.hpbridge index 834c27002e..b6eafab250 100644 --- a/tests/data/acpi/pc/DSDT.hpbridge +++ b/tests/data/acpi/pc/DSDT.hpbridge Binary files differdiff --git a/tests/data/acpi/pc/DSDT.hpbrroot b/tests/data/acpi/pc/DSDT.hpbrroot index a71ed4fbaa..a4073f36d6 100644 --- a/tests/data/acpi/pc/DSDT.hpbrroot +++ b/tests/data/acpi/pc/DSDT.hpbrroot Binary files differdiff --git a/tests/data/acpi/pc/DSDT.ipmikcs b/tests/data/acpi/pc/DSDT.ipmikcs index dd71356027..06aa7bfdec 100644 --- a/tests/data/acpi/pc/DSDT.ipmikcs +++ b/tests/data/acpi/pc/DSDT.ipmikcs Binary files differdiff --git a/tests/data/acpi/pc/DSDT.memhp b/tests/data/acpi/pc/DSDT.memhp index 2f895e9b38..10a0e44d61 100644 --- a/tests/data/acpi/pc/DSDT.memhp +++ b/tests/data/acpi/pc/DSDT.memhp Binary files differdiff --git a/tests/data/acpi/pc/DSDT.nohpet b/tests/data/acpi/pc/DSDT.nohpet index c012b63ace..6905312d82 100644 --- a/tests/data/acpi/pc/DSDT.nohpet +++ b/tests/data/acpi/pc/DSDT.nohpet Binary files differdiff --git a/tests/data/acpi/pc/DSDT.numamem b/tests/data/acpi/pc/DSDT.numamem index f2ef4b9729..59e31338ee 100644 --- a/tests/data/acpi/pc/DSDT.numamem +++ b/tests/data/acpi/pc/DSDT.numamem Binary files differdiff --git a/tests/data/acpi/pc/DSDT.roothp b/tests/data/acpi/pc/DSDT.roothp index 657c8263f0..448d596cf4 100644 --- a/tests/data/acpi/pc/DSDT.roothp +++ b/tests/data/acpi/pc/DSDT.roothp Binary files differdiff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/q35/DSDT index d68c472b46..720e8cbbbb 100644 --- a/tests/data/acpi/q35/DSDT +++ b/tests/data/acpi/q35/DSDT Binary files differdiff --git a/tests/data/acpi/q35/DSDT.acpierst b/tests/data/acpi/q35/DSDT.acpierst index de7ae27125..f26b1f2a19 100644 --- a/tests/data/acpi/q35/DSDT.acpierst +++ b/tests/data/acpi/q35/DSDT.acpierst Binary files differdiff --git a/tests/data/acpi/q35/DSDT.acpihmat b/tests/data/acpi/q35/DSDT.acpihmat index 48e2862257..86771f1746 100644 --- a/tests/data/acpi/q35/DSDT.acpihmat +++ b/tests/data/acpi/q35/DSDT.acpihmat Binary files differdiff --git a/tests/data/acpi/q35/DSDT.acpihmat-noinitiator b/tests/data/acpi/q35/DSDT.acpihmat-noinitiator index 30a4aa2ec8..a894a2d16c 100644 --- a/tests/data/acpi/q35/DSDT.acpihmat-noinitiator +++ b/tests/data/acpi/q35/DSDT.acpihmat-noinitiator Binary files differdiff --git a/tests/data/acpi/q35/DSDT.applesmc b/tests/data/acpi/q35/DSDT.applesmc index 84e2b5cbc4..276ae1df51 100644 --- a/tests/data/acpi/q35/DSDT.applesmc +++ b/tests/data/acpi/q35/DSDT.applesmc Binary files differdiff --git a/tests/data/acpi/q35/DSDT.bridge b/tests/data/acpi/q35/DSDT.bridge index e411d40fd1..9f8a208aaa 100644 --- a/tests/data/acpi/q35/DSDT.bridge +++ b/tests/data/acpi/q35/DSDT.bridge Binary files differdiff --git a/tests/data/acpi/q35/DSDT.core-count2 b/tests/data/acpi/q35/DSDT.core-count2 index 0603db8cc6..2ec11fe3c3 100644 --- a/tests/data/acpi/q35/DSDT.core-count2 +++ b/tests/data/acpi/q35/DSDT.core-count2 Binary files differdiff --git a/tests/data/acpi/q35/DSDT.cphp b/tests/data/acpi/q35/DSDT.cphp index beeb83c33b..612c85b1b4 100644 --- a/tests/data/acpi/q35/DSDT.cphp +++ b/tests/data/acpi/q35/DSDT.cphp Binary files differdiff --git a/tests/data/acpi/q35/DSDT.cxl b/tests/data/acpi/q35/DSDT.cxl index 4586b9a18b..f049f414f0 100644 --- a/tests/data/acpi/q35/DSDT.cxl +++ b/tests/data/acpi/q35/DSDT.cxl Binary files differdiff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm index 99a93e12a7..23dabeacb0 100644 --- a/tests/data/acpi/q35/DSDT.dimmpxm +++ b/tests/data/acpi/q35/DSDT.dimmpxm Binary files differdiff --git a/tests/data/acpi/q35/DSDT.ipmibt b/tests/data/acpi/q35/DSDT.ipmibt index 7f7601dbff..541bb70522 100644 --- a/tests/data/acpi/q35/DSDT.ipmibt +++ b/tests/data/acpi/q35/DSDT.ipmibt Binary files differdiff --git a/tests/data/acpi/q35/DSDT.ipmismbus b/tests/data/acpi/q35/DSDT.ipmismbus index 6c5d1afe44..e2d57a3318 100644 --- a/tests/data/acpi/q35/DSDT.ipmismbus +++ b/tests/data/acpi/q35/DSDT.ipmismbus Binary files differdiff --git a/tests/data/acpi/q35/DSDT.ivrs b/tests/data/acpi/q35/DSDT.ivrs index de7ae27125..f26b1f2a19 100644 --- a/tests/data/acpi/q35/DSDT.ivrs +++ b/tests/data/acpi/q35/DSDT.ivrs Binary files differdiff --git a/tests/data/acpi/q35/DSDT.memhp b/tests/data/acpi/q35/DSDT.memhp index 79bce5c8f0..809d7e2f0f 100644 --- a/tests/data/acpi/q35/DSDT.memhp +++ b/tests/data/acpi/q35/DSDT.memhp Binary files differdiff --git a/tests/data/acpi/q35/DSDT.mmio64 b/tests/data/acpi/q35/DSDT.mmio64 index c249929add..ab3fe3c1b5 100644 --- a/tests/data/acpi/q35/DSDT.mmio64 +++ b/tests/data/acpi/q35/DSDT.mmio64 Binary files differdiff --git a/tests/data/acpi/q35/DSDT.multi-bridge b/tests/data/acpi/q35/DSDT.multi-bridge index 66b39be294..9ae8ee0b41 100644 --- a/tests/data/acpi/q35/DSDT.multi-bridge +++ b/tests/data/acpi/q35/DSDT.multi-bridge Binary files differdiff --git a/tests/data/acpi/q35/DSDT.noacpihp b/tests/data/acpi/q35/DSDT.noacpihp new file mode 100644 index 0000000000..6ab1f0e525 --- /dev/null +++ b/tests/data/acpi/q35/DSDT.noacpihp Binary files differdiff --git a/tests/data/acpi/q35/DSDT.nohpet b/tests/data/acpi/q35/DSDT.nohpet index 9ff9983a80..becb5f7cad 100644 --- a/tests/data/acpi/q35/DSDT.nohpet +++ b/tests/data/acpi/q35/DSDT.nohpet Binary files differdiff --git a/tests/data/acpi/q35/DSDT.numamem b/tests/data/acpi/q35/DSDT.numamem index 1e7c45ef3c..0cdec0b4c5 100644 --- a/tests/data/acpi/q35/DSDT.numamem +++ b/tests/data/acpi/q35/DSDT.numamem Binary files differdiff --git a/tests/data/acpi/q35/DSDT.pvpanic-isa b/tests/data/acpi/q35/DSDT.pvpanic-isa index ed47451c44..6a9904ec94 100644 --- a/tests/data/acpi/q35/DSDT.pvpanic-isa +++ b/tests/data/acpi/q35/DSDT.pvpanic-isa Binary files differdiff --git a/tests/data/acpi/q35/DSDT.tis.tpm12 b/tests/data/acpi/q35/DSDT.tis.tpm12 index efc2efc19f..628bf628f6 100644 --- a/tests/data/acpi/q35/DSDT.tis.tpm12 +++ b/tests/data/acpi/q35/DSDT.tis.tpm12 Binary files differdiff --git a/tests/data/acpi/q35/DSDT.tis.tpm2 b/tests/data/acpi/q35/DSDT.tis.tpm2 index 675339715f..35c6b08068 100644 --- a/tests/data/acpi/q35/DSDT.tis.tpm2 +++ b/tests/data/acpi/q35/DSDT.tis.tpm2 Binary files differdiff --git a/tests/data/acpi/q35/DSDT.viot b/tests/data/acpi/q35/DSDT.viot index eeb40b360f..3ad4d26b7f 100644 --- a/tests/data/acpi/q35/DSDT.viot +++ b/tests/data/acpi/q35/DSDT.viot Binary files differdiff --git a/tests/data/acpi/q35/DSDT.xapic b/tests/data/acpi/q35/DSDT.xapic index 3aa86f0724..d4a34e2351 100644 --- a/tests/data/acpi/q35/DSDT.xapic +++ b/tests/data/acpi/q35/DSDT.xapic Binary files differdiff --git a/tests/data/acpi/virt/SRAT.acpihmatvirt b/tests/data/acpi/virt/SRAT.acpihmatvirt index 691ef56e34..6fe55dd7d0 100644 --- a/tests/data/acpi/virt/SRAT.acpihmatvirt +++ b/tests/data/acpi/virt/SRAT.acpihmatvirt Binary files differdiff --git a/tests/data/acpi/virt/SSDT.memhp b/tests/data/acpi/virt/SSDT.memhp index 2fcfc5fda9..ef93c44464 100644 --- a/tests/data/acpi/virt/SSDT.memhp +++ b/tests/data/acpi/virt/SSDT.memhp Binary files differdiff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 54ed77f671..9401525325 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -39,7 +39,7 @@ docker-qemu-src: $(DOCKER_SRC_COPY) # General rule for building docker images. docker-image-%: $(DOCKER_FILES_DIR)/%.docker $(call quiet-command, \ - $(RUNC) build \ + DOCKER_BUILDKIT=1 $(RUNC) build \ $(if $V,,--quiet) \ $(if $(NOCACHE),--no-cache, \ $(if $(DOCKER_REGISTRY),--cache-from $(DOCKER_REGISTRY)/qemu/$*)) \ diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 3a1ed7cb18..688ef62989 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -23,10 +23,10 @@ import enum import tempfile import re import signal +import getpass from tarfile import TarFile, TarInfo from io import StringIO, BytesIO from shutil import copy, rmtree -from pwd import getpwuid from datetime import datetime, timedelta @@ -316,7 +316,7 @@ class Docker(object): if user: uid = os.getuid() - uname = getpwuid(uid).pw_name + uname = getpass.getuser() tmp_df.write("\n") tmp_df.write("RUN id %s 2>/dev/null || useradd -u %d -U %s" % (uname, uid, uname)) @@ -570,7 +570,7 @@ class UpdateCommand(SubCommand): if args.user: uid = os.getuid() - uname = getpwuid(uid).pw_name + uname = getpass.getuser() df.write("\n") df.write("RUN id %s 2>/dev/null || useradd -u %d -U %s" % (uname, uid, uname)) diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index 09275e9a10..de12b2b1b9 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -370,6 +370,49 @@ echo echo '=== Compare copy with original ===' $QEMU_IMG compare -f raw -F $IMGFMT "$COPIED_IMG" "$TEST_IMG" +_cleanup_test_img + +echo +echo '=== Writing zeroes while unmapping ===' +# Regression test for https://gitlab.com/qemu-project/qemu/-/issues/1507 +_make_test_img 64M +$QEMU_IO -c 'write -s /dev/urandom 0 64M' "$TEST_IMG" | _filter_qemu_io + +_launch_qemu +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'qmp_capabilities'}" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'blockdev-add', + 'arguments': { + 'driver': '$IMGFMT', + 'node-name': 'node-format', + 'file': { + 'driver': 'file', + 'filename': '$TEST_IMG' + } + } }" \ + 'return' + +fuse_export_add 'export' "'mountpoint': '$EXT_MP', 'writable': true" + +# Try writing zeroes by unmapping +$QEMU_IO -f raw -c 'write -zu 0 64M' "$EXT_MP" | _filter_qemu_io + +# Check the result +$QEMU_IO -f raw -c 'read -P 0 0 64M' "$EXT_MP" | _filter_qemu_io + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'quit'}" \ + 'return' + +wait=yes _cleanup_qemu + +# Check the original image +$QEMU_IO -c 'read -P 0 0 64M' "$TEST_IMG" | _filter_qemu_io + +_cleanup_test_img # success, all done echo "*** done" diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out index e4467a10cf..d5767133b1 100644 --- a/tests/qemu-iotests/308.out +++ b/tests/qemu-iotests/308.out @@ -171,4 +171,39 @@ OK: Post-truncate image size is as expected === Compare copy with original === Images are identical. + +=== Writing zeroes while unmapping === +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{'execute': 'qmp_capabilities'} +{"return": {}} +{'execute': 'blockdev-add', + 'arguments': { + 'driver': 'IMGFMT', + 'node-name': 'node-format', + 'file': { + 'driver': 'file', + 'filename': 'TEST_DIR/t.IMGFMT' + } + } } +{"return": {}} +{'execute': 'block-export-add', + 'arguments': { + 'type': 'fuse', + 'id': 'export', + 'node-name': 'node-format', + 'mountpoint': 'TEST_DIR/t.IMGFMT.fuse', 'writable': true + } } +{"return": {}} +wrote 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{'execute': 'quit'} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}} +read 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 9bdda1394e..f2e9d27dcf 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -26,9 +26,23 @@ from findtests import TestFinder from testenv import TestEnv from testrunner import TestRunner +def get_default_path(follow_link=False): + """ + Try to automagically figure out the path we are running from. + """ + # called from the build tree? + if os.path.islink(sys.argv[0]): + if follow_link: + return os.path.dirname(os.readlink(sys.argv[0])) + else: + return os.path.dirname(os.path.abspath(sys.argv[0])) + else: # or source tree? + return os.getcwd() def make_argparser() -> argparse.ArgumentParser: - p = argparse.ArgumentParser(description="Test run options") + p = argparse.ArgumentParser( + description="Test run options", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) p.add_argument('-n', '--dry-run', action='store_true', help='show me, do not run tests') @@ -113,6 +127,11 @@ def make_argparser() -> argparse.ArgumentParser: 'middle of the process.') g_sel.add_argument('tests', metavar='TEST_FILES', nargs='*', help='tests to run, or "--" followed by a command') + g_sel.add_argument('--build-dir', default=get_default_path(), + help='Path to iotests build directory') + g_sel.add_argument('--source-dir', + default=get_default_path(follow_link=True), + help='Path to iotests build directory') return p @@ -120,11 +139,14 @@ def make_argparser() -> argparse.ArgumentParser: if __name__ == '__main__': args = make_argparser().parse_args() - env = TestEnv(imgfmt=args.imgfmt, imgproto=args.imgproto, + env = TestEnv(source_dir=args.source_dir, + build_dir=args.build_dir, + imgfmt=args.imgfmt, imgproto=args.imgproto, aiomode=args.aiomode, cachemode=args.cachemode, imgopts=args.imgopts, misalign=args.misalign, debug=args.debug, valgrind=args.valgrind, - gdb=args.gdb, qprint=args.print) + gdb=args.gdb, qprint=args.print, + dry_run=args.dry_run) if len(sys.argv) > 1 and sys.argv[-len(args.tests)-1] == '--': if not args.tests: @@ -162,7 +184,7 @@ if __name__ == '__main__': sys.exit(str(e)) if args.dry_run: - print('\n'.join(tests)) + print('\n'.join([os.path.basename(t) for t in tests])) else: with TestRunner(env, tap=args.tap, color=args.color) as tr: diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build index 323a4acb6a..a162f683ef 100644 --- a/tests/qemu-iotests/meson.build +++ b/tests/qemu-iotests/meson.build @@ -32,16 +32,39 @@ foreach k, v : emulators endif endforeach +qemu_iotests_check_cmd = files('check') + foreach format, speed: qemu_iotests_formats if speed == 'quick' suites = 'block' else suites = ['block-' + speed, speed] endif - test('qemu-iotests ' + format, sh, args: [files('../check-block.sh'), format], - depends: qemu_iotests_binaries, env: qemu_iotests_env, - protocol: 'tap', - suite: suites, - timeout: 0, - is_parallel: false) + + args = ['-tap', '-' + format] + if speed == 'quick' + args += ['-g', 'auto'] + endif + + rc = run_command( + [qemu_iotests_check_cmd] + args + ['-n'], + check: true, + ) + + foreach item: rc.stdout().strip().split() + args = ['-tap', '-' + format, item, + '--source-dir', meson.current_source_dir(), + '--build-dir', meson.current_build_dir()] + # Some individual tests take as long as 45 seconds + # Bump the timeout to 3 minutes for some headroom + # on slow machines to minimize spurious failures + test('io-' + format + '-' + item, + qemu_iotests_check_cmd, + args: args, + depends: qemu_iotests_binaries, + env: qemu_iotests_env, + protocol: 'tap', + timeout: 180, + suite: suites) + endforeach endforeach diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index a864c74b12..9a37ad9152 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -170,14 +170,16 @@ class TestEnv(ContextManager['TestEnv']): if not isxfile(b): sys.exit('Not executable: ' + b) - def __init__(self, imgfmt: str, imgproto: str, aiomode: str, + def __init__(self, source_dir: str, build_dir: str, + imgfmt: str, imgproto: str, aiomode: str, cachemode: Optional[str] = None, imgopts: Optional[str] = None, misalign: bool = False, debug: bool = False, valgrind: bool = False, gdb: bool = False, - qprint: bool = False) -> None: + qprint: bool = False, + dry_run: bool = False) -> None: self.imgfmt = imgfmt self.imgproto = imgproto self.aiomode = aiomode @@ -211,18 +213,16 @@ class TestEnv(ContextManager['TestEnv']): # which are needed to initialize some environment variables. They are # used by init_*() functions as well. - if os.path.islink(sys.argv[0]): - # called from the build tree - self.source_iotests = os.path.dirname(os.readlink(sys.argv[0])) - self.build_iotests = os.path.dirname(os.path.abspath(sys.argv[0])) - else: - # called from the source tree - self.source_iotests = os.getcwd() - self.build_iotests = self.source_iotests + self.source_iotests = source_dir + self.build_iotests = build_dir self.build_root = os.path.join(self.build_iotests, '..', '..') self.init_directories() + + if dry_run: + return + self.init_binaries() self.malloc_perturb_ = os.getenv('MALLOC_PERTURB_', diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index 5a771da86e..7b322272e9 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -24,12 +24,10 @@ import difflib import subprocess import contextlib import json -import termios import shutil import sys from multiprocessing import Pool -from contextlib import contextmanager -from typing import List, Optional, Iterator, Any, Sequence, Dict, \ +from typing import List, Optional, Any, Sequence, Dict, \ ContextManager from testenv import TestEnv @@ -56,22 +54,6 @@ def file_diff(file1: str, file2: str) -> List[str]: return res -# We want to save current tty settings during test run, -# since an aborting qemu call may leave things screwed up. -@contextmanager -def savetty() -> Iterator[None]: - isterm = sys.stdin.isatty() - if isterm: - fd = sys.stdin.fileno() - attr = termios.tcgetattr(fd) - - try: - yield - finally: - if isterm: - termios.tcsetattr(fd, termios.TCSADRAIN, attr) - - class LastElapsedTime(ContextManager['LastElapsedTime']): """ Cache for elapsed time for tests, to show it during new test run @@ -169,7 +151,6 @@ class TestRunner(ContextManager['TestRunner']): self._stack = contextlib.ExitStack() self._stack.enter_context(self.env) self._stack.enter_context(self.last_elapsed) - self._stack.enter_context(savetty()) return self def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: @@ -247,13 +228,11 @@ class TestRunner(ContextManager['TestRunner']): return f'{test}.out' - def do_run_test(self, test: str, mp: bool) -> TestResult: + def do_run_test(self, test: str) -> TestResult: """ Run one test :param test: test file path - :param mp: if true, we are in a multiprocessing environment, use - personal subdirectories for test run Note: this method may be called from subprocess, so it does not change ``self`` object in any way! @@ -276,12 +255,14 @@ class TestRunner(ContextManager['TestRunner']): args = [str(f_test.resolve())] env = self.env.prepare_subprocess(args) - if mp: - # Split test directories, so that tests running in parallel don't - # break each other. - for d in ['TEST_DIR', 'SOCK_DIR']: - env[d] = os.path.join(env[d], f_test.name) - Path(env[d]).mkdir(parents=True, exist_ok=True) + + # Split test directories, so that tests running in parallel don't + # break each other. + for d in ['TEST_DIR', 'SOCK_DIR']: + env[d] = os.path.join( + env[d], + f"{self.env.imgfmt}-{self.env.imgproto}-{f_test.name}") + Path(env[d]).mkdir(parents=True, exist_ok=True) test_dir = env['TEST_DIR'] f_bad = Path(test_dir, f_test.name + '.out.bad') @@ -294,6 +275,7 @@ class TestRunner(ContextManager['TestRunner']): t0 = time.time() with f_bad.open('w', encoding="utf-8") as f: with subprocess.Popen(args, cwd=str(f_test.parent), env=env, + stdin=subprocess.DEVNULL, stdout=f, stderr=subprocess.STDOUT) as proc: try: proc.wait() @@ -365,7 +347,7 @@ class TestRunner(ContextManager['TestRunner']): testname = os.path.basename(test) print(f'# running {self.env.imgfmt} {testname}') - res = self.do_run_test(test, mp) + res = self.do_run_test(test) end = datetime.datetime.now().strftime('%H:%M:%S') self.test_print_one_line(test=test, @@ -391,6 +373,7 @@ class TestRunner(ContextManager['TestRunner']): casenotrun = [] if self.tap: + print('TAP version 13') self.env.print_env('# ') print('1..%d' % len(tests)) else: diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index d29a4e47af..8733589437 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -949,9 +949,14 @@ static void test_acpi_piix4_no_acpi_pci_hotplug(void) data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one("-global PIIX4_PM.acpi-root-pci-hotplug=off " "-global PIIX4_PM.acpi-pci-hotplug-with-bridge-support=off " - "-device pci-bridge,chassis_nr=1 " - "-device pci-testdev,bus=pci.0 " - "-device pci-testdev,bus=pci.1", &data); + "-device pci-bridge,chassis_nr=1,addr=4.0 " + "-device pci-testdev,bus=pci.0,addr=5.0 " + "-device pci-testdev,bus=pci.0,addr=6.0,acpi-index=101 " + "-device pci-testdev,bus=pci.1,addr=1.0 " + "-device pci-testdev,bus=pci.1,addr=2.0,acpi-index=201 " + "-device pci-bridge,id=nhpbr,chassis_nr=2,shpc=off,addr=7.0 " + "-device pci-testdev,bus=nhpbr,addr=1.0,acpi-index=301 " + , &data); free_test_data(&data); } @@ -1002,18 +1007,42 @@ static void test_acpi_q35_tcg_bridge(void) free_test_data(&data); } +static void test_acpi_q35_tcg_no_acpi_hotplug(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".noacpihp"; + data.required_struct_types = base_required_struct_types; + data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); + test_acpi_one("-global ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off" + " -device pci-testdev,bus=pcie.0,acpi-index=101,addr=3.0" + " -device pci-bridge,chassis_nr=1,id=shpcbr,addr=4.0" + " -device pci-testdev,bus=shpcbr,addr=1.0,acpi-index=201" + " -device pci-bridge,chassis_nr=2,shpc=off,id=noshpcbr,addr=5.0" + " -device pci-testdev,bus=noshpcbr,addr=1.0,acpi-index=301" + " -device pcie-root-port,id=hprp,port=0x0,chassis=1,addr=6.0" + " -device pci-testdev,bus=hprp,acpi-index=401" + " -device pcie-root-port,id=nohprp,port=0x0,chassis=2,hotplug=off," + "addr=7.0" + " -device pci-testdev,bus=nohprp,acpi-index=501" + " -device pcie-root-port,id=nohprpint,port=0x0,chassis=3,hotplug=off," + "multifunction=on,addr=8.0" + " -device pci-testdev,bus=nohprpint,acpi-index=601,addr=8.1" + " -device pcie-root-port,id=hprp2,port=0x0,chassis=4,bus=nohprpint," + "addr=9.0" + " -device pci-testdev,bus=hprp2,acpi-index=602" + , &data); + free_test_data(&data); +} + static void test_acpi_q35_multif_bridge(void) { test_data data = { .machine = MACHINE_Q35, .variant = ".multi-bridge", }; - - if (!qtest_has_device("pcie-root-port")) { - g_test_skip("Device pcie-root-port is not available"); - goto out; - } - test_vm_prepare("-S" " -device virtio-balloon,id=balloon0,addr=0x4.0x2" " -device pcie-root-port,id=rp0,multifunction=on," @@ -1025,9 +1054,14 @@ static void test_acpi_q35_multif_bridge(void) " -device pcie-root-port,id=rphptgt2,port=0x0,chassis=6,addr=2.2" " -device pcie-root-port,id=rphptgt3,port=0x0,chassis=7,addr=2.3" " -device pci-testdev,bus=pcie.0,addr=2.4" + " -device pci-testdev,bus=pcie.0,addr=2.5,acpi-index=102" " -device pci-testdev,bus=pcie.0,addr=5.0" + " -device pci-testdev,bus=pcie.0,addr=0xf.0,acpi-index=101" " -device pci-testdev,bus=rp0,addr=0.0" - " -device pci-testdev,bus=br1", &data); + " -device pci-testdev,bus=br1" + " -device pcie-root-port,id=rpnohp,chassis=8,addr=0xA.0,hotplug=off" + " -device pcie-root-port,id=rp3,chassis=9,bus=rpnohp" + , &data); /* hotplugged bridges section */ qtest_qmp_device_add(data.qts, "pci-bridge", "hpbr1", @@ -1049,7 +1083,6 @@ static void test_acpi_q35_multif_bridge(void) /* check that reboot/reset doesn't change any ACPI tables */ qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); process_acpi_tables(&data); -out: free_test_data(&data); } @@ -1403,11 +1436,6 @@ static void test_acpi_tcg_dimm_pxm(const char *machine) { test_data data; - if (!qtest_has_device("nvdimm")) { - g_test_skip("Device nvdimm is not available"); - return; - } - memset(&data, 0, sizeof(data)); data.machine = machine; data.variant = ".dimmpxm"; @@ -1456,11 +1484,6 @@ static void test_acpi_virt_tcg_memhp(void) .scan_len = 256ULL * 1024 * 1024, }; - if (!qtest_has_device("nvdimm")) { - g_test_skip("Device nvdimm is not available"); - goto out; - } - data.variant = ".memhp"; test_acpi_one(" -machine nvdimm=on" " -cpu cortex-a57" @@ -1474,7 +1497,7 @@ static void test_acpi_virt_tcg_memhp(void) " -device pc-dimm,id=dimm0,memdev=ram2,node=0" " -device nvdimm,id=dimm1,memdev=nvm0,node=1", &data); -out: + free_test_data(&data); } @@ -1492,11 +1515,6 @@ static void test_acpi_microvm_tcg(void) { test_data data; - if (!qtest_has_device("virtio-blk-device")) { - g_test_skip("Device virtio-blk-device is not available"); - return; - } - test_acpi_microvm_prepare(&data); test_acpi_one(" -machine microvm,acpi=on,ioapic2=off,rtc=off", &data); @@ -1507,11 +1525,6 @@ static void test_acpi_microvm_usb_tcg(void) { test_data data; - if (!qtest_has_device("virtio-blk-device")) { - g_test_skip("Device virtio-blk-device is not available"); - return; - } - test_acpi_microvm_prepare(&data); data.variant = ".usb"; test_acpi_one(" -machine microvm,acpi=on,ioapic2=off,usb=on,rtc=off", @@ -1523,11 +1536,6 @@ static void test_acpi_microvm_rtc_tcg(void) { test_data data; - if (!qtest_has_device("virtio-blk-device")) { - g_test_skip("Device virtio-blk-device is not available"); - return; - } - test_acpi_microvm_prepare(&data); data.variant = ".rtc"; test_acpi_one(" -machine microvm,acpi=on,ioapic2=off,rtc=on", @@ -1539,11 +1547,6 @@ static void test_acpi_microvm_pcie_tcg(void) { test_data data; - if (!qtest_has_device("virtio-blk-device")) { - g_test_skip("Device virtio-blk-device is not available"); - return; - } - test_acpi_microvm_prepare(&data); data.variant = ".pcie"; data.tcg_only = true; /* need constant host-phys-bits */ @@ -1556,11 +1559,6 @@ static void test_acpi_microvm_ioapic2_tcg(void) { test_data data; - if (!qtest_has_device("virtio-blk-device")) { - g_test_skip("Device virtio-blk-device is not available"); - return; - } - test_acpi_microvm_prepare(&data); data.variant = ".ioapic2"; test_acpi_one(" -machine microvm,acpi=on,ioapic2=on,rtc=off", @@ -1600,12 +1598,6 @@ static void test_acpi_virt_tcg_pxb(void) .ram_start = 0x40000000ULL, .scan_len = 128ULL * 1024 * 1024, }; - - if (!qtest_has_device("pcie-root-port")) { - g_test_skip("Device pcie-root-port is not available"); - goto out; - } - /* * While using -cdrom, the cdrom would auto plugged into pxb-pcie, * the reason is the bus of pxb-pcie is also root bus, it would lead @@ -1624,7 +1616,7 @@ static void test_acpi_virt_tcg_pxb(void) " -cpu cortex-a57" " -device pxb-pcie,bus_nr=128", &data); -out: + free_test_data(&data); } @@ -1687,9 +1679,9 @@ static void test_acpi_virt_tcg_acpi_hmat(void) test_acpi_one(" -machine hmat=on" " -cpu cortex-a57" " -smp 4,sockets=2" - " -m 256M" - " -object memory-backend-ram,size=64M,id=ram0" - " -object memory-backend-ram,size=64M,id=ram1" + " -m 384M" + " -object memory-backend-ram,size=128M,id=ram0" + " -object memory-backend-ram,size=128M,id=ram1" " -object memory-backend-ram,size=128M,id=ram2" " -numa node,nodeid=0,memdev=ram0" " -numa node,nodeid=1,memdev=ram1" @@ -1812,12 +1804,6 @@ static void test_acpi_microvm_acpi_erst(void) gchar *params; test_data data; - if (!qtest_has_device("virtio-blk-device")) { - g_test_skip("Device virtio-blk-device is not available"); - g_free(tmp_path); - return; - } - test_acpi_microvm_prepare(&data); data.variant = ".pcie"; data.tcg_only = true; /* need constant host-phys-bits */ @@ -1878,11 +1864,6 @@ static void test_acpi_q35_viot(void) .variant = ".viot", }; - if (!qtest_has_device("virtio-iommu")) { - g_test_skip("Device virtio-iommu is not available"); - goto out; - } - /* * To keep things interesting, two buses bypass the IOMMU. * VIOT should only describes the other two buses. @@ -1893,7 +1874,6 @@ static void test_acpi_q35_viot(void) "-device pxb-pcie,bus_nr=0x20,id=pcie.200,bus=pcie.0,bypass_iommu=on " "-device pxb-pcie,bus_nr=0x30,id=pcie.300,bus=pcie.0", &data); -out: free_test_data(&data); } @@ -1954,10 +1934,8 @@ static void test_acpi_virt_viot(void) .scan_len = 128ULL * 1024 * 1024, }; - if (qtest_has_device("virtio-iommu")) { - test_acpi_one("-cpu cortex-a57 " - "-device virtio-iommu-pci", &data); - } + test_acpi_one("-cpu cortex-a57 " + "-device virtio-iommu-pci", &data); free_test_data(&data); } @@ -2066,11 +2044,6 @@ static void test_acpi_microvm_oem_fields(void) test_data data; char *args; - if (!qtest_has_device("virtio-blk-device")) { - g_test_skip("Device virtio-blk-device is not available"); - return; - } - test_acpi_microvm_prepare(&data); args = test_acpi_create_args(&data, @@ -2161,6 +2134,8 @@ int main(int argc, char *argv[]) test_acpi_q35_tcg_tpm12_tis); } qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge); + qtest_add_func("acpi/q35/no-acpi-hotplug", + test_acpi_q35_tcg_no_acpi_hotplug); qtest_add_func("acpi/q35/multif-bridge", test_acpi_q35_multif_bridge); qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64); diff --git a/tests/qtest/e1000e-test.c b/tests/qtest/e1000e-test.c index b63a4d3c91..de9738fdb7 100644 --- a/tests/qtest/e1000e-test.c +++ b/tests/qtest/e1000e-test.c @@ -27,6 +27,7 @@ #include "qemu/osdep.h" #include "libqtest-single.h" #include "libqos/pci-pc.h" +#include "net/eth.h" #include "qemu/sockets.h" #include "qemu/iov.h" #include "qemu/module.h" @@ -35,9 +36,13 @@ #include "libqos/e1000e.h" #include "hw/net/e1000_regs.h" +static const struct eth_header packet = { + .h_dest = E1000E_ADDRESS, + .h_source = E1000E_ADDRESS, +}; + static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) { - static const char test[] = "TEST"; struct e1000_tx_desc descr; char buffer[64]; int ret; @@ -45,7 +50,7 @@ static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *a /* Prepare test data buffer */ uint64_t data = guest_alloc(alloc, sizeof(buffer)); - memwrite(data, test, sizeof(test)); + memwrite(data, &packet, sizeof(packet)); /* Prepare TX descriptor */ memset(&descr, 0, sizeof(descr)); @@ -71,7 +76,7 @@ static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *a g_assert_cmpint(ret, == , sizeof(recv_len)); ret = recv(test_sockets[0], buffer, sizeof(buffer), 0); g_assert_cmpint(ret, ==, sizeof(buffer)); - g_assert_cmpstr(buffer, == , test); + g_assert_false(memcmp(buffer, &packet, sizeof(packet))); /* Free test data buffer */ guest_free(alloc, data); @@ -81,15 +86,15 @@ static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator { union e1000_rx_desc_extended descr; - char test[] = "TEST"; - int len = htonl(sizeof(test)); + struct eth_header test_iov = packet; + int len = htonl(sizeof(packet)); struct iovec iov[] = { { .iov_base = &len, .iov_len = sizeof(len), },{ - .iov_base = test, - .iov_len = sizeof(test), + .iov_base = &test_iov, + .iov_len = sizeof(packet), }, }; @@ -97,8 +102,8 @@ static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator 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)); + ret = iov_send(test_sockets[0], iov, 2, 0, sizeof(len) + sizeof(packet)); + g_assert_cmpint(ret, == , sizeof(packet) + sizeof(len)); /* Prepare test data buffer */ uint64_t data = guest_alloc(alloc, sizeof(buffer)); @@ -119,7 +124,7 @@ static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator /* Check data sent to the backend */ memread(data, buffer, sizeof(buffer)); - g_assert_cmpstr(buffer, == , test); + g_assert_false(memcmp(buffer, &packet, sizeof(packet))); /* Free test data buffer */ guest_free(alloc, data); diff --git a/tests/qtest/fuzz/generic_fuzz_configs.h b/tests/qtest/fuzz/generic_fuzz_configs.h index a825b78c14..50689da653 100644 --- a/tests/qtest/fuzz/generic_fuzz_configs.h +++ b/tests/qtest/fuzz/generic_fuzz_configs.h @@ -91,6 +91,11 @@ const generic_fuzz_config predefined_configs[] = { "-device e1000e,netdev=net0 -netdev user,id=net0", .objects = "e1000e", },{ + .name = "igb", + .args = "-M q35 -nodefaults " + "-device igb,netdev=net0 -netdev user,id=net0", + .objects = "igb", + },{ .name = "cirrus-vga", .args = "-machine q35 -nodefaults -device cirrus-vga", .objects = "cirrus*", diff --git a/tests/qtest/igb-test.c b/tests/qtest/igb-test.c new file mode 100644 index 0000000000..3d397ea697 --- /dev/null +++ b/tests/qtest/igb-test.c @@ -0,0 +1,256 @@ +/* + * QTest testcase for igb NIC + * + * Copyright (c) 2022-2023 Red Hat, Inc. + * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Akihiko Odaki <akihiko.odaki@daynix.com> + * Dmitry Fleytman <dmitry@daynix.com> + * Leonid Bloch <leonid@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "libqos/pci-pc.h" +#include "net/eth.h" +#include "qemu/sockets.h" +#include "qemu/iov.h" +#include "qemu/module.h" +#include "qemu/bitops.h" +#include "libqos/libqos-malloc.h" +#include "libqos/e1000e.h" +#include "hw/net/igb_regs.h" + +#ifndef _WIN32 + +static const struct eth_header packet = { + .h_dest = E1000E_ADDRESS, + .h_source = E1000E_ADDRESS, +}; + +static void igb_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) +{ + union e1000_adv_tx_desc descr; + char buffer[64]; + int ret; + uint32_t recv_len; + + /* Prepare test data buffer */ + uint64_t data = guest_alloc(alloc, sizeof(buffer)); + memwrite(data, &packet, sizeof(packet)); + + /* Prepare TX descriptor */ + memset(&descr, 0, sizeof(descr)); + descr.read.buffer_addr = cpu_to_le64(data); + descr.read.cmd_type_len = cpu_to_le32(E1000_TXD_CMD_RS | + E1000_TXD_CMD_EOP | + E1000_TXD_DTYP_D | + sizeof(buffer)); + + /* 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.wb.status) & E1000_TXD_STAT_DD, ==, + E1000_TXD_STAT_DD); + + /* Check data sent to the backend */ + ret = recv(test_sockets[0], &recv_len, sizeof(recv_len), 0); + g_assert_cmpint(ret, == , sizeof(recv_len)); + ret = recv(test_sockets[0], buffer, sizeof(buffer), 0); + g_assert_cmpint(ret, ==, sizeof(buffer)); + g_assert_false(memcmp(buffer, &packet, sizeof(packet))); + + /* Free test data buffer */ + guest_free(alloc, data); +} + +static void igb_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) +{ + union e1000_adv_rx_desc descr; + + struct eth_header test_iov = packet; + int len = htonl(sizeof(packet)); + struct iovec iov[] = { + { + .iov_base = &len, + .iov_len = sizeof(len), + },{ + .iov_base = &test_iov, + .iov_len = sizeof(packet), + }, + }; + + 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(packet)); + g_assert_cmpint(ret, == , sizeof(packet) + sizeof(len)); + + /* Prepare test data buffer */ + uint64_t data = guest_alloc(alloc, sizeof(buffer)); + + /* Prepare RX descriptor */ + memset(&descr, 0, sizeof(descr)); + descr.read.pkt_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) & + E1000_RXD_STAT_DD, ==, E1000_RXD_STAT_DD); + + /* Check data sent to the backend */ + memread(data, buffer, sizeof(buffer)); + g_assert_false(memcmp(buffer, &packet, sizeof(packet))); + + /* 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_igb_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; + } + + igb_send_verify(d, data, alloc); +} + +static void test_igb_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; + } + + igb_receive_verify(d, data, alloc); +} + +static void test_igb_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++) { + igb_send_verify(d, data, alloc); + igb_receive_verify(d, data, alloc); + } + +} + +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; +} + +#endif + +static void *data_test_init_no_socket(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 "); + return arg; +} + +static void test_igb_hotplug(void *obj, void *data, QGuestAllocator * alloc) +{ + QTestState *qts = global_qtest; /* TODO: get rid of global_qtest here */ + QE1000E_PCI *dev = obj; + + if (dev->pci_dev.bus->not_hotpluggable) { + g_test_skip("pci bus does not support hotplug"); + return; + } + + qtest_qmp_device_add(qts, "igb", "igb_net", "{'addr': '0x06'}"); + qpci_unplug_acpi_device_test(qts, "igb_net", 0x06); +} + +static void register_igb_test(void) +{ + QOSGraphTestOptions opts = { 0 }; + +#ifndef _WIN32 + opts.before = data_test_init, + qos_add_test("init", "igb", test_e1000e_init, &opts); + qos_add_test("tx", "igb", test_igb_tx, &opts); + qos_add_test("rx", "igb", test_igb_rx, &opts); + qos_add_test("multiple_transfers", "igb", + test_igb_multiple_transfers, &opts); +#endif + + opts.before = data_test_init_no_socket; + qos_add_test("hotplug", "igb", test_igb_hotplug, &opts); +} + +libqos_init(register_igb_test); diff --git a/tests/qtest/libqos/e1000e.c b/tests/qtest/libqos/e1000e.c index 28fb3052aa..925654c7fd 100644 --- a/tests/qtest/libqos/e1000e.c +++ b/tests/qtest/libqos/e1000e.c @@ -36,18 +36,6 @@ #define E1000E_RING_LEN (0x1000) -static void e1000e_macreg_write(QE1000E *d, uint32_t reg, uint32_t val) -{ - QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); - qpci_io_writel(&d_pci->pci_dev, d_pci->mac_regs, reg, val); -} - -static uint32_t e1000e_macreg_read(QE1000E *d, uint32_t reg) -{ - QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); - return qpci_io_readl(&d_pci->pci_dev, d_pci->mac_regs, reg); -} - void e1000e_tx_ring_push(QE1000E *d, void *descr) { QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); diff --git a/tests/qtest/libqos/e1000e.h b/tests/qtest/libqos/e1000e.h index 091ce139da..30643c8094 100644 --- a/tests/qtest/libqos/e1000e.h +++ b/tests/qtest/libqos/e1000e.h @@ -25,6 +25,8 @@ #define E1000E_RX0_MSG_ID (0) #define E1000E_TX0_MSG_ID (1) +#define E1000E_ADDRESS { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 } + typedef struct QE1000E QE1000E; typedef struct QE1000E_PCI QE1000E_PCI; @@ -40,6 +42,18 @@ struct QE1000E_PCI { QE1000E e1000e; }; +static inline void e1000e_macreg_write(QE1000E *d, uint32_t reg, uint32_t val) +{ + QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); + qpci_io_writel(&d_pci->pci_dev, d_pci->mac_regs, reg, val); +} + +static inline uint32_t e1000e_macreg_read(QE1000E *d, uint32_t reg) +{ + QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); + return qpci_io_readl(&d_pci->pci_dev, d_pci->mac_regs, reg); +} + void e1000e_wait_isr(QE1000E *d, uint16_t msg_id); void e1000e_tx_ring_push(QE1000E *d, void *descr); void e1000e_rx_ring_push(QE1000E *d, void *descr); diff --git a/tests/qtest/libqos/igb.c b/tests/qtest/libqos/igb.c new file mode 100644 index 0000000000..12fb531bf0 --- /dev/null +++ b/tests/qtest/libqos/igb.c @@ -0,0 +1,185 @@ +/* + * libqos driver framework + * + * Copyright (c) 2022-2023 Red Hat, Inc. + * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com> + * + * 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.1 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 <http://www.gnu.org/licenses/> + */ + +#include "qemu/osdep.h" +#include "hw/net/igb_regs.h" +#include "hw/net/mii.h" +#include "hw/pci/pci_ids.h" +#include "../libqtest.h" +#include "pci-pc.h" +#include "qemu/sockets.h" +#include "qemu/iov.h" +#include "qemu/module.h" +#include "qemu/bitops.h" +#include "libqos-malloc.h" +#include "qgraph.h" +#include "e1000e.h" + +#define IGB_IVAR_TEST_CFG \ + ((E1000E_RX0_MSG_ID | E1000_IVAR_VALID) << (igb_ivar_entry_rx(0) * 8) | \ + ((E1000E_TX0_MSG_ID | E1000_IVAR_VALID) << (igb_ivar_entry_tx(0) * 8))) + +#define E1000E_RING_LEN (0x1000) + +static void e1000e_foreach_callback(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice *res = data; + memcpy(res, dev, sizeof(QPCIDevice)); + g_free(dev); +} + +static void e1000e_pci_destructor(QOSGraphObject *obj) +{ + QE1000E_PCI *epci = (QE1000E_PCI *) obj; + qpci_iounmap(&epci->pci_dev, epci->mac_regs); + qpci_msix_disable(&epci->pci_dev); +} + +static void igb_pci_start_hw(QOSGraphObject *obj) +{ + static const uint8_t address[] = E1000E_ADDRESS; + QE1000E_PCI *d = (QE1000E_PCI *) obj; + uint32_t val; + + /* Enable the device */ + qpci_device_enable(&d->pci_dev); + + /* Reset the device */ + val = e1000e_macreg_read(&d->e1000e, E1000_CTRL); + e1000e_macreg_write(&d->e1000e, E1000_CTRL, val | E1000_CTRL_RST | E1000_CTRL_SLU); + + /* Setup link */ + e1000e_macreg_write(&d->e1000e, E1000_MDIC, + MII_BMCR_AUTOEN | MII_BMCR_ANRESTART | + (MII_BMCR << E1000_MDIC_REG_SHIFT) | + (1 << E1000_MDIC_PHY_SHIFT) | + E1000_MDIC_OP_WRITE); + + qtest_clock_step(d->pci_dev.bus->qts, 900000000); + + /* Enable and configure MSI-X */ + qpci_msix_enable(&d->pci_dev); + e1000e_macreg_write(&d->e1000e, E1000_IVAR0, IGB_IVAR_TEST_CFG); + + /* Check the device link status */ + val = e1000e_macreg_read(&d->e1000e, E1000_STATUS); + g_assert_cmphex(val & E1000_STATUS_LU, ==, E1000_STATUS_LU); + + /* Initialize TX/RX logic */ + e1000e_macreg_write(&d->e1000e, E1000_RCTL, 0); + e1000e_macreg_write(&d->e1000e, E1000_TCTL, 0); + + e1000e_macreg_write(&d->e1000e, E1000_TDBAL(0), + (uint32_t) d->e1000e.tx_ring); + e1000e_macreg_write(&d->e1000e, E1000_TDBAH(0), + (uint32_t) (d->e1000e.tx_ring >> 32)); + e1000e_macreg_write(&d->e1000e, E1000_TDLEN(0), E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000_TDT(0), 0); + e1000e_macreg_write(&d->e1000e, E1000_TDH(0), 0); + + /* Enable transmit */ + e1000e_macreg_write(&d->e1000e, E1000_TCTL, E1000_TCTL_EN); + + e1000e_macreg_write(&d->e1000e, E1000_RDBAL(0), + (uint32_t)d->e1000e.rx_ring); + e1000e_macreg_write(&d->e1000e, E1000_RDBAH(0), + (uint32_t)(d->e1000e.rx_ring >> 32)); + e1000e_macreg_write(&d->e1000e, E1000_RDLEN(0), E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000_RDT(0), 0); + e1000e_macreg_write(&d->e1000e, E1000_RDH(0), 0); + e1000e_macreg_write(&d->e1000e, E1000_RA, + le32_to_cpu(*(uint32_t *)address)); + e1000e_macreg_write(&d->e1000e, E1000_RA + 4, + E1000_RAH_AV | E1000_RAH_POOL_1 | + le16_to_cpu(*(uint16_t *)(address + 4))); + + /* Enable receive */ + e1000e_macreg_write(&d->e1000e, E1000_RFCTL, E1000_RFCTL_EXTEN); + e1000e_macreg_write(&d->e1000e, E1000_RCTL, E1000_RCTL_EN); + + /* Enable all interrupts */ + e1000e_macreg_write(&d->e1000e, E1000_IMS, 0xFFFFFFFF); + e1000e_macreg_write(&d->e1000e, E1000_EIMS, 0xFFFFFFFF); + +} + +static void *igb_pci_get_driver(void *obj, const char *interface) +{ + QE1000E_PCI *epci = obj; + if (!g_strcmp0(interface, "igb-if")) { + return &epci->e1000e; + } + + /* implicit contains */ + if (!g_strcmp0(interface, "pci-device")) { + return &epci->pci_dev; + } + + fprintf(stderr, "%s not present in igb\n", interface); + g_assert_not_reached(); +} + +static void *igb_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QE1000E_PCI *d = g_new0(QE1000E_PCI, 1); + QPCIBus *bus = pci_bus; + QPCIAddress *address = addr; + + qpci_device_foreach(bus, address->vendor_id, address->device_id, + e1000e_foreach_callback, &d->pci_dev); + + /* Map BAR0 (mac registers) */ + d->mac_regs = qpci_iomap(&d->pci_dev, 0, NULL); + + /* Allocate and setup TX ring */ + d->e1000e.tx_ring = guest_alloc(alloc, E1000E_RING_LEN); + g_assert(d->e1000e.tx_ring != 0); + + /* Allocate and setup RX ring */ + d->e1000e.rx_ring = guest_alloc(alloc, E1000E_RING_LEN); + g_assert(d->e1000e.rx_ring != 0); + + d->obj.get_driver = igb_pci_get_driver; + d->obj.start_hw = igb_pci_start_hw; + d->obj.destructor = e1000e_pci_destructor; + + return &d->obj; +} + +static void igb_register_nodes(void) +{ + QPCIAddress addr = { + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = E1000_DEV_ID_82576, + }; + + /* + * FIXME: every test using this node needs to setup a -netdev socket,id=hs0 + * otherwise QEMU is not going to start + */ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "netdev=hs0", + }; + add_qpci_address(&opts, &addr); + + qos_node_create_driver("igb", igb_pci_create); + qos_node_consumes("igb", "pci-bus", &opts); +} + +libqos_init(igb_register_nodes); diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index 32f028872c..cc209a8de5 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -30,6 +30,7 @@ libqos_srcs = files( 'i2c.c', 'i2c-imx.c', 'i2c-omap.c', + 'igb.c', 'sdhci.c', 'tpci200.c', 'virtio.c', diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 2bfd460531..c3a0ef5bb4 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -124,7 +124,7 @@ static int socket_accept(int sock) (void *)&timeout, sizeof(timeout))) { fprintf(stderr, "%s failed to set SO_RCVTIMEO: %s\n", __func__, strerror(errno)); - closesocket(sock); + close(sock); return -1; } @@ -135,7 +135,7 @@ static int socket_accept(int sock) if (ret == -1) { fprintf(stderr, "%s failed: %s\n", __func__, strerror(errno)); } - closesocket(sock); + close(sock); return ret; } @@ -564,8 +564,8 @@ void qtest_quit(QTestState *s) qtest_remove_abrt_handler(s); qtest_kill_qemu(s); - closesocket(s->fd); - closesocket(s->qmp_fd); + close(s->fd); + close(s->qmp_fd); g_string_free(s->rx, true); for (GList *it = s->pending_events; it != NULL; it = it->next) { @@ -1478,13 +1478,28 @@ void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, qobject_unref(args); } -#ifndef _WIN32 void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd) { QDict *resp; +#ifdef WIN32 + WSAPROTOCOL_INFOW info; + g_autofree char *info64 = NULL; + SOCKET s; + + assert(fd_is_socket(fd)); + s = _get_osfhandle(fd); + if (WSADuplicateSocketW(s, GetProcessId((HANDLE)qts->qemu_pid), &info) == SOCKET_ERROR) { + g_autofree char *emsg = g_win32_error_message(WSAGetLastError()); + g_error("WSADuplicateSocketW failed: %s", emsg); + } + info64 = g_base64_encode((guchar *)&info, sizeof(info)); + resp = qtest_qmp(qts, "{'execute': 'get-win32-socket'," + "'arguments': {'fdname': 'fdname', 'info': %s}}", info64); +#else resp = qtest_qmp_fds(qts, &fd, 1, "{'execute': 'getfd'," "'arguments': {'fdname': 'fdname'}}"); +#endif g_assert(resp); g_assert(!qdict_haskey(resp, "event")); /* We don't expect any events */ g_assert(!qdict_haskey(resp, "error")); @@ -1498,7 +1513,6 @@ void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd) g_assert(!qdict_haskey(resp, "error")); qobject_unref(resp); } -#endif /* * Generic hot-unplugging test via the device_del QMP command. diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index fcf1c3c3b3..8d7d450963 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -758,17 +758,16 @@ void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv, void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, const char *fmt, ...) G_GNUC_PRINTF(4, 5); -#ifndef _WIN32 /** * qtest_qmp_add_client: * @qts: QTestState instance to operate on * @protocol: the protocol to add to * @fd: the client file-descriptor * - * Call QMP ``getfd`` followed by ``add_client`` with the given @fd. + * Call QMP ``getfd`` (on Windows ``get-win32-socket``) followed by + * ``add_client`` with the given @fd. */ void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd); -#endif /* _WIN32 */ /** * qtest_qmp_device_del_send: diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 29a4efb4c2..85ea4e8d99 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -101,7 +101,7 @@ qtests_i386 = \ 'numa-test' ] -if dbus_display +if dbus_display and targetos != 'windows' qtests_i386 += ['dbus-display-test'] endif @@ -259,6 +259,7 @@ qos_test_ss.add( 'virtio-scsi-test.c', 'virtio-iommu-test.c', 'vmxnet3-test.c', + 'igb-test.c', ) if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL') @@ -309,10 +310,12 @@ qtests = { 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'), } -gvnc = dependency('gvnc-1.0', required: false) -if gvnc.found() - qtests += {'vnc-display-test': [gvnc]} - qtests_generic += [ 'vnc-display-test' ] +if vnc.found() + gvnc = dependency('gvnc-1.0', required: false) + if gvnc.found() + qtests += {'vnc-display-test': [gvnc]} + qtests_generic += [ 'vnc-display-test' ] + endif endif if dbus_display diff --git a/tests/qtest/microbit-test.c b/tests/qtest/microbit-test.c index 4bc267020b..6022a92b6a 100644 --- a/tests/qtest/microbit-test.c +++ b/tests/qtest/microbit-test.c @@ -107,7 +107,7 @@ static void test_nrf51_uart(void) g_assert_true(recv(sock_fd, s, 10, 0) == 5); g_assert_true(memcmp(s, "world", 5) == 0); - closesocket(sock_fd); + close(sock_fd); qtest_quit(qts); } diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index d4ab3934ed..3b615b0da9 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -408,8 +408,8 @@ static void migrate_set_parameter_str(QTestState *who, const char *parameter, static void migrate_ensure_non_converge(QTestState *who) { - /* Can't converge with 1ms downtime + 30 mbs bandwidth limit */ - migrate_set_parameter_int(who, "max-bandwidth", 30 * 1000 * 1000); + /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); migrate_set_parameter_int(who, "downtime-limit", 1); } @@ -1808,7 +1808,7 @@ static void test_migrate_auto_converge(void) * 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 init_pct = 5, inc_pct = 25, max_pct = 95; if (test_migrate_start(&from, &to, uri, &args)) { return; @@ -1835,13 +1835,16 @@ static void test_migrate_auto_converge(void) /* Wait for throttling begins */ percentage = 0; - while (percentage == 0) { + do { percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - usleep(100); + if (percentage != 0) { + break; + } + usleep(20); g_assert_false(got_stop); - } - /* The first percentage of throttling should be equal to init_pct */ - g_assert_cmpint(percentage, ==, init_pct); + } while (true); + /* The first percentage of throttling should be at least init_pct */ + g_assert_cmpint(percentage, >=, init_pct); /* Now, when we tested that throttling works, let it converge */ migrate_ensure_converge(from); @@ -2459,14 +2462,18 @@ static bool kvm_dirty_ring_supported(void) int main(int argc, char **argv) { - const bool has_kvm = qtest_has_accel("kvm"); - const bool has_uffd = ufd_version_check(); - const char *arch = qtest_get_arch(); + bool has_kvm; + bool has_uffd; + const char *arch; g_autoptr(GError) err = NULL; int ret; g_test_init(&argc, &argv, NULL); + has_kvm = qtest_has_accel("kvm"); + has_uffd = ufd_version_check(); + arch = qtest_get_arch(); + /* * 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 diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c index 270e424bee..9cf1b0698e 100644 --- a/tests/qtest/netdev-socket.c +++ b/tests/qtest/netdev-socket.c @@ -99,7 +99,7 @@ static int inet_get_free_port_multiple(int nb, int *port, bool ipv6) nb = i; for (i = 0; i < nb; i++) { - closesocket(sock[i]); + close(sock[i]); } return nb; @@ -361,8 +361,8 @@ static void test_stream_fd(void) qtest_quit(qts1); qtest_quit(qts0); - closesocket(sock[0]); - closesocket(sock[1]); + close(sock[0]); + close(sock[1]); } #endif @@ -487,8 +487,8 @@ static void test_dgram_fd(void) qtest_quit(qts1); qtest_quit(qts0); - closesocket(sv[0]); - closesocket(sv[1]); + close(sv[0]); + close(sv[1]); } #endif diff --git a/tests/qtest/readconfig-test.c b/tests/qtest/readconfig-test.c index 9ef870643d..2160603880 100644 --- a/tests/qtest/readconfig-test.c +++ b/tests/qtest/readconfig-test.c @@ -124,13 +124,15 @@ static void test_spice(void) } #endif -static void test_object_rng_resp(QObject *res) +static void test_object_available(QObject *res, const char *name, + const char *type) { Visitor *v; g_autoptr(ObjectPropertyInfoList) objs = NULL; ObjectPropertyInfoList *tmp; ObjectPropertyInfo *obj; - bool seen_rng = false; + bool object_available = false; + g_autofree char *childtype = g_strdup_printf("child<%s>", type); g_assert(res); v = qobject_input_visitor_new(res); @@ -142,16 +144,15 @@ static void test_object_rng_resp(QObject *res) g_assert(tmp->value); obj = tmp->value; - if (g_str_equal(obj->name, "rng0") && - g_str_equal(obj->type, "child<rng-builtin>")) { - seen_rng = true; + if (g_str_equal(obj->name, name) && g_str_equal(obj->type, childtype)) { + object_available = true; break; } tmp = tmp->next; } - g_assert(seen_rng); + g_assert(object_available); visit_free(v); } @@ -170,7 +171,27 @@ static void test_object_rng(void) resp = qtest_qmp(qts, "{ 'execute': 'qom-list'," " 'arguments': {'path': '/objects' }}"); - test_object_rng_resp(qdict_get(resp, "return")); + test_object_available(qdict_get(resp, "return"), "rng0", "rng-builtin"); + qobject_unref(resp); + + qtest_quit(qts); +} + +static void test_docs_config_ich9(void) +{ + QTestState *qts; + QDict *resp; + QObject *qobj; + + qts = qtest_initf("-nodefaults -readconfig docs/config/ich9-ehci-uhci.cfg"); + + resp = qtest_qmp(qts, "{ 'execute': 'qom-list'," + " 'arguments': {'path': '/machine/peripheral' }}"); + qobj = qdict_get(resp, "return"); + test_object_available(qobj, "ehci", "ich9-usb-ehci1"); + test_object_available(qobj, "uhci-1", "ich9-usb-uhci1"); + test_object_available(qobj, "uhci-2", "ich9-usb-uhci2"); + test_object_available(qobj, "uhci-3", "ich9-usb-uhci3"); qobject_unref(resp); qtest_quit(qts); @@ -186,6 +207,7 @@ int main(int argc, char *argv[]) if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64")) { qtest_add_func("readconfig/x86/memdev", test_x86_memdev); + qtest_add_func("readconfig/x86/ich9-ehci-uhci", test_docs_config_ich9); } #ifdef CONFIG_SPICE qtest_add_func("readconfig/spice", test_spice); diff --git a/tests/qtest/vnc-display-test.c b/tests/qtest/vnc-display-test.c index e52a4326ec..f8933b0761 100644 --- a/tests/qtest/vnc-display-test.c +++ b/tests/qtest/vnc-display-test.c @@ -19,7 +19,7 @@ typedef struct Test { GMainLoop *loop; } Test; -#if !defined(WIN32) && !defined(CONFIG_DARWIN) +#if !defined(CONFIG_DARWIN) static void on_vnc_error(VncConnection* self, const char* msg) @@ -38,10 +38,7 @@ static void on_vnc_auth_failure(VncConnection *self, static bool test_setup(Test *test) { -#ifdef WIN32 - g_test_skip("Not supported on Windows yet"); - return false; -#elif defined(CONFIG_DARWIN) +#if defined(CONFIG_DARWIN) g_test_skip("Broken on Darwin"); return false; #else @@ -59,7 +56,12 @@ test_setup(Test *test) g_signal_connect(test->conn, "vnc-auth-failure", G_CALLBACK(on_vnc_auth_failure), NULL); vnc_connection_set_auth_type(test->conn, VNC_CONNECTION_AUTH_NONE); + +#ifdef WIN32 + vnc_connection_open_fd(test->conn, _get_osfhandle(pair[0])); +#else vnc_connection_open_fd(test->conn, pair[0]); +#endif test->loop = g_main_loop_new(NULL, FALSE); return true; diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index a3b0aaf8af..8318caf924 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -201,3 +201,10 @@ clean: distclean: rm -f config-cc.mak config-target.mak ../config-$(TARGET).mak + +.PHONY: help +help: + @echo "TCG tests help $(TARGET_NAME)" + @echo "Built with $(CC)" + @echo "Available tests:" + @$(foreach t,$(RUN_TESTS),echo " $t";) diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index db122ab4ff..9e91a20b0d 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -81,7 +81,7 @@ sha512-vector: sha512.c TESTS += sha512-vector -ifneq ($(HAVE_GDB_BIN),) +ifeq ($(HOST_GDB_SUPPORTS_ARCH),y) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py run-gdbstub-sysregs: sysregs diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 18e6a5969e..0d82dfa76e 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -1,5 +1,5 @@ ## -## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -45,6 +45,10 @@ HEX_TESTS += fpstuff HEX_TESTS += overflow HEX_TESTS += signal_context HEX_TESTS += reg_mut +HEX_TESTS += vector_add_int +HEX_TESTS += scatter_gather +HEX_TESTS += hvx_misc +HEX_TESTS += hvx_histogram HEX_TESTS += test_abs HEX_TESTS += test_bitcnt @@ -78,3 +82,10 @@ TESTS += $(HEX_TESTS) usr: usr.c $(CC) $(CFLAGS) -mv67t -O2 -Wno-inline-asm -Wno-expansion-to-defined $< -o $@ $(LDFLAGS) +scatter_gather: CFLAGS += -mhvx +vector_add_int: CFLAGS += -mhvx -fvectorize +hvx_misc: CFLAGS += -mhvx +hvx_histogram: CFLAGS += -mhvx -Wno-gnu-folding-constant + +hvx_histogram: hvx_histogram.c hvx_histogram_row.S + $(CC) $(CFLAGS) $(CROSS_CC_GUEST_CFLAGS) $^ -o $@ $(LDFLAGS) diff --git a/tests/tcg/hexagon/fpstuff.c b/tests/tcg/hexagon/fpstuff.c index 56bf562a40..90ce9a6ef3 100644 --- a/tests/tcg/hexagon/fpstuff.c +++ b/tests/tcg/hexagon/fpstuff.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2020-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2020-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,6 +40,7 @@ const int SF_HEX_NAN = 0xffffffff; const int SF_small_neg = 0xab98fba8; const int SF_denorm = 0x00000001; const int SF_random = 0x346001d6; +const int SF_neg_zero = 0x80000000; const long long DF_QNaN = 0x7ff8000000000000ULL; const long long DF_SNaN = 0x7ff7000000000000ULL; @@ -536,6 +537,33 @@ static void check_sffixupd(void) check32(result, 0x146001d6); } +static void check_sffms(void) +{ + int result; + + /* Check that sffms properly deals with -0 */ + result = SF_neg_zero; + asm ("%0 -= sfmpy(%1 , %2)\n\t" + : "+r"(result) + : "r"(SF_ZERO), "r"(SF_ZERO) + : "r12", "r8"); + check32(result, SF_neg_zero); + + result = SF_ZERO; + asm ("%0 -= sfmpy(%1 , %2)\n\t" + : "+r"(result) + : "r"(SF_neg_zero), "r"(SF_ZERO) + : "r12", "r8"); + check32(result, SF_ZERO); + + result = SF_ZERO; + asm ("%0 -= sfmpy(%1 , %2)\n\t" + : "+r"(result) + : "r"(SF_ZERO), "r"(SF_neg_zero) + : "r12", "r8"); + check32(result, SF_ZERO); +} + static void check_float2int_convs() { int res32; @@ -688,6 +716,7 @@ int main() check_invsqrta(); check_sffixupn(); check_sffixupd(); + check_sffms(); check_float2int_convs(); puts(err ? "FAIL" : "PASS"); diff --git a/tests/tcg/hexagon/preg_alias.c b/tests/tcg/hexagon/preg_alias.c index b44a8112b4..8798fbcaf3 100644 --- a/tests/tcg/hexagon/preg_alias.c +++ b/tests/tcg/hexagon/preg_alias.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -65,7 +65,7 @@ static inline void creg_alias(int cval, PRegs *pregs) : "=r"(pregs->pregs.p0), "=r"(pregs->pregs.p1), "=r"(pregs->pregs.p2), "=r"(pregs->pregs.p3) : "r"(cval) - : "p0", "p1", "p2", "p3"); + : "c4", "p0", "p1", "p2", "p3"); } int err; @@ -92,7 +92,7 @@ static inline void creg_alias_pair(unsigned int cval, PRegs *pregs) : "=r"(pregs->pregs.p0), "=r"(pregs->pregs.p1), "=r"(pregs->pregs.p2), "=r"(pregs->pregs.p3), "=r"(c5) : "r"(cval_pair) - : "p0", "p1", "p2", "p3"); + : "c4", "c5", "p0", "p1", "p2", "p3"); check(c5, 0xdeadbeef); } @@ -117,7 +117,7 @@ static void test_packet(void) "}\n\t" : "+r"(result) : "r"(0xffffffff), "r"(0xff00ffff), "r"(0x837ed653) - : "p0", "p1", "p2", "p3"); + : "c4", "p0", "p1", "p2", "p3"); check(result, old_val); /* Test a predicated store */ @@ -129,7 +129,7 @@ static void test_packet(void) "}\n\t" : : "r"(0), "r"(0xffffffff), "r"(&result) - : "p0", "p1", "p2", "p3", "memory"); + : "c4", "p0", "p1", "p2", "p3", "memory"); check(result, 0x0); } diff --git a/tests/tcg/hexagon/scatter_gather.c b/tests/tcg/hexagon/scatter_gather.c index b93eb18133..bf8b5e0317 100644 --- a/tests/tcg/hexagon/scatter_gather.c +++ b/tests/tcg/hexagon/scatter_gather.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,47 +40,6 @@ typedef long HVX_VectorPair __attribute__((__vector_size__(256))) typedef long HVX_VectorPred __attribute__((__vector_size__(128))) __attribute__((aligned(128))); -#define VSCATTER_16(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermh_128B((int)BASE, RGN, OFF, VALS) -#define VSCATTER_16_MASKED(MASK, BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermhq_128B(MASK, (int)BASE, RGN, OFF, VALS) -#define VSCATTER_32(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermw_128B((int)BASE, RGN, OFF, VALS) -#define VSCATTER_32_MASKED(MASK, BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermwq_128B(MASK, (int)BASE, RGN, OFF, VALS) -#define VSCATTER_16_32(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermhw_128B((int)BASE, RGN, OFF, VALS) -#define VSCATTER_16_32_MASKED(MASK, BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermhwq_128B(MASK, (int)BASE, RGN, OFF, VALS) -#define VSCATTER_16_ACC(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermh_add_128B((int)BASE, RGN, OFF, VALS) -#define VSCATTER_32_ACC(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermw_add_128B((int)BASE, RGN, OFF, VALS) -#define VSCATTER_16_32_ACC(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermhw_add_128B((int)BASE, RGN, OFF, VALS) - -#define VGATHER_16(DSTADDR, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermh_128B(DSTADDR, (int)BASE, RGN, OFF) -#define VGATHER_16_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermhq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF) -#define VGATHER_32(DSTADDR, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermw_128B(DSTADDR, (int)BASE, RGN, OFF) -#define VGATHER_32_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermwq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF) -#define VGATHER_16_32(DSTADDR, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermhw_128B(DSTADDR, (int)BASE, RGN, OFF) -#define VGATHER_16_32_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermhwq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF) - -#define VSHUFF_H(V) \ - __builtin_HEXAGON_V6_vshuffh_128B(V) -#define VSPLAT_H(X) \ - __builtin_HEXAGON_V6_lvsplath_128B(X) -#define VAND_VAL(PRED, VAL) \ - __builtin_HEXAGON_V6_vandvrt_128B(PRED, VAL) -#define VDEAL_H(V) \ - __builtin_HEXAGON_V6_vdealh_128B(V) - int err; /* define the number of rows/cols in a square matrix */ @@ -108,22 +67,22 @@ unsigned short vscatter16_32_ref[SCATTER_BUFFER_SIZE]; unsigned short vgather16_32_ref[MATRIX_SIZE]; /* declare the arrays of offsets */ -unsigned short half_offsets[MATRIX_SIZE]; -unsigned int word_offsets[MATRIX_SIZE]; +unsigned short half_offsets[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned int word_offsets[MATRIX_SIZE] __attribute__((aligned(128))); /* declare the arrays of values */ -unsigned short half_values[MATRIX_SIZE]; -unsigned short half_values_acc[MATRIX_SIZE]; -unsigned short half_values_masked[MATRIX_SIZE]; -unsigned int word_values[MATRIX_SIZE]; -unsigned int word_values_acc[MATRIX_SIZE]; -unsigned int word_values_masked[MATRIX_SIZE]; +unsigned short half_values[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned short half_values_acc[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned short half_values_masked[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned int word_values[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned int word_values_acc[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned int word_values_masked[MATRIX_SIZE] __attribute__((aligned(128))); /* declare the arrays of predicates */ -unsigned short half_predicates[MATRIX_SIZE]; -unsigned int word_predicates[MATRIX_SIZE]; +unsigned short half_predicates[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned int word_predicates[MATRIX_SIZE] __attribute__((aligned(128))); -/* make this big enough for all the intrinsics */ +/* make this big enough for all the operations */ const size_t region_len = sizeof(vtcm); /* optionally add sync instructions */ @@ -261,164 +220,201 @@ void create_offsets_values_preds_16_32(void) } } -/* scatter the 16 bit elements using intrinsics */ +/* scatter the 16 bit elements using HVX */ void vector_scatter_16(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsets = *(HVX_Vector *)half_offsets; - HVX_Vector values = *(HVX_Vector *)half_values; - - VSCATTER_16(&vtcm.vscatter16, region_len, offsets, values); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.h).h = v1\n\t" + : : "r"(vtcm.vscatter16), "r"(region_len), + "r"(half_offsets), "r"(half_values) + : "m0", "v0", "v1", "memory"); sync_scatter(vtcm.vscatter16); } -/* scatter-accumulate the 16 bit elements using intrinsics */ +/* scatter-accumulate the 16 bit elements using HVX */ void vector_scatter_16_acc(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsets = *(HVX_Vector *)half_offsets; - HVX_Vector values = *(HVX_Vector *)half_values_acc; - - VSCATTER_16_ACC(&vtcm.vscatter16, region_len, offsets, values); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.h).h += v1\n\t" + : : "r"(vtcm.vscatter16), "r"(region_len), + "r"(half_offsets), "r"(half_values_acc) + : "m0", "v0", "v1", "memory"); sync_scatter(vtcm.vscatter16); } -/* scatter the 16 bit elements using intrinsics */ +/* masked scatter the 16 bit elements using HVX */ void vector_scatter_16_masked(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsets = *(HVX_Vector *)half_offsets; - HVX_Vector values = *(HVX_Vector *)half_values_masked; - HVX_Vector pred_reg = *(HVX_Vector *)half_predicates; - HVX_VectorPred preds = VAND_VAL(pred_reg, ~0); - - VSCATTER_16_MASKED(preds, &vtcm.vscatter16, region_len, offsets, values); + asm ("r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v1 = vmem(%4 + #0)\n\t" + "if (q0) vscatter(%1, m0, v0.h).h = v1\n\t" + : : "r"(half_predicates), "r"(vtcm.vscatter16), "r"(region_len), + "r"(half_offsets), "r"(half_values_masked) + : "r1", "q0", "m0", "q0", "v0", "v1", "memory"); sync_scatter(vtcm.vscatter16); } -/* scatter the 32 bit elements using intrinsics */ +/* scatter the 32 bit elements using HVX */ void vector_scatter_32(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; - HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; - HVX_Vector valueslo = *(HVX_Vector *)word_values; - HVX_Vector valueshi = *(HVX_Vector *)&word_values[MATRIX_SIZE / 2]; - - VSCATTER_32(&vtcm.vscatter32, region_len, offsetslo, valueslo); - VSCATTER_32(&vtcm.vscatter32, region_len, offsetshi, valueshi); + HVX_Vector *offsetslo = (HVX_Vector *)word_offsets; + HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector *valueslo = (HVX_Vector *)word_values; + HVX_Vector *valueshi = (HVX_Vector *)&word_values[MATRIX_SIZE / 2]; + + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.w).w = v1\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetslo), "r"(valueslo) + : "m0", "v0", "v1", "memory"); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.w).w = v1\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetshi), "r"(valueshi) + : "m0", "v0", "v1", "memory"); sync_scatter(vtcm.vscatter32); } -/* scatter-acc the 32 bit elements using intrinsics */ +/* scatter-accumulate the 32 bit elements using HVX */ void vector_scatter_32_acc(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; - HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; - HVX_Vector valueslo = *(HVX_Vector *)word_values_acc; - HVX_Vector valueshi = *(HVX_Vector *)&word_values_acc[MATRIX_SIZE / 2]; - - VSCATTER_32_ACC(&vtcm.vscatter32, region_len, offsetslo, valueslo); - VSCATTER_32_ACC(&vtcm.vscatter32, region_len, offsetshi, valueshi); + HVX_Vector *offsetslo = (HVX_Vector *)word_offsets; + HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector *valueslo = (HVX_Vector *)word_values_acc; + HVX_Vector *valueshi = (HVX_Vector *)&word_values_acc[MATRIX_SIZE / 2]; + + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.w).w += v1\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetslo), "r"(valueslo) + : "m0", "v0", "v1", "memory"); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.w).w += v1\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetshi), "r"(valueshi) + : "m0", "v0", "v1", "memory"); sync_scatter(vtcm.vscatter32); } -/* scatter the 32 bit elements using intrinsics */ +/* masked scatter the 32 bit elements using HVX */ void vector_scatter_32_masked(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; - HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; - HVX_Vector valueslo = *(HVX_Vector *)word_values_masked; - HVX_Vector valueshi = *(HVX_Vector *)&word_values_masked[MATRIX_SIZE / 2]; - HVX_Vector pred_reglo = *(HVX_Vector *)word_predicates; - HVX_Vector pred_reghi = *(HVX_Vector *)&word_predicates[MATRIX_SIZE / 2]; - HVX_VectorPred predslo = VAND_VAL(pred_reglo, ~0); - HVX_VectorPred predshi = VAND_VAL(pred_reghi, ~0); - - VSCATTER_32_MASKED(predslo, &vtcm.vscatter32, region_len, offsetslo, - valueslo); - VSCATTER_32_MASKED(predshi, &vtcm.vscatter32, region_len, offsetshi, - valueshi); + HVX_Vector *offsetslo = (HVX_Vector *)word_offsets; + HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector *valueslo = (HVX_Vector *)word_values_masked; + HVX_Vector *valueshi = (HVX_Vector *)&word_values_masked[MATRIX_SIZE / 2]; + HVX_Vector *predslo = (HVX_Vector *)word_predicates; + HVX_Vector *predshi = (HVX_Vector *)&word_predicates[MATRIX_SIZE / 2]; + + asm ("r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v1 = vmem(%4 + #0)\n\t" + "if (q0) vscatter(%1, m0, v0.w).w = v1\n\t" + : : "r"(predslo), "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetslo), "r"(valueslo) + : "r1", "q0", "m0", "q0", "v0", "v1", "memory"); + asm ("r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v1 = vmem(%4 + #0)\n\t" + "if (q0) vscatter(%1, m0, v0.w).w = v1\n\t" + : : "r"(predshi), "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetshi), "r"(valueshi) + : "r1", "q0", "m0", "q0", "v0", "v1", "memory"); - sync_scatter(vtcm.vscatter16); + sync_scatter(vtcm.vscatter32); } -/* scatter the 16 bit elements with 32 bit offsets using intrinsics */ +/* scatter the 16 bit elements with 32 bit offsets using HVX */ void vector_scatter_16_32(void) { - HVX_VectorPair offsets; - HVX_Vector values; - - /* get the word offsets in a vector pair */ - offsets = *(HVX_VectorPair *)word_offsets; - - /* these values need to be shuffled for the scatter */ - values = *(HVX_Vector *)half_values; - values = VSHUFF_H(values); - - VSCATTER_16_32(&vtcm.vscatter16_32, region_len, offsets, values); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%2 + #1)\n\t" + "v2 = vmem(%3 + #0)\n\t" + "v2.h = vshuff(v2.h)\n\t" /* shuffle the values for the scatter */ + "vscatter(%0, m0, v1:0.w).h = v2\n\t" + : : "r"(vtcm.vscatter16_32), "r"(region_len), + "r"(word_offsets), "r"(half_values) + : "m0", "v0", "v1", "v2", "memory"); sync_scatter(vtcm.vscatter16_32); } -/* scatter-acc the 16 bit elements with 32 bit offsets using intrinsics */ +/* scatter-accumulate the 16 bit elements with 32 bit offsets using HVX */ void vector_scatter_16_32_acc(void) { - HVX_VectorPair offsets; - HVX_Vector values; - - /* get the word offsets in a vector pair */ - offsets = *(HVX_VectorPair *)word_offsets; - - /* these values need to be shuffled for the scatter */ - values = *(HVX_Vector *)half_values_acc; - values = VSHUFF_H(values); - - VSCATTER_16_32_ACC(&vtcm.vscatter16_32, region_len, offsets, values); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%2 + #1)\n\t" + "v2 = vmem(%3 + #0)\n\t" \ + "v2.h = vshuff(v2.h)\n\t" /* shuffle the values for the scatter */ + "vscatter(%0, m0, v1:0.w).h += v2\n\t" + : : "r"(vtcm.vscatter16_32), "r"(region_len), + "r"(word_offsets), "r"(half_values_acc) + : "m0", "v0", "v1", "v2", "memory"); sync_scatter(vtcm.vscatter16_32); } -/* masked scatter the 16 bit elements with 32 bit offsets using intrinsics */ +/* masked scatter the 16 bit elements with 32 bit offsets using HVX */ void vector_scatter_16_32_masked(void) { - HVX_VectorPair offsets; - HVX_Vector values; - HVX_Vector pred_reg; - - /* get the word offsets in a vector pair */ - offsets = *(HVX_VectorPair *)word_offsets; - - /* these values need to be shuffled for the scatter */ - values = *(HVX_Vector *)half_values_masked; - values = VSHUFF_H(values); - - pred_reg = *(HVX_Vector *)half_predicates; - pred_reg = VSHUFF_H(pred_reg); - HVX_VectorPred preds = VAND_VAL(pred_reg, ~0); - - VSCATTER_16_32_MASKED(preds, &vtcm.vscatter16_32, region_len, offsets, - values); + asm ("r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "v0.h = vshuff(v0.h)\n\t" /* shuffle the predicates */ + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v1 = vmem(%3 + #1)\n\t" + "v2 = vmem(%4 + #0)\n\t" \ + "v2.h = vshuff(v2.h)\n\t" /* shuffle the values for the scatter */ + "if (q0) vscatter(%1, m0, v1:0.w).h = v2\n\t" + : : "r"(half_predicates), "r"(vtcm.vscatter16_32), "r"(region_len), + "r"(word_offsets), "r"(half_values_masked) + : "r1", "q0", "m0", "v0", "v1", "v2", "memory"); sync_scatter(vtcm.vscatter16_32); } -/* gather the elements from the scatter16 buffer */ +/* gather the elements from the scatter16 buffer using HVX */ void vector_gather_16(void) { - HVX_Vector *vgather = (HVX_Vector *)&vtcm.vgather16; - HVX_Vector offsets = *(HVX_Vector *)half_offsets; - - VGATHER_16(vgather, &vtcm.vscatter16, region_len, offsets); - - sync_gather(vgather); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "{ vtmp.h = vgather(%0, m0, v0.h).h\n\t" + " vmem(%3 + #0) = vtmp.new }\n\t" + : : "r"(vtcm.vscatter16), "r"(region_len), + "r"(half_offsets), "r"(vtcm.vgather16) + : "m0", "v0", "memory"); + + sync_gather(vtcm.vgather16); } static unsigned short gather_16_masked_init(void) @@ -427,31 +423,51 @@ static unsigned short gather_16_masked_init(void) return letter | (letter << 8); } +/* masked gather the elements from the scatter16 buffer using HVX */ void vector_gather_16_masked(void) { - HVX_Vector *vgather = (HVX_Vector *)&vtcm.vgather16; - HVX_Vector offsets = *(HVX_Vector *)half_offsets; - HVX_Vector pred_reg = *(HVX_Vector *)half_predicates; - HVX_VectorPred preds = VAND_VAL(pred_reg, ~0); - - *vgather = VSPLAT_H(gather_16_masked_init()); - VGATHER_16_MASKED(vgather, preds, &vtcm.vscatter16, region_len, offsets); - - sync_gather(vgather); + unsigned short init = gather_16_masked_init(); + + asm ("v0.h = vsplat(%5)\n\t" + "vmem(%4 + #0) = v0\n\t" /* initialize the write area */ + "r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "{ if (q0) vtmp.h = vgather(%1, m0, v0.h).h\n\t" + " vmem(%4 + #0) = vtmp.new }\n\t" + : : "r"(half_predicates), "r"(vtcm.vscatter16), "r"(region_len), + "r"(half_offsets), "r"(vtcm.vgather16), "r"(init) + : "r1", "q0", "m0", "v0", "memory"); + + sync_gather(vtcm.vgather16); } -/* gather the elements from the scatter32 buffer */ +/* gather the elements from the scatter32 buffer using HVX */ void vector_gather_32(void) { - HVX_Vector *vgatherlo = (HVX_Vector *)&vtcm.vgather32; - HVX_Vector *vgatherhi = - (HVX_Vector *)((int)&vtcm.vgather32 + (MATRIX_SIZE * 2)); - HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; - HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; - - VGATHER_32(vgatherlo, &vtcm.vscatter32, region_len, offsetslo); - VGATHER_32(vgatherhi, &vtcm.vscatter32, region_len, offsetshi); + HVX_Vector *vgatherlo = (HVX_Vector *)vtcm.vgather32; + HVX_Vector *vgatherhi = (HVX_Vector *)&vtcm.vgather32[MATRIX_SIZE / 2]; + HVX_Vector *offsetslo = (HVX_Vector *)word_offsets; + HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "{ vtmp.w = vgather(%0, m0, v0.w).w\n\t" + " vmem(%3 + #0) = vtmp.new }\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetslo), "r"(vgatherlo) + : "m0", "v0", "memory"); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "{ vtmp.w = vgather(%0, m0, v0.w).w\n\t" + " vmem(%3 + #0) = vtmp.new }\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetshi), "r"(vgatherhi) + : "m0", "v0", "memory"); + sync_gather(vgatherlo); sync_gather(vgatherhi); } @@ -461,79 +477,88 @@ static unsigned int gather_32_masked_init(void) return letter | (letter << 8) | (letter << 16) | (letter << 24); } +/* masked gather the elements from the scatter32 buffer using HVX */ void vector_gather_32_masked(void) { - HVX_Vector *vgatherlo = (HVX_Vector *)&vtcm.vgather32; - HVX_Vector *vgatherhi = - (HVX_Vector *)((int)&vtcm.vgather32 + (MATRIX_SIZE * 2)); - HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; - HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; - HVX_Vector pred_reglo = *(HVX_Vector *)word_predicates; - HVX_VectorPred predslo = VAND_VAL(pred_reglo, ~0); - HVX_Vector pred_reghi = *(HVX_Vector *)&word_predicates[MATRIX_SIZE / 2]; - HVX_VectorPred predshi = VAND_VAL(pred_reghi, ~0); - - *vgatherlo = VSPLAT_H(gather_32_masked_init()); - *vgatherhi = VSPLAT_H(gather_32_masked_init()); - VGATHER_32_MASKED(vgatherlo, predslo, &vtcm.vscatter32, region_len, - offsetslo); - VGATHER_32_MASKED(vgatherhi, predshi, &vtcm.vscatter32, region_len, - offsetshi); + unsigned int init = gather_32_masked_init(); + HVX_Vector *vgatherlo = (HVX_Vector *)vtcm.vgather32; + HVX_Vector *vgatherhi = (HVX_Vector *)&vtcm.vgather32[MATRIX_SIZE / 2]; + HVX_Vector *offsetslo = (HVX_Vector *)word_offsets; + HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector *predslo = (HVX_Vector *)word_predicates; + HVX_Vector *predshi = (HVX_Vector *)&word_predicates[MATRIX_SIZE / 2]; + + asm ("v0.h = vsplat(%5)\n\t" + "vmem(%4 + #0) = v0\n\t" /* initialize the write area */ + "r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "{ if (q0) vtmp.w = vgather(%1, m0, v0.w).w\n\t" + " vmem(%4 + #0) = vtmp.new }\n\t" + : : "r"(predslo), "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetslo), "r"(vgatherlo), "r"(init) + : "r1", "q0", "m0", "v0", "memory"); + asm ("v0.h = vsplat(%5)\n\t" + "vmem(%4 + #0) = v0\n\t" /* initialize the write area */ + "r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "{ if (q0) vtmp.w = vgather(%1, m0, v0.w).w\n\t" + " vmem(%4 + #0) = vtmp.new }\n\t" + : : "r"(predshi), "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetshi), "r"(vgatherhi), "r"(init) + : "r1", "q0", "m0", "v0", "memory"); sync_gather(vgatherlo); sync_gather(vgatherhi); } -/* gather the elements from the scatter16_32 buffer */ +/* gather the elements from the scatter16_32 buffer using HVX */ void vector_gather_16_32(void) { - HVX_Vector *vgather; - HVX_VectorPair offsets; - HVX_Vector values; - - /* get the vtcm address to gather from */ - vgather = (HVX_Vector *)&vtcm.vgather16_32; - - /* get the word offsets in a vector pair */ - offsets = *(HVX_VectorPair *)word_offsets; - - VGATHER_16_32(vgather, &vtcm.vscatter16_32, region_len, offsets); - - /* deal the elements to get the order back */ - values = *(HVX_Vector *)vgather; - values = VDEAL_H(values); - - /* write it back to vtcm address */ - *(HVX_Vector *)vgather = values; + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%2 + #1)\n\t" + "{ vtmp.h = vgather(%0, m0, v1:0.w).h\n\t" + " vmem(%3 + #0) = vtmp.new }\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v0.h = vdeal(v0.h)\n\t" /* deal the elements to get the order back */ + "vmem(%3 + #0) = v0\n\t" + : : "r"(vtcm.vscatter16_32), "r"(region_len), + "r"(word_offsets), "r"(vtcm.vgather16_32) + : "m0", "v0", "v1", "memory"); + + sync_gather(vtcm.vgather16_32); } +/* masked gather the elements from the scatter16_32 buffer using HVX */ void vector_gather_16_32_masked(void) { - HVX_Vector *vgather; - HVX_VectorPair offsets; - HVX_Vector pred_reg; - HVX_VectorPred preds; - HVX_Vector values; - - /* get the vtcm address to gather from */ - vgather = (HVX_Vector *)&vtcm.vgather16_32; - - /* get the word offsets in a vector pair */ - offsets = *(HVX_VectorPair *)word_offsets; - pred_reg = *(HVX_Vector *)half_predicates; - pred_reg = VSHUFF_H(pred_reg); - preds = VAND_VAL(pred_reg, ~0); - - *vgather = VSPLAT_H(gather_16_masked_init()); - VGATHER_16_32_MASKED(vgather, preds, &vtcm.vscatter16_32, region_len, - offsets); - - /* deal the elements to get the order back */ - values = *(HVX_Vector *)vgather; - values = VDEAL_H(values); - - /* write it back to vtcm address */ - *(HVX_Vector *)vgather = values; + unsigned short init = gather_16_masked_init(); + + asm ("v0.h = vsplat(%5)\n\t" + "vmem(%4 + #0) = v0\n\t" /* initialize the write area */ + "r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "v0.h = vshuff(v0.h)\n\t" /* shuffle the predicates */ + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v1 = vmem(%3 + #1)\n\t" + "{ if (q0) vtmp.h = vgather(%1, m0, v1:0.w).h\n\t" + " vmem(%4 + #0) = vtmp.new }\n\t" + "v0 = vmem(%4 + #0)\n\t" + "v0.h = vdeal(v0.h)\n\t" /* deal the elements to get the order back */ + "vmem(%4 + #0) = v0\n\t" + : : "r"(half_predicates), "r"(vtcm.vscatter16_32), "r"(region_len), + "r"(word_offsets), "r"(vtcm.vgather16_32), "r"(init) + : "r1", "q0", "m0", "v0", "v1", "memory"); + + sync_gather(vtcm.vgather16_32); } static void check_buffer(const char *name, void *c, void *r, size_t size) @@ -579,6 +604,7 @@ void scalar_scatter_16_acc(unsigned short *vscatter16) } } +/* scatter-accumulate the 16 bit elements using C */ void check_scatter_16_acc() { memset(vscatter16_ref, FILL_CHAR, @@ -589,7 +615,7 @@ void check_scatter_16_acc() SCATTER_BUFFER_SIZE * sizeof(unsigned short)); } -/* scatter the 16 bit elements using C */ +/* masked scatter the 16 bit elements using C */ void scalar_scatter_16_masked(unsigned short *vscatter16) { for (int i = 0; i < MATRIX_SIZE; i++) { @@ -628,7 +654,7 @@ void check_scatter_32() SCATTER_BUFFER_SIZE * sizeof(unsigned int)); } -/* scatter the 32 bit elements using C */ +/* scatter-accumulate the 32 bit elements using C */ void scalar_scatter_32_acc(unsigned int *vscatter32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -646,7 +672,7 @@ void check_scatter_32_acc() SCATTER_BUFFER_SIZE * sizeof(unsigned int)); } -/* scatter the 32 bit elements using C */ +/* masked scatter the 32 bit elements using C */ void scalar_scatter_32_masked(unsigned int *vscatter32) { for (int i = 0; i < MATRIX_SIZE; i++) { @@ -667,7 +693,7 @@ void check_scatter_32_masked() SCATTER_BUFFER_SIZE * sizeof(unsigned int)); } -/* scatter the 32 bit elements using C */ +/* scatter the 16 bit elements with 32 bit offsets using C */ void scalar_scatter_16_32(unsigned short *vscatter16_32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -684,7 +710,7 @@ void check_scatter_16_32() SCATTER_BUFFER_SIZE * sizeof(unsigned short)); } -/* scatter the 32 bit elements using C */ +/* scatter-accumulate the 16 bit elements with 32 bit offsets using C */ void scalar_scatter_16_32_acc(unsigned short *vscatter16_32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -702,6 +728,7 @@ void check_scatter_16_32_acc() SCATTER_BUFFER_SIZE * sizeof(unsigned short)); } +/* masked scatter the 16 bit elements with 32 bit offsets using C */ void scalar_scatter_16_32_masked(unsigned short *vscatter16_32) { for (int i = 0; i < MATRIX_SIZE; i++) { @@ -738,6 +765,7 @@ void check_gather_16() MATRIX_SIZE * sizeof(unsigned short)); } +/* masked gather the elements from the scatter buffer using C */ void scalar_gather_16_masked(unsigned short *vgather16) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -756,7 +784,7 @@ void check_gather_16_masked() MATRIX_SIZE * sizeof(unsigned short)); } -/* gather the elements from the scatter buffer using C */ +/* gather the elements from the scatter32 buffer using C */ void scalar_gather_32(unsigned int *vgather32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -772,6 +800,7 @@ void check_gather_32(void) MATRIX_SIZE * sizeof(unsigned int)); } +/* masked gather the elements from the scatter32 buffer using C */ void scalar_gather_32_masked(unsigned int *vgather32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -781,7 +810,6 @@ void scalar_gather_32_masked(unsigned int *vgather32) } } - void check_gather_32_masked(void) { memset(vgather32_ref, gather_32_masked_init(), @@ -791,7 +819,7 @@ void check_gather_32_masked(void) vgather32_ref, MATRIX_SIZE * sizeof(unsigned int)); } -/* gather the elements from the scatter buffer using C */ +/* gather the elements from the scatter16_32 buffer using C */ void scalar_gather_16_32(unsigned short *vgather16_32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -807,6 +835,7 @@ void check_gather_16_32(void) MATRIX_SIZE * sizeof(unsigned short)); } +/* masked gather the elements from the scatter16_32 buffer using C */ void scalar_gather_16_32_masked(unsigned short *vgather16_32) { for (int i = 0; i < MATRIX_SIZE; ++i) { diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index ae8b3d7268..373db69648 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -64,6 +64,7 @@ run-test-mmap-%: test-mmap $(call run-test, test-mmap-$*, $(QEMU) -p $* $<, $< ($* byte pages)) ifneq ($(HAVE_GDB_BIN),) +ifeq ($(HOST_GDB_SUPPORTS_ARCH),y) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py run-gdbstub-sha1: sha1 @@ -89,6 +90,10 @@ run-gdbstub-thread-breakpoint: testthread else run-gdbstub-%: + $(call skip-test, "gdbstub test $*", "no guest arch support") +endif +else +run-gdbstub-%: $(call skip-test, "gdbstub test $*", "need working gdb") endif EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \ diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 368b64d531..5f432c95f3 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -15,6 +15,7 @@ MULTIARCH_TEST_SRCS=$(wildcard $(MULTIARCH_SYSTEM_SRC)/*.c) MULTIARCH_TESTS = $(patsubst $(MULTIARCH_SYSTEM_SRC)/%.c, %, $(MULTIARCH_TEST_SRCS)) ifneq ($(HAVE_GDB_BIN),) +ifeq ($(HOST_GDB_SUPPORTS_ARCH),y) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py run-gdbstub-memory: memory @@ -26,7 +27,10 @@ run-gdbstub-memory: memory "-monitor none -display none -chardev file$(COMMA)path=$<.out$(COMMA)id=output $(QEMU_OPTS)" \ --bin $< --test $(MULTIARCH_SRC)/gdbstub/memory.py, \ softmmu gdbstub support) - +else +run-gdbstub-%: + $(call skip-test, "gdbstub test $*", "no guest arch support") +endif else run-gdbstub-%: $(call skip-test, "gdbstub test $*", "need working gdb") diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index 725b6c598d..3e7f72abcd 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -1,11 +1,25 @@ S390X_SRC=$(SRC_PATH)/tests/tcg/s390x VPATH+=$(S390X_SRC) QEMU_OPTS=-action panic=exit-failure -kernel +LINK_SCRIPT=$(S390X_SRC)/softmmu.ld +LDFLAGS=-nostdlib -static -Wl,-T$(LINK_SCRIPT) -Wl,--build-id=none -%: %.S - $(CC) -march=z13 -m64 -nostdlib -static -Wl,-Ttext=0 \ - -Wl,--build-id=none $< -o $@ +%.o: %.S + $(CC) -march=z13 -m64 -c $< -o $@ + +%: %.o $(LINK_SCRIPT) + $(CC) $< -o $@ $(LDFLAGS) TESTS += unaligned-lowcore TESTS += bal TESTS += sam +TESTS += lpsw +TESTS += lpswe-early +TESTS += ssm-early +TESTS += stosm-early +TESTS += exrl-ssm-early + +include $(S390X_SRC)/pgm-specification.mak +$(PGM_SPECIFICATION_TESTS): pgm-specification-softmmu.o +$(PGM_SPECIFICATION_TESTS): LDFLAGS+=pgm-specification-softmmu.o +TESTS += $(PGM_SPECIFICATION_TESTS) diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 72ad309b27..0031868b13 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -2,6 +2,9 @@ S390X_SRC=$(SRC_PATH)/tests/tcg/s390x VPATH+=$(S390X_SRC) CFLAGS+=-march=zEC12 -m64 +%.o: %.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@ + config-cc.mak: Makefile $(quiet-@)( \ $(call cc-option,-march=z14, CROSS_CC_HAS_Z14); \ @@ -28,10 +31,20 @@ TESTS+=div TESTS+=clst TESTS+=long-double TESTS+=cdsg +TESTS+=chrl +TESTS+=rxsbg +TESTS+=ex-relative-long cdsg: CFLAGS+=-pthread cdsg: LDFLAGS+=-pthread +rxsbg: CFLAGS+=-O2 + +include $(S390X_SRC)/pgm-specification.mak +$(PGM_SPECIFICATION_TESTS): pgm-specification-user.o +$(PGM_SPECIFICATION_TESTS): LDFLAGS+=pgm-specification-user.o +TESTS += $(PGM_SPECIFICATION_TESTS) + Z13_TESTS=vistr $(Z13_TESTS): CFLAGS+=-march=z13 -O2 TESTS+=$(Z13_TESTS) @@ -51,7 +64,7 @@ $(Z15_TESTS): CFLAGS+=-march=z15 -O2 TESTS+=$(Z15_TESTS) endif -ifneq ($(HAVE_GDB_BIN),) +ifeq ($(HOST_GDB_SUPPORTS_ARCH),y) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py run-gdbstub-signals-s390x: signals-s390x diff --git a/tests/tcg/s390x/br-odd.S b/tests/tcg/s390x/br-odd.S new file mode 100644 index 0000000000..2fae47a9e3 --- /dev/null +++ b/tests/tcg/s390x/br-odd.S @@ -0,0 +1,16 @@ +/* + * Test BRanching to a non-mapped odd address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + lgrl %r1,odd_addr + br %r1 + + .align 8 +odd_addr: + .quad 0xDDDDDDDDDDDDDDDD + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,0xDDDDDDDDDDDDDDDD diff --git a/tests/tcg/s390x/cgrl-unaligned.S b/tests/tcg/s390x/cgrl-unaligned.S new file mode 100644 index 0000000000..164d68f2e6 --- /dev/null +++ b/tests/tcg/s390x/cgrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test CGRL with a non-doubleword aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + cgrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .long 0 +unaligned: + .quad 0 diff --git a/tests/tcg/s390x/chrl.c b/tests/tcg/s390x/chrl.c new file mode 100644 index 0000000000..b1c3a1c561 --- /dev/null +++ b/tests/tcg/s390x/chrl.c @@ -0,0 +1,80 @@ +#include <stdlib.h> +#include <assert.h> +#include <stdint.h> + +static void test_chrl(void) +{ + uint32_t program_mask, cc; + + asm volatile ( + ".pushsection .rodata\n" + "0:\n\t" + ".short 1, 0x8000\n\t" + ".popsection\n\t" + + "chrl %[r], 0b\n\t" + "ipm %[program_mask]\n" + : [program_mask] "=r" (program_mask) + : [r] "r" (1) + ); + + cc = program_mask >> 28; + assert(!cc); + + asm volatile ( + ".pushsection .rodata\n" + "0:\n\t" + ".short -1, 0x8000\n\t" + ".popsection\n\t" + + "chrl %[r], 0b\n\t" + "ipm %[program_mask]\n" + : [program_mask] "=r" (program_mask) + : [r] "r" (-1) + ); + + cc = program_mask >> 28; + assert(!cc); +} + +static void test_cghrl(void) +{ + uint32_t program_mask, cc; + + asm volatile ( + ".pushsection .rodata\n" + "0:\n\t" + ".short 1, 0x8000, 0, 0\n\t" + ".popsection\n\t" + + "cghrl %[r], 0b\n\t" + "ipm %[program_mask]\n" + : [program_mask] "=r" (program_mask) + : [r] "r" (1L) + ); + + cc = program_mask >> 28; + assert(!cc); + + asm volatile ( + ".pushsection .rodata\n" + "0:\n\t" + ".short -1, 0x8000, 0, 0\n\t" + ".popsection\n\t" + + "cghrl %[r], 0b\n\t" + "ipm %[program_mask]\n" + : [program_mask] "=r" (program_mask) + : [r] "r" (-1L) + ); + + cc = program_mask >> 28; + assert(!cc); +} + +int main(void) +{ + test_chrl(); + test_cghrl(); + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/clrl-unaligned.S b/tests/tcg/s390x/clrl-unaligned.S new file mode 100644 index 0000000000..182b1b6462 --- /dev/null +++ b/tests/tcg/s390x/clrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test CLRL with a non-word aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + clrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .short 0 +unaligned: + .long 0 diff --git a/tests/tcg/s390x/crl-unaligned.S b/tests/tcg/s390x/crl-unaligned.S new file mode 100644 index 0000000000..b86fbe0ef3 --- /dev/null +++ b/tests/tcg/s390x/crl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test CRL with a non-word aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + crl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .short 0 +unaligned: + .long 0 diff --git a/tests/tcg/s390x/ex-odd.S b/tests/tcg/s390x/ex-odd.S new file mode 100644 index 0000000000..4e42a47df3 --- /dev/null +++ b/tests/tcg/s390x/ex-odd.S @@ -0,0 +1,17 @@ +/* + * Test EXECUTing a non-mapped odd address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + lgrl %r1,odd_addr +fail: + ex 0,0(%r1) + + .align 8 +odd_addr: + .quad 0xDDDDDDDDDDDDDDDD + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,fail diff --git a/tests/tcg/s390x/ex-relative-long.c b/tests/tcg/s390x/ex-relative-long.c new file mode 100644 index 0000000000..21fbef6258 --- /dev/null +++ b/tests/tcg/s390x/ex-relative-long.c @@ -0,0 +1,156 @@ +/* Check EXECUTE with relative long instructions as targets. */ +#include <stdlib.h> +#include <stdio.h> + +struct test { + const char *name; + long (*func)(long reg, long *cc); + long exp_reg; + long exp_mem; + long exp_cc; +}; + +/* + * Each test sets the MEM_IDXth element of the mem array to MEM and uses a + * single relative long instruction on it. The other elements remain zero. + * This is in order to prevent stumbling upon MEM in random memory in case + * there is an off-by-a-small-value bug. + * + * Note that while gcc supports the ZL constraint for relative long operands, + * clang doesn't, so the assembly code accesses mem[MEM_IDX] using MEM_ASM. + */ +static long mem[0x1000]; +#define MEM_IDX 0x800 +#define MEM_ASM "mem+0x800*8" + +/* Initial %r2 value. */ +#define REG 0x1234567887654321 + +/* Initial mem[MEM_IDX] value. */ +#define MEM 0xfedcba9889abcdef + +/* Initial cc value. */ +#define CC 0 + +/* Relative long instructions and their expected effects. */ +#define FOR_EACH_INSN(F) \ + F(cgfrl, REG, MEM, 2) \ + F(cghrl, REG, MEM, 2) \ + F(cgrl, REG, MEM, 2) \ + F(chrl, REG, MEM, 1) \ + F(clgfrl, REG, MEM, 2) \ + F(clghrl, REG, MEM, 2) \ + F(clgrl, REG, MEM, 1) \ + F(clhrl, REG, MEM, 2) \ + F(clrl, REG, MEM, 1) \ + F(crl, REG, MEM, 1) \ + F(larl, (long)&mem[MEM_IDX], MEM, CC) \ + F(lgfrl, 0xfffffffffedcba98, MEM, CC) \ + F(lghrl, 0xfffffffffffffedc, MEM, CC) \ + F(lgrl, MEM, MEM, CC) \ + F(lhrl, 0x12345678fffffedc, MEM, CC) \ + F(llghrl, 0x000000000000fedc, MEM, CC) \ + F(llhrl, 0x123456780000fedc, MEM, CC) \ + F(lrl, 0x12345678fedcba98, MEM, CC) \ + F(stgrl, REG, REG, CC) \ + F(sthrl, REG, 0x4321ba9889abcdef, CC) \ + F(strl, REG, 0x8765432189abcdef, CC) + +/* Test functions. */ +#define DEFINE_EX_TEST(insn, exp_reg, exp_mem, exp_cc) \ + static long test_ex_ ## insn(long reg, long *cc) \ + { \ + register long r2 asm("r2"); \ + char mask = 0x20; /* make target use %r2 */ \ + long pm, target; \ + \ + r2 = reg; \ + asm("larl %[target],0f\n" \ + "cr %%r0,%%r0\n" /* initial cc */ \ + "ex %[mask],0(%[target])\n" \ + "jg 1f\n" \ + "0: " #insn " %%r0," MEM_ASM "\n" \ + "1: ipm %[pm]\n" \ + : [target] "=&a" (target), [r2] "+r" (r2), [pm] "=r" (pm) \ + : [mask] "a" (mask) \ + : "cc", "memory"); \ + reg = r2; \ + *cc = (pm >> 28) & 3; \ + \ + return reg; \ + } + +#define DEFINE_EXRL_TEST(insn, exp_reg, exp_mem, exp_cc) \ + static long test_exrl_ ## insn(long reg, long *cc) \ + { \ + register long r2 asm("r2"); \ + char mask = 0x20; /* make target use %r2 */ \ + long pm; \ + \ + r2 = reg; \ + asm("cr %%r0,%%r0\n" /* initial cc */ \ + "exrl %[mask],0f\n" \ + "jg 1f\n" \ + "0: " #insn " %%r0," MEM_ASM "\n" \ + "1: ipm %[pm]\n" \ + : [r2] "+r" (r2), [pm] "=r" (pm) \ + : [mask] "a" (mask) \ + : "cc", "memory"); \ + reg = r2; \ + *cc = (pm >> 28) & 3; \ + \ + return reg; \ + } + +FOR_EACH_INSN(DEFINE_EX_TEST) +FOR_EACH_INSN(DEFINE_EXRL_TEST) + +/* Test definitions. */ +#define REGISTER_EX_EXRL_TEST(ex_insn, insn, _exp_reg, _exp_mem, _exp_cc) \ + { \ + .name = #ex_insn " " #insn, \ + .func = test_ ## ex_insn ## _ ## insn, \ + .exp_reg = (_exp_reg), \ + .exp_mem = (_exp_mem), \ + .exp_cc = (_exp_cc), \ + }, + +#define REGISTER_EX_TEST(insn, exp_reg, exp_mem, exp_cc) \ + REGISTER_EX_EXRL_TEST(ex, insn, exp_reg, exp_mem, exp_cc) + +#define REGISTER_EXRL_TEST(insn, exp_reg, exp_mem, exp_cc) \ + REGISTER_EX_EXRL_TEST(exrl, insn, exp_reg, exp_mem, exp_cc) + +static const struct test tests[] = { + FOR_EACH_INSN(REGISTER_EX_TEST) + FOR_EACH_INSN(REGISTER_EXRL_TEST) +}; + +/* Loop over all tests and run them. */ +int main(void) +{ + const struct test *test; + int ret = EXIT_SUCCESS; + long reg, cc; + size_t i; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + test = &tests[i]; + mem[MEM_IDX] = MEM; + cc = -1; + reg = test->func(REG, &cc); +#define ASSERT_EQ(expected, actual) do { \ + if (expected != actual) { \ + fprintf(stderr, "%s: " #expected " (0x%lx) != " #actual " (0x%lx)\n", \ + test->name, expected, actual); \ + ret = EXIT_FAILURE; \ + } \ +} while (0) + ASSERT_EQ(test->exp_reg, reg); + ASSERT_EQ(test->exp_mem, mem[MEM_IDX]); + ASSERT_EQ(test->exp_cc, cc); +#undef ASSERT_EQ + } + + return ret; +} diff --git a/tests/tcg/s390x/exrl-ssm-early.S b/tests/tcg/s390x/exrl-ssm-early.S new file mode 100644 index 0000000000..68fbd87b3a --- /dev/null +++ b/tests/tcg/s390x/exrl-ssm-early.S @@ -0,0 +1,43 @@ +/* + * Test early exception recognition using EXRL + SSM. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + exrl %r0,ssm +expected_pswa: + j failure +ssm: + ssm ssm_op + +pgm: + chhsi program_interruption_code,0x6 /* specification exception? */ + jne failure + cli ilc,6 /* ilc for EXRL? */ + jne failure + clc program_old_psw(16),expected_old_psw /* correct old PSW? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + +ssm_op: + .byte 0x08 /* bit 4 set */ + .align 8 +expected_old_psw: + .quad 0x0800000180000000,expected_pswa /* bit 2 set */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/lgrl-unaligned.S b/tests/tcg/s390x/lgrl-unaligned.S new file mode 100644 index 0000000000..ef8d51d47c --- /dev/null +++ b/tests/tcg/s390x/lgrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test LGRL from a non-doubleword aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + lgrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .long 0 +unaligned: + .quad 0 diff --git a/tests/tcg/s390x/llgfrl-unaligned.S b/tests/tcg/s390x/llgfrl-unaligned.S new file mode 100644 index 0000000000..c9b4eeaecf --- /dev/null +++ b/tests/tcg/s390x/llgfrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test LLGFRL from a non-word aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + llgfrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .short 0 +unaligned: + .long 0 diff --git a/tests/tcg/s390x/lpsw.S b/tests/tcg/s390x/lpsw.S new file mode 100644 index 0000000000..b37dec59b7 --- /dev/null +++ b/tests/tcg/s390x/lpsw.S @@ -0,0 +1,36 @@ +/* + * Test the LPSW instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x140 +svc_old_psw: + .org 0x1c0 /* supervisor call new PSW */ + .quad 0x80000000,svc /* 31-bit mode */ + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + lpsw short_psw +lpsw_target: + svc 0 +expected_pswa: + j failure + +svc: + clc svc_old_psw(16),expected_psw /* correct full PSW? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + + .align 8 +short_psw: + .long 0x90001,0x80000000+lpsw_target /* problem state, + 64-bit mode */ +expected_psw: + .quad 0x1000180000000,expected_pswa /* corresponds to short_psw */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/lpswe-early.S b/tests/tcg/s390x/lpswe-early.S new file mode 100644 index 0000000000..90a7f213df --- /dev/null +++ b/tests/tcg/s390x/lpswe-early.S @@ -0,0 +1,38 @@ +/* + * Test early exception recognition using LPSWE. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + lpswe bad_psw + j failure + +pgm: + chhsi program_interruption_code,0x6 /* specification exception? */ + jne failure + cli ilc,0 /* ilc zero? */ + jne failure + clc program_old_psw(16),bad_psw /* correct old PSW? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + + .align 8 +bad_psw: + .quad 0x8000000000000000,0xfedcba9876543210 /* bit 0 set */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/lpswe-unaligned.S b/tests/tcg/s390x/lpswe-unaligned.S new file mode 100644 index 0000000000..989f249a6a --- /dev/null +++ b/tests/tcg/s390x/lpswe-unaligned.S @@ -0,0 +1,18 @@ +/* + * Test LPSWE from a non-doubleword aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + larl %r1,unaligned +fail: + lpswe 0(%r1) + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,fail + .long 0 +unaligned: + .quad 0 diff --git a/tests/tcg/s390x/lrl-unaligned.S b/tests/tcg/s390x/lrl-unaligned.S new file mode 100644 index 0000000000..11eb07f93a --- /dev/null +++ b/tests/tcg/s390x/lrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test LRL from a non-word aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + lrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .short 0 +unaligned: + .long 0 diff --git a/tests/tcg/s390x/pgm-specification-softmmu.S b/tests/tcg/s390x/pgm-specification-softmmu.S new file mode 100644 index 0000000000..d534f4e505 --- /dev/null +++ b/tests/tcg/s390x/pgm-specification-softmmu.S @@ -0,0 +1,40 @@ +/* + * Common softmmu code for specification exception testing. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .section .head + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0x180000000,pgm /* 64-bit mode */ + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + lpswe test_psw + +pgm: + chhsi program_interruption_code,0x6 /* PGM_SPECIFICATION? */ + jne failure + lg %r0,expected_old_psw+8 /* ilc adjustment */ + llgc %r1,ilc + agr %r0,%r1 + stg %r0,expected_old_psw+8 + clc expected_old_psw(16),program_old_psw /* correct location? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + + .align 8 +test_psw: + .quad 0x180000000,test /* 64-bit mode */ +success_psw: + .quad 0x2000180000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000180000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/pgm-specification-user.c b/tests/tcg/s390x/pgm-specification-user.c new file mode 100644 index 0000000000..9ee6907b7c --- /dev/null +++ b/tests/tcg/s390x/pgm-specification-user.c @@ -0,0 +1,37 @@ +/* + * Common user code for specification exception testing. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <assert.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +extern void test(void); +extern long expected_old_psw[2]; + +static void handle_sigill(int sig, siginfo_t *info, void *ucontext) +{ + if ((long)info->si_addr != expected_old_psw[1]) { + _exit(EXIT_FAILURE); + } + _exit(EXIT_SUCCESS); +} + +int main(void) +{ + struct sigaction act; + int err; + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = handle_sigill; + act.sa_flags = SA_SIGINFO; + err = sigaction(SIGILL, &act, NULL); + assert(err == 0); + + test(); + + return EXIT_FAILURE; +} diff --git a/tests/tcg/s390x/pgm-specification.mak b/tests/tcg/s390x/pgm-specification.mak new file mode 100644 index 0000000000..2999aee26e --- /dev/null +++ b/tests/tcg/s390x/pgm-specification.mak @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# List of specification exception tests. +# Shared between the softmmu and the user makefiles. +PGM_SPECIFICATION_TESTS = \ + br-odd \ + cgrl-unaligned \ + clrl-unaligned \ + crl-unaligned \ + ex-odd \ + lgrl-unaligned \ + llgfrl-unaligned \ + lpswe-unaligned \ + lrl-unaligned \ + stgrl-unaligned \ + strl-unaligned diff --git a/tests/tcg/s390x/rxsbg.c b/tests/tcg/s390x/rxsbg.c new file mode 100644 index 0000000000..4b155db304 --- /dev/null +++ b/tests/tcg/s390x/rxsbg.c @@ -0,0 +1,46 @@ +/* + * Test the RXSBG instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <assert.h> +#include <stdlib.h> + +static inline __attribute__((__always_inline__)) void +rxsbg(unsigned long *r1, unsigned long r2, int i3, int i4, int i5, int *cc) +{ + asm("rxsbg %[r1],%[r2],%[i3],%[i4],%[i5]\n" + "ipm %[cc]" + : [r1] "+r" (*r1), [cc] "=r" (*cc) + : [r2] "r" (r2) , [i3] "i" (i3) , [i4] "i" (i4) , [i5] "i" (i5) + : "cc"); + *cc = (*cc >> 28) & 3; +} + +void test_cc0(void) +{ + unsigned long r1 = 6; + int cc; + + rxsbg(&r1, 3, 61 | 0x80, 62, 1, &cc); + assert(r1 == 6); + assert(cc == 0); +} + +void test_cc1(void) +{ + unsigned long r1 = 2; + int cc; + + rxsbg(&r1, 3, 61 | 0x80, 62, 1, &cc); + assert(r1 == 2); + assert(cc == 1); +} + +int main(void) +{ + test_cc0(); + test_cc1(); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/softmmu.ld b/tests/tcg/s390x/softmmu.ld new file mode 100644 index 0000000000..ea944eaa3c --- /dev/null +++ b/tests/tcg/s390x/softmmu.ld @@ -0,0 +1,20 @@ +/* + * Linker script for the softmmu test kernels. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +ENTRY(_start) + +SECTIONS { + . = 0; + + .text : { + *(.head) + *(.text) + } + + /DISCARD/ : { + *(*) + } +} diff --git a/tests/tcg/s390x/ssm-early.S b/tests/tcg/s390x/ssm-early.S new file mode 100644 index 0000000000..6dfe40c597 --- /dev/null +++ b/tests/tcg/s390x/ssm-early.S @@ -0,0 +1,41 @@ +/* + * Test early exception recognition using SSM. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + ssm ssm_op +expected_pswa: + j failure + +pgm: + chhsi program_interruption_code,0x6 /* specification exception? */ + jne failure + cli ilc,4 /* ilc for SSM? */ + jne failure + clc program_old_psw(16),expected_old_psw /* correct old PSW? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + +ssm_op: + .byte 0x20 /* bit 2 set */ + .align 8 +expected_old_psw: + .quad 0x2000000180000000,expected_pswa /* bit 2 set */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/stgrl-unaligned.S b/tests/tcg/s390x/stgrl-unaligned.S new file mode 100644 index 0000000000..32df37780a --- /dev/null +++ b/tests/tcg/s390x/stgrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test STGRL to a non-doubleword aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + stgrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .long 0 +unaligned: + .quad 0 diff --git a/tests/tcg/s390x/stosm-early.S b/tests/tcg/s390x/stosm-early.S new file mode 100644 index 0000000000..0689924f3a --- /dev/null +++ b/tests/tcg/s390x/stosm-early.S @@ -0,0 +1,41 @@ +/* + * Test early exception recognition using STOSM. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + stosm ssm_op,0x10 /* bit 3 set */ +expected_pswa: + j failure + +pgm: + chhsi program_interruption_code,0x6 /* specification exception? */ + jne failure + cli ilc,4 /* ilc for STOSM? */ + jne failure + clc program_old_psw(16),expected_old_psw /* correct old PSW? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + +ssm_op: + .byte 0 + .align 8 +expected_old_psw: + .quad 0x1000000180000000,expected_pswa /* bit 3 set */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/strl-unaligned.S b/tests/tcg/s390x/strl-unaligned.S new file mode 100644 index 0000000000..1d248819f0 --- /dev/null +++ b/tests/tcg/s390x/strl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test STRL to a non-word aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + strl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .short 0 +unaligned: + .long 0 diff --git a/tests/tcg/xtensa/Makefile.softmmu-target b/tests/tcg/xtensa/Makefile.softmmu-target index 973e55298e..ba6cd9fde3 100644 --- a/tests/tcg/xtensa/Makefile.softmmu-target +++ b/tests/tcg/xtensa/Makefile.softmmu-target @@ -2,7 +2,8 @@ # Xtensa softmmu tests # -ifneq ($(TARGET_BIG_ENDIAN),y) +CORE=dc232b +ifneq ($(shell $(QEMU) -cpu help | grep -w $(CORE)),) XTENSA_SRC = $(SRC_PATH)/tests/tcg/xtensa XTENSA_ALL = $(filter-out $(XTENSA_SRC)/linker.ld.S,$(wildcard $(XTENSA_SRC)/*.S)) @@ -15,7 +16,6 @@ XTENSA_USABLE_TESTS = $(filter-out $(XTENSA_BROKEN_TESTS), $(XTENSA_TESTS)) TESTS += $(XTENSA_USABLE_TESTS) VPATH += $(XTENSA_SRC) -CORE=dc232b QEMU_OPTS+=-M sim -cpu $(CORE) -nographic -semihosting -icount 6 $(EXTFLAGS) -kernel INCLUDE_DIRS = $(SRC_PATH)/target/xtensa/core-$(CORE) @@ -26,6 +26,7 @@ ASFLAGS = -Wa,--no-absolute-literals LDFLAGS = -Tlinker.ld -nostartfiles -nostdlib CRT = crt.o vectors.o +CLEANFILES += linker.ld linker.ld: linker.ld.S $(CC) $(XTENSA_INC) -E -P $< -o $@ diff --git a/tests/tcg/xtensaeb/Makefile.softmmu-target b/tests/tcg/xtensaeb/Makefile.softmmu-target new file mode 100644 index 0000000000..4204a96d53 --- /dev/null +++ b/tests/tcg/xtensaeb/Makefile.softmmu-target @@ -0,0 +1,5 @@ +# +# Xtensa softmmu tests +# + +include $(SRC_PATH)/tests/tcg/xtensa/Makefile.softmmu-target diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 51f453e6c4..fa63cfe6ff 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -11,6 +11,7 @@ tests = { 'check-qobject': [], 'check-qjson': [], 'check-qlit': [], + 'test-error-report': [], 'test-qobject-output-visitor': [testqapi], 'test-clone-visitor': [testqapi], 'test-qobject-input-visitor': [testqapi], @@ -47,6 +48,7 @@ tests = { 'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'], 'test-qapi-util': [], 'test-interval-tree': [], + 'test-xs-node': [qom], } if have_system or have_tools diff --git a/tests/unit/socket-helpers.c b/tests/unit/socket-helpers.c index eecadf3a3c..6de27baee2 100644 --- a/tests/unit/socket-helpers.c +++ b/tests/unit/socket-helpers.c @@ -160,7 +160,7 @@ void socket_check_afunix_support(bool *has_afunix) int fd; fd = socket(PF_UNIX, SOCK_STREAM, 0); - closesocket(fd); + close(fd); #ifdef _WIN32 *has_afunix = (fd != (int)INVALID_SOCKET); diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c index c0426bd10c..a130f6fefb 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -531,6 +531,13 @@ int main(int argc, char **argv) g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); - g_test_add_func("/blockjob/complete_in_standby", test_complete_in_standby); + + /* + * This test is flaky and sometimes fails in CI and otherwise: + * don't run unless user opts in via environment variable. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + g_test_add_func("/blockjob/complete_in_standby", test_complete_in_standby); + } return g_test_run(); } diff --git a/tests/unit/test-error-report.c b/tests/unit/test-error-report.c new file mode 100644 index 0000000000..54319c86c9 --- /dev/null +++ b/tests/unit/test-error-report.c @@ -0,0 +1,139 @@ +/* + * Error reporting test + * + * Copyright (C) 2022 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 "glib-compat.h" +#include <locale.h> + +#include "qemu/error-report.h" +#include "qapi/error.h" + +static void +test_error_report_simple(void) +{ + if (g_test_subprocess()) { + error_report("%s", "test error"); + warn_report("%s", "test warn"); + info_report("%s", "test info"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report: test error*\ +test-error-report: warning: test warn*\ +test-error-report: info: test info*\ +"); +} + +static void +test_error_report_loc(void) +{ + if (g_test_subprocess()) { + loc_set_file("some-file.c", 7717); + error_report("%s", "test error1"); + loc_set_none(); + error_report("%s", "test error2"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report:some-file.c:7717: test error1*\ +test-error-report: test error2*\ +"); +} + +static void +test_error_report_glog(void) +{ + if (g_test_subprocess()) { + g_message("gmessage"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("test-error-report: info: gmessage*"); +} + +static void +test_error_report_once(void) +{ + int i; + + if (g_test_subprocess()) { + for (i = 0; i < 3; i++) { + warn_report_once("warn"); + error_report_once("err"); + } + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report: warning: warn*\ +test-error-report: err*\ +"); +} + +static void +test_error_report_timestamp(void) +{ + if (g_test_subprocess()) { + message_with_timestamp = true; + warn_report("warn"); + error_report("err"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +*-*-*:*:* test-error-report: warning: warn*\ +*-*-*:*:* test-error-report: err*\ +"); +} + +static void +test_error_warn(void) +{ + if (g_test_subprocess()) { + error_setg(&error_warn, "Testing &error_warn"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report: warning: Testing &error_warn*\ +"); +} + + +int +main(int argc, char *argv[]) +{ + setlocale(LC_ALL, ""); + + g_test_init(&argc, &argv, NULL); + error_init("test-error-report"); + + g_test_add_func("/error-report/simple", test_error_report_simple); + g_test_add_func("/error-report/loc", test_error_report_loc); + g_test_add_func("/error-report/glog", test_error_report_glog); + g_test_add_func("/error-report/once", test_error_report_once); + g_test_add_func("/error-report/timestamp", test_error_report_timestamp); + g_test_add_func("/error-report/warn", test_error_warn); + + return g_test_run(); +} diff --git a/tests/unit/test-io-channel-command.c b/tests/unit/test-io-channel-command.c index c6e66a8c33..4f022617df 100644 --- a/tests/unit/test-io-channel-command.c +++ b/tests/unit/test-io-channel-command.c @@ -35,7 +35,7 @@ static char *socat = NULL; static void test_io_channel_command_fifo(bool async) { g_autofree gchar *tmpdir = g_dir_make_tmp("qemu-test-io-channel.XXXXXX", NULL); - g_autofree gchar *fifo = g_strdup_printf("%s/%s", tmpdir, TEST_FIFO); + g_autofree gchar *fifo = g_build_filename(tmpdir, TEST_FIFO, NULL); g_autofree gchar *srcargs = g_strdup_printf("%s - PIPE:%s,wronly", socat, fifo); g_autofree gchar *dstargs = g_strdup_printf("%s PIPE:%s,rdonly -", socat, fifo); g_auto(GStrv) srcargv = g_strsplit(srcargs, " ", -1); diff --git a/tests/unit/test-rcu-list.c b/tests/unit/test-rcu-list.c index 9964171da4..8f0adb8b00 100644 --- a/tests/unit/test-rcu-list.c +++ b/tests/unit/test-rcu-list.c @@ -151,7 +151,7 @@ static QSLIST_HEAD(, list_element) Q_list_head; #define TEST_NAME "qslist" #define TEST_LIST_REMOVE_RCU(el, f) \ - QSLIST_REMOVE_RCU(&Q_list_head, el, list_element, f) + QSLIST_REMOVE_RCU(&Q_list_head, el, list_element, f) #define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ QSLIST_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) diff --git a/tests/unit/test-xs-node.c b/tests/unit/test-xs-node.c new file mode 100644 index 0000000000..b80d10ff98 --- /dev/null +++ b/tests/unit/test-xs-node.c @@ -0,0 +1,871 @@ +/* + * QEMU XenStore XsNode testing + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" + +static int nr_xs_nodes; +static GList *xs_node_list; + +#define XS_NODE_UNIT_TEST + +/* + * We don't need the core Xen definitions. And we *do* want to be able + * to run the unit tests even on architectures that Xen doesn't support + * (because life's too short to bother doing otherwise, and test coverage + * doesn't hurt). + */ +#define __XEN_PUBLIC_XEN_H__ +typedef unsigned long xen_pfn_t; + +#include "hw/i386/kvm/xenstore_impl.c" + +#define DOMID_QEMU 0 +#define DOMID_GUEST 1 + +static void dump_ref(const char *name, XsNode *n, int indent) +{ + int i; + + if (!indent && name) { + printf("%s:\n", name); + } + + for (i = 0; i < indent; i++) { + printf(" "); + } + + printf("->%p(%d, '%s'): '%.*s'%s%s\n", n, n->ref, n->name, + (int)(n->content ? n->content->len : strlen("<empty>")), + n->content ? (char *)n->content->data : "<empty>", + n->modified_in_tx ? " MODIFIED" : "", + n->deleted_in_tx ? " DELETED" : ""); + + if (n->children) { + g_hash_table_foreach(n->children, (void *)dump_ref, + GINT_TO_POINTER(indent + 2)); + } +} + +/* This doesn't happen in qemu but we want to make valgrind happy */ +static void xs_impl_delete(XenstoreImplState *s, bool last) +{ + int err; + + xs_impl_reset_watches(s, DOMID_GUEST); + g_assert(!s->nr_domu_watches); + + err = xs_impl_rm(s, DOMID_QEMU, XBT_NULL, "/local"); + g_assert(!err); + g_assert(s->nr_nodes == 1); + + g_hash_table_unref(s->watches); + g_hash_table_unref(s->transactions); + xs_node_unref(s->root); + g_free(s); + + if (!last) { + return; + } + + if (xs_node_list) { + GList *l; + for (l = xs_node_list; l; l = l->next) { + XsNode *n = l->data; + printf("Remaining node at %p name %s ref %u\n", n, n->name, + n->ref); + } + } + g_assert(!nr_xs_nodes); +} + +struct compare_walk { + char path[XENSTORE_ABS_PATH_MAX + 1]; + XsNode *parent_2; + bool compare_ok; +}; + + +static bool compare_perms(GList *p1, GList *p2) +{ + while (p1) { + if (!p2 || g_strcmp0(p1->data, p2->data)) { + return false; + } + p1 = p1->next; + p2 = p2->next; + } + return (p2 == NULL); +} + +static bool compare_content(GByteArray *c1, GByteArray *c2) +{ + size_t len1 = 0, len2 = 0; + + if (c1) { + len1 = c1->len; + } + if (c2) { + len2 = c2->len; + } + if (len1 != len2) { + return false; + } + + if (!len1) { + return true; + } + + return !memcmp(c1->data, c2->data, len1); +} + +static void compare_child(gpointer, gpointer, gpointer); + +static void compare_nodes(struct compare_walk *cw, XsNode *n1, XsNode *n2) +{ + int nr_children1 = 0, nr_children2 = 0; + + if (n1->children) { + nr_children1 = g_hash_table_size(n1->children); + } + if (n2->children) { + nr_children2 = g_hash_table_size(n2->children); + } + + if (n1->ref != n2->ref || + n1->deleted_in_tx != n2->deleted_in_tx || + n1->modified_in_tx != n2->modified_in_tx || + !compare_perms(n1->perms, n2->perms) || + !compare_content(n1->content, n2->content) || + nr_children1 != nr_children2) { + cw->compare_ok = false; + printf("Compare failure on '%s'\n", cw->path); + } + + if (nr_children1) { + XsNode *oldparent = cw->parent_2; + cw->parent_2 = n2; + g_hash_table_foreach(n1->children, compare_child, cw); + + cw->parent_2 = oldparent; + } +} + +static void compare_child(gpointer key, gpointer val, gpointer opaque) +{ + struct compare_walk *cw = opaque; + char *childname = key; + XsNode *child1 = val; + XsNode *child2 = g_hash_table_lookup(cw->parent_2->children, childname); + int pathlen = strlen(cw->path); + + if (!child2) { + cw->compare_ok = false; + printf("Child '%s' does not exist under '%s'\n", childname, cw->path); + return; + } + + strncat(cw->path, "/", sizeof(cw->path) - 1); + strncat(cw->path, childname, sizeof(cw->path) - 1); + + compare_nodes(cw, child1, child2); + cw->path[pathlen] = '\0'; +} + +static bool compare_trees(XsNode *n1, XsNode *n2) +{ + struct compare_walk cw; + + cw.path[0] = '\0'; + cw.parent_2 = n2; + cw.compare_ok = true; + + if (!n1 || !n2) { + return false; + } + + compare_nodes(&cw, n1, n2); + return cw.compare_ok; +} + +static void compare_tx(gpointer key, gpointer val, gpointer opaque) +{ + XenstoreImplState *s2 = opaque; + XsTransaction *t1 = val, *t2; + unsigned int tx_id = GPOINTER_TO_INT(key); + + t2 = g_hash_table_lookup(s2->transactions, key); + g_assert(t2); + + g_assert(t1->tx_id == tx_id); + g_assert(t2->tx_id == tx_id); + g_assert(t1->base_tx == t2->base_tx); + g_assert(t1->dom_id == t2->dom_id); + if (!compare_trees(t1->root, t2->root)) { + printf("Comparison failure in TX %u after serdes:\n", tx_id); + dump_ref("Original", t1->root, 0); + dump_ref("Deserialised", t2->root, 0); + g_assert(0); + } + g_assert(t1->nr_nodes == t2->nr_nodes); +} + +static int write_str(XenstoreImplState *s, unsigned int dom_id, + unsigned int tx_id, const char *path, + const char *content) +{ + GByteArray *d = g_byte_array_new(); + int err; + + g_byte_array_append(d, (void *)content, strlen(content)); + err = xs_impl_write(s, dom_id, tx_id, path, d); + g_byte_array_unref(d); + return err; +} + +static void watch_cb(void *_str, const char *path, const char *token) +{ + GString *str = _str; + + g_string_append(str, path); + g_string_append(str, token); +} + +static void check_serdes(XenstoreImplState *s) +{ + XenstoreImplState *s2 = xs_impl_create(DOMID_GUEST); + GByteArray *bytes = xs_impl_serialize(s); + int nr_transactions1, nr_transactions2; + int ret; + + ret = xs_impl_deserialize(s2, bytes, DOMID_GUEST, watch_cb, NULL); + g_assert(!ret); + + g_byte_array_unref(bytes); + + g_assert(s->last_tx == s2->last_tx); + g_assert(s->root_tx == s2->root_tx); + + if (!compare_trees(s->root, s2->root)) { + printf("Comparison failure in main tree after serdes:\n"); + dump_ref("Original", s->root, 0); + dump_ref("Deserialised", s2->root, 0); + g_assert(0); + } + + nr_transactions1 = g_hash_table_size(s->transactions); + nr_transactions2 = g_hash_table_size(s2->transactions); + g_assert(nr_transactions1 == nr_transactions2); + + g_hash_table_foreach(s->transactions, compare_tx, s2); + + g_assert(s->nr_domu_watches == s2->nr_domu_watches); + g_assert(s->nr_domu_transactions == s2->nr_domu_transactions); + g_assert(s->nr_nodes == s2->nr_nodes); + xs_impl_delete(s2, false); +} + +static XenstoreImplState *setup(void) +{ + XenstoreImplState *s = xs_impl_create(DOMID_GUEST); + char *abspath; + GList *perms; + int err; + + abspath = g_strdup_printf("/local/domain/%u", DOMID_GUEST); + + err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); + g_assert(!err); + g_assert(s->nr_nodes == 4); + + perms = g_list_append(NULL, g_strdup_printf("n%u", DOMID_QEMU)); + perms = g_list_append(perms, g_strdup_printf("r%u", DOMID_GUEST)); + + err = xs_impl_set_perms(s, DOMID_QEMU, XBT_NULL, abspath, perms); + g_assert(!err); + + g_list_free_full(perms, g_free); + g_free(abspath); + + abspath = g_strdup_printf("/local/domain/%u/some", DOMID_GUEST); + + err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); + g_assert(!err); + g_assert(s->nr_nodes == 5); + + perms = g_list_append(NULL, g_strdup_printf("n%u", DOMID_GUEST)); + + err = xs_impl_set_perms(s, DOMID_QEMU, XBT_NULL, abspath, perms); + g_assert(!err); + + g_list_free_full(perms, g_free); + g_free(abspath); + + return s; +} + +static void test_xs_node_simple(void) +{ + GByteArray *data = g_byte_array_new(); + XenstoreImplState *s = setup(); + GString *guest_watches = g_string_new(NULL); + GString *qemu_watches = g_string_new(NULL); + GList *items = NULL; + XsNode *old_root; + uint64_t gencnt; + int err; + + g_assert(s); + + err = xs_impl_watch(s, DOMID_GUEST, "some", "guestwatch", + watch_cb, guest_watches); + g_assert(!err); + g_assert(guest_watches->len == strlen("someguestwatch")); + g_assert(!strcmp(guest_watches->str, "someguestwatch")); + g_string_truncate(guest_watches, 0); + + err = xs_impl_watch(s, 0, "/local/domain/1/some", "qemuwatch", + watch_cb, qemu_watches); + g_assert(!err); + g_assert(qemu_watches->len == strlen("/local/domain/1/someqemuwatch")); + g_assert(!strcmp(qemu_watches->str, "/local/domain/1/someqemuwatch")); + g_string_truncate(qemu_watches, 0); + + /* Read gives ENOENT when it should */ + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "foo", data); + g_assert(err == ENOENT); + + /* Write works */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", + "something"); + g_assert(s->nr_nodes == 7); + g_assert(!err); + g_assert(!strcmp(guest_watches->str, + "some/relative/pathguestwatch")); + g_assert(!strcmp(qemu_watches->str, + "/local/domain/1/some/relative/pathqemuwatch")); + + g_string_truncate(qemu_watches, 0); + g_string_truncate(guest_watches, 0); + xs_impl_reset_watches(s, 0); + + /* Read gives back what we wrote */ + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); + g_assert(!err); + g_assert(data->len == strlen("something")); + g_assert(!memcmp(data->data, "something", data->len)); + + /* Even if we use an abolute path */ + g_byte_array_set_size(data, 0); + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, + "/local/domain/1/some/relative/path", data); + g_assert(!err); + g_assert(data->len == strlen("something")); + + g_assert(!qemu_watches->len); + g_assert(!guest_watches->len); + /* Keep a copy, to force COW mode */ + old_root = xs_node_ref(s->root); + + /* Write somewhere we aren't allowed, in COW mode */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "/local/domain/badplace", + "moredata"); + g_assert(err == EACCES); + g_assert(s->nr_nodes == 7); + + /* Write works again */ + err = write_str(s, DOMID_GUEST, XBT_NULL, + "/local/domain/1/some/relative/path2", + "something else"); + g_assert(!err); + g_assert(s->nr_nodes == 8); + g_assert(!qemu_watches->len); + g_assert(!strcmp(guest_watches->str, "some/relative/path2guestwatch")); + g_string_truncate(guest_watches, 0); + + /* Overwrite an existing node */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", + "another thing"); + g_assert(!err); + g_assert(s->nr_nodes == 8); + g_assert(!qemu_watches->len); + g_assert(!strcmp(guest_watches->str, "some/relative/pathguestwatch")); + g_string_truncate(guest_watches, 0); + + /* We can list the two files we wrote */ + err = xs_impl_directory(s, DOMID_GUEST, XBT_NULL, "some/relative", &gencnt, + &items); + g_assert(!err); + g_assert(items); + g_assert(gencnt == 2); + g_assert(!strcmp(items->data, "path")); + g_assert(items->next); + g_assert(!strcmp(items->next->data, "path2")); + g_assert(!items->next->next); + g_list_free_full(items, g_free); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", + watch_cb, guest_watches); + g_assert(!err); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", + watch_cb, guest_watches); + g_assert(err == ENOENT); + + err = xs_impl_watch(s, DOMID_GUEST, "some/relative/path2", "watchp2", + watch_cb, guest_watches); + g_assert(!err); + g_assert(guest_watches->len == strlen("some/relative/path2watchp2")); + g_assert(!strcmp(guest_watches->str, "some/relative/path2watchp2")); + g_string_truncate(guest_watches, 0); + + err = xs_impl_watch(s, DOMID_GUEST, "/local/domain/1/some/relative", + "watchrel", watch_cb, guest_watches); + g_assert(!err); + g_assert(guest_watches->len == + strlen("/local/domain/1/some/relativewatchrel")); + g_assert(!strcmp(guest_watches->str, + "/local/domain/1/some/relativewatchrel")); + g_string_truncate(guest_watches, 0); + + /* Write somewhere else which already existed */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "moredata"); + g_assert(!err); + g_assert(s->nr_nodes == 8); + + /* Write somewhere we aren't allowed */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "/local/domain/badplace", + "moredata"); + g_assert(err == EACCES); + + g_assert(!strcmp(guest_watches->str, + "/local/domain/1/some/relativewatchrel")); + g_string_truncate(guest_watches, 0); + + g_byte_array_set_size(data, 0); + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); + g_assert(!err); + g_assert(data->len == strlen("moredata")); + g_assert(!memcmp(data->data, "moredata", data->len)); + + /* Overwrite existing data */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "otherdata"); + g_assert(!err); + g_string_truncate(guest_watches, 0); + + g_byte_array_set_size(data, 0); + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); + g_assert(!err); + g_assert(data->len == strlen("otherdata")); + g_assert(!memcmp(data->data, "otherdata", data->len)); + + /* Remove the subtree */ + err = xs_impl_rm(s, DOMID_GUEST, XBT_NULL, "some/relative"); + g_assert(!err); + g_assert(s->nr_nodes == 5); + + /* Each watch fires with the least specific relevant path */ + g_assert(strstr(guest_watches->str, + "some/relative/path2watchp2")); + g_assert(strstr(guest_watches->str, + "/local/domain/1/some/relativewatchrel")); + g_string_truncate(guest_watches, 0); + + g_byte_array_set_size(data, 0); + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); + g_assert(err == ENOENT); + g_byte_array_unref(data); + + xs_impl_reset_watches(s, DOMID_GUEST); + g_string_free(qemu_watches, true); + g_string_free(guest_watches, true); + xs_node_unref(old_root); + xs_impl_delete(s, true); +} + + +static void do_test_xs_node_tx(bool fail, bool commit) +{ + XenstoreImplState *s = setup(); + GString *watches = g_string_new(NULL); + GByteArray *data = g_byte_array_new(); + unsigned int tx_id = XBT_NULL; + int err; + + g_assert(s); + + /* Set a watch */ + err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + g_assert(watches->len == strlen("somewatch")); + g_assert(!strcmp(watches->str, "somewatch")); + g_string_truncate(watches, 0); + + /* Write something */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", + "something"); + g_assert(s->nr_nodes == 7); + g_assert(!err); + g_assert(!strcmp(watches->str, + "some/relative/pathwatch")); + g_string_truncate(watches, 0); + + /* Create a transaction */ + err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); + g_assert(!err); + + if (fail) { + /* Write something else in the root */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", + "another thing"); + g_assert(!err); + g_assert(s->nr_nodes == 7); + g_assert(!strcmp(watches->str, + "some/relative/pathwatch")); + g_string_truncate(watches, 0); + } + + g_assert(!watches->len); + + /* Perform a write in the transaction */ + err = write_str(s, DOMID_GUEST, tx_id, "some/relative/path", + "something else"); + g_assert(!err); + g_assert(s->nr_nodes == 7); + g_assert(!watches->len); + + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); + g_assert(!err); + if (fail) { + g_assert(data->len == strlen("another thing")); + g_assert(!memcmp(data->data, "another thing", data->len)); + } else { + g_assert(data->len == strlen("something")); + g_assert(!memcmp(data->data, "something", data->len)); + } + g_byte_array_set_size(data, 0); + + err = xs_impl_read(s, DOMID_GUEST, tx_id, "some/relative/path", data); + g_assert(!err); + g_assert(data->len == strlen("something else")); + g_assert(!memcmp(data->data, "something else", data->len)); + g_byte_array_set_size(data, 0); + + check_serdes(s); + + /* Attempt to commit the transaction */ + err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, commit); + if (commit && fail) { + g_assert(err == EAGAIN); + } else { + g_assert(!err); + } + if (commit && !fail) { + g_assert(!strcmp(watches->str, + "some/relative/pathwatch")); + g_string_truncate(watches, 0); + } else { + g_assert(!watches->len); + } + g_assert(s->nr_nodes == 7); + + check_serdes(s); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); + g_assert(!err); + if (fail) { + g_assert(data->len == strlen("another thing")); + g_assert(!memcmp(data->data, "another thing", data->len)); + } else if (commit) { + g_assert(data->len == strlen("something else")); + g_assert(!memcmp(data->data, "something else", data->len)); + } else { + g_assert(data->len == strlen("something")); + g_assert(!memcmp(data->data, "something", data->len)); + } + g_byte_array_unref(data); + g_string_free(watches, true); + xs_impl_delete(s, true); +} + +static void test_xs_node_tx_fail(void) +{ + do_test_xs_node_tx(true, true); +} + +static void test_xs_node_tx_abort(void) +{ + do_test_xs_node_tx(false, false); + do_test_xs_node_tx(true, false); +} +static void test_xs_node_tx_succeed(void) +{ + do_test_xs_node_tx(false, true); +} + +static void test_xs_node_tx_rm(void) +{ + XenstoreImplState *s = setup(); + GString *watches = g_string_new(NULL); + GByteArray *data = g_byte_array_new(); + unsigned int tx_id = XBT_NULL; + int err; + + g_assert(s); + + /* Set a watch */ + err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + g_assert(watches->len == strlen("somewatch")); + g_assert(!strcmp(watches->str, "somewatch")); + g_string_truncate(watches, 0); + + /* Write something */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + "something"); + g_assert(!err); + g_assert(s->nr_nodes == 9); + g_assert(!strcmp(watches->str, + "some/deep/dark/relative/pathwatch")); + g_string_truncate(watches, 0); + + /* Create a transaction */ + err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); + g_assert(!err); + + /* Delete the tree in the transaction */ + err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep/dark"); + g_assert(!err); + g_assert(s->nr_nodes == 9); + g_assert(!watches->len); + + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + data); + g_assert(!err); + g_assert(data->len == strlen("something")); + g_assert(!memcmp(data->data, "something", data->len)); + g_byte_array_set_size(data, 0); + + check_serdes(s); + + /* Commit the transaction */ + err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true); + g_assert(!err); + g_assert(s->nr_nodes == 6); + + g_assert(!strcmp(watches->str, "some/deep/darkwatch")); + g_string_truncate(watches, 0); + + /* Now the node is gone */ + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + data); + g_assert(err == ENOENT); + g_byte_array_unref(data); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + + g_string_free(watches, true); + xs_impl_delete(s, true); +} + +static void test_xs_node_tx_resurrect(void) +{ + XenstoreImplState *s = setup(); + GString *watches = g_string_new(NULL); + GByteArray *data = g_byte_array_new(); + unsigned int tx_id = XBT_NULL; + int err; + + g_assert(s); + + /* Write something */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + "something"); + g_assert(!err); + g_assert(s->nr_nodes == 9); + + /* Another node to remain shared */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/place/safe", "keepme"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + /* This node will be wiped and resurrected */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark", + "foo"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + /* Set a watch */ + err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + g_assert(watches->len == strlen("somewatch")); + g_assert(!strcmp(watches->str, "somewatch")); + g_string_truncate(watches, 0); + + /* Create a transaction */ + err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); + g_assert(!err); + + /* Delete the tree in the transaction */ + err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + g_assert(!watches->len); + + /* Resurrect part of it */ + err = write_str(s, DOMID_GUEST, tx_id, "some/deep/dark/different/path", + "something"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + check_serdes(s); + + /* Commit the transaction */ + err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + check_serdes(s); + + /* lost data */ + g_assert(strstr(watches->str, "some/deep/dark/different/pathwatch")); + /* topmost deleted */ + g_assert(strstr(watches->str, "some/deep/dark/relativewatch")); + /* lost data */ + g_assert(strstr(watches->str, "some/deep/darkwatch")); + + g_string_truncate(watches, 0); + + /* Now the node is gone */ + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + data); + g_assert(err == ENOENT); + g_byte_array_unref(data); + + check_serdes(s); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + + g_string_free(watches, true); + xs_impl_delete(s, true); +} + +static void test_xs_node_tx_resurrect2(void) +{ + XenstoreImplState *s = setup(); + GString *watches = g_string_new(NULL); + GByteArray *data = g_byte_array_new(); + unsigned int tx_id = XBT_NULL; + int err; + + g_assert(s); + + /* Write something */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + "something"); + g_assert(!err); + g_assert(s->nr_nodes == 9); + + /* Another node to remain shared */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/place/safe", "keepme"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + /* This node will be wiped and resurrected */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark", + "foo"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + /* Set a watch */ + err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + g_assert(watches->len == strlen("somewatch")); + g_assert(!strcmp(watches->str, "somewatch")); + g_string_truncate(watches, 0); + + /* Create a transaction */ + err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); + g_assert(!err); + + /* Delete the tree in the transaction */ + err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + g_assert(!watches->len); + + /* Resurrect part of it */ + err = write_str(s, DOMID_GUEST, tx_id, "some/deep/dark/relative/path", + "something"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + check_serdes(s); + + /* Commit the transaction */ + err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + check_serdes(s); + + /* lost data */ + g_assert(strstr(watches->str, "some/deep/dark/relative/pathwatch")); + /* lost data */ + g_assert(strstr(watches->str, "some/deep/darkwatch")); + + g_string_truncate(watches, 0); + + /* Now the node is gone */ + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + data); + g_assert(!err); + g_assert(data->len == strlen("something")); + g_assert(!memcmp(data->data, "something", data->len)); + + g_byte_array_unref(data); + + check_serdes(s); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + + g_string_free(watches, true); + xs_impl_delete(s, true); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + module_call_init(MODULE_INIT_QOM); + + g_test_add_func("/xs_node/simple", test_xs_node_simple); + g_test_add_func("/xs_node/tx_abort", test_xs_node_tx_abort); + g_test_add_func("/xs_node/tx_fail", test_xs_node_tx_fail); + g_test_add_func("/xs_node/tx_succeed", test_xs_node_tx_succeed); + g_test_add_func("/xs_node/tx_rm", test_xs_node_tx_rm); + g_test_add_func("/xs_node/tx_resurrect", test_xs_node_tx_resurrect); + g_test_add_func("/xs_node/tx_resurrect2", test_xs_node_tx_resurrect2); + + return g_test_run(); +} diff --git a/tests/vm/openbsd b/tests/vm/openbsd index eaeb201e91..6b4fc29793 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -106,8 +106,7 @@ class OpenBSDVM(basevm.BaseVM): self.console_wait("Password for root account") self.console_send("%s\n" % self._config["root_pass"]) self.console_wait_send("Start sshd(8)", "yes\n") - self.console_wait_send("X Window System", "\n") - self.console_wait_send("xenodm", "\n") + self.console_wait_send("X Window System", "no\n") self.console_wait_send("console to com0", "\n") self.console_wait_send("Which speed", "\n") @@ -124,7 +123,32 @@ class OpenBSDVM(basevm.BaseVM): self.console_wait_send("timezone", "UTC\n") self.console_wait_send("root disk", "\n") self.console_wait_send("(W)hole disk", "\n") - self.console_wait_send("(A)uto layout", "\n") + self.console_wait_send("(A)uto layout", "c\n") + + # 4000 MB / as /dev/sd0a, at start of disk + self.console_wait_send("sd0>", "a a\n") + self.console_wait_send("offset:", "\n") + self.console_wait_send("size:", "4000M\n") + self.console_wait_send("FS type", "4.2BSD\n") + self.console_wait_send("mount point:", "/\n") + + # 256 MB swap as /dev/sd0b + self.console_wait_send("sd0*>", "a b\n") + self.console_wait_send("offset:", "\n") + self.console_wait_send("size:", "256M\n") + self.console_wait_send("FS type", "swap\n") + + # All remaining space for /home as /dev/sd0d + # NB, 'c' isn't allowed to be used. + self.console_wait_send("sd0*>", "a d\n") + self.console_wait_send("offset:", "\n") + self.console_wait_send("size:", "\n") + self.console_wait_send("FS type", "4.2BSD\n") + self.console_wait_send("mount point:", "/home\n") + + self.console_wait_send("sd0*>", "q\n") + self.console_wait_send("Write new label?:", "y\n") + self.console_wait_send("Location of sets", "cd0\n") self.console_wait_send("Pathname to the sets", "\n") self.console_wait_send("Set name(s)", "\n") |