2005-06-23 22:01:16 -07: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 .
*
* Copyright ( C ) 2001 - 2005 Tensilica Inc .
*
* 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 02:58:56 -07:00
# include <linux/signal.h>
2005-06-23 22:01:16 -07:00
# include <asm/pgtable.h>
# include <asm/page.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# include <asm/ptrace.h>
# include <asm/elf.h>
# define TEST_KERNEL // verify kernel operations FIXME: remove
/*
* Called by kernel / ptrace . c when detaching . .
*
* Make sure single step bits etc are not set .
*/
void ptrace_disable ( struct task_struct * child )
{
/* Nothing to do.. */
}
2005-11-07 00:59:47 -08:00
long arch_ptrace ( struct task_struct * child , long request , long addr , long data )
2005-06-23 22:01:16 -07:00
{
int ret = - EPERM ;
switch ( request ) {
case PTRACE_PEEKTEXT : /* read word at location addr. */
case PTRACE_PEEKDATA :
2007-07-17 04:03:43 -07:00
ret = generic_ptrace_peekdata ( child , addr , data ) ;
2005-06-23 22:01:16 -07:00
goto out ;
/* Read the word at location addr in the USER area. */
case PTRACE_PEEKUSR :
{
struct pt_regs * regs ;
unsigned long tmp ;
2006-01-12 01:05:50 -08:00
regs = task_pt_regs ( child ) ;
2005-06-23 22:01:16 -07:00
tmp = 0 ; /* Default return value. */
switch ( addr ) {
case REG_AR_BASE . . . REG_AR_BASE + XCHAL_NUM_AREGS - 1 :
{
int ar = addr - REG_AR_BASE - regs - > windowbase * 4 ;
ar & = ( XCHAL_NUM_AREGS - 1 ) ;
if ( ar < 16 & & ar + ( regs - > wmask > > 4 ) * 4 > = 0 )
tmp = regs - > areg [ ar ] ;
else
ret = - EIO ;
break ;
}
case REG_A_BASE . . . REG_A_BASE + 15 :
tmp = regs - > areg [ addr - REG_A_BASE ] ;
break ;
case REG_PC :
tmp = regs - > pc ;
break ;
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 02:18:48 -08:00
tmp = ( regs - > ps & ~ ( 1 < < PS_EXCM_BIT ) ) ;
2005-06-23 22:01:16 -07:00
break ;
case REG_WB :
tmp = regs - > windowbase ;
break ;
case REG_WS :
tmp = regs - > windowstart ;
break ;
case REG_LBEG :
tmp = regs - > lbeg ;
break ;
case REG_LEND :
tmp = regs - > lend ;
break ;
case REG_LCOUNT :
tmp = regs - > lcount ;
break ;
case REG_SAR :
tmp = regs - > sar ;
break ;
case REG_DEPC :
tmp = regs - > depc ;
break ;
case REG_EXCCAUSE :
tmp = regs - > exccause ;
break ;
case REG_EXCVADDR :
tmp = regs - > excvaddr ;
break ;
case SYSCALL_NR :
tmp = regs - > syscall ;
break ;
default :
tmp = 0 ;
ret = - EIO ;
goto out ;
}
ret = put_user ( tmp , ( unsigned long * ) data ) ;
goto out ;
}
case PTRACE_POKETEXT : /* write the word at location addr. */
case PTRACE_POKEDATA :
2007-07-17 04:03:44 -07:00
ret = generic_ptrace_pokedata ( child , addr , data ) ;
2005-06-23 22:01:16 -07:00
goto out ;
case PTRACE_POKEUSR :
{
struct pt_regs * regs ;
2006-01-12 01:05:50 -08:00
regs = task_pt_regs ( child ) ;
2005-06-23 22:01:16 -07:00
switch ( addr ) {
case REG_AR_BASE . . . REG_AR_BASE + XCHAL_NUM_AREGS - 1 :
{
int ar = addr - REG_AR_BASE - regs - > windowbase * 4 ;
if ( ar < 16 & & ar + ( regs - > wmask > > 4 ) * 4 > = 0 )
regs - > areg [ ar & ( XCHAL_NUM_AREGS - 1 ) ] = data ;
else
ret = - EIO ;
break ;
}
case REG_A_BASE . . . REG_A_BASE + 15 :
regs - > areg [ addr - REG_A_BASE ] = data ;
break ;
case REG_PC :
regs - > pc = data ;
break ;
case SYSCALL_NR :
regs - > syscall = data ;
break ;
# ifdef TEST_KERNEL
case REG_WB :
regs - > windowbase = data ;
break ;
case REG_WS :
regs - > windowstart = data ;
break ;
# endif
default :
/* The rest are not allowed. */
ret = - EIO ;
break ;
}
break ;
}
/* continue and stop at next (return from) syscall */
case PTRACE_SYSCALL :
case PTRACE_CONT : /* restart after signal. */
{
ret = - EIO ;
2005-06-30 02:58:56 -07:00
if ( ! valid_signal ( data ) )
2005-06-23 22:01:16 -07:00
break ;
if ( request = = PTRACE_SYSCALL )
set_tsk_thread_flag ( child , TIF_SYSCALL_TRACE ) ;
else
clear_tsk_thread_flag ( child , TIF_SYSCALL_TRACE ) ;
child - > exit_code = data ;
/* Make sure the single step bit is not set. */
child - > ptrace & = ~ PT_SINGLESTEP ;
wake_up_process ( child ) ;
ret = 0 ;
break ;
}
/*
* make the child exit . Best I can do is send it a sigkill .
* perhaps it should be put in the status that it wants to
* exit .
*/
case PTRACE_KILL :
ret = 0 ;
2006-08-31 21:27:39 -07:00
if ( child - > exit_state = = EXIT_ZOMBIE ) /* already dead */
2005-06-23 22:01:16 -07:00
break ;
child - > exit_code = SIGKILL ;
child - > ptrace & = ~ PT_SINGLESTEP ;
wake_up_process ( child ) ;
break ;
case PTRACE_SINGLESTEP :
ret = - EIO ;
2005-06-30 02:58:56 -07:00
if ( ! valid_signal ( data ) )
2005-06-23 22:01:16 -07:00
break ;
clear_tsk_thread_flag ( child , TIF_SYSCALL_TRACE ) ;
child - > ptrace | = PT_SINGLESTEP ;
child - > exit_code = data ;
wake_up_process ( child ) ;
ret = 0 ;
break ;
case PTRACE_GETREGS :
{
/* 'data' points to user memory in which to write.
* Mainly due to the non - live register values , we
* reformat the register values into something more
* standard . For convenience , we use the handy
* elf_gregset_t format . */
xtensa_gregset_t format ;
2006-01-12 01:05:50 -08:00
struct pt_regs * regs = task_pt_regs ( child ) ;
2005-06-23 22:01:16 -07:00
do_copy_regs ( & format , regs , child ) ;
/* Now, copy to user space nice and easy... */
ret = 0 ;
if ( copy_to_user ( ( void * ) data , & format , sizeof ( elf_gregset_t ) ) )
ret = - EFAULT ;
break ;
}
case PTRACE_SETREGS :
{
/* 'data' points to user memory that contains the new
* values in the elf_gregset_t format . */
xtensa_gregset_t format ;
2006-01-12 01:05:50 -08:00
struct pt_regs * regs = task_pt_regs ( child ) ;
2005-06-23 22:01:16 -07:00
if ( copy_from_user ( & format , ( void * ) data , sizeof ( elf_gregset_t ) ) ) {
ret = - EFAULT ;
break ;
}
/* FIXME: Perhaps we want some sanity checks on
* these user - space values ? See ARM version . Are
* debuggers a security concern ? */
do_restore_regs ( & format , regs , child ) ;
ret = 0 ;
break ;
}
case PTRACE_GETFPREGS :
{
/* 'data' points to user memory in which to write.
* For convenience , we use the handy
* elf_fpregset_t format . */
elf_fpregset_t fpregs ;
2006-01-12 01:05:50 -08:00
struct pt_regs * regs = task_pt_regs ( child ) ;
2005-06-23 22:01:16 -07:00
do_save_fpregs ( & fpregs , regs , child ) ;
/* Now, copy to user space nice and easy... */
ret = 0 ;
if ( copy_to_user ( ( void * ) data , & fpregs , sizeof ( elf_fpregset_t ) ) )
ret = - EFAULT ;
break ;
}
case PTRACE_SETFPREGS :
{
/* 'data' points to user memory that contains the new
* values in the elf_fpregset_t format .
*/
elf_fpregset_t fpregs ;
2006-01-12 01:05:50 -08:00
struct pt_regs * regs = task_pt_regs ( child ) ;
2005-06-23 22:01:16 -07:00
ret = 0 ;
if ( copy_from_user ( & fpregs , ( void * ) data , sizeof ( elf_fpregset_t ) ) ) {
ret = - EFAULT ;
break ;
}
if ( do_restore_fpregs ( & fpregs , regs , child ) )
ret = - EIO ;
break ;
}
case PTRACE_GETFPREGSIZE :
/* 'data' points to 'unsigned long' set to the size
* of elf_fpregset_t
*/
ret = put_user ( sizeof ( elf_fpregset_t ) , ( unsigned long * ) data ) ;
break ;
default :
ret = ptrace_request ( child , request , addr , data ) ;
goto out ;
}
2005-11-07 00:59:47 -08:00
out :
2005-06-23 22:01:16 -07: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 02:18:52 -08: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
if ( unlikely ( current - > audit_context ) )
audit_syscall_entry ( current , AUDIT_ARCH_XTENSA . . ) ;
# endif
}
void do_syscall_trace_leave ( struct pt_regs * regs )
{
if ( ( test_thread_flag ( TIF_SYSCALL_TRACE ) )
& & ( current - > ptrace & PT_PTRACED ) )
do_syscall_trace ( ) ;
}