Linux-libre 5.4.49-gnu
[librecmc/linux-libre.git] / arch / nds32 / kernel / fpu.c
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
13 const 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
21 void 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                 /* fall through */
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                 /* fall through */
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                 /* fall through */
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
85 void 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                 /* fall through */
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                 /* fall through */
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                 /* fall through */
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 }
143 void 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 }
156 inline 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
188 inline 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
204 inline 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));
247 done:
248         own_fpu();
249 }
250
251 bool 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 }