NetBSD-5.0.2/sys/arch/hppa/hppa/trap.S

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: trap.S,v 1.26 2008/08/22 19:32:57 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.46 2001/09/20 18:33:03 mickey Exp $	*/

/*
 * Copyright (c) 1998-2001 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Michael Shalayeff.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * 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.
 */
/*
 * Copyright (c) 1990,1991,1992,1994 The University of Utah and
 * the Computer Systems Laboratory (CSL).  All rights reserved.
 *
 * THE UNIVERSITY OF UTAH AND CSL PROVIDE THIS SOFTWARE IN ITS "AS IS"
 * CONDITION, AND DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES
 * WHATSOEVER RESULTING FROM ITS USE.
 *
 * 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.62 94/12/15$
 */
/*
 *  (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.
 */

#include "opt_compat_osf1.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
	.align	64
$trap_tmp_save:
	.block	TF_PHYS
	.align	64
	.export emergency_stack_start, data
emergency_stack_start:
	.block	32768		/* XXX must be aligned to 64 */
	.export emergency_stack_end, data
emergency_stack_end:
	.block	4

	.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) */
#ifdef COMPAT_OSF1
	bl,n	$osf_syscall,%r0
	bl,n	$osf_syscall,%r0
#else
	nop				/* @ 0.C0000008 (HPOSF UNIX) */
	nop				/* @ 0.C000000C (HPOSF Mach) */
#endif
	nop
	nop
	nop
	nop

#ifdef COMPAT_OSF1
$osf_syscall:
	/*
	 * Ripped screaming from OSF/MkLinux:
	 *
	 * Convert HPOSF system call to a BSD one by stashing arg4 and arg5
	 * back into the frame, and moving the system call number into r22.
	 * Fortunately, the HPOSF compiler has a bigger stack frame, which
	 * allows this horrible hack.
	 *
	 * We also need to save r29 (aka ret1) for the emulator since it may
	 * get clobbered between here and there.
	 */
	stw	%r22, HPPA_FRAME_ARG(4)(%sp)
	stw	%r21, HPPA_FRAME_ARG(5)(%sp)
	stw	%r29, HPPA_FRAME_SL(%sp)
	gate	$bsd_syscall,%r0
	copy	%r1, %r22
#endif /* COMPAT_OSF1 */

$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, %r28
	ldi	HPPA_PID_KERNEL, %t2
	mtctl	%t2, %pidr1

	/*
	 * now call the syscall handler
	 */
	.import $syscall,code
	.call
	ldil	L%$syscall, %t2
	be,n	R%$syscall(%sr1, %t2)
	nop

	.align	NBPG
	.export	gateway_page_end, entry
gateway_page_end:

	.export $syscall,entry
	.proc
	.callinfo calls
	.entry
$syscall:
	/*
	 *
	 * t1:	syscall number
	 * t2:	user
	 * t3:	args
	 * t4:	user stack
	 *
	 * N.B. we are trying to rely on the fact that bottom of kernel
	 *	stack contains a print of some past trapframe, so
	 *	we do not save hard to get information, but do restore
	 *	the whole context later on return anyway.
	 * XXXXXX this is very bad. everything must be saved
	 */
	ldil	L%curlwp, %t3
	ldw	R%curlwp(%sr1, %t3), %t3
	ldw	L_ADDR(%sr1, %t3), %t2	/* XXX can use ,sl */

	/* calculate kernel sp, load, create kernel stack frame */
	/*
	 * 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.
	 */
	ldo	NBPG+TRAPFRAME_SIZEOF(%t2), %t3
	stw	%t1, TF_R22 -TRAPFRAME_SIZEOF(%sr1, %t3)	/* syscall # */
	stw	%t4, TF_R19 -TRAPFRAME_SIZEOF(%sr1, %t3)	/* convenience */
	copy	%sp, %t4

	/*
	 * 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.
	 */
	ldo	HPPA_FRAME_SIZE+HPPA_FRAME_MAXARGS(%t3), %sp

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

	stw	%t4, TF_R30 -TRAPFRAME_SIZEOF(%sr1, %t3)	/* user stack */
	stw	%r1, TF_CR15-TRAPFRAME_SIZEOF(%sr1, %t3)	/* eiem */
	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	%r27, TF_R27-TRAPFRAME_SIZEOF(%sr1, %t3)	/* dp */
	stw	%r3 , TF_R3 -TRAPFRAME_SIZEOF(%sr1, %t3)
#if defined(DDB) || defined(KGDB) || defined(FPEMUL)
	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)
#endif /* DDB || KGDB || FPEMUL */
	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
	 */

	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%(PSW_Q | PSW_P | PSW_C | PSW_D | PSW_I), %arg1
	ldo	R%(PSW_Q | PSW_P | PSW_C | PSW_D | PSW_I)(%arg1), %arg1
	mfsp	%sr3, %arg0
	stw	%arg1, TF_CR22-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg0, TF_SR3-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%r28, TF_CR8-TRAPFRAME_SIZEOF(%sr1, %t3)	/* pidr1 */

	copy	%r0, %arg0
	ldil	L%(TFF_LAST|TFF_SYS), %arg1
	stw	%arg0, TF_CR19-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg1, TF_FLAGS-TRAPFRAME_SIZEOF(%sr1, %t3)

	mfsp	%sr0, %arg0
	copy	%arg0, %arg1	/* we overwrote sr1 earlier */
	mfsp	%sr2, %arg2
	mfsp	%sr4, %arg3
	stw	%arg0, TF_SR0-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg1, TF_SR1-TRAPFRAME_SIZEOF(%sr1, %t3)
	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

#if defined(DDB) || defined(KGDB)
	/*
	 * Save hpt mask and v2p translation table pointer
	 */
	mfctl	%eirr, %arg0
	mfctl	%hptmask, %arg1
	stw	%arg0, TF_CR23-TRAPFRAME_SIZEOF(%sr1, %t3)
	stw	%arg1, TF_CR24-TRAPFRAME_SIZEOF(%sr1, %t3)

	mfctl	%vtop, %arg0
	mfctl	%cr28, %arg1
	stw	%arg0, TF_CR25-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)

	ldil	L%curlwp, %r1
	ldw	R%curlwp(%r1), %r1
	ldw	L_MD(%r1), %t3

	.exit
	.procend
	/* FALLTHROUGH */

	.export	$syscall_return, entry
	.proc
	.callinfo no_calls
	.entry
$syscall_return:
	/* t3 == VA trapframe */
	/* check for AST ? XXX */

	/* splhigh(), just in case */
	mtctl	%r0, %eiem

	/*
	 * 1a. Copy a `phys' part of the frame into temp store
	 *	(see a note for trapall)
	 *	hopefully no page fault would happen on or after the copy,
	 *	and interrupts are disabled.
	 */
	copy	%t3, %arg0
	ldil	L%$trap_tmp_save, %arg1
	ldo	R%$trap_tmp_save(%arg1), %arg1
	ldi	TF_PHYS - 4, %arg2
$syscall_return_copy_loop:
	ldwm	4(%arg0), %t1
	addib,>= -4, %arg2, $syscall_return_copy_loop
	stwm	%t1, 4(%arg1)

	/* 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.
	 */
#if defined(DDB) || defined(KGDB) || defined(FPEMUL)
	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
#endif /* DDB || KGDB || FPEMUL */
	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_CR0(%sr3, %t3), %t1
	mtctl	%t1, %rctr
	ldw	TF_CR30(%sr3, %t3), %t1
	mtctl	%t1, %cr30

	/*
	 * clear the system mask, this puts us back into physical mode.
	 * reload trapframe pointer w/ correspondent PA value.
	 * sp will be left in virtual until restored from trapframe,
	 * since we don't use it anyway.
	 */
	rsm	RESET_PSW, %r0
	nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop	/* XXX really? */

	ldil	L%$trap_tmp_save, %t3
	ldo     R%$trap_tmp_save(%t3), %t3

	/* 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
$syscall_end:

/*
 * interrupt vector table
 */
#define	TLABEL(name)	$trap$name
#define	TELABEL(num)	__CONCAT(trap_ep_,num)
#define TRAP(name,num) \
	.import TLABEL(name), code	! \
	mtctl	%r1, %tr7			! \
	ldil	L%TLABEL(name), %r1	! \
	.call				! \
	be	R%TLABEL(name)(%sr7, %r1)	! \
	ldi	num, %r1			! \
	.align	32

#define	ATRAP(name,num) \
	.export	TLABEL(name)$num, entry	! \
	.label	TLABEL(name)$num	! \
	TRAP(all,num)

#define	CTRAP(name,num,pre) \
	.export	TLABEL(name)$num, entry	! \
	.label	TLABEL(name)$num	! \
	pre				! \
	TRAP(name,num)

#define	STRAP(name,num,pre) \
	.export	TLABEL(name)$num, entry	! \
	.label	TLABEL(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

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

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

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

#ifdef HP7100_CPU
LDILDO(itlb_t)
LDILDO(dtlb_t)
LDILDO(dtlbna_t)
LDILDO(tlbd_t)
#endif

#ifdef HP7100LC_CPU
LDILDO(itlb_l)
LDILDO(dtlb_l)
LDILDO(dtlbna_l)
LDILDO(tlbd_l)
#endif

#define	ITLBPRE \
	mfctl	%pcoq,%r9		/* Offset */			! \
	mfctl	%pcsq,%r8		/* Space  */			! \
	depi	0,31,PGSHIFT,%r9	/* align offset to page */
#define	DTLBPRE \
	mfctl	%ior, %r9		/* Offset */			! \
	mfctl	%isr, %r8		/* Space  */			! \
	depi	0,31,PGSHIFT,%r9	/* align offset to page */
	/* CR28XXX according to a popular belief cr28 should be read here */
#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(itlb,T_ITLBMISSNA,ITLBPRE)/* 16. ITLB non-access miss fault */
	STRAP(dtlb,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(unk29,29)
	ATRAP(unk30,30)
	ATRAP(unk31,31)
	ATRAP(unk32,32)
	ATRAP(unk33,33)
	ATRAP(unk34,34)
	ATRAP(unk35,35)
	ATRAP(unk36,36)
	ATRAP(unk37,37)
	ATRAP(unk38,38)
	ATRAP(unk39,39)
	ATRAP(unk40,40)
	ATRAP(unk41,41)
	ATRAP(unk42,42)
	ATRAP(unk43,43)
	ATRAP(unk44,44)
	ATRAP(unk45,45)
	ATRAP(unk46,46)
	ATRAP(unk47,47)
	ATRAP(unk48,48)
	ATRAP(unk49,49)
	ATRAP(unk50,50)
	ATRAP(unk51,51)
	ATRAP(unk52,52)
	ATRAP(unk53,53)
	ATRAP(unk54,54)
	ATRAP(unk55,55)
	ATRAP(unk56,56)
	ATRAP(unk57,57)
	ATRAP(unk58,58)
	ATRAP(unk59,59)
	ATRAP(unk60,60)
	ATRAP(unk61,61)
	ATRAP(unk62,62)
	ATRAP(unk63,63)
					/* 64 */

/*
 * 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%hpt_base, %r1
	ldw	R%hpt_base(%r1), %r1
	mtctl	%r1, %vtop
	ldil	L%hpt_mask, %r1
	ldw	R%hpt_mask(%r1), %r1
	mtctl	%r1, %hptmask

	/* Disable interrupts for the long haul. */
	ldil	L%kpsw, %t1
	ldw	R%kpsw(%t1), %r1
	depi	0, PSW_I_POS, 1, %r1
	stw	%r1, R%kpsw(%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
	stw,ma	%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
	stw,ma	%r1, HPPA_FRAME_SIZE(%sp)

	/* If this is a TOC, remap the kernel. */
	comib,<>,n T_INTERRUPT, %arg0, $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

$check_do_rfi:

	/* Load IPSW. */
	ldil	L%kpsw, %r1
	ldw	R%kpsw(%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
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: emulate special
 * function unit, emulate non-FPU coprocessor, and
 * emulate FPU coprocessor, and dispatch accordingly.
 */
	.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, $emulate_sfu
	comib,=,n 0xe, %r1, hppa_fpu_nop0

	/*
	 * 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, $emulate_coproc

	/*
	 * If we're still here, this is a FPU 
	 * coprocessor instruction.  That we trapped
	 * to emulate it means one of three things.
	 *
	 * If we do not have a hardware FPU, we need
	 * to emulate this instruction.
	 *
	 * 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.
	 */

	/*
	 * As an optimization, hppa_fpu_bootstrap
	 * replaces this branch instruction with a
	 * nop if there is a hardware FPU.
	 *
	 * Otherwise, this is the branch to emulate
	 * an FPU coprocessor.
	 */
ALTENTRY(hppa_fpu_nop0)
	b,n	$emulate_fpu

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

	/*
	 * The hardware FPU is disabled, so we need to swap 
	 * in the FPU state of the process whose uspace
	 * physical address in %cr30.  We may also need 
	 * to swap out the FPU state of any process whose 
	 * uspace physical address is in the fpu_cur_uspace 
	 * variable.
	 */
	
	/*
	 * 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, %tr3
	mtctl	%rp, %tr5

	/*
	 * Call hppa_fpu_swap.
	 */
	ldil	L%fpu_cur_uspace, %arg0
	ldw	R%fpu_cur_uspace(%arg0), %arg0
	mfctl	%cr30, %arg1
	blr	0, %rp
	b	hppa_fpu_swap
	nop
	
	/* Restore registers and rfi. */
	mfctl	%tr5, %rp
	mfctl	%tr3, %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).
	 */
$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).
	 */
$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).
	 */
$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 instructions again, it assumes
	 * that these kinds of instructions have already 
	 * been translated into some other trap type (as 
	 * they have, by the above $emulate_sfu and 
	 * $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_swap(paddr_t out, paddr_t in);
 * or
 * void hppa_fpu_swap(struct pcb *out, NULL);
 */
LEAF_ENTRY_NOPROFILE(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 coprocessor instructions,
	 * so we'd better enable it.
	 * 
	 * Also, branch if there's no FPU state to swap out.
	 */
	mfctl	%ccr, %r1
	depi	3, 25, 2, %r1
	comb,=	%r0, %arg0, $fpu_swap_in
	mtctl	%r1, %ccr

	/*
	 * Swap out the current FPU state.
	 */
	ldo	PCB_FPREGS(%arg0), %arg0
	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)

	ldo	-248(%arg0), %arg0
	ldil	L%dcache_stride, %r1
	ldw	R%dcache_stride(%r1), %r1
	fdc,m	%r1(%arg0)
	fdc,m	%r1(%arg0)
	fdc,m	%r1(%arg0)
	fdc,m	%r1(%arg0)
	fdc,m	%r1(%arg0)
	fdc,m	%r1(%arg0)
	fdc,m	%r1(%arg0)
	fdc,m	%r1(%arg0)
	sync

$fpu_swap_in:

	/*
	 * Stash the incoming user structure in
	 * fpu_cur_uspace.  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 fpu_cur_uspace, branching
	 * past the swap-in code if it is zero.
	 */
	ldil	L%fpu_cur_uspace, %r1
	comb,=	%r0, %arg1, $fpu_no_swap_in
	stw	%arg1, R%fpu_cur_uspace(%r1)

	/*
	 * Swap in the new FPU state.
	 */
	ldo	PCB_FPREGS+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 */

	ldil	L%dcache_stride, %r1
	ldw	R%dcache_stride(%r1), %r1
	fdc,m	%r1(%arg1)
	fdc,m	%r1(%arg1)
	fdc,m	%r1(%arg1)
	fdc,m	%r1(%arg1)
	fdc,m	%r1(%arg1)
	fdc,m	%r1(%arg1)
	fdc,m	%r1(%arg1)
	fdc,m	%r1(%arg1)
	sync

$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)

$fpu_no_swap_in:

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

	/* Compute the hpt entry ptr */
#define	HPTENT \
	extru	%r9, 23, 24, %r16	/* r16 = (offset >> 8) */	! \
	zdep	%r8, 22, 16, %r24	/* r24 = (space << 9) */	! \
	mfctl	%hptmask, %r17		/* r17 = sizeof(HPT)-1 */	! \
	xor	%r16, %r24, %r24	/* r24 ^= r16 */		! \
	and	%r17, %r24, %r24	/* r24 &= r17 */		! \
	mfctl	%vtop, %r16		/* r16 = address of HPT table */! \
	or	%r16, %r24, %r24	/* r24 = HPT entry */

	/* 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 defined(HP7000_CPU)
/*
 * int desidhash_s(void)
 */
	.align	64
LEAF_ENTRY_NOPROFILE(desidhash_s)
ALTENTRY(desidhash_x)
	MFCPU_T(DR_CPUCFG,22)	/* t1 */
	MFCPU_T(DR_CPUCFG,22)
	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
	MTCPU_T(22,DR_CPUCFG)
	MTCPU_T(22,DR_CPUCFG)
	bv	0(%rp)
	extru	%t1, 4, 5, %ret0	/* return chip revision */
EXIT(desidhash_s)
#endif /* HP7000_CPU */

#ifdef HP7100_CPU
/*
 * int desidhash_t(void)
 */
	.align	64
LEAF_ENTRY_NOPROFILE(desidhash_t)
	MFCPU_T(DR_CPUCFG,22)	/* t1 */
	MFCPU_T(DR_CPUCFG,22)
	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
	MTCPU_T(22,DR_CPUCFG)
	MTCPU_T(22,DR_CPUCFG)
	bv	0(%rp)
	extru	%t1, 4, 5, %ret0	/* return chip revision */
EXIT(desidhash_t)
#endif

/*
 * 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
 * %r9 = data address offset
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
$tlbd_x:
$tlbd_s:
$tlbd_t:
	/*
	 * Calculate the offset of the HPT entry into %r24,
	 * and save it into %cr28 for recovery later, when
	 * we want to update the HPT.
	 */
	HPTENT
	mtctl	%r24, %cr28

	/*
	 * Search the list of mappings attached to this
	 * HPT entry until we find the faulting mapping.
	 * If we don't find it, trap to C.
	 */
	ldw	HPT_ENTRY(%r24), %r24
$hash_loop_tlbd_t:
	comb,=,n %r0, %r24, TLABEL(all)
	ldw	PV_VA(%r24), %r25
	ldw	PV_SPACE(%r24), %r17
	comb,<>,n %r9, %r25, $hash_loop_tlbd_t
	ldw	PV_HASH(%r24), %r24
	comb,<>,n %r8, %r17, $hash_loop_tlbd_t
	ldw	PV_HASH(%r24), %r24

	/*
	 * We found the faulting mapping.  If it is not
	 * marked TLB_NO_RW_ALIAS, we have to flush all 
	 * other mappings of this page.
	 */
	ldw	PV_TLBPROT(%r24), %r25
	bb,>=,n	%r25, TLB_NO_RW_ALIAS_POS, $flush_all_tlbd_t

	/*
	 * Otherwise, just calculate the HPT tag, set the 
	 * dirty bit on the mapping, and load it into the 
	 * HPT and TLB.
	 */
$load_tlbd_t:
	VTAG(%r8, %r9, %r16)
	b	$tlb_inshpt_t
	depi	1, TLB_DIRTY_POS, 1, %r25

	/*
	 * This flushes all other mappings of the page.  Since
	 * the flushing subroutine destroys %r8, %r9, and %r25,
	 * we have to reload them before we can continue.
	 */
$flush_all_tlbd_t:
	bl	$flush_all_tlbd, %r1
	nop
	copy	%r0, %r1
	DTLBPRE
	ldw	PV_TLBPROT(%r24), %r25
	b	$load_tlbd_t

/*
 * This is a handler for interruption 6, "Instruction TLB miss fault",
 * and interruption 16, "Non-access instruction TLB miss fault".  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 = instruction address space identifier
 * %r9 = instruction address offset
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
$itlb_x:
$itlb_s:
$itlb_t:
	depi	1, TFF_ITLB_POS, 1, %r1	/* mark for ITLB insert */
	/* FALLTHROUGH */

/*
 * This is a handler for interruption 15, "Data TLB miss fault",
 * and interruption 17, "Non-access data TLB miss fault".  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
 * %r9 = data address offset
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
$dtlb_x:
$dtlbna_x:
$dtlb_s:
$dtlbna_s:
$dtlb_t:
$dtlbna_t:
	/*
	 * Calculate the offset of the HPT entry into %r24,
	 * and save it into %cr28 for recovery later, when
	 * we want to update the HPT.
	 */
	HPTENT
	mtctl	%r24, %cr28

	/*
	 * Calculate the HPT tag for the faulting address
	 * and compare it against the one in the HPT entry.
	 * If they are different, we must find the mapping
	 * for the faulting address.
	 */
	ldw	HPT_TAG(%r24),%r17
	VTAG(%r8, %r9, %r16)
	comb,<>,n %r16, %r17, $tlb_gottalook_t

	/*
	 * Otherwise, do the TLB insertion using the
	 * information in the HPT entry.
	 */
	ldw	HPT_TLBPAGE(%r24), %r17
	b	$tlb_gothpt_t
	ldw	HPT_TLBPROT(%r24), %r25

$tlb_gottalook_t:
	/*
	 * Search the list of mappings attached to this
	 * HPT entry until we find the faulting mapping.
	 * If we don't find it, branch to a subhandler
	 * that checks for a non-access fault caused by
	 * an LPA instruction.
	 */
	ldw	HPT_ENTRY(%r24),%r24
$hash_loop_t:
	comb,=,n %r0, %r24, $tlbiflpa
	ldw	PV_VA(%r24),%r25
	ldw	PV_SPACE(%r24),%r17
	comb,<>,n %r9,%r25,$hash_loop_t
	ldw	PV_HASH(%r24),%r24
	comb,<>,n %r8,%r17,$hash_loop_t
	ldw	PV_HASH(%r24),%r24

	/*
	 * If this mapping is not marked TLB_REF, and 
	 * it isn't marked TLB_NO_RW_ALIAS, we have to
	 * flush any writable mapping for this page.
	 * Since TLB_REF and TLB_NO_RW_ALIAS are adjacent,
	 * we can test if they are both clear with one
	 * extru instruction.
	 */
	ldw	PV_TLBPROT(%r24),%r25
	extru,<> %r25, TLB_NO_RW_ALIAS_POS, 2, %r0
	b,n	$flush_writable_t

	/* Mark this mapping as referenced. */
	depi	1, TLB_REF_POS, 1, %r25
	/* FALLTHROUGH */

/*
 * Load the HPT entry with a mapping.  On entry:
 *
 * %r1 = C trap number
 * %r8 = address space identifier
 * %r9 = address offset
 * %r16 = HPT tag
 * %r17 = undefined
 * %r24 = mapping
 * %r25 = TLB protection
 * %cr28 = HPT entry
 */
$tlb_inshpt_t:
	stw	%r25, PV_TLBPROT(%r24)
	ldw	PV_TLBPAGE(%r24),%r17
	mfctl	%cr28, %r24

	stw	%r16, HPT_TAG(%r24)
	stw	%r25, HPT_TLBPROT(%r24)
	stw	%r17, HPT_TLBPAGE(%r24)
	/* FALLTHROUGH */

/*
 * Do the real TLB insertion.  On entry:
 *
 * %r1 = C trap number
 * %r8 = address space identifier
 * %r9 = address offset
 * %r16 = undefined
 * %r17 = TLB page
 * %r24 = undefined
 * %r25 = TLB protection
 */
$tlb_gothpt_t:
	mfsp	%sr1, %r16
	bb,<	%r1, TFF_ITLB_POS, $tlb_itlb_t
	mtsp	%r8, %sr1

	idtlba	%r17,(%sr1, %r9)
	idtlbp	%r25,(%sr1, %r9)
	nop ! nop
	mtsp	%r16, %sr1
	rfir
	nop

$tlb_itlb_t:
	iitlba	%r17,(%sr1, %r9)
	iitlbp	%r25,(%sr1, %r9)
	nop ! nop
	mtsp	%r16, %sr1
	rfir
	nop

/*
 * Flush any writable mapping in the TLB and cache, in order
 * to insert a readable mapping.  On entry:
 *
 * %r1 = C trap number
 * %r8 = undefined
 * %r9 = undefined
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = struct pv_entry * of readable mapping
 * %r25 = undefined
 */
$flush_writable_t:
	/*
	 * Branch if this was an ITLB fault.
	 */
	bb,<,n	%r1, TFF_ITLB_POS, $flush_writable_itlb_t

	/*
	 * Flush any writable mapping, then reload registers
	 * for a DTLB insertion.
	 */
	bl	$flush_writable, %r1
	nop
	copy	%r0, %r1
	DTLBPRE
	VTAG(%r8, %r9, %r16)
	ldw	PV_TLBPROT(%r24), %r25
	b	$tlb_inshpt_t
	depi	1, TLB_REF_POS, 1, %r25

$flush_writable_itlb_t:
	/*
	 * Flush any writable mapping, then reload registers
	 * for an ITLB insertion.
	 */
	bl	$flush_writable, %r1
	nop
	ldil	L%TFF_ITLB, %r1
	ITLBPRE
	VTAG(%r8, %r9, %r16)
	ldw	PV_TLBPROT(%r24), %r25
	b	$tlb_inshpt_t
	depi	1, TLB_REF_POS, 1, %r25

#ifdef HP7100LC_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

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

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

/*
 * int desidhash_l(void)
 */
LEAF_ENTRY_NOPROFILE(desidhash_l)
	MFCPU_C(DR_CPUCFG,22)	/* t1 */
	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 */
		/* set DR0_PCXL_L1ICACHE_EN ??? */
	MTCPU_C(22,DR_CPUCFG)
	bv	0(%rp)
	extru	%t1, 4, 5, %ret0	/* return chip revision */
EXIT(desidhash_l)


/*
 * This is a handler for interruption 20, "TLB dirty bit trap".  It
 * is used on the PA7100LC (PCX-L), PA7300LC (PCX-L2), 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
 * %r9 = data address offset
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
$tlbd_l:
	/*
	 * Calculate the offset of the HPT entry into %r24,
	 * and save it into %cr28 for recovery later, when
	 * we want to update the HPT.
	 */
	HPTENT
	mtctl	%r24, %cr28

	/*
	 * Search the list of mappings attached to this
	 * HPT entry until we find the faulting mapping.
	 * If we don't find it, trap to C.
	 */
	ldw	HPT_ENTRY(%r24), %r16
$hash_loop_tlbd_l:
	comb,=,n %r0, %r16, TLABEL(all)
	ldw	PV_VA(%r16), %r25
	ldw	PV_SPACE(%r16), %r17
	comb,<>,n %r9, %r25, $hash_loop_tlbd_l
	ldw	PV_HASH(%r16), %r16
	comb,<>,n %r8, %r17, $hash_loop_tlbd_l
	ldw	PV_HASH(%r16), %r16

	/*
	 * We found the faulting mapping.  If it is not
	 * marked TLB_NO_RW_ALIAS, we have to flush all 
	 * other mappings of this page.
	 */
	ldw	PV_TLBPAGE(%r16), %r17
	ldw	PV_TLBPROT(%r16), %r25
	bb,>=,n	%r25, TLB_NO_RW_ALIAS_POS, $flush_all_tlbd_l

	/*
	 * Otherwise, set the dirty bit on the mapping, and 
	 * load it into the HPT and TLB.
	 */
	b	$tlb_inshpt_l
	depi	1, TLB_DIRTY_POS, 1, %r25

	/*
	 * This flushes all other mappings of the page.  Since
	 * the flushing subroutine destroys effectively preserves
	 * only the address of the writable mapping, we have to
	 * reload all other registers before we can continue.
	 */
$flush_all_tlbd_l:
	bl	$flush_all_tlbd, %r1
	copy	%r16, %r24
	copy	%r0, %r1			/* reload %r1, 0 => DTLB */
	DTLBPRE					/* reload %r8, %r9 */
	copy	%r24, %r16			/* reload %r16 */
	mfctl	%cr28, %r24			/* reload %r24 */
	ldw	PV_TLBPAGE(%r16), %r17		/* reload %r17 */
	ldw	PV_TLBPROT(%r16), %r25		/* reload %r25 */
	b	$tlb_inshpt_l
	depi	1, TLB_DIRTY_POS, 1, %r25	/* set dirty bit */

/*
 * This is a handler for interruption 6, "Instruction TLB miss fault",
 * and interruption 16, "Non-access instruction TLB miss fault".  It
 * is used on the PA7100LC (PCX-L), PA7300LC (PCX-L2), 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
 * %r9 = instruction address offset
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
$itlb_l:
	depi	1, TFF_ITLB_POS, 1, %r1	/* mark for ITLB insert */
	/* FALLTHROUGH */

/*
 * This is a handler for interruption 17, "Non-access data TLB miss 
 * fault".  It is used on the PA7100LC (PCX-L), PA7300LC (PCX-L2), 
 * 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
 * %r9 = data address offset
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
$dtlbna_l:
	/*
	 * Calculate the offset of the HPT entry into %r24,
	 * and save it into %cr28 for recovery later, when
	 * we want to update the HPT.
	 *
	 * XXX fredette: does the PA7100LC not provide the
	 * HPT entry in %cr28 for a non-access DTLB miss?
	 * What about an ITLB miss or non-access ITLB miss?
	 * Having this here, if unnecessary, hurts the
	 * performance of all of these faults.
	 */
	HPTENT
	mtctl	%r24, %cr28
	/* FALLTHROUGH */

/*
 * This is a handler for interruption 15, "Data TLB miss fault".
 * It is used on the PA7100LC (PCX-L), PA7300LC (PCX-L2),
 * 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
 * %r9 = data address offset
 * %r16 = undefined
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
$dtlb_l:
	/*
	 * On entry, %cr28 contains the address of the 
	 * relevant HPT entry, courtesy of hpti_g's call 
	 * to PDC_TLB(8)/PDC_TLB_CONFIG(1) asking for 
	 * PDC_TLB_CURRPDE.
	 */
	mfctl	%cr28, %r24

	/*
	 * Search the list of mappings attached to this
	 * HPT entry until we find the faulting mapping.
	 * If we don't find it, branch to a subhandler
	 * that checks for a non-access fault caused by
	 * an LPA instruction.
	 */
	ldw	HPT_ENTRY(%r24), %r16
$hash_loop_l:
	comb,=,n %r0, %r16, $tlbiflpa
	ldw	PV_VA(%r16), %r25
	ldw	PV_SPACE(%r16), %r17
	comb,<>,n %r9, %r25, $hash_loop_l
	ldw	PV_HASH(%r16), %r16
	comb,<>,n %r8, %r17, $hash_loop_l
	ldw	PV_HASH(%r16), %r16

	/*
	 * If this mapping is not marked TLB_REF, and 
	 * it isn't marked TLB_NO_RW_ALIAS, we have to
	 * flush any writable mapping for this page.
	 * Since TLB_REF and TLB_NO_RW_ALIAS are adjacent,
	 * we can test if they are both clear with one
	 * extru instruction.
	 */
	ldw	PV_TLBPAGE(%r16), %r17
	ldw	PV_TLBPROT(%r16), %r25
	extru,<> %r25, TLB_NO_RW_ALIAS_POS, 2, %r0
	b,n	$flush_writable_l

	/* Mark this mapping as referenced. */
	depi	1, TLB_REF_POS, 1, %r25

/*
 * Load the HPT entry and TLB with a mapping.  On entry:
 *
 * %r1 = C trap number
 * %r8 = address space identifier
 * %r9 = address offset
 * %r16 = mapping
 * %r17 = TLB page
 * %r24 = HPT entry
 * %r25 = TLB protection
 */
$tlb_inshpt_l:
	stw	%r25, PV_TLBPROT(%r16)
	VTAG(%r8, %r9, %r16)

	stw	%r16, HPT_TAG(%r24)
	stw	%r25, HPT_TLBPROT(%r24)
	bb,<	%r1, TFF_ITLB_POS, $tlb_itlb_l
	stw	%r17, HPT_TLBPAGE(%r24)

	.word	0x04111440	; idtlbaf	r17
	.word	0x04191400	; idtlbpf	r25
	nop ! nop
	rfir
	nop

$tlb_itlb_l:
	.word	0x04110440	; iitlbaf	r17
	.word	0x04190400	; iitlbpf	r25
	nop ! nop
	rfir
	nop

/*
 * Flush any writable mapping in the TLB and cache, in order
 * to insert a readable mapping.  On entry:
 *
 * %r1 = C trap number
 * %r8 = undefined
 * %r9 = undefined
 * %r16 = struct pv_entry * of readable mapping
 * %r17 = undefined
 * %r24 = undefined
 * %r25 = undefined
 */
$flush_writable_l:
	/*
	 * Branch if this was an ITLB fault.
	 */
	bb,<,n	%r1, TFF_ITLB_POS, $flush_writable_itlb_l

	/*
	 * Flush any writable mapping, then reload registers
	 * for an HPT and DTLB insertion.
	 */
	bl	$flush_writable, %r1
	copy	%r16, %r24
	copy	%r0, %r1			/* reload %r0, 0 => DTLB */
	DTLBPRE					/* reload %r8, %r9 */
	copy	%r24, %r16			/* reload %r16 */
	ldw	PV_TLBPAGE(%r16), %r17		/* reload %r17 */
	ldw	PV_TLBPROT(%r16), %r25		/* reload %r25 */
	mfctl	%cr28, %r24			/* reload %r24 */
	b	$tlb_inshpt_l
	depi	1, TLB_REF_POS, 1, %r25		/* set ref bit */

$flush_writable_itlb_l:
	/*
	 * Flush any writable mapping, then reload registers
	 * for an HPT and ITLB insertion.
	 */
	bl	$flush_writable, %r1
	copy	%r16, %r24
	ldil	L%TFF_ITLB, %r1			/* reload %r0 */
	ITLBPRE					/* reload %r8, %r9 */
	copy	%r24, %r16			/* reload %r16 */
	ldw	PV_TLBPAGE(%r16), %r17		/* reload %r17 */
	ldw	PV_TLBPROT(%r16), %r25		/* reload %r25 */
	mfctl	%cr28, %r24			/* reload %r24 */
	b	$tlb_inshpt_l
	depi	1, TLB_REF_POS, 1, %r25		/* set ref bit */

#endif /* HP7100LC_CPU */

	.export $tlbiflpa, entry
$tlbiflpa:
	ldi	T_DTLBMISSNA, %r16
	mfctl	%iir, %r17
	comb,<>,n %r1, %r16, TLABEL(all)
	extru	%r17, 5, 6, %r16
	ldi	0x4d, %r25
	comib,<>,n 1, %r16, TLABEL(all)
	extru	%r17, 25, 8, %r16
	comb,<>,n %r25, %r16, TLABEL(all)

	/* ok, this is a miss in LPA */
	mfctl	%ipsw, %r16
	depi	1, PSW_N_POS, 1, %r16
	depi	0, 26, 27, %r17
	mtctl	%r16, %ipsw

	ldi	$tlbiflpa_zr, %r25
	bv	%r17(%r25)
$tlbiflpa_zr:
	copy	%r0, %r0	!	rfir
	copy	%r0, %r1	!	rfir
	copy	%r0, %r2	!	rfir
	copy	%r0, %r3	!	rfir
	copy	%r0, %r4	!	rfir
	copy	%r0, %r5	!	rfir
	copy	%r0, %r6	!	rfir
	copy	%r0, %r7	!	rfir
	copy	%r0, %r8	!	rfir
	copy	%r0, %r9	!	rfir
	copy	%r0, %r10	!	rfir
	copy	%r0, %r11	!	rfir
	copy	%r0, %r12	!	rfir
	copy	%r0, %r13	!	rfir
	copy	%r0, %r14	!	rfir
	copy	%r0, %r15	!	rfir
	copy	%r0, %r16	!	rfir
	copy	%r0, %r17	!	rfir
	copy	%r0, %r18	!	rfir
	copy	%r0, %r19	!	rfir
	copy	%r0, %r20	!	rfir
	copy	%r0, %r21	!	rfir
	copy	%r0, %r22	!	rfir
	copy	%r0, %r23	!	rfir
	copy	%r0, %r24	!	rfir
	copy	%r0, %r25	!	rfir
	copy	%r0, %r26	!	rfir
	copy	%r0, %r27	!	rfir
	copy	%r0, %r28	!	rfir
	copy	%r0, %r29	!	rfir
	copy	%r0, %r30	!	rfir
	copy	%r0, %r31	!	rfir

	.export	$tlb_missend, entry
$tlb_missend:

	.align	64
	.export	TLABEL(all), entry
ENTRY_NOPROFILE(TLABEL(all),0)
	/* r1 still has trap type */

	/*
	 * at this point we have:
	 *	psw copied into ipsw
	 *	psw = E(default), M(1 if HPMC, else 0)
	 *	PL = 0
	 *	r1, r8, r9, r16, r17, r24, r25 shadowed (maybe)
	 *	trap number in r1 (old r1 is saved in tr7)
	 */

	/* do not overwrite %tr4(%cr28) */
	mtctl	%t3, %tr2

	ldil	L%$trap_tmp_save, %t3
	ldo	R%$trap_tmp_save(%t3), %t3
	stw	%t1, TF_R22(%t3)	/* use ,bc */
	stw	%t2, TF_R21(%t3)

	mfctl	%tr2, %t1
	stw	%sp, TF_R30(%t3)	/* sp */
	stw	%t1, 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, TF_CR15(%t3)	/* use ,bc */
	stw	%t2, TF_CR22(%t3)

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

	/*
	 * Setup kernel context
	 */

	ldi	HPPA_PID_KERNEL,%t1
	mtctl	%t1, %pidr1
	mtsp	%r0, %sr3

	/* this will enable interrupts after `cold' */
	ldil	L%kpsw, %t1
	ldw	R%kpsw(%t1), %t2
	mtctl	%r0, %eiem
	mtctl	%t2, %ipsw

	mfctl	%pcsq, %t1
	mtctl	%r0, %pcsq
	mfctl	%pcsq, %t2
	stw	%t1, TF_IISQH(%t3)	/* use ,bc */
	stw	%t2, 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
	/* Start aligning the assumed kernel sp. */
	ldo	HPPA_FRAME_SIZE-1(%sp), %sp
	/* This dep leaves t2 with SYSCALLGATE | (pcoqh & PAGE_MASK). */
	dep	%t1, 31, PGSHIFT, %t2
	/* Nullify if pcoqh & HPPA_PC_PRIV_MASK != 0. */
	dep,<>	%t1, 31, 2, %r0
	/* Branch if (pcoqh & ~PAGE_MASK) != SYSCALLGATE */
	comb,<> %t1, %t2, $trap_from_kernel
	/* Finish aligning the assumed kernel sp. */
	dep	%r0, 31, 6, %sp

	mfctl	%cr30, %t2
	depi	1, T_USER_POS, 1, %r1
	depi	1, TFF_LAST_POS, 1, %r1
	ldw	U_PCB+PCB_UVA(%t2), %sp
#ifdef DIAGNOSTIC
	b	$trap_have_stack
#endif
	ldo	NBPG(%sp), %sp

$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	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, $trap_have_stack
	mfctl	%tr2, %t1
	mfctl	%isr, %t2
	comib,<>,n HPPA_SID_KERNEL, %t2, $trap_have_stack
#define _CHECK_TRAP_TYPE(tt) ldi tt, %t2 ! comb,= %r1, %t2, $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, $trap_have_stack
#undef _CHECK_TRAP_TYPE
$trap_kstack_fault:
	ldil	L%emergency_stack_start, %sp
	ldo	R%emergency_stack_start(%sp), %sp
$trap_have_stack:
#endif
	ldil	L%$trapnowvirt, %t2
	ldo	R%$trapnowvirt(%t2), %t2
	mtctl	%t2, %pcoq
	stw	%t1, TF_IIOQH(%t3)
	ldo	4(%t2), %t2
	mfctl	%pcoq, %t1
	stw	%t1, TF_IIOQT(%t3)
	mtctl	%t2, %pcoq

	mfctl	%isr, %t1
	mfctl	%ior, %t2
	stw	%t1, TF_CR20(%t3)	/* use ,bc */
	stw	%t2, TF_CR21(%t3)

	mfctl	%iir, %t2
	stw	%t2, TF_CR19(%t3)
	stw	%r1, TF_FLAGS(%t3)
	mfctl	%tr7, %r1

	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
	 */
	ldo	HPPA_FRAME_MAXARGS+HPPA_FRAME_SIZE-1(%sp),%sp
	depi	0, 31, 6, %sp
#endif
	rfir
	nop
$trapnowvirt:
	/*
	 * t3 contains the virtual address of the trapframe
	 * sp is loaded w/ the right VA (we did not need it being physical)
	 */

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

#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
	 */
	ldil	L%$trap_tmp_save, %t2
	ldo	R%$trap_tmp_save(%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.
	 */
#if defined(DDB) || defined(KGDB) || defined(FPEMUL)
	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)
#endif /* DDB || KGDB || FPEMUL */
	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.
	 */

	mfctl	%rctr, %t1
	stw	%t1, TF_CR0(%t3)
	/* XXX save %ccr here w/ rctr */

#if defined(DDB) || defined(KGDB)
	/*
	 * Save hpt mask and v2p translation table pointer
	 */
	mfctl	%eirr, %t1
	mfctl	%hptmask, %t2
	stw	%t1, TF_CR23(%t3)
	stw	%t2, TF_CR24(%t3)

	mfctl	%vtop, %t1
	mfctl	%cr28, %t2
	stw	%t1, TF_CR25(%t3)
	stw	%t2, TF_CR28(%t3)
#endif
	mfctl	%cr30, %t1
	stw	%t1, 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, $trap_return
	nop

	/* load curlwp's trapframe pointer */
	ldil	L%curlwp, %t1
	ldw	R%curlwp(%t1), %t2
	ldw	L_MD(%t2), %t3

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

	.export	$trap$all$end, entry
$trap$all$end:
EXIT(TLABEL(all))

	.align	32
	.export	TLABEL(ibrk), entry
ENTRY_NOPROFILE(TLABEL(ibrk),0)
	mtctl	%t1, %tr2
	mtctl	%t2, %tr3

	/* If called by a user process then always pass it to trap() */
	mfctl	%pcoq, %t1
	extru,=	%t1, 31, 2, %r0
	b,n	$ibrk_bad

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

	mfctl	%iir, %t1
	extru	%t1, 31, 5, %t2
	comib,<>,n HPPA_BREAK_KERNEL, %t2, $ibrk_bad

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

$ibrk_bad:
	/* illegal (unimplemented) break entry point */
	mfctl	%tr3, %t2
	b	TLABEL(all)
	mfctl	%tr2, %t1

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

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

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

$ibrk_exit:
	/* skip the break */
	mtctl	%r0, %pcoq
	mfctl	%pcoq, %t1
	mtctl	%t1, %pcoq
	ldo	4(%t1), %t1
	mtctl	%t1, %pcoq
	mfctl	%tr3, %t2
	mfctl	%tr2, %t1
	mfctl	%tr7, %r1
	rfi
	nop
EXIT(TLABEL(ibrk))

/*
 * This macro changes the tlbpage register from a tlbpage 
 * value into the struct pv_head * for that page.  It also 
 * junks another register.
 *
 * The extru gets the physical page number out of the tlbpage, 
 * and since sizeof(struct pv_head) == 8, we can use sh3add to 
 * generate the final struct pv_head *.
 */
#define PV_HEAD(tlbpage, junk)			  \
	ldil	L%pv_head_tbl, junk		! \
	extru	tlbpage, 26, 20, tlbpage	! \
	ldw	R%pv_head_tbl(junk), junk	! \
	sh3add	tlbpage, junk, tlbpage

/*
 * This macro copies the referenced and dirty information
 * from a TLB protection to a pv_head_writable_dirty_ref
 * value, but only if the TLB protection is managed.  The
 * magic 12 targets the instruction after the macro.
 */
#define PV_MODREF(tlbprot, pv_head_wdr)			  \
	bb,<,n	tlbprot, TLB_UNMANAGED_POS, 12		! \
	extru,=	tlbprot, TLB_REF_POS, 1, %r0		! \
	depi	1, PV_HEAD_REF_POS, 1, pv_head_wdr	! \
	extru,= tlbprot, TLB_DIRTY_POS, 1, %r0		! \
	depi	1, PV_HEAD_DIRTY_POS, 1, pv_head_wdr

/*
 * This subroutine flushes all mappings of a page out of the TLB
 * and cache, in order to install a writable mapping.  Only shadowed 
 * registers are available, and they are:
 *
 * %r1 = INPUT: return address, OUTPUT: preserved
 * %r8 = INPUT: undefined, OUTPUT: destroyed
 * %r9 = INPUT: undefined, OUTPUT: destroyed
 * %r16 = INPUT: undefined, OUTPUT: destroyed
 * %r17 = INPUT: undefined, OUTPUT: destroyed
 * %r24 = INPUT: struct pv_entry * of writable mapping, OUTPUT: preserved
 * %r25 = INPUT: undefined, OUTPUT: destroyed
 */
$flush_all_tlbd:

	/*
	 * Find the struct pv_head for this physical page. 
	 * If this writable mapping is managed, we know 
	 * for sure that this page is now referenced
	 * and dirty.  The ldi sets both PV_HEAD_REF_POS 
	 * and PV_HEAD_DIRTY_POS.
	 */
	ldw	PV_TLBPAGE(%r24), %r17
	ldw	PV_TLBPROT(%r24), %r25
	PV_HEAD(%r17, %r16)
	ldi	3, %r16
	bb,>=,n	%r25, TLB_UNMANAGED_POS, $flush_all_tlbd_set_wdr

	/*
	 * Otherwise, this writable mapping is unmanaged,
	 * which means we need to save referenced and dirty
	 * information from all managed mappings that we're
	 * about to flush.
	 */
	copy	%r0, %r16
	ldw	PV_HEAD_PVS(%r17), %r8
	ldw	PV_TLBPROT(%r8), %r25
$flush_all_tlbd_modref_loop:
	ldw	PV_NEXT(%r8), %r8
	PV_MODREF(%r25, %r16)
	comb,<>,n %r0, %r8, $flush_all_tlbd_modref_loop
	ldw	PV_TLBPROT(%r8), %r25

$flush_all_tlbd_set_wdr:
	/*
	 * Now set the pv_head_writable_dirty_ref field,
	 * preserving any previously set referenced and
	 * dirty bits, and noting that this writable
	 * mapping is the current writable mapping.
	 */
	ldw	PV_HEAD_WRITABLE_DIRTY_REF(%r17), %r8
	or	%r24, %r16, %r16
	depi	0, PV_HEAD_WRITABLE_POS, 30, %r8
	or	%r16, %r8, %r8
	stw	%r8, PV_HEAD_WRITABLE_DIRTY_REF(%r17)

	/*
	 * Now flush all other mappings out of the cache.
	 */
	ldw	PV_HEAD_PVS(%r17), %r17
	bl	0, %r16
$flush_all_tlbd_loop:
	comb,<> %r17, %r24, $flush_mapping
	nop
	ldw	PV_NEXT(%r17), %r17
	comb,<>	%r17, %r0, $flush_all_tlbd_loop
	nop

	/* Return to our caller. */
	bv	%r0(%r1)
	nop

/*
 * This subroutine flushes any writable mapping of a page out of the 
 * TLB and cache, in order to install a readable mapping.  Only shadowed 
 * registers are available, and they are:
 *
 * %r1 = INPUT: return address, OUTPUT: preserved
 * %r8 = INPUT: undefined, OUTPUT: destroyed
 * %r9 = INPUT: undefined, OUTPUT: destroyed
 * %r16 = INPUT: undefined, OUTPUT: destroyed
 * %r17 = INPUT: undefined, OUTPUT: destroyed
 * %r24 = INPUT: struct pv_entry * of readable mapping, OUTPUT: preserved
 * %r25 = INPUT: undefined, OUTPUT: destroyed
 */
$flush_writable:

	/* Find the struct pv_head for this physical page. */
	ldw	PV_TLBPAGE(%r24), %r17
	PV_HEAD(%r17, %r16)

	/*
	 * If this page doesn't have a writable mapping
	 * entered into the TLB and cache, return.  The 
	 * single depi clears both PV_HEAD_REF_POS and 
	 * PV_HEAD_DIRTY_POS.
         */
	ldw	PV_HEAD_WRITABLE_DIRTY_REF(%r17), %r16
	copy	%r16, %r8
	depi,<>	0, PV_HEAD_REF_POS, 2, %r16
	bv,n	%r0(%r1)

	/*
	 * Clear the writable mapping, preserving the
	 * current referenced and dirty bits.
	 */
	depi	0, PV_HEAD_WRITABLE_POS, 30, %r8
	stw	%r8, PV_HEAD_WRITABLE_DIRTY_REF(%r17)

	/*
	 * Flush the writable mapping.  We have $flush_mapping 
	 * return directly to our caller.
	 */
	copy	%r16, %r17
	b	$flush_mapping
	copy	%r1, %r16

/*
 * This flushes a mapping out of the TLB and cache.  Only shadowed 
 * registers are available, and on entry they are:
 *
 * %r1 = INPUT: undefined, OUTPUT: preserved
 * %r8 = INPUT: undefined, OUTPUT: destroyed
 * %r9 = INPUT: undefined, OUTPUT: destroyed
 * %r16 = INPUT: return address, OUTPUT: preserved
 * %r17 = INPUT: mapping to flush, OUTPUT: preserved
 * %r24 = INPUT: undefined, OUTPUT: preserved
 * %r25 = INPUT: undefined, OUTPUT: destroyed
 *
 * NB: this function assumes that, once inserted, a TLB entry
 * remains inserted as long as the only virtual references made 
 * use that TLB entry.  This function must be called in physical 
 * mode.
 */
$flush_mapping:

	/*
	 * If this mapping has not been referenced, it cannot
	 * be in the TLB and cache, so just return.
	 */
	ldw	PV_TLBPROT(%r17), %r25
	extru,<> %r25, TLB_REF_POS, 1, %r0
	bv,n	%r0(%r16)
	
	/*
	 * Clear the referenced and dirty bits, save %sr1 
	 * into %r8, load the space into %sr1, load the 
	 * virtual address into %r9.  If the mapping is
	 * not executable, skip the instruction cache flush.
	 * (Bit 6 is the "execute" bit in the TLB protection,
	 * assuming that we never see gateway page protections.)
	 */
	ldw	PV_SPACE(%r17), %r9
	depi	0, TLB_REF_POS, 1, %r25
	mfsp	%sr1, %r8
	depi	0, TLB_DIRTY_POS, 1, %r25
	mtsp	%r9, %sr1
	ldw	PV_VA(%r17), %r9
	bb,>=	%r25, 6, $flush_mapping_data
	stw	%r25, PV_TLBPROT(%r17)

	/*
	 * Load this mapping into the ITLB and the DTLB, since
	 * fic may use the DTLB.
	 */
	ldw	PV_TLBPAGE(%r17), %r25
	iitlba	%r25, (%sr1, %r9)
	idtlba	%r25, (%sr1, %r9)
	ldw	PV_TLBPROT(%r17), %r25
	iitlbp	%r25, (%sr1, %r9)
	idtlbp	%r25, (%sr1, %r9)
	nop
	nop

	/* Load the instruction cache stride. */
	ldil	L%icache_stride, %r25
	ldw	R%icache_stride(%r25), %r25

	/*
	 * Enable data address translation (fic explicitly uses 
	 * the D-bit to determine whether virtual or absolute 
	 * addresses are used), and flush the instruction cache 
	 * using a loop of 16 fic instructions. */
	ssm	PSW_D, %r0
$flush_mapping_fic_loop:
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	fic,m	%r25(%sr1, %r9)
	/* Stop looping if the page offset bits are all zero. */
	extru,= %r9, 31, PGSHIFT, %r0
	b,n	$flush_mapping_fic_loop

	/* Sync. */
	sync
	nop
	nop
	syncdma

	/* Disable data address translation. */
	rsm	PSW_D, %r0

	/* Reload the virtual address and purge the ITLB. */
	ldw	PV_VA(%r17), %r9
	pitlb	%r0(%sr1, %r9)

$flush_mapping_data:

	/* Load this mapping into the DTLB. */
	ldw	PV_TLBPAGE(%r17), %r25
	idtlba	%r25, (%sr1, %r9)
	ldw	PV_TLBPROT(%r17), %r25
	idtlbp	%r25, (%sr1, %r9)
	nop
	nop

	/* Load the data cache stride. */
	ldil	L%dcache_stride, %r25
	ldw	R%dcache_stride(%r25), %r25

	/*
	 * Enable data address translation and flush the data cache 
	 * using a loop of 16 fdc instructions. */
	ssm	PSW_D, %r0
$flush_mapping_fdc_loop:
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	fdc,m	%r25(%sr1, %r9)
	/* Stop looping if the page offset bits are all zero. */
	extru,= %r9, 31, PGSHIFT, %r0
	b,n	$flush_mapping_fdc_loop

	/* Sync. */
	sync
	nop
	nop
	syncdma

	/* Disable data address translation. */
	rsm	PSW_D, %r0

	/* Reload the virtual address and purge the DTLB. */
	ldw	PV_VA(%r17), %r9
	pdtlb	%r0(%sr1, %r9)

	/* Restore %sr1. */
	mtsp	%r8, %sr1

	/*
	 * If this mapping is in the HPT, invalidate the
	 * HPT entry.
	 */
	ldw	PV_SPACE(%r17), %r8
	ldw	PV_HPT(%r17), %r25
	VTAG(%r8, %r9, %r9)
	ldw	HPT_TAG(%r25), %r8
	comb,<>,n %r8, %r9, $flush_mapping_done
	zdepi	-1, 31, 16, %r8	/* Make an invalid HPT tag. */
	stw	%r8, HPT_TAG(%r25)

$flush_mapping_done:
	/* Return. */
	bv	%r0(%r16)
	nop

/*
 * void __pmap_pv_update(paddr_t pa, struct pv_entry *pv,
 * 			 u_int tlbprot_clear, u_int tlbprot_set);
 *
 * This is the helper function for _pmap_pv_update.  It flushes
 * one or more mappings of page pa and changes their protection.
 * tlbprot_clear and tlbprot_set describe the protection changes.
 * If pv is non-NULL, that mapping is flushed and takes all of
 * the protection changes.  If tlbprot_clear features any of
 * TLB_REF, TLB_DIRTY, and TLB_NO_RW_ALIAS, all other mappings of
 * the page are flushed and take only those protection changes.
 *
 * All of this work is done in physical mode.
 */
ENTRY(__pmap_pv_update,64)

	/* Start stack calling convention. */
	stw	%rp, HPPA_FRAME_CRP(%sp)
	copy	%r3, %r1 
	copy	%sp, %r3
	stw,ma	%r1, HPPA_FRAME_SIZE*2(%sp)

	/*
	 * Save various callee-saved registers.  Note that as
	 * part of the stack calling convention, the caller's
	 * %r3 was saved at 0(%r3).
	 */
	stw	%r8, 4(%r3)
	stw	%r9, 8(%r3)
	stw	%r16, 12(%r3)
	stw	%r17, 16(%r3)

	/*
	 * Disable interrupts and enter physical mode, but leave the 
	 * interruption queues on.  This will leave the previous PSW 
	 * in %ret0, where it will remain untouched until we're ready 
	 * to return to virtual mode.
	 */
	copy	%arg0, %t1
	ldi	PSW_Q, %arg0
	break	HPPA_BREAK_KERNEL, HPPA_BREAK_SET_PSW
	copy	%t1, %arg0
	
	/*
	 * Find the struct pv_head for this physical page.
	 * If this is a page in I/O space, conjure up a fake 
	 * struct pv_head on the stack.
	 */
	shd	%r0, %arg0, (PGSHIFT - 5), %t1	/* t1 = tlbbtop(%arg0) */
	ldil	L%HPPA_IOSPACE, %t2
	copy	%r0, %r1			/* fake pv_head_wrd value */
	comb,<<= %t2, %arg0, $pmap_pv_update_given
	ldo	20(%r3), %arg0			/* fake pv_head pointer */
	copy	%t1, %arg0
	PV_HEAD(%arg0, %t1)
	ldw	PV_HEAD_WRITABLE_DIRTY_REF(%arg0), %r1

	/*
	 * This helper macro saves TLB_REF and TLB_DIRTY from
	 * a managed mapping, and if that mapping is the currently 
	 * loaded writable mapping, clears that.  Then it
	 * flushes the mapping (saving %r25 around the call
	 * to $flush_mapping) and updates its protection.
	 * It destroys the t1 register.
	 */
#define PV_UPDATE(pv)				  \
	ldw	PV_TLBPROT(pv), %t1		! \
	PV_MODREF(%t1, %r1)			! \
	copy	%r1, %t1			! \
	depi	0, PV_HEAD_REF_POS, 2, %t1	! \
	comb,<>,n %t1, pv, 0			! \
	depi	0, PV_HEAD_WRITABLE_POS, 30, %r1 ! \
	copy	%r25, %t1			! \
	bl	$flush_mapping, %r16		! \
	copy	pv, %r17			! \
	copy	%t1, %r25			! \
	ldw	PV_TLBPROT(pv), %t1		! \
	andcm	%t1, %arg2, %t1			! \
	or	%t1, %arg3, %t1			! \
	stw	%t1, PV_TLBPROT(pv)

$pmap_pv_update_given:

	/* If pv is non-NULL, flush and update that mapping. */
	comb,=,n %r0, %arg1, $pmap_pv_update_others
	PV_UPDATE(%arg1)

$pmap_pv_update_others:

	/*
	 * If none of TLB_REF, TLB_DIRTY, or TLB_NO_RW_ALIAS are
	 * set in tlbprot_clear, we're done.
	 */
	zdepi	1, TLB_REF_POS, 1, %t1
	depi	1, TLB_DIRTY_POS, 1, %t1
	depi	1, TLB_NO_RW_ALIAS_POS, 1, %t1
	and,<>	%arg2, %t1, %arg2
	b	$pmap_pv_update_done
	and	%arg3, %t1, %arg3

	/*
	 * Otherwise, update all of the mappings of this page,
	 * skipping any mapping we did above.
	 */
	ldw	PV_HEAD_PVS(%arg0), %r17
$pmap_pv_update_loop:
	comb,=,n %r17, %r0, $pmap_pv_update_done
	comb,=,n %r17, %arg1, $pmap_pv_update_loop
	ldw	PV_NEXT(%r17), %r17
	PV_UPDATE(%r17)
	b	$pmap_pv_update_loop
	ldw	PV_NEXT(%r17), %r17

$pmap_pv_update_done:

	/* Store the new pv_head_writable_ref_dirty value. */
	stw	%r1, PV_HEAD_WRITABLE_DIRTY_REF(%arg0)

	/*
	 * Return to virtual mode and reenable interrupts.
	 */
	copy	%ret0, %arg0
	break	HPPA_BREAK_KERNEL, HPPA_BREAK_SET_PSW

	/*
	 * Restore various callee-saved registers.  Note that
	 * as part of the stack calling convention, the caller's
	 * %r3 was saved at 0(%r3) and is restored at the end.
	 */
	ldw	4(%r3), %r8
	ldw	8(%r3), %r9
	ldw	12(%r3), %r16
	ldw	16(%r3), %r17

        /* End stack calling convention. */
	ldw	HPPA_FRAME_CRP(%r3), %rp
	ldo	HPPA_FRAME_SIZE(%r3), %sp
	ldw,mb	-HPPA_FRAME_SIZE(%sp), %r3
	
	/* Return. */
	bv	%r0(%rp)
	nop
EXIT(__pmap_pv_update)