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