]>
Commit | Line | Data |
---|---|---|
1 | #include "qemu/osdep.h" | |
2 | #include "qemu-common.h" | |
3 | #include "qapi/qmp/qlist.h" | |
4 | #include "qapi/qmp/qdict.h" | |
5 | #include "qapi/qmp/qint.h" | |
6 | #include "qapi/qmp/qbool.h" | |
7 | #include "libqtest.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 | QDECREF(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_incref(ret); | |
34 | QDECREF(resp); | |
35 | return ret; | |
36 | } | |
37 | ||
38 | #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS | |
39 | static bool qom_get_bool(const char *path, const char *prop) | |
40 | { | |
41 | QBool *value = qobject_to_qbool(qom_get(path, prop)); | |
42 | bool b = qbool_get_bool(value); | |
43 | ||
44 | QDECREF(value); | |
45 | return b; | |
46 | } | |
47 | #endif | |
48 | ||
49 | typedef struct CpuidTestArgs { | |
50 | const char *cmdline; | |
51 | const char *property; | |
52 | int64_t expected_value; | |
53 | } CpuidTestArgs; | |
54 | ||
55 | static void test_cpuid_prop(const void *data) | |
56 | { | |
57 | const CpuidTestArgs *args = data; | |
58 | char *path; | |
59 | QInt *value; | |
60 | ||
61 | qtest_start(args->cmdline); | |
62 | path = get_cpu0_qom_path(); | |
63 | value = qobject_to_qint(qom_get(path, args->property)); | |
64 | g_assert_cmpint(qint_get_int(value), ==, args->expected_value); | |
65 | qtest_end(); | |
66 | ||
67 | QDECREF(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 | #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS | |
82 | static void test_plus_minus_subprocess(void) | |
83 | { | |
84 | char *path; | |
85 | ||
86 | /* Rules: | |
87 | * 1)"-foo" overrides "+foo" | |
88 | * 2) "[+-]foo" overrides "foo=..." | |
89 | * 3) Old feature names with underscores (e.g. "sse4_2") | |
90 | * should keep working | |
91 | * | |
92 | * Note: rules 1 and 2 are planned to be removed soon, and | |
93 | * should generate a warning. | |
94 | */ | |
95 | qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on"); | |
96 | path = get_cpu0_qom_path(); | |
97 | ||
98 | g_assert_false(qom_get_bool(path, "fpu")); | |
99 | g_assert_false(qom_get_bool(path, "mce")); | |
100 | g_assert_true(qom_get_bool(path, "cx8")); | |
101 | ||
102 | /* Test both the original and the alias feature names: */ | |
103 | g_assert_true(qom_get_bool(path, "sse4-1")); | |
104 | g_assert_true(qom_get_bool(path, "sse4.1")); | |
105 | ||
106 | g_assert_true(qom_get_bool(path, "sse4-2")); | |
107 | g_assert_true(qom_get_bool(path, "sse4.2")); | |
108 | ||
109 | qtest_end(); | |
110 | g_free(path); | |
111 | } | |
112 | ||
113 | static void test_plus_minus(void) | |
114 | { | |
115 | g_test_trap_subprocess("/x86/cpuid/parsing-plus-minus/subprocess", 0, 0); | |
116 | g_test_trap_assert_passed(); | |
117 | g_test_trap_assert_stderr("*Ambiguous CPU model string. " | |
118 | "Don't mix both \"-mce\" and \"mce=on\"*"); | |
119 | g_test_trap_assert_stderr("*Ambiguous CPU model string. " | |
120 | "Don't mix both \"+cx8\" and \"cx8=off\"*"); | |
121 | g_test_trap_assert_stdout(""); | |
122 | } | |
123 | #endif | |
124 | ||
125 | int main(int argc, char **argv) | |
126 | { | |
127 | g_test_init(&argc, &argv, NULL); | |
128 | ||
129 | #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS | |
130 | g_test_add_func("/x86/cpuid/parsing-plus-minus/subprocess", | |
131 | test_plus_minus_subprocess); | |
132 | g_test_add_func("/x86/cpuid/parsing-plus-minus", test_plus_minus); | |
133 | #endif | |
134 | ||
135 | /* Original level values for CPU models: */ | |
136 | add_cpuid_test("x86/cpuid/phenom/level", | |
137 | "-cpu phenom", "level", 5); | |
138 | add_cpuid_test("x86/cpuid/Conroe/level", | |
139 | "-cpu Conroe", "level", 10); | |
140 | add_cpuid_test("x86/cpuid/SandyBridge/level", | |
141 | "-cpu SandyBridge", "level", 0xd); | |
142 | add_cpuid_test("x86/cpuid/486/xlevel", | |
143 | "-cpu 486", "xlevel", 0); | |
144 | add_cpuid_test("x86/cpuid/core2duo/xlevel", | |
145 | "-cpu core2duo", "xlevel", 0x80000008); | |
146 | add_cpuid_test("x86/cpuid/phenom/xlevel", | |
147 | "-cpu phenom", "xlevel", 0x8000001A); | |
148 | add_cpuid_test("x86/cpuid/athlon/xlevel", | |
149 | "-cpu athlon", "xlevel", 0x80000008); | |
150 | ||
151 | /* If level is not large enough, it should increase automatically: */ | |
152 | /* CPUID[6].EAX: */ | |
153 | add_cpuid_test("x86/cpuid/auto-level/phenom/arat", | |
154 | "-cpu 486,+arat", "level", 6); | |
155 | /* CPUID[EAX=7,ECX=0].EBX: */ | |
156 | add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase", | |
157 | "-cpu phenom,+fsgsbase", "level", 7); | |
158 | /* CPUID[EAX=7,ECX=0].ECX: */ | |
159 | add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi", | |
160 | "-cpu phenom,+avx512vbmi", "level", 7); | |
161 | /* CPUID[EAX=0xd,ECX=1].EAX: */ | |
162 | add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt", | |
163 | "-cpu phenom,+xsaveopt", "level", 0xd); | |
164 | /* CPUID[8000_0001].EDX: */ | |
165 | add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow", | |
166 | "-cpu 486,+3dnow", "xlevel", 0x80000001); | |
167 | /* CPUID[8000_0001].ECX: */ | |
168 | add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a", | |
169 | "-cpu 486,+sse4a", "xlevel", 0x80000001); | |
170 | /* CPUID[8000_0007].EDX: */ | |
171 | add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc", | |
172 | "-cpu 486,+invtsc", "xlevel", 0x80000007); | |
173 | /* CPUID[8000_000A].EDX: */ | |
174 | add_cpuid_test("x86/cpuid/auto-xlevel/486/npt", | |
175 | "-cpu 486,+npt", "xlevel", 0x8000000A); | |
176 | /* CPUID[C000_0001].EDX: */ | |
177 | add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore", | |
178 | "-cpu phenom,+xstore", "xlevel2", 0xC0000001); | |
179 | /* SVM needs CPUID[0x8000000A] */ | |
180 | add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm", | |
181 | "-cpu athlon,+svm", "xlevel", 0x8000000A); | |
182 | ||
183 | ||
184 | /* If level is already large enough, it shouldn't change: */ | |
185 | add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple", | |
186 | "-cpu SandyBridge,+arat,+fsgsbase,+avx512vbmi", | |
187 | "level", 0xd); | |
188 | /* If level is explicitly set, it shouldn't change: */ | |
189 | add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF", | |
190 | "-cpu 486,level=0xF,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", | |
191 | "level", 0xF); | |
192 | add_cpuid_test("x86/cpuid/auto-level/486/fixed/2", | |
193 | "-cpu 486,level=2,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", | |
194 | "level", 2); | |
195 | add_cpuid_test("x86/cpuid/auto-level/486/fixed/0", | |
196 | "-cpu 486,level=0,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", | |
197 | "level", 0); | |
198 | ||
199 | /* if xlevel is already large enough, it shouldn't change: */ | |
200 | add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow", | |
201 | "-cpu phenom,+3dnow,+sse4a,+invtsc,+npt,+svm", | |
202 | "xlevel", 0x8000001A); | |
203 | /* If xlevel is explicitly set, it shouldn't change: */ | |
204 | add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002", | |
205 | "-cpu 486,xlevel=0x80000002,+3dnow,+sse4a,+invtsc,+npt,+svm", | |
206 | "xlevel", 0x80000002); | |
207 | add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A", | |
208 | "-cpu 486,xlevel=0x8000001A,+3dnow,+sse4a,+invtsc,+npt,+svm", | |
209 | "xlevel", 0x8000001A); | |
210 | add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0", | |
211 | "-cpu 486,xlevel=0,+3dnow,+sse4a,+invtsc,+npt,+svm", | |
212 | "xlevel", 0); | |
213 | ||
214 | /* if xlevel2 is already large enough, it shouldn't change: */ | |
215 | add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed", | |
216 | "-cpu 486,xlevel2=0xC0000002,+xstore", | |
217 | "xlevel2", 0xC0000002); | |
218 | ||
219 | /* Check compatibility of old machine-types that didn't | |
220 | * auto-increase level/xlevel/xlevel2: */ | |
221 | ||
222 | add_cpuid_test("x86/cpuid/auto-level/pc-2.7", | |
223 | "-machine pc-i440fx-2.7 -cpu 486,+arat,+avx512vbmi,+xsaveopt", | |
224 | "level", 1); | |
225 | add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7", | |
226 | "-machine pc-i440fx-2.7 -cpu 486,+3dnow,+sse4a,+invtsc,+npt,+svm", | |
227 | "xlevel", 0); | |
228 | add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7", | |
229 | "-machine pc-i440fx-2.7 -cpu 486,+xstore", | |
230 | "xlevel2", 0); | |
231 | ||
232 | return g_test_run(); | |
233 | } |