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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22/* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27#ident "%Z%%M% %I% %E% SMI" 28 29#include <sys/asm_linkage.h> 30#include <sys/trap.h> 31#include <sys/machpcb.h> 32 33#if !defined(lint) && !defined(__lint) 34#include "assym.h" 35#endif /* lint */ 36 37/* 38 * Floating point trap handling. 39 * 40 * The FPU is always in a V9 current configuration. 41 * 42 * When a user process is first started via exec, 43 * floating point operations will be disabled by default. 44 * Upon execution of the first floating point instruction, 45 * a fp_disabled trap will be generated; then a word in 46 * the uarea is written signifying use of the floating point 47 * registers so that subsequent context switches will save 48 * and restore the floating point them. The trapped instruction 49 * will be restarted and processing will continue as normal. 50 * 51 * When a operation occurs that the hardware cannot properly 52 * handle, an unfinshed fp_op exception will be generated. 53 * Software routines in the kernel will be executed to 54 * simulate proper handling of such conditions. 55 * 56 * Exception handling will emulate all instructions 57 * in the floating point address queue. Note that there 58 * is no %fq in sun4u, because it has precise FP traps. 59 * 60 * Floating point queues are now machine dependent, and std %fq 61 * is an illegal V9 instruction. The fp_exception code has been 62 * moved to sun4u/ml/machfloat.s. 63 * 64 * NOTE: This code DOES NOT SUPPORT KERNEL (DEVICE DRIVER) 65 * USE OF THE FPU 66 * 67 * Instructions for running without the hardware fpu: 68 * 1. Setting fpu_exists to 0 now only works on a DEBUG kernel. 69 * 2. adb -w unix and set fpu_exists, use_hw_bcopy, use_hw_copyio, and 70 * use_hw_bzero to 0 and rename libc_psr.so.1 in 71 * /usr/platform/sun4u/lib so that it will not get used by 72 * the libc bcopy routines. Then reboot the system and you 73 * should see the bootup message "FPU not in use". 74 * 3. To run kaos, you must comment out the code which sets the 75 * version number of the fsr to 7, in fldst: stfsr/stxfsr 76 * (unless you are running against a comparison system that 77 * has the same fsr version number). 78 * 4. The stqf{a}/ldqf{a} instructions cause kaos errors, for reasons 79 * that appear to be a kaos bug, so don't use them! 80 */ 81 82#if defined(lint) || defined(__lint) 83 84#ifdef FP_DISABLED 85int fpu_exists = 0; 86#else 87int fpu_exists = 1; 88#endif 89 90#else /* lint */ 91 92 .section ".data" 93 .align 8 94fsrholder: 95 .word 0 ! dummy place to write fsr 96 .word 0 97 98 DGDEF(fpu_exists) ! always exists for V9 99#ifdef FP_DISABLED 100 .word 0 101#else 102 .word 1 ! sundiag (gack) uses this variable 103#endif 104 105 DGDEF(fpu_version) 106 .word -1 107 108#endif /* lint */ 109 110/* 111 * FPU probe - read the %fsr and get fpu_version. 112 * Called from autoconf. If a %fq is created for 113 * future cpu versions, a fq_exists variable 114 * could be created by this function. 115 */ 116 117#if defined(lint) || defined(__lint) 118 119/*ARGSUSED*/ 120void 121fpu_probe(void) 122{} 123 124#else /* lint */ 125 126 ENTRY_NP(fpu_probe) 127 wr %g0, FPRS_FEF, %fprs ! enable fpu in fprs 128 rdpr %pstate, %g2 ! read pstate, save value in %g2 129 or %g2, PSTATE_PEF, %g1 ! new pstate with fpu enabled 130 wrpr %g1, %g0, %pstate ! write pstate 131 132 sethi %hi(fsrholder), %g2 133 stx %fsr, [%g2 + %lo(fsrholder)] 134 ldx [%g2 + %lo(fsrholder)], %g2 ! snarf the FSR 135 set FSR_VER, %g1 136 and %g2, %g1, %g2 ! get version 137 srl %g2, FSR_VER_SHIFT, %g2 ! and shift it down 138 sethi %hi(fpu_version), %g3 ! save the FPU version 139 st %g2, [%g3 + %lo(fpu_version)] 140 141 ba fp_kstat_init ! initialize the fpu_kstat 142 wr %g0, %g0, %fprs ! disable fpu and clear fprs 143 SET_SIZE(fpu_probe) 144 145#endif /* lint */ 146 147/* 148 * fp_clearregs(fp) 149 * struct v9_fpu *fp; 150 * 151 * Initialization for the hardware fpu. 152 * Clear the fsr and initialize registers to NaN (-1) 153 * The caller (fp_disabled) is supposed to update the fprs 154 * so when the return to userland is made, the fpu is enabled. 155 */ 156 157#if defined(lint) || defined(__lint) 158 159/*ARGSUSED*/ 160void 161fp_clearregs(kfpu_t *fp) 162{} 163 164#else /* lint */ 165 166 ENTRY_NP(fp_clearregs) 167 ldx [%o0 + FPU_FSR], %fsr ! load fsr 168 169 mov -1, %g2 ! -1 is NaN 170 stx %g2, [%o0] ! initialize %f0 171 ldd [%o0], %d0 172 ldd [%o0], %d2 173 ldd [%o0], %d4 174 ldd [%o0], %d6 175 ldd [%o0], %d8 176 ldd [%o0], %d10 177 ldd [%o0], %d12 178 ldd [%o0], %d14 179 ldd [%o0], %d16 180 ldd [%o0], %d18 181 ldd [%o0], %d20 182 ldd [%o0], %d22 183 ldd [%o0], %d24 184 ldd [%o0], %d26 185 ldd [%o0], %d28 186 ldd [%o0], %d30 187 ldd [%o0], %d32 188 ldd [%o0], %d34 189 ldd [%o0], %d36 190 ldd [%o0], %d38 191 ldd [%o0], %d40 192 ldd [%o0], %d42 193 ldd [%o0], %d44 194 ldd [%o0], %d46 195 ldd [%o0], %d48 196 ldd [%o0], %d50 197 ldd [%o0], %d52 198 ldd [%o0], %d54 199 ldd [%o0], %d56 200 ldd [%o0], %d58 201 ldd [%o0], %d60 202 retl 203 ldd [%o0], %d62 204 SET_SIZE(fp_clearregs) 205 206#endif /* lint */ 207 208/* 209 * void _fp_read_pfreg(pf, n) 210 * uint32_t *pf; Old freg value. 211 * unsigned n; Want to read register n 212 * 213 * { 214 * *pf = %f[n]; 215 * } 216 * 217 * void 218 * _fp_write_pfreg(pf, n) 219 * uint32_t *pf; New freg value. 220 * unsigned n; Want to write register n. 221 * 222 * { 223 * %f[n] = *pf; 224 * } 225 */ 226 227#if defined(lint) || defined(__lint) 228 229/*ARGSUSED*/ 230void 231_fp_read_pfreg(uint32_t *pf, u_int n) 232{} 233 234/*ARGSUSED*/ 235void 236_fp_write_pfreg(uint32_t *pf, u_int n) 237{} 238 239#else /* lint */ 240 241 ENTRY_NP(_fp_read_pfreg) 242 sll %o1, 3, %o1 ! Table entries are 8 bytes each. 243 set .stable, %g1 ! g1 gets base of table. 244 jmp %g1 + %o1 ! Jump into table 245 nop ! Can't follow CTI by CTI. 246 247 ENTRY_NP(_fp_write_pfreg) 248 sll %o1, 3, %o1 ! Table entries are 8 bytes each. 249 set .ltable, %g1 ! g1 gets base of table. 250 jmp %g1 + %o1 ! Jump into table 251 nop ! Can't follow CTI by CTI. 252 253#define STOREFP(n) jmp %o7+8 ; st %f/**/n, [%o0] 254 255.stable: 256 STOREFP(0) 257 STOREFP(1) 258 STOREFP(2) 259 STOREFP(3) 260 STOREFP(4) 261 STOREFP(5) 262 STOREFP(6) 263 STOREFP(7) 264 STOREFP(8) 265 STOREFP(9) 266 STOREFP(10) 267 STOREFP(11) 268 STOREFP(12) 269 STOREFP(13) 270 STOREFP(14) 271 STOREFP(15) 272 STOREFP(16) 273 STOREFP(17) 274 STOREFP(18) 275 STOREFP(19) 276 STOREFP(20) 277 STOREFP(21) 278 STOREFP(22) 279 STOREFP(23) 280 STOREFP(24) 281 STOREFP(25) 282 STOREFP(26) 283 STOREFP(27) 284 STOREFP(28) 285 STOREFP(29) 286 STOREFP(30) 287 STOREFP(31) 288 289#define LOADFP(n) jmp %o7+8 ; ld [%o0],%f/**/n 290 291.ltable: 292 LOADFP(0) 293 LOADFP(1) 294 LOADFP(2) 295 LOADFP(3) 296 LOADFP(4) 297 LOADFP(5) 298 LOADFP(6) 299 LOADFP(7) 300 LOADFP(8) 301 LOADFP(9) 302 LOADFP(10) 303 LOADFP(11) 304 LOADFP(12) 305 LOADFP(13) 306 LOADFP(14) 307 LOADFP(15) 308 LOADFP(16) 309 LOADFP(17) 310 LOADFP(18) 311 LOADFP(19) 312 LOADFP(20) 313 LOADFP(21) 314 LOADFP(22) 315 LOADFP(23) 316 LOADFP(24) 317 LOADFP(25) 318 LOADFP(26) 319 LOADFP(27) 320 LOADFP(28) 321 LOADFP(29) 322 LOADFP(30) 323 LOADFP(31) 324 SET_SIZE(_fp_read_pfreg) 325 SET_SIZE(_fp_write_pfreg) 326 327#endif /* lint */ 328 329/* 330 * void _fp_read_pdreg( 331 * uint64_t *pd, Old dreg value. 332 * u_int n) Want to read register n 333 * 334 * { 335 * *pd = %d[n]; 336 * } 337 * 338 * void 339 * _fp_write_pdreg( 340 * uint64_t *pd, New dreg value. 341 * u_int n) Want to write register n. 342 * 343 * { 344 * %d[n] = *pd; 345 * } 346 */ 347 348#if defined(lint) || defined(__lint) 349 350/*ARGSUSED*/ 351void 352_fp_read_pdreg(uint64_t *pd, u_int n) 353{} 354 355/*ARGSUSED*/ 356void 357_fp_write_pdreg(uint64_t *pd, u_int n) 358{} 359 360#else /* lint */ 361 362 ENTRY_NP(_fp_read_pdreg) 363 sll %o1, 3, %o1 ! Table entries are 8 bytes each. 364 set .dstable, %g1 ! g1 gets base of table. 365 jmp %g1 + %o1 ! Jump into table 366 nop ! Can't follow CTI by CTI. 367 368 ENTRY_NP(_fp_write_pdreg) 369 sll %o1, 3, %o1 ! Table entries are 8 bytes each. 370 set .dltable, %g1 ! g1 gets base of table. 371 jmp %g1 + %o1 ! Jump into table 372 nop ! Can't follow CTI by CTI. 373 374#define STOREDP(n) jmp %o7+8 ; std %d/**/n, [%o0] 375 376.dstable: 377 STOREDP(0) 378 STOREDP(2) 379 STOREDP(4) 380 STOREDP(6) 381 STOREDP(8) 382 STOREDP(10) 383 STOREDP(12) 384 STOREDP(14) 385 STOREDP(16) 386 STOREDP(18) 387 STOREDP(20) 388 STOREDP(22) 389 STOREDP(24) 390 STOREDP(26) 391 STOREDP(28) 392 STOREDP(30) 393 STOREDP(32) 394 STOREDP(34) 395 STOREDP(36) 396 STOREDP(38) 397 STOREDP(40) 398 STOREDP(42) 399 STOREDP(44) 400 STOREDP(46) 401 STOREDP(48) 402 STOREDP(50) 403 STOREDP(52) 404 STOREDP(54) 405 STOREDP(56) 406 STOREDP(58) 407 STOREDP(60) 408 STOREDP(62) 409 410#define LOADDP(n) jmp %o7+8 ; ldd [%o0],%d/**/n 411 412.dltable: 413 LOADDP(0) 414 LOADDP(2) 415 LOADDP(4) 416 LOADDP(6) 417 LOADDP(8) 418 LOADDP(10) 419 LOADDP(12) 420 LOADDP(14) 421 LOADDP(16) 422 LOADDP(18) 423 LOADDP(20) 424 LOADDP(22) 425 LOADDP(24) 426 LOADDP(26) 427 LOADDP(28) 428 LOADDP(30) 429 LOADDP(32) 430 LOADDP(34) 431 LOADDP(36) 432 LOADDP(38) 433 LOADDP(40) 434 LOADDP(42) 435 LOADDP(44) 436 LOADDP(46) 437 LOADDP(48) 438 LOADDP(50) 439 LOADDP(52) 440 LOADDP(54) 441 LOADDP(56) 442 LOADDP(58) 443 LOADDP(60) 444 LOADDP(62) 445 SET_SIZE(_fp_read_pdreg) 446 SET_SIZE(_fp_write_pdreg) 447 448#endif /* lint */ 449 450#if defined(lint) || defined(__lint) 451 452/*ARGSUSED*/ 453void 454_fp_write_pfsr(uint64_t *fsr) 455{} 456 457#else /* lint */ 458 459 ENTRY_NP(_fp_write_pfsr) 460 retl 461 ldx [%o0], %fsr 462 SET_SIZE(_fp_write_pfsr) 463 464#endif /* lint */ 465 466#if defined(lint) || defined(__lint) 467 468/*ARGSUSED*/ 469void 470_fp_read_pfsr(uint64_t *fsr) 471{} 472 473#else /* lint */ 474 475 ENTRY_NP(_fp_read_pfsr) 476 retl 477 stx %fsr, [%o0] 478 SET_SIZE(_fp_read_pfsr) 479 480#endif /* lint */ 481 482#if defined(lint) || defined(__lint) 483 484/*ARGSUSED*/ 485void 486_fp_write_fprs(u_int fprs_val) 487{} 488 489#else /* lint */ 490 491 ENTRY_NP(_fp_write_fprs) 492 retl 493 wr %o0, %g0, %fprs ! write fprs 494 SET_SIZE(_fp_write_fprs) 495 496#endif /* lint */ 497 498#if defined(lint) || defined(__lint) 499 500unsigned 501_fp_read_fprs(void) 502{return 0;} 503 504#else /* lint */ 505 506 ENTRY_NP(_fp_read_fprs) 507 retl 508 rd %fprs, %o0 ! save fprs 509 SET_SIZE(_fp_read_fprs) 510 511#endif /* lint */ 512 513#if defined(lint) || defined(__lint) 514 515unsigned 516_fp_subcc_ccr(void) 517{return 0;} 518 519#else /* lint */ 520 521 ENTRY_NP(_fp_subcc_ccr) 522 subcc %o0, %o1, %g0 523 retl 524 rd %ccr, %o0 ! save ccr 525 SET_SIZE(_fp_subcc_ccr) 526 527#endif /* lint */ 528