Skip to content

Commit c47e568

Browse files
Claggy3popcornmix
authored andcommitted
Update vfpmodule.c
Christopher Alexander Tobias Schulze - May 2, 2015, 11:57 a.m. This patch fixes a problem with VFP state save and restore related to exception handling (panic with message "BUG: unsupported FP instruction in kernel mode") present on VFP11 floating point units (as used with ARM1176JZF-S CPUs, e.g. on first generation Raspberry Pi boards). This patch was developed and discussed on #859 A precondition to see the crashes is that floating point exception traps are enabled. In this case, the VFP11 might determine that a FPU operation needs to trap at a point in time when it is not possible to signal this to the ARM11 core any more. The VFP11 will then set the FPEXC.EX bit and store the trapped opcode in FPINST. (In some cases, a second opcode might have been accepted by the VFP11 before the exception was detected and could be reported to the ARM11 - in this case, the VFP11 also sets FPEXC.FP2V and stores the second opcode in FPINST2.) If FPEXC.EX is set, the VFP11 will "bounce" the next FPU opcode issued by the ARM11 CPU, which will be seen by the ARM11 as an undefined opcode trap. The VFP support code examines the FPEXC.EX and FPEXC.FP2V bits to decide what actions to take, i.e., whether to emulate the opcodes found in FPINST and FPINST2, and whether to retry the bounced instruction. If a user space application has left the VFP11 in this "pending trap" state, the next FPU opcode issued to the VFP11 might actually be the VSTMIA operation vfp_save_state() uses to store the FPU registers to memory (in our test cases, when building the signal stack frame). In this case, the kernel crashes as described above. This patch fixes the problem by making sure that vfp_save_state() is always entered with FPEXC.EX cleared. (The current value of FPEXC has already been saved, so this does not corrupt the context. Clearing FPEXC.EX has no effects on FPINST or FPINST2. Also note that many callers already modify FPEXC by setting FPEXC.EN before invoking vfp_save_state().) This patch also addresses a second problem related to FPEXC.EX: After returning from signal handling, the kernel reloads the VFP context from the user mode stack. However, the current code explicitly clears both FPEXC.EX and FPEXC.FP2V during reload. As VFP11 requires these bits to be preserved, this patch disables clearing them for VFP implementations belonging to architecture 1. There should be no negative side effects: the user can set both bits by executing FPU opcodes anyway, and while user code may now place arbitrary values into FPINST and FPINST2 (e.g., non-VFP ARM opcodes) the VFP support code knows which instructions can be emulated, and rejects other opcodes with "unhandled bounce" messages, so there should be no security impact from allowing reloading FPEXC.EX and FPEXC.FP2V. Signed-off-by: Christopher Alexander Tobias Schulze <[email protected]>
1 parent 385efec commit c47e568

File tree

1 file changed

+19
-6
lines changed

1 file changed

+19
-6
lines changed

arch/arm/vfp/vfpmodule.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,11 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
204204
* case the thread migrates to a different CPU. The
205205
* restoring is done lazily.
206206
*/
207-
if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
207+
if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) {
208+
/* vfp_save_state oopses on VFP11 if EX bit set */
209+
fmxr(FPEXC, fpexc & ~FPEXC_EX);
208210
vfp_save_state(vfp_current_hw_state[cpu], fpexc);
211+
}
209212
#endif
210213

211214
/*
@@ -485,13 +488,16 @@ static int vfp_pm_suspend(void)
485488
/* if vfp is on, then save state for resumption */
486489
if (fpexc & FPEXC_EN) {
487490
pr_debug("%s: saving vfp state\n", __func__);
491+
/* vfp_save_state oopses on VFP11 if EX bit set */
492+
fmxr(FPEXC, fpexc & ~FPEXC_EX);
488493
vfp_save_state(&ti->vfpstate, fpexc);
489494

490495
/* disable, just in case */
491496
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
492497
} else if (vfp_current_hw_state[ti->cpu]) {
493498
#ifndef CONFIG_SMP
494-
fmxr(FPEXC, fpexc | FPEXC_EN);
499+
/* vfp_save_state oopses on VFP11 if EX bit set */
500+
fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
495501
vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc);
496502
fmxr(FPEXC, fpexc);
497503
#endif
@@ -554,7 +560,8 @@ void vfp_sync_hwstate(struct thread_info *thread)
554560
/*
555561
* Save the last VFP state on this CPU.
556562
*/
557-
fmxr(FPEXC, fpexc | FPEXC_EN);
563+
/* vfp_save_state oopses on VFP11 if EX bit set */
564+
fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
558565
vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN);
559566
fmxr(FPEXC, fpexc);
560567
}
@@ -620,6 +627,7 @@ int vfp_restore_user_hwstate(struct user_vfp *ufp, struct user_vfp_exc *ufp_exc)
620627
struct thread_info *thread = current_thread_info();
621628
struct vfp_hard_struct *hwstate = &thread->vfpstate.hard;
622629
unsigned long fpexc;
630+
u32 fpsid = fmrx(FPSID);
623631

624632
/* Disable VFP to avoid corrupting the new thread state. */
625633
vfp_flush_hwstate(thread);
@@ -642,8 +650,12 @@ int vfp_restore_user_hwstate(struct user_vfp *ufp, struct user_vfp_exc *ufp_exc)
642650
/* Ensure the VFP is enabled. */
643651
fpexc |= FPEXC_EN;
644652

645-
/* Ensure FPINST2 is invalid and the exception flag is cleared. */
646-
fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
653+
/* Mask FPXEC_EX and FPEXC_FP2V if not required by VFP arch */
654+
if ((fpsid & FPSID_ARCH_MASK) != (1 << FPSID_ARCH_BIT)) {
655+
/* Ensure FPINST2 is invalid and the exception flag is cleared. */
656+
fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
657+
}
658+
647659
hwstate->fpexc = fpexc;
648660

649661
hwstate->fpinst = ufp_exc->fpinst;
@@ -880,7 +892,8 @@ void kernel_neon_begin(void)
880892
cpu = __smp_processor_id();
881893

882894
fpexc = fmrx(FPEXC) | FPEXC_EN;
883-
fmxr(FPEXC, fpexc);
895+
/* vfp_save_state oopses on VFP11 if EX bit set */
896+
fmxr(FPEXC, fpexc & ~FPEXC_EX);
884897

885898
/*
886899
* Save the userland NEON/VFP state. Under UP,

0 commit comments

Comments
 (0)