From: Thomas Huth Date: Fri, 30 Aug 2024 13:38:06 +0000 (+0200) Subject: tests/functional: Convert simple avocado tests into standalone python tests X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=cce85725f10fbe92481e8314986e69dbe6ca0dd1;p=qemu.git tests/functional: Convert simple avocado tests into standalone python tests These test are rather simple and don't need any modifications apart from adjusting the "from avocado_qemu" line. To ease debugging, make the files executable and add a shebang line and Python '__main__' handling, too, so that these tests can now be run by executing them directly. Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20240830133841.142644-13-thuth@redhat.com> Signed-off-by: Thomas Huth --- diff --git a/MAINTAINERS b/MAINTAINERS index 80cc84dd99..b3a0ac0e8b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1833,6 +1833,8 @@ F: hw/isa/apm.c F: include/hw/isa/apm.h F: tests/unit/test-x86-topo.c F: tests/qtest/test-x86-cpuid-compat.c +F: tests/functional/test_mem_addr_space.py +F: tests/functional/test_pc_cpu_hotplug_props.py PC Chipset M: Michael S. Tsirkin @@ -1899,6 +1901,8 @@ F: include/hw/boards.h F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h F: include/sysemu/numa.h +F: tests/functional/test_cpu_queries.py +F: tests/functional/test_empty_cpu_model.py F: tests/unit/test-smp-parse.c T: git https://gitlab.com/ehabkost/qemu.git machine-next @@ -2238,6 +2242,7 @@ F: net/vhost-user.c F: include/hw/virtio/ F: docs/devel/virtio* F: docs/devel/migration/virtio.rst +F: tests/functional/test_virtio_version.py virtio-balloon M: Michael S. Tsirkin diff --git a/tests/avocado/cpu_queries.py b/tests/avocado/cpu_queries.py deleted file mode 100644 index d3faa14720..0000000000 --- a/tests/avocado/cpu_queries.py +++ /dev/null @@ -1,35 +0,0 @@ -# Sanity check of query-cpu-* results -# -# Copyright (c) 2019 Red Hat, Inc. -# -# Author: -# Eduardo Habkost -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -from avocado_qemu import QemuSystemTest - -class QueryCPUModelExpansion(QemuSystemTest): - """ - Run query-cpu-model-expansion for each CPU model, and validate results - """ - - def test(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:none - """ - self.vm.add_args('-S') - self.vm.launch() - - cpus = self.vm.cmd('query-cpu-definitions') - for c in cpus: - self.log.info("Checking CPU: %s", c) - self.assertNotIn('', c['unavailable-features'], c['name']) - - for c in cpus: - model = {'name': c['name']} - e = self.vm.cmd('query-cpu-model-expansion', model=model, - type='full') - self.assertEqual(e['model']['name'], c['name']) diff --git a/tests/avocado/empty_cpu_model.py b/tests/avocado/empty_cpu_model.py deleted file mode 100644 index d906ef3d3c..0000000000 --- a/tests/avocado/empty_cpu_model.py +++ /dev/null @@ -1,19 +0,0 @@ -# Check for crash when using empty -cpu option -# -# Copyright (c) 2019 Red Hat, Inc. -# -# Author: -# Eduardo Habkost -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. -from avocado_qemu import QemuSystemTest - -class EmptyCPUModel(QemuSystemTest): - def test(self): - self.vm.add_args('-S', '-display', 'none', '-machine', 'none', '-cpu', '') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - self.vm.wait() - self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") - self.assertRegex(self.vm.get_log(), r'-cpu option cannot be empty') diff --git a/tests/avocado/mem-addr-space-check.py b/tests/avocado/mem-addr-space-check.py deleted file mode 100644 index d3974599f4..0000000000 --- a/tests/avocado/mem-addr-space-check.py +++ /dev/null @@ -1,354 +0,0 @@ -# Check for crash when using memory beyond the available guest processor -# address space. -# -# Copyright (c) 2023 Red Hat, Inc. -# -# Author: -# Ani Sinha -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from avocado_qemu import QemuSystemTest -import time - -class MemAddrCheck(QemuSystemTest): - # after launch, in order to generate the logs from QEMU we need to - # wait for some time. Launching and then immediately shutting down - # the VM generates empty logs. A delay of 1 second is added for - # this reason. - DELAY_Q35_BOOT_SEQUENCE = 1 - - # first, lets test some 32-bit processors. - # for all 32-bit cases, pci64_hole_size is 0. - def test_phybits_low_pse36(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - With pse36 feature ON, a processor has 36 bits of addressing. So it can - access up to a maximum of 64GiB of memory. Memory hotplug region begins - at 4 GiB boundary when "above_4g_mem_size" is 0 (this would be true when - we have 0.5 GiB of VM memory, see pc_q35_init()). This means total - hotpluggable memory size is 60 GiB. Per slot, we reserve 1 GiB of memory - for dimm alignment for all machines. That leaves total hotpluggable - actual memory size of 59 GiB. If the VM is started with 0.5 GiB of - memory, maxmem should be set to a maximum value of 59.5 GiB to ensure - that the processor can address all memory directly. - Note that 64-bit pci hole size is 0 in this case. If maxmem is set to - 59.6G, QEMU should fail to start with a message "phy-bits are too low". - If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU - should start fine. - """ - self.vm.add_args('-S', '-machine', 'q35', '-m', - '512,slots=1,maxmem=59.6G', - '-cpu', 'pentium,pse36=on', '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - self.vm.wait() - self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") - self.assertRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_low_pae(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - With pae feature ON, a processor has 36 bits of addressing. So it can - access up to a maximum of 64GiB of memory. Rest is the same as the case - with pse36 above. - """ - self.vm.add_args('-S', '-machine', 'q35', '-m', - '512,slots=1,maxmem=59.6G', - '-cpu', 'pentium,pae=on', '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - self.vm.wait() - self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") - self.assertRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_ok_pentium_pse36(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - Setting maxmem to 59.5G and making sure that QEMU can start with the - same options as the failing case above with pse36 cpu feature. - """ - self.vm.add_args('-machine', 'q35', '-m', - '512,slots=1,maxmem=59.5G', - '-cpu', 'pentium,pse36=on', '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) - self.vm.shutdown() - self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_ok_pentium_pae(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - Test is same as above but now with pae cpu feature turned on. - Setting maxmem to 59.5G and making sure that QEMU can start fine - with the same options as the case above. - """ - self.vm.add_args('-machine', 'q35', '-m', - '512,slots=1,maxmem=59.5G', - '-cpu', 'pentium,pae=on', '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) - self.vm.shutdown() - self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_ok_pentium2(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - Pentium2 has 36 bits of addressing, so its same as pentium - with pse36 ON. - """ - self.vm.add_args('-machine', 'q35', '-m', - '512,slots=1,maxmem=59.5G', - '-cpu', 'pentium2', '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) - self.vm.shutdown() - self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_low_nonpse36(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - Pentium processor has 32 bits of addressing without pse36 or pae - so it can access physical address up to 4 GiB. Setting maxmem to - 4 GiB should make QEMU fail to start with "phys-bits too low" - message because the region for memory hotplug is always placed - above 4 GiB due to the PCI hole and simplicity. - """ - self.vm.add_args('-S', '-machine', 'q35', '-m', - '512,slots=1,maxmem=4G', - '-cpu', 'pentium', '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - self.vm.wait() - self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") - self.assertRegex(self.vm.get_log(), r'phys-bits too low') - - # now lets test some 64-bit CPU cases. - def test_phybits_low_tcg_q35_70_amd(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - For q35 7.1 machines and above, there is a HT window that starts at - 1024 GiB and ends at 1 TiB - 1. If the max GPA falls in this range, - "above_4G" memory is adjusted to start at 1 TiB boundary for AMD cpus - in the default case. Lets test without that case for machines 7.0. - For q35-7.0 machines, "above 4G" memory starts are 4G. - pci64_hole size is 32 GiB. Since TCG_PHYS_ADDR_BITS is defined to - be 40, TCG emulated CPUs have maximum of 1 TiB (1024 GiB) of - directly addressable memory. - Hence, maxmem value at most can be - 1024 GiB - 4 GiB - 1 GiB per slot for alignment - 32 GiB + 0.5 GiB - which is equal to 987.5 GiB. Setting the value to 988 GiB should - make QEMU fail with the error message. - """ - self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', - '512,slots=1,maxmem=988G', - '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - self.vm.wait() - self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") - self.assertRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_low_tcg_q35_71_amd(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - AMD_HT_START is defined to be at 1012 GiB. So for q35 machines - version > 7.0 and AMD cpus, instead of 1024 GiB limit for 40 bit - processor address space, it has to be 1012 GiB , that is 12 GiB - less than the case above in order to accommodate HT hole. - Make sure QEMU fails when maxmem size is 976 GiB (12 GiB less - than 988 GiB). - """ - self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=976G', - '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - self.vm.wait() - self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") - self.assertRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_ok_tcg_q35_70_amd(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - Same as q35-7.0 AMD case except that here we check that QEMU can - successfully start when maxmem is < 988G. - """ - self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', - '512,slots=1,maxmem=987.5G', - '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) - self.vm.shutdown() - self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_ok_tcg_q35_71_amd(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - Same as q35-7.1 AMD case except that here we check that QEMU can - successfully start when maxmem is < 976G. - """ - self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=975.5G', - '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) - self.vm.shutdown() - self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_ok_tcg_q35_71_intel(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - Same parameters as test_phybits_low_tcg_q35_71_amd() but use - Intel cpu instead. QEMU should start fine in this case as - "above_4G" memory starts at 4G. - """ - self.vm.add_args('-S', '-cpu', 'Skylake-Server', - '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=976G', - '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) - self.vm.shutdown() - self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_low_tcg_q35_71_amd_41bits(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - AMD processor with 41 bits. Max cpu hw address = 2 TiB. - By setting maxram above 1012 GiB - 32 GiB - 4 GiB = 976 GiB, we can - force "above_4G" memory to start at 1 TiB for q35-7.1 machines - (max GPA will be above AMD_HT_START which is defined as 1012 GiB). - - With pci_64_hole size at 32 GiB, in this case, maxmem should be 991.5 - GiB with 1 GiB per slot for alignment and 0.5 GiB as non-hotplug - memory for the VM (1024 - 32 - 1 + 0.5). With 992 GiB, QEMU should - fail to start. - """ - self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', - '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=992G', - '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - self.vm.wait() - self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") - self.assertRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_ok_tcg_q35_71_amd_41bits(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - AMD processor with 41 bits. Max cpu hw address = 2 TiB. - Same as above but by setting maxram between 976 GiB and 992 Gib, - QEMU should start fine. - """ - self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', - '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=990G', - '-display', 'none', - '-object', 'memory-backend-ram,id=mem1,size=1G', - '-device', 'pc-dimm,id=vm0,memdev=mem1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) - self.vm.shutdown() - self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_low_tcg_q35_intel_cxl(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - cxl memory window starts after memory device range. Here, we use 1 GiB - of cxl window memory. 4G_mem end aligns at 4G. pci64_hole is 32 GiB and - starts after the cxl memory window. - So maxmem here should be at most 986 GiB considering all memory boundary - alignment constraints with 40 bits (1 TiB) of processor physical bits. - """ - self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', - '-machine', 'q35,cxl=on', '-m', - '512,slots=1,maxmem=987G', - '-display', 'none', - '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1', - '-M', 'cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - self.vm.wait() - self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") - self.assertRegex(self.vm.get_log(), r'phys-bits too low') - - def test_phybits_ok_tcg_q35_intel_cxl(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - - Same as above but here we do not reserve any cxl memory window. Hence, - with the exact same parameters as above, QEMU should start fine even - with cxl enabled. - """ - self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', - '-machine', 'q35,cxl=on', '-m', - '512,slots=1,maxmem=987G', - '-display', 'none', - '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) - self.vm.shutdown() - self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') diff --git a/tests/avocado/pc_cpu_hotplug_props.py b/tests/avocado/pc_cpu_hotplug_props.py deleted file mode 100644 index 4bd3e02665..0000000000 --- a/tests/avocado/pc_cpu_hotplug_props.py +++ /dev/null @@ -1,35 +0,0 @@ -# -# Ensure CPU die-id can be omitted on -device -# -# Copyright (c) 2019 Red Hat Inc -# -# Author: -# Eduardo Habkost -# -# 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 . -# - -from avocado_qemu import QemuSystemTest - -class OmittedCPUProps(QemuSystemTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=cpu:qemu64 - """ - def test_no_die_id(self): - self.vm.add_args('-nodefaults', '-S') - self.vm.add_args('-smp', '1,sockets=2,cores=2,threads=2,maxcpus=8') - self.vm.add_args('-device', 'qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0') - self.vm.launch() - self.assertEqual(len(self.vm.cmd('query-cpus-fast')), 2) diff --git a/tests/avocado/virtio_version.py b/tests/avocado/virtio_version.py deleted file mode 100644 index afe5e828b5..0000000000 --- a/tests/avocado/virtio_version.py +++ /dev/null @@ -1,175 +0,0 @@ -""" -Check compatibility of virtio device types -""" -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Eduardo Habkost -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. -import sys -import os - -from qemu.machine import QEMUMachine -from avocado_qemu import QemuSystemTest - -# Virtio Device IDs: -VIRTIO_NET = 1 -VIRTIO_BLOCK = 2 -VIRTIO_CONSOLE = 3 -VIRTIO_RNG = 4 -VIRTIO_BALLOON = 5 -VIRTIO_RPMSG = 7 -VIRTIO_SCSI = 8 -VIRTIO_9P = 9 -VIRTIO_RPROC_SERIAL = 11 -VIRTIO_CAIF = 12 -VIRTIO_GPU = 16 -VIRTIO_INPUT = 18 -VIRTIO_VSOCK = 19 -VIRTIO_CRYPTO = 20 - -PCI_VENDOR_ID_REDHAT_QUMRANET = 0x1af4 - -# Device IDs for legacy/transitional devices: -PCI_LEGACY_DEVICE_IDS = { - VIRTIO_NET: 0x1000, - VIRTIO_BLOCK: 0x1001, - VIRTIO_BALLOON: 0x1002, - VIRTIO_CONSOLE: 0x1003, - VIRTIO_SCSI: 0x1004, - VIRTIO_RNG: 0x1005, - VIRTIO_9P: 0x1009, - VIRTIO_VSOCK: 0x1012, -} - -def pci_modern_device_id(virtio_devid): - return virtio_devid + 0x1040 - -def devtype_implements(vm, devtype, implements): - return devtype in [d['name'] for d in - vm.cmd('qom-list-types', implements=implements)] - -def get_pci_interfaces(vm, devtype): - interfaces = ('pci-express-device', 'conventional-pci-device') - return [i for i in interfaces if devtype_implements(vm, devtype, i)] - -class VirtioVersionCheck(QemuSystemTest): - """ - Check if virtio-version-specific device types result in the - same device tree created by `disable-modern` and - `disable-legacy`. - - :avocado: tags=arch:x86_64 - """ - - # just in case there are failures, show larger diff: - maxDiff = 4096 - - def run_device(self, devtype, opts=None, machine='pc'): - """ - Run QEMU with `-device DEVTYPE`, return device info from `query-pci` - """ - with QEMUMachine(self.qemu_bin) as vm: - vm.set_machine(machine) - if opts: - devtype += ',' + opts - vm.add_args('-device', '%s,id=devfortest' % (devtype)) - vm.add_args('-S') - vm.launch() - - pcibuses = vm.cmd('query-pci') - alldevs = [dev for bus in pcibuses for dev in bus['devices']] - devfortest = [dev for dev in alldevs - if dev['qdev_id'] == 'devfortest'] - return devfortest[0], get_pci_interfaces(vm, devtype) - - - def assert_devids(self, dev, devid, non_transitional=False): - self.assertEqual(dev['id']['vendor'], PCI_VENDOR_ID_REDHAT_QUMRANET) - self.assertEqual(dev['id']['device'], devid) - if non_transitional: - self.assertTrue(0x1040 <= dev['id']['device'] <= 0x107f) - self.assertGreaterEqual(dev['id']['subsystem'], 0x40) - - def check_all_variants(self, qemu_devtype, virtio_devid): - """Check if a virtio device type and its variants behave as expected""" - # Force modern mode: - dev_modern, _ = self.run_device(qemu_devtype, - 'disable-modern=off,disable-legacy=on') - self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), - non_transitional=True) - - # -non-transitional device types should be 100% equivalent to - # ,disable-modern=off,disable-legacy=on - dev_1_0, nt_ifaces = self.run_device('%s-non-transitional' % (qemu_devtype)) - self.assertEqual(dev_modern, dev_1_0) - - # Force transitional mode: - dev_trans, _ = self.run_device(qemu_devtype, - 'disable-modern=off,disable-legacy=off') - self.assert_devids(dev_trans, PCI_LEGACY_DEVICE_IDS[virtio_devid]) - - # Force legacy mode: - dev_legacy, _ = self.run_device(qemu_devtype, - 'disable-modern=on,disable-legacy=off') - self.assert_devids(dev_legacy, PCI_LEGACY_DEVICE_IDS[virtio_devid]) - - # No options: default to transitional on PC machine-type: - no_opts_pc, generic_ifaces = self.run_device(qemu_devtype) - self.assertEqual(dev_trans, no_opts_pc) - - #TODO: check if plugging on a PCI Express bus will make the - # device non-transitional - #no_opts_q35 = self.run_device(qemu_devtype, machine='q35') - #self.assertEqual(dev_modern, no_opts_q35) - - # -transitional device types should be 100% equivalent to - # ,disable-modern=off,disable-legacy=off - dev_trans, trans_ifaces = self.run_device('%s-transitional' % (qemu_devtype)) - self.assertEqual(dev_trans, dev_trans) - - # ensure the interface information is correct: - self.assertIn('conventional-pci-device', generic_ifaces) - self.assertIn('pci-express-device', generic_ifaces) - - self.assertIn('conventional-pci-device', nt_ifaces) - self.assertIn('pci-express-device', nt_ifaces) - - self.assertIn('conventional-pci-device', trans_ifaces) - self.assertNotIn('pci-express-device', trans_ifaces) - - - def test_conventional_devs(self): - self.check_all_variants('virtio-net-pci', VIRTIO_NET) - # virtio-blk requires 'driver' parameter - #self.check_all_variants('virtio-blk-pci', VIRTIO_BLOCK) - self.check_all_variants('virtio-serial-pci', VIRTIO_CONSOLE) - self.check_all_variants('virtio-rng-pci', VIRTIO_RNG) - self.check_all_variants('virtio-balloon-pci', VIRTIO_BALLOON) - self.check_all_variants('virtio-scsi-pci', VIRTIO_SCSI) - # virtio-9p requires 'fsdev' parameter - #self.check_all_variants('virtio-9p-pci', VIRTIO_9P) - - def check_modern_only(self, qemu_devtype, virtio_devid): - """Check if a modern-only virtio device type behaves as expected""" - # Force modern mode: - dev_modern, _ = self.run_device(qemu_devtype, - 'disable-modern=off,disable-legacy=on') - self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), - non_transitional=True) - - # No options: should be modern anyway - dev_no_opts, ifaces = self.run_device(qemu_devtype) - self.assertEqual(dev_modern, dev_no_opts) - - self.assertIn('conventional-pci-device', ifaces) - self.assertIn('pci-express-device', ifaces) - - def test_modern_only_devs(self): - self.check_modern_only('virtio-vga', VIRTIO_GPU) - self.check_modern_only('virtio-gpu-pci', VIRTIO_GPU) - self.check_modern_only('virtio-mouse-pci', VIRTIO_INPUT) - self.check_modern_only('virtio-tablet-pci', VIRTIO_INPUT) - self.check_modern_only('virtio-keyboard-pci', VIRTIO_INPUT) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 052d2e8781..91201a2e26 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -14,6 +14,7 @@ test_timeouts = { } tests_generic_system = [ + 'empty_cpu_model', ] tests_generic_linuxuser = [ @@ -23,6 +24,10 @@ tests_generic_bsduser = [ ] tests_x86_64_system_quick = [ + 'cpu_queries', + 'mem_addr_space', + 'pc_cpu_hotplug_props', + 'virtio_version', ] tests_x86_64_system_thorough = [ diff --git a/tests/functional/test_cpu_queries.py b/tests/functional/test_cpu_queries.py new file mode 100755 index 0000000000..b1122a0e8f --- /dev/null +++ b/tests/functional/test_cpu_queries.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# +# Sanity check of query-cpu-* results +# +# Copyright (c) 2019 Red Hat, Inc. +# +# Author: +# Eduardo Habkost +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from qemu_test import QemuSystemTest + +class QueryCPUModelExpansion(QemuSystemTest): + """ + Run query-cpu-model-expansion for each CPU model, and validate results + """ + + def test(self): + self.set_machine('none') + self.vm.add_args('-S') + self.vm.launch() + + cpus = self.vm.cmd('query-cpu-definitions') + for c in cpus: + self.log.info("Checking CPU: %s", c) + self.assertNotIn('', c['unavailable-features'], c['name']) + + for c in cpus: + model = {'name': c['name']} + e = self.vm.cmd('query-cpu-model-expansion', model=model, + type='full') + self.assertEqual(e['model']['name'], c['name']) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_empty_cpu_model.py b/tests/functional/test_empty_cpu_model.py new file mode 100755 index 0000000000..0081b06d85 --- /dev/null +++ b/tests/functional/test_empty_cpu_model.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# +# Check for crash when using empty -cpu option +# +# Copyright (c) 2019 Red Hat, Inc. +# +# Author: +# Eduardo Habkost +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. +from qemu_test import QemuSystemTest + +class EmptyCPUModel(QemuSystemTest): + def test(self): + self.vm.add_args('-S', '-display', 'none', '-machine', 'none', '-cpu', '') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'-cpu option cannot be empty') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_mem_addr_space.py b/tests/functional/test_mem_addr_space.py new file mode 100755 index 0000000000..bb0cf062ca --- /dev/null +++ b/tests/functional/test_mem_addr_space.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python3 +# +# Check for crash when using memory beyond the available guest processor +# address space. +# +# Copyright (c) 2023 Red Hat, Inc. +# +# Author: +# Ani Sinha +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest +import time + +class MemAddrCheck(QemuSystemTest): + # after launch, in order to generate the logs from QEMU we need to + # wait for some time. Launching and then immediately shutting down + # the VM generates empty logs. A delay of 1 second is added for + # this reason. + DELAY_Q35_BOOT_SEQUENCE = 1 + + # first, lets test some 32-bit processors. + # for all 32-bit cases, pci64_hole_size is 0. + def test_phybits_low_pse36(self): + """ + With pse36 feature ON, a processor has 36 bits of addressing. So it can + access up to a maximum of 64GiB of memory. Memory hotplug region begins + at 4 GiB boundary when "above_4g_mem_size" is 0 (this would be true when + we have 0.5 GiB of VM memory, see pc_q35_init()). This means total + hotpluggable memory size is 60 GiB. Per slot, we reserve 1 GiB of memory + for dimm alignment for all machines. That leaves total hotpluggable + actual memory size of 59 GiB. If the VM is started with 0.5 GiB of + memory, maxmem should be set to a maximum value of 59.5 GiB to ensure + that the processor can address all memory directly. + Note that 64-bit pci hole size is 0 in this case. If maxmem is set to + 59.6G, QEMU should fail to start with a message "phy-bits are too low". + If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU + should start fine. + """ + self.vm.add_args('-S', '-machine', 'q35', '-m', + '512,slots=1,maxmem=59.6G', + '-cpu', 'pentium,pse36=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_pae(self): + """ + With pae feature ON, a processor has 36 bits of addressing. So it can + access up to a maximum of 64GiB of memory. Rest is the same as the case + with pse36 above. + """ + self.vm.add_args('-S', '-machine', 'q35', '-m', + '512,slots=1,maxmem=59.6G', + '-cpu', 'pentium,pae=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_pentium_pse36(self): + """ + Setting maxmem to 59.5G and making sure that QEMU can start with the + same options as the failing case above with pse36 cpu feature. + """ + self.vm.add_args('-machine', 'q35', '-m', + '512,slots=1,maxmem=59.5G', + '-cpu', 'pentium,pse36=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_pentium_pae(self): + """ + Test is same as above but now with pae cpu feature turned on. + Setting maxmem to 59.5G and making sure that QEMU can start fine + with the same options as the case above. + """ + self.vm.add_args('-machine', 'q35', '-m', + '512,slots=1,maxmem=59.5G', + '-cpu', 'pentium,pae=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_pentium2(self): + """ + Pentium2 has 36 bits of addressing, so its same as pentium + with pse36 ON. + """ + self.vm.add_args('-machine', 'q35', '-m', + '512,slots=1,maxmem=59.5G', + '-cpu', 'pentium2', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_nonpse36(self): + """ + Pentium processor has 32 bits of addressing without pse36 or pae + so it can access physical address up to 4 GiB. Setting maxmem to + 4 GiB should make QEMU fail to start with "phys-bits too low" + message because the region for memory hotplug is always placed + above 4 GiB due to the PCI hole and simplicity. + """ + self.vm.add_args('-S', '-machine', 'q35', '-m', + '512,slots=1,maxmem=4G', + '-cpu', 'pentium', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + # now lets test some 64-bit CPU cases. + def test_phybits_low_tcg_q35_70_amd(self): + """ + For q35 7.1 machines and above, there is a HT window that starts at + 1024 GiB and ends at 1 TiB - 1. If the max GPA falls in this range, + "above_4G" memory is adjusted to start at 1 TiB boundary for AMD cpus + in the default case. Lets test without that case for machines 7.0. + For q35-7.0 machines, "above 4G" memory starts are 4G. + pci64_hole size is 32 GiB. Since TCG_PHYS_ADDR_BITS is defined to + be 40, TCG emulated CPUs have maximum of 1 TiB (1024 GiB) of + directly addressable memory. + Hence, maxmem value at most can be + 1024 GiB - 4 GiB - 1 GiB per slot for alignment - 32 GiB + 0.5 GiB + which is equal to 987.5 GiB. Setting the value to 988 GiB should + make QEMU fail with the error message. + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', + '512,slots=1,maxmem=988G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_tcg_q35_71_amd(self): + """ + AMD_HT_START is defined to be at 1012 GiB. So for q35 machines + version > 7.0 and AMD cpus, instead of 1024 GiB limit for 40 bit + processor address space, it has to be 1012 GiB , that is 12 GiB + less than the case above in order to accommodate HT hole. + Make sure QEMU fails when maxmem size is 976 GiB (12 GiB less + than 988 GiB). + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=976G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_70_amd(self): + """ + Same as q35-7.0 AMD case except that here we check that QEMU can + successfully start when maxmem is < 988G. + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', + '512,slots=1,maxmem=987.5G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_71_amd(self): + """ + Same as q35-7.1 AMD case except that here we check that QEMU can + successfully start when maxmem is < 976G. + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=975.5G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_71_intel(self): + """ + Same parameters as test_phybits_low_tcg_q35_71_amd() but use + Intel cpu instead. QEMU should start fine in this case as + "above_4G" memory starts at 4G. + """ + self.vm.add_args('-S', '-cpu', 'Skylake-Server', + '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=976G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_tcg_q35_71_amd_41bits(self): + """ + AMD processor with 41 bits. Max cpu hw address = 2 TiB. + By setting maxram above 1012 GiB - 32 GiB - 4 GiB = 976 GiB, we can + force "above_4G" memory to start at 1 TiB for q35-7.1 machines + (max GPA will be above AMD_HT_START which is defined as 1012 GiB). + + With pci_64_hole size at 32 GiB, in this case, maxmem should be 991.5 + GiB with 1 GiB per slot for alignment and 0.5 GiB as non-hotplug + memory for the VM (1024 - 32 - 1 + 0.5). With 992 GiB, QEMU should + fail to start. + """ + self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', + '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=992G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_71_amd_41bits(self): + """ + AMD processor with 41 bits. Max cpu hw address = 2 TiB. + Same as above but by setting maxram between 976 GiB and 992 Gib, + QEMU should start fine. + """ + self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', + '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=990G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_tcg_q35_intel_cxl(self): + """ + cxl memory window starts after memory device range. Here, we use 1 GiB + of cxl window memory. 4G_mem end aligns at 4G. pci64_hole is 32 GiB and + starts after the cxl memory window. + So maxmem here should be at most 986 GiB considering all memory boundary + alignment constraints with 40 bits (1 TiB) of processor physical bits. + """ + self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', + '-machine', 'q35,cxl=on', '-m', + '512,slots=1,maxmem=987G', + '-display', 'none', + '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1', + '-M', 'cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_intel_cxl(self): + """ + Same as above but here we do not reserve any cxl memory window. Hence, + with the exact same parameters as above, QEMU should start fine even + with cxl enabled. + """ + self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', + '-machine', 'q35,cxl=on', '-m', + '512,slots=1,maxmem=987G', + '-display', 'none', + '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_pc_cpu_hotplug_props.py b/tests/functional/test_pc_cpu_hotplug_props.py new file mode 100755 index 0000000000..9d5a37cb17 --- /dev/null +++ b/tests/functional/test_pc_cpu_hotplug_props.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# Ensure CPU die-id can be omitted on -device +# +# Copyright (c) 2019 Red Hat Inc +# +# Author: +# Eduardo Habkost +# +# 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 . +# + +from qemu_test import QemuSystemTest + +class OmittedCPUProps(QemuSystemTest): + + def test_no_die_id(self): + self.vm.add_args('-nodefaults', '-S') + self.vm.add_args('-smp', '1,sockets=2,cores=2,threads=2,maxcpus=8') + self.vm.add_args('-device', 'qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0') + self.vm.launch() + self.assertEqual(len(self.vm.cmd('query-cpus-fast')), 2) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_virtio_version.py b/tests/functional/test_virtio_version.py new file mode 100755 index 0000000000..eb23060564 --- /dev/null +++ b/tests/functional/test_virtio_version.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +""" +Check compatibility of virtio device types +""" +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Eduardo Habkost +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. +import sys +import os + +from qemu.machine import QEMUMachine +from qemu_test import QemuSystemTest + +# Virtio Device IDs: +VIRTIO_NET = 1 +VIRTIO_BLOCK = 2 +VIRTIO_CONSOLE = 3 +VIRTIO_RNG = 4 +VIRTIO_BALLOON = 5 +VIRTIO_RPMSG = 7 +VIRTIO_SCSI = 8 +VIRTIO_9P = 9 +VIRTIO_RPROC_SERIAL = 11 +VIRTIO_CAIF = 12 +VIRTIO_GPU = 16 +VIRTIO_INPUT = 18 +VIRTIO_VSOCK = 19 +VIRTIO_CRYPTO = 20 + +PCI_VENDOR_ID_REDHAT_QUMRANET = 0x1af4 + +# Device IDs for legacy/transitional devices: +PCI_LEGACY_DEVICE_IDS = { + VIRTIO_NET: 0x1000, + VIRTIO_BLOCK: 0x1001, + VIRTIO_BALLOON: 0x1002, + VIRTIO_CONSOLE: 0x1003, + VIRTIO_SCSI: 0x1004, + VIRTIO_RNG: 0x1005, + VIRTIO_9P: 0x1009, + VIRTIO_VSOCK: 0x1012, +} + +def pci_modern_device_id(virtio_devid): + return virtio_devid + 0x1040 + +def devtype_implements(vm, devtype, implements): + return devtype in [d['name'] for d in + vm.cmd('qom-list-types', implements=implements)] + +def get_pci_interfaces(vm, devtype): + interfaces = ('pci-express-device', 'conventional-pci-device') + return [i for i in interfaces if devtype_implements(vm, devtype, i)] + +class VirtioVersionCheck(QemuSystemTest): + """ + Check if virtio-version-specific device types result in the + same device tree created by `disable-modern` and + `disable-legacy`. + """ + + # just in case there are failures, show larger diff: + maxDiff = 4096 + + def run_device(self, devtype, opts=None, machine='pc'): + """ + Run QEMU with `-device DEVTYPE`, return device info from `query-pci` + """ + with QEMUMachine(self.qemu_bin) as vm: + vm.set_machine(machine) + if opts: + devtype += ',' + opts + vm.add_args('-device', '%s,id=devfortest' % (devtype)) + vm.add_args('-S') + vm.launch() + + pcibuses = vm.cmd('query-pci') + alldevs = [dev for bus in pcibuses for dev in bus['devices']] + devfortest = [dev for dev in alldevs + if dev['qdev_id'] == 'devfortest'] + return devfortest[0], get_pci_interfaces(vm, devtype) + + + def assert_devids(self, dev, devid, non_transitional=False): + self.assertEqual(dev['id']['vendor'], PCI_VENDOR_ID_REDHAT_QUMRANET) + self.assertEqual(dev['id']['device'], devid) + if non_transitional: + self.assertTrue(0x1040 <= dev['id']['device'] <= 0x107f) + self.assertGreaterEqual(dev['id']['subsystem'], 0x40) + + def check_all_variants(self, qemu_devtype, virtio_devid): + """Check if a virtio device type and its variants behave as expected""" + # Force modern mode: + dev_modern, _ = self.run_device(qemu_devtype, + 'disable-modern=off,disable-legacy=on') + self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), + non_transitional=True) + + # -non-transitional device types should be 100% equivalent to + # ,disable-modern=off,disable-legacy=on + dev_1_0, nt_ifaces = self.run_device('%s-non-transitional' % (qemu_devtype)) + self.assertEqual(dev_modern, dev_1_0) + + # Force transitional mode: + dev_trans, _ = self.run_device(qemu_devtype, + 'disable-modern=off,disable-legacy=off') + self.assert_devids(dev_trans, PCI_LEGACY_DEVICE_IDS[virtio_devid]) + + # Force legacy mode: + dev_legacy, _ = self.run_device(qemu_devtype, + 'disable-modern=on,disable-legacy=off') + self.assert_devids(dev_legacy, PCI_LEGACY_DEVICE_IDS[virtio_devid]) + + # No options: default to transitional on PC machine-type: + no_opts_pc, generic_ifaces = self.run_device(qemu_devtype) + self.assertEqual(dev_trans, no_opts_pc) + + #TODO: check if plugging on a PCI Express bus will make the + # device non-transitional + #no_opts_q35 = self.run_device(qemu_devtype, machine='q35') + #self.assertEqual(dev_modern, no_opts_q35) + + # -transitional device types should be 100% equivalent to + # ,disable-modern=off,disable-legacy=off + dev_trans, trans_ifaces = self.run_device('%s-transitional' % (qemu_devtype)) + self.assertEqual(dev_trans, dev_trans) + + # ensure the interface information is correct: + self.assertIn('conventional-pci-device', generic_ifaces) + self.assertIn('pci-express-device', generic_ifaces) + + self.assertIn('conventional-pci-device', nt_ifaces) + self.assertIn('pci-express-device', nt_ifaces) + + self.assertIn('conventional-pci-device', trans_ifaces) + self.assertNotIn('pci-express-device', trans_ifaces) + + + def test_conventional_devs(self): + self.check_all_variants('virtio-net-pci', VIRTIO_NET) + # virtio-blk requires 'driver' parameter + #self.check_all_variants('virtio-blk-pci', VIRTIO_BLOCK) + self.check_all_variants('virtio-serial-pci', VIRTIO_CONSOLE) + self.check_all_variants('virtio-rng-pci', VIRTIO_RNG) + self.check_all_variants('virtio-balloon-pci', VIRTIO_BALLOON) + self.check_all_variants('virtio-scsi-pci', VIRTIO_SCSI) + # virtio-9p requires 'fsdev' parameter + #self.check_all_variants('virtio-9p-pci', VIRTIO_9P) + + def check_modern_only(self, qemu_devtype, virtio_devid): + """Check if a modern-only virtio device type behaves as expected""" + # Force modern mode: + dev_modern, _ = self.run_device(qemu_devtype, + 'disable-modern=off,disable-legacy=on') + self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), + non_transitional=True) + + # No options: should be modern anyway + dev_no_opts, ifaces = self.run_device(qemu_devtype) + self.assertEqual(dev_modern, dev_no_opts) + + self.assertIn('conventional-pci-device', ifaces) + self.assertIn('pci-express-device', ifaces) + + def test_modern_only_devs(self): + self.check_modern_only('virtio-vga', VIRTIO_GPU) + self.check_modern_only('virtio-gpu-pci', VIRTIO_GPU) + self.check_modern_only('virtio-mouse-pci', VIRTIO_INPUT) + self.check_modern_only('virtio-tablet-pci', VIRTIO_INPUT) + self.check_modern_only('virtio-keyboard-pci', VIRTIO_INPUT) + +if __name__ == '__main__': + QemuSystemTest.main()