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