1 #include "qemu/osdep.h" 2 #include "qemu-common.h" 3 #include "qapi/qmp/qdict.h" 4 #include "qapi/qmp/qlist.h" 5 #include "qapi/qmp/qnum.h" 6 #include "qapi/qmp/qbool.h" 7 #include "libqtest-single.h" 8 9 static char *get_cpu0_qom_path(void) 10 { 11 QDict *resp; 12 QList *ret; 13 QDict *cpu0; 14 char *path; 15 16 resp = qmp("{'execute': 'query-cpus', 'arguments': {}}"); 17 g_assert(qdict_haskey(resp, "return")); 18 ret = qdict_get_qlist(resp, "return"); 19 20 cpu0 = qobject_to(QDict, qlist_peek(ret)); 21 path = g_strdup(qdict_get_str(cpu0, "qom_path")); 22 qobject_unref(resp); 23 return path; 24 } 25 26 static QObject *qom_get(const char *path, const char *prop) 27 { 28 QDict *resp = qmp("{ 'execute': 'qom-get'," 29 " 'arguments': { 'path': %s," 30 " 'property': %s } }", 31 path, prop); 32 QObject *ret = qdict_get(resp, "return"); 33 qobject_ref(ret); 34 qobject_unref(resp); 35 return ret; 36 } 37 38 static bool qom_get_bool(const char *path, const char *prop) 39 { 40 QBool *value = qobject_to(QBool, qom_get(path, prop)); 41 bool b = qbool_get_bool(value); 42 43 qobject_unref(value); 44 return b; 45 } 46 47 typedef struct CpuidTestArgs { 48 const char *cmdline; 49 const char *property; 50 int64_t expected_value; 51 } CpuidTestArgs; 52 53 static void test_cpuid_prop(const void *data) 54 { 55 const CpuidTestArgs *args = data; 56 char *path; 57 QNum *value; 58 int64_t val; 59 60 qtest_start(args->cmdline); 61 path = get_cpu0_qom_path(); 62 value = qobject_to(QNum, qom_get(path, args->property)); 63 g_assert(qnum_get_try_int(value, &val)); 64 g_assert_cmpint(val, ==, args->expected_value); 65 qtest_end(); 66 67 qobject_unref(value); 68 g_free(path); 69 } 70 71 static void add_cpuid_test(const char *name, const char *cmdline, 72 const char *property, int64_t expected_value) 73 { 74 CpuidTestArgs *args = g_new0(CpuidTestArgs, 1); 75 args->cmdline = cmdline; 76 args->property = property; 77 args->expected_value = expected_value; 78 qtest_add_data_func(name, args, test_cpuid_prop); 79 } 80 81 82 /* Parameters to a add_feature_test() test case */ 83 typedef struct FeatureTestArgs { 84 /* cmdline to start QEMU */ 85 const char *cmdline; 86 /* 87 * cpuid-input-eax and cpuid-input-ecx values to look for, 88 * in "feature-words" and "filtered-features" properties. 89 */ 90 uint32_t in_eax, in_ecx; 91 /* The register name to look for, in the X86CPUFeatureWordInfo array */ 92 const char *reg; 93 /* The bit to check in X86CPUFeatureWordInfo.features */ 94 int bitnr; 95 /* The expected value for the bit in (X86CPUFeatureWordInfo.features) */ 96 bool expected_value; 97 } FeatureTestArgs; 98 99 /* Get the value for a feature word in a X86CPUFeatureWordInfo list */ 100 static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx, 101 const char *reg) 102 { 103 const QListEntry *e; 104 105 for (e = qlist_first(features); e; e = qlist_next(e)) { 106 QDict *w = qobject_to(QDict, qlist_entry_obj(e)); 107 const char *rreg = qdict_get_str(w, "cpuid-register"); 108 uint32_t reax = qdict_get_int(w, "cpuid-input-eax"); 109 bool has_ecx = qdict_haskey(w, "cpuid-input-ecx"); 110 uint32_t recx = 0; 111 int64_t val; 112 113 if (has_ecx) { 114 recx = qdict_get_int(w, "cpuid-input-ecx"); 115 } 116 if (eax == reax && (!has_ecx || ecx == recx) && !strcmp(rreg, reg)) { 117 g_assert(qnum_get_try_int(qobject_to(QNum, 118 qdict_get(w, "features")), 119 &val)); 120 return val; 121 } 122 } 123 return 0; 124 } 125 126 static void test_feature_flag(const void *data) 127 { 128 const FeatureTestArgs *args = data; 129 char *path; 130 QList *present, *filtered; 131 uint32_t value; 132 133 qtest_start(args->cmdline); 134 path = get_cpu0_qom_path(); 135 present = qobject_to(QList, qom_get(path, "feature-words")); 136 filtered = qobject_to(QList, qom_get(path, "filtered-features")); 137 value = get_feature_word(present, args->in_eax, args->in_ecx, args->reg); 138 value |= get_feature_word(filtered, args->in_eax, args->in_ecx, args->reg); 139 qtest_end(); 140 141 g_assert(!!(value & (1U << args->bitnr)) == args->expected_value); 142 143 qobject_unref(present); 144 qobject_unref(filtered); 145 g_free(path); 146 } 147 148 /* 149 * Add test case to ensure that a given feature flag is set in 150 * either "feature-words" or "filtered-features", when running QEMU 151 * using cmdline 152 */ 153 static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline, 154 uint32_t eax, uint32_t ecx, 155 const char *reg, int bitnr, 156 bool expected_value) 157 { 158 FeatureTestArgs *args = g_new0(FeatureTestArgs, 1); 159 args->cmdline = cmdline; 160 args->in_eax = eax; 161 args->in_ecx = ecx; 162 args->reg = reg; 163 args->bitnr = bitnr; 164 args->expected_value = expected_value; 165 qtest_add_data_func(name, args, test_feature_flag); 166 return args; 167 } 168 169 static void test_plus_minus_subprocess(void) 170 { 171 char *path; 172 173 /* Rules: 174 * 1)"-foo" overrides "+foo" 175 * 2) "[+-]foo" overrides "foo=..." 176 * 3) Old feature names with underscores (e.g. "sse4_2") 177 * should keep working 178 * 179 * Note: rules 1 and 2 are planned to be removed soon, and 180 * should generate a warning. 181 */ 182 qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on"); 183 path = get_cpu0_qom_path(); 184 185 g_assert_false(qom_get_bool(path, "fpu")); 186 g_assert_false(qom_get_bool(path, "mce")); 187 g_assert_true(qom_get_bool(path, "cx8")); 188 189 /* Test both the original and the alias feature names: */ 190 g_assert_true(qom_get_bool(path, "sse4-1")); 191 g_assert_true(qom_get_bool(path, "sse4.1")); 192 193 g_assert_true(qom_get_bool(path, "sse4-2")); 194 g_assert_true(qom_get_bool(path, "sse4.2")); 195 196 qtest_end(); 197 g_free(path); 198 } 199 200 static void test_plus_minus(void) 201 { 202 g_test_trap_subprocess("/x86/cpuid/parsing-plus-minus/subprocess", 0, 0); 203 g_test_trap_assert_passed(); 204 g_test_trap_assert_stderr("*Ambiguous CPU model string. " 205 "Don't mix both \"-mce\" and \"mce=on\"*"); 206 g_test_trap_assert_stderr("*Ambiguous CPU model string. " 207 "Don't mix both \"+cx8\" and \"cx8=off\"*"); 208 g_test_trap_assert_stdout(""); 209 } 210 211 int main(int argc, char **argv) 212 { 213 g_test_init(&argc, &argv, NULL); 214 215 g_test_add_func("/x86/cpuid/parsing-plus-minus/subprocess", 216 test_plus_minus_subprocess); 217 g_test_add_func("/x86/cpuid/parsing-plus-minus", test_plus_minus); 218 219 /* Original level values for CPU models: */ 220 add_cpuid_test("x86/cpuid/phenom/level", 221 "-cpu phenom", "level", 5); 222 add_cpuid_test("x86/cpuid/Conroe/level", 223 "-cpu Conroe", "level", 10); 224 add_cpuid_test("x86/cpuid/SandyBridge/level", 225 "-cpu SandyBridge", "level", 0xd); 226 add_cpuid_test("x86/cpuid/486/xlevel", 227 "-cpu 486", "xlevel", 0); 228 add_cpuid_test("x86/cpuid/core2duo/xlevel", 229 "-cpu core2duo", "xlevel", 0x80000008); 230 add_cpuid_test("x86/cpuid/phenom/xlevel", 231 "-cpu phenom", "xlevel", 0x8000001A); 232 add_cpuid_test("x86/cpuid/athlon/xlevel", 233 "-cpu athlon", "xlevel", 0x80000008); 234 235 /* If level is not large enough, it should increase automatically: */ 236 /* CPUID[6].EAX: */ 237 add_cpuid_test("x86/cpuid/auto-level/phenom/arat", 238 "-cpu 486,+arat", "level", 6); 239 /* CPUID[EAX=7,ECX=0].EBX: */ 240 add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase", 241 "-cpu phenom,+fsgsbase", "level", 7); 242 /* CPUID[EAX=7,ECX=0].ECX: */ 243 add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi", 244 "-cpu phenom,+avx512vbmi", "level", 7); 245 /* CPUID[EAX=0xd,ECX=1].EAX: */ 246 add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt", 247 "-cpu phenom,+xsaveopt", "level", 0xd); 248 /* CPUID[8000_0001].EDX: */ 249 add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow", 250 "-cpu 486,+3dnow", "xlevel", 0x80000001); 251 /* CPUID[8000_0001].ECX: */ 252 add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a", 253 "-cpu 486,+sse4a", "xlevel", 0x80000001); 254 /* CPUID[8000_0007].EDX: */ 255 add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc", 256 "-cpu 486,+invtsc", "xlevel", 0x80000007); 257 /* CPUID[8000_000A].EDX: */ 258 add_cpuid_test("x86/cpuid/auto-xlevel/486/npt", 259 "-cpu 486,+svm,+npt", "xlevel", 0x8000000A); 260 /* CPUID[C000_0001].EDX: */ 261 add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore", 262 "-cpu phenom,+xstore", "xlevel2", 0xC0000001); 263 /* SVM needs CPUID[0x8000000A] */ 264 add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm", 265 "-cpu athlon,+svm", "xlevel", 0x8000000A); 266 267 268 /* If level is already large enough, it shouldn't change: */ 269 add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple", 270 "-cpu SandyBridge,+arat,+fsgsbase,+avx512vbmi", 271 "level", 0xd); 272 /* If level is explicitly set, it shouldn't change: */ 273 add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF", 274 "-cpu 486,level=0xF,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", 275 "level", 0xF); 276 add_cpuid_test("x86/cpuid/auto-level/486/fixed/2", 277 "-cpu 486,level=2,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", 278 "level", 2); 279 add_cpuid_test("x86/cpuid/auto-level/486/fixed/0", 280 "-cpu 486,level=0,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", 281 "level", 0); 282 283 /* if xlevel is already large enough, it shouldn't change: */ 284 add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow", 285 "-cpu phenom,+3dnow,+sse4a,+invtsc,+npt,+svm", 286 "xlevel", 0x8000001A); 287 /* If xlevel is explicitly set, it shouldn't change: */ 288 add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002", 289 "-cpu 486,xlevel=0x80000002,+3dnow,+sse4a,+invtsc,+npt,+svm", 290 "xlevel", 0x80000002); 291 add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A", 292 "-cpu 486,xlevel=0x8000001A,+3dnow,+sse4a,+invtsc,+npt,+svm", 293 "xlevel", 0x8000001A); 294 add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0", 295 "-cpu 486,xlevel=0,+3dnow,+sse4a,+invtsc,+npt,+svm", 296 "xlevel", 0); 297 298 /* if xlevel2 is already large enough, it shouldn't change: */ 299 add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed", 300 "-cpu 486,xlevel2=0xC0000002,+xstore", 301 "xlevel2", 0xC0000002); 302 303 /* Check compatibility of old machine-types that didn't 304 * auto-increase level/xlevel/xlevel2: */ 305 306 add_cpuid_test("x86/cpuid/auto-level/pc-2.7", 307 "-machine pc-i440fx-2.7 -cpu 486,+arat,+avx512vbmi,+xsaveopt", 308 "level", 1); 309 add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7", 310 "-machine pc-i440fx-2.7 -cpu 486,+3dnow,+sse4a,+invtsc,+npt,+svm", 311 "xlevel", 0); 312 add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7", 313 "-machine pc-i440fx-2.7 -cpu 486,+xstore", 314 "xlevel2", 0); 315 /* 316 * QEMU 1.4.0 had auto-level enabled for CPUID[7], already, 317 * and the compat code that sets default level shouldn't 318 * disable the auto-level=7 code: 319 */ 320 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.4/off", 321 "-machine pc-i440fx-1.4 -cpu Nehalem", 322 "level", 2); 323 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.5/on", 324 "-machine pc-i440fx-1.4 -cpu Nehalem,+smap", 325 "level", 7); 326 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off", 327 "-machine pc-i440fx-2.3 -cpu Penryn", 328 "level", 4); 329 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on", 330 "-machine pc-i440fx-2.3 -cpu Penryn,+erms", 331 "level", 7); 332 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off", 333 "-machine pc-i440fx-2.9 -cpu Conroe", 334 "level", 10); 335 add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/on", 336 "-machine pc-i440fx-2.9 -cpu Conroe,+erms", 337 "level", 10); 338 339 /* 340 * xlevel doesn't have any feature that triggers auto-level 341 * code on old machine-types. Just check that the compat code 342 * is working correctly: 343 */ 344 add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3", 345 "-machine pc-i440fx-2.3 -cpu SandyBridge", 346 "xlevel", 0x8000000a); 347 add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off", 348 "-machine pc-i440fx-2.4 -cpu SandyBridge,", 349 "xlevel", 0x80000008); 350 add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on", 351 "-machine pc-i440fx-2.4 -cpu SandyBridge,+svm,+npt", 352 "xlevel", 0x80000008); 353 354 /* Test feature parsing */ 355 add_feature_test("x86/cpuid/features/plus", 356 "-cpu 486,+arat", 357 6, 0, "EAX", 2, true); 358 add_feature_test("x86/cpuid/features/minus", 359 "-cpu pentium,-mmx", 360 1, 0, "EDX", 23, false); 361 add_feature_test("x86/cpuid/features/on", 362 "-cpu 486,arat=on", 363 6, 0, "EAX", 2, true); 364 add_feature_test("x86/cpuid/features/off", 365 "-cpu pentium,mmx=off", 366 1, 0, "EDX", 23, false); 367 add_feature_test("x86/cpuid/features/max-plus-invtsc", 368 "-cpu max,+invtsc", 369 0x80000007, 0, "EDX", 8, true); 370 add_feature_test("x86/cpuid/features/max-invtsc-on", 371 "-cpu max,invtsc=on", 372 0x80000007, 0, "EDX", 8, true); 373 add_feature_test("x86/cpuid/features/max-minus-mmx", 374 "-cpu max,-mmx", 375 1, 0, "EDX", 23, false); 376 add_feature_test("x86/cpuid/features/max-invtsc-on,mmx=off", 377 "-cpu max,mmx=off", 378 1, 0, "EDX", 23, false); 379 380 return g_test_run(); 381 } 382