aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kernel/cpu/common.c47
1 files changed, 47 insertions, 0 deletions
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 0f8656361e04..21f086b4c1a8 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -213,6 +213,49 @@ static inline void squash_the_stupid_serial_number(struct cpuinfo_x86 *c)
213#endif 213#endif
214 214
215/* 215/*
216 * Some CPU features depend on higher CPUID levels, which may not always
217 * be available due to CPUID level capping or broken virtualization
218 * software. Add those features to this table to auto-disable them.
219 */
220struct cpuid_dependent_feature {
221 u32 feature;
222 u32 level;
223};
224static const struct cpuid_dependent_feature __cpuinitconst
225cpuid_dependent_features[] = {
226 { X86_FEATURE_MWAIT, 0x00000005 },
227 { X86_FEATURE_DCA, 0x00000009 },
228 { X86_FEATURE_XSAVE, 0x0000000d },
229 { 0, 0 }
230};
231
232static void __cpuinit filter_cpuid_features(struct cpuinfo_x86 *c, bool warn)
233{
234 const struct cpuid_dependent_feature *df;
235 for (df = cpuid_dependent_features; df->feature; df++) {
236 /*
237 * Note: cpuid_level is set to -1 if unavailable, but
238 * extended_extended_level is set to 0 if unavailable
239 * and the legitimate extended levels are all negative
240 * when signed; hence the weird messing around with
241 * signs here...
242 */
243 if (cpu_has(c, df->feature) &&
244 ((s32)df->feature < 0 ?
245 (u32)df->feature > (u32)c->extended_cpuid_level :
246 (s32)df->feature > (s32)c->cpuid_level)) {
247 clear_cpu_cap(c, df->feature);
248 if (warn)
249 printk(KERN_WARNING
250 "CPU: CPU feature %s disabled "
251 "due to lack of CPUID level 0x%x\n",
252 x86_cap_flags[df->feature],
253 df->level);
254 }
255 }
256}
257
258/*
216 * Naming convention should be: <Name> [(<Codename>)] 259 * Naming convention should be: <Name> [(<Codename>)]
217 * This table only is used unless init_<vendor>() below doesn't set it; 260 * This table only is used unless init_<vendor>() below doesn't set it;
218 * in particular, if CPUID levels 0x80000002..4 are supported, this isn't used 261 * in particular, if CPUID levels 0x80000002..4 are supported, this isn't used
@@ -573,6 +616,7 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
573#ifdef CONFIG_SMP 616#ifdef CONFIG_SMP
574 c->cpu_index = boot_cpu_id; 617 c->cpu_index = boot_cpu_id;
575#endif 618#endif
619 filter_cpuid_features(c, false);
576} 620}
577 621
578void __init early_cpu_init(void) 622void __init early_cpu_init(void)
@@ -706,6 +750,9 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
706 * we do "generic changes." 750 * we do "generic changes."
707 */ 751 */
708 752
753 /* Filter out anything that depends on CPUID levels we don't have */
754 filter_cpuid_features(c, true);
755
709 /* If the model name is still unset, do table lookup. */ 756 /* If the model name is still unset, do table lookup. */
710 if (!c->x86_model_id[0]) { 757 if (!c->x86_model_id[0]) {
711 char *p; 758 char *p;