/* @(#)float.s 1.1 94/10/31 SMI */ /* * Copyright (c) 1989 by Sun Microsystems, Inc. */ #include #include #include #include #include #include "assym.s" /* * Floating point trap handling. * * The FPU may not be in the current configuration. * If an fp_disabled trap is generated and the EF bit * in the psr equals 1 (floating point was enabled) * then there is not a FPU in the configuration * and the global variable fp_exists is cleared. * * 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; at which point * a check is made to see if the FPU exists, (fp_exists > 0), * if not the instruction is emulated in software, otherwise * the uarea is updated signifying use of the FPU so that * future floating point context switches will save and restore * floating point state. 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. * * At process context switch time we save fp state of * the current process if it was using the fpu. * The next user of the FPU will get a fp_disabled * trap which restores the state if it was already * using it or initializes it if the trap was because * the first use of the FPU by a process. * An optimization in context switch causes a user whose * context is already in the FPU to have the FPU reenabled * automatically without taking fp_disabled trap. * * The fp context of process is not stored in the uarea. * Only a pointer to its state is kept in the uarea. * The variable fp_ctxp points to the floating point * context save area for the current context loaded in * the fpu. * * Forks currently always execute the child before returning * to the parent, therefore fp state of the parent is stored into * its fp context durring the fork. The child starts with the fpu * enabled inheriting the parent's registers. When the parent runs * again it will restore the fp state from the context saved * during the fork. * * NOTE: This code DOES NOT SUPPORT KERNEL USE OF THE FPU */ .seg "text" .align 4 /* * syncfpu() synchronizes the fpu with the * the iu as it causes the processor to wait until all * floating point operations in the FPQ complete... * by the time the kernel gets to the point of executing * syncfpu, most floating point operations should have completed * and generally this happens quickly unless there is pending * exception which has to be handled. * We are passed the pointer to trap's (or syscall's) register * structure, which we save in a global for use in fp_traps() in case we * do take a pending exception. If we didn't, we'd process the * exception using the kernel's registers and not the user's, and could * panic if the user was doing an unimplemented fpop (1025966). */ ENTRY(syncfpu) mov %psr, %o5 ! is floating point enabled sethi %hi(PSR_EF), %o4 btst %o4, %o5 bz 1f sethi %hi(_fptraprp), %o5 ld [%o5 + %lo(_fptraprp)], %o3 st %o0, [%o5 + %lo(_fptraprp)] sethi %hi(fsrholder), %o4 ! ! synchronize with fpu, take an exception, if one is pending ! st %fsr, [%o4 + %lo(fsrholder)] retl st %o3, [%o5 + %lo(_fptraprp)] 1: retl .empty ! the following set PSR_EF is ok in delay slot /* * FPU probe - try a floating point instruction to see if there * really is an FPU in the current configuration, exectued once * from autoconf when booting. */ ENTRY(fpu_probe) set PSR_EF, %g1 ! enable floating-point mov %psr, %g2 ! read psr, save value in %g2 or %g1, %g2, %g3 ! new psr with fpu enabled mov %g3, %psr ! write psr nop;nop ! psr delay... sethi %hi(_zeros), %g3 fpuprobe_ld: ld [%g3 + %lo(_zeros)], %fsr ! probe for fpu, maybe trap ! ! If there is not an FPU, we will get a fp_disabled trap ! when we try to load the fsr, which will clear the fpu_exists flag, ! and skip over the ld ! part of fix for 1041977 (fitoX fix can panic kernel) ! snarf the FPU version, if it exists sethi %hi(_fpu_exists), %g3 ld [%g3 + %lo(_fpu_exists)], %g3 mov 7, %g1 ! assume no FPU tst %g3 bz 1f sethi %hi(_fpu_version), %g3 ! We know the fpu exists; we are still enabled for it sethi %hi(fsrholder), %g1 st %fsr, [%g1 + %lo(fsrholder)] ld [%g1 + %lo(fsrholder)], %g1 ! snarf the FSR set FSR_VERS, %o0 and %g1, %o0, %g1 ! get version srl %g1, FSR_VERS_SHIFT, %g1 ! and shift it down 1: st %g1, [%g3 + %lo(_fpu_version)] retl mov %g2, %psr ! restore old psr, turn off FPU /* * Floating Point Exceptions. * handled according to type: * 0) no_exception * do nothing * 1) IEEE_exception * re-execute the faulty instruction(s) using * software emulation (must do every instruction in FQ) * 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 evironment * for execution of kernel code. */ .global _fp_exception _fp_exception: sethi %hi(_fp_ctxp), %l5 ld [%l5 + %lo(_fp_ctxp)], %l5 mov %l5, %g4 st %fsr, [%l5 + FPCTX_FSR] ! get floating point status ld [%l5 + FPCTX_FSR], %g1 set FSR_FTT, %l4 and %g1, %l4, %g1 ! mask out trap type srl %g1, FSR_FTT_SHIFT, %l4 ! use ftt after we dump queue dumpfq: clr %g2 ! FQ offset set FSR_QNE, %g3 ! the queue is not empty bit add %l5, FPCTX_Q, %l3 1: ld [%l5 + FPCTX_FSR], %g1 ! test fsr btst %g3, %g1 ! is the queue empty? bz 2f ! yes, go figure out what to do ! for Calvin FPU, if two instructions in FQ, and exception and ! [%l3 + %g2] not in cache, gives wrong answer. we preload cache ! BUGID 1038405 ld [%l3 + %g2], %g0 ! Calvin FPU FIX KLUDGE XXX HACK std %fq, [%l3 + %g2] ! store an entry from FQ ! the fpc that works with the ti8847 requires this nop; nop; add %g2, 8, %g2 ! increment offset in FQ b 1b st %fsr, [%l5 + FPCTX_FSR] ! get floating point status 2: ! ! emulating floating point instructions ! wr %l0, PSR_ET, %psr ! enable traps ! three cycle delay required, we are ! not changing the wim so the next ! three instructions are safe #ifdef MULTIPROCESSOR nop ! psr delay call _klock_enter nop #endif MULTIPROCESSOR cmp %l4, FTT_SEQ ! sanity check for bogus exceptions blt,a fpeok mov FPCTX_Q, %l4 ! offset into stored queue entries ! ! Sequence error or unknown ftt exception. ! set badfpexcpmsg, %o0 ! panic call _panic mov %l4, %o1 ! mov ftt to o1 for stack backtrace fpeok: srl %g2, 3, %l6 ! number of entries stored from fpq st %l6, [%l5 + FPCTX_QCNT] ! store number of entries (for debug) ! run the floating point q call _fp_runq add %sp, MINFRAME, %o0 fp_ret: ! ! clear interrupting condition in fsr ! ld [%l5 + FPCTX_FSR], %o0 set (FSR_FTT|FSR_QNE), %o1 ! clear ftt bits and qne andn %o0, %o1, %o0 st %o0, [%l5 + FPCTX_FSR] b sys_rtt ld [%l5 + FPCTX_FSR], %fsr ! ld new fsr to set condition codes /* * fp_enable(fp) * struct fpu *fp; * * Initialization when there is a hardware fpu. * Clear the fsr and initialize registers to NaN (-1) * The caller is supposed to update the return psr * so when the return to usrerland is made, the fpu is enabled. */ ENTRY(fp_enable) mov %psr, %o1 ! enable the fpu set PSR_EF, %o2 or %o1, %o2, %o3 mov %o3, %psr nop;nop;nop ! psr delay LOAD_FPREGS(%o0) ! read in fpu state retl ld [%o0 + FPCTX_FSR], %fsr # /* * fp_dumpregs(fp_ctx_pointer) * * This routine is called when a fork is done to cause the current * register set inside the fpu is put into the new context. */ ENTRY(fp_dumpregs) st %fsr, [%o0 + FPCTX_FSR] ! store fsr in current fpctx STORE_FPREGS(%o0) ! store rest of regsiters retl nop !void _fp_read_pfreg(pf, n) ! FPU_REGS_TYPE *pf; /* Old freg value. */ ! unsigned n; /* Want to read register n. */ ! !{ ! *pf = %f[n]; !} ! !void !_fp_write_pfreg(pf, n) ! FPU_REGS_TYPE *pf; /* New freg value. */ ! unsigned n; /* Want to read register n. */ ! !{ ! %f[n] = *pf; !} .global __fp_read_pfreg __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. .global __fp_write_pfreg __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) .global __fp_write_pfsr __fp_write_pfsr: retl ld [%o0], %fsr badfpexcpmsg: .asciz "unexpected floating point exception %x\n" fpemptyqmsg: .asciz "fp exception with empty queue, fsr 0x%x\n" badfpdisabledmsg: .asciz "fp_disabled: no FPU but not fpu_probe" .align 4 .seg "data" .align 8 fpjunk: .double 0 .double 0 /* in the case of MP, we assume either both processors have a * FPU or they don't. */ .global _fpu_exists _fpu_exists: .word 1 ! assume FPU exists fsrholder: .word 0 ! dummy place to write fsr ! part of fix for 1041977 (fitoX fix can panic kernel) .global _fpu_version _fpu_version: .word -1 ! place to store FPU version .seg "text" /* * Floating point disabled trap, run after trap preamble. * If FPU does not exist, emulate instruction otherwise, * enable floating point. * */ .global _fp_disabled _fp_disabled: ! ! lower priority, allow other interrupts, overflows, ... ! wr %l0, PSR_ET, %psr ! enable traps #ifdef MULTIPROCESSOR nop ! psr delay call _klock_enter nop #endif MULTIPROCESSOR ! ! fp_disable trap when the FPU is enabled; should only happen ! once from autoconf when there is not an FPU in the board. ! set fpuprobe_ld, %l7 cmp %l1, %l7 ! was trap in fpu_probe, above? bne 2f ! if not let fp_is_disabled handle it set PSR_EF, %l5 ! set pSR_EF in delay slot sethi %hi(_fpu_exists), %l6 clr [%l6 + %lo(_fpu_exists)] ! FPU does not exist ! ! Get the system trap handler's version of the psr and fix it up ! so that sys_rtt will restore a psr with fpu disabled. ! ld [%sp + MINFRAME + PSR*4], %l6 bclr %l5, %l6 ! clear enable fp bit in psr st %l6, [%sp + MINFRAME + PSR*4] ! ! skip the probe instruction ! ld [%sp + MINFRAME + nPC*4], %l6 ! skip the probe instruction st %l6, [%sp + MINFRAME + PC*4] ! pc = npc add %l6, 4, %l6 b sys_rtt ! return from trap st %l6, [%sp + MINFRAME + nPC*4] ! npc += 4 2: call _fp_is_disabled add %sp, MINFRAME, %o0 ba sys_rtt nop