diff options
author | Will Deacon <will.deacon@arm.com> | 2010-11-28 14:57:24 +0000 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2010-12-06 11:55:56 +0000 |
commit | 7e20269647169e7ea08a62bdc4979a3ba32e615c (patch) | |
tree | b66a6c5e893a34ee9659403f66f44841dfb34232 /arch/arm/kernel/hw_breakpoint.c | |
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/arm/kernel/hw_breakpoint.c')
-rw-r--r-- | arch/arm/kernel/hw_breakpoint.c | 18 |
1 files changed, 13 insertions, 5 deletions
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index d37ed3501e5..eeba3800803 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -24,6 +24,7 @@ #define pr_fmt(fmt) "hw-breakpoint: " fmt #include <linux/errno.h> +#include <linux/hardirq.h> #include <linux/perf_event.h> #include <linux/hw_breakpoint.h> #include <linux/smp.h> @@ -736,14 +737,17 @@ unlock: /* * 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, struct pt_regs *regs) { - int ret = 1; /* Unhandled fault. */ + int ret = 0; u32 dscr; + /* We must be called with preemption disabled. */ + WARN_ON(preemptible()); + /* We only handle watchpoints and hardware breakpoints. */ ARM_DBG_READ(c1, 0, dscr); @@ -758,11 +762,15 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, watchpoint_handler(addr, regs); break; 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; } |