Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph...
[pandora-kernel.git] / arch / arm / kernel / hw_breakpoint.c
index b16c456..c9f3f04 100644 (file)
@@ -219,23 +219,6 @@ static int get_num_brps(void)
        return brps;
 }
 
-int hw_breakpoint_slots(int type)
-{
-       /*
-        * We can be called early, so don't rely on
-        * our static variables being initialised.
-        */
-       switch (type) {
-       case TYPE_INST:
-               return get_num_brps();
-       case TYPE_DATA:
-               return get_num_wrps();
-       default:
-               pr_warning("unknown slot type: %d\n", type);
-               return 0;
-       }
-}
-
 /*
  * In order to access the breakpoint/watchpoint control registers,
  * we must be running in debug monitor mode. Unfortunately, we can
@@ -256,8 +239,12 @@ static int enable_monitor_mode(void)
                goto out;
        }
 
+       /* If monitor mode is already enabled, just return. */
+       if (dscr & ARM_DSCR_MDBGEN)
+               goto out;
+
        /* Write to the corresponding DSCR. */
-       switch (debug_arch) {
+       switch (get_debug_arch()) {
        case ARM_DEBUG_ARCH_V6:
        case ARM_DEBUG_ARCH_V6_1:
                ARM_DBG_WRITE(c1, 0, (dscr | ARM_DSCR_MDBGEN));
@@ -272,15 +259,30 @@ static int enable_monitor_mode(void)
 
        /* Check that the write made it through. */
        ARM_DBG_READ(c1, 0, dscr);
-       if (WARN_ONCE(!(dscr & ARM_DSCR_MDBGEN),
-                               "failed to enable monitor mode.")) {
+       if (!(dscr & ARM_DSCR_MDBGEN))
                ret = -EPERM;
-       }
 
 out:
        return ret;
 }
 
+int hw_breakpoint_slots(int type)
+{
+       /*
+        * We can be called early, so don't rely on
+        * our static variables being initialised.
+        */
+       switch (type) {
+       case TYPE_INST:
+               return get_num_brps();
+       case TYPE_DATA:
+               return get_num_wrps();
+       default:
+               pr_warning("unknown slot type: %d\n", type);
+               return 0;
+       }
+}
+
 /*
  * Check if 8-bit byte-address select is available.
  * This clobbers WRP 0.
@@ -294,9 +296,6 @@ static u8 get_max_wp_len(void)
        if (debug_arch < ARM_DEBUG_ARCH_V7_ECP14)
                goto out;
 
-       if (enable_monitor_mode())
-               goto out;
-
        memset(&ctrl, 0, sizeof(ctrl));
        ctrl.len = ARM_BREAKPOINT_LEN_8;
        ctrl_reg = encode_ctrl_reg(ctrl);
@@ -315,23 +314,6 @@ u8 arch_get_max_wp_len(void)
        return max_watchpoint_len;
 }
 
-/*
- * Handler for reactivating a suspended watchpoint when the single
- * step `mismatch' breakpoint is triggered.
- */
-static void wp_single_step_handler(struct perf_event *bp, int unused,
-                                  struct perf_sample_data *data,
-                                  struct pt_regs *regs)
-{
-       perf_event_enable(counter_arch_bp(bp)->suspended_wp);
-       unregister_hw_breakpoint(bp);
-}
-
-static int bp_is_single_step(struct perf_event *bp)
-{
-       return bp->overflow_handler == wp_single_step_handler;
-}
-
 /*
  * Install a perf counter breakpoint.
  */
@@ -340,30 +322,41 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
        struct arch_hw_breakpoint *info = counter_arch_bp(bp);
        struct perf_event **slot, **slots;
        int i, max_slots, ctrl_base, val_base, ret = 0;
+       u32 addr, ctrl;
 
        /* Ensure that we are in monitor mode and halting mode is disabled. */
        ret = enable_monitor_mode();
        if (ret)
                goto out;
 
+       addr = info->address;
+       ctrl = encode_ctrl_reg(info->ctrl) | 0x1;
+
        if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
                /* Breakpoint */
                ctrl_base = ARM_BASE_BCR;
                val_base = ARM_BASE_BVR;
-               slots = __get_cpu_var(bp_on_reg);
+               slots = (struct perf_event **)__get_cpu_var(bp_on_reg);
                max_slots = core_num_brps;
-
-               if (bp_is_single_step(bp)) {
-                       info->ctrl.mismatch = 1;
-                       i = max_slots;
-                       slots[i] = bp;
-                       goto setup;
+               if (info->step_ctrl.enabled) {
+                       /* Override the breakpoint data with the step data. */
+                       addr = info->trigger & ~0x3;
+                       ctrl = encode_ctrl_reg(info->step_ctrl);
                }
        } else {
                /* Watchpoint */
-               ctrl_base = ARM_BASE_WCR;
-               val_base = ARM_BASE_WVR;
-               slots = __get_cpu_var(wp_on_reg);
+               if (info->step_ctrl.enabled) {
+                       /* Install into the reserved breakpoint region. */
+                       ctrl_base = ARM_BASE_BCR + core_num_brps;
+                       val_base = ARM_BASE_BVR + core_num_brps;
+                       /* Override the watchpoint data with the step data. */
+                       addr = info->trigger & ~0x3;
+                       ctrl = encode_ctrl_reg(info->step_ctrl);
+               } else {
+                       ctrl_base = ARM_BASE_WCR;
+                       val_base = ARM_BASE_WVR;
+               }
+               slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
                max_slots = core_num_wrps;
        }
 
@@ -381,12 +374,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
                goto out;
        }
 
-setup:
        /* Setup the address register. */
-       write_wb_reg(val_base + i, info->address);
+       write_wb_reg(val_base + i, addr);
 
        /* Setup the control register. */
-       write_wb_reg(ctrl_base + i, encode_ctrl_reg(info->ctrl) | 0x1);
+       write_wb_reg(ctrl_base + i, ctrl);
 
 out:
        return ret;
@@ -401,18 +393,15 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
        if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
                /* Breakpoint */
                base = ARM_BASE_BCR;
-               slots = __get_cpu_var(bp_on_reg);
+               slots = (struct perf_event **)__get_cpu_var(bp_on_reg);
                max_slots = core_num_brps;
-
-               if (bp_is_single_step(bp)) {
-                       i = max_slots;
-                       slots[i] = NULL;
-                       goto reset;
-               }
        } else {
                /* Watchpoint */
-               base = ARM_BASE_WCR;
-               slots = __get_cpu_var(wp_on_reg);
+               if (info->step_ctrl.enabled)
+                       base = ARM_BASE_BCR + core_num_brps;
+               else
+                       base = ARM_BASE_WCR;
+               slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
                max_slots = core_num_wrps;
        }
 
@@ -429,7 +418,6 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
        if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot"))
                return;
 
-reset:
        /* Reset the control register. */
        write_wb_reg(base + i, 0);
 }
@@ -579,7 +567,7 @@ static int arch_build_bp_info(struct perf_event *bp)
 
        /* Privilege */
        info->ctrl.privilege = ARM_BREAKPOINT_USER;
-       if (arch_check_bp_in_kernelspace(bp) && !bp_is_single_step(bp))
+       if (arch_check_bp_in_kernelspace(bp))
                info->ctrl.privilege |= ARM_BREAKPOINT_PRIV;
 
        /* Enabled? */
@@ -633,10 +621,12 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
         * Currently we rely on an overflow handler to take
         * care of single-stepping the breakpoint when it fires.
         * In the case of userspace breakpoints on a core with V7 debug,
-        * we can use the mismatch feature as a poor-man's hardware single-step.
+        * we can use the mismatch feature as a poor-man's hardware
+        * single-step, but this only works for per-task breakpoints.
         */
        if (WARN_ONCE(!bp->overflow_handler &&
-               (arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_brps()),
+               (arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_brps()
+                || !bp->hw.bp_target),
                        "overflow handler required but none found")) {
                ret = -EINVAL;
        }
@@ -644,42 +634,47 @@ out:
        return ret;
 }
 
-static void update_mismatch_flag(int idx, int flag)
+/*
+ * Enable/disable single-stepping over the breakpoint bp at address addr.
+ */
+static void enable_single_step(struct perf_event *bp, u32 addr)
 {
-       struct perf_event *bp = __get_cpu_var(bp_on_reg[idx]);
-       struct arch_hw_breakpoint *info;
-
-       if (bp == NULL)
-               return;
+       struct arch_hw_breakpoint *info = counter_arch_bp(bp);
 
-       info = counter_arch_bp(bp);
+       arch_uninstall_hw_breakpoint(bp);
+       info->step_ctrl.mismatch  = 1;
+       info->step_ctrl.len       = ARM_BREAKPOINT_LEN_4;
+       info->step_ctrl.type      = ARM_BREAKPOINT_EXECUTE;
+       info->step_ctrl.privilege = info->ctrl.privilege;
+       info->step_ctrl.enabled   = 1;
+       info->trigger             = addr;
+       arch_install_hw_breakpoint(bp);
+}
 
-       /* Update the mismatch field to enter/exit `single-step' mode */
-       if (!bp->overflow_handler && info->ctrl.mismatch != flag) {
-               info->ctrl.mismatch = flag;
-               write_wb_reg(ARM_BASE_BCR + idx, encode_ctrl_reg(info->ctrl) | 0x1);
-       }
+static void disable_single_step(struct perf_event *bp)
+{
+       arch_uninstall_hw_breakpoint(bp);
+       counter_arch_bp(bp)->step_ctrl.enabled = 0;
+       arch_install_hw_breakpoint(bp);
 }
 
 static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
 {
        int i;
-       struct perf_event *bp, **slots = __get_cpu_var(wp_on_reg);
+       struct perf_event *wp, **slots;
        struct arch_hw_breakpoint *info;
-       struct perf_event_attr attr;
+
+       slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
 
        /* Without a disassembler, we can only handle 1 watchpoint. */
        BUG_ON(core_num_wrps > 1);
 
-       hw_breakpoint_init(&attr);
-       attr.bp_addr    = regs->ARM_pc & ~0x3;
-       attr.bp_len     = HW_BREAKPOINT_LEN_4;
-       attr.bp_type    = HW_BREAKPOINT_X;
-
        for (i = 0; i < core_num_wrps; ++i) {
                rcu_read_lock();
 
-               if (slots[i] == NULL) {
+               wp = slots[i];
+
+               if (wp == NULL) {
                        rcu_read_unlock();
                        continue;
                }
@@ -689,75 +684,106 @@ static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
                 * single watchpoint, we can set the trigger to the lowest
                 * possible faulting address.
                 */
-               info = counter_arch_bp(slots[i]);
-               info->trigger = slots[i]->attr.bp_addr;
+               info = counter_arch_bp(wp);
+               info->trigger = wp->attr.bp_addr;
                pr_debug("watchpoint fired: address = 0x%x\n", info->trigger);
-               perf_bp_event(slots[i], regs);
+               perf_bp_event(wp, regs);
 
                /*
                 * If no overflow handler is present, insert a temporary
                 * mismatch breakpoint so we can single-step over the
                 * watchpoint trigger.
                 */
-               if (!slots[i]->overflow_handler) {
-                       bp = register_user_hw_breakpoint(&attr,
-                                                        wp_single_step_handler,
-                                                        current);
-                       counter_arch_bp(bp)->suspended_wp = slots[i];
-                       perf_event_disable(slots[i]);
-               }
+               if (!wp->overflow_handler)
+                       enable_single_step(wp, instruction_pointer(regs));
 
                rcu_read_unlock();
        }
 }
 
+static void watchpoint_single_step_handler(unsigned long pc)
+{
+       int i;
+       struct perf_event *wp, **slots;
+       struct arch_hw_breakpoint *info;
+
+       slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
+
+       for (i = 0; i < core_num_reserved_brps; ++i) {
+               rcu_read_lock();
+
+               wp = slots[i];
+
+               if (wp == NULL)
+                       goto unlock;
+
+               info = counter_arch_bp(wp);
+               if (!info->step_ctrl.enabled)
+                       goto unlock;
+
+               /*
+                * Restore the original watchpoint if we've completed the
+                * single-step.
+                */
+               if (info->trigger != pc)
+                       disable_single_step(wp);
+
+unlock:
+               rcu_read_unlock();
+       }
+}
+
 static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)
 {
        int i;
-       int mismatch;
        u32 ctrl_reg, val, addr;
-       struct perf_event *bp, **slots = __get_cpu_var(bp_on_reg);
+       struct perf_event *bp, **slots;
        struct arch_hw_breakpoint *info;
        struct arch_hw_breakpoint_ctrl ctrl;
 
+       slots = (struct perf_event **)__get_cpu_var(bp_on_reg);
+
        /* The exception entry code places the amended lr in the PC. */
        addr = regs->ARM_pc;
 
-       for (i = 0; i < core_num_brps + core_num_reserved_brps; ++i) {
+       /* Check the currently installed breakpoints first. */
+       for (i = 0; i < core_num_brps; ++i) {
                rcu_read_lock();
 
                bp = slots[i];
 
-               if (bp == NULL) {
-                       rcu_read_unlock();
-                       continue;
-               }
+               if (bp == NULL)
+                       goto unlock;
 
-               mismatch = 0;
+               info = counter_arch_bp(bp);
 
                /* Check if the breakpoint value matches. */
                val = read_wb_reg(ARM_BASE_BVR + i);
                if (val != (addr & ~0x3))
-                       goto unlock;
+                       goto mismatch;
 
                /* Possible match, check the byte address select to confirm. */
                ctrl_reg = read_wb_reg(ARM_BASE_BCR + i);
                decode_ctrl_reg(ctrl_reg, &ctrl);
                if ((1 << (addr & 0x3)) & ctrl.len) {
-                       mismatch = 1;
-                       info = counter_arch_bp(bp);
                        info->trigger = addr;
-               }
-
-unlock:
-               if ((mismatch && !info->ctrl.mismatch) || bp_is_single_step(bp)) {
                        pr_debug("breakpoint fired: address = 0x%x\n", addr);
                        perf_bp_event(bp, regs);
+                       if (!bp->overflow_handler)
+                               enable_single_step(bp, addr);
+                       goto unlock;
                }
 
-               update_mismatch_flag(i, mismatch);
+mismatch:
+               /* If we're stepping a breakpoint, it can now be restored. */
+               if (info->step_ctrl.enabled)
+                       disable_single_step(bp);
+unlock:
                rcu_read_unlock();
        }
+
+       /* Handle any pending watchpoint single-step breakpoints. */
+       watchpoint_single_step_handler(addr);
 }
 
 /*
@@ -852,15 +878,13 @@ static struct notifier_block __cpuinitdata dbg_reset_nb = {
 
 static int __init arch_hw_breakpoint_init(void)
 {
-       int ret = 0;
        u32 dscr;
 
        debug_arch = get_debug_arch();
 
        if (debug_arch > ARM_DEBUG_ARCH_V7_ECP14) {
                pr_info("debug architecture 0x%x unsupported.\n", debug_arch);
-               ret = -ENODEV;
-               goto out;
+               return 0;
        }
 
        /* Determine how many BRPs/WRPs are available. */
@@ -901,8 +925,7 @@ static int __init arch_hw_breakpoint_init(void)
 
        /* Register hotplug notifier. */
        register_cpu_notifier(&dbg_reset_nb);
-out:
-       return ret;
+       return 0;
 }
 arch_initcall(arch_hw_breakpoint_init);