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