diff options
Diffstat (limited to 'arch/arm/kernel/hw_breakpoint.c')
-rw-r--r-- | arch/arm/kernel/hw_breakpoint.c | 134 |
1 files changed, 77 insertions, 57 deletions
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index b16c4568cb01..81b63e94dfe0 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c | |||
@@ -316,23 +316,6 @@ u8 arch_get_max_wp_len(void) | |||
316 | } | 316 | } |
317 | 317 | ||
318 | /* | 318 | /* |
319 | * Handler for reactivating a suspended watchpoint when the single | ||
320 | * step `mismatch' breakpoint is triggered. | ||
321 | */ | ||
322 | static void wp_single_step_handler(struct perf_event *bp, int unused, | ||
323 | struct perf_sample_data *data, | ||
324 | struct pt_regs *regs) | ||
325 | { | ||
326 | perf_event_enable(counter_arch_bp(bp)->suspended_wp); | ||
327 | unregister_hw_breakpoint(bp); | ||
328 | } | ||
329 | |||
330 | static int bp_is_single_step(struct perf_event *bp) | ||
331 | { | ||
332 | return bp->overflow_handler == wp_single_step_handler; | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * Install a perf counter breakpoint. | 319 | * Install a perf counter breakpoint. |
337 | */ | 320 | */ |
338 | int arch_install_hw_breakpoint(struct perf_event *bp) | 321 | int arch_install_hw_breakpoint(struct perf_event *bp) |
@@ -340,29 +323,35 @@ int arch_install_hw_breakpoint(struct perf_event *bp) | |||
340 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | 323 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); |
341 | struct perf_event **slot, **slots; | 324 | struct perf_event **slot, **slots; |
342 | int i, max_slots, ctrl_base, val_base, ret = 0; | 325 | int i, max_slots, ctrl_base, val_base, ret = 0; |
326 | u32 addr, ctrl; | ||
343 | 327 | ||
344 | /* Ensure that we are in monitor mode and halting mode is disabled. */ | 328 | /* Ensure that we are in monitor mode and halting mode is disabled. */ |
345 | ret = enable_monitor_mode(); | 329 | ret = enable_monitor_mode(); |
346 | if (ret) | 330 | if (ret) |
347 | goto out; | 331 | goto out; |
348 | 332 | ||
333 | addr = info->address; | ||
334 | ctrl = encode_ctrl_reg(info->ctrl) | 0x1; | ||
335 | |||
349 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { | 336 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { |
350 | /* Breakpoint */ | 337 | /* Breakpoint */ |
351 | ctrl_base = ARM_BASE_BCR; | 338 | ctrl_base = ARM_BASE_BCR; |
352 | val_base = ARM_BASE_BVR; | 339 | val_base = ARM_BASE_BVR; |
353 | slots = __get_cpu_var(bp_on_reg); | 340 | slots = __get_cpu_var(bp_on_reg); |
354 | max_slots = core_num_brps; | 341 | max_slots = core_num_brps; |
355 | |||
356 | if (bp_is_single_step(bp)) { | ||
357 | info->ctrl.mismatch = 1; | ||
358 | i = max_slots; | ||
359 | slots[i] = bp; | ||
360 | goto setup; | ||
361 | } | ||
362 | } else { | 342 | } else { |
363 | /* Watchpoint */ | 343 | /* Watchpoint */ |
364 | ctrl_base = ARM_BASE_WCR; | 344 | if (info->step_ctrl.enabled) { |
365 | val_base = ARM_BASE_WVR; | 345 | /* Install into the reserved breakpoint region. */ |
346 | ctrl_base = ARM_BASE_BCR + core_num_brps; | ||
347 | val_base = ARM_BASE_BVR + core_num_brps; | ||
348 | /* Override the watchpoint data with the step data. */ | ||
349 | addr = info->trigger & ~0x3; | ||
350 | ctrl = encode_ctrl_reg(info->step_ctrl); | ||
351 | } else { | ||
352 | ctrl_base = ARM_BASE_WCR; | ||
353 | val_base = ARM_BASE_WVR; | ||
354 | } | ||
366 | slots = __get_cpu_var(wp_on_reg); | 355 | slots = __get_cpu_var(wp_on_reg); |
367 | max_slots = core_num_wrps; | 356 | max_slots = core_num_wrps; |
368 | } | 357 | } |
@@ -381,12 +370,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp) | |||
381 | goto out; | 370 | goto out; |
382 | } | 371 | } |
383 | 372 | ||
384 | setup: | ||
385 | /* Setup the address register. */ | 373 | /* Setup the address register. */ |
386 | write_wb_reg(val_base + i, info->address); | 374 | write_wb_reg(val_base + i, addr); |
387 | 375 | ||
388 | /* Setup the control register. */ | 376 | /* Setup the control register. */ |
389 | write_wb_reg(ctrl_base + i, encode_ctrl_reg(info->ctrl) | 0x1); | 377 | write_wb_reg(ctrl_base + i, ctrl); |
390 | 378 | ||
391 | out: | 379 | out: |
392 | return ret; | 380 | return ret; |
@@ -403,15 +391,12 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) | |||
403 | base = ARM_BASE_BCR; | 391 | base = ARM_BASE_BCR; |
404 | slots = __get_cpu_var(bp_on_reg); | 392 | slots = __get_cpu_var(bp_on_reg); |
405 | max_slots = core_num_brps; | 393 | max_slots = core_num_brps; |
406 | |||
407 | if (bp_is_single_step(bp)) { | ||
408 | i = max_slots; | ||
409 | slots[i] = NULL; | ||
410 | goto reset; | ||
411 | } | ||
412 | } else { | 394 | } else { |
413 | /* Watchpoint */ | 395 | /* Watchpoint */ |
414 | base = ARM_BASE_WCR; | 396 | if (info->step_ctrl.enabled) |
397 | base = ARM_BASE_BCR + core_num_brps; | ||
398 | else | ||
399 | base = ARM_BASE_WCR; | ||
415 | slots = __get_cpu_var(wp_on_reg); | 400 | slots = __get_cpu_var(wp_on_reg); |
416 | max_slots = core_num_wrps; | 401 | max_slots = core_num_wrps; |
417 | } | 402 | } |
@@ -429,7 +414,6 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) | |||
429 | if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) | 414 | if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) |
430 | return; | 415 | return; |
431 | 416 | ||
432 | reset: | ||
433 | /* Reset the control register. */ | 417 | /* Reset the control register. */ |
434 | write_wb_reg(base + i, 0); | 418 | write_wb_reg(base + i, 0); |
435 | } | 419 | } |
@@ -579,7 +563,7 @@ static int arch_build_bp_info(struct perf_event *bp) | |||
579 | 563 | ||
580 | /* Privilege */ | 564 | /* Privilege */ |
581 | info->ctrl.privilege = ARM_BREAKPOINT_USER; | 565 | info->ctrl.privilege = ARM_BREAKPOINT_USER; |
582 | if (arch_check_bp_in_kernelspace(bp) && !bp_is_single_step(bp)) | 566 | if (arch_check_bp_in_kernelspace(bp)) |
583 | info->ctrl.privilege |= ARM_BREAKPOINT_PRIV; | 567 | info->ctrl.privilege |= ARM_BREAKPOINT_PRIV; |
584 | 568 | ||
585 | /* Enabled? */ | 569 | /* Enabled? */ |
@@ -664,22 +648,18 @@ static void update_mismatch_flag(int idx, int flag) | |||
664 | static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs) | 648 | static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs) |
665 | { | 649 | { |
666 | int i; | 650 | int i; |
667 | struct perf_event *bp, **slots = __get_cpu_var(wp_on_reg); | 651 | struct perf_event *wp, **slots = __get_cpu_var(wp_on_reg); |
668 | struct arch_hw_breakpoint *info; | 652 | struct arch_hw_breakpoint *info; |
669 | struct perf_event_attr attr; | ||
670 | 653 | ||
671 | /* Without a disassembler, we can only handle 1 watchpoint. */ | 654 | /* Without a disassembler, we can only handle 1 watchpoint. */ |
672 | BUG_ON(core_num_wrps > 1); | 655 | BUG_ON(core_num_wrps > 1); |
673 | 656 | ||
674 | hw_breakpoint_init(&attr); | ||
675 | attr.bp_addr = regs->ARM_pc & ~0x3; | ||
676 | attr.bp_len = HW_BREAKPOINT_LEN_4; | ||
677 | attr.bp_type = HW_BREAKPOINT_X; | ||
678 | |||
679 | for (i = 0; i < core_num_wrps; ++i) { | 657 | for (i = 0; i < core_num_wrps; ++i) { |
680 | rcu_read_lock(); | 658 | rcu_read_lock(); |
681 | 659 | ||
682 | if (slots[i] == NULL) { | 660 | wp = slots[i]; |
661 | |||
662 | if (wp == NULL) { | ||
683 | rcu_read_unlock(); | 663 | rcu_read_unlock(); |
684 | continue; | 664 | continue; |
685 | } | 665 | } |
@@ -689,24 +669,60 @@ static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs) | |||
689 | * single watchpoint, we can set the trigger to the lowest | 669 | * single watchpoint, we can set the trigger to the lowest |
690 | * possible faulting address. | 670 | * possible faulting address. |
691 | */ | 671 | */ |
692 | info = counter_arch_bp(slots[i]); | 672 | info = counter_arch_bp(wp); |
693 | info->trigger = slots[i]->attr.bp_addr; | 673 | info->trigger = wp->attr.bp_addr; |
694 | pr_debug("watchpoint fired: address = 0x%x\n", info->trigger); | 674 | pr_debug("watchpoint fired: address = 0x%x\n", info->trigger); |
695 | perf_bp_event(slots[i], regs); | 675 | perf_bp_event(wp, regs); |
696 | 676 | ||
697 | /* | 677 | /* |
698 | * If no overflow handler is present, insert a temporary | 678 | * If no overflow handler is present, insert a temporary |
699 | * mismatch breakpoint so we can single-step over the | 679 | * mismatch breakpoint so we can single-step over the |
700 | * watchpoint trigger. | 680 | * watchpoint trigger. |
701 | */ | 681 | */ |
702 | if (!slots[i]->overflow_handler) { | 682 | if (!wp->overflow_handler) { |
703 | bp = register_user_hw_breakpoint(&attr, | 683 | arch_uninstall_hw_breakpoint(wp); |
704 | wp_single_step_handler, | 684 | info->step_ctrl.mismatch = 1; |
705 | current); | 685 | info->step_ctrl.len = ARM_BREAKPOINT_LEN_4; |
706 | counter_arch_bp(bp)->suspended_wp = slots[i]; | 686 | info->step_ctrl.type = ARM_BREAKPOINT_EXECUTE; |
707 | perf_event_disable(slots[i]); | 687 | info->step_ctrl.privilege = info->ctrl.privilege; |
688 | info->step_ctrl.enabled = 1; | ||
689 | info->trigger = regs->ARM_pc; | ||
690 | arch_install_hw_breakpoint(wp); | ||
691 | } | ||
692 | |||
693 | rcu_read_unlock(); | ||
694 | } | ||
695 | } | ||
696 | |||
697 | static void watchpoint_single_step_handler(unsigned long pc) | ||
698 | { | ||
699 | int i; | ||
700 | struct perf_event *wp, **slots = __get_cpu_var(wp_on_reg); | ||
701 | struct arch_hw_breakpoint *info; | ||
702 | |||
703 | for (i = 0; i < core_num_reserved_brps; ++i) { | ||
704 | rcu_read_lock(); | ||
705 | |||
706 | wp = slots[i]; | ||
707 | |||
708 | if (wp == NULL) | ||
709 | goto unlock; | ||
710 | |||
711 | info = counter_arch_bp(wp); | ||
712 | if (!info->step_ctrl.enabled) | ||
713 | goto unlock; | ||
714 | |||
715 | /* | ||
716 | * Restore the original watchpoint if we've completed the | ||
717 | * single-step. | ||
718 | */ | ||
719 | if (info->trigger != pc) { | ||
720 | arch_uninstall_hw_breakpoint(wp); | ||
721 | info->step_ctrl.enabled = 0; | ||
722 | arch_install_hw_breakpoint(wp); | ||
708 | } | 723 | } |
709 | 724 | ||
725 | unlock: | ||
710 | rcu_read_unlock(); | 726 | rcu_read_unlock(); |
711 | } | 727 | } |
712 | } | 728 | } |
@@ -723,7 +739,8 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs) | |||
723 | /* The exception entry code places the amended lr in the PC. */ | 739 | /* The exception entry code places the amended lr in the PC. */ |
724 | addr = regs->ARM_pc; | 740 | addr = regs->ARM_pc; |
725 | 741 | ||
726 | for (i = 0; i < core_num_brps + core_num_reserved_brps; ++i) { | 742 | /* Check the currently installed breakpoints first. */ |
743 | for (i = 0; i < core_num_brps; ++i) { | ||
727 | rcu_read_lock(); | 744 | rcu_read_lock(); |
728 | 745 | ||
729 | bp = slots[i]; | 746 | bp = slots[i]; |
@@ -750,7 +767,7 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs) | |||
750 | } | 767 | } |
751 | 768 | ||
752 | unlock: | 769 | unlock: |
753 | if ((mismatch && !info->ctrl.mismatch) || bp_is_single_step(bp)) { | 770 | if (mismatch && !info->ctrl.mismatch) { |
754 | pr_debug("breakpoint fired: address = 0x%x\n", addr); | 771 | pr_debug("breakpoint fired: address = 0x%x\n", addr); |
755 | perf_bp_event(bp, regs); | 772 | perf_bp_event(bp, regs); |
756 | } | 773 | } |
@@ -758,6 +775,9 @@ unlock: | |||
758 | update_mismatch_flag(i, mismatch); | 775 | update_mismatch_flag(i, mismatch); |
759 | rcu_read_unlock(); | 776 | rcu_read_unlock(); |
760 | } | 777 | } |
778 | |||
779 | /* Handle any pending watchpoint single-step breakpoints. */ | ||
780 | watchpoint_single_step_handler(addr); | ||
761 | } | 781 | } |
762 | 782 | ||
763 | /* | 783 | /* |