1#!/usr/bin/env python3
2#
3# Basic validation of x86 versioned CPU models and CPU model aliases
4#
5#  Copyright (c) 2019 Red Hat Inc
6#
7# Author:
8#  Eduardo Habkost <ehabkost@redhat.com>
9#
10# This library is free software; you can redistribute it and/or
11# modify it under the terms of the GNU Lesser General Public
12# License as published by the Free Software Foundation; either
13# version 2.1 of the License, or (at your option) any later version.
14#
15# This library is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18# Lesser General Public License for more details.
19#
20# You should have received a copy of the GNU Lesser General Public
21# License along with this library; if not, see <http://www.gnu.org/licenses/>.
22#
23
24import re
25
26from qemu_test import QemuSystemTest
27
28class X86CPUModelAliases(QemuSystemTest):
29    """
30    Validation of PC CPU model versions and CPU model aliases
31    """
32    def validate_aliases(self, cpus):
33        for c in cpus.values():
34            if 'alias-of' in c:
35                # all aliases must point to a valid CPU model name:
36                self.assertIn(c['alias-of'], cpus,
37                              '%s.alias-of (%s) is not a valid CPU model name' % (c['name'], c['alias-of']))
38                # aliases must not point to aliases
39                self.assertNotIn('alias-of', cpus[c['alias-of']],
40                                 '%s.alias-of (%s) points to another alias' % (c['name'], c['alias-of']))
41
42                # aliases must not be static
43                self.assertFalse(c['static'])
44
45    def validate_variant_aliases(self, cpus):
46        # -noTSX, -IBRS and -IBPB variants of CPU models are special:
47        # they shouldn't have their own versions:
48        self.assertNotIn("Haswell-noTSX-v1", cpus,
49                         "Haswell-noTSX shouldn't be versioned")
50        self.assertNotIn("Broadwell-noTSX-v1", cpus,
51                         "Broadwell-noTSX shouldn't be versioned")
52        self.assertNotIn("Nehalem-IBRS-v1", cpus,
53                         "Nehalem-IBRS shouldn't be versioned")
54        self.assertNotIn("Westmere-IBRS-v1", cpus,
55                         "Westmere-IBRS shouldn't be versioned")
56        self.assertNotIn("SandyBridge-IBRS-v1", cpus,
57                         "SandyBridge-IBRS shouldn't be versioned")
58        self.assertNotIn("IvyBridge-IBRS-v1", cpus,
59                         "IvyBridge-IBRS shouldn't be versioned")
60        self.assertNotIn("Haswell-noTSX-IBRS-v1", cpus,
61                         "Haswell-noTSX-IBRS shouldn't be versioned")
62        self.assertNotIn("Haswell-IBRS-v1", cpus,
63                         "Haswell-IBRS shouldn't be versioned")
64        self.assertNotIn("Broadwell-noTSX-IBRS-v1", cpus,
65                         "Broadwell-noTSX-IBRS shouldn't be versioned")
66        self.assertNotIn("Broadwell-IBRS-v1", cpus,
67                         "Broadwell-IBRS shouldn't be versioned")
68        self.assertNotIn("Skylake-Client-IBRS-v1", cpus,
69                         "Skylake-Client-IBRS shouldn't be versioned")
70        self.assertNotIn("Skylake-Server-IBRS-v1", cpus,
71                         "Skylake-Server-IBRS shouldn't be versioned")
72        self.assertNotIn("EPYC-IBPB-v1", cpus,
73                         "EPYC-IBPB shouldn't be versioned")
74
75    def test_4_0_alias_compatibility(self):
76        """
77        Check if pc-*-4.0 unversioned CPU model won't be reported as aliases
78        """
79        self.set_machine('pc-i440fx-4.0')
80        # pc-*-4.0 won't expose non-versioned CPU models as aliases
81        # We do this to help management software to keep compatibility
82        # with older QEMU versions that didn't have the versioned CPU model
83        self.vm.add_args('-S')
84        self.vm.launch()
85        cpus = dict((m['name'], m) for m in
86                    self.vm.cmd('query-cpu-definitions'))
87
88        self.assertFalse(cpus['Cascadelake-Server']['static'],
89                         'unversioned Cascadelake-Server CPU model must not be static')
90        self.assertNotIn('alias-of', cpus['Cascadelake-Server'],
91                         'Cascadelake-Server must not be an alias')
92        self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'],
93                         'Cascadelake-Server-v1 must not be an alias')
94
95        self.assertFalse(cpus['qemu64']['static'],
96                         'unversioned qemu64 CPU model must not be static')
97        self.assertNotIn('alias-of', cpus['qemu64'],
98                         'qemu64 must not be an alias')
99        self.assertNotIn('alias-of', cpus['qemu64-v1'],
100                         'qemu64-v1 must not be an alias')
101
102        self.validate_variant_aliases(cpus)
103
104        # On pc-*-4.0, no CPU model should be reported as an alias:
105        for name,c in cpus.items():
106            self.assertNotIn('alias-of', c, "%s shouldn't be an alias" % (name))
107
108    def test_4_1_alias(self):
109        """
110        Check if unversioned CPU model is an alias pointing to right version
111        """
112        self.set_machine('pc-i440fx-4.1')
113        self.vm.add_args('-S')
114        self.vm.launch()
115
116        cpus = dict((m['name'], m) for m in
117                    self.vm.cmd('query-cpu-definitions'))
118
119        self.assertFalse(cpus['Cascadelake-Server']['static'],
120                         'unversioned Cascadelake-Server CPU model must not be static')
121        self.assertEqual(cpus['Cascadelake-Server'].get('alias-of'),
122                         'Cascadelake-Server-v1',
123                         'Cascadelake-Server must be an alias of Cascadelake-Server-v1')
124        self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'],
125                         'Cascadelake-Server-v1 must not be an alias')
126
127        self.assertFalse(cpus['qemu64']['static'],
128                         'unversioned qemu64 CPU model must not be static')
129        self.assertEqual(cpus['qemu64'].get('alias-of'), 'qemu64-v1',
130                         'qemu64 must be an alias of qemu64-v1')
131        self.assertNotIn('alias-of', cpus['qemu64-v1'],
132                         'qemu64-v1 must not be an alias')
133
134        self.validate_variant_aliases(cpus)
135
136        # On pc-*-4.1, -noTSX and -IBRS models should be aliases:
137        self.assertEqual(cpus["Haswell"].get('alias-of'),
138                         "Haswell-v1",
139                         "Haswell must be an alias")
140        self.assertEqual(cpus["Haswell-noTSX"].get('alias-of'),
141                         "Haswell-v2",
142                         "Haswell-noTSX must be an alias")
143        self.assertEqual(cpus["Haswell-IBRS"].get('alias-of'),
144                         "Haswell-v3",
145                         "Haswell-IBRS must be an alias")
146        self.assertEqual(cpus["Haswell-noTSX-IBRS"].get('alias-of'),
147                         "Haswell-v4",
148                         "Haswell-noTSX-IBRS must be an alias")
149
150        self.assertEqual(cpus["Broadwell"].get('alias-of'),
151                         "Broadwell-v1",
152                         "Broadwell must be an alias")
153        self.assertEqual(cpus["Broadwell-noTSX"].get('alias-of'),
154                         "Broadwell-v2",
155                         "Broadwell-noTSX must be an alias")
156        self.assertEqual(cpus["Broadwell-IBRS"].get('alias-of'),
157                         "Broadwell-v3",
158                         "Broadwell-IBRS must be an alias")
159        self.assertEqual(cpus["Broadwell-noTSX-IBRS"].get('alias-of'),
160                         "Broadwell-v4",
161                         "Broadwell-noTSX-IBRS must be an alias")
162
163        self.assertEqual(cpus["Nehalem"].get('alias-of'),
164                         "Nehalem-v1",
165                         "Nehalem must be an alias")
166        self.assertEqual(cpus["Nehalem-IBRS"].get('alias-of'),
167                         "Nehalem-v2",
168                         "Nehalem-IBRS must be an alias")
169
170        self.assertEqual(cpus["Westmere"].get('alias-of'),
171                         "Westmere-v1",
172                         "Westmere must be an alias")
173        self.assertEqual(cpus["Westmere-IBRS"].get('alias-of'),
174                         "Westmere-v2",
175                         "Westmere-IBRS must be an alias")
176
177        self.assertEqual(cpus["SandyBridge"].get('alias-of'),
178                         "SandyBridge-v1",
179                         "SandyBridge must be an alias")
180        self.assertEqual(cpus["SandyBridge-IBRS"].get('alias-of'),
181                         "SandyBridge-v2",
182                         "SandyBridge-IBRS must be an alias")
183
184        self.assertEqual(cpus["IvyBridge"].get('alias-of'),
185                         "IvyBridge-v1",
186                         "IvyBridge must be an alias")
187        self.assertEqual(cpus["IvyBridge-IBRS"].get('alias-of'),
188                         "IvyBridge-v2",
189                         "IvyBridge-IBRS must be an alias")
190
191        self.assertEqual(cpus["Skylake-Client"].get('alias-of'),
192                         "Skylake-Client-v1",
193                         "Skylake-Client must be an alias")
194        self.assertEqual(cpus["Skylake-Client-IBRS"].get('alias-of'),
195                         "Skylake-Client-v2",
196                         "Skylake-Client-IBRS must be an alias")
197
198        self.assertEqual(cpus["Skylake-Server"].get('alias-of'),
199                         "Skylake-Server-v1",
200                         "Skylake-Server must be an alias")
201        self.assertEqual(cpus["Skylake-Server-IBRS"].get('alias-of'),
202                         "Skylake-Server-v2",
203                         "Skylake-Server-IBRS must be an alias")
204
205        self.assertEqual(cpus["EPYC"].get('alias-of'),
206                         "EPYC-v1",
207                         "EPYC must be an alias")
208        self.assertEqual(cpus["EPYC-IBPB"].get('alias-of'),
209                         "EPYC-v2",
210                         "EPYC-IBPB must be an alias")
211
212        self.validate_aliases(cpus)
213
214    def test_none_alias(self):
215        """
216        Check if unversioned CPU model is an alias pointing to some version
217        """
218        self.set_machine('none')
219        self.vm.add_args('-S')
220        self.vm.launch()
221
222        cpus = dict((m['name'], m) for m in
223                    self.vm.cmd('query-cpu-definitions'))
224
225        self.assertFalse(cpus['Cascadelake-Server']['static'],
226                         'unversioned Cascadelake-Server CPU model must not be static')
227        self.assertTrue(re.match('Cascadelake-Server-v[0-9]+', cpus['Cascadelake-Server']['alias-of']),
228                        'Cascadelake-Server must be an alias of versioned CPU model')
229        self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'],
230                         'Cascadelake-Server-v1 must not be an alias')
231
232        self.assertFalse(cpus['qemu64']['static'],
233                         'unversioned qemu64 CPU model must not be static')
234        self.assertTrue(re.match('qemu64-v[0-9]+', cpus['qemu64']['alias-of']),
235                        'qemu64 must be an alias of versioned CPU model')
236        self.assertNotIn('alias-of', cpus['qemu64-v1'],
237                         'qemu64-v1 must not be an alias')
238
239        self.validate_aliases(cpus)
240
241
242class CascadelakeArchCapabilities(QemuSystemTest):
243    """
244    Validation of Cascadelake arch-capabilities
245    """
246    def get_cpu_prop(self, prop):
247        cpu_path = self.vm.cmd('query-cpus-fast')[0].get('qom-path')
248        return self.vm.cmd('qom-get', path=cpu_path, property=prop)
249
250    def test_4_1(self):
251        self.set_machine('pc-i440fx-4.1')
252        # machine-type only:
253        self.vm.add_args('-S')
254        self.set_vm_arg('-cpu',
255                        'Cascadelake-Server,x-force-features=on,check=off,'
256                        'enforce=off')
257        self.vm.launch()
258        self.assertFalse(self.get_cpu_prop('arch-capabilities'),
259                         'pc-i440fx-4.1 + Cascadelake-Server should not have arch-capabilities')
260
261    def test_4_0(self):
262        self.set_machine('pc-i440fx-4.0')
263        self.vm.add_args('-S')
264        self.set_vm_arg('-cpu',
265                        'Cascadelake-Server,x-force-features=on,check=off,'
266                        'enforce=off')
267        self.vm.launch()
268        self.assertFalse(self.get_cpu_prop('arch-capabilities'),
269                         'pc-i440fx-4.0 + Cascadelake-Server should not have arch-capabilities')
270
271    def test_set_4_0(self):
272        self.set_machine('pc-i440fx-4.0')
273        # command line must override machine-type if CPU model is not versioned:
274        self.vm.add_args('-S')
275        self.set_vm_arg('-cpu',
276                        'Cascadelake-Server,x-force-features=on,check=off,'
277                        'enforce=off,+arch-capabilities')
278        self.vm.launch()
279        self.assertTrue(self.get_cpu_prop('arch-capabilities'),
280                        'pc-i440fx-4.0 + Cascadelake-Server,+arch-capabilities should have arch-capabilities')
281
282    def test_unset_4_1(self):
283        self.set_machine('pc-i440fx-4.1')
284        self.vm.add_args('-S')
285        self.set_vm_arg('-cpu',
286                        'Cascadelake-Server,x-force-features=on,check=off,'
287                        'enforce=off,-arch-capabilities')
288        self.vm.launch()
289        self.assertFalse(self.get_cpu_prop('arch-capabilities'),
290                         'pc-i440fx-4.1 + Cascadelake-Server,-arch-capabilities should not have arch-capabilities')
291
292    def test_v1_4_0(self):
293        self.set_machine('pc-i440fx-4.0')
294        # versioned CPU model overrides machine-type:
295        self.vm.add_args('-S')
296        self.set_vm_arg('-cpu',
297                        'Cascadelake-Server-v1,x-force-features=on,check=off,'
298                        'enforce=off')
299        self.vm.launch()
300        self.assertFalse(self.get_cpu_prop('arch-capabilities'),
301                         'pc-i440fx-4.0 + Cascadelake-Server-v1 should not have arch-capabilities')
302
303    def test_v2_4_0(self):
304        self.set_machine('pc-i440fx-4.0')
305        self.vm.add_args('-S')
306        self.set_vm_arg('-cpu',
307                        'Cascadelake-Server-v2,x-force-features=on,check=off,'
308                        'enforce=off')
309        self.vm.launch()
310        self.assertTrue(self.get_cpu_prop('arch-capabilities'),
311                        'pc-i440fx-4.0 + Cascadelake-Server-v2 should have arch-capabilities')
312
313    def test_v1_set_4_0(self):
314        self.set_machine('pc-i440fx-4.0')
315        # command line must override machine-type and versioned CPU model:
316        self.vm.add_args('-S')
317        self.set_vm_arg('-cpu',
318                        'Cascadelake-Server-v1,x-force-features=on,check=off,'
319                        'enforce=off,+arch-capabilities')
320        self.vm.launch()
321        self.assertTrue(self.get_cpu_prop('arch-capabilities'),
322                        'pc-i440fx-4.0 + Cascadelake-Server-v1,+arch-capabilities should have arch-capabilities')
323
324    def test_v2_unset_4_1(self):
325        self.set_machine('pc-i440fx-4.1')
326        self.vm.add_args('-S')
327        self.set_vm_arg('-cpu',
328                        'Cascadelake-Server-v2,x-force-features=on,check=off,'
329                        'enforce=off,-arch-capabilities')
330        self.vm.launch()
331        self.assertFalse(self.get_cpu_prop('arch-capabilities'),
332                         'pc-i440fx-4.1 + Cascadelake-Server-v2,-arch-capabilities should not have arch-capabilities')
333
334if __name__ == '__main__':
335    QemuSystemTest.main()
336