linux/arch/arm64/kernel/fpsimd.c
<<
>>
Prefs
   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 */
  38void 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 */
  47void 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
  71void fpsimd_thread_switch(struct task_struct *next)
  72{
  73        /* check if not kernel threads */
  74        if (current->mm)
  75                fpsimd_save_state(&current->thread.fpsimd_state);
  76        if (next->mm)
  77                fpsimd_load_state(&next->thread.fpsimd_state);
  78}
  79
  80void fpsimd_flush_thread(void)
  81{
  82        memset(&current->thread.fpsimd_state, 0, sizeof(struct fpsimd_state));
  83        fpsimd_load_state(&current->thread.fpsimd_state);
  84}
  85
  86/*
  87 * FP/SIMD support code initialisation.
  88 */
  89static 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}
 106late_initcall(fpsimd_init);
 107