Merge tag 'for-5.15/parisc' of git://git.kernel.org/pub/scm/linux/kernel/git/deller...
[linux-2.6-microblaze.git] / arch / parisc / math-emu / decode_exc.c
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
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 }