Pull thermal into release branch
[linux-drm-fsl-dcu.git] / arch / arm / kernel / ptrace.c
index 2b84f78d7b0f78585698e54bf137f3cde7634205..6f2f46c2e406ee844632da068353b0167735886e 100644 (file)
@@ -9,12 +9,10 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
-#include <linux/smp_lock.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
 #include <linux/security.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.
@@ -79,7 +60,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];
 }
 
 /*
@@ -91,7 +72,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;
@@ -421,7 +402,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)) {
@@ -475,13 +456,10 @@ void ptrace_cancel_bpt(struct task_struct *child)
 
 /*
  * Called by kernel/ptrace.c when detaching..
- *
- * Make sure the single step bit is not set.
  */
 void ptrace_disable(struct task_struct *child)
 {
-       child->ptrace &= ~PT_SINGLESTEP;
-       ptrace_cancel_bpt(child);
+       single_step_disable(child);
 }
 
 /*
@@ -572,7 +550,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;
 }
@@ -587,7 +565,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)) {
@@ -604,7 +582,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;
 }
 
@@ -613,7 +591,7 @@ 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;
@@ -626,16 +604,13 @@ static int ptrace_setfpregs(struct task_struct *tsk, void __user *ufp)
  */
 static int ptrace_getwmmxregs(struct task_struct *tsk, void __user *ufp)
 {
-       struct thread_info *thread = tsk->thread_info;
-       void *ptr = &thread->fpstate;
+       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 */
-       /* The iWMMXt state is stored doubleword-aligned.  */
-       if (((long) ptr) & 4)
-               ptr += 4;
-       return copy_to_user(ufp, ptr, 0x98) ? -EFAULT : 0;
+       return copy_to_user(ufp, &thread->fpstate.iwmmxt, IWMMXT_SIZE)
+               ? -EFAULT : 0;
 }
 
 /*
@@ -643,18 +618,41 @@ static int ptrace_getwmmxregs(struct task_struct *tsk, void __user *ufp)
  */
 static int ptrace_setwmmxregs(struct task_struct *tsk, void __user *ufp)
 {
-       struct thread_info *thread = tsk->thread_info;
-       void *ptr = &thread->fpstate;
+       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 */
-       /* The iWMMXt state is stored doubleword-aligned.  */
-       if (((long) ptr) & 4)
-               ptr += 4;
-       return copy_from_user(ptr, ufp, 0x98) ? -EFAULT : 0;
+       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)
@@ -710,9 +708,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                        else
                                clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
                        child->exit_code = data;
-                       /* make sure single-step breakpoint is gone. */
-                       child->ptrace &= ~PT_SINGLESTEP;
-                       ptrace_cancel_bpt(child);
+                       single_step_disable(child);
                        wake_up_process(child);
                        ret = 0;
                        break;
@@ -723,9 +719,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                 * exit.
                 */
                case PTRACE_KILL:
-                       /* make sure single-step breakpoint is gone. */
-                       child->ptrace &= ~PT_SINGLESTEP;
-                       ptrace_cancel_bpt(child);
+                       single_step_disable(child);
                        if (child->exit_state != EXIT_ZOMBIE) {
                                child->exit_code = SIGKILL;
                                wake_up_process(child);
@@ -740,7 +734,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                        ret = -EIO;
                        if (!valid_signal(data))
                                break;
-                       child->ptrace |= PT_SINGLESTEP;
+                       single_step_enable(child);
                        clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
                        child->exit_code = data;
                        /* give it a chance to run. */
@@ -779,10 +773,25 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 #endif
 
                case PTRACE_GET_THREAD_AREA:
-                       ret = put_user(child->thread_info->tp_value,
+                       ret = put_user(task_thread_info(child)->tp_value,
                                       (unsigned long __user *) data);
                        break;
 
+               case PTRACE_SET_SYSCALL:
+                       task_thread_info(child)->syscall = data;
+                       ret = 0;
+                       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;
@@ -791,14 +800,14 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
        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;
+               return scno;
        if (!(current->ptrace & PT_PTRACED))
-               return;
+               return scno;
 
        /*
         * Save IP.  IP is used to denote syscall entry/exit:
@@ -807,6 +816,8 @@ asmlinkage void syscall_trace(int why, struct pt_regs *regs)
        ip = regs->ARM_ip;
        regs->ARM_ip = why;
 
+       current_thread_info()->syscall = scno;
+
        /* the 0x80 provides a way for the tracing parent to distinguish
           between a syscall stop and SIGTRAP delivery */
        ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
@@ -821,4 +832,6 @@ asmlinkage void syscall_trace(int why, struct pt_regs *regs)
                current->exit_code = 0;
        }
        regs->ARM_ip = ip;
+
+       return current_thread_info()->syscall;
 }