diff options
author | George G. Davis <gdavis@mvista.com> | 2009-04-01 15:27:18 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-04-01 17:17:50 -0400 |
commit | f2255be8126e86142901c61dd482c1c2a5ffdda7 (patch) | |
tree | a59ff8fecbbf30a7a1e33f97b59e1ed3cd38c018 /arch/arm/vfp | |
parent | fe68e68f6a379d317a87ae24de050a65b11ea1fb (diff) |
[ARM] 5440/1: Fix VFP state corruption due to preemption during VFP exceptions
We've observed that ARM VFP state can be corrupted during VFP exception
handling when PREEMPT is enabled. The exact conditions are difficult
to reproduce but appear to occur during VFP exception handling when a
task causes a VFP exception which is handled via VFP_bounce and is then
preempted by yet another task which in turn causes yet another VFP
exception. Since the VFP_bounce code is not preempt safe, VFP state then
becomes corrupt. In order to prevent preemption from occuring while
handling a VFP exception, this patch disables preemption while handling
VFP exceptions.
Signed-off-by: George G. Davis <gdavis@mvista.com>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/vfp')
-rw-r--r-- | arch/arm/vfp/entry.S | 23 | ||||
-rw-r--r-- | arch/arm/vfp/vfphw.S | 12 | ||||
-rw-r--r-- | arch/arm/vfp/vfpmodule.c | 6 |
3 files changed, 35 insertions, 6 deletions
diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S index ba592a9e6fb3..a2bed62aec21 100644 --- a/arch/arm/vfp/entry.S +++ b/arch/arm/vfp/entry.S | |||
@@ -15,13 +15,16 @@ | |||
15 | * r10 = thread_info structure | 15 | * r10 = thread_info structure |
16 | * lr = failure return | 16 | * lr = failure return |
17 | */ | 17 | */ |
18 | #include <linux/linkage.h> | 18 | #include <asm/thread_info.h> |
19 | #include <linux/init.h> | ||
20 | #include <asm/asm-offsets.h> | ||
21 | #include <asm/assembler.h> | ||
22 | #include <asm/vfpmacros.h> | 19 | #include <asm/vfpmacros.h> |
20 | #include "../kernel/entry-header.S" | ||
23 | 21 | ||
24 | ENTRY(do_vfp) | 22 | ENTRY(do_vfp) |
23 | #ifdef CONFIG_PREEMPT | ||
24 | ldr r4, [r10, #TI_PREEMPT] @ get preempt count | ||
25 | add r11, r4, #1 @ increment it | ||
26 | str r11, [r10, #TI_PREEMPT] | ||
27 | #endif | ||
25 | enable_irq | 28 | enable_irq |
26 | ldr r4, .LCvfp | 29 | ldr r4, .LCvfp |
27 | ldr r11, [r10, #TI_CPU] @ CPU number | 30 | ldr r11, [r10, #TI_CPU] @ CPU number |
@@ -30,6 +33,12 @@ ENTRY(do_vfp) | |||
30 | ENDPROC(do_vfp) | 33 | ENDPROC(do_vfp) |
31 | 34 | ||
32 | ENTRY(vfp_null_entry) | 35 | ENTRY(vfp_null_entry) |
36 | #ifdef CONFIG_PREEMPT | ||
37 | get_thread_info r10 | ||
38 | ldr r4, [r10, #TI_PREEMPT] @ get preempt count | ||
39 | sub r11, r4, #1 @ decrement it | ||
40 | str r11, [r10, #TI_PREEMPT] | ||
41 | #endif | ||
33 | mov pc, lr | 42 | mov pc, lr |
34 | ENDPROC(vfp_null_entry) | 43 | ENDPROC(vfp_null_entry) |
35 | 44 | ||
@@ -41,6 +50,12 @@ ENDPROC(vfp_null_entry) | |||
41 | 50 | ||
42 | __INIT | 51 | __INIT |
43 | ENTRY(vfp_testing_entry) | 52 | ENTRY(vfp_testing_entry) |
53 | #ifdef CONFIG_PREEMPT | ||
54 | get_thread_info r10 | ||
55 | ldr r4, [r10, #TI_PREEMPT] @ get preempt count | ||
56 | sub r11, r4, #1 @ decrement it | ||
57 | str r11, [r10, #TI_PREEMPT] | ||
58 | #endif | ||
44 | ldr r0, VFP_arch_address | 59 | ldr r0, VFP_arch_address |
45 | str r5, [r0] @ known non-zero value | 60 | str r5, [r0] @ known non-zero value |
46 | mov pc, r9 @ we have handled the fault | 61 | mov pc, r9 @ we have handled the fault |
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S index a5a4e57763c3..83c4e384b16d 100644 --- a/arch/arm/vfp/vfphw.S +++ b/arch/arm/vfp/vfphw.S | |||
@@ -137,6 +137,12 @@ check_for_exception: | |||
137 | VFPFMXR FPEXC, r1 @ restore FPEXC last | 137 | VFPFMXR FPEXC, r1 @ restore FPEXC last |
138 | sub r2, r2, #4 | 138 | sub r2, r2, #4 |
139 | str r2, [sp, #S_PC] @ retry the instruction | 139 | str r2, [sp, #S_PC] @ retry the instruction |
140 | #ifdef CONFIG_PREEMPT | ||
141 | get_thread_info r10 | ||
142 | ldr r4, [r10, #TI_PREEMPT] @ get preempt count | ||
143 | sub r11, r4, #1 @ decrement it | ||
144 | str r11, [r10, #TI_PREEMPT] | ||
145 | #endif | ||
140 | mov pc, r9 @ we think we have handled things | 146 | mov pc, r9 @ we think we have handled things |
141 | 147 | ||
142 | 148 | ||
@@ -155,6 +161,12 @@ look_for_VFP_exceptions: | |||
155 | @ not recognised by VFP | 161 | @ not recognised by VFP |
156 | 162 | ||
157 | DBGSTR "not VFP" | 163 | DBGSTR "not VFP" |
164 | #ifdef CONFIG_PREEMPT | ||
165 | get_thread_info r10 | ||
166 | ldr r4, [r10, #TI_PREEMPT] @ get preempt count | ||
167 | sub r11, r4, #1 @ decrement it | ||
168 | str r11, [r10, #TI_PREEMPT] | ||
169 | #endif | ||
158 | mov pc, lr | 170 | mov pc, lr |
159 | 171 | ||
160 | process_exception: | 172 | process_exception: |
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 75457b30d813..01599c4ef726 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c | |||
@@ -266,7 +266,7 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) | |||
266 | * on VFP subarch 1. | 266 | * on VFP subarch 1. |
267 | */ | 267 | */ |
268 | vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr, regs); | 268 | vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr, regs); |
269 | return; | 269 | goto exit; |
270 | } | 270 | } |
271 | 271 | ||
272 | /* | 272 | /* |
@@ -297,7 +297,7 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) | |||
297 | * the FPEXC.FP2V bit is valid only if FPEXC.EX is 1. | 297 | * the FPEXC.FP2V bit is valid only if FPEXC.EX is 1. |
298 | */ | 298 | */ |
299 | if (fpexc ^ (FPEXC_EX | FPEXC_FP2V)) | 299 | if (fpexc ^ (FPEXC_EX | FPEXC_FP2V)) |
300 | return; | 300 | goto exit; |
301 | 301 | ||
302 | /* | 302 | /* |
303 | * The barrier() here prevents fpinst2 being read | 303 | * The barrier() here prevents fpinst2 being read |
@@ -310,6 +310,8 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) | |||
310 | exceptions = vfp_emulate_instruction(trigger, orig_fpscr, regs); | 310 | exceptions = vfp_emulate_instruction(trigger, orig_fpscr, regs); |
311 | if (exceptions) | 311 | if (exceptions) |
312 | vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); | 312 | vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); |
313 | exit: | ||
314 | preempt_enable(); | ||
313 | } | 315 | } |
314 | 316 | ||
315 | static void vfp_enable(void *unused) | 317 | static void vfp_enable(void *unused) |