2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 1994 Linus Torvalds
*
* Pentium III FXSR , SSE support
* General FPU state handling cleanups
* Gareth Hughes < gareth @ valinux . com > , May 2000
*/
# include <linux/sched.h>
2005-06-23 11:08:33 +04:00
# include <linux/module.h>
2008-01-30 15:31:50 +03:00
# include <linux/regset.h>
2005-04-17 02:20:36 +04:00
# include <asm/processor.h>
# include <asm/i387.h>
# include <asm/math_emu.h>
# include <asm/sigcontext.h>
# include <asm/user.h>
# include <asm/ptrace.h>
# include <asm/uaccess.h>
2008-01-30 15:31:50 +03:00
# ifdef CONFIG_X86_64
# include <asm/sigcontext32.h>
# include <asm/user32.h>
# else
# define save_i387_ia32 save_i387
# define restore_i387_ia32 restore_i387
# define _fpstate_ia32 _fpstate
# define user_i387_ia32_struct user_i387_struct
# define user32_fxsr_struct user_fxsr_struct
# endif
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_MATH_EMULATION
# define HAVE_HWFP (boot_cpu_data.hard_math)
# else
# define HAVE_HWFP 1
# endif
2008-01-30 15:31:50 +03:00
unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu ;
2005-04-17 02:20:36 +04:00
void mxcsr_feature_mask_init ( void )
{
unsigned long mask = 0 ;
clts ( ) ;
if ( cpu_has_fxsr ) {
2008-01-30 15:31:26 +03:00
memset ( & current - > thread . i387 . fxsave , 0 ,
sizeof ( struct i387_fxsave_struct ) ) ;
asm volatile ( " fxsave %0 " : : " m " ( current - > thread . i387 . fxsave ) ) ;
2005-04-17 02:20:36 +04:00
mask = current - > thread . i387 . fxsave . mxcsr_mask ;
2008-01-30 15:31:26 +03:00
if ( mask = = 0 )
mask = 0x0000ffbf ;
}
2005-04-17 02:20:36 +04:00
mxcsr_feature_mask & = mask ;
stts ( ) ;
}
2008-01-30 15:31:50 +03:00
# ifdef CONFIG_X86_64
/*
* Called at bootup to set up the initial FPU state that is later cloned
* into all processes .
*/
void __cpuinit fpu_init ( void )
{
unsigned long oldcr0 = read_cr0 ( ) ;
extern void __bad_fxsave_alignment ( void ) ;
if ( offsetof ( struct task_struct , thread . i387 . fxsave ) & 15 )
__bad_fxsave_alignment ( ) ;
set_in_cr4 ( X86_CR4_OSFXSR ) ;
set_in_cr4 ( X86_CR4_OSXMMEXCPT ) ;
write_cr0 ( oldcr0 & ~ ( ( 1UL < < 3 ) | ( 1UL < < 2 ) ) ) ; /* clear TS and EM */
mxcsr_feature_mask_init ( ) ;
/* clean state in init */
current_thread_info ( ) - > status = 0 ;
clear_used_math ( ) ;
}
# endif /* CONFIG_X86_64 */
2005-04-17 02:20:36 +04:00
/*
* The _current_ task is using the FPU for the first time
* so initialize it and set the mxcsr to its default
* value at reset if we support XMM instructions and then
* remeber the current task has used the FPU .
*/
void init_fpu ( struct task_struct * tsk )
{
2008-01-30 15:31:50 +03:00
if ( tsk_used_math ( tsk ) ) {
if ( tsk = = current )
unlazy_fpu ( tsk ) ;
return ;
}
2005-04-17 02:20:36 +04:00
if ( cpu_has_fxsr ) {
2008-01-30 15:31:26 +03:00
memset ( & tsk - > thread . i387 . fxsave , 0 ,
sizeof ( struct i387_fxsave_struct ) ) ;
2005-04-17 02:20:36 +04:00
tsk - > thread . i387 . fxsave . cwd = 0x37f ;
if ( cpu_has_xmm )
2008-01-30 15:31:50 +03:00
tsk - > thread . i387 . fxsave . mxcsr = MXCSR_DEFAULT ;
2005-04-17 02:20:36 +04:00
} else {
2008-01-30 15:31:26 +03:00
memset ( & tsk - > thread . i387 . fsave , 0 ,
sizeof ( struct i387_fsave_struct ) ) ;
2005-04-17 02:20:36 +04:00
tsk - > thread . i387 . fsave . cwd = 0xffff037fu ;
tsk - > thread . i387 . fsave . swd = 0xffff0000u ;
tsk - > thread . i387 . fsave . twd = 0xffffffffu ;
tsk - > thread . i387 . fsave . fos = 0xffff0000u ;
}
2008-01-30 15:31:50 +03:00
/*
* Only the device not available exception or ptrace can call init_fpu .
*/
2005-04-17 02:20:36 +04:00
set_stopped_child_used_math ( tsk ) ;
}
2008-01-30 15:31:50 +03:00
int fpregs_active ( struct task_struct * target , const struct user_regset * regset )
{
return tsk_used_math ( target ) ? regset - > n : 0 ;
}
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
int xfpregs_active ( struct task_struct * target , const struct user_regset * regset )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
return ( cpu_has_fxsr & & tsk_used_math ( target ) ) ? regset - > n : 0 ;
}
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
int xfpregs_get ( struct task_struct * target , const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
{
if ( ! cpu_has_fxsr )
return - ENODEV ;
unlazy_fpu ( target ) ;
return user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
& target - > thread . i387 . fxsave , 0 , - 1 ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
int xfpregs_set ( struct task_struct * target , const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
{
int ret ;
if ( ! cpu_has_fxsr )
return - ENODEV ;
unlazy_fpu ( target ) ;
set_stopped_child_used_math ( target ) ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
& target - > thread . i387 . fxsave , 0 , - 1 ) ;
/*
* mxcsr reserved bits must be masked to zero for security reasons .
*/
target - > thread . i387 . fxsave . mxcsr & = mxcsr_feature_mask ;
return ret ;
}
# if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
2005-04-17 02:20:36 +04:00
/*
* FPU tag word conversions .
*/
2008-01-30 15:31:26 +03:00
static inline unsigned short twd_i387_to_fxsr ( unsigned short twd )
2005-04-17 02:20:36 +04:00
{
unsigned int tmp ; /* to avoid 16 bit prefixes in the code */
2008-01-30 15:31:26 +03:00
2005-04-17 02:20:36 +04:00
/* Transform each pair of bits into 01 (valid) or 00 (empty) */
2008-01-30 15:31:26 +03:00
tmp = ~ twd ;
2008-01-30 15:31:50 +03:00
tmp = ( tmp | ( tmp > > 1 ) ) & 0x5555 ; /* 0V0V0V0V0V0V0V0V */
2008-01-30 15:31:26 +03:00
/* and move the valid bits to the lower byte. */
tmp = ( tmp | ( tmp > > 1 ) ) & 0x3333 ; /* 00VV00VV00VV00VV */
tmp = ( tmp | ( tmp > > 2 ) ) & 0x0f0f ; /* 0000VVVV0000VVVV */
tmp = ( tmp | ( tmp > > 4 ) ) & 0x00ff ; /* 00000000VVVVVVVV */
return tmp ;
2005-04-17 02:20:36 +04:00
}
# define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16);
2008-01-30 15:31:50 +03:00
# define FP_EXP_TAG_VALID 0
# define FP_EXP_TAG_ZERO 1
# define FP_EXP_TAG_SPECIAL 2
# define FP_EXP_TAG_EMPTY 3
static inline u32 twd_fxsr_to_i387 ( struct i387_fxsave_struct * fxsave )
{
struct _fpxreg * st ;
u32 tos = ( fxsave - > swd > > 11 ) & 7 ;
u32 twd = ( unsigned long ) fxsave - > twd ;
u32 tag ;
u32 ret = 0xffff0000u ;
int i ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
for ( i = 0 ; i < 8 ; i + + , twd > > = 1 ) {
2008-01-30 15:31:26 +03:00
if ( twd & 0x1 ) {
st = FPREG_ADDR ( fxsave , ( i - tos ) & 7 ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:26 +03:00
switch ( st - > exponent & 0x7fff ) {
2005-04-17 02:20:36 +04:00
case 0x7fff :
2008-01-30 15:31:50 +03:00
tag = FP_EXP_TAG_SPECIAL ;
2005-04-17 02:20:36 +04:00
break ;
case 0x0000 :
2008-01-30 15:31:26 +03:00
if ( ! st - > significand [ 0 ] & &
! st - > significand [ 1 ] & &
! st - > significand [ 2 ] & &
2008-01-30 15:31:50 +03:00
! st - > significand [ 3 ] )
tag = FP_EXP_TAG_ZERO ;
else
tag = FP_EXP_TAG_SPECIAL ;
2005-04-17 02:20:36 +04:00
break ;
default :
2008-01-30 15:31:50 +03:00
if ( st - > significand [ 3 ] & 0x8000 )
tag = FP_EXP_TAG_VALID ;
else
tag = FP_EXP_TAG_SPECIAL ;
2005-04-17 02:20:36 +04:00
break ;
}
} else {
2008-01-30 15:31:50 +03:00
tag = FP_EXP_TAG_EMPTY ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
ret | = tag < < ( 2 * i ) ;
2005-04-17 02:20:36 +04:00
}
return ret ;
}
/*
2008-01-30 15:31:50 +03:00
* FXSR floating point environment conversions .
2005-04-17 02:20:36 +04:00
*/
2008-01-30 15:31:50 +03:00
static void convert_from_fxsr ( struct user_i387_ia32_struct * env ,
struct task_struct * tsk )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
struct i387_fxsave_struct * fxsave = & tsk - > thread . i387 . fxsave ;
struct _fpreg * to = ( struct _fpreg * ) & env - > st_space [ 0 ] ;
struct _fpxreg * from = ( struct _fpxreg * ) & fxsave - > st_space [ 0 ] ;
int i ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
env - > cwd = fxsave - > cwd | 0xffff0000u ;
env - > swd = fxsave - > swd | 0xffff0000u ;
env - > twd = twd_fxsr_to_i387 ( fxsave ) ;
# ifdef CONFIG_X86_64
env - > fip = fxsave - > rip ;
env - > foo = fxsave - > rdp ;
if ( tsk = = current ) {
/*
* should be actually ds / cs at fpu exception time , but
* that information is not available in 64 bit mode .
*/
asm ( " mov %%ds,%0 " : " =r " ( env - > fos ) ) ;
asm ( " mov %%cs,%0 " : " =r " ( env - > fcs ) ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-01-30 15:31:50 +03:00
struct pt_regs * regs = task_pt_regs ( tsk ) ;
env - > fos = 0xffff0000 | tsk - > thread . ds ;
env - > fcs = regs - > cs ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
# else
env - > fip = fxsave - > fip ;
env - > fcs = fxsave - > fcs ;
env - > foo = fxsave - > foo ;
env - > fos = fxsave - > fos ;
# endif
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
for ( i = 0 ; i < 8 ; + + i )
memcpy ( & to [ i ] , & from [ i ] , sizeof ( to [ 0 ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
static void convert_to_fxsr ( struct task_struct * tsk ,
const struct user_i387_ia32_struct * env )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
struct i387_fxsave_struct * fxsave = & tsk - > thread . i387 . fxsave ;
struct _fpreg * from = ( struct _fpreg * ) & env - > st_space [ 0 ] ;
struct _fpxreg * to = ( struct _fpxreg * ) & fxsave - > st_space [ 0 ] ;
int i ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
fxsave - > cwd = env - > cwd ;
fxsave - > swd = env - > swd ;
fxsave - > twd = twd_i387_to_fxsr ( env - > twd ) ;
fxsave - > fop = ( u16 ) ( ( u32 ) env - > fcs > > 16 ) ;
# ifdef CONFIG_X86_64
fxsave - > rip = env - > fip ;
fxsave - > rdp = env - > foo ;
/* cs and ds ignored */
# else
fxsave - > fip = env - > fip ;
fxsave - > fcs = ( env - > fcs & 0xffff ) ;
fxsave - > foo = env - > foo ;
fxsave - > fos = env - > fos ;
# endif
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
for ( i = 0 ; i < 8 ; + + i )
memcpy ( & to [ i ] , & from [ i ] , sizeof ( from [ 0 ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
int fpregs_get ( struct task_struct * target , const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
struct user_i387_ia32_struct env ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
if ( ! HAVE_HWFP )
return fpregs_soft_get ( target , regset , pos , count , kbuf , ubuf ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
unlazy_fpu ( target ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
if ( ! cpu_has_fxsr )
return user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
& target - > thread . i387 . fsave , 0 , - 1 ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
if ( kbuf & & pos = = 0 & & count = = sizeof ( env ) ) {
convert_from_fxsr ( kbuf , target ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
convert_from_fxsr ( & env , target ) ;
return user_regset_copyout ( & pos , & count , & kbuf , & ubuf , & env , 0 , - 1 ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
int fpregs_set ( struct task_struct * target , const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
struct user_i387_ia32_struct env ;
int ret ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
if ( ! HAVE_HWFP )
return fpregs_soft_set ( target , regset , pos , count , kbuf , ubuf ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
unlazy_fpu ( target ) ;
set_stopped_child_used_math ( target ) ;
if ( ! cpu_has_fxsr )
return user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
& target - > thread . i387 . fsave , 0 , - 1 ) ;
if ( pos > 0 | | count < sizeof ( env ) )
convert_from_fxsr ( & env , target ) ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf , & env , 0 , - 1 ) ;
if ( ! ret )
convert_to_fxsr ( target , & env ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
/*
* Signal frame handlers .
*/
2008-01-30 15:31:50 +03:00
static inline int save_i387_fsave ( struct _fpstate_ia32 __user * buf )
2005-04-17 02:20:36 +04:00
{
struct task_struct * tsk = current ;
2008-01-30 15:31:26 +03:00
unlazy_fpu ( tsk ) ;
2005-04-17 02:20:36 +04:00
tsk - > thread . i387 . fsave . status = tsk - > thread . i387 . fsave . swd ;
2008-01-30 15:31:26 +03:00
if ( __copy_to_user ( buf , & tsk - > thread . i387 . fsave ,
sizeof ( struct i387_fsave_struct ) ) )
2005-04-17 02:20:36 +04:00
return - 1 ;
return 1 ;
}
2008-01-30 15:31:50 +03:00
static int save_i387_fxsave ( struct _fpstate_ia32 __user * buf )
2005-04-17 02:20:36 +04:00
{
struct task_struct * tsk = current ;
2008-01-30 15:31:50 +03:00
struct user_i387_ia32_struct env ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
2008-01-30 15:31:26 +03:00
unlazy_fpu ( tsk ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
convert_from_fxsr ( & env , tsk ) ;
if ( __copy_to_user ( buf , & env , sizeof ( env ) ) )
2005-04-17 02:20:36 +04:00
return - 1 ;
2008-01-30 15:31:26 +03:00
err | = __put_user ( tsk - > thread . i387 . fxsave . swd , & buf - > status ) ;
err | = __put_user ( X86_FXSR_MAGIC , & buf - > magic ) ;
if ( err )
2005-04-17 02:20:36 +04:00
return - 1 ;
2008-01-30 15:31:26 +03:00
if ( __copy_to_user ( & buf - > _fxsr_env [ 0 ] , & tsk - > thread . i387 . fxsave ,
sizeof ( struct i387_fxsave_struct ) ) )
2005-04-17 02:20:36 +04:00
return - 1 ;
return 1 ;
}
2008-01-30 15:31:50 +03:00
int save_i387_ia32 ( struct _fpstate_ia32 __user * buf )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:26 +03:00
if ( ! used_math ( ) )
2005-04-17 02:20:36 +04:00
return 0 ;
/* This will cause a "finit" to be triggered by the next
* attempted FPU operation by the ' current ' process .
*/
clear_used_math ( ) ;
2008-01-30 15:31:26 +03:00
if ( HAVE_HWFP ) {
if ( cpu_has_fxsr ) {
return save_i387_fxsave ( buf ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-01-30 15:31:26 +03:00
return save_i387_fsave ( buf ) ;
2005-04-17 02:20:36 +04:00
}
} else {
2008-01-30 15:31:50 +03:00
return fpregs_soft_get ( current , NULL ,
0 , sizeof ( struct user_i387_ia32_struct ) ,
NULL , buf ) ? - 1 : 1 ;
2005-04-17 02:20:36 +04:00
}
}
2008-01-30 15:31:50 +03:00
static inline int restore_i387_fsave ( struct _fpstate_ia32 __user * buf )
2005-04-17 02:20:36 +04:00
{
struct task_struct * tsk = current ;
2008-01-30 15:31:26 +03:00
clear_fpu ( tsk ) ;
return __copy_from_user ( & tsk - > thread . i387 . fsave , buf ,
sizeof ( struct i387_fsave_struct ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
static int restore_i387_fxsave ( struct _fpstate_ia32 __user * buf )
2005-04-17 02:20:36 +04:00
{
int err ;
struct task_struct * tsk = current ;
2008-01-30 15:31:50 +03:00
struct user_i387_ia32_struct env ;
2008-01-30 15:31:26 +03:00
clear_fpu ( tsk ) ;
err = __copy_from_user ( & tsk - > thread . i387 . fxsave , & buf - > _fxsr_env [ 0 ] ,
sizeof ( struct i387_fxsave_struct ) ) ;
2005-04-17 02:20:36 +04:00
/* mxcsr reserved bits must be masked to zero for security reasons */
tsk - > thread . i387 . fxsave . mxcsr & = mxcsr_feature_mask ;
2008-01-30 15:31:50 +03:00
if ( err | | __copy_from_user ( & env , buf , sizeof ( env ) ) )
return 1 ;
convert_to_fxsr ( tsk , & env ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
int restore_i387_ia32 ( struct _fpstate_ia32 __user * buf )
2005-04-17 02:20:36 +04:00
{
int err ;
2008-01-30 15:31:26 +03:00
if ( HAVE_HWFP ) {
if ( cpu_has_fxsr ) {
err = restore_i387_fxsave ( buf ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-01-30 15:31:26 +03:00
err = restore_i387_fsave ( buf ) ;
2005-04-17 02:20:36 +04:00
}
} else {
2008-01-30 15:31:50 +03:00
err = fpregs_soft_set ( current , NULL ,
0 , sizeof ( struct user_i387_ia32_struct ) ,
NULL , buf ) ! = 0 ;
2005-04-17 02:20:36 +04:00
}
set_used_math ( ) ;
return err ;
}
2008-01-30 15:31:50 +03:00
# endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:50 +03:00
# ifdef CONFIG_X86_64
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:26 +03:00
int get_fpregs ( struct user_i387_struct __user * buf , struct task_struct * tsk )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
return xfpregs_get ( tsk , NULL , 0 , sizeof ( * buf ) , NULL , buf ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
int set_fpregs ( struct task_struct * tsk , struct user_i387_struct __user * buf )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
return xfpregs_set ( tsk , NULL , 0 , sizeof ( * buf ) , NULL , buf ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
# else
int get_fpregs ( struct user_i387_struct __user * buf , struct task_struct * tsk )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
return fpregs_get ( tsk , NULL , 0 , sizeof ( * buf ) , NULL , buf ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:26 +03:00
int set_fpregs ( struct task_struct * tsk , struct user_i387_struct __user * buf )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
return fpregs_set ( tsk , NULL , 0 , sizeof ( * buf ) , NULL , buf ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:26 +03:00
int get_fpxregs ( struct user_fxsr_struct __user * buf , struct task_struct * tsk )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
return xfpregs_get ( tsk , NULL , 0 , sizeof ( * buf ) , NULL , buf ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:26 +03:00
int set_fpxregs ( struct task_struct * tsk , struct user_fxsr_struct __user * buf )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:50 +03:00
return xfpregs_get ( tsk , NULL , 0 , sizeof ( * buf ) , NULL , buf ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:50 +03:00
# endif
2005-04-17 02:20:36 +04:00
/*
* FPU state for core dumps .
*/
2008-01-30 15:31:26 +03:00
static inline void copy_fpu_fsave ( struct task_struct * tsk ,
struct user_i387_struct * fpu )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:26 +03:00
memcpy ( fpu , & tsk - > thread . i387 . fsave ,
sizeof ( struct user_i387_struct ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:26 +03:00
static inline void copy_fpu_fxsave ( struct task_struct * tsk ,
struct user_i387_struct * fpu )
2005-04-17 02:20:36 +04:00
{
unsigned short * to ;
unsigned short * from ;
int i ;
2008-01-30 15:31:26 +03:00
memcpy ( fpu , & tsk - > thread . i387 . fxsave , 7 * sizeof ( long ) ) ;
2005-04-17 02:20:36 +04:00
to = ( unsigned short * ) & fpu - > st_space [ 0 ] ;
from = ( unsigned short * ) & tsk - > thread . i387 . fxsave . st_space [ 0 ] ;
2008-01-30 15:31:26 +03:00
for ( i = 0 ; i < 8 ; i + + , to + = 5 , from + = 8 )
memcpy ( to , from , 5 * sizeof ( unsigned short ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:26 +03:00
int dump_fpu ( struct pt_regs * regs , struct user_i387_struct * fpu )
2005-04-17 02:20:36 +04:00
{
int fpvalid ;
struct task_struct * tsk = current ;
fpvalid = ! ! used_math ( ) ;
2008-01-30 15:31:26 +03:00
if ( fpvalid ) {
unlazy_fpu ( tsk ) ;
if ( cpu_has_fxsr ) {
copy_fpu_fxsave ( tsk , fpu ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-01-30 15:31:26 +03:00
copy_fpu_fsave ( tsk , fpu ) ;
2005-04-17 02:20:36 +04:00
}
}
return fpvalid ;
}
2005-06-23 11:08:33 +04:00
EXPORT_SYMBOL ( dump_fpu ) ;
2005-04-17 02:20:36 +04:00
int dump_task_fpu ( struct task_struct * tsk , struct user_i387_struct * fpu )
{
int fpvalid = ! ! tsk_used_math ( tsk ) ;
if ( fpvalid ) {
if ( tsk = = current )
unlazy_fpu ( tsk ) ;
if ( cpu_has_fxsr )
copy_fpu_fxsave ( tsk , fpu ) ;
else
copy_fpu_fsave ( tsk , fpu ) ;
}
return fpvalid ;
}
2008-01-30 15:31:26 +03:00
int dump_task_extended_fpu ( struct task_struct * tsk ,
2008-01-30 15:31:50 +03:00
struct user32_fxsr_struct * fpu )
2005-04-17 02:20:36 +04:00
{
int fpvalid = tsk_used_math ( tsk ) & & cpu_has_fxsr ;
if ( fpvalid ) {
if ( tsk = = current )
unlazy_fpu ( tsk ) ;
memcpy ( fpu , & tsk - > thread . i387 . fxsave , sizeof ( * fpu ) ) ;
}
return fpvalid ;
}