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