diff options
Diffstat (limited to 'tests')
60 files changed, 1880 insertions, 360 deletions
diff --git a/tests/avocado/igb.py b/tests/avocado/igb.py new file mode 100644 index 0000000000..abf5dfa07f --- /dev/null +++ b/tests/avocado/igb.py @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# ethtool tests for igb registers, interrupts, etc + +from avocado_qemu import LinuxTest + +class IGB(LinuxTest): + """ + :avocado: tags=accel:kvm + :avocado: tags=arch:x86_64 + :avocado: tags=distro:fedora + :avocado: tags=distro_version:31 + :avocado: tags=machine:q35 + """ + + timeout = 180 + + def test(self): + self.require_accelerator('kvm') + kernel_url = self.distro.pxeboot_url + 'vmlinuz' + kernel_hash = '5b6f6876e1b5bda314f93893271da0d5777b1f3c' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + initrd_url = self.distro.pxeboot_url + 'initrd.img' + initrd_hash = 'dd0340a1b39bd28f88532babd4581c67649ec5b1' + initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + + # Ideally we want to test MSI as well, but it is blocked by a bug + # fixed with: + # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=28e96556baca7056d11d9fb3cdd0aba4483e00d8 + kernel_params = self.distro.default_kernel_params + ' pci=nomsi' + + self.vm.add_args('-kernel', kernel_path, + '-initrd', initrd_path, + '-append', kernel_params, + '-accel', 'kvm', + '-device', 'igb') + self.launch_and_wait() + self.ssh_command('dnf -y install ethtool') + self.ssh_command('ethtool -t eth1 offline') 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/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/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index d29a4e47af..76d5100911 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); } @@ -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/meson.build b/tests/qtest/meson.build index 29a4efb4c2..c9292b64fb 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -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/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/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.target b/tests/tcg/s390x/Makefile.target index 72ad309b27..b7f576f983 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -51,7 +51,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/unit/meson.build b/tests/unit/meson.build index 51f453e6c4..d9c0a7eae6 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -47,6 +47,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/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(); +} |