@@ -683,6 +683,40 @@ static void disable_single_step(struct perf_event *bp)
683683 arch_install_hw_breakpoint (bp );
684684}
685685
686+ /*
687+ * Arm32 hardware does not always report a watchpoint hit address that matches
688+ * one of the watchpoints set. It can also report an address "near" the
689+ * watchpoint if a single instruction access both watched and unwatched
690+ * addresses. There is no straight-forward way, short of disassembling the
691+ * offending instruction, to map that address back to the watchpoint. This
692+ * function computes the distance of the memory access from the watchpoint as a
693+ * heuristic for the likelyhood that a given access triggered the watchpoint.
694+ *
695+ * See this same function in the arm64 platform code, which has the same
696+ * problem.
697+ *
698+ * The function returns the distance of the address from the bytes watched by
699+ * the watchpoint. In case of an exact match, it returns 0.
700+ */
701+ static u32 get_distance_from_watchpoint (unsigned long addr , u32 val ,
702+ struct arch_hw_breakpoint_ctrl * ctrl )
703+ {
704+ u32 wp_low , wp_high ;
705+ u32 lens , lene ;
706+
707+ lens = __ffs (ctrl -> len );
708+ lene = __fls (ctrl -> len );
709+
710+ wp_low = val + lens ;
711+ wp_high = val + lene ;
712+ if (addr < wp_low )
713+ return wp_low - addr ;
714+ else if (addr > wp_high )
715+ return addr - wp_high ;
716+ else
717+ return 0 ;
718+ }
719+
686720static int watchpoint_fault_on_uaccess (struct pt_regs * regs ,
687721 struct arch_hw_breakpoint * info )
688722{
@@ -692,23 +726,25 @@ static int watchpoint_fault_on_uaccess(struct pt_regs *regs,
692726static void watchpoint_handler (unsigned long addr , unsigned int fsr ,
693727 struct pt_regs * regs )
694728{
695- int i , access ;
696- u32 val , ctrl_reg , alignment_mask ;
729+ int i , access , closest_match = 0 ;
730+ u32 min_dist = -1 , dist ;
731+ u32 val , ctrl_reg ;
697732 struct perf_event * wp , * * slots ;
698733 struct arch_hw_breakpoint * info ;
699734 struct arch_hw_breakpoint_ctrl ctrl ;
700735
701736 slots = this_cpu_ptr (wp_on_reg );
702737
738+ /*
739+ * Find all watchpoints that match the reported address. If no exact
740+ * match is found. Attribute the hit to the closest watchpoint.
741+ */
742+ rcu_read_lock ();
703743 for (i = 0 ; i < core_num_wrps ; ++ i ) {
704- rcu_read_lock ();
705-
706744 wp = slots [i ];
707-
708745 if (wp == NULL )
709- goto unlock ;
746+ continue ;
710747
711- info = counter_arch_bp (wp );
712748 /*
713749 * The DFAR is an unknown value on debug architectures prior
714750 * to 7.1. Since we only allow a single watchpoint on these
@@ -717,33 +753,31 @@ static void watchpoint_handler(unsigned long addr, unsigned int fsr,
717753 */
718754 if (debug_arch < ARM_DEBUG_ARCH_V7_1 ) {
719755 BUG_ON (i > 0 );
756+ info = counter_arch_bp (wp );
720757 info -> trigger = wp -> attr .bp_addr ;
721758 } else {
722- if (info -> ctrl .len == ARM_BREAKPOINT_LEN_8 )
723- alignment_mask = 0x7 ;
724- else
725- alignment_mask = 0x3 ;
726-
727- /* Check if the watchpoint value matches. */
728- val = read_wb_reg (ARM_BASE_WVR + i );
729- if (val != (addr & ~alignment_mask ))
730- goto unlock ;
731-
732- /* Possible match, check the byte address select. */
733- ctrl_reg = read_wb_reg (ARM_BASE_WCR + i );
734- decode_ctrl_reg (ctrl_reg , & ctrl );
735- if (!((1 << (addr & alignment_mask )) & ctrl .len ))
736- goto unlock ;
737-
738759 /* Check that the access type matches. */
739760 if (debug_exception_updates_fsr ()) {
740761 access = (fsr & ARM_FSR_ACCESS_MASK ) ?
741762 HW_BREAKPOINT_W : HW_BREAKPOINT_R ;
742763 if (!(access & hw_breakpoint_type (wp )))
743- goto unlock ;
764+ continue ;
744765 }
745766
767+ val = read_wb_reg (ARM_BASE_WVR + i );
768+ ctrl_reg = read_wb_reg (ARM_BASE_WCR + i );
769+ decode_ctrl_reg (ctrl_reg , & ctrl );
770+ dist = get_distance_from_watchpoint (addr , val , & ctrl );
771+ if (dist < min_dist ) {
772+ min_dist = dist ;
773+ closest_match = i ;
774+ }
775+ /* Is this an exact match? */
776+ if (dist != 0 )
777+ continue ;
778+
746779 /* We have a winner. */
780+ info = counter_arch_bp (wp );
747781 info -> trigger = addr ;
748782 }
749783
@@ -765,13 +799,23 @@ static void watchpoint_handler(unsigned long addr, unsigned int fsr,
765799 * we can single-step over the watchpoint trigger.
766800 */
767801 if (!is_default_overflow_handler (wp ))
768- goto unlock ;
769-
802+ continue ;
770803step :
771804 enable_single_step (wp , instruction_pointer (regs ));
772- unlock :
773- rcu_read_unlock ();
774805 }
806+
807+ if (min_dist > 0 && min_dist != -1 ) {
808+ /* No exact match found. */
809+ wp = slots [closest_match ];
810+ info = counter_arch_bp (wp );
811+ info -> trigger = addr ;
812+ pr_debug ("watchpoint fired: address = 0x%x\n" , info -> trigger );
813+ perf_bp_event (wp , regs );
814+ if (is_default_overflow_handler (wp ))
815+ enable_single_step (wp , instruction_pointer (regs ));
816+ }
817+
818+ rcu_read_unlock ();
775819}
776820
777821static void watchpoint_single_step_handler (unsigned long pc )
0 commit comments