diff options
| -rw-r--r-- | arch/arm64/kernel/hw_breakpoint.c | 96 |
1 files changed, 69 insertions, 27 deletions
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index 3f7bc65e7ef6..13035d06b498 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c | |||
| @@ -664,11 +664,46 @@ unlock: | |||
| 664 | } | 664 | } |
| 665 | NOKPROBE_SYMBOL(breakpoint_handler); | 665 | NOKPROBE_SYMBOL(breakpoint_handler); |
| 666 | 666 | ||
| 667 | /* | ||
| 668 | * Arm64 hardware does not always report a watchpoint hit address that matches | ||
| 669 | * one of the watchpoints set. It can also report an address "near" the | ||
| 670 | * watchpoint if a single instruction access both watched and unwatched | ||
| 671 | * addresses. There is no straight-forward way, short of disassembling the | ||
| 672 | * offending instruction, to map that address back to the watchpoint. This | ||
| 673 | * function computes the distance of the memory access from the watchpoint as a | ||
| 674 | * heuristic for the likelyhood that a given access triggered the watchpoint. | ||
| 675 | * | ||
| 676 | * See Section D2.10.5 "Determining the memory location that caused a Watchpoint | ||
| 677 | * exception" of ARMv8 Architecture Reference Manual for details. | ||
| 678 | * | ||
| 679 | * The function returns the distance of the address from the bytes watched by | ||
| 680 | * the watchpoint. In case of an exact match, it returns 0. | ||
| 681 | */ | ||
| 682 | static u64 get_distance_from_watchpoint(unsigned long addr, u64 val, | ||
| 683 | struct arch_hw_breakpoint_ctrl *ctrl) | ||
| 684 | { | ||
| 685 | u64 wp_low, wp_high; | ||
| 686 | u32 lens, lene; | ||
| 687 | |||
| 688 | lens = __ffs(ctrl->len); | ||
| 689 | lene = __fls(ctrl->len); | ||
| 690 | |||
| 691 | wp_low = val + lens; | ||
| 692 | wp_high = val + lene; | ||
| 693 | if (addr < wp_low) | ||
| 694 | return wp_low - addr; | ||
| 695 | else if (addr > wp_high) | ||
| 696 | return addr - wp_high; | ||
| 697 | else | ||
| 698 | return 0; | ||
| 699 | } | ||
| 700 | |||
| 667 | static int watchpoint_handler(unsigned long addr, unsigned int esr, | 701 | static int watchpoint_handler(unsigned long addr, unsigned int esr, |
| 668 | struct pt_regs *regs) | 702 | struct pt_regs *regs) |
| 669 | { | 703 | { |
| 670 | int i, step = 0, *kernel_step, access; | 704 | int i, step = 0, *kernel_step, access, closest_match = 0; |
| 671 | u32 ctrl_reg, lens, lene; | 705 | u64 min_dist = -1, dist; |
| 706 | u32 ctrl_reg; | ||
| 672 | u64 val; | 707 | u64 val; |
| 673 | struct perf_event *wp, **slots; | 708 | struct perf_event *wp, **slots; |
| 674 | struct debug_info *debug_info; | 709 | struct debug_info *debug_info; |
| @@ -678,31 +713,15 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr, | |||
| 678 | slots = this_cpu_ptr(wp_on_reg); | 713 | slots = this_cpu_ptr(wp_on_reg); |
| 679 | debug_info = ¤t->thread.debug; | 714 | debug_info = ¤t->thread.debug; |
| 680 | 715 | ||
| 716 | /* | ||
| 717 | * Find all watchpoints that match the reported address. If no exact | ||
| 718 | * match is found. Attribute the hit to the closest watchpoint. | ||
| 719 | */ | ||
| 720 | rcu_read_lock(); | ||
| 681 | for (i = 0; i < core_num_wrps; ++i) { | 721 | for (i = 0; i < core_num_wrps; ++i) { |
| 682 | rcu_read_lock(); | ||
| 683 | |||
| 684 | wp = slots[i]; | 722 | wp = slots[i]; |
| 685 | |||
| 686 | if (wp == NULL) | 723 | if (wp == NULL) |
| 687 | goto unlock; | 724 | continue; |
| 688 | |||
| 689 | info = counter_arch_bp(wp); | ||
| 690 | |||
| 691 | /* Check if the watchpoint value and byte select match. */ | ||
| 692 | val = read_wb_reg(AARCH64_DBG_REG_WVR, i); | ||
| 693 | ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i); | ||
| 694 | decode_ctrl_reg(ctrl_reg, &ctrl); | ||
| 695 | lens = ffs(ctrl.len) - 1; | ||
| 696 | lene = fls(ctrl.len) - 1; | ||
| 697 | /* | ||
| 698 | * FIXME: reported address can be anywhere between "the | ||
| 699 | * lowest address accessed by the memory access that | ||
| 700 | * triggered the watchpoint" and "the highest watchpointed | ||
| 701 | * address accessed by the memory access". So, it may not | ||
| 702 | * lie in the interval of watchpoint address range. | ||
| 703 | */ | ||
| 704 | if (addr < val + lens || addr > val + lene) | ||
| 705 | goto unlock; | ||
| 706 | 725 | ||
| 707 | /* | 726 | /* |
| 708 | * Check that the access type matches. | 727 | * Check that the access type matches. |
| @@ -711,18 +730,41 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr, | |||
| 711 | access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W : | 730 | access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W : |
| 712 | HW_BREAKPOINT_R; | 731 | HW_BREAKPOINT_R; |
| 713 | if (!(access & hw_breakpoint_type(wp))) | 732 | if (!(access & hw_breakpoint_type(wp))) |
| 714 | goto unlock; | 733 | continue; |
| 715 | 734 | ||
| 735 | /* Check if the watchpoint value and byte select match. */ | ||
| 736 | val = read_wb_reg(AARCH64_DBG_REG_WVR, i); | ||
| 737 | ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i); | ||
| 738 | decode_ctrl_reg(ctrl_reg, &ctrl); | ||
| 739 | dist = get_distance_from_watchpoint(addr, val, &ctrl); | ||
| 740 | if (dist < min_dist) { | ||
| 741 | min_dist = dist; | ||
| 742 | closest_match = i; | ||
| 743 | } | ||
| 744 | /* Is this an exact match? */ | ||
| 745 | if (dist != 0) | ||
| 746 | continue; | ||
| 747 | |||
| 748 | info = counter_arch_bp(wp); | ||
| 716 | info->trigger = addr; | 749 | info->trigger = addr; |
| 717 | perf_bp_event(wp, regs); | 750 | perf_bp_event(wp, regs); |
| 718 | 751 | ||
| 719 | /* Do we need to handle the stepping? */ | 752 | /* Do we need to handle the stepping? */ |
| 720 | if (is_default_overflow_handler(wp)) | 753 | if (is_default_overflow_handler(wp)) |
| 721 | step = 1; | 754 | step = 1; |
| 755 | } | ||
| 756 | if (min_dist > 0 && min_dist != -1) { | ||
| 757 | /* No exact match found. */ | ||
| 758 | wp = slots[closest_match]; | ||
| 759 | info = counter_arch_bp(wp); | ||
| 760 | info->trigger = addr; | ||
| 761 | perf_bp_event(wp, regs); | ||
| 722 | 762 | ||
| 723 | unlock: | 763 | /* Do we need to handle the stepping? */ |
| 724 | rcu_read_unlock(); | 764 | if (is_default_overflow_handler(wp)) |
| 765 | step = 1; | ||
| 725 | } | 766 | } |
| 767 | rcu_read_unlock(); | ||
| 726 | 768 | ||
| 727 | if (!step) | 769 | if (!step) |
| 728 | return 0; | 770 | return 0; |
