]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/sparc/kernel/ptrace.c
Merge commit 'v2.6.26' into core/locking
[linux-2.6-omap-h63xx.git] / arch / sparc / kernel / ptrace.c
index 29fa6e5cb450026b45fd16fc3d154a5273821475..81f3b929743f12c17ec44642acb58f6526d4319c 100644 (file)
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
-#define MAGIC_CONSTANT 0x80000000
-
-
-/* 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.
- */
-static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
-{
-       regs->u_regs[UREG_I0] = error;
-       regs->psr |= PSR_C;
-       regs->pc = regs->npc;
-       regs->npc += 4;
-}
-
-static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
-{
-       regs->u_regs[UREG_I0] = value;
-       regs->psr &= ~PSR_C;
-       regs->pc = regs->npc;
-       regs->npc += 4;
-}
-
-static void
-pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr)
-{
-       if (put_user(value, addr)) {
-               pt_error_return(regs, EFAULT);
-               return;
-       }
-       regs->u_regs[UREG_I0] = 0;
-       regs->psr &= ~PSR_C;
-       regs->pc = regs->npc;
-       regs->npc += 4;
-}
-
-static void
-pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr)
-{
-       if (current->personality == PER_SUNOS)
-               pt_succ_return (regs, val);
-       else
-               pt_succ_return_linux (regs, val, addr);
-}
-
-/* Fuck me gently with a chainsaw... */
-static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset,
-                                  struct task_struct *tsk, long __user *addr)
-{
-       struct pt_regs *cregs = tsk->thread.kregs;
-       struct thread_info *t = task_thread_info(tsk);
-       int v;
-       
-       if(offset >= 1024)
-               offset -= 1024; /* whee... */
-       if(offset & ((sizeof(unsigned long) - 1))) {
-               pt_error_return(regs, EIO);
-               return;
-       }
-       if(offset >= 16 && offset < 784) {
-               offset -= 16; offset >>= 2;
-               pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
-               return;
-       }
-       if(offset >= 784 && offset < 832) {
-               offset -= 784; offset >>= 2;
-               pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
-               return;
-       }
-       switch(offset) {
-       case 0:
-               v = t->ksp;
-               break;
-       case 4:
-               v = t->kpc;
-               break;
-       case 8:
-               v = t->kpsr;
-               break;
-       case 12:
-               v = t->uwinmask;
-               break;
-       case 832:
-               v = t->w_saved;
-               break;
-       case 896:
-               v = cregs->u_regs[UREG_I0];
-               break;
-       case 900:
-               v = cregs->u_regs[UREG_I1];
-               break;
-       case 904:
-               v = cregs->u_regs[UREG_I2];
-               break;
-       case 908:
-               v = cregs->u_regs[UREG_I3];
-               break;
-       case 912:
-               v = cregs->u_regs[UREG_I4];
-               break;
-       case 916:
-               v = cregs->u_regs[UREG_I5];
-               break;
-       case 920:
-               v = cregs->u_regs[UREG_I6];
-               break;
-       case 924:
-               if(tsk->thread.flags & MAGIC_CONSTANT)
-                       v = cregs->u_regs[UREG_G1];
-               else
-                       v = 0;
-               break;
-       case 940:
-               v = cregs->u_regs[UREG_I0];
-               break;
-       case 944:
-               v = cregs->u_regs[UREG_I1];
-               break;
-
-       case 948:
-               /* Isn't binary compatibility _fun_??? */
-               if(cregs->psr & PSR_C)
-                       v = cregs->u_regs[UREG_I0] << 24;
-               else
-                       v = 0;
-               break;
-
-               /* Rest of them are completely unsupported. */
-       default:
-               printk("%s [%d]: Wants to read user offset %ld\n",
-                      current->comm, task_pid_nr(current), offset);
-               pt_error_return(regs, EIO);
-               return;
-       }
-       if (current->personality == PER_SUNOS)
-               pt_succ_return (regs, v);
-       else
-               pt_succ_return_linux (regs, v, addr);
-       return;
-}
-
-static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset,
-                                   struct task_struct *tsk)
-{
-       struct pt_regs *cregs = tsk->thread.kregs;
-       struct thread_info *t = task_thread_info(tsk);
-       unsigned long value = regs->u_regs[UREG_I3];
-
-       if(offset >= 1024)
-               offset -= 1024; /* whee... */
-       if(offset & ((sizeof(unsigned long) - 1)))
-               goto failure;
-       if(offset >= 16 && offset < 784) {
-               offset -= 16; offset >>= 2;
-               *(((unsigned long *)(&t->reg_window[0]))+offset) = value;
-               goto success;
-       }
-       if(offset >= 784 && offset < 832) {
-               offset -= 784; offset >>= 2;
-               *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value;
-               goto success;
-       }
-       switch(offset) {
-       case 896:
-               cregs->u_regs[UREG_I0] = value;
-               break;
-       case 900:
-               cregs->u_regs[UREG_I1] = value;
-               break;
-       case 904:
-               cregs->u_regs[UREG_I2] = value;
-               break;
-       case 908:
-               cregs->u_regs[UREG_I3] = value;
-               break;
-       case 912:
-               cregs->u_regs[UREG_I4] = value;
-               break;
-       case 916:
-               cregs->u_regs[UREG_I5] = value;
-               break;
-       case 920:
-               cregs->u_regs[UREG_I6] = value;
-               break;
-       case 924:
-               cregs->u_regs[UREG_I7] = value;
-               break;
-       case 940:
-               cregs->u_regs[UREG_I0] = value;
-               break;
-       case 944:
-               cregs->u_regs[UREG_I1] = value;
-               break;
-
-               /* Rest of them are completely unsupported or "no-touch". */
-       default:
-               printk("%s [%d]: Wants to write user offset %ld\n",
-                      current->comm, task_pid_nr(current), offset);
-               goto failure;
-       }
-success:
-       pt_succ_return(regs, 0);
-       return;
-failure:
-       pt_error_return(regs, EIO);
-       return;
-}
-
 /* #define ALLOW_INIT_TRACING */
 
 /*
@@ -379,8 +170,8 @@ static int genregs32_set(struct task_struct *target,
                switch (pos) {
                case 32: /* PSR */
                        psr = regs->psr;
-                       psr &= ~PSR_ICC;
-                       psr |= (reg & PSR_ICC);
+                       psr &= ~(PSR_ICC | PSR_SYSCALL);
+                       psr |= (reg & (PSR_ICC | PSR_SYSCALL));
                        regs->psr = psr;
                        break;
                case 33: /* PC */
@@ -528,129 +319,43 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task)
        return &user_sparc32_view;
 }
 
-asmlinkage void do_ptrace(struct pt_regs *regs)
+long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 {
-       unsigned long request = regs->u_regs[UREG_I0];
-       unsigned long 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;
+       unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4];
+       const struct user_regset_view *view;
        int ret;
 
-       lock_kernel();
-
-       if (request == PTRACE_TRACEME) {
-               ret = ptrace_traceme();
-               if (ret < 0)
-                       pt_error_return(regs, -ret);
-               else
-                       pt_succ_return(regs, 0);
-               goto out;
-       }
-
-       child = ptrace_get_task_struct(pid);
-       if (IS_ERR(child)) {
-               ret = PTR_ERR(child);
-               pt_error_return(regs, -ret);
-               goto out;
-       }
-
-       if (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;
-       }
+       view = task_user_regset_view(current);
 
        switch(request) {
-       case PTRACE_PEEKTEXT: /* read word at location addr. */ 
-       case PTRACE_PEEKDATA: {
-               unsigned long tmp;
-
-               if (access_process_vm(child, addr,
-                                     &tmp, sizeof(tmp), 0) == sizeof(tmp))
-                       pt_os_succ_return(regs, tmp, (long __user *)data);
-               else
-                       pt_error_return(regs, EIO);
-               goto out_tsk;
-       }
-
-       case PTRACE_PEEKUSR:
-               read_sunos_user(regs, addr, child, (long __user *) data);
-               goto out_tsk;
-
-       case PTRACE_POKEUSR:
-               write_sunos_user(regs, addr, child);
-               goto out_tsk;
-
-       case PTRACE_POKETEXT: /* write the word at location addr. */
-       case PTRACE_POKEDATA: {
-               if (access_process_vm(child, addr,
-                                     &data, sizeof(data), 1) == sizeof(data))
-                       pt_succ_return(regs, 0);
-               else
-                       pt_error_return(regs, EIO);
-               goto out_tsk;
-       }
-
        case PTRACE_GETREGS: {
                struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
-               struct pt_regs *cregs = child->thread.kregs;
-               int rval;
 
-               if (!access_ok(VERIFY_WRITE, pregs, sizeof(struct pt_regs))) {
-                       rval = -EFAULT;
-                       pt_error_return(regs, -rval);
-                       goto out_tsk;
-               }
-               __put_user(cregs->psr, (&pregs->psr));
-               __put_user(cregs->pc, (&pregs->pc));
-               __put_user(cregs->npc, (&pregs->npc));
-               __put_user(cregs->y, (&pregs->y));
-               for(rval = 1; rval < 16; rval++)
-                       __put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]));
-               pt_succ_return(regs, 0);
-               goto out_tsk;
+               ret = copy_regset_to_user(child, view, REGSET_GENERAL,
+                                         32 * sizeof(u32),
+                                         4 * sizeof(u32),
+                                         &pregs->psr);
+               if (!ret)
+                       copy_regset_to_user(child, view, REGSET_GENERAL,
+                                           1 * sizeof(u32),
+                                           15 * sizeof(u32),
+                                           &pregs->u_regs[0]);
+               break;
        }
 
        case PTRACE_SETREGS: {
                struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
-               struct pt_regs *cregs = child->thread.kregs;
-               unsigned long psr, pc, npc, y;
-               int i;
-
-               /* Must be careful, tracing process can only set certain
-                * bits in the psr.
-                */
-               if (!access_ok(VERIFY_READ, pregs, sizeof(struct pt_regs))) {
-                       pt_error_return(regs, EFAULT);
-                       goto out_tsk;
-               }
-               __get_user(psr, (&pregs->psr));
-               __get_user(pc, (&pregs->pc));
-               __get_user(npc, (&pregs->npc));
-               __get_user(y, (&pregs->y));
-               psr &= PSR_ICC;
-               cregs->psr &= ~PSR_ICC;
-               cregs->psr |= psr;
-               if (!((pc | npc) & 3)) {
-                       cregs->pc = pc;
-                       cregs->npc =npc;
-               }
-               cregs->y = y;
-               for(i = 1; i < 16; i++)
-                       __get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]));
-               pt_succ_return(regs, 0);
-               goto out_tsk;
+
+               ret = copy_regset_from_user(child, view, REGSET_GENERAL,
+                                           32 * sizeof(u32),
+                                           4 * sizeof(u32),
+                                           &pregs->psr);
+               if (!ret)
+                       copy_regset_from_user(child, view, REGSET_GENERAL,
+                                             1 * sizeof(u32),
+                                             15 * sizeof(u32),
+                                             &pregs->u_regs[0]);
+               break;
        }
 
        case PTRACE_GETFPREGS: {
@@ -666,26 +371,25 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
                        } fpq[16];
                };
                struct fps __user *fps = (struct fps __user *) addr;
-               int i;
 
-               if (!access_ok(VERIFY_WRITE, fps, sizeof(struct fps))) {
-                       i = -EFAULT;
-                       pt_error_return(regs, -i);
-                       goto out_tsk;
-               }
-               for(i = 0; i < 32; i++)
-                       __put_user(child->thread.float_regs[i], (&fps->regs[i]));
-               __put_user(child->thread.fsr, (&fps->fsr));
-               __put_user(child->thread.fpqdepth, (&fps->fpqd));
-               __put_user(0, (&fps->flags));
-               __put_user(0, (&fps->extra));
-               for(i = 0; i < 16; i++) {
-                       __put_user(child->thread.fpqueue[i].insn_addr,
-                                  (&fps->fpq[i].insnaddr));
-                       __put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
+               ret = copy_regset_to_user(child, view, REGSET_FP,
+                                         0 * sizeof(u32),
+                                         32 * sizeof(u32),
+                                         &fps->regs[0]);
+               if (!ret)
+                       ret = copy_regset_to_user(child, view, REGSET_FP,
+                                                 33 * sizeof(u32),
+                                                 1 * sizeof(u32),
+                                                 &fps->fsr);
+
+               if (!ret) {
+                       if (__put_user(0, &fps->fpqd) ||
+                           __put_user(0, &fps->flags) ||
+                           __put_user(0, &fps->extra) ||
+                           clear_user(fps->fpq, sizeof(fps->fpq)))
+                               ret = -EFAULT;
                }
-               pt_succ_return(regs, 0);
-               goto out_tsk;
+               break;
        }
 
        case PTRACE_SETFPREGS: {
@@ -701,107 +405,49 @@ asmlinkage void do_ptrace(struct pt_regs *regs)
                        } fpq[16];
                };
                struct fps __user *fps = (struct fps __user *) addr;
-               int i;
 
-               if (!access_ok(VERIFY_READ, fps, sizeof(struct fps))) {
-                       i = -EFAULT;
-                       pt_error_return(regs, -i);
-                       goto out_tsk;
-               }
-               copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long)));
-               __get_user(child->thread.fsr, (&fps->fsr));
-               __get_user(child->thread.fpqdepth, (&fps->fpqd));
-               for(i = 0; i < 16; i++) {
-                       __get_user(child->thread.fpqueue[i].insn_addr,
-                                  (&fps->fpq[i].insnaddr));
-                       __get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
-               }
-               pt_succ_return(regs, 0);
-               goto out_tsk;
+               ret = copy_regset_from_user(child, view, REGSET_FP,
+                                           0 * sizeof(u32),
+                                           32 * sizeof(u32),
+                                           &fps->regs[0]);
+               if (!ret)
+                       ret = copy_regset_from_user(child, view, REGSET_FP,
+                                                   33 * sizeof(u32),
+                                                   1 * sizeof(u32),
+                                                   &fps->fsr);
+               break;
        }
 
        case PTRACE_READTEXT:
-       case PTRACE_READDATA: {
-               int res = ptrace_readdata(child, addr,
-                                         (void __user *) addr2, data);
-
-               if (res == data) {
-                       pt_succ_return(regs, 0);
-                       goto out_tsk;
-               }
-               /* Partial read is an IO failure */
-               if (res >= 0)
-                       res = -EIO;
-               pt_error_return(regs, -res);
-               goto out_tsk;
-       }
+       case PTRACE_READDATA:
+               ret = ptrace_readdata(child, addr,
+                                     (void __user *) addr2, data);
+
+               if (ret == data)
+                       ret = 0;
+               else if (ret >= 0)
+                       ret = -EIO;
+               break;
 
        case PTRACE_WRITETEXT:
-       case PTRACE_WRITEDATA: {
-               int res = ptrace_writedata(child, (void __user *) addr2,
-                                          addr, data);
-
-               if (res == data) {
-                       pt_succ_return(regs, 0);
-                       goto out_tsk;
-               }
-               /* Partial write is an IO failure */
-               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;
-               wake_up_process(child);
-               pt_succ_return(regs, 0);
-               goto out_tsk;
-       }
+       case PTRACE_WRITEDATA:
+               ret = ptrace_writedata(child, (void __user *) addr2,
+                                      addr, data);
+
+               if (ret == data)
+                       ret = 0;
+               else if (ret >= 0)
+                       ret = -EIO;
+               break;
 
-/*
- * 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;
-               }
-               wake_up_process(child);
-               child->exit_code = SIGKILL;
-               pt_succ_return(regs, 0);
-               goto out_tsk;
+       default:
+               if (request == PTRACE_SPARC_DETACH)
+                       request = PTRACE_DETACH;
+               ret = ptrace_request(child, request, addr, data);
+               break;
        }
 
-       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();
+       return ret;
 }
 
 asmlinkage void syscall_trace(void)
@@ -810,7 +456,6 @@ asmlinkage void syscall_trace(void)
                return;
        if (!(current->ptrace & PT_PTRACED))
                return;
-       current->thread.flags ^= MAGIC_CONSTANT;
        ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
                                 ? 0x80 : 0));
        /*