2005-04-17 02:20:36 +04: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 18:49:25 +04:00
# include <linux/compat.h>
2005-04-17 02:20:36 +04: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/security.h>
# include <asm/cpu.h>
2005-05-31 15:49:19 +04:00
# include <asm/dsp.h>
2005-04-17 02:20:36 +04:00
# include <asm/fpu.h>
# include <asm/mipsregs.h>
2005-10-06 20:39:32 +04:00
# include <asm/mipsmtregs.h>
2005-04-17 02:20:36 +04:00
# include <asm/pgtable.h>
# include <asm/page.h>
2014-07-23 17:40:15 +04:00
# include <asm/reg.h>
2005-04-17 02:20:36 +04:00
# 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 18:49:25 +04:00
long compat_arch_ptrace ( struct task_struct * child , compat_long_t request ,
compat_ulong_t caddr , compat_ulong_t cdata )
2005-04-17 02:20:36 +04:00
{
2008-08-17 18:49:25 +04:00
int addr = caddr ;
int data = cdata ;
2005-04-17 02:20:36 +04:00
int ret ;
switch ( request ) {
2005-09-29 02: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 ,
2016-10-13 03:20:20 +03:00
sizeof ( tmp ) , FOLL_FORCE ) ;
2005-09-29 02:11:15 +04:00
if ( copied ! = sizeof ( tmp ) )
break ;
ret = put_user ( tmp , ( u32 __user * ) ( unsigned long ) data ) ;
break ;
}
2005-04-17 02:20:36 +04:00
/* Read the word at location addr in the USER area. */
case PTRACE_PEEKUSR : {
struct pt_regs * regs ;
2014-02-13 15:26:41 +04:00
union fpureg * fregs ;
2005-04-17 02:20:36 +04:00
unsigned int tmp ;
2006-01-12 12:06:07 +03:00
regs = task_pt_regs ( child ) ;
2005-04-17 02:20:36 +04: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 17:12:07 +04: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-17 02:20:36 +04: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 15:26:41 +04:00
tmp = get_fpr32 ( & fregs [ ( addr & ~ 1 ) - FPR_BASE ] ,
addr & 1 ) ;
2013-11-22 17:12:07 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
2014-02-13 15:26:41 +04:00
tmp = get_fpr32 ( & fregs [ addr - FPR_BASE ] , 0 ) ;
2005-04-17 02:20:36 +04: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-15 20:26:03 +04:00
tmp = child - > thread . fpu . fcr31 ;
2005-04-17 02:20:36 +04:00
break ;
2013-11-19 21:30:35 +04:00
case FPC_EIR :
/* implementation / version register */
2014-07-23 17:40:06 +04:00
tmp = boot_cpu_data . fpu_id ;
2005-04-17 02:20:36 +04:00
break ;
2006-01-29 16:34:32 +03:00
case DSP_BASE . . . DSP_BASE + 5 : {
dspreg_t * dregs ;
2005-05-31 15:49:19 +04:00
if ( ! cpu_has_dsp ) {
tmp = 0 ;
ret = - EIO ;
2008-08-17 18:49:25 +04:00
goto out ;
2005-05-31 15:49:19 +04:00
}
2006-01-29 16:34:32 +03:00
dregs = __get_dsp_regs ( child ) ;
2005-12-05 16:47:25 +03:00
tmp = ( unsigned long ) ( dregs [ addr - DSP_BASE ] ) ;
2005-05-31 15:49:19 +04:00
break ;
2006-01-29 16:34:32 +03:00
}
2005-05-31 15:49:19 +04:00
case DSP_CONTROL :
if ( ! cpu_has_dsp ) {
tmp = 0 ;
ret = - EIO ;
2008-08-17 18:49:25 +04:00
goto out ;
2005-05-31 15:49:19 +04:00
}
tmp = child - > thread . dsp . dspcontrol ;
break ;
2005-04-17 02:20:36 +04:00
default :
tmp = 0 ;
ret = - EIO ;
2008-08-17 18:49:25 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
2006-01-29 16:34:32 +03:00
ret = put_user ( tmp , ( unsigned __user * ) ( unsigned long ) data ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2005-09-29 02: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 ,
2016-10-13 03:20:20 +03:00
sizeof ( data ) ,
FOLL_FORCE | FOLL_WRITE ) = = sizeof ( data ) )
2005-09-29 02:11:15 +04:00
break ;
ret = - EIO ;
break ;
}
2005-04-17 02:20:36 +04:00
case PTRACE_POKEUSR : {
struct pt_regs * regs ;
ret = 0 ;
2006-01-12 12:06:07 +03:00
regs = task_pt_regs ( child ) ;
2005-04-17 02:20:36 +04:00
switch ( addr ) {
case 0 . . . 31 :
regs - > regs [ addr ] = data ;
break ;
case FPR_BASE . . . FPR_BASE + 31 : {
2014-02-13 15:26:41 +04:00
union fpureg * fregs = get_fpu_regs ( child ) ;
2005-04-17 02:20:36 +04:00
if ( ! tsk_used_math ( child ) ) {
/* FP not yet used */
2006-05-15 20:26:03 +04:00
memset ( & child - > thread . fpu , ~ 0 ,
sizeof ( child - > thread . fpu ) ) ;
child - > thread . fpu . fcr31 = 0 ;
2005-04-17 02:20:36 +04:00
}
2013-11-22 17:12:07 +04: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 15:26:41 +04:00
set_fpr32 ( & fregs [ ( addr & ~ 1 ) - FPR_BASE ] ,
addr & 1 , data ) ;
2013-11-22 17:12:07 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
2014-02-13 15:26:41 +04:00
set_fpr64 ( & fregs [ addr - FPR_BASE ] , 0 , data ) ;
2005-04-17 02:20:36 +04: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-15 20:26:03 +04:00
child - > thread . fpu . fcr31 = data ;
2005-04-17 02:20:36 +04:00
break ;
2006-01-29 16:34:32 +03:00
case DSP_BASE . . . DSP_BASE + 5 : {
dspreg_t * dregs ;
2005-05-31 15:49:19 +04:00
if ( ! cpu_has_dsp ) {
ret = - EIO ;
break ;
}
2006-01-29 16:34:32 +03:00
dregs = __get_dsp_regs ( child ) ;
2005-05-31 15:49:19 +04:00
dregs [ addr - DSP_BASE ] = data ;
break ;
2006-01-29 16:34:32 +03:00
}
2005-05-31 15:49:19 +04:00
case DSP_CONTROL :
if ( ! cpu_has_dsp ) {
ret = - EIO ;
break ;
}
child - > thread . dsp . dspcontrol = data ;
break ;
2005-04-17 02:20:36 +04:00
default :
/* The rest are not allowed. */
ret = - EIO ;
break ;
}
break ;
}
2005-09-29 02:11:15 +04:00
case PTRACE_GETREGS :
2014-07-23 17:40:13 +04:00
ret = ptrace_getregs ( child ,
( struct user_pt_regs __user * ) ( __u64 ) data ) ;
2005-09-29 02:11:15 +04:00
break ;
case PTRACE_SETREGS :
2014-07-23 17:40:13 +04:00
ret = ptrace_setregs ( child ,
( struct user_pt_regs __user * ) ( __u64 ) data ) ;
2005-09-29 02:11:15 +04:00
break ;
case PTRACE_GETFPREGS :
2007-10-12 02:46:15 +04:00
ret = ptrace_getfpregs ( child , ( __u32 __user * ) ( __u64 ) data ) ;
2005-09-29 02:11:15 +04:00
break ;
case PTRACE_SETFPREGS :
2007-10-12 02:46:15 +04:00
ret = ptrace_setfpregs ( child , ( __u32 __user * ) ( __u64 ) data ) ;
2005-09-29 02:11:15 +04:00
break ;
2005-04-13 21:43:59 +04:00
case PTRACE_GET_THREAD_AREA :
2006-01-12 12:06:07 +03:00
ret = put_user ( task_thread_info ( child ) - > tp_value ,
2005-04-13 21:43:59 +04:00
( unsigned int __user * ) ( unsigned long ) data ) ;
break ;
2005-09-29 02:11:15 +04:00
case PTRACE_GET_THREAD_AREA_3264 :
2006-01-12 12:06:07 +03:00
ret = put_user ( task_thread_info ( child ) - > tp_value ,
2005-09-29 02:11:15 +04:00
( unsigned long __user * ) ( unsigned long ) data ) ;
break ;
2008-09-23 11:11:26 +04: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-17 02:20:36 +04:00
default :
2008-11-13 22:50:12 +03:00
ret = compat_ptrace_request ( child , request , addr , data ) ;
2005-04-17 02:20:36 +04:00
break ;
}
out :
return ret ;
}