Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+42 -43
+2 -2
arch/arm/include/asm/hw_breakpoint.h
··· 20 20 struct arch_hw_breakpoint { 21 21 u32 address; 22 22 u32 trigger; 23 - struct arch_hw_breakpoint_ctrl step_ctrl; 24 - struct arch_hw_breakpoint_ctrl ctrl; 23 + struct arch_hw_breakpoint_ctrl step_ctrl; 24 + struct arch_hw_breakpoint_ctrl ctrl; 25 25 }; 26 26 27 27 static inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl)
+40 -41
arch/arm/kernel/hw_breakpoint.c
··· 339 339 val_base = ARM_BASE_BVR; 340 340 slots = __get_cpu_var(bp_on_reg); 341 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 347 } else { 343 348 /* Watchpoint */ 344 349 if (info->step_ctrl.enabled) { ··· 633 628 return ret; 634 629 } 635 630 636 - static void update_mismatch_flag(int idx, int flag) 631 + /* 632 + * Enable/disable single-stepping over the breakpoint bp at address addr. 633 + */ 634 + static void enable_single_step(struct perf_event *bp, u32 addr) 637 635 { 638 - struct perf_event *bp = __get_cpu_var(bp_on_reg[idx]); 639 - struct arch_hw_breakpoint *info; 636 + struct arch_hw_breakpoint *info = counter_arch_bp(bp); 640 637 641 - if (bp == NULL) 642 - return; 638 + arch_uninstall_hw_breakpoint(bp); 639 + info->step_ctrl.mismatch = 1; 640 + info->step_ctrl.len = ARM_BREAKPOINT_LEN_4; 641 + info->step_ctrl.type = ARM_BREAKPOINT_EXECUTE; 642 + info->step_ctrl.privilege = info->ctrl.privilege; 643 + info->step_ctrl.enabled = 1; 644 + info->trigger = addr; 645 + arch_install_hw_breakpoint(bp); 646 + } 643 647 644 - info = counter_arch_bp(bp); 645 - 646 - /* Update the mismatch field to enter/exit `single-step' mode */ 647 - if (!bp->overflow_handler && info->ctrl.mismatch != flag) { 648 - info->ctrl.mismatch = flag; 649 - write_wb_reg(ARM_BASE_BCR + idx, encode_ctrl_reg(info->ctrl) | 0x1); 650 - } 648 + static void disable_single_step(struct perf_event *bp) 649 + { 650 + arch_uninstall_hw_breakpoint(bp); 651 + counter_arch_bp(bp)->step_ctrl.enabled = 0; 652 + arch_install_hw_breakpoint(bp); 651 653 } 652 654 653 655 static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs) ··· 691 679 * mismatch breakpoint so we can single-step over the 692 680 * watchpoint trigger. 693 681 */ 694 - if (!wp->overflow_handler) { 695 - arch_uninstall_hw_breakpoint(wp); 696 - info->step_ctrl.mismatch = 1; 697 - info->step_ctrl.len = ARM_BREAKPOINT_LEN_4; 698 - info->step_ctrl.type = ARM_BREAKPOINT_EXECUTE; 699 - info->step_ctrl.privilege = info->ctrl.privilege; 700 - info->step_ctrl.enabled = 1; 701 - info->trigger = regs->ARM_pc; 702 - arch_install_hw_breakpoint(wp); 703 - } 682 + if (!wp->overflow_handler) 683 + enable_single_step(wp, instruction_pointer(regs)); 704 684 705 685 rcu_read_unlock(); 706 686 } ··· 720 716 * Restore the original watchpoint if we've completed the 721 717 * single-step. 722 718 */ 723 - if (info->trigger != pc) { 724 - arch_uninstall_hw_breakpoint(wp); 725 - info->step_ctrl.enabled = 0; 726 - arch_install_hw_breakpoint(wp); 727 - } 719 + if (info->trigger != pc) 720 + disable_single_step(wp); 728 721 729 722 unlock: 730 723 rcu_read_unlock(); ··· 731 730 static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs) 732 731 { 733 732 int i; 734 - int mismatch; 735 733 u32 ctrl_reg, val, addr; 736 734 struct perf_event *bp, **slots = __get_cpu_var(bp_on_reg); 737 735 struct arch_hw_breakpoint *info; ··· 745 745 746 746 bp = slots[i]; 747 747 748 - if (bp == NULL) { 749 - rcu_read_unlock(); 750 - continue; 751 - } 748 + if (bp == NULL) 749 + goto unlock; 752 750 753 - mismatch = 0; 751 + info = counter_arch_bp(bp); 754 752 755 753 /* Check if the breakpoint value matches. */ 756 754 val = read_wb_reg(ARM_BASE_BVR + i); 757 755 if (val != (addr & ~0x3)) 758 - goto unlock; 756 + goto mismatch; 759 757 760 758 /* Possible match, check the byte address select to confirm. */ 761 759 ctrl_reg = read_wb_reg(ARM_BASE_BCR + i); 762 760 decode_ctrl_reg(ctrl_reg, &ctrl); 763 761 if ((1 << (addr & 0x3)) & ctrl.len) { 764 - mismatch = 1; 765 - info = counter_arch_bp(bp); 766 762 info->trigger = addr; 767 - } 768 - 769 - unlock: 770 - if (mismatch && !info->ctrl.mismatch) { 771 763 pr_debug("breakpoint fired: address = 0x%x\n", addr); 772 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 775 rcu_read_unlock(); 777 776 } 778 777