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 
save_fpu(struct task_struct * tsk)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 
load_fpu(const struct fpu_struct * fpregs)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 }
store_fpu_for_suspend(void)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 }
do_fpu_context_switch(struct pt_regs * regs)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 
fill_sigfpe_signo(unsigned int fpcsr,int * signo)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 
handle_fpu_exception(struct pt_regs * regs)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 
do_fpu_exception(unsigned int subtype,struct pt_regs * regs)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 }
267