 6db83cea1c
			
		
	
	
	6db83cea1c
	
	
	
		
			
			If context switching happens during executing fpsimd_flush_thread(), stale value in FPSIMD registers will be saved into current thread's fpsimd_state by fpsimd_thread_switch(). That may cause invalid initialization state for the new process, so disable preemption when executing fpsimd_flush_thread(). Signed-off-by: Jiang Liu <jiang.liu@huawei.com> Cc: Jiang Liu <liuj97@gmail.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
		
			
				
	
	
		
			136 lines
		
	
	
	
		
			3.1 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
	
		
			3.1 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * FP/SIMD context switching and fault handling
 | |
|  *
 | |
|  * Copyright (C) 2012 ARM Ltd.
 | |
|  * Author: Catalin Marinas <catalin.marinas@arm.com>
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License version 2 as
 | |
|  * published by the Free Software Foundation.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/sched.h>
 | |
| #include <linux/signal.h>
 | |
| #include <linux/hardirq.h>
 | |
| 
 | |
| #include <asm/fpsimd.h>
 | |
| #include <asm/cputype.h>
 | |
| 
 | |
| #define FPEXC_IOF	(1 << 0)
 | |
| #define FPEXC_DZF	(1 << 1)
 | |
| #define FPEXC_OFF	(1 << 2)
 | |
| #define FPEXC_UFF	(1 << 3)
 | |
| #define FPEXC_IXF	(1 << 4)
 | |
| #define FPEXC_IDF	(1 << 7)
 | |
| 
 | |
| /*
 | |
|  * Trapped FP/ASIMD access.
 | |
|  */
 | |
| void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs)
 | |
| {
 | |
| 	/* TODO: implement lazy context saving/restoring */
 | |
| 	WARN_ON(1);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Raise a SIGFPE for the current process.
 | |
|  */
 | |
| void do_fpsimd_exc(unsigned int esr, struct pt_regs *regs)
 | |
| {
 | |
| 	siginfo_t info;
 | |
| 	unsigned int si_code = 0;
 | |
| 
 | |
| 	if (esr & FPEXC_IOF)
 | |
| 		si_code = FPE_FLTINV;
 | |
| 	else if (esr & FPEXC_DZF)
 | |
| 		si_code = FPE_FLTDIV;
 | |
| 	else if (esr & FPEXC_OFF)
 | |
| 		si_code = FPE_FLTOVF;
 | |
| 	else if (esr & FPEXC_UFF)
 | |
| 		si_code = FPE_FLTUND;
 | |
| 	else if (esr & FPEXC_IXF)
 | |
| 		si_code = FPE_FLTRES;
 | |
| 
 | |
| 	memset(&info, 0, sizeof(info));
 | |
| 	info.si_signo = SIGFPE;
 | |
| 	info.si_code = si_code;
 | |
| 	info.si_addr = (void __user *)instruction_pointer(regs);
 | |
| 
 | |
| 	send_sig_info(SIGFPE, &info, current);
 | |
| }
 | |
| 
 | |
| void fpsimd_thread_switch(struct task_struct *next)
 | |
| {
 | |
| 	/* check if not kernel threads */
 | |
| 	if (current->mm)
 | |
| 		fpsimd_save_state(¤t->thread.fpsimd_state);
 | |
| 	if (next->mm)
 | |
| 		fpsimd_load_state(&next->thread.fpsimd_state);
 | |
| }
 | |
| 
 | |
| void fpsimd_flush_thread(void)
 | |
| {
 | |
| 	preempt_disable();
 | |
| 	memset(¤t->thread.fpsimd_state, 0, sizeof(struct fpsimd_state));
 | |
| 	fpsimd_load_state(¤t->thread.fpsimd_state);
 | |
| 	preempt_enable();
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_KERNEL_MODE_NEON
 | |
| 
 | |
| /*
 | |
|  * Kernel-side NEON support functions
 | |
|  */
 | |
| void kernel_neon_begin(void)
 | |
| {
 | |
| 	/* Avoid using the NEON in interrupt context */
 | |
| 	BUG_ON(in_interrupt());
 | |
| 	preempt_disable();
 | |
| 
 | |
| 	if (current->mm)
 | |
| 		fpsimd_save_state(¤t->thread.fpsimd_state);
 | |
| }
 | |
| EXPORT_SYMBOL(kernel_neon_begin);
 | |
| 
 | |
| void kernel_neon_end(void)
 | |
| {
 | |
| 	if (current->mm)
 | |
| 		fpsimd_load_state(¤t->thread.fpsimd_state);
 | |
| 
 | |
| 	preempt_enable();
 | |
| }
 | |
| EXPORT_SYMBOL(kernel_neon_end);
 | |
| 
 | |
| #endif /* CONFIG_KERNEL_MODE_NEON */
 | |
| 
 | |
| /*
 | |
|  * FP/SIMD support code initialisation.
 | |
|  */
 | |
| static int __init fpsimd_init(void)
 | |
| {
 | |
| 	u64 pfr = read_cpuid(ID_AA64PFR0_EL1);
 | |
| 
 | |
| 	if (pfr & (0xf << 16)) {
 | |
| 		pr_notice("Floating-point is not implemented\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 	elf_hwcap |= HWCAP_FP;
 | |
| 
 | |
| 	if (pfr & (0xf << 20))
 | |
| 		pr_notice("Advanced SIMD is not implemented\n");
 | |
| 	else
 | |
| 		elf_hwcap |= HWCAP_ASIMD;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| late_initcall(fpsimd_init);
 |