fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / arm / kernel / ptrace.c
index d4058ed..b145c7e 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/user.h>
 #include <linux/security.h>
 #include <linux/init.h>
+#include <linux/signal.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #define BREAKINST_THUMB        0xde01
 #endif
 
-/*
- * Get the address of the live pt_regs for the specified task.
- * These are saved onto the top kernel stack when the process
- * is not running.
- *
- * Note: if a user thread is execve'd from kernel space, the
- * kernel stack will not be empty on entry to the kernel, so
- * ptracing these tasks will fail.
- */
-static inline struct pt_regs *
-get_user_regs(struct task_struct *task)
-{
-       return (struct pt_regs *)
-               ((unsigned long)task->thread_info + THREAD_SIZE -
-                                8 - sizeof(struct pt_regs));
-}
-
 /*
  * this routine will get a word off of the processes privileged stack.
  * the offset is how far from the base addr as stored in the THREAD.
@@ -77,7 +61,7 @@ get_user_regs(struct task_struct *task)
  */
 static inline long get_user_reg(struct task_struct *task, int offset)
 {
-       return get_user_regs(task)->uregs[offset];
+       return task_pt_regs(task)->uregs[offset];
 }
 
 /*
@@ -89,7 +73,7 @@ static inline long get_user_reg(struct task_struct *task, int offset)
 static inline int
 put_user_reg(struct task_struct *task, int offset, long data)
 {
-       struct pt_regs newregs, *regs = get_user_regs(task);
+       struct pt_regs newregs, *regs = task_pt_regs(task);
        int ret = -EINVAL;
 
        newregs = *regs;
@@ -240,6 +224,15 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in
                 */
                long aluop1, aluop2, ccbit;
 
+               if ((insn & 0x0fffffd0) == 0x012fff10) {
+                       /*
+                        * bx or blx
+                        */
+                       alt = get_user_reg(child, insn & 15);
+                       break;
+               }
+
+
                if ((insn & 0xf000) != 0xf000)
                        break;
 
@@ -410,7 +403,7 @@ void ptrace_set_bpt(struct task_struct *child)
        u32 insn;
        int res;
 
-       regs = get_user_regs(child);
+       regs = task_pt_regs(child);
        pc = instruction_pointer(regs);
 
        if (thumb_mode(regs)) {
@@ -485,7 +478,7 @@ void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
        info.si_signo = SIGTRAP;
        info.si_errno = 0;
        info.si_code  = TRAP_BRKPT;
-       info.si_addr  = (void *)instruction_pointer(regs);
+       info.si_addr  = (void __user *)instruction_pointer(regs);
 
        force_sig_info(SIGTRAP, &info, tsk);
 }
@@ -561,7 +554,7 @@ static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
  */
 static int ptrace_getregs(struct task_struct *tsk, void __user *uregs)
 {
-       struct pt_regs *regs = get_user_regs(tsk);
+       struct pt_regs *regs = task_pt_regs(tsk);
 
        return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0;
 }
@@ -576,7 +569,7 @@ static int ptrace_setregs(struct task_struct *tsk, void __user *uregs)
 
        ret = -EFAULT;
        if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) {
-               struct pt_regs *regs = get_user_regs(tsk);
+               struct pt_regs *regs = task_pt_regs(tsk);
 
                ret = -EINVAL;
                if (valid_user_regs(&newregs)) {
@@ -593,7 +586,7 @@ static int ptrace_setregs(struct task_struct *tsk, void __user *uregs)
  */
 static int ptrace_getfpregs(struct task_struct *tsk, void __user *ufp)
 {
-       return copy_to_user(ufp, &tsk->thread_info->fpstate,
+       return copy_to_user(ufp, &task_thread_info(tsk)->fpstate,
                            sizeof(struct user_fp)) ? -EFAULT : 0;
 }
 
@@ -602,13 +595,71 @@ static int ptrace_getfpregs(struct task_struct *tsk, void __user *ufp)
  */
 static int ptrace_setfpregs(struct task_struct *tsk, void __user *ufp)
 {
-       struct thread_info *thread = tsk->thread_info;
+       struct thread_info *thread = task_thread_info(tsk);
        thread->used_cp[1] = thread->used_cp[2] = 1;
        return copy_from_user(&thread->fpstate, ufp,
                              sizeof(struct user_fp)) ? -EFAULT : 0;
 }
 
-static int do_ptrace(int request, struct task_struct *child, long addr, long data)
+#ifdef CONFIG_IWMMXT
+
+/*
+ * Get the child iWMMXt state.
+ */
+static int ptrace_getwmmxregs(struct task_struct *tsk, void __user *ufp)
+{
+       struct thread_info *thread = task_thread_info(tsk);
+
+       if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT))
+               return -ENODATA;
+       iwmmxt_task_disable(thread);  /* force it to ram */
+       return copy_to_user(ufp, &thread->fpstate.iwmmxt, IWMMXT_SIZE)
+               ? -EFAULT : 0;
+}
+
+/*
+ * Set the child iWMMXt state.
+ */
+static int ptrace_setwmmxregs(struct task_struct *tsk, void __user *ufp)
+{
+       struct thread_info *thread = task_thread_info(tsk);
+
+       if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT))
+               return -EACCES;
+       iwmmxt_task_release(thread);  /* force a reload */
+       return copy_from_user(&thread->fpstate.iwmmxt, ufp, IWMMXT_SIZE)
+               ? -EFAULT : 0;
+}
+
+#endif
+
+#ifdef CONFIG_CRUNCH
+/*
+ * Get the child Crunch state.
+ */
+static int ptrace_getcrunchregs(struct task_struct *tsk, void __user *ufp)
+{
+       struct thread_info *thread = task_thread_info(tsk);
+
+       crunch_task_disable(thread);  /* force it to ram */
+       return copy_to_user(ufp, &thread->crunchstate, CRUNCH_SIZE)
+               ? -EFAULT : 0;
+}
+
+/*
+ * Set the child Crunch state.
+ */
+static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp)
+{
+       struct thread_info *thread = task_thread_info(tsk);
+
+       crunch_task_release(thread);  /* force a reload */
+       return copy_from_user(&thread->crunchstate, ufp, CRUNCH_SIZE)
+               ? -EFAULT : 0;
+}
+#endif
+
+long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 {
        unsigned long tmp;
        int ret;
@@ -622,7 +673,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
                        ret = access_process_vm(child, addr, &tmp,
                                                sizeof(unsigned long), 0);
                        if (ret == sizeof(unsigned long))
-                               ret = put_user(tmp, (unsigned long *) data);
+                               ret = put_user(tmp, (unsigned long __user *) data);
                        else
                                ret = -EIO;
                        break;
@@ -654,7 +705,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
                case PTRACE_SYSCALL:
                case PTRACE_CONT:
                        ret = -EIO;
-                       if ((unsigned long) data > _NSIG)
+                       if (!valid_signal(data))
                                break;
                        if (request == PTRACE_SYSCALL)
                                set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
@@ -677,7 +728,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
                        /* make sure single-step breakpoint is gone. */
                        child->ptrace &= ~PT_SINGLESTEP;
                        ptrace_cancel_bpt(child);
-                       if (child->state != TASK_ZOMBIE) {
+                       if (child->exit_state != EXIT_ZOMBIE) {
                                child->exit_code = SIGKILL;
                                wake_up_process(child);
                        }
@@ -689,7 +740,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
                 */
                case PTRACE_SINGLESTEP:
                        ret = -EIO;
-                       if ((unsigned long) data > _NSIG)
+                       if (!valid_signal(data))
                                break;
                        child->ptrace |= PT_SINGLESTEP;
                        clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
@@ -719,92 +770,60 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
                        ret = ptrace_setfpregs(child, (void __user *)data);
                        break;
 
-               default:
-                       ret = ptrace_request(child, request, addr, data);
+#ifdef CONFIG_IWMMXT
+               case PTRACE_GETWMMXREGS:
+                       ret = ptrace_getwmmxregs(child, (void __user *)data);
                        break;
-       }
 
-       return ret;
-}
+               case PTRACE_SETWMMXREGS:
+                       ret = ptrace_setwmmxregs(child, (void __user *)data);
+                       break;
+#endif
 
-asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
-{
-       struct task_struct *child;
-       int ret;
+               case PTRACE_GET_THREAD_AREA:
+                       ret = put_user(task_thread_info(child)->tp_value,
+                                      (unsigned long __user *) data);
+                       break;
 
-       lock_kernel();
-       ret = -EPERM;
-       if (request == PTRACE_TRACEME) {
-               /* are we already being traced? */
-               if (current->ptrace & PT_PTRACED)
-                       goto out;
-               ret = security_ptrace(current->parent, current);
-               if (ret)
-                       goto out;
-               /* set the ptrace bit in the process flags. */
-               current->ptrace |= PT_PTRACED;
-               ret = 0;
-               goto out;
-       }
-       ret = -ESRCH;
-       read_lock(&tasklist_lock);
-       child = find_task_by_pid(pid);
-       if (child)
-               get_task_struct(child);
-       read_unlock(&tasklist_lock);
-       if (!child)
-               goto out;
-
-       ret = -EPERM;
-       if (pid == 1)           /* you may not mess with init */
-               goto out_tsk;
-
-       if (request == PTRACE_ATTACH) {
-               ret = ptrace_attach(child);
-               goto out_tsk;
+               case PTRACE_SET_SYSCALL:
+                       ret = 0;
+                       child->ptrace_message = data;
+                       break;
+
+#ifdef CONFIG_CRUNCH
+               case PTRACE_GETCRUNCHREGS:
+                       ret = ptrace_getcrunchregs(child, (void __user *)data);
+                       break;
+
+               case PTRACE_SETCRUNCHREGS:
+                       ret = ptrace_setcrunchregs(child, (void __user *)data);
+                       break;
+#endif
+
+               default:
+                       ret = ptrace_request(child, request, addr, data);
+                       break;
        }
-       ret = ptrace_check_attach(child, request == PTRACE_KILL);
-       if (ret == 0)
-               ret = do_ptrace(request, child, addr, data);
-
-out_tsk:
-       put_task_struct(child);
-out:
-       unlock_kernel();
+
        return ret;
 }
 
-asmlinkage void syscall_trace(int why, struct pt_regs *regs)
+asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
 {
        unsigned long ip;
 
-       if (!test_thread_flag(TIF_SYSCALL_TRACE))
-               return;
-       if (!(current->ptrace & PT_PTRACED))
-               return;
+       if (test_thread_flag(TIF_SYSCALL_TRACE)) {
+               /*
+                * Save IP.  IP is used to denote syscall entry/exit:
+                *  IP = 0 -> entry, = 1 -> exit
+                */
+               ip = regs->ARM_ip;
+               regs->ARM_ip = why;
 
-       /*
-        * Save IP.  IP is used to denote syscall entry/exit:
-        *  IP = 0 -> entry, = 1 -> exit
-        */
-       ip = regs->ARM_ip;
-       regs->ARM_ip = why;
-
-       /* the 0x80 provides a way for the tracing parent to distinguish
-          between a syscall stop and SIGTRAP delivery */
-       current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
-                                       ? 0x80 : 0);
-       current->state = TASK_STOPPED;
-       notify_parent(current, SIGCHLD);
-       schedule();
-       /*
-        * 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;
+               tracehook_report_syscall(regs, why);
+
+               regs->ARM_ip = ip;
        }
-       regs->ARM_ip = ip;
+
+       return scno;
 }