blob: fddd40c7a16f9880d2082cb71aa3431ac4192ac5 [file] [log] [blame]
Vincent Chene46bf832018-11-22 11:14:34 +08001// 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},
Vincent Chen44e92e02018-11-22 11:14:36 +080015 .fpcsr = FPCSR_INIT,
16#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
17 .UDF_trap = 0
18#endif
Vincent Chene46bf832018-11-22 11:14:34 +080019};
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 /* 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
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 /* 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}
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);
Vincent Chen44e92e02018-11-22 11:14:36 +0800180#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
181 current->thread.fpu.UDF_trap = init_fpuregs.UDF_trap;
182#endif
Vincent Chene46bf832018-11-22 11:14:34 +0800183 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;
Vincent Chen44e92e02018-11-22 11:14:36 +0800192#ifndef CONFIG_SUPPORT_DENORMAL_ARITHMETIC
Vincent Chen1ac83252018-11-22 11:14:35 +0800193 else if (fpcsr & FPCSR_mskUDFT)
194 *signo = FPE_FLTUND;
Vincent Chen44e92e02018-11-22 11:14:36 +0800195#endif
196 else if (fpcsr & FPCSR_mskIVOT)
197 *signo = FPE_FLTINV;
Vincent Chene46bf832018-11-22 11:14:34 +0800198 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;
Vincent Chen44e92e02018-11-22 11:14:36 +0800208#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
209 unsigned long redo_except = FPCSR_mskDNIT|FPCSR_mskUDFT;
210#else
211 unsigned long redo_except = FPCSR_mskDNIT;
212#endif
Vincent Chene46bf832018-11-22 11:14:34 +0800213
214 lose_fpu();
215 fpcsr = current->thread.fpu.fpcsr;
216
Vincent Chen44e92e02018-11-22 11:14:36 +0800217 if (fpcsr & redo_except) {
218#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
219 if (fpcsr & FPCSR_mskUDFT)
220 current->thread.fpu.fpcsr &= ~FPCSR_mskIEX;
221#endif
Vincent Chen1ac83252018-11-22 11:14:35 +0800222 si_signo = do_fpuemu(regs, &current->thread.fpu);
223 fpcsr = current->thread.fpu.fpcsr;
224 if (!si_signo)
225 goto done;
226 } else if (fpcsr & FPCSR_mskRIT) {
Vincent Chene46bf832018-11-22 11:14:34 +0800227 if (!user_mode(regs))
228 do_exit(SIGILL);
229 si_signo = SIGILL;
Vincent Chen1ac83252018-11-22 11:14:35 +0800230 }
231
232
233 switch (si_signo) {
234 case SIGFPE:
235 fill_sigfpe_signo(fpcsr, &si_code);
236 break;
237 case SIGILL:
Vincent Chene46bf832018-11-22 11:14:34 +0800238 show_regs(regs);
239 si_code = ILL_COPROC;
Vincent Chen1ac83252018-11-22 11:14:35 +0800240 break;
241 case SIGBUS:
242 si_code = BUS_ADRERR;
243 break;
244 default:
245 break;
246 }
247
Vincent Chene46bf832018-11-22 11:14:34 +0800248 force_sig_fault(si_signo, si_code,
249 (void __user *)instruction_pointer(regs), current);
Vincent Chen1ac83252018-11-22 11:14:35 +0800250done:
251 own_fpu();
Vincent Chene46bf832018-11-22 11:14:34 +0800252}
253
254bool do_fpu_exception(unsigned int subtype, struct pt_regs *regs)
255{
256 int done = true;
257 /* Coprocessor disabled exception */
258 if (subtype == FPU_DISABLE_EXCEPTION) {
259 preempt_disable();
260 do_fpu_context_switch(regs);
261 preempt_enable();
262 }
263 /* Coprocessor exception such as underflow and overflow */
264 else if (subtype == FPU_EXCEPTION)
265 handle_fpu_exception(regs);
266 else
267 done = false;
268 return done;
269}