diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kernel/cpu/common.c | 47 |
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 | */ | ||
220 | struct cpuid_dependent_feature { | ||
221 | u32 feature; | ||
222 | u32 level; | ||
223 | }; | ||
224 | static const struct cpuid_dependent_feature __cpuinitconst | ||
225 | cpuid_dependent_features[] = { | ||
226 | { X86_FEATURE_MWAIT, 0x00000005 }, | ||
227 | { X86_FEATURE_DCA, 0x00000009 }, | ||
228 | { X86_FEATURE_XSAVE, 0x0000000d }, | ||
229 | { 0, 0 } | ||
230 | }; | ||
231 | |||
232 | static 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 | ||
578 | void __init early_cpu_init(void) | 622 | void __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; |