1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #ident  "%Z%%M% %I%     %E% SMI"
  27 
  28 #include <sys/asm_linkage.h>
  29 #include <sys/trap.h>
  30 #include <sys/machpcb.h>
  31 #include <sys/machtrap.h>
  32 #include <sys/machsig.h>
  33 #include <sys/machthread.h>
  34 
  35 #if !defined(lint) && !defined(__lint)
  36 #include "assym.h"
  37 #endif  /* lint */
  38 
  39 /*
  40  * Floating point trap handling.
  41  *
  42  *      The FPU is always in a V9 current configuration.
  43  *
  44  *      When a user process is first started via exec,
  45  *      floating point operations will be disabled by default.
  46  *      Upon execution of the first floating point instruction,
  47  *      a fp_disabled trap will be generated; then a word in
  48  *      the uarea is written signifying use of the floating point
  49  *      registers so that subsequent context switches will save
  50  *      and restore the floating point them. The trapped instruction
  51  *      will be restarted and processing will continue as normal.
  52  *
  53  *      When a operation occurs that the hardware cannot properly
  54  *      handle, an unfinshed fp_op exception will be generated.
  55  *      Software routines in the kernel will be executed to
  56  *      simulate proper handling of such conditions.
  57  *
  58  *      Exception handling will emulate all instructions
  59  *      in the floating point address queue. Note that there
  60  *      is no %fq in sun4u, because it has precise FP traps.
  61  *
  62  *      Floating point queues are now machine dependent, and std %fq
  63  *      is an illegal V9 instruction. The fp_exception code has been
  64  *      moved to sun4u/ml/machfloat.s.
  65  *
  66  *      NOTE: This code DOES NOT SUPPORT KERNEL (DEVICE DRIVER)
  67  *              USE OF THE FPU
  68  *
  69  *      Instructions for running without the hardware fpu:
  70  *      1. Setting fpu_exists to 0 now only works on a DEBUG kernel.
  71  *      2. adb -w unix and set fpu_exists, use_hw_bcopy, use_hw_copyio, and
  72  *              use_hw_bzero to 0 and rename libc_psr.so.1 in
  73  *              /usr/platform/sun4u/lib so that it will not get used by
  74  *              the libc bcopy routines. Then reboot the system and you
  75  *              should see the bootup message "FPU not in use".
  76  *      3. To run kaos, you must comment out the code which sets the
  77  *              version number of the fsr to 7, in fldst: stfsr/stxfsr
  78  *              (unless you are running against a comparison system that
  79  *              has the same fsr version number).
  80  *      4. The stqf{a}/ldqf{a} instructions cause kaos errors, for reasons
  81  *              that appear to be a kaos bug, so don't use them!
  82  */
  83 
  84 #if defined(lint) || defined(__lint)
  85 
  86 #ifdef FP_DISABLED
  87 int fpu_exists = 0;
  88 #else
  89 int fpu_exists = 1;
  90 #endif
  91 
  92 #else   /* lint */
  93 
  94         .section ".data"
  95         .align  8
  96 fsrholder:
  97         .word   0                       ! dummy place to write fsr
  98         .word   0
  99 
 100         DGDEF(fpu_exists)               ! always exists for V9
 101 #ifdef FP_DISABLED
 102         .word   0
 103 #else
 104         .word   1                       ! sundiag (gack) uses this variable
 105 #endif
 106 
 107         DGDEF(fpu_version)
 108         .word   -1
 109 
 110 #endif  /* lint */
 111 
 112 /*
 113  * FPU probe - read the %fsr and get fpu_version.
 114  * Called from autoconf. If a %fq is created for
 115  * future cpu versions, a fq_exists variable
 116  * could be created by this function.
 117  */
 118 
 119 #if defined(lint) || defined(__lint)
 120 
 121 /*ARGSUSED*/
 122 void
 123 fpu_probe(void)
 124 {}
 125 
 126 #else   /* lint */
 127 
 128         ENTRY_NP(fpu_probe)
 129         wr      %g0, FPRS_FEF, %fprs    ! enable fpu in fprs
 130         rdpr    %pstate, %g2            ! read pstate, save value in %g2
 131         or      %g2, PSTATE_PEF, %g1    ! new pstate with fpu enabled
 132         wrpr    %g1, %g0, %pstate       ! write pstate
 133 
 134         sethi   %hi(fsrholder), %g2
 135         stx     %fsr, [%g2 + %lo(fsrholder)]
 136         ldx     [%g2 + %lo(fsrholder)], %g2     ! snarf the FSR
 137         set     FSR_VER, %g1
 138         and     %g2, %g1, %g2                   ! get version
 139         srl     %g2, FSR_VER_SHIFT, %g2         ! and shift it down
 140         sethi   %hi(fpu_version), %g3           ! save the FPU version
 141         st      %g2, [%g3 + %lo(fpu_version)]
 142 
 143         ba      fp_kstat_init           ! initialize the fpu_kstat
 144         wr      %g0, %g0, %fprs         ! disable fpu and clear fprs
 145         SET_SIZE(fpu_probe)
 146 
 147 #endif  /* lint */
 148 
 149 /*
 150  * fp_clearregs(fp)
 151  *      struct v9_fpu *fp;
 152  *
 153  * Initialization for the hardware fpu.
 154  * Clear the fsr and initialize registers to NaN (-1)
 155  * The caller (fp_disabled) is supposed to update the fprs
 156  * so when the return to userland is made, the fpu is enabled.
 157  */
 158 
 159 #if defined(lint) || defined(__lint)
 160 
 161 /*ARGSUSED*/
 162 void
 163 fp_clearregs(kfpu_t *fp)
 164 {}
 165 
 166 #else   /* lint */
 167 
 168         ENTRY_NP(fp_clearregs)
 169         ldx     [%o0 + FPU_FSR], %fsr           ! load fsr
 170 
 171         mov     -1, %g2                         ! -1 is NaN
 172         stx     %g2, [%o0]                      ! initialize %f0
 173         ldd     [%o0], %d0
 174         ldd     [%o0], %d2
 175         ldd     [%o0], %d4
 176         ldd     [%o0], %d6
 177         ldd     [%o0], %d8
 178         ldd     [%o0], %d10
 179         ldd     [%o0], %d12
 180         ldd     [%o0], %d14
 181         ldd     [%o0], %d16
 182         ldd     [%o0], %d18
 183         ldd     [%o0], %d20
 184         ldd     [%o0], %d22
 185         ldd     [%o0], %d24
 186         ldd     [%o0], %d26
 187         ldd     [%o0], %d28
 188         ldd     [%o0], %d30
 189         ldd     [%o0], %d32
 190         ldd     [%o0], %d34
 191         ldd     [%o0], %d36
 192         ldd     [%o0], %d38
 193         ldd     [%o0], %d40
 194         ldd     [%o0], %d42
 195         ldd     [%o0], %d44
 196         ldd     [%o0], %d46
 197         ldd     [%o0], %d48
 198         ldd     [%o0], %d50
 199         ldd     [%o0], %d52
 200         ldd     [%o0], %d54
 201         ldd     [%o0], %d56
 202         ldd     [%o0], %d58
 203         ldd     [%o0], %d60
 204         retl
 205         ldd     [%o0], %d62
 206         SET_SIZE(fp_clearregs)
 207 
 208 #endif  /* lint */
 209 
 210 /*
 211  * void _fp_read_pfreg(pf, n)
 212  *      uint32_t        *pf;    Old freg value.
 213  *      unsigned        n;      Want to read register n
 214  *
 215  * {
 216  *      *pf = %f[n];
 217  * }
 218  *
 219  * void
 220  * _fp_write_pfreg(pf, n)
 221  *      uint32_t        *pf;    New freg value.
 222  *      unsigned        n;      Want to write register n.
 223  *
 224  * {
 225  *      %f[n] = *pf;
 226  * }
 227  */
 228 
 229 #if defined(lint) || defined(__lint)
 230 
 231 /*ARGSUSED*/
 232 void
 233 _fp_read_pfreg(uint32_t *pf, u_int n)
 234 {}
 235 
 236 /*ARGSUSED*/
 237 void
 238 _fp_write_pfreg(uint32_t *pf, u_int n)
 239 {}
 240 
 241 #else   /* lint */
 242 
 243         ENTRY_NP(_fp_read_pfreg)
 244         sll     %o1, 3, %o1             ! Table entries are 8 bytes each.
 245         set     .stable, %g1            ! g1 gets base of table.
 246         jmp     %g1 + %o1               ! Jump into table
 247         nop                             ! Can't follow CTI by CTI.
 248 
 249         ENTRY_NP(_fp_write_pfreg)
 250         sll     %o1, 3, %o1             ! Table entries are 8 bytes each.
 251         set     .ltable, %g1            ! g1 gets base of table.
 252         jmp     %g1 + %o1               ! Jump into table
 253         nop                             ! Can't follow CTI by CTI.
 254 
 255 #define STOREFP(n) jmp %o7+8 ; st %f/**/n, [%o0]
 256 
 257 .stable:
 258         STOREFP(0)
 259         STOREFP(1)
 260         STOREFP(2)
 261         STOREFP(3)
 262         STOREFP(4)
 263         STOREFP(5)
 264         STOREFP(6)
 265         STOREFP(7)
 266         STOREFP(8)
 267         STOREFP(9)
 268         STOREFP(10)
 269         STOREFP(11)
 270         STOREFP(12)
 271         STOREFP(13)
 272         STOREFP(14)
 273         STOREFP(15)
 274         STOREFP(16)
 275         STOREFP(17)
 276         STOREFP(18)
 277         STOREFP(19)
 278         STOREFP(20)
 279         STOREFP(21)
 280         STOREFP(22)
 281         STOREFP(23)
 282         STOREFP(24)
 283         STOREFP(25)
 284         STOREFP(26)
 285         STOREFP(27)
 286         STOREFP(28)
 287         STOREFP(29)
 288         STOREFP(30)
 289         STOREFP(31)
 290 
 291 #define LOADFP(n) jmp %o7+8 ; ld [%o0],%f/**/n
 292 
 293 .ltable:
 294         LOADFP(0)
 295         LOADFP(1)
 296         LOADFP(2)
 297         LOADFP(3)
 298         LOADFP(4)
 299         LOADFP(5)
 300         LOADFP(6)
 301         LOADFP(7)
 302         LOADFP(8)
 303         LOADFP(9)
 304         LOADFP(10)
 305         LOADFP(11)
 306         LOADFP(12)
 307         LOADFP(13)
 308         LOADFP(14)
 309         LOADFP(15)
 310         LOADFP(16)
 311         LOADFP(17)
 312         LOADFP(18)
 313         LOADFP(19)
 314         LOADFP(20)
 315         LOADFP(21)
 316         LOADFP(22)
 317         LOADFP(23)
 318         LOADFP(24)
 319         LOADFP(25)
 320         LOADFP(26)
 321         LOADFP(27)
 322         LOADFP(28)
 323         LOADFP(29)
 324         LOADFP(30)
 325         LOADFP(31)
 326         SET_SIZE(_fp_read_pfreg)
 327         SET_SIZE(_fp_write_pfreg)
 328 
 329 #endif  /* lint */
 330 
 331 /*
 332  * void _fp_read_pdreg(
 333  *      uint64_t        *pd,    Old dreg value.
 334  *      u_int   n)              Want to read register n
 335  *
 336  * {
 337  *      *pd = %d[n];
 338  * }
 339  *
 340  * void
 341  * _fp_write_pdreg(
 342  *      uint64_t        *pd,    New dreg value.
 343  *      u_int   n)              Want to write register n.
 344  *
 345  * {
 346  *      %d[n] = *pd;
 347  * }
 348  */
 349 
 350 #if defined(lint) || defined(__lint)
 351 
 352 /*ARGSUSED*/
 353 void
 354 _fp_read_pdreg(uint64_t *pd, u_int n)
 355 {}
 356 
 357 /*ARGSUSED*/
 358 void
 359 _fp_write_pdreg(uint64_t *pd, u_int n)
 360 {}
 361 
 362 #else   /* lint */
 363 
 364         ENTRY_NP(_fp_read_pdreg)
 365         sll     %o1, 3, %o1             ! Table entries are 8 bytes each.
 366         set     .dstable, %g1           ! g1 gets base of table.
 367         jmp     %g1 + %o1               ! Jump into table
 368         nop                             ! Can't follow CTI by CTI.
 369 
 370         ENTRY_NP(_fp_write_pdreg)
 371         sll     %o1, 3, %o1             ! Table entries are 8 bytes each.
 372         set     .dltable, %g1           ! g1 gets base of table.
 373         jmp     %g1 + %o1               ! Jump into table
 374         nop                             ! Can't follow CTI by CTI.
 375 
 376 #define STOREDP(n) jmp %o7+8 ; std %d/**/n, [%o0]
 377 
 378 .dstable:
 379         STOREDP(0)
 380         STOREDP(2)
 381         STOREDP(4)
 382         STOREDP(6)
 383         STOREDP(8)
 384         STOREDP(10)
 385         STOREDP(12)
 386         STOREDP(14)
 387         STOREDP(16)
 388         STOREDP(18)
 389         STOREDP(20)
 390         STOREDP(22)
 391         STOREDP(24)
 392         STOREDP(26)
 393         STOREDP(28)
 394         STOREDP(30)
 395         STOREDP(32)
 396         STOREDP(34)
 397         STOREDP(36)
 398         STOREDP(38)
 399         STOREDP(40)
 400         STOREDP(42)
 401         STOREDP(44)
 402         STOREDP(46)
 403         STOREDP(48)
 404         STOREDP(50)
 405         STOREDP(52)
 406         STOREDP(54)
 407         STOREDP(56)
 408         STOREDP(58)
 409         STOREDP(60)
 410         STOREDP(62)
 411 
 412 #define LOADDP(n) jmp %o7+8 ; ldd [%o0],%d/**/n
 413 
 414 .dltable:
 415         LOADDP(0)
 416         LOADDP(2)
 417         LOADDP(4)
 418         LOADDP(6)
 419         LOADDP(8)
 420         LOADDP(10)
 421         LOADDP(12)
 422         LOADDP(14)
 423         LOADDP(16)
 424         LOADDP(18)
 425         LOADDP(20)
 426         LOADDP(22)
 427         LOADDP(24)
 428         LOADDP(26)
 429         LOADDP(28)
 430         LOADDP(30)
 431         LOADDP(32)
 432         LOADDP(34)
 433         LOADDP(36)
 434         LOADDP(38)
 435         LOADDP(40)
 436         LOADDP(42)
 437         LOADDP(44)
 438         LOADDP(46)
 439         LOADDP(48)
 440         LOADDP(50)
 441         LOADDP(52)
 442         LOADDP(54)
 443         LOADDP(56)
 444         LOADDP(58)
 445         LOADDP(60)
 446         LOADDP(62)
 447         SET_SIZE(_fp_read_pdreg)
 448         SET_SIZE(_fp_write_pdreg)
 449 
 450 #endif  /* lint */
 451 
 452 #if defined(lint) || defined(__lint)
 453 
 454 /*ARGSUSED*/
 455 void
 456 _fp_write_pfsr(uint64_t *fsr)
 457 {}
 458 
 459 #else   /* lint */
 460 
 461         ENTRY_NP(_fp_write_pfsr)
 462         retl
 463         ldx     [%o0], %fsr
 464         SET_SIZE(_fp_write_pfsr)
 465 
 466 #endif  /* lint */
 467 
 468 #if defined(lint) || defined(__lint)
 469 
 470 /*ARGSUSED*/
 471 void
 472 _fp_read_pfsr(uint64_t *fsr)
 473 {}
 474 
 475 #else   /* lint */
 476 
 477         ENTRY_NP(_fp_read_pfsr)
 478         retl
 479         stx     %fsr, [%o0]
 480         SET_SIZE(_fp_read_pfsr)
 481 
 482 #endif  /* lint */
 483 
 484 #if defined(lint) || defined(__lint)
 485 
 486 /*ARGSUSED*/
 487 void
 488 _fp_write_fprs(u_int fprs_val)
 489 {}
 490 
 491 #else   /* lint */
 492 
 493         ENTRY_NP(_fp_write_fprs)
 494         retl
 495         wr      %o0, %g0, %fprs                 ! write fprs
 496         SET_SIZE(_fp_write_fprs)
 497 
 498 #endif  /* lint */
 499 
 500 #if defined(lint) || defined(__lint)
 501 
 502 unsigned
 503 _fp_read_fprs(void)
 504 {return 0;}
 505 
 506 #else   /* lint */
 507 
 508         ENTRY_NP(_fp_read_fprs)
 509         retl
 510         rd      %fprs, %o0                      ! save fprs
 511         SET_SIZE(_fp_read_fprs)
 512 
 513 #endif  /* lint */
 514 
 515 #if defined(lint) || defined(__lint)
 516 
 517 unsigned
 518 _fp_subcc_ccr(void)
 519 {return 0;}
 520 
 521 #else   /* lint */
 522 
 523         ENTRY_NP(_fp_subcc_ccr)
 524         subcc   %o0, %o1, %g0
 525         retl
 526         rd      %ccr, %o0                       ! save ccr
 527         SET_SIZE(_fp_subcc_ccr)
 528 
 529 #endif  /* lint */
 530 
 531 /*
 532  * Floating Point Exceptions handled according to type:
 533  *      2) unfinished_fpop
 534  *              re-execute the faulty instruction(s) using
 535  *              software emulation (must do every instruction in FQ)
 536  *      3) unimplemented_fpop
 537  *              an unimplemented instruction, if it is legal,
 538  *              will cause emulation of the instruction (and all
 539  *              other instuctions in the FQ)
 540  *      4) sequence_error
 541  *              panic, this should not happen, and if it does it
 542  *              it is the result of a kernel bug
 543  *
 544  * This code assumes the trap preamble has set up the window environment
 545  * for execution of kernel code.
 546  * Note: this code could be changed to be part of the cpu-specific
 547  * (ie, Spitfire-specific) module code before final release.
 548  */
 549 
 550 #if defined(lint)
 551 
 552 /* ARGSUSED */
 553 void
 554 _fp_exception(struct regs *rp, uint64_t fsr)
 555 {}
 556 
 557 #else   /* lint */
 558 
 559         ENTRY_NP(_fp_exception)
 560         mov     %o7, %l0                ! saved return address
 561         mov     %o0, %l1                ! saved *rp
 562         set     FSR_FTT, %o4            ! put FSR_FTT in %o4
 563         xor     %o4, 0xffffffffffffffff, %o3 ! xor FSR_FTT to get
 564         and     %o1, %o3, %o2           ! an fsr with a zero'd ftt
 565         ldn     [THREAD_REG + T_LWP], %o3 ! get lwp
 566         ldn     [%o3 + LWP_FPU], %l3    ! get lwp_fpu
 567         stx     %o2, [%l3 + FPU_FSR]    ! save floating point status
 568         and     %o1, %o4, %g2           ! get the ftt trap type
 569 #ifdef  DEBUG
 570         brnz,a,pt %g2, fttok
 571           nop
 572         set     .badfpfttmsg, %o0       ! panic message
 573         call    panic                   ! %o1 has the fsr w/ftt value
 574         nop
 575 fttok:
 576 #endif  /* DEBUG */
 577         srl     %g2, FSR_FTT_SHIFT, %o4 ! check ftt
 578         cmp     %o4, FTT_SEQ            ! sanity check for bogus exceptions
 579         !
 580         ! traps are already enabled to allow other
 581         ! interrupts while emulating floating point instructions
 582         !
 583         blt,a,pt %xcc, fpeok
 584         nop
 585         !
 586         ! Sequence error or unknown ftt exception.
 587         !
 588 seq_error:
 589         set     .badfpexcpmsg, %o0      ! panic if bad ftt
 590         call    panic
 591         sra     %o4, 0, %o1             ! mov ftt to o1 for panic message
 592 
 593 fpeok:
 594         call    fp_kstat_update         ! fp_kstat_update(ftt)
 595         mov     %o4, %o0                ! ftt
 596         !
 597         ! Get the floating point instruction, and run the floating
 598         ! point simulator. There is no floating point queue, so we fake one.
 599         !
 600         call    fp_precise              ! fp_precise(&regs)
 601         mov     %l1, %o0                ! saved *rp
 602 
 603 fp_ret:
 604         rd      %fprs, %g1              ! read fprs, save value in %g1
 605         st      %g1, [%l3 + FPU_FPRS]   ! save fprs
 606         jmp     %l0 + 8                 ! jump to saved return address
 607         stx     %fsr, [%l3 + FPU_FSR]   ! save fsr
 608         SET_SIZE(_fp_exception)
 609 
 610 .badfpexcpmsg:
 611         .asciz  "unexpected floating point exception %x"
 612 
 613 #ifdef  DEBUG
 614 .badfpfttmsg:
 615         .asciz  "No floating point ftt, fsr %llx"
 616 #endif  /* DEBUG */
 617 
 618 #endif  /* lint */
 619 
 620 /*
 621  * Floating Point Exceptions.
 622  * handled according to type:
 623  *      1) IEEE_exception
 624  *              re-execute the faulty instruction(s) using
 625  *              software emulation (must do every instruction in FQ)
 626  *
 627  * This code assumes the trap preamble has set up the window environment
 628  * for execution of kernel code.
 629  */
 630 
 631 #if defined(lint)
 632 
 633 /* ARGSUSED */
 634 void
 635 _fp_ieee_exception(struct regs *rp, uint64_t fsr)
 636 {}
 637 
 638 #else   /* lint */
 639 
 640         ENTRY_NP(_fp_ieee_exception)
 641         mov     %o7, %l0                ! saved return address
 642         mov     %o0, %l1                ! saved *rp
 643         mov     %o1, %l2                ! saved fsr
 644         set     FSR_FTT, %o4            ! put FSR_FTT in %o4
 645         xor     %o4, 0xffffffffffffffff, %o3 ! ! xor FSR_FTT to get
 646         and     %o1, %o3, %o2           ! an fsr with a zero'd ftt
 647         ldn     [THREAD_REG + T_LWP], %o3 ! get lwp
 648         ldn     [%o3 + LWP_FPU], %l3    ! get lwp_fpu
 649         stx     %o2, [%l3 + FPU_FSR]    ! save floating point status
 650         stub    %g0, [%l3 + FPU_QCNT]   ! clear fpu_qcnt
 651         and     %o1, %o4, %g2           ! mask out trap type
 652 #ifdef  DEBUG
 653         brnz,a,pt %g2, fttgd
 654           nop
 655         set     .badfpfttmsg, %o0       ! panic message
 656         call    panic                   ! %o1 has the fsr w/ftt value
 657         nop
 658 fttgd:
 659 #endif  /* DEBUG */
 660         srl     %g2, FSR_FTT_SHIFT, %o4 ! check ftt
 661         cmp     %o4, FTT_SEQ            ! sanity check for bogus exceptions
 662         !
 663         ! traps are already enabled to allow other
 664         ! interrupts while emulating floating point instructions
 665         !
 666         blt,a,pt %xcc, fpegd
 667         nop
 668         !
 669         ! Sequence error or unknown ftt exception.
 670         !
 671 seq_err:
 672         set     .badfpexcpmsg, %o0      ! panic if bad ftt
 673         call    panic
 674         sra     %o4, 0, %o1             ! mov ftt to o1 for panic message
 675 
 676 fpegd:
 677         call    fp_kstat_update         ! fp_kstat_update(ftt)
 678         mov     %o4, %o0                ! ftt
 679         !
 680         ! Call fpu_trap directly, don't bother to run the fp simulator.
 681         ! The *rp is already in %o0. Clear fpu_qcnt.
 682         !
 683         set     (T_FP_EXCEPTION_IEEE), %o2      ! trap type
 684 
 685         set     FSR_CEXC, %o3
 686         and     %l2, %o3, %g2           ! mask out cexc
 687 
 688         andcc   %g2, FSR_CEXC_NX, %g0   ! check for inexact
 689         bnz,a,pt %xcc, fpok
 690         or      %g0, FPE_FLTRES, %o3    ! fp inexact code
 691 
 692         andcc   %g2, FSR_CEXC_DZ, %g0   ! check for divide-by-zero
 693         bnz,a,pt %xcc, fpok
 694         or      %g0, FPE_FLTDIV, %o3    ! fp divide by zero code
 695 
 696         andcc   %g2, FSR_CEXC_UF, %g0   ! check for underflow
 697         bnz,a,pt %xcc, fpok
 698         or      %g0, FPE_FLTUND, %o3    ! fp underflow code
 699 
 700         andcc   %g2, FSR_CEXC_OF, %g0   ! check for overflow
 701         bnz,a,pt %xcc, fpok
 702         or      %g0, FPE_FLTOVF, %o3    ! fp overflow code
 703 
 704         andcc   %g2, FSR_CEXC_NV, %g0   ! check for invalid
 705         bnz,a,pn %xcc, fpok
 706         or      %g0, FPE_FLTINV, %o3    ! fp invalid code
 707 
 708 cexec_err:
 709         set     .badfpcexcmsg, %o0      ! panic message
 710         call    panic                   ! panic if no cexc bit set
 711         mov     %g1, %o1
 712 fpok:
 713         mov     %l1, %o0                ! saved *rp
 714         call    fpu_trap                ! fpu_trap(&regs, addr, type, code)
 715         ldn     [%o0 + PC_OFF], %o1     ! address of trapping instruction
 716 
 717         rd      %fprs, %g1              ! read fprs, save value in %g1
 718         st      %g1, [%l3 + FPU_FPRS]   ! save fprs
 719         jmp     %l0 + 8                 ! jump to saved return address
 720         stx     %fsr, [%l3 + FPU_FSR]   ! save fsr
 721         SET_SIZE(_fp_ieee_exception)
 722 
 723 .badfpcexcmsg:
 724         .asciz  "No floating point exception, fsr %llx"
 725 
 726 #endif  /* lint */