xref: /qemu/tests/functional/test_mem_addr_space.py (revision 9f7cf938efc6016f7ce323b064c2f3f46360c751)
1#!/usr/bin/env python3
2#
3# Check for crash when using memory beyond the available guest processor
4# address space.
5#
6# Copyright (c) 2023 Red Hat, Inc.
7#
8# Author:
9#  Ani Sinha <anisinha@redhat.com>
10#
11# SPDX-License-Identifier: GPL-2.0-or-later
12
13from qemu_test import QemuSystemTest
14import time
15
16class MemAddrCheck(QemuSystemTest):
17    # after launch, in order to generate the logs from QEMU we need to
18    # wait for some time. Launching and then immediately shutting down
19    # the VM generates empty logs. A delay of 1 second is added for
20    # this reason.
21    DELAY_Q35_BOOT_SEQUENCE = 1
22
23    # This helper can go away when the 32-bit host deprecation
24    # turns into full & final removal of support.
25    def ensure_64bit_binary(self):
26        with open(self.qemu_bin, "rb") as fh:
27            ident = fh.read(4)
28
29            # "\x7fELF"
30            if ident != bytes([0x7f, 0x45, 0x4C, 0x46]):
31                # Non-ELF file implies macOS or Windows which
32                # we already assume to be 64-bit only
33                return
34
35            # bits == 1 -> 32-bit; bits == 2 -> 64-bit
36            bits = int.from_bytes(fh.read(1), byteorder='little')
37            if bits != 2:
38                # 32-bit ELF builds won't be able to address sufficient
39                # RAM to run the tests
40                self.skipTest("64-bit build host is required")
41
42    # first, lets test some 32-bit processors.
43    # for all 32-bit cases, pci64_hole_size is 0.
44    def test_phybits_low_pse36(self):
45        """
46        With pse36 feature ON, a processor has 36 bits of addressing. So it can
47        access up to a maximum of 64GiB of memory. Memory hotplug region begins
48        at 4 GiB boundary when "above_4g_mem_size" is 0 (this would be true when
49        we have 0.5 GiB of VM memory, see pc_q35_init()). This means total
50        hotpluggable memory size is 60 GiB. Per slot, we reserve 1 GiB of memory
51        for dimm alignment for all machines. That leaves total hotpluggable
52        actual memory size of 59 GiB. If the VM is started with 0.5 GiB of
53        memory, maxmem should be set to a maximum value of 59.5 GiB to ensure
54        that the processor can address all memory directly.
55        Note that 64-bit pci hole size is 0 in this case. If maxmem is set to
56        59.6G, QEMU should fail to start with a message "phy-bits are too low".
57        If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU
58        should start fine.
59        """
60        self.ensure_64bit_binary()
61        self.set_machine('q35')
62        self.vm.add_args('-S', '-m', '512,slots=1,maxmem=59.6G',
63                         '-cpu', 'pentium,pse36=on', '-display', 'none',
64                         '-object', 'memory-backend-ram,id=mem1,size=1G',
65                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
66        self.vm.set_qmp_monitor(enabled=False)
67        self.vm.launch()
68        self.vm.wait()
69        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
70        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
71
72    def test_phybits_low_pae(self):
73        """
74        With pae feature ON, a processor has 36 bits of addressing. So it can
75        access up to a maximum of 64GiB of memory. Rest is the same as the case
76        with pse36 above.
77        """
78        self.ensure_64bit_binary()
79        self.set_machine('q35')
80        self.vm.add_args('-S', '-m', '512,slots=1,maxmem=59.6G',
81                         '-cpu', 'pentium,pae=on', '-display', 'none',
82                         '-object', 'memory-backend-ram,id=mem1,size=1G',
83                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
84        self.vm.set_qmp_monitor(enabled=False)
85        self.vm.launch()
86        self.vm.wait()
87        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
88        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
89
90    def test_phybits_ok_pentium_pse36(self):
91        """
92        Setting maxmem to 59.5G and making sure that QEMU can start with the
93        same options as the failing case above with pse36 cpu feature.
94        """
95        self.ensure_64bit_binary()
96        self.set_machine('q35')
97        self.vm.add_args('-m', '512,slots=1,maxmem=59.5G',
98                         '-cpu', 'pentium,pse36=on', '-display', 'none',
99                         '-object', 'memory-backend-ram,id=mem1,size=1G',
100                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
101        self.vm.set_qmp_monitor(enabled=False)
102        self.vm.launch()
103        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
104        self.vm.shutdown()
105        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
106
107    def test_phybits_ok_pentium_pae(self):
108        """
109        Test is same as above but now with pae cpu feature turned on.
110        Setting maxmem to 59.5G and making sure that QEMU can start fine
111        with the same options as the case above.
112        """
113        self.ensure_64bit_binary()
114        self.set_machine('q35')
115        self.vm.add_args('-m', '512,slots=1,maxmem=59.5G',
116                         '-cpu', 'pentium,pae=on', '-display', 'none',
117                         '-object', 'memory-backend-ram,id=mem1,size=1G',
118                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
119        self.vm.set_qmp_monitor(enabled=False)
120        self.vm.launch()
121        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
122        self.vm.shutdown()
123        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
124
125    def test_phybits_ok_pentium2(self):
126        """
127        Pentium2 has 36 bits of addressing, so its same as pentium
128        with pse36 ON.
129        """
130        self.ensure_64bit_binary()
131        self.set_machine('q35')
132        self.vm.add_args('-m', '512,slots=1,maxmem=59.5G',
133                         '-cpu', 'pentium2', '-display', 'none',
134                         '-object', 'memory-backend-ram,id=mem1,size=1G',
135                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
136        self.vm.set_qmp_monitor(enabled=False)
137        self.vm.launch()
138        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
139        self.vm.shutdown()
140        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
141
142    def test_phybits_low_nonpse36(self):
143        """
144        Pentium processor has 32 bits of addressing without pse36 or pae
145        so it can access physical address up to 4 GiB. Setting maxmem to
146        4 GiB should make QEMU fail to start with "phys-bits too low"
147        message because the region for memory hotplug is always placed
148        above 4 GiB due to the PCI hole and simplicity.
149        """
150        self.ensure_64bit_binary()
151        self.set_machine('q35')
152        self.vm.add_args('-S', '-m', '512,slots=1,maxmem=4G',
153                         '-cpu', 'pentium', '-display', 'none',
154                         '-object', 'memory-backend-ram,id=mem1,size=1G',
155                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
156        self.vm.set_qmp_monitor(enabled=False)
157        self.vm.launch()
158        self.vm.wait()
159        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
160        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
161
162    # now lets test some 64-bit CPU cases.
163    def test_phybits_low_tcg_q35_70_amd(self):
164        """
165        For q35 7.1 machines and above, there is a HT window that starts at
166        1024 GiB and ends at 1 TiB - 1. If the max GPA falls in this range,
167        "above_4G" memory is adjusted to start at 1 TiB boundary for AMD cpus
168        in the default case. Lets test without that case for machines 7.0.
169        For q35-7.0 machines, "above 4G" memory starts are 4G.
170        pci64_hole size is 32 GiB. Since TCG_PHYS_ADDR_BITS is defined to
171        be 40, TCG emulated CPUs have maximum of 1 TiB (1024 GiB) of
172        directly addressable memory.
173        Hence, maxmem value at most can be
174        1024 GiB - 4 GiB - 1 GiB per slot for alignment - 32 GiB + 0.5 GiB
175        which is equal to 987.5 GiB. Setting the value to 988 GiB should
176        make QEMU fail with the error message.
177        """
178        self.ensure_64bit_binary()
179        self.set_machine('pc-q35-7.0')
180        self.vm.add_args('-S', '-m', '512,slots=1,maxmem=988G',
181                         '-display', 'none',
182                         '-object', 'memory-backend-ram,id=mem1,size=1G',
183                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
184        self.vm.set_qmp_monitor(enabled=False)
185        self.vm.launch()
186        self.vm.wait()
187        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
188        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
189
190    def test_phybits_low_tcg_q35_71_amd(self):
191        """
192        AMD_HT_START is defined to be at 1012 GiB. So for q35 machines
193        version > 7.0 and AMD cpus, instead of 1024 GiB limit for 40 bit
194        processor address space, it has to be 1012 GiB , that is 12 GiB
195        less than the case above in order to accommodate HT hole.
196        Make sure QEMU fails when maxmem size is 976 GiB (12 GiB less
197        than 988 GiB).
198        """
199        self.ensure_64bit_binary()
200        self.set_machine('pc-q35-7.1')
201        self.vm.add_args('-S', '-m', '512,slots=1,maxmem=976G',
202                         '-display', 'none',
203                         '-object', 'memory-backend-ram,id=mem1,size=1G',
204                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
205        self.vm.set_qmp_monitor(enabled=False)
206        self.vm.launch()
207        self.vm.wait()
208        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
209        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
210
211    def test_phybits_ok_tcg_q35_70_amd(self):
212        """
213        Same as q35-7.0 AMD case except that here we check that QEMU can
214        successfully start when maxmem is < 988G.
215        """
216        self.ensure_64bit_binary()
217        self.set_machine('pc-q35-7.0')
218        self.vm.add_args('-S', '-m', '512,slots=1,maxmem=987.5G',
219                         '-display', 'none',
220                         '-object', 'memory-backend-ram,id=mem1,size=1G',
221                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
222        self.vm.set_qmp_monitor(enabled=False)
223        self.vm.launch()
224        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
225        self.vm.shutdown()
226        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
227
228    def test_phybits_ok_tcg_q35_71_amd(self):
229        """
230        Same as q35-7.1 AMD case except that here we check that QEMU can
231        successfully start when maxmem is < 976G.
232        """
233        self.ensure_64bit_binary()
234        self.set_machine('pc-q35-7.1')
235        self.vm.add_args('-S', '-m', '512,slots=1,maxmem=975.5G',
236                         '-display', 'none',
237                         '-object', 'memory-backend-ram,id=mem1,size=1G',
238                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
239        self.vm.set_qmp_monitor(enabled=False)
240        self.vm.launch()
241        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
242        self.vm.shutdown()
243        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
244
245    def test_phybits_ok_tcg_q35_71_intel(self):
246        """
247        Same parameters as test_phybits_low_tcg_q35_71_amd() but use
248        Intel cpu instead. QEMU should start fine in this case as
249        "above_4G" memory starts at 4G.
250        """
251        self.ensure_64bit_binary()
252        self.set_machine('pc-q35-7.1')
253        self.vm.add_args('-S', '-cpu', 'Skylake-Server',
254                         '-m', '512,slots=1,maxmem=976G',
255                         '-display', 'none',
256                         '-object', 'memory-backend-ram,id=mem1,size=1G',
257                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
258        self.vm.set_qmp_monitor(enabled=False)
259        self.vm.launch()
260        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
261        self.vm.shutdown()
262        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
263
264    def test_phybits_low_tcg_q35_71_amd_41bits(self):
265        """
266        AMD processor with 41 bits. Max cpu hw address = 2 TiB.
267        By setting maxram above 1012 GiB  - 32 GiB - 4 GiB = 976 GiB, we can
268        force "above_4G" memory to start at 1 TiB for q35-7.1 machines
269        (max GPA will be above AMD_HT_START which is defined as 1012 GiB).
270
271        With pci_64_hole size at 32 GiB, in this case, maxmem should be 991.5
272        GiB with 1 GiB per slot for alignment and 0.5 GiB as non-hotplug
273        memory for the VM (1024 - 32 - 1 + 0.5). With 992 GiB, QEMU should
274        fail to start.
275        """
276        self.ensure_64bit_binary()
277        self.set_machine('pc-q35-7.1')
278        self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41',
279                         '-m', '512,slots=1,maxmem=992G',
280                         '-display', 'none',
281                         '-object', 'memory-backend-ram,id=mem1,size=1G',
282                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
283        self.vm.set_qmp_monitor(enabled=False)
284        self.vm.launch()
285        self.vm.wait()
286        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
287        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
288
289    def test_phybits_ok_tcg_q35_71_amd_41bits(self):
290        """
291        AMD processor with 41 bits. Max cpu hw address = 2 TiB.
292        Same as above but by setting maxram between 976 GiB and 992 Gib,
293        QEMU should start fine.
294        """
295        self.ensure_64bit_binary()
296        self.set_machine('pc-q35-7.1')
297        self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41',
298                         '-m', '512,slots=1,maxmem=990G',
299                         '-display', 'none',
300                         '-object', 'memory-backend-ram,id=mem1,size=1G',
301                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
302        self.vm.set_qmp_monitor(enabled=False)
303        self.vm.launch()
304        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
305        self.vm.shutdown()
306        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
307
308    def test_phybits_low_tcg_q35_intel_cxl(self):
309        """
310        cxl memory window starts after memory device range. Here, we use 1 GiB
311        of cxl window memory. 4G_mem end aligns at 4G. pci64_hole is 32 GiB and
312        starts after the cxl memory window.
313        So maxmem here should be at most 986 GiB considering all memory boundary
314        alignment constraints with 40 bits (1 TiB) of processor physical bits.
315        """
316        self.ensure_64bit_binary()
317        self.set_machine('q35')
318        self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40',
319                         '-m', '512,slots=1,maxmem=987G',
320                         '-display', 'none',
321                         '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1',
322                         '-M', 'cxl=on,cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G')
323        self.vm.set_qmp_monitor(enabled=False)
324        self.vm.launch()
325        self.vm.wait()
326        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
327        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
328
329    def test_phybits_ok_tcg_q35_intel_cxl(self):
330        """
331        Same as above but here we do not reserve any cxl memory window. Hence,
332        with the exact same parameters as above, QEMU should start fine even
333        with cxl enabled.
334        """
335        self.ensure_64bit_binary()
336        self.set_machine('q35')
337        self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40',
338                         '-machine', 'cxl=on',
339                         '-m', '512,slots=1,maxmem=987G',
340                         '-display', 'none',
341                         '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1')
342        self.vm.set_qmp_monitor(enabled=False)
343        self.vm.launch()
344        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
345        self.vm.shutdown()
346        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
347
348if __name__ == '__main__':
349    QemuSystemTest.main()
350