diff options
author | Will Deacon <will.deacon@arm.com> | 2010-11-28 09:57:24 -0500 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2010-12-06 06:55:56 -0500 |
commit | 7e20269647169e7ea08a62bdc4979a3ba32e615c (patch) | |
tree | b66a6c5e893a34ee9659403f66f44841dfb34232 /arch | |
parent | 6ee33c2712fcdff2568d9bbadb25c8e5a7c36212 (diff) |
ARM: hw_breakpoint: disable preemption during debug exception handling
On ARM, debug exceptions occur in the form of data or prefetch aborts.
One difference is that debug exceptions require access to per-cpu banked
registers and data structures which are not saved in the low-level exception
code. For kernels built with CONFIG_PREEMPT, there is an unlikely scenario
that the debug handler ends up running on a different CPU from the one
that originally signalled the event, resulting in random data being read
from the wrong registers.
This patch adds a debug_entry macro to the low-level exception handling
code which checks whether the taken exception is a debug exception. If
it is, the preempt count for the faulting process is incremented. After
the debug handler has finished, the count is decremented.
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/kernel/entry-armv.S | 4 | ||||
-rw-r--r-- | arch/arm/kernel/entry-header.S | 19 | ||||
-rw-r--r-- | arch/arm/kernel/hw_breakpoint.c | 18 |
3 files changed, 36 insertions, 5 deletions
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index c09e3573c5de..34bbef0d2e7e 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S | |||
@@ -198,6 +198,7 @@ __dabt_svc: | |||
198 | @ | 198 | @ |
199 | @ set desired IRQ state, then call main handler | 199 | @ set desired IRQ state, then call main handler |
200 | @ | 200 | @ |
201 | debug_entry r1 | ||
201 | msr cpsr_c, r9 | 202 | msr cpsr_c, r9 |
202 | mov r2, sp | 203 | mov r2, sp |
203 | bl do_DataAbort | 204 | bl do_DataAbort |
@@ -324,6 +325,7 @@ __pabt_svc: | |||
324 | #else | 325 | #else |
325 | bl CPU_PABORT_HANDLER | 326 | bl CPU_PABORT_HANDLER |
326 | #endif | 327 | #endif |
328 | debug_entry r1 | ||
327 | msr cpsr_c, r9 @ Maybe enable interrupts | 329 | msr cpsr_c, r9 @ Maybe enable interrupts |
328 | mov r2, sp @ regs | 330 | mov r2, sp @ regs |
329 | bl do_PrefetchAbort @ call abort handler | 331 | bl do_PrefetchAbort @ call abort handler |
@@ -439,6 +441,7 @@ __dabt_usr: | |||
439 | @ | 441 | @ |
440 | @ IRQs on, then call the main handler | 442 | @ IRQs on, then call the main handler |
441 | @ | 443 | @ |
444 | debug_entry r1 | ||
442 | enable_irq | 445 | enable_irq |
443 | mov r2, sp | 446 | mov r2, sp |
444 | adr lr, BSYM(ret_from_exception) | 447 | adr lr, BSYM(ret_from_exception) |
@@ -703,6 +706,7 @@ __pabt_usr: | |||
703 | #else | 706 | #else |
704 | bl CPU_PABORT_HANDLER | 707 | bl CPU_PABORT_HANDLER |
705 | #endif | 708 | #endif |
709 | debug_entry r1 | ||
706 | enable_irq @ Enable interrupts | 710 | enable_irq @ Enable interrupts |
707 | mov r2, sp @ regs | 711 | mov r2, sp @ regs |
708 | bl do_PrefetchAbort @ call abort handler | 712 | bl do_PrefetchAbort @ call abort handler |
diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index d93f976fb389..ae9464900168 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S | |||
@@ -165,6 +165,25 @@ | |||
165 | .endm | 165 | .endm |
166 | #endif /* !CONFIG_THUMB2_KERNEL */ | 166 | #endif /* !CONFIG_THUMB2_KERNEL */ |
167 | 167 | ||
168 | @ | ||
169 | @ Debug exceptions are taken as prefetch or data aborts. | ||
170 | @ We must disable preemption during the handler so that | ||
171 | @ we can access the debug registers safely. | ||
172 | @ | ||
173 | .macro debug_entry, fsr | ||
174 | #if defined(CONFIG_HAVE_HW_BREAKPOINT) && defined(CONFIG_PREEMPT) | ||
175 | ldr r4, =0x40f @ mask out fsr.fs | ||
176 | and r5, r4, \fsr | ||
177 | cmp r5, #2 @ debug exception | ||
178 | bne 1f | ||
179 | get_thread_info r10 | ||
180 | ldr r6, [r10, #TI_PREEMPT] @ get preempt count | ||
181 | add r11, r6, #1 @ increment it | ||
182 | str r11, [r10, #TI_PREEMPT] | ||
183 | 1: | ||
184 | #endif | ||
185 | .endm | ||
186 | |||
168 | /* | 187 | /* |
169 | * These are the registers used in the syscall handler, and allow us to | 188 | * These are the registers used in the syscall handler, and allow us to |
170 | * have in theory up to 7 arguments to a function - r0 to r6. | 189 | * have in theory up to 7 arguments to a function - r0 to r6. |
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index d37ed3501e57..eeba38008032 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #define pr_fmt(fmt) "hw-breakpoint: " fmt | 24 | #define pr_fmt(fmt) "hw-breakpoint: " fmt |
25 | 25 | ||
26 | #include <linux/errno.h> | 26 | #include <linux/errno.h> |
27 | #include <linux/hardirq.h> | ||
27 | #include <linux/perf_event.h> | 28 | #include <linux/perf_event.h> |
28 | #include <linux/hw_breakpoint.h> | 29 | #include <linux/hw_breakpoint.h> |
29 | #include <linux/smp.h> | 30 | #include <linux/smp.h> |
@@ -736,14 +737,17 @@ unlock: | |||
736 | 737 | ||
737 | /* | 738 | /* |
738 | * Called from either the Data Abort Handler [watchpoint] or the | 739 | * Called from either the Data Abort Handler [watchpoint] or the |
739 | * Prefetch Abort Handler [breakpoint]. | 740 | * Prefetch Abort Handler [breakpoint] with preemption disabled. |
740 | */ | 741 | */ |
741 | static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, | 742 | static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, |
742 | struct pt_regs *regs) | 743 | struct pt_regs *regs) |
743 | { | 744 | { |
744 | int ret = 1; /* Unhandled fault. */ | 745 | int ret = 0; |
745 | u32 dscr; | 746 | u32 dscr; |
746 | 747 | ||
748 | /* We must be called with preemption disabled. */ | ||
749 | WARN_ON(preemptible()); | ||
750 | |||
747 | /* We only handle watchpoints and hardware breakpoints. */ | 751 | /* We only handle watchpoints and hardware breakpoints. */ |
748 | ARM_DBG_READ(c1, 0, dscr); | 752 | ARM_DBG_READ(c1, 0, dscr); |
749 | 753 | ||
@@ -758,11 +762,15 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, | |||
758 | watchpoint_handler(addr, regs); | 762 | watchpoint_handler(addr, regs); |
759 | break; | 763 | break; |
760 | default: | 764 | default: |
761 | goto out; | 765 | ret = 1; /* Unhandled fault. */ |
762 | } | 766 | } |
763 | 767 | ||
764 | ret = 0; | 768 | /* |
765 | out: | 769 | * Re-enable preemption after it was disabled in the |
770 | * low-level exception handling code. | ||
771 | */ | ||
772 | preempt_enable(); | ||
773 | |||
766 | return ret; | 774 | return ret; |
767 | } | 775 | } |
768 | 776 | ||