linux/arch/nds32/kernel/fpu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (C) 2005-2018 Andes Technology Corporation
   3
   4#include <linux/sched.h>
   5#include <linux/signal.h>
   6#include <linux/sched/signal.h>
   7#include <asm/processor.h>
   8#include <asm/user.h>
   9#include <asm/io.h>
  10#include <asm/bitfield.h>
  11#include <asm/fpu.h>
  12
  13const struct fpu_struct init_fpuregs = {
  14        .fd_regs = {[0 ... 31] = sNAN64},
  15        .fpcsr = FPCSR_INIT,
  16#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
  17        .UDF_IEX_trap = 0
  18#endif
  19};
  20
  21void save_fpu(struct task_struct *tsk)
  22{
  23        unsigned int fpcfg, fpcsr;
  24
  25        enable_fpu();
  26        fpcfg = ((__nds32__fmfcfg() & FPCFG_mskFREG) >> FPCFG_offFREG);
  27        switch (fpcfg) {
  28        case SP32_DP32_reg:
  29                asm volatile ("fsdi $fd31, [%0+0xf8]\n\t"
  30                              "fsdi $fd30, [%0+0xf0]\n\t"
  31                              "fsdi $fd29, [%0+0xe8]\n\t"
  32                              "fsdi $fd28, [%0+0xe0]\n\t"
  33                              "fsdi $fd27, [%0+0xd8]\n\t"
  34                              "fsdi $fd26, [%0+0xd0]\n\t"
  35                              "fsdi $fd25, [%0+0xc8]\n\t"
  36                              "fsdi $fd24, [%0+0xc0]\n\t"
  37                              "fsdi $fd23, [%0+0xb8]\n\t"
  38                              "fsdi $fd22, [%0+0xb0]\n\t"
  39                              "fsdi $fd21, [%0+0xa8]\n\t"
  40                              "fsdi $fd20, [%0+0xa0]\n\t"
  41                              "fsdi $fd19, [%0+0x98]\n\t"
  42                              "fsdi $fd18, [%0+0x90]\n\t"
  43                              "fsdi $fd17, [%0+0x88]\n\t"
  44                              "fsdi $fd16, [%0+0x80]\n\t"
  45                              : /* no output */
  46                              : "r" (&tsk->thread.fpu)
  47                              : "memory");
  48                fallthrough;
  49        case SP32_DP16_reg:
  50                asm volatile ("fsdi $fd15, [%0+0x78]\n\t"
  51                              "fsdi $fd14, [%0+0x70]\n\t"
  52                              "fsdi $fd13, [%0+0x68]\n\t"
  53                              "fsdi $fd12, [%0+0x60]\n\t"
  54                              "fsdi $fd11, [%0+0x58]\n\t"
  55                              "fsdi $fd10, [%0+0x50]\n\t"
  56                              "fsdi $fd9,  [%0+0x48]\n\t"
  57                              "fsdi $fd8,  [%0+0x40]\n\t"
  58                              : /* no output */
  59                              : "r" (&tsk->thread.fpu)
  60                              : "memory");
  61                fallthrough;
  62        case SP16_DP8_reg:
  63                asm volatile ("fsdi $fd7,  [%0+0x38]\n\t"
  64                              "fsdi $fd6,  [%0+0x30]\n\t"
  65                              "fsdi $fd5,  [%0+0x28]\n\t"
  66                              "fsdi $fd4,  [%0+0x20]\n\t"
  67                              : /* no output */
  68                              : "r" (&tsk->thread.fpu)
  69                              : "memory");
  70                fallthrough;
  71        case SP8_DP4_reg:
  72                asm volatile ("fsdi $fd3,  [%1+0x18]\n\t"
  73                              "fsdi $fd2,  [%1+0x10]\n\t"
  74                              "fsdi $fd1,  [%1+0x8]\n\t"
  75                              "fsdi $fd0,  [%1+0x0]\n\t"
  76                              "fmfcsr   %0\n\t"
  77                              "swi  %0, [%1+0x100]\n\t"
  78                              : "=&r" (fpcsr)
  79                              : "r"(&tsk->thread.fpu)
  80                              : "memory");
  81        }
  82        disable_fpu();
  83}
  84
  85void load_fpu(const struct fpu_struct *fpregs)
  86{
  87        unsigned int fpcfg, fpcsr;
  88
  89        enable_fpu();
  90        fpcfg = ((__nds32__fmfcfg() & FPCFG_mskFREG) >> FPCFG_offFREG);
  91        switch (fpcfg) {
  92        case SP32_DP32_reg:
  93                asm volatile ("fldi $fd31, [%0+0xf8]\n\t"
  94                              "fldi $fd30, [%0+0xf0]\n\t"
  95                              "fldi $fd29, [%0+0xe8]\n\t"
  96                              "fldi $fd28, [%0+0xe0]\n\t"
  97                              "fldi $fd27, [%0+0xd8]\n\t"
  98                              "fldi $fd26, [%0+0xd0]\n\t"
  99                              "fldi $fd25, [%0+0xc8]\n\t"
 100                              "fldi $fd24, [%0+0xc0]\n\t"
 101                              "fldi $fd23, [%0+0xb8]\n\t"
 102                              "fldi $fd22, [%0+0xb0]\n\t"
 103                              "fldi $fd21, [%0+0xa8]\n\t"
 104                              "fldi $fd20, [%0+0xa0]\n\t"
 105                              "fldi $fd19, [%0+0x98]\n\t"
 106                              "fldi $fd18, [%0+0x90]\n\t"
 107                              "fldi $fd17, [%0+0x88]\n\t"
 108                              "fldi $fd16, [%0+0x80]\n\t"
 109                              : /* no output */
 110                              : "r" (fpregs));
 111                fallthrough;
 112        case SP32_DP16_reg:
 113                asm volatile ("fldi $fd15, [%0+0x78]\n\t"
 114                              "fldi $fd14, [%0+0x70]\n\t"
 115                              "fldi $fd13, [%0+0x68]\n\t"
 116                              "fldi $fd12, [%0+0x60]\n\t"
 117                              "fldi $fd11, [%0+0x58]\n\t"
 118                              "fldi $fd10, [%0+0x50]\n\t"
 119                              "fldi $fd9,  [%0+0x48]\n\t"
 120                              "fldi $fd8,  [%0+0x40]\n\t"
 121                              : /* no output */
 122                              : "r" (fpregs));
 123                fallthrough;
 124        case SP16_DP8_reg:
 125                asm volatile ("fldi $fd7,  [%0+0x38]\n\t"
 126                              "fldi $fd6,  [%0+0x30]\n\t"
 127                              "fldi $fd5,  [%0+0x28]\n\t"
 128                              "fldi $fd4,  [%0+0x20]\n\t"
 129                              : /* no output */
 130                              : "r" (fpregs));
 131                fallthrough;
 132        case SP8_DP4_reg:
 133                asm volatile ("fldi $fd3,  [%1+0x18]\n\t"
 134                              "fldi $fd2,  [%1+0x10]\n\t"
 135                              "fldi $fd1,  [%1+0x8]\n\t"
 136                              "fldi $fd0,  [%1+0x0]\n\t"
 137                              "lwi  %0, [%1+0x100]\n\t"
 138                              "fmtcsr   %0\n\t":"=&r" (fpcsr)
 139                              : "r"(fpregs));
 140        }
 141        disable_fpu();
 142}
 143void store_fpu_for_suspend(void)
 144{
 145#ifdef CONFIG_LAZY_FPU
 146        if (last_task_used_math != NULL)
 147                save_fpu(last_task_used_math);
 148        last_task_used_math = NULL;
 149#else
 150        if (!used_math())
 151                return;
 152        unlazy_fpu(current);
 153#endif
 154        clear_fpu(task_pt_regs(current));
 155}
 156inline void do_fpu_context_switch(struct pt_regs *regs)
 157{
 158        /* Enable to use FPU. */
 159
 160        if (!user_mode(regs)) {
 161                pr_err("BUG: FPU is used in kernel mode.\n");
 162                BUG();
 163                return;
 164        }
 165
 166        enable_ptreg_fpu(regs);
 167#ifdef CONFIG_LAZY_FPU  //Lazy FPU is used
 168        if (last_task_used_math == current)
 169                return;
 170        if (last_task_used_math != NULL)
 171                /* Other processes fpu state, save away */
 172                save_fpu(last_task_used_math);
 173        last_task_used_math = current;
 174#endif
 175        if (used_math()) {
 176                load_fpu(&current->thread.fpu);
 177        } else {
 178                /* First time FPU user.  */
 179                load_fpu(&init_fpuregs);
 180#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
 181                current->thread.fpu.UDF_IEX_trap = init_fpuregs.UDF_IEX_trap;
 182#endif
 183                set_used_math();
 184        }
 185
 186}
 187
 188inline void fill_sigfpe_signo(unsigned int fpcsr, int *signo)
 189{
 190        if (fpcsr & FPCSR_mskOVFT)
 191                *signo = FPE_FLTOVF;
 192#ifndef CONFIG_SUPPORT_DENORMAL_ARITHMETIC
 193        else if (fpcsr & FPCSR_mskUDFT)
 194                *signo = FPE_FLTUND;
 195#endif
 196        else if (fpcsr & FPCSR_mskIVOT)
 197                *signo = FPE_FLTINV;
 198        else if (fpcsr & FPCSR_mskDBZT)
 199                *signo = FPE_FLTDIV;
 200        else if (fpcsr & FPCSR_mskIEXT)
 201                *signo = FPE_FLTRES;
 202}
 203
 204inline void handle_fpu_exception(struct pt_regs *regs)
 205{
 206        unsigned int fpcsr;
 207        int si_code = 0, si_signo = SIGFPE;
 208#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
 209        unsigned long redo_except = FPCSR_mskDNIT|FPCSR_mskUDFT|FPCSR_mskIEXT;
 210#else
 211        unsigned long redo_except = FPCSR_mskDNIT;
 212#endif
 213
 214        lose_fpu();
 215        fpcsr = current->thread.fpu.fpcsr;
 216
 217        if (fpcsr & redo_except) {
 218                si_signo = do_fpuemu(regs, &current->thread.fpu);
 219                fpcsr = current->thread.fpu.fpcsr;
 220                if (!si_signo) {
 221                        current->thread.fpu.fpcsr &= ~(redo_except);
 222                        goto done;
 223                }
 224        } else if (fpcsr & FPCSR_mskRIT) {
 225                if (!user_mode(regs))
 226                        do_exit(SIGILL);
 227                si_signo = SIGILL;
 228        }
 229
 230        switch (si_signo) {
 231        case SIGFPE:
 232                fill_sigfpe_signo(fpcsr, &si_code);
 233                break;
 234        case SIGILL:
 235                show_regs(regs);
 236                si_code = ILL_COPROC;
 237                break;
 238        case SIGBUS:
 239                si_code = BUS_ADRERR;
 240                break;
 241        default:
 242                break;
 243        }
 244
 245        force_sig_fault(si_signo, si_code,
 246                        (void __user *)instruction_pointer(regs));
 247done:
 248        own_fpu();
 249}
 250
 251bool do_fpu_exception(unsigned int subtype, struct pt_regs *regs)
 252{
 253        int done = true;
 254        /* Coprocessor disabled exception */
 255        if (subtype == FPU_DISABLE_EXCEPTION) {
 256                preempt_disable();
 257                do_fpu_context_switch(regs);
 258                preempt_enable();
 259        }
 260        /* Coprocessor exception such as underflow and overflow */
 261        else if (subtype == FPU_EXCEPTION)
 262                handle_fpu_exception(regs);
 263        else
 264                done = false;
 265        return done;
 266}
 267