• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2005-2018 Andes Technology Corporation
3 
4 #include <asm/bitfield.h>
5 #include <asm/uaccess.h>
6 #include <asm/sfp-machine.h>
7 #include <asm/fpuemu.h>
8 #include <asm/nds32_fpu_inst.h>
9 
10 #define DPFROMREG(dp, x) (dp = (void *)((unsigned long *)fpu_reg + 2*x))
11 #ifdef __NDS32_EL__
12 #define SPFROMREG(sp, x)\
13 	((sp) = (void *)((unsigned long *)fpu_reg + (x^1)))
14 #else
15 #define SPFROMREG(sp, x) ((sp) = (void *)((unsigned long *)fpu_reg + x))
16 #endif
17 
18 #define DEF3OP(name, p, f1, f2) \
19 void fpemu_##name##p(void *ft, void *fa, void *fb) \
20 { \
21 	f1(fa, fa, fb); \
22 	f2(ft, ft, fa); \
23 }
24 
25 #define DEF3OPNEG(name, p, f1, f2, f3) \
26 void fpemu_##name##p(void *ft, void *fa, void *fb) \
27 { \
28 	f1(fa, fa, fb); \
29 	f2(ft, ft, fa); \
30 	f3(ft, ft); \
31 }
32 DEF3OP(fmadd, s, fmuls, fadds);
33 DEF3OP(fmsub, s, fmuls, fsubs);
34 DEF3OP(fmadd, d, fmuld, faddd);
35 DEF3OP(fmsub, d, fmuld, fsubd);
36 DEF3OPNEG(fnmadd, s, fmuls, fadds, fnegs);
37 DEF3OPNEG(fnmsub, s, fmuls, fsubs, fnegs);
38 DEF3OPNEG(fnmadd, d, fmuld, faddd, fnegd);
39 DEF3OPNEG(fnmsub, d, fmuld, fsubd, fnegd);
40 
41 static const unsigned char cmptab[8] = {
42 	SF_CEQ,
43 	SF_CEQ,
44 	SF_CLT,
45 	SF_CLT,
46 	SF_CLT | SF_CEQ,
47 	SF_CLT | SF_CEQ,
48 	SF_CUN,
49 	SF_CUN
50 };
51 
52 enum ARGTYPE {
53 	S1S = 1,
54 	S2S,
55 	S1D,
56 	CS,
57 	D1D,
58 	D2D,
59 	D1S,
60 	CD
61 };
62 union func_t {
63 	void (*t)(void *ft, void *fa, void *fb);
64 	void (*b)(void *ft, void *fa);
65 };
66 /*
67  * Emulate a single FPU arithmetic instruction.
68  */
fpu_emu(struct fpu_struct * fpu_reg,unsigned long insn)69 static int fpu_emu(struct fpu_struct *fpu_reg, unsigned long insn)
70 {
71 	int rfmt;		/* resulting format */
72 	union func_t func;
73 	int ftype = 0;
74 
75 	switch (rfmt = NDS32Insn_OPCODE_COP0(insn)) {
76 	case fs1_op:{
77 			switch (NDS32Insn_OPCODE_BIT69(insn)) {
78 			case fadds_op:
79 				func.t = fadds;
80 				ftype = S2S;
81 				break;
82 			case fsubs_op:
83 				func.t = fsubs;
84 				ftype = S2S;
85 				break;
86 			case fmadds_op:
87 				func.t = fpemu_fmadds;
88 				ftype = S2S;
89 				break;
90 			case fmsubs_op:
91 				func.t = fpemu_fmsubs;
92 				ftype = S2S;
93 				break;
94 			case fnmadds_op:
95 				func.t = fpemu_fnmadds;
96 				ftype = S2S;
97 				break;
98 			case fnmsubs_op:
99 				func.t = fpemu_fnmsubs;
100 				ftype = S2S;
101 				break;
102 			case fmuls_op:
103 				func.t = fmuls;
104 				ftype = S2S;
105 				break;
106 			case fdivs_op:
107 				func.t = fdivs;
108 				ftype = S2S;
109 				break;
110 			case fs1_f2op_op:
111 				switch (NDS32Insn_OPCODE_BIT1014(insn)) {
112 				case fs2d_op:
113 					func.b = fs2d;
114 					ftype = S1D;
115 					break;
116 				case fs2si_op:
117 					func.b = fs2si;
118 					ftype = S1S;
119 					break;
120 				case fs2si_z_op:
121 					func.b = fs2si_z;
122 					ftype = S1S;
123 					break;
124 				case fs2ui_op:
125 					func.b = fs2ui;
126 					ftype = S1S;
127 					break;
128 				case fs2ui_z_op:
129 					func.b = fs2ui_z;
130 					ftype = S1S;
131 					break;
132 				case fsi2s_op:
133 					func.b = fsi2s;
134 					ftype = S1S;
135 					break;
136 				case fui2s_op:
137 					func.b = fui2s;
138 					ftype = S1S;
139 					break;
140 				case fsqrts_op:
141 					func.b = fsqrts;
142 					ftype = S1S;
143 					break;
144 				default:
145 					return SIGILL;
146 				}
147 				break;
148 			default:
149 				return SIGILL;
150 			}
151 			break;
152 		}
153 	case fs2_op:
154 		switch (NDS32Insn_OPCODE_BIT69(insn)) {
155 		case fcmpeqs_op:
156 		case fcmpeqs_e_op:
157 		case fcmplts_op:
158 		case fcmplts_e_op:
159 		case fcmples_op:
160 		case fcmples_e_op:
161 		case fcmpuns_op:
162 		case fcmpuns_e_op:
163 			ftype = CS;
164 			break;
165 		default:
166 			return SIGILL;
167 		}
168 		break;
169 	case fd1_op:{
170 			switch (NDS32Insn_OPCODE_BIT69(insn)) {
171 			case faddd_op:
172 				func.t = faddd;
173 				ftype = D2D;
174 				break;
175 			case fsubd_op:
176 				func.t = fsubd;
177 				ftype = D2D;
178 				break;
179 			case fmaddd_op:
180 				func.t = fpemu_fmaddd;
181 				ftype = D2D;
182 				break;
183 			case fmsubd_op:
184 				func.t = fpemu_fmsubd;
185 				ftype = D2D;
186 				break;
187 			case fnmaddd_op:
188 				func.t = fpemu_fnmaddd;
189 				ftype = D2D;
190 				break;
191 			case fnmsubd_op:
192 				func.t = fpemu_fnmsubd;
193 				ftype = D2D;
194 				break;
195 			case fmuld_op:
196 				func.t = fmuld;
197 				ftype = D2D;
198 				break;
199 			case fdivd_op:
200 				func.t = fdivd;
201 				ftype = D2D;
202 				break;
203 			case fd1_f2op_op:
204 				switch (NDS32Insn_OPCODE_BIT1014(insn)) {
205 				case fd2s_op:
206 					func.b = fd2s;
207 					ftype = D1S;
208 					break;
209 				case fd2si_op:
210 					func.b = fd2si;
211 					ftype = D1S;
212 					break;
213 				case fd2si_z_op:
214 					func.b = fd2si_z;
215 					ftype = D1S;
216 					break;
217 				case fd2ui_op:
218 					func.b = fd2ui;
219 					ftype = D1S;
220 					break;
221 				case fd2ui_z_op:
222 					func.b = fd2ui_z;
223 					ftype = D1S;
224 					break;
225 				case fsi2d_op:
226 					func.b = fsi2d;
227 					ftype = D1S;
228 					break;
229 				case fui2d_op:
230 					func.b = fui2d;
231 					ftype = D1S;
232 					break;
233 				case fsqrtd_op:
234 					func.b = fsqrtd;
235 					ftype = D1D;
236 					break;
237 				default:
238 					return SIGILL;
239 				}
240 				break;
241 			default:
242 				return SIGILL;
243 
244 			}
245 			break;
246 		}
247 
248 	case fd2_op:
249 		switch (NDS32Insn_OPCODE_BIT69(insn)) {
250 		case fcmpeqd_op:
251 		case fcmpeqd_e_op:
252 		case fcmpltd_op:
253 		case fcmpltd_e_op:
254 		case fcmpled_op:
255 		case fcmpled_e_op:
256 		case fcmpund_op:
257 		case fcmpund_e_op:
258 			ftype = CD;
259 			break;
260 		default:
261 			return SIGILL;
262 		}
263 		break;
264 
265 	default:
266 		return SIGILL;
267 	}
268 
269 	switch (ftype) {
270 	case S1S:{
271 			void *ft, *fa;
272 
273 			SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
274 			SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
275 			func.b(ft, fa);
276 			break;
277 		}
278 	case S2S:{
279 			void *ft, *fa, *fb;
280 
281 			SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
282 			SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
283 			SPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn));
284 			func.t(ft, fa, fb);
285 			break;
286 		}
287 	case S1D:{
288 			void *ft, *fa;
289 
290 			DPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
291 			SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
292 			func.b(ft, fa);
293 			break;
294 		}
295 	case CS:{
296 			unsigned int cmpop = NDS32Insn_OPCODE_BIT69(insn);
297 			void *ft, *fa, *fb;
298 
299 			SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
300 			SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
301 			SPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn));
302 			if (cmpop < 0x8) {
303 				cmpop = cmptab[cmpop];
304 				fcmps(ft, fa, fb, cmpop);
305 			} else
306 				return SIGILL;
307 			break;
308 		}
309 	case D1D:{
310 			void *ft, *fa;
311 
312 			DPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
313 			DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
314 			func.b(ft, fa);
315 			break;
316 		}
317 	case D2D:{
318 			void *ft, *fa, *fb;
319 
320 			DPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
321 			DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
322 			DPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn));
323 			func.t(ft, fa, fb);
324 			break;
325 		}
326 	case D1S:{
327 			void *ft, *fa;
328 
329 			SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
330 			DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
331 			func.b(ft, fa);
332 			break;
333 		}
334 	case CD:{
335 			unsigned int cmpop = NDS32Insn_OPCODE_BIT69(insn);
336 			void *ft, *fa, *fb;
337 
338 			SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
339 			DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
340 			DPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn));
341 			if (cmpop < 0x8) {
342 				cmpop = cmptab[cmpop];
343 				fcmpd(ft, fa, fb, cmpop);
344 			} else
345 				return SIGILL;
346 			break;
347 		}
348 	default:
349 		return SIGILL;
350 	}
351 
352 	/*
353 	 * If an exception is required, generate a tidy SIGFPE exception.
354 	 */
355 #if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
356 	if (((fpu_reg->fpcsr << 5) & fpu_reg->fpcsr & FPCSR_mskALLE_NO_UDF_IEXE)
357 	    || ((fpu_reg->fpcsr << 5) & (fpu_reg->UDF_IEX_trap))) {
358 #else
359 	if ((fpu_reg->fpcsr << 5) & fpu_reg->fpcsr & FPCSR_mskALLE) {
360 #endif
361 		return SIGFPE;
362 	}
363 	return 0;
364 }
365 
366 int do_fpuemu(struct pt_regs *regs, struct fpu_struct *fpu)
367 {
368 	unsigned long insn = 0, addr = regs->ipc;
369 	unsigned long emulpc, contpc;
370 	unsigned char *pc = (void *)&insn;
371 	char c;
372 	int i = 0, ret;
373 
374 	for (i = 0; i < 4; i++) {
375 		if (__get_user(c, (unsigned char *)addr++))
376 			return SIGBUS;
377 		*pc++ = c;
378 	}
379 
380 	insn = be32_to_cpu(insn);
381 
382 	emulpc = regs->ipc;
383 	contpc = regs->ipc + 4;
384 
385 	if (NDS32Insn_OPCODE(insn) != cop0_op)
386 		return SIGILL;
387 
388 	switch (NDS32Insn_OPCODE_COP0(insn)) {
389 	case fs1_op:
390 	case fs2_op:
391 	case fd1_op:
392 	case fd2_op:
393 		{
394 			/* a real fpu computation instruction */
395 			ret = fpu_emu(fpu, insn);
396 			if (!ret)
397 				regs->ipc = contpc;
398 		}
399 		break;
400 
401 	default:
402 		return SIGILL;
403 	}
404 
405 	return ret;
406 }
407