arm64: Floating point and SIMD
This patch adds support for FP/ASIMD register bank saving and restoring during context switch and FP exception handling to generate SIGFPE. There are 32 128-bit registers and the context switching is currently done non-lazily. Benchmarks on real hardware are required before implementing lazy FP state saving/restoring. Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Tony Lindgren <tony@atomide.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Nicolas Pitre <nico@linaro.org> Acked-by: Olof Johansson <olof@lixom.net> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
This commit is contained in:
		
							
								
								
									
										64
									
								
								arch/arm64/include/asm/fpsimd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								arch/arm64/include/asm/fpsimd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| /* | ||||
|  * Copyright (C) 2012 ARM Ltd. | ||||
|  * | ||||
|  * 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/>. | ||||
|  */ | ||||
| #ifndef __ASM_FP_H | ||||
| #define __ASM_FP_H | ||||
|  | ||||
| #include <asm/ptrace.h> | ||||
|  | ||||
| #ifndef __ASSEMBLY__ | ||||
|  | ||||
| /* | ||||
|  * FP/SIMD storage area has: | ||||
|  *  - FPSR and FPCR | ||||
|  *  - 32 128-bit data registers | ||||
|  * | ||||
|  * Note that user_fp forms a prefix of this structure, which is relied | ||||
|  * upon in the ptrace FP/SIMD accessors. struct user_fpsimd_state must | ||||
|  * form a prefix of struct fpsimd_state. | ||||
|  */ | ||||
| struct fpsimd_state { | ||||
| 	union { | ||||
| 		struct user_fpsimd_state user_fpsimd; | ||||
| 		struct { | ||||
| 			__uint128_t vregs[32]; | ||||
| 			u32 fpsr; | ||||
| 			u32 fpcr; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| #if defined(__KERNEL__) && defined(CONFIG_COMPAT) | ||||
| /* Masks for extracting the FPSR and FPCR from the FPSCR */ | ||||
| #define VFP_FPSCR_STAT_MASK	0xf800009f | ||||
| #define VFP_FPSCR_CTRL_MASK	0x07f79f00 | ||||
| /* | ||||
|  * The VFP state has 32x64-bit registers and a single 32-bit | ||||
|  * control/status register. | ||||
|  */ | ||||
| #define VFP_STATE_SIZE		((32 * 8) + 4) | ||||
| #endif | ||||
|  | ||||
| struct task_struct; | ||||
|  | ||||
| extern void fpsimd_save_state(struct fpsimd_state *state); | ||||
| extern void fpsimd_load_state(struct fpsimd_state *state); | ||||
|  | ||||
| extern void fpsimd_thread_switch(struct task_struct *next); | ||||
| extern void fpsimd_flush_thread(void); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										80
									
								
								arch/arm64/kernel/entry-fpsimd.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								arch/arm64/kernel/entry-fpsimd.S
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| /* | ||||
|  * FP/SIMD state saving and restoring | ||||
|  * | ||||
|  * 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/linkage.h> | ||||
|  | ||||
| #include <asm/assembler.h> | ||||
|  | ||||
| /* | ||||
|  * Save the FP registers. | ||||
|  * | ||||
|  * x0 - pointer to struct fpsimd_state | ||||
|  */ | ||||
| ENTRY(fpsimd_save_state) | ||||
| 	stp	q0, q1, [x0, #16 * 0] | ||||
| 	stp	q2, q3, [x0, #16 * 2] | ||||
| 	stp	q4, q5, [x0, #16 * 4] | ||||
| 	stp	q6, q7, [x0, #16 * 6] | ||||
| 	stp	q8, q9, [x0, #16 * 8] | ||||
| 	stp	q10, q11, [x0, #16 * 10] | ||||
| 	stp	q12, q13, [x0, #16 * 12] | ||||
| 	stp	q14, q15, [x0, #16 * 14] | ||||
| 	stp	q16, q17, [x0, #16 * 16] | ||||
| 	stp	q18, q19, [x0, #16 * 18] | ||||
| 	stp	q20, q21, [x0, #16 * 20] | ||||
| 	stp	q22, q23, [x0, #16 * 22] | ||||
| 	stp	q24, q25, [x0, #16 * 24] | ||||
| 	stp	q26, q27, [x0, #16 * 26] | ||||
| 	stp	q28, q29, [x0, #16 * 28] | ||||
| 	stp	q30, q31, [x0, #16 * 30]! | ||||
| 	mrs	x8, fpsr | ||||
| 	str	w8, [x0, #16 * 2] | ||||
| 	mrs	x8, fpcr | ||||
| 	str	w8, [x0, #16 * 2 + 4] | ||||
| 	ret | ||||
| ENDPROC(fpsimd_save_state) | ||||
|  | ||||
| /* | ||||
|  * Load the FP registers. | ||||
|  * | ||||
|  * x0 - pointer to struct fpsimd_state | ||||
|  */ | ||||
| ENTRY(fpsimd_load_state) | ||||
| 	ldp	q0, q1, [x0, #16 * 0] | ||||
| 	ldp	q2, q3, [x0, #16 * 2] | ||||
| 	ldp	q4, q5, [x0, #16 * 4] | ||||
| 	ldp	q6, q7, [x0, #16 * 6] | ||||
| 	ldp	q8, q9, [x0, #16 * 8] | ||||
| 	ldp	q10, q11, [x0, #16 * 10] | ||||
| 	ldp	q12, q13, [x0, #16 * 12] | ||||
| 	ldp	q14, q15, [x0, #16 * 14] | ||||
| 	ldp	q16, q17, [x0, #16 * 16] | ||||
| 	ldp	q18, q19, [x0, #16 * 18] | ||||
| 	ldp	q20, q21, [x0, #16 * 20] | ||||
| 	ldp	q22, q23, [x0, #16 * 22] | ||||
| 	ldp	q24, q25, [x0, #16 * 24] | ||||
| 	ldp	q26, q27, [x0, #16 * 26] | ||||
| 	ldp	q28, q29, [x0, #16 * 28] | ||||
| 	ldp	q30, q31, [x0, #16 * 30]! | ||||
| 	ldr	w8, [x0, #16 * 2] | ||||
| 	ldr	w9, [x0, #16 * 2 + 4] | ||||
| 	msr	fpsr, x8 | ||||
| 	msr	fpcr, x9 | ||||
| 	ret | ||||
| ENDPROC(fpsimd_load_state) | ||||
							
								
								
									
										106
									
								
								arch/arm64/kernel/fpsimd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								arch/arm64/kernel/fpsimd.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| /* | ||||
|  * 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 <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) | ||||
| { | ||||
| 	memset(¤t->thread.fpsimd_state, 0, sizeof(struct fpsimd_state)); | ||||
| 	fpsimd_load_state(¤t->thread.fpsimd_state); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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); | ||||
		Reference in New Issue
	
	Block a user