float.s revision 7bafd14382b5b097b3e3940091dfccb91247dfd5
/*
* 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 <sys/asm_linkage.h>
#include <sys/trap.h>
#include <sys/machpcb.h>
#include <sys/machtrap.h>
#include <sys/machsig.h>
#include <sys/machthread.h>
#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(&regs)
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(&regs, 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 */