/*	$NetBSD: trap.S,v 1.72 2019/04/16 07:08:46 skrll Exp $	*/

/*-
 * Copyright (c) 2002 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Matthew Fredette.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*	$OpenBSD: locore.S,v 1.158 2008/07/28 19:08:46 miod Exp $	*/

/*
 * Copyright (c) 1998-2004 Michael Shalayeff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Portitions of this file are derived from other sources, see
 * the copyrights and acknowledgements below.
 */
/*
 *  (c) Copyright 1988 HEWLETT-PACKARD COMPANY
 *
 *  To anyone who acknowledges that this file is provided "AS IS"
 *  without any express or implied warranty:
 *      permission to use, copy, modify, and distribute this file
 *  for any purpose is hereby granted without fee, provided that
 *  the above copyright notice and this notice appears in all
 *  copies, and that the name of Hewlett-Packard Company not be
 *  used in advertising or publicity pertaining to distribution
 *  of the software without specific, written prior permission.
 *  Hewlett-Packard Company makes no representations about the
 *  suitability of this software for any purpose.
 */
/*
 * Copyright (c) 1990,1991,1992,1994 The University of Utah and
 * the Computer Systems Laboratory (CSL).  All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software is hereby
 * granted provided that (1) source code retains these copyright, permission,
 * and disclaimer notices, and (2) redistributions including binaries
 * reproduce the notices in supporting documentation, and (3) all advertising
 * materials mentioning features or use of this software display the following
 * acknowledgement: ``This product includes software developed by the
 * Computer Systems Laboratory at the University of Utah.''
 *
 * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
 * IS" CONDITION.  THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * CSL requests users of this software to return to csl-dist@cs.utah.edu any
 * improvements that they make and grant CSL redistribution rights.
 *
 *	Utah $Hdr: locore.s 1.63 95/01/20$
 */

#include "opt_multiprocessor.h"
#include "opt_cputype.h"

/*
 * NOTICE: This is not a standalone file.  To use it, #include it in
 * your port's locore.S, like so:
 *
 *	#include <hppa/hppa/trap.S>
 */

	.section .data

	/* Normal stack alignment */
	.align	64
	.export emergency_stack_start, data
emergency_stack_start:
	.block	32768
	.export emergency_stack_end, data
emergency_stack_end:

	.text

/*
 * Kernel Gateway Page (must be at known address)
 *	System Call Gate
 *
 * GATEway instructions have to be at a fixed known locations because their
 * addresses are hard coded in routines such as those in the C library.
 */

	.align	NBPG
	.export	gateway_page, entry
gateway_page:
	nop				/* @ 0.C0000000 (Nothing)  */
	gate,n	bsd_syscall,%r0		/* @ 0.C0000004 (HPUX/BSD) */
	nop				/* @ 0.C0000008 (HPOSF UNIX) */
	nop				/* @ 0.C000000C (HPOSF Mach) */
	nop
	nop
	nop
	nop

bsd_syscall:
	/*
	 * Set up a space register and a protection id so that we can access
	 * kernel memory.
	 */
	mfctl	%eiem, %r1
	mtctl	%r0, %eiem
	mtsp	%r0, %sr1
	mfctl	%pidr1, %ret0
	ldi	HPPA_PID_KERNEL, %t2
	mtctl	%t2, %pidr1

	/*
	 * now call the syscall handler
	 */
	.import syscall_entry,code
	.call
	ldil	L%syscall_entry, %t2
	be	R%syscall_entry(%sr1, %t2)
	nop ! nop ! nop ! nop
	.size	gateway_page, .-gateway_page

	.align	NBPG
	.export	gateway_page_end, entry
gateway_page_end:

	.export syscall_entry,entry
	.proc
	.callinfo calls
	.entry
syscall_entry:
	/*
	 * %r1:		eiem
	 * %ret0:	process protection id
	 * %t1:		syscall number
	 * %sr0, %r31:	return address
	 * %sp:		user stack
	 *
	 */

	/* t2 = curlwp PCB */
	GET_CURLWP_SPACE(%sr1, %t3)
	ldw	L_PCB(%sr1, %t3), %t2			/* XXX can use ,sl */

	/*
	 * NB: Even though t4 is a caller-saved register, we save it anyways, as
	 * a convenience to __vfork14 and any other syscalls that absolutely
	 * must have a register that is saved for it.
	 */

	/* calculate kernel sp, load, create kernel stack frame */
	ldo	NBPG+TRAPFRAME_SIZEOF(%t2), %t3		/* see cpu_lwp_fork */

	stw	%t4, TF_R19-TRAPFRAME_SIZEOF(%sr1, %t3)	/* t4 for vfork */
	stw	%t1, TF_R22-TRAPFRAME_SIZEOF(%sr1, %t3)	/* syscall # */
	copy	%sp, %t4

	/* gotta save the args, in case we gonna restart */
	stw	%arg3, TF_R23 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg2, TF_R24 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg1, TF_R25 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg0, TF_R26 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r27, TF_R27 -TRAPFRAME_SIZEOF(%sr1, %t3)	/* dp */
	stw	%sp, TF_R30 -TRAPFRAME_SIZEOF(%sr1, %t3)	/* user stack */
	copy	%t3, %sp
	/*
	 * Make space for the syscall arguments.
	 *
	 * Match the offset from %sp to the trapframe with the offset in
	 * TLABEL(all) for the benefit of ddb.
	 */
	stwm	%r0, HPPA_FRAME_SIZE+HPPA_FRAME_MAXARGS(%sr1, %sp)

	/* Align correctly */
	ldo	HPPA_FRAME_SIZE-1(%sp),%sp
	depi	0, 31, 6, %sp

	stw	%r0, HPPA_FRAME_CRP(%sr1, %sp)

	GET_CURCPU_SPACE(%sr1, %t1)
	ldw	CI_PSW(%sr1, %t1), %t1
	stw	%r1, TF_CR15-TRAPFRAME_SIZEOF(%sr1, %t3)	/* eiem ,bc (block copy cache control hint) */
	stw	%t1, TF_CR22-TRAPFRAME_SIZEOF(%sr1, %t3)	/* ipsw */

	mfsp	%sr3, %t1
	stw	%t1, TF_SR3-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%ret0, TF_CR8-TRAPFRAME_SIZEOF(%sr1, %t3)	/* pidr1 */

	/* now we can allow interrupts to happen */
	mtctl	%r1, %eiem

	/*
	 * Normally, we only have to save the caller-saved registers, because
	 * the callee-saved registers will be naturally saved and restored by
	 * our callee(s).  However, see the longer comment in the trap handling
	 * code below for the reasons why we need to save and restore all of
	 * them.
	 */
	stw	%r2 , TF_R2 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r3 , TF_R3 -TRAPFRAME_SIZEOF(%sr1, %t3)

	/* We can now set the frame pointer */
	copy	%t3, %r3

	stw	%r4 , TF_R4 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r5 , TF_R5 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r6 , TF_R6 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r7 , TF_R7 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r8 , TF_R8 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r9 , TF_R9 -TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r10, TF_R10-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r11, TF_R11-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r12, TF_R12-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r13, TF_R13-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r14, TF_R14-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r15, TF_R15-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r16, TF_R16-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r17, TF_R17-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r18, TF_R18-TRAPFRAME_SIZEOF(%sr1, %t3)

	stw	%r0, 0(%sr1, %t3)	/* terminate frame */
	copy	%r0 , %r3
	stw	%r0, HPPA_FRAME_PSP(%sr1, %sp)
	stw	%r0, HPPA_FRAME_CRP(%sr1, %sp)

	/*
	 * Copy Arguments
	 *
	 * Unfortunately mmap() under bsd requires 7 words; linux is confined to
	 * 5, and hpux to 6.  Assuming the `long' syscall it gives us the
	 * maximum 9 words, which very much overkill for an average of 3.  We
	 * keep it at 10, since bundling will keep it at the same speed as 9
	 * anyway.
	 */
	/*
	 * XXX fredette - possible security hole here.
	 * What happens if the user hands us a stack
	 * that points to nowhere, or to data that they
	 * should not be reading?
	 */
	stw	%arg0, 1*4(%sr1, %t3)	/* XXX can use ,bc */
	stw	%arg1, 2*4(%sr1, %t3)
	stw	%arg2, 3*4(%sr1, %t3)
	stw	%arg3, 4*4(%sr1, %t3)
	ldw	HPPA_FRAME_ARG( 4)(%t4), %arg0
	ldw	HPPA_FRAME_ARG( 5)(%t4), %arg1
	ldw	HPPA_FRAME_ARG( 6)(%t4), %arg2
	ldw	HPPA_FRAME_ARG( 7)(%t4), %arg3
	stw	%arg0, 5*4(%sr1, %t3)
	stw	%arg1, 6*4(%sr1, %t3)
	stw	%arg2, 7*4(%sr1, %t3)
	stw	%arg3, 8*4(%sr1, %t3)
	ldw	HPPA_FRAME_ARG( 8)(%t4), %arg0
	ldw	HPPA_FRAME_ARG( 9)(%t4), %arg1
	stw	%arg0, 9*4(%sr1, %t3)
	stw	%arg1,10*4(%sr1, %t3)

	/*
	 * Save the rest of the CPU context
	 */

	/* XXXNH: Should do a be 0(%sr1, %r31) instead of rfi when possible */
	ldo	4(%r31), %arg1
	stw	%r31, TF_IIOQH-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg1, TF_IIOQT-TRAPFRAME_SIZEOF(%sr1, %t3)

	mfsp	%sr0, %arg0
	stw	%arg0, TF_IISQH-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg0, TF_IISQT-TRAPFRAME_SIZEOF(%sr1, %t3)

	stw	%arg0, TF_CR20-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r31, TF_CR21-TRAPFRAME_SIZEOF(%sr1, %t3)

	ldil	L%(TFF_LAST|TFF_SYS), %arg1
	stw	%r0, TF_CR19-TRAPFRAME_SIZEOF(%sr1, %t3)	/* iir */
	stw	%arg1, TF_FLAGS-TRAPFRAME_SIZEOF(%sr1, %t3)

	/* Already done above: mfsp	%sr0, %arg0 */
	mfsp	%sr2, %arg2
	mfsp	%sr4, %arg3
	stw	%arg0, TF_SR0-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg0, TF_SR1-TRAPFRAME_SIZEOF(%sr1, %t3)
					/* we overwrote sr1 earlier */
	stw	%arg2, TF_SR2-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg3, TF_SR4-TRAPFRAME_SIZEOF(%sr1, %t3)

	mfsp	%sr5, %arg0
	mfsp	%sr6, %arg1
	mfsp	%sr7, %arg2
	mfctl	%pidr2, %arg3
	stw	%arg0, TF_SR5-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg1, TF_SR6-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg2, TF_SR7-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg3, TF_CR9-TRAPFRAME_SIZEOF(%sr1, %t3)

#if pbably_not_worth_it
	mfctl	%pidr3, %arg2
	mfctl	%pidr4, %arg3
	stw	%arg2, TF_CR12-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg3, TF_CR13-TRAPFRAME_SIZEOF(%sr1, %t3)
#endif

	mfctl	CR_TLS, %arg0
	stw	%arg0, TF_CR27-TRAPFRAME_SIZEOF(%sr1, %t3)

#if defined(DDB) || defined(KGDB)
	/*
	 * Save v2p translation table pointer
	 */
	mfctl	%eirr, %arg0
	mfctl	CR_VTOP, %arg1
	stw	%arg0, TF_CR23-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg1, TF_CR25-TRAPFRAME_SIZEOF(%sr1, %t3)

	mfctl	%cr24, %arg0
	mfctl	%cr28, %arg1
	stw	%arg0, TF_CR24-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg1, TF_CR28-TRAPFRAME_SIZEOF(%sr1, %t3)
#endif

	/* setup kernel context */
	mtsp	%r0, %sr0
	mtsp	%r0, %sr1
	mtsp	%r0, %sr2
	mtsp	%r0, %sr3
	mtsp	%r0, %sr4
	mtsp	%r0, %sr5
	mtsp	%r0, %sr6
	mtsp	%r0, %sr7

	ldo	-TRAPFRAME_SIZEOF(%t3), %arg0
	ldo	4(%t3), %arg1

	ldil	L%$global$,%dp
	ldo	R%$global$(%dp),%dp

	/* do a syscall */
	.import	syscall,code
	CALL(syscall, %r1)

	/* load curlwp's trapframe pointer */
	GET_CURLWP(%r1)
	ldw	L_MD(%r1), %t3

	.exit
	.procend
	/* FALLTHROUGH */

	.export	syscall_return, entry
	.proc
	.callinfo no_calls
	.entry
syscall_return:
	/* t3 == VA trapframe */

	/* disable interrupts, just in case */
	mtctl	%r0, %eiem

	/*
	 * Copy the `phys' part of the frame into CPU local temporary store (see
	 * a note for trapall).  Hopefully no page fault would happen on or after
	 * the copy, and interrupts are disabled.
	 */
	GET_CURCPU(%t2)
	ldo	CI_TRAPSAVE(%t2), %t2

	ldw  0(%t3), %r1 ! ldw  4(%t3), %t1 ! stw %r1,  0(%t2) ! stw %t1,  4(%t2)
	ldw  8(%t3), %r1 ! ldw 12(%t3), %t1 ! stw %r1,  8(%t2) ! stw %t1, 12(%t2)
	ldw 16(%t3), %r1 ! ldw 20(%t3), %t1 ! stw %r1, 16(%t2) ! stw %t1, 20(%t2)
	ldw 24(%t3), %r1 ! ldw 28(%t3), %t1 ! stw %r1, 24(%t2) ! stw %t1, 28(%t2)
	ldw 32(%t3), %r1 ! ldw 36(%t3), %t1 ! stw %r1, 32(%t2) ! stw %t1, 36(%t2)
	ldw 40(%t3), %r1 ! ldw 44(%t3), %t1 ! stw %r1, 40(%t2) ! stw %t1, 44(%t2)
	ldw 48(%t3), %r1 ! ldw 52(%t3), %t1 ! stw %r1, 48(%t2) ! stw %t1, 52(%t2)
	ldw 56(%t3), %r1 ! ldw 60(%t3), %t1 ! stw %r1, 56(%t2) ! stw %t1, 60(%t2)

	/* 1b. restore most of the general registers */
	ldw	TF_CR11(%t3), %t1
	mtctl	%t1, %sar
	ldw	TF_R1(%t3), %r1
	ldw	TF_R2(%t3), %r2
	ldw	TF_R3(%t3), %r3

	/*
	 * See the comment in the trap handling code below about why we need to
	 * save and restore all general registers under these cases.
	 */
	ldw	TF_R4(%t3), %r4
	ldw	TF_R5(%t3), %r5
	ldw	TF_R6(%t3), %r6
	ldw	TF_R7(%t3), %r7
	ldw	TF_R8(%t3), %r8
	ldw	TF_R9(%t3), %r9
	ldw	TF_R10(%t3), %r10
	ldw	TF_R11(%t3), %r11
	ldw	TF_R12(%t3), %r12
	ldw	TF_R13(%t3), %r13
	ldw	TF_R14(%t3), %r14
	ldw	TF_R15(%t3), %r15
	ldw	TF_R16(%t3), %r16
	ldw	TF_R17(%t3), %r17
	ldw	TF_R18(%t3), %r18

	ldw	TF_R19(%t3), %t4
	/*	%r20(%t3) is used as a temporary and will be restored later */
	/*	%r21(%t2) is used as a temporary and will be restored later */
	/*	%r22(%t1) is used as a temporary and will be restored later */
	ldw	TF_R23(%t3), %r23
	ldw	TF_R24(%t3), %r24
	ldw	TF_R25(%t3), %r25
	ldw	TF_R26(%t3), %r26
	ldw	TF_R27(%t3), %r27
	ldw	TF_R28(%t3), %r28
	ldw	TF_R29(%t3), %r29
	/*	%r30 (%sp) will be restored later */
	ldw	TF_R31(%t3), %r31

	/* 2. restore all the space regs and pid regs, except sr3, pidr1 */
	ldw	TF_SR0(%t3), %t1
	ldw	TF_SR1(%t3), %t2
	mtsp	%t1, %sr0
	mtsp	%t2, %sr1

	ldw	TF_SR2(%sr3, %t3), %t1
	ldw	TF_SR4(%sr3, %t3), %t2
	mtsp	%t1, %sr2
	mtsp	%t2, %sr4

	ldw	TF_SR5(%sr3, %t3), %t1
	ldw	TF_SR6(%sr3, %t3), %t2
	mtsp	%t1, %sr5
	mtsp	%t2, %sr6

	ldw	TF_SR7(%sr3, %t3), %t1
	ldw	TF_CR9(%sr3, %t3), %t2
	mtsp	%t1, %sr7
	mtctl	%t2, %pidr2

#if pbably_not_worth_it
	ldw	TF_CR12(%sr3, %t3), %t1
	ldw	TF_CR13(%sr3, %t3), %t2
	mtctl	%t1, %pidr3
	mtctl	%t2, %pidr4
#endif
	ldw	TF_CR27(%sr3, %t3), %t1
	ldw	TF_CR30(%sr3, %t3), %t2
	mtctl	%t1, CR_TLS
	mtctl	%t2, CR_FPPADDR

	ldw	TF_CR0(%sr3, %t3), %t1
	mtctl	%t1, CR_RCTR

	GET_CURCPU_SPACE(%sr3, %t3)

	/*
	 * Clear the system mask, this puts us back into physical mode.  Reload
	 * the trapframe pointer with the correspondent PA value.  %sp will be
	 * left in virtual until restored from trapframe, since we don't use it
	 * anyway.
	 */
	ssm	0, %r0
	ldo	CI_TRAPSAVE(%t3), %t3
	nop ! nop ! nop ! nop ! nop ! nop
	rsm	RESET_PSW, %r0

	/* finally we can restore the space and offset queues and the ipsw */
	ldw	TF_IISQH(%t3), %t1
	ldw	TF_IISQT(%t3), %t2
	mtctl	%t1, %pcsq
	mtctl	%t2, %pcsq

	ldw	TF_IIOQH(%t3), %t1
	ldw	TF_IIOQT(%t3), %t2
	mtctl	%t1, %pcoq
	mtctl	%t2, %pcoq

	ldw	TF_CR15(%t3), %t1
	ldw	TF_CR22(%t3), %t2
	mtctl	%t1, %eiem
	mtctl	%t2, %ipsw

	ldw	TF_SR3(%t3), %t1
	ldw	TF_CR8(%t3), %t2
	mtsp	%t1, %sr3
	mtctl	%t2, %pidr1

	ldw	TF_R22(%t3), %t1
	ldw	TF_R21(%t3), %t2
	ldw	TF_R30(%t3), %sp
	ldw	TF_R20(%t3), %t3

	rfi
	nop
	.exit
	.procend
	.size	syscall_entry, .- syscall_entry

/*
 * interrupt vector table
 */
#define	TLABEL(name)		__CONCAT(trap_,name)
#define TRAPLABEL(name,num)	__CONCAT(TLABEL(name),num)
#define	TELABEL(num)		__CONCAT(trap_ep_,num)

#define TRAP(name,num) \
	mtctl	%r1, %tr7		! \
	.call				! \
	.import TLABEL(name), code	! \
	b	TLABEL(name)		! \
	ldi	num, %r1		! \
	.align	32

#define	ATRAP(name,num) \
	.export	TRAPLABEL(name,num), entry	! \
	.label	TRAPLABEL(name,num)		! \
	TRAP(all,num)				! \
	.size	TRAPLABEL(name,num), .-TRAPLABEL(name,num)

#define	CTRAP(name,num,pre) \
	.export	TRAPLABEL(name,num), entry	! \
	.label	TRAPLABEL(name,num)		! \
	pre				! \
	TRAP(name,num)			! \
	.size	TRAPLABEL(name,num), .-TRAPLABEL(name,num)

#define	STRAP(name,num,pre) \
	.export	TRAPLABEL(name,num), entry	! \
	.label	TRAPLABEL(name,num)		! \
	pre				! \
	mtctl	%r1, %tr7		! \
	.export	TELABEL(num), entry	! \
	.label	TELABEL(num)		! \
	ldil	0,%r1			! \
	ldo	0(%r1), %r1		! \
	.call				! \
	bv	0(%r1)			! \
	ldi	num, %r1		! \
	.align	32			! \
	.size	TRAPLABEL(name,num), .-TRAPLABEL(name,num)

#define	LDILDO(name)			! \
	.export	name, entry		! \
	.label	name			! \
	ldil	L%TLABEL(name),%r1	! \
	ldo	R%TLABEL(name)(%r1), %r1

#ifdef HP7000_CPU
LDILDO(itlb_x)
LDILDO(itlbna_x)
LDILDO(dtlb_x)
LDILDO(dtlbna_x)
LDILDO(tlbd_x)

LDILDO(itlb_s)
LDILDO(itlbna_s)
LDILDO(dtlb_s)
LDILDO(dtlbna_s)
LDILDO(tlbd_s)
#endif

#if defined(HP7100_CPU) || defined(HP7200_CPU)
LDILDO(itlb_t)
LDILDO(itlbna_t)
LDILDO(dtlb_t)
LDILDO(dtlbna_t)
LDILDO(tlbd_t)
#endif

#if defined(HP7100LC_CPU) || defined(HP7300LC_CPU)
LDILDO(itlb_l)
LDILDO(itlbna_l)
LDILDO(dtlb_l)
LDILDO(dtlbna_l)
LDILDO(tlbd_l)
#endif

#if defined(HP8000_CPU) || defined(HP8200_CPU) || \
    defined(HP8500_CPU) || defined(HP8600_CPU) || \
    defined(HP8700_CPU)
LDILDO(itlb_u)
LDILDO(itlbna_u)
LDILDO(dtlb_u)
LDILDO(dtlbna_u)
LDILDO(tlbd_u)
#endif

#define	ITLBPRE \
	mfctl	%pcoq,%r9		/* Offset */			! \
	mfctl	%pcsq,%r8		/* Space  */

#define	DTLBPRE \
	mfctl	%ior, %r9		/* Offset */			! \
	mfctl	%isr, %r8		/* Space  */

#define	HPMCPRE	\
	nop

	.align NBPG
	.export ivaaddr, entry
	.export os_hpmc, entry
ivaaddr:
	ATRAP(null,T_NONEXIST)		/*  0. invalid interrupt vector */
os_hpmc:
	CTRAP(hpmc,T_HPMC,HPMCPRE)	/*  1. high priority machine check */
	ATRAP(power,T_POWERFAIL)	/*  2. power failure */
	ATRAP(recnt,T_RECOVERY)		/*  3. recovery counter trap */
	ATRAP(intr,T_INTERRUPT)		/*  4. external interrupt */
	ATRAP(lpmc,T_LPMC)		/*  5. low-priority machine check */
	STRAP(itlb,T_ITLBMISS,ITLBPRE)	/*  6. instruction TLB miss fault */
	ATRAP(iprot,T_IPROT)		/*  7. instruction protection trap */
	ATRAP(ill,T_ILLEGAL)		/*  8. Illegal instruction trap */
	CTRAP(ibrk,T_IBREAK,)		/*  9. break instruction trap */
	ATRAP(privop,T_PRIV_OP)		/* 10. privileged operation trap */
	ATRAP(privr,T_PRIV_REG)		/* 11. privileged register trap */
	ATRAP(ovrfl,T_OVERFLOW)		/* 12. overflow trap */
	ATRAP(cond,T_CONDITION)		/* 13. conditional trap */
	ATRAP(excpt,T_EXCEPTION)	/* 14. assist exception trap */
	STRAP(dtlb,T_DTLBMISS,DTLBPRE)	/* 15. data TLB miss fault */
	STRAP(itlbna,T_ITLBMISSNA,DTLBPRE)/* 16. ITLB non-access miss fault */
	STRAP(dtlbna,T_DTLBMISSNA,DTLBPRE)/* 17. DTLB non-access miss fault */
	ATRAP(dprot,T_DPROT)		/* 18. data protection trap
					       unaligned data reference trap */
	ATRAP(dbrk,T_DBREAK)		/* 19. data break trap */
	STRAP(tlbd,T_TLB_DIRTY,DTLBPRE)	/* 20. TLB dirty bit trap */
	ATRAP(pgref,T_PAGEREF)		/* 21. page reference trap */
	CTRAP(emu,T_EMULATION,)		/* 22. assist emulation trap */
	ATRAP(hpl,T_HIGHERPL)		/* 23. higher-privelege transfer trap*/
	ATRAP(lpl,T_LOWERPL)		/* 24. lower-privilege transfer trap */
	ATRAP(tknbr,T_TAKENBR)		/* 25. taken branch trap */
	ATRAP(dacc,T_DATACC)		/* 26. data access rights trap */
	ATRAP(dpid,T_DATAPID)		/* 27. data protection ID trap */
	ATRAP(dalgn,T_DATALIGN)		/* 28. unaligned data ref trap */
	ATRAP(unk,29)
	ATRAP(unk,30)
	ATRAP(unk,31)
	ATRAP(unk,32)
	ATRAP(unk,33)
	ATRAP(unk,34)
	ATRAP(unk,35)
	ATRAP(unk,36)
	ATRAP(unk,37)
	ATRAP(unk,38)
	ATRAP(unk,39)
	ATRAP(unk,40)
	ATRAP(unk,41)
	ATRAP(unk,42)
	ATRAP(unk,43)
	ATRAP(unk,44)
	ATRAP(unk,45)
	ATRAP(unk,46)
	ATRAP(unk,47)
	ATRAP(unk,48)
	ATRAP(unk,49)
	ATRAP(unk,50)
	ATRAP(unk,51)
	ATRAP(unk,52)
	ATRAP(unk,53)
	ATRAP(unk,54)
	ATRAP(unk,55)
	ATRAP(unk,56)
	ATRAP(unk,57)
	ATRAP(unk,58)
	ATRAP(unk,59)
	ATRAP(unk,60)
	ATRAP(unk,61)
	ATRAP(unk,62)
	ATRAP(unk,63)
					/* 64 */
	.size	ivaaddr, .-ivaaddr

/*
 * This is the locore support for HPMC and TOC machine checks.  In the HPMC
 * case, this is a continuation of the HPMC handler that begins in the interrupt
 * vector table.  In the TOC case, this is the handler installed in page zero.
 *
 * Notable points about the CPU state for the OS_TOC handler:
 *
 * - The PSW Q bit is 1, all other PSW bits are 0.
 * - CR14 (IVA) does not point to our vector table.
 * - CR22 (IPSW) is valid.
 * - All other control registers HVERSION dependent.
 * - The TLB is initialized and invalid.
 *
 * Notable points about the CPU state for the OS_HPMC handler:
 *
 * - The PSW M bit is 1, all other PSW bits are 0.
 * - CR14 (IVA) does point to our vector table.
 * - CR22 (IPSW) is valid.
 * - All other control registers HVERSION dependent.
 * - The TLB is unchanged.
 *
 * The TOC CPU state is actually trickier.  Whereas in the HPMC case, we can
 * return to virtual mode right away, in the TOC case we can't return to virtual
 * mode until the kernel mapping is reloaded into the BTLB.
 *
 * Otherwise, we set up the kernel context, move onto the emergency stack, and
 * call hppa_machine_check.
 */
ENTRY_NOPROFILE(os_toc, 0)
	/* This loads %arg0 and nullifies the next instruction. */
	addi,tr	T_INTERRUPT, %r0, %arg0
EXIT(os_toc)
ENTRY_NOPROFILE(TLABEL(hpmc),0)
ALTENTRY(os_hpmc_cont)
	ldi	T_HPMC, %arg0

	/* Disable interrupts. */
	mtctl	%r0, %eiem

	/* Load protection and space registers for the kernel. */
	ldi	HPPA_PID_KERNEL, %r1
	mtctl	%r1, %pidr1
	ldi	HPPA_SID_KERNEL, %r1
	mtsp	%r1, %sr0
	mtsp	%r1, %sr1
	mtsp	%r1, %sr2
	mtsp	%r1, %sr3
	mtsp	%r1, %sr4
	mtsp	%r1, %sr5
	mtsp	%r1, %sr6
	mtsp	%r1, %sr7

	/* Reload the Interruption Vector Address. */
	ldil	L%ivaaddr, %r1
	ldo	R%ivaaddr(%r1), %r1
	mtctl	%r1, %iva

	/* Reload the HPT base and mask. */
	ldil	L%hppa_vtop, %r1
	ldw	R%hppa_vtop(%r1), %r1
	mtctl	%r1, CR_VTOP

	/* Disable interrupts for the long haul. */
	GET_CURCPU(%t1)
	ldw	CI_PSW(%t1), %r1
	depi	0, PSW_I_POS, 1, %r1
	stw	%r1, CI_PSW(%t1)

	/* Reload the global data pointer. */
	ldil	L%$global$, %dp
	ldo	R%$global$(%dp), %dp

	/* Move onto the emergency stack. */
	ldil	L%emergency_stack_start, %sp
	ldo	R%emergency_stack_start(%sp), %sp
	stwm	%r0, HPPA_FRAME_SIZE(%sp)
	copy	%sp, %r3

	/* Start stack calling convention. */
	stw	%r0, HPPA_FRAME_CRP(%sp)
	stw	%r0, HPPA_FRAME_PSP(%sp)
	copy	%r3, %r1
	copy	%sp, %r3
	stwm	%r1, HPPA_FRAME_SIZE(%sp)

	/* If this is a TOC, remap the kernel. */
	comib,<>,n T_INTERRUPT, %arg0, L$check_do_rfi

	/* Clear kernelmapped. */
	ldil	L%kernelmapped, %r1
	stw	%r0, R%kernelmapped(%r1)

	/* Call hppa_btlb_reload. */
	ldil	L%hppa_btlb_reload, %r1
	ldo	R%hppa_btlb_reload(%r1), %r1
	blr	0, %rp
	bv	%r0(%r1)
	nop

	/* Set kernelmapped. */
	ldil	L%kernelmapped, %r1
	stw	%r1, R%kernelmapped(%r1)

	/* Reload %arg0 (it may have been destroyed). */
	ldi	T_INTERRUPT, %arg0

	/* Disable the interrupt queues. */
	rsm	RESET_PSW, %r0

L$check_do_rfi:

	/* Load IPSW. */
	GET_CURCPU(%r1)
	ldw	CI_PSW(%r1), %r1
	mtctl	%r1, %ipsw

	/* Get the address of hppa_machine_check. */
	ldil	L%hppa_machine_check, %r1
	ldo	R%hppa_machine_check(%r1), %r1

	/* Load the instruction address queues. */
	mtctl	%r1, %pcoq
	ldo	4(%r1), %r1
	mtctl	%r1, %pcoq
	ldi	HPPA_SID_KERNEL, %r1
	mtctl	%r1, %pcsq
	mtctl	%r1, %pcsq

	blr	0, %rp
	rfi
	nop
	nop
	nop
	nop
	nop
	nop
ALTENTRY(os_hpmc_cont_end)
	nop
ALTENTRY(os_toc_end)
EXIT(TLABEL(hpmc))

/*
 * This handles all assist emulation traps.  We break these down into three
 * categories and dispatch accordingly. The categories are:
 *
 *  - emulate special function unit,
 *  - emulate non-FPU coprocessor, and
 *  - emulate FPU coprocessor.
 *
 */
	.export TLABEL(emu), entry
LEAF_ENTRY_NOPROFILE(TLABEL(emu))

	/*
	 * Save %arg0 and load it with the instruction that caused the emulation
	 * trap.
	 */
	mtctl	%arg0, %tr2
	mfctl	%iir, %arg0

	/*
	 * If the opcode field in the instruction is 4, indicating a special
	 * function unit SPOP instruction, branch to emulate an sfu.  If the
	 * opcode field is 0xe, then it's an FPU instruction.
	 */
	extru	%arg0, 5, 6, %r1
	comib,=,n 4, %r1, L$emulate_sfu
	comib,=,n 0xe, %r1, hppa_fpu_emulate

	/*
	 * If the uid field in the instruction is not zero or one, indicating a
	 * coprocessor other than an FPU, branch to emulate a non-FPU
	 * coprocessor.
	 */
	extru	%arg0, 25, 3, %r1
	comib,<<,n 1, %r1, L$emulate_coproc

	/*
	 * If we're still here, this is a FPU coprocessor instruction.  That we
	 * trapped to emulate it means one of two things.
	 *
	 * If we do have a hardware FPU but it is disabled, we trapped because
	 * the current process' state is not loaded into the FPU.  We load that
	 * state in, possibly swapping out another process' state first.
	 *
	 * If we do have a hardware FPU and it is enabled, we trapped because of
	 * an instruction that isn't supported by this FPU, and so we need to
	 * emulate it.
	 */

hppa_fpu_emulate:

	/*
	 * We have a hardware FPU.  If it is enabled, branch to emulate the
	 * instruction.
	 */
	mfctl	CR_CCR, %arg0
	extru,= %arg0, 25, 2, %r1
	b,n	L$emulate_fpu

	/*
	 * The hardware FPU is disabled, so we need to swap in the FPU state of
	 * the LWP whose uspace physical address in CR_FPPADDR.  We may also
	 * need to swap out the FPU state of any LWP whose uspace physical
	 * address is in curcpu()->ci_fpu_state.
	 */

	/*
	 * So far, the CTRAP() macro has saved %r1 in %tr7, and the dispatching
	 * above has saved %arg0 in tr2.  Save the other registers that we want
	 * to use.  hppa_fpu_swap deliberately uses only these registers and %r1
	 * and %arg0.
	 */
	mtctl	%arg1, %tr4
	mtctl	%rp, %tr5

	/* Call hppa_fpu_swap. */
	GET_CURCPU(%arg0)
	ldw	CI_FPU_STATE(%arg0), %arg0
	mfctl	CR_FPPADDR, %arg1
	blr	0, %rp
	b	hppa_fpu_swap
	nop

	/* Restore registers and rfi. */
	mfctl	%tr5, %rp
	mfctl	%tr4, %arg1
	mfctl	%tr2, %arg0
	mfctl	%tr7, %r1
	rfi
	nop

	/*
	 * We branch here to emulate a special function unit instruction.  On
	 * entry, %r1 is saved in %tr7 (courtesy of CTRAP), and %arg0 is saved
	 * in %tr2 (courtesy of the sfu/coprocessor dispatcher).
	 */
L$emulate_sfu:
	/*
	 * Currently we just restore %arg0 and trap with an illegal instruction.
	 */
	mfctl	%tr2, %arg0
	b	TLABEL(all)
	ldi	T_ILLEGAL, %r1

	/*
	 * We branch here to emulate a non-FPU coprocessor instruction.  On
	 * entry, %r1 is saved in %tr7 (courtesy of CTRAP), and %t1 is saved in
	 * %tr2 (courtesy of the sfu/coprocessor dispatcher).
	 */
L$emulate_coproc:
	/*
	 * Currently we just restore %arg0 and trap with an illegal instruction.
	 */
	mfctl	%tr2, %arg0
	b	TLABEL(all)
	ldi	T_ILLEGAL, %r1

	/*
	 * We branch here to emulate an FPU coprocessor instruction.  On entry,
	 * %r1 is saved in %tr7 (courtesy of CTRAP), and %t1 is saved in %tr2
	 * (courtesy of the sfu/coprocessor dispatcher).
	 */
L$emulate_fpu:
	/*
	 * We get back to C via the normal generic trap mechanism, as opposed to
	 * switching to a special stack, setting up a trapframe, etc. ourselves,
	 * for three reasons.
	 *
	 * One, I want to turn interrupts back on, since the emulation code
	 * might not be fast.  Two, because the instruction to emulate might be
	 * a load or a store, I need to turn address translation back on (i.e.,
	 * return to virtual mode.)  Third, doing both of those plus setting up
	 * a trapframe is a pain, and the generic trap handling already does it
	 * all.
	 *
	 * To relieve trap() from having to check for sfu and non-FPU inst-
	 * ructions again, it assumes that these kinds of instructions have
	 * already been translated into some other trap type (as they have, by
	 * the above L$emulate_sfu and L$emulate_coproc), and all
	 * T_EMULATION | T_USER traps are FPU instructions that need emulating.
	 *
	 * So we just restore %arg0 and trap with T_EMULATION.
	 */
	mfctl	%tr2, %arg0
	b	TLABEL(all)
	ldi	T_EMULATION, %r1
EXIT(TLABEL(emu))

/*
 * void hppa_fpu_swapout(struct pcb *out);
 * void hppa_fpu_swap(struct fpreg *out, struct fpreg *in);
 */
LEAF_ENTRY_NOPROFILE(hppa_fpu_swapout)
	ldw	PCB_FPREGS(%arg0), %arg0
	copy	%r0, %arg1

ALTENTRY(hppa_fpu_swap)

	/*
	 * Note that this function must work in physical mode as well as virtual
	 * mode, because it can be called by a trap handler.  This also further
	 * restricts the registers we can use.  We can only use %arg0, %arg1,
	 * and %r1.
	 */

	/*
	 * Assuming that out and in aren't both NULL, we will have to run co-
	 * processor instructions, so we'd better enable it.
	 *
	 * Also, branch if there's no FPU state to swap out.
	 */
	mfctl	CR_CCR, %r1
	depi	3, 25, 2, %r1
	comb,=	%r0, %arg0, L$fpu_swap_in
	mtctl	%r1, CR_CCR

	/*
	 * Swap out the current FPU state.
	 */
	fstds,ma %fr0 , 8(%arg0)	/* fr0 must be saved first */
	fstds,ma %fr1 , 8(%arg0)
	fstds,ma %fr2 , 8(%arg0)
	fstds,ma %fr3 , 8(%arg0)
	fstds,ma %fr4 , 8(%arg0)
	fstds,ma %fr5 , 8(%arg0)
	fstds,ma %fr6 , 8(%arg0)
	fstds,ma %fr7 , 8(%arg0)
	fstds,ma %fr8 , 8(%arg0)
	fstds,ma %fr9 , 8(%arg0)
	fstds,ma %fr10, 8(%arg0)
	fstds,ma %fr11, 8(%arg0)
	fstds,ma %fr12, 8(%arg0)
	fstds,ma %fr13, 8(%arg0)
	fstds,ma %fr14, 8(%arg0)
	fstds,ma %fr15, 8(%arg0)
	fstds,ma %fr16, 8(%arg0)
	fstds,ma %fr17, 8(%arg0)
	fstds,ma %fr18, 8(%arg0)
	fstds,ma %fr19, 8(%arg0)
	fstds,ma %fr20, 8(%arg0)
	fstds,ma %fr21, 8(%arg0)
	fstds,ma %fr22, 8(%arg0)
	fstds,ma %fr23, 8(%arg0)
	fstds,ma %fr24, 8(%arg0)
	fstds,ma %fr25, 8(%arg0)
	fstds,ma %fr26, 8(%arg0)
	fstds,ma %fr27, 8(%arg0)
	fstds,ma %fr28, 8(%arg0)
	fstds,ma %fr29, 8(%arg0)
	fstds,ma %fr30, 8(%arg0)
	fstds    %fr31, 0(%arg0)

L$fpu_swap_in:

	/*
	 * Stash the incoming user structure in curcpu->ci_fpu_state.  Because
	 * this variable holds a physical address, this means that hppa_fpu_swap
	 * can only be called with a non-zero user_in from physical mode (i.e.,
	 * from the emulation assist trap handler).  And that's exactly what
	 * happens now.
	 *
	 * So stash ci->ci_fpu_state, branching past the swap-in code if it is
	 * zero.
	 */
	GET_CURCPU(%r1)
	comb,=	%r0, %arg1, L$fpu_no_swap_in
	stw	%arg1, CI_FPU_STATE(%r1)

	/*
	 * Swap in the new FPU state.
	 */
	ldo	31*8(%arg1), %arg1
	fldds,ma -8(%arg1), %fr31
	fldds,ma -8(%arg1), %fr30
	fldds,ma -8(%arg1), %fr29
	fldds,ma -8(%arg1), %fr28
	fldds,ma -8(%arg1), %fr27
	fldds,ma -8(%arg1), %fr26
	fldds,ma -8(%arg1), %fr25
	fldds,ma -8(%arg1), %fr24
	fldds,ma -8(%arg1), %fr23
	fldds,ma -8(%arg1), %fr22
	fldds,ma -8(%arg1), %fr21
	fldds,ma -8(%arg1), %fr20
	fldds,ma -8(%arg1), %fr19
	fldds,ma -8(%arg1), %fr18
	fldds,ma -8(%arg1), %fr17
	fldds,ma -8(%arg1), %fr16
	fldds,ma -8(%arg1), %fr15
	fldds,ma -8(%arg1), %fr14
	fldds,ma -8(%arg1), %fr13
	fldds,ma -8(%arg1), %fr12
	fldds,ma -8(%arg1), %fr11
	fldds,ma -8(%arg1), %fr10
	fldds,ma -8(%arg1), %fr9
	fldds,ma -8(%arg1), %fr8
	fldds,ma -8(%arg1), %fr7
	fldds,ma -8(%arg1), %fr6
	fldds,ma -8(%arg1), %fr5
	fldds,ma -8(%arg1), %fr4
	fldds,ma -8(%arg1), %fr3
	fldds,ma -8(%arg1), %fr2
	fldds,ma -8(%arg1), %fr1
	fldds     0(%arg1), %fr0	/* fr0 must be restored last */

L$fpu_swap_done:

	/* Increment the switch count and return. */
	ldil	L%fpu_csw, %r1
	ldw	R%fpu_csw(%r1), %arg0
	ldo	1(%arg0), %arg0
	bv	%r0(%rp)
	stw	%arg0, R%fpu_csw(%r1)

L$fpu_no_swap_in:

	/* We didn't swap any FPU state in, so disable the FPU. */
	mfctl	CR_CCR, %r1
	depi	0, 25, 2, %r1
	b	L$fpu_swap_done
	mtctl	%r1, CR_CCR
EXIT(hppa_fpu_swapout)

	/* Construct the virtual address tag. */
	/* NB: it is legal for off and t to be the same. */
#define VTAG(sp,off,t) \
	shd	%r0, off, 1, t		/* t[1..15] = off[0..14] */	! \
	dep	sp, 31, 16, t		/* put in the space id */	! \
	depi	1, 0, 1, t		/* and set the valid bit */

#if 0
	BSS(dtlb_c, 8)
	BSS(tlbd_c, 8)
	BSS(itlb_c, 8)

	/* XXX this touches tr5, which it should not, perhaps */
#define	TLB_STATS_PRE(t) \
	mfctl	CR_ITMR, %r17			! \
	mtctl	%r17, %tr5

#define	TLB_STATS_AFT(t) \
	mfctl	CR_ITMR, %r16			! \
	mfctl	%tr5, %r17			! \
	ldil	L%__CONCAT(t,_c), %r25		! \
	ldo	R%__CONCAT(t,_c)(%r25), %r25	! \
	sub	%r16, %r17, %r16		! \
	ldw	0(%r25), %r24			! \
	ldw	4(%r25), %r17			! \
	ldo	1(%r24), %r24			! \
	ldo	-2(%r16), %r16 /* for mtctl */	! \
	add	%r16, %r17, %r17		! \
	stw	%r24, 0(%r25)			! \
	stw	%r17, 4(%r25)

#else
#define	TLB_STATS_PRE(t)	/**/
#define	TLB_STATS_AFT(t)	/**/
#endif

#define	TLB_PULL(bits,lbl)						! \
	/* space:pgaddr -- %r8:%r9 */					! \
	mfctl	CR_VTOP, %r16						! \
	ldwax,s	%r8(%r16), %r17		/* space -> page directory */	! \
	extru	%r9, 9, 10, %r25	/* r25 = r9[31..22] */		! \
	combt,=,n %r0, %r17, lbl					! \
	ldwax,s	%r25(%r17), %r24	/* page -> page table */	! \
	extru	%r9, 19, 10, %r16	/* r16 = r9[22..12] */		! \
	combt,=,n %r0, %r24, lbl					! \
	ldwax,s	%r16(%r24), %r17	/* va -> pa:prot */		! \
	sh2addl	%r16, %r24, %r25	/* r25 = r16 << 2 + r24 */	! \
	combt,=,n %r0, %r17, lbl					! \
	copy	%r17, %r16						! \
	depi	(bits), 21+bits, 1+bits, %r17				! \
	sub,=	%r16, %r17, %r0		/* do not store if unchanged */	! \
	stwas	%r17, 0(%r25)		/* store back w/ the bits */	! \
	shd	%r17, %r0, 13, %r25					! \
	dep	%r8, 30, 15, %r25	/* mix0r the pid from the sid */! \
	dep	%r0, 31, 12, %r17	/* needed ? */			! \
	addi	2, %r25, %r25						! \
	extru	%r17, 24, 25, %r17

/*
 * possible optimizations:
 *	change pte to reduce number of shifts
 *	reorder to reduce stalls
 */
#define	TLB_PULL_L(bits,lbl)						! \
	/* space:pgaddr -- %r8:%r9 */					! \
	mfctl	CR_VTOP, %r16						! \
	ldwx,s	%r8(%r16), %r17		/* space -> page directory */	! \
	extru	%r9, 9, 10, %r25					! \
	combt,=,n %r0, %r17, lbl					! \
	ldwx,s	%r25(%r17), %r24	/* page -> page table */	! \
	extru	%r9, 19, 10, %r16					! \
	combt,=,n %r0, %r24, lbl					! \
	ldwx,s	%r16(%r24), %r17	/* va -> pa:prot */		! \
	sh2addl	%r16, %r24, %r25					! \
	combt,=,n %r0, %r17, lbl					! \
	copy	%r17, %r16						! \
	depi	(bits), 21+bits, 1+bits, %r17				! \
	sub,=	%r16, %r17, %r0		/* do not store if unchanged */	! \
	stws	%r17, 0(%r25)		/* store back w/ the bits */	! \
	shd	%r17, %r0, 13, %r25					! \
	dep	%r8, 30, 15, %r25	/* mix0r the pid from the sid */! \
	dep	%r0, 31, 12, %r17	/* needed ? */			! \
	addi	2, %r25, %r25						! \
	extru	%r17, 24, 25, %r17	/* tlbbtop(%r17) */		! \
	sync

#if defined(HP7000_CPU) || defined(HP7100_CPU) || defined(HP7200_CPU)

	.align	32

/*
 * This is a handler for interruption 20, "TLB dirty bit trap".  It is used on
 * the PA7000 (PCX), PA7000 (PCX-S), and PA7100 (PCX-T).
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = data address space identifier.  Copied from %isr.
 * %r9 = data address offset.  Copied from %ior.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
LEAF_ENTRY_NOPROFILE(TLABEL(tlbd_s))
ALTENTRY(TLABEL(tlbd_t))
ALTENTRY(TLABEL(tlbd_x))
	TLB_STATS_PRE(tlbd)
	TLB_PULL(1, TLABEL(all))
	mfsp	%sr1, %r16
	mtsp	%r8, %sr1
	idtlba	%r17,(%sr1, %r9)
	idtlbp	%r25,(%sr1, %r9)
	mtsp	%r16, %sr1
	TLB_STATS_AFT(tlbd)
	rfir
	nop
EXIT(TLABEL(tlbd_s))

/*
 * This is a handler for interruption 6, "Instruction TLB miss fault". It is
 * used on the PA7000 (PCX), PA7000 (PCX-S), PA7100 (PCX-T) and PA7200 (PCX-T')
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = instruction address space identifier. Copied from %pcsq.
 * %r9 = instruction address offset. Copied from %pcoq.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
LEAF_ENTRY_NOPROFILE(TLABEL(itlb_s))
ALTENTRY(TLABEL(itlb_t))
ALTENTRY(TLABEL(itlb_x))
	TLB_STATS_PRE(itlb)
	TLB_PULL(0, TLABEL(all))
	extru,=	%r25, 5, 1, %r0	/* gate needs a kernel pid */
	depi	0, 30, 15, %r25
	mfsp	%sr1, %r16
	mtsp	%r8, %sr1
	iitlba	%r17,(%sr1, %r9)
	iitlbp	%r25,(%sr1, %r9)
	mtsp	%r16, %sr1
	TLB_STATS_AFT(itlb)
	rfir
	nop
EXIT(TLABEL(itlb_s))

/*
 * This is a handler for interruption 15, "Data TLB miss fault".  It is used on
 * the PA7000 (PCX), PA7000 (PCX-S), PA7100 (PCX-T) and PA7200 (PCX-T').
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = data address space identifier.  Copied from %isr.
 * %r9 = data address offset.  Copied from %ior.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
LEAF_ENTRY_NOPROFILE(TLABEL(dtlb_s))
ALTENTRY(TLABEL(dtlb_t))
ALTENTRY(TLABEL(dtlb_x))
	TLB_STATS_PRE(dtlb)
	TLB_PULL(0, TLABEL(all))
	mfsp	%sr1, %r16
	mtsp	%r8, %sr1
	idtlba	%r17,(%sr1, %r9)
	idtlbp	%r25,(%sr1, %r9)
	mtsp	%r16, %sr1
	TLB_STATS_AFT(dtlb)
	rfir
	nop
EXIT(TLABEL(dtlb_s))

/*
 * This is a handler for interruption 16, "Non-access instruction TLB miss
 * fault", and interrupt 17, "Non-access data TLB miss fault / Non-access data
 * page fault".  It is used on the PA7000 (PCX), PA7000 (PCX-S), PA7100 (PCX-T)
 * and PA7200 (PCX-T').
 *
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = instruction address space identifier.  Copied from %isr.
 * %r9 = instruction address offset.  Copied from %ior.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
LEAF_ENTRY_NOPROFILE(TLABEL(dtlbna_s))
ALTENTRY(TLABEL(itlbna_s))
ALTENTRY(TLABEL(dtlbna_t))
ALTENTRY(TLABEL(itlbna_t))
ALTENTRY(TLABEL(dtlbna_x))
ALTENTRY(TLABEL(itlbna_x))
	TLB_STATS_PRE(dtlb)
	TLB_PULL(0, L$dtlbna_t_fake)
	mfsp	%sr1, %r16
	mtsp	%r8, %sr1
	idtlba	%r17,(%sr1, %r9)
	idtlbp	%r25,(%sr1, %r9)
	mtsp	%r16, %sr1
	TLB_STATS_AFT(dtlb)
	rfir
	nop
L$dtlbna_s_fake:
L$dtlbna_t_fake:
	/* parse prober/w insns, have to decent to trap() to set regs proper */
	mfctl	%iir, %r16
	extru	%r16, 6, 6, %r24
	comib,=,n 1, %r24, TLABEL(all)
	extru	%r16, 24, 6, %r24
	subi,<>	0x23, %r24, %r0
	b	TLABEL(all)
	/* otherwise generate a flush-only tlb entry */
	copy	%r0, %r17
	zdep	%r8, 30, 15, %r25
	depi	-13, 11, 7, %r25
	ldo	2(%r25), %r25   /* 3? */
	mfsp	%sr1, %r16
	mtsp	%r8, %sr1
	idtlba	%r17,(%sr1, %r9)
	idtlbp	%r25,(%sr1, %r9)
	mtsp	%r16, %sr1
	TLB_STATS_AFT(dtlb)
	rfir
	nop
EXIT(TLABEL(dtlbna_s))

#endif /* defined(HP7000_CPU) || defined(HP7100_CPU) || defined(HP7200_CPU) */


#if defined(HP7100LC_CPU) || defined(HP7300LC_CPU)

#define	IITLBAF(r)	.word	0x04000440 | ((r) << 16)
#define	IITLBPF(r)	.word	0x04000400 | ((r) << 16)
#define	IDTLBAF(r)	.word	0x04001440 | ((r) << 16)
#define	IDTLBPF(r)	.word	0x04001400 | ((r) << 16)

	.align	32

/*
 * This is a handler for interruption 20, "TLB dirty bit trap".  It is used on
 * the PA7100LC (PCX-L), PA7300LC (PCX-L2).
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = data address space identifier.  Copied from %ior.
 * %r9 = data address offset.  Copied from %isr.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */

LEAF_ENTRY_NOPROFILE(TLABEL(tlbd_l))
	TLB_STATS_PRE(tlbd)
	TLB_PULL_L(1, TLABEL(all))
	IDTLBAF(17)
	IDTLBPF(25)
#ifdef USE_HPT
	/* invalidate instead of update */
	mfctl	%cr28, %r17
	ldw	0(%r17), %r24
	VTAG(%r8, %r9, %r16)
	sub,<>	%r16, %r24, %r0
	stw	%r0, 0(%r17)
#endif
	TLB_STATS_AFT(tlbd)
	rfir
	nop
EXIT(TLABEL(tlbd_l))

/*
 * This is a handler for interruption 6, "Instruction TLB miss fault".  It is
 * used on the PA7100LC (PCX-L), PA7300LC (PCX-L2).
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = instruction address space identifier.  Copied from %pcsq.
 * %r9 = instruction address offset.  Copied from %pcoq.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */

/*
 * from 7100lc ers, pg.6:
 * we found a post-silicon bug that makes cr28
 * unreliable for the itlb miss handler
 */
LEAF_ENTRY_NOPROFILE(TLABEL(itlb_l))
	TLB_STATS_PRE(itlb)
	TLB_PULL_L(0, TLABEL(all))
	extru,=	%r25, 5, 1, %r0	/* gate needs a kernel pid */
	depi	0, 30, 15, %r25
	IITLBAF(17)
	IITLBPF(25)
	TLB_STATS_AFT(itlb)
	rfir
	nop
EXIT(TLABEL(itlb_l))

/*
 * This is a handler for interruption 16, "Non-access instruction TLB miss
 * fault", and interrupt 17, "Non-access data TLB miss fault / Non-access data
 * page fault".  It is used on the PA7100LC (PCX-L), PA7300LC (PCX-L2).
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = instruction address space identifier.  Copied from %isr.
 * %r9 = instruction address offset.  Copied from %ior.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
LEAF_ENTRY_NOPROFILE(TLABEL(dtlbna_l))
ALTENTRY(TLABEL(itlbna_l))
	TLB_STATS_PRE(dtlb)
	TLB_PULL_L(0, L$dtlbna_l_fake)
	IDTLBAF(17)
	IDTLBPF(25)
	TLB_STATS_AFT(dtlb)
	rfir
	nop
L$dtlbna_l_fake:
	/* parse prober/w insns, have to decent to trap() to set regs proper */
	mfctl	%iir, %r16
	extru	%r16, 6, 6, %r24
	comib,=,n 1, %r24, TLABEL(all)
	extru	%r16, 24, 6, %r24
	subi,<>	0x23, %r24, %r0
	b	TLABEL(all)
	/* otherwise generate a flush-only tlb entry */
	copy	%r0, %r17
	zdep	%r8, 30, 15, %r25
	depi	-13, 11, 7, %r25
	ldo	2(%r25), %r25   /* 3? */
	IDTLBAF(17)
	IDTLBPF(25)
	TLB_STATS_AFT(dtlb)
	rfir
	nop
EXIT(TLABEL(dtlbna_l))

/*
 * This is a handler for interruption 15, "Data TLB miss fault".  It is used on
 * the PA7100LC (PCX-L), PA7300LC (PCX-L2).
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = instruction address space identifier.  Copied from %isr.
 * %r9 = instruction address offset.  Copied from %ior.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
LEAF_ENTRY_NOPROFILE(TLABEL(dtlb_l))
	TLB_STATS_PRE(dtlb)
	TLB_PULL_L(0, TLABEL(all))
	IDTLBAF(17)
	IDTLBPF(25)
#ifdef USE_HPT
	/*
	 * cache the next page mapping in the hpt.
	 *
	 * mapping for a page at the end of each 128k is uncachable
	 * in the hvt since it'd be in the tlb itself and thus there
	 * is no reason to cache it!
	 * as a side effect this avoids recomputing hpt entry and
	 * retraversing the whole page table each time.
	 */

	ldo	PAGE_SIZE(%r9), %r9
	extru,<> %r9, 20, 5, %r0
	b,n	L$dtlb_done_l	/* skip if no simple advance */
	/* do not check the PT overlap since the above
	 * check already guaranties that */

	/* ripped from TLB_PULL_L(0) */
	extru	%r9, 19, 10, %r16		/* %r24 was loaded in the TLB_PULL_L */
	ldwx,s	%r16(%r24), %r17		/* va -> pa:prot */
	sh2addl	%r16, %r24, %r25
	combt,=,n %r0, %r17, L$dtlb_done_l
	copy	%r17, %r16
	depi	0, 21, 1, %r17
	sub,=	%r16, %r17, %r0			/* do not store if unchanged */
	stws	%r17, 0(%r25)			/* store back w/ the bits */
	shd	%r17, %r0, 13, %r25
	dep	%r8, 30, 15, %r25		/* mix0r the pid from the sid */
	dep	%r0, 31, 12, %r17		/* needed ? */
	addi	2, %r25, %r25
	extru	%r17, 24, 25, %r17
	sync

	mfctl	%cr28, %r24
	VTAG(%r8, %r9, %r16)
	ldo	16(%r24), %r24
	stw	%r16, 0(%r24)
	stw	%r25, 4(%r24)
	stw	%r17, 8(%r24)
L$dtlb_done_l:
#endif
	TLB_STATS_AFT(dtlb)
	rfir
	nop
EXIT(TLABEL(dtlb_l))
#endif /* defined(HP7100LC_CPU) || defined(HP7300LC_CPU) */

#if defined(HP8000_CPU) || defined(HP8200_CPU) || \
    defined(HP8500_CPU) || defined(HP8600_CPU) || \
    defined(HP8700_CPU)

	.level	2.0w

	/* xlate 32bit->64bit pte */
#define	TLB_PCX2PCXU \
	extrw,u	%r25, 14, 13, %r16		! \
	depdi	0, 31, 32, %r17			! \
		/* fix io mappings */		! \
	extrd,s	%r17, 42, 4, %r1		! \
	addi,<>	1, %r1, %r0			! \
	depdi	-1, 38, 32, %r17		! \
		/* fix prom mappings */		! \
	extrd,s	%r17, 46, 8, %r1		! \
	addi,<>	0x10, %r1, %r0			! \
	depdi	0, 38, 4, %r17			! \
		/* weak ordering, dyn bp */	! \
	depwi	1, 31, 2, %r16			! \
	depdi	0, 44, 30, %r25			! \
	depd	%r16, 14, 15, %r25

/*
 * This is a handler for interruption 20, "TLB dirty bit trap".  It is used by
 * the PA8000 (PCX-U), PA8200 (PCX-W), PA8500 (PCX-W), and PA8600 (PCX-W+).
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = data address space identifier.  Copied from %isr.
 * %r9 = data address offset.  Copied from %ior.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
LEAF_ENTRY_NOPROFILE(TLABEL(tlbd_u))
	TLB_STATS_PRE(tlbd)
	TLB_PULL_L(1, TLABEL(all))
	TLB_PCX2PCXU
	idtlbt	%r17, %r25
	TLB_STATS_AFT(tlbd)
	rfir
	nop
EXIT(TLABEL(tlbd_u))

/*
 * This is a handler for interruption 6, "Instruction TLB miss fault".  It is
 * the PA8000 (PCX-U), PA8200 (PCX-W), PA8500 (PCX-W), and PA8600 (PCX-W+).
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = data address space identifier.  Copied from %isr.
 * %r9 = data address offset.  Copied from %ior.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
LEAF_ENTRY_NOPROFILE(TLABEL(itlb_u))
	TLB_STATS_PRE(itlb)
	TLB_PULL_L(0, TLABEL(all))
	extru,=	%r25, 5, 1, %r0	/* gate needs a kernel pid */
	depi	0, 30, 15, %r25
	TLB_PCX2PCXU
	iitlbt	%r17, %r25
	TLB_STATS_AFT(itlb)
	rfir
	nop
EXIT(TLABEL(itlb_u))


/*
 * This is a handler for interruption 16, "Non-access instruction TLB miss
 * fault", and interrupt 17, "Non-access data TLB miss fault / Non-access data
 * page fault".  It is the PA8000 (PCX-U), PA8200 (PCX-W), PA8500 (PCX-W),
 * and PA8600 (PCX-W+).
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = instruction address space identifier.  Copied from %isr.
 * %r9 = instruction address offset.  Copied from %ior.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
LEAF_ENTRY_NOPROFILE(TLABEL(dtlbna_u))
ALTENTRY(TLABEL(itlbna_u))
	TLB_STATS_PRE(dtlb)
	TLB_PULL_L(0, L$dtlbna_u_fake)
	TLB_PCX2PCXU
	idtlbt	%r17, %r25
	TLB_STATS_AFT(dtlb)
	rfir
	nop
L$dtlbna_u_fake:
	/* parse prober/w insns, have to decent to trap() to set regs proper */
	mfctl	%iir, %r16
	extru	%r16, 6, 6, %r24
	comib,=,n 1, %r24, TLABEL(all)
	extru	%r16, 24, 6, %r24
	subi,<>	0x23, %r24, %r0
	b	TLABEL(all)
	/* otherwise generate a flush-only tlb entry */
	copy	%r0, %r17
	zdep	%r8, 30, 15, %r25
	depi	-13, 11, 7, %r25
	ldo	2(%r25), %r25   /* 3? */
	idtlbt	%r17, %r25
	TLB_STATS_AFT(dtlb)
	rfir
	nop
EXIT(TLABEL(dtlbna_u))

/*
 * This is a handler for interruption 15, "Data TLB miss fault".  It is the
 * PA8000 (PCX-U), PA8200 (PCX-W), PA8500 (PCX-W), and PA8600 (PCX-W+).
 * Only shadowed registers are available, and they are:
 *
 * %r1 = C trap number
 * %r8 = instruction address space identifier.  Copied from %isr.
 * %r9 = instruction address offset.  Copied from %ior.
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
LEAF_ENTRY_NOPROFILE(TLABEL(dtlb_u))
	TLB_STATS_PRE(dtlb)
	TLB_PULL_L(0, TLABEL(all))
	TLB_PCX2PCXU
	idtlbt	%r17, %r25
	TLB_STATS_AFT(dtlb)
	rfir
	nop
EXIT(TLABEL(dtlb_u))

	.level	1.1
#endif /* HP8000_CPU */

#if defined(HP7000_CPU)
/*
 * int desidhash_s(void)
 * int desidhash_x(void)
 */
LEAF_ENTRY_NOPROFILE(desidhash_s)
ALTENTRY(desidhash_x)
	sync
	MFCPU_T(DR_CPUCFG,22)	/* %t1 */
	MFCPU_T(DR_CPUCFG,22)
	nop
	nop
	depi	0, DR0_PCXS_DHE, 3, %t1	/* 3 4 DR0_PCXS_DOMAIN|DR0_PCXS_IHE */
	depi	1, DR0_PCXS_EQWSTO, 1, %t1
	depi	0, DR0_PCXS_DHPMC, 1, %t1
	depi	0, DR0_PCXS_ILPMC, 1, %t1
	sync
	MTCPU_T(22,DR_CPUCFG)
	MTCPU_T(22,DR_CPUCFG)
	nop
	nop
	bv	0(%rp)
	extru	%t1, 4, 5, %ret0	/* return chip revision */
EXIT(desidhash_s)
#endif /* defined(HP7000_CPU) */

#if defined(HP7100_CPU) || defined(HP7200_CPU)
/*
 * void desidhash_t(void)
 */
LEAF_ENTRY_NOPROFILE(desidhash_t)
	sync
	MFCPU_T(DR_CPUCFG,22)	/* %t1 */
	MFCPU_T(DR_CPUCFG,22)
	nop
	nop
	depi	0, DR0_PCXT_IHE, 1, %t1
	depi	0, DR0_PCXT_DHE, 1, %t1
	depi	0, DR0_PCXT_DHPMC, 1, %t1
	depi	0, DR0_PCXT_ILPMC, 1, %t1
	sync
	MTCPU_T(22,DR_CPUCFG)
	MTCPU_T(22,DR_CPUCFG)
	nop
	nop
	bv	0(%rp)
	extru	%t1, 4, 5, %ret0	/* return chip revision */
EXIT(desidhash_t)
#endif /* defined(HP7100_CPU) || defined(HP7200_CPU) */

#if defined(HP7100LC_CPU) || defined(HP7300LC_CPU)
/*
 * int
 * ibtlb_l(int i, pa_space_t sp, vaddr_t va, paddr_t pa, vsize_t sz,
 *     u_int prot)
 */
LEAF_ENTRY_NOPROFILE(ibtlb_l)
	rsm	(PSW_R|PSW_I), %t4
	nop ! nop ! nop ! nop ! nop ! nop ! nop	/* XXXNH why? */

	bv	0(%rp)
	mtsm	%t4
EXIT(ibtlb_l)

/*
 * void
 * hpti_l(addr,size)
*/
LEAF_ENTRY_NOPROFILE(hpti_l)
	ldo	-1(%arg1), %arg1
	depi	0, 31, 12, %arg1
	ldi	0x1c0, %t1		/* cache size assumed 128k XXX */
	or	%arg0, %t1, %arg0
	sync
	MTCPU_C(26,DR0_PCXL2_HTLB_ADDR)
	MTCPU_C(25,DR0_PCXL2_HTLB_CFG)
	nop
	nop
	bv,n	%r0(%rp)
	nop
EXIT(hpti_l)

/*
 * int
 * pbtlb_l(int i)
 */
LEAF_ENTRY_NOPROFILE(pbtlb_l)
	; DR_PAGE0
	rsm	(PSW_R|PSW_I), %t4
	nop ! nop ! nop ! nop
	ldil	L%0xc041, %t1
	ldo	R%0xc041(%t1), %t1
	dep	%arg0, 30, 3, %t1
	sync
	MTCPU_T(22,DR_DTLB)	/* t1 */
	nop
	nop
	mtsp	%r0, %sr1
	idtlba	%r0,(%sr1,%r0)
	idtlbp	%r0,(%sr1,%r0)
	zdepi	-1, 18, 1, %t1
	nop
	sync
	MTCPU_T(22,DR_DTLB)
	nop
	nop
	bv	0(%rp)
	mtsm	%t4
EXIT(pbtlb_l)

/*
 * int desidhash_l(void)
 */
LEAF_ENTRY_NOPROFILE(desidhash_l)
	MFCPU_C(DR_CPUCFG,22)	/* t1 */
	nop
	nop
	depi	0, DR0_PCXL_L2IHASH_EN, 2, %t1	/* + DR0_PCXL_L2DHASH_EN */
	depi	0, DR0_PCXL_L2IHPMC, 1, %t1	/* don't reset */
	depi	0, DR0_PCXL_L2DHPMC, 1, %t1	/* don't reset */
	depi	0, DR0_PCXL_L1IHPMC, 1, %t1	/* don't reset */
	depi	0, DR0_PCXL_L2PARERR,1, %t1	/* don't reset */
	sync
		/* set DR0_PCXL_L1ICACHE_EN ??? */
	MTCPU_C(22,DR_CPUCFG)
	nop
	nop
	bv	0(%rp)
	extru	%t1, 4, 5, %ret0	/* return chip revision */
EXIT(desidhash_l)

#endif /* defined(HP7100LC_CPU) || defined(HP7300LC_CPU) */

#if defined(HP8000_CPU) || defined(HP8200_CPU) || \
    defined(HP8500_CPU) || defined(HP8600_CPU) || \
    defined(HP8700_CPU)

	.level	2.0w
LEAF_ENTRY_NOPROFILE(desidhash_u)
	MFCPU_U(2,28)
	depdi	0, 54, 1, %r28
	MTCPU_U(28,2)
	bv	%r0(%rp)
	copy	%r0, %ret0	/* XXX dunno how to get chip rev */
EXIT(desidhash_u)

LEAF_ENTRY_NOPROFILE(ibtlb_u)
	/* TODO insert a locked large tlb entry */
	bv	0(%rp)
	nop
EXIT(ibtlb_u)

LEAF_ENTRY_NOPROFILE(pbtlb_u)
	/* TODO purge a locked tlb entry */
	bv	0(%rp)
	nop
EXIT(pbtlb_u)
	.level	1.1
#endif /* HP8000_CPU || HP8200_CPU || HP8500_CPU || HP8600_CPU || HP8700_CPU */

	.align	64
	.export	TLABEL(all), entry
ENTRY_NOPROFILE(TLABEL(all),0)
	/*
	 * at this point we have:
	 *  - psw copied into ipsw
	 *  - psw	E(default), M(1 if HPMC, else 0), all others 0, i.e.
	 *		interrupts masked, absolute accesses are enabled, etc.
	 *  - PL	0 (highest privilege)
	 *  - r1, r8, r9, r16, r17, r24, r25 shadowed (maybe)
	 *  - r1:	trap number
	 *  - tr7:	old r1
	 */

	mtctl	%t3, %tr2

	GET_CURCPU(%t3)

	stw	%t1, CI_TRAPSAVE + TF_R22(%t3)	/* use ,bc */
	stw	%t2, CI_TRAPSAVE + TF_R21(%t3)

	mfctl	%tr2, %t1
	stw	%sp, CI_TRAPSAVE + TF_R30(%t3)	/* sp */
	stw	%t1, CI_TRAPSAVE + TF_R20(%t3)	/* t3 */

	/*
	 * Now, save away other volatile state that prevents us from turning
	 * the PC queue back on, namely, the pc queue and ipsw, and the
	 * interrupt information.
	 */

	mfctl	%eiem, %t1
	mfctl	%ipsw, %t2
	stw	%t1, CI_TRAPSAVE + TF_CR15(%t3)	/* use ,bc */
	stw	%t2, CI_TRAPSAVE + TF_CR22(%t3)

	mfsp	%sr3, %t1
	mfctl	%pidr1, %t2
	stw	%t1, CI_TRAPSAVE + TF_SR3(%t3)
	stw	%t2, CI_TRAPSAVE + TF_CR8(%t3)

	/* Setup kernel context */
	ldi	HPPA_PID_KERNEL,%t1
	mtctl	%t1, %pidr1
	mtsp	%r0, %sr3

	/* this will enable interrupts after `cold' */
	GET_CURCPU(%t1)
	ldw	CI_PSW(%t1), %t2
	mtctl	%r0, %eiem
	mtctl	%t2, %ipsw

	/* save interruption instruction address space queue */
	mfctl	%pcsq, %t1
	mtctl	%r0, %pcsq
	mfctl	%pcsq, %t2
	stw	%t1, CI_TRAPSAVE + TF_IISQH(%t3)	/* use ,bc */
	stw	%t2, CI_TRAPSAVE + TF_IISQT(%t3)
	mtctl	%r0, %pcsq

	/*
	 * Set up the kernel stack pointer.  If the trap happened while we were
	 * in unprivileged code, or in privileged code in the SYSCALLGATE page,
	 * move to the kernel stack in curlwp's PCB; otherwise, start a new
	 * stack frame on whatever kernel stack we're already on.
	 *
	 * This used to check only for a trap while we were in unprivileged
	 * code, but this ignored the possibility that a trap could come in
	 * during the period between a gateway instruction to raise privilege
	 * and the disabling of interrupts.  During this period we're still on
	 * the user's stack, and we must move to the kernel stack.
	 */
	mfctl	%pcoq, %t1
	ldil	L%SYSCALLGATE, %t2		/* t2 = SYSCALLGATE */
	ldo	HPPA_FRAME_SIZE-1(%sp), %sp	/* Assumed stack align step 1 */
	dep	%t1, 31, PGSHIFT, %t2		/* t2 |= (pcoqh & PAGE_MASK) */
	dep,<>	%t1, 31, 2, %r0			/* Nullify if user mode (!0) */
	comb,<>	%t1, %t2, L$trap_from_kernel	/* if %t1 != %t2 => kernel */
	dep	%r0, 31, 6, %sp			/* Assumed stack align step 2 */

	GET_CURLWP(%t2)
	depi	1, T_USER_POS, 1, %r1
	depi	1, TFF_LAST_POS, 1, %r1
	ldw	L_PCB(%t2), %sp
#ifdef DIAGNOSTIC
	b	L$trap_have_stack
#endif
	ldo	NBPG(%sp), %sp

L$trap_from_kernel:
#ifdef DIAGNOSTIC
	/*
	 * Use the emergency stack if we have taken some kind
	 * of TLB or protection fault on the kernel stack.
	 */
	mtctl	%t1, %tr2
	ldw	CI_TRAPSAVE + TF_R30(%t3), %t1
	mfctl	%ior, %t2
	dep	%r0, 31, PGSHIFT, %t1
	dep	%r0, 31, PGSHIFT, %t2
	comb,=,n %t1, %t2, 0
	ldo	NBPG(%t1), %t1
	comb,<>	%t1, %t2, L$trap_have_stack
	mfctl	%tr2, %t1
	mfctl	%isr, %t2
	comib,<>,n HPPA_SID_KERNEL, %t2, L$trap_have_stack
#define _CHECK_TRAP_TYPE(tt) ldi tt, %t2 ! comb,= %r1, %t2, L$trap_kstack_fault
	_CHECK_TRAP_TYPE(T_ITLBMISS)
	_CHECK_TRAP_TYPE(T_DTLBMISS)
	_CHECK_TRAP_TYPE(T_ITLBMISSNA)
	_CHECK_TRAP_TYPE(T_DTLBMISSNA)
	_CHECK_TRAP_TYPE(T_DPROT)
	_CHECK_TRAP_TYPE(T_DATACC)
	_CHECK_TRAP_TYPE(T_DATAPID)
	ldi	T_DATALIGN, %t2
	comb,<>,n %r1, %t2, L$trap_have_stack
#undef _CHECK_TRAP_TYPE
L$trap_kstack_fault:
	ldil	L%emergency_stack_start, %sp
	ldo	R%emergency_stack_start(%sp), %sp
L$trap_have_stack:
#endif
	ldil	L%trapnowvirt, %t2
	ldo	R%trapnowvirt(%t2), %t2
	mtctl	%t2, %pcoq
	stw	%t1, CI_TRAPSAVE + TF_IIOQH(%t3)
	ldo	4(%t2), %t2
	mfctl	%pcoq, %t1
	stw	%t1, CI_TRAPSAVE + TF_IIOQT(%t3)
	mtctl	%t2, %pcoq

	/* save the interruption space and offset registers */
	mfctl	%isr, %t1
	mfctl	%ior, %t2
	stw	%t1, CI_TRAPSAVE + TF_CR20(%t3)	/* use ,bc */
	stw	%t2, CI_TRAPSAVE + TF_CR21(%t3)

	/* save the interruption instruction register */
	mfctl	%iir, %t2
	stw	%t2, CI_TRAPSAVE + TF_CR19(%t3)

	/* save the trap type and flags */
	stw	%r1, CI_TRAPSAVE + TF_FLAGS(%t3)

	/* gotta get it before R is up */
	mfctl	CR_RCTR, %t1

	copy	%sp, %t3
	ldo	HPPA_FRAME_SIZE+TRAPFRAME_SIZEOF(%sp), %sp

#if defined(DDB) || defined(KGDB)
	/*
	 * Match the offset from %sp for the trapframe with syscall_entry
	 */
	ldo	HPPA_FRAME_MAXARGS+HPPA_FRAME_SIZE-1(%sp),%sp
	depi	0, 31, 6, %sp
#endif
	rfir
	nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop
trapnowvirt:
	/*
	 * %t3 contains the virtual address of the trapframe
	 * %sp is loaded w/ the right VA (we did not need it being physical)
	 */

	mfctl	CR_CCR, %t2
	stw	%t1, TF_CR0(%sr3, %t3)
	stw	%t2, TF_CR10(%sr3, %t3)

	mfsp	%sr0, %t1
	mfsp	%sr1, %t2
	stw	%t1, TF_SR0(%sr3, %t3)
	stw	%t2, TF_SR1(%sr3, %t3)

	mfsp	%sr2, %t1
	mfsp	%sr4, %t2
	stw	%t1, TF_SR2(%sr3, %t3)
	stw	%t2, TF_SR4(%sr3, %t3)

	mfsp	%sr5, %t2
	mfsp	%sr6, %t1
	stw	%t2, TF_SR5(%sr3, %t3)
	stw	%t1, TF_SR6(%sr3, %t3)

	mfsp	%sr7, %t1
	mfctl	%pidr2, %t2
	stw	%t1, TF_SR7(%sr3, %t3)
	stw	%t2, TF_CR9(%sr3, %t3)

	mtsp	%r0, %sr0
	mtsp	%r0, %sr1
	mtsp	%r0, %sr2
	mtsp	%r0, %sr4
	mtsp	%r0, %sr5
	mtsp	%r0, %sr6
	mtsp	%r0, %sr7

#if pbably_not_worth_it
	mfctl	%pidr3, %t1
	mfctl	%pidr4, %t2
	stw	%t1, TF_CR12(%t3)
	stw	%t2, TF_CR13(%t3)
#endif

	/*
	 * Save all general registers that we haven't saved already
	 */

	/* XXXNH check this!!! */
#if defined(DDB) || defined(KGDB)
	stw	%rp, HPPA_FRAME_CRP(%sp)
	stw	%r0, -HPPA_FRAME_SIZE(%sp)
#endif
	stw	%t3, -HPPA_FRAME_SIZE+4(%sp)

	mfctl	%sar, %t1		/* use ,bc each cache line */
	stw	%t1, TF_CR11(%t3)
	stw	%r1, TF_R1(%t3)
	stw	%r2, TF_R2(%t3)
	stw	%r3, TF_R3(%t3)

	/*
	 * Copy partially saved state from the store into the frame
	 */
	GET_CURCPU(%t2)
	ldo	CI_TRAPSAVE(%t2), %t2

	/* use ,bc each line */
	ldw  0(%t2), %r1 ! ldw  4(%t2), %t1 ! stw %r1,  0(%t3) ! stw %t1,  4(%t3)
	ldw  8(%t2), %r1 ! ldw 12(%t2), %t1 ! stw %r1,  8(%t3) ! stw %t1, 12(%t3)
	ldw 16(%t2), %r1 ! ldw 20(%t2), %t1 ! stw %r1, 16(%t3) ! stw %t1, 20(%t3)
	ldw 24(%t2), %r1 ! ldw 28(%t2), %t1 ! stw %r1, 24(%t3) ! stw %t1, 28(%t3)
	ldw 32(%t2), %r1 ! ldw 36(%t2), %t1 ! stw %r1, 32(%t3) ! stw %t1, 36(%t3)
	ldw 40(%t2), %r1 ! ldw 44(%t2), %t1 ! stw %r1, 40(%t3) ! stw %t1, 44(%t3)
	ldw 48(%t2), %r1 ! ldw 52(%t2), %t1 ! stw %r1, 48(%t3) ! stw %t1, 52(%t3)
	ldw 56(%t2), %r1 ! ldw 60(%t2), %t1 ! stw %r1, 56(%t3) ! stw %t1, 60(%t3)

	/*
	 * Normally, we'd only have to save and restore the caller-save
	 * registers, because the callee-save registers will be saved and
	 * restored automatically by our callee(s).
	 *
	 * However, in two cases we need to save and restore all of the general
	 * registers in the trapframe.  One, if we're running a debugger, we
	 * want the debugging person to be able to see and change any and all
	 * general register values at the trap site.  Two, if we have an FPU
	 * emulator, this trap may be to emulate an instruction that needs to
	 * read and write any and all general registers (for example, a load
	 * or store instruction with a modify completer).
	 *
	 * See similar #ifdefs in the syscall entry and exit code.
	 */
	stw	%r4, TF_R4(%t3)
	stw	%r5, TF_R5(%t3)
	stw	%r6, TF_R6(%t3)
	stw	%r7, TF_R7(%t3)
	stw	%r8, TF_R8(%t3)
	stw	%r9, TF_R9(%t3)
	stw	%r10, TF_R10(%t3)
	stw	%r11, TF_R11(%t3)
	stw	%r12, TF_R12(%t3)
	stw	%r13, TF_R13(%t3)
	stw	%r14, TF_R14(%t3)
	stw	%r15, TF_R15(%t3)
	stw	%r16, TF_R16(%t3)
	stw	%r17, TF_R17(%t3)
	stw	%r18, TF_R18(%t3)

	stw	%t4, TF_R19(%t3)
	stw	%r23,TF_R23(%t3)
	stw	%r24,TF_R24(%t3)
	stw	%r25,TF_R25(%t3)
	stw	%r26,TF_R26(%t3)
	stw	%r27,TF_R27(%t3)
	stw	%r28,TF_R28(%t3)
	stw	%r29,TF_R29(%t3)
	stw	%r31,TF_R31(%t3)

	/*
	 * Save the necessary control registers that have not already saved.
	 */
#if defined(DDB) || defined(KGDB)
	/*
	 * Save v2p translation table pointer
	 */
	mfctl	%eirr, %t1
	mfctl	CR_VTOP, %t2
	stw	%t1, TF_CR23(%t3)
	stw	%t2, TF_CR25(%t3)

	mfctl	%cr24, %t1
	mfctl	%cr28, %t2
	stw	%t1, TF_CR24(%t3)
	stw	%t2, TF_CR28(%t3)

#endif
	mfctl	CR_TLS, %t1
	mfctl	CR_FPPADDR, %t2
	stw	%t1, TF_CR27(%t3)
	stw	%t2, TF_CR30(%t3)

	/*
	 * load the global pointer for the kernel
	 */

	ldil	L%$global$, %dp
	ldo	R%$global$(%dp), %dp

	/*
	 * call the C routine trap().
	 * form trap type in the first argument to trap()
	 */
	ldw	TF_FLAGS(%t3), %arg0
	dep	%r0, 24, 25, %arg0
	copy	%t3, %arg1

#if defined(DDB) || defined(KGDB)
	/* Mark frame pointer as NULL to indicate syscall/trap */
	copy	%r0, %r3
#endif

	.import	trap, code
	CALL(trap, %t1)

	ldw	-HPPA_FRAME_SIZE+4(%sp), %t3
	/* see if curlwp has changed */
	ldw	TF_FLAGS(%t3), %arg0
	bb,>=,n	%arg0, TFF_LAST_POS, L$trap_return
	nop

	/* load curlwp's trapframe pointer */
	GET_CURLWP(%t2)
	ldw	L_MD(%t2), %t3

L$trap_return:
	ldil	L%syscall_return, %t1
	ldo	R%syscall_return(%t1), %t1
	bv,n	%r0(%t1)
	nop

EXIT(TLABEL(all))

	.align	32
ENTRY_NOPROFILE(TLABEL(ibrk),0)
	/* If called by a user process then always pass it to trap() */
	mfctl	%pcoq, %r8
	extru,=	%r8, 31, 2, %r0
	b,n	L$ibrk_bad

	/* don't accept breaks from data segments */
	.import etext
	ldil	L%etext, %r9
	ldo	R%etext(%r9), %r9
	comb,>>=,n %r8, %r9, L$ibrk_bad

	mfctl	%iir, %r8
	extru	%r8, 31, 5, %r9
	comib,<>,n HPPA_BREAK_KERNEL, %r9, L$ibrk_bad

	/* now process all those `break' calls we make */
	extru	%r8, 18, 13, %r9
	comib,=,n HPPA_BREAK_GET_PSW, %r9, L$ibrk_getpsw
	comib,=,n HPPA_BREAK_SET_PSW, %r9, L$ibrk_setpsw

L$ibrk_bad:
	/* illegal (unimplemented) break entry point */
	b	TLABEL(all)
	nop

L$ibrk_getpsw:
	b	L$ibrk_exit
	mfctl	%ipsw, %ret0

L$ibrk_setpsw:
	mfctl	%ipsw, %ret0
	b	L$ibrk_exit
	mtctl	%arg0, %ipsw

	/* insert other fast breaks here */
	nop ! nop

L$ibrk_exit:
	/* skip the break */
	mtctl	%r0, %pcoq
	mfctl	%pcoq, %r9
	mtctl	%r9, %pcoq
	ldo	4(%r9), %r9
	mtctl	%r9, %pcoq
	rfir
	nop
EXIT(TLABEL(ibrk))
