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>
This commit is contained in:
Will Deacon
2010-11-28 14:57:24 +00:00
parent 6ee33c2712
commit 7e20269647
3 changed files with 36 additions and 5 deletions

View File

@@ -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;
}