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