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" 7 8 static char *get_cpu0_qom_path(void) 9 { 10 QDict *resp; 11 QList *ret; 12 QDict *cpu0; 13 char *path; 14 15 resp = qmp("{'execute': 'query-cpus-fast', 'arguments': {}}"); 16 g_assert(qdict_haskey(resp, "return")); 17 ret = qdict_get_qlist(resp, "return"); 18 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; 23 } 24 25 static QObject *qom_get(const char *path, const char *prop) 26 { 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; 35 } 36 37 static bool qom_get_bool(const char *path, const char *prop) 38 { 39 QBool *value = qobject_to(QBool, qom_get(path, prop)); 40 bool b = qbool_get_bool(value); 41 42 qobject_unref(value); 43 return b; 44 } 45 46 typedef struct CpuidTestArgs { 47 const char *cmdline; 48 const char *property; 49 int64_t expected_value; 50 } CpuidTestArgs; 51 52 static void test_cpuid_prop(const void *data) 53 { 54 const CpuidTestArgs *args = data; 55 char *path; 56 QNum *value; 57 int64_t val; 58 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(); 65 66 qobject_unref(value); 67 g_free(path); 68 } 69 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) 73 { 74 CpuidTestArgs *args = g_new0(CpuidTestArgs, 1); 75 char *cmdline; 76 char *save; 77 78 if (!qtest_has_cpu_model(cpu)) { 79 return; 80 } 81 cmdline = g_strdup_printf("-cpu %s", cpu); 82 83 if (cpufeat) { 84 save = cmdline; 85 cmdline = g_strdup_printf("%s,%s", cmdline, cpufeat); 86 g_free(save); 87 } 88 if (machine) { 89 save = cmdline; 90 cmdline = g_strdup_printf("-machine %s %s", machine, cmdline); 91 g_free(save); 92 } 93 args->cmdline = cmdline; 94 args->property = property; 95 args->expected_value = expected_value; 96 qtest_add_data_func(name, args, test_cpuid_prop); 97 } 98 99 100 /* Parameters to a add_feature_test() test case */ 101 typedef struct FeatureTestArgs { 102 /* cmdline to start QEMU */ 103 const char *cmdline; 104 /* 105 * cpuid-input-eax and cpuid-input-ecx values to look for, 106 * in "feature-words" and "filtered-features" properties. 107 */ 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; 116 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) 120 { 121 const QListEntry *e; 122 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; 130 131 if (has_ecx) { 132 recx = qdict_get_int(w, "cpuid-input-ecx"); 133 } 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; 139 } 140 } 141 return 0; 142 } 143 144 static void test_feature_flag(const void *data) 145 { 146 const FeatureTestArgs *args = data; 147 char *path; 148 QList *present, *filtered; 149 uint32_t value; 150 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(); 158 159 g_assert(!!(value & (1U << args->bitnr)) == args->expected_value); 160 161 qobject_unref(present); 162 qobject_unref(filtered); 163 g_free(path); 164 } 165 166 /* 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 170 */ 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) 175 { 176 FeatureTestArgs *args = g_new0(FeatureTestArgs, 1); 177 char *cmdline; 178 179 if (!qtest_has_cpu_model(cpu)) { 180 return; 181 } 182 183 if (cpufeat) { 184 cmdline = g_strdup_printf("-cpu %s,%s", cpu, cpufeat); 185 } else { 186 cmdline = g_strdup_printf("-cpu %s", cpu); 187 } 188 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; 197 } 198 199 static void test_plus_minus_subprocess(void) 200 { 201 char *path; 202 203 if (!qtest_has_cpu_model("pentium")) { 204 return; 205 } 206 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 212 * 213 * Note: rules 1 and 2 are planned to be removed soon, and 214 * should generate a warning. 215 */ 216 qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on"); 217 path = get_cpu0_qom_path(); 218 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")); 222 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")); 226 227 g_assert_true(qom_get_bool(path, "sse4-2")); 228 g_assert_true(qom_get_bool(path, "sse4.2")); 229 230 qtest_end(); 231 g_free(path); 232 } 233 234 static void test_plus_minus(void) 235 { 236 if (!qtest_has_cpu_model("pentium")) { 237 return; 238 } 239 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(""); 247 } 248 249 int main(int argc, char **argv) 250 { 251 g_test_init(&argc, &argv, NULL); 252 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); 256 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); 272 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); 304 305 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); 323 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); 341 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); 346 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); 359 } 360 /* 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: 364 */ 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); 372 } 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); 380 } 381 382 /* 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: 386 */ 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); 391 } 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); 399 } 400 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); 414 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); 427 428 return g_test_run(); 429 } 430