Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7e202696 authored by Will Deacon's avatar Will Deacon
Browse files

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: default avatarCatalin Marinas <catalin.marinas@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent 6ee33c27
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -198,6 +198,7 @@ __dabt_svc:
	@
	@ set desired IRQ state, then call main handler
	@
	debug_entry r1
	msr	cpsr_c, r9
	mov	r2, sp
	bl	do_DataAbort
@@ -324,6 +325,7 @@ __pabt_svc:
#else
	bl	CPU_PABORT_HANDLER
#endif
	debug_entry r1
	msr	cpsr_c, r9			@ Maybe enable interrupts
	mov	r2, sp				@ regs
	bl	do_PrefetchAbort		@ call abort handler
@@ -439,6 +441,7 @@ __dabt_usr:
	@
	@ IRQs on, then call the main handler
	@
	debug_entry r1
	enable_irq
	mov	r2, sp
	adr	lr, BSYM(ret_from_exception)
@@ -703,6 +706,7 @@ __pabt_usr:
#else
	bl	CPU_PABORT_HANDLER
#endif
	debug_entry r1
	enable_irq				@ Enable interrupts
	mov	r2, sp				@ regs
	bl	do_PrefetchAbort		@ call abort handler
+19 −0
Original line number Diff line number Diff line
@@ -165,6 +165,25 @@
	.endm
#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
 * have in theory up to 7 arguments to a function - r0 to r6.
+13 −5
Original line number Diff line number Diff line
@@ -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 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)

/*
 * 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;
}