2005-04-16 15:20:36 -07:00
/*
* 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 ) 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 .
* Copyright ( C ) 2000 Ulf Carlsson
*
* At this time Linux / MIPS64 only supports syscall tracing , even for 32 - bit
* binaries .
*/
# include <linux/compiler.h>
2008-08-17 16:49:25 +02:00
# include <linux/compat.h>
2005-04-16 15:20:36 -07:00
# 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/user.h>
# include <linux/security.h>
# include <asm/cpu.h>
2005-05-31 11:49:19 +00:00
# include <asm/dsp.h>
2005-04-16 15:20:36 -07:00
# include <asm/fpu.h>
# include <asm/mipsregs.h>
2005-10-06 17:39:32 +01:00
# include <asm/mipsmtregs.h>
2005-04-16 15:20:36 -07:00
# include <asm/pgtable.h>
# include <asm/page.h>
# include <asm/uaccess.h>
# include <asm/bootinfo.h>
/*
* Tracing a 32 - bit process with a 64 - bit strace and vice versa will not
* work . I don ' t know how to fix this .
*/
2008-08-17 16:49:25 +02:00
long compat_arch_ptrace ( struct task_struct * child , compat_long_t request ,
compat_ulong_t caddr , compat_ulong_t cdata )
2005-04-16 15:20:36 -07:00
{
2008-08-17 16:49:25 +02:00
int addr = caddr ;
int data = cdata ;
2005-04-16 15:20:36 -07:00
int ret ;
switch ( request ) {
2005-09-28 18:11:15 -04:00
/*
* Read 4 bytes of the other process ' storage
* data is a pointer specifying where the user wants the
* 4 bytes copied into
* addr is a pointer in the user ' s storage that contains an 8 byte
* address in the other process of the 4 bytes that is to be read
* ( this is run in a 32 - bit process looking at a 64 - bit process )
* when I and D space are separate , these will need to be fixed .
*/
case PTRACE_PEEKTEXT_3264 :
case PTRACE_PEEKDATA_3264 : {
u32 tmp ;
int copied ;
u32 __user * addrOthers ;
ret = - EIO ;
/* Get the addr in the other process that we want to read */
if ( get_user ( addrOthers , ( u32 __user * __user * ) ( unsigned long ) addr ) ! = 0 )
break ;
copied = access_process_vm ( child , ( u64 ) addrOthers , & tmp ,
sizeof ( tmp ) , 0 ) ;
if ( copied ! = sizeof ( tmp ) )
break ;
ret = put_user ( tmp , ( u32 __user * ) ( unsigned long ) data ) ;
break ;
}
2005-04-16 15:20:36 -07:00
/* Read the word at location addr in the USER area. */
case PTRACE_PEEKUSR : {
struct pt_regs * regs ;
2014-02-13 11:26:41 +00:00
union fpureg * fregs ;
2005-04-16 15:20:36 -07:00
unsigned int tmp ;
2006-01-12 01:06:07 -08:00
regs = task_pt_regs ( child ) ;
2005-04-16 15:20:36 -07:00
ret = 0 ; /* Default return value. */
switch ( addr ) {
case 0 . . . 31 :
tmp = regs - > regs [ addr ] ;
break ;
case FPR_BASE . . . FPR_BASE + 31 :
2013-11-22 13:12:07 +00:00
if ( ! tsk_used_math ( child ) ) {
/* FP not yet used */
tmp = - 1 ;
break ;
}
fregs = get_fpu_regs ( child ) ;
if ( test_thread_flag ( TIF_32BIT_FPREGS ) ) {
2005-04-16 15:20:36 -07:00
/*
* The odd registers are actually the high
* order bits of the values stored in the even
* registers - unless we ' re using r2k_switch . S .
*/
2014-02-13 11:26:41 +00:00
tmp = get_fpr32 ( & fregs [ ( addr & ~ 1 ) - FPR_BASE ] ,
addr & 1 ) ;
2013-11-22 13:12:07 +00:00
break ;
2005-04-16 15:20:36 -07:00
}
2014-02-13 11:26:41 +00:00
tmp = get_fpr32 ( & fregs [ addr - FPR_BASE ] , 0 ) ;
2005-04-16 15:20:36 -07:00
break ;
case PC :
tmp = regs - > cp0_epc ;
break ;
case CAUSE :
tmp = regs - > cp0_cause ;
break ;
case BADVADDR :
tmp = regs - > cp0_badvaddr ;
break ;
case MMHI :
tmp = regs - > hi ;
break ;
case MMLO :
tmp = regs - > lo ;
break ;
case FPC_CSR :
2006-05-16 01:26:03 +09:00
tmp = child - > thread . fpu . fcr31 ;
2005-04-16 15:20:36 -07:00
break ;
2013-11-19 17:30:35 +00:00
case FPC_EIR :
/* implementation / version register */
tmp = current_cpu_data . fpu_id ;
2005-04-16 15:20:36 -07:00
break ;
2006-01-29 22:34:32 +09:00
case DSP_BASE . . . DSP_BASE + 5 : {
dspreg_t * dregs ;
2005-05-31 11:49:19 +00:00
if ( ! cpu_has_dsp ) {
tmp = 0 ;
ret = - EIO ;
2008-08-17 16:49:25 +02:00
goto out ;
2005-05-31 11:49:19 +00:00
}
2006-01-29 22:34:32 +09:00
dregs = __get_dsp_regs ( child ) ;
2005-12-05 13:47:25 +00:00
tmp = ( unsigned long ) ( dregs [ addr - DSP_BASE ] ) ;
2005-05-31 11:49:19 +00:00
break ;
2006-01-29 22:34:32 +09:00
}
2005-05-31 11:49:19 +00:00
case DSP_CONTROL :
if ( ! cpu_has_dsp ) {
tmp = 0 ;
ret = - EIO ;
2008-08-17 16:49:25 +02:00
goto out ;
2005-05-31 11:49:19 +00:00
}
tmp = child - > thread . dsp . dspcontrol ;
break ;
2005-04-16 15:20:36 -07:00
default :
tmp = 0 ;
ret = - EIO ;
2008-08-17 16:49:25 +02:00
goto out ;
2005-04-16 15:20:36 -07:00
}
2006-01-29 22:34:32 +09:00
ret = put_user ( tmp , ( unsigned __user * ) ( unsigned long ) data ) ;
2005-04-16 15:20:36 -07:00
break ;
}
2005-09-28 18:11:15 -04:00
/*
* Write 4 bytes into the other process ' storage
* data is the 4 bytes that the user wants written
* addr is a pointer in the user ' s storage that contains an
* 8 byte address in the other process where the 4 bytes
* that is to be written
* ( this is run in a 32 - bit process looking at a 64 - bit process )
* when I and D space are separate , these will need to be fixed .
*/
case PTRACE_POKETEXT_3264 :
case PTRACE_POKEDATA_3264 : {
u32 __user * addrOthers ;
/* Get the addr in the other process that we want to write into */
ret = - EIO ;
if ( get_user ( addrOthers , ( u32 __user * __user * ) ( unsigned long ) addr ) ! = 0 )
break ;
ret = 0 ;
if ( access_process_vm ( child , ( u64 ) addrOthers , & data ,
sizeof ( data ) , 1 ) = = sizeof ( data ) )
break ;
ret = - EIO ;
break ;
}
2005-04-16 15:20:36 -07:00
case PTRACE_POKEUSR : {
struct pt_regs * regs ;
ret = 0 ;
2006-01-12 01:06:07 -08:00
regs = task_pt_regs ( child ) ;
2005-04-16 15:20:36 -07:00
switch ( addr ) {
case 0 . . . 31 :
regs - > regs [ addr ] = data ;
break ;
case FPR_BASE . . . FPR_BASE + 31 : {
2014-02-13 11:26:41 +00:00
union fpureg * fregs = get_fpu_regs ( child ) ;
2005-04-16 15:20:36 -07:00
if ( ! tsk_used_math ( child ) ) {
/* FP not yet used */
2006-05-16 01:26:03 +09:00
memset ( & child - > thread . fpu , ~ 0 ,
sizeof ( child - > thread . fpu ) ) ;
child - > thread . fpu . fcr31 = 0 ;
2005-04-16 15:20:36 -07:00
}
2013-11-22 13:12:07 +00:00
if ( test_thread_flag ( TIF_32BIT_FPREGS ) ) {
/*
* The odd registers are actually the high
* order bits of the values stored in the even
* registers - unless we ' re using r2k_switch . S .
*/
2014-02-13 11:26:41 +00:00
set_fpr32 ( & fregs [ ( addr & ~ 1 ) - FPR_BASE ] ,
addr & 1 , data ) ;
2013-11-22 13:12:07 +00:00
break ;
2005-04-16 15:20:36 -07:00
}
2014-02-13 11:26:41 +00:00
set_fpr64 ( & fregs [ addr - FPR_BASE ] , 0 , data ) ;
2005-04-16 15:20:36 -07:00
break ;
}
case PC :
regs - > cp0_epc = data ;
break ;
case MMHI :
regs - > hi = data ;
break ;
case MMLO :
regs - > lo = data ;
break ;
case FPC_CSR :
2006-05-16 01:26:03 +09:00
child - > thread . fpu . fcr31 = data ;
2005-04-16 15:20:36 -07:00
break ;
2006-01-29 22:34:32 +09:00
case DSP_BASE . . . DSP_BASE + 5 : {
dspreg_t * dregs ;
2005-05-31 11:49:19 +00:00
if ( ! cpu_has_dsp ) {
ret = - EIO ;
break ;
}
2006-01-29 22:34:32 +09:00
dregs = __get_dsp_regs ( child ) ;
2005-05-31 11:49:19 +00:00
dregs [ addr - DSP_BASE ] = data ;
break ;
2006-01-29 22:34:32 +09:00
}
2005-05-31 11:49:19 +00:00
case DSP_CONTROL :
if ( ! cpu_has_dsp ) {
ret = - EIO ;
break ;
}
child - > thread . dsp . dspcontrol = data ;
break ;
2005-04-16 15:20:36 -07:00
default :
/* The rest are not allowed. */
ret = - EIO ;
break ;
}
break ;
}
2005-09-28 18:11:15 -04:00
case PTRACE_GETREGS :
2007-10-26 00:53:02 +09:00
ret = ptrace_getregs ( child , ( __s64 __user * ) ( __u64 ) data ) ;
2005-09-28 18:11:15 -04:00
break ;
case PTRACE_SETREGS :
2007-10-26 00:53:02 +09:00
ret = ptrace_setregs ( child , ( __s64 __user * ) ( __u64 ) data ) ;
2005-09-28 18:11:15 -04:00
break ;
case PTRACE_GETFPREGS :
2007-10-11 23:46:15 +01:00
ret = ptrace_getfpregs ( child , ( __u32 __user * ) ( __u64 ) data ) ;
2005-09-28 18:11:15 -04:00
break ;
case PTRACE_SETFPREGS :
2007-10-11 23:46:15 +01:00
ret = ptrace_setfpregs ( child , ( __u32 __user * ) ( __u64 ) data ) ;
2005-09-28 18:11:15 -04:00
break ;
2005-04-13 17:43:59 +00:00
case PTRACE_GET_THREAD_AREA :
2006-01-12 01:06:07 -08:00
ret = put_user ( task_thread_info ( child ) - > tp_value ,
2005-04-13 17:43:59 +00:00
( unsigned int __user * ) ( unsigned long ) data ) ;
break ;
2005-09-28 18:11:15 -04:00
case PTRACE_GET_THREAD_AREA_3264 :
2006-01-12 01:06:07 -08:00
ret = put_user ( task_thread_info ( child ) - > tp_value ,
2005-09-28 18:11:15 -04:00
( unsigned long __user * ) ( unsigned long ) data ) ;
break ;
2008-09-23 00:11:26 -07:00
case PTRACE_GET_WATCH_REGS :
ret = ptrace_get_watch_regs ( child ,
( struct pt_watch_regs __user * ) ( unsigned long ) addr ) ;
break ;
case PTRACE_SET_WATCH_REGS :
ret = ptrace_set_watch_regs ( child ,
( struct pt_watch_regs __user * ) ( unsigned long ) addr ) ;
break ;
2005-04-16 15:20:36 -07:00
default :
2008-11-13 11:50:12 -08:00
ret = compat_ptrace_request ( child , request , addr , data ) ;
2005-04-16 15:20:36 -07:00
break ;
}
out :
return ret ;
}