]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
powerpc/hw-breakpoint: Use generic hw-breakpoint interfaces for new PPC ptrace flags
authorK.Prasad <prasad@linux.vnet.ibm.com>
Thu, 10 May 2012 22:43:38 +0000 (22:43 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Mon, 14 May 2012 00:48:55 +0000 (10:48 +1000)
PPC_PTRACE_GETHWDBGINFO, PPC_PTRACE_SETHWDEBUG and PPC_PTRACE_DELHWDEBUG are
PowerPC specific ptrace flags that use the watchpoint register. While they are
targeted primarily towards BookE users, user-space applications such as GDB
have started using them for BookS too. This patch enables the use of generic
hardware breakpoint interfaces for these new flags.

Apart from the usual benefits of using generic hw-breakpoint interfaces, these
changes allow debuggers (such as GDB) to use a common set of ptrace flags for
their watchpoint needs and allow more precise breakpoint specification (length
of the variable can be specified).

Signed-off-by: K.Prasad <prasad@linux.vnet.ibm.com>
Acked-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Documentation/powerpc/ptrace.txt
arch/powerpc/kernel/ptrace.c

index f4a5499b7bc6823fd95d6701965e4e1e0e7d07c5..f2a7a3919772ef8e36075231cdad82435cba718a 100644 (file)
@@ -127,6 +127,22 @@ Some examples of using the structure to:
   p.addr2           = (uint64_t) end_range;
   p.condition_value = 0;
 
+- set a watchpoint in server processors (BookS)
+
+  p.version         = 1;
+  p.trigger_type    = PPC_BREAKPOINT_TRIGGER_RW;
+  p.addr_mode       = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+  or
+  p.addr_mode       = PPC_BREAKPOINT_MODE_EXACT;
+
+  p.condition_mode  = PPC_BREAKPOINT_CONDITION_NONE;
+  p.addr            = (uint64_t) begin_range;
+  /* For PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE addr2 needs to be specified, where
+   * addr2 - addr <= 8 Bytes.
+   */
+  p.addr2           = (uint64_t) end_range;
+  p.condition_value = 0;
+
 3. PTRACE_DELHWDEBUG
 
 Takes an integer which identifies an existing breakpoint or watchpoint
index ac3cb008fb31a261460853b54d93452f825c5226..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,10 +1473,25 @@ 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
@@ -1533,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,