xref: /netbsd-src/sys/arch/sparc/fpu/fpu.c (revision ae9172d6cd9432a6a1a56760d86b32c57a66c39c)
1 /*	$NetBSD: fpu.c,v 1.2 1994/11/20 20:52:33 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * All advertising materials mentioning features or use of this software
12  * must display the following acknowledgement:
13  *	This product includes software developed by the University of
14  *	California, Lawrence Berkeley Laboratory.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. All advertising materials mentioning features or use of this software
25  *    must display the following acknowledgement:
26  *	This product includes software developed by the University of
27  *	California, Berkeley and its contributors.
28  * 4. Neither the name of the University nor the names of its contributors
29  *    may be used to endorse or promote products derived from this software
30  *    without specific prior written permission.
31  *
32  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42  * SUCH DAMAGE.
43  *
44  *	@(#)fpu.c	8.1 (Berkeley) 6/11/93
45  */
46 
47 #include <sys/param.h>
48 #include <sys/proc.h>
49 #include <sys/signal.h>
50 #include <sys/systm.h>
51 #include <sys/syslog.h>
52 
53 #include <machine/instr.h>
54 #include <machine/reg.h>
55 
56 #include <sparc/fpu/fpu_emu.h>
57 
58 /*
59  * fpu_execute returns the following error numbers (0 = no error):
60  */
61 #define	FPE		1	/* take a floating point exception */
62 #define	NOTFPU		2	/* not an FPU instruction */
63 
64 /*
65  * Translate current exceptions into `first' exception.  The
66  * bits go the wrong way for ffs() (0x10 is most important, etc).
67  * There are only 5, so do it the obvious way.
68  */
69 #define	X1(x) x
70 #define	X2(x) x,x
71 #define	X4(x) x,x,x,x
72 #define	X8(x) X4(x),X4(x)
73 #define	X16(x) X8(x),X8(x)
74 
75 static char cx_to_trapx[] = {
76 	X1(FSR_NX),
77 	X2(FSR_DZ),
78 	X4(FSR_UF),
79 	X8(FSR_OF),
80 	X16(FSR_NV)
81 };
82 static u_char fpu_codes[] = {
83 	X1(FPE_FLTINEX_TRAP),
84 	X2(FPE_FLTDIV_TRAP),
85 	X4(FPE_FLTUND_TRAP),
86 	X8(FPE_FLTOVF_TRAP),
87 	X16(FPE_FLTOPERR_TRAP)
88 };
89 
90 /*
91  * The FPU gave us an exception.  Clean up the mess.  Note that the
92  * fp queue can only have FPops in it, never load/store FP registers
93  * nor FBfcc instructions.  Experiments with `crashme' prove that
94  * unknown FPops do enter the queue, however.
95  */
96 fpu_cleanup(p, fs)
97 	register struct proc *p;
98 	register struct fpstate *fs;
99 {
100 	register int i, fsr = fs->fs_fsr, error;
101 	union instr instr;
102 	struct fpemu fe;
103 
104 	switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) {
105 
106 	case FSR_TT_NONE:
107 		panic("fpu_cleanup 1");	/* ??? */
108 		break;
109 
110 	case FSR_TT_IEEE:
111 		/* XXX missing trap address! */
112 		if ((i = fsr & FSR_CX) == 0)
113 			panic("fpu ieee trap, but no exception");
114 		trapsignal(p, SIGFPE, fpu_codes[i - 1]);
115 		break;		/* XXX should return, but queue remains */
116 
117 	case FSR_TT_UNFIN:
118 	case FSR_TT_UNIMP:
119 		if (fs->fs_qsize == 0)
120 			panic("fpu_cleanup 2");
121 		break;
122 
123 	case FSR_TT_SEQ:
124 		panic("fpu sequence error");
125 		/* NOTREACHED */
126 
127 	case FSR_TT_HWERR:
128 		log(LOG_ERR, "fpu hardware error (%s[%d])\n",
129 		    p->p_comm, p->p_pid);
130 		uprintf("%s[%d]: fpu hardware error\n", p->p_comm, p->p_pid);
131 		trapsignal(p, SIGFPE, -1);	/* ??? */
132 		goto out;
133 
134 	default:
135 		printf("fsr=%x\n", fsr);
136 		panic("fpu error");
137 	}
138 
139 	/* emulate the instructions left in the queue */
140 	fe.fe_fpstate = fs;
141 	for (i = 0; i < fs->fs_qsize; i++) {
142 		instr.i_int = fs->fs_queue[i].fq_instr;
143 		if (instr.i_any.i_op != IOP_reg ||
144 		    (instr.i_op3.i_op3 != IOP3_FPop1 &&
145 		     instr.i_op3.i_op3 != IOP3_FPop2))
146 			panic("bogus fpu queue");
147 		error = fpu_execute(&fe, instr);
148 		switch (error) {
149 
150 		case 0:
151 			continue;
152 
153 		case FPE:
154 			trapsignal(p, SIGFPE,
155 			    fpu_codes[(fs->fs_fsr & FSR_CX) - 1]);
156 			break;
157 
158 		case NOTFPU:
159 			trapsignal(p, SIGILL, 0);	/* ??? code?  */
160 			break;
161 
162 		default:
163 			panic("fpu_cleanup 3");
164 			/* NOTREACHED */
165 		}
166 		/* XXX should stop here, but queue remains */
167 	}
168 out:
169 	fs->fs_qsize = 0;
170 }
171 
172 #ifdef notyet
173 /*
174  * If we have no FPU at all (are there any machines like this out
175  * there!?) we have to emulate each instruction, and we need a pointer
176  * to the trapframe so that we can step over them and do FBfcc's.
177  * We know the `queue' is empty, though; we just want to emulate
178  * the instruction at tf->tf_pc.
179  */
180 fpu_emulate(p, tf, fs)
181 	struct proc *p;
182 	register struct trapframe *tf;
183 	register struct fpstate *fs;
184 {
185 
186 	do {
187 		fetch instr from pc
188 		decode
189 		if (integer instr) {
190 			/*
191 			 * We do this here, rather than earlier, to avoid
192 			 * losing even more badly than usual.
193 			 */
194 			if (p->p_addr->u_pcb.pcb_uw) {
195 				write_user_windows();
196 				if (rwindow_save(p))
197 					sigexit(p, SIGILL);
198 			}
199 			if (loadstore) {
200 				do_it;
201 				pc = npc, npc += 4
202 			} else if (fbfcc) {
203 				do_annul_stuff;
204 			} else
205 				return;
206 		} else if (fpu instr) {
207 			fe.fe_fsr = fs->fs_fsr &= ~FSR_CX;
208 			error = fpu_execute(&fe, fs, instr);
209 			switch (error) {
210 				etc;
211 			}
212 		} else
213 			return;
214 		if (want to reschedule)
215 			return;
216 	} while (error == 0);
217 }
218 #endif
219 
220 /*
221  * Execute an FPU instruction (one that runs entirely in the FPU; not
222  * FBfcc or STF, for instance).  On return, fe->fe_fs->fs_fsr will be
223  * modified to reflect the setting the hardware would have left.
224  *
225  * Note that we do not catch all illegal opcodes, so you can, for instance,
226  * multiply two integers this way.
227  */
228 int
229 fpu_execute(fe, instr)
230 	register struct fpemu *fe;
231 	union instr instr;
232 {
233 	register struct fpn *fp;
234 	register int opf, rs1, rs2, rd, type, mask, fsr, cx;
235 	register struct fpstate *fs;
236 	u_int space[4];
237 
238 	/*
239 	 * `Decode' and execute instruction.  Start with no exceptions.
240 	 * The type of any i_opf opcode is in the bottom two bits, so we
241 	 * squish them out here.
242 	 */
243 	opf = instr.i_opf.i_opf;
244 	type = opf & 3;
245 	mask = "\0\0\1\3"[type];
246 	rs1 = instr.i_opf.i_rs1 & ~mask;
247 	rs2 = instr.i_opf.i_rs2 & ~mask;
248 	rd = instr.i_opf.i_rd & ~mask;
249 #ifdef notdef
250 	if ((rs1 | rs2 | rd) & mask)
251 		return (BADREG);
252 #endif
253 	fs = fe->fe_fpstate;
254 	fe->fe_fsr = fs->fs_fsr & ~FSR_CX;
255 	fe->fe_cx = 0;
256 	switch (opf >>= 2) {
257 
258 	default:
259 		return (NOTFPU);
260 
261 	case FMOV >> 2:		/* these should all be pretty obvious */
262 		rs1 = fs->fs_regs[rs2];
263 		goto mov;
264 
265 	case FNEG >> 2:
266 		rs1 = fs->fs_regs[rs2] ^ (1 << 31);
267 		goto mov;
268 
269 	case FABS >> 2:
270 		rs1 = fs->fs_regs[rs2] & ~(1 << 31);
271 	mov:
272 		fs->fs_regs[rd] = rs1;
273 		fs->fs_fsr = fe->fe_fsr;
274 		return (0);	/* success */
275 
276 	case FSQRT >> 2:
277 		fpu_explode(fe, &fe->fe_f1, type, rs2);
278 		fp = fpu_sqrt(fe);
279 		break;
280 
281 	case FADD >> 2:
282 		fpu_explode(fe, &fe->fe_f1, type, rs1);
283 		fpu_explode(fe, &fe->fe_f2, type, rs2);
284 		fp = fpu_add(fe);
285 		break;
286 
287 	case FSUB >> 2:
288 		fpu_explode(fe, &fe->fe_f1, type, rs1);
289 		fpu_explode(fe, &fe->fe_f2, type, rs2);
290 		fp = fpu_sub(fe);
291 		break;
292 
293 	case FMUL >> 2:
294 		fpu_explode(fe, &fe->fe_f1, type, rs1);
295 		fpu_explode(fe, &fe->fe_f2, type, rs2);
296 		fp = fpu_mul(fe);
297 		break;
298 
299 	case FDIV >> 2:
300 		fpu_explode(fe, &fe->fe_f1, type, rs1);
301 		fpu_explode(fe, &fe->fe_f2, type, rs2);
302 		fp = fpu_div(fe);
303 		break;
304 
305 	case FCMP >> 2:
306 		fpu_explode(fe, &fe->fe_f1, type, rs1);
307 		fpu_explode(fe, &fe->fe_f2, type, rs2);
308 		fpu_compare(fe, 0);
309 		goto cmpdone;
310 
311 	case FCMPE >> 2:
312 		fpu_explode(fe, &fe->fe_f1, type, rs1);
313 		fpu_explode(fe, &fe->fe_f2, type, rs2);
314 		fpu_compare(fe, 1);
315 	cmpdone:
316 		/*
317 		 * The only possible exception here is NV; catch it
318 		 * early and get out, as there is no result register.
319 		 */
320 		cx = fe->fe_cx;
321 		fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT);
322 		if (cx != 0) {
323 			if (fsr & (FSR_NV << FSR_TEM_SHIFT)) {
324 				fs->fs_fsr = (fsr & ~FSR_FTT) |
325 				    (FSR_TT_IEEE << FSR_FTT_SHIFT);
326 				return (FPE);
327 			}
328 			fsr |= FSR_NV << FSR_AX_SHIFT;
329 		}
330 		fs->fs_fsr = fsr;
331 		return (0);
332 
333 	case FSMULD >> 2:
334 	case FDMULX >> 2:
335 		if (type == FTYPE_EXT)
336 			return (NOTFPU);
337 		fpu_explode(fe, &fe->fe_f1, type, rs1);
338 		fpu_explode(fe, &fe->fe_f2, type, rs2);
339 		type++;	/* single to double, or double to quad */
340 		fp = fpu_mul(fe);
341 		break;
342 
343 	case FTOS >> 2:
344 	case FTOD >> 2:
345 	case FTOX >> 2:
346 	case FTOI >> 2:
347 		fpu_explode(fe, fp = &fe->fe_f1, type, rs2);
348 		type = opf & 3;	/* sneaky; depends on instruction encoding */
349 		break;
350 	}
351 
352 	/*
353 	 * ALU operation is complete.  Collapse the result and then check
354 	 * for exceptions.  If we got any, and they are enabled, do not
355 	 * alter the destination register, just stop with an exception.
356 	 * Otherwise set new current exceptions and accrue.
357 	 */
358 	fpu_implode(fe, fp, type, space);
359 	cx = fe->fe_cx;
360 	fsr = fe->fe_fsr;
361 	if (cx != 0) {
362 		mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK;
363 		if (cx & mask) {
364 			/* not accrued??? */
365 			fs->fs_fsr = (fsr & ~FSR_FTT) |
366 			    (FSR_TT_IEEE << FSR_FTT_SHIFT) |
367 			    (cx_to_trapx[(cx & mask) - 1] << FSR_CX_SHIFT);
368 			return (FPE);
369 		}
370 		fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT);
371 	}
372 	fs->fs_fsr = fsr;
373 	fs->fs_regs[rd] = space[0];
374 	if (type >= FTYPE_DBL) {
375 		fs->fs_regs[rd + 1] = space[1];
376 		if (type > FTYPE_DBL) {
377 			fs->fs_regs[rd + 2] = space[2];
378 			fs->fs_regs[rd + 3] = space[3];
379 		}
380 	}
381 	return (0);	/* success */
382 }
383