2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* include/asm-x86_64/i387.h
|
|
|
|
*
|
|
|
|
* Copyright (C) 1994 Linus Torvalds
|
|
|
|
*
|
|
|
|
* Pentium III FXSR, SSE support
|
|
|
|
* General FPU state handling cleanups
|
|
|
|
* Gareth Hughes <gareth@valinux.com>, May 2000
|
|
|
|
* x86-64 work by Andi Kleen 2002
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __ASM_X86_64_I387_H
|
|
|
|
#define __ASM_X86_64_I387_H
|
|
|
|
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/sigcontext.h>
|
|
|
|
#include <asm/user.h>
|
|
|
|
#include <asm/thread_info.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
|
|
|
extern void fpu_init(void);
|
|
|
|
extern unsigned int mxcsr_feature_mask;
|
|
|
|
extern void mxcsr_feature_mask_init(void);
|
|
|
|
extern void init_fpu(struct task_struct *child);
|
|
|
|
extern int save_i387(struct _fpstate __user *buf);
|
2006-09-26 15:52:36 +07:00
|
|
|
extern asmlinkage void math_state_restore(void);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* FPU lazy state save handling...
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define unlazy_fpu(tsk) do { \
|
2006-01-12 16:05:38 +07:00
|
|
|
if (task_thread_info(tsk)->status & TS_USEDFPU) \
|
2006-09-26 15:52:36 +07:00
|
|
|
save_init_fpu(tsk); \
|
|
|
|
else \
|
|
|
|
tsk->fpu_counter = 0; \
|
2005-04-17 05:20:36 +07:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
/* Ignore delayed exceptions from user space */
|
|
|
|
static inline void tolerant_fwait(void)
|
|
|
|
{
|
|
|
|
asm volatile("1: fwait\n"
|
|
|
|
"2:\n"
|
|
|
|
" .section __ex_table,\"a\"\n"
|
|
|
|
" .align 8\n"
|
|
|
|
" .quad 1b,2b\n"
|
|
|
|
" .previous\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#define clear_fpu(tsk) do { \
|
2006-01-12 16:05:38 +07:00
|
|
|
if (task_thread_info(tsk)->status & TS_USEDFPU) { \
|
2005-04-17 05:20:36 +07:00
|
|
|
tolerant_fwait(); \
|
2006-01-12 16:05:38 +07:00
|
|
|
task_thread_info(tsk)->status &= ~TS_USEDFPU; \
|
2005-04-17 05:20:36 +07:00
|
|
|
stts(); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ptrace request handers...
|
|
|
|
*/
|
|
|
|
extern int get_fpregs(struct user_i387_struct __user *buf,
|
|
|
|
struct task_struct *tsk);
|
|
|
|
extern int set_fpregs(struct task_struct *tsk,
|
|
|
|
struct user_i387_struct __user *buf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* i387 state interaction
|
|
|
|
*/
|
|
|
|
#define get_fpu_mxcsr(t) ((t)->thread.i387.fxsave.mxcsr)
|
|
|
|
#define get_fpu_cwd(t) ((t)->thread.i387.fxsave.cwd)
|
|
|
|
#define get_fpu_fxsr_twd(t) ((t)->thread.i387.fxsave.twd)
|
|
|
|
#define get_fpu_swd(t) ((t)->thread.i387.fxsave.swd)
|
|
|
|
#define set_fpu_cwd(t,val) ((t)->thread.i387.fxsave.cwd = (val))
|
|
|
|
#define set_fpu_swd(t,val) ((t)->thread.i387.fxsave.swd = (val))
|
|
|
|
#define set_fpu_fxsr_twd(t,val) ((t)->thread.i387.fxsave.twd = (val))
|
|
|
|
|
2006-04-20 07:36:45 +07:00
|
|
|
#define X87_FSW_ES (1 << 7) /* Exception Summary */
|
|
|
|
|
|
|
|
/* AMD CPUs don't save/restore FDP/FIP/FOP unless an exception
|
|
|
|
is pending. Clear the x87 state here by setting it to fixed
|
|
|
|
values. The kernel data segment can be sometimes 0 and sometimes
|
|
|
|
new user value. Both should be ok.
|
|
|
|
Use the PDA as safe address because it should be already in L1. */
|
|
|
|
static inline void clear_fpu_state(struct i387_fxsave_struct *fx)
|
|
|
|
{
|
|
|
|
if (unlikely(fx->swd & X87_FSW_ES))
|
|
|
|
asm volatile("fnclex");
|
|
|
|
alternative_input(ASM_NOP8 ASM_NOP2,
|
|
|
|
" emms\n" /* clear stack tags */
|
|
|
|
" fildl %%gs:0", /* load to clear state */
|
|
|
|
X86_FEATURE_FXSAVE_LEAK);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static inline int restore_fpu_checking(struct i387_fxsave_struct *fx)
|
|
|
|
{
|
|
|
|
int err;
|
2006-01-12 04:43:36 +07:00
|
|
|
|
|
|
|
asm volatile("1: rex64/fxrstor (%[fx])\n\t"
|
2005-04-17 05:20:36 +07:00
|
|
|
"2:\n"
|
|
|
|
".section .fixup,\"ax\"\n"
|
|
|
|
"3: movl $-1,%[err]\n"
|
|
|
|
" jmp 2b\n"
|
|
|
|
".previous\n"
|
|
|
|
".section __ex_table,\"a\"\n"
|
|
|
|
" .align 8\n"
|
|
|
|
" .quad 1b,3b\n"
|
|
|
|
".previous"
|
|
|
|
: [err] "=r" (err)
|
2006-01-12 04:43:36 +07:00
|
|
|
#if 0 /* See comment in __fxsave_clear() below. */
|
|
|
|
: [fx] "r" (fx), "m" (*fx), "0" (0));
|
|
|
|
#else
|
|
|
|
: [fx] "cdaSDb" (fx), "m" (*fx), "0" (0));
|
|
|
|
#endif
|
2005-04-17 05:20:36 +07:00
|
|
|
if (unlikely(err))
|
|
|
|
init_fpu(current);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int save_i387_checking(struct i387_fxsave_struct __user *fx)
|
|
|
|
{
|
|
|
|
int err;
|
2006-01-12 04:43:36 +07:00
|
|
|
|
|
|
|
asm volatile("1: rex64/fxsave (%[fx])\n\t"
|
2005-04-17 05:20:36 +07:00
|
|
|
"2:\n"
|
|
|
|
".section .fixup,\"ax\"\n"
|
|
|
|
"3: movl $-1,%[err]\n"
|
|
|
|
" jmp 2b\n"
|
|
|
|
".previous\n"
|
|
|
|
".section __ex_table,\"a\"\n"
|
|
|
|
" .align 8\n"
|
|
|
|
" .quad 1b,3b\n"
|
|
|
|
".previous"
|
2006-01-12 04:43:36 +07:00
|
|
|
: [err] "=r" (err), "=m" (*fx)
|
|
|
|
#if 0 /* See comment in __fxsave_clear() below. */
|
|
|
|
: [fx] "r" (fx), "0" (0));
|
|
|
|
#else
|
|
|
|
: [fx] "cdaSDb" (fx), "0" (0));
|
|
|
|
#endif
|
2006-09-26 15:52:39 +07:00
|
|
|
if (unlikely(err) && __clear_user(fx, sizeof(struct i387_fxsave_struct)))
|
|
|
|
err = -EFAULT;
|
2006-04-20 07:36:45 +07:00
|
|
|
/* No need to clear here because the caller clears USED_MATH */
|
2005-04-17 05:20:36 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2006-01-12 04:43:36 +07:00
|
|
|
static inline void __fxsave_clear(struct task_struct *tsk)
|
|
|
|
{
|
|
|
|
/* Using "rex64; fxsave %0" is broken because, if the memory operand
|
|
|
|
uses any extended registers for addressing, a second REX prefix
|
|
|
|
will be generated (to the assembler, rex64 followed by semicolon
|
|
|
|
is a separate instruction), and hence the 64-bitness is lost. */
|
|
|
|
#if 0
|
|
|
|
/* Using "fxsaveq %0" would be the ideal choice, but is only supported
|
|
|
|
starting with gas 2.16. */
|
|
|
|
__asm__ __volatile__("fxsaveq %0"
|
|
|
|
: "=m" (tsk->thread.i387.fxsave));
|
|
|
|
#elif 0
|
|
|
|
/* Using, as a workaround, the properly prefixed form below isn't
|
|
|
|
accepted by any binutils version so far released, complaining that
|
|
|
|
the same type of prefix is used twice if an extended register is
|
|
|
|
needed for addressing (fix submitted to mainline 2005-11-21). */
|
|
|
|
__asm__ __volatile__("rex64/fxsave %0"
|
|
|
|
: "=m" (tsk->thread.i387.fxsave));
|
|
|
|
#else
|
|
|
|
/* This, however, we can work around by forcing the compiler to select
|
|
|
|
an addressing mode that doesn't require extended registers. */
|
|
|
|
__asm__ __volatile__("rex64/fxsave %P2(%1)"
|
|
|
|
: "=m" (tsk->thread.i387.fxsave)
|
|
|
|
: "cdaSDb" (tsk),
|
|
|
|
"i" (offsetof(__typeof__(*tsk),
|
|
|
|
thread.i387.fxsave)));
|
|
|
|
#endif
|
2006-04-20 07:36:45 +07:00
|
|
|
clear_fpu_state(&tsk->thread.i387.fxsave);
|
2006-01-12 04:43:36 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static inline void kernel_fpu_begin(void)
|
|
|
|
{
|
|
|
|
struct thread_info *me = current_thread_info();
|
|
|
|
preempt_disable();
|
2006-01-12 04:43:36 +07:00
|
|
|
if (me->status & TS_USEDFPU) {
|
|
|
|
__fxsave_clear(me->task);
|
2005-04-17 05:20:36 +07:00
|
|
|
me->status &= ~TS_USEDFPU;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
clts();
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void kernel_fpu_end(void)
|
|
|
|
{
|
|
|
|
stts();
|
|
|
|
preempt_enable();
|
|
|
|
}
|
|
|
|
|
2006-01-12 16:05:38 +07:00
|
|
|
static inline void save_init_fpu(struct task_struct *tsk)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-01-12 04:43:36 +07:00
|
|
|
__fxsave_clear(tsk);
|
2006-01-12 16:05:38 +07:00
|
|
|
task_thread_info(tsk)->status &= ~TS_USEDFPU;
|
2005-04-17 05:20:36 +07:00
|
|
|
stts();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This restores directly out of user space. Exceptions are handled.
|
|
|
|
*/
|
|
|
|
static inline int restore_i387(struct _fpstate __user *buf)
|
|
|
|
{
|
|
|
|
return restore_fpu_checking((__force struct i387_fxsave_struct *)buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* __ASM_X86_64_I387_H */
|