/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "assym.h"
#include <sys/asm_linkage.h>
#include <sys/privregs.h>
#include <sys/sun4asi.h>
#include <sys/machasi.h>
#include <sys/hypervisor_api.h>
#include <sys/machtrap.h>
#include <sys/machthread.h>
#include <sys/machbrand.h>
#include <sys/pcb.h>
#include <sys/pte.h>
#include <sys/mmu.h>
#include <sys/machpcb.h>
#include <sys/async.h>
#include <sys/intreg.h>
#include <sys/scb.h>
#include <sys/psr_compat.h>
#include <sys/syscall.h>
#include <sys/machparam.h>
#include <sys/traptrace.h>
#include <vm/hat_sfmmu.h>
#include <sys/archsystm.h>
#include <sys/utrap.h>
#include <sys/clock.h>
#include <sys/intr.h>
#include <sys/fpu/fpu_simulator.h>
#include <vm/seg_spt.h>

/*
 * WARNING: If you add a fast trap handler which can be invoked by a
 * non-privileged user, you may have to use the FAST_TRAP_DONE macro
 * instead of "done" instruction to return back to the user mode. See
 * comments for the "fast_trap_done" entry point for more information.
 *
 * An alternate FAST_TRAP_DONE_CHK_INTR macro should be used for the
 * cases where you always want to process any pending interrupts before
 * returning back to the user mode.
 */
#define	FAST_TRAP_DONE		\
	ba,a	fast_trap_done

#define	FAST_TRAP_DONE_CHK_INTR	\
	ba,a	fast_trap_done_chk_intr

/*
 * SPARC V9 Trap Table
 *
 * Most of the trap handlers are made from common building
 * blocks, and some are instantiated multiple times within
 * the trap table. So, I build a bunch of macros, then
 * populate the table using only the macros.
 *
 * Many macros branch to sys_trap.  Its calling convention is:
 *	%g1		kernel trap handler
 *	%g2, %g3	args for above
 *	%g4		desire %pil
 */

#ifdef	TRAPTRACE

/*
 * Tracing macro. Adds two instructions if TRAPTRACE is defined.
 */
#define	TT_TRACE(label)		\
	ba	label		;\
	rd	%pc, %g7
#define	TT_TRACE_INS	2

#define	TT_TRACE_L(label)	\
	ba	label		;\
	rd	%pc, %l4	;\
	clr	%l4
#define	TT_TRACE_L_INS	3

#else

#define	TT_TRACE(label)
#define	TT_TRACE_INS	0

#define	TT_TRACE_L(label)
#define	TT_TRACE_L_INS	0

#endif

/*
 * This first set are funneled to trap() with %tt as the type.
 * Trap will then either panic or send the user a signal.
 */
/*
 * NOT is used for traps that just shouldn't happen.
 * It comes in both single and quadruple flavors.
 */
	.global	trap
#define	NOT			\
	TT_TRACE(trace_gen)	;\
	set	trap, %g1	;\
	rdpr	%tt, %g3	;\
	ba,pt	%xcc, sys_trap	;\
	sub	%g0, 1, %g4	;\
	.align	32
#define	NOT4	NOT; NOT; NOT; NOT

#define	NOTP				\
	TT_TRACE(trace_gen)		;\
	ba,pt	%xcc, ptl1_panic	;\
	  mov	PTL1_BAD_TRAP, %g1	;\
	.align	32
#define	NOTP4	NOTP; NOTP; NOTP; NOTP


/*
 * BAD is used for trap vectors we don't have a kernel
 * handler for.
 * It also comes in single and quadruple versions.
 */
#define	BAD	NOT
#define	BAD4	NOT4

#define	DONE			\
	done;			\
	.align	32

/*
 * TRAP vectors to the trap() function.
 * It's main use is for user errors.
 */
	.global	trap
#define	TRAP(arg)		\
	TT_TRACE(trace_gen)	;\
	set	trap, %g1	;\
	mov	arg, %g3	;\
	ba,pt	%xcc, sys_trap	;\
	sub	%g0, 1, %g4	;\
	.align	32

/*
 * SYSCALL is used for unsupported syscall interfaces (with 'which'
 * set to 'nosys') and legacy support of old SunOS 4.x syscalls (with
 * 'which' set to 'syscall_trap32').
 *
 * The SYSCALL_TRAP* macros are used for syscall entry points.
 * SYSCALL_TRAP is used to support LP64 syscalls and SYSCALL_TRAP32
 * is used to support ILP32.  Each macro can only be used once
 * since they each define a symbol.  The symbols are used as hot patch
 * points by the brand infrastructure to dynamically enable and disable
 * brand syscall interposition.  See the comments around BRAND_CALLBACK
 * and brand_plat_interposition_enable() for more information.
 */
#define	SYSCALL_NOTT(which)		\
	set	(which), %g1		;\
	ba,pt	%xcc, user_trap		;\
	sub	%g0, 1, %g4		;\
	.align	32

#define	SYSCALL(which)			\
	TT_TRACE(trace_gen)		;\
	SYSCALL_NOTT(which)

#define	SYSCALL_TRAP32				\
	TT_TRACE(trace_gen)			;\
	ALTENTRY(syscall_trap32_patch_point)	\
	SYSCALL_NOTT(syscall_trap32)

#define	SYSCALL_TRAP				\
	TT_TRACE(trace_gen)			;\
	ALTENTRY(syscall_trap_patch_point)	\
	SYSCALL_NOTT(syscall_trap)

/*
 * GOTO just jumps to a label.
 * It's used for things that can be fixed without going thru sys_trap.
 */
#define	GOTO(label)		\
	.global	label		;\
	ba,a	label		;\
	.empty			;\
	.align	32

/*
 * GOTO_TT just jumps to a label.
 * correctable ECC error traps at  level 0 and 1 will use this macro.
 * It's used for things that can be fixed without going thru sys_trap.
 */
#define	GOTO_TT(label, ttlabel)		\
	.global	label		;\
	TT_TRACE(ttlabel)	;\
	ba,a	label		;\
	.empty			;\
	.align	32

/*
 * Privileged traps
 * Takes breakpoint if privileged, calls trap() if not.
 */
#define	PRIV(label)			\
	rdpr	%tstate, %g1		;\
	btst	TSTATE_PRIV, %g1	;\
	bnz	label			;\
	rdpr	%tt, %g3		;\
	set	trap, %g1		;\
	ba,pt	%xcc, sys_trap		;\
	sub	%g0, 1, %g4		;\
	.align	32


/*
 * DTrace traps.
 */
#define	DTRACE_PID			\
	.global dtrace_pid_probe				;\
	set	dtrace_pid_probe, %g1				;\
	ba,pt	%xcc, user_trap					;\
	sub	%g0, 1, %g4					;\
	.align	32

#define	DTRACE_RETURN			\
	.global dtrace_return_probe				;\
	set	dtrace_return_probe, %g1			;\
	ba,pt	%xcc, user_trap					;\
	sub	%g0, 1, %g4					;\
	.align	32

/*
 * REGISTER WINDOW MANAGEMENT MACROS
 */

/*
 * various convenient units of padding
 */
#define	SKIP(n)	.skip 4*(n)

/*
 * CLEAN_WINDOW is the simple handler for cleaning a register window.
 */
#define	CLEAN_WINDOW						\
	TT_TRACE_L(trace_win)					;\
	rdpr %cleanwin, %l0; inc %l0; wrpr %l0, %cleanwin	;\
	clr %l0; clr %l1; clr %l2; clr %l3			;\
	clr %l4; clr %l5; clr %l6; clr %l7			;\
	clr %o0; clr %o1; clr %o2; clr %o3			;\
	clr %o4; clr %o5; clr %o6; clr %o7			;\
	retry; .align 128

/*
 * If we get an unresolved tlb miss while in a window handler, the fault
 * handler will resume execution at the last instruction of the window
 * hander, instead of delivering the fault to the kernel.  Spill handlers
 * use this to spill windows into the wbuf.
 *
 * The mixed handler works by checking %sp, and branching to the correct
 * handler.  This is done by branching back to label 1: for 32b frames,
 * or label 2: for 64b frames; which implies the handler order is: 32b,
 * 64b, mixed.  The 1: and 2: labels are offset into the routines to
 * allow the branchs' delay slots to contain useful instructions.
 */

/*
 * SPILL_32bit spills a 32-bit-wide kernel register window.  It
 * assumes that the kernel context and the nucleus context are the
 * same.  The stack pointer is required to be eight-byte aligned even
 * though this code only needs it to be four-byte aligned.
 */
#define	SPILL_32bit(tail)					\
	srl	%sp, 0, %sp					;\
1:	st	%l0, [%sp + 0]					;\
	st	%l1, [%sp + 4]					;\
	st	%l2, [%sp + 8]					;\
	st	%l3, [%sp + 12]					;\
	st	%l4, [%sp + 16]					;\
	st	%l5, [%sp + 20]					;\
	st	%l6, [%sp + 24]					;\
	st	%l7, [%sp + 28]					;\
	st	%i0, [%sp + 32]					;\
	st	%i1, [%sp + 36]					;\
	st	%i2, [%sp + 40]					;\
	st	%i3, [%sp + 44]					;\
	st	%i4, [%sp + 48]					;\
	st	%i5, [%sp + 52]					;\
	st	%i6, [%sp + 56]					;\
	st	%i7, [%sp + 60]					;\
	TT_TRACE_L(trace_win)					;\
	saved							;\
	retry							;\
	SKIP(31-19-TT_TRACE_L_INS)				;\
	ba,a,pt	%xcc, fault_32bit_##tail			;\
	.empty

/*
 * SPILL_32bit_asi spills a 32-bit-wide register window into a 32-bit
 * wide address space via the designated asi.  It is used to spill
 * non-kernel windows.  The stack pointer is required to be eight-byte
 * aligned even though this code only needs it to be four-byte
 * aligned.
 */
#define	SPILL_32bit_asi(asi_num, tail)				\
	srl	%sp, 0, %sp					;\
1:	sta	%l0, [%sp + %g0]asi_num				;\
	mov	4, %g1						;\
	sta	%l1, [%sp + %g1]asi_num				;\
	mov	8, %g2						;\
	sta	%l2, [%sp + %g2]asi_num				;\
	mov	12, %g3						;\
	sta	%l3, [%sp + %g3]asi_num				;\
	add	%sp, 16, %g4					;\
	sta	%l4, [%g4 + %g0]asi_num				;\
	sta	%l5, [%g4 + %g1]asi_num				;\
	sta	%l6, [%g4 + %g2]asi_num				;\
	sta	%l7, [%g4 + %g3]asi_num				;\
	add	%g4, 16, %g4					;\
	sta	%i0, [%g4 + %g0]asi_num				;\
	sta	%i1, [%g4 + %g1]asi_num				;\
	sta	%i2, [%g4 + %g2]asi_num				;\
	sta	%i3, [%g4 + %g3]asi_num				;\
	add	%g4, 16, %g4					;\
	sta	%i4, [%g4 + %g0]asi_num				;\
	sta	%i5, [%g4 + %g1]asi_num				;\
	sta	%i6, [%g4 + %g2]asi_num				;\
	sta	%i7, [%g4 + %g3]asi_num				;\
	TT_TRACE_L(trace_win)					;\
	saved							;\
	retry							;\
	SKIP(31-25-TT_TRACE_L_INS)				;\
	ba,a,pt %xcc, fault_32bit_##tail			;\
	.empty

#define	SPILL_32bit_tt1(asi_num, tail)				\
	ba,a,pt	%xcc, fault_32bit_##tail			;\
	.empty							;\
	.align 128


/*
 * FILL_32bit fills a 32-bit-wide kernel register window.  It assumes
 * that the kernel context and the nucleus context are the same.  The
 * stack pointer is required to be eight-byte aligned even though this
 * code only needs it to be four-byte aligned.
 */
#define	FILL_32bit(tail)					\
	srl	%sp, 0, %sp					;\
1:	TT_TRACE_L(trace_win)					;\
	ld	[%sp + 0], %l0					;\
	ld	[%sp + 4], %l1					;\
	ld	[%sp + 8], %l2					;\
	ld	[%sp + 12], %l3					;\
	ld	[%sp + 16], %l4					;\
	ld	[%sp + 20], %l5					;\
	ld	[%sp + 24], %l6					;\
	ld	[%sp + 28], %l7					;\
	ld	[%sp + 32], %i0					;\
	ld	[%sp + 36], %i1					;\
	ld	[%sp + 40], %i2					;\
	ld	[%sp + 44], %i3					;\
	ld	[%sp + 48], %i4					;\
	ld	[%sp + 52], %i5					;\
	ld	[%sp + 56], %i6					;\
	ld	[%sp + 60], %i7					;\
	restored						;\
	retry							;\
	SKIP(31-19-TT_TRACE_L_INS)				;\
	ba,a,pt	%xcc, fault_32bit_##tail			;\
	.empty

/*
 * FILL_32bit_asi fills a 32-bit-wide register window from a 32-bit
 * wide address space via the designated asi.  It is used to fill
 * non-kernel windows.  The stack pointer is required to be eight-byte
 * aligned even though this code only needs it to be four-byte
 * aligned.
 */
#define	FILL_32bit_asi(asi_num, tail)				\
	srl	%sp, 0, %sp					;\
1:	TT_TRACE_L(trace_win)					;\
	mov	4, %g1						;\
	lda	[%sp + %g0]asi_num, %l0				;\
	mov	8, %g2						;\
	lda	[%sp + %g1]asi_num, %l1				;\
	mov	12, %g3						;\
	lda	[%sp + %g2]asi_num, %l2				;\
	lda	[%sp + %g3]asi_num, %l3				;\
	add	%sp, 16, %g4					;\
	lda	[%g4 + %g0]asi_num, %l4				;\
	lda	[%g4 + %g1]asi_num, %l5				;\
	lda	[%g4 + %g2]asi_num, %l6				;\
	lda	[%g4 + %g3]asi_num, %l7				;\
	add	%g4, 16, %g4					;\
	lda	[%g4 + %g0]asi_num, %i0				;\
	lda	[%g4 + %g1]asi_num, %i1				;\
	lda	[%g4 + %g2]asi_num, %i2				;\
	lda	[%g4 + %g3]asi_num, %i3				;\
	add	%g4, 16, %g4					;\
	lda	[%g4 + %g0]asi_num, %i4				;\
	lda	[%g4 + %g1]asi_num, %i5				;\
	lda	[%g4 + %g2]asi_num, %i6				;\
	lda	[%g4 + %g3]asi_num, %i7				;\
	restored						;\
	retry							;\
	SKIP(31-25-TT_TRACE_L_INS)				;\
	ba,a,pt %xcc, fault_32bit_##tail			;\
	.empty


/*
 * SPILL_64bit spills a 64-bit-wide kernel register window.  It
 * assumes that the kernel context and the nucleus context are the
 * same.  The stack pointer is required to be eight-byte aligned.
 */
#define	SPILL_64bit(tail)					\
2:	stx	%l0, [%sp + V9BIAS64 + 0]			;\
	stx	%l1, [%sp + V9BIAS64 + 8]			;\
	stx	%l2, [%sp + V9BIAS64 + 16]			;\
	stx	%l3, [%sp + V9BIAS64 + 24]			;\
	stx	%l4, [%sp + V9BIAS64 + 32]			;\
	stx	%l5, [%sp + V9BIAS64 + 40]			;\
	stx	%l6, [%sp + V9BIAS64 + 48]			;\
	stx	%l7, [%sp + V9BIAS64 + 56]			;\
	stx	%i0, [%sp + V9BIAS64 + 64]			;\
	stx	%i1, [%sp + V9BIAS64 + 72]			;\
	stx	%i2, [%sp + V9BIAS64 + 80]			;\
	stx	%i3, [%sp + V9BIAS64 + 88]			;\
	stx	%i4, [%sp + V9BIAS64 + 96]			;\
	stx	%i5, [%sp + V9BIAS64 + 104]			;\
	stx	%i6, [%sp + V9BIAS64 + 112]			;\
	stx	%i7, [%sp + V9BIAS64 + 120]			;\
	TT_TRACE_L(trace_win)					;\
	saved							;\
	retry							;\
	SKIP(31-18-TT_TRACE_L_INS)				;\
	ba,a,pt	%xcc, fault_64bit_##tail			;\
	.empty

#define	SPILL_64bit_ktt1(tail)				\
	ba,a,pt	%xcc, fault_64bit_##tail			;\
	.empty							;\
	.align 128

#define	SPILL_mixed_ktt1(tail)				\
	btst	1, %sp						;\
	bz,a,pt	%xcc, fault_32bit_##tail			;\
	srl	%sp, 0, %sp					;\
	ba,a,pt	%xcc, fault_64bit_##tail			;\
	.empty							;\
	.align 128

/*
 * SPILL_64bit_asi spills a 64-bit-wide register window into a 64-bit
 * wide address space via the designated asi.  It is used to spill
 * non-kernel windows.  The stack pointer is required to be eight-byte
 * aligned.
 */
#define	SPILL_64bit_asi(asi_num, tail)				\
	mov	0 + V9BIAS64, %g1				;\
2:	stxa	%l0, [%sp + %g1]asi_num				;\
	mov	8 + V9BIAS64, %g2				;\
	stxa	%l1, [%sp + %g2]asi_num				;\
	mov	16 + V9BIAS64, %g3				;\
	stxa	%l2, [%sp + %g3]asi_num				;\
	mov	24 + V9BIAS64, %g4				;\
	stxa	%l3, [%sp + %g4]asi_num				;\
	add	%sp, 32, %g5					;\
	stxa	%l4, [%g5 + %g1]asi_num				;\
	stxa	%l5, [%g5 + %g2]asi_num				;\
	stxa	%l6, [%g5 + %g3]asi_num				;\
	stxa	%l7, [%g5 + %g4]asi_num				;\
	add	%g5, 32, %g5					;\
	stxa	%i0, [%g5 + %g1]asi_num				;\
	stxa	%i1, [%g5 + %g2]asi_num				;\
	stxa	%i2, [%g5 + %g3]asi_num				;\
	stxa	%i3, [%g5 + %g4]asi_num				;\
	add	%g5, 32, %g5					;\
	stxa	%i4, [%g5 + %g1]asi_num				;\
	stxa	%i5, [%g5 + %g2]asi_num				;\
	stxa	%i6, [%g5 + %g3]asi_num				;\
	stxa	%i7, [%g5 + %g4]asi_num				;\
	TT_TRACE_L(trace_win)					;\
	saved							;\
	retry							;\
	SKIP(31-25-TT_TRACE_L_INS)				;\
	ba,a,pt %xcc, fault_64bit_##tail			;\
	.empty

#define	SPILL_64bit_tt1(asi_num, tail)				\
	ba,a,pt	%xcc, fault_64bit_##tail			;\
	.empty							;\
	.align 128

/*
 * FILL_64bit fills a 64-bit-wide kernel register window.  It assumes
 * that the kernel context and the nucleus context are the same.  The
 * stack pointer is required to be eight-byte aligned.
 */
#define	FILL_64bit(tail)					\
2:	TT_TRACE_L(trace_win)					;\
	ldx	[%sp + V9BIAS64 + 0], %l0			;\
	ldx	[%sp + V9BIAS64 + 8], %l1			;\
	ldx	[%sp + V9BIAS64 + 16], %l2			;\
	ldx	[%sp + V9BIAS64 + 24], %l3			;\
	ldx	[%sp + V9BIAS64 + 32], %l4			;\
	ldx	[%sp + V9BIAS64 + 40], %l5			;\
	ldx	[%sp + V9BIAS64 + 48], %l6			;\
	ldx	[%sp + V9BIAS64 + 56], %l7			;\
	ldx	[%sp + V9BIAS64 + 64], %i0			;\
	ldx	[%sp + V9BIAS64 + 72], %i1			;\
	ldx	[%sp + V9BIAS64 + 80], %i2			;\
	ldx	[%sp + V9BIAS64 + 88], %i3			;\
	ldx	[%sp + V9BIAS64 + 96], %i4			;\
	ldx	[%sp + V9BIAS64 + 104], %i5			;\
	ldx	[%sp + V9BIAS64 + 112], %i6			;\
	ldx	[%sp + V9BIAS64 + 120], %i7			;\
	restored						;\
	retry							;\
	SKIP(31-18-TT_TRACE_L_INS)				;\
	ba,a,pt	%xcc, fault_64bit_##tail			;\
	.empty

/*
 * FILL_64bit_asi fills a 64-bit-wide register window from a 64-bit
 * wide address space via the designated asi.  It is used to fill
 * non-kernel windows.  The stack pointer is required to be eight-byte
 * aligned.
 */
#define	FILL_64bit_asi(asi_num, tail)				\
	mov	V9BIAS64 + 0, %g1				;\
2:	TT_TRACE_L(trace_win)					;\
	ldxa	[%sp + %g1]asi_num, %l0				;\
	mov	V9BIAS64 + 8, %g2				;\
	ldxa	[%sp + %g2]asi_num, %l1				;\
	mov	V9BIAS64 + 16, %g3				;\
	ldxa	[%sp + %g3]asi_num, %l2				;\
	mov	V9BIAS64 + 24, %g4				;\
	ldxa	[%sp + %g4]asi_num, %l3				;\
	add	%sp, 32, %g5					;\
	ldxa	[%g5 + %g1]asi_num, %l4				;\
	ldxa	[%g5 + %g2]asi_num, %l5				;\
	ldxa	[%g5 + %g3]asi_num, %l6				;\
	ldxa	[%g5 + %g4]asi_num, %l7				;\
	add	%g5, 32, %g5					;\
	ldxa	[%g5 + %g1]asi_num, %i0				;\
	ldxa	[%g5 + %g2]asi_num, %i1				;\
	ldxa	[%g5 + %g3]asi_num, %i2				;\
	ldxa	[%g5 + %g4]asi_num, %i3				;\
	add	%g5, 32, %g5					;\
	ldxa	[%g5 + %g1]asi_num, %i4				;\
	ldxa	[%g5 + %g2]asi_num, %i5				;\
	ldxa	[%g5 + %g3]asi_num, %i6				;\
	ldxa	[%g5 + %g4]asi_num, %i7				;\
	restored						;\
	retry							;\
	SKIP(31-25-TT_TRACE_L_INS)				;\
	ba,a,pt	%xcc, fault_64bit_##tail			;\
	.empty


/*
 * SPILL_mixed spills either size window, depending on
 * whether %sp is even or odd, to a 32-bit address space.
 * This may only be used in conjunction with SPILL_32bit/
 * FILL_64bit.
 * Clear upper 32 bits of %sp if it is odd.
 * We won't need to clear them in 64 bit kernel.
 */
#define	SPILL_mixed						\
	btst	1, %sp						;\
	bz,a,pt	%xcc, 1b					;\
	srl	%sp, 0, %sp					;\
	ba,pt	%xcc, 2b					;\
	nop							;\
	.align	128

/*
 * FILL_mixed(ASI) fills either size window, depending on
 * whether %sp is even or odd, from a 32-bit address space.
 * This may only be used in conjunction with FILL_32bit/
 * FILL_64bit. New versions of FILL_mixed_{tt1,asi} would be
 * needed for use with FILL_{32,64}bit_{tt1,asi}. Particular
 * attention should be paid to the instructions that belong
 * in the delay slots of the branches depending on the type
 * of fill handler being branched to.
 * Clear upper 32 bits of %sp if it is odd.
 * We won't need to clear them in 64 bit kernel.
 */
#define	FILL_mixed						\
	btst	1, %sp						;\
	bz,a,pt	%xcc, 1b					;\
	srl	%sp, 0, %sp					;\
	ba,pt	%xcc, 2b					;\
	nop							;\
	.align	128


/*
 * SPILL_32clean/SPILL_64clean spill 32-bit and 64-bit register windows,
 * respectively, into the address space via the designated asi.  The
 * unbiased stack pointer is required to be eight-byte aligned (even for
 * the 32-bit case even though this code does not require such strict
 * alignment).
 *
 * With SPARC v9 the spill trap takes precedence over the cleanwin trap
 * so when cansave == 0, canrestore == 6, and cleanwin == 6 the next save
 * will cause cwp + 2 to be spilled but will not clean cwp + 1.  That
 * window may contain kernel data so in user_rtt we set wstate to call
 * these spill handlers on the first user spill trap.  These handler then
 * spill the appropriate window but also back up a window and clean the
 * window that didn't get a cleanwin trap.
 */
#define	SPILL_32clean(asi_num, tail)				\
	srl	%sp, 0, %sp					;\
	sta	%l0, [%sp + %g0]asi_num				;\
	mov	4, %g1						;\
	sta	%l1, [%sp + %g1]asi_num				;\
	mov	8, %g2						;\
	sta	%l2, [%sp + %g2]asi_num				;\
	mov	12, %g3						;\
	sta	%l3, [%sp + %g3]asi_num				;\
	add	%sp, 16, %g4					;\
	sta	%l4, [%g4 + %g0]asi_num				;\
	sta	%l5, [%g4 + %g1]asi_num				;\
	sta	%l6, [%g4 + %g2]asi_num				;\
	sta	%l7, [%g4 + %g3]asi_num				;\
	add	%g4, 16, %g4					;\
	sta	%i0, [%g4 + %g0]asi_num				;\
	sta	%i1, [%g4 + %g1]asi_num				;\
	sta	%i2, [%g4 + %g2]asi_num				;\
	sta	%i3, [%g4 + %g3]asi_num				;\
	add	%g4, 16, %g4					;\
	sta	%i4, [%g4 + %g0]asi_num				;\
	sta	%i5, [%g4 + %g1]asi_num				;\
	sta	%i6, [%g4 + %g2]asi_num				;\
	sta	%i7, [%g4 + %g3]asi_num				;\
	TT_TRACE_L(trace_win)					;\
	b	.spill_clean					;\
	  mov	WSTATE_USER32, %g7				;\
	SKIP(31-25-TT_TRACE_L_INS)				;\
	ba,a,pt	%xcc, fault_32bit_##tail			;\
	.empty

#define	SPILL_64clean(asi_num, tail)				\
	mov	0 + V9BIAS64, %g1				;\
	stxa	%l0, [%sp + %g1]asi_num				;\
	mov	8 + V9BIAS64, %g2				;\
	stxa	%l1, [%sp + %g2]asi_num				;\
	mov	16 + V9BIAS64, %g3				;\
	stxa	%l2, [%sp + %g3]asi_num				;\
	mov	24 + V9BIAS64, %g4				;\
	stxa	%l3, [%sp + %g4]asi_num				;\
	add	%sp, 32, %g5					;\
	stxa	%l4, [%g5 + %g1]asi_num				;\
	stxa	%l5, [%g5 + %g2]asi_num				;\
	stxa	%l6, [%g5 + %g3]asi_num				;\
	stxa	%l7, [%g5 + %g4]asi_num				;\
	add	%g5, 32, %g5					;\
	stxa	%i0, [%g5 + %g1]asi_num				;\
	stxa	%i1, [%g5 + %g2]asi_num				;\
	stxa	%i2, [%g5 + %g3]asi_num				;\
	stxa	%i3, [%g5 + %g4]asi_num				;\
	add	%g5, 32, %g5					;\
	stxa	%i4, [%g5 + %g1]asi_num				;\
	stxa	%i5, [%g5 + %g2]asi_num				;\
	stxa	%i6, [%g5 + %g3]asi_num				;\
	stxa	%i7, [%g5 + %g4]asi_num				;\
	TT_TRACE_L(trace_win)					;\
	b	.spill_clean					;\
	  mov	WSTATE_USER64, %g7				;\
	SKIP(31-25-TT_TRACE_L_INS)				;\
	ba,a,pt	%xcc, fault_64bit_##tail			;\
	.empty


/*
 * Floating point disabled.
 */
#define	FP_DISABLED_TRAP		\
	TT_TRACE(trace_gen)		;\
	ba,pt	%xcc,.fp_disabled	;\
	nop				;\
	.align	32

/*
 * Floating point exceptions.
 */
#define	FP_IEEE_TRAP			\
	TT_TRACE(trace_gen)		;\
	ba,pt	%xcc,.fp_ieee_exception	;\
	nop				;\
	.align	32

#define	FP_TRAP				\
	TT_TRACE(trace_gen)		;\
	ba,pt	%xcc,.fp_exception	;\
	nop				;\
	.align	32

/*
 * ECACHE_ECC error traps at level 0 and level 1
 */
#define	ECACHE_ECC(table_name)		\
	.global	table_name		;\
table_name:				;\
	membar	#Sync			;\
	set	trap, %g1		;\
	rdpr	%tt, %g3		;\
	ba,pt	%xcc, sys_trap		;\
	sub	%g0, 1, %g4		;\
	.align	32

/*
 * illegal instruction trap
 */
#define	ILLTRAP_INSTR			  \
	membar	#Sync			  ;\
	TT_TRACE(trace_gen)		  ;\
	or	%g0, P_UTRAP4, %g2	  ;\
	or	%g0, T_UNIMP_INSTR, %g3   ;\
	sethi	%hi(.check_v9utrap), %g4  ;\
	jmp	%g4 + %lo(.check_v9utrap) ;\
	nop				  ;\
	.align	32

/*
 * tag overflow trap
 */
#define	TAG_OVERFLOW			  \
	TT_TRACE(trace_gen)		  ;\
	or	%g0, P_UTRAP10, %g2	  ;\
	or	%g0, T_TAG_OVERFLOW, %g3  ;\
	sethi	%hi(.check_v9utrap), %g4  ;\
	jmp	%g4 + %lo(.check_v9utrap) ;\
	nop				  ;\
	.align	32

/*
 * divide by zero trap
 */
#define	DIV_BY_ZERO			  \
	TT_TRACE(trace_gen)		  ;\
	or	%g0, P_UTRAP11, %g2	  ;\
	or	%g0, T_IDIV0, %g3	  ;\
	sethi	%hi(.check_v9utrap), %g4  ;\
	jmp	%g4 + %lo(.check_v9utrap) ;\
	nop				  ;\
	.align	32

/*
 * trap instruction for V9 user trap handlers
 */
#define	TRAP_INSTR			  \
	TT_TRACE(trace_gen)		  ;\
	or	%g0, T_SOFTWARE_TRAP, %g3 ;\
	sethi	%hi(.check_v9utrap), %g4  ;\
	jmp	%g4 + %lo(.check_v9utrap) ;\
	nop				  ;\
	.align	32
#define	TRP4	TRAP_INSTR; TRAP_INSTR; TRAP_INSTR; TRAP_INSTR

/*
 * LEVEL_INTERRUPT is for level N interrupts.
 * VECTOR_INTERRUPT is for the vector trap.
 */
#define	LEVEL_INTERRUPT(level)		\
	.global	tt_pil##level		;\
tt_pil##level:			;\
	ba,pt	%xcc, pil_interrupt	;\
	mov	level, %g4		;\
	.align	32

#define	LEVEL14_INTERRUPT			\
	ba	pil14_interrupt			;\
	mov	PIL_14, %g4			;\
	.align	32

#define        LEVEL15_INTERRUPT                       \
       ba      pil15_interrupt                 ;\
       mov     PIL_15, %g4                     ;\
       .align  32

#define CPU_MONDO			\
	ba,a,pt	%xcc, cpu_mondo		;\
	.align	32

#define DEV_MONDO			\
	ba,a,pt	%xcc, dev_mondo		;\
	.align	32

/*
 * We take over the rtba after we set our trap table and
 * fault status area. The watchdog reset trap is now handled by the OS.
 */
#define WATCHDOG_RESET			\
	mov	PTL1_BAD_WATCHDOG, %g1	;\
	ba,a,pt	%xcc, .watchdog_trap	;\
	.align	32

/*
 * RED is for traps that use the red mode handler.
 * We should never see these either.
 */
#define RED			\
	mov	PTL1_BAD_RED, %g1	;\
	ba,a,pt	%xcc, .watchdog_trap	;\
	.align	32


/*
 * MMU Trap Handlers.
 */

/*
 * synthesize for trap(): SFSR in %g3
 */
#define	IMMU_EXCEPTION							\
	MMU_FAULT_STATUS_AREA(%g3)					;\
	rdpr	%tpc, %g2						;\
	ldx	[%g3 + MMFSA_I_TYPE], %g1				;\
	ldx	[%g3 + MMFSA_I_CTX], %g3				;\
	sllx	%g3, SFSR_CTX_SHIFT, %g3				;\
	or	%g3, %g1, %g3						;\
	ba,pt	%xcc, .mmu_exception_end				;\
	mov	T_INSTR_EXCEPTION, %g1					;\
	.align	32

/*
 * synthesize for trap(): TAG_ACCESS in %g2, SFSR in %g3
 */
#define	DMMU_EXCEPTION							\
	ba,a,pt	%xcc, .dmmu_exception					;\
	.align	32

/*
 * synthesize for trap(): SFAR in %g2, SFSR in %g3
 */
#define	DMMU_EXC_AG_PRIV						\
	MMU_FAULT_STATUS_AREA(%g3)					;\
	ldx	[%g3 + MMFSA_D_ADDR], %g2				;\
	/* Fault type not available in MMU fault status area */		;\
	mov	MMFSA_F_PRVACT, %g1					;\
	ldx	[%g3 + MMFSA_D_CTX], %g3				;\
	sllx	%g3, SFSR_CTX_SHIFT, %g3				;\
	ba,pt	%xcc, .mmu_priv_exception				;\
	or	%g3, %g1, %g3						;\
	.align	32

/*
 * synthesize for trap(): SFAR in %g2, SFSR in %g3
 */
#define	DMMU_EXC_AG_NOT_ALIGNED						\
	MMU_FAULT_STATUS_AREA(%g3)					;\
	ldx	[%g3 + MMFSA_D_ADDR], %g2				;\
	/* Fault type not available in MMU fault status area */		;\
	mov	MMFSA_F_UNALIGN, %g1					;\
	ldx	[%g3 + MMFSA_D_CTX], %g3				;\
	sllx	%g3, SFSR_CTX_SHIFT, %g3				;\
	ba,pt	%xcc, .mmu_exception_not_aligned			;\
	or	%g3, %g1, %g3			/* SFSR */		;\
	.align	32
/*
 * SPARC V9 IMPL. DEP. #109(1) and (2) and #110(1) and (2)
 */

/*
 * synthesize for trap(): SFAR in %g2, SFSR in %g3
 */
#define	DMMU_EXC_LDDF_NOT_ALIGNED					\
	ba,a,pt	%xcc, .dmmu_exc_lddf_not_aligned			;\
	.align	32
/*
 * synthesize for trap(): SFAR in %g2, SFSR in %g3
 */
#define	DMMU_EXC_STDF_NOT_ALIGNED					\
	ba,a,pt	%xcc, .dmmu_exc_stdf_not_aligned			;\
	.align	32

#if defined(cscope)
/*
 * Define labels to direct cscope quickly to labels that
 * are generated by macro expansion of DTLB_MISS().
 */
	.global	tt0_dtlbmiss
tt0_dtlbmiss:
	.global	tt1_dtlbmiss
tt1_dtlbmiss:
	nop
#endif

/*
 * Data miss handler (must be exactly 32 instructions)
 *
 * This handler is invoked only if the hypervisor has been instructed
 * not to do any TSB walk.
 *
 * Kernel and invalid context cases are handled by the sfmmu_kdtlb_miss
 * handler.
 *
 * User TLB miss handling depends upon whether a user process has one or
 * two TSBs. User TSB information (physical base and size code) is kept
 * in two dedicated scratchpad registers. Absence of a user TSB (primarily
 * second TSB) is indicated by a negative value (-1) in that register.
 */

/*
 * synthesize for miss handler: pseudo-tag access in %g2 (with context "type"
 * (0=kernel, 1=invalid, or 2=user) rather than context ID)
 */
#define	DTLB_MISS(table_name)						;\
	.global	table_name##_dtlbmiss					;\
table_name##_dtlbmiss:						;\
	GET_MMU_D_PTAGACC_CTXTYPE(%g2, %g3)	/* 8 instr */		;\
	cmp	%g3, INVALID_CONTEXT					;\
	ble,pn	%xcc, sfmmu_kdtlb_miss					;\
	  srlx	%g2, TAG_VALO_SHIFT, %g7	/* g7 = tsb tag */	;\
	mov	SCRATCHPAD_UTSBREG2, %g1				;\
	ldxa	[%g1]ASI_SCRATCHPAD, %g1	/* get 2nd tsbreg */	;\
	brgez,pn %g1, sfmmu_udtlb_slowpath	/* branch if 2 TSBs */	;\
	  nop								;\
	GET_1ST_TSBE_PTR(%g2, %g1, %g4, %g5)	/* 11 instr */		;\
	ba,pt	%xcc, sfmmu_udtlb_fastpath	/* no 4M TSB, miss */	;\
	  srlx	%g2, TAG_VALO_SHIFT, %g7	/* g7 = tsb tag */	;\
	.align 128


#if defined(cscope)
/*
 * Define labels to direct cscope quickly to labels that
 * are generated by macro expansion of ITLB_MISS().
 */
	.global	tt0_itlbmiss
tt0_itlbmiss:
	.global	tt1_itlbmiss
tt1_itlbmiss:
	nop
#endif

/*
 * Instruction miss handler.
 *
 * This handler is invoked only if the hypervisor has been instructed
 * not to do any TSB walk.
 *
 * ldda instructions will have their ASI patched
 * by sfmmu_patch_ktsb at runtime.
 * MUST be EXACTLY 32 instructions or we'll break.
 */

/*
 * synthesize for miss handler: TAG_ACCESS in %g2 (with context "type"
 * (0=kernel, 1=invalid, or 2=user) rather than context ID)
 */
#define	ITLB_MISS(table_name)						 \
	.global	table_name##_itlbmiss					;\
table_name##_itlbmiss:						;\
	GET_MMU_I_PTAGACC_CTXTYPE(%g2, %g3)	/* 8 instr */		;\
	cmp	%g3, INVALID_CONTEXT					;\
	ble,pn	%xcc, sfmmu_kitlb_miss					;\
	  srlx	%g2, TAG_VALO_SHIFT, %g7	/* g7 = tsb tag */	;\
	mov	SCRATCHPAD_UTSBREG2, %g1				;\
	ldxa	[%g1]ASI_SCRATCHPAD, %g1	/* get 2nd tsbreg */	;\
	brgez,pn %g1, sfmmu_uitlb_slowpath	/* branch if 2 TSBs */	;\
	  nop								;\
	GET_1ST_TSBE_PTR(%g2, %g1, %g4, %g5)	/* 11 instr */		;\
	ba,pt	%xcc, sfmmu_uitlb_fastpath	/* no 4M TSB, miss */	;\
	  srlx	%g2, TAG_VALO_SHIFT, %g7	/* g7 = tsb tag */	;\
	.align 128

#define	DTSB_MISS \
	GOTO_TT(sfmmu_slow_dmmu_miss,trace_dmmu)

#define	ITSB_MISS \
	GOTO_TT(sfmmu_slow_immu_miss,trace_immu)

/*
 * This macro is the first level handler for fast protection faults.
 * It first demaps the tlb entry which generated the fault and then
 * attempts to set the modify bit on the hash.  It needs to be
 * exactly 32 instructions.
 */
/*
 * synthesize for miss handler: TAG_ACCESS in %g2 (with context "type"
 * (0=kernel, 1=invalid, or 2=user) rather than context ID)
 */
#define	DTLB_PROT							 \
	GET_MMU_D_PTAGACC_CTXTYPE(%g2, %g3)	/* 8 instr */		;\
	/*								;\
	 *   g2 = pseudo-tag access register (ctx type rather than ctx ID) ;\
	 *   g3 = ctx type (0, 1, or 2)					;\
	 */								;\
	TT_TRACE(trace_dataprot)	/* 2 instr ifdef TRAPTRACE */	;\
					/* clobbers g1 and g6 XXXQ? */	;\
	brnz,pt %g3, sfmmu_uprot_trap		/* user trap */		;\
	  nop								;\
	ba,a,pt	%xcc, sfmmu_kprot_trap		/* kernel trap */	;\
	.align 128

#define	DMMU_EXCEPTION_TL1						;\
	ba,a,pt	%xcc, mmu_trap_tl1					;\
	.align 32

#define	MISALIGN_ADDR_TL1						;\
	ba,a,pt	%xcc, mmu_trap_tl1					;\
	.align 32

/*
 * Trace a tsb hit
 * g1 = tsbe pointer (in/clobbered)
 * g2 = tag access register (in)
 * g3 - g4 = scratch (clobbered)
 * g5 = tsbe data (in)
 * g6 = scratch (clobbered)
 * g7 = pc we jumped here from (in)
 * ttextra = value to OR in to trap type (%tt) (in)
 */
#ifdef TRAPTRACE
#define TRACE_TSBHIT(ttextra)						 \
	membar	#Sync							;\
	sethi	%hi(FLUSH_ADDR), %g6					;\
	flush	%g6							;\
	TRACE_PTR(%g3, %g6)						;\
	GET_TRACE_TICK(%g6, %g4)					;\
	stxa	%g6, [%g3 + TRAP_ENT_TICK]%asi				;\
	stna	%g2, [%g3 + TRAP_ENT_SP]%asi	/* tag access */	;\
	stna	%g5, [%g3 + TRAP_ENT_F1]%asi	/* tsb data */		;\
	rdpr	%tnpc, %g6						;\
	stna	%g6, [%g3 + TRAP_ENT_F2]%asi				;\
	stna	%g1, [%g3 + TRAP_ENT_F3]%asi	/* tsb pointer */	;\
	stna	%g0, [%g3 + TRAP_ENT_F4]%asi				;\
	rdpr	%tpc, %g6						;\
	stna	%g6, [%g3 + TRAP_ENT_TPC]%asi				;\
	TRACE_SAVE_TL_GL_REGS(%g3, %g6)					;\
	rdpr	%tt, %g6						;\
	or	%g6, (ttextra), %g1					;\
	stha	%g1, [%g3 + TRAP_ENT_TT]%asi				;\
	MMU_FAULT_STATUS_AREA(%g4)					;\
	mov	MMFSA_D_ADDR, %g1					;\
	cmp	%g6, FAST_IMMU_MISS_TT					;\
	move	%xcc, MMFSA_I_ADDR, %g1					;\
	cmp	%g6, T_INSTR_MMU_MISS					;\
	move	%xcc, MMFSA_I_ADDR, %g1					;\
	ldx	[%g4 + %g1], %g1					;\
	stxa	%g1, [%g3 + TRAP_ENT_TSTATE]%asi /* fault addr */	;\
	mov	MMFSA_D_CTX, %g1					;\
	cmp	%g6, FAST_IMMU_MISS_TT					;\
	move	%xcc, MMFSA_I_CTX, %g1					;\
	cmp	%g6, T_INSTR_MMU_MISS					;\
	move	%xcc, MMFSA_I_CTX, %g1					;\
	ldx	[%g4 + %g1], %g1					;\
	stna	%g1, [%g3 + TRAP_ENT_TR]%asi				;\
	TRACE_NEXT(%g3, %g4, %g6)
#else
#define TRACE_TSBHIT(ttextra)
#endif


/*
 * =======================================================================
 *		SPARC V9 TRAP TABLE
 *
 * The trap table is divided into two halves: the first half is used when
 * taking traps when TL=0; the second half is used when taking traps from
 * TL>0. Note that handlers in the second half of the table might not be able
 * to make the same assumptions as handlers in the first half of the table.
 *
 * Worst case trap nesting so far:
 *
 *	at TL=0 client issues software trap requesting service
 *	at TL=1 nucleus wants a register window
 *	at TL=2 register window clean/spill/fill takes a TLB miss
 *	at TL=3 processing TLB miss
 *	at TL=4 handle asynchronous error
 *
 * Note that a trap from TL=4 to TL=5 places Spitfire in "RED mode".
 *
 * =======================================================================
 */
	.section ".text"
	.align	4
	.global trap_table, scb, trap_table0, trap_table1, etrap_table
	.type	trap_table, #object
	.type	trap_table0, #object
	.type	trap_table1, #object
	.type	scb, #object
trap_table:
scb:
trap_table0:
	/* hardware traps */
	NOT;				/* 000	reserved */
	RED;				/* 001	power on reset */
	WATCHDOG_RESET;			/* 002	watchdog reset */
	RED;				/* 003	externally initiated reset */
	RED;				/* 004	software initiated reset */
	RED;				/* 005	red mode exception */
	NOT; NOT;			/* 006 - 007 reserved */
	IMMU_EXCEPTION;			/* 008	instruction access exception */
	ITSB_MISS;			/* 009	instruction access MMU miss */
 	NOT;				/* 00A  reserved */
	NOT; NOT4;			/* 00B - 00F reserved */
	ILLTRAP_INSTR;			/* 010	illegal instruction */
	TRAP(T_PRIV_INSTR);		/* 011	privileged opcode */
	TRAP(T_UNIMP_LDD);		/* 012	unimplemented LDD */
	TRAP(T_UNIMP_STD);		/* 013	unimplemented STD */
	NOT4; NOT4; NOT4;		/* 014 - 01F reserved */
	FP_DISABLED_TRAP;		/* 020	fp disabled */
	FP_IEEE_TRAP;			/* 021	fp exception ieee 754 */
	FP_TRAP;			/* 022	fp exception other */
	TAG_OVERFLOW;			/* 023	tag overflow */
	CLEAN_WINDOW;			/* 024 - 027 clean window */
	DIV_BY_ZERO;			/* 028	division by zero */
	NOT;				/* 029	internal processor error */
	NOT; NOT; NOT4;			/* 02A - 02F reserved */
	DMMU_EXCEPTION;			/* 030	data access exception */
	DTSB_MISS;			/* 031	data access MMU miss */
	NOT;				/* 032  reserved */
	NOT;				/* 033	data access protection */
	DMMU_EXC_AG_NOT_ALIGNED;	/* 034	mem address not aligned */
	DMMU_EXC_LDDF_NOT_ALIGNED;	/* 035	LDDF mem address not aligned */
	DMMU_EXC_STDF_NOT_ALIGNED;	/* 036	STDF mem address not aligned */
	DMMU_EXC_AG_PRIV;		/* 037	privileged action */
	NOT;				/* 038	LDQF mem address not aligned */
	NOT;				/* 039	STQF mem address not aligned */
	NOT; NOT; NOT4;			/* 03A - 03F reserved */
	NOT;				/* 040	async data error */
	LEVEL_INTERRUPT(1);		/* 041	interrupt level 1 */
	LEVEL_INTERRUPT(2);		/* 042	interrupt level 2 */
	LEVEL_INTERRUPT(3);		/* 043	interrupt level 3 */
	LEVEL_INTERRUPT(4);		/* 044	interrupt level 4 */
	LEVEL_INTERRUPT(5);		/* 045	interrupt level 5 */
	LEVEL_INTERRUPT(6);		/* 046	interrupt level 6 */
	LEVEL_INTERRUPT(7);		/* 047	interrupt level 7 */
	LEVEL_INTERRUPT(8);		/* 048	interrupt level 8 */
	LEVEL_INTERRUPT(9);		/* 049	interrupt level 9 */
	LEVEL_INTERRUPT(10);		/* 04A	interrupt level 10 */
	LEVEL_INTERRUPT(11);		/* 04B	interrupt level 11 */
	LEVEL_INTERRUPT(12);		/* 04C	interrupt level 12 */
	LEVEL_INTERRUPT(13);		/* 04D	interrupt level 13 */
	LEVEL14_INTERRUPT;		/* 04E	interrupt level 14 */
	LEVEL15_INTERRUPT;		/* 04F	interrupt level 15 */
	NOT4; NOT4; NOT4; NOT4;		/* 050 - 05F reserved */
	NOT;				/* 060	interrupt vector */
	GOTO(kmdb_trap);		/* 061	PA watchpoint */
	GOTO(kmdb_trap);		/* 062	VA watchpoint */
	NOT;				/* 063	reserved */
	ITLB_MISS(tt0);			/* 064	instruction access MMU miss */
	DTLB_MISS(tt0);			/* 068	data access MMU miss */
	DTLB_PROT;			/* 06C	data access protection */
	NOT;				/* 070  reserved */
	NOT;				/* 071  reserved */
	NOT;				/* 072  reserved */
	NOT;				/* 073  reserved */
	NOT4; NOT4			/* 074 - 07B reserved */
	CPU_MONDO;			/* 07C	cpu_mondo */
	DEV_MONDO;			/* 07D	dev_mondo */
	GOTO_TT(resumable_error, trace_gen);	/* 07E  resumable error */
	GOTO_TT(nonresumable_error, trace_gen);	/* 07F  non-reasumable error */
	NOT4;				/* 080	spill 0 normal */
	SPILL_32bit_asi(ASI_AIUP,sn0);	/* 084	spill 1 normal */
	SPILL_64bit_asi(ASI_AIUP,sn0);	/* 088	spill 2 normal */
	SPILL_32clean(ASI_AIUP,sn0);	/* 08C	spill 3 normal */
	SPILL_64clean(ASI_AIUP,sn0);	/* 090	spill 4 normal */
	SPILL_32bit(not);		/* 094	spill 5 normal */
	SPILL_64bit(not);		/* 098	spill 6 normal */
	SPILL_mixed;			/* 09C	spill 7 normal */
	NOT4;				/* 0A0	spill 0 other */
	SPILL_32bit_asi(ASI_AIUS,so0);	/* 0A4	spill 1 other */
	SPILL_64bit_asi(ASI_AIUS,so0);	/* 0A8	spill 2 other */
	SPILL_32bit_asi(ASI_AIUS,so0);	/* 0AC	spill 3 other */
	SPILL_64bit_asi(ASI_AIUS,so0);	/* 0B0	spill 4 other */
	NOT4;				/* 0B4	spill 5 other */
	NOT4;				/* 0B8	spill 6 other */
	NOT4;				/* 0BC	spill 7 other */
	NOT4;				/* 0C0	fill 0 normal */
	FILL_32bit_asi(ASI_AIUP,fn0);	/* 0C4	fill 1 normal */
	FILL_64bit_asi(ASI_AIUP,fn0);	/* 0C8	fill 2 normal */
	FILL_32bit_asi(ASI_AIUP,fn0);	/* 0CC	fill 3 normal */
	FILL_64bit_asi(ASI_AIUP,fn0);	/* 0D0	fill 4 normal */
	FILL_32bit(not);		/* 0D4	fill 5 normal */
	FILL_64bit(not);		/* 0D8	fill 6 normal */
	FILL_mixed;			/* 0DC	fill 7 normal */
	NOT4;				/* 0E0	fill 0 other */
	NOT4;				/* 0E4	fill 1 other */
	NOT4;				/* 0E8	fill 2 other */
	NOT4;				/* 0EC	fill 3 other */
	NOT4;				/* 0F0	fill 4 other */
	NOT4;				/* 0F4	fill 5 other */
	NOT4;				/* 0F8	fill 6 other */
	NOT4;				/* 0FC	fill 7 other */
	/* user traps */
	GOTO(syscall_trap_4x);		/* 100	old system call */
	TRAP(T_BREAKPOINT);		/* 101	user breakpoint */
	TRAP(T_DIV0);			/* 102	user divide by zero */
	GOTO(.flushw);			/* 103	flush windows */
	GOTO(.clean_windows);		/* 104	clean windows */
	BAD;				/* 105	range check ?? */
	GOTO(.fix_alignment);		/* 106	do unaligned references */
	BAD;				/* 107	unused */
	SYSCALL_TRAP32;			/* 108	ILP32 system call on LP64 */
	GOTO(set_trap0_addr);		/* 109	set trap0 address */
	BAD; BAD; BAD4;			/* 10A - 10F unused */
	TRP4; TRP4; TRP4; TRP4;		/* 110 - 11F V9 user trap handlers */
	GOTO(.getcc);			/* 120	get condition codes */
	GOTO(.setcc);			/* 121	set condition codes */
	GOTO(.getpsr);			/* 122	get psr */
	GOTO(.setpsr);			/* 123	set psr (some fields) */
	GOTO(get_timestamp);		/* 124	get timestamp */
	GOTO(get_virtime);		/* 125	get lwp virtual time */
	PRIV(self_xcall);		/* 126	self xcall */
	GOTO(get_hrestime);		/* 127	get hrestime */
	BAD;				/* 128	ST_SETV9STACK */
	GOTO(.getlgrp);			/* 129  get lgrpid */
	BAD; BAD; BAD4;			/* 12A - 12F unused */
	BAD4; BAD4; 			/* 130 - 137 unused */
	DTRACE_PID;			/* 138  dtrace pid tracing provider */
	BAD;				/* 139  unused */
	DTRACE_RETURN;			/* 13A	dtrace pid return probe */
	BAD; BAD4;			/* 13B - 13F unused */
	SYSCALL_TRAP;			/* 140  LP64 system call */
	SYSCALL(nosys);			/* 141  unused system call trap */
#ifdef DEBUG_USER_TRAPTRACECTL
	GOTO(.traptrace_freeze);	/* 142  freeze traptrace */
	GOTO(.traptrace_unfreeze);	/* 143  unfreeze traptrace */
#else
	SYSCALL(nosys);			/* 142  unused system call trap */
	SYSCALL(nosys);			/* 143  unused system call trap */
#endif
	BAD4; BAD4; BAD4;		/* 144 - 14F unused */
	BAD4; BAD4; BAD4; BAD4;		/* 150 - 15F unused */
	BAD4; BAD4; BAD4; BAD4;		/* 160 - 16F unused */
	BAD;				/* 170 - unused */
	BAD;				/* 171 - unused */
	BAD; BAD;			/* 172 - 173 unused */
	BAD4; BAD4;			/* 174 - 17B unused */
#ifdef	PTL1_PANIC_DEBUG
	mov PTL1_BAD_DEBUG, %g1; GOTO(ptl1_panic);
					/* 17C	test ptl1_panic */
#else
	BAD;				/* 17C  unused */
#endif	/* PTL1_PANIC_DEBUG */
	PRIV(kmdb_trap);		/* 17D	kmdb enter (L1-A) */
	PRIV(kmdb_trap);		/* 17E	kmdb breakpoint */
	PRIV(obp_bpt);			/* 17F	obp breakpoint */
	/* reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 180 - 18F reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 190 - 19F reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1A0 - 1AF reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1B0 - 1BF reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1C0 - 1CF reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1D0 - 1DF reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1E0 - 1EF reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1F0 - 1FF reserved */
	.size	trap_table0, (.-trap_table0)
trap_table1:
	NOT4; NOT4;			/* 000 - 007 unused */
	NOT;				/* 008	instruction access exception */
	ITSB_MISS;			/* 009	instruction access MMU miss */
 	NOT;				/* 00A  reserved */
	NOT; NOT4;			/* 00B - 00F unused */
	NOT4; NOT4; NOT4; NOT4;		/* 010 - 01F unused */
	NOT4;				/* 020 - 023 unused */
	CLEAN_WINDOW;			/* 024 - 027 clean window */
	NOT4; NOT4;			/* 028 - 02F unused */
	DMMU_EXCEPTION_TL1;		/* 030 	data access exception */
	DTSB_MISS;			/* 031  data access MMU miss */
	NOT;				/* 032  reserved */
	NOT;				/* 033	unused */
	MISALIGN_ADDR_TL1;		/* 034	mem address not aligned */
	NOT; NOT; NOT; NOT4; NOT4	/* 035 - 03F unused */
	NOT4; NOT4; NOT4; NOT4;		/* 040 - 04F unused */
	NOT4; NOT4; NOT4; NOT4;		/* 050 - 05F unused */
	NOT;				/* 060	unused */
	GOTO(kmdb_trap_tl1);		/* 061	PA watchpoint */
	GOTO(kmdb_trap_tl1);		/* 062	VA watchpoint */
	NOT;				/* 063	reserved */
	ITLB_MISS(tt1);			/* 064	instruction access MMU miss */
	DTLB_MISS(tt1);			/* 068	data access MMU miss */
	DTLB_PROT;			/* 06C	data access protection */
	NOT;				/* 070  reserved */
	NOT;				/* 071  reserved */
	NOT;				/* 072  reserved */
	NOT;				/* 073  reserved */
	NOT4; NOT4;			/* 074 - 07B reserved */
	NOT;				/* 07C  reserved */
	NOT;				/* 07D  reserved */
	NOT;				/* 07E  resumable error */
	GOTO_TT(nonresumable_error, trace_gen);	/* 07F  nonresumable error */
	NOTP4;				/* 080	spill 0 normal */
	SPILL_32bit_tt1(ASI_AIUP,sn1);	/* 084	spill 1 normal */
	SPILL_64bit_tt1(ASI_AIUP,sn1);	/* 088	spill 2 normal */
	SPILL_32bit_tt1(ASI_AIUP,sn1);	/* 08C	spill 3 normal */
	SPILL_64bit_tt1(ASI_AIUP,sn1);	/* 090	spill 4 normal */
	NOTP4;				/* 094	spill 5 normal */
	SPILL_64bit_ktt1(sk);		/* 098	spill 6 normal */
	SPILL_mixed_ktt1(sk);		/* 09C	spill 7 normal */
	NOTP4;				/* 0A0	spill 0 other */
	SPILL_32bit_tt1(ASI_AIUS,so1);	/* 0A4  spill 1 other */
	SPILL_64bit_tt1(ASI_AIUS,so1);	/* 0A8	spill 2 other */
	SPILL_32bit_tt1(ASI_AIUS,so1);	/* 0AC	spill 3 other */
	SPILL_64bit_tt1(ASI_AIUS,so1);	/* 0B0  spill 4 other */
	NOTP4;				/* 0B4  spill 5 other */
	NOTP4;				/* 0B8  spill 6 other */
	NOTP4;				/* 0BC  spill 7 other */
	NOT4;				/* 0C0	fill 0 normal */
	NOT4;				/* 0C4	fill 1 normal */
	NOT4;				/* 0C8	fill 2 normal */
	NOT4;				/* 0CC	fill 3 normal */
	NOT4;				/* 0D0	fill 4 normal */
	NOT4;				/* 0D4	fill 5 normal */
	NOT4;				/* 0D8	fill 6 normal */
	NOT4;				/* 0DC	fill 7 normal */
	NOT4; NOT4; NOT4; NOT4;		/* 0E0 - 0EF unused */
	NOT4; NOT4; NOT4; NOT4;		/* 0F0 - 0FF unused */
/*
 * Code running at TL>0 does not use soft traps, so
 * we can truncate the table here.
 * However:
 * sun4v uses (hypervisor) ta instructions at TL > 0, so
 * provide a safety net for now.
 */
	/* soft traps */
	BAD4; BAD4; BAD4; BAD4;		/* 100 - 10F unused */
	BAD4; BAD4; BAD4; BAD4;		/* 110 - 11F unused */
	BAD4; BAD4; BAD4; BAD4;		/* 120 - 12F unused */
	BAD4; BAD4; BAD4; BAD4;		/* 130 - 13F unused */
	BAD4; BAD4; BAD4; BAD4;		/* 140 - 14F unused */
	BAD4; BAD4; BAD4; BAD4;		/* 150 - 15F unused */
	BAD4; BAD4; BAD4; BAD4;		/* 160 - 16F unused */
	BAD4; BAD4; BAD4; BAD4;		/* 170 - 17F unused */
	/* reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 180 - 18F reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 190 - 19F reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1A0 - 1AF reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1B0 - 1BF reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1C0 - 1CF reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1D0 - 1DF reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1E0 - 1EF reserved */
	NOT4; NOT4; NOT4; NOT4;		/* 1F0 - 1FF reserved */
etrap_table:
	.size	trap_table1, (.-trap_table1)
	.size	trap_table, (.-trap_table)
	.size	scb, (.-scb)

/*
 * We get to exec_fault in the case of an instruction miss and tte
 * has no execute bit set.  We go to tl0 to handle it.
 *
 * g1 = tsbe pointer (in/clobbered)
 * g2 = tag access register (in)
 * g3 - g4 = scratch (clobbered)
 * g5 = tsbe data (in)
 * g6 = scratch (clobbered)
 * g7 = pc we jumped here from (in)
 */
/*
 * synthesize for miss handler: TAG_ACCESS in %g2 (with context "type"
 * (0=kernel, 1=invalid, or 2=user) rather than context ID)
 */
	ALTENTRY(exec_fault)
	TRACE_TSBHIT(TT_MMU_EXEC)
	MMU_FAULT_STATUS_AREA(%g4)
	ldx	[%g4 + MMFSA_I_ADDR], %g2	/* g2 = address */
	ldx	[%g4 + MMFSA_I_CTX], %g3	/* g3 = ctx */
	srlx	%g2, MMU_PAGESHIFT, %g2		! align address to page boundry
	cmp	%g3, USER_CONTEXT_TYPE
	sllx	%g2, MMU_PAGESHIFT, %g2
	movgu	%icc, USER_CONTEXT_TYPE, %g3
	or	%g2, %g3, %g2			/* TAG_ACCESS */
	mov	T_INSTR_MMU_MISS, %g3		! arg2 = traptype
	set	trap, %g1
	ba,pt	%xcc, sys_trap
	  mov	-1, %g4

.mmu_exception_not_aligned:
	/* %g2 = sfar, %g3 = sfsr */
	rdpr	%tstate, %g1
	btst	TSTATE_PRIV, %g1
	bnz,pn	%icc, 2f
	nop
	CPU_ADDR(%g1, %g4)				! load CPU struct addr
	ldn	[%g1 + CPU_THREAD], %g1			! load thread pointer
	ldn	[%g1 + T_PROCP], %g1			! load proc pointer
	ldn	[%g1 + P_UTRAPS], %g5			! are there utraps?
	brz,pt	%g5, 2f
	nop
	ldn	[%g5 + P_UTRAP15], %g5			! unaligned utrap?
	brz,pn	%g5, 2f
	nop
	btst	1, %sp
	bz,pt	%xcc, 1f				! 32 bit user program
	nop
	ba,pt	%xcc, .setup_v9utrap			! 64 bit user program
	nop
1:
	ba,pt	%xcc, .setup_utrap
	or	%g2, %g0, %g7
2:
	ba,pt	%xcc, .mmu_exception_end
	mov	T_ALIGNMENT, %g1

.mmu_priv_exception:
	rdpr	%tstate, %g1
	btst	TSTATE_PRIV, %g1
	bnz,pn	%icc, 1f
	nop
	CPU_ADDR(%g1, %g4)				! load CPU struct addr
	ldn	[%g1 + CPU_THREAD], %g1			! load thread pointer
	ldn	[%g1 + T_PROCP], %g1			! load proc pointer
	ldn	[%g1 + P_UTRAPS], %g5			! are there utraps?
	brz,pt	%g5, 1f
	nop
	ldn	[%g5 + P_UTRAP16], %g5
	brnz,pt	%g5, .setup_v9utrap
	nop
1:
	mov	T_PRIV_INSTR, %g1

.mmu_exception_end:
	CPU_INDEX(%g4, %g5)
	set	cpu_core, %g5
	sllx	%g4, CPU_CORE_SHIFT, %g4
	add	%g4, %g5, %g4
	lduh	[%g4 + CPUC_DTRACE_FLAGS], %g5
	andcc	%g5, CPU_DTRACE_NOFAULT, %g0
	bz	1f
	or	%g5, CPU_DTRACE_BADADDR, %g5
	stuh	%g5, [%g4 + CPUC_DTRACE_FLAGS]
	done

1:
	sllx	%g3, 32, %g3
	or	%g3, %g1, %g3
	set	trap, %g1
	ba,pt	%xcc, sys_trap
	sub	%g0, 1, %g4

.fp_disabled:
	CPU_ADDR(%g1, %g4)				! load CPU struct addr
	ldn	[%g1 + CPU_THREAD], %g1			! load thread pointer
	rdpr	%tstate, %g4
	btst	TSTATE_PRIV, %g4
	bnz,a,pn %icc, ptl1_panic
	  mov	PTL1_BAD_FPTRAP, %g1

	ldn	[%g1 + T_PROCP], %g1			! load proc pointer
	ldn	[%g1 + P_UTRAPS], %g5			! are there utraps?
	brz,a,pt %g5, 2f
	  nop
	ldn	[%g5 + P_UTRAP7], %g5			! fp_disabled utrap?
	brz,a,pn %g5, 2f
	  nop
	btst	1, %sp
	bz,a,pt	%xcc, 1f				! 32 bit user program
	  nop
	ba,a,pt	%xcc, .setup_v9utrap			! 64 bit user program
	  nop
1:
	ba,pt	%xcc, .setup_utrap
	  or	%g0, %g0, %g7
2:
	set	fp_disabled, %g1
	ba,pt	%xcc, sys_trap
	  sub	%g0, 1, %g4

.fp_ieee_exception:
	rdpr	%tstate, %g1
	btst	TSTATE_PRIV, %g1
	bnz,a,pn %icc, ptl1_panic
	  mov	PTL1_BAD_FPTRAP, %g1
	CPU_ADDR(%g1, %g4)				! load CPU struct addr
	stx	%fsr, [%g1 + CPU_TMP1]
	ldx	[%g1 + CPU_TMP1], %g2
	ldn	[%g1 + CPU_THREAD], %g1			! load thread pointer
	ldn	[%g1 + T_PROCP], %g1			! load proc pointer
	ldn	[%g1 + P_UTRAPS], %g5			! are there utraps?
	brz,a,pt %g5, 1f
	  nop
	ldn	[%g5 + P_UTRAP8], %g5
	brnz,a,pt %g5, .setup_v9utrap
	  nop
1:
	set	_fp_ieee_exception, %g1
	ba,pt	%xcc, sys_trap
	  sub	%g0, 1, %g4

/*
 * Register Inputs:
 *	%g5		user trap handler
 *	%g7		misaligned addr - for alignment traps only
 */
.setup_utrap:
	set	trap, %g1			! setup in case we go
	mov	T_FLUSH_PCB, %g3		! through sys_trap on
	sub	%g0, 1, %g4			! the save instruction below

	/*
	 * If the DTrace pid provider is single stepping a copied-out
	 * instruction, t->t_dtrace_step will be set. In that case we need
	 * to abort the single-stepping (since execution of the instruction
	 * was interrupted) and use the value of t->t_dtrace_npc as the %npc.
	 */
	save	%sp, -SA(MINFRAME32), %sp	! window for trap handler
	CPU_ADDR(%g1, %g4)			! load CPU struct addr
	ldn	[%g1 + CPU_THREAD], %g1		! load thread pointer
	ldub	[%g1 + T_DTRACE_STEP], %g2	! load t->t_dtrace_step
	rdpr	%tnpc, %l2			! arg1 == tnpc
	brz,pt	%g2, 1f
	rdpr	%tpc, %l1			! arg0 == tpc

	ldub	[%g1 + T_DTRACE_AST], %g2	! load t->t_dtrace_ast
	ldn	[%g1 + T_DTRACE_NPC], %l2	! arg1 = t->t_dtrace_npc (step)
	brz,pt	%g2, 1f
	st	%g0, [%g1 + T_DTRACE_FT]	! zero all pid provider flags
	stub	%g2, [%g1 + T_ASTFLAG]		! aston(t) if t->t_dtrace_ast
1:
	mov	%g7, %l3			! arg2 == misaligned address

	rdpr	%tstate, %g1			! cwp for trap handler
	rdpr	%cwp, %g4
	bclr	TSTATE_CWP_MASK, %g1
	wrpr	%g1, %g4, %tstate
	wrpr	%g0, %g5, %tnpc			! trap handler address
	FAST_TRAP_DONE
	/* NOTREACHED */

.check_v9utrap:
	rdpr	%tstate, %g1
	btst	TSTATE_PRIV, %g1
	bnz,a,pn %icc, 3f
	  nop
	CPU_ADDR(%g4, %g1)				! load CPU struct addr
	ldn	[%g4 + CPU_THREAD], %g5			! load thread pointer
	ldn	[%g5 + T_PROCP], %g5			! load proc pointer
	ldn	[%g5 + P_UTRAPS], %g5			! are there utraps?

	cmp	%g3, T_SOFTWARE_TRAP
	bne,a,pt %icc, 1f
	  nop

	brz,pt %g5, 3f			! if p_utraps == NULL goto trap()
	  rdpr	%tt, %g3		! delay - get actual hw trap type

	sub	%g3, 254, %g1		! UT_TRAP_INSTRUCTION_16 = p_utraps[18]
	ba,pt	%icc, 2f
	  smul	%g1, CPTRSIZE, %g2
1:
	brz,a,pt %g5, 3f		! if p_utraps == NULL goto trap()
	  nop

	cmp	%g3, T_UNIMP_INSTR
	bne,a,pt %icc, 2f
	  nop

	mov	1, %g1
	st	%g1, [%g4 + CPU_TL1_HDLR] ! set CPU_TL1_HDLR
	rdpr	%tpc, %g1		! ld trapping instruction using
	lduwa	[%g1]ASI_AIUP, %g1	! "AS IF USER" ASI which could fault
	st	%g0, [%g4 + CPU_TL1_HDLR] ! clr CPU_TL1_HDLR

	sethi	%hi(0xc1c00000), %g4	! setup mask for illtrap instruction
	andcc	%g1, %g4, %g4		! and instruction with mask
	bnz,a,pt %icc, 3f		! if %g4 == zero, %g1 is an ILLTRAP
	  nop				! fall thru to setup
2:
	ldn	[%g5 + %g2], %g5
	brnz,a,pt %g5, .setup_v9utrap
	  nop
3:
	set	trap, %g1
	ba,pt	%xcc, sys_trap
	  sub	%g0, 1, %g4
	/* NOTREACHED */

/*
 * Register Inputs:
 *	%g5		user trap handler
 */
.setup_v9utrap:
	set	trap, %g1			! setup in case we go
	mov	T_FLUSH_PCB, %g3		! through sys_trap on
	sub	%g0, 1, %g4			! the save instruction below

	/*
	 * If the DTrace pid provider is single stepping a copied-out
	 * instruction, t->t_dtrace_step will be set. In that case we need
	 * to abort the single-stepping (since execution of the instruction
	 * was interrupted) and use the value of t->t_dtrace_npc as the %npc.
	 */
	save	%sp, -SA(MINFRAME64), %sp	! window for trap handler
	CPU_ADDR(%g1, %g4)			! load CPU struct addr
	ldn	[%g1 + CPU_THREAD], %g1		! load thread pointer
	ldub	[%g1 + T_DTRACE_STEP], %g2	! load t->t_dtrace_step
	rdpr	%tnpc, %l7			! arg1 == tnpc
	brz,pt	%g2, 1f
	rdpr	%tpc, %l6			! arg0 == tpc

	ldub	[%g1 + T_DTRACE_AST], %g2	! load t->t_dtrace_ast
	ldn	[%g1 + T_DTRACE_NPC], %l7	! arg1 == t->t_dtrace_npc (step)
	brz,pt	%g2, 1f
	st	%g0, [%g1 + T_DTRACE_FT]	! zero all pid provider flags
	stub	%g2, [%g1 + T_ASTFLAG]		! aston(t) if t->t_dtrace_ast
1:
	rdpr	%tstate, %g2			! cwp for trap handler
	rdpr	%cwp, %g4
	bclr	TSTATE_CWP_MASK, %g2
	wrpr	%g2, %g4, %tstate

	ldn	[%g1 + T_PROCP], %g4		! load proc pointer
	ldn	[%g4 + P_AS], %g4		! load as pointer
	ldn	[%g4 + A_USERLIMIT], %g4	! load as userlimit
	cmp	%l7, %g4			! check for single-step set
	bne,pt	%xcc, 4f
	  nop
	ldn	[%g1 + T_LWP], %g1		! load klwp pointer
	ld	[%g1 + PCB_STEP], %g4		! load single-step flag
	cmp	%g4, STEP_ACTIVE		! step flags set in pcb?
	bne,pt	%icc, 4f
	  nop
	stn	%g5, [%g1 + PCB_TRACEPC]	! save trap handler addr in pcb
	mov	%l7, %g4			! on entry to precise user trap
	add	%l6, 4, %l7			! handler, %l6 == pc, %l7 == npc
						! at time of trap
	wrpr	%g0, %g4, %tnpc			! generate FLTBOUNDS,
						! %g4 == userlimit
	FAST_TRAP_DONE
	/* NOTREACHED */
4:
	wrpr	%g0, %g5, %tnpc			! trap handler address
	FAST_TRAP_DONE_CHK_INTR
	/* NOTREACHED */

.fp_exception:
	CPU_ADDR(%g1, %g4)
	stx	%fsr, [%g1 + CPU_TMP1]
	ldx	[%g1 + CPU_TMP1], %g2

	/*
	 * Cheetah takes unfinished_FPop trap for certain range of operands
	 * to the "fitos" instruction. Instead of going through the slow
	 * software emulation path, we try to simulate the "fitos" instruction
	 * via "fitod" and "fdtos" provided the following conditions are met:
	 *
	 *	fpu_exists is set (if DEBUG)
	 *	not in privileged mode
	 *	ftt is unfinished_FPop
	 *	NXM IEEE trap is not enabled
	 *	instruction at %tpc is "fitos"
	 *
	 *  Usage:
	 *	%g1	per cpu address
	 *	%g2	%fsr
	 *	%g6	user instruction
	 *
	 * Note that we can take a memory access related trap while trying
	 * to fetch the user instruction. Therefore, we set CPU_TL1_HDLR
	 * flag to catch those traps and let the SFMMU code deal with page
	 * fault and data access exception.
	 */
#if defined(DEBUG) || defined(NEED_FPU_EXISTS)
	sethi	%hi(fpu_exists), %g7
	ld	[%g7 + %lo(fpu_exists)], %g7
	brz,pn %g7, .fp_exception_cont
	  nop
#endif
	rdpr	%tstate, %g7			! branch if in privileged mode
	btst	TSTATE_PRIV, %g7
	bnz,pn	%xcc, .fp_exception_cont
	srl	%g2, FSR_FTT_SHIFT, %g7		! extract ftt from %fsr
	and	%g7, (FSR_FTT>>FSR_FTT_SHIFT), %g7
	cmp	%g7, FTT_UNFIN
	set	FSR_TEM_NX, %g5
	bne,pn	%xcc, .fp_exception_cont	! branch if NOT unfinished_FPop
	  andcc	%g2, %g5, %g0
	bne,pn	%xcc, .fp_exception_cont	! branch if FSR_TEM_NX enabled
	  rdpr	%tpc, %g5			! get faulting PC

	or	%g0, 1, %g7
	st	%g7, [%g1 + CPU_TL1_HDLR]	! set tl1_hdlr flag
	lda	[%g5]ASI_USER, %g6		! get user's instruction
	st	%g0, [%g1 + CPU_TL1_HDLR]	! clear tl1_hdlr flag

	set	FITOS_INSTR_MASK, %g7
	and	%g6, %g7, %g7
	set	FITOS_INSTR, %g5
	cmp	%g7, %g5
	bne,pn	%xcc, .fp_exception_cont	! branch if not FITOS_INSTR
	 nop

	/*
	 * This is unfinished FPops trap for "fitos" instruction. We
	 * need to simulate "fitos" via "fitod" and "fdtos" instruction
	 * sequence.
	 *
	 * We need a temporary FP register to do the conversion. Since
	 * both source and destination operands for the "fitos" instruction
	 * have to be within %f0-%f31, we use an FP register from the upper
	 * half to guarantee that it won't collide with the source or the
	 * dest operand. However, we do have to save and restore its value.
	 *
	 * We use %d62 as a temporary FP register for the conversion and
	 * branch to appropriate instruction within the conversion tables
	 * based upon the rs2 and rd values.
	 */

	std	%d62, [%g1 + CPU_TMP1]		! save original value

	srl	%g6, FITOS_RS2_SHIFT, %g7
	and	%g7, FITOS_REG_MASK, %g7
	set	_fitos_fitod_table, %g4
	sllx	%g7, 2, %g7
	jmp	%g4 + %g7
	  ba,pt	%xcc, _fitos_fitod_done
	.empty

_fitos_fitod_table:
	  fitod	%f0, %d62
	  fitod	%f1, %d62
	  fitod	%f2, %d62
	  fitod	%f3, %d62
	  fitod	%f4, %d62
	  fitod	%f5, %d62
	  fitod	%f6, %d62
	  fitod	%f7, %d62
	  fitod	%f8, %d62
	  fitod	%f9, %d62
	  fitod	%f10, %d62
	  fitod	%f11, %d62
	  fitod	%f12, %d62
	  fitod	%f13, %d62
	  fitod	%f14, %d62
	  fitod	%f15, %d62
	  fitod	%f16, %d62
	  fitod	%f17, %d62
	  fitod	%f18, %d62
	  fitod	%f19, %d62
	  fitod	%f20, %d62
	  fitod	%f21, %d62
	  fitod	%f22, %d62
	  fitod	%f23, %d62
	  fitod	%f24, %d62
	  fitod	%f25, %d62
	  fitod	%f26, %d62
	  fitod	%f27, %d62
	  fitod	%f28, %d62
	  fitod	%f29, %d62
	  fitod	%f30, %d62
	  fitod	%f31, %d62
_fitos_fitod_done:

	/*
	 * Now convert data back into single precision
	 */
	srl	%g6, FITOS_RD_SHIFT, %g7
	and	%g7, FITOS_REG_MASK, %g7
	set	_fitos_fdtos_table, %g4
	sllx	%g7, 2, %g7
	jmp	%g4 + %g7
	  ba,pt	%xcc, _fitos_fdtos_done
	.empty

_fitos_fdtos_table:
	  fdtos	%d62, %f0
	  fdtos	%d62, %f1
	  fdtos	%d62, %f2
	  fdtos	%d62, %f3
	  fdtos	%d62, %f4
	  fdtos	%d62, %f5
	  fdtos	%d62, %f6
	  fdtos	%d62, %f7
	  fdtos	%d62, %f8
	  fdtos	%d62, %f9
	  fdtos	%d62, %f10
	  fdtos	%d62, %f11
	  fdtos	%d62, %f12
	  fdtos	%d62, %f13
	  fdtos	%d62, %f14
	  fdtos	%d62, %f15
	  fdtos	%d62, %f16
	  fdtos	%d62, %f17
	  fdtos	%d62, %f18
	  fdtos	%d62, %f19
	  fdtos	%d62, %f20
	  fdtos	%d62, %f21
	  fdtos	%d62, %f22
	  fdtos	%d62, %f23
	  fdtos	%d62, %f24
	  fdtos	%d62, %f25
	  fdtos	%d62, %f26
	  fdtos	%d62, %f27
	  fdtos	%d62, %f28
	  fdtos	%d62, %f29
	  fdtos	%d62, %f30
	  fdtos	%d62, %f31
_fitos_fdtos_done:

	ldd	[%g1 + CPU_TMP1], %d62		! restore %d62

#if DEBUG
	/*
	 * Update FPop_unfinished trap kstat
	 */
	set	fpustat+FPUSTAT_UNFIN_KSTAT, %g7
	ldx	[%g7], %g5
1:
	add	%g5, 1, %g6

	casxa	[%g7] ASI_N, %g5, %g6
	cmp	%g5, %g6
	bne,a,pn %xcc, 1b
	  or	%g0, %g6, %g5

	/*
	 * Update fpu_sim_fitos kstat
	 */
	set	fpuinfo+FPUINFO_FITOS_KSTAT, %g7
	ldx	[%g7], %g5
1:
	add	%g5, 1, %g6

	casxa	[%g7] ASI_N, %g5, %g6
	cmp	%g5, %g6
	bne,a,pn %xcc, 1b
	  or	%g0, %g6, %g5
#endif /* DEBUG */

	FAST_TRAP_DONE

.fp_exception_cont:
	/*
	 * Let _fp_exception deal with simulating FPop instruction.
	 * Note that we need to pass %fsr in %g2 (already read above).
	 */

	set	_fp_exception, %g1
	ba,pt	%xcc, sys_trap
	sub	%g0, 1, %g4


/*
 * Register windows
 */
.flushw:
.clean_windows:
	rdpr	%tnpc, %g1
	wrpr	%g1, %tpc
	add	%g1, 4, %g1
	wrpr	%g1, %tnpc
	set	trap, %g1
	mov	T_FLUSH_PCB, %g3
	ba,pt	%xcc, sys_trap
	sub	%g0, 1, %g4

/*
 * .spill_clean: clean the previous window, restore the wstate, and
 * "done".
 *
 * Entry: %g7 contains new wstate
 */
.spill_clean:
	sethi	%hi(nwin_minus_one), %g5
	ld	[%g5 + %lo(nwin_minus_one)], %g5 ! %g5 = nwin - 1
	rdpr	%cwp, %g6			! %g6 = %cwp
	deccc	%g6				! %g6--
	movneg	%xcc, %g5, %g6			! if (%g6<0) %g6 = nwin-1
	wrpr	%g6, %cwp
	TT_TRACE_L(trace_win)
	clr	%l0
	clr	%l1
	clr	%l2
	clr	%l3
	clr	%l4
	clr	%l5
	clr	%l6
	clr	%l7
	wrpr	%g0, %g7, %wstate
	saved
	retry			! restores correct %cwp

.fix_alignment:
	CPU_ADDR(%g1, %g2)		! load CPU struct addr to %g1 using %g2
	ldn	[%g1 + CPU_THREAD], %g1	! load thread pointer
	ldn	[%g1 + T_PROCP], %g1
	mov	1, %g2
	stb	%g2, [%g1 + P_FIXALIGNMENT]
	FAST_TRAP_DONE

#define	STDF_REG(REG, ADDR, TMP)		\
	sll	REG, 3, REG			;\
mark1:	set	start1, TMP			;\
	jmp	REG + TMP			;\
	  nop					;\
start1:	ba,pt	%xcc, done1			;\
	  std	%f0, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f32, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f2, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f34, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f4, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f36, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f6, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f38, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f8, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f40, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f10, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f42, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f12, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f44, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f14, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f46, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f16, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f48, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f18, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f50, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f20, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f52, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f22, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f54, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f24, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f56, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f26, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f58, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f28, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f60, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f30, [ADDR + CPU_TMP1]		;\
	ba,pt	%xcc, done1			;\
	  std	%f62, [ADDR + CPU_TMP1]		;\
done1:

#define	LDDF_REG(REG, ADDR, TMP)		\
	sll	REG, 3, REG			;\
mark2:	set	start2, TMP			;\
	jmp	REG + TMP			;\
	  nop					;\
start2:	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f0		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f32		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f2		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f34		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f4		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f36		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f6		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f38		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f8		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f40		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f10		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f42		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f12		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f44		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f14		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f46		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f16		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f48		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f18		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f50		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f20		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f52		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f22		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f54		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f24		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f56		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f26		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f58		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f28		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f60		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f30		;\
	ba,pt	%xcc, done2			;\
	  ldd	[ADDR + CPU_TMP1], %f62		;\
done2:

.lddf_exception_not_aligned:
	/* %g2 = sfar, %g3 = sfsr */
	mov	%g2, %g5		! stash sfar
#if defined(DEBUG) || defined(NEED_FPU_EXISTS)
	sethi	%hi(fpu_exists), %g2	! check fpu_exists
	ld	[%g2 + %lo(fpu_exists)], %g2
	brz,a,pn %g2, 4f
	  nop
#endif
	CPU_ADDR(%g1, %g4)
	or	%g0, 1, %g4
	st	%g4, [%g1 + CPU_TL1_HDLR] ! set tl1_hdlr flag

	rdpr	%tpc, %g2
	lda	[%g2]ASI_AIUP, %g6	! get the user's lddf instruction
	srl	%g6, 23, %g1		! using ldda or not?
	and	%g1, 1, %g1
	brz,a,pt %g1, 2f		! check for ldda instruction
	  nop
	srl	%g6, 13, %g1		! check immflag
	and	%g1, 1, %g1
	rdpr	%tstate, %g2		! %tstate in %g2
	brnz,a,pn %g1, 1f
	  srl	%g2, 31, %g1		! get asi from %tstate
	srl	%g6, 5, %g1		! get asi from instruction
	and	%g1, 0xFF, %g1		! imm_asi field
1:
	cmp	%g1, ASI_P		! primary address space
	be,a,pt %icc, 2f
	  nop
	cmp	%g1, ASI_PNF		! primary no fault address space
	be,a,pt %icc, 2f
	  nop
	cmp	%g1, ASI_S		! secondary address space
	be,a,pt %icc, 2f
	  nop
	cmp	%g1, ASI_SNF		! secondary no fault address space
	bne,a,pn %icc, 3f
	  nop
2:
	lduwa	[%g5]ASI_USER, %g7	! get first half of misaligned data
	add	%g5, 4, %g5		! increment misaligned data address
	lduwa	[%g5]ASI_USER, %g5	! get second half of misaligned data

	sllx	%g7, 32, %g7
	or	%g5, %g7, %g5		! combine data
	CPU_ADDR(%g7, %g1)		! save data on a per-cpu basis
	stx	%g5, [%g7 + CPU_TMP1]	! save in cpu_tmp1

	srl	%g6, 25, %g3		! %g6 has the instruction
	and	%g3, 0x1F, %g3		! %g3 has rd
	LDDF_REG(%g3, %g7, %g4)

	CPU_ADDR(%g1, %g4)
	st	%g0, [%g1 + CPU_TL1_HDLR] ! clear tl1_hdlr flag
	FAST_TRAP_DONE
3:
	CPU_ADDR(%g1, %g4)
	st	%g0, [%g1 + CPU_TL1_HDLR] ! clear tl1_hdlr flag
4:
	set	T_USER, %g3		! trap type in %g3
	or	%g3, T_LDDF_ALIGN, %g3
	mov	%g5, %g2		! misaligned vaddr in %g2
	set	fpu_trap, %g1		! goto C for the little and
	ba,pt	%xcc, sys_trap		! no fault little asi's
	  sub	%g0, 1, %g4

.stdf_exception_not_aligned:
	/* %g2 = sfar, %g3 = sfsr */
	mov	%g2, %g5

#if defined(DEBUG) || defined(NEED_FPU_EXISTS)
	sethi	%hi(fpu_exists), %g7		! check fpu_exists
	ld	[%g7 + %lo(fpu_exists)], %g3
	brz,a,pn %g3, 4f
	  nop
#endif
	CPU_ADDR(%g1, %g4)
	or	%g0, 1, %g4
	st	%g4, [%g1 + CPU_TL1_HDLR] ! set tl1_hdlr flag

	rdpr	%tpc, %g2
	lda	[%g2]ASI_AIUP, %g6	! get the user's stdf instruction

	srl	%g6, 23, %g1		! using stda or not?
	and	%g1, 1, %g1
	brz,a,pt %g1, 2f		! check for stda instruction
	  nop
	srl	%g6, 13, %g1		! check immflag
	and	%g1, 1, %g1
	rdpr	%tstate, %g2		! %tstate in %g2
	brnz,a,pn %g1, 1f
	  srl	%g2, 31, %g1		! get asi from %tstate
	srl	%g6, 5, %g1		! get asi from instruction
	and	%g1, 0xff, %g1		! imm_asi field
1:
	cmp	%g1, ASI_P		! primary address space
	be,a,pt %icc, 2f
	  nop
	cmp	%g1, ASI_S		! secondary address space
	bne,a,pn %icc, 3f
	  nop
2:
	srl	%g6, 25, %g6
	and	%g6, 0x1F, %g6		! %g6 has rd
	CPU_ADDR(%g7, %g1)
	STDF_REG(%g6, %g7, %g4)		! STDF_REG(REG, ADDR, TMP)

	ldx	[%g7 + CPU_TMP1], %g6
	srlx	%g6, 32, %g7
	stuwa	%g7, [%g5]ASI_USER	! first half
	add	%g5, 4, %g5		! increment misaligned data address
	stuwa	%g6, [%g5]ASI_USER	! second half

	CPU_ADDR(%g1, %g4)
	st	%g0, [%g1 + CPU_TL1_HDLR] ! clear tl1_hdlr flag
	FAST_TRAP_DONE
3:
	CPU_ADDR(%g1, %g4)
	st	%g0, [%g1 + CPU_TL1_HDLR] ! clear tl1_hdlr flag
4:
	set	T_USER, %g3		! trap type in %g3
	or	%g3, T_STDF_ALIGN, %g3
	mov	%g5, %g2		! misaligned vaddr in %g2
	set	fpu_trap, %g1		! goto C for the little and
	ba,pt	%xcc, sys_trap		! nofault little asi's
	  sub	%g0, 1, %g4

#ifdef DEBUG_USER_TRAPTRACECTL

.traptrace_freeze:
	mov	%l0, %g1 ; mov	%l1, %g2 ; mov	%l2, %g3 ; mov	%l4, %g4
	TT_TRACE_L(trace_win)
	mov	%g4, %l4 ; mov	%g3, %l2 ; mov	%g2, %l1 ; mov	%g1, %l0
	set	trap_freeze, %g1
	mov	1, %g2
	st	%g2, [%g1]
	FAST_TRAP_DONE

.traptrace_unfreeze:
	set	trap_freeze, %g1
	st	%g0, [%g1]
	mov	%l0, %g1 ; mov	%l1, %g2 ; mov	%l2, %g3 ; mov	%l4, %g4
	TT_TRACE_L(trace_win)
	mov	%g4, %l4 ; mov	%g3, %l2 ; mov	%g2, %l1 ; mov	%g1, %l0
	FAST_TRAP_DONE

#endif /* DEBUG_USER_TRAPTRACECTL */

.getcc:
	CPU_ADDR(%g1, %g2)
	stx	%o0, [%g1 + CPU_TMP1]		! save %o0
	rdpr	%tstate, %g3			! get tstate
	srlx	%g3, PSR_TSTATE_CC_SHIFT, %o0	! shift ccr to V8 psr
	set	PSR_ICC, %g2
	and	%o0, %g2, %o0			! mask out the rest
	srl	%o0, PSR_ICC_SHIFT, %o0		! right justify
	wrpr	%g0, 0, %gl
	mov	%o0, %g1			! move ccr to normal %g1
	wrpr	%g0, 1, %gl
	! cannot assume globals retained their values after increasing %gl
	CPU_ADDR(%g1, %g2)
	ldx	[%g1 + CPU_TMP1], %o0		! restore %o0
	FAST_TRAP_DONE

.setcc:
	CPU_ADDR(%g1, %g2)
	stx	%o0, [%g1 + CPU_TMP1]		! save %o0
	wrpr	%g0, 0, %gl
	mov	%g1, %o0
	wrpr	%g0, 1, %gl
	! cannot assume globals retained their values after increasing %gl
	CPU_ADDR(%g1, %g2)
	sll	%o0, PSR_ICC_SHIFT, %g2
	set	PSR_ICC, %g3
	and	%g2, %g3, %g2			! mask out rest
	sllx	%g2, PSR_TSTATE_CC_SHIFT, %g2
	rdpr	%tstate, %g3			! get tstate
	srl	%g3, 0, %g3			! clear upper word
	or	%g3, %g2, %g3			! or in new bits
	wrpr	%g3, %tstate
	ldx	[%g1 + CPU_TMP1], %o0		! restore %o0
	FAST_TRAP_DONE

/*
 * getpsr(void)
 * Note that the xcc part of the ccr is not provided.
 * The V8 code shows why the V9 trap is not faster:
 * #define GETPSR_TRAP() \
 *      mov %psr, %i0; jmp %l2; rett %l2+4; nop;
 */

	.type	.getpsr, #function
.getpsr:
	rdpr	%tstate, %g1			! get tstate
	srlx	%g1, PSR_TSTATE_CC_SHIFT, %o0	! shift ccr to V8 psr
	set	PSR_ICC, %g2
	and	%o0, %g2, %o0			! mask out the rest

	rd	%fprs, %g1			! get fprs
	and	%g1, FPRS_FEF, %g2		! mask out dirty upper/lower
	sllx	%g2, PSR_FPRS_FEF_SHIFT, %g2	! shift fef to V8 psr.ef
	or	%o0, %g2, %o0			! or result into psr.ef

	set	V9_PSR_IMPLVER, %g2		! SI assigned impl/ver: 0xef
	or	%o0, %g2, %o0			! or psr.impl/ver
	FAST_TRAP_DONE
	SET_SIZE(.getpsr)

/*
 * setpsr(newpsr)
 * Note that there is no support for ccr.xcc in the V9 code.
 */

	.type	.setpsr, #function
.setpsr:
	rdpr	%tstate, %g1			! get tstate
!	setx	TSTATE_V8_UBITS, %g2
	or 	%g0, CCR_ICC, %g3
	sllx	%g3, TSTATE_CCR_SHIFT, %g2

	andn	%g1, %g2, %g1			! zero current user bits
	set	PSR_ICC, %g2
	and	%g2, %o0, %g2			! clear all but psr.icc bits
	sllx	%g2, PSR_TSTATE_CC_SHIFT, %g3	! shift to tstate.ccr.icc
	wrpr	%g1, %g3, %tstate		! write tstate

	set	PSR_EF, %g2
	and	%g2, %o0, %g2			! clear all but fp enable bit
	srlx	%g2, PSR_FPRS_FEF_SHIFT, %g4	! shift ef to V9 fprs.fef
	wr	%g0, %g4, %fprs			! write fprs

	CPU_ADDR(%g1, %g2)			! load CPU struct addr to %g1
	ldn	[%g1 + CPU_THREAD], %g2		! load thread pointer
	ldn	[%g2 + T_LWP], %g3		! load klwp pointer
	ldn	[%g3 + LWP_FPU], %g2		! get lwp_fpu pointer
	stuw	%g4, [%g2 + FPU_FPRS]		! write fef value to fpu_fprs
	srlx	%g4, 2, %g4			! shift fef value to bit 0
	stub	%g4, [%g2 + FPU_EN]		! write fef value to fpu_en
	FAST_TRAP_DONE
	SET_SIZE(.setpsr)

/*
 * getlgrp
 * get home lgrpid on which the calling thread is currently executing.
 */
	.type	.getlgrp, #function
.getlgrp:
	! Thanks for the incredibly helpful comments
	CPU_ADDR(%g1, %g2)		! load CPU struct addr to %g1 using %g2
	ld	[%g1 + CPU_ID], %o0	! load cpu_id
	ldn	[%g1 + CPU_THREAD], %g2	! load thread pointer
	ldn	[%g2 + T_LPL], %g2	! load lpl pointer
	ld	[%g2 + LPL_LGRPID], %g1	! load lpl_lgrpid
	sra	%g1, 0, %o1
	FAST_TRAP_DONE
	SET_SIZE(.getlgrp)

/*
 * Entry for old 4.x trap (trap 0).
 */
	ENTRY_NP(syscall_trap_4x)
	CPU_ADDR(%g1, %g2)		! load CPU struct addr to %g1 using %g2
	ldn	[%g1 + CPU_THREAD], %g2	! load thread pointer
	ldn	[%g2 + T_LWP], %g2	! load klwp pointer
	ld	[%g2 + PCB_TRAP0], %g2	! lwp->lwp_pcb.pcb_trap0addr
	brz,pn	%g2, 1f			! has it been set?
	st	%l0, [%g1 + CPU_TMP1]	! delay - save some locals
	st	%l1, [%g1 + CPU_TMP2]
	rdpr	%tnpc, %l1		! save old tnpc
	wrpr	%g0, %g2, %tnpc		! setup tnpc

	mov	%g1, %l0		! save CPU struct addr
	wrpr	%g0, 0, %gl
	mov	%l1, %g6		! pass tnpc to user code in %g6
	wrpr	%g0, 1, %gl
	ld	[%l0 + CPU_TMP2], %l1	! restore locals
	ld	[%l0 + CPU_TMP1], %l0
	FAST_TRAP_DONE_CHK_INTR
1:
	!
	! check for old syscall mmap which is the only different one which
	! must be the same.  Others are handled in the compatibility library.
	!
	mov	%g1, %l0		! save CPU struct addr
	wrpr	%g0, 0, %gl
	cmp	%g1, OSYS_mmap		! compare to old 4.x mmap
	movz	%icc, SYS_mmap, %g1
	wrpr	%g0, 1, %gl
	ld	[%l0 + CPU_TMP1], %l0
	SYSCALL(syscall_trap32)
	SET_SIZE(syscall_trap_4x)

/*
 * Handler for software trap 9.
 * Set trap0 emulation address for old 4.x system call trap.
 * XXX - this should be a system call.
 */
	ENTRY_NP(set_trap0_addr)
	CPU_ADDR(%g1, %g2)		! load CPU struct addr to %g1 using %g2
	st	%l0, [%g1 + CPU_TMP1]	! save some locals
	st	%l1, [%g1 + CPU_TMP2]
	mov	%g1, %l0	! preserve CPU addr
	wrpr	%g0, 0, %gl
	mov	%g1, %l1
	wrpr	%g0, 1, %gl
	! cannot assume globals retained their values after increasing %gl
	ldn	[%l0 + CPU_THREAD], %g2	! load thread pointer
	ldn	[%g2 + T_LWP], %g2	! load klwp pointer
	andn	%l1, 3, %l1		! force alignment
	st	%l1, [%g2 + PCB_TRAP0]	! lwp->lwp_pcb.pcb_trap0addr
	ld	[%l0 + CPU_TMP2], %l1	! restore locals
	ld	[%l0 + CPU_TMP1], %l0
	FAST_TRAP_DONE
	SET_SIZE(set_trap0_addr)

/*
 * mmu_trap_tl1
 * trap handler for unexpected mmu traps.
 * simply checks if the trap was a user lddf/stdf alignment trap, in which
 * case we go to fpu_trap or a user trap from the window handler, in which
 * case we go save the state on the pcb.  Otherwise, we go to ptl1_panic.
 */
	.type	mmu_trap_tl1, #function
mmu_trap_tl1:
#ifdef	TRAPTRACE
	TRACE_PTR(%g5, %g6)
	GET_TRACE_TICK(%g6, %g7)
	stxa	%g6, [%g5 + TRAP_ENT_TICK]%asi
	TRACE_SAVE_TL_GL_REGS(%g5, %g6)
	rdpr	%tt, %g6
	stha	%g6, [%g5 + TRAP_ENT_TT]%asi
	rdpr	%tstate, %g6
	stxa	%g6, [%g5 + TRAP_ENT_TSTATE]%asi
	stna	%sp, [%g5 + TRAP_ENT_SP]%asi
	stna	%g0, [%g5 + TRAP_ENT_TR]%asi
	rdpr	%tpc, %g6
	stna	%g6, [%g5 + TRAP_ENT_TPC]%asi
	MMU_FAULT_STATUS_AREA(%g6)
	ldx	[%g6 + MMFSA_D_ADDR], %g6
	stna	%g6, [%g5 + TRAP_ENT_F1]%asi !  MMU fault address
	CPU_PADDR(%g7, %g6);
	add	%g7, CPU_TL1_HDLR, %g7
	lda	[%g7]ASI_MEM, %g6
	stna	%g6, [%g5 + TRAP_ENT_F2]%asi
	MMU_FAULT_STATUS_AREA(%g6)
	ldx	[%g6 + MMFSA_D_TYPE], %g7 ! XXXQ should be a MMFSA_F_ constant?
	ldx	[%g6 + MMFSA_D_CTX], %g6
	sllx	%g6, SFSR_CTX_SHIFT, %g6
	or	%g6, %g7, %g6
	stna	%g6, [%g5 + TRAP_ENT_F3]%asi ! MMU context/type
	set	0xdeadbeef, %g6
	stna	%g6, [%g5 + TRAP_ENT_F4]%asi
	TRACE_NEXT(%g5, %g6, %g7)
#endif /* TRAPTRACE */
	CPU_PADDR(%g7, %g6);
	add     %g7, CPU_TL1_HDLR, %g7		! %g7 = &cpu_m.tl1_hdlr (PA)
	lda	[%g7]ASI_MEM, %g6
	brz,a,pt %g6, 1f
	  nop
	sta     %g0, [%g7]ASI_MEM
	! XXXQ need to setup registers for sfmmu_mmu_trap?
	ba,a,pt	%xcc, sfmmu_mmu_trap		! handle page faults
1:
	rdpr	%tpc, %g7
	/* in user_rtt? */
	set	rtt_fill_start, %g6
	cmp	%g7, %g6
	blu,pn	%xcc, 6f
	 .empty
	set	rtt_fill_end, %g6
	cmp	%g7, %g6
	bgeu,pn %xcc, 6f
	 nop
	set	fault_rtt_fn1, %g7
	ba,a	7f
6:
	! check to see if the trap pc is in a window spill/fill handling
	rdpr	%tpc, %g7
	/* tpc should be in the trap table */
	set	trap_table, %g6
	cmp	%g7, %g6
	blu,a,pn %xcc, ptl1_panic
	  mov	PTL1_BAD_MMUTRAP, %g1
	set	etrap_table, %g6
	cmp	%g7, %g6
	bgeu,a,pn %xcc, ptl1_panic
	  mov	PTL1_BAD_MMUTRAP, %g1
	! pc is inside the trap table, convert to trap type
	srl	%g7, 5, %g6		! XXXQ need #define
	and	%g6, 0x1ff, %g6		! XXXQ need #define
	! and check for a window trap type
	and	%g6, WTRAP_TTMASK, %g6
	cmp	%g6, WTRAP_TYPE
	bne,a,pn %xcc, ptl1_panic
	  mov	PTL1_BAD_MMUTRAP, %g1
	andn	%g7, WTRAP_ALIGN, %g7	/* 128 byte aligned */
	add	%g7, WTRAP_FAULTOFF, %g7

7:
	! Arguments are passed in the global set active after the
	! 'done' instruction. Before switching sets, must save
	! the calculated next pc
	wrpr	%g0, %g7, %tnpc
	wrpr	%g0, 1, %gl
	rdpr	%tt, %g5
	MMU_FAULT_STATUS_AREA(%g7)
	cmp	%g5, T_ALIGNMENT
	be,pn	%xcc, 1f
	ldx	[%g7 + MMFSA_D_ADDR], %g6
	ldx	[%g7 + MMFSA_D_CTX], %g7
	srlx	%g6, MMU_PAGESHIFT, %g6		/* align address */
	cmp	%g7, USER_CONTEXT_TYPE
	sllx	%g6, MMU_PAGESHIFT, %g6
	movgu	%icc, USER_CONTEXT_TYPE, %g7
	or	%g6, %g7, %g6			/* TAG_ACCESS */
1:
	done
	SET_SIZE(mmu_trap_tl1)

/*
 * Several traps use kmdb_trap and kmdb_trap_tl1 as their handlers.  These
 * traps are valid only when kmdb is loaded.  When the debugger is active,
 * the code below is rewritten to transfer control to the appropriate
 * debugger entry points.
 */
	.global	kmdb_trap
	.align	8
kmdb_trap:
	ba,a	trap_table0
	jmp	%g1 + 0
	nop

	.global	kmdb_trap_tl1
	.align	8
kmdb_trap_tl1:
	ba,a	trap_table0
	jmp	%g1 + 0
	nop

/*
 * This entry is copied from OBP's trap table during boot.
 */
	.global	obp_bpt
	.align	8
obp_bpt:
	NOT



#ifdef	TRAPTRACE
/*
 * TRAPTRACE support.
 * labels here are branched to with "rd %pc, %g7" in the delay slot.
 * Return is done by "jmp %g7 + 4".
 */

trace_dmmu:
	TRACE_PTR(%g3, %g6)
	GET_TRACE_TICK(%g6, %g5)
	stxa	%g6, [%g3 + TRAP_ENT_TICK]%asi
	TRACE_SAVE_TL_GL_REGS(%g3, %g6)
	rdpr	%tt, %g6
	stha	%g6, [%g3 + TRAP_ENT_TT]%asi
	rdpr	%tstate, %g6
	stxa	%g6, [%g3 + TRAP_ENT_TSTATE]%asi
	stna	%sp, [%g3 + TRAP_ENT_SP]%asi
	rdpr	%tpc, %g6
	stna	%g6, [%g3 + TRAP_ENT_TPC]%asi
	MMU_FAULT_STATUS_AREA(%g6)
	ldx	[%g6 + MMFSA_D_ADDR], %g4
	stxa	%g4, [%g3 + TRAP_ENT_TR]%asi
	ldx	[%g6 + MMFSA_D_CTX], %g4
	stxa	%g4, [%g3 + TRAP_ENT_F1]%asi
	ldx	[%g6 + MMFSA_D_TYPE], %g4
	stxa	%g4, [%g3 + TRAP_ENT_F2]%asi
	stxa	%g6, [%g3 + TRAP_ENT_F3]%asi
	stna	%g0, [%g3 + TRAP_ENT_F4]%asi
	TRACE_NEXT(%g3, %g4, %g5)
	jmp	%g7 + 4
	nop

trace_immu:
	TRACE_PTR(%g3, %g6)
	GET_TRACE_TICK(%g6, %g5)
	stxa	%g6, [%g3 + TRAP_ENT_TICK]%asi
	TRACE_SAVE_TL_GL_REGS(%g3, %g6)
	rdpr	%tt, %g6
	stha	%g6, [%g3 + TRAP_ENT_TT]%asi
	rdpr	%tstate, %g6
	stxa	%g6, [%g3 + TRAP_ENT_TSTATE]%asi
	stna	%sp, [%g3 + TRAP_ENT_SP]%asi
	rdpr	%tpc, %g6
	stna	%g6, [%g3 + TRAP_ENT_TPC]%asi
	MMU_FAULT_STATUS_AREA(%g6)
	ldx	[%g6 + MMFSA_I_ADDR], %g4
	stxa	%g4, [%g3 + TRAP_ENT_TR]%asi
	ldx	[%g6 + MMFSA_I_CTX], %g4
	stxa	%g4, [%g3 + TRAP_ENT_F1]%asi
	ldx	[%g6 + MMFSA_I_TYPE], %g4
	stxa	%g4, [%g3 + TRAP_ENT_F2]%asi
	stxa	%g6, [%g3 + TRAP_ENT_F3]%asi
	stna	%g0, [%g3 + TRAP_ENT_F4]%asi
	TRACE_NEXT(%g3, %g4, %g5)
	jmp	%g7 + 4
	nop

trace_gen:
	TRACE_PTR(%g3, %g6)
	GET_TRACE_TICK(%g6, %g5)
	stxa	%g6, [%g3 + TRAP_ENT_TICK]%asi
	TRACE_SAVE_TL_GL_REGS(%g3, %g6)
	rdpr	%tt, %g6
	stha	%g6, [%g3 + TRAP_ENT_TT]%asi
	rdpr	%tstate, %g6
	stxa	%g6, [%g3 + TRAP_ENT_TSTATE]%asi
	stna	%sp, [%g3 + TRAP_ENT_SP]%asi
	rdpr	%tpc, %g6
	stna	%g6, [%g3 + TRAP_ENT_TPC]%asi
	stna	%g0, [%g3 + TRAP_ENT_TR]%asi
	stna	%g0, [%g3 + TRAP_ENT_F1]%asi
	stna	%g0, [%g3 + TRAP_ENT_F2]%asi
	stna	%g0, [%g3 + TRAP_ENT_F3]%asi
	stna	%g0, [%g3 + TRAP_ENT_F4]%asi
	TRACE_NEXT(%g3, %g4, %g5)
	jmp	%g7 + 4
	nop

trace_win:
	TRACE_WIN_INFO(0, %l0, %l1, %l2)
	! Keep the locals as clean as possible, caller cleans %l4
	clr	%l2
	clr	%l1
	jmp	%l4 + 4
	  clr	%l0

/*
 * Trace a tsb hit
 * g1 = tsbe pointer (in/clobbered)
 * g2 = tag access register (in)
 * g3 - g4 = scratch (clobbered)
 * g5 = tsbe data (in)
 * g6 = scratch (clobbered)
 * g7 = pc we jumped here from (in)
 */

	! Do not disturb %g5, it will be used after the trace
	ALTENTRY(trace_tsbhit)
	TRACE_TSBHIT(0)
	jmp	%g7 + 4
	nop

/*
 * Trace a TSB miss
 *
 * g1 = tsb8k pointer (in)
 * g2 = tag access register (in)
 * g3 = tsb4m pointer (in)
 * g4 = tsbe tag (in/clobbered)
 * g5 - g6 = scratch (clobbered)
 * g7 = pc we jumped here from (in)
 */
	.global	trace_tsbmiss
trace_tsbmiss:
	membar	#Sync
	sethi	%hi(FLUSH_ADDR), %g6
	flush	%g6
	TRACE_PTR(%g5, %g6)
	stna	%g2, [%g5 + TRAP_ENT_SP]%asi		! tag access
	stna	%g4, [%g5 + TRAP_ENT_F1]%asi		! XXX? tsb tag
	GET_TRACE_TICK(%g6, %g4)
	stxa	%g6, [%g5 + TRAP_ENT_TICK]%asi
	rdpr	%tnpc, %g6
	stna	%g6, [%g5 + TRAP_ENT_F2]%asi
	stna	%g1, [%g5 + TRAP_ENT_F3]%asi		! tsb8k pointer
	rdpr	%tpc, %g6
	stna	%g6, [%g5 + TRAP_ENT_TPC]%asi
	TRACE_SAVE_TL_GL_REGS(%g5, %g6)
	rdpr	%tt, %g6
	or	%g6, TT_MMU_MISS, %g4
	stha	%g4, [%g5 + TRAP_ENT_TT]%asi
	mov	MMFSA_D_ADDR, %g4
	cmp	%g6, FAST_IMMU_MISS_TT
	move	%xcc, MMFSA_I_ADDR, %g4
	cmp	%g6, T_INSTR_MMU_MISS
	move	%xcc, MMFSA_I_ADDR, %g4
	MMU_FAULT_STATUS_AREA(%g6)
	ldx	[%g6 + %g4], %g6
	stxa	%g6, [%g5 + TRAP_ENT_TSTATE]%asi	! tag target
	cmp	%g4, MMFSA_D_ADDR
	move	%xcc, MMFSA_D_CTX, %g4
	movne	%xcc, MMFSA_I_CTX, %g4
	MMU_FAULT_STATUS_AREA(%g6)
	ldx	[%g6 + %g4], %g6
	stxa	%g6, [%g5 + TRAP_ENT_F4]%asi		! context ID
	stna	%g3, [%g5 + TRAP_ENT_TR]%asi		! tsb4m pointer
	TRACE_NEXT(%g5, %g4, %g6)
	jmp	%g7 + 4
	nop

/*
 * g2 = tag access register (in)
 * g3 = ctx type (0, 1 or 2) (in) (not used)
 */
trace_dataprot:
	membar	#Sync
	sethi	%hi(FLUSH_ADDR), %g6
	flush	%g6
	TRACE_PTR(%g1, %g6)
	GET_TRACE_TICK(%g6, %g4)
	stxa	%g6, [%g1 + TRAP_ENT_TICK]%asi
	rdpr	%tpc, %g6
	stna	%g6, [%g1 + TRAP_ENT_TPC]%asi
	rdpr	%tstate, %g6
	stxa	%g6, [%g1 + TRAP_ENT_TSTATE]%asi
	stna	%g2, [%g1 + TRAP_ENT_SP]%asi		! tag access reg
	stna	%g0, [%g1 + TRAP_ENT_F1]%asi
	stna	%g0, [%g1 + TRAP_ENT_F2]%asi
	stna	%g0, [%g1 + TRAP_ENT_F3]%asi
	stna	%g0, [%g1 + TRAP_ENT_F4]%asi
	TRACE_SAVE_TL_GL_REGS(%g1, %g6)
	rdpr	%tt, %g6
	stha	%g6, [%g1 + TRAP_ENT_TT]%asi
	mov	MMFSA_D_CTX, %g4
	cmp	%g6, FAST_IMMU_MISS_TT
	move	%xcc, MMFSA_I_CTX, %g4
	cmp	%g6, T_INSTR_MMU_MISS
	move	%xcc, MMFSA_I_CTX, %g4
	MMU_FAULT_STATUS_AREA(%g6)
	ldx	[%g6 + %g4], %g6
	stxa	%g6, [%g1 + TRAP_ENT_TR]%asi	! context ID
	TRACE_NEXT(%g1, %g4, %g5)
	jmp	%g7 + 4
	nop

#endif /* TRAPTRACE */

/*
 * Handle watchdog reset trap. Enable the MMU using the MMU_ENABLE
 * HV service, which requires the return target to be specified as a VA
 * since we are enabling the MMU. We set the target to ptl1_panic.
 */

	.type	.watchdog_trap, #function
.watchdog_trap:
	mov	1, %o0
	setx	ptl1_panic, %g2, %o1
	mov	MMU_ENABLE, %o5
	ta	FAST_TRAP
	done
	SET_SIZE(.watchdog_trap)
/*
 * synthesize for trap(): SFAR in %g2, SFSR in %g3
 */
	.type	.dmmu_exc_lddf_not_aligned, #function
.dmmu_exc_lddf_not_aligned:
	MMU_FAULT_STATUS_AREA(%g3)
	ldx	[%g3 + MMFSA_D_ADDR], %g2
	/* Fault type not available in MMU fault status area */
	mov	MMFSA_F_UNALIGN, %g1
	ldx	[%g3 + MMFSA_D_CTX], %g3
	sllx	%g3, SFSR_CTX_SHIFT, %g3
	btst	1, %sp
	bnz,pt	%xcc, .lddf_exception_not_aligned
	or	%g3, %g1, %g3			/* SFSR */
	ba,a,pt	%xcc, .mmu_exception_not_aligned
	SET_SIZE(.dmmu_exc_lddf_not_aligned)

/*
 * synthesize for trap(): SFAR in %g2, SFSR in %g3
 */
	.type	.dmmu_exc_stdf_not_aligned, #function
.dmmu_exc_stdf_not_aligned:
	MMU_FAULT_STATUS_AREA(%g3)
	ldx	[%g3 + MMFSA_D_ADDR], %g2
	/* Fault type not available in MMU fault status area */
	mov	MMFSA_F_UNALIGN, %g1
	ldx	[%g3 + MMFSA_D_CTX], %g3
	sllx	%g3, SFSR_CTX_SHIFT, %g3
	btst	1, %sp
	bnz,pt	%xcc, .stdf_exception_not_aligned
	or	%g3, %g1, %g3			/* SFSR */
	ba,a,pt	%xcc, .mmu_exception_not_aligned
	SET_SIZE(.dmmu_exc_stdf_not_aligned)

	.type	.dmmu_exception, #function
.dmmu_exception:
	MMU_FAULT_STATUS_AREA(%g3)
	ldx	[%g3 + MMFSA_D_ADDR], %g2
	ldx	[%g3 + MMFSA_D_TYPE], %g1
	ldx	[%g3 + MMFSA_D_CTX], %g4
	srlx	%g2, MMU_PAGESHIFT, %g2		/* align address */
	sllx	%g2, MMU_PAGESHIFT, %g2
	sllx	%g4, SFSR_CTX_SHIFT, %g3
	or	%g3, %g1, %g3			/* SFSR */
	cmp	%g4, USER_CONTEXT_TYPE
	movgeu	%icc, USER_CONTEXT_TYPE, %g4
	or	%g2, %g4, %g2			/* TAG_ACCESS */
	ba,pt	%xcc, .mmu_exception_end
	mov	T_DATA_EXCEPTION, %g1
	SET_SIZE(.dmmu_exception)

	.align 32
	.global pil15_epilogue
pil15_epilogue:
	ba	pil_interrupt_common
	nop
	.align 32

/*
 * fast_trap_done, fast_trap_done_chk_intr:
 *
 * Due to the design of UltraSPARC pipeline, pending interrupts are not
 * taken immediately after a RETRY or DONE instruction which causes IE to
 * go from 0 to 1. Instead, the instruction at %tpc or %tnpc is allowed
 * to execute first before taking any interrupts. If that instruction
 * results in other traps, and if the corresponding trap handler runs
 * entirely at TL=1 with interrupts disabled, then pending interrupts
 * won't be taken until after yet another instruction following the %tpc
 * or %tnpc.
 *
 * A malicious user program can use this feature to block out interrupts
 * for extended durations, which can result in send_mondo_timeout kernel
 * panic.
 *
 * This problem is addressed by servicing any pending interrupts via
 * sys_trap before returning back to the user mode from a fast trap
 * handler. The "done" instruction within a fast trap handler, which
 * runs entirely at TL=1 with interrupts disabled, is replaced with the
 * FAST_TRAP_DONE macro, which branches control to this fast_trap_done
 * entry point.
 *
 * We check for any pending interrupts here and force a sys_trap to
 * service those interrupts, if any. To minimize overhead, pending
 * interrupts are checked if the %tpc happens to be at 16K boundary,
 * which allows a malicious program to execute at most 4K consecutive
 * instructions before we service any pending interrupts. If a worst
 * case fast trap handler takes about 2 usec, then interrupts will be
 * blocked for at most 8 msec, less than a clock tick.
 *
 * For the cases where we don't know if the %tpc will cross a 16K
 * boundary, we can't use the above optimization and always process
 * any pending interrupts via fast_frap_done_chk_intr entry point.
 *
 * Entry Conditions:
 * 	%pstate		am:0 priv:1 ie:0
 * 			globals are AG (not normal globals)
 */

	.global	fast_trap_done, fast_trap_done_chk_intr
fast_trap_done:
	rdpr	%tpc, %g5
	sethi	%hi(0xffffc000), %g6	! 1's complement of 0x3fff
	andncc	%g5, %g6, %g0		! check lower 14 bits of %tpc
	bz,pn	%icc, 1f		! branch if zero (lower 32 bits only)
	nop
	done

fast_trap_done_chk_intr:
1:	rd	SOFTINT, %g6
	brnz,pn	%g6, 2f		! branch if any pending intr
	nop
	done

2:
	/*
	 * We get here if there are any pending interrupts.
	 * Adjust %tpc/%tnpc as we'll be resuming via "retry"
	 * instruction.
	 */
	rdpr	%tnpc, %g5
	wrpr	%g0, %g5, %tpc
	add	%g5, 4, %g5
	wrpr	%g0, %g5, %tnpc

	/*
	 * Force a dummy sys_trap call so that interrupts can be serviced.
	 */
	set	fast_trap_dummy_call, %g1
	ba,pt	%xcc, sys_trap
	  mov	-1, %g4

fast_trap_dummy_call:
	retl
	nop

/*
 * Currently the brand syscall interposition code is not enabled by
 * default.  Instead, when a branded zone is first booted the brand
 * infrastructure will patch the trap table so that the syscall
 * entry points are redirected to syscall_wrapper32 and syscall_wrapper
 * for ILP32 and LP64 syscalls respectively.  this is done in
 * brand_plat_interposition_enable().  Note that the syscall wrappers
 * below do not collect any trap trace data since the syscall hot patch
 * points are reached after trap trace data has already been collected.
 */
#define	BRAND_CALLBACK(callback_id)					    \
	CPU_ADDR(%g2, %g1)		/* load CPU struct addr to %g2	*/ ;\
	ldn	[%g2 + CPU_THREAD], %g3	/* load thread pointer		*/ ;\
	ldn	[%g3 + T_PROCP], %g3	/* get proc pointer		*/ ;\
	ldn	[%g3 + P_BRAND], %g3	/* get brand pointer		*/ ;\
	brz	%g3, 1f			/* No brand?  No callback. 	*/ ;\
	nop 								   ;\
	ldn	[%g3 + B_MACHOPS], %g3	/* get machops list		*/ ;\
	ldn	[%g3 + (callback_id << 3)], %g3 			   ;\
	brz	%g3, 1f							   ;\
	/*								    \
	 * This isn't pretty.  We want a low-latency way for the callback   \
	 * routine to decline to do anything.  We just pass in an address   \
	 * the routine can directly jmp back to, pretending that nothing    \
	 * has happened.						    \
	 * 								    \
	 * %g1: return address (where the brand handler jumps back to)	    \
	 * %g2: address of CPU structure				    \
	 * %g3: address of brand handler (where we will jump to)	    \
	 */								    \
	mov	%pc, %g1						   ;\
	add	%g1, 16, %g1						   ;\
	jmp	%g3							   ;\
	nop								   ;\
1:

	ENTRY_NP(syscall_wrapper32)
	BRAND_CALLBACK(BRAND_CB_SYSCALL32)
	SYSCALL_NOTT(syscall_trap32)
	SET_SIZE(syscall_wrapper32)

	ENTRY_NP(syscall_wrapper)
	BRAND_CALLBACK(BRAND_CB_SYSCALL)
	SYSCALL_NOTT(syscall_trap)
	SET_SIZE(syscall_wrapper)
