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(®s) 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(®s, 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 */