aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2009-01-23 20:20:50 -0500
committerH. Peter Anvin <hpa@linux.intel.com>2009-01-23 21:08:05 -0500
commitb38b0665905538e76e26f2a4c686179abb1f69f6 (patch)
tree849514b44863461c542872173221c3f365a0e6a7 /arch
parent75a048119e76540d73132cfc8e0fa0c0a8bb6c83 (diff)
x86: filter CPU features dependent on unavailable CPUID levels
Impact: Fixes potential crashes on misconfigured systems. Some CPU features require specific CPUID levels to be available in order to function, as they contain information about the operation of a specific feature. However, some BIOSes and virtualization software provide the ability to mask CPUID levels in order to support legacy operating systems. We try to enable such CPUID levels when we know how to do it, but for the remaining cases, filter out such CPU features when there is no way for us to support them. Do this in one place, in the CPUID code, with a table-driven approach. Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
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;