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 * %sccs.include.redist.c%
15 *
16 * @(#)fpu.c 8.1 (Berkeley) 06/11/93
17 *
18 * from: $Header: fpu.c,v 1.3 92/11/26 01:39:42 torek Exp $
19 */
20
21 #include <sys/param.h>
22 #include <sys/proc.h>
23 #include <sys/signal.h>
24 #include <sys/systm.h>
25 #include <sys/syslog.h>
26
27 #include <machine/instr.h>
28 #include <machine/reg.h>
29
30 #include <sparc/fpu/fpu_emu.h>
31
32 /*
33 * fpu_execute returns the following error numbers (0 = no error):
34 */
35 #define FPE 1 /* take a floating point exception */
36 #define NOTFPU 2 /* not an FPU instruction */
37
38 /*
39 * Translate current exceptions into `first' exception. The
40 * bits go the wrong way for ffs() (0x10 is most important, etc).
41 * There are only 5, so do it the obvious way.
42 */
43 #define X1(x) x
44 #define X2(x) x,x
45 #define X4(x) x,x,x,x
46 #define X8(x) X4(x),X4(x)
47 #define X16(x) X8(x),X8(x)
48
49 static char cx_to_trapx[] = {
50 X1(FSR_NX),
51 X2(FSR_DZ),
52 X4(FSR_UF),
53 X8(FSR_OF),
54 X16(FSR_NV)
55 };
56 static u_char fpu_codes[] = {
57 X1(FPE_FLTINEX_TRAP),
58 X2(FPE_FLTDIV_TRAP),
59 X4(FPE_FLTUND_TRAP),
60 X8(FPE_FLTOVF_TRAP),
61 X16(FPE_FLTOPERR_TRAP)
62 };
63
64 /*
65 * The FPU gave us an exception. Clean up the mess. Note that the
66 * fp queue can only have FPops in it, never load/store FP registers
67 * nor FBfcc instructions. Experiments with `crashme' prove that
68 * unknown FPops do enter the queue, however.
69 */
fpu_cleanup(p,fs)70 fpu_cleanup(p, fs)
71 register struct proc *p;
72 register struct fpstate *fs;
73 {
74 register int i, fsr = fs->fs_fsr, error;
75 union instr instr;
76 struct fpemu fe;
77
78 switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) {
79
80 case FSR_TT_NONE:
81 panic("fpu_cleanup 1"); /* ??? */
82 break;
83
84 case FSR_TT_IEEE:
85 /* XXX missing trap address! */
86 if ((i = fsr & FSR_CX) == 0)
87 panic("fpu ieee trap, but no exception");
88 trapsignal(p, SIGFPE, fpu_codes[i - 1]);
89 break; /* XXX should return, but queue remains */
90
91 case FSR_TT_UNFIN:
92 case FSR_TT_UNIMP:
93 if (fs->fs_qsize == 0)
94 panic("fpu_cleanup 2");
95 break;
96
97 case FSR_TT_SEQ:
98 panic("fpu sequence error");
99 /* NOTREACHED */
100
101 case FSR_TT_HWERR:
102 log(LOG_ERR, "fpu hardware error (%s[%d])\n",
103 p->p_comm, p->p_pid);
104 uprintf("%s[%d]: fpu hardware error\n", p->p_comm, p->p_pid);
105 trapsignal(p, SIGFPE, -1); /* ??? */
106 goto out;
107
108 default:
109 printf("fsr=%x\n", fsr);
110 panic("fpu error");
111 }
112
113 /* emulate the instructions left in the queue */
114 fe.fe_fpstate = fs;
115 for (i = 0; i < fs->fs_qsize; i++) {
116 instr.i_int = fs->fs_queue[i].fq_instr;
117 if (instr.i_any.i_op != IOP_reg ||
118 (instr.i_op3.i_op3 != IOP3_FPop1 &&
119 instr.i_op3.i_op3 != IOP3_FPop2))
120 panic("bogus fpu queue");
121 error = fpu_execute(&fe, instr);
122 switch (error) {
123
124 case 0:
125 continue;
126
127 case FPE:
128 trapsignal(p, SIGFPE,
129 fpu_codes[(fs->fs_fsr & FSR_CX) - 1]);
130 break;
131
132 case NOTFPU:
133 trapsignal(p, SIGILL, 0); /* ??? code? */
134 break;
135
136 default:
137 panic("fpu_cleanup 3");
138 /* NOTREACHED */
139 }
140 /* XXX should stop here, but queue remains */
141 }
142 out:
143 fs->fs_qsize = 0;
144 }
145
146 #ifdef notyet
147 /*
148 * If we have no FPU at all (are there any machines like this out
149 * there!?) we have to emulate each instruction, and we need a pointer
150 * to the trapframe so that we can step over them and do FBfcc's.
151 * We know the `queue' is empty, though; we just want to emulate
152 * the instruction at tf->tf_pc.
153 */
154 fpu_emulate(p, tf, fs)
155 struct proc *p;
156 register struct trapframe *tf;
157 register struct fpstate *fs;
158 {
159
160 do {
161 fetch instr from pc
162 decode
163 if (integer instr) {
164 /*
165 * We do this here, rather than earlier, to avoid
166 * losing even more badly than usual.
167 */
168 if (p->p_addr->u_pcb.pcb_uw) {
169 write_user_windows();
170 if (rwindow_save(p))
171 sigexit(p, SIGILL);
172 }
173 if (loadstore) {
174 do_it;
175 pc = npc, npc += 4
176 } else if (fbfcc) {
177 do_annul_stuff;
178 } else
179 return;
180 } else if (fpu instr) {
181 fe.fe_fsr = fs->fs_fsr &= ~FSR_CX;
182 error = fpu_execute(&fe, fs, instr);
183 switch (error) {
184 etc;
185 }
186 } else
187 return;
188 if (want to reschedule)
189 return;
190 } while (error == 0);
191 }
192 #endif
193
194 /*
195 * Execute an FPU instruction (one that runs entirely in the FPU; not
196 * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be
197 * modified to reflect the setting the hardware would have left.
198 *
199 * Note that we do not catch all illegal opcodes, so you can, for instance,
200 * multiply two integers this way.
201 */
202 int
fpu_execute(fe,instr)203 fpu_execute(fe, instr)
204 register struct fpemu *fe;
205 union instr instr;
206 {
207 register struct fpn *fp;
208 register int opf, rs1, rs2, rd, type, mask, fsr, cx;
209 register struct fpstate *fs;
210 u_int space[4];
211
212 /*
213 * `Decode' and execute instruction. Start with no exceptions.
214 * The type of any i_opf opcode is in the bottom two bits, so we
215 * squish them out here.
216 */
217 opf = instr.i_opf.i_opf;
218 type = opf & 3;
219 mask = "\0\0\1\3"[type];
220 rs1 = instr.i_opf.i_rs1 & ~mask;
221 rs2 = instr.i_opf.i_rs2 & ~mask;
222 rd = instr.i_opf.i_rd & ~mask;
223 #ifdef notdef
224 if ((rs1 | rs2 | rd) & mask)
225 return (BADREG);
226 #endif
227 fs = fe->fe_fpstate;
228 fe->fe_fsr = fs->fs_fsr & ~FSR_CX;
229 fe->fe_cx = 0;
230 switch (opf >>= 2) {
231
232 default:
233 return (NOTFPU);
234
235 case FMOV >> 2: /* these should all be pretty obvious */
236 rs1 = fs->fs_regs[rs2];
237 goto mov;
238
239 case FNEG >> 2:
240 rs1 = fs->fs_regs[rs2] ^ (1 << 31);
241 goto mov;
242
243 case FABS >> 2:
244 rs1 = fs->fs_regs[rs2] & ~(1 << 31);
245 mov:
246 fs->fs_regs[rd] = rs1;
247 fs->fs_fsr = fe->fe_fsr;
248 return (0); /* success */
249
250 case FSQRT >> 2:
251 fpu_explode(fe, &fe->fe_f1, type, rs2);
252 fp = fpu_sqrt(fe);
253 break;
254
255 case FADD >> 2:
256 fpu_explode(fe, &fe->fe_f1, type, rs1);
257 fpu_explode(fe, &fe->fe_f2, type, rs2);
258 fp = fpu_add(fe);
259 break;
260
261 case FSUB >> 2:
262 fpu_explode(fe, &fe->fe_f1, type, rs1);
263 fpu_explode(fe, &fe->fe_f2, type, rs2);
264 fp = fpu_sub(fe);
265 break;
266
267 case FMUL >> 2:
268 fpu_explode(fe, &fe->fe_f1, type, rs1);
269 fpu_explode(fe, &fe->fe_f2, type, rs2);
270 fp = fpu_mul(fe);
271 break;
272
273 case FDIV >> 2:
274 fpu_explode(fe, &fe->fe_f1, type, rs1);
275 fpu_explode(fe, &fe->fe_f2, type, rs2);
276 fp = fpu_div(fe);
277 break;
278
279 case FCMP >> 2:
280 fpu_explode(fe, &fe->fe_f1, type, rs1);
281 fpu_explode(fe, &fe->fe_f2, type, rs2);
282 fpu_compare(fe, 0);
283 goto cmpdone;
284
285 case FCMPE >> 2:
286 fpu_explode(fe, &fe->fe_f1, type, rs1);
287 fpu_explode(fe, &fe->fe_f2, type, rs2);
288 fpu_compare(fe, 1);
289 cmpdone:
290 /*
291 * The only possible exception here is NV; catch it
292 * early and get out, as there is no result register.
293 */
294 cx = fe->fe_cx;
295 fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT);
296 if (cx != 0) {
297 if (fsr & (FSR_NV << FSR_TEM_SHIFT)) {
298 fs->fs_fsr = (fsr & ~FSR_FTT) |
299 (FSR_TT_IEEE << FSR_FTT_SHIFT);
300 return (FPE);
301 }
302 fsr |= FSR_NV << FSR_AX_SHIFT;
303 }
304 fs->fs_fsr = fsr;
305 return (0);
306
307 case FSMULD >> 2:
308 case FDMULX >> 2:
309 if (type == FTYPE_EXT)
310 return (NOTFPU);
311 fpu_explode(fe, &fe->fe_f1, type, rs1);
312 fpu_explode(fe, &fe->fe_f2, type, rs2);
313 type++; /* single to double, or double to quad */
314 fp = fpu_mul(fe);
315 break;
316
317 case FTOS >> 2:
318 case FTOD >> 2:
319 case FTOX >> 2:
320 case FTOI >> 2:
321 fpu_explode(fe, fp = &fe->fe_f1, type, rs2);
322 type = opf & 3; /* sneaky; depends on instruction encoding */
323 break;
324 }
325
326 /*
327 * ALU operation is complete. Collapse the result and then check
328 * for exceptions. If we got any, and they are enabled, do not
329 * alter the destination register, just stop with an exception.
330 * Otherwise set new current exceptions and accrue.
331 */
332 fpu_implode(fe, fp, type, space);
333 cx = fe->fe_cx;
334 fsr = fe->fe_fsr;
335 if (cx != 0) {
336 mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK;
337 if (cx & mask) {
338 /* not accrued??? */
339 fs->fs_fsr = (fsr & ~FSR_FTT) |
340 (FSR_TT_IEEE << FSR_FTT_SHIFT) |
341 (cx_to_trapx[(cx & mask) - 1] << FSR_CX_SHIFT);
342 return (FPE);
343 }
344 fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT);
345 }
346 fs->fs_fsr = fsr;
347 fs->fs_regs[rd] = space[0];
348 if (type >= FTYPE_DBL) {
349 fs->fs_regs[rd + 1] = space[1];
350 if (type > FTYPE_DBL) {
351 fs->fs_regs[rd + 2] = space[2];
352 fs->fs_regs[rd + 3] = space[3];
353 }
354 }
355 return (0); /* success */
356 }
357