Commit ea3d710f authored by Daniel Jacobowitz's avatar Daniel Jacobowitz Committed by Ralf Baechle

Revise MIPS 64-bit ptrace interface

    
Change the N32 debugging ABI to something more sane, and add support
for o32 and n32 debuggers to trace n64 programs.
Signed-off-by: default avatarDaniel Jacobowitz <dan@codesourcery.com>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 9043f7e9
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <asm/system.h> #include <asm/system.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/bootinfo.h> #include <asm/bootinfo.h>
#include <asm/reg.h>
/* /*
* Called by kernel/ptrace.c when detaching.. * Called by kernel/ptrace.c when detaching..
...@@ -49,6 +50,118 @@ void ptrace_disable(struct task_struct *child) ...@@ -49,6 +50,118 @@ void ptrace_disable(struct task_struct *child)
/* Nothing to do.. */ /* Nothing to do.. */
} }
/*
* Read a general register set. We always use the 64-bit format, even
* for 32-bit kernels and for 32-bit processes on a 64-bit kernel.
* Registers are sign extended to fill the available space.
*/
int ptrace_getregs (struct task_struct *child, __s64 __user *data)
{
struct pt_regs *regs;
int i;
if (!access_ok(VERIFY_WRITE, data, 38 * 8))
return -EIO;
regs = (struct pt_regs *) ((unsigned long) child->thread_info +
THREAD_SIZE - 32 - sizeof(struct pt_regs));
for (i = 0; i < 32; i++)
__put_user (regs->regs[i], data + i);
__put_user (regs->lo, data + EF_LO - EF_R0);
__put_user (regs->hi, data + EF_HI - EF_R0);
__put_user (regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
__put_user (regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0);
__put_user (regs->cp0_status, data + EF_CP0_STATUS - EF_R0);
__put_user (regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0);
return 0;
}
/*
* Write a general register set. As for PTRACE_GETREGS, we always use
* the 64-bit format. On a 32-bit kernel only the lower order half
* (according to endianness) will be used.
*/
int ptrace_setregs (struct task_struct *child, __s64 __user *data)
{
struct pt_regs *regs;
int i;
if (!access_ok(VERIFY_READ, data, 38 * 8))
return -EIO;
regs = (struct pt_regs *) ((unsigned long) child->thread_info +
THREAD_SIZE - 32 - sizeof(struct pt_regs));
for (i = 0; i < 32; i++)
__get_user (regs->regs[i], data + i);
__get_user (regs->lo, data + EF_LO - EF_R0);
__get_user (regs->hi, data + EF_HI - EF_R0);
__get_user (regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
/* badvaddr, status, and cause may not be written. */
return 0;
}
int ptrace_getfpregs (struct task_struct *child, __u32 __user *data)
{
int i;
if (!access_ok(VERIFY_WRITE, data, 33 * 8))
return -EIO;
if (tsk_used_math(child)) {
fpureg_t *fregs = get_fpu_regs(child);
for (i = 0; i < 32; i++)
__put_user (fregs[i], i + (__u64 __user *) data);
} else {
for (i = 0; i < 32; i++)
__put_user ((__u64) -1, i + (__u64 __user *) data);
}
if (cpu_has_fpu) {
unsigned int flags, tmp;
__put_user (child->thread.fpu.hard.fcr31, data + 64);
flags = read_c0_status();
__enable_fpu();
__asm__ __volatile__("cfc1\t%0,$0" : "=r" (tmp));
write_c0_status(flags);
__put_user (tmp, data + 65);
} else {
__put_user (child->thread.fpu.soft.fcr31, data + 64);
__put_user ((__u32) 0, data + 65);
}
return 0;
}
int ptrace_setfpregs (struct task_struct *child, __u32 __user *data)
{
fpureg_t *fregs;
int i;
if (!access_ok(VERIFY_READ, data, 33 * 8))
return -EIO;
fregs = get_fpu_regs(child);
for (i = 0; i < 32; i++)
__get_user (fregs[i], i + (__u64 __user *) data);
if (cpu_has_fpu)
__get_user (child->thread.fpu.hard.fcr31, data + 64);
else
__get_user (child->thread.fpu.soft.fcr31, data + 64);
/* FIR may not be written. */
return 0;
}
asmlinkage int sys_ptrace(long request, long pid, long addr, long data) asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
{ {
struct task_struct *child; struct task_struct *child;
...@@ -300,6 +413,22 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ...@@ -300,6 +413,22 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
break; break;
} }
case PTRACE_GETREGS:
ret = ptrace_getregs (child, (__u64 __user *) data);
break;
case PTRACE_SETREGS:
ret = ptrace_setregs (child, (__u64 __user *) data);
break;
case PTRACE_GETFPREGS:
ret = ptrace_getfpregs (child, (__u32 __user *) data);
break;
case PTRACE_SETFPREGS:
ret = ptrace_setfpregs (child, (__u32 __user *) data);
break;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
case PTRACE_CONT: { /* restart after signal. */ case PTRACE_CONT: { /* restart after signal. */
ret = -EIO; ret = -EIO;
......
...@@ -35,6 +35,12 @@ ...@@ -35,6 +35,12 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/bootinfo.h> #include <asm/bootinfo.h>
int ptrace_getregs (struct task_struct *child, __s64 __user *data);
int ptrace_setregs (struct task_struct *child, __s64 __user *data);
int ptrace_getfpregs (struct task_struct *child, __u32 __user *data);
int ptrace_setfpregs (struct task_struct *child, __u32 __user *data);
/* /*
* Tracing a 32-bit process with a 64-bit strace and vice versa will not * Tracing a 32-bit process with a 64-bit strace and vice versa will not
* work. I don't know how to fix this. * work. I don't know how to fix this.
...@@ -99,6 +105,35 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) ...@@ -99,6 +105,35 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
break; break;
} }
/*
* 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;
}
/* Read the word at location addr in the USER area. */ /* Read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: { case PTRACE_PEEKUSR: {
struct pt_regs *regs; struct pt_regs *regs;
...@@ -202,6 +237,31 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) ...@@ -202,6 +237,31 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
ret = -EIO; ret = -EIO;
break; break;
/*
* 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;
}
case PTRACE_POKEUSR: { case PTRACE_POKEUSR: {
struct pt_regs *regs; struct pt_regs *regs;
ret = 0; ret = 0;
...@@ -276,6 +336,22 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) ...@@ -276,6 +336,22 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
break; break;
} }
case PTRACE_GETREGS:
ret = ptrace_getregs (child, (__u64 __user *) (__u64) data);
break;
case PTRACE_SETREGS:
ret = ptrace_setregs (child, (__u64 __user *) (__u64) data);
break;
case PTRACE_GETFPREGS:
ret = ptrace_getfpregs (child, (__u32 __user *) (__u64) data);
break;
case PTRACE_SETFPREGS:
ret = ptrace_setfpregs (child, (__u32 __user *) (__u64) data);
break;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
case PTRACE_CONT: { /* restart after signal. */ case PTRACE_CONT: { /* restart after signal. */
ret = -EIO; ret = -EIO;
...@@ -320,6 +396,11 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) ...@@ -320,6 +396,11 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
(unsigned int __user *) (unsigned long) data); (unsigned int __user *) (unsigned long) data);
break; break;
case PTRACE_GET_THREAD_AREA_3264:
ret = put_user(child->thread_info->tp_value,
(unsigned long __user *) (unsigned long) data);
break;
default: default:
ret = ptrace_request(child, request, addr, data); ret = ptrace_request(child, request, addr, data);
break; break;
......
...@@ -216,7 +216,7 @@ EXPORT(sysn32_call_table) ...@@ -216,7 +216,7 @@ EXPORT(sysn32_call_table)
PTR compat_sys_getrusage PTR compat_sys_getrusage
PTR sys32_sysinfo PTR sys32_sysinfo
PTR compat_sys_times PTR compat_sys_times
PTR sys_ptrace PTR sys32_ptrace
PTR sys_getuid /* 6100 */ PTR sys_getuid /* 6100 */
PTR sys_syslog PTR sys_syslog
PTR sys_getgid PTR sys_getgid
......
...@@ -48,10 +48,10 @@ struct pt_regs { ...@@ -48,10 +48,10 @@ struct pt_regs {
}; };
/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ /* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */
/* #define PTRACE_GETREGS 12 */ #define PTRACE_GETREGS 12
/* #define PTRACE_SETREGS 13 */ #define PTRACE_SETREGS 13
/* #define PTRACE_GETFPREGS 14 */ #define PTRACE_GETFPREGS 14
/* #define PTRACE_SETFPREGS 15 */ #define PTRACE_SETFPREGS 15
/* #define PTRACE_GETFPXREGS 18 */ /* #define PTRACE_GETFPXREGS 18 */
/* #define PTRACE_SETFPXREGS 19 */ /* #define PTRACE_SETFPXREGS 19 */
...@@ -60,6 +60,13 @@ struct pt_regs { ...@@ -60,6 +60,13 @@ struct pt_regs {
#define PTRACE_GET_THREAD_AREA 25 #define PTRACE_GET_THREAD_AREA 25
#define PTRACE_SET_THREAD_AREA 26 #define PTRACE_SET_THREAD_AREA 26
/* Calls to trace a 64bit program from a 32bit program. */
#define PTRACE_PEEKTEXT_3264 0xc0
#define PTRACE_PEEKDATA_3264 0xc1
#define PTRACE_POKETEXT_3264 0xc2
#define PTRACE_POKEDATA_3264 0xc3
#define PTRACE_GET_THREAD_AREA_3264 0xc4
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/linkage.h> #include <linux/linkage.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment