123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- From 124370babf0428ec8db2d0ba314105cd9e6ea2c7 Mon Sep 17 00:00:00 2001
- From: Claggy3 <stephen.maclagan@hotmail.com>
- Date: Sat, 11 Feb 2017 14:00:30 +0000
- Subject: [PATCH] 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
- https://github.com/raspberrypi/linux/issues/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 <cat.schulze@alice-dsl.net>
- ---
- arch/arm/vfp/vfpmodule.c | 27 ++++++++++++++++++++-------
- 1 file changed, 20 insertions(+), 7 deletions(-)
- --- a/arch/arm/vfp/vfpmodule.c
- +++ b/arch/arm/vfp/vfpmodule.c
- @@ -183,8 +183,11 @@ static int vfp_notifier(struct notifier_
- * case the thread migrates to a different CPU. The
- * restoring is done lazily.
- */
- - if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
- - vfp_save_state(vfp_current_hw_state[cpu], fpexc);
- + if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) {
- + /* vfp_save_state oopses on VFP11 if EX bit set */
- + fmxr(FPEXC, fpexc & ~FPEXC_EX);
- + vfp_save_state(vfp_current_hw_state[cpu], fpexc);
- + }
- #endif
-
- /*
- @@ -467,13 +470,16 @@ static int vfp_pm_suspend(void)
- /* if vfp is on, then save state for resumption */
- if (fpexc & FPEXC_EN) {
- pr_debug("%s: saving vfp state\n", __func__);
- + /* vfp_save_state oopses on VFP11 if EX bit set */
- + fmxr(FPEXC, fpexc & ~FPEXC_EX);
- vfp_save_state(&ti->vfpstate, fpexc);
-
- /* disable, just in case */
- fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
- } else if (vfp_current_hw_state[ti->cpu]) {
- #ifndef CONFIG_SMP
- - fmxr(FPEXC, fpexc | FPEXC_EN);
- + /* vfp_save_state oopses on VFP11 if EX bit set */
- + fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
- vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc);
- fmxr(FPEXC, fpexc);
- #endif
- @@ -536,7 +542,8 @@ void vfp_sync_hwstate(struct thread_info
- /*
- * Save the last VFP state on this CPU.
- */
- - fmxr(FPEXC, fpexc | FPEXC_EN);
- + /* vfp_save_state oopses on VFP11 if EX bit set */
- + fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
- vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN);
- fmxr(FPEXC, fpexc);
- }
- @@ -608,6 +615,7 @@ int vfp_restore_user_hwstate(struct user
- struct vfp_hard_struct *hwstate = &thread->vfpstate.hard;
- unsigned long fpexc;
- int err = 0;
- + u32 fpsid = fmrx(FPSID);
-
- /* Disable VFP to avoid corrupting the new thread state. */
- vfp_flush_hwstate(thread);
- @@ -631,8 +639,12 @@ int vfp_restore_user_hwstate(struct user
- /* Ensure the VFP is enabled. */
- fpexc |= FPEXC_EN;
-
- - /* Ensure FPINST2 is invalid and the exception flag is cleared. */
- - fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
- + /* Mask FPXEC_EX and FPEXC_FP2V if not required by VFP arch */
- + if ((fpsid & FPSID_ARCH_MASK) != (1 << FPSID_ARCH_BIT)) {
- + /* Ensure FPINST2 is invalid and the exception flag is cleared. */
- + fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
- + }
- +
- hwstate->fpexc = fpexc;
-
- __get_user_error(hwstate->fpinst, &ufp_exc->fpinst, err);
- @@ -702,7 +714,8 @@ void kernel_neon_begin(void)
- cpu = get_cpu();
-
- fpexc = fmrx(FPEXC) | FPEXC_EN;
- - fmxr(FPEXC, fpexc);
- + /* vfp_save_state oopses on VFP11 if EX bit set */
- + fmxr(FPEXC, fpexc & ~FPEXC_EX);
-
- /*
- * Save the userland NEON/VFP state. Under UP,
|