diff options
author | Will Deacon <will.deacon@arm.com> | 2010-12-01 09:12:13 -0500 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2010-12-06 06:55:57 -0500 |
commit | 9ebb3cbcc39d4e61ae6751167086acfb5c201e6f (patch) | |
tree | 4e88970d75cb2e7b3a5a2f1b50c58ab358d30339 | |
parent | 93a04a3416da12647c47840ebe2bb812fcb801d0 (diff) |
ARM: hw_breakpoint: unify single-stepping code for watchpoints and breakpoints
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>
-rw-r--r-- | arch/arm/include/asm/hw_breakpoint.h | 4 | ||||
-rw-r--r-- | arch/arm/kernel/hw_breakpoint.c | 81 |
2 files changed, 42 insertions, 43 deletions
diff --git a/arch/arm/include/asm/hw_breakpoint.h b/arch/arm/include/asm/hw_breakpoint.h index 881429d0b849..f389b2704d82 100644 --- a/arch/arm/include/asm/hw_breakpoint.h +++ b/arch/arm/include/asm/hw_breakpoint.h | |||
@@ -20,8 +20,8 @@ struct arch_hw_breakpoint_ctrl { | |||
20 | struct arch_hw_breakpoint { | 20 | struct arch_hw_breakpoint { |
21 | u32 address; | 21 | u32 address; |
22 | u32 trigger; | 22 | u32 trigger; |
23 | struct arch_hw_breakpoint_ctrl step_ctrl; | 23 | struct arch_hw_breakpoint_ctrl step_ctrl; |
24 | struct arch_hw_breakpoint_ctrl ctrl; | 24 | struct arch_hw_breakpoint_ctrl ctrl; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | static inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl) | 27 | static inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl) |
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 81b63e94dfe0..36cd7680d3d2 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c | |||
@@ -339,6 +339,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp) | |||
339 | val_base = ARM_BASE_BVR; | 339 | val_base = ARM_BASE_BVR; |
340 | slots = __get_cpu_var(bp_on_reg); | 340 | slots = __get_cpu_var(bp_on_reg); |
341 | max_slots = core_num_brps; | 341 | max_slots = core_num_brps; |
342 | if (info->step_ctrl.enabled) { | ||
343 | /* Override the breakpoint data with the step data. */ | ||
344 | addr = info->trigger & ~0x3; | ||
345 | ctrl = encode_ctrl_reg(info->step_ctrl); | ||
346 | } | ||
342 | } else { | 347 | } else { |
343 | /* Watchpoint */ | 348 | /* Watchpoint */ |
344 | if (info->step_ctrl.enabled) { | 349 | if (info->step_ctrl.enabled) { |
@@ -628,21 +633,28 @@ out: | |||
628 | return ret; | 633 | return ret; |
629 | } | 634 | } |
630 | 635 | ||
631 | static void update_mismatch_flag(int idx, int flag) | 636 | /* |
637 | * Enable/disable single-stepping over the breakpoint bp at address addr. | ||
638 | */ | ||
639 | static void enable_single_step(struct perf_event *bp, u32 addr) | ||
632 | { | 640 | { |
633 | struct perf_event *bp = __get_cpu_var(bp_on_reg[idx]); | 641 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); |
634 | struct arch_hw_breakpoint *info; | ||
635 | |||
636 | if (bp == NULL) | ||
637 | return; | ||
638 | 642 | ||
639 | info = counter_arch_bp(bp); | 643 | arch_uninstall_hw_breakpoint(bp); |
644 | info->step_ctrl.mismatch = 1; | ||
645 | info->step_ctrl.len = ARM_BREAKPOINT_LEN_4; | ||
646 | info->step_ctrl.type = ARM_BREAKPOINT_EXECUTE; | ||
647 | info->step_ctrl.privilege = info->ctrl.privilege; | ||
648 | info->step_ctrl.enabled = 1; | ||
649 | info->trigger = addr; | ||
650 | arch_install_hw_breakpoint(bp); | ||
651 | } | ||
640 | 652 | ||
641 | /* Update the mismatch field to enter/exit `single-step' mode */ | 653 | static void disable_single_step(struct perf_event *bp) |
642 | if (!bp->overflow_handler && info->ctrl.mismatch != flag) { | 654 | { |
643 | info->ctrl.mismatch = flag; | 655 | arch_uninstall_hw_breakpoint(bp); |
644 | write_wb_reg(ARM_BASE_BCR + idx, encode_ctrl_reg(info->ctrl) | 0x1); | 656 | counter_arch_bp(bp)->step_ctrl.enabled = 0; |
645 | } | 657 | arch_install_hw_breakpoint(bp); |
646 | } | 658 | } |
647 | 659 | ||
648 | static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs) | 660 | 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) | |||
679 | * mismatch breakpoint so we can single-step over the | 691 | * mismatch breakpoint so we can single-step over the |
680 | * watchpoint trigger. | 692 | * watchpoint trigger. |
681 | */ | 693 | */ |
682 | if (!wp->overflow_handler) { | 694 | if (!wp->overflow_handler) |
683 | arch_uninstall_hw_breakpoint(wp); | 695 | enable_single_step(wp, instruction_pointer(regs)); |
684 | info->step_ctrl.mismatch = 1; | ||
685 | info->step_ctrl.len = ARM_BREAKPOINT_LEN_4; | ||
686 | info->step_ctrl.type = ARM_BREAKPOINT_EXECUTE; | ||
687 | info->step_ctrl.privilege = info->ctrl.privilege; | ||
688 | info->step_ctrl.enabled = 1; | ||
689 | info->trigger = regs->ARM_pc; | ||
690 | arch_install_hw_breakpoint(wp); | ||
691 | } | ||
692 | 696 | ||
693 | rcu_read_unlock(); | 697 | rcu_read_unlock(); |
694 | } | 698 | } |
@@ -716,11 +720,8 @@ static void watchpoint_single_step_handler(unsigned long pc) | |||
716 | * Restore the original watchpoint if we've completed the | 720 | * Restore the original watchpoint if we've completed the |
717 | * single-step. | 721 | * single-step. |
718 | */ | 722 | */ |
719 | if (info->trigger != pc) { | 723 | if (info->trigger != pc) |
720 | arch_uninstall_hw_breakpoint(wp); | 724 | disable_single_step(wp); |
721 | info->step_ctrl.enabled = 0; | ||
722 | arch_install_hw_breakpoint(wp); | ||
723 | } | ||
724 | 725 | ||
725 | unlock: | 726 | unlock: |
726 | rcu_read_unlock(); | 727 | rcu_read_unlock(); |
@@ -730,7 +731,6 @@ unlock: | |||
730 | static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs) | 731 | static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs) |
731 | { | 732 | { |
732 | int i; | 733 | int i; |
733 | int mismatch; | ||
734 | u32 ctrl_reg, val, addr; | 734 | u32 ctrl_reg, val, addr; |
735 | struct perf_event *bp, **slots = __get_cpu_var(bp_on_reg); | 735 | struct perf_event *bp, **slots = __get_cpu_var(bp_on_reg); |
736 | struct arch_hw_breakpoint *info; | 736 | struct arch_hw_breakpoint *info; |
@@ -745,34 +745,33 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs) | |||
745 | 745 | ||
746 | bp = slots[i]; | 746 | bp = slots[i]; |
747 | 747 | ||
748 | if (bp == NULL) { | 748 | if (bp == NULL) |
749 | rcu_read_unlock(); | 749 | goto unlock; |
750 | continue; | ||
751 | } | ||
752 | 750 | ||
753 | mismatch = 0; | 751 | info = counter_arch_bp(bp); |
754 | 752 | ||
755 | /* Check if the breakpoint value matches. */ | 753 | /* Check if the breakpoint value matches. */ |
756 | val = read_wb_reg(ARM_BASE_BVR + i); | 754 | val = read_wb_reg(ARM_BASE_BVR + i); |
757 | if (val != (addr & ~0x3)) | 755 | if (val != (addr & ~0x3)) |
758 | goto unlock; | 756 | goto mismatch; |
759 | 757 | ||
760 | /* Possible match, check the byte address select to confirm. */ | 758 | /* Possible match, check the byte address select to confirm. */ |
761 | ctrl_reg = read_wb_reg(ARM_BASE_BCR + i); | 759 | ctrl_reg = read_wb_reg(ARM_BASE_BCR + i); |
762 | decode_ctrl_reg(ctrl_reg, &ctrl); | 760 | decode_ctrl_reg(ctrl_reg, &ctrl); |
763 | if ((1 << (addr & 0x3)) & ctrl.len) { | 761 | if ((1 << (addr & 0x3)) & ctrl.len) { |
764 | mismatch = 1; | ||
765 | info = counter_arch_bp(bp); | ||
766 | info->trigger = addr; | 762 | info->trigger = addr; |
767 | } | ||
768 | |||
769 | unlock: | ||
770 | if (mismatch && !info->ctrl.mismatch) { | ||
771 | pr_debug("breakpoint fired: address = 0x%x\n", addr); | 763 | pr_debug("breakpoint fired: address = 0x%x\n", addr); |
772 | perf_bp_event(bp, regs); | 764 | perf_bp_event(bp, regs); |
765 | if (!bp->overflow_handler) | ||
766 | enable_single_step(bp, addr); | ||
767 | goto unlock; | ||
773 | } | 768 | } |
774 | 769 | ||
775 | update_mismatch_flag(i, mismatch); | 770 | mismatch: |
771 | /* If we're stepping a breakpoint, it can now be restored. */ | ||
772 | if (info->step_ctrl.enabled) | ||
773 | disable_single_step(bp); | ||
774 | unlock: | ||
776 | rcu_read_unlock(); | 775 | rcu_read_unlock(); |
777 | } | 776 | } |
778 | 777 | ||