/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #if !defined(lint) && !defined(__lint) #include "assym.h" #endif /* lint */ /* * Floating point trap handling. * * The FPU is always in a V9 current configuration. * * When a user process is first started via exec, * floating point operations will be disabled by default. * Upon execution of the first floating point instruction, * a fp_disabled trap will be generated; then a word in * the uarea is written signifying use of the floating point * registers so that subsequent context switches will save * and restore the floating point them. The trapped instruction * will be restarted and processing will continue as normal. * * When a operation occurs that the hardware cannot properly * handle, an unfinshed fp_op exception will be generated. * Software routines in the kernel will be executed to * simulate proper handling of such conditions. * * Exception handling will emulate all instructions * in the floating point address queue. Note that there * is no %fq in sun4u, because it has precise FP traps. * * Floating point queues are now machine dependent, and std %fq * is an illegal V9 instruction. The fp_exception code has been * moved to sun4u/ml/machfloat.s. * * NOTE: This code DOES NOT SUPPORT KERNEL (DEVICE DRIVER) * USE OF THE FPU * * Instructions for running without the hardware fpu: * 1. Setting fpu_exists to 0 now only works on a DEBUG kernel. * 2. adb -w unix and set fpu_exists, use_hw_bcopy, use_hw_copyio, and * use_hw_bzero to 0 and rename libc_psr.so.1 in * /usr/platform/sun4u/lib so that it will not get used by * the libc bcopy routines. Then reboot the system and you * should see the bootup message "FPU not in use". * 3. To run kaos, you must comment out the code which sets the * version number of the fsr to 7, in fldst: stfsr/stxfsr * (unless you are running against a comparison system that * has the same fsr version number). * 4. The stqf{a}/ldqf{a} instructions cause kaos errors, for reasons * that appear to be a kaos bug, so don't use them! */ #if defined(lint) || defined(__lint) #ifdef FP_DISABLED int fpu_exists = 0; #else int fpu_exists = 1; #endif #else /* lint */ .section ".data" .align 8 fsrholder: .word 0 ! dummy place to write fsr .word 0 DGDEF(fpu_exists) ! always exists for V9 #ifdef FP_DISABLED .word 0 #else .word 1 ! sundiag (gack) uses this variable #endif DGDEF(fpu_version) .word -1 #endif /* lint */ /* * FPU probe - read the %fsr and get fpu_version. * Called from autoconf. If a %fq is created for * future cpu versions, a fq_exists variable * could be created by this function. */ #if defined(lint) || defined(__lint) /*ARGSUSED*/ void fpu_probe(void) {} #else /* lint */ ENTRY_NP(fpu_probe) wr %g0, FPRS_FEF, %fprs ! enable fpu in fprs rdpr %pstate, %g2 ! read pstate, save value in %g2 or %g2, PSTATE_PEF, %g1 ! new pstate with fpu enabled wrpr %g1, %g0, %pstate ! write pstate sethi %hi(fsrholder), %g2 stx %fsr, [%g2 + %lo(fsrholder)] ldx [%g2 + %lo(fsrholder)], %g2 ! snarf the FSR set FSR_VER, %g1 and %g2, %g1, %g2 ! get version srl %g2, FSR_VER_SHIFT, %g2 ! and shift it down sethi %hi(fpu_version), %g3 ! save the FPU version st %g2, [%g3 + %lo(fpu_version)] ba fp_kstat_init ! initialize the fpu_kstat wr %g0, %g0, %fprs ! disable fpu and clear fprs SET_SIZE(fpu_probe) #endif /* lint */ /* * fp_clearregs(fp) * struct v9_fpu *fp; * * Initialization for the hardware fpu. * Clear the fsr and initialize registers to NaN (-1) * The caller (fp_disabled) is supposed to update the fprs * so when the return to userland is made, the fpu is enabled. */ #if defined(lint) || defined(__lint) /*ARGSUSED*/ void fp_clearregs(kfpu_t *fp) {} #else /* lint */ ENTRY_NP(fp_clearregs) ldx [%o0 + FPU_FSR], %fsr ! load fsr mov -1, %g2 ! -1 is NaN stx %g2, [%o0] ! initialize %f0 ldd [%o0], %d0 ldd [%o0], %d2 ldd [%o0], %d4 ldd [%o0], %d6 ldd [%o0], %d8 ldd [%o0], %d10 ldd [%o0], %d12 ldd [%o0], %d14 ldd [%o0], %d16 ldd [%o0], %d18 ldd [%o0], %d20 ldd [%o0], %d22 ldd [%o0], %d24 ldd [%o0], %d26 ldd [%o0], %d28 ldd [%o0], %d30 ldd [%o0], %d32 ldd [%o0], %d34 ldd [%o0], %d36 ldd [%o0], %d38 ldd [%o0], %d40 ldd [%o0], %d42 ldd [%o0], %d44 ldd [%o0], %d46 ldd [%o0], %d48 ldd [%o0], %d50 ldd [%o0], %d52 ldd [%o0], %d54 ldd [%o0], %d56 ldd [%o0], %d58 ldd [%o0], %d60 retl ldd [%o0], %d62 SET_SIZE(fp_clearregs) #endif /* lint */ /* * void _fp_read_pfreg(pf, n) * uint32_t *pf; Old freg value. * unsigned n; Want to read register n * * { * *pf = %f[n]; * } * * void * _fp_write_pfreg(pf, n) * uint32_t *pf; New freg value. * unsigned n; Want to write register n. * * { * %f[n] = *pf; * } */ #if defined(lint) || defined(__lint) /*ARGSUSED*/ void _fp_read_pfreg(uint32_t *pf, u_int n) {} /*ARGSUSED*/ void _fp_write_pfreg(uint32_t *pf, u_int n) {} #else /* lint */ ENTRY_NP(_fp_read_pfreg) sll %o1, 3, %o1 ! Table entries are 8 bytes each. set .stable, %g1 ! g1 gets base of table. jmp %g1 + %o1 ! Jump into table nop ! Can't follow CTI by CTI. ENTRY_NP(_fp_write_pfreg) sll %o1, 3, %o1 ! Table entries are 8 bytes each. set .ltable, %g1 ! g1 gets base of table. jmp %g1 + %o1 ! Jump into table nop ! Can't follow CTI by CTI. #define STOREFP(n) jmp %o7+8 ; st %f/**/n, [%o0] .stable: STOREFP(0) STOREFP(1) STOREFP(2) STOREFP(3) STOREFP(4) STOREFP(5) STOREFP(6) STOREFP(7) STOREFP(8) STOREFP(9) STOREFP(10) STOREFP(11) STOREFP(12) STOREFP(13) STOREFP(14) STOREFP(15) STOREFP(16) STOREFP(17) STOREFP(18) STOREFP(19) STOREFP(20) STOREFP(21) STOREFP(22) STOREFP(23) STOREFP(24) STOREFP(25) STOREFP(26) STOREFP(27) STOREFP(28) STOREFP(29) STOREFP(30) STOREFP(31) #define LOADFP(n) jmp %o7+8 ; ld [%o0],%f/**/n .ltable: LOADFP(0) LOADFP(1) LOADFP(2) LOADFP(3) LOADFP(4) LOADFP(5) LOADFP(6) LOADFP(7) LOADFP(8) LOADFP(9) LOADFP(10) LOADFP(11) LOADFP(12) LOADFP(13) LOADFP(14) LOADFP(15) LOADFP(16) LOADFP(17) LOADFP(18) LOADFP(19) LOADFP(20) LOADFP(21) LOADFP(22) LOADFP(23) LOADFP(24) LOADFP(25) LOADFP(26) LOADFP(27) LOADFP(28) LOADFP(29) LOADFP(30) LOADFP(31) SET_SIZE(_fp_read_pfreg) SET_SIZE(_fp_write_pfreg) #endif /* lint */ /* * void _fp_read_pdreg( * uint64_t *pd, Old dreg value. * u_int n) Want to read register n * * { * *pd = %d[n]; * } * * void * _fp_write_pdreg( * uint64_t *pd, New dreg value. * u_int n) Want to write register n. * * { * %d[n] = *pd; * } */ #if defined(lint) || defined(__lint) /*ARGSUSED*/ void _fp_read_pdreg(uint64_t *pd, u_int n) {} /*ARGSUSED*/ void _fp_write_pdreg(uint64_t *pd, u_int n) {} #else /* lint */ ENTRY_NP(_fp_read_pdreg) sll %o1, 3, %o1 ! Table entries are 8 bytes each. set .dstable, %g1 ! g1 gets base of table. jmp %g1 + %o1 ! Jump into table nop ! Can't follow CTI by CTI. ENTRY_NP(_fp_write_pdreg) sll %o1, 3, %o1 ! Table entries are 8 bytes each. set .dltable, %g1 ! g1 gets base of table. jmp %g1 + %o1 ! Jump into table nop ! Can't follow CTI by CTI. #define STOREDP(n) jmp %o7+8 ; std %d/**/n, [%o0] .dstable: STOREDP(0) STOREDP(2) STOREDP(4) STOREDP(6) STOREDP(8) STOREDP(10) STOREDP(12) STOREDP(14) STOREDP(16) STOREDP(18) STOREDP(20) STOREDP(22) STOREDP(24) STOREDP(26) STOREDP(28) STOREDP(30) STOREDP(32) STOREDP(34) STOREDP(36) STOREDP(38) STOREDP(40) STOREDP(42) STOREDP(44) STOREDP(46) STOREDP(48) STOREDP(50) STOREDP(52) STOREDP(54) STOREDP(56) STOREDP(58) STOREDP(60) STOREDP(62) #define LOADDP(n) jmp %o7+8 ; ldd [%o0],%d/**/n .dltable: LOADDP(0) LOADDP(2) LOADDP(4) LOADDP(6) LOADDP(8) LOADDP(10) LOADDP(12) LOADDP(14) LOADDP(16) LOADDP(18) LOADDP(20) LOADDP(22) LOADDP(24) LOADDP(26) LOADDP(28) LOADDP(30) LOADDP(32) LOADDP(34) LOADDP(36) LOADDP(38) LOADDP(40) LOADDP(42) LOADDP(44) LOADDP(46) LOADDP(48) LOADDP(50) LOADDP(52) LOADDP(54) LOADDP(56) LOADDP(58) LOADDP(60) LOADDP(62) SET_SIZE(_fp_read_pdreg) SET_SIZE(_fp_write_pdreg) #endif /* lint */ #if defined(lint) || defined(__lint) /*ARGSUSED*/ void _fp_write_pfsr(uint64_t *fsr) {} #else /* lint */ ENTRY_NP(_fp_write_pfsr) retl ldx [%o0], %fsr SET_SIZE(_fp_write_pfsr) #endif /* lint */ #if defined(lint) || defined(__lint) /*ARGSUSED*/ void _fp_read_pfsr(uint64_t *fsr) {} #else /* lint */ ENTRY_NP(_fp_read_pfsr) retl stx %fsr, [%o0] SET_SIZE(_fp_read_pfsr) #endif /* lint */ #if defined(lint) || defined(__lint) /*ARGSUSED*/ void _fp_write_fprs(u_int fprs_val) {} #else /* lint */ ENTRY_NP(_fp_write_fprs) retl wr %o0, %g0, %fprs ! write fprs SET_SIZE(_fp_write_fprs) #endif /* lint */ #if defined(lint) || defined(__lint) unsigned _fp_read_fprs(void) {return 0;} #else /* lint */ ENTRY_NP(_fp_read_fprs) retl rd %fprs, %o0 ! save fprs SET_SIZE(_fp_read_fprs) #endif /* lint */ #if defined(lint) || defined(__lint) unsigned _fp_subcc_ccr(void) {return 0;} #else /* lint */ ENTRY_NP(_fp_subcc_ccr) subcc %o0, %o1, %g0 retl rd %ccr, %o0 ! save ccr SET_SIZE(_fp_subcc_ccr) #endif /* lint */ /* * Floating Point Exceptions handled according to type: * 2) unfinished_fpop * re-execute the faulty instruction(s) using * software emulation (must do every instruction in FQ) * 3) unimplemented_fpop * an unimplemented instruction, if it is legal, * will cause emulation of the instruction (and all * other instuctions in the FQ) * 4) sequence_error * panic, this should not happen, and if it does it * it is the result of a kernel bug * * This code assumes the trap preamble has set up the window environment * for execution of kernel code. * Note: this code could be changed to be part of the cpu-specific * (ie, Spitfire-specific) module code before final release. */ #if defined(lint) /* ARGSUSED */ void _fp_exception(struct regs *rp, uint64_t fsr) {} #else /* lint */ ENTRY_NP(_fp_exception) mov %o7, %l0 ! saved return address mov %o0, %l1 ! saved *rp set FSR_FTT, %o4 ! put FSR_FTT in %o4 xor %o4, 0xffffffffffffffff, %o3 ! xor FSR_FTT to get and %o1, %o3, %o2 ! an fsr with a zero'd ftt ldn [THREAD_REG + T_LWP], %o3 ! get lwp ldn [%o3 + LWP_FPU], %l3 ! get lwp_fpu stx %o2, [%l3 + FPU_FSR] ! save floating point status and %o1, %o4, %g2 ! get the ftt trap type #ifdef DEBUG brnz,a,pt %g2, fttok nop set .badfpfttmsg, %o0 ! panic message call panic ! %o1 has the fsr w/ftt value nop fttok: #endif /* DEBUG */ srl %g2, FSR_FTT_SHIFT, %o4 ! check ftt cmp %o4, FTT_SEQ ! sanity check for bogus exceptions ! ! traps are already enabled to allow other ! interrupts while emulating floating point instructions ! blt,a,pt %xcc, fpeok nop ! ! Sequence error or unknown ftt exception. ! seq_error: set .badfpexcpmsg, %o0 ! panic if bad ftt call panic sra %o4, 0, %o1 ! mov ftt to o1 for panic message fpeok: call fp_kstat_update ! fp_kstat_update(ftt) mov %o4, %o0 ! ftt ! ! Get the floating point instruction, and run the floating ! point simulator. There is no floating point queue, so we fake one. ! call fp_precise ! fp_precise(®s) mov %l1, %o0 ! saved *rp fp_ret: rd %fprs, %g1 ! read fprs, save value in %g1 st %g1, [%l3 + FPU_FPRS] ! save fprs jmp %l0 + 8 ! jump to saved return address stx %fsr, [%l3 + FPU_FSR] ! save fsr SET_SIZE(_fp_exception) .badfpexcpmsg: .asciz "unexpected floating point exception %x" #ifdef DEBUG .badfpfttmsg: .asciz "No floating point ftt, fsr %llx" #endif /* DEBUG */ #endif /* lint */ /* * Floating Point Exceptions. * handled according to type: * 1) IEEE_exception * re-execute the faulty instruction(s) using * software emulation (must do every instruction in FQ) * * This code assumes the trap preamble has set up the window environment * for execution of kernel code. */ #if defined(lint) /* ARGSUSED */ void _fp_ieee_exception(struct regs *rp, uint64_t fsr) {} #else /* lint */ ENTRY_NP(_fp_ieee_exception) mov %o7, %l0 ! saved return address mov %o0, %l1 ! saved *rp mov %o1, %l2 ! saved fsr set FSR_FTT, %o4 ! put FSR_FTT in %o4 xor %o4, 0xffffffffffffffff, %o3 ! ! xor FSR_FTT to get and %o1, %o3, %o2 ! an fsr with a zero'd ftt ldn [THREAD_REG + T_LWP], %o3 ! get lwp ldn [%o3 + LWP_FPU], %l3 ! get lwp_fpu stx %o2, [%l3 + FPU_FSR] ! save floating point status stub %g0, [%l3 + FPU_QCNT] ! clear fpu_qcnt and %o1, %o4, %g2 ! mask out trap type #ifdef DEBUG brnz,a,pt %g2, fttgd nop set .badfpfttmsg, %o0 ! panic message call panic ! %o1 has the fsr w/ftt value nop fttgd: #endif /* DEBUG */ srl %g2, FSR_FTT_SHIFT, %o4 ! check ftt cmp %o4, FTT_SEQ ! sanity check for bogus exceptions ! ! traps are already enabled to allow other ! interrupts while emulating floating point instructions ! blt,a,pt %xcc, fpegd nop ! ! Sequence error or unknown ftt exception. ! seq_err: set .badfpexcpmsg, %o0 ! panic if bad ftt call panic sra %o4, 0, %o1 ! mov ftt to o1 for panic message fpegd: call fp_kstat_update ! fp_kstat_update(ftt) mov %o4, %o0 ! ftt ! ! Call fpu_trap directly, don't bother to run the fp simulator. ! The *rp is already in %o0. Clear fpu_qcnt. ! set (T_FP_EXCEPTION_IEEE), %o2 ! trap type set FSR_CEXC, %o3 and %l2, %o3, %g2 ! mask out cexc andcc %g2, FSR_CEXC_NX, %g0 ! check for inexact bnz,a,pt %xcc, fpok or %g0, FPE_FLTRES, %o3 ! fp inexact code andcc %g2, FSR_CEXC_DZ, %g0 ! check for divide-by-zero bnz,a,pt %xcc, fpok or %g0, FPE_FLTDIV, %o3 ! fp divide by zero code andcc %g2, FSR_CEXC_UF, %g0 ! check for underflow bnz,a,pt %xcc, fpok or %g0, FPE_FLTUND, %o3 ! fp underflow code andcc %g2, FSR_CEXC_OF, %g0 ! check for overflow bnz,a,pt %xcc, fpok or %g0, FPE_FLTOVF, %o3 ! fp overflow code andcc %g2, FSR_CEXC_NV, %g0 ! check for invalid bnz,a,pn %xcc, fpok or %g0, FPE_FLTINV, %o3 ! fp invalid code cexec_err: set .badfpcexcmsg, %o0 ! panic message call panic ! panic if no cexc bit set mov %g1, %o1 fpok: mov %l1, %o0 ! saved *rp call fpu_trap ! fpu_trap(®s, addr, type, code) ldn [%o0 + PC_OFF], %o1 ! address of trapping instruction rd %fprs, %g1 ! read fprs, save value in %g1 st %g1, [%l3 + FPU_FPRS] ! save fprs jmp %l0 + 8 ! jump to saved return address stx %fsr, [%l3 + FPU_FSR] ! save fsr SET_SIZE(_fp_ieee_exception) .badfpcexcmsg: .asciz "No floating point exception, fsr %llx" #endif /* lint */