1 /* 2 * FP/SIMD context switching and fault handling 3 * 4 * Copyright (C) 2012 ARM Ltd. 5 * Author: Catalin Marinas <catalin.marinas@arm.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <linux/kernel.h> 21 #include <linux/init.h> 22 #include <linux/sched.h> 23 #include <linux/signal.h> 24 25 #include <asm/fpsimd.h> 26 #include <asm/cputype.h> 27 28 #define FPEXC_IOF (1 << 0) 29 #define FPEXC_DZF (1 << 1) 30 #define FPEXC_OFF (1 << 2) 31 #define FPEXC_UFF (1 << 3) 32 #define FPEXC_IXF (1 << 4) 33 #define FPEXC_IDF (1 << 7) 34 35 /* 36 * Trapped FP/ASIMD access. 37 */ 38 void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs) 39 { 40 /* TODO: implement lazy context saving/restoring */ 41 WARN_ON(1); 42 } 43 44 /* 45 * Raise a SIGFPE for the current process. 46 */ 47 void do_fpsimd_exc(unsigned int esr, struct pt_regs *regs) 48 { 49 siginfo_t info; 50 unsigned int si_code = 0; 51 52 if (esr & FPEXC_IOF) 53 si_code = FPE_FLTINV; 54 else if (esr & FPEXC_DZF) 55 si_code = FPE_FLTDIV; 56 else if (esr & FPEXC_OFF) 57 si_code = FPE_FLTOVF; 58 else if (esr & FPEXC_UFF) 59 si_code = FPE_FLTUND; 60 else if (esr & FPEXC_IXF) 61 si_code = FPE_FLTRES; 62 63 memset(&info, 0, sizeof(info)); 64 info.si_signo = SIGFPE; 65 info.si_code = si_code; 66 info.si_addr = (void __user *)instruction_pointer(regs); 67 68 send_sig_info(SIGFPE, &info, current); 69 } 70 71 void fpsimd_thread_switch(struct task_struct *next) 72 { 73 /* check if not kernel threads */ 74 if (current->mm) 75 fpsimd_save_state(¤t->thread.fpsimd_state); 76 if (next->mm) 77 fpsimd_load_state(&next->thread.fpsimd_state); 78 } 79 80 void fpsimd_flush_thread(void) 81 { 82 memset(¤t->thread.fpsimd_state, 0, sizeof(struct fpsimd_state)); 83 fpsimd_load_state(¤t->thread.fpsimd_state); 84 } 85 86 /* 87 * FP/SIMD support code initialisation. 88 */ 89 static int __init fpsimd_init(void) 90 { 91 u64 pfr = read_cpuid(ID_AA64PFR0_EL1); 92 93 if (pfr & (0xf << 16)) { 94 pr_notice("Floating-point is not implemented\n"); 95 return 0; 96 } 97 elf_hwcap |= HWCAP_FP; 98 99 if (pfr & (0xf << 20)) 100 pr_notice("Advanced SIMD is not implemented\n"); 101 else 102 elf_hwcap |= HWCAP_ASIMD; 103 104 return 0; 105 } 106 late_initcall(fpsimd_init); 107