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 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