]> err.no Git - linux-2.6/blobdiff - arch/ia64/kernel/ptrace.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6.25
[linux-2.6] / arch / ia64 / kernel / ptrace.c
index 00f80324694829f5eef2e647299a8227213689f1..ab784ec4319dbd2eea568a5a6cbc566e829eb358 100644 (file)
@@ -547,6 +547,129 @@ ia64_sync_user_rbs (struct task_struct *child, struct switch_stack *sw,
        return 0;
 }
 
+static long
+ia64_sync_kernel_rbs (struct task_struct *child, struct switch_stack *sw,
+               unsigned long user_rbs_start, unsigned long user_rbs_end)
+{
+       unsigned long addr, val;
+       long ret;
+
+       /* now copy word for word from user rbs to kernel rbs: */
+       for (addr = user_rbs_start; addr < user_rbs_end; addr += 8) {
+               if (access_process_vm(child, addr, &val, sizeof(val), 0)
+                               != sizeof(val))
+                       return -EIO;
+
+               ret = ia64_poke(child, sw, user_rbs_end, addr, val);
+               if (ret < 0)
+                       return ret;
+       }
+       return 0;
+}
+
+typedef long (*syncfunc_t)(struct task_struct *, struct switch_stack *,
+                           unsigned long, unsigned long);
+
+static void do_sync_rbs(struct unw_frame_info *info, void *arg)
+{
+       struct pt_regs *pt;
+       unsigned long urbs_end;
+       syncfunc_t fn = arg;
+
+       if (unw_unwind_to_user(info) < 0)
+               return;
+       pt = task_pt_regs(info->task);
+       urbs_end = ia64_get_user_rbs_end(info->task, pt, NULL);
+
+       fn(info->task, info->sw, pt->ar_bspstore, urbs_end);
+}
+
+/*
+ * when a thread is stopped (ptraced), debugger might change thread's user
+ * stack (change memory directly), and we must avoid the RSE stored in kernel
+ * to override user stack (user space's RSE is newer than kernel's in the
+ * case). To workaround the issue, we copy kernel RSE to user RSE before the
+ * task is stopped, so user RSE has updated data.  we then copy user RSE to
+ * kernel after the task is resummed from traced stop and kernel will use the
+ * newer RSE to return to user. TIF_RESTORE_RSE is the flag to indicate we need
+ * synchronize user RSE to kernel.
+ */
+void ia64_ptrace_stop(void)
+{
+       if (test_and_set_tsk_thread_flag(current, TIF_RESTORE_RSE))
+               return;
+       tsk_set_notify_resume(current);
+       unw_init_running(do_sync_rbs, ia64_sync_user_rbs);
+}
+
+/*
+ * This is called to read back the register backing store.
+ */
+void ia64_sync_krbs(void)
+{
+       clear_tsk_thread_flag(current, TIF_RESTORE_RSE);
+       tsk_clear_notify_resume(current);
+
+       unw_init_running(do_sync_rbs, ia64_sync_kernel_rbs);
+}
+
+/*
+ * After PTRACE_ATTACH, a thread's register backing store area in user
+ * space is assumed to contain correct data whenever the thread is
+ * stopped.  arch_ptrace_stop takes care of this on tracing stops.
+ * But if the child was already stopped for job control when we attach
+ * to it, then it might not ever get into ptrace_stop by the time we
+ * want to examine the user memory containing the RBS.
+ */
+void
+ptrace_attach_sync_user_rbs (struct task_struct *child)
+{
+       int stopped = 0;
+       struct unw_frame_info info;
+
+       /*
+        * If the child is in TASK_STOPPED, we need to change that to
+        * TASK_TRACED momentarily while we operate on it.  This ensures
+        * that the child won't be woken up and return to user mode while
+        * we are doing the sync.  (It can only be woken up for SIGKILL.)
+        */
+
+       read_lock(&tasklist_lock);
+       if (child->signal) {
+               spin_lock_irq(&child->sighand->siglock);
+               if (child->state == TASK_STOPPED &&
+                   !test_and_set_tsk_thread_flag(child, TIF_RESTORE_RSE)) {
+                       tsk_set_notify_resume(child);
+
+                       child->state = TASK_TRACED;
+                       stopped = 1;
+               }
+               spin_unlock_irq(&child->sighand->siglock);
+       }
+       read_unlock(&tasklist_lock);
+
+       if (!stopped)
+               return;
+
+       unw_init_from_blocked_task(&info, child);
+       do_sync_rbs(&info, ia64_sync_user_rbs);
+
+       /*
+        * Now move the child back into TASK_STOPPED if it should be in a
+        * job control stop, so that SIGCONT can be used to wake it up.
+        */
+       read_lock(&tasklist_lock);
+       if (child->signal) {
+               spin_lock_irq(&child->sighand->siglock);
+               if (child->state == TASK_TRACED &&
+                   (child->signal->flags & SIGNAL_STOP_STOPPED)) {
+                       child->state = TASK_STOPPED;
+               }
+               spin_unlock_irq(&child->sighand->siglock);
+       }
+       read_unlock(&tasklist_lock);
+}
+
 static inline int
 thread_matches (struct task_struct *thread, unsigned long addr)
 {
@@ -574,52 +697,6 @@ thread_matches (struct task_struct *thread, unsigned long addr)
        return 1;       /* looks like we've got a winner */
 }
 
-/*
- * GDB apparently wants to be able to read the register-backing store
- * of any thread when attached to a given process.  If we are peeking
- * or poking an address that happens to reside in the kernel-backing
- * store of another thread, we need to attach to that thread, because
- * otherwise we end up accessing stale data.
- *
- * task_list_lock must be read-locked before calling this routine!
- */
-static struct task_struct *
-find_thread_for_addr (struct task_struct *child, unsigned long addr)
-{
-       struct task_struct *p;
-       struct mm_struct *mm;
-       struct list_head *this, *next;
-       int mm_users;
-
-       if (!(mm = get_task_mm(child)))
-               return child;
-
-       /* -1 because of our get_task_mm(): */
-       mm_users = atomic_read(&mm->mm_users) - 1;
-       if (mm_users <= 1)
-               goto out;               /* not multi-threaded */
-
-       /*
-        * Traverse the current process' children list.  Every task that
-        * one attaches to becomes a child.  And it is only attached children
-        * of the debugger that are of interest (ptrace_check_attach checks
-        * for this).
-        */
-       list_for_each_safe(this, next, &current->children) {
-               p = list_entry(this, struct task_struct, sibling);
-               if (p->tgid != child->tgid)
-                       continue;
-               if (thread_matches(p, addr)) {
-                       child = p;
-                       goto out;
-               }
-       }
-
-  out:
-       mmput(mm);
-       return child;
-}
-
 /*
  * Write f32-f127 back to task->thread.fph if it has been modified.
  */
@@ -703,14 +780,14 @@ convert_to_non_syscall (struct task_struct *child, struct pt_regs  *pt,
                if ((long)((unsigned long)child + IA64_STK_OFFSET - sp)
                    < IA64_PT_REGS_SIZE) {
                        dprintk("ptrace.%s: ran off the top of the kernel "
-                               "stack\n", __FUNCTION__);
+                               "stack\n", __func__);
                        return;
                }
                if (unw_get_pr (&prev_info, &pr) < 0) {
                        unw_get_rp(&prev_info, &ip);
                        dprintk("ptrace.%s: failed to read "
                                "predicate register (ip=0x%lx)\n",
-                               __FUNCTION__, ip);
+                               __func__, ip);
                        return;
                }
                if (unw_is_intr_frame(&info)
@@ -785,7 +862,7 @@ static int
 access_uarea (struct task_struct *child, unsigned long addr,
              unsigned long *data, int write_access)
 {
-       unsigned long *ptr, regnum, urbs_end, rnat_addr, cfm;
+       unsigned long *ptr, regnum, urbs_end, cfm;
        struct switch_stack *sw;
        struct pt_regs *pt;
 #      define pt_reg_addr(pt, reg)     ((void *)                           \
@@ -888,14 +965,9 @@ access_uarea (struct task_struct *child, unsigned long addr,
                         * the kernel was entered.
                         *
                         * Furthermore, when changing the contents of
-                        * PT_AR_BSP (or PT_CFM) we MUST copy any
-                        * users-level stacked registers that are
-                        * stored on the kernel stack back to
-                        * user-space because otherwise, we might end
-                        * up clobbering kernel stacked registers.
-                        * Also, if this happens while the task is
-                        * blocked in a system call, which convert the
-                        * state such that the non-system-call exit
+                        * PT_AR_BSP (or PT_CFM) while the task is
+                        * blocked in a system call, convert the state
+                        * so that the non-system-call exit
                         * path is used.  This ensures that the proper
                         * state will be picked up when resuming
                         * execution.  However, it *also* means that
@@ -912,10 +984,6 @@ access_uarea (struct task_struct *child, unsigned long addr,
                        urbs_end = ia64_get_user_rbs_end(child, pt, &cfm);
                        if (write_access) {
                                if (*data != urbs_end) {
-                                       if (ia64_sync_user_rbs(child, sw,
-                                                              pt->ar_bspstore,
-                                                              urbs_end) < 0)
-                                               return -1;
                                        if (in_syscall(pt))
                                                convert_to_non_syscall(child,
                                                                       pt,
@@ -935,10 +1003,6 @@ access_uarea (struct task_struct *child, unsigned long addr,
                        urbs_end = ia64_get_user_rbs_end(child, pt, &cfm);
                        if (write_access) {
                                if (((cfm ^ *data) & PFM_MASK) != 0) {
-                                       if (ia64_sync_user_rbs(child, sw,
-                                                              pt->ar_bspstore,
-                                                              urbs_end) < 0)
-                                               return -1;
                                        if (in_syscall(pt))
                                                convert_to_non_syscall(child,
                                                                       pt,
@@ -951,10 +1015,14 @@ access_uarea (struct task_struct *child, unsigned long addr,
                        return 0;
 
                      case PT_CR_IPSR:
-                       if (write_access)
-                               pt->cr_ipsr = ((*data & IPSR_MASK)
+                       if (write_access) {
+                               unsigned long tmp = *data;
+                               /* psr.ri==3 is a reserved value: SDM 2:25 */
+                               if ((tmp & IA64_PSR_RI) == IA64_PSR_RI)
+                                       tmp &= ~IA64_PSR_RI;
+                               pt->cr_ipsr = ((tmp & IPSR_MASK)
                                               | (pt->cr_ipsr & ~IPSR_MASK));
-                       else
+                       else
                                *data = (pt->cr_ipsr & IPSR_MASK);
                        return 0;
 
@@ -966,16 +1034,8 @@ access_uarea (struct task_struct *child, unsigned long addr,
                        return 0;
 
                      case PT_AR_RNAT:
-                       urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
-                       rnat_addr = (long) ia64_rse_rnat_addr((long *)
-                                                             urbs_end);
-                       if (write_access)
-                               return ia64_poke(child, sw, urbs_end,
-                                                rnat_addr, *data);
-                       else
-                               return ia64_peek(child, sw, urbs_end,
-                                                rnat_addr, data);
-
+                       ptr = pt_reg_addr(pt, ar_rnat);
+                       break;
                      case PT_R1:
                        ptr = pt_reg_addr(pt, r1);
                        break;
@@ -1394,208 +1454,97 @@ ptrace_setregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
        return ret;
 }
 
-/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure the single step bit is not set.
- */
 void
-ptrace_disable (struct task_struct *child)
+user_enable_single_step (struct task_struct *child)
 {
        struct ia64_psr *child_psr = ia64_psr(task_pt_regs(child));
 
-       /* make sure the single step/taken-branch trap bits are not set: */
-       clear_tsk_thread_flag(child, TIF_SINGLESTEP);
-       child_psr->ss = 0;
-       child_psr->tb = 0;
+       set_tsk_thread_flag(child, TIF_SINGLESTEP);
+       child_psr->ss = 1;
 }
 
-asmlinkage long
-sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data)
+void
+user_enable_block_step (struct task_struct *child)
 {
-       struct pt_regs *pt;
-       unsigned long urbs_end, peek_or_poke;
-       struct task_struct *child;
-       struct switch_stack *sw;
-       long ret;
+       struct ia64_psr *child_psr = ia64_psr(task_pt_regs(child));
 
-       lock_kernel();
-       ret = -EPERM;
-       if (request == PTRACE_TRACEME) {
-               ret = ptrace_traceme();
-               goto out;
-       }
+       set_tsk_thread_flag(child, TIF_SINGLESTEP);
+       child_psr->tb = 1;
+}
 
-       peek_or_poke = (request == PTRACE_PEEKTEXT
-                       || request == PTRACE_PEEKDATA
-                       || request == PTRACE_POKETEXT
-                       || request == PTRACE_POKEDATA);
-       ret = -ESRCH;
-       read_lock(&tasklist_lock);
-       {
-               child = find_task_by_pid(pid);
-               if (child) {
-                       if (peek_or_poke)
-                               child = find_thread_for_addr(child, addr);
-                       get_task_struct(child);
-               }
-       }
-       read_unlock(&tasklist_lock);
-       if (!child)
-               goto out;
-       ret = -EPERM;
-       if (pid == 1)           /* no messing around with init! */
-               goto out_tsk;
-
-       if (request == PTRACE_ATTACH) {
-               ret = ptrace_attach(child);
-               goto out_tsk;
-       }
+void
+user_disable_single_step (struct task_struct *child)
+{
+       struct ia64_psr *child_psr = ia64_psr(task_pt_regs(child));
 
-       ret = ptrace_check_attach(child, request == PTRACE_KILL);
-       if (ret < 0)
-               goto out_tsk;
+       /* make sure the single step/taken-branch trap bits are not set: */
+       clear_tsk_thread_flag(child, TIF_SINGLESTEP);
+       child_psr->ss = 0;
+       child_psr->tb = 0;
+}
 
-       pt = task_pt_regs(child);
-       sw = (struct switch_stack *) (child->thread.ksp + 16);
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure the single step bit is not set.
+ */
+void
+ptrace_disable (struct task_struct *child)
+{
+       user_disable_single_step(child);
+}
 
+long
+arch_ptrace (struct task_struct *child, long request, long addr, long data)
+{
        switch (request) {
-             case PTRACE_PEEKTEXT:
-             case PTRACE_PEEKDATA:
+       case PTRACE_PEEKTEXT:
+       case PTRACE_PEEKDATA:
                /* read word at location addr */
-               urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
-               ret = ia64_peek(child, sw, urbs_end, addr, &data);
-               if (ret == 0) {
-                       ret = data;
-                       /* ensure "ret" is not mistaken as an error code: */
-                       force_successful_syscall_return();
-               }
-               goto out_tsk;
+               if (access_process_vm(child, addr, &data, sizeof(data), 0)
+                   != sizeof(data))
+                       return -EIO;
+               /* ensure return value is not mistaken for error code */
+               force_successful_syscall_return();
+               return data;
 
-             case PTRACE_POKETEXT:
-             case PTRACE_POKEDATA:
-               /* write the word at location addr */
-               urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
-               ret = ia64_poke(child, sw, urbs_end, addr, data);
-               goto out_tsk;
+       /* PTRACE_POKETEXT and PTRACE_POKEDATA is handled
+        * by the generic ptrace_request().
+        */
 
-             case PTRACE_PEEKUSR:
+       case PTRACE_PEEKUSR:
                /* read the word at addr in the USER area */
-               if (access_uarea(child, addr, &data, 0) < 0) {
-                       ret = -EIO;
-                       goto out_tsk;
-               }
-               ret = data;
-               /* ensure "ret" is not mistaken as an error code */
+               if (access_uarea(child, addr, &data, 0) < 0)
+                       return -EIO;
+               /* ensure return value is not mistaken for error code */
                force_successful_syscall_return();
-               goto out_tsk;
+               return data;
 
-             case PTRACE_POKEUSR:
+       case PTRACE_POKEUSR:
                /* write the word at addr in the USER area */
-               if (access_uarea(child, addr, &data, 1) < 0) {
-                       ret = -EIO;
-                       goto out_tsk;
-               }
-               ret = 0;
-               goto out_tsk;
+               if (access_uarea(child, addr, &data, 1) < 0)
+                       return -EIO;
+               return 0;
 
-             case PTRACE_OLD_GETSIGINFO:
+       case PTRACE_OLD_GETSIGINFO:
                /* for backwards-compatibility */
-               ret = ptrace_request(child, PTRACE_GETSIGINFO, addr, data);
-               goto out_tsk;
+               return ptrace_request(child, PTRACE_GETSIGINFO, addr, data);
 
-             case PTRACE_OLD_SETSIGINFO:
+       case PTRACE_OLD_SETSIGINFO:
                /* for backwards-compatibility */
-               ret = ptrace_request(child, PTRACE_SETSIGINFO, addr, data);
-               goto out_tsk;
-
-             case PTRACE_SYSCALL:
-               /* continue and stop at next (return from) syscall */
-             case PTRACE_CONT:
-               /* restart after signal. */
-               ret = -EIO;
-               if (!valid_signal(data))
-                       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;
+               return ptrace_request(child, PTRACE_SETSIGINFO, addr, data);
 
-               /*
-                * Make sure the single step/taken-branch trap bits
-                * are not set:
-                */
-               clear_tsk_thread_flag(child, TIF_SINGLESTEP);
-               ia64_psr(pt)->ss = 0;
-               ia64_psr(pt)->tb = 0;
+       case PTRACE_GETREGS:
+               return ptrace_getregs(child,
+                                     (struct pt_all_user_regs __user *) data);
 
-               wake_up_process(child);
-               ret = 0;
-               goto out_tsk;
+       case PTRACE_SETREGS:
+               return ptrace_setregs(child,
+                                     (struct pt_all_user_regs __user *) data);
 
-             case PTRACE_KILL:
-               /*
-                * 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.
-                */
-               if (child->exit_state == EXIT_ZOMBIE)
-                       /* already dead */
-                       goto out_tsk;
-               child->exit_code = SIGKILL;
-
-               ptrace_disable(child);
-               wake_up_process(child);
-               ret = 0;
-               goto out_tsk;
-
-             case PTRACE_SINGLESTEP:
-               /* let child execute for one instruction */
-             case PTRACE_SINGLEBLOCK:
-               ret = -EIO;
-               if (!valid_signal(data))
-                       goto out_tsk;
-
-               clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-               set_tsk_thread_flag(child, TIF_SINGLESTEP);
-               if (request == PTRACE_SINGLESTEP) {
-                       ia64_psr(pt)->ss = 1;
-               } else {
-                       ia64_psr(pt)->tb = 1;
-               }
-               child->exit_code = data;
-
-               /* give it a chance to run. */
-               wake_up_process(child);
-               ret = 0;
-               goto out_tsk;
-
-             case PTRACE_DETACH:
-               /* detach a process that was attached. */
-               clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-               ret = ptrace_detach(child, data);
-               goto out_tsk;
-
-             case PTRACE_GETREGS:
-               ret = ptrace_getregs(child,
-                                    (struct pt_all_user_regs __user *) data);
-               goto out_tsk;
-
-             case PTRACE_SETREGS:
-               ret = ptrace_setregs(child,
-                                    (struct pt_all_user_regs __user *) data);
-               goto out_tsk;
-
-             default:
-               ret = ptrace_request(child, request, addr, data);
-               goto out_tsk;
+       default:
+               return ptrace_request(child, request, addr, data);
        }
-  out_tsk:
-       put_task_struct(child);
-  out:
-       unlock_kernel();
-       return ret;
 }
 
 
@@ -1631,6 +1580,10 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3,
            && (current->ptrace & PT_PTRACED))
                syscall_trace();
 
+       /* copy user rbs to kernel rbs */
+       if (test_thread_flag(TIF_RESTORE_RSE))
+               ia64_sync_krbs();
+
        if (unlikely(current->audit_context)) {
                long syscall;
                int arch;
@@ -1668,4 +1621,8 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3,
            || test_thread_flag(TIF_SINGLESTEP))
            && (current->ptrace & PT_PTRACED))
                syscall_trace();
+
+       /* copy user rbs to kernel rbs */
+       if (test_thread_flag(TIF_RESTORE_RSE))
+               ia64_sync_krbs();
 }