2005-04-16 15:20:36 -07:00
/*
* linux / arch / m68k / kernel / ptrace . c
*
* Copyright ( C ) 1994 by Hamish Macdonald
* Taken from linux / kernel / ptrace . c and modified for M680x0 .
* linux / kernel / ptrace . c is by Ross Biro 1 / 23 / 92 , edited by Linus Torvalds
*
* 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/sched.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/errno.h>
# include <linux/ptrace.h>
# include <linux/user.h>
2005-05-01 08:59:14 -07:00
# include <linux/signal.h>
2005-04-16 15:20:36 -07:00
# include <asm/uaccess.h>
# include <asm/page.h>
# include <asm/pgtable.h>
# include <asm/system.h>
# include <asm/processor.h>
/*
* does not yet catch signals sent when the child dies .
* in exit . c or in signal . c .
*/
/* determines which bits in the SR the user has access to. */
/* 1 = access 0 = no access */
# define SR_MASK 0x001f
/* sets the trace bits. */
2009-05-10 21:14:52 +02:00
# define TRACE_BITS 0xC000
# define T1_BIT 0x8000
# define T0_BIT 0x4000
2005-04-16 15:20:36 -07:00
/* Find the stack offset for a register, relative to thread.esp0. */
# define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg)
# define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \
- sizeof ( struct switch_stack ) )
/* Mapping from PT_xxx to the stack offset at which the register is
saved . Notice that usp has no stack - slot and needs to be treated
specially ( see get_reg / put_reg below ) . */
2009-05-19 15:38:01 +02:00
static const int regoff [ ] = {
2005-04-16 15:20:36 -07:00
[ 0 ] = PT_REG ( d1 ) ,
[ 1 ] = PT_REG ( d2 ) ,
[ 2 ] = PT_REG ( d3 ) ,
[ 3 ] = PT_REG ( d4 ) ,
[ 4 ] = PT_REG ( d5 ) ,
[ 5 ] = SW_REG ( d6 ) ,
[ 6 ] = SW_REG ( d7 ) ,
[ 7 ] = PT_REG ( a0 ) ,
[ 8 ] = PT_REG ( a1 ) ,
[ 9 ] = PT_REG ( a2 ) ,
[ 10 ] = SW_REG ( a3 ) ,
[ 11 ] = SW_REG ( a4 ) ,
[ 12 ] = SW_REG ( a5 ) ,
[ 13 ] = SW_REG ( a6 ) ,
[ 14 ] = PT_REG ( d0 ) ,
[ 15 ] = - 1 ,
[ 16 ] = PT_REG ( orig_d0 ) ,
[ 17 ] = PT_REG ( sr ) ,
[ 18 ] = PT_REG ( pc ) ,
} ;
/*
* Get contents of register REGNO in task TASK .
*/
static inline long get_reg ( struct task_struct * task , int regno )
{
unsigned long * addr ;
if ( regno = = PT_USP )
addr = & task - > thread . usp ;
2007-02-10 01:43:47 -08:00
else if ( regno < ARRAY_SIZE ( regoff ) )
2005-04-16 15:20:36 -07:00
addr = ( unsigned long * ) ( task - > thread . esp0 + regoff [ regno ] ) ;
else
return 0 ;
2009-05-19 15:38:01 +02:00
/* Need to take stkadj into account. */
if ( regno = = PT_SR | | regno = = PT_PC ) {
long stkadj = * ( long * ) ( task - > thread . esp0 + PT_REG ( stkadj ) ) ;
addr = ( unsigned long * ) ( ( unsigned long ) addr + stkadj ) ;
/* The sr is actually a 16 bit register. */
if ( regno = = PT_SR )
return * ( unsigned short * ) addr ;
}
2005-04-16 15:20:36 -07:00
return * addr ;
}
/*
* Write contents of register REGNO in task TASK .
*/
static inline int put_reg ( struct task_struct * task , int regno ,
unsigned long data )
{
unsigned long * addr ;
if ( regno = = PT_USP )
addr = & task - > thread . usp ;
2007-02-10 01:43:47 -08:00
else if ( regno < ARRAY_SIZE ( regoff ) )
2005-09-03 15:57:07 -07:00
addr = ( unsigned long * ) ( task - > thread . esp0 + regoff [ regno ] ) ;
2005-04-16 15:20:36 -07:00
else
return - 1 ;
2009-05-19 15:38:01 +02:00
/* Need to take stkadj into account. */
if ( regno = = PT_SR | | regno = = PT_PC ) {
long stkadj = * ( long * ) ( task - > thread . esp0 + PT_REG ( stkadj ) ) ;
addr = ( unsigned long * ) ( ( unsigned long ) addr + stkadj ) ;
/* The sr is actually a 16 bit register. */
if ( regno = = PT_SR ) {
* ( unsigned short * ) addr = data ;
return 0 ;
}
}
2005-04-16 15:20:36 -07:00
* addr = data ;
return 0 ;
}
/*
* Make sure the single step bit is not set .
*/
2005-09-03 15:57:08 -07:00
static inline void singlestep_disable ( struct task_struct * child )
2005-04-16 15:20:36 -07:00
{
2009-05-19 15:38:01 +02:00
unsigned long tmp = get_reg ( child , PT_SR ) & ~ TRACE_BITS ;
2005-04-16 15:20:36 -07:00
put_reg ( child , PT_SR , tmp ) ;
2005-11-13 16:06:59 -08:00
clear_tsk_thread_flag ( child , TIF_DELAYED_TRACE ) ;
2005-09-03 15:57:08 -07:00
}
/*
* Called by kernel / ptrace . c when detaching . .
*/
void ptrace_disable ( struct task_struct * child )
{
singlestep_disable ( child ) ;
2005-04-16 15:20:36 -07:00
}
2009-05-10 21:14:52 +02:00
void user_enable_single_step ( struct task_struct * child )
{
2009-05-19 15:38:01 +02:00
unsigned long tmp = get_reg ( child , PT_SR ) & ~ TRACE_BITS ;
put_reg ( child , PT_SR , tmp | T1_BIT ) ;
2009-05-10 21:14:52 +02:00
set_tsk_thread_flag ( child , TIF_DELAYED_TRACE ) ;
}
void user_enable_block_step ( struct task_struct * child )
{
2009-05-19 15:38:01 +02:00
unsigned long tmp = get_reg ( child , PT_SR ) & ~ TRACE_BITS ;
put_reg ( child , PT_SR , tmp | T0_BIT ) ;
2009-05-10 21:14:52 +02:00
}
void user_disable_single_step ( struct task_struct * child )
{
singlestep_disable ( child ) ;
}
2010-10-27 15:33:47 -07:00
long arch_ptrace ( struct task_struct * child , long request ,
unsigned long addr , unsigned long data )
2005-04-16 15:20:36 -07:00
{
2005-09-03 15:57:08 -07:00
unsigned long tmp ;
int i , ret = 0 ;
2005-04-16 15:20:36 -07:00
switch ( request ) {
/* read the word at location addr in the USER area. */
2005-09-03 15:57:08 -07:00
case PTRACE_PEEKUSR :
if ( addr & 3 )
goto out_eio ;
addr > > = 2 ; /* temporary hack. */
2005-04-16 15:20:36 -07:00
2005-09-03 15:57:08 -07:00
if ( addr > = 0 & & addr < 19 ) {
2005-09-03 15:57:07 -07:00
tmp = get_reg ( child , addr ) ;
} else if ( addr > = 21 & & addr < 49 ) {
tmp = child - > thread . fp [ addr - 21 ] ;
/* Convert internal fpu reg representation
* into long double format
*/
if ( FPU_IS_EMU & & ( addr < 45 ) & & ! ( addr % 3 ) )
tmp = ( ( tmp & 0xffff0000 ) < < 15 ) |
( ( tmp & 0x0000ffff ) < < 16 ) ;
} else
2009-05-19 15:38:01 +02:00
goto out_eio ;
2005-09-03 15:57:07 -07:00
ret = put_user ( tmp , ( unsigned long * ) data ) ;
break ;
2005-04-16 15:20:36 -07:00
2005-09-03 15:57:07 -07:00
case PTRACE_POKEUSR : /* write the word at location addr in the USER area */
2005-09-03 15:57:08 -07:00
if ( addr & 3 )
goto out_eio ;
addr > > = 2 ; /* temporary hack. */
2005-04-16 15:20:36 -07:00
2005-09-03 15:57:07 -07:00
if ( addr = = PT_SR ) {
data & = SR_MASK ;
2009-05-19 15:38:01 +02:00
data | = get_reg ( child , PT_SR ) & ~ SR_MASK ;
}
if ( addr > = 0 & & addr < 19 ) {
2005-09-03 15:57:07 -07:00
if ( put_reg ( child , addr , data ) )
2005-09-03 15:57:08 -07:00
goto out_eio ;
} else if ( addr > = 21 & & addr < 48 ) {
2005-09-03 15:57:07 -07:00
/* Convert long double format
* into internal fpu reg representation
*/
if ( FPU_IS_EMU & & ( addr < 45 ) & & ! ( addr % 3 ) ) {
2010-10-27 15:33:47 -07:00
data < < = 15 ;
2005-09-03 15:57:07 -07:00
data = ( data & 0xffff0000 ) |
( ( data & 0x0000ffff ) > > 1 ) ;
}
child - > thread . fp [ addr - 21 ] = data ;
2005-09-03 15:57:08 -07:00
} else
goto out_eio ;
2005-09-03 15:57:07 -07:00
break ;
2005-04-16 15:20:36 -07:00
2005-09-03 15:57:08 -07:00
case PTRACE_GETREGS : /* Get all gp regs from the child. */
2005-09-03 15:57:07 -07:00
for ( i = 0 ; i < 19 ; i + + ) {
tmp = get_reg ( child , i ) ;
2005-09-03 15:57:08 -07:00
ret = put_user ( tmp , ( unsigned long * ) data ) ;
if ( ret )
2005-04-16 15:20:36 -07:00
break ;
2010-10-27 15:33:47 -07:00
data + = sizeof ( unsigned long ) ;
2005-04-16 15:20:36 -07:00
}
2005-09-03 15:57:07 -07:00
break ;
2005-04-16 15:20:36 -07:00
2005-09-03 15:57:08 -07:00
case PTRACE_SETREGS : /* Set all gp regs in the child. */
2005-09-03 15:57:07 -07:00
for ( i = 0 ; i < 19 ; i + + ) {
2005-09-03 15:57:08 -07:00
ret = get_user ( tmp , ( unsigned long * ) data ) ;
if ( ret )
2005-04-16 15:20:36 -07:00
break ;
2005-09-03 15:57:07 -07:00
if ( i = = PT_SR ) {
2005-04-16 15:20:36 -07:00
tmp & = SR_MASK ;
2009-05-19 15:38:01 +02:00
tmp | = get_reg ( child , PT_SR ) & ~ SR_MASK ;
2005-04-16 15:20:36 -07:00
}
2005-09-03 15:57:07 -07:00
put_reg ( child , i , tmp ) ;
2010-10-27 15:33:47 -07:00
data + = sizeof ( unsigned long ) ;
2005-04-16 15:20:36 -07:00
}
2005-09-03 15:57:07 -07:00
break ;
2005-04-16 15:20:36 -07:00
2005-09-03 15:57:08 -07:00
case PTRACE_GETFPREGS : /* Get the child FPU state. */
2005-09-03 15:57:07 -07:00
if ( copy_to_user ( ( void * ) data , & child - > thread . fp ,
sizeof ( struct user_m68kfp_struct ) ) )
ret = - EFAULT ;
break ;
2005-04-16 15:20:36 -07:00
2005-09-03 15:57:08 -07:00
case PTRACE_SETFPREGS : /* Set the child FPU state. */
2005-09-03 15:57:07 -07:00
if ( copy_from_user ( & child - > thread . fp , ( void * ) data ,
sizeof ( struct user_m68kfp_struct ) ) )
ret = - EFAULT ;
break ;
2005-04-16 15:20:36 -07:00
2009-12-07 00:24:27 -08:00
case PTRACE_GET_THREAD_AREA :
ret = put_user ( task_thread_info ( child ) - > tp_value ,
( unsigned long __user * ) data ) ;
break ;
2005-09-03 15:57:07 -07:00
default :
ret = ptrace_request ( child , request , addr , data ) ;
break ;
2005-04-16 15:20:36 -07:00
}
2005-11-07 00:59:47 -08:00
2005-04-16 15:20:36 -07:00
return ret ;
2005-09-03 15:57:08 -07:00
out_eio :
2005-11-07 00:59:47 -08:00
return - EIO ;
2005-04-16 15:20:36 -07:00
}
asmlinkage void syscall_trace ( void )
{
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 ;
}
}