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 #include <linux/hardirq.h> 25 26 #include <asm/fpsimd.h> 27 #include <asm/cputype.h> 28 29 #define FPEXC_IOF (1 << 0) 30 #define FPEXC_DZF (1 << 1) 31 #define FPEXC_OFF (1 << 2) 32 #define FPEXC_UFF (1 << 3) 33 #define FPEXC_IXF (1 << 4) 34 #define FPEXC_IDF (1 << 7) 35 36 /* 37 * Trapped FP/ASIMD access. 38 */ 39 void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs) 40 { 41 /* TODO: implement lazy context saving/restoring */ 42 WARN_ON(1); 43 } 44 45 /* 46 * Raise a SIGFPE for the current process. 47 */ 48 void do_fpsimd_exc(unsigned int esr, struct pt_regs *regs) 49 { 50 siginfo_t info; 51 unsigned int si_code = 0; 52 53 if (esr & FPEXC_IOF) 54 si_code = FPE_FLTINV; 55 else if (esr & FPEXC_DZF) 56 si_code = FPE_FLTDIV; 57 else if (esr & FPEXC_OFF) 58 si_code = FPE_FLTOVF; 59 else if (esr & FPEXC_UFF) 60 si_code = FPE_FLTUND; 61 else if (esr & FPEXC_IXF) 62 si_code = FPE_FLTRES; 63 64 memset(&info, 0, sizeof(info)); 65 info.si_signo = SIGFPE; 66 info.si_code = si_code; 67 info.si_addr = (void __user *)instruction_pointer(regs); 68 69 send_sig_info(SIGFPE, &info, current); 70 } 71 72 void fpsimd_thread_switch(struct task_struct *next) 73 { 74 /* check if not kernel threads */ 75 if (current->mm) 76 fpsimd_save_state(¤t->thread.fpsimd_state); 77 if (next->mm) 78 fpsimd_load_state(&next->thread.fpsimd_state); 79 } 80 81 void fpsimd_flush_thread(void) 82 { 83 memset(¤t->thread.fpsimd_state, 0, sizeof(struct fpsimd_state)); 84 fpsimd_load_state(¤t->thread.fpsimd_state); 85 } 86 87 #ifdef CONFIG_KERNEL_MODE_NEON 88 89 /* 90 * Kernel-side NEON support functions 91 */ 92 void kernel_neon_begin(void) 93 { 94 /* Avoid using the NEON in interrupt context */ 95 BUG_ON(in_interrupt()); 96 preempt_disable(); 97 98 if (current->mm) 99 fpsimd_save_state(¤t->thread.fpsimd_state); 100 } 101 EXPORT_SYMBOL(kernel_neon_begin); 102 103 void kernel_neon_end(void) 104 { 105 if (current->mm) 106 fpsimd_load_state(¤t->thread.fpsimd_state); 107 108 preempt_enable(); 109 } 110 EXPORT_SYMBOL(kernel_neon_end); 111 112 #endif /* CONFIG_KERNEL_MODE_NEON */ 113 114 /* 115 * FP/SIMD support code initialisation. 116 */ 117 static int __init fpsimd_init(void) 118 { 119 u64 pfr = read_cpuid(ID_AA64PFR0_EL1); 120 121 if (pfr & (0xf << 16)) { 122 pr_notice("Floating-point is not implemented\n"); 123 return 0; 124 } 125 elf_hwcap |= HWCAP_FP; 126 127 if (pfr & (0xf << 20)) 128 pr_notice("Advanced SIMD is not implemented\n"); 129 else 130 elf_hwcap |= HWCAP_ASIMD; 131 132 return 0; 133 } 134 late_initcall(fpsimd_init); 135