diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-06-25 06:44:06 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-07-02 05:56:00 -0400 |
commit | 02fe2845d6a837ab02f0738f6cf4591a02cc88d4 (patch) | |
tree | e50d06a1ab73a2735dd145edde458463b1da4a37 /arch | |
parent | 8b4186160b7894ca4583f702a562856d5d9e9118 (diff) |
ARM: entry: avoid enabling interrupts in prefetch/data abort handlers
Avoid enabling interrupts if the parent context had interrupts enabled
in the abort handler assembly code, and move this into the breakpoint/
page/alignment fault handlers instead.
This gets rid of some special-casing for the breakpoint fault handlers
from the low level abort handler path.
Acked-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/kernel/entry-armv.S | 43 | ||||
-rw-r--r-- | arch/arm/kernel/entry-header.S | 19 | ||||
-rw-r--r-- | arch/arm/kernel/hw_breakpoint.c | 12 | ||||
-rw-r--r-- | arch/arm/mm/alignment.c | 3 | ||||
-rw-r--r-- | arch/arm/mm/fault.c | 4 |
5 files changed, 31 insertions, 50 deletions
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index d644d0240ad3..c46bafa2f6dc 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S | |||
@@ -185,20 +185,15 @@ ENDPROC(__und_invalid) | |||
185 | __dabt_svc: | 185 | __dabt_svc: |
186 | svc_entry | 186 | svc_entry |
187 | 187 | ||
188 | @ | 188 | #ifdef CONFIG_TRACE_IRQFLAGS |
189 | @ get ready to re-enable interrupts if appropriate | 189 | bl trace_hardirqs_off |
190 | @ | 190 | #endif |
191 | mrs r9, cpsr | ||
192 | tst r5, #PSR_I_BIT | ||
193 | biceq r9, r9, #PSR_I_BIT | ||
194 | 191 | ||
195 | dabt_helper | 192 | dabt_helper |
196 | 193 | ||
197 | @ | 194 | @ |
198 | @ set desired IRQ state, then call main handler | 195 | @ call main handler |
199 | @ | 196 | @ |
200 | debug_entry r1 | ||
201 | msr cpsr_c, r9 | ||
202 | mov r2, sp | 197 | mov r2, sp |
203 | bl do_DataAbort | 198 | bl do_DataAbort |
204 | 199 | ||
@@ -211,6 +206,12 @@ __dabt_svc: | |||
211 | @ restore SPSR and restart the instruction | 206 | @ restore SPSR and restart the instruction |
212 | @ | 207 | @ |
213 | ldr r5, [sp, #S_PSR] | 208 | ldr r5, [sp, #S_PSR] |
209 | #ifdef CONFIG_TRACE_IRQFLAGS | ||
210 | tst r5, #PSR_I_BIT | ||
211 | bleq trace_hardirqs_on | ||
212 | tst r5, #PSR_I_BIT | ||
213 | blne trace_hardirqs_off | ||
214 | #endif | ||
214 | svc_exit r5 @ return from exception | 215 | svc_exit r5 @ return from exception |
215 | UNWIND(.fnend ) | 216 | UNWIND(.fnend ) |
216 | ENDPROC(__dabt_svc) | 217 | ENDPROC(__dabt_svc) |
@@ -307,16 +308,11 @@ ENDPROC(__und_svc) | |||
307 | __pabt_svc: | 308 | __pabt_svc: |
308 | svc_entry | 309 | svc_entry |
309 | 310 | ||
310 | @ | 311 | #ifdef CONFIG_TRACE_IRQFLAGS |
311 | @ re-enable interrupts if appropriate | 312 | bl trace_hardirqs_off |
312 | @ | 313 | #endif |
313 | mrs r9, cpsr | ||
314 | tst r5, #PSR_I_BIT | ||
315 | biceq r9, r9, #PSR_I_BIT | ||
316 | 314 | ||
317 | pabt_helper | 315 | pabt_helper |
318 | debug_entry r1 | ||
319 | msr cpsr_c, r9 @ Maybe enable interrupts | ||
320 | mov r2, sp @ regs | 316 | mov r2, sp @ regs |
321 | bl do_PrefetchAbort @ call abort handler | 317 | bl do_PrefetchAbort @ call abort handler |
322 | 318 | ||
@@ -329,6 +325,12 @@ __pabt_svc: | |||
329 | @ restore SPSR and restart the instruction | 325 | @ restore SPSR and restart the instruction |
330 | @ | 326 | @ |
331 | ldr r5, [sp, #S_PSR] | 327 | ldr r5, [sp, #S_PSR] |
328 | #ifdef CONFIG_TRACE_IRQFLAGS | ||
329 | tst r5, #PSR_I_BIT | ||
330 | bleq trace_hardirqs_on | ||
331 | tst r5, #PSR_I_BIT | ||
332 | blne trace_hardirqs_off | ||
333 | #endif | ||
332 | svc_exit r5 @ return from exception | 334 | svc_exit r5 @ return from exception |
333 | UNWIND(.fnend ) | 335 | UNWIND(.fnend ) |
334 | ENDPROC(__pabt_svc) | 336 | ENDPROC(__pabt_svc) |
@@ -412,11 +414,6 @@ __dabt_usr: | |||
412 | kuser_cmpxchg_check | 414 | kuser_cmpxchg_check |
413 | dabt_helper | 415 | dabt_helper |
414 | 416 | ||
415 | @ | ||
416 | @ IRQs on, then call the main handler | ||
417 | @ | ||
418 | debug_entry r1 | ||
419 | enable_irq | ||
420 | mov r2, sp | 417 | mov r2, sp |
421 | adr lr, BSYM(ret_from_exception) | 418 | adr lr, BSYM(ret_from_exception) |
422 | b do_DataAbort | 419 | b do_DataAbort |
@@ -663,8 +660,6 @@ ENDPROC(__und_usr_unknown) | |||
663 | __pabt_usr: | 660 | __pabt_usr: |
664 | usr_entry | 661 | usr_entry |
665 | pabt_helper | 662 | pabt_helper |
666 | debug_entry r1 | ||
667 | enable_irq @ Enable interrupts | ||
668 | mov r2, sp @ regs | 663 | mov r2, sp @ regs |
669 | bl do_PrefetchAbort @ call abort handler | 664 | bl do_PrefetchAbort @ call abort handler |
670 | UNWIND(.fnend ) | 665 | UNWIND(.fnend ) |
diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index 051166c2a932..4d6ad8348e89 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S | |||
@@ -165,25 +165,6 @@ | |||
165 | .endm | 165 | .endm |
166 | #endif /* !CONFIG_THUMB2_KERNEL */ | 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 | |||
187 | /* | 168 | /* |
188 | * These are the registers used in the syscall handler, and allow us to | 169 | * These are the registers used in the syscall handler, and allow us to |
189 | * have in theory up to 7 arguments to a function - r0 to r6. | 170 | * have in theory up to 7 arguments to a function - r0 to r6. |
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 87acc25d7a3e..a927ca1f5566 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c | |||
@@ -796,7 +796,7 @@ unlock: | |||
796 | 796 | ||
797 | /* | 797 | /* |
798 | * Called from either the Data Abort Handler [watchpoint] or the | 798 | * Called from either the Data Abort Handler [watchpoint] or the |
799 | * Prefetch Abort Handler [breakpoint] with preemption disabled. | 799 | * Prefetch Abort Handler [breakpoint] with interrupts disabled. |
800 | */ | 800 | */ |
801 | static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, | 801 | static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, |
802 | struct pt_regs *regs) | 802 | struct pt_regs *regs) |
@@ -804,8 +804,10 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, | |||
804 | int ret = 0; | 804 | int ret = 0; |
805 | u32 dscr; | 805 | u32 dscr; |
806 | 806 | ||
807 | /* We must be called with preemption disabled. */ | 807 | preempt_disable(); |
808 | WARN_ON(preemptible()); | 808 | |
809 | if (interrupts_enabled(regs)) | ||
810 | local_irq_enable(); | ||
809 | 811 | ||
810 | /* We only handle watchpoints and hardware breakpoints. */ | 812 | /* We only handle watchpoints and hardware breakpoints. */ |
811 | ARM_DBG_READ(c1, 0, dscr); | 813 | ARM_DBG_READ(c1, 0, dscr); |
@@ -824,10 +826,6 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, | |||
824 | ret = 1; /* Unhandled fault. */ | 826 | ret = 1; /* Unhandled fault. */ |
825 | } | 827 | } |
826 | 828 | ||
827 | /* | ||
828 | * Re-enable preemption after it was disabled in the | ||
829 | * low-level exception handling code. | ||
830 | */ | ||
831 | preempt_enable(); | 829 | preempt_enable(); |
832 | 830 | ||
833 | return ret; | 831 | return ret; |
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c index 724ba3bce72c..be7c638b648b 100644 --- a/arch/arm/mm/alignment.c +++ b/arch/arm/mm/alignment.c | |||
@@ -727,6 +727,9 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
727 | int isize = 4; | 727 | int isize = 4; |
728 | int thumb2_32b = 0; | 728 | int thumb2_32b = 0; |
729 | 729 | ||
730 | if (interrupts_enabled(regs)) | ||
731 | local_irq_enable(); | ||
732 | |||
730 | instrptr = instruction_pointer(regs); | 733 | instrptr = instruction_pointer(regs); |
731 | 734 | ||
732 | fs = get_fs(); | 735 | fs = get_fs(); |
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index bc0e1d88fd3b..20e5d5120609 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c | |||
@@ -285,6 +285,10 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
285 | tsk = current; | 285 | tsk = current; |
286 | mm = tsk->mm; | 286 | mm = tsk->mm; |
287 | 287 | ||
288 | /* Enable interrupts if they were enabled in the parent context. */ | ||
289 | if (interrupts_enabled(regs)) | ||
290 | local_irq_enable(); | ||
291 | |||
288 | /* | 292 | /* |
289 | * If we're in an interrupt or have no user | 293 | * If we're in an interrupt or have no user |
290 | * context, we must not take the fault.. | 294 | * context, we must not take the fault.. |