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: Daniel Jacobowitz <dan@codesourcery.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 7428336..122433f 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -38,6 +38,7 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/bootinfo.h>
+#include <asm/reg.h>
 
 /*
  * Called by kernel/ptrace.c when detaching..
@@ -49,6 +50,118 @@
 	/* 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)
 {
 	struct task_struct *child;
@@ -300,6 +413,22 @@
 		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_CONT: { /* restart after signal. */
 		ret = -EIO;