mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			142 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 3f1530ac551baefb56f0e6e0cd7dc1d0d3e17076 Mon Sep 17 00:00:00 2001
 | |
| From: Claggy3 <stephen.maclagan@hotmail.com>
 | |
| Date: Sat, 11 Feb 2017 14:00:30 +0000
 | |
| Subject: [PATCH 031/277] 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 | 25 +++++++++++++++++++------
 | |
|  1 file changed, 19 insertions(+), 6 deletions(-)
 | |
| 
 | |
| diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
 | |
| index a71a48e71fff..d6c0a5a0a5ae 100644
 | |
| --- a/arch/arm/vfp/vfpmodule.c
 | |
| +++ b/arch/arm/vfp/vfpmodule.c
 | |
| @@ -179,8 +179,11 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
 | |
|  		 * case the thread migrates to a different CPU. The
 | |
|  		 * restoring is done lazily.
 | |
|  		 */
 | |
| -		if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
 | |
| +		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
 | |
|  
 | |
|  		/*
 | |
| @@ -463,13 +466,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
 | |
| @@ -532,7 +538,8 @@ void vfp_sync_hwstate(struct thread_info *thread)
 | |
|  		/*
 | |
|  		 * 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);
 | |
|  	}
 | |
| @@ -604,6 +611,7 @@ int vfp_restore_user_hwstate(struct user_vfp __user *ufp,
 | |
|  	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);
 | |
| @@ -627,8 +635,12 @@ int vfp_restore_user_hwstate(struct user_vfp __user *ufp,
 | |
|  	/* 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);
 | |
| @@ -698,7 +710,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,
 | |
| -- 
 | |
| 2.16.1
 | |
| 
 |