2005-04-16 15: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 ) ;
/*
* FPU lazy state save handling . . .
*/
# define unlazy_fpu(tsk) do { \
if ( ( tsk ) - > thread_info - > status & TS_USEDFPU ) \
save_init_fpu ( tsk ) ; \
} 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 { \
if ( ( tsk ) - > thread_info - > status & TS_USEDFPU ) { \
tolerant_fwait ( ) ; \
( tsk ) - > thread_info - > status & = ~ TS_USEDFPU ; \
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))
static inline int restore_fpu_checking ( struct i387_fxsave_struct * fx )
{
int err ;
2006-01-11 22:43:36 +01:00
asm volatile ( " 1: rex64/fxrstor (%[fx]) \n \t "
2005-04-16 15: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-11 22:43:36 +01: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-16 15: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-11 22:43:36 +01:00
asm volatile ( " 1: rex64/fxsave (%[fx]) \n \t "
2005-04-16 15: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-11 22:43:36 +01: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
2005-04-16 15:20:36 -07:00
if ( unlikely ( err ) )
__clear_user ( fx , sizeof ( struct i387_fxsave_struct ) ) ;
return err ;
}
2006-01-11 22:43:36 +01: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
__asm__ __volatile__ ( " fnclex " ) ;
}
2005-04-16 15:20:36 -07:00
static inline void kernel_fpu_begin ( void )
{
struct thread_info * me = current_thread_info ( ) ;
preempt_disable ( ) ;
2006-01-11 22:43:36 +01:00
if ( me - > status & TS_USEDFPU ) {
__fxsave_clear ( me - > task ) ;
2005-04-16 15:20:36 -07:00
me - > status & = ~ TS_USEDFPU ;
return ;
}
clts ( ) ;
}
static inline void kernel_fpu_end ( void )
{
stts ( ) ;
preempt_enable ( ) ;
}
static inline void save_init_fpu ( struct task_struct * tsk )
{
2006-01-11 22:43:36 +01:00
__fxsave_clear ( tsk ) ;
2005-04-16 15:20:36 -07:00
tsk - > thread_info - > status & = ~ TS_USEDFPU ;
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 */