Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+36 -5
+4
arch/arm/kernel/entry-armv.S
··· 198 198 @ 199 199 @ set desired IRQ state, then call main handler 200 200 @ 201 + debug_entry r1 201 202 msr cpsr_c, r9 202 203 mov r2, sp 203 204 bl do_DataAbort ··· 325 324 #else 326 325 bl CPU_PABORT_HANDLER 327 326 #endif 327 + debug_entry r1 328 328 msr cpsr_c, r9 @ Maybe enable interrupts 329 329 mov r2, sp @ regs 330 330 bl do_PrefetchAbort @ call abort handler ··· 441 439 @ 442 440 @ IRQs on, then call the main handler 443 441 @ 442 + debug_entry r1 444 443 enable_irq 445 444 mov r2, sp 446 445 adr lr, BSYM(ret_from_exception) ··· 706 703 #else 707 704 bl CPU_PABORT_HANDLER 708 705 #endif 706 + debug_entry r1 709 707 enable_irq @ Enable interrupts 710 708 mov r2, sp @ regs 711 709 bl do_PrefetchAbort @ call abort handler
+19
arch/arm/kernel/entry-header.S
··· 165 165 .endm 166 166 #endif /* !CONFIG_THUMB2_KERNEL */ 167 167 168 + @ 169 + @ Debug exceptions are taken as prefetch or data aborts. 170 + @ We must disable preemption during the handler so that 171 + @ we can access the debug registers safely. 172 + @ 173 + .macro debug_entry, fsr 174 + #if defined(CONFIG_HAVE_HW_BREAKPOINT) && defined(CONFIG_PREEMPT) 175 + ldr r4, =0x40f @ mask out fsr.fs 176 + and r5, r4, \fsr 177 + cmp r5, #2 @ debug exception 178 + bne 1f 179 + get_thread_info r10 180 + ldr r6, [r10, #TI_PREEMPT] @ get preempt count 181 + add r11, r6, #1 @ increment it 182 + str r11, [r10, #TI_PREEMPT] 183 + 1: 184 + #endif 185 + .endm 186 + 168 187 /* 169 188 * These are the registers used in the syscall handler, and allow us to 170 189 * have in theory up to 7 arguments to a function - r0 to r6.
+13 -5
arch/arm/kernel/hw_breakpoint.c
··· 24 24 #define pr_fmt(fmt) "hw-breakpoint: " fmt 25 25 26 26 #include <linux/errno.h> 27 + #include <linux/hardirq.h> 27 28 #include <linux/perf_event.h> 28 29 #include <linux/hw_breakpoint.h> 29 30 #include <linux/smp.h> ··· 737 736 738 737 /* 739 738 * Called from either the Data Abort Handler [watchpoint] or the 740 - * Prefetch Abort Handler [breakpoint]. 739 + * Prefetch Abort Handler [breakpoint] with preemption disabled. 741 740 */ 742 741 static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, 743 742 struct pt_regs *regs) 744 743 { 745 - int ret = 1; /* Unhandled fault. */ 744 + int ret = 0; 746 745 u32 dscr; 746 + 747 + /* We must be called with preemption disabled. */ 748 + WARN_ON(preemptible()); 747 749 748 750 /* We only handle watchpoints and hardware breakpoints. */ 749 751 ARM_DBG_READ(c1, 0, dscr); ··· 762 758 watchpoint_handler(addr, regs); 763 759 break; 764 760 default: 765 - goto out; 761 + ret = 1; /* Unhandled fault. */ 766 762 } 767 763 768 - ret = 0; 769 - out: 764 + /* 765 + * Re-enable preemption after it was disabled in the 766 + * low-level exception handling code. 767 + */ 768 + preempt_enable(); 769 + 770 770 return ret; 771 771 } 772 772