/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2005 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>

#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 */