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 preempt_disable(); 84 memset(¤t->thread.fpsimd_state, 0, sizeof(struct fpsimd_state)); 85 fpsimd_load_state(¤t->thread.fpsimd_state); 86 preempt_enable(); 87 } 88 89 #ifdef CONFIG_KERNEL_MODE_NEON 90 91 /* 92 * Kernel-side NEON support functions 93 */ 94 void kernel_neon_begin(void) 95 { 96 /* Avoid using the NEON in interrupt context */ 97 BUG_ON(in_interrupt()); 98 preempt_disable(); 99 100 if (current->mm) 101 fpsimd_save_state(¤t->thread.fpsimd_state); 102 } 103 EXPORT_SYMBOL(kernel_neon_begin); 104 105 void kernel_neon_end(void) 106 { 107 if (current->mm) 108 fpsimd_load_state(¤t->thread.fpsimd_state); 109 110 preempt_enable(); 111 } 112 EXPORT_SYMBOL(kernel_neon_end); 113 114 #endif /* CONFIG_KERNEL_MODE_NEON */ 115 116 /* 117 * FP/SIMD support code initialisation. 118 */ 119 static int __init fpsimd_init(void) 120 { 121 u64 pfr = read_cpuid(ID_AA64PFR0_EL1); 122 123 if (pfr & (0xf << 16)) { 124 pr_notice("Floating-point is not implemented\n"); 125 return 0; 126 } 127 elf_hwcap |= HWCAP_FP; 128 129 if (pfr & (0xf << 20)) 130 pr_notice("Advanced SIMD is not implemented\n"); 131 else 132 elf_hwcap |= HWCAP_ASIMD; 133 134 return 0; 135 } 136 late_initcall(fpsimd_init); 137