1 #include "qemu/osdep.h" 2 #include "qobject/qdict.h" 3 #include "qobject/qlist.h" 4 #include "qobject/qnum.h" 5 #include "qobject/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 } 197 198 static void test_plus_minus_subprocess(void) 199 { 200 char *path; 201 202 if (!qtest_has_cpu_model("pentium")) { 203 return; 204 } 205 206 /* Rules: 207 * 1)"-foo" overrides "+foo" 208 * 2) "[+-]foo" overrides "foo=..." 209 * 3) Old feature names with underscores (e.g. "sse4_2") 210 * should keep working 211 * 212 * Note: rules 1 and 2 are planned to be removed soon, and 213 * should generate a warning. 214 */ 215 qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on"); 216 path = get_cpu0_qom_path(); 217 218 g_assert_false(qom_get_bool(path, "fpu")); 219 g_assert_false(qom_get_bool(path, "mce")); 220 g_assert_true(qom_get_bool(path, "cx8")); 221 222 /* Test both the original and the alias feature names: */ 223 g_assert_true(qom_get_bool(path, "sse4-1")); 224 g_assert_true(qom_get_bool(path, "sse4.1")); 225 226 g_assert_true(qom_get_bool(path, "sse4-2")); 227 g_assert_true(qom_get_bool(path, "sse4.2")); 228 229 qtest_end(); 230 g_free(path); 231 } 232 233 static void test_plus_minus(void) 234 { 235 if (!qtest_has_cpu_model("pentium")) { 236 return; 237 } 238 239 g_test_trap_subprocess("/x86/cpuid/parsing-plus-minus/subprocess", 0, 0); 240 g_test_trap_assert_passed(); 241 g_test_trap_assert_stderr("*Ambiguous CPU model string. " 242 "Don't mix both \"-mce\" and \"mce=on\"*"); 243 g_test_trap_assert_stderr("*Ambiguous CPU model string. " 244 "Don't mix both \"+cx8\" and \"cx8=off\"*"); 245 g_test_trap_assert_stdout(""); 246 } 247 248 int main(int argc, char **argv) 249 { 250 g_test_init(&argc, &argv, NULL); 251 252 g_test_add_func("/x86/cpuid/parsing-plus-minus/subprocess", 253 test_plus_minus_subprocess); 254 g_test_add_func("/x86/cpuid/parsing-plus-minus", test_plus_minus); 255 256 /* Original level values for CPU models: */ 257 add_cpuid_test("x86/cpuid/phenom/level", 258 "phenom", NULL, NULL, "level", 5); 259 add_cpuid_test("x86/cpuid/Conroe/level", 260 "Conroe", NULL, NULL, "level", 10); 261 add_cpuid_test("x86/cpuid/SandyBridge/level", 262 "SandyBridge", NULL, NULL, "level", 0xd); 263 add_cpuid_test("x86/cpuid/486/xlevel", 264 "486", NULL, NULL, "xlevel", 0); 265 add_cpuid_test("x86/cpuid/core2duo/xlevel", 266 "core2duo", NULL, NULL, "xlevel", 0x80000008); 267 add_cpuid_test("x86/cpuid/phenom/xlevel", 268 "phenom", NULL, NULL, "xlevel", 0x8000001A); 269 add_cpuid_test("x86/cpuid/athlon/xlevel", 270 "athlon", NULL, NULL, "xlevel", 0x80000008); 271 272 /* If level is not large enough, it should increase automatically: */ 273 /* CPUID[6].EAX: */ 274 add_cpuid_test("x86/cpuid/auto-level/486/arat", 275 "486", "arat=on", NULL, "level", 6); 276 /* CPUID[EAX=7,ECX=0].EBX: */ 277 add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase", 278 "phenom", "fsgsbase=on", NULL, "level", 7); 279 /* CPUID[EAX=7,ECX=0].ECX: */ 280 add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi", 281 "phenom", "avx512vbmi=on", NULL, "level", 7); 282 /* CPUID[EAX=0xd,ECX=1].EAX: */ 283 add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt", 284 "phenom", "xsaveopt=on", NULL, "level", 0xd); 285 /* CPUID[8000_0001].EDX: */ 286 add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow", 287 "486", "3dnow=on", NULL, "xlevel", 0x80000001); 288 /* CPUID[8000_0001].ECX: */ 289 add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a", 290 "486", "sse4a=on", NULL, "xlevel", 0x80000001); 291 /* CPUID[8000_0007].EDX: */ 292 add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc", 293 "486", "invtsc=on", NULL, "xlevel", 0x80000007); 294 /* CPUID[8000_000A].EDX: */ 295 add_cpuid_test("x86/cpuid/auto-xlevel/486/npt", 296 "486", "svm=on,npt=on", NULL, "xlevel", 0x8000000A); 297 /* CPUID[C000_0001].EDX: */ 298 add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore", 299 "phenom", "xstore=on", NULL, "xlevel2", 0xC0000001); 300 /* SVM needs CPUID[0x8000000A] */ 301 add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm", 302 "athlon", "svm=on", NULL, "xlevel", 0x8000000A); 303 304 305 /* If level is already large enough, it shouldn't change: */ 306 add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple", 307 "SandyBridge", "arat=on,fsgsbase=on,avx512vbmi=on", 308 NULL, "level", 0xd); 309 /* If level is explicitly set, it shouldn't change: */ 310 add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF", 311 "486", 312 "level=0xF,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", 313 NULL, "level", 0xF); 314 add_cpuid_test("x86/cpuid/auto-level/486/fixed/2", 315 "486", 316 "level=2,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", 317 NULL, "level", 2); 318 add_cpuid_test("x86/cpuid/auto-level/486/fixed/0", 319 "486", 320 "level=0,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", 321 NULL, "level", 0); 322 323 /* if xlevel is already large enough, it shouldn't change: */ 324 add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow", 325 "phenom", "3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", 326 NULL, "xlevel", 0x8000001A); 327 /* If xlevel is explicitly set, it shouldn't change: */ 328 add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002", 329 "486", 330 "xlevel=0x80000002,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", 331 NULL, "xlevel", 0x80000002); 332 add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A", 333 "486", 334 "xlevel=0x8000001A,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", 335 NULL, "xlevel", 0x8000001A); 336 add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0", 337 "486", 338 "xlevel=0,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", 339 NULL, "xlevel", 0); 340 341 /* if xlevel2 is already large enough, it shouldn't change: */ 342 add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed", 343 "486", "xlevel2=0xC0000002,xstore=on", 344 NULL, "xlevel2", 0xC0000002); 345 346 /* Check compatibility of old machine-types that didn't 347 * auto-increase level/xlevel/xlevel2: */ 348 if (qtest_has_machine("pc-i440fx-2.7")) { 349 add_cpuid_test("x86/cpuid/auto-level/pc-2.7", 350 "486", "arat=on,avx512vbmi=on,xsaveopt=on", 351 "pc-i440fx-2.7", "level", 1); 352 add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7", 353 "486", "3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", 354 "pc-i440fx-2.7", "xlevel", 0); 355 add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7", 356 "486", "xstore=on", "pc-i440fx-2.7", 357 "xlevel2", 0); 358 } 359 if (qtest_has_machine("pc-i440fx-2.9")) { 360 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off", 361 "Conroe", NULL, "pc-i440fx-2.9", 362 "level", 10); 363 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/on", 364 "Conroe", "erms=on", "pc-i440fx-2.9", 365 "level", 10); 366 } 367 368 /* Test feature parsing */ 369 add_feature_test("x86/cpuid/features/plus", 370 "486", "+arat", 371 6, 0, "EAX", 2, true); 372 add_feature_test("x86/cpuid/features/minus", 373 "pentium", "-mmx", 374 1, 0, "EDX", 23, false); 375 add_feature_test("x86/cpuid/features/on", 376 "486", "arat=on", 377 6, 0, "EAX", 2, true); 378 add_feature_test("x86/cpuid/features/off", 379 "pentium", "mmx=off", 380 1, 0, "EDX", 23, false); 381 382 add_feature_test("x86/cpuid/features/max-plus-invtsc", 383 "max" , "+invtsc", 384 0x80000007, 0, "EDX", 8, true); 385 add_feature_test("x86/cpuid/features/max-invtsc-on", 386 "max", "invtsc=on", 387 0x80000007, 0, "EDX", 8, true); 388 add_feature_test("x86/cpuid/features/max-minus-mmx", 389 "max", "-mmx", 390 1, 0, "EDX", 23, false); 391 add_feature_test("x86/cpuid/features/max-invtsc-on,mmx=off", 392 "max", "mmx=off", 393 1, 0, "EDX", 23, false); 394 395 return g_test_run(); 396 } 397