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)
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"));
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 } }",
31 QObject
*ret
= qdict_get(resp
, "return");
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
);
46 typedef struct CpuidTestArgs
{
49 int64_t expected_value
;
52 static void test_cpuid_prop(const void *data
)
54 const CpuidTestArgs
*args
= data
;
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
);
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);
78 if (!qtest_has_cpu_model(cpu
)) {
81 cmdline
= g_strdup_printf("-cpu %s", cpu
);
85 cmdline
= g_strdup_printf("%s,%s", cmdline
, cpufeat
);
90 cmdline
= g_strdup_printf("-machine %s %s", machine
, cmdline
);
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 */
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 */
111 /* The bit to check in X86CPUFeatureWordInfo.features */
113 /* The expected value for the bit in (X86CPUFeatureWordInfo.features) */
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
,
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");
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")),
144 static void test_feature_flag(const void *data
)
146 const FeatureTestArgs
*args
= data
;
148 QList
*present
, *filtered
;
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
);
159 g_assert(!!(value
& (1U << args
->bitnr
)) == args
->expected_value
);
161 qobject_unref(present
);
162 qobject_unref(filtered
);
167 * Add test case to ensure that a given feature flag is set in
168 * either "feature-words" or "filtered-features", when running QEMU
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);
179 if (!qtest_has_cpu_model(cpu
)) {
184 cmdline
= g_strdup_printf("-cpu %s,%s", cpu
, cpufeat
);
186 cmdline
= g_strdup_printf("-cpu %s", cpu
);
189 args
->cmdline
= cmdline
;
194 args
->expected_value
= expected_value
;
195 qtest_add_data_func(name
, args
, test_feature_flag
);
199 static void test_plus_minus_subprocess(void)
203 if (!qtest_has_cpu_model("pentium")) {
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"));
234 static void test_plus_minus(void)
236 if (!qtest_has_cpu_model("pentium")) {
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: */
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",
310 /* If level is explicitly set, it shouldn't change: */
311 add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF",
313 "level=0xF,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
315 add_cpuid_test("x86/cpuid/auto-level/486/fixed/2",
317 "level=2,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
319 add_cpuid_test("x86/cpuid/auto-level/486/fixed/0",
321 "level=0,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
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",
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",
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",
339 "xlevel=0,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
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",
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",
369 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on",
370 "Penryn", "erms=on", "pc-i440fx-2.3",
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",
377 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/on",
378 "Conroe", "erms=on", "pc-i440fx-2.9",
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",
404 6, 0, "EAX", 2, true);
405 add_feature_test("x86/cpuid/features/minus",
407 1, 0, "EDX", 23, false);
408 add_feature_test("x86/cpuid/features/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",
417 0x80000007, 0, "EDX", 8, true);
418 add_feature_test("x86/cpuid/features/max-invtsc-on",
420 0x80000007, 0, "EDX", 8, true);
421 add_feature_test("x86/cpuid/features/max-minus-mmx",
423 1, 0, "EDX", 23, false);
424 add_feature_test("x86/cpuid/features/max-invtsc-on,mmx=off",
426 1, 0, "EDX", 23, false);