aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2010-11-29 11:56:01 -0500
committerWill Deacon <will.deacon@arm.com>2010-12-06 06:55:57 -0500
commit93a04a3416da12647c47840ebe2bb812fcb801d0 (patch)
treebee210af11e5a3c4d7ff33f50916819b2190ea81 /arch/arm
parent0017ff42ac37ff6aeb87d0b006c5d32b9a39f5fc (diff)
ARM: hw_breakpoint: do not allocate new breakpoints with preemption disabled
The watchpoint single-stepping code calls register_user_hw_breakpoint to register a mismatch breakpoint for stepping over the watchpoint. This is performed with preemption disabled, which is unsafe as we may end up scheduling whilst in_atomic(). Furthermore, using the perf API is rather overkill since we are already in the hw-breakpoint backend and only require access to reserved breakpoints anyway. This patch reworks the watchpoint stepping code so that we don't require another perf_event for the mismatch breakpoint. Instead, we hold a separate arch_hw_breakpoint_ctrl struct inside the watchpoint which is used exclusively for stepping. We can check whether or not stepping is enabled when installing or uninstalling the watchpoint and operate on the breakpoint accordingly. Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/include/asm/hw_breakpoint.h2
-rw-r--r--arch/arm/kernel/hw_breakpoint.c134
2 files changed, 78 insertions, 58 deletions
diff --git a/arch/arm/include/asm/hw_breakpoint.h b/arch/arm/include/asm/hw_breakpoint.h
index 4d8ae9d67abe..881429d0b849 100644
--- a/arch/arm/include/asm/hw_breakpoint.h
+++ b/arch/arm/include/asm/hw_breakpoint.h
@@ -20,7 +20,7 @@ struct arch_hw_breakpoint_ctrl {
20struct arch_hw_breakpoint { 20struct arch_hw_breakpoint {
21 u32 address; 21 u32 address;
22 u32 trigger; 22 u32 trigger;
23 struct perf_event *suspended_wp; 23 struct arch_hw_breakpoint_ctrl step_ctrl;
24 struct arch_hw_breakpoint_ctrl ctrl; 24 struct arch_hw_breakpoint_ctrl ctrl;
25}; 25};
26 26
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/*