1 /*
2 * Linux/PA-RISC Project (http://www.parisc-linux.org/)
3 *
4 * Floating-point emulation code
5 * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 /*
22 * BEGIN_DESC
23 *
24 * File:
25 * @(#) pa/fp/decode_exc.c $ Revision: $
26 *
27 * Purpose:
28 * <<please update with a synopsis of the functionality provided by this file>>
29 *
30 * External Interfaces:
31 * <<the following list was autogenerated, please review>>
32 * decode_fpu(Fpu_register, trap_counts)
33 *
34 * Internal Interfaces:
35 * <<please update>>
36 *
37 * Theory:
38 * <<please update with a overview of the operation of this file>>
39 *
40 * END_DESC
41 */
42
43 #include <linux/kernel.h>
44 #include "float.h"
45 #include "sgl_float.h"
46 #include "dbl_float.h"
47 #include "cnv_float.h"
48 /* #include "types.h" */
49 #include <asm/signal.h>
50 #include <asm/siginfo.h>
51 /* #include <machine/sys/mdep_private.h> */
52
53 #undef Fpustatus_register
54 #define Fpustatus_register Fpu_register[0]
55
56 /* General definitions */
57 #define DOESTRAP 1
58 #define NOTRAP 0
59 #define SIGNALCODE(signal, code) ((signal) << 24 | (code));
60 #define copropbit 1<<31-2 /* bit position 2 */
61 #define opclass 9 /* bits 21 & 22 */
62 #define fmt 11 /* bits 19 & 20 */
63 #define df 13 /* bits 17 & 18 */
64 #define twobits 3 /* mask low-order 2 bits */
65 #define fivebits 31 /* mask low-order 5 bits */
66 #define MAX_EXCP_REG 7 /* number of excpeption registers to check */
67
68 /* Exception register definitions */
69 #define Excp_type(index) Exceptiontype(Fpu_register[index])
70 #define Excp_instr(index) Instructionfield(Fpu_register[index])
71 #define Clear_excp_register(index) Allexception(Fpu_register[index]) = 0
72 #define Excp_format() \
73 (current_ir >> ((current_ir>>opclass & twobits)==1 ? df : fmt) & twobits)
74
75 /* Miscellaneous definitions */
76 #define Fpu_sgl(index) Fpu_register[index*2]
77
78 #define Fpu_dblp1(index) Fpu_register[index*2]
79 #define Fpu_dblp2(index) Fpu_register[(index*2)+1]
80
81 #define Fpu_quadp1(index) Fpu_register[index*2]
82 #define Fpu_quadp2(index) Fpu_register[(index*2)+1]
83 #define Fpu_quadp3(index) Fpu_register[(index*2)+2]
84 #define Fpu_quadp4(index) Fpu_register[(index*2)+3]
85
86 /* Single precision floating-point definitions */
87 #ifndef Sgl_decrement
88 # define Sgl_decrement(sgl_value) Sall(sgl_value)--
89 #endif
90
91 /* Double precision floating-point definitions */
92 #ifndef Dbl_decrement
93 # define Dbl_decrement(dbl_valuep1,dbl_valuep2) \
94 if ((Dallp2(dbl_valuep2)--) == 0) Dallp1(dbl_valuep1)--
95 #endif
96
97
98 #define update_trap_counts(Fpu_register, aflags, bflags, trap_counts) { \
99 aflags=(Fpu_register[0])>>27; /* assumes zero fill. 32 bit */ \
100 Fpu_register[0] |= bflags; \
101 }
102
103 u_int
decode_fpu(unsigned int Fpu_register[],unsigned int trap_counts[])104 decode_fpu(unsigned int Fpu_register[], unsigned int trap_counts[])
105 {
106 unsigned int current_ir, excp;
107 int target, exception_index = 1;
108 boolean inexact;
109 unsigned int aflags;
110 unsigned int bflags;
111 unsigned int excptype;
112
113
114 /* Keep stats on how many floating point exceptions (based on type)
115 * that happen. Want to keep this overhead low, but still provide
116 * some information to the customer. All exits from this routine
117 * need to restore Fpu_register[0]
118 */
119
120 bflags=(Fpu_register[0] & 0xf8000000);
121 Fpu_register[0] &= 0x07ffffff;
122
123 /* exception_index is used to index the exception register queue. It
124 * always points at the last register that contains a valid exception. A
125 * zero value implies no exceptions (also the initialized value). Setting
126 * the T-bit resets the exception_index to zero.
127 */
128
129 /*
130 * Check for reserved-op exception. A reserved-op exception does not
131 * set any exception registers nor does it set the T-bit. If the T-bit
132 * is not set then a reserved-op exception occurred.
133 *
134 * At some point, we may want to report reserved op exceptions as
135 * illegal instructions.
136 */
137
138 if (!Is_tbit_set()) {
139 update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
140 return SIGNALCODE(SIGILL, ILL_COPROC);
141 }
142
143 /*
144 * Is a coprocessor op.
145 *
146 * Now we need to determine what type of exception occurred.
147 */
148 for (exception_index=1; exception_index<=MAX_EXCP_REG; exception_index++) {
149 current_ir = Excp_instr(exception_index);
150 /*
151 * On PA89: there are 5 different unimplemented exception
152 * codes: 0x1, 0x9, 0xb, 0x3, and 0x23. PA-RISC 2.0 adds
153 * another, 0x2b. Only these have the low order bit set.
154 */
155 excptype = Excp_type(exception_index);
156 if (excptype & UNIMPLEMENTEDEXCEPTION) {
157 /*
158 * Clear T-bit and exception register so that
159 * we can tell if a trap really occurs while
160 * emulating the instruction.
161 */
162 Clear_tbit();
163 Clear_excp_register(exception_index);
164 /*
165 * Now emulate this instruction. If a trap occurs,
166 * fpudispatch will return a non-zero number
167 */
168 excp = fpudispatch(current_ir,excptype,0,Fpu_register);
169 /* accumulate the status flags, don't lose them as in hpux */
170 if (excp) {
171 /*
172 * We now need to make sure that the T-bit and the
173 * exception register contain the correct values
174 * before continuing.
175 */
176 /*
177 * Set t-bit since it might still be needed for a
178 * subsequent real trap (I don't understand fully -PB)
179 */
180 Set_tbit();
181 /* some of the following code uses
182 * Excp_type(exception_index) so fix that up */
183 Set_exceptiontype_and_instr_field(excp,current_ir,
184 Fpu_register[exception_index]);
185 if (excp == UNIMPLEMENTEDEXCEPTION) {
186 /*
187 * it is really unimplemented, so restore the
188 * TIMEX extended unimplemented exception code
189 */
190 excp = excptype;
191 update_trap_counts(Fpu_register, aflags, bflags,
192 trap_counts);
193 return SIGNALCODE(SIGILL, ILL_COPROC);
194 }
195 /* some of the following code uses excptype, so
196 * fix that up too */
197 excptype = excp;
198 }
199 /* handle exceptions other than the real UNIMPLIMENTED the
200 * same way as if the hardware had caused them */
201 if (excp == NOEXCEPTION)
202 /* For now use 'break', should technically be 'continue' */
203 break;
204 }
205
206 /*
207 * In PA89, the underflow exception has been extended to encode
208 * additional information. The exception looks like pp01x0,
209 * where x is 1 if inexact and pp represent the inexact bit (I)
210 * and the round away bit (RA)
211 */
212 if (excptype & UNDERFLOWEXCEPTION) {
213 /* check for underflow trap enabled */
214 if (Is_underflowtrap_enabled()) {
215 update_trap_counts(Fpu_register, aflags, bflags,
216 trap_counts);
217 return SIGNALCODE(SIGFPE, FPE_FLTUND);
218 } else {
219 /*
220 * Isn't a real trap; we need to
221 * return the default value.
222 */
223 target = current_ir & fivebits;
224 #ifndef lint
225 if (Ibit(Fpu_register[exception_index])) inexact = TRUE;
226 else inexact = FALSE;
227 #endif
228 switch (Excp_format()) {
229 case SGL:
230 /*
231 * If ra (round-away) is set, will
232 * want to undo the rounding done
233 * by the hardware.
234 */
235 if (Rabit(Fpu_register[exception_index]))
236 Sgl_decrement(Fpu_sgl(target));
237
238 /* now denormalize */
239 sgl_denormalize(&Fpu_sgl(target),&inexact,Rounding_mode());
240 break;
241 case DBL:
242 /*
243 * If ra (round-away) is set, will
244 * want to undo the rounding done
245 * by the hardware.
246 */
247 if (Rabit(Fpu_register[exception_index]))
248 Dbl_decrement(Fpu_dblp1(target),Fpu_dblp2(target));
249
250 /* now denormalize */
251 dbl_denormalize(&Fpu_dblp1(target),&Fpu_dblp2(target),
252 &inexact,Rounding_mode());
253 break;
254 }
255 if (inexact) Set_underflowflag();
256 /*
257 * Underflow can generate an inexact
258 * exception. If inexact trap is enabled,
259 * want to do an inexact trap, otherwise
260 * set inexact flag.
261 */
262 if (inexact && Is_inexacttrap_enabled()) {
263 /*
264 * Set exception field of exception register
265 * to inexact, parm field to zero.
266 * Underflow bit should be cleared.
267 */
268 Set_exceptiontype(Fpu_register[exception_index],
269 INEXACTEXCEPTION);
270 Set_parmfield(Fpu_register[exception_index],0);
271 update_trap_counts(Fpu_register, aflags, bflags,
272 trap_counts);
273 return SIGNALCODE(SIGFPE, FPE_FLTRES);
274 }
275 else {
276 /*
277 * Exception register needs to be cleared.
278 * Inexact flag needs to be set if inexact.
279 */
280 Clear_excp_register(exception_index);
281 if (inexact) Set_inexactflag();
282 }
283 }
284 continue;
285 }
286 switch(Excp_type(exception_index)) {
287 case OVERFLOWEXCEPTION:
288 case OVERFLOWEXCEPTION | INEXACTEXCEPTION:
289 /* check for overflow trap enabled */
290 update_trap_counts(Fpu_register, aflags, bflags,
291 trap_counts);
292 if (Is_overflowtrap_enabled()) {
293 update_trap_counts(Fpu_register, aflags, bflags,
294 trap_counts);
295 return SIGNALCODE(SIGFPE, FPE_FLTOVF);
296 } else {
297 /*
298 * Isn't a real trap; we need to
299 * return the default value.
300 */
301 target = current_ir & fivebits;
302 switch (Excp_format()) {
303 case SGL:
304 Sgl_setoverflow(Fpu_sgl(target));
305 break;
306 case DBL:
307 Dbl_setoverflow(Fpu_dblp1(target),Fpu_dblp2(target));
308 break;
309 }
310 Set_overflowflag();
311 /*
312 * Overflow always generates an inexact
313 * exception. If inexact trap is enabled,
314 * want to do an inexact trap, otherwise
315 * set inexact flag.
316 */
317 if (Is_inexacttrap_enabled()) {
318 /*
319 * Set exception field of exception
320 * register to inexact. Overflow
321 * bit should be cleared.
322 */
323 Set_exceptiontype(Fpu_register[exception_index],
324 INEXACTEXCEPTION);
325 update_trap_counts(Fpu_register, aflags, bflags,
326 trap_counts);
327 return SIGNALCODE(SIGFPE, FPE_FLTRES);
328 }
329 else {
330 /*
331 * Exception register needs to be cleared.
332 * Inexact flag needs to be set.
333 */
334 Clear_excp_register(exception_index);
335 Set_inexactflag();
336 }
337 }
338 break;
339 case INVALIDEXCEPTION:
340 case OPC_2E_INVALIDEXCEPTION:
341 update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
342 return SIGNALCODE(SIGFPE, FPE_FLTINV);
343 case DIVISIONBYZEROEXCEPTION:
344 update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
345 Clear_excp_register(exception_index);
346 return SIGNALCODE(SIGFPE, FPE_FLTDIV);
347 case INEXACTEXCEPTION:
348 update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
349 return SIGNALCODE(SIGFPE, FPE_FLTRES);
350 default:
351 update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
352 printk("%s(%d) Unknown FPU exception 0x%x\n", __FILE__,
353 __LINE__, Excp_type(exception_index));
354 return SIGNALCODE(SIGILL, ILL_COPROC);
355 case NOEXCEPTION: /* no exception */
356 /*
357 * Clear exception register in case
358 * other fields are non-zero.
359 */
360 Clear_excp_register(exception_index);
361 break;
362 }
363 }
364 /*
365 * No real exceptions occurred.
366 */
367 Clear_tbit();
368 update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
369 return(NOTRAP);
370 }
371