]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
ARM: hw_breakpoint: unify single-stepping code for watchpoints and breakpoints
authorWill Deacon <will.deacon@arm.com>
Wed, 1 Dec 2010 14:12:13 +0000 (14:12 +0000)
committerWill Deacon <will.deacon@arm.com>
Mon, 6 Dec 2010 11:55:57 +0000 (11:55 +0000)
The single-stepping code is currently different depending on whether
we are stepping over a breakpoint or a watchpoint. There is no good
reason for this, so let's sort it out.

This patch adds functions for enabling/disabling single-step for
a particular hw_breakpoint and integrates this with the exception
handling code.

Signed-off-by: Will Deacon <will.deacon@arm.com>
arch/arm/include/asm/hw_breakpoint.h
arch/arm/kernel/hw_breakpoint.c

index 881429d0b8492fe02fa8de442e88384bdd95be4f..f389b2704d82110226254551e873d18f6c93a69c 100644 (file)
@@ -20,8 +20,8 @@ struct arch_hw_breakpoint_ctrl {
 struct arch_hw_breakpoint {
        u32     address;
        u32     trigger;
-       struct arch_hw_breakpoint_ctrl step_ctrl;
-       struct arch_hw_breakpoint_ctrl ctrl;
+       struct  arch_hw_breakpoint_ctrl step_ctrl;
+       struct  arch_hw_breakpoint_ctrl ctrl;
 };
 
 static inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl)
index 81b63e94dfe08cd983ca22cde35b66774023d23a..36cd7680d3d2a0e8cf7538911137d40f54248559 100644 (file)
@@ -339,6 +339,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
                val_base = ARM_BASE_BVR;
                slots = __get_cpu_var(bp_on_reg);
                max_slots = core_num_brps;
+               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 */
                if (info->step_ctrl.enabled) {
@@ -628,21 +633,28 @@ 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)
@@ -679,16 +691,8 @@ static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
                 * mismatch breakpoint so we can single-step over the
                 * watchpoint trigger.
                 */
-               if (!wp->overflow_handler) {
-                       arch_uninstall_hw_breakpoint(wp);
-                       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             = regs->ARM_pc;
-                       arch_install_hw_breakpoint(wp);
-               }
+               if (!wp->overflow_handler)
+                       enable_single_step(wp, instruction_pointer(regs));
 
                rcu_read_unlock();
        }
@@ -716,11 +720,8 @@ static void watchpoint_single_step_handler(unsigned long pc)
                 * Restore the original watchpoint if we've completed the
                 * single-step.
                 */
-               if (info->trigger != pc) {
-                       arch_uninstall_hw_breakpoint(wp);
-                       info->step_ctrl.enabled = 0;
-                       arch_install_hw_breakpoint(wp);
-               }
+               if (info->trigger != pc)
+                       disable_single_step(wp);
 
 unlock:
                rcu_read_unlock();
@@ -730,7 +731,6 @@ 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 arch_hw_breakpoint *info;
@@ -745,34 +745,33 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)
 
                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) {
                        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();
        }