|
@@ -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)
|
|
/* 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)
|
|
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;
|