2008-02-08 04:19:31 -08:00
/* MN10300 FPU management
*
* Copyright ( C ) 2007 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation ; either version
* 2 of the Licence , or ( at your option ) any later version .
*/
# include <asm/uaccess.h>
# include <asm/fpu.h>
# include <asm/elf.h>
# include <asm/exceptions.h>
2010-10-27 17:28:52 +01:00
# ifdef CONFIG_LAZY_SAVE_FPU
2008-02-08 04:19:31 -08:00
struct task_struct * fpu_state_owner ;
2010-10-27 17:28:52 +01:00
# endif
2008-02-08 04:19:31 -08:00
/*
2010-10-27 17:28:52 +01:00
* error functions in FPU disabled exception
2008-02-08 04:19:31 -08:00
*/
2010-10-27 17:28:52 +01:00
asmlinkage void fpu_disabled_in_kernel ( struct pt_regs * regs )
2008-02-08 04:19:31 -08:00
{
2010-10-27 17:28:52 +01:00
die_if_no_fixup ( " An FPU Disabled exception happened in kernel space \n " ,
regs , EXCEP_FPU_DISABLED ) ;
2008-02-08 04:19:31 -08:00
}
/*
* handle an FPU operational exception
* - there ' s a possibility that if the FPU is asynchronous , the signal might
* be meant for a process other than the current one
*/
asmlinkage void fpu_exception ( struct pt_regs * regs , enum exception_code code )
{
2010-10-27 17:28:52 +01:00
struct task_struct * tsk = current ;
2008-02-08 04:19:31 -08:00
siginfo_t info ;
2010-10-27 17:28:52 +01:00
u32 fpcr ;
2008-02-08 04:19:31 -08:00
if ( ! user_mode ( regs ) )
die_if_no_fixup ( " An FPU Operation exception happened in "
" kernel space \n " ,
regs , code ) ;
2010-10-27 17:28:52 +01:00
if ( ! is_using_fpu ( tsk ) )
2008-02-08 04:19:31 -08:00
die_if_no_fixup ( " An FPU Operation exception happened, "
" but the FPU is not in use " ,
regs , code ) ;
info . si_signo = SIGFPE ;
info . si_errno = 0 ;
info . si_addr = ( void * ) tsk - > thread . uregs - > pc ;
info . si_code = FPE_FLTINV ;
2010-10-27 17:28:52 +01:00
unlazy_fpu ( tsk ) ;
2008-02-08 04:19:31 -08:00
2010-10-27 17:28:52 +01:00
fpcr = tsk - > thread . fpu_state . fpcr ;
if ( fpcr & FPCR_EC_Z )
info . si_code = FPE_FLTDIV ;
else if ( fpcr & FPCR_EC_O )
info . si_code = FPE_FLTOVF ;
else if ( fpcr & FPCR_EC_U )
info . si_code = FPE_FLTUND ;
else if ( fpcr & FPCR_EC_I )
info . si_code = FPE_FLTRES ;
2008-02-08 04:19:31 -08:00
force_sig_info ( SIGFPE , & info , tsk ) ;
}
/*
* save the FPU state to a signal context
*/
int fpu_setup_sigcontext ( struct fpucontext * fpucontext )
{
struct task_struct * tsk = current ;
if ( ! is_using_fpu ( tsk ) )
return 0 ;
/* transfer the current FPU state to memory and cause fpu_init() to be
* triggered by the next attempted FPU operation by the current
* process .
*/
preempt_disable ( ) ;
2010-10-27 17:28:52 +01:00
# ifndef CONFIG_LAZY_SAVE_FPU
if ( tsk - > thread . fpu_flags & THREAD_HAS_FPU ) {
fpu_save ( & tsk - > thread . fpu_state ) ;
tsk - > thread . uregs - > epsw & = ~ EPSW_FE ;
tsk - > thread . fpu_flags & = ~ THREAD_HAS_FPU ;
}
# else /* !CONFIG_LAZY_SAVE_FPU */
2008-02-08 04:19:31 -08:00
if ( fpu_state_owner = = tsk ) {
fpu_save ( & tsk - > thread . fpu_state ) ;
fpu_state_owner - > thread . uregs - > epsw & = ~ EPSW_FE ;
fpu_state_owner = NULL ;
}
2010-10-27 17:28:52 +01:00
# endif /* !CONFIG_LAZY_SAVE_FPU */
2008-02-08 04:19:31 -08:00
preempt_enable ( ) ;
/* we no longer have a valid current FPU state */
clear_using_fpu ( tsk ) ;
/* transfer the saved FPU state onto the userspace stack */
if ( copy_to_user ( fpucontext ,
& tsk - > thread . fpu_state ,
min ( sizeof ( struct fpu_state_struct ) ,
sizeof ( struct fpucontext ) ) ) )
return - 1 ;
return 1 ;
}
/*
* kill a process ' s FPU state during restoration after signal handling
*/
void fpu_kill_state ( struct task_struct * tsk )
{
/* disown anything left in the FPU */
preempt_disable ( ) ;
2010-10-27 17:28:52 +01:00
# ifndef CONFIG_LAZY_SAVE_FPU
if ( tsk - > thread . fpu_flags & THREAD_HAS_FPU ) {
tsk - > thread . uregs - > epsw & = ~ EPSW_FE ;
tsk - > thread . fpu_flags & = ~ THREAD_HAS_FPU ;
}
# else /* !CONFIG_LAZY_SAVE_FPU */
2008-02-08 04:19:31 -08:00
if ( fpu_state_owner = = tsk ) {
fpu_state_owner - > thread . uregs - > epsw & = ~ EPSW_FE ;
fpu_state_owner = NULL ;
}
2010-10-27 17:28:52 +01:00
# endif /* !CONFIG_LAZY_SAVE_FPU */
2008-02-08 04:19:31 -08:00
preempt_enable ( ) ;
2010-10-27 17:28:52 +01:00
2008-02-08 04:19:31 -08:00
/* we no longer have a valid current FPU state */
clear_using_fpu ( tsk ) ;
}
/*
* restore the FPU state from a signal context
*/
int fpu_restore_sigcontext ( struct fpucontext * fpucontext )
{
struct task_struct * tsk = current ;
int ret ;
/* load up the old FPU state */
2010-10-27 17:28:52 +01:00
ret = copy_from_user ( & tsk - > thread . fpu_state , fpucontext ,
2008-02-08 04:19:31 -08:00
min ( sizeof ( struct fpu_state_struct ) ,
sizeof ( struct fpucontext ) ) ) ;
if ( ! ret )
set_using_fpu ( tsk ) ;
return ret ;
}
/*
* fill in the FPU structure for a core dump
*/
int dump_fpu ( struct pt_regs * regs , elf_fpregset_t * fpreg )
{
struct task_struct * tsk = current ;
int fpvalid ;
fpvalid = is_using_fpu ( tsk ) ;
if ( fpvalid ) {
unlazy_fpu ( tsk ) ;
memcpy ( fpreg , & tsk - > thread . fpu_state , sizeof ( * fpreg ) ) ;
}
return fpvalid ;
}