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    # first, lets test some 32-bit processors.
24    # for all 32-bit cases, pci64_hole_size is 0.
25    def test_phybits_low_pse36(self):
26        """
27        With pse36 feature ON, a processor has 36 bits of addressing. So it can
28        access up to a maximum of 64GiB of memory. Memory hotplug region begins
29        at 4 GiB boundary when "above_4g_mem_size" is 0 (this would be true when
30        we have 0.5 GiB of VM memory, see pc_q35_init()). This means total
31        hotpluggable memory size is 60 GiB. Per slot, we reserve 1 GiB of memory
32        for dimm alignment for all machines. That leaves total hotpluggable
33        actual memory size of 59 GiB. If the VM is started with 0.5 GiB of
34        memory, maxmem should be set to a maximum value of 59.5 GiB to ensure
35        that the processor can address all memory directly.
36        Note that 64-bit pci hole size is 0 in this case. If maxmem is set to
37        59.6G, QEMU should fail to start with a message "phy-bits are too low".
38        If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU
39        should start fine.
40        """
41        self.vm.add_args('-S', '-machine', 'q35', '-m',
42                         '512,slots=1,maxmem=59.6G',
43                         '-cpu', 'pentium,pse36=on', '-display', 'none',
44                         '-object', 'memory-backend-ram,id=mem1,size=1G',
45                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
46        self.vm.set_qmp_monitor(enabled=False)
47        self.vm.launch()
48        self.vm.wait()
49        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
50        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
51
52    def test_phybits_low_pae(self):
53        """
54        With pae feature ON, a processor has 36 bits of addressing. So it can
55        access up to a maximum of 64GiB of memory. Rest is the same as the case
56        with pse36 above.
57        """
58        self.vm.add_args('-S', '-machine', 'q35', '-m',
59                         '512,slots=1,maxmem=59.6G',
60                         '-cpu', 'pentium,pae=on', '-display', 'none',
61                         '-object', 'memory-backend-ram,id=mem1,size=1G',
62                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
63        self.vm.set_qmp_monitor(enabled=False)
64        self.vm.launch()
65        self.vm.wait()
66        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
67        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
68
69    def test_phybits_ok_pentium_pse36(self):
70        """
71        Setting maxmem to 59.5G and making sure that QEMU can start with the
72        same options as the failing case above with pse36 cpu feature.
73        """
74        self.vm.add_args('-machine', 'q35', '-m',
75                         '512,slots=1,maxmem=59.5G',
76                         '-cpu', 'pentium,pse36=on', '-display', 'none',
77                         '-object', 'memory-backend-ram,id=mem1,size=1G',
78                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
79        self.vm.set_qmp_monitor(enabled=False)
80        self.vm.launch()
81        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
82        self.vm.shutdown()
83        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
84
85    def test_phybits_ok_pentium_pae(self):
86        """
87        Test is same as above but now with pae cpu feature turned on.
88        Setting maxmem to 59.5G and making sure that QEMU can start fine
89        with the same options as the case above.
90        """
91        self.vm.add_args('-machine', 'q35', '-m',
92                         '512,slots=1,maxmem=59.5G',
93                         '-cpu', 'pentium,pae=on', '-display', 'none',
94                         '-object', 'memory-backend-ram,id=mem1,size=1G',
95                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
96        self.vm.set_qmp_monitor(enabled=False)
97        self.vm.launch()
98        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
99        self.vm.shutdown()
100        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
101
102    def test_phybits_ok_pentium2(self):
103        """
104        Pentium2 has 36 bits of addressing, so its same as pentium
105        with pse36 ON.
106        """
107        self.vm.add_args('-machine', 'q35', '-m',
108                         '512,slots=1,maxmem=59.5G',
109                         '-cpu', 'pentium2', '-display', 'none',
110                         '-object', 'memory-backend-ram,id=mem1,size=1G',
111                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
112        self.vm.set_qmp_monitor(enabled=False)
113        self.vm.launch()
114        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
115        self.vm.shutdown()
116        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
117
118    def test_phybits_low_nonpse36(self):
119        """
120        Pentium processor has 32 bits of addressing without pse36 or pae
121        so it can access physical address up to 4 GiB. Setting maxmem to
122        4 GiB should make QEMU fail to start with "phys-bits too low"
123        message because the region for memory hotplug is always placed
124        above 4 GiB due to the PCI hole and simplicity.
125        """
126        self.vm.add_args('-S', '-machine', 'q35', '-m',
127                         '512,slots=1,maxmem=4G',
128                         '-cpu', 'pentium', '-display', 'none',
129                         '-object', 'memory-backend-ram,id=mem1,size=1G',
130                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
131        self.vm.set_qmp_monitor(enabled=False)
132        self.vm.launch()
133        self.vm.wait()
134        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
135        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
136
137    # now lets test some 64-bit CPU cases.
138    def test_phybits_low_tcg_q35_70_amd(self):
139        """
140        For q35 7.1 machines and above, there is a HT window that starts at
141        1024 GiB and ends at 1 TiB - 1. If the max GPA falls in this range,
142        "above_4G" memory is adjusted to start at 1 TiB boundary for AMD cpus
143        in the default case. Lets test without that case for machines 7.0.
144        For q35-7.0 machines, "above 4G" memory starts are 4G.
145        pci64_hole size is 32 GiB. Since TCG_PHYS_ADDR_BITS is defined to
146        be 40, TCG emulated CPUs have maximum of 1 TiB (1024 GiB) of
147        directly addressable memory.
148        Hence, maxmem value at most can be
149        1024 GiB - 4 GiB - 1 GiB per slot for alignment - 32 GiB + 0.5 GiB
150        which is equal to 987.5 GiB. Setting the value to 988 GiB should
151        make QEMU fail with the error message.
152        """
153        self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m',
154                         '512,slots=1,maxmem=988G',
155                         '-display', 'none',
156                         '-object', 'memory-backend-ram,id=mem1,size=1G',
157                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
158        self.vm.set_qmp_monitor(enabled=False)
159        self.vm.launch()
160        self.vm.wait()
161        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
162        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
163
164    def test_phybits_low_tcg_q35_71_amd(self):
165        """
166        AMD_HT_START is defined to be at 1012 GiB. So for q35 machines
167        version > 7.0 and AMD cpus, instead of 1024 GiB limit for 40 bit
168        processor address space, it has to be 1012 GiB , that is 12 GiB
169        less than the case above in order to accommodate HT hole.
170        Make sure QEMU fails when maxmem size is 976 GiB (12 GiB less
171        than 988 GiB).
172        """
173        self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m',
174                         '512,slots=1,maxmem=976G',
175                         '-display', 'none',
176                         '-object', 'memory-backend-ram,id=mem1,size=1G',
177                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
178        self.vm.set_qmp_monitor(enabled=False)
179        self.vm.launch()
180        self.vm.wait()
181        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
182        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
183
184    def test_phybits_ok_tcg_q35_70_amd(self):
185        """
186        Same as q35-7.0 AMD case except that here we check that QEMU can
187        successfully start when maxmem is < 988G.
188        """
189        self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m',
190                         '512,slots=1,maxmem=987.5G',
191                         '-display', 'none',
192                         '-object', 'memory-backend-ram,id=mem1,size=1G',
193                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
194        self.vm.set_qmp_monitor(enabled=False)
195        self.vm.launch()
196        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
197        self.vm.shutdown()
198        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
199
200    def test_phybits_ok_tcg_q35_71_amd(self):
201        """
202        Same as q35-7.1 AMD case except that here we check that QEMU can
203        successfully start when maxmem is < 976G.
204        """
205        self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m',
206                         '512,slots=1,maxmem=975.5G',
207                         '-display', 'none',
208                         '-object', 'memory-backend-ram,id=mem1,size=1G',
209                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
210        self.vm.set_qmp_monitor(enabled=False)
211        self.vm.launch()
212        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
213        self.vm.shutdown()
214        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
215
216    def test_phybits_ok_tcg_q35_71_intel(self):
217        """
218        Same parameters as test_phybits_low_tcg_q35_71_amd() but use
219        Intel cpu instead. QEMU should start fine in this case as
220        "above_4G" memory starts at 4G.
221        """
222        self.vm.add_args('-S', '-cpu', 'Skylake-Server',
223                         '-machine', 'pc-q35-7.1', '-m',
224                         '512,slots=1,maxmem=976G',
225                         '-display', 'none',
226                         '-object', 'memory-backend-ram,id=mem1,size=1G',
227                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
228        self.vm.set_qmp_monitor(enabled=False)
229        self.vm.launch()
230        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
231        self.vm.shutdown()
232        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
233
234    def test_phybits_low_tcg_q35_71_amd_41bits(self):
235        """
236        AMD processor with 41 bits. Max cpu hw address = 2 TiB.
237        By setting maxram above 1012 GiB  - 32 GiB - 4 GiB = 976 GiB, we can
238        force "above_4G" memory to start at 1 TiB for q35-7.1 machines
239        (max GPA will be above AMD_HT_START which is defined as 1012 GiB).
240
241        With pci_64_hole size at 32 GiB, in this case, maxmem should be 991.5
242        GiB with 1 GiB per slot for alignment and 0.5 GiB as non-hotplug
243        memory for the VM (1024 - 32 - 1 + 0.5). With 992 GiB, QEMU should
244        fail to start.
245        """
246        self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41',
247                         '-machine', 'pc-q35-7.1', '-m',
248                         '512,slots=1,maxmem=992G',
249                         '-display', 'none',
250                         '-object', 'memory-backend-ram,id=mem1,size=1G',
251                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
252        self.vm.set_qmp_monitor(enabled=False)
253        self.vm.launch()
254        self.vm.wait()
255        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
256        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
257
258    def test_phybits_ok_tcg_q35_71_amd_41bits(self):
259        """
260        AMD processor with 41 bits. Max cpu hw address = 2 TiB.
261        Same as above but by setting maxram between 976 GiB and 992 Gib,
262        QEMU should start fine.
263        """
264        self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41',
265                         '-machine', 'pc-q35-7.1', '-m',
266                         '512,slots=1,maxmem=990G',
267                         '-display', 'none',
268                         '-object', 'memory-backend-ram,id=mem1,size=1G',
269                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
270        self.vm.set_qmp_monitor(enabled=False)
271        self.vm.launch()
272        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
273        self.vm.shutdown()
274        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
275
276    def test_phybits_low_tcg_q35_intel_cxl(self):
277        """
278        cxl memory window starts after memory device range. Here, we use 1 GiB
279        of cxl window memory. 4G_mem end aligns at 4G. pci64_hole is 32 GiB and
280        starts after the cxl memory window.
281        So maxmem here should be at most 986 GiB considering all memory boundary
282        alignment constraints with 40 bits (1 TiB) of processor physical bits.
283        """
284        self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40',
285                         '-machine', 'q35,cxl=on', '-m',
286                         '512,slots=1,maxmem=987G',
287                         '-display', 'none',
288                         '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1',
289                         '-M', 'cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G')
290        self.vm.set_qmp_monitor(enabled=False)
291        self.vm.launch()
292        self.vm.wait()
293        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
294        self.assertRegex(self.vm.get_log(), r'phys-bits too low')
295
296    def test_phybits_ok_tcg_q35_intel_cxl(self):
297        """
298        Same as above but here we do not reserve any cxl memory window. Hence,
299        with the exact same parameters as above, QEMU should start fine even
300        with cxl enabled.
301        """
302        self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40',
303                         '-machine', 'q35,cxl=on', '-m',
304                         '512,slots=1,maxmem=987G',
305                         '-display', 'none',
306                         '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1')
307        self.vm.set_qmp_monitor(enabled=False)
308        self.vm.launch()
309        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
310        self.vm.shutdown()
311        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
312
313if __name__ == '__main__':
314    QemuSystemTest.main()
315