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

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * General machine architecture & implementation specific
 * assembly language routines.
 */
#if defined(lint)
#include <sys/types.h>
#include <sys/t_lock.h>
#else	/* lint */
#include "assym.h"
#endif	/* lint */

#include <sys/asm_linkage.h>
#include <sys/machsystm.h>
#include <sys/machthread.h>
#include <sys/privregs.h>
#include <sys/cmpregs.h>
#include <sys/clock.h>
#include <sys/fpras.h>

#if defined(lint)

/*ARGSUSED*/
int
getprocessorid(void)
{ return (0); }

#else	/* lint */

/*
 * Get the processor ID.
 * === MID reg as specified in 15dec89 sun4u spec, sec 5.4.3
 */

	ENTRY(getprocessorid)
	CPU_INDEX(%o0, %o1)
	retl
	nop
	SET_SIZE(getprocessorid)

#endif	/* lint */

#if defined(lint)
/*ARGSUSED*/
void
set_error_enable_tl1(uint64_t neer, uint64_t action)
{}

/* ARGSUSED */
void
set_error_enable(uint64_t neer)
{}

uint64_t
get_error_enable()
{
	return ((uint64_t)0);
}
#else /* lint */

	ENTRY(set_error_enable_tl1)
	cmp	%g2, EER_SET_ABSOLUTE
	be	%xcc, 1f
	  nop
	ldxa	[%g0]ASI_ESTATE_ERR, %g3
	membar	#Sync
	cmp	%g2, EER_SET_SETBITS
	be,a	%xcc, 1f
	  or	%g3, %g1, %g1
	andn	%g3, %g1, %g1			/* EER_SET_CLRBITS */
1:
	stxa	%g1, [%g0]ASI_ESTATE_ERR	/* ecache error enable reg */
	membar	#Sync
	retry
	SET_SIZE(set_error_enable_tl1)

	ENTRY(set_error_enable)
	stxa	%o0, [%g0]ASI_ESTATE_ERR	/* ecache error enable reg */
	membar	#Sync
	retl
	nop
	SET_SIZE(set_error_enable)

	ENTRY(get_error_enable)
	retl
	ldxa	[%g0]ASI_ESTATE_ERR, %o0	/* ecache error enable reg */
	SET_SIZE(get_error_enable)

#endif /* lint */

#if defined(lint)
void
get_asyncflt(uint64_t *afsr)
{
	afsr = afsr;
}
#else /* lint */

	ENTRY(get_asyncflt)
	ldxa	[%g0]ASI_AFSR, %o1		! afsr reg
	retl
	stx	%o1, [%o0]
	SET_SIZE(get_asyncflt)

#endif /* lint */

#if defined(lint)
void
set_asyncflt(uint64_t afsr)
{
	afsr = afsr;
}
#else /* lint */

	ENTRY(set_asyncflt)
	stxa	%o0, [%g0]ASI_AFSR		! afsr reg
	membar	#Sync
	retl
	nop
	SET_SIZE(set_asyncflt)

#endif /* lint */

#if defined(lint)
void
get_asyncaddr(uint64_t *afar)
{
	afar = afar;
}
#else /* lint */

	ENTRY(get_asyncaddr)
	ldxa	[%g0]ASI_AFAR, %o1		! afar reg
	retl
	stx	%o1, [%o0]
	SET_SIZE(get_asyncaddr)

#endif /* lint */

#if defined(lint) || defined(__lint)

/* ARGSUSED */
hrtime_t
tick2ns(hrtime_t tick, uint_t cpuid)
{ return 0; }

#else	/* lint */

	ENTRY_NP(tick2ns)
	sethi	%hi(cpunodes), %o4
	or	%o4, %lo(cpunodes), %o4		! %o4 = &cpunodes
	! Register usage:
	!
	! o0 = timestamp
	! o2 = byte offset into cpunodes for tick_nsec_scale of this CPU
	! o4 = &cpunodes
	!
	mulx	%o1, CPU_NODE_SIZE, %o2	! %o2 = byte offset into cpunodes
	add	%o2, TICK_NSEC_SCALE, %o2
	ld	[%o4 + %o2], %o2	! %o2 = cpunodes[cpuid].tick_nsec_scale
	NATIVE_TIME_TO_NSEC_SCALE(%o0, %o2, %o3, TICK_NSEC_SHIFT)
	retl
	nop
	SET_SIZE(tick2ns)

#endif  /* lint */

#if defined(lint)

/* ARGSUSED */
void
set_cmp_error_steering(void)
{}

#else	/* lint */

	ENTRY(set_cmp_error_steering)
	membar	#Sync
	set	ASI_CORE_ID, %o0		! %o0 = ASI_CORE_ID
	ldxa	[%o0]ASI_CMP_PER_CORE, %o0	! get ASI_CORE_ID
	and	%o0, COREID_MASK, %o0
	set	ASI_CMP_ERROR_STEERING, %o1	! %o1 = ERROR_STEERING_REG
	stxa	%o0, [%o1]ASI_CMP_SHARED	! this core now hadles
	membar	#Sync				!  non-core specific errors
	retl
	nop
	SET_SIZE(set_cmp_error_steering)

#endif	/* lint */

#if defined(lint)

/* ARGSUSED */
uint64_t
ultra_getver(void)
{
	return (0); 
}

#else /* lint */

	ENTRY(ultra_getver)
	retl
	rdpr	%ver, %o0
	SET_SIZE(ultra_getver)

#endif /* lint */

#if defined(lint)

int
fpras_chkfn_type1(void)
{ return 0; }

#else	/* lint */

	/*
	 * Check instructions using just the AX pipelines, designed by
	 * C.B. Liaw of PNP.
	 *
	 * This function must match a struct fpras_chkfn and must be
	 * block aligned.  A zero return means all was well.  These
	 * instructions are chosen to be sensitive to bit corruptions
	 * on the fpras rewrite, so if a bit corruption still produces
	 * a valid instruction we should still get an incorrect result
	 * here.  This function is never called directly - it is copied
	 * into per-cpu and per-operation buffers;  it must therefore
	 * be absolutely position independent.  If an illegal instruction
	 * is encountered then the trap handler trampolines to the final
	 * three instructions of this function.
	 *
	 * We want two instructions that are complements of one another,
	 * and which can perform a calculation with a known result.
	 *
	 * SETHI:
	 *
	 * | 0 0 |  rd   | 1 0 0 |	imm22				|
	 *  31 30 29   25 24   22 21				       0
	 *
	 * ADDCCC with two source registers:
	 *
	 * | 1 0 |  rd   | 0 1 1   0 0 0 |  rs1  | 0 |	   -	|  rs2  |
	 *  31 30 29   25 24           19 18   14 13  12       5 4     0
	 *
	 * We can choose rd and imm2 of the SETHI and rd, rs1 and rs2 of
	 * the ADDCCC to obtain instructions that are complements in all but
	 * bit 30.
	 *
	 * Registers are numbered as follows:
	 *
	 * r[31]	%i7
	 * r[30]	%i6
	 * r[29]	%i5
	 * r[28]	%i4
	 * r[27]	%i3
	 * r[26]	%i2
	 * r[25]	%i1
	 * r[24]	%i0
	 * r[23]	%l7
	 * r[22]	%l6
	 * r[21]	%l5
	 * r[20]	%l4
	 * r[19]	%l3
	 * r[18]	%l2
	 * r[17]	%l1
	 * r[16]	%l0
	 * r[15]	%o7
	 * r[14]	%o6
	 * r[13]	%o5
	 * r[12]	%o4
	 * r[11]	%o3
	 * r[10]	%o2
	 * r[9]		%o1
	 * r[8]		%o0	
	 * r[7]		%g7
	 * r[6]		%g6
	 * r[5]		%g5
	 * r[4]		%g4
	 * r[3]		%g3
	 * r[2]		%g2
	 * r[1]		%g1
	 * r[0]		%g0
	 *
	 * For register r[n], register r[31-n] is the complement.  We must
	 * avoid use of %i6/%i7 and %o6/%o7 as well as %g7.  Clearly we need
	 * to use a local or input register as one half of the pair, which
	 * requires us to obtain our own register window or take steps
	 * to preserve any local or input we choose to use.  We choose
	 * %o1 as rd for the SETHI, so rd of the ADDCCC must be %l6.
	 * We'll use %o1 as rs1 and %l6 as rs2 of the ADDCCC, which then
	 * requires that imm22 be 0b111 10110 1 11111111 01001 or 0x3dbfe9,
	 * or %hi(0xf6ffa400).  This determines the value of the constant
	 * CBV2 below.
	 *
	 * The constant CBV1 is chosen such that an initial subcc %g0, CBV1
	 * will set the carry bit and every addccc thereafter will continue
	 * to generate a carry.  Other values are possible for CBV1 - this
	 * is just one that works this way.
	 *
	 * Finally CBV3 is the expected answer when we perform our repeated
	 * calculations on CBV1 and CBV2 - it is not otherwise specially
	 * derived.  If this result is not obtained then a corruption has
	 * occured during the FPRAS_REWRITE of one of the two blocks of
	 * 16 instructions.  A corruption could also result in an illegal
	 * instruction or other unexpected trap - we catch illegal
	 * instruction traps in the PC range and trampoline to the
	 * last instructions of the function to return a failure indication.
	 *
	 */

#define	CBV1		0xc11
#define	CBV2		0xf6ffa400
#define	CBV3		0x66f9d800
#define	CBR1		%o1
#define	CBR2		%l6
#define	CBO2		%o2
#define	SETHI_CBV2_CBR1		sethi %hi(CBV2), CBR1
#define	ADDCCC_CBR1_CBR2_CBR2	addccc CBR1, CBR2, CBR2

	.align	64
	ENTRY_NP(fpras_chkfn_type1)
	mov	CBR2, CBO2		! 1, preserve CBR2 of (callers) window
	mov	FPRAS_OK, %o0		! 2, default return value
	ba,pt	%icc, 1f		! 3
	  subcc %g0, CBV1, CBR2		! 4
					! 5 - 16
	.align	64
1:	SETHI_CBV2_CBR1			! 1
	ADDCCC_CBR1_CBR2_CBR2		! 2
	SETHI_CBV2_CBR1			! 3
	ADDCCC_CBR1_CBR2_CBR2		! 4
	SETHI_CBV2_CBR1			! 5
	ADDCCC_CBR1_CBR2_CBR2		! 6
	SETHI_CBV2_CBR1			! 7
	ADDCCC_CBR1_CBR2_CBR2		! 8
	SETHI_CBV2_CBR1			! 9
	ADDCCC_CBR1_CBR2_CBR2		! 10
	SETHI_CBV2_CBR1			! 11
	ADDCCC_CBR1_CBR2_CBR2		! 12
	SETHI_CBV2_CBR1			! 13
	ADDCCC_CBR1_CBR2_CBR2		! 14
	SETHI_CBV2_CBR1			! 15
	ADDCCC_CBR1_CBR2_CBR2		! 16

	ADDCCC_CBR1_CBR2_CBR2		! 1
	SETHI_CBV2_CBR1			! 2
	ADDCCC_CBR1_CBR2_CBR2		! 3
	SETHI_CBV2_CBR1			! 4
	ADDCCC_CBR1_CBR2_CBR2		! 5
	SETHI_CBV2_CBR1			! 6
	ADDCCC_CBR1_CBR2_CBR2		! 7
	SETHI_CBV2_CBR1			! 8
	ADDCCC_CBR1_CBR2_CBR2		! 9
	SETHI_CBV2_CBR1			! 10
	ADDCCC_CBR1_CBR2_CBR2		! 11
	SETHI_CBV2_CBR1			! 12
	ADDCCC_CBR1_CBR2_CBR2		! 13
	SETHI_CBV2_CBR1			! 14
	ADDCCC_CBR1_CBR2_CBR2		! 15
	SETHI_CBV2_CBR1			! 16

	addc	CBR1, CBR2, CBR2	! 1
	sethi	%hi(CBV3), CBR1		! 2
	cmp	CBR1, CBR2		! 3
	movnz	%icc, FPRAS_BADCALC, %o0! 4, how detected
	retl				! 5
	  mov	CBO2, CBR2		! 6, restore borrowed register
	.skip 4*(13-7+1)		! 7 - 13
					!
					! illegal instr'n trap comes here
					!
	mov	CBO2, CBR2		! 14, restore borrowed register
	retl				! 15
	  mov	FPRAS_BADTRAP, %o0	! 16, how detected
	SET_SIZE(fpras_chkfn_type1)

#endif	/* lint */

/*
 * fp_zero() - clear all fp data registers and the fsr
 */

#if defined(lint) || defined(__lint)

void
fp_zero(void)
{}

#else	/* lint */

	ENTRY_NP(fp_zero)
	std	%g0, [%sp + ARGPUSH + STACK_BIAS]
	fzero	%f0
	fzero	%f2
	ldd	[%sp + ARGPUSH + STACK_BIAS], %fsr
	faddd	%f0, %f2, %f4
	fmuld	%f0, %f2, %f6
	faddd	%f0, %f2, %f8
	fmuld	%f0, %f2, %f10
	faddd	%f0, %f2, %f12
	fmuld	%f0, %f2, %f14
	faddd	%f0, %f2, %f16
	fmuld	%f0, %f2, %f18
	faddd	%f0, %f2, %f20
	fmuld	%f0, %f2, %f22
	faddd	%f0, %f2, %f24
	fmuld	%f0, %f2, %f26
	faddd	%f0, %f2, %f28
	fmuld	%f0, %f2, %f30
	faddd	%f0, %f2, %f32
	fmuld	%f0, %f2, %f34
	faddd	%f0, %f2, %f36
	fmuld	%f0, %f2, %f38
	faddd	%f0, %f2, %f40
	fmuld	%f0, %f2, %f42
	faddd	%f0, %f2, %f44
	fmuld	%f0, %f2, %f46
	faddd	%f0, %f2, %f48
	fmuld	%f0, %f2, %f50
	faddd	%f0, %f2, %f52
	fmuld	%f0, %f2, %f54
	faddd	%f0, %f2, %f56
	fmuld	%f0, %f2, %f58
	faddd	%f0, %f2, %f60
	retl
	fmuld	%f0, %f2, %f62
	SET_SIZE(fp_zero)

#endif	/* lint */