Merge tag 'pull-request-2024-07-02' of https://gitlab.com/thuth/qemu into staging
[qemu/armbru.git] / tests / qtest / test-x86-cpuid-compat.c
blobb9e7e5ef7b513bd1eb8a3e8ea37dee96cbb4e8d7
1 #include "qemu/osdep.h"
2 #include "qapi/qmp/qdict.h"
3 #include "qapi/qmp/qlist.h"
4 #include "qapi/qmp/qnum.h"
5 #include "qapi/qmp/qbool.h"
6 #include "libqtest-single.h"
8 static char *get_cpu0_qom_path(void)
10 QDict *resp;
11 QList *ret;
12 QDict *cpu0;
13 char *path;
15 resp = qmp("{'execute': 'query-cpus-fast', 'arguments': {}}");
16 g_assert(qdict_haskey(resp, "return"));
17 ret = qdict_get_qlist(resp, "return");
19 cpu0 = qobject_to(QDict, qlist_peek(ret));
20 path = g_strdup(qdict_get_str(cpu0, "qom-path"));
21 qobject_unref(resp);
22 return path;
25 static QObject *qom_get(const char *path, const char *prop)
27 QDict *resp = qmp("{ 'execute': 'qom-get',"
28 " 'arguments': { 'path': %s,"
29 " 'property': %s } }",
30 path, prop);
31 QObject *ret = qdict_get(resp, "return");
32 qobject_ref(ret);
33 qobject_unref(resp);
34 return ret;
37 static bool qom_get_bool(const char *path, const char *prop)
39 QBool *value = qobject_to(QBool, qom_get(path, prop));
40 bool b = qbool_get_bool(value);
42 qobject_unref(value);
43 return b;
46 typedef struct CpuidTestArgs {
47 const char *cmdline;
48 const char *property;
49 int64_t expected_value;
50 } CpuidTestArgs;
52 static void test_cpuid_prop(const void *data)
54 const CpuidTestArgs *args = data;
55 char *path;
56 QNum *value;
57 int64_t val;
59 qtest_start(args->cmdline);
60 path = get_cpu0_qom_path();
61 value = qobject_to(QNum, qom_get(path, args->property));
62 g_assert(qnum_get_try_int(value, &val));
63 g_assert_cmpint(val, ==, args->expected_value);
64 qtest_end();
66 qobject_unref(value);
67 g_free(path);
70 static void add_cpuid_test(const char *name, const char *cpu,
71 const char *cpufeat, const char *machine,
72 const char *property, int64_t expected_value)
74 CpuidTestArgs *args = g_new0(CpuidTestArgs, 1);
75 char *cmdline;
76 char *save;
78 if (!qtest_has_cpu_model(cpu)) {
79 return;
81 cmdline = g_strdup_printf("-cpu %s", cpu);
83 if (cpufeat) {
84 save = cmdline;
85 cmdline = g_strdup_printf("%s,%s", cmdline, cpufeat);
86 g_free(save);
88 if (machine) {
89 save = cmdline;
90 cmdline = g_strdup_printf("-machine %s %s", machine, cmdline);
91 g_free(save);
93 args->cmdline = cmdline;
94 args->property = property;
95 args->expected_value = expected_value;
96 qtest_add_data_func(name, args, test_cpuid_prop);
100 /* Parameters to a add_feature_test() test case */
101 typedef struct FeatureTestArgs {
102 /* cmdline to start QEMU */
103 const char *cmdline;
105 * cpuid-input-eax and cpuid-input-ecx values to look for,
106 * in "feature-words" and "filtered-features" properties.
108 uint32_t in_eax, in_ecx;
109 /* The register name to look for, in the X86CPUFeatureWordInfo array */
110 const char *reg;
111 /* The bit to check in X86CPUFeatureWordInfo.features */
112 int bitnr;
113 /* The expected value for the bit in (X86CPUFeatureWordInfo.features) */
114 bool expected_value;
115 } FeatureTestArgs;
117 /* Get the value for a feature word in a X86CPUFeatureWordInfo list */
118 static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx,
119 const char *reg)
121 const QListEntry *e;
123 for (e = qlist_first(features); e; e = qlist_next(e)) {
124 QDict *w = qobject_to(QDict, qlist_entry_obj(e));
125 const char *rreg = qdict_get_str(w, "cpuid-register");
126 uint32_t reax = qdict_get_int(w, "cpuid-input-eax");
127 bool has_ecx = qdict_haskey(w, "cpuid-input-ecx");
128 uint32_t recx = 0;
129 int64_t val;
131 if (has_ecx) {
132 recx = qdict_get_int(w, "cpuid-input-ecx");
134 if (eax == reax && (!has_ecx || ecx == recx) && !strcmp(rreg, reg)) {
135 g_assert(qnum_get_try_int(qobject_to(QNum,
136 qdict_get(w, "features")),
137 &val));
138 return val;
141 return 0;
144 static void test_feature_flag(const void *data)
146 const FeatureTestArgs *args = data;
147 char *path;
148 QList *present, *filtered;
149 uint32_t value;
151 qtest_start(args->cmdline);
152 path = get_cpu0_qom_path();
153 present = qobject_to(QList, qom_get(path, "feature-words"));
154 filtered = qobject_to(QList, qom_get(path, "filtered-features"));
155 value = get_feature_word(present, args->in_eax, args->in_ecx, args->reg);
156 value |= get_feature_word(filtered, args->in_eax, args->in_ecx, args->reg);
157 qtest_end();
159 g_assert(!!(value & (1U << args->bitnr)) == args->expected_value);
161 qobject_unref(present);
162 qobject_unref(filtered);
163 g_free(path);
167 * Add test case to ensure that a given feature flag is set in
168 * either "feature-words" or "filtered-features", when running QEMU
169 * using cmdline
171 static void add_feature_test(const char *name, const char *cpu,
172 const char *cpufeat, uint32_t eax,
173 uint32_t ecx, const char *reg,
174 int bitnr, bool expected_value)
176 FeatureTestArgs *args = g_new0(FeatureTestArgs, 1);
177 char *cmdline;
179 if (!qtest_has_cpu_model(cpu)) {
180 return;
183 if (cpufeat) {
184 cmdline = g_strdup_printf("-cpu %s,%s", cpu, cpufeat);
185 } else {
186 cmdline = g_strdup_printf("-cpu %s", cpu);
189 args->cmdline = cmdline;
190 args->in_eax = eax;
191 args->in_ecx = ecx;
192 args->reg = reg;
193 args->bitnr = bitnr;
194 args->expected_value = expected_value;
195 qtest_add_data_func(name, args, test_feature_flag);
196 return;
199 static void test_plus_minus_subprocess(void)
201 char *path;
203 if (!qtest_has_cpu_model("pentium")) {
204 return;
207 /* Rules:
208 * 1)"-foo" overrides "+foo"
209 * 2) "[+-]foo" overrides "foo=..."
210 * 3) Old feature names with underscores (e.g. "sse4_2")
211 * should keep working
213 * Note: rules 1 and 2 are planned to be removed soon, and
214 * should generate a warning.
216 qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on");
217 path = get_cpu0_qom_path();
219 g_assert_false(qom_get_bool(path, "fpu"));
220 g_assert_false(qom_get_bool(path, "mce"));
221 g_assert_true(qom_get_bool(path, "cx8"));
223 /* Test both the original and the alias feature names: */
224 g_assert_true(qom_get_bool(path, "sse4-1"));
225 g_assert_true(qom_get_bool(path, "sse4.1"));
227 g_assert_true(qom_get_bool(path, "sse4-2"));
228 g_assert_true(qom_get_bool(path, "sse4.2"));
230 qtest_end();
231 g_free(path);
234 static void test_plus_minus(void)
236 if (!qtest_has_cpu_model("pentium")) {
237 return;
240 g_test_trap_subprocess("/x86/cpuid/parsing-plus-minus/subprocess", 0, 0);
241 g_test_trap_assert_passed();
242 g_test_trap_assert_stderr("*Ambiguous CPU model string. "
243 "Don't mix both \"-mce\" and \"mce=on\"*");
244 g_test_trap_assert_stderr("*Ambiguous CPU model string. "
245 "Don't mix both \"+cx8\" and \"cx8=off\"*");
246 g_test_trap_assert_stdout("");
249 int main(int argc, char **argv)
251 g_test_init(&argc, &argv, NULL);
253 g_test_add_func("/x86/cpuid/parsing-plus-minus/subprocess",
254 test_plus_minus_subprocess);
255 g_test_add_func("/x86/cpuid/parsing-plus-minus", test_plus_minus);
257 /* Original level values for CPU models: */
258 add_cpuid_test("x86/cpuid/phenom/level",
259 "phenom", NULL, NULL, "level", 5);
260 add_cpuid_test("x86/cpuid/Conroe/level",
261 "Conroe", NULL, NULL, "level", 10);
262 add_cpuid_test("x86/cpuid/SandyBridge/level",
263 "SandyBridge", NULL, NULL, "level", 0xd);
264 add_cpuid_test("x86/cpuid/486/xlevel",
265 "486", NULL, NULL, "xlevel", 0);
266 add_cpuid_test("x86/cpuid/core2duo/xlevel",
267 "core2duo", NULL, NULL, "xlevel", 0x80000008);
268 add_cpuid_test("x86/cpuid/phenom/xlevel",
269 "phenom", NULL, NULL, "xlevel", 0x8000001A);
270 add_cpuid_test("x86/cpuid/athlon/xlevel",
271 "athlon", NULL, NULL, "xlevel", 0x80000008);
273 /* If level is not large enough, it should increase automatically: */
274 /* CPUID[6].EAX: */
275 add_cpuid_test("x86/cpuid/auto-level/486/arat",
276 "486", "arat=on", NULL, "level", 6);
277 /* CPUID[EAX=7,ECX=0].EBX: */
278 add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase",
279 "phenom", "fsgsbase=on", NULL, "level", 7);
280 /* CPUID[EAX=7,ECX=0].ECX: */
281 add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi",
282 "phenom", "avx512vbmi=on", NULL, "level", 7);
283 /* CPUID[EAX=0xd,ECX=1].EAX: */
284 add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt",
285 "phenom", "xsaveopt=on", NULL, "level", 0xd);
286 /* CPUID[8000_0001].EDX: */
287 add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow",
288 "486", "3dnow=on", NULL, "xlevel", 0x80000001);
289 /* CPUID[8000_0001].ECX: */
290 add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a",
291 "486", "sse4a=on", NULL, "xlevel", 0x80000001);
292 /* CPUID[8000_0007].EDX: */
293 add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc",
294 "486", "invtsc=on", NULL, "xlevel", 0x80000007);
295 /* CPUID[8000_000A].EDX: */
296 add_cpuid_test("x86/cpuid/auto-xlevel/486/npt",
297 "486", "svm=on,npt=on", NULL, "xlevel", 0x8000000A);
298 /* CPUID[C000_0001].EDX: */
299 add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore",
300 "phenom", "xstore=on", NULL, "xlevel2", 0xC0000001);
301 /* SVM needs CPUID[0x8000000A] */
302 add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm",
303 "athlon", "svm=on", NULL, "xlevel", 0x8000000A);
306 /* If level is already large enough, it shouldn't change: */
307 add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple",
308 "SandyBridge", "arat=on,fsgsbase=on,avx512vbmi=on",
309 NULL, "level", 0xd);
310 /* If level is explicitly set, it shouldn't change: */
311 add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF",
312 "486",
313 "level=0xF,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
314 NULL, "level", 0xF);
315 add_cpuid_test("x86/cpuid/auto-level/486/fixed/2",
316 "486",
317 "level=2,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
318 NULL, "level", 2);
319 add_cpuid_test("x86/cpuid/auto-level/486/fixed/0",
320 "486",
321 "level=0,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
322 NULL, "level", 0);
324 /* if xlevel is already large enough, it shouldn't change: */
325 add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow",
326 "phenom", "3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
327 NULL, "xlevel", 0x8000001A);
328 /* If xlevel is explicitly set, it shouldn't change: */
329 add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002",
330 "486",
331 "xlevel=0x80000002,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
332 NULL, "xlevel", 0x80000002);
333 add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A",
334 "486",
335 "xlevel=0x8000001A,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
336 NULL, "xlevel", 0x8000001A);
337 add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0",
338 "486",
339 "xlevel=0,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
340 NULL, "xlevel", 0);
342 /* if xlevel2 is already large enough, it shouldn't change: */
343 add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed",
344 "486", "xlevel2=0xC0000002,xstore=on",
345 NULL, "xlevel2", 0xC0000002);
347 /* Check compatibility of old machine-types that didn't
348 * auto-increase level/xlevel/xlevel2: */
349 if (qtest_has_machine("pc-i440fx-2.7")) {
350 add_cpuid_test("x86/cpuid/auto-level/pc-2.7",
351 "486", "arat=on,avx512vbmi=on,xsaveopt=on",
352 "pc-i440fx-2.7", "level", 1);
353 add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7",
354 "486", "3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
355 "pc-i440fx-2.7", "xlevel", 0);
356 add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7",
357 "486", "xstore=on", "pc-i440fx-2.7",
358 "xlevel2", 0);
361 * QEMU 2.3.0 had auto-level enabled for CPUID[7], already,
362 * and the compat code that sets default level shouldn't
363 * disable the auto-level=7 code:
365 if (qtest_has_machine("pc-i440fx-2.3")) {
366 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off",
367 "Penryn", NULL, "pc-i440fx-2.3",
368 "level", 4);
369 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on",
370 "Penryn", "erms=on", "pc-i440fx-2.3",
371 "level", 7);
373 if (qtest_has_machine("pc-i440fx-2.9")) {
374 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off",
375 "Conroe", NULL, "pc-i440fx-2.9",
376 "level", 10);
377 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/on",
378 "Conroe", "erms=on", "pc-i440fx-2.9",
379 "level", 10);
383 * xlevel doesn't have any feature that triggers auto-level
384 * code on old machine-types. Just check that the compat code
385 * is working correctly:
387 if (qtest_has_machine("pc-i440fx-2.3")) {
388 add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3",
389 "SandyBridge", NULL, "pc-i440fx-2.3",
390 "xlevel", 0x8000000a);
392 if (qtest_has_machine("pc-i440fx-2.4")) {
393 add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off",
394 "SandyBridge", NULL, "pc-i440fx-2.4",
395 "xlevel", 0x80000008);
396 add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on",
397 "SandyBridge", "svm=on,npt=on", "pc-i440fx-2.4",
398 "xlevel", 0x80000008);
401 /* Test feature parsing */
402 add_feature_test("x86/cpuid/features/plus",
403 "486", "+arat",
404 6, 0, "EAX", 2, true);
405 add_feature_test("x86/cpuid/features/minus",
406 "pentium", "-mmx",
407 1, 0, "EDX", 23, false);
408 add_feature_test("x86/cpuid/features/on",
409 "486", "arat=on",
410 6, 0, "EAX", 2, true);
411 add_feature_test("x86/cpuid/features/off",
412 "pentium", "mmx=off",
413 1, 0, "EDX", 23, false);
415 add_feature_test("x86/cpuid/features/max-plus-invtsc",
416 "max" , "+invtsc",
417 0x80000007, 0, "EDX", 8, true);
418 add_feature_test("x86/cpuid/features/max-invtsc-on",
419 "max", "invtsc=on",
420 0x80000007, 0, "EDX", 8, true);
421 add_feature_test("x86/cpuid/features/max-minus-mmx",
422 "max", "-mmx",
423 1, 0, "EDX", 23, false);
424 add_feature_test("x86/cpuid/features/max-invtsc-on,mmx=off",
425 "max", "mmx=off",
426 1, 0, "EDX", 23, false);
428 return g_test_run();