]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - arch/powerpc/kernel/ptrace.c
powerpc/hw-breakpoint: Use generic hw-breakpoint interfaces for new PPC ptrace flags
[karo-tx-linux.git] / arch / powerpc / kernel / ptrace.c
index 8d8e028893be6e89346d89e984ae76b0a4cfb583..8643ac89023523f760f7aa27c6df10c9d62757a1 100644 (file)
@@ -1336,6 +1336,12 @@ static int set_dac_range(struct task_struct *child,
 static long ppc_set_hwdebug(struct task_struct *child,
                     struct ppc_hw_breakpoint *bp_info)
 {
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+       int len = 0;
+       struct thread_struct *thread = &(child->thread);
+       struct perf_event *bp;
+       struct perf_event_attr attr;
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 #ifndef CONFIG_PPC_ADV_DEBUG_REGS
        unsigned long dabr;
 #endif
@@ -1379,13 +1385,9 @@ static long ppc_set_hwdebug(struct task_struct *child,
         */
        if ((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0 ||
            (bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0 ||
-           bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT ||
            bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)
                return -EINVAL;
 
-       if (child->thread.dabr)
-               return -ENOSPC;
-
        if ((unsigned long)bp_info->addr >= TASK_SIZE)
                return -EIO;
 
@@ -1395,15 +1397,63 @@ static long ppc_set_hwdebug(struct task_struct *child,
                dabr |= DABR_DATA_READ;
        if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
                dabr |= DABR_DATA_WRITE;
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+       if (ptrace_get_breakpoints(child) < 0)
+               return -ESRCH;
 
-       child->thread.dabr = dabr;
+       /*
+        * Check if the request is for 'range' breakpoints. We can
+        * support it if range < 8 bytes.
+        */
+       if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE) {
+               len = bp_info->addr2 - bp_info->addr;
+       } else if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) {
+               ptrace_put_breakpoints(child);
+               return -EINVAL;
+       }
+       bp = thread->ptrace_bps[0];
+       if (bp) {
+               ptrace_put_breakpoints(child);
+               return -ENOSPC;
+       }
 
+       /* Create a new breakpoint request if one doesn't exist already */
+       hw_breakpoint_init(&attr);
+       attr.bp_addr = (unsigned long)bp_info->addr & ~HW_BREAKPOINT_ALIGN;
+       attr.bp_len = len;
+       arch_bp_generic_fields(dabr & (DABR_DATA_WRITE | DABR_DATA_READ),
+                                                               &attr.bp_type);
+
+       thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
+                                              ptrace_triggered, NULL, child);
+       if (IS_ERR(bp)) {
+               thread->ptrace_bps[0] = NULL;
+               ptrace_put_breakpoints(child);
+               return PTR_ERR(bp);
+       }
+
+       ptrace_put_breakpoints(child);
+       return 1;
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
+
+       if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT)
+               return -EINVAL;
+
+       if (child->thread.dabr)
+               return -ENOSPC;
+
+       child->thread.dabr = dabr;
        return 1;
 #endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */
 }
 
 static long ppc_del_hwdebug(struct task_struct *child, long addr, long data)
 {
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+       int ret = 0;
+       struct thread_struct *thread = &(child->thread);
+       struct perf_event *bp;
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 #ifdef CONFIG_PPC_ADV_DEBUG_REGS
        int rc;
 
@@ -1423,49 +1473,30 @@ static long ppc_del_hwdebug(struct task_struct *child, long addr, long data)
 #else
        if (data != 1)
                return -EINVAL;
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+       if (ptrace_get_breakpoints(child) < 0)
+               return -ESRCH;
+
+       bp = thread->ptrace_bps[0];
+       if (bp) {
+               unregister_hw_breakpoint(bp);
+               thread->ptrace_bps[0] = NULL;
+       } else
+               ret = -ENOENT;
+       ptrace_put_breakpoints(child);
+       return ret;
+#else /* CONFIG_HAVE_HW_BREAKPOINT */
        if (child->thread.dabr == 0)
                return -ENOENT;
 
        child->thread.dabr = 0;
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 
        return 0;
 #endif
 }
 
-/*
- * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls,
- * we mark them as obsolete now, they will be removed in a future version
- */
-static long arch_ptrace_old(struct task_struct *child, long request,
-                           unsigned long addr, unsigned long data)
-{
-       void __user *datavp = (void __user *) data;
-
-       switch (request) {
-       case PPC_PTRACE_GETREGS:        /* Get GPRs 0 - 31. */
-               return copy_regset_to_user(child, &user_ppc_native_view,
-                                          REGSET_GPR, 0, 32 * sizeof(long),
-                                          datavp);
-
-       case PPC_PTRACE_SETREGS:        /* Set GPRs 0 - 31. */
-               return copy_regset_from_user(child, &user_ppc_native_view,
-                                            REGSET_GPR, 0, 32 * sizeof(long),
-                                            datavp);
-
-       case PPC_PTRACE_GETFPREGS:      /* Get FPRs 0 - 31. */
-               return copy_regset_to_user(child, &user_ppc_native_view,
-                                          REGSET_FPR, 0, 32 * sizeof(double),
-                                          datavp);
-
-       case PPC_PTRACE_SETFPREGS:      /* Set FPRs 0 - 31. */
-               return copy_regset_from_user(child, &user_ppc_native_view,
-                                            REGSET_FPR, 0, 32 * sizeof(double),
-                                            datavp);
-       }
-
-       return -EPERM;
-}
-
 long arch_ptrace(struct task_struct *child, long request,
                 unsigned long addr, unsigned long data)
 {
@@ -1567,7 +1598,7 @@ long arch_ptrace(struct task_struct *child, long request,
                dbginfo.data_bp_alignment = 4;
 #endif
                dbginfo.sizeof_condition = 0;
-               dbginfo.features = 0;
+               dbginfo.features = PPC_DEBUG_FEATURE_DATA_BP_RANGE;
 #endif /* CONFIG_PPC_ADV_DEBUG_REGS */
 
                if (!access_ok(VERIFY_WRITE, datavp,
@@ -1687,14 +1718,6 @@ long arch_ptrace(struct task_struct *child, long request,
                                             datavp);
 #endif
 
-       /* Old reverse args ptrace callss */
-       case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
-       case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
-       case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */
-       case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */
-               ret = arch_ptrace_old(child, request, addr, data);
-               break;
-
        default:
                ret = ptrace_request(child, request, addr, data);
                break;