aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/hw_breakpoint.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/kernel/hw_breakpoint.c')
-rw-r--r--arch/arm/kernel/hw_breakpoint.c134
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 */
322static 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
330static 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 */
338int arch_install_hw_breakpoint(struct perf_event *bp) 321int 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
384setup:
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
391out: 379out:
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
432reset:
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)
664static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs) 648static 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
697static 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
725unlock:
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
752unlock: 769unlock:
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/*