2005-06-24 09:01:16 +04:00
// TODO some minor issues
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
2008-02-13 00:17:07 +03:00
* Copyright ( C ) 2001 - 2007 Tensilica Inc .
2005-06-24 09:01:16 +04:00
*
* Joe Taylor < joe @ tensilica . com , joetylr @ yahoo . com >
* Chris Zankel < chris @ zankel . net >
* Scott Foehner < sfoehner @ yahoo . com > ,
* Kevin Chea
* Marc Gauthier < marc @ tensilica . com > < marc @ alumni . uwaterloo . ca >
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/errno.h>
# include <linux/ptrace.h>
# include <linux/smp.h>
# include <linux/security.h>
2005-06-30 13:58:56 +04:00
# include <linux/signal.h>
2005-06-24 09:01:16 +04:00
# include <asm/pgtable.h>
# include <asm/page.h>
# include <asm/uaccess.h>
# include <asm/ptrace.h>
# include <asm/elf.h>
2008-02-13 00:17:07 +03:00
# include <asm/coprocessor.h>
2005-06-24 09:01:16 +04:00
2010-03-11 02:22:57 +03:00
void user_enable_single_step ( struct task_struct * child )
{
child - > ptrace | = PT_SINGLESTEP ;
}
void user_disable_single_step ( struct task_struct * child )
{
child - > ptrace & = ~ PT_SINGLESTEP ;
}
2005-06-24 09:01:16 +04:00
/*
2008-02-13 00:17:07 +03:00
* Called by kernel / ptrace . c when detaching to disable single stepping .
2005-06-24 09:01:16 +04:00
*/
void ptrace_disable ( struct task_struct * child )
{
/* Nothing to do.. */
}
2008-02-13 00:17:07 +03:00
int ptrace_getregs ( struct task_struct * child , void __user * uregs )
2005-06-24 09:01:16 +04:00
{
2008-02-13 00:17:07 +03:00
struct pt_regs * regs = task_pt_regs ( child ) ;
xtensa_gregset_t __user * gregset = uregs ;
unsigned long wm = regs - > wmask ;
2008-01-29 02:55:01 +03:00
unsigned long wb = regs - > windowbase ;
int live , i ;
2008-02-13 00:17:07 +03:00
if ( ! access_ok ( VERIFY_WRITE , uregs , sizeof ( xtensa_gregset_t ) ) )
return - EIO ;
2008-01-29 02:55:01 +03:00
__put_user ( regs - > pc , & gregset - > pc ) ;
__put_user ( regs - > ps & ~ ( 1 < < PS_EXCM_BIT ) , & gregset - > ps ) ;
__put_user ( regs - > lbeg , & gregset - > lbeg ) ;
__put_user ( regs - > lend , & gregset - > lend ) ;
__put_user ( regs - > lcount , & gregset - > lcount ) ;
__put_user ( regs - > windowstart , & gregset - > windowstart ) ;
__put_user ( regs - > windowbase , & gregset - > windowbase ) ;
2008-02-13 00:17:07 +03:00
live = ( wm & 2 ) ? 4 : ( wm & 4 ) ? 8 : ( wm & 8 ) ? 12 : 16 ;
2008-01-29 02:55:01 +03:00
for ( i = 0 ; i < live ; i + + )
__put_user ( regs - > areg [ i ] , gregset - > a + ( ( wb * 4 + i ) % XCHAL_NUM_AREGS ) ) ;
for ( i = XCHAL_NUM_AREGS - ( wm > > 4 ) * 4 ; i < XCHAL_NUM_AREGS ; i + + )
__put_user ( regs - > areg [ i ] , gregset - > a + ( ( wb * 4 + i ) % XCHAL_NUM_AREGS ) ) ;
return 0 ;
2008-02-13 00:17:07 +03:00
}
2005-06-24 09:01:16 +04:00
2008-02-13 00:17:07 +03:00
int ptrace_setregs ( struct task_struct * child , void __user * uregs )
{
struct pt_regs * regs = task_pt_regs ( child ) ;
xtensa_gregset_t * gregset = uregs ;
const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK ;
unsigned long ps ;
2008-01-29 02:55:01 +03:00
unsigned long wb ;
2008-02-13 00:17:07 +03:00
if ( ! access_ok ( VERIFY_WRITE , uregs , sizeof ( xtensa_gregset_t ) ) )
return - EIO ;
2008-01-29 02:55:01 +03:00
__get_user ( regs - > pc , & gregset - > pc ) ;
__get_user ( ps , & gregset - > ps ) ;
__get_user ( regs - > lbeg , & gregset - > lbeg ) ;
__get_user ( regs - > lend , & gregset - > lend ) ;
__get_user ( regs - > lcount , & gregset - > lcount ) ;
__get_user ( regs - > windowstart , & gregset - > windowstart ) ;
__get_user ( wb , & gregset - > windowbase ) ;
2008-02-13 00:17:07 +03:00
regs - > ps = ( regs - > ps & ~ ps_mask ) | ( ps & ps_mask ) | ( 1 < < PS_EXCM_BIT ) ;
2008-01-29 02:55:01 +03:00
if ( wb > = XCHAL_NUM_AREGS / 4 )
return - EFAULT ;
2008-02-13 00:17:07 +03:00
2008-01-29 02:55:01 +03:00
regs - > windowbase = wb ;
if ( wb ! = 0 & & __copy_from_user ( regs - > areg + XCHAL_NUM_AREGS - wb * 4 ,
gregset - > a , wb * 16 ) )
return - EFAULT ;
if ( __copy_from_user ( regs - > areg , gregset - > a + wb * 4 , ( WSBITS - wb ) * 16 ) )
return - EFAULT ;
return 0 ;
2008-02-13 00:17:07 +03:00
}
2005-06-24 09:01:16 +04:00
2008-02-13 00:17:07 +03:00
int ptrace_getxregs ( struct task_struct * child , void __user * uregs )
{
struct pt_regs * regs = task_pt_regs ( child ) ;
struct thread_info * ti = task_thread_info ( child ) ;
elf_xtregs_t __user * xtregs = uregs ;
int ret = 0 ;
if ( ! access_ok ( VERIFY_WRITE , uregs , sizeof ( elf_xtregs_t ) ) )
return - EIO ;
# if XTENSA_HAVE_COPROCESSORS
/* Flush all coprocessor registers to memory. */
coprocessor_flush_all ( ti ) ;
ret | = __copy_to_user ( & xtregs - > cp0 , & ti - > xtregs_cp ,
sizeof ( xtregs_coprocessor_t ) ) ;
# endif
ret | = __copy_to_user ( & xtregs - > opt , & regs - > xtregs_opt ,
sizeof ( xtregs - > opt ) ) ;
ret | = __copy_to_user ( & xtregs - > user , & ti - > xtregs_user ,
sizeof ( xtregs - > user ) ) ;
2005-06-24 09:01:16 +04:00
2008-02-13 00:17:07 +03:00
return ret ? - EFAULT : 0 ;
}
int ptrace_setxregs ( struct task_struct * child , void __user * uregs )
{
struct thread_info * ti = task_thread_info ( child ) ;
struct pt_regs * regs = task_pt_regs ( child ) ;
elf_xtregs_t * xtregs = uregs ;
int ret = 0 ;
2011-07-26 04:11:53 +04:00
if ( ! access_ok ( VERIFY_READ , uregs , sizeof ( elf_xtregs_t ) ) )
return - EFAULT ;
2008-02-13 00:17:07 +03:00
# if XTENSA_HAVE_COPROCESSORS
/* Flush all coprocessors before we overwrite them. */
coprocessor_flush_all ( ti ) ;
coprocessor_release_all ( ti ) ;
ret | = __copy_from_user ( & ti - > xtregs_cp , & xtregs - > cp0 ,
sizeof ( xtregs_coprocessor_t ) ) ;
# endif
ret | = __copy_from_user ( & regs - > xtregs_opt , & xtregs - > opt ,
sizeof ( xtregs - > opt ) ) ;
ret | = __copy_from_user ( & ti - > xtregs_user , & xtregs - > user ,
sizeof ( xtregs - > user ) ) ;
return ret ? - EFAULT : 0 ;
}
int ptrace_peekusr ( struct task_struct * child , long regno , long __user * ret )
{
struct pt_regs * regs ;
unsigned long tmp ;
regs = task_pt_regs ( child ) ;
tmp = 0 ; /* Default return value. */
2005-06-24 09:01:16 +04:00
2008-02-13 00:17:07 +03:00
switch ( regno ) {
2005-06-24 09:01:16 +04:00
case REG_AR_BASE . . . REG_AR_BASE + XCHAL_NUM_AREGS - 1 :
2008-02-13 00:17:07 +03:00
tmp = regs - > areg [ regno - REG_AR_BASE ] ;
2005-06-24 09:01:16 +04:00
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case REG_A_BASE . . . REG_A_BASE + 15 :
2008-02-13 00:17:07 +03:00
tmp = regs - > areg [ regno - REG_A_BASE ] ;
2005-06-24 09:01:16 +04:00
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case REG_PC :
tmp = regs - > pc ;
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case REG_PS :
/* Note: PS.EXCM is not set while user task is running;
* its being set in regs is for exception handling
* convenience . */
2006-12-10 13:18:48 +03:00
tmp = ( regs - > ps & ~ ( 1 < < PS_EXCM_BIT ) ) ;
2005-06-24 09:01:16 +04:00
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case REG_WB :
2008-02-13 00:17:07 +03:00
break ; /* tmp = 0 */
2005-06-24 09:01:16 +04:00
case REG_WS :
2008-02-13 00:17:07 +03:00
{
unsigned long wb = regs - > windowbase ;
unsigned long ws = regs - > windowstart ;
tmp = ( ( ws > > wb ) | ( ws < < ( WSBITS - wb ) ) ) & ( ( 1 < < WSBITS ) - 1 ) ;
2005-06-24 09:01:16 +04:00
break ;
2008-02-13 00:17:07 +03:00
}
2005-06-24 09:01:16 +04:00
case REG_LBEG :
tmp = regs - > lbeg ;
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case REG_LEND :
tmp = regs - > lend ;
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case REG_LCOUNT :
tmp = regs - > lcount ;
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case REG_SAR :
tmp = regs - > sar ;
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case SYSCALL_NR :
tmp = regs - > syscall ;
break ;
2008-02-13 00:17:07 +03:00
default :
return - EIO ;
}
return put_user ( tmp , ret ) ;
}
2005-06-24 09:01:16 +04:00
2008-02-13 00:17:07 +03:00
int ptrace_pokeusr ( struct task_struct * child , long regno , long val )
{
struct pt_regs * regs ;
regs = task_pt_regs ( child ) ;
2005-06-24 09:01:16 +04:00
2008-02-13 00:17:07 +03:00
switch ( regno ) {
2005-06-24 09:01:16 +04:00
case REG_AR_BASE . . . REG_AR_BASE + XCHAL_NUM_AREGS - 1 :
2008-02-13 00:17:07 +03:00
regs - > areg [ regno - REG_AR_BASE ] = val ;
2005-06-24 09:01:16 +04:00
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case REG_A_BASE . . . REG_A_BASE + 15 :
2008-02-13 00:17:07 +03:00
regs - > areg [ regno - REG_A_BASE ] = val ;
2005-06-24 09:01:16 +04:00
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case REG_PC :
2008-02-13 00:17:07 +03:00
regs - > pc = val ;
2005-06-24 09:01:16 +04:00
break ;
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
case SYSCALL_NR :
2008-02-13 00:17:07 +03:00
regs - > syscall = val ;
2005-06-24 09:01:16 +04:00
break ;
default :
2008-02-13 00:17:07 +03:00
return - EIO ;
}
return 0 ;
}
2010-10-28 02:33:47 +04:00
long arch_ptrace ( struct task_struct * child , long request ,
unsigned long addr , unsigned long data )
2008-02-13 00:17:07 +03:00
{
int ret = - EPERM ;
2010-10-28 02:34:06 +04:00
void __user * datap = ( void __user * ) data ;
2008-02-13 00:17:07 +03:00
switch ( request ) {
case PTRACE_PEEKTEXT : /* read word at location addr. */
case PTRACE_PEEKDATA :
ret = generic_ptrace_peekdata ( child , addr , data ) ;
break ;
case PTRACE_PEEKUSR : /* read register specified by addr. */
2010-10-28 02:34:06 +04:00
ret = ptrace_peekusr ( child , addr , datap ) ;
2008-02-13 00:17:07 +03:00
break ;
case PTRACE_POKETEXT : /* write the word at location addr. */
case PTRACE_POKEDATA :
ret = generic_ptrace_pokedata ( child , addr , data ) ;
break ;
case PTRACE_POKEUSR : /* write register specified by addr. */
ret = ptrace_pokeusr ( child , addr , data ) ;
2005-06-24 09:01:16 +04:00
break ;
case PTRACE_GETREGS :
2010-10-28 02:34:06 +04:00
ret = ptrace_getregs ( child , datap ) ;
2005-06-24 09:01:16 +04:00
break ;
case PTRACE_SETREGS :
2010-10-28 02:34:06 +04:00
ret = ptrace_setregs ( child , datap ) ;
2005-06-24 09:01:16 +04:00
break ;
2008-02-13 00:17:07 +03:00
case PTRACE_GETXTREGS :
2010-10-28 02:34:06 +04:00
ret = ptrace_getxregs ( child , datap ) ;
2005-06-24 09:01:16 +04:00
break ;
2008-02-13 00:17:07 +03:00
case PTRACE_SETXTREGS :
2010-10-28 02:34:06 +04:00
ret = ptrace_setxregs ( child , datap ) ;
2005-06-24 09:01:16 +04:00
break ;
default :
ret = ptrace_request ( child , request , addr , data ) ;
2008-02-13 00:17:07 +03:00
break ;
2005-06-24 09:01:16 +04:00
}
2008-02-13 00:17:07 +03:00
2005-06-24 09:01:16 +04:00
return ret ;
}
void do_syscall_trace ( void )
{
/*
* The 0x80 provides a way for the tracing parent to distinguish
* between a syscall stop and SIGTRAP delivery
*/
ptrace_notify ( SIGTRAP | ( ( current - > ptrace & PT_TRACESYSGOOD ) ? 0x80 : 0 ) ) ;
/*
* this isn ' t the same as continuing with a signal , but it will do
* for normal use . strace only continues with a signal if the
* stopping signal is not SIGTRAP . - brl
*/
if ( current - > exit_code ) {
send_sig ( current - > exit_code , current , 1 ) ;
current - > exit_code = 0 ;
}
}
2006-12-10 13:18:52 +03:00
void do_syscall_trace_enter ( struct pt_regs * regs )
{
if ( test_thread_flag ( TIF_SYSCALL_TRACE )
& & ( current - > ptrace & PT_PTRACED ) )
do_syscall_trace ( ) ;
#if 0
2012-01-03 23:23:06 +04:00
audit_syscall_entry ( current , AUDIT_ARCH_XTENSA . . ) ;
2006-12-10 13:18:52 +03:00
# endif
}
void do_syscall_trace_leave ( struct pt_regs * regs )
{
if ( ( test_thread_flag ( TIF_SYSCALL_TRACE ) )
& & ( current - > ptrace & PT_PTRACED ) )
do_syscall_trace ( ) ;
}