[PARISC] Fix floating point invalid exception trap handler
[linux-2.6.git] / arch / parisc / math-emu / decode_exc.c
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
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
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                 return SIGNALCODE(SIGFPE, FPE_FLTDIV);
346           case INEXACTEXCEPTION:
347                 update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
348                 return SIGNALCODE(SIGFPE, FPE_FLTRES);
349           default:
350                 update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
351                 printk("%s(%d) Unknown FPU exception 0x%x\n", __FILE__,
352                         __LINE__, Excp_type(exception_index));
353                 return SIGNALCODE(SIGILL, ILL_COPROC);
354           case NOEXCEPTION:     /* no exception */
355                 /*
356                  * Clear exception register in case 
357                  * other fields are non-zero.
358                  */
359                 Clear_excp_register(exception_index);
360                 break;
361         }
362     }
363     /*
364      * No real exceptions occurred.
365      */
366     Clear_tbit();
367     update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
368     return(NOTRAP);
369 }