aboutsummaryrefslogtreecommitdiff
path: root/tests/avocado/mem-addr-space-check.py
blob: af019969c066ee6c5e6892155d16f9b5b5549e56 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# Check for crash when using memory beyond the available guest processor
# address space.
#
# Copyright (c) 2023 Red Hat, Inc.
#
# Author:
#  Ani Sinha <anisinha@redhat.com>
#
# SPDX-License-Identifier: GPL-2.0-or-later

from avocado_qemu import QemuSystemTest
import signal
import time

class MemAddrCheck(QemuSystemTest):
    # after launch, in order to generate the logs from QEMU we need to
    # wait for some time. Launching and then immediately shutting down
    # the VM generates empty logs. A delay of 1 second is added for
    # this reason.
    DELAY_Q35_BOOT_SEQUENCE = 1

    # first, lets test some 32-bit processors.
    # for all 32-bit cases, pci64_hole_size is 0.
    def test_phybits_low_pse36(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        With pse36 feature ON, a processor has 36 bits of addressing. So it can
        access up to a maximum of 64GiB of memory. Memory hotplug region begins
        at 4 GiB boundary when "above_4g_mem_size" is 0 (this would be true when
        we have 0.5 GiB of VM memory, see pc_q35_init()). This means total
        hotpluggable memory size is 60 GiB. Per slot, we reserve 1 GiB of memory
        for dimm alignment for all newer machines (see enforce_aligned_dimm
        property for pc machines and pc_get_device_memory_range()). That leaves
        total hotpluggable actual memory size of 59 GiB. If the VM is started
        with 0.5 GiB of memory, maxmem should be set to a maximum value of
        59.5 GiB to ensure that the processor can address all memory directly.
        Note that 64-bit pci hole size is 0 in this case. If maxmem is set to
        59.6G, QEMU should fail to start with a message "phy-bits are too low".
        If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU
        should start fine.
        """
        self.vm.add_args('-S', '-machine', 'q35', '-m',
                         '512,slots=1,maxmem=59.6G',
                         '-cpu', 'pentium,pse36=on', '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        self.vm.wait()
        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
        self.assertRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_low_pae(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        With pae feature ON, a processor has 36 bits of addressing. So it can
        access up to a maximum of 64GiB of memory. Rest is the same as the case
        with pse36 above.
        """
        self.vm.add_args('-S', '-machine', 'q35', '-m',
                         '512,slots=1,maxmem=59.6G',
                         '-cpu', 'pentium,pae=on', '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        self.vm.wait()
        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
        self.assertRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_ok_pentium_pse36(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        Setting maxmem to 59.5G and making sure that QEMU can start with the
        same options as the failing case above with pse36 cpu feature.
        """
        self.vm.add_args('-machine', 'q35', '-m',
                         '512,slots=1,maxmem=59.5G',
                         '-cpu', 'pentium,pse36=on', '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
        self.vm.shutdown()
        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_ok_pentium_pae(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        Test is same as above but now with pae cpu feature turned on.
        Setting maxmem to 59.5G and making sure that QEMU can start fine
        with the same options as the case above.
        """
        self.vm.add_args('-machine', 'q35', '-m',
                         '512,slots=1,maxmem=59.5G',
                         '-cpu', 'pentium,pae=on', '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
        self.vm.shutdown()
        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_ok_pentium2(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        Pentium2 has 36 bits of addressing, so its same as pentium
        with pse36 ON.
        """
        self.vm.add_args('-machine', 'q35', '-m',
                         '512,slots=1,maxmem=59.5G',
                         '-cpu', 'pentium2', '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
        self.vm.shutdown()
        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_low_nonpse36(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        Pentium processor has 32 bits of addressing without pse36 or pae
        so it can access physical address up to 4 GiB. Setting maxmem to
        4 GiB should make QEMU fail to start with "phys-bits too low"
        message because the region for memory hotplug is always placed
        above 4 GiB due to the PCI hole and simplicity.
        """
        self.vm.add_args('-S', '-machine', 'q35', '-m',
                         '512,slots=1,maxmem=4G',
                         '-cpu', 'pentium', '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        self.vm.wait()
        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
        self.assertRegex(self.vm.get_log(), r'phys-bits too low')

    # now lets test some 64-bit CPU cases.
    def test_phybits_low_tcg_q35_70_amd(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        For q35 7.1 machines and above, there is a HT window that starts at
        1024 GiB and ends at 1 TiB - 1. If the max GPA falls in this range,
        "above_4G" memory is adjusted to start at 1 TiB boundary for AMD cpus
        in the default case. Lets test without that case for machines 7.0.
        For q35-7.0 machines, "above 4G" memory starts are 4G.
        pci64_hole size is 32 GiB. Since TCG_PHYS_ADDR_BITS is defined to
        be 40, TCG emulated CPUs have maximum of 1 TiB (1024 GiB) of
        directly addressable memory.
        Hence, maxmem value at most can be
        1024 GiB - 4 GiB - 1 GiB per slot for alignment - 32 GiB + 0.5 GiB
        which is equal to 987.5 GiB. Setting the value to 988 GiB should
        make QEMU fail with the error message.
        """
        self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m',
                         '512,slots=1,maxmem=988G',
                         '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        self.vm.wait()
        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
        self.assertRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_low_tcg_q35_71_amd(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        AMD_HT_START is defined to be at 1012 GiB. So for q35 machines
        version > 7.0 and AMD cpus, instead of 1024 GiB limit for 40 bit
        processor address space, it has to be 1012 GiB , that is 12 GiB
        less than the case above in order to accommodate HT hole.
        Make sure QEMU fails when maxmem size is 976 GiB (12 GiB less
        than 988 GiB).
        """
        self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m',
                         '512,slots=1,maxmem=976G',
                         '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        self.vm.wait()
        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
        self.assertRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_ok_tcg_q35_70_amd(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        Same as q35-7.0 AMD case except that here we check that QEMU can
        successfully start when maxmem is < 988G.
        """
        self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m',
                         '512,slots=1,maxmem=987.5G',
                         '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
        self.vm.shutdown()
        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_ok_tcg_q35_71_amd(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        Same as q35-7.1 AMD case except that here we check that QEMU can
        successfully start when maxmem is < 976G.
        """
        self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m',
                         '512,slots=1,maxmem=975.5G',
                         '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
        self.vm.shutdown()
        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_ok_tcg_q35_71_intel(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        Same parameters as test_phybits_low_tcg_q35_71_amd() but use
        Intel cpu instead. QEMU should start fine in this case as
        "above_4G" memory starts at 4G.
        """
        self.vm.add_args('-S', '-cpu', 'Skylake-Server',
                         '-machine', 'pc-q35-7.1', '-m',
                         '512,slots=1,maxmem=976G',
                         '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
        self.vm.shutdown()
        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_low_tcg_q35_71_amd_41bits(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        AMD processor with 41 bits. Max cpu hw address = 2 TiB.
        By setting maxram above 1012 GiB  - 32 GiB - 4 GiB = 976 GiB, we can
        force "above_4G" memory to start at 1 TiB for q35-7.1 machines
        (max GPA will be above AMD_HT_START which is defined as 1012 GiB).

        With pci_64_hole size at 32 GiB, in this case, maxmem should be 991.5
        GiB with 1 GiB per slot for alignment and 0.5 GiB as non-hotplug
        memory for the VM (1024 - 32 - 1 + 0.5). With 992 GiB, QEMU should
        fail to start.
        """
        self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41',
                         '-machine', 'pc-q35-7.1', '-m',
                         '512,slots=1,maxmem=992G',
                         '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        self.vm.wait()
        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
        self.assertRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_ok_tcg_q35_71_amd_41bits(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        AMD processor with 41 bits. Max cpu hw address = 2 TiB.
        Same as above but by setting maxram between 976 GiB and 992 Gib,
        QEMU should start fine.
        """
        self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41',
                         '-machine', 'pc-q35-7.1', '-m',
                         '512,slots=1,maxmem=990G',
                         '-display', 'none',
                         '-object', 'memory-backend-ram,id=mem1,size=1G',
                         '-device', 'pc-dimm,id=vm0,memdev=mem1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
        self.vm.shutdown()
        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_low_tcg_q35_intel_cxl(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        cxl memory window starts after memory device range. Here, we use 1 GiB
        of cxl window memory. 4G_mem end aligns at 4G. pci64_hole is 32 GiB and
        starts after the cxl memory window.
        So maxmem here should be at most 986 GiB considering all memory boundary
        alignment constraints with 40 bits (1 TiB) of processor physical bits.
        """
        self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40',
                         '-machine', 'q35,cxl=on', '-m',
                         '512,slots=1,maxmem=987G',
                         '-display', 'none',
                         '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1',
                         '-M', 'cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        self.vm.wait()
        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
        self.assertRegex(self.vm.get_log(), r'phys-bits too low')

    def test_phybits_ok_tcg_q35_intel_cxl(self):
        """
        :avocado: tags=machine:q35
        :avocado: tags=arch:x86_64

        Same as above but here we do not reserve any cxl memory window. Hence,
        with the exact same parameters as above, QEMU should start fine even
        with cxl enabled.
        """
        self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40',
                         '-machine', 'q35,cxl=on', '-m',
                         '512,slots=1,maxmem=987G',
                         '-display', 'none',
                         '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1')
        self.vm.set_qmp_monitor(enabled=False)
        self.vm.launch()
        time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
        self.vm.shutdown()
        self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')