master
py 349 lines 15.6 KB
Raw
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
13 from qemu_test import QemuSystemTest
14 import time
15
16 class 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
348 if __name__ == '__main__':
349 QemuSystemTest.main()