Skip to content

Commit c71c593

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 f2486c9 commit c71c593

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
@@ -176,8 +176,11 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
176176
* case the thread migrates to a different CPU. The
177177
* restoring is done lazily.
178178
*/
179-
if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
179+
if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) {
180+
/* vfp_save_state oopses on VFP11 if EX bit set */
181+
fmxr(FPEXC, fpexc & ~FPEXC_EX);
180182
vfp_save_state(vfp_current_hw_state[cpu], fpexc);
183+
}
181184
#endif
182185

183186
/*
@@ -451,13 +454,16 @@ static int vfp_pm_suspend(void)
451454
/* if vfp is on, then save state for resumption */
452455
if (fpexc & FPEXC_EN) {
453456
pr_debug("%s: saving vfp state\n", __func__);
457+
/* vfp_save_state oopses on VFP11 if EX bit set */
458+
fmxr(FPEXC, fpexc & ~FPEXC_EX);
454459
vfp_save_state(&ti->vfpstate, fpexc);
455460

456461
/* disable, just in case */
457462
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
458463
} else if (vfp_current_hw_state[ti->cpu]) {
459464
#ifndef CONFIG_SMP
460-
fmxr(FPEXC, fpexc | FPEXC_EN);
465+
/* vfp_save_state oopses on VFP11 if EX bit set */
466+
fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
461467
vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc);
462468
fmxr(FPEXC, fpexc);
463469
#endif
@@ -522,7 +528,8 @@ void vfp_sync_hwstate(struct thread_info *thread)
522528
/*
523529
* Save the last VFP state on this CPU.
524530
*/
525-
fmxr(FPEXC, fpexc | FPEXC_EN);
531+
/* vfp_save_state oopses on VFP11 if EX bit set */
532+
fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
526533
vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN);
527534
fmxr(FPEXC, fpexc);
528535
}
@@ -589,6 +596,7 @@ int vfp_restore_user_hwstate(struct user_vfp *ufp, struct user_vfp_exc *ufp_exc)
589596
struct thread_info *thread = current_thread_info();
590597
struct vfp_hard_struct *hwstate = &thread->vfpstate.hard;
591598
unsigned long fpexc;
599+
u32 fpsid = fmrx(FPSID);
592600

593601
/* Disable VFP to avoid corrupting the new thread state. */
594602
vfp_flush_hwstate(thread);
@@ -611,8 +619,12 @@ int vfp_restore_user_hwstate(struct user_vfp *ufp, struct user_vfp_exc *ufp_exc)
611619
/* Ensure the VFP is enabled. */
612620
fpexc |= FPEXC_EN;
613621

614-
/* Ensure FPINST2 is invalid and the exception flag is cleared. */
615-
fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
622+
/* Mask FPXEC_EX and FPEXC_FP2V if not required by VFP arch */
623+
if ((fpsid & FPSID_ARCH_MASK) != (1 << FPSID_ARCH_BIT)) {
624+
/* Ensure FPINST2 is invalid and the exception flag is cleared. */
625+
fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
626+
}
627+
616628
hwstate->fpexc = fpexc;
617629

618630
hwstate->fpinst = ufp_exc->fpinst;
@@ -848,7 +860,8 @@ void kernel_neon_begin(void)
848860
cpu = __smp_processor_id();
849861

850862
fpexc = fmrx(FPEXC) | FPEXC_EN;
851-
fmxr(FPEXC, fpexc);
863+
/* vfp_save_state oopses on VFP11 if EX bit set */
864+
fmxr(FPEXC, fpexc & ~FPEXC_EX);
852865

853866
/*
854867
* Save the userland NEON/VFP state. Under UP,

0 commit comments

Comments
 (0)