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