> On 27-Oct-2023, at 4:12 PM, Ani Sinha <anisi...@redhat.com> wrote:
>
> QEMU has validations to make sure that a VM is not started with more memory
> (static and hotpluggable memory) than what the guest processor can address
> directly with its addressing bits. This change adds a test to make sure QEMU
> fails to start with a specific error message when an attempt is made to
> start a VM with more memory than what the processor can directly address.
> The test also checks for passing cases when the address space of the processor
> is capable of addressing all memory. Boundary cases are tested.
>
> CC: imamm...@redhat.com
> CC: David Hildenbrand <da...@redhat.com>
> Signed-off-by: Ani Sinha <anisi...@redhat.com>
Gentle ping on this. David does these tests look good and cover all cases more
or less?
> ---
> tests/avocado/mem-addr-space-check.py | 331 ++++++++++++++++++++++++++
> 1 file changed, 331 insertions(+)
> create mode 100644 tests/avocado/mem-addr-space-check.py
>
> Changelog:
> v3: added pae tests as well.
> v2: added 64-bit tests. Added cxl tests.
>
> Sample run:
> $ ./pyvenv/bin/avocado run tests/avocado/mem-addr-space-check.py --tap -
> 1..14
> ok 1 tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_low_pse36
> ok 2 tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_low_pae
> ok 3
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_ok_pentium_pse36
> ok 4
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_ok_pentium_pae
> ok 5
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_ok_pentium2
> ok 6
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_low_nonpse36
> ok 7
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_low_tcg_q35_70_amd
> ok 8
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_low_tcg_q35_71_amd
> ok 9
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_ok_tcg_q35_70_amd
> ok 10
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_ok_tcg_q35_71_amd
> ok 11
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_ok_tcg_q35_71_intel
> ok 12
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_low_tcg_q35_71_amd_41bits
> ok 13
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_low_tcg_q35_intel_cxl
> ok 14
> tests/avocado/mem-addr-space-check.py:MemAddrCheck.test_phybits_ok_tcg_q35_intel_cxl
>
> diff --git a/tests/avocado/mem-addr-space-check.py
> b/tests/avocado/mem-addr-space-check.py
> new file mode 100644
> index 0000000000..6ded11d658
> --- /dev/null
> +++ b/tests/avocado/mem-addr-space-check.py
> @@ -0,0 +1,331 @@
> +# Check for crash when using memory beyond the available guest processor
> +# address space.
> +#
> +# Copyright (c) 2023 Red Hat, Inc.
> +#
> +# Author:
> +# Ani Sinha <anisi...@redhat.com>
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 2 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
> +
> +from avocado_qemu import QemuSystemTest
> +import signal
> +
> +class MemAddrCheck(QemuSystemTest):
> + # 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 newer machines (see enforce_aligned_dimm
> + property for pc machines and pc_get_device_memory_range()). 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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.wait()
> + self.assertEquals(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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.wait()
> + self.assertEquals(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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.shutdown()
> + self.assertEquals(self.vm.exitcode(), -signal.SIGTERM,
> + "QEMU did not terminate gracefully upon SIGTERM")
> +
> + 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 CPUID 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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.shutdown()
> + self.assertEquals(self.vm.exitcode(), -signal.SIGTERM,
> + "QEMU did not terminate gracefully upon SIGTERM")
> +
> + 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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.shutdown()
> + self.assertEquals(self.vm.exitcode(), -signal.SIGTERM,
> + "QEMU did not terminate gracefully upon SIGTERM")
> +
> + 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 up to 4 GiB of memory. Setting maxmem to 4GiB
> + should make QEMU fail to start with "phys-bits too low" message.
> + """
> + 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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.wait()
> + self.assertEquals(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, "above_4G" memory starts at 1 TiB
> + boundary for AMD cpus (default). Lets test without that case.
> + 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 addressible 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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.wait()
> + self.assertEquals(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.
> + 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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.wait()
> + self.assertEquals(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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.shutdown()
> + self.assertEquals(self.vm.exitcode(), -signal.SIGTERM,
> + "QEMU did not terminate gracefully upon SIGTERM")
> +
> + 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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.shutdown()
> + self.assertEquals(self.vm.exitcode(), -signal.SIGTERM,
> + "QEMU did not terminate gracefully upon SIGTERM")
> +
> + 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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.shutdown()
> + self.assertEquals(self.vm.exitcode(), -signal.SIGTERM,
> + "QEMU did not terminate gracefully upon SIGTERM")
> +
> + 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.
> + "above_4G" memory starts at 1 TiB for q35-7.1 machines. So with
> + pci_64_hole size at 32 GiB, maxmem should be 991.5 GiB with 1 GiB per
> + slot for alignment and 0.5 GiB as static 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', 'virtio-mem-pci,id=vm0,memdev=mem1')
> + self.vm.set_qmp_monitor(enabled=False)
> + self.vm.launch()
> + self.vm.wait()
> + self.assertEquals(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_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.assertEquals(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()
> + self.vm.shutdown()
> + self.assertEquals(self.vm.exitcode(), -signal.SIGTERM,
> + "QEMU did not terminate gracefully upon SIGTERM")
> --
> 2.42.0
>