diff options
author | Will Deacon <will.deacon@arm.com> | 2012-04-23 10:38:28 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-04-23 10:44:41 -0400 |
commit | 2498814fcb3068f19b82b1519b4038721f61af43 (patch) | |
tree | 6a3483bab350438a768b56bb56611d609b2396b4 /arch | |
parent | ab4d536890853ab6675ede65db40e2c0980cb0ea (diff) |
ARM: 7399/1: vfp: move user vfp state save/restore code out of signal.c
The user VFP state must be preserved (subject to ucontext modifications)
across invocation of a signal handler and this is currently handled by
vfp_{preserve,restore}_context in signal.c
Since this code requires intimate low-level knowledge of the VFP state,
this patch moves it into vfpmodule.c.
Signed-off-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/include/asm/thread_info.h | 7 | ||||
-rw-r--r-- | arch/arm/kernel/signal.c | 55 | ||||
-rw-r--r-- | arch/arm/vfp/vfpmodule.c | 79 |
3 files changed, 90 insertions, 51 deletions
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index d4c24d412a8d..0f04d84582e1 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h | |||
@@ -118,6 +118,13 @@ extern void iwmmxt_task_switch(struct thread_info *); | |||
118 | extern void vfp_sync_hwstate(struct thread_info *); | 118 | extern void vfp_sync_hwstate(struct thread_info *); |
119 | extern void vfp_flush_hwstate(struct thread_info *); | 119 | extern void vfp_flush_hwstate(struct thread_info *); |
120 | 120 | ||
121 | struct user_vfp; | ||
122 | struct user_vfp_exc; | ||
123 | |||
124 | extern int vfp_preserve_user_clear_hwstate(struct user_vfp __user *, | ||
125 | struct user_vfp_exc __user *); | ||
126 | extern int vfp_restore_user_hwstate(struct user_vfp __user *, | ||
127 | struct user_vfp_exc __user *); | ||
121 | #endif | 128 | #endif |
122 | 129 | ||
123 | /* | 130 | /* |
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 7cb532fc8aa4..d68d1b694680 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
@@ -180,44 +180,23 @@ static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame) | |||
180 | 180 | ||
181 | static int preserve_vfp_context(struct vfp_sigframe __user *frame) | 181 | static int preserve_vfp_context(struct vfp_sigframe __user *frame) |
182 | { | 182 | { |
183 | struct thread_info *thread = current_thread_info(); | ||
184 | struct vfp_hard_struct *h = &thread->vfpstate.hard; | ||
185 | const unsigned long magic = VFP_MAGIC; | 183 | const unsigned long magic = VFP_MAGIC; |
186 | const unsigned long size = VFP_STORAGE_SIZE; | 184 | const unsigned long size = VFP_STORAGE_SIZE; |
187 | int err = 0; | 185 | int err = 0; |
188 | 186 | ||
189 | vfp_sync_hwstate(thread); | ||
190 | __put_user_error(magic, &frame->magic, err); | 187 | __put_user_error(magic, &frame->magic, err); |
191 | __put_user_error(size, &frame->size, err); | 188 | __put_user_error(size, &frame->size, err); |
192 | 189 | ||
193 | /* | 190 | if (err) |
194 | * Copy the floating point registers. There can be unused | 191 | return -EFAULT; |
195 | * registers see asm/hwcap.h for details. | ||
196 | */ | ||
197 | err |= __copy_to_user(&frame->ufp.fpregs, &h->fpregs, | ||
198 | sizeof(h->fpregs)); | ||
199 | /* | ||
200 | * Copy the status and control register. | ||
201 | */ | ||
202 | __put_user_error(h->fpscr, &frame->ufp.fpscr, err); | ||
203 | |||
204 | /* | ||
205 | * Copy the exception registers. | ||
206 | */ | ||
207 | __put_user_error(h->fpexc, &frame->ufp_exc.fpexc, err); | ||
208 | __put_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); | ||
209 | __put_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); | ||
210 | 192 | ||
211 | return err ? -EFAULT : 0; | 193 | return vfp_preserve_user_clear_hwstate(&frame->ufp, &frame->ufp_exc); |
212 | } | 194 | } |
213 | 195 | ||
214 | static int restore_vfp_context(struct vfp_sigframe __user *frame) | 196 | static int restore_vfp_context(struct vfp_sigframe __user *frame) |
215 | { | 197 | { |
216 | struct thread_info *thread = current_thread_info(); | ||
217 | struct vfp_hard_struct *h = &thread->vfpstate.hard; | ||
218 | unsigned long magic; | 198 | unsigned long magic; |
219 | unsigned long size; | 199 | unsigned long size; |
220 | unsigned long fpexc; | ||
221 | int err = 0; | 200 | int err = 0; |
222 | 201 | ||
223 | __get_user_error(magic, &frame->magic, err); | 202 | __get_user_error(magic, &frame->magic, err); |
@@ -228,33 +207,7 @@ static int restore_vfp_context(struct vfp_sigframe __user *frame) | |||
228 | if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) | 207 | if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) |
229 | return -EINVAL; | 208 | return -EINVAL; |
230 | 209 | ||
231 | vfp_flush_hwstate(thread); | 210 | return vfp_restore_user_hwstate(&frame->ufp, &frame->ufp_exc); |
232 | |||
233 | /* | ||
234 | * Copy the floating point registers. There can be unused | ||
235 | * registers see asm/hwcap.h for details. | ||
236 | */ | ||
237 | err |= __copy_from_user(&h->fpregs, &frame->ufp.fpregs, | ||
238 | sizeof(h->fpregs)); | ||
239 | /* | ||
240 | * Copy the status and control register. | ||
241 | */ | ||
242 | __get_user_error(h->fpscr, &frame->ufp.fpscr, err); | ||
243 | |||
244 | /* | ||
245 | * Sanitise and restore the exception registers. | ||
246 | */ | ||
247 | __get_user_error(fpexc, &frame->ufp_exc.fpexc, err); | ||
248 | /* Ensure the VFP is enabled. */ | ||
249 | fpexc |= FPEXC_EN; | ||
250 | /* Ensure FPINST2 is invalid and the exception flag is cleared. */ | ||
251 | fpexc &= ~(FPEXC_EX | FPEXC_FP2V); | ||
252 | h->fpexc = fpexc; | ||
253 | |||
254 | __get_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); | ||
255 | __get_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); | ||
256 | |||
257 | return err ? -EFAULT : 0; | ||
258 | } | 211 | } |
259 | 212 | ||
260 | #endif | 213 | #endif |
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 858748eaa144..05872d92fca2 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/sched.h> | 17 | #include <linux/sched.h> |
18 | #include <linux/smp.h> | 18 | #include <linux/smp.h> |
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/uaccess.h> | ||
21 | #include <linux/user.h> | ||
20 | 22 | ||
21 | #include <asm/cp15.h> | 23 | #include <asm/cp15.h> |
22 | #include <asm/cputype.h> | 24 | #include <asm/cputype.h> |
@@ -529,6 +531,83 @@ void vfp_flush_hwstate(struct thread_info *thread) | |||
529 | } | 531 | } |
530 | 532 | ||
531 | /* | 533 | /* |
534 | * Save the current VFP state into the provided structures and prepare | ||
535 | * for entry into a new function (signal handler). | ||
536 | */ | ||
537 | int vfp_preserve_user_clear_hwstate(struct user_vfp __user *ufp, | ||
538 | struct user_vfp_exc __user *ufp_exc) | ||
539 | { | ||
540 | struct thread_info *thread = current_thread_info(); | ||
541 | struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; | ||
542 | int err = 0; | ||
543 | |||
544 | /* Ensure that the saved hwstate is up-to-date. */ | ||
545 | vfp_sync_hwstate(thread); | ||
546 | |||
547 | /* | ||
548 | * Copy the floating point registers. There can be unused | ||
549 | * registers see asm/hwcap.h for details. | ||
550 | */ | ||
551 | err |= __copy_to_user(&ufp->fpregs, &hwstate->fpregs, | ||
552 | sizeof(hwstate->fpregs)); | ||
553 | /* | ||
554 | * Copy the status and control register. | ||
555 | */ | ||
556 | __put_user_error(hwstate->fpscr, &ufp->fpscr, err); | ||
557 | |||
558 | /* | ||
559 | * Copy the exception registers. | ||
560 | */ | ||
561 | __put_user_error(hwstate->fpexc, &ufp_exc->fpexc, err); | ||
562 | __put_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); | ||
563 | __put_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); | ||
564 | |||
565 | if (err) | ||
566 | return -EFAULT; | ||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | /* Sanitise and restore the current VFP state from the provided structures. */ | ||
571 | int vfp_restore_user_hwstate(struct user_vfp __user *ufp, | ||
572 | struct user_vfp_exc __user *ufp_exc) | ||
573 | { | ||
574 | struct thread_info *thread = current_thread_info(); | ||
575 | struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; | ||
576 | unsigned long fpexc; | ||
577 | int err = 0; | ||
578 | |||
579 | vfp_flush_hwstate(thread); | ||
580 | |||
581 | /* | ||
582 | * Copy the floating point registers. There can be unused | ||
583 | * registers see asm/hwcap.h for details. | ||
584 | */ | ||
585 | err |= __copy_from_user(&hwstate->fpregs, &ufp->fpregs, | ||
586 | sizeof(hwstate->fpregs)); | ||
587 | /* | ||
588 | * Copy the status and control register. | ||
589 | */ | ||
590 | __get_user_error(hwstate->fpscr, &ufp->fpscr, err); | ||
591 | |||
592 | /* | ||
593 | * Sanitise and restore the exception registers. | ||
594 | */ | ||
595 | __get_user_error(fpexc, &ufp_exc->fpexc, err); | ||
596 | |||
597 | /* Ensure the VFP is enabled. */ | ||
598 | fpexc |= FPEXC_EN; | ||
599 | |||
600 | /* Ensure FPINST2 is invalid and the exception flag is cleared. */ | ||
601 | fpexc &= ~(FPEXC_EX | FPEXC_FP2V); | ||
602 | hwstate->fpexc = fpexc; | ||
603 | |||
604 | __get_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); | ||
605 | __get_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); | ||
606 | |||
607 | return err ? -EFAULT : 0; | ||
608 | } | ||
609 | |||
610 | /* | ||
532 | * VFP hardware can lose all context when a CPU goes offline. | 611 | * VFP hardware can lose all context when a CPU goes offline. |
533 | * As we will be running in SMP mode with CPU hotplug, we will save the | 612 | * As we will be running in SMP mode with CPU hotplug, we will save the |
534 | * hardware state at every thread switch. We clear our held state when | 613 | * hardware state at every thread switch. We clear our held state when |