提交 7e202696 编写于 作者: W Will Deacon

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: NCatalin Marinas <catalin.marinas@arm.com>
Signed-off-by: NWill Deacon <will.deacon@arm.com>
上级 6ee33c27
...@@ -198,6 +198,7 @@ __dabt_svc: ...@@ -198,6 +198,7 @@ __dabt_svc:
@ @
@ set desired IRQ state, then call main handler @ set desired IRQ state, then call main handler
@ @
debug_entry r1
msr cpsr_c, r9 msr cpsr_c, r9
mov r2, sp mov r2, sp
bl do_DataAbort bl do_DataAbort
...@@ -324,6 +325,7 @@ __pabt_svc: ...@@ -324,6 +325,7 @@ __pabt_svc:
#else #else
bl CPU_PABORT_HANDLER bl CPU_PABORT_HANDLER
#endif #endif
debug_entry r1
msr cpsr_c, r9 @ Maybe enable interrupts msr cpsr_c, r9 @ Maybe enable interrupts
mov r2, sp @ regs mov r2, sp @ regs
bl do_PrefetchAbort @ call abort handler bl do_PrefetchAbort @ call abort handler
...@@ -439,6 +441,7 @@ __dabt_usr: ...@@ -439,6 +441,7 @@ __dabt_usr:
@ @
@ IRQs on, then call the main handler @ IRQs on, then call the main handler
@ @
debug_entry r1
enable_irq enable_irq
mov r2, sp mov r2, sp
adr lr, BSYM(ret_from_exception) adr lr, BSYM(ret_from_exception)
...@@ -703,6 +706,7 @@ __pabt_usr: ...@@ -703,6 +706,7 @@ __pabt_usr:
#else #else
bl CPU_PABORT_HANDLER bl CPU_PABORT_HANDLER
#endif #endif
debug_entry r1
enable_irq @ Enable interrupts enable_irq @ Enable interrupts
mov r2, sp @ regs mov r2, sp @ regs
bl do_PrefetchAbort @ call abort handler bl do_PrefetchAbort @ call abort handler
......
...@@ -165,6 +165,25 @@ ...@@ -165,6 +165,25 @@
.endm .endm
#endif /* !CONFIG_THUMB2_KERNEL */ #endif /* !CONFIG_THUMB2_KERNEL */
@
@ Debug exceptions are taken as prefetch or data aborts.
@ We must disable preemption during the handler so that
@ we can access the debug registers safely.
@
.macro debug_entry, fsr
#if defined(CONFIG_HAVE_HW_BREAKPOINT) && defined(CONFIG_PREEMPT)
ldr r4, =0x40f @ mask out fsr.fs
and r5, r4, \fsr
cmp r5, #2 @ debug exception
bne 1f
get_thread_info r10
ldr r6, [r10, #TI_PREEMPT] @ get preempt count
add r11, r6, #1 @ increment it
str r11, [r10, #TI_PREEMPT]
1:
#endif
.endm
/* /*
* These are the registers used in the syscall handler, and allow us to * These are the registers used in the syscall handler, and allow us to
* have in theory up to 7 arguments to a function - r0 to r6. * have in theory up to 7 arguments to a function - r0 to r6.
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#define pr_fmt(fmt) "hw-breakpoint: " fmt #define pr_fmt(fmt) "hw-breakpoint: " fmt
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/hardirq.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <linux/hw_breakpoint.h> #include <linux/hw_breakpoint.h>
#include <linux/smp.h> #include <linux/smp.h>
...@@ -736,14 +737,17 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs) ...@@ -736,14 +737,17 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)
/* /*
* Called from either the Data Abort Handler [watchpoint] or the * Called from either the Data Abort Handler [watchpoint] or the
* Prefetch Abort Handler [breakpoint]. * Prefetch Abort Handler [breakpoint] with preemption disabled.
*/ */
static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,
struct pt_regs *regs) struct pt_regs *regs)
{ {
int ret = 1; /* Unhandled fault. */ int ret = 0;
u32 dscr; u32 dscr;
/* We must be called with preemption disabled. */
WARN_ON(preemptible());
/* We only handle watchpoints and hardware breakpoints. */ /* We only handle watchpoints and hardware breakpoints. */
ARM_DBG_READ(c1, 0, dscr); ARM_DBG_READ(c1, 0, dscr);
...@@ -758,11 +762,15 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, ...@@ -758,11 +762,15 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,
watchpoint_handler(addr, regs); watchpoint_handler(addr, regs);
break; break;
default: default:
goto out; ret = 1; /* Unhandled fault. */
} }
ret = 0; /*
out: * Re-enable preemption after it was disabled in the
* low-level exception handling code.
*/
preempt_enable();
return ret; return ret;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册