/* * FP/SIMD context switching and fault handling * * Copyright (C) 2012 ARM Ltd. * Author: Catalin Marinas * * 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 . */ #include #include #include #include #include #include #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(); } /* * 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);