x86: Get rid of ->hard_math and all the FPU asm fu

Reimplement FPU detection code in C and drop old, not-so-recommended
detection method in asm. Move all the relevant stuff into i387.c where
it conceptually belongs. Finally drop cpuinfo_x86.hard_math.

[ hpa: huge thanks to Borislav for taking my original concept patch
  and productizing it ]

[ Boris, note to self: do not use static_cpu_has before alternatives! ]

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Link: http://lkml.kernel.org/r/1367244262-29511-2-git-send-email-bp@alien8.de
Link: http://lkml.kernel.org/r/1365436666-9837-2-git-send-email-bp@alien8.de
Signed-off-by: Borislav Petkov <bp@suse.de>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
This commit is contained in:
H. Peter Anvin 2013-04-29 16:04:20 +02:00 committed by H. Peter Anvin
parent d683b96b07
commit 60e019eb37
12 changed files with 60 additions and 65 deletions

View File

@ -62,10 +62,8 @@ extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set,
#define xstateregs_active fpregs_active #define xstateregs_active fpregs_active
#ifdef CONFIG_MATH_EMULATION #ifdef CONFIG_MATH_EMULATION
# define HAVE_HWFP (boot_cpu_data.hard_math)
extern void finit_soft_fpu(struct i387_soft_struct *soft); extern void finit_soft_fpu(struct i387_soft_struct *soft);
#else #else
# define HAVE_HWFP 1
static inline void finit_soft_fpu(struct i387_soft_struct *soft) {} static inline void finit_soft_fpu(struct i387_soft_struct *soft) {}
#endif #endif

View File

@ -89,9 +89,9 @@ struct cpuinfo_x86 {
char wp_works_ok; /* It doesn't on 386's */ char wp_works_ok; /* It doesn't on 386's */
/* Problems on some 486Dx4's and old 386's: */ /* Problems on some 486Dx4's and old 386's: */
char hard_math;
char rfu; char rfu;
char pad0; char pad0;
char pad1;
#else #else
/* Number of 4K pages in DTLB/ITLB combined(in pages): */ /* Number of 4K pages in DTLB/ITLB combined(in pages): */
int x86_tlbsize; int x86_tlbsize;
@ -164,6 +164,7 @@ extern const struct seq_operations cpuinfo_op;
#define cache_line_size() (boot_cpu_data.x86_cache_alignment) #define cache_line_size() (boot_cpu_data.x86_cache_alignment)
extern void cpu_detect(struct cpuinfo_x86 *c); extern void cpu_detect(struct cpuinfo_x86 *c);
extern void __cpuinit fpu_detect(struct cpuinfo_x86 *c);
extern void early_cpu_init(void); extern void early_cpu_init(void);
extern void identify_boot_cpu(void); extern void identify_boot_cpu(void);

View File

@ -28,7 +28,6 @@ void foo(void)
OFFSET(CPUINFO_x86_vendor, cpuinfo_x86, x86_vendor); OFFSET(CPUINFO_x86_vendor, cpuinfo_x86, x86_vendor);
OFFSET(CPUINFO_x86_model, cpuinfo_x86, x86_model); OFFSET(CPUINFO_x86_model, cpuinfo_x86, x86_model);
OFFSET(CPUINFO_x86_mask, cpuinfo_x86, x86_mask); OFFSET(CPUINFO_x86_mask, cpuinfo_x86, x86_mask);
OFFSET(CPUINFO_hard_math, cpuinfo_x86, hard_math);
OFFSET(CPUINFO_cpuid_level, cpuinfo_x86, cpuid_level); OFFSET(CPUINFO_cpuid_level, cpuinfo_x86, cpuid_level);
OFFSET(CPUINFO_x86_capability, cpuinfo_x86, x86_capability); OFFSET(CPUINFO_x86_capability, cpuinfo_x86, x86_capability);
OFFSET(CPUINFO_x86_vendor_id, cpuinfo_x86, x86_vendor_id); OFFSET(CPUINFO_x86_vendor_id, cpuinfo_x86, x86_vendor_id);

View File

@ -17,15 +17,6 @@
#include <asm/paravirt.h> #include <asm/paravirt.h>
#include <asm/alternative.h> #include <asm/alternative.h>
static int __init no_387(char *s)
{
boot_cpu_data.hard_math = 0;
write_cr0(X86_CR0_TS | X86_CR0_EM | X86_CR0_MP | read_cr0());
return 1;
}
__setup("no387", no_387);
static double __initdata x = 4195835.0; static double __initdata x = 4195835.0;
static double __initdata y = 3145727.0; static double __initdata y = 3145727.0;
@ -44,15 +35,6 @@ static void __init check_fpu(void)
{ {
s32 fdiv_bug; s32 fdiv_bug;
if (!boot_cpu_data.hard_math) {
#ifndef CONFIG_MATH_EMULATION
pr_emerg("No coprocessor found and no math emulation present\n");
pr_emerg("Giving up\n");
for (;;) ;
#endif
return;
}
kernel_fpu_begin(); kernel_fpu_begin();
/* /*
@ -107,5 +89,6 @@ void __init check_bugs(void)
* kernel_fpu_begin/end() in check_fpu() relies on the patched * kernel_fpu_begin/end() in check_fpu() relies on the patched
* alternative instructions. * alternative instructions.
*/ */
check_fpu(); if (cpu_has_fpu)
check_fpu();
} }

View File

@ -711,10 +711,9 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
return; return;
cpu_detect(c); cpu_detect(c);
get_cpu_vendor(c); get_cpu_vendor(c);
get_cpu_cap(c); get_cpu_cap(c);
fpu_detect(c);
if (this_cpu->c_early_init) if (this_cpu->c_early_init)
this_cpu->c_early_init(c); this_cpu->c_early_init(c);

View File

@ -333,7 +333,7 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c)
switch (dir0_lsn) { switch (dir0_lsn) {
case 0xd: /* either a 486SLC or DLC w/o DEVID */ case 0xd: /* either a 486SLC or DLC w/o DEVID */
dir0_msn = 0; dir0_msn = 0;
p = Cx486_name[(c->hard_math) ? 1 : 0]; p = Cx486_name[(cpu_has_fpu ? 1 : 0)];
break; break;
case 0xe: /* a 486S A step */ case 0xe: /* a 486S A step */

View File

@ -37,8 +37,8 @@ static void show_cpuinfo_misc(struct seq_file *m, struct cpuinfo_x86 *c)
static_cpu_has_bug(X86_BUG_FDIV) ? "yes" : "no", static_cpu_has_bug(X86_BUG_FDIV) ? "yes" : "no",
static_cpu_has_bug(X86_BUG_F00F) ? "yes" : "no", static_cpu_has_bug(X86_BUG_F00F) ? "yes" : "no",
static_cpu_has_bug(X86_BUG_COMA) ? "yes" : "no", static_cpu_has_bug(X86_BUG_COMA) ? "yes" : "no",
c->hard_math ? "yes" : "no", static_cpu_has(X86_FEATURE_FPU) ? "yes" : "no",
c->hard_math ? "yes" : "no", static_cpu_has(X86_FEATURE_FPU) ? "yes" : "no",
c->cpuid_level, c->cpuid_level,
c->wp_works_ok ? "yes" : "no"); c->wp_works_ok ? "yes" : "no");
} }

View File

@ -444,7 +444,6 @@ is486:
orl %ecx,%eax orl %ecx,%eax
movl %eax,%cr0 movl %eax,%cr0
call check_x87
lgdt early_gdt_descr lgdt early_gdt_descr
lidt idt_descr lidt idt_descr
ljmp $(__KERNEL_CS),$1f ljmp $(__KERNEL_CS),$1f
@ -467,26 +466,6 @@ is486:
pushl $0 # fake return address for unwinder pushl $0 # fake return address for unwinder
jmp *(initial_code) jmp *(initial_code)
/*
* We depend on ET to be correct. This checks for 287/387.
*/
check_x87:
movb $0,X86_HARD_MATH
clts
fninit
fstsw %ax
cmpb $0,%al
je 1f
movl %cr0,%eax /* no coprocessor: have to set bits */
xorl $4,%eax /* set EM */
movl %eax,%cr0
ret
ALIGN
1: movb $1,X86_HARD_MATH
.byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
ret
#include "verify_cpu.S" #include "verify_cpu.S"
/* /*

View File

@ -131,7 +131,7 @@ static void __cpuinit init_thread_xstate(void)
* xsave_init(). * xsave_init().
*/ */
if (!HAVE_HWFP) { if (!cpu_has_fpu) {
/* /*
* Disable xsave as we do not support it if i387 * Disable xsave as we do not support it if i387
* emulation is enabled. * emulation is enabled.
@ -158,6 +158,14 @@ void __cpuinit fpu_init(void)
unsigned long cr0; unsigned long cr0;
unsigned long cr4_mask = 0; unsigned long cr4_mask = 0;
#ifndef CONFIG_MATH_EMULATION
if (!cpu_has_fpu) {
pr_emerg("No FPU found and no math emulation present\n");
pr_emerg("Giving up\n");
for (;;)
asm volatile("hlt");
}
#endif
if (cpu_has_fxsr) if (cpu_has_fxsr)
cr4_mask |= X86_CR4_OSFXSR; cr4_mask |= X86_CR4_OSFXSR;
if (cpu_has_xmm) if (cpu_has_xmm)
@ -167,7 +175,7 @@ void __cpuinit fpu_init(void)
cr0 = read_cr0(); cr0 = read_cr0();
cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */ cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */
if (!HAVE_HWFP) if (!cpu_has_fpu)
cr0 |= X86_CR0_EM; cr0 |= X86_CR0_EM;
write_cr0(cr0); write_cr0(cr0);
@ -185,7 +193,7 @@ void __cpuinit fpu_init(void)
void fpu_finit(struct fpu *fpu) void fpu_finit(struct fpu *fpu)
{ {
if (!HAVE_HWFP) { if (!cpu_has_fpu) {
finit_soft_fpu(&fpu->state->soft); finit_soft_fpu(&fpu->state->soft);
return; return;
} }
@ -214,7 +222,7 @@ int init_fpu(struct task_struct *tsk)
int ret; int ret;
if (tsk_used_math(tsk)) { if (tsk_used_math(tsk)) {
if (HAVE_HWFP && tsk == current) if (cpu_has_fpu && tsk == current)
unlazy_fpu(tsk); unlazy_fpu(tsk);
tsk->thread.fpu.last_cpu = ~0; tsk->thread.fpu.last_cpu = ~0;
return 0; return 0;
@ -511,14 +519,13 @@ int fpregs_get(struct task_struct *target, const struct user_regset *regset,
if (ret) if (ret)
return ret; return ret;
if (!HAVE_HWFP) if (!static_cpu_has(X86_FEATURE_FPU))
return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf); return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);
if (!cpu_has_fxsr) { if (!cpu_has_fxsr)
return user_regset_copyout(&pos, &count, &kbuf, &ubuf, return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.fpu.state->fsave, 0, &target->thread.fpu.state->fsave, 0,
-1); -1);
}
sanitize_i387_state(target); sanitize_i387_state(target);
@ -545,13 +552,13 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset,
sanitize_i387_state(target); sanitize_i387_state(target);
if (!HAVE_HWFP) if (!static_cpu_has(X86_FEATURE_FPU))
return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf); return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);
if (!cpu_has_fxsr) { if (!cpu_has_fxsr)
return user_regset_copyin(&pos, &count, &kbuf, &ubuf, return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&target->thread.fpu.state->fsave, 0, -1); &target->thread.fpu.state->fsave, 0,
} -1);
if (pos > 0 || count < sizeof(env)) if (pos > 0 || count < sizeof(env))
convert_from_fxsr(&env, target); convert_from_fxsr(&env, target);
@ -592,3 +599,33 @@ int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(dump_fpu);
#endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */ #endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
static int __init no_387(char *s)
{
setup_clear_cpu_cap(X86_FEATURE_FPU);
return 1;
}
__setup("no387", no_387);
void __cpuinit fpu_detect(struct cpuinfo_x86 *c)
{
unsigned long cr0;
u16 fsw, fcw;
fsw = fcw = 0xffff;
cr0 = read_cr0();
cr0 &= ~(X86_CR0_TS | X86_CR0_EM);
write_cr0(cr0);
asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
: "+m" (fsw), "+m" (fcw));
if (fsw == 0 && (fcw & 0x103f) == 0x003f)
set_cpu_cap(c, X86_FEATURE_FPU);
else
clear_cpu_cap(c, X86_FEATURE_FPU);
/* The final cr0 value is set in fpu_init() */
}

View File

@ -243,7 +243,7 @@ int save_xstate_sig(void __user *buf, void __user *buf_fx, int size)
if (!access_ok(VERIFY_WRITE, buf, size)) if (!access_ok(VERIFY_WRITE, buf, size))
return -EACCES; return -EACCES;
if (!HAVE_HWFP) if (!static_cpu_has(X86_FEATURE_FPU))
return fpregs_soft_get(current, NULL, 0, return fpregs_soft_get(current, NULL, 0,
sizeof(struct user_i387_ia32_struct), NULL, sizeof(struct user_i387_ia32_struct), NULL,
(struct _fpstate_ia32 __user *) buf) ? -1 : 1; (struct _fpstate_ia32 __user *) buf) ? -1 : 1;
@ -350,11 +350,10 @@ int __restore_xstate_sig(void __user *buf, void __user *buf_fx, int size)
if (!used_math() && init_fpu(tsk)) if (!used_math() && init_fpu(tsk))
return -1; return -1;
if (!HAVE_HWFP) { if (!static_cpu_has(X86_FEATURE_FPU))
return fpregs_soft_set(current, NULL, return fpregs_soft_set(current, NULL,
0, sizeof(struct user_i387_ia32_struct), 0, sizeof(struct user_i387_ia32_struct),
NULL, buf) != 0; NULL, buf) != 0;
}
if (use_xsave()) { if (use_xsave()) {
struct _fpx_sw_bytes fx_sw_user; struct _fpx_sw_bytes fx_sw_user;

View File

@ -1410,7 +1410,7 @@ __init void lguest_init(void)
new_cpu_data.x86_capability[0] = cpuid_edx(1); new_cpu_data.x86_capability[0] = cpuid_edx(1);
/* Math is always hard! */ /* Math is always hard! */
new_cpu_data.hard_math = 1; set_cpu_cap(&new_cpu_data, X86_FEATURE_FPU);
/* We don't have features. We have puppies! Puppies! */ /* We don't have features. We have puppies! Puppies! */
#ifdef CONFIG_X86_MCE #ifdef CONFIG_X86_MCE

View File

@ -1557,7 +1557,7 @@ asmlinkage void __init xen_start_kernel(void)
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
/* set up basic CPUID stuff */ /* set up basic CPUID stuff */
cpu_detect(&new_cpu_data); cpu_detect(&new_cpu_data);
new_cpu_data.hard_math = 1; set_cpu_cap(&new_cpu_data, X86_FEATURE_FPU);
new_cpu_data.wp_works_ok = 1; new_cpu_data.wp_works_ok = 1;
new_cpu_data.x86_capability[0] = cpuid_edx(1); new_cpu_data.x86_capability[0] = cpuid_edx(1);
#endif #endif