1/*---------------------------------------------------------------------------+ 2 | errors.c | 3 | | 4 | The error handling functions for wm-FPU-emu | 5 | | 6 | Copyright (C) 1992,1993,1994,1996 | 7 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | 8 | E-mail billm@jacobi.maths.monash.edu.au | 9 | | 10 | | 11 +---------------------------------------------------------------------------*/ 12 13/*---------------------------------------------------------------------------+ 14 | Note: | 15 | The file contains code which accesses user memory. | 16 | Emulator static data may change when user memory is accessed, due to | 17 | other processes using the emulator while swapping is in progress. | 18 +---------------------------------------------------------------------------*/ 19 20#include <linux/signal.h> 21 22#include <asm/uaccess.h> 23 24#include "fpu_emu.h" 25#include "fpu_system.h" 26#include "exception.h" 27#include "status_w.h" 28#include "control_w.h" 29#include "reg_constant.h" 30#include "version.h" 31 32/* */ 33#undef PRINT_MESSAGES 34/* */ 35 36 37 38 39/* 40 Called for opcodes which are illegal and which are known to result in a 41 SIGILL with a real 80486. 42 */ 43void FPU_illegal(void) 44{ 45 math_abort(FPU_info,SIGILL); 46} 47 48 49 50void FPU_printall(void) 51{ 52 int i; 53 static const char *tag_desc[] = { "Valid", "Zero", "ERROR", "Empty", 54 "DeNorm", "Inf", "NaN" }; 55 u_char byte1, FPU_modrm; 56 unsigned long address = FPU_ORIG_EIP; 57 58 RE_ENTRANT_CHECK_OFF; 59 /* No need to check access_ok(), we have previously fetched these bytes. */ 60 printk("At %p:", (void *) address); 61 if ( FPU_CS == __USER_CS ) 62 { 63#define MAX_PRINTED_BYTES 20 64 for ( i = 0; i < MAX_PRINTED_BYTES; i++ ) 65 { 66 FPU_get_user(byte1, (u_char __user *) address); 67 if ( (byte1 & 0xf8) == 0xd8 ) 68 { 69 printk(" %02x", byte1); 70 break; 71 } 72 printk(" [%02x]", byte1); 73 address++; 74 } 75 if ( i == MAX_PRINTED_BYTES ) 76 printk(" [more..]\n"); 77 else 78 { 79 FPU_get_user(FPU_modrm, 1 + (u_char __user *) address); 80 81 if (FPU_modrm >= 0300) 82 printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); 83 else 84 printk(" /%d, mod=%d rm=%d\n", 85 (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7); 86 } 87 } 88 else 89 { 90 printk("%04x\n", FPU_CS); 91 } 92 93 partial_status = status_word(); 94 95#ifdef DEBUGGING 96if ( partial_status & SW_Backward ) printk("SW: backward compatibility\n"); 97if ( partial_status & SW_C3 ) printk("SW: condition bit 3\n"); 98if ( partial_status & SW_C2 ) printk("SW: condition bit 2\n"); 99if ( partial_status & SW_C1 ) printk("SW: condition bit 1\n"); 100if ( partial_status & SW_C0 ) printk("SW: condition bit 0\n"); 101if ( partial_status & SW_Summary ) printk("SW: exception summary\n"); 102if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n"); 103if ( partial_status & SW_Precision ) printk("SW: loss of precision\n"); 104if ( partial_status & SW_Underflow ) printk("SW: underflow\n"); 105if ( partial_status & SW_Overflow ) printk("SW: overflow\n"); 106if ( partial_status & SW_Zero_Div ) printk("SW: divide by zero\n"); 107if ( partial_status & SW_Denorm_Op ) printk("SW: denormalized operand\n"); 108if ( partial_status & SW_Invalid ) printk("SW: invalid operation\n"); 109#endif /* DEBUGGING */ 110 111 printk(" SW: b=%d st=%ld es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n", 112 partial_status & 0x8000 ? 1 : 0, /* busy */ 113 (partial_status & 0x3800) >> 11, /* stack top pointer */ 114 partial_status & 0x80 ? 1 : 0, /* Error summary status */ 115 partial_status & 0x40 ? 1 : 0, /* Stack flag */ 116 partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */ 117 partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */ 118 partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0, 119 partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0, 120 partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0); 121 122printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d ef=%d%d%d%d%d%d\n", 123 control_word & 0x1000 ? 1 : 0, 124 (control_word & 0x800) >> 11, (control_word & 0x400) >> 10, 125 (control_word & 0x200) >> 9, (control_word & 0x100) >> 8, 126 control_word & 0x80 ? 1 : 0, 127 control_word & SW_Precision?1:0, control_word & SW_Underflow?1:0, 128 control_word & SW_Overflow?1:0, control_word & SW_Zero_Div?1:0, 129 control_word & SW_Denorm_Op?1:0, control_word & SW_Invalid?1:0); 130 131 for ( i = 0; i < 8; i++ ) 132 { 133 FPU_REG *r = &st(i); 134 u_char tagi = FPU_gettagi(i); 135 switch (tagi) 136 { 137 case TAG_Empty: 138 continue; 139 break; 140 case TAG_Zero: 141 case TAG_Special: 142 tagi = FPU_Special(r); 143 case TAG_Valid: 144 printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6d ", i, 145 getsign(r) ? '-' : '+', 146 (long)(r->sigh >> 16), 147 (long)(r->sigh & 0xFFFF), 148 (long)(r->sigl >> 16), 149 (long)(r->sigl & 0xFFFF), 150 exponent(r) - EXP_BIAS + 1); 151 break; 152 default: 153 printk("Whoops! Error in errors.c: tag%d is %d ", i, tagi); 154 continue; 155 break; 156 } 157 printk("%s\n", tag_desc[(int) (unsigned) tagi]); 158 } 159 160 RE_ENTRANT_CHECK_ON; 161 162} 163 164static struct { 165 int type; 166 const char *name; 167} exception_names[] = { 168 { EX_StackOver, "stack overflow" }, 169 { EX_StackUnder, "stack underflow" }, 170 { EX_Precision, "loss of precision" }, 171 { EX_Underflow, "underflow" }, 172 { EX_Overflow, "overflow" }, 173 { EX_ZeroDiv, "divide by zero" }, 174 { EX_Denormal, "denormalized operand" }, 175 { EX_Invalid, "invalid operation" }, 176 { EX_INTERNAL, "INTERNAL BUG in "FPU_VERSION }, 177 { 0, NULL } 178}; 179 180/* 181 EX_INTERNAL is always given with a code which indicates where the 182 error was detected. 183 184 Internal error types: 185 0x14 in fpu_etc.c 186 0x1nn in a *.c file: 187 0x101 in reg_add_sub.c 188 0x102 in reg_mul.c 189 0x104 in poly_atan.c 190 0x105 in reg_mul.c 191 0x107 in fpu_trig.c 192 0x108 in reg_compare.c 193 0x109 in reg_compare.c 194 0x110 in reg_add_sub.c 195 0x111 in fpe_entry.c 196 0x112 in fpu_trig.c 197 0x113 in errors.c 198 0x115 in fpu_trig.c 199 0x116 in fpu_trig.c 200 0x117 in fpu_trig.c 201 0x118 in fpu_trig.c 202 0x119 in fpu_trig.c 203 0x120 in poly_atan.c 204 0x121 in reg_compare.c 205 0x122 in reg_compare.c 206 0x123 in reg_compare.c 207 0x125 in fpu_trig.c 208 0x126 in fpu_entry.c 209 0x127 in poly_2xm1.c 210 0x128 in fpu_entry.c 211 0x129 in fpu_entry.c 212 0x130 in get_address.c 213 0x131 in get_address.c 214 0x132 in get_address.c 215 0x133 in get_address.c 216 0x140 in load_store.c 217 0x141 in load_store.c 218 0x150 in poly_sin.c 219 0x151 in poly_sin.c 220 0x160 in reg_ld_str.c 221 0x161 in reg_ld_str.c 222 0x162 in reg_ld_str.c 223 0x163 in reg_ld_str.c 224 0x164 in reg_ld_str.c 225 0x170 in fpu_tags.c 226 0x171 in fpu_tags.c 227 0x172 in fpu_tags.c 228 0x180 in reg_convert.c 229 0x2nn in an *.S file: 230 0x201 in reg_u_add.S 231 0x202 in reg_u_div.S 232 0x203 in reg_u_div.S 233 0x204 in reg_u_div.S 234 0x205 in reg_u_mul.S 235 0x206 in reg_u_sub.S 236 0x207 in wm_sqrt.S 237 0x208 in reg_div.S 238 0x209 in reg_u_sub.S 239 0x210 in reg_u_sub.S 240 0x211 in reg_u_sub.S 241 0x212 in reg_u_sub.S 242 0x213 in wm_sqrt.S 243 0x214 in wm_sqrt.S 244 0x215 in wm_sqrt.S 245 0x220 in reg_norm.S 246 0x221 in reg_norm.S 247 0x230 in reg_round.S 248 0x231 in reg_round.S 249 0x232 in reg_round.S 250 0x233 in reg_round.S 251 0x234 in reg_round.S 252 0x235 in reg_round.S 253 0x236 in reg_round.S 254 0x240 in div_Xsig.S 255 0x241 in div_Xsig.S 256 0x242 in div_Xsig.S 257 */ 258 259asmlinkage void FPU_exception(int n) 260{ 261 int i, int_type; 262 263 int_type = 0; /* Needed only to stop compiler warnings */ 264 if ( n & EX_INTERNAL ) 265 { 266 int_type = n - EX_INTERNAL; 267 n = EX_INTERNAL; 268 /* Set lots of exception bits! */ 269 partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward); 270 } 271 else 272 { 273 /* Extract only the bits which we use to set the status word */ 274 n &= (SW_Exc_Mask); 275 /* Set the corresponding exception bit */ 276 partial_status |= n; 277 /* Set summary bits iff exception isn't masked */ 278 if ( partial_status & ~control_word & CW_Exceptions ) 279 partial_status |= (SW_Summary | SW_Backward); 280 if ( n & (SW_Stack_Fault | EX_Precision) ) 281 { 282 if ( !(n & SW_C1) ) 283 /* This bit distinguishes over- from underflow for a stack fault, 284 and roundup from round-down for precision loss. */ 285 partial_status &= ~SW_C1; 286 } 287 } 288 289 RE_ENTRANT_CHECK_OFF; 290 if ( (~control_word & n & CW_Exceptions) || (n == EX_INTERNAL) ) 291 { 292#ifdef PRINT_MESSAGES 293 /* My message from the sponsor */ 294 printk(FPU_VERSION" "__DATE__" (C) W. Metzenthen.\n"); 295#endif /* PRINT_MESSAGES */ 296 297 /* Get a name string for error reporting */ 298 for (i=0; exception_names[i].type; i++) 299 if ( (exception_names[i].type & n) == exception_names[i].type ) 300 break; 301 302 if (exception_names[i].type) 303 { 304#ifdef PRINT_MESSAGES 305 printk("FP Exception: %s!\n", exception_names[i].name); 306#endif /* PRINT_MESSAGES */ 307 } 308 else 309 printk("FPU emulator: Unknown Exception: 0x%04x!\n", n); 310 311 if ( n == EX_INTERNAL ) 312 { 313 printk("FPU emulator: Internal error type 0x%04x\n", int_type); 314 FPU_printall(); 315 } 316#ifdef PRINT_MESSAGES 317 else 318 FPU_printall(); 319#endif /* PRINT_MESSAGES */ 320 321 /* 322 * The 80486 generates an interrupt on the next non-control FPU 323 * instruction. So we need some means of flagging it. 324 * We use the ES (Error Summary) bit for this. 325 */ 326 } 327 RE_ENTRANT_CHECK_ON; 328 329#ifdef __DEBUG__ 330 math_abort(FPU_info,SIGFPE); 331#endif /* __DEBUG__ */ 332 333} 334 335 336/* Real operation attempted on a NaN. */ 337/* Returns < 0 if the exception is unmasked */ 338int real_1op_NaN(FPU_REG *a) 339{ 340 int signalling, isNaN; 341 342 isNaN = (exponent(a) == EXP_OVER) && (a->sigh & 0x80000000); 343 344 /* The default result for the case of two "equal" NaNs (signs may 345 differ) is chosen to reproduce 80486 behaviour */ 346 signalling = isNaN && !(a->sigh & 0x40000000); 347 348 if ( !signalling ) 349 { 350 if ( !isNaN ) /* pseudo-NaN, or other unsupported? */ 351 { 352 if ( control_word & CW_Invalid ) 353 { 354 /* Masked response */ 355 reg_copy(&CONST_QNaN, a); 356 } 357 EXCEPTION(EX_Invalid); 358 return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special; 359 } 360 return TAG_Special; 361 } 362 363 if ( control_word & CW_Invalid ) 364 { 365 /* The masked response */ 366 if ( !(a->sigh & 0x80000000) ) /* pseudo-NaN ? */ 367 { 368 reg_copy(&CONST_QNaN, a); 369 } 370 /* ensure a Quiet NaN */ 371 a->sigh |= 0x40000000; 372 } 373 374 EXCEPTION(EX_Invalid); 375 376 return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special; 377} 378 379 380/* Real operation attempted on two operands, one a NaN. */ 381/* Returns < 0 if the exception is unmasked */ 382int real_2op_NaN(FPU_REG const *b, u_char tagb, 383 int deststnr, 384 FPU_REG const *defaultNaN) 385{ 386 FPU_REG *dest = &st(deststnr); 387 FPU_REG const *a = dest; 388 u_char taga = FPU_gettagi(deststnr); 389 FPU_REG const *x; 390 int signalling, unsupported; 391 392 if ( taga == TAG_Special ) 393 taga = FPU_Special(a); 394 if ( tagb == TAG_Special ) 395 tagb = FPU_Special(b); 396 397 /* TW_NaN is also used for unsupported data types. */ 398 unsupported = ((taga == TW_NaN) 399 && !((exponent(a) == EXP_OVER) && (a->sigh & 0x80000000))) 400 || ((tagb == TW_NaN) 401 && !((exponent(b) == EXP_OVER) && (b->sigh & 0x80000000))); 402 if ( unsupported ) 403 { 404 if ( control_word & CW_Invalid ) 405 { 406 /* Masked response */ 407 FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr); 408 } 409 EXCEPTION(EX_Invalid); 410 return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special; 411 } 412 413 if (taga == TW_NaN) 414 { 415 x = a; 416 if (tagb == TW_NaN) 417 { 418 signalling = !(a->sigh & b->sigh & 0x40000000); 419 if ( significand(b) > significand(a) ) 420 x = b; 421 else if ( significand(b) == significand(a) ) 422 { 423 /* The default result for the case of two "equal" NaNs (signs may 424 differ) is chosen to reproduce 80486 behaviour */ 425 x = defaultNaN; 426 } 427 } 428 else 429 { 430 /* return the quiet version of the NaN in a */ 431 signalling = !(a->sigh & 0x40000000); 432 } 433 } 434 else 435#ifdef PARANOID 436 if (tagb == TW_NaN) 437#endif /* PARANOID */ 438 { 439 signalling = !(b->sigh & 0x40000000); 440 x = b; 441 } 442#ifdef PARANOID 443 else 444 { 445 signalling = 0; 446 EXCEPTION(EX_INTERNAL|0x113); 447 x = &CONST_QNaN; 448 } 449#endif /* PARANOID */ 450 451 if ( (!signalling) || (control_word & CW_Invalid) ) 452 { 453 if ( ! x ) 454 x = b; 455 456 if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */ 457 x = &CONST_QNaN; 458 459 FPU_copy_to_regi(x, TAG_Special, deststnr); 460 461 if ( !signalling ) 462 return TAG_Special; 463 464 /* ensure a Quiet NaN */ 465 dest->sigh |= 0x40000000; 466 } 467 468 EXCEPTION(EX_Invalid); 469 470 return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special; 471} 472 473 474/* Invalid arith operation on Valid registers */ 475/* Returns < 0 if the exception is unmasked */ 476asmlinkage int arith_invalid(int deststnr) 477{ 478 479 EXCEPTION(EX_Invalid); 480 481 if ( control_word & CW_Invalid ) 482 { 483 /* The masked response */ 484 FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr); 485 } 486 487 return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Valid; 488 489} 490 491 492/* Divide a finite number by zero */ 493asmlinkage int FPU_divide_by_zero(int deststnr, u_char sign) 494{ 495 FPU_REG *dest = &st(deststnr); 496 int tag = TAG_Valid; 497 498 if ( control_word & CW_ZeroDiv ) 499 { 500 /* The masked response */ 501 FPU_copy_to_regi(&CONST_INF, TAG_Special, deststnr); 502 setsign(dest, sign); 503 tag = TAG_Special; 504 } 505 506 EXCEPTION(EX_ZeroDiv); 507 508 return (!(control_word & CW_ZeroDiv) ? FPU_Exception : 0) | tag; 509 510} 511 512 513/* This may be called often, so keep it lean */ 514int set_precision_flag(int flags) 515{ 516 if ( control_word & CW_Precision ) 517 { 518 partial_status &= ~(SW_C1 & flags); 519 partial_status |= flags; /* The masked response */ 520 return 0; 521 } 522 else 523 { 524 EXCEPTION(flags); 525 return 1; 526 } 527} 528 529 530/* This may be called often, so keep it lean */ 531asmlinkage void set_precision_flag_up(void) 532{ 533 if ( control_word & CW_Precision ) 534 partial_status |= (SW_Precision | SW_C1); /* The masked response */ 535 else 536 EXCEPTION(EX_Precision | SW_C1); 537} 538 539 540/* This may be called often, so keep it lean */ 541asmlinkage void set_precision_flag_down(void) 542{ 543 if ( control_word & CW_Precision ) 544 { /* The masked response */ 545 partial_status &= ~SW_C1; 546 partial_status |= SW_Precision; 547 } 548 else 549 EXCEPTION(EX_Precision); 550} 551 552 553asmlinkage int denormal_operand(void) 554{ 555 if ( control_word & CW_Denormal ) 556 { /* The masked response */ 557 partial_status |= SW_Denorm_Op; 558 return TAG_Special; 559 } 560 else 561 { 562 EXCEPTION(EX_Denormal); 563 return TAG_Special | FPU_Exception; 564 } 565} 566 567 568asmlinkage int arith_overflow(FPU_REG *dest) 569{ 570 int tag = TAG_Valid; 571 572 if ( control_word & CW_Overflow ) 573 { 574 /* The masked response */ 575/* ###### The response here depends upon the rounding mode */ 576 reg_copy(&CONST_INF, dest); 577 tag = TAG_Special; 578 } 579 else 580 { 581 /* Subtract the magic number from the exponent */ 582 addexponent(dest, (-3 * (1 << 13))); 583 } 584 585 EXCEPTION(EX_Overflow); 586 if ( control_word & CW_Overflow ) 587 { 588 /* The overflow exception is masked. */ 589 /* By definition, precision is lost. 590 The roundup bit (C1) is also set because we have 591 "rounded" upwards to Infinity. */ 592 EXCEPTION(EX_Precision | SW_C1); 593 return tag; 594 } 595 596 return tag; 597 598} 599 600 601asmlinkage int arith_underflow(FPU_REG *dest) 602{ 603 int tag = TAG_Valid; 604 605 if ( control_word & CW_Underflow ) 606 { 607 /* The masked response */ 608 if ( exponent16(dest) <= EXP_UNDER - 63 ) 609 { 610 reg_copy(&CONST_Z, dest); 611 partial_status &= ~SW_C1; /* Round down. */ 612 tag = TAG_Zero; 613 } 614 else 615 { 616 stdexp(dest); 617 } 618 } 619 else 620 { 621 /* Add the magic number to the exponent. */ 622 addexponent(dest, (3 * (1 << 13)) + EXTENDED_Ebias); 623 } 624 625 EXCEPTION(EX_Underflow); 626 if ( control_word & CW_Underflow ) 627 { 628 /* The underflow exception is masked. */ 629 EXCEPTION(EX_Precision); 630 return tag; 631 } 632 633 return tag; 634 635} 636 637 638void FPU_stack_overflow(void) 639{ 640 641 if ( control_word & CW_Invalid ) 642 { 643 /* The masked response */ 644 top--; 645 FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); 646 } 647 648 EXCEPTION(EX_StackOver); 649 650 return; 651 652} 653 654 655void FPU_stack_underflow(void) 656{ 657 658 if ( control_word & CW_Invalid ) 659 { 660 /* The masked response */ 661 FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); 662 } 663 664 EXCEPTION(EX_StackUnder); 665 666 return; 667 668} 669 670 671void FPU_stack_underflow_i(int i) 672{ 673 674 if ( control_word & CW_Invalid ) 675 { 676 /* The masked response */ 677 FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i); 678 } 679 680 EXCEPTION(EX_StackUnder); 681 682 return; 683 684} 685 686 687void FPU_stack_underflow_pop(int i) 688{ 689 690 if ( control_word & CW_Invalid ) 691 { 692 /* The masked response */ 693 FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i); 694 FPU_pop(); 695 } 696 697 EXCEPTION(EX_StackUnder); 698 699 return; 700 701} 702