vserver 2.0 rc7
[linux-2.6.git] / arch / sparc64 / kernel / ptrace.c
index d935eb6..f493d03 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/security.h>
+#include <linux/signal.h>
 
 #include <asm/asi.h>
 #include <asm/pgtable.h>
@@ -50,14 +51,18 @@ static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
 }
 
 static inline void
-pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long *addr)
+pt_succ_return_linux(struct pt_regs *regs, unsigned long value, void __user *addr)
 {
        if (test_thread_flag(TIF_32BIT)) {
-               if (put_user(value, (unsigned int *)addr))
-                       return pt_error_return(regs, EFAULT);
+               if (put_user(value, (unsigned int __user *) addr)) {
+                       pt_error_return(regs, EFAULT);
+                       return;
+               }
        } else {
-               if (put_user(value, addr))
-                       return pt_error_return(regs, EFAULT);
+               if (put_user(value, (long __user *) addr)) {
+                       pt_error_return(regs, EFAULT);
+                       return;
+               }
        }
        regs->u_regs[UREG_I0] = 0;
        regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY);
@@ -66,7 +71,7 @@ pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long *addr)
 }
 
 static void
-pt_os_succ_return (struct pt_regs *regs, unsigned long val, long *addr)
+pt_os_succ_return (struct pt_regs *regs, unsigned long val, void __user *addr)
 {
        if (current->personality == PER_SUNOS)
                pt_succ_return (regs, val);
@@ -99,6 +104,55 @@ void ptrace_disable(struct task_struct *child)
        /* nothing to do */
 }
 
+/* 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
+ * to memcpy to read/write the data from that page.
+ *
+ * Now, the only thing we have to do is:
+ * 1) flush the D-cache if it's possible than an illegal alias
+ *    has been created
+ * 2) flush the I-cache if this is pre-cheetah and we did a write
+ */
+void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
+                        unsigned long uaddr, void *kaddr,
+                        unsigned long len, int write)
+{
+       BUG_ON(len > PAGE_SIZE);
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+       /* If bit 13 of the kernel address we used to access the
+        * user page is the same as the virtual address that page
+        * is mapped to in the user's address space, we can skip the
+        * D-cache flush.
+        */
+       if ((uaddr ^ kaddr) & (1UL << 13)) {
+               unsigned long start = __pa(kaddr);
+               unsigned long end = start + len;
+
+               if (tlb_type == spitfire) {
+                       for (; start < end; start += 32)
+                               spitfire_put_dcache_tag(va & 0x3fe0, 0x0);
+               } else {
+                       for (; start < end; start += 32)
+                               __asm__ __volatile__(
+                                       "stxa %%g0, [%0] %1\n\t"
+                                       "membar #Sync"
+                                       : /* no outputs */
+                                       : "r" (va),
+                                       "i" (ASI_DCACHE_INVALIDATE));
+               }
+       }
+#endif
+       if (write && tlb_type == spitfire) {
+               unsigned long start = (unsigned long) kaddr;
+               unsigned long end = start + len;
+
+               for (; start < end; start += 32)
+                       flushi(start);
+       }
+}
+
 asmlinkage void do_ptrace(struct pt_regs *regs)
 {
        int request = regs->u_regs[UREG_I0];
@@ -168,6 +222,10 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
                pt_error_return(regs, ESRCH);
                goto out;
        }
+       if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT)) {
+               pt_error_return(regs, ESRCH);
+               goto out_tsk;
+       }
 
        if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
            || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
@@ -222,8 +280,8 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
                if (res < 0)
                        pt_error_return(regs, -res);
                else
-                       pt_os_succ_return(regs, tmp64, (long *) data);
-               goto flush_and_out;
+                       pt_os_succ_return(regs, tmp64, (void __user *) data);
+               goto out_tsk;
        }
 
        case PTRACE_POKETEXT: /* write the word at location addr. */
@@ -249,11 +307,12 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
                        pt_error_return(regs, -res);
                else
                        pt_succ_return(regs, res);
-               goto flush_and_out;
+               goto out_tsk;
        }
 
        case PTRACE_GETREGS: {
-               struct pt_regs32 *pregs = (struct pt_regs32 *) addr;
+               struct pt_regs32 __user *pregs =
+                       (struct pt_regs32 __user *) addr;
                struct pt_regs *cregs = child->thread_info->kregs;
                int rval;
 
@@ -277,7 +336,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
        }
 
        case PTRACE_GETREGS64: {
-               struct pt_regs *pregs = (struct pt_regs *) addr;
+               struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
                struct pt_regs *cregs = child->thread_info->kregs;
                unsigned long tpc = cregs->tpc;
                int rval;
@@ -304,7 +363,8 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
        }
 
        case PTRACE_SETREGS: {
-               struct pt_regs32 *pregs = (struct pt_regs32 *) addr;
+               struct pt_regs32 __user *pregs =
+                       (struct pt_regs32 __user *) addr;
                struct pt_regs *cregs = child->thread_info->kregs;
                unsigned int psr, pc, npc, y;
                int i;
@@ -337,7 +397,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
        }
 
        case PTRACE_SETREGS64: {
-               struct pt_regs *pregs = (struct pt_regs *) addr;
+               struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
                struct pt_regs *cregs = child->thread_info->kregs;
                unsigned long tstate, tpc, tnpc, y;
                int i;
@@ -385,7 +445,8 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
                                unsigned int insnaddr;
                                unsigned int insn;
                        } fpq[16];
-               } *fps = (struct fps *) addr;
+               };
+               struct fps __user *fps = (struct fps __user *) addr;
                unsigned long *fpregs = child->thread_info->fpregs;
 
                if (copy_to_user(&fps->regs[0], fpregs,
@@ -406,7 +467,8 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
                struct fps {
                        unsigned int regs[64];
                        unsigned long fsr;
-               } *fps = (struct fps *) addr;
+               };
+               struct fps __user *fps = (struct fps __user *) addr;
                unsigned long *fpregs = child->thread_info->fpregs;
 
                if (copy_to_user(&fps->regs[0], fpregs,
@@ -430,7 +492,8 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
                                unsigned int insnaddr;
                                unsigned int insn;
                        } fpq[16];
-               } *fps = (struct fps *) addr;
+               };
+               struct fps __user *fps = (struct fps __user *) addr;
                unsigned long *fpregs = child->thread_info->fpregs;
                unsigned fsr;
 
@@ -453,7 +516,8 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
                struct fps {
                        unsigned int regs[64];
                        unsigned long fsr;
-               } *fps = (struct fps *) addr;
+               };
+               struct fps __user *fps = (struct fps __user *) addr;
                unsigned long *fpregs = child->thread_info->fpregs;
 
                if (copy_from_user(fpregs, &fps->regs[0],
@@ -472,57 +536,38 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
        case PTRACE_READTEXT:
        case PTRACE_READDATA: {
                int res = ptrace_readdata(child, addr,
-                                         (void *)addr2, data);
+                                         (char __user *)addr2, data);
                if (res == data) {
                        pt_succ_return(regs, 0);
-                       goto flush_and_out;
+                       goto out_tsk;
                }
                if (res >= 0)
                        res = -EIO;
                pt_error_return(regs, -res);
-               goto flush_and_out;
+               goto out_tsk;
        }
 
        case PTRACE_WRITETEXT:
        case PTRACE_WRITEDATA: {
-               int res = ptrace_writedata(child, (void *) addr2,
+               int res = ptrace_writedata(child, (char __user *) addr2,
                                           addr, data);
                if (res == data) {
                        pt_succ_return(regs, 0);
-                       goto flush_and_out;
+                       goto out_tsk;
                }
                if (res >= 0)
                        res = -EIO;
                pt_error_return(regs, -res);
-               goto flush_and_out;
+               goto out_tsk;
        }
        case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
                addr = 1;
 
        case PTRACE_CONT: { /* restart after signal. */
-               if (data > _NSIG) {
+               if (!valid_signal(data)) {
                        pt_error_return(regs, EIO);
                        goto out_tsk;
                }
-               if (addr != 1) {
-                       unsigned long pc_mask = ~0UL;
-
-                       if ((child->thread_info->flags & _TIF_32BIT) != 0)
-                               pc_mask = 0xffffffff;
-
-                       if (addr & 3) {
-                               pt_error_return(regs, EINVAL);
-                               goto out_tsk;
-                       }
-#ifdef DEBUG_PTRACE
-                       printk ("Original: %016lx %016lx\n",
-                               child->thread_info->kregs->tpc,
-                               child->thread_info->kregs->tnpc);
-                       printk ("Continuing with %016lx %016lx\n", addr, addr+4);
-#endif
-                       child->thread_info->kregs->tpc = (addr & pc_mask);
-                       child->thread_info->kregs->tnpc = ((addr + 4) & pc_mask);
-               }
 
                if (request == PTRACE_SYSCALL) {
                        set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
@@ -549,7 +594,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
  * exit.
  */
        case PTRACE_KILL: {
-               if (child->state == TASK_ZOMBIE) {      /* already dead */
+               if (child->exit_state == EXIT_ZOMBIE) { /* already dead */
                        pt_succ_return(regs, 0);
                        goto out_tsk;
                }
@@ -580,27 +625,6 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
                goto out_tsk;
        }
        }
-flush_and_out:
-       {
-               unsigned long va;
-
-               if (tlb_type == cheetah || tlb_type == cheetah_plus) {
-                       for (va = 0; va < (1 << 16); va += (1 << 5))
-                               spitfire_put_dcache_tag(va, 0x0);
-                       /* No need to mess with I-cache on Cheetah. */
-               } else {
-                       for (va =  0; va < L1DCACHE_SIZE; va += 32)
-                               spitfire_put_dcache_tag(va, 0x0);
-                       if (request == PTRACE_PEEKTEXT ||
-                           request == PTRACE_POKETEXT ||
-                           request == PTRACE_READTEXT ||
-                           request == PTRACE_WRITETEXT) {
-                               for (va =  0; va < (PAGE_SIZE << 1); va += 32)
-                                       spitfire_put_icache_tag(va, 0x0);
-                               __asm__ __volatile__("flush %g6");
-                       }
-               }
-       }
 out_tsk:
        if (child)
                put_task_struct(child);
@@ -617,11 +641,8 @@ asmlinkage void syscall_trace(void)
                return;
        if (!(current->ptrace & PT_PTRACED))
                return;
-       current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
-                                       ? 0x80 : 0);
-       current->state = TASK_STOPPED;
-       notify_parent(current, SIGCHLD);
-       schedule();
+       ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+                                ? 0x80 : 0));
 
        /*
         * this isn't the same as continuing with a signal, but it will do