Skip to content

Commit 28439f3

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 ce74fc8 commit 28439f3

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
@@ -179,8 +179,11 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
179179
* case the thread migrates to a different CPU. The
180180
* restoring is done lazily.
181181
*/
182-
if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
182+
if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) {
183+
/* vfp_save_state oopses on VFP11 if EX bit set */
184+
fmxr(FPEXC, fpexc & ~FPEXC_EX);
183185
vfp_save_state(vfp_current_hw_state[cpu], fpexc);
186+
}
184187
#endif
185188

186189
/*
@@ -463,13 +466,16 @@ static int vfp_pm_suspend(void)
463466
/* if vfp is on, then save state for resumption */
464467
if (fpexc & FPEXC_EN) {
465468
pr_debug("%s: saving vfp state\n", __func__);
469+
/* vfp_save_state oopses on VFP11 if EX bit set */
470+
fmxr(FPEXC, fpexc & ~FPEXC_EX);
466471
vfp_save_state(&ti->vfpstate, fpexc);
467472

468473
/* disable, just in case */
469474
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
470475
} else if (vfp_current_hw_state[ti->cpu]) {
471476
#ifndef CONFIG_SMP
472-
fmxr(FPEXC, fpexc | FPEXC_EN);
477+
/* vfp_save_state oopses on VFP11 if EX bit set */
478+
fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
473479
vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc);
474480
fmxr(FPEXC, fpexc);
475481
#endif
@@ -532,7 +538,8 @@ void vfp_sync_hwstate(struct thread_info *thread)
532538
/*
533539
* Save the last VFP state on this CPU.
534540
*/
535-
fmxr(FPEXC, fpexc | FPEXC_EN);
541+
/* vfp_save_state oopses on VFP11 if EX bit set */
542+
fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
536543
vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN);
537544
fmxr(FPEXC, fpexc);
538545
}
@@ -604,6 +611,7 @@ int vfp_restore_user_hwstate(struct user_vfp __user *ufp,
604611
struct vfp_hard_struct *hwstate = &thread->vfpstate.hard;
605612
unsigned long fpexc;
606613
int err = 0;
614+
u32 fpsid = fmrx(FPSID);
607615

608616
/* Disable VFP to avoid corrupting the new thread state. */
609617
vfp_flush_hwstate(thread);
@@ -627,8 +635,12 @@ int vfp_restore_user_hwstate(struct user_vfp __user *ufp,
627635
/* Ensure the VFP is enabled. */
628636
fpexc |= FPEXC_EN;
629637

630-
/* Ensure FPINST2 is invalid and the exception flag is cleared. */
631-
fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
638+
/* Mask FPXEC_EX and FPEXC_FP2V if not required by VFP arch */
639+
if ((fpsid & FPSID_ARCH_MASK) != (1 << FPSID_ARCH_BIT)) {
640+
/* Ensure FPINST2 is invalid and the exception flag is cleared. */
641+
fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
642+
}
643+
632644
hwstate->fpexc = fpexc;
633645

634646
__get_user_error(hwstate->fpinst, &ufp_exc->fpinst, err);
@@ -698,7 +710,8 @@ void kernel_neon_begin(void)
698710
cpu = get_cpu();
699711

700712
fpexc = fmrx(FPEXC) | FPEXC_EN;
701-
fmxr(FPEXC, fpexc);
713+
/* vfp_save_state oopses on VFP11 if EX bit set */
714+
fmxr(FPEXC, fpexc & ~FPEXC_EX);
702715

703716
/*
704717
* Save the userland NEON/VFP state. Under UP,

0 commit comments

Comments
 (0)