aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm64/kernel/hw_breakpoint.c96
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}
665NOKPROBE_SYMBOL(breakpoint_handler); 665NOKPROBE_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 */
682static 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
667static int watchpoint_handler(unsigned long addr, unsigned int esr, 701static 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 = &current->thread.debug; 714 debug_info = &current->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
723unlock: 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;