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/user.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>
# 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 ,
sizeof ( tmp ) , 0 ) ;
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 ;
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 :
if ( tsk_used_math ( child ) ) {
fpureg_t * fregs = get_fpu_regs ( child ) ;
/*
* The odd registers are actually the high
* order bits of the values stored in the even
* registers - unless we ' re using r2k_switch . S .
*/
if ( addr & 1 )
tmp = ( unsigned long ) ( fregs [ ( ( addr & ~ 1 ) - 32 ) ] > > 32 ) ;
else
tmp = ( unsigned long ) ( fregs [ ( addr - 32 ) ] & 0xffffffff ) ;
} else {
tmp = - 1 ; /* FP not yet used */
}
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-01-22 15:59:30 +04:00
case FPC_EIR : { /* implementation / version register */
2005-04-17 02:20:36 +04:00
unsigned int flags ;
2006-04-05 12:45:45 +04:00
# ifdef CONFIG_MIPS_MT_SMTC
unsigned int irqflags ;
unsigned int mtflags ;
# endif /* CONFIG_MIPS_MT_SMTC */
2005-04-17 02:20:36 +04:00
2006-10-08 19:10:01 +04:00
preempt_disable ( ) ;
2006-01-29 16:34:32 +03:00
if ( ! cpu_has_fpu ) {
2006-10-08 19:10:01 +04:00
preempt_enable ( ) ;
2006-01-29 16:34:32 +03:00
tmp = 0 ;
2005-04-17 02:20:36 +04:00
break ;
2006-01-29 16:34:32 +03:00
}
2005-04-17 02:20:36 +04:00
2006-04-05 12:45:45 +04:00
# ifdef CONFIG_MIPS_MT_SMTC
/* Read-modify-write of Status must be atomic */
local_irq_save ( irqflags ) ;
mtflags = dmt ( ) ;
# endif /* CONFIG_MIPS_MT_SMTC */
2005-10-06 20:39:32 +04:00
if ( cpu_has_mipsmt ) {
unsigned int vpflags = dvpe ( ) ;
flags = read_c0_status ( ) ;
__enable_fpu ( ) ;
__asm__ __volatile__ ( " cfc1 \t %0,$0 " : " =r " ( tmp ) ) ;
write_c0_status ( flags ) ;
evpe ( vpflags ) ;
} else {
flags = read_c0_status ( ) ;
__enable_fpu ( ) ;
__asm__ __volatile__ ( " cfc1 \t %0,$0 " : " =r " ( tmp ) ) ;
write_c0_status ( flags ) ;
}
2006-04-05 12:45:45 +04:00
# ifdef CONFIG_MIPS_MT_SMTC
emt ( mtflags ) ;
local_irq_restore ( irqflags ) ;
# endif /* CONFIG_MIPS_MT_SMTC */
2005-10-06 20:39:32 +04:00
preempt_enable ( ) ;
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 ,
sizeof ( data ) , 1 ) = = sizeof ( data ) )
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 : {
fpureg_t * fregs = get_fpu_regs ( child ) ;
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
}
/*
* The odd registers are actually the high order bits
* of the values stored in the even registers - unless
* we ' re using r2k_switch . S .
*/
if ( addr & 1 ) {
fregs [ ( addr & ~ 1 ) - FPR_BASE ] & = 0xffffffff ;
fregs [ ( addr & ~ 1 ) - FPR_BASE ] | = ( ( unsigned long long ) data ) < < 32 ;
} else {
fregs [ addr - FPR_BASE ] & = ~ 0xffffffffLL ;
/* Must cast, lest sign extension fill upper
bits ! */
fregs [ addr - FPR_BASE ] | = ( unsigned int ) data ;
}
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 :
2007-10-25 19:53:02 +04:00
ret = ptrace_getregs ( child , ( __s64 __user * ) ( __u64 ) data ) ;
2005-09-29 02:11:15 +04:00
break ;
case PTRACE_SETREGS :
2007-10-25 19:53:02 +04:00
ret = ptrace_setregs ( child , ( __s64 __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 ;
}