Merge to Fedora kernel-2.6.18-1.2255_FC5-vs2.0.2.2-rc9 patched with stable patch...
[linux-2.6.git] / arch / sparc64 / kernel / ptrace.c
index d8cfba6..ded4046 100644 (file)
@@ -1,6 +1,6 @@
-/* ptrace.c: Sparc64 process tracing support.
+/* ptrace.c: Sparc process tracing support.
  *
- * Copyright (C) 1996, 2006 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  *
  * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
  */
 
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
 #include <linux/security.h>
 #include <linux/seccomp.h>
 #include <linux/audit.h>
-#include <linux/tracehook.h>
-#include <linux/elf.h>
-#include <linux/ptrace.h>
+#include <linux/signal.h>
 
 #include <asm/asi.h>
 #include <asm/pgtable.h>
 #include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/psrcompat.h>
+#include <asm/visasm.h>
 #include <asm/spitfire.h>
 #include <asm/page.h>
 #include <asm/cpudata.h>
-#include <asm/psrcompat.h>
-
-#define GENREG_G0      0
-#define GENREG_O0      8
-#define GENREG_L0      16
-#define GENREG_I0      24
-#define GENREG_TSTATE  32
-#define GENREG_TPC     33
-#define GENREG_TNPC    34
-#define GENREG_Y       35
-
-#define SPARC64_NGREGS 36
-
-static int genregs_get(struct task_struct *target,
-                      const struct utrace_regset *regset,
-                      unsigned int pos, unsigned int count,
-                      void *kbuf, void __user *ubuf)
-{
-       struct pt_regs *regs = task_pt_regs(target);
-       int err;
-
-       err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, regs->u_regs,
-                                   GENREG_G0 * 8, GENREG_L0 * 8);
-
-       if (err == 0 && count > 0 && pos < (GENREG_TSTATE * 8)) {
-               struct thread_info *t = task_thread_info(target);
-               unsigned long rwindow[16], fp, *win;
-               int wsaved;
-
-               if (target == current)
-                       flushw_user();
-
-               wsaved = __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_WSAVED];
-               fp = regs->u_regs[UREG_FP] + STACK_BIAS;
-               if (wsaved && t->rwbuf_stkptrs[wsaved - 1] == fp)
-                       win = &t->reg_window[wsaved - 1].locals[0];
-               else {
-                       if (target == current) {
-                               if (copy_from_user(rwindow,
-                                                  (void __user *) fp,
-                                                  16 * sizeof(long)))
-                                       err = -EFAULT;
-                       } else
-                               err = access_process_vm(target, fp, rwindow,
-                                                       16 * sizeof(long), 0);
-                       if (err)
-                               return err;
-                       win = rwindow;
-               }
-
-               err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
-                                           win, GENREG_L0 * 8,
-                                           GENREG_TSTATE * 8);
-       }
-
-       if (err == 0)
-               err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
-                                           &regs->tstate, GENREG_TSTATE * 8,
-                                           GENREG_Y * 8);
-       if (err == 0 && count > 0) {
-               if (kbuf)
-                       *(unsigned long *) kbuf = regs->y;
-               else if (put_user(regs->y, (unsigned long __user *) ubuf))
-                       return -EFAULT;
-       }
-
-       return err;
-}
 
-/* Consistent with signal handling, we only allow userspace to
- * modify the %asi, %icc, and %xcc fields of the %tstate register.
+/* Returning from ptrace is a bit tricky because the syscall return
+ * low level code assumes any value returned which is negative and
+ * is a valid errno will mean setting the condition codes to indicate
+ * an error return.  This doesn't work, so we have this hook.
  */
-#define TSTATE_DEBUGCHANGE     (TSTATE_ASI | TSTATE_ICC | TSTATE_XCC)
-
-static int genregs_set(struct task_struct *target,
-                      const struct utrace_regset *regset,
-                      unsigned int pos, unsigned int count,
-                      const void *kbuf, const void __user *ubuf)
+static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
 {
-       struct pt_regs *regs = task_pt_regs(target);
-       unsigned long tstate_save;
-       int err;
-
-       err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, regs->u_regs,
-                                  GENREG_G0 * 8, GENREG_L0 * 8);
-
-       if (err == 0 && count > 0 && pos < (GENREG_TSTATE * 8)) {
-               unsigned long fp = regs->u_regs[UREG_FP] + STACK_BIAS;
-               unsigned long rwindow[16], *winbuf;
-               unsigned int copy = (GENREG_TSTATE * 8) - pos;
-               unsigned int off;
-               int err;
-
-               if (target == current)
-                       flushw_user();
-
-               if (count < copy)
-                       copy = count;
-               off = pos - (GENREG_L0 * 8);
-
-               if (kbuf) {
-                       winbuf = (unsigned long *) kbuf;
-                       kbuf += copy;
-               }
-               else {
-                       winbuf = rwindow;
-                       if (copy_from_user(winbuf, ubuf, copy))
-                               return -EFAULT;
-                       ubuf += copy;
-               }
-               count -= copy;
-               pos += copy;
-
-               if (target == current)
-                       err = copy_to_user((void __user *) fp + off,
-                                          winbuf, copy);
-               else
-                       err = access_process_vm(target, fp + off,
-                                               winbuf, copy, 1);
-       }
-
-       tstate_save = regs->tstate &~ TSTATE_DEBUGCHANGE;
-
-       if (err == 0)
-               err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
-                                           &regs->tstate, GENREG_TSTATE * 8,
-                                           GENREG_Y * 8);
-
-       regs->tstate &= TSTATE_DEBUGCHANGE;
-       regs->tstate |= tstate_save;
-
-       if (err == 0 && count > 0) {
-               if (kbuf)
-                       regs->y = *(unsigned long *) kbuf;
-               else if (get_user(regs->y, (unsigned long __user *) ubuf))
-                       return -EFAULT;
-       }
-
-       return err;
+       regs->u_regs[UREG_I0] = error;
+       regs->tstate |= (TSTATE_ICARRY | TSTATE_XCARRY);
+       regs->tpc = regs->tnpc;
+       regs->tnpc += 4;
 }
 
-#define FPREG_F0       0
-#define FPREG_FSR      32
-#define FPREG_GSR      33
-#define FPREG_FPRS     34
-
-#define SPARC64_NFPREGS        35
-
-static int fpregs_get(struct task_struct *target,
-                     const struct utrace_regset *regset,
-                     unsigned int pos, unsigned int count,
-                     void *kbuf, void __user *ubuf)
+static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
 {
-       struct thread_info *t = task_thread_info(target);
-       int err;
-
-       err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
-                                   t->fpregs, FPREG_F0 * 8, FPREG_FSR * 8);
-
-       if (err == 0)
-               err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
-                                           &t->xfsr[0], FPREG_FSR * 8,
-                                           FPREG_GSR * 8);
-
-       if (err == 0)
-               err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
-                                           &t->gsr[0], FPREG_GSR * 8,
-                                           FPREG_FPRS * 8);
-
-       if (err == 0 && count > 0) {
-               struct pt_regs *regs = task_pt_regs(target);
-
-               if (kbuf)
-                       *(unsigned long *) kbuf = regs->fprs;
-               else if (put_user(regs->fprs, (unsigned long __user *) ubuf))
-                       return -EFAULT;
-       }
-       if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT)) {
-               pt_error_return(regs, ESRCH);
-               goto out_tsk;
-       }
-
-       return err;
+       regs->u_regs[UREG_I0] = value;
+       regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY);
+       regs->tpc = regs->tnpc;
+       regs->tnpc += 4;
 }
 
-static int fpregs_set(struct task_struct *target,
-                     const struct utrace_regset *regset,
-                     unsigned int pos, unsigned int count,
-                     const void *kbuf, const void __user *ubuf)
+static inline void
+pt_succ_return_linux(struct pt_regs *regs, unsigned long value, void __user *addr)
 {
-       struct thread_info *t = task_thread_info(target);
-       int err;
-
-       err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
-                                  t->fpregs, FPREG_F0 * 8, FPREG_FSR * 8);
-
-       if (err == 0)
-               err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
-                                          &t->xfsr[0], FPREG_FSR * 8,
-                                          FPREG_GSR * 8);
-
-       if (err == 0)
-               err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
-                                          &t->gsr[0], FPREG_GSR * 8,
-                                          FPREG_FPRS * 8);
-
-       if (err == 0 && count > 0) {
-               struct pt_regs *regs = task_pt_regs(target);
-
-               if (kbuf)
-                       regs->fprs = *(unsigned long *) kbuf;
-               else if (get_user(regs->fprs, (unsigned long __user *) ubuf))
-                       return -EFAULT;
-       }
-
-       return err;
-}
-
-static const struct utrace_regset native_regsets[] = {
-       {
-               .n = SPARC64_NGREGS,
-               .size = sizeof(long), .align = sizeof(long),
-               .get = genregs_get, .set = genregs_set
-       },
-       {
-               .n = SPARC64_NFPREGS,
-               .size = sizeof(long), .align = sizeof(long),
-               .get = fpregs_get, .set = fpregs_set
-       },
-};
-
-const struct utrace_regset_view utrace_sparc64_native_view = {
-       .name = UTS_MACHINE, .e_machine = ELF_ARCH,
-       .regsets = native_regsets,
-       .n = sizeof native_regsets / sizeof native_regsets[0],
-};
-EXPORT_SYMBOL_GPL(utrace_sparc64_native_view);
-
-#ifdef CONFIG_COMPAT
-
-#define GENREG32_G0    0
-#define GENREG32_O0    8
-#define GENREG32_L0    16
-#define GENREG32_I0    24
-#define GENREG32_PSR   32
-#define GENREG32_PC    33
-#define GENREG32_NPC   34
-#define GENREG32_Y     35
-#define GENREG32_WIM   36
-#define GENREG32_TBR   37
-
-#define SPARC32_NGREGS 38
-
-static int genregs32_get(struct task_struct *target,
-                        const struct utrace_regset *regset,
-                        unsigned int pos, unsigned int count,
-                        void *kbuf, void __user *ubuf)
-{
-       struct pt_regs *regs = task_pt_regs(target);
-
-       while (count > 0 && pos < (GENREG32_L0 * 4)) {
-               u32 val = regs->u_regs[(pos - (GENREG32_G0*4))/sizeof(u32)];
-               if (kbuf) {
-                       *(u32 *) kbuf = val;
-                       kbuf += sizeof(u32);
-               } else if (put_user(val, (u32 __user *) ubuf))
-                       return -EFAULT;
-               else
-                       ubuf += sizeof(u32);
-               pos += sizeof(u32);
-               count -= sizeof(u32);
-       }
-
-       if (count > 0 && pos < (GENREG32_PSR * 4)) {
-               struct thread_info *t = task_thread_info(target);
-               unsigned long fp;
-               u32 rwindow[16];
-               int wsaved;
-
-               if (target == current)
-                       flushw_user();
-
-               wsaved = __thread_flag_byte_ptr(t)[TI_FLAG_BYTE_WSAVED];
-               fp = regs->u_regs[UREG_FP] & 0xffffffffUL;
-               if (wsaved && t->rwbuf_stkptrs[wsaved - 1] == fp) {
-                       int i;
-                       for (i = 0; i < 8; i++)
-                               rwindow[i + 0] =
-                                       t->reg_window[wsaved-1].locals[i];
-                       for (i = 0; i < 8; i++)
-                               rwindow[i + 8] =
-                                       t->reg_window[wsaved-1].ins[i];
-               } else {
-                       int err;
-
-                       if (target == current) {
-                               err = 0;
-                               if (copy_from_user(rwindow, (void __user *) fp,
-                                                  16 * sizeof(u32)))
-                                       err = -EFAULT;
-                       } else
-                               err = access_process_vm(target, fp, rwindow,
-                                                       16 * sizeof(u32), 0);
-                       if (err)
-                               return err;
+       if (test_thread_flag(TIF_32BIT)) {
+               if (put_user(value, (unsigned int __user *) addr)) {
+                       pt_error_return(regs, EFAULT);
+                       return;
                }
-
-               while (count > 0 && pos < (GENREG32_PSR * 4)) {
-                       u32 val = rwindow[(pos - (GENREG32_L0*4))/sizeof(u32)];
-
-                       if (kbuf) {
-                               *(u32 *) kbuf = val;
-                               kbuf += sizeof(u32);
-                       } else if (put_user(val, (u32 __user *) ubuf))
-                               return -EFAULT;
-                       else
-                               ubuf += sizeof(u32);
-                       pos += sizeof(u32);
-                       count -= sizeof(u32);
+       } else {
+               if (put_user(value, (long __user *) addr)) {
+                       pt_error_return(regs, EFAULT);
+                       return;
                }
        }
-
-       if (count > 0 && pos == (GENREG32_PSR * 4)) {
-               u32 psr = tstate_to_psr(regs->tstate);
-
-               if (kbuf) {
-                       *(u32 *) kbuf = psr;
-                       kbuf += sizeof(u32);
-               } else if (put_user(psr, (u32 __user *) ubuf))
-                       return -EFAULT;
-               else
-                       ubuf += sizeof(u32);
-               pos += sizeof(u32);
-               count -= sizeof(u32);
-       }
-
-       if (count > 0 && pos == (GENREG32_PC * 4)) {
-               u32 val = regs->tpc;
-
-               if (kbuf) {
-                       *(u32 *) kbuf = val;
-                       kbuf += sizeof(u32);
-               } else if (put_user(val, (u32 __user *) ubuf))
-                       return -EFAULT;
-               else
-                       ubuf += sizeof(u32);
-               pos += sizeof(u32);
-               count -= sizeof(u32);
-       }
-
-       if (count > 0 && pos == (GENREG32_NPC * 4)) {
-               u32 val = regs->tnpc;
-
-               if (kbuf) {
-                       *(u32 *) kbuf = val;
-                       kbuf += sizeof(u32);
-               } else if (put_user(val, (u32 __user *) ubuf))
-                       return -EFAULT;
-               else
-                       ubuf += sizeof(u32);
-               pos += sizeof(u32);
-               count -= sizeof(u32);
-       }
-
-       if (count > 0 && pos == (GENREG32_Y * 4)) {
-               if (kbuf) {
-                       *(u32 *) kbuf = regs->y;
-                       kbuf += sizeof(u32);
-               } else if (put_user(regs->y, (u32 __user *) ubuf))
-                       return -EFAULT;
-               else
-                       ubuf += sizeof(u32);
-               pos += sizeof(u32);
-               count -= sizeof(u32);
-       }
-
-       if (count > 0) {
-               if (kbuf)
-                       memset(kbuf, 0, count);
-               else if (clear_user(ubuf, count))
-                       return -EFAULT;
-       }
-
-       return 0;
+       regs->u_regs[UREG_I0] = 0;
+       regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY);
+       regs->tpc = regs->tnpc;
+       regs->tnpc += 4;
 }
 
-static int genregs32_set(struct task_struct *target,
-                        const struct utrace_regset *regset,
-                        unsigned int pos, unsigned int count,
-                        const void *kbuf, const void __user *ubuf)
+static void
+pt_os_succ_return (struct pt_regs *regs, unsigned long val, void __user *addr)
 {
-       struct pt_regs *regs = task_pt_regs(target);
-
-       while (count > 0 && pos < (GENREG32_L0 * 4)) {
-               unsigned long *loc;
-               loc = &regs->u_regs[(pos - (GENREG32_G0*4))/sizeof(u32)];
-               if (kbuf) {
-                       *loc = *(u32 *) kbuf;
-                       kbuf += sizeof(u32);
-               } else if (get_user(*loc, (u32 __user *) ubuf))
-                       return -EFAULT;
-               else
-                       ubuf += sizeof(u32);
-               pos += sizeof(u32);
-               count -= sizeof(u32);
-       }
-
-       if (count > 0 && pos < (GENREG32_PSR * 4)) {
-               unsigned long fp;
-               u32 regbuf[16];
-               unsigned int off, copy;
-               int err;
-
-               if (target == current)
-                       flushw_user();
-
-               copy = (GENREG32_PSR * 4) - pos;
-               if (count < copy)
-                       copy = count;
-               BUG_ON(copy > 16 * sizeof(u32));
-
-               fp = regs->u_regs[UREG_FP] & 0xffffffffUL;
-               off = pos - (GENREG32_L0 * 4);
-               if (kbuf) {
-                       memcpy(regbuf, kbuf, copy);
-                       kbuf += copy;
-               } else if (copy_from_user(regbuf, ubuf, copy))
-                       return -EFAULT;
-               else
-                       ubuf += copy;
-               pos += copy;
-               count -= copy;
-
-               if (target == current) {
-                       err = 0;
-                       if (copy_to_user((void __user *) fp + off,
-                                        regbuf, count))
-                               err = -EFAULT;
-               } else
-                       err = access_process_vm(target, fp + off,
-                                               regbuf, count, 1);
-               if (err)
-                       return err;
-       }
-
-       if (count > 0 && pos == (GENREG32_PSR * 4)) {
-               unsigned long tstate, tstate_save;
-               u32 psr;
-
-               tstate_save = regs->tstate&~(TSTATE_ICC|TSTATE_XCC);
-
-               if (kbuf) {
-                       psr = *(u32 *) kbuf;
-                       kbuf += sizeof(u32);
-               } else if (get_user(psr, (u32 __user *) ubuf))
-                       return -EFAULT;
-               else
-                       ubuf += sizeof(u32);
-               pos += sizeof(u32);
-               count -= sizeof(u32);
-
-               tstate = psr_to_tstate_icc(psr);
-               regs->tstate = tstate_save | tstate;
-       }
-
-       if (count > 0 && pos == (GENREG32_PC * 4)) {
-               if (kbuf) {
-                       regs->tpc = *(u32 *) kbuf;
-                       kbuf += sizeof(u32);
-               } else if (get_user(regs->tpc, (u32 __user *) ubuf))
-                       return -EFAULT;
-               else
-                       ubuf += sizeof(u32);
-               pos += sizeof(u32);
-               count -= sizeof(u32);
-       }
-
-       if (count > 0 && pos == (GENREG32_NPC * 4)) {
-               if (kbuf) {
-                       regs->tnpc = *(u32 *) kbuf;
-                       kbuf += sizeof(u32);
-               } else if (get_user(regs->tnpc, (u32 __user *) ubuf))
-                       return -EFAULT;
-               else
-                       ubuf += sizeof(u32);
-               pos += sizeof(u32);
-               count -= sizeof(u32);
-       }
-
-       if (count > 0 && pos == (GENREG32_Y * 4)) {
-               if (kbuf) {
-                       regs->y = *(u32 *) kbuf;
-                       kbuf += sizeof(u32);
-               } else if (get_user(regs->y, (u32 __user *) ubuf))
-                       return -EFAULT;
-               else
-                       ubuf += sizeof(u32);
-               pos += sizeof(u32);
-               count -= sizeof(u32);
-       }
-
-       /* Ignore WIM and TBR */
-
-       return 0;
+       if (current->personality == PER_SUNOS)
+               pt_succ_return (regs, val);
+       else
+               pt_succ_return_linux (regs, val, addr);
 }
 
-#define FPREG32_F0     0
-#define FPREG32_FSR    32
-
-#define SPARC32_NFPREGS        33
-
-static int fpregs32_get(struct task_struct *target,
-                       const struct utrace_regset *regset,
-                       unsigned int pos, unsigned int count,
-                       void *kbuf, void __user *ubuf)
-{
-       struct thread_info *t = task_thread_info(target);
-       int err;
-
-       err = utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
-                                   t->fpregs, FPREG32_F0 * 4,
-                                   FPREG32_FSR * 4);
-
-       if (err == 0 && count > 0) {
-               if (kbuf) {
-                       *(u32 *) kbuf = t->xfsr[0];
-               } else if (put_user(t->xfsr[0], (u32 __user *) ubuf))
-                       return -EFAULT;
-       }
-
-       return err;
-}
+/* #define ALLOW_INIT_TRACING */
+/* #define DEBUG_PTRACE */
+
+#ifdef DEBUG_PTRACE
+char *pt_rq [] = {
+       /* 0  */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR",
+       /* 4  */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT",
+       /* 8  */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH",
+       /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS",
+       /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT",
+       /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown",
+       /* 24 */ "SYSCALL", ""
+};
+#endif
 
-static int fpregs32_set(struct task_struct *target,
-                       const struct utrace_regset *regset,
-                       unsigned int pos, unsigned int count,
-                       const void *kbuf, const void __user *ubuf)
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure single step bits etc are not set.
+ */
+void ptrace_disable(struct task_struct *child)
 {
-       struct thread_info *t = task_thread_info(target);
-       int err;
-
-       err = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
-                                  t->fpregs, FPREG32_F0 * 4,
-                                  FPREG32_FSR * 4);
-
-       if (err == 0 && count > 0) {
-               u32 fsr;
-               if (kbuf) {
-                       fsr = *(u32 *) kbuf;
-               } else if (get_user(fsr, (u32 __user *) ubuf))
-                       return -EFAULT;
-               t->xfsr[0] = (t->xfsr[0] & 0xffffffff00000000UL) | fsr;
-       }
-
-       return 0;
+       /* nothing to do */
 }
 
-static const struct utrace_regset sparc32_regsets[] = {
-       {
-               .n = SPARC32_NGREGS,
-               .size = sizeof(u32), .align = sizeof(u32),
-               .get = genregs32_get, .set = genregs32_set
-       },
-       {
-               .n = SPARC32_NFPREGS,
-               .size = sizeof(u32), .align = sizeof(u32),
-               .get = fpregs32_get, .set = fpregs32_set
-       },
-};
-
-const struct utrace_regset_view utrace_sparc32_view = {
-       .name = "sparc", .e_machine = EM_SPARC,
-       .regsets = sparc32_regsets,
-       .n = sizeof sparc32_regsets / sizeof sparc32_regsets[0],
-};
-EXPORT_SYMBOL_GPL(utrace_sparc32_view);
-
-#endif /* CONFIG_COMPAT */
-
 /* To get the necessary page struct, access_process_vm() first calls
  * get_user_pages().  This has done a flush_dcache_page() on the
  * accessed page.  Then our caller (copy_{to,from}_user_page()) did
@@ -665,124 +167,488 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
        }
 }
 
-#ifdef CONFIG_PTRACE
-static const struct ptrace_layout_segment sparc64_getregs_layout[] = {
-       { 0, offsetof(struct pt_regs, u_regs[15]), 0, sizeof(long) },
-       { offsetof(struct pt_regs, u_regs[15]),
-         offsetof(struct pt_regs, tstate),
-         -1, 0 },
-       { offsetof(struct pt_regs, tstate), offsetof(struct pt_regs, y),
-         0, 32 * sizeof(long) },
-       {0, 0, -1, 0}
-};
-
-int arch_ptrace(long *request, struct task_struct *child,
-               struct utrace_attached_engine *engine,
-               unsigned long addr, unsigned long data,
-               long *retval)
+asmlinkage void do_ptrace(struct pt_regs *regs)
 {
-       void __user *uaddr = (void __user *) addr;
-       struct pt_regs *uregs = uaddr;
-       int err = -ENOSYS;
-
-       switch (*request) {
-       case PTRACE_GETREGS64:
-               err = ptrace_layout_access(child, engine,
-                                          &utrace_sparc64_native_view,
-                                          sparc64_getregs_layout,
-                                          0, offsetof(struct pt_regs, y),
-                                          uaddr, NULL, 0);
-               if (!err &&
-                   (put_user(task_pt_regs(child)->y, &uregs->y) ||
-                    put_user(task_pt_regs(child)->fprs, &uregs->fprs)))
-                       err = -EFAULT;
-               break;
+       int request = regs->u_regs[UREG_I0];
+       pid_t pid = regs->u_regs[UREG_I1];
+       unsigned long addr = regs->u_regs[UREG_I2];
+       unsigned long data = regs->u_regs[UREG_I3];
+       unsigned long addr2 = regs->u_regs[UREG_I4];
+       struct task_struct *child;
+       int ret;
+
+       if (test_thread_flag(TIF_32BIT)) {
+               addr &= 0xffffffffUL;
+               data &= 0xffffffffUL;
+               addr2 &= 0xffffffffUL;
+       }
+       lock_kernel();
+#ifdef DEBUG_PTRACE
+       {
+               char *s;
 
-       case PTRACE_SETREGS64:
-               err = ptrace_layout_access(child, engine,
-                                          &utrace_sparc64_native_view,
-                                          sparc64_getregs_layout,
-                                          0, offsetof(struct pt_regs, y),
-                                          uaddr, NULL, 1);
-               if (!err &&
-                   (get_user(task_pt_regs(child)->y, &uregs->y) ||
-                    get_user(task_pt_regs(child)->fprs, &uregs->fprs)))
-                       err = -EFAULT;
-               break;
+               if ((request >= 0) && (request <= 24))
+                       s = pt_rq [request];
+               else
+                       s = "unknown";
+
+               if (request == PTRACE_POKEDATA && data == 0x91d02001){
+                       printk ("do_ptrace: breakpoint pid=%d, addr=%016lx addr2=%016lx\n",
+                               pid, addr, addr2);
+               } else 
+                       printk("do_ptrace: rq=%s(%d) pid=%d addr=%016lx data=%016lx addr2=%016lx\n",
+                              s, request, pid, addr, data, addr2);
+       }
+#endif
+       if (request == PTRACE_TRACEME) {
+               ret = ptrace_traceme();
+               pt_succ_return(regs, 0);
+               goto out;
+       }
 
-       case PTRACE_GETFPREGS64:
-       case PTRACE_SETFPREGS64:
-               err = ptrace_regset_access(child, engine,
-                                          utrace_native_view(current),
-                                          2, 0, 34 * sizeof(long), uaddr,
-                                          (*request == PTRACE_SETFPREGS64));
-               break;
+       child = ptrace_get_task_struct(pid);
+       if (IS_ERR(child)) {
+               ret = PTR_ERR(child);
+               pt_error_return(regs, -ret);
+               goto out;
+       }
+       if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT)) {
+               pt_error_return(regs, ESRCH);
+               goto out_tsk;
+       }
 
-       case PTRACE_SUNDETACH:
-               *request = PTRACE_DETACH;
-               break;
+       if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
+           || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
+               if (ptrace_attach(child)) {
+                       pt_error_return(regs, EPERM);
+                       goto out_tsk;
+               }
+               pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
+
+       ret = ptrace_check_attach(child, request == PTRACE_KILL);
+       if (ret < 0) {
+               pt_error_return(regs, -ret);
+               goto out_tsk;
+       }
+
+       if (!(test_thread_flag(TIF_32BIT))      &&
+           ((request == PTRACE_READDATA64)             ||
+            (request == PTRACE_WRITEDATA64)            ||
+            (request == PTRACE_READTEXT64)             ||
+            (request == PTRACE_WRITETEXT64)            ||
+            (request == PTRACE_PEEKTEXT64)             ||
+            (request == PTRACE_POKETEXT64)             ||
+            (request == PTRACE_PEEKDATA64)             ||
+            (request == PTRACE_POKEDATA64))) {
+               addr = regs->u_regs[UREG_G2];
+               addr2 = regs->u_regs[UREG_G3];
+               request -= 30; /* wheee... */
+       }
+
+       switch(request) {
+       case PTRACE_PEEKUSR:
+               if (addr != 0)
+                       pt_error_return(regs, EIO);
+               else
+                       pt_succ_return(regs, 0);
+               goto out_tsk;
+
+       case PTRACE_PEEKTEXT: /* read word at location addr. */ 
+       case PTRACE_PEEKDATA: {
+               unsigned long tmp64;
+               unsigned int tmp32;
+               int res, copied;
+
+               res = -EIO;
+               if (test_thread_flag(TIF_32BIT)) {
+                       copied = access_process_vm(child, addr,
+                                                  &tmp32, sizeof(tmp32), 0);
+                       tmp64 = (unsigned long) tmp32;
+                       if (copied == sizeof(tmp32))
+                               res = 0;
+               } else {
+                       copied = access_process_vm(child, addr,
+                                                  &tmp64, sizeof(tmp64), 0);
+                       if (copied == sizeof(tmp64))
+                               res = 0;
+               }
+               if (res < 0)
+                       pt_error_return(regs, -res);
+               else
+                       pt_os_succ_return(regs, tmp64, (void __user *) data);
+               goto out_tsk;
+       }
+
+       case PTRACE_POKETEXT: /* write the word at location addr. */
+       case PTRACE_POKEDATA: {
+               unsigned long tmp64;
+               unsigned int tmp32;
+               int copied, res = -EIO;
+
+               if (test_thread_flag(TIF_32BIT)) {
+                       tmp32 = data;
+                       copied = access_process_vm(child, addr,
+                                                  &tmp32, sizeof(tmp32), 1);
+                       if (copied == sizeof(tmp32))
+                               res = 0;
+               } else {
+                       tmp64 = data;
+                       copied = access_process_vm(child, addr,
+                                                  &tmp64, sizeof(tmp64), 1);
+                       if (copied == sizeof(tmp64))
+                               res = 0;
+               }
+               if (res < 0)
+                       pt_error_return(regs, -res);
+               else
+                       pt_succ_return(regs, res);
+               goto out_tsk;
+       }
+
+       case PTRACE_GETREGS: {
+               struct pt_regs32 __user *pregs =
+                       (struct pt_regs32 __user *) addr;
+               struct pt_regs *cregs = task_pt_regs(child);
+               int rval;
+
+               if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) ||
+                   __put_user(cregs->tpc, (&pregs->pc)) ||
+                   __put_user(cregs->tnpc, (&pregs->npc)) ||
+                   __put_user(cregs->y, (&pregs->y))) {
+                       pt_error_return(regs, EFAULT);
+                       goto out_tsk;
+               }
+               for (rval = 1; rval < 16; rval++)
+                       if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
+                               pt_error_return(regs, EFAULT);
+                               goto out_tsk;
+                       }
+               pt_succ_return(regs, 0);
+#ifdef DEBUG_PTRACE
+               printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
+#endif
+               goto out_tsk;
+       }
+
+       case PTRACE_GETREGS64: {
+               struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
+               struct pt_regs *cregs = task_pt_regs(child);
+               unsigned long tpc = cregs->tpc;
+               int rval;
+
+               if ((task_thread_info(child)->flags & _TIF_32BIT) != 0)
+                       tpc &= 0xffffffff;
+               if (__put_user(cregs->tstate, (&pregs->tstate)) ||
+                   __put_user(tpc, (&pregs->tpc)) ||
+                   __put_user(cregs->tnpc, (&pregs->tnpc)) ||
+                   __put_user(cregs->y, (&pregs->y))) {
+                       pt_error_return(regs, EFAULT);
+                       goto out_tsk;
+               }
+               for (rval = 1; rval < 16; rval++)
+                       if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
+                               pt_error_return(regs, EFAULT);
+                               goto out_tsk;
+                       }
+               pt_succ_return(regs, 0);
+#ifdef DEBUG_PTRACE
+               printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
+#endif
+               goto out_tsk;
+       }
+
+       case PTRACE_SETREGS: {
+               struct pt_regs32 __user *pregs =
+                       (struct pt_regs32 __user *) addr;
+               struct pt_regs *cregs = task_pt_regs(child);
+               unsigned int psr, pc, npc, y;
+               int i;
+
+               /* Must be careful, tracing process can only set certain
+                * bits in the psr.
+                */
+               if (__get_user(psr, (&pregs->psr)) ||
+                   __get_user(pc, (&pregs->pc)) ||
+                   __get_user(npc, (&pregs->npc)) ||
+                   __get_user(y, (&pregs->y))) {
+                       pt_error_return(regs, EFAULT);
+                       goto out_tsk;
+               }
+               cregs->tstate &= ~(TSTATE_ICC);
+               cregs->tstate |= psr_to_tstate_icc(psr);
+                       if (!((pc | npc) & 3)) {
+                       cregs->tpc = pc;
+                       cregs->tnpc = npc;
+               }
+               cregs->y = y;
+               for (i = 1; i < 16; i++) {
+                       if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
+                               pt_error_return(regs, EFAULT);
+                               goto out_tsk;
+                       }
+               }
+               pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
+
+       case PTRACE_SETREGS64: {
+               struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
+               struct pt_regs *cregs = task_pt_regs(child);
+               unsigned long tstate, tpc, tnpc, y;
+               int i;
+
+               /* Must be careful, tracing process can only set certain
+                * bits in the psr.
+                */
+               if (__get_user(tstate, (&pregs->tstate)) ||
+                   __get_user(tpc, (&pregs->tpc)) ||
+                   __get_user(tnpc, (&pregs->tnpc)) ||
+                   __get_user(y, (&pregs->y))) {
+                       pt_error_return(regs, EFAULT);
+                       goto out_tsk;
+               }
+               if ((task_thread_info(child)->flags & _TIF_32BIT) != 0) {
+                       tpc &= 0xffffffff;
+                       tnpc &= 0xffffffff;
+               }
+               tstate &= (TSTATE_ICC | TSTATE_XCC);
+               cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
+               cregs->tstate |= tstate;
+               if (!((tpc | tnpc) & 3)) {
+                       cregs->tpc = tpc;
+                       cregs->tnpc = tnpc;
+               }
+               cregs->y = y;
+               for (i = 1; i < 16; i++) {
+                       if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
+                               pt_error_return(regs, EFAULT);
+                               goto out_tsk;
+                       }
+               }
+               pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
+
+       case PTRACE_GETFPREGS: {
+               struct fps {
+                       unsigned int regs[32];
+                       unsigned int fsr;
+                       unsigned int flags;
+                       unsigned int extra;
+                       unsigned int fpqd;
+                       struct fq {
+                               unsigned int insnaddr;
+                               unsigned int insn;
+                       } fpq[16];
+               };
+               struct fps __user *fps = (struct fps __user *) addr;
+               unsigned long *fpregs = task_thread_info(child)->fpregs;
+
+               if (copy_to_user(&fps->regs[0], fpregs,
+                                (32 * sizeof(unsigned int))) ||
+                   __put_user(task_thread_info(child)->xfsr[0], (&fps->fsr)) ||
+                   __put_user(0, (&fps->fpqd)) ||
+                   __put_user(0, (&fps->flags)) ||
+                   __put_user(0, (&fps->extra)) ||
+                   clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) {
+                       pt_error_return(regs, EFAULT);
+                       goto out_tsk;
+               }
+               pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
+
+       case PTRACE_GETFPREGS64: {
+               struct fps {
+                       unsigned int regs[64];
+                       unsigned long fsr;
+               };
+               struct fps __user *fps = (struct fps __user *) addr;
+               unsigned long *fpregs = task_thread_info(child)->fpregs;
+
+               if (copy_to_user(&fps->regs[0], fpregs,
+                                (64 * sizeof(unsigned int))) ||
+                   __put_user(task_thread_info(child)->xfsr[0], (&fps->fsr))) {
+                       pt_error_return(regs, EFAULT);
+                       goto out_tsk;
+               }
+               pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
+
+       case PTRACE_SETFPREGS: {
+               struct fps {
+                       unsigned int regs[32];
+                       unsigned int fsr;
+                       unsigned int flags;
+                       unsigned int extra;
+                       unsigned int fpqd;
+                       struct fq {
+                               unsigned int insnaddr;
+                               unsigned int insn;
+                       } fpq[16];
+               };
+               struct fps __user *fps = (struct fps __user *) addr;
+               unsigned long *fpregs = task_thread_info(child)->fpregs;
+               unsigned fsr;
+
+               if (copy_from_user(fpregs, &fps->regs[0],
+                                  (32 * sizeof(unsigned int))) ||
+                   __get_user(fsr, (&fps->fsr))) {
+                       pt_error_return(regs, EFAULT);
+                       goto out_tsk;
+               }
+               task_thread_info(child)->xfsr[0] &= 0xffffffff00000000UL;
+               task_thread_info(child)->xfsr[0] |= fsr;
+               if (!(task_thread_info(child)->fpsaved[0] & FPRS_FEF))
+                       task_thread_info(child)->gsr[0] = 0;
+               task_thread_info(child)->fpsaved[0] |= (FPRS_FEF | FPRS_DL);
+               pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
+
+       case PTRACE_SETFPREGS64: {
+               struct fps {
+                       unsigned int regs[64];
+                       unsigned long fsr;
+               };
+               struct fps __user *fps = (struct fps __user *) addr;
+               unsigned long *fpregs = task_thread_info(child)->fpregs;
+
+               if (copy_from_user(fpregs, &fps->regs[0],
+                                  (64 * sizeof(unsigned int))) ||
+                   __get_user(task_thread_info(child)->xfsr[0], (&fps->fsr))) {
+                       pt_error_return(regs, EFAULT);
+                       goto out_tsk;
+               }
+               if (!(task_thread_info(child)->fpsaved[0] & FPRS_FEF))
+                       task_thread_info(child)->gsr[0] = 0;
+               task_thread_info(child)->fpsaved[0] |= (FPRS_FEF | FPRS_DL | FPRS_DU);
+               pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
+
+       case PTRACE_READTEXT:
+       case PTRACE_READDATA: {
+               int res = ptrace_readdata(child, addr,
+                                         (char __user *)addr2, data);
+               if (res == data) {
+                       pt_succ_return(regs, 0);
+                       goto out_tsk;
+               }
+               if (res >= 0)
+                       res = -EIO;
+               pt_error_return(regs, -res);
+               goto out_tsk;
+       }
+
+       case PTRACE_WRITETEXT:
+       case PTRACE_WRITEDATA: {
+               int res = ptrace_writedata(child, (char __user *) addr2,
+                                          addr, data);
+               if (res == data) {
+                       pt_succ_return(regs, 0);
+                       goto out_tsk;
+               }
+               if (res >= 0)
+                       res = -EIO;
+               pt_error_return(regs, -res);
+               goto out_tsk;
+       }
+       case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
+               addr = 1;
+
+       case PTRACE_CONT: { /* restart after signal. */
+               if (!valid_signal(data)) {
+                       pt_error_return(regs, EIO);
+                       goto out_tsk;
+               }
+
+               if (request == PTRACE_SYSCALL) {
+                       set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+               } else {
+                       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+               }
+
+               child->exit_code = data;
+#ifdef DEBUG_PTRACE
+               printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", child->comm,
+                       child->pid, child->exit_code,
+                       task_pt_regs(child)->tpc,
+                       task_pt_regs(child)->tnpc);
                       
-       default:
-               break;
-       };
-       return err;
-}
+#endif
+               wake_up_process(child);
+               pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
 
-#ifdef CONFIG_COMPAT
-static const struct ptrace_layout_segment sparc32_getregs_layout[] = {
-       { 0, offsetof(struct pt_regs32, u_regs[0]),
-         0, GENREG32_PSR * sizeof(u32) },
-       { offsetof(struct pt_regs32, u_regs[0]),
-         offsetof(struct pt_regs32, u_regs[15]),
-         0, 1 * sizeof(u32) },
-       { offsetof(struct pt_regs32, u_regs[15]), sizeof(struct pt_regs32),
-         -1, 0 },
-       {0, 0, -1, 0}
-};
+/*
+ * make the child exit.  Best I can do is send it a sigkill. 
+ * perhaps it should be put in the status that it wants to 
+ * exit.
+ */
+       case PTRACE_KILL: {
+               if (child->exit_state == EXIT_ZOMBIE) { /* already dead */
+                       pt_succ_return(regs, 0);
+                       goto out_tsk;
+               }
+               child->exit_code = SIGKILL;
+               wake_up_process(child);
+               pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
 
-int arch_compat_ptrace(compat_long_t *request, struct task_struct *child,
-                      struct utrace_attached_engine *engine,
-                      compat_ulong_t addr, compat_ulong_t data,
-                      compat_long_t *retval)
-{
-       void __user *uaddr = (void __user *) (unsigned long) addr;
-       int err = -ENOSYS;
-
-       switch (*request) {
-       case PTRACE_GETREGS:
-       case PTRACE_SETREGS:
-               err = ptrace_layout_access(child, engine,
-                                          &utrace_sparc32_view,
-                                          sparc32_getregs_layout,
-                                          0, sizeof(struct pt_regs32),
-                                          uaddr, NULL,
-                                          (*request ==
-                                           PTRACE_SETREGS));
-               break;
+       case PTRACE_SUNDETACH: { /* detach a process that was attached. */
+               int error = ptrace_detach(child, data);
+               if (error) {
+                       pt_error_return(regs, EIO);
+                       goto out_tsk;
+               }
+               pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
 
-       case PTRACE_GETFPREGS:
-       case PTRACE_SETFPREGS:
-               err = ptrace_whole_regset(child, engine, addr, 1,
-                                         (*request == PTRACE_SETFPREGS));
-               break;
+       /* PTRACE_DUMPCORE unsupported... */
 
-       case PTRACE_SUNDETACH:
-               *request = PTRACE_DETACH;
-               break;
+       case PTRACE_GETEVENTMSG: {
+               int err;
 
-       default:
+               if (test_thread_flag(TIF_32BIT))
+                       err = put_user(child->ptrace_message,
+                                      (unsigned int __user *) data);
+               else
+                       err = put_user(child->ptrace_message,
+                                      (unsigned long __user *) data);
+               if (err)
+                       pt_error_return(regs, -err);
+               else
+                       pt_succ_return(regs, 0);
                break;
-       };
-       return err;
+       }
+
+       default: {
+               int err = ptrace_request(child, request, addr, data);
+               if (err)
+                       pt_error_return(regs, -err);
+               else
+                       pt_succ_return(regs, 0);
+               goto out_tsk;
+       }
+       }
+out_tsk:
+       if (child)
+               put_task_struct(child);
+out:
+       unlock_kernel();
 }
-#endif /* CONFIG_COMPAT */
-#endif /* CONFIG_PTRACE */
 
 asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p)
 {
        /* do the secure computing check first */
-       if (!syscall_exit_p)
-               secure_computing(regs->u_regs[UREG_G1]);
+       secure_computing(regs->u_regs[UREG_G1]);
 
        if (unlikely(current->audit_context) && syscall_exit_p) {
                unsigned long tstate = regs->tstate;
@@ -794,9 +660,26 @@ asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p)
                audit_syscall_exit(result, regs->u_regs[UREG_I0]);
        }
 
-       if (test_thread_flag(TIF_SYSCALL_TRACE))
-               tracehook_report_syscall(regs, syscall_exit_p);
+       if (!(current->ptrace & PT_PTRACED))
+               goto out;
+
+       if (!test_thread_flag(TIF_SYSCALL_TRACE))
+               goto out;
+
+       ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+                                ? 0x80 : 0));
+
+       /*
+        * this isn't the same as continuing with a signal, but it will do
+        * for normal use.  strace only continues with a signal if the
+        * stopping signal is not SIGTRAP.  -brl
+        */
+       if (current->exit_code) {
+               send_sig(current->exit_code, current, 1);
+               current->exit_code = 0;
+       }
 
+out:
        if (unlikely(current->audit_context) && !syscall_exit_p)
                audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
                                     AUDIT_ARCH_SPARC :