2005-04-16 15:20:36 -07:00
/*
* arch / v850 / kernel / ptrace . c - - ` ptrace ' system call
*
* Copyright ( C ) 2002 , 03 , 04 NEC Electronics Corporation
* Copyright ( C ) 2002 , 03 , 04 Miles Bader < miles @ gnu . org >
*
* Derived from arch / mips / kernel / ptrace . c :
*
* Copyright ( C ) 1992 Ross Biro
* Copyright ( C ) Linus Torvalds
* Copyright ( C ) 1994 , 95 , 96 , 97 , 98 , 2000 Ralf Baechle
* Copyright ( C ) 1996 David S . Miller
* Kevin D . Kissell , kevink @ mips . com and Carsten Langgaard , carstenl @ mips . com
* Copyright ( C ) 1999 MIPS Technologies , Inc .
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/sched.h>
# include <linux/smp_lock.h>
# include <linux/ptrace.h>
2005-05-01 08:59:14 -07:00
# include <linux/signal.h>
2005-04-16 15:20:36 -07:00
# include <asm/errno.h>
# include <asm/ptrace.h>
# include <asm/processor.h>
# include <asm/uaccess.h>
/* Returns the address where the register at REG_OFFS in P is stashed away. */
static v850_reg_t * reg_save_addr ( unsigned reg_offs , struct task_struct * t )
{
struct pt_regs * regs ;
/* Three basic cases:
( 1 ) A register normally saved before calling the scheduler , is
available in the kernel entry pt_regs structure at the top
of the kernel stack . The kernel trap / irq exit path takes
care to save / restore almost all registers for ptrace ' d
processes .
( 2 ) A call - clobbered register , where the process P entered the
kernel via [ syscall ] trap , is not stored anywhere ; that ' s
OK , because such registers are not expected to be preserved
when the trap returns anyway ( so we don ' t actually bother to
test for this case ) .
( 3 ) A few registers not used at all by the kernel , and so
normally never saved except by context - switches , are in the
context switch state . */
if ( reg_offs = = PT_CTPC | | reg_offs = = PT_CTPSW | | reg_offs = = PT_CTBP )
/* Register saved during context switch. */
regs = thread_saved_regs ( t ) ;
else
/* Register saved during kernel entry (or not available). */
2006-01-12 01:05:51 -08:00
regs = task_pt_regs ( t ) ;
2005-04-16 15:20:36 -07:00
return ( v850_reg_t * ) ( ( char * ) regs + reg_offs ) ;
}
/* Set the bits SET and clear the bits CLEAR in the v850e DIR
( ` debug information register ' ) . Returns the new value of DIR . */
static inline v850_reg_t set_dir ( v850_reg_t set , v850_reg_t clear )
{
register v850_reg_t rval asm ( " r10 " ) ;
register v850_reg_t arg0 asm ( " r6 " ) = set ;
register v850_reg_t arg1 asm ( " r7 " ) = clear ;
/* The dbtrap handler has exactly this functionality when called
from kernel mode . 0xf840 is a ` dbtrap ' insn . */
asm ( " .short 0xf840 " : " =r " ( rval ) : " r " ( arg0 ) , " r " ( arg1 ) ) ;
return rval ;
}
/* Makes sure hardware single-stepping is (globally) enabled.
Returns true if successful . */
static inline int enable_single_stepping ( void )
{
static int enabled = 0 ; /* Remember whether we already did it. */
if ( ! enabled ) {
/* Turn on the SE (`single-step enable') bit, 0x100, in the
DIR ( ` debug information register ' ) . This may fail if a
processor doesn ' t support it or something . We also try
to clear bit 0x40 ( ` INI ' ) , which is necessary to use the
debug stuff on the v850e2 ; on the v850e , clearing 0x40
shouldn ' t cause any problem . */
v850_reg_t dir = set_dir ( 0x100 , 0x40 ) ;
/* Make sure it really got set. */
if ( dir & 0x100 )
enabled = 1 ;
}
return enabled ;
}
/* Try to set CHILD's single-step flag to VAL. Returns true if successful. */
static int set_single_step ( struct task_struct * t , int val )
{
v850_reg_t * psw_addr = reg_save_addr ( PT_PSW , t ) ;
if ( val ) {
/* Make sure single-stepping is enabled. */
if ( ! enable_single_stepping ( ) )
return 0 ;
/* Set T's single-step flag. */
* psw_addr | = 0x800 ;
} else
* psw_addr & = ~ 0x800 ;
return 1 ;
}
2005-11-07 00:59:47 -08:00
long arch_ptrace ( struct task_struct * child , long request , long addr , long data )
2005-04-16 15:20:36 -07:00
{
int rval ;
switch ( request ) {
unsigned long val , copied ;
case PTRACE_PEEKTEXT : /* read word at location addr. */
case PTRACE_PEEKDATA :
copied = access_process_vm ( child , addr , & val , sizeof ( val ) , 0 ) ;
rval = - EIO ;
if ( copied ! = sizeof ( val ) )
break ;
rval = put_user ( val , ( unsigned long * ) data ) ;
goto out ;
case PTRACE_POKETEXT : /* write the word at location addr. */
case PTRACE_POKEDATA :
rval = 0 ;
if ( access_process_vm ( child , addr , & data , sizeof ( data ) , 1 )
= = sizeof ( data ) )
break ;
rval = - EIO ;
goto out ;
/* Read/write the word at location ADDR in the registers. */
case PTRACE_PEEKUSR :
case PTRACE_POKEUSR :
rval = 0 ;
if ( addr > = PT_SIZE & & request = = PTRACE_PEEKUSR ) {
/* Special requests that don't actually correspond
to offsets in struct pt_regs . */
if ( addr = = PT_TEXT_ADDR )
val = child - > mm - > start_code ;
else if ( addr = = PT_DATA_ADDR )
val = child - > mm - > start_data ;
else if ( addr = = PT_TEXT_LEN )
val = child - > mm - > end_code
- child - > mm - > start_code ;
else
rval = - EIO ;
} else if ( addr > = 0 & & addr < PT_SIZE & & ( addr & 0x3 ) = = 0 ) {
v850_reg_t * reg_addr = reg_save_addr ( addr , child ) ;
if ( request = = PTRACE_PEEKUSR )
val = * reg_addr ;
else
* reg_addr = data ;
} else
rval = - EIO ;
if ( rval = = 0 & & request = = PTRACE_PEEKUSR )
rval = put_user ( val , ( unsigned long * ) data ) ;
goto out ;
/* Continue and stop at next (return from) syscall */
case PTRACE_SYSCALL :
/* Restart after a signal. */
case PTRACE_CONT :
/* Execute a single instruction. */
case PTRACE_SINGLESTEP :
rval = - EIO ;
2005-05-01 08:59:14 -07:00
if ( ! valid_signal ( data ) )
2005-04-16 15:20:36 -07:00
break ;
/* Turn CHILD's single-step flag on or off. */
if ( ! set_single_step ( child , request = = PTRACE_SINGLESTEP ) )
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 ;
wake_up_process ( child ) ;
rval = 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 :
rval = 0 ;
if ( child - > exit_state = = EXIT_ZOMBIE ) /* already dead */
break ;
child - > exit_code = SIGKILL ;
wake_up_process ( child ) ;
break ;
case PTRACE_DETACH : /* detach a process that was attached. */
set_single_step ( child , 0 ) ; /* Clear single-step flag */
rval = ptrace_detach ( child , data ) ;
break ;
default :
rval = - EIO ;
goto out ;
}
2005-11-07 00:59:47 -08:00
out :
2005-04-16 15:20:36 -07:00
return rval ;
}
asmlinkage void syscall_trace ( void )
{
if ( ! test_thread_flag ( TIF_SYSCALL_TRACE ) )
return ;
if ( ! ( current - > ptrace & PT_PTRACED ) )
return ;
/* 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 ;
}
}
void ptrace_disable ( struct task_struct * child )
{
/* nothing to do */
}