mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-22 10:34:55 +08:00
6e6a4932b0
When a function is used in more than one file it may not be possible to immediately tell from context what the intended meaning is. As such, it is more important that the naming be self-evident. Thus, change get_flags() to get_cpuflags(). For consistency, change check_flags() to check_cpuflags() even though it is only used in cpucheck.c. Link: http://lkml.kernel.org/r/1381450698-28710-2-git-send-email-keescook@chromium.org Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
105 lines
2.2 KiB
C
105 lines
2.2 KiB
C
#include <linux/types.h>
|
|
#include "bitops.h"
|
|
|
|
#include <asm/processor-flags.h>
|
|
#include <asm/required-features.h>
|
|
#include <asm/msr-index.h>
|
|
#include "cpuflags.h"
|
|
|
|
struct cpu_features cpu;
|
|
u32 cpu_vendor[3];
|
|
|
|
static bool loaded_flags;
|
|
|
|
static int has_fpu(void)
|
|
{
|
|
u16 fcw = -1, fsw = -1;
|
|
unsigned long cr0;
|
|
|
|
asm volatile("mov %%cr0,%0" : "=r" (cr0));
|
|
if (cr0 & (X86_CR0_EM|X86_CR0_TS)) {
|
|
cr0 &= ~(X86_CR0_EM|X86_CR0_TS);
|
|
asm volatile("mov %0,%%cr0" : : "r" (cr0));
|
|
}
|
|
|
|
asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
|
|
: "+m" (fsw), "+m" (fcw));
|
|
|
|
return fsw == 0 && (fcw & 0x103f) == 0x003f;
|
|
}
|
|
|
|
int has_eflag(unsigned long mask)
|
|
{
|
|
unsigned long f0, f1;
|
|
|
|
asm volatile("pushf \n\t"
|
|
"pushf \n\t"
|
|
"pop %0 \n\t"
|
|
"mov %0,%1 \n\t"
|
|
"xor %2,%1 \n\t"
|
|
"push %1 \n\t"
|
|
"popf \n\t"
|
|
"pushf \n\t"
|
|
"pop %1 \n\t"
|
|
"popf"
|
|
: "=&r" (f0), "=&r" (f1)
|
|
: "ri" (mask));
|
|
|
|
return !!((f0^f1) & mask);
|
|
}
|
|
|
|
/* Handle x86_32 PIC using ebx. */
|
|
#if defined(__i386__) && defined(__PIC__)
|
|
# define EBX_REG "=r"
|
|
#else
|
|
# define EBX_REG "=b"
|
|
#endif
|
|
|
|
static inline void cpuid(u32 id, u32 *a, u32 *b, u32 *c, u32 *d)
|
|
{
|
|
asm volatile(".ifnc %%ebx,%3 ; movl %%ebx,%3 ; .endif \n\t"
|
|
"cpuid \n\t"
|
|
".ifnc %%ebx,%3 ; xchgl %%ebx,%3 ; .endif \n\t"
|
|
: "=a" (*a), "=c" (*c), "=d" (*d), EBX_REG (*b)
|
|
: "a" (id)
|
|
);
|
|
}
|
|
|
|
void get_cpuflags(void)
|
|
{
|
|
u32 max_intel_level, max_amd_level;
|
|
u32 tfms;
|
|
u32 ignored;
|
|
|
|
if (loaded_flags)
|
|
return;
|
|
loaded_flags = true;
|
|
|
|
if (has_fpu())
|
|
set_bit(X86_FEATURE_FPU, cpu.flags);
|
|
|
|
if (has_eflag(X86_EFLAGS_ID)) {
|
|
cpuid(0x0, &max_intel_level, &cpu_vendor[0], &cpu_vendor[2],
|
|
&cpu_vendor[1]);
|
|
|
|
if (max_intel_level >= 0x00000001 &&
|
|
max_intel_level <= 0x0000ffff) {
|
|
cpuid(0x1, &tfms, &ignored, &cpu.flags[4],
|
|
&cpu.flags[0]);
|
|
cpu.level = (tfms >> 8) & 15;
|
|
cpu.model = (tfms >> 4) & 15;
|
|
if (cpu.level >= 6)
|
|
cpu.model += ((tfms >> 16) & 0xf) << 4;
|
|
}
|
|
|
|
cpuid(0x80000000, &max_amd_level, &ignored, &ignored,
|
|
&ignored);
|
|
|
|
if (max_amd_level >= 0x80000001 &&
|
|
max_amd_level <= 0x8000ffff) {
|
|
cpuid(0x80000001, &ignored, &ignored, &cpu.flags[6],
|
|
&cpu.flags[1]);
|
|
}
|
|
}
|
|
}
|