]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/powerpc/kernel/ptrace.c
Merge tag 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[karo-tx-linux.git] / arch / powerpc / kernel / ptrace.c
index 79d8e56470df8105c9119aee7f01f4938101acc8..c4970004d44d2cfa0e190cf6d7be973b5f4b03ec 100644 (file)
@@ -952,6 +952,10 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
                arch_bp_generic_fields(data &
                                        (DABR_DATA_WRITE | DABR_DATA_READ),
                                                        &attr.bp_type);
+
+               /* Enable breakpoint */
+               attr.disabled = false;
+
                ret =  modify_user_hw_breakpoint(bp, &attr);
                if (ret) {
                        ptrace_put_breakpoints(task);
@@ -1037,7 +1041,7 @@ void ptrace_disable(struct task_struct *child)
 }
 
 #ifdef CONFIG_PPC_ADV_DEBUG_REGS
-static long set_intruction_bp(struct task_struct *child,
+static long set_instruction_bp(struct task_struct *child,
                              struct ppc_hw_breakpoint *bp_info)
 {
        int slot;
@@ -1338,6 +1342,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
@@ -1365,7 +1375,7 @@ static long ppc_set_hwdebug(struct task_struct *child,
                if ((bp_info->trigger_type != PPC_BREAKPOINT_TRIGGER_EXECUTE) ||
                    (bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE))
                        return -EINVAL;
-               return set_intruction_bp(child, bp_info);
+               return set_instruction_bp(child, bp_info);
        }
        if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
                return set_dac(child, bp_info);
@@ -1381,13 +1391,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;
 
@@ -1397,6 +1403,50 @@ 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;
+
+       /*
+        * 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;
        child->thread.dabrx = DABRX_ALL;
@@ -1405,8 +1455,13 @@ static long ppc_set_hwdebug(struct task_struct *child,
 #endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */
 }
 
-static long ppc_del_hwdebug(struct task_struct *child, long addr, long data)
+static long ppc_del_hwdebug(struct task_struct *child, 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;
 
@@ -1426,10 +1481,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
@@ -1536,7 +1606,11 @@ long arch_ptrace(struct task_struct *child, long request,
                dbginfo.data_bp_alignment = 4;
 #endif
                dbginfo.sizeof_condition = 0;
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+               dbginfo.features = PPC_DEBUG_FEATURE_DATA_BP_RANGE;
+#else
                dbginfo.features = 0;
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 #endif /* CONFIG_PPC_ADV_DEBUG_REGS */
 
                if (!access_ok(VERIFY_WRITE, datavp,
@@ -1563,7 +1637,7 @@ long arch_ptrace(struct task_struct *child, long request,
        }
 
        case PPC_PTRACE_DELHWDEBUG: {
-               ret = ppc_del_hwdebug(child, addr, data);
+               ret = ppc_del_hwdebug(child, data);
                break;
        }