2008-07-29 21:29:19 +04:00
/*
* xsave / xrstor support .
*
* Author : Suresh Siddha < suresh . b . siddha @ intel . com >
*/
# include <linux/bootmem.h>
# include <linux/compat.h>
# include <asm/i387.h>
2008-07-29 21:29:25 +04:00
# ifdef CONFIG_IA32_EMULATION
# include <asm/sigcontext32.h>
# endif
2008-07-30 04:23:16 +04:00
# include <asm/xcr.h>
2008-07-29 21:29:19 +04:00
/*
* Supported feature mask by the CPU and the kernel .
*/
2008-07-30 04:23:16 +04:00
u64 pcntxt_mask ;
2008-07-29 21:29:19 +04:00
2008-07-29 21:29:25 +04:00
struct _fpx_sw_bytes fx_sw_reserved ;
# ifdef CONFIG_IA32_EMULATION
struct _fpx_sw_bytes fx_sw_reserved_ia32 ;
# endif
/*
* Check for the presence of extended state information in the
* user fpstate pointer in the sigcontext .
*/
int check_for_xstate ( struct i387_fxsave_struct __user * buf ,
void __user * fpstate ,
struct _fpx_sw_bytes * fx_sw_user )
{
int min_xstate_size = sizeof ( struct i387_fxsave_struct ) +
sizeof ( struct xsave_hdr_struct ) ;
unsigned int magic2 ;
int err ;
err = __copy_from_user ( fx_sw_user , & buf - > sw_reserved [ 0 ] ,
sizeof ( struct _fpx_sw_bytes ) ) ;
if ( err )
return err ;
/*
* First Magic check failed .
*/
if ( fx_sw_user - > magic1 ! = FP_XSTATE_MAGIC1 )
return - 1 ;
/*
* Check for error scenarios .
*/
if ( fx_sw_user - > xstate_size < min_xstate_size | |
fx_sw_user - > xstate_size > xstate_size | |
fx_sw_user - > xstate_size > fx_sw_user - > extended_size )
return - 1 ;
err = __get_user ( magic2 , ( __u32 * ) ( ( ( void * ) fpstate ) +
fx_sw_user - > extended_size -
FP_XSTATE_MAGIC2_SIZE ) ) ;
/*
* Check for the presence of second magic word at the end of memory
* layout . This detects the case where the user just copied the legacy
* fpstate layout with out copying the extended state information
* in the memory layout .
*/
if ( err | | magic2 ! = FP_XSTATE_MAGIC2 )
return - 1 ;
return 0 ;
}
2008-07-29 21:29:22 +04:00
# ifdef CONFIG_X86_64
/*
* Signal frame handlers .
*/
int save_i387_xstate ( void __user * buf )
{
struct task_struct * tsk = current ;
int err = 0 ;
if ( ! access_ok ( VERIFY_WRITE , buf , sig_xstate_size ) )
return - EACCES ;
2008-08-13 22:38:15 +04:00
BUG_ON ( sig_xstate_size < xstate_size ) ;
2008-07-29 21:29:22 +04:00
2008-07-29 21:29:25 +04:00
if ( ( unsigned long ) buf % 64 )
2008-07-29 21:29:22 +04:00
printk ( " save_i387_xstate: bad fpstate %p \n " , buf ) ;
if ( ! used_math ( ) )
return 0 ;
clear_used_math ( ) ; /* trigger finit */
if ( task_thread_info ( tsk ) - > status & TS_USEDFPU ) {
2008-08-13 22:38:14 +04:00
/*
* Start with clearing the user buffer . This will present a
* clean context for the bytes not touched by the fxsave / xsave .
*/
2008-08-18 14:59:32 +04:00
err = __clear_user ( buf , sig_xstate_size ) ;
if ( err )
return err ;
2008-08-13 22:38:14 +04:00
2008-07-29 21:29:25 +04:00
if ( task_thread_info ( tsk ) - > status & TS_XSAVE )
err = xsave_user ( buf ) ;
else
err = fxsave_user ( buf ) ;
2008-07-29 21:29:22 +04:00
if ( err )
return err ;
task_thread_info ( tsk ) - > status & = ~ TS_USEDFPU ;
stts ( ) ;
} else {
if ( __copy_to_user ( buf , & tsk - > thread . xstate - > fxsave ,
xstate_size ) )
return - 1 ;
}
2008-07-29 21:29:25 +04:00
if ( task_thread_info ( tsk ) - > status & TS_XSAVE ) {
struct _fpstate __user * fx = buf ;
2008-10-08 01:04:28 +04:00
struct _xstate __user * x = buf ;
u64 xstate_bv ;
2008-07-29 21:29:25 +04:00
err = __copy_to_user ( & fx - > sw_reserved , & fx_sw_reserved ,
sizeof ( struct _fpx_sw_bytes ) ) ;
err | = __put_user ( FP_XSTATE_MAGIC2 ,
( __u32 __user * ) ( buf + sig_xstate_size
- FP_XSTATE_MAGIC2_SIZE ) ) ;
2008-10-08 01:04:28 +04:00
/*
* Read the xstate_bv which we copied ( directly from the cpu or
* from the state in task struct ) to the user buffers and
* set the FP / SSE bits .
*/
err | = __get_user ( xstate_bv , & x - > xstate_hdr . xstate_bv ) ;
/*
* 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 apps 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 .
*/
xstate_bv | = XSTATE_FPSSE ;
err | = __put_user ( xstate_bv , & x - > xstate_hdr . xstate_bv ) ;
2008-10-08 01:04:27 +04:00
if ( err )
return err ;
2008-07-29 21:29:25 +04:00
}
2008-07-29 21:29:22 +04:00
return 1 ;
}
2008-07-29 21:29:25 +04:00
/*
* Restore the extended state if present . Otherwise , restore the FP / SSE
* state .
*/
int restore_user_xstate ( void __user * buf )
{
struct _fpx_sw_bytes fx_sw_user ;
2008-07-30 04:23:16 +04:00
u64 mask ;
2008-07-29 21:29:25 +04:00
int err ;
if ( ( ( unsigned long ) buf % 64 ) | |
check_for_xstate ( buf , buf , & fx_sw_user ) )
goto fx_only ;
2008-07-30 04:23:16 +04:00
mask = fx_sw_user . xstate_bv ;
2008-07-29 21:29:25 +04:00
/*
* restore the state passed by the user .
*/
2008-07-30 04:23:16 +04:00
err = xrestore_user ( buf , mask ) ;
2008-07-29 21:29:25 +04:00
if ( err )
return err ;
/*
* init the state skipped by the user .
*/
2008-07-30 04:23:16 +04:00
mask = pcntxt_mask & ~ mask ;
2008-07-29 21:29:25 +04:00
2008-07-30 04:23:16 +04:00
xrstor_state ( init_xstate_buf , mask ) ;
2008-07-29 21:29:25 +04:00
return 0 ;
fx_only :
/*
* couldn ' t find the extended state information in the
* memory layout . Restore just the FP / SSE and init all
* the other extended state .
*/
2008-07-30 04:23:16 +04:00
xrstor_state ( init_xstate_buf , pcntxt_mask & ~ XSTATE_FPSSE ) ;
2008-07-29 21:29:25 +04:00
return fxrstor_checking ( ( __force struct i387_fxsave_struct * ) buf ) ;
}
2008-07-29 21:29:22 +04:00
/*
* This restores directly out of user space . Exceptions are handled .
*/
int restore_i387_xstate ( void __user * buf )
{
struct task_struct * tsk = current ;
2008-07-29 21:29:25 +04:00
int err = 0 ;
2008-07-29 21:29:22 +04:00
if ( ! buf ) {
2008-07-29 21:29:25 +04:00
if ( used_math ( ) )
goto clear ;
2008-07-29 21:29:22 +04:00
return 0 ;
} else
if ( ! access_ok ( VERIFY_READ , buf , sig_xstate_size ) )
return - EACCES ;
if ( ! used_math ( ) ) {
err = init_fpu ( tsk ) ;
if ( err )
return err ;
}
if ( ! ( task_thread_info ( current ) - > status & TS_USEDFPU ) ) {
clts ( ) ;
task_thread_info ( current ) - > status | = TS_USEDFPU ;
}
2008-07-29 21:29:25 +04:00
if ( task_thread_info ( tsk ) - > status & TS_XSAVE )
err = restore_user_xstate ( buf ) ;
else
err = fxrstor_checking ( ( __force struct i387_fxsave_struct * )
buf ) ;
2008-07-29 21:29:22 +04:00
if ( unlikely ( err ) ) {
/*
* Encountered an error while doing the restore from the
* user buffer , clear the fpu state .
*/
2008-07-29 21:29:25 +04:00
clear :
2008-07-29 21:29:22 +04:00
clear_fpu ( tsk ) ;
clear_used_math ( ) ;
}
return err ;
}
# endif
2008-07-29 21:29:25 +04:00
/*
* Prepare the SW reserved portion of the fxsave memory layout , indicating
* the presence of the extended state information in the memory layout
* pointed by the fpstate pointer in the sigcontext .
* This will be saved when ever the FP and extended state context is
* saved on the user stack during the signal handler delivery to the user .
*/
2008-10-22 03:49:09 +04:00
static void prepare_fx_sw_frame ( void )
2008-07-29 21:29:25 +04:00
{
int size_extended = ( xstate_size - sizeof ( struct i387_fxsave_struct ) ) +
FP_XSTATE_MAGIC2_SIZE ;
sig_xstate_size = sizeof ( struct _fpstate ) + size_extended ;
# ifdef CONFIG_IA32_EMULATION
sig_xstate_ia32_size = sizeof ( struct _fpstate_ia32 ) + size_extended ;
# endif
memset ( & fx_sw_reserved , 0 , sizeof ( fx_sw_reserved ) ) ;
fx_sw_reserved . magic1 = FP_XSTATE_MAGIC1 ;
fx_sw_reserved . extended_size = sig_xstate_size ;
2008-07-30 04:23:16 +04:00
fx_sw_reserved . xstate_bv = pcntxt_mask ;
2008-07-29 21:29:25 +04:00
fx_sw_reserved . xstate_size = xstate_size ;
# ifdef CONFIG_IA32_EMULATION
memcpy ( & fx_sw_reserved_ia32 , & fx_sw_reserved ,
sizeof ( struct _fpx_sw_bytes ) ) ;
fx_sw_reserved_ia32 . extended_size = sig_xstate_ia32_size ;
# endif
}
2008-07-29 21:29:19 +04:00
/*
* Represents init state for the supported extended state .
*/
struct xsave_struct * init_xstate_buf ;
2008-07-29 21:29:21 +04:00
# ifdef CONFIG_X86_64
unsigned int sig_xstate_size = sizeof ( struct _fpstate ) ;
# endif
2008-07-29 21:29:19 +04:00
/*
* Enable the extended processor state save / restore feature
*/
void __cpuinit xsave_init ( void )
{
if ( ! cpu_has_xsave )
return ;
set_in_cr4 ( X86_CR4_OSXSAVE ) ;
/*
* Enable all the features that the HW is capable of
* and the Linux kernel is aware of .
*/
2008-07-30 04:23:16 +04:00
xsetbv ( XCR_XFEATURE_ENABLED_MASK , pcntxt_mask ) ;
2008-07-29 21:29:19 +04:00
}
/*
* setup the xstate image representing the init state
*/
2008-08-30 06:03:34 +04:00
static void __init setup_xstate_init ( void )
2008-07-29 21:29:19 +04:00
{
init_xstate_buf = alloc_bootmem ( xstate_size ) ;
init_xstate_buf - > i387 . mxcsr = MXCSR_DEFAULT ;
}
/*
* Enable and initialize the xsave feature .
*/
2008-11-20 16:12:50 +03:00
void __ref xsave_cntxt_init ( void )
2008-07-29 21:29:19 +04:00
{
unsigned int eax , ebx , ecx , edx ;
cpuid_count ( 0xd , 0 , & eax , & ebx , & ecx , & edx ) ;
2008-07-30 04:23:16 +04:00
pcntxt_mask = eax + ( ( u64 ) edx < < 32 ) ;
2008-07-29 21:29:19 +04:00
2008-07-30 04:23:16 +04:00
if ( ( pcntxt_mask & XSTATE_FPSSE ) ! = XSTATE_FPSSE ) {
printk ( KERN_ERR " FP/SSE not shown under xsave features 0x%llx \n " ,
pcntxt_mask ) ;
2008-07-29 21:29:19 +04:00
BUG ( ) ;
}
/*
* for now OS knows only about FP / SSE
*/
2008-07-30 04:23:16 +04:00
pcntxt_mask = pcntxt_mask & XCNTXT_MASK ;
2008-07-29 21:29:19 +04:00
xsave_init ( ) ;
/*
* Recompute the context size for enabled features
*/
cpuid_count ( 0xd , 0 , & eax , & ebx , & ecx , & edx ) ;
xstate_size = ebx ;
2008-07-29 21:29:25 +04:00
prepare_fx_sw_frame ( ) ;
2008-07-29 21:29:19 +04:00
setup_xstate_init ( ) ;
2008-07-30 04:23:16 +04:00
printk ( KERN_INFO " xsave/xrstor: enabled xstate_bv 0x%llx, "
2008-07-29 21:29:19 +04:00
" cntxt size 0x%x \n " ,
2008-07-30 04:23:16 +04:00
pcntxt_mask , xstate_size ) ;
2008-07-29 21:29:19 +04:00
}