From de770e023aebdb70a7eecf354fcb215e4a6795eb Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Tue, 30 Sep 2014 21:18:51 +0400 Subject: [PATCH] cpuinfo: x86 -- Rework cpuinfo features fetching Instead of parsing procfs lets use native cpuid(), it's a way faster. The dark side is that the kernel may disable some of features via bootline options even if they are present on hardware but for us it's fine -- we will be testing hardware cpu for features anyway. The X86_FEATURE_ bits are gathered from two sources: linux kernel and cpu specifications. Signed-off-by: Cyrill Gorcunov Signed-off-by: Pavel Emelyanov --- arch/x86/cpu.c | 180 ++++++++++++++++++++++++++++++++----- arch/x86/include/asm/cpu.h | 21 ++++- 2 files changed, 180 insertions(+), 21 deletions(-) diff --git a/arch/x86/cpu.c b/arch/x86/cpu.c index 82d8e1364..12943e469 100644 --- a/arch/x86/cpu.c +++ b/arch/x86/cpu.c @@ -22,41 +22,181 @@ #undef LOG_PREFIX #define LOG_PREFIX "cpu: " -const char * const x86_cap_flags[NCAPINTS_BITS] = { - [X86_FEATURE_FPU] = "fpu", - [X86_FEATURE_FXSR] = "fxsr", - [X86_FEATURE_XSAVE] = "xsave", -}; +static struct cpuinfo_x86 rt_cpu_info; -static DECLARE_BITMAP(cpu_features, NCAPINTS_BITS); - -static void cpu_set_feature(unsigned int feature) +static void set_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature) { if (likely(feature < NCAPINTS_BITS)) - set_bit(feature, cpu_features); + set_bit(feature, (unsigned long *)c->x86_capability); +} + +static void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature) +{ + if (likely(feature < NCAPINTS_BITS)) + clear_bit(feature, (unsigned long *)c->x86_capability); +} + +static int test_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature) +{ + if (likely(feature < NCAPINTS_BITS)) + return test_bit(feature, (unsigned long *)c->x86_capability); + return 0; } bool cpu_has_feature(unsigned int feature) { - if (likely(feature < NCAPINTS_BITS)) - return test_bit(feature, cpu_features); - return false; + return test_cpu_cap(&rt_cpu_info, feature); } -static int proc_cpuinfo_match(char *tok) +static int cpu_init_cpuid(struct cpuinfo_x86 *c) { - if (!strcmp(tok, x86_cap_flags[X86_FEATURE_FXSR])) - cpu_set_feature(X86_FEATURE_FXSR); - else if (!strcmp(tok, x86_cap_flags[X86_FEATURE_XSAVE])) - cpu_set_feature(X86_FEATURE_XSAVE); - else if (!strcmp(tok, x86_cap_flags[X86_FEATURE_FPU])) - cpu_set_feature(X86_FEATURE_FPU); + /* + * See cpu_detect() in the kernel, also + * read cpuid specs not only from general + * SDM but for extended instructions set + * reference. + */ + + /* Get vendor name */ + cpuid(0x00000000, + (unsigned int *)&c->cpuid_level, + (unsigned int *)&c->x86_vendor_id[0], + (unsigned int *)&c->x86_vendor_id[8], + (unsigned int *)&c->x86_vendor_id[4]); + + if (!strcmp(c->x86_vendor_id, "GenuineIntel")) { + c->x86_vendor = X86_VENDOR_INTEL; + } else if (!strcmp(c->x86_vendor_id, "AuthenticAMD")) { + c->x86_vendor = X86_VENDOR_AMD; + } else { + pr_err("Unsupported CPU vendor %s\n", + c->x86_vendor_id); + return -1; + } + + c->x86_family = 4; + + /* Intel-defined flags: level 0x00000001 */ + if (c->cpuid_level >= 0x00000001) { + u32 eax, ebx, ecx, edx; + + cpuid(0x00000001, &eax, &ebx, &ecx, &edx); + c->x86_family = (eax >> 8) & 0xf; + c->x86_model = (eax >> 4) & 0xf; + c->x86_mask = eax & 0xf; + + if (c->x86_family == 0xf) + c->x86_family += (eax >> 20) & 0xff; + if (c->x86_family >= 0x6) + c->x86_model += ((eax >> 16) & 0xf) << 4; + + c->x86_capability[0] = edx; + c->x86_capability[4] = ecx; + } + + /* Additional Intel-defined flags: level 0x00000007 */ + if (c->cpuid_level >= 0x00000007) { + u32 eax, ebx, ecx, edx; + + cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx); + c->x86_capability[9] = ebx; + c->x86_capability[11] = ecx; + } + + /* Extended state features: level 0x0000000d */ + if (c->cpuid_level >= 0x0000000d) { + u32 eax, ebx, ecx, edx; + + cpuid_count(0x0000000d, 1, &eax, &ebx, &ecx, &edx); + c->x86_capability[10] = eax; + } + + /* AMD-defined flags: level 0x80000001 */ + c->extended_cpuid_level = cpuid_eax(0x80000000); + + if ((c->extended_cpuid_level & 0xffff0000) == 0x80000000) { + if (c->extended_cpuid_level >= 0x80000001) { + c->x86_capability[1] = cpuid_edx(0x80000001); + c->x86_capability[6] = cpuid_ecx(0x80000001); + } + } + + /* + * We're don't care about scattered features for now, + * otherwise look into init_scattered_cpuid_features() + * in kernel. + */ + + if (c->extended_cpuid_level >= 0x80000004) { + unsigned int *v; + char *p, *q; + v = (unsigned int *)c->x86_model_id; + cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); + cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); + cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); + c->x86_model_id[48] = 0; + + /* + * Intel chips right-justify this string for some dumb reason; + * undo that brain damage: + */ + p = q = &c->x86_model_id[0]; + while (*p == ' ') + p++; + if (p != q) { + while (*p) + *q++ = *p++; + while (q <= &c->x86_model_id[48]) + *q++ = '\0'; /* Zero-pad the rest */ + } + } + + /* On x86-64 NOP is always present */ + set_cpu_cap(c, X86_FEATURE_NOPL); + + switch (c->x86_vendor) { + case X86_VENDOR_INTEL: + /* + * Strictly speaking we need to read MSR_IA32_MISC_ENABLE + * here but on ring3 it's impossible. + */ + if (c->x86_family == 15) { + clear_cpu_cap(c, X86_FEATURE_REP_GOOD); + clear_cpu_cap(c, X86_FEATURE_ERMS); + } else if (c->x86_family == 6) { + /* On x86-64 rep is fine */ + set_cpu_cap(c, X86_FEATURE_REP_GOOD); + } + + /* See filter_cpuid_features in kernel */ + if ((s32)c->cpuid_level < (s32)0x0000000d) + clear_cpu_cap(c, X86_FEATURE_XSAVE); + break; + case X86_VENDOR_AMD: + /* + * Bit 31 in normal CPUID used for nonstandard 3DNow ID; + * 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway + */ + clear_cpu_cap(c, 0 * 32 + 31); + if (c->x86_family >= 0x10) + set_cpu_cap(c, X86_FEATURE_REP_GOOD); + if (c->x86_family == 0xf) { + u32 level; + + /* On C+ stepping K8 rep microcode works well for copy/memset */ + level = cpuid_eax(1); + if ((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58) + set_cpu_cap(c, X86_FEATURE_REP_GOOD); + } + break; + } + return 0; } int cpu_init(void) { - if (parse_cpuinfo_features(proc_cpuinfo_match)) + if (cpu_init_cpuid(&rt_cpu_info)) return -1; BUILD_BUG_ON(sizeof(struct xsave_struct) != XSAVE_SIZE); diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index 5914c5c2b..4d5f3ed84 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -176,7 +176,26 @@ static inline unsigned int cpuid_edx(unsigned int op) return edx; } -extern const char * const x86_cap_flags[NCAPINTS_BITS]; +#define X86_FEATURE_VERSION 1 + +enum { + X86_VENDOR_INTEL = 0, + X86_VENDOR_AMD = 1, + + X86_VENDOR_MAX +}; + +struct cpuinfo_x86 { + u8 x86_family; + u8 x86_vendor; + u8 x86_model; + u8 x86_mask; + u32 x86_capability[NCAPINTS]; + u32 extended_cpuid_level; + int cpuid_level; + char x86_vendor_id[16]; + char x86_model_id[64]; +}; extern bool cpu_has_feature(unsigned int feature); extern int cpu_init(void);