diff options
Diffstat (limited to 'arch/arm64/kernel/hw_breakpoint.c')
-rw-r--r-- | arch/arm64/kernel/hw_breakpoint.c | 154 |
1 files changed, 116 insertions, 38 deletions
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index 367a954f9937..1c694f3c643c 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <asm/traps.h> | 36 | #include <asm/traps.h> |
37 | #include <asm/cputype.h> | 37 | #include <asm/cputype.h> |
38 | #include <asm/system_misc.h> | 38 | #include <asm/system_misc.h> |
39 | #include <asm/uaccess.h> | ||
39 | 40 | ||
40 | /* Breakpoint currently in use for each BRP. */ | 41 | /* Breakpoint currently in use for each BRP. */ |
41 | static DEFINE_PER_CPU(struct perf_event *, bp_on_reg[ARM_MAX_BRP]); | 42 | static DEFINE_PER_CPU(struct perf_event *, bp_on_reg[ARM_MAX_BRP]); |
@@ -317,9 +318,21 @@ static int get_hbp_len(u8 hbp_len) | |||
317 | case ARM_BREAKPOINT_LEN_2: | 318 | case ARM_BREAKPOINT_LEN_2: |
318 | len_in_bytes = 2; | 319 | len_in_bytes = 2; |
319 | break; | 320 | break; |
321 | case ARM_BREAKPOINT_LEN_3: | ||
322 | len_in_bytes = 3; | ||
323 | break; | ||
320 | case ARM_BREAKPOINT_LEN_4: | 324 | case ARM_BREAKPOINT_LEN_4: |
321 | len_in_bytes = 4; | 325 | len_in_bytes = 4; |
322 | break; | 326 | break; |
327 | case ARM_BREAKPOINT_LEN_5: | ||
328 | len_in_bytes = 5; | ||
329 | break; | ||
330 | case ARM_BREAKPOINT_LEN_6: | ||
331 | len_in_bytes = 6; | ||
332 | break; | ||
333 | case ARM_BREAKPOINT_LEN_7: | ||
334 | len_in_bytes = 7; | ||
335 | break; | ||
323 | case ARM_BREAKPOINT_LEN_8: | 336 | case ARM_BREAKPOINT_LEN_8: |
324 | len_in_bytes = 8; | 337 | len_in_bytes = 8; |
325 | break; | 338 | break; |
@@ -349,7 +362,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp) | |||
349 | * to generic breakpoint descriptions. | 362 | * to generic breakpoint descriptions. |
350 | */ | 363 | */ |
351 | int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, | 364 | int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, |
352 | int *gen_len, int *gen_type) | 365 | int *gen_len, int *gen_type, int *offset) |
353 | { | 366 | { |
354 | /* Type */ | 367 | /* Type */ |
355 | switch (ctrl.type) { | 368 | switch (ctrl.type) { |
@@ -369,17 +382,33 @@ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, | |||
369 | return -EINVAL; | 382 | return -EINVAL; |
370 | } | 383 | } |
371 | 384 | ||
385 | if (!ctrl.len) | ||
386 | return -EINVAL; | ||
387 | *offset = __ffs(ctrl.len); | ||
388 | |||
372 | /* Len */ | 389 | /* Len */ |
373 | switch (ctrl.len) { | 390 | switch (ctrl.len >> *offset) { |
374 | case ARM_BREAKPOINT_LEN_1: | 391 | case ARM_BREAKPOINT_LEN_1: |
375 | *gen_len = HW_BREAKPOINT_LEN_1; | 392 | *gen_len = HW_BREAKPOINT_LEN_1; |
376 | break; | 393 | break; |
377 | case ARM_BREAKPOINT_LEN_2: | 394 | case ARM_BREAKPOINT_LEN_2: |
378 | *gen_len = HW_BREAKPOINT_LEN_2; | 395 | *gen_len = HW_BREAKPOINT_LEN_2; |
379 | break; | 396 | break; |
397 | case ARM_BREAKPOINT_LEN_3: | ||
398 | *gen_len = HW_BREAKPOINT_LEN_3; | ||
399 | break; | ||
380 | case ARM_BREAKPOINT_LEN_4: | 400 | case ARM_BREAKPOINT_LEN_4: |
381 | *gen_len = HW_BREAKPOINT_LEN_4; | 401 | *gen_len = HW_BREAKPOINT_LEN_4; |
382 | break; | 402 | break; |
403 | case ARM_BREAKPOINT_LEN_5: | ||
404 | *gen_len = HW_BREAKPOINT_LEN_5; | ||
405 | break; | ||
406 | case ARM_BREAKPOINT_LEN_6: | ||
407 | *gen_len = HW_BREAKPOINT_LEN_6; | ||
408 | break; | ||
409 | case ARM_BREAKPOINT_LEN_7: | ||
410 | *gen_len = HW_BREAKPOINT_LEN_7; | ||
411 | break; | ||
383 | case ARM_BREAKPOINT_LEN_8: | 412 | case ARM_BREAKPOINT_LEN_8: |
384 | *gen_len = HW_BREAKPOINT_LEN_8; | 413 | *gen_len = HW_BREAKPOINT_LEN_8; |
385 | break; | 414 | break; |
@@ -423,9 +452,21 @@ static int arch_build_bp_info(struct perf_event *bp) | |||
423 | case HW_BREAKPOINT_LEN_2: | 452 | case HW_BREAKPOINT_LEN_2: |
424 | info->ctrl.len = ARM_BREAKPOINT_LEN_2; | 453 | info->ctrl.len = ARM_BREAKPOINT_LEN_2; |
425 | break; | 454 | break; |
455 | case HW_BREAKPOINT_LEN_3: | ||
456 | info->ctrl.len = ARM_BREAKPOINT_LEN_3; | ||
457 | break; | ||
426 | case HW_BREAKPOINT_LEN_4: | 458 | case HW_BREAKPOINT_LEN_4: |
427 | info->ctrl.len = ARM_BREAKPOINT_LEN_4; | 459 | info->ctrl.len = ARM_BREAKPOINT_LEN_4; |
428 | break; | 460 | break; |
461 | case HW_BREAKPOINT_LEN_5: | ||
462 | info->ctrl.len = ARM_BREAKPOINT_LEN_5; | ||
463 | break; | ||
464 | case HW_BREAKPOINT_LEN_6: | ||
465 | info->ctrl.len = ARM_BREAKPOINT_LEN_6; | ||
466 | break; | ||
467 | case HW_BREAKPOINT_LEN_7: | ||
468 | info->ctrl.len = ARM_BREAKPOINT_LEN_7; | ||
469 | break; | ||
429 | case HW_BREAKPOINT_LEN_8: | 470 | case HW_BREAKPOINT_LEN_8: |
430 | info->ctrl.len = ARM_BREAKPOINT_LEN_8; | 471 | info->ctrl.len = ARM_BREAKPOINT_LEN_8; |
431 | break; | 472 | break; |
@@ -517,18 +558,17 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) | |||
517 | default: | 558 | default: |
518 | return -EINVAL; | 559 | return -EINVAL; |
519 | } | 560 | } |
520 | |||
521 | info->address &= ~alignment_mask; | ||
522 | info->ctrl.len <<= offset; | ||
523 | } else { | 561 | } else { |
524 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) | 562 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) |
525 | alignment_mask = 0x3; | 563 | alignment_mask = 0x3; |
526 | else | 564 | else |
527 | alignment_mask = 0x7; | 565 | alignment_mask = 0x7; |
528 | if (info->address & alignment_mask) | 566 | offset = info->address & alignment_mask; |
529 | return -EINVAL; | ||
530 | } | 567 | } |
531 | 568 | ||
569 | info->address &= ~alignment_mask; | ||
570 | info->ctrl.len <<= offset; | ||
571 | |||
532 | /* | 572 | /* |
533 | * Disallow per-task kernel breakpoints since these would | 573 | * Disallow per-task kernel breakpoints since these would |
534 | * complicate the stepping code. | 574 | * complicate the stepping code. |
@@ -661,12 +701,47 @@ unlock: | |||
661 | } | 701 | } |
662 | NOKPROBE_SYMBOL(breakpoint_handler); | 702 | NOKPROBE_SYMBOL(breakpoint_handler); |
663 | 703 | ||
704 | /* | ||
705 | * Arm64 hardware does not always report a watchpoint hit address that matches | ||
706 | * one of the watchpoints set. It can also report an address "near" the | ||
707 | * watchpoint if a single instruction access both watched and unwatched | ||
708 | * addresses. There is no straight-forward way, short of disassembling the | ||
709 | * offending instruction, to map that address back to the watchpoint. This | ||
710 | * function computes the distance of the memory access from the watchpoint as a | ||
711 | * heuristic for the likelyhood that a given access triggered the watchpoint. | ||
712 | * | ||
713 | * See Section D2.10.5 "Determining the memory location that caused a Watchpoint | ||
714 | * exception" of ARMv8 Architecture Reference Manual for details. | ||
715 | * | ||
716 | * The function returns the distance of the address from the bytes watched by | ||
717 | * the watchpoint. In case of an exact match, it returns 0. | ||
718 | */ | ||
719 | static u64 get_distance_from_watchpoint(unsigned long addr, u64 val, | ||
720 | struct arch_hw_breakpoint_ctrl *ctrl) | ||
721 | { | ||
722 | u64 wp_low, wp_high; | ||
723 | u32 lens, lene; | ||
724 | |||
725 | lens = __ffs(ctrl->len); | ||
726 | lene = __fls(ctrl->len); | ||
727 | |||
728 | wp_low = val + lens; | ||
729 | wp_high = val + lene; | ||
730 | if (addr < wp_low) | ||
731 | return wp_low - addr; | ||
732 | else if (addr > wp_high) | ||
733 | return addr - wp_high; | ||
734 | else | ||
735 | return 0; | ||
736 | } | ||
737 | |||
664 | static int watchpoint_handler(unsigned long addr, unsigned int esr, | 738 | static int watchpoint_handler(unsigned long addr, unsigned int esr, |
665 | struct pt_regs *regs) | 739 | struct pt_regs *regs) |
666 | { | 740 | { |
667 | int i, step = 0, *kernel_step, access; | 741 | int i, step = 0, *kernel_step, access, closest_match = 0; |
742 | u64 min_dist = -1, dist; | ||
668 | u32 ctrl_reg; | 743 | u32 ctrl_reg; |
669 | u64 val, alignment_mask; | 744 | u64 val; |
670 | struct perf_event *wp, **slots; | 745 | struct perf_event *wp, **slots; |
671 | struct debug_info *debug_info; | 746 | struct debug_info *debug_info; |
672 | struct arch_hw_breakpoint *info; | 747 | struct arch_hw_breakpoint *info; |
@@ -675,35 +750,15 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr, | |||
675 | slots = this_cpu_ptr(wp_on_reg); | 750 | slots = this_cpu_ptr(wp_on_reg); |
676 | debug_info = ¤t->thread.debug; | 751 | debug_info = ¤t->thread.debug; |
677 | 752 | ||
753 | /* | ||
754 | * Find all watchpoints that match the reported address. If no exact | ||
755 | * match is found. Attribute the hit to the closest watchpoint. | ||
756 | */ | ||
757 | rcu_read_lock(); | ||
678 | for (i = 0; i < core_num_wrps; ++i) { | 758 | for (i = 0; i < core_num_wrps; ++i) { |
679 | rcu_read_lock(); | ||
680 | |||
681 | wp = slots[i]; | 759 | wp = slots[i]; |
682 | |||
683 | if (wp == NULL) | 760 | if (wp == NULL) |
684 | goto unlock; | 761 | continue; |
685 | |||
686 | info = counter_arch_bp(wp); | ||
687 | /* AArch32 watchpoints are either 4 or 8 bytes aligned. */ | ||
688 | if (is_compat_task()) { | ||
689 | if (info->ctrl.len == ARM_BREAKPOINT_LEN_8) | ||
690 | alignment_mask = 0x7; | ||
691 | else | ||
692 | alignment_mask = 0x3; | ||
693 | } else { | ||
694 | alignment_mask = 0x7; | ||
695 | } | ||
696 | |||
697 | /* Check if the watchpoint value matches. */ | ||
698 | val = read_wb_reg(AARCH64_DBG_REG_WVR, i); | ||
699 | if (val != (addr & ~alignment_mask)) | ||
700 | goto unlock; | ||
701 | |||
702 | /* Possible match, check the byte address select to confirm. */ | ||
703 | ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i); | ||
704 | decode_ctrl_reg(ctrl_reg, &ctrl); | ||
705 | if (!((1 << (addr & alignment_mask)) & ctrl.len)) | ||
706 | goto unlock; | ||
707 | 762 | ||
708 | /* | 763 | /* |
709 | * Check that the access type matches. | 764 | * Check that the access type matches. |
@@ -712,18 +767,41 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr, | |||
712 | access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W : | 767 | access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W : |
713 | HW_BREAKPOINT_R; | 768 | HW_BREAKPOINT_R; |
714 | if (!(access & hw_breakpoint_type(wp))) | 769 | if (!(access & hw_breakpoint_type(wp))) |
715 | goto unlock; | 770 | continue; |
716 | 771 | ||
772 | /* Check if the watchpoint value and byte select match. */ | ||
773 | val = read_wb_reg(AARCH64_DBG_REG_WVR, i); | ||
774 | ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i); | ||
775 | decode_ctrl_reg(ctrl_reg, &ctrl); | ||
776 | dist = get_distance_from_watchpoint(addr, val, &ctrl); | ||
777 | if (dist < min_dist) { | ||
778 | min_dist = dist; | ||
779 | closest_match = i; | ||
780 | } | ||
781 | /* Is this an exact match? */ | ||
782 | if (dist != 0) | ||
783 | continue; | ||
784 | |||
785 | info = counter_arch_bp(wp); | ||
717 | info->trigger = addr; | 786 | info->trigger = addr; |
718 | perf_bp_event(wp, regs); | 787 | perf_bp_event(wp, regs); |
719 | 788 | ||
720 | /* Do we need to handle the stepping? */ | 789 | /* Do we need to handle the stepping? */ |
721 | if (!wp->overflow_handler) | 790 | if (!wp->overflow_handler) |
722 | step = 1; | 791 | step = 1; |
792 | } | ||
793 | if (min_dist > 0 && min_dist != -1) { | ||
794 | /* No exact match found. */ | ||
795 | wp = slots[closest_match]; | ||
796 | info = counter_arch_bp(wp); | ||
797 | info->trigger = addr; | ||
798 | perf_bp_event(wp, regs); | ||
723 | 799 | ||
724 | unlock: | 800 | /* Do we need to handle the stepping? */ |
725 | rcu_read_unlock(); | 801 | if (!wp->overflow_handler) |
802 | step = 1; | ||
726 | } | 803 | } |
804 | rcu_read_unlock(); | ||
727 | 805 | ||
728 | if (!step) | 806 | if (!step) |
729 | return 0; | 807 | return 0; |