/src/xen/xen/arch/x86/lib/cpu-policy/cpuid.c
Line | Count | Source |
1 | | #include "private.h" |
2 | | |
3 | | #include <xen/lib/x86/cpu-policy.h> |
4 | | |
5 | | static void zero_leaves(struct cpuid_leaf *l, |
6 | | unsigned int first, unsigned int last) |
7 | 0 | { |
8 | 0 | if ( first <= last ) |
9 | 0 | memset(&l[first], 0, sizeof(*l) * (last - first + 1)); |
10 | 0 | } |
11 | | |
12 | | unsigned int x86_cpuid_lookup_vendor(uint32_t ebx, uint32_t ecx, uint32_t edx) |
13 | 2 | { |
14 | 2 | switch ( ebx ) |
15 | 2 | { |
16 | 2 | case X86_VENDOR_INTEL_EBX: |
17 | 2 | if ( ecx == X86_VENDOR_INTEL_ECX && |
18 | 2 | edx == X86_VENDOR_INTEL_EDX ) |
19 | 2 | return X86_VENDOR_INTEL; |
20 | 0 | break; |
21 | | |
22 | 0 | case X86_VENDOR_AMD_EBX: |
23 | 0 | if ( ecx == X86_VENDOR_AMD_ECX && |
24 | 0 | edx == X86_VENDOR_AMD_EDX ) |
25 | 0 | return X86_VENDOR_AMD; |
26 | 0 | break; |
27 | | |
28 | 0 | case X86_VENDOR_CENTAUR_EBX: |
29 | 0 | if ( ecx == X86_VENDOR_CENTAUR_ECX && |
30 | 0 | edx == X86_VENDOR_CENTAUR_EDX ) |
31 | 0 | return X86_VENDOR_CENTAUR; |
32 | 0 | break; |
33 | | |
34 | 0 | case X86_VENDOR_SHANGHAI_EBX: |
35 | 0 | if ( ecx == X86_VENDOR_SHANGHAI_ECX && |
36 | 0 | edx == X86_VENDOR_SHANGHAI_EDX ) |
37 | 0 | return X86_VENDOR_SHANGHAI; |
38 | 0 | break; |
39 | | |
40 | 0 | case X86_VENDOR_HYGON_EBX: |
41 | 0 | if ( ecx == X86_VENDOR_HYGON_ECX && |
42 | 0 | edx == X86_VENDOR_HYGON_EDX ) |
43 | 0 | return X86_VENDOR_HYGON; |
44 | 0 | break; |
45 | 2 | } |
46 | | |
47 | 0 | return X86_VENDOR_UNKNOWN; |
48 | 2 | } |
49 | | |
50 | | const char *x86_cpuid_vendor_to_str(unsigned int vendor) |
51 | 0 | { |
52 | 0 | switch ( vendor ) |
53 | 0 | { |
54 | 0 | case X86_VENDOR_INTEL: return "Intel"; |
55 | 0 | case X86_VENDOR_AMD: return "AMD"; |
56 | 0 | case X86_VENDOR_CENTAUR: return "Centaur"; |
57 | 0 | case X86_VENDOR_SHANGHAI: return "Shanghai"; |
58 | 0 | case X86_VENDOR_HYGON: return "Hygon"; |
59 | 0 | default: return "Unknown"; |
60 | 0 | } |
61 | 0 | } |
62 | | |
63 | | void x86_cpu_policy_to_featureset( |
64 | | const struct cpu_policy *p, uint32_t fs[FEATURESET_NR_ENTRIES]) |
65 | 0 | { |
66 | 0 | fs[FEATURESET_1d] = p->basic._1d; |
67 | 0 | fs[FEATURESET_1c] = p->basic._1c; |
68 | 0 | fs[FEATURESET_e1d] = p->extd.e1d; |
69 | 0 | fs[FEATURESET_e1c] = p->extd.e1c; |
70 | 0 | fs[FEATURESET_Da1] = p->xstate.Da1; |
71 | 0 | fs[FEATURESET_7b0] = p->feat._7b0; |
72 | 0 | fs[FEATURESET_7c0] = p->feat._7c0; |
73 | 0 | fs[FEATURESET_e7d] = p->extd.e7d; |
74 | 0 | fs[FEATURESET_e8b] = p->extd.e8b; |
75 | 0 | fs[FEATURESET_7d0] = p->feat._7d0; |
76 | 0 | fs[FEATURESET_7a1] = p->feat._7a1; |
77 | 0 | fs[FEATURESET_e21a] = p->extd.e21a; |
78 | 0 | fs[FEATURESET_7b1] = p->feat._7b1; |
79 | 0 | fs[FEATURESET_7d2] = p->feat._7d2; |
80 | 0 | fs[FEATURESET_7c1] = p->feat._7c1; |
81 | 0 | fs[FEATURESET_7d1] = p->feat._7d1; |
82 | 0 | fs[FEATURESET_m10Al] = p->arch_caps.lo; |
83 | 0 | fs[FEATURESET_m10Ah] = p->arch_caps.hi; |
84 | 0 | fs[FEATURESET_e21c] = p->extd.e21c; |
85 | 0 | } |
86 | | |
87 | | void x86_cpu_featureset_to_policy( |
88 | | const uint32_t fs[FEATURESET_NR_ENTRIES], struct cpu_policy *p) |
89 | 0 | { |
90 | 0 | p->basic._1d = fs[FEATURESET_1d]; |
91 | 0 | p->basic._1c = fs[FEATURESET_1c]; |
92 | 0 | p->extd.e1d = fs[FEATURESET_e1d]; |
93 | 0 | p->extd.e1c = fs[FEATURESET_e1c]; |
94 | 0 | p->xstate.Da1 = fs[FEATURESET_Da1]; |
95 | 0 | p->feat._7b0 = fs[FEATURESET_7b0]; |
96 | 0 | p->feat._7c0 = fs[FEATURESET_7c0]; |
97 | 0 | p->extd.e7d = fs[FEATURESET_e7d]; |
98 | 0 | p->extd.e8b = fs[FEATURESET_e8b]; |
99 | 0 | p->feat._7d0 = fs[FEATURESET_7d0]; |
100 | 0 | p->feat._7a1 = fs[FEATURESET_7a1]; |
101 | 0 | p->extd.e21a = fs[FEATURESET_e21a]; |
102 | 0 | p->feat._7b1 = fs[FEATURESET_7b1]; |
103 | 0 | p->feat._7d2 = fs[FEATURESET_7d2]; |
104 | 0 | p->feat._7c1 = fs[FEATURESET_7c1]; |
105 | 0 | p->feat._7d1 = fs[FEATURESET_7d1]; |
106 | 0 | p->arch_caps.lo = fs[FEATURESET_m10Al]; |
107 | 0 | p->arch_caps.hi = fs[FEATURESET_m10Ah]; |
108 | 0 | p->extd.e21c = fs[FEATURESET_e21c]; |
109 | 0 | } |
110 | | |
111 | | void x86_cpu_policy_recalc_synth(struct cpu_policy *p) |
112 | 2 | { |
113 | 2 | p->x86_vendor = x86_cpuid_lookup_vendor( |
114 | 2 | p->basic.vendor_ebx, p->basic.vendor_ecx, p->basic.vendor_edx); |
115 | 2 | } |
116 | | |
117 | | void x86_cpu_policy_fill_native(struct cpu_policy *p) |
118 | 2 | { |
119 | 2 | unsigned int i; |
120 | | |
121 | 2 | cpuid_leaf(0, &p->basic.raw[0]); |
122 | 28 | for ( i = 1; i <= MIN(p->basic.max_leaf, |
123 | 26 | ARRAY_SIZE(p->basic.raw) - 1); ++i ) |
124 | 26 | { |
125 | 26 | switch ( i ) |
126 | 26 | { |
127 | 8 | case 0x4: case 0x7: case 0xb: case 0xd: |
128 | | /* Multi-invocation leaves. Deferred. */ |
129 | 8 | continue; |
130 | 26 | } |
131 | | |
132 | 18 | cpuid_leaf(i, &p->basic.raw[i]); |
133 | 18 | } |
134 | | |
135 | 2 | if ( p->basic.max_leaf >= 4 ) |
136 | 2 | { |
137 | 10 | for ( i = 0; i < ARRAY_SIZE(p->cache.raw); ++i ) |
138 | 10 | { |
139 | 10 | union { |
140 | 10 | struct cpuid_leaf l; |
141 | 10 | struct cpuid_cache_leaf c; |
142 | 10 | } u; |
143 | | |
144 | 10 | cpuid_count_leaf(4, i, &u.l); |
145 | | |
146 | 10 | if ( u.c.type == 0 ) |
147 | 2 | break; |
148 | | |
149 | 8 | p->cache.subleaf[i] = u.c; |
150 | 8 | } |
151 | | |
152 | | /* |
153 | | * The choice of CPUID_GUEST_NR_CACHE is arbitrary. It is expected |
154 | | * that it will eventually need increasing for future hardware. |
155 | | */ |
156 | | #ifdef __XEN__ |
157 | | if ( i == ARRAY_SIZE(p->cache.raw) ) |
158 | | printk(XENLOG_WARNING |
159 | | "CPUID: Insufficient Leaf 4 space for this hardware\n"); |
160 | | #endif |
161 | 2 | } |
162 | | |
163 | 2 | if ( p->basic.max_leaf >= 7 ) |
164 | 2 | { |
165 | 2 | cpuid_count_leaf(7, 0, &p->feat.raw[0]); |
166 | | |
167 | 2 | for ( i = 1; i <= MIN(p->feat.max_subleaf, |
168 | 2 | ARRAY_SIZE(p->feat.raw) - 1); ++i ) |
169 | 0 | cpuid_count_leaf(7, i, &p->feat.raw[i]); |
170 | 2 | } |
171 | | |
172 | 2 | if ( p->basic.max_leaf >= 0xb ) |
173 | 2 | { |
174 | 2 | union { |
175 | 2 | struct cpuid_leaf l; |
176 | 2 | struct cpuid_topo_leaf t; |
177 | 2 | } u; |
178 | | |
179 | 6 | for ( i = 0; i < ARRAY_SIZE(p->topo.raw); ++i ) |
180 | 4 | { |
181 | 4 | cpuid_count_leaf(0xb, i, &u.l); |
182 | | |
183 | 4 | if ( u.t.type == 0 ) |
184 | 0 | break; |
185 | | |
186 | 4 | p->topo.subleaf[i] = u.t; |
187 | 4 | } |
188 | | |
189 | | /* |
190 | | * The choice of CPUID_GUEST_NR_TOPO is per the manual. It may need |
191 | | * to grow for future hardware. |
192 | | */ |
193 | | #ifdef __XEN__ |
194 | | if ( i == ARRAY_SIZE(p->topo.raw) && |
195 | | (cpuid_count_leaf(0xb, i, &u.l), u.t.type != 0) ) |
196 | | printk(XENLOG_WARNING |
197 | | "CPUID: Insufficient Leaf 0xb space for this hardware\n"); |
198 | | #endif |
199 | 2 | } |
200 | | |
201 | 2 | if ( p->basic.max_leaf >= 0xd ) |
202 | 2 | { |
203 | 2 | uint64_t xstates; |
204 | | |
205 | 2 | cpuid_count_leaf(0xd, 0, &p->xstate.raw[0]); |
206 | 2 | cpuid_count_leaf(0xd, 1, &p->xstate.raw[1]); |
207 | | |
208 | 2 | xstates = cpu_policy_xstates(p); |
209 | | |
210 | | /* This logic will probably need adjusting when XCR0[63] gets used. */ |
211 | 2 | BUILD_BUG_ON(ARRAY_SIZE(p->xstate.raw) > 63); |
212 | | |
213 | 124 | for ( i = 2; i < min_t(unsigned int, 63, |
214 | 122 | ARRAY_SIZE(p->xstate.raw)); ++i ) |
215 | 122 | { |
216 | 122 | if ( xstates & (1ULL << i) ) |
217 | 2 | cpuid_count_leaf(0xd, i, &p->xstate.raw[i]); |
218 | 122 | } |
219 | 2 | } |
220 | | |
221 | | /* Extended leaves. */ |
222 | 2 | cpuid_leaf(0x80000000U, &p->extd.raw[0]); |
223 | 18 | for ( i = 1; i <= MIN(p->extd.max_leaf & 0xffffU, |
224 | 16 | ARRAY_SIZE(p->extd.raw) - 1); ++i ) |
225 | 16 | cpuid_leaf(0x80000000U + i, &p->extd.raw[i]); |
226 | | |
227 | | /* Don't report leaves from possible lower level hypervisor, for now. */ |
228 | 2 | p->hv_limit = 0; |
229 | 2 | p->hv2_limit = 0; |
230 | | |
231 | | #ifdef __XEN__ |
232 | | /* TODO MSR_PLATFORM_INFO */ |
233 | | |
234 | | if ( p->feat.arch_caps ) |
235 | | rdmsrl(MSR_ARCH_CAPABILITIES, p->arch_caps.raw); |
236 | | #endif |
237 | | |
238 | 2 | x86_cpu_policy_recalc_synth(p); |
239 | 2 | } |
240 | | |
241 | | void x86_cpu_policy_clear_out_of_range_leaves(struct cpu_policy *p) |
242 | 0 | { |
243 | 0 | unsigned int i; |
244 | |
|
245 | 0 | zero_leaves(p->basic.raw, p->basic.max_leaf + 1, |
246 | 0 | ARRAY_SIZE(p->basic.raw) - 1); |
247 | |
|
248 | 0 | if ( p->basic.max_leaf < 4 ) |
249 | 0 | memset(p->cache.raw, 0, sizeof(p->cache.raw)); |
250 | 0 | else |
251 | 0 | { |
252 | 0 | for ( i = 0; (i < ARRAY_SIZE(p->cache.raw) && |
253 | 0 | p->cache.subleaf[i].type); ++i ) |
254 | 0 | ; |
255 | |
|
256 | 0 | zero_leaves(p->cache.raw, i, ARRAY_SIZE(p->cache.raw) - 1); |
257 | 0 | } |
258 | |
|
259 | 0 | if ( p->basic.max_leaf < 7 ) |
260 | 0 | memset(p->feat.raw, 0, sizeof(p->feat.raw)); |
261 | 0 | else |
262 | 0 | zero_leaves(p->feat.raw, p->feat.max_subleaf + 1, |
263 | 0 | ARRAY_SIZE(p->feat.raw) - 1); |
264 | |
|
265 | 0 | if ( p->basic.max_leaf < 0xb ) |
266 | 0 | memset(p->topo.raw, 0, sizeof(p->topo.raw)); |
267 | 0 | else |
268 | 0 | { |
269 | 0 | for ( i = 0; (i < ARRAY_SIZE(p->topo.raw) && |
270 | 0 | p->topo.subleaf[i].type); ++i ) |
271 | 0 | ; |
272 | |
|
273 | 0 | zero_leaves(p->topo.raw, i, ARRAY_SIZE(p->topo.raw) - 1); |
274 | 0 | } |
275 | |
|
276 | 0 | if ( p->basic.max_leaf < 0xd || !cpu_policy_xstates(p) ) |
277 | 0 | memset(p->xstate.raw, 0, sizeof(p->xstate.raw)); |
278 | 0 | else |
279 | 0 | { |
280 | | /* This logic will probably need adjusting when XCR0[63] gets used. */ |
281 | 0 | BUILD_BUG_ON(ARRAY_SIZE(p->xstate.raw) > 63); |
282 | | |
283 | | /* First two leaves always valid. Rest depend on xstates. */ |
284 | 0 | i = max(2, 64 - __builtin_clzll(cpu_policy_xstates(p))); |
285 | |
|
286 | 0 | zero_leaves(p->xstate.raw, i, |
287 | 0 | ARRAY_SIZE(p->xstate.raw) - 1); |
288 | 0 | } |
289 | |
|
290 | 0 | zero_leaves(p->extd.raw, (p->extd.max_leaf & 0xffff) + 1, |
291 | 0 | ARRAY_SIZE(p->extd.raw) - 1); |
292 | 0 | } |
293 | | |
294 | | const uint32_t *x86_cpu_policy_lookup_deep_deps(uint32_t feature) |
295 | 0 | { |
296 | 0 | static const uint32_t deep_features[] = INIT_DEEP_FEATURES; |
297 | 0 | static const struct { |
298 | 0 | uint32_t feature; |
299 | 0 | uint32_t fs[FEATURESET_NR_ENTRIES]; |
300 | 0 | } deep_deps[] = INIT_DEEP_DEPS; |
301 | 0 | unsigned int start = 0, end = ARRAY_SIZE(deep_deps); |
302 | |
|
303 | 0 | BUILD_BUG_ON(ARRAY_SIZE(deep_deps) != NR_DEEP_DEPS); |
304 | | |
305 | | /* Fast early exit. */ |
306 | 0 | if ( !test_bit(feature, deep_features) ) |
307 | 0 | return NULL; |
308 | | |
309 | | /* deep_deps[] is sorted. Perform a binary search. */ |
310 | 0 | while ( start < end ) |
311 | 0 | { |
312 | 0 | unsigned int mid = start + ((end - start) / 2); |
313 | |
|
314 | 0 | if ( deep_deps[mid].feature > feature ) |
315 | 0 | end = mid; |
316 | 0 | else if ( deep_deps[mid].feature < feature ) |
317 | 0 | start = mid + 1; |
318 | 0 | else |
319 | 0 | return deep_deps[mid].fs; |
320 | 0 | } |
321 | | |
322 | 0 | return NULL; |
323 | 0 | } |
324 | | |
325 | | /* |
326 | | * Local variables: |
327 | | * mode: C |
328 | | * c-file-style: "BSD" |
329 | | * c-basic-offset: 4 |
330 | | * tab-width: 4 |
331 | | * indent-tabs-mode: nil |
332 | | * End: |
333 | | */ |