2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) 1994 Linus Torvalds
*
* Pentium III FXSR , SSE support
* General FPU state handling cleanups
* Gareth Hughes < gareth @ valinux . com > , May 2000
*/
2005-06-23 00:08:33 -07:00
# include <linux/module.h>
2008-01-30 13:31:50 +01:00
# include <linux/regset.h>
2008-03-05 15:37:32 +01:00
# include <linux/sched.h>
# include <asm/sigcontext.h>
2005-04-16 15:20:36 -07:00
# include <asm/processor.h>
# include <asm/math_emu.h>
# include <asm/uaccess.h>
2008-03-05 15:37:32 +01:00
# include <asm/ptrace.h>
# include <asm/i387.h>
# include <asm/user.h>
2005-04-16 15:20:36 -07:00
2008-01-30 13:31:50 +01:00
# ifdef CONFIG_X86_64
2008-03-05 15:37:32 +01:00
# include <asm / sigcontext32.h>
# include <asm / user32.h>
2008-01-30 13:31:50 +01:00
# else
2008-07-29 10:29:22 -07:00
# define save_i387_xstate_ia32 save_i387_xstate
# define restore_i387_xstate_ia32 restore_i387_xstate
2008-03-05 15:37:32 +01:00
# define _fpstate_ia32 _fpstate
2008-07-29 10:29:22 -07:00
# define _xstate_ia32 _xstate
2008-07-29 10:29:21 -07:00
# define sig_xstate_ia32_size sig_xstate_size
2008-07-29 10:29:25 -07:00
# define fx_sw_reserved_ia32 fx_sw_reserved
2008-03-05 15:37:32 +01:00
# define user_i387_ia32_struct user_i387_struct
# define user32_fxsr_struct user_fxsr_struct
2008-01-30 13:31:50 +01:00
# endif
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_MATH_EMULATION
2008-03-05 15:37:32 +01:00
# define HAVE_HWFP (boot_cpu_data.hard_math)
2005-04-16 15:20:36 -07:00
# else
2008-03-05 15:37:32 +01:00
# define HAVE_HWFP 1
2005-04-16 15:20:36 -07:00
# endif
2008-03-05 15:37:32 +01:00
static unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu ;
2008-03-10 15:28:04 -07:00
unsigned int xstate_size ;
2008-07-29 10:29:21 -07:00
unsigned int sig_xstate_ia32_size = sizeof ( struct _fpstate_ia32 ) ;
2008-03-10 15:28:04 -07:00
static struct i387_fxsave_struct fx_scratch __cpuinitdata ;
2005-04-16 15:20:36 -07:00
2008-03-10 15:28:04 -07:00
void __cpuinit mxcsr_feature_mask_init ( void )
2005-04-16 15:20:36 -07:00
{
unsigned long mask = 0 ;
2008-03-05 15:37:32 +01:00
2005-04-16 15:20:36 -07:00
clts ( ) ;
if ( cpu_has_fxsr ) {
2008-03-10 15:28:04 -07:00
memset ( & fx_scratch , 0 , sizeof ( struct i387_fxsave_struct ) ) ;
asm volatile ( " fxsave %0 " : : " m " ( fx_scratch ) ) ;
mask = fx_scratch . mxcsr_mask ;
2008-01-30 13:31:26 +01:00
if ( mask = = 0 )
mask = 0x0000ffbf ;
}
2005-04-16 15:20:36 -07:00
mxcsr_feature_mask & = mask ;
stts ( ) ;
}
2008-03-10 15:28:04 -07:00
void __init init_thread_xstate ( void )
{
2008-05-23 16:26:37 -07:00
if ( ! HAVE_HWFP ) {
xstate_size = sizeof ( struct i387_soft_struct ) ;
return ;
}
2008-07-29 10:29:19 -07:00
if ( cpu_has_xsave ) {
xsave_cntxt_init ( ) ;
return ;
}
2008-03-10 15:28:04 -07:00
if ( cpu_has_fxsr )
xstate_size = sizeof ( struct i387_fxsave_struct ) ;
# ifdef CONFIG_X86_32
else
xstate_size = sizeof ( struct i387_fsave_struct ) ;
# endif
}
2008-01-30 13:31:50 +01: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 ( ) ;
2008-03-05 15:37:32 +01:00
2008-01-30 13:31:50 +01:00
set_in_cr4 ( X86_CR4_OSFXSR ) ;
set_in_cr4 ( X86_CR4_OSXMMEXCPT ) ;
2008-03-05 15:37:32 +01:00
write_cr0 ( oldcr0 & ~ ( X86_CR0_TS | X86_CR0_EM ) ) ; /* clear TS and EM */
2008-01-30 13:31:50 +01:00
2008-07-29 10:29:19 -07:00
/*
* Boot processor to setup the FP and extended state context info .
*/
if ( ! smp_processor_id ( ) )
init_thread_xstate ( ) ;
xsave_init ( ) ;
2008-01-30 13:31:50 +01:00
mxcsr_feature_mask_init ( ) ;
/* clean state in init */
2008-07-29 10:29:20 -07:00
if ( cpu_has_xsave )
current_thread_info ( ) - > status = TS_XSAVE ;
else
current_thread_info ( ) - > status = 0 ;
2008-01-30 13:31:50 +01:00
clear_used_math ( ) ;
}
# endif /* CONFIG_X86_64 */
2005-04-16 15:20:36 -07: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 .
*/
2008-03-10 15:28:05 -07:00
int init_fpu ( struct task_struct * tsk )
2005-04-16 15:20:36 -07:00
{
2008-01-30 13:31:50 +01:00
if ( tsk_used_math ( tsk ) ) {
2008-05-23 16:26:37 -07:00
if ( HAVE_HWFP & & tsk = = current )
2008-01-30 13:31:50 +01:00
unlazy_fpu ( tsk ) ;
2008-03-10 15:28:05 -07:00
return 0 ;
}
/*
* Memory allocation at the first usage of the FPU and other state .
*/
if ( ! tsk - > thread . xstate ) {
tsk - > thread . xstate = kmem_cache_alloc ( task_xstate_cachep ,
GFP_KERNEL ) ;
if ( ! tsk - > thread . xstate )
return - ENOMEM ;
2008-01-30 13:31:50 +01:00
}
2008-05-23 16:26:37 -07:00
# ifdef CONFIG_X86_32
if ( ! HAVE_HWFP ) {
memset ( tsk - > thread . xstate , 0 , xstate_size ) ;
finit ( ) ;
set_stopped_child_used_math ( tsk ) ;
return 0 ;
}
# endif
2005-04-16 15:20:36 -07:00
if ( cpu_has_fxsr ) {
2008-03-10 15:28:04 -07:00
struct i387_fxsave_struct * fx = & tsk - > thread . xstate - > fxsave ;
memset ( fx , 0 , xstate_size ) ;
fx - > cwd = 0x37f ;
2005-04-16 15:20:36 -07:00
if ( cpu_has_xmm )
2008-03-10 15:28:04 -07:00
fx - > mxcsr = MXCSR_DEFAULT ;
2005-04-16 15:20:36 -07:00
} else {
2008-03-10 15:28:04 -07:00
struct i387_fsave_struct * fp = & tsk - > thread . xstate - > fsave ;
memset ( fp , 0 , xstate_size ) ;
fp - > cwd = 0xffff037fu ;
fp - > swd = 0xffff0000u ;
fp - > twd = 0xffffffffu ;
fp - > fos = 0xffff0000u ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:31:50 +01:00
/*
* Only the device not available exception or ptrace can call init_fpu .
*/
2005-04-16 15:20:36 -07:00
set_stopped_child_used_math ( tsk ) ;
2008-03-10 15:28:05 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:31:50 +01:00
int fpregs_active ( struct task_struct * target , const struct user_regset * regset )
{
return tsk_used_math ( target ) ? regset - > n : 0 ;
}
2005-04-16 15:20:36 -07:00
2008-01-30 13:31:50 +01:00
int xfpregs_active ( struct task_struct * target , const struct user_regset * regset )
2005-04-16 15:20:36 -07:00
{
2008-01-30 13:31:50 +01:00
return ( cpu_has_fxsr & & tsk_used_math ( target ) ) ? regset - > n : 0 ;
}
2005-04-16 15:20:36 -07:00
2008-01-30 13:31:50 +01:00
int xfpregs_get ( struct task_struct * target , const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
{
2008-03-10 15:28:05 -07:00
int ret ;
2008-01-30 13:31:50 +01:00
if ( ! cpu_has_fxsr )
return - ENODEV ;
2008-03-10 15:28:05 -07:00
ret = init_fpu ( target ) ;
if ( ret )
return ret ;
2008-01-30 13:31:50 +01:00
return user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
2008-03-10 15:28:04 -07:00
& target - > thread . xstate - > fxsave , 0 , - 1 ) ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:31:50 +01: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 ;
2008-03-10 15:28:05 -07:00
ret = init_fpu ( target ) ;
if ( ret )
return ret ;
2008-01-30 13:31:50 +01:00
set_stopped_child_used_math ( target ) ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
2008-03-10 15:28:04 -07:00
& target - > thread . xstate - > fxsave , 0 , - 1 ) ;
2008-01-30 13:31:50 +01:00
/*
* mxcsr reserved bits must be masked to zero for security reasons .
*/
2008-03-10 15:28:04 -07:00
target - > thread . xstate - > fxsave . mxcsr & = mxcsr_feature_mask ;
2008-01-30 13:31:50 +01:00
2008-07-29 10:29:26 -07:00
/*
* update the header bits in the xsave header , indicating the
* presence of FP and SSE state .
*/
if ( cpu_has_xsave )
target - > thread . xstate - > xsave . xsave_hdr . xstate_bv | = XSTATE_FPSSE ;
2008-01-30 13:31:50 +01:00
return ret ;
}
# if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
2005-04-16 15:20:36 -07:00
/*
* FPU tag word conversions .
*/
2008-01-30 13:31:26 +01:00
static inline unsigned short twd_i387_to_fxsr ( unsigned short twd )
2005-04-16 15:20:36 -07:00
{
unsigned int tmp ; /* to avoid 16 bit prefixes in the code */
2008-01-30 13:31:26 +01:00
2005-04-16 15:20:36 -07:00
/* Transform each pair of bits into 01 (valid) or 00 (empty) */
2008-01-30 13:31:26 +01:00
tmp = ~ twd ;
2008-01-30 13:31:50 +01:00
tmp = ( tmp | ( tmp > > 1 ) ) & 0x5555 ; /* 0V0V0V0V0V0V0V0V */
2008-01-30 13:31:26 +01: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 */
2008-03-05 15:37:32 +01:00
2008-01-30 13:31:26 +01:00
return tmp ;
2005-04-16 15:20:36 -07:00
}
# define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16);
2008-01-30 13:31:50 +01: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-16 15:20:36 -07:00
2008-01-30 13:31:50 +01:00
for ( i = 0 ; i < 8 ; i + + , twd > > = 1 ) {
2008-01-30 13:31:26 +01:00
if ( twd & 0x1 ) {
st = FPREG_ADDR ( fxsave , ( i - tos ) & 7 ) ;
2005-04-16 15:20:36 -07:00
2008-01-30 13:31:26 +01:00
switch ( st - > exponent & 0x7fff ) {
2005-04-16 15:20:36 -07:00
case 0x7fff :
2008-01-30 13:31:50 +01:00
tag = FP_EXP_TAG_SPECIAL ;
2005-04-16 15:20:36 -07:00
break ;
case 0x0000 :
2008-01-30 13:31:26 +01:00
if ( ! st - > significand [ 0 ] & &
! st - > significand [ 1 ] & &
! st - > significand [ 2 ] & &
2008-01-30 13:31:50 +01:00
! st - > significand [ 3 ] )
tag = FP_EXP_TAG_ZERO ;
else
tag = FP_EXP_TAG_SPECIAL ;
2005-04-16 15:20:36 -07:00
break ;
default :
2008-01-30 13:31:50 +01:00
if ( st - > significand [ 3 ] & 0x8000 )
tag = FP_EXP_TAG_VALID ;
else
tag = FP_EXP_TAG_SPECIAL ;
2005-04-16 15:20:36 -07:00
break ;
}
} else {
2008-01-30 13:31:50 +01:00
tag = FP_EXP_TAG_EMPTY ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:31:50 +01:00
ret | = tag < < ( 2 * i ) ;
2005-04-16 15:20:36 -07:00
}
return ret ;
}
/*
2008-01-30 13:31:50 +01:00
* FXSR floating point environment conversions .
2005-04-16 15:20:36 -07:00
*/
2008-03-05 15:37:32 +01:00
static void
convert_from_fxsr ( struct user_i387_ia32_struct * env , struct task_struct * tsk )
2005-04-16 15:20:36 -07:00
{
2008-03-10 15:28:04 -07:00
struct i387_fxsave_struct * fxsave = & tsk - > thread . xstate - > fxsave ;
2008-01-30 13:31:50 +01:00
struct _fpreg * to = ( struct _fpreg * ) & env - > st_space [ 0 ] ;
struct _fpxreg * from = ( struct _fpxreg * ) & fxsave - > st_space [ 0 ] ;
int i ;
2005-04-16 15:20:36 -07:00
2008-01-30 13:31:50 +01: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 .
*/
2008-03-05 15:37:32 +01:00
asm ( " mov %%ds, %[fos] " : [ fos ] " =r " ( env - > fos ) ) ;
asm ( " mov %%cs, %[fcs] " : [ fcs ] " =r " ( env - > fcs ) ) ;
2005-04-16 15:20:36 -07:00
} else {
2008-01-30 13:31:50 +01:00
struct pt_regs * regs = task_pt_regs ( tsk ) ;
2008-03-05 15:37:32 +01:00
2008-01-30 13:31:50 +01:00
env - > fos = 0xffff0000 | tsk - > thread . ds ;
env - > fcs = regs - > cs ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:31:50 +01:00
# else
env - > fip = fxsave - > fip ;
2008-03-05 08:35:14 +00:00
env - > fcs = ( u16 ) fxsave - > fcs | ( ( u32 ) fxsave - > fop < < 16 ) ;
2008-01-30 13:31:50 +01:00
env - > foo = fxsave - > foo ;
env - > fos = fxsave - > fos ;
# endif
2005-04-16 15:20:36 -07:00
2008-01-30 13:31:50 +01:00
for ( i = 0 ; i < 8 ; + + i )
memcpy ( & to [ i ] , & from [ i ] , sizeof ( to [ 0 ] ) ) ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:31:50 +01:00
static void convert_to_fxsr ( struct task_struct * tsk ,
const struct user_i387_ia32_struct * env )
2005-04-16 15:20:36 -07:00
{
2008-03-10 15:28:04 -07:00
struct i387_fxsave_struct * fxsave = & tsk - > thread . xstate - > fxsave ;
2008-01-30 13:31:50 +01:00
struct _fpreg * from = ( struct _fpreg * ) & env - > st_space [ 0 ] ;
struct _fpxreg * to = ( struct _fpxreg * ) & fxsave - > st_space [ 0 ] ;
int i ;
2005-04-16 15:20:36 -07:00
2008-01-30 13:31:50 +01: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-16 15:20:36 -07:00
2008-01-30 13:31:50 +01:00
for ( i = 0 ; i < 8 ; + + i )
memcpy ( & to [ i ] , & from [ i ] , sizeof ( from [ 0 ] ) ) ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:31:50 +01: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-16 15:20:36 -07:00
{
2008-01-30 13:31:50 +01:00
struct user_i387_ia32_struct env ;
2008-03-10 15:28:05 -07:00
int ret ;
2005-04-16 15:20:36 -07:00
2008-03-10 15:28:05 -07:00
ret = init_fpu ( target ) ;
if ( ret )
return ret ;
2005-04-16 15:20:36 -07:00
2008-05-23 16:26:37 -07:00
if ( ! HAVE_HWFP )
return fpregs_soft_get ( target , regset , pos , count , kbuf , ubuf ) ;
2008-03-05 15:37:32 +01:00
if ( ! cpu_has_fxsr ) {
2008-01-30 13:31:50 +01:00
return user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
2008-03-10 15:28:04 -07:00
& target - > thread . xstate - > fsave , 0 ,
- 1 ) ;
2008-03-05 15:37:32 +01:00
}
2005-04-16 15:20:36 -07:00
2008-01-30 13:31:50 +01:00
if ( kbuf & & pos = = 0 & & count = = sizeof ( env ) ) {
convert_from_fxsr ( kbuf , target ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:31:50 +01:00
convert_from_fxsr ( & env , target ) ;
2008-03-05 15:37:32 +01:00
2008-01-30 13:31:50 +01:00
return user_regset_copyout ( & pos , & count , & kbuf , & ubuf , & env , 0 , - 1 ) ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:31:50 +01: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-16 15:20:36 -07:00
{
2008-01-30 13:31:50 +01:00
struct user_i387_ia32_struct env ;
int ret ;
2005-04-16 15:20:36 -07:00
2008-03-10 15:28:05 -07:00
ret = init_fpu ( target ) ;
if ( ret )
return ret ;
2008-01-30 13:31:50 +01:00
set_stopped_child_used_math ( target ) ;
2008-05-23 16:26:37 -07:00
if ( ! HAVE_HWFP )
return fpregs_soft_set ( target , regset , pos , count , kbuf , ubuf ) ;
2008-03-05 15:37:32 +01:00
if ( ! cpu_has_fxsr ) {
2008-01-30 13:31:50 +01:00
return user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
2008-03-10 15:28:04 -07:00
& target - > thread . xstate - > fsave , 0 , - 1 ) ;
2008-03-05 15:37:32 +01:00
}
2008-01-30 13:31:50 +01:00
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 ) ;
2008-07-29 10:29:26 -07:00
/*
* update the header bit in the xsave header , indicating the
* presence of FP .
*/
if ( cpu_has_xsave )
target - > thread . xstate - > xsave . xsave_hdr . xstate_bv | = XSTATE_FP ;
2008-01-30 13:31:50 +01:00
return ret ;
2005-04-16 15:20:36 -07:00
}
/*
* Signal frame handlers .
*/
2008-01-30 13:31:50 +01:00
static inline int save_i387_fsave ( struct _fpstate_ia32 __user * buf )
2005-04-16 15:20:36 -07:00
{
struct task_struct * tsk = current ;
2008-03-10 15:28:04 -07:00
struct i387_fsave_struct * fp = & tsk - > thread . xstate - > fsave ;
2005-04-16 15:20:36 -07:00
2008-03-10 15:28:04 -07:00
fp - > status = fp - > swd ;
if ( __copy_to_user ( buf , fp , sizeof ( struct i387_fsave_struct ) ) )
2005-04-16 15:20:36 -07:00
return - 1 ;
return 1 ;
}
2008-01-30 13:31:50 +01:00
static int save_i387_fxsave ( struct _fpstate_ia32 __user * buf )
2005-04-16 15:20:36 -07:00
{
struct task_struct * tsk = current ;
2008-03-10 15:28:04 -07:00
struct i387_fxsave_struct * fx = & tsk - > thread . xstate - > fxsave ;
2008-01-30 13:31:50 +01:00
struct user_i387_ia32_struct env ;
2005-04-16 15:20:36 -07:00
int err = 0 ;
2008-01-30 13:31:50 +01:00
convert_from_fxsr ( & env , tsk ) ;
if ( __copy_to_user ( buf , & env , sizeof ( env ) ) )
2005-04-16 15:20:36 -07:00
return - 1 ;
2008-03-10 15:28:04 -07:00
err | = __put_user ( fx - > swd , & buf - > status ) ;
2008-01-30 13:31:26 +01:00
err | = __put_user ( X86_FXSR_MAGIC , & buf - > magic ) ;
if ( err )
2005-04-16 15:20:36 -07:00
return - 1 ;
2008-07-29 10:29:25 -07:00
if ( __copy_to_user ( & buf - > _fxsr_env [ 0 ] , fx , xstate_size ) )
2005-04-16 15:20:36 -07:00
return - 1 ;
return 1 ;
}
2008-07-29 10:29:25 -07:00
static int save_i387_xsave ( void __user * buf )
{
2008-10-07 14:04:28 -07:00
struct task_struct * tsk = current ;
2008-07-29 10:29:25 -07:00
struct _fpstate_ia32 __user * fx = buf ;
int err = 0 ;
2008-10-07 14:04:28 -07:00
/*
* For legacy compatible , we always set FP / SSE bits in the bit
* vector while saving the state to the user context .
* This will enable us capturing any changes ( during sigreturn ) to
* the FP / SSE bits by the legacy applications which don ' t touch
* xstate_bv in the xsave header .
*
* xsave aware applications can change the xstate_bv in the xsave
* header as well as change any contents in the memory layout .
* xrestore as part of sigreturn will capture all the changes .
*/
tsk - > thread . xstate - > xsave . xsave_hdr . xstate_bv | = XSTATE_FPSSE ;
2008-07-29 10:29:25 -07:00
if ( save_i387_fxsave ( fx ) < 0 )
return - 1 ;
err = __copy_to_user ( & fx - > sw_reserved , & fx_sw_reserved_ia32 ,
sizeof ( struct _fpx_sw_bytes ) ) ;
err | = __put_user ( FP_XSTATE_MAGIC2 ,
( __u32 __user * ) ( buf + sig_xstate_ia32_size
- FP_XSTATE_MAGIC2_SIZE ) ) ;
if ( err )
return - 1 ;
return 1 ;
}
2008-07-29 10:29:22 -07:00
int save_i387_xstate_ia32 ( void __user * buf )
2005-04-16 15:20:36 -07:00
{
2008-07-29 10:29:22 -07:00
struct _fpstate_ia32 __user * fp = ( struct _fpstate_ia32 __user * ) buf ;
struct task_struct * tsk = current ;
2008-01-30 13:31:26 +01:00
if ( ! used_math ( ) )
2005-04-16 15:20:36 -07:00
return 0 ;
2008-07-29 10:29:22 -07:00
if ( ! access_ok ( VERIFY_WRITE , buf , sig_xstate_ia32_size ) )
return - EACCES ;
2008-03-05 15:37:32 +01:00
/*
* This will cause a " finit " to be triggered by the next
2005-04-16 15:20:36 -07:00
* attempted FPU operation by the ' current ' process .
*/
clear_used_math ( ) ;
2008-03-05 15:37:32 +01:00
if ( ! HAVE_HWFP ) {
2008-01-30 13:31:50 +01:00
return fpregs_soft_get ( current , NULL ,
0 , sizeof ( struct user_i387_ia32_struct ) ,
2008-07-29 10:29:22 -07:00
NULL , fp ) ? - 1 : 1 ;
2005-04-16 15:20:36 -07:00
}
2008-03-05 15:37:32 +01:00
2008-07-29 10:29:22 -07:00
unlazy_fpu ( tsk ) ;
2008-07-29 10:29:25 -07:00
if ( cpu_has_xsave )
return save_i387_xsave ( fp ) ;
2008-03-05 15:37:32 +01:00
if ( cpu_has_fxsr )
2008-07-29 10:29:22 -07:00
return save_i387_fxsave ( fp ) ;
2008-03-05 15:37:32 +01:00
else
2008-07-29 10:29:22 -07:00
return save_i387_fsave ( fp ) ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:31:50 +01:00
static inline int restore_i387_fsave ( struct _fpstate_ia32 __user * buf )
2005-04-16 15:20:36 -07:00
{
struct task_struct * tsk = current ;
2008-03-05 15:37:32 +01:00
2008-03-10 15:28:04 -07:00
return __copy_from_user ( & tsk - > thread . xstate - > fsave , buf ,
2008-01-30 13:31:26 +01:00
sizeof ( struct i387_fsave_struct ) ) ;
2005-04-16 15:20:36 -07:00
}
2008-07-29 10:29:25 -07:00
static int restore_i387_fxsave ( struct _fpstate_ia32 __user * buf ,
unsigned int size )
2005-04-16 15:20:36 -07:00
{
struct task_struct * tsk = current ;
2008-01-30 13:31:50 +01:00
struct user_i387_ia32_struct env ;
2008-03-05 15:37:32 +01:00
int err ;
2008-03-10 15:28:04 -07:00
err = __copy_from_user ( & tsk - > thread . xstate - > fxsave , & buf - > _fxsr_env [ 0 ] ,
2008-07-29 10:29:25 -07:00
size ) ;
2005-04-16 15:20:36 -07:00
/* mxcsr reserved bits must be masked to zero for security reasons */
2008-03-10 15:28:04 -07:00
tsk - > thread . xstate - > fxsave . mxcsr & = mxcsr_feature_mask ;
2008-01-30 13:31:50 +01:00
if ( err | | __copy_from_user ( & env , buf , sizeof ( env ) ) )
return 1 ;
convert_to_fxsr ( tsk , & env ) ;
2008-03-05 15:37:32 +01:00
2008-01-30 13:31:50 +01:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-07-29 10:29:25 -07:00
static int restore_i387_xsave ( void __user * buf )
{
struct _fpx_sw_bytes fx_sw_user ;
struct _fpstate_ia32 __user * fx_user =
( ( struct _fpstate_ia32 __user * ) buf ) ;
struct i387_fxsave_struct __user * fx =
( struct i387_fxsave_struct __user * ) & fx_user - > _fxsr_env [ 0 ] ;
struct xsave_hdr_struct * xsave_hdr =
& current - > thread . xstate - > xsave . xsave_hdr ;
2008-07-29 17:23:16 -07:00
u64 mask ;
2008-07-29 10:29:25 -07:00
int err ;
if ( check_for_xstate ( fx , buf , & fx_sw_user ) )
goto fx_only ;
2008-07-29 17:23:16 -07:00
mask = fx_sw_user . xstate_bv ;
2008-07-29 10:29:25 -07:00
err = restore_i387_fxsave ( buf , fx_sw_user . xstate_size ) ;
2008-07-29 17:23:16 -07:00
xsave_hdr - > xstate_bv & = pcntxt_mask ;
2008-07-29 10:29:25 -07:00
/*
* These bits must be zero .
*/
xsave_hdr - > reserved1 [ 0 ] = xsave_hdr - > reserved1 [ 1 ] = 0 ;
/*
* Init the state that is not present in the memory layout
* and enabled by the OS .
*/
2008-07-29 17:23:16 -07:00
mask = ~ ( pcntxt_mask & ~ mask ) ;
xsave_hdr - > xstate_bv & = mask ;
2008-07-29 10:29:25 -07:00
return err ;
fx_only :
/*
* Couldn ' t find the extended state information in the memory
* layout . Restore the FP / SSE and init the other extended state
* enabled by the OS .
*/
xsave_hdr - > xstate_bv = XSTATE_FPSSE ;
return restore_i387_fxsave ( buf , sizeof ( struct i387_fxsave_struct ) ) ;
}
2008-07-29 10:29:22 -07:00
int restore_i387_xstate_ia32 ( void __user * buf )
2005-04-16 15:20:36 -07:00
{
int err ;
2008-05-23 16:26:37 -07:00
struct task_struct * tsk = current ;
2008-07-29 10:29:22 -07:00
struct _fpstate_ia32 __user * fp = ( struct _fpstate_ia32 __user * ) buf ;
2005-04-16 15:20:36 -07:00
2008-05-23 16:26:37 -07:00
if ( HAVE_HWFP )
2008-05-07 12:09:52 -07:00
clear_fpu ( tsk ) ;
2008-07-29 10:29:22 -07:00
if ( ! buf ) {
if ( used_math ( ) ) {
clear_fpu ( tsk ) ;
clear_used_math ( ) ;
}
return 0 ;
} else
if ( ! access_ok ( VERIFY_READ , buf , sig_xstate_ia32_size ) )
return - EACCES ;
2008-05-23 16:26:37 -07:00
if ( ! used_math ( ) ) {
err = init_fpu ( tsk ) ;
if ( err )
return err ;
}
2008-05-07 12:09:52 -07:00
2008-05-23 16:26:37 -07:00
if ( HAVE_HWFP ) {
2008-07-29 10:29:25 -07:00
if ( cpu_has_xsave )
err = restore_i387_xsave ( buf ) ;
else if ( cpu_has_fxsr )
err = restore_i387_fxsave ( fp , sizeof ( struct
i387_fxsave_struct ) ) ;
2008-03-05 15:37:32 +01:00
else
2008-07-29 10:29:22 -07:00
err = restore_i387_fsave ( fp ) ;
2005-04-16 15:20:36 -07:00
} else {
2008-01-30 13:31:50 +01:00
err = fpregs_soft_set ( current , NULL ,
0 , sizeof ( struct user_i387_ia32_struct ) ,
2008-07-29 10:29:22 -07:00
NULL , fp ) ! = 0 ;
2005-04-16 15:20:36 -07:00
}
set_used_math ( ) ;
2008-03-05 15:37:32 +01:00
2005-04-16 15:20:36 -07:00
return err ;
}
/*
* FPU state for core dumps .
2008-01-30 13:31:55 +01:00
* This is only used for a . out dumps now .
* It is declared generically using elf_fpregset_t ( which is
* struct user_i387_struct ) but is in fact only used for 32 - bit
* dumps , so on 64 - bit it is really struct user_i387_ia32_struct .
2005-04-16 15:20:36 -07:00
*/
2008-01-30 13:31:26 +01:00
int dump_fpu ( struct pt_regs * regs , struct user_i387_struct * fpu )
2005-04-16 15:20:36 -07:00
{
struct task_struct * tsk = current ;
2008-03-05 15:37:32 +01:00
int fpvalid ;
2005-04-16 15:20:36 -07:00
fpvalid = ! ! used_math ( ) ;
2008-01-30 13:31:55 +01:00
if ( fpvalid )
fpvalid = ! fpregs_get ( tsk , NULL ,
0 , sizeof ( struct user_i387_ia32_struct ) ,
fpu , NULL ) ;
2005-04-16 15:20:36 -07:00
return fpvalid ;
}
2005-06-23 00:08:33 -07:00
EXPORT_SYMBOL ( dump_fpu ) ;
2005-04-16 15:20:36 -07:00
2008-01-30 13:31:55 +01:00
# endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */