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:
parent
6ee33c2712
commit
7e20269647
@ -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:
|
|||||||
#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:
|
|||||||
@
|
@
|
||||||
@ 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:
|
|||||||
#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 @@
|
|||||||
.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 @@
|
|||||||
#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 @@ unlock:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user