Merge branch 'misc2' into devel
[safe/jmp/linux-2.6] / arch / arm / kernel / ptrace.c
index 7b6256b..08f899f 100644 (file)
@@ -9,19 +9,17 @@
  * 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>
 #include <linux/init.h>
 #include <linux/signal.h>
+#include <linux/uaccess.h>
 
-#include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/system.h>
 #include <asm/traps.h>
@@ -128,7 +126,7 @@ ptrace_getrn(struct task_struct *child, unsigned long insn)
 
        val = get_user_reg(child, reg);
        if (reg == 15)
-               val = pc_pointer(val + 8);
+               val += 8;
 
        return val;
 }
@@ -280,8 +278,7 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in
                                else
                                        base -= aluop2;
                        }
-                       if (read_u32(child, base, &alt) == 0)
-                               alt = pc_pointer(alt);
+                       read_u32(child, base, &alt);
                }
                break;
 
@@ -307,8 +304,7 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in
 
                        base = ptrace_getrn(child, insn);
 
-                       if (read_u32(child, base + nr_regs, &alt) == 0)
-                               alt = pc_pointer(alt);
+                       read_u32(child, base + nr_regs, &alt);
                        break;
                }
                break;
@@ -384,16 +380,16 @@ static void clear_breakpoint(struct task_struct *task, struct debug_entry *bp)
 
                if (ret != 2 || old_insn.thumb != BREAKINST_THUMB)
                        printk(KERN_ERR "%s:%d: corrupted Thumb breakpoint at "
-                               "0x%08lx (0x%04x)\n", task->comm, task->pid,
-                               addr, old_insn.thumb);
+                               "0x%08lx (0x%04x)\n", task->comm,
+                               task_pid_nr(task), addr, old_insn.thumb);
        } else {
                ret = swap_insn(task, addr & ~3, &old_insn.arm,
                                &bp->insn.arm, 4);
 
                if (ret != 4 || old_insn.arm != BREAKINST_ARM)
                        printk(KERN_ERR "%s:%d: corrupted ARM breakpoint at "
-                               "0x%08lx (0x%08x)\n", task->comm, task->pid,
-                               addr, old_insn.arm);
+                               "0x%08lx (0x%08x)\n", task->comm,
+                               task_pid_nr(task), addr, old_insn.arm);
        }
 }
 
@@ -458,13 +454,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);
 }
 
 /*
@@ -506,10 +499,41 @@ static struct undef_hook thumb_break_hook = {
        .fn             = break_trap,
 };
 
+static int thumb2_break_trap(struct pt_regs *regs, unsigned int instr)
+{
+       unsigned int instr2;
+       void __user *pc;
+
+       /* Check the second half of the instruction.  */
+       pc = (void __user *)(instruction_pointer(regs) + 2);
+
+       if (processor_mode(regs) == SVC_MODE) {
+               instr2 = *(u16 *) pc;
+       } else {
+               get_user(instr2, (u16 __user *)pc);
+       }
+
+       if (instr2 == 0xa000) {
+               ptrace_break(current, regs);
+               return 0;
+       } else {
+               return 1;
+       }
+}
+
+static struct undef_hook thumb2_break_hook = {
+       .instr_mask     = 0xffff,
+       .instr_val      = 0xf7f0,
+       .cpsr_mask      = PSR_T_BIT,
+       .cpsr_val       = PSR_T_BIT,
+       .fn             = thumb2_break_trap,
+};
+
 static int __init ptrace_break_init(void)
 {
        register_undef_hook(&arm_break_hook);
        register_undef_hook(&thumb_break_hook);
+       register_undef_hook(&thumb2_break_hook);
        return 0;
 }
 
@@ -528,7 +552,13 @@ static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
                return -EIO;
 
        tmp = 0;
-       if (off < sizeof(struct pt_regs))
+       if (off == PT_TEXT_ADDR)
+               tmp = tsk->mm->start_code;
+       else if (off == PT_DATA_ADDR)
+               tmp = tsk->mm->start_data;
+       else if (off == PT_TEXT_END_ADDR)
+               tmp = tsk->mm->end_code;
+       else if (off < sizeof(struct pt_regs))
                tmp = get_user_reg(tsk, off >> 2);
 
        return put_user(tmp, ret);
@@ -610,15 +640,12 @@ 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 = task_thread_info(tsk);
-       void *ptr = &thread->fpstate;
 
        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;
 }
 
 /*
@@ -627,55 +654,101 @@ 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 = task_thread_info(tsk);
-       void *ptr = &thread->fpstate;
 
        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
+
+#ifdef CONFIG_VFP
+/*
+ * Get the child VFP state.
+ */
+static int ptrace_getvfpregs(struct task_struct *tsk, void __user *data)
+{
+       struct thread_info *thread = task_thread_info(tsk);
+       union vfp_state *vfp = &thread->vfpstate;
+       struct user_vfp __user *ufp = data;
+
+       vfp_sync_hwstate(thread);
+
+       /* copy the floating point registers */
+       if (copy_to_user(&ufp->fpregs, &vfp->hard.fpregs,
+                        sizeof(vfp->hard.fpregs)))
+               return -EFAULT;
+
+       /* copy the status and control register */
+       if (put_user(vfp->hard.fpscr, &ufp->fpscr))
+               return -EFAULT;
+
+       return 0;
+}
+
+/*
+ * Set the child VFP state.
+ */
+static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data)
+{
+       struct thread_info *thread = task_thread_info(tsk);
+       union vfp_state *vfp = &thread->vfpstate;
+       struct user_vfp __user *ufp = data;
+
+       vfp_sync_hwstate(thread);
+
+       /* copy the floating point registers */
+       if (copy_from_user(&vfp->hard.fpregs, &ufp->fpregs,
+                          sizeof(vfp->hard.fpregs)))
+               return -EFAULT;
+
+       /* copy the status and control register */
+       if (get_user(vfp->hard.fpscr, &ufp->fpscr))
+               return -EFAULT;
+
+       vfp_flush_hwstate(thread);
+
+       return 0;
+}
 #endif
 
 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 {
-       unsigned long tmp;
        int ret;
 
        switch (request) {
-               /*
-                * read word at location "addr" in the child process.
-                */
-               case PTRACE_PEEKTEXT:
-               case PTRACE_PEEKDATA:
-                       ret = access_process_vm(child, addr, &tmp,
-                                               sizeof(unsigned long), 0);
-                       if (ret == sizeof(unsigned long))
-                               ret = put_user(tmp, (unsigned long __user *) data);
-                       else
-                               ret = -EIO;
-                       break;
-
                case PTRACE_PEEKUSR:
                        ret = ptrace_read_user(child, addr, (unsigned long __user *)data);
                        break;
 
-               /*
-                * write the word at location addr.
-                */
-               case PTRACE_POKETEXT:
-               case PTRACE_POKEDATA:
-                       ret = access_process_vm(child, addr, &data,
-                                               sizeof(unsigned long), 1);
-                       if (ret == sizeof(unsigned long))
-                               ret = 0;
-                       else
-                               ret = -EIO;
-                       break;
-
                case PTRACE_POKEUSR:
                        ret = ptrace_write_user(child, addr, data);
                        break;
@@ -693,9 +766,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;
@@ -706,9 +777,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);
@@ -723,7 +792,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. */
@@ -731,10 +800,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                        ret = 0;
                        break;
 
-               case PTRACE_DETACH:
-                       ret = ptrace_detach(child, data);
-                       break;
-
                case PTRACE_GETREGS:
                        ret = ptrace_getregs(child, (void __user *)data);
                        break;
@@ -767,10 +832,30 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                        break;
 
                case PTRACE_SET_SYSCALL:
+                       task_thread_info(child)->syscall = data;
                        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
+
+#ifdef CONFIG_VFP
+               case PTRACE_GETVFPREGS:
+                       ret = ptrace_getvfpregs(child, (void __user *)data);
+                       break;
+
+               case PTRACE_SETVFPREGS:
+                       ret = ptrace_setvfpregs(child, (void __user *)data);
+                       break;
+#endif
+
                default:
                        ret = ptrace_request(child, request, addr, data);
                        break;
@@ -795,7 +880,7 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
        ip = regs->ARM_ip;
        regs->ARM_ip = why;
 
-       current->ptrace_message = scno;
+       current_thread_info()->syscall = scno;
 
        /* the 0x80 provides a way for the tracing parent to distinguish
           between a syscall stop and SIGTRAP delivery */
@@ -812,5 +897,5 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
        }
        regs->ARM_ip = ip;
 
-       return current->ptrace_message;
+       return current_thread_info()->syscall;
 }