patch-2.4.0-test12 linux/arch/parisc/kernel/entry.S

Next file: linux/arch/parisc/kernel/hardware.c
Previous file: linux/arch/parisc/kernel/drivers.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test11/linux/arch/parisc/kernel/entry.S linux/arch/parisc/kernel/entry.S
@@ -0,0 +1,1867 @@
+/*------------------------------------------------------------------------------
+ * Native PARISC/Linux Project (http://www.puffingroup.com/parisc)
+ *
+ * kernel entry points (interruptions, system call wrappers)
+ *  Copyright (C) 1999,2000 Philipp Rumpf 
+ *  Copyright (C) 1999 SuSE GmbH Nuernberg 
+ *  Copyright (C) 2000 Hewlett-Packard (John Marvin)
+ *  Copyright (C) 1999 Hewlett-Packard (Frank Rowand)
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2, or (at your option)
+ *    any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <asm/offset.h>
+
+/* the following is the setup i think we should follow:
+ * whenever the CPU is interruptible, the following has to be true:
+ *  CR30 is the kernel sp or 0 if we currently use the kernel stack
+ *  CR31 is the kernel gp */ 
+
+/* we have the following possibilities to act on an interruption:
+ *  - handle in assembly and use shadowed registers only
+ *  - save registers to kernel stack and handle in assembly or C */
+
+	.text
+
+#ifdef __LP64__
+	.level 2.0w
+#endif
+
+#define __ASSEMBLY__
+#include <asm/assembly.h>	/* for LDREG/STREG defines */
+#include <asm/pgtable.h>
+#include <asm/psw.h>
+#include <asm/signal.h>
+
+#ifdef __LP64__
+#define FRAME_SIZE	64
+#else
+#define FRAME_SIZE	64
+#endif
+
+	/* Switch to virtual mapping, trashing only %r1 */
+	.macro	virt_map rfi_type
+	mtsm	%r0
+	tovirt	%r29
+	tovirt	%r30
+	mfsp	%sr7, %r1
+	mtsp	%r1, %sr3
+	mtsp	%r0, %sr4
+	mtsp	%r0, %sr5
+	mtsp	%r0, %sr6
+	mtsp	%r0, %sr7
+	ldil	L%KERNEL_PSW, %r1
+	ldo	R%KERNEL_PSW(%r1), %r1
+	LDIL_FIXUP(%r1)
+	mtctl	%r1, %cr22
+	mtctl	%r0, %cr17
+	mtctl	%r0, %cr17
+	ldil	L%.+28, %r1
+	ldo	R%.+24(%r1), %r1
+	LDIL_FIXUP(%r1)
+	mtctl	%r1, %cr18
+	ldo	4(%r1), %r1
+	mtctl	%r1, %cr18
+	\rfi_type
+	nop
+	.endm
+
+	.macro	get_stack
+	mfctl	%cr30, %r1 
+	comib,=,n 0, %r1, 0f   /* forward so predicted not taken */
+
+	/* we save the registers in the task struct */
+	ldo	TASK_REGS(%r1), %r29
+	tophys	%r29
+	STREG	%r30, PT_GR30(%r29)
+	STREG	%r1,  PT_CR30(%r29)
+	ldo	TASK_SZ_ALGN(%r1), %r30
+	b	1f		    /* unconditional so predicted taken */	
+	mtctl	%r0,%cr30
+0:
+	/* we put a struct pt_regs on the stack and save the registers there */
+	copy	%r30,%r29
+	ldo	PT_SZ_ALGN(%r30),%r30
+	tophys	%r29
+	STREG	%r30,PT_GR30(%r29)
+	STREG	%r0,PT_CR30(%r29)
+1:
+	.endm
+
+	.macro	rest_stack regs
+	LDREG	PT_CR30(\regs), %r1
+	comib,=,n 0, %r1, 2f/* forward so predicted not taken */
+
+	/* we restore the registers out of the task struct */
+	mtctl	%r1, %cr30
+	LDREG	PT_GR1(\regs), %r1
+	LDREG	PT_GR30(\regs),%r30
+	b	3f
+	LDREG	PT_GR29(\regs),%r29
+2:
+	/* we take a struct pt_regs off the stack */
+	LDREG	PT_GR1(\regs),  %r1
+	LDREG	PT_GR29(\regs), %r29
+	ldo	-PT_SZ_ALGN(%r30), %r30
+3:
+	.endm
+
+#ifdef OLD
+	/* fixme interruption handler */
+	.macro	def code
+	/* WARNING!!! THIS IS DEBUG CODE ONLY!!! */
+	b	unimplemented_64bitirq
+	ldi	\code, %r1
+	.align	32
+	.endm
+
+	/* Use def to enable break - KWDB wants em
+	 * (calls traps.c:handle_interruption) */
+	.macro	pass_break code
+
+#else
+	/* default interruption handler
+	 * (calls traps.c:handle_interruption) */
+	.macro	def code
+#endif
+	mtctl	%r29, %cr31
+	mtctl	%r1,  %cr28
+	ldi	\code, %r1
+	b	intr_save
+	mtctl   %r1, %cr29
+	.align	32
+	.endm
+
+	/* Interrupt interruption handler
+	 * (calls irq.c:do_irq_mask) */
+	.macro	extint code
+	mtctl	%r29, %cr31
+	mtctl	%r1,  %cr28
+	mfctl	%cr23, %r1
+	mtctl	%r1, %cr23
+	b	intr_extint
+	mtctl	%r1, %cr29
+	.align	32
+	.endm	
+
+	.import	os_hpmc, code
+
+	/* HPMC handler */
+	.macro	hpmc code
+	nop			/* must be a NOP, will be patched later */
+	ldil	L%PA(os_hpmc), %r3
+	ldo	R%PA(os_hpmc)(%r3), %r3
+	bv,n	0(%r3)
+	nop
+	.word	0		/* checksum (will be patched) */
+	.word	PA(os_hpmc)	/* address of handler */
+	.word	0		/* length of handler */
+	.endm
+
+	/*
+	 * Performance Note: Instructions will be moved up into
+	 * this part of the code later on, once we are sure
+	 * that the tlb miss handlers are close to final form.
+	 */
+
+	/* Register definitions for tlb miss handler macros */
+
+	va  = r8	/* virtual address for which the trap occured */
+	spc = r24	/* space for which the trap occured */
+
+#ifndef __LP64__
+
+	/*
+	 * itlb miss interruption handler (parisc 1.1 - 32 bit)
+	 */
+
+	.macro	itlb_11 code
+
+	mfctl	%pcsq, spc
+	b	itlb_miss_11
+	mfctl	%pcoq, va
+
+	.align		32
+	.endm
+#endif
+	
+	/*
+	 * itlb miss interruption handler (parisc 2.0)
+	 */
+
+	.macro	itlb_20 code
+
+	mfctl	%pcsq, spc
+#ifdef __LP64__
+	b       itlb_miss_20w
+#else
+	b	itlb_miss_20
+#endif
+	mfctl	%pcoq, va
+
+	.align		32
+	.endm
+	
+#ifndef __LP64__
+	/*
+	 * naitlb miss interruption handler (parisc 1.1 - 32 bit)
+	 *
+	 * Note: naitlb misses will be treated
+	 * as an ordinary itlb miss for now.
+	 * However, note that naitlb misses
+	 * have the faulting address in the
+	 * IOR/ISR.
+	 */
+
+	.macro	naitlb_11 code
+
+	mfctl	%isr,spc
+	b	itlb_miss_11
+	mfctl 	%ior,va
+	/* FIXME: If user causes a naitlb miss, the priv level may not be in
+	 * lower bits of va, where the itlb miss handler is expecting them
+	 */
+
+	.align		32
+	.endm
+#endif
+	
+	/*
+	 * naitlb miss interruption handler (parisc 2.0)
+	 *
+	 * Note: naitlb misses will be treated
+	 * as an ordinary itlb miss for now.
+	 * However, note that naitlb misses
+	 * have the faulting address in the
+	 * IOR/ISR.
+	 */
+
+	.macro	naitlb_20 code
+
+	mfctl	%isr,spc
+#ifdef __LP64__
+	b       itlb_miss_20w
+#else
+	b	itlb_miss_20
+#endif
+	mfctl 	%ior,va
+	/* FIXME: If user causes a naitlb miss, the priv level may not be in
+	 * lower bits of va, where the itlb miss handler is expecting them
+	 */
+
+	.align		32
+	.endm
+	
+#ifndef __LP64__
+	/*
+	 * dtlb miss interruption handler (parisc 1.1 - 32 bit)
+	 */
+
+	.macro	dtlb_11 code
+
+	mfctl	%isr, spc
+	b	dtlb_miss_11
+	mfctl	%ior, va
+
+	.align		32
+	.endm
+#endif
+
+	/*
+	 * dtlb miss interruption handler (parisc 2.0)
+	 */
+
+	.macro	dtlb_20 code
+
+	mfctl	%isr, spc
+#ifdef __LP64__
+	b       dtlb_miss_20w
+#else
+	b	dtlb_miss_20
+#endif
+	mfctl	%ior, va
+
+	.align		32
+	.endm
+	
+#ifndef __LP64__
+	/* nadtlb miss interruption handler (parisc 1.1 - 32 bit)
+	 *
+	 * Note: nadtlb misses will be treated
+	 * as an ordinary dtlb miss for now.
+	 *
+	 */
+
+	.macro	nadtlb_11 code
+
+	mfctl	%isr,spc
+	b	dtlb_miss_11
+	mfctl	%ior,va
+
+	.align		32
+	.endm
+#endif
+	
+	/* nadtlb miss interruption handler (parisc 2.0)
+	 *
+	 * Note: nadtlb misses will be treated
+	 * as an ordinary dtlb miss for now.
+	 *
+	 */
+
+	.macro	nadtlb_20 code
+
+	mfctl	%isr,spc
+#ifdef __LP64__
+	b       dtlb_miss_20w
+#else
+	b	dtlb_miss_20
+#endif
+	mfctl	%ior,va
+
+	.align		32
+	.endm
+	
+#ifndef __LP64__
+	/*
+	 * dirty bit trap interruption handler (parisc 1.1 - 32 bit)
+	 */
+
+	.macro	dbit_11 code
+
+	mfctl	%isr,spc
+	b	dbit_trap_11
+	mfctl	%ior,va
+
+	.align		32
+	.endm
+#endif
+
+	/*
+	 * dirty bit trap interruption handler (parisc 2.0)
+	 */
+
+	.macro	dbit_20 code
+
+	mfctl	%isr,spc
+#ifdef __LP64__
+	b       dbit_trap_20w
+#else
+	b	dbit_trap_20
+#endif
+	mfctl	%ior,va
+
+	.align		32
+	.endm
+
+	/*
+	 * Align fault_vector_20 on 4K boundary so that both
+	 * fault_vector_11 and fault_vector_20 are on the
+	 * same page. This is only necessary as long as we
+	 * write protect the kernel text, which we may stop
+	 * doing once we use large parge translations to cover
+	 * the static part of the kernel address space.
+	 */
+
+
+	.export fault_vector_20
+
+	.align 4096
+
+fault_vector_20:
+	/* First vector is invalid (0) */
+	.ascii	"cows can fly"
+	.byte 0
+	.align 32
+
+	hpmc		 1
+	def		 2
+	def		 3
+	extint		 4
+	def		 5
+	itlb_20		 6
+	def		 7
+	def		 8
+	def              9
+	def		10
+	def		11
+	def		12
+	def		13
+	def		14
+	dtlb_20		15
+	naitlb_20	16
+	nadtlb_20	17
+	def		18
+	def		19
+	dbit_20		20
+	def		21
+	def		22
+	def		23
+	def		24
+	def		25
+	def		26
+	def		27
+	def		28
+	def		29
+	def		30
+	def		31
+
+#ifndef __LP64__
+
+	.export fault_vector_11
+	
+	.align 2048
+
+fault_vector_11:
+	/* First vector is invalid (0) */
+	.ascii	"cows can fly"
+	.byte 0
+	.align 32
+
+	hpmc		 1
+	def		 2
+	def		 3
+	extint		 4
+	def		 5
+	itlb_11		 6
+	def		 7
+	def		 8
+	def              9
+	def		10
+	def		11
+	def		12
+	def		13
+	def		14
+	dtlb_11		15
+	naitlb_11	16
+	nadtlb_11	17
+	def		18
+	def		19
+	dbit_11		20
+	def		21
+	def		22
+	def		23
+	def		24
+	def		25
+	def		26
+	def		27
+	def		28
+	def		29
+	def		30
+	def		31
+
+#endif
+
+	.import		handle_interruption,code
+	.import		handle_real_interruption,code
+	.import		do_irq_mask,code
+	.import		parisc_stopkernel,code
+	.import		cpu_irq_region,data
+
+	/*
+	 * r26 = function to be called
+	 * r25 = argument to pass in
+	 * r24 = flags for do_fork()
+	 *
+	 * Kernel threads don't ever return, so they don't need
+	 * a true register context. We just save away the arguments
+	 * for copy_thread/ret_ to properly set up the child.
+	 */
+
+#define CLONE_VM 0x100	/* Must agree with <linux/sched.h> */
+
+	.export __kernel_thread, code
+	.import do_fork
+__kernel_thread:
+	STREG	%r2, -RP_OFFSET(%r30)
+
+	copy	%r30, %r1
+	ldo	PT_SZ_ALGN(%r30),%r30
+#ifdef __LP64__
+	/* Yo, function pointers in wide mode are little structs... -PB */
+	/* XXX FIXME do we need to honor the fptr's %dp value too? */
+	ldd	16(%r26), %r26
+#endif
+	STREG	%r26, PT_GR26(%r1)  /* Store function & argument for child */
+	STREG	%r25, PT_GR25(%r1)
+	ldo	CLONE_VM(%r0), %r26   /* Force CLONE_VM since only init_mm */
+	or	%r26, %r24, %r26      /* will have kernel mappings.	 */
+	copy	%r0, %r25
+	bl	do_fork, %r2
+	copy	%r1, %r24
+
+	/* Parent Returns here */
+
+	ldo	-PT_SZ_ALGN(%r30), %r30
+	LDREG	-RP_OFFSET(%r30), %r2
+	bv	%r0(%r2)
+	nop
+
+	/*
+	 * Child Returns here
+	 *
+	 * copy_thread moved args from temp save area set up above
+	 * into task save area.
+	 */
+
+	.export	ret_from_kernel_thread
+ret_from_kernel_thread:
+
+	LDREG	TASK_PT_GR26-TASK_SZ_ALGN(%r30), %r1
+	LDREG	TASK_PT_GR25-TASK_SZ_ALGN(%r30), %r26
+	ble	0(%sr7, %r1)
+	copy	%r31, %r2
+
+	b	sys_exit
+	ldi	0, %r26
+
+	.import	sys_execve, code
+	.export	__execve, code
+__execve:
+	copy	%r2, %r15
+	copy	%r23, %r17
+	copy	%r30, %r16
+	ldo	PT_SZ_ALGN(%r30), %r30
+	STREG	%r26, PT_GR26(%r16)
+	STREG	%r25, PT_GR25(%r16)
+	STREG	%r24, PT_GR24(%r16)
+	bl	sys_execve, %r2
+	copy	%r16, %r26
+
+	comib,<>,n 0,%r28,__execve_failed
+
+	b	intr_return
+	STREG	%r17, PT_CR30(%r16)
+
+__execve_failed:
+	/* yes, this will trap and die. */
+	copy	%r15, %r2
+	bv	%r0(%r2)
+	nop
+
+	.align 4
+
+	/*
+	 * struct task_struct *_switch_to(struct task_struct *prev,
+	 *	struct task_struct *next)
+	 *
+	 * switch kernel stacks and return prev */
+	.export	_switch_to, code
+_switch_to:
+	STREG	 %r2, -RP_OFFSET(%r30)
+
+	callee_save
+
+	ldil	L%_switch_to_ret, %r2
+	ldo	R%_switch_to_ret(%r2), %r2
+	LDIL_FIXUP(%r2)
+
+	STREG	%r2, TASK_PT_KPC(%r26)
+	LDREG	TASK_PT_KPC(%r25), %r2
+
+	STREG	%r30, TASK_PT_KSP(%r26)
+	LDREG	TASK_PT_KSP(%r25), %r30
+
+	bv	%r0(%r2)
+	nop
+
+_switch_to_ret:
+	mtctl	%r0, %cr0		/* Needed for single stepping */
+	callee_rest
+
+	LDREG	-RP_OFFSET(%r30), %r2
+	bv	%r0(%r2)
+	copy	%r26, %r28
+
+	/*
+	 * Common rfi return path for interruptions, kernel execve, and some
+	 * syscalls.  The sys_rt_sigreturn syscall will return via this path
+	 * if the signal was received when the process was running; if the
+	 * process was blocked on a syscall then the normal syscall_exit
+	 * path is used.  All syscalls for traced proceses exit via
+	 * intr_restore.
+	 * Note that the following code uses a "relied upon translation". See
+	 * the parisc ACD for details. The ssm is necessary due to a PCXT bug.
+	 */
+
+	.align 4096
+
+	.export	syscall_exit_rfi
+syscall_exit_rfi:
+	copy    %r30,%r16
+	/* FIXME! depi below has hardcoded dependency on kernel stack size */
+	depi    0,31,14,%r16 /* get task pointer */
+	ldo	TASK_REGS(%r16),%r16
+	/* Force iaoq to userspace, as the user has had access to our current
+	 * context via sigcontext.
+	 * XXX do we need any other protection here?
+	 */
+	LDREG	PT_IAOQ0(%r16),%r19
+	depi	3,31,2,%r19
+	STREG	%r19,PT_IAOQ0(%r16)
+	LDREG	PT_IAOQ1(%r16),%r19
+	depi	3,31,2,%r19
+	STREG	%r19,PT_IAOQ1(%r16)
+	
+intr_return:
+
+	/* Check for software interrupts */
+
+	.import irq_stat,data
+
+	ldil    L%irq_stat,%r19
+	ldo     R%irq_stat(%r19),%r19
+	LDIL_FIXUP(%r19)
+
+#ifdef CONFIG_SMP
+	copy    %r30,%r1
+	/* FIXME! depi below has hardcoded dependency on kernel stack size */
+	depi    0,31,14,%r1 /* get task pointer */
+	ldw     TASK_PROCESSOR(%r1),%r20 /* get cpu # - int */
+#if (IRQSTAT_SZ == 32)
+	dep     %r20,26,27,%r20 /* shift left 5 bits */
+#else
+#error IRQSTAT_SZ changed, fix dep
+#endif /* IRQSTAT_SZ */
+	add     %r19,%r20,%r19
+#endif /* CONFIG_SMP */
+
+	ldw     IRQSTAT_SI_ACTIVE(%r19),%r20	/* hardirq.h: unsigned int */
+	ldw     IRQSTAT_SI_MASK(%r19),%r19	/* hardirq.h: unsigned int */
+	and     %r19,%r20,%r20
+	comib,<>,n 0,%r20,intr_do_softirq /* forward */
+
+intr_check_resched:
+
+	/* check for reschedule */
+	copy    %r30,%r1
+	/* FIXME! depi below has hardcoded dependency on kernel stack size */
+	depi    0,31,14,%r1 /* get task pointer */
+	LDREG     TASK_NEED_RESCHED(%r1),%r19	/* sched.h: long need_resched */
+	comib,<>,n 0,%r19,intr_do_resched /* forward */
+
+intr_check_sig:
+	/* As above */
+	copy    %r30,%r1
+	depi    0,31,14,%r1 /* get task pointer */
+	ldw	TASK_SIGPENDING(%r1),%r19	/* sched.h: int sigpending */
+	comib,<>,n 0,%r19,intr_do_signal /* forward */
+
+intr_restore:
+	copy	    	%r16, %r29
+	ldo		PT_FR31(%r29), %r29
+	rest_fp		%r29
+	copy    	%r16, %r29
+	rest_general 	%r29
+	ssm		0,%r0
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	tophys		%r29
+	mtsm		%r0
+	rest_specials	%r29
+	rest_stack	%r29
+	rfi
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+
+	.import do_softirq,code
+intr_do_softirq:
+	bl      do_softirq,%r2
+	nop
+	b       intr_check_resched
+	nop
+
+	.import schedule,code
+intr_do_resched:
+	/* Only do reschedule if we are returning to user space */
+	LDREG     PT_SR7(%r16), %r20
+	comib,= 0,%r20,intr_restore /* backward */
+	nop
+
+	bl      schedule,%r2
+	ssm     PSW_SM_I, %r0
+
+	/* It's OK to leave I bit on */
+	b       intr_return /* start over if we got a resched */
+	nop
+
+	.import do_signal,code
+intr_do_signal:
+	/* Only do signals if we are returning to user space */
+	LDREG   PT_SR7(%r16), %r20
+	comib,= 0,%r20,intr_restore /* backward */
+	nop
+
+	copy	%r0, %r24			/* unsigned long in_syscall */
+	copy	%r16, %r25			/* struct pt_regs *regs */
+	ssm     PSW_SM_I, %r0
+	bl	do_signal,%r2
+	copy	%r0, %r26			/* sigset_t *oldset = NULL */
+
+	b	intr_restore
+	nop
+
+	/* CR28 - saved GR1
+	 * CR29 - argument for do_irq_mask */
+
+	/* External interrupts */
+intr_extint:
+	get_stack
+	save_specials	%r29
+	virt_map	rfi
+	save_general	%r29
+
+	ldo		PT_FR0(%r29), %r24
+	save_fp		%r24
+	
+	loadgp
+
+	copy		%r29, %r24	/* arg2 is pt_regs */
+	copy		%r29, %r16	/* save pt_regs */
+#ifdef CONFIG_KWDB
+	copy		%r29, %r3	/* KWDB - update frame pointer (gr3) */
+#endif
+
+	/* sorry to put this cruft in the interrupt path */
+	ldil		L%cpu_irq_region, %r25
+	ldo		R%cpu_irq_region(%r25), %r25
+	bl		do_irq_mask,%r2
+#ifdef __LP64__
+	LDIL_FIXUP(%r25)
+#else
+	nop
+#endif
+
+	b		intr_return
+	nop
+
+	/* Generic interruptions (illegal insn, unaligned, page fault, etc) */
+
+	.export         intr_save, code /* for os_hpmc */
+
+intr_save:
+	get_stack
+	save_specials	%r29
+
+	mfctl		%cr20, %r1
+	STREG		%r1, PT_ISR(%r29)
+	mfctl		%cr21, %r1
+	STREG		%r1, PT_IOR(%r29)
+
+	virt_map	rfi
+	save_general	%r29
+
+	ldo		PT_FR0(%r29), %r25
+	save_fp		%r25
+	
+	loadgp
+
+	copy		%r29, %r25	/* arg1 is pt_regs */
+#ifdef CONFIG_KWDB
+	copy		%r29, %r3	/* KWDB - update frame pointer (gr3) */
+#endif
+
+	bl		handle_interruption,%r2
+	copy		%r29, %r16	/* save pt_regs */
+
+	b		intr_return
+	nop
+
+	/*
+	 * Note for all tlb miss handlers:
+	 *
+	 * cr24 contains a pointer to the kernel address space
+	 * page directory.
+	 *
+	 * cr25 contains a pointer to the current user address
+	 * space page directory.
+	 *
+	 * sr3 will contain the space id of the user address space
+	 * of the current running thread while that thread is
+	 * running in the kernel.
+	 */
+
+	/*
+	 * register number allocations.  Note that these are all
+	 * in the shadowed registers
+	 */
+
+	t0 = r1		/* temporary register 0 */
+	va = r8		/* virtual address for which the trap occured */
+	t1 = r9		/* temporary register 1 */
+	pte  = r16	/* pte/phys page # */
+	prot = r17	/* prot bits */
+	spc  = r24	/* space for which the trap occured */
+	ptp = r25	/* page directory/page table pointer */
+
+#ifdef __LP64__
+
+dtlb_miss_20w:
+
+	extrd,u         spc,31,7,t1     /* adjust va */
+	depd            t1,31,7,va      /* adjust va */
+	depdi           0,31,7,spc      /* adjust space */
+	mfctl           %cr25,ptp	/* Assume user space miss */
+	or,*<>          %r0,spc,%r0     /* If it is user space, nullify */
+	mfctl           %cr24,ptp	/* Load kernel pgd instead */
+	extrd,u         va,33,9,t1      /* Get pgd index */
+
+	mfsp            %sr7,t0		/* Get current space */
+	or,*=           %r0,t0,%r0      /* If kernel, nullify following test */
+	comb,<>,n       t0,spc,dtlb_fault /* forward */
+
+	/* First level page table lookup */
+
+	ldd,s           t1(ptp),ptp
+	extrd,u         va,42,9,t0     /* get second-level index */
+	bb,>=,n 	ptp,_PAGE_PRESENT_BIT,dtlb_fault
+	depdi           0,63,12,ptp     /* clear prot bits */
+
+	/* Second level page table lookup */
+
+	ldd,s           t0(ptp),ptp
+	extrd,u         va,51,9,t0     /* get third-level index */
+	bb,>=,n 	ptp,_PAGE_PRESENT_BIT,dtlb_fault
+	depdi           0,63,12,ptp     /* clear prot bits */
+
+	/* Third level page table lookup */
+
+	shladd           t0,3,ptp,ptp
+	ldi		_PAGE_ACCESSED,t1
+	ldd              0(ptp),pte
+	bb,>=,n          pte,_PAGE_PRESENT_BIT,dtlb_fault
+
+	/* Check whether the "accessed" bit was set, otherwise do so */
+
+	or		t1,pte,t0	/* t0 has R bit set */
+	and,*<>         t1,pte,%r0      /* test and nullify if already set */
+	std             t0,0(ptp)       /* write back pte */
+
+	copy            spc,prot	/* init prot with faulting space */
+	
+	depd		pte,8,7,prot
+	extrd,u,*=	pte,_PAGE_NO_CACHE_BIT+32,1,r0
+	depdi		1,12,1,prot
+	extrd,u,*=      pte,_PAGE_USER_BIT+32,1,r0
+	depdi		7,11,3,prot   /* Set for user space (1 rsvd for read) */
+	extrd,u,*= 	pte,_PAGE_GATEWAY_BIT+32,1,r0
+	depdi		0,11,2,prot	/* If Gateway, Set PL2 to 0 */
+
+	/* Get rid of prot bits and convert to page addr for idtlbt */
+
+	depdi		0,63,12,pte
+	extrd,u         pte,56,32,pte
+	idtlbt		%r16,%r17
+
+	rfir
+	nop
+#else
+
+dtlb_miss_11:
+	mfctl           %cr25,ptp	/* Assume user space miss */
+	or,<>           %r0,spc,%r0	/* If it is user space, nullify */
+	mfctl           %cr24,ptp	/* Load kernel pgd instead */
+	extru		va,9,10,t1	/* Get pgd index */
+
+	mfsp            %sr7,t0		/* Get current space */
+	or,=            %r0,t0,%r0	/* If kernel, nullify following test */
+	comb,<>,n       t0,spc,dtlb_fault /* forward */
+
+	/* First level page table lookup */
+
+	ldwx,s		t1(ptp),ptp
+	extru		va,19,10,t0	/* get second-level index */
+	bb,>=,n 	ptp,_PAGE_PRESENT_BIT,dtlb_fault
+	depi		0,31,12,ptp	/* clear prot bits */
+
+	/* Second level page table lookup */
+
+	sh2addl 	 t0,ptp,ptp
+	ldi		_PAGE_ACCESSED,t1
+	ldw		 0(ptp),pte
+	bb,>=,n 	 pte,_PAGE_PRESENT_BIT,dtlb_fault
+
+	/* Check whether the "accessed" bit was set, otherwise do so */
+
+	or		t1,pte,t0	/* t0 has R bit set */
+	and,<>		t1,pte,%r0	/* test and nullify if already set */
+	stw		t0,0(ptp)	/* write back pte */
+
+	copy            spc,prot	/* init prot with faulting space */
+	dep		pte,8,7,prot
+
+	extru,=		pte,_PAGE_NO_CACHE_BIT,1,r0
+	depi		1,12,1,prot
+	extru,=         pte,_PAGE_USER_BIT,1,r0
+	depi		7,11,3,prot   /* Set for user space (1 rsvd for read) */
+	extru,= 	pte,_PAGE_GATEWAY_BIT,1,r0
+	depi		0,11,2,prot	/* If Gateway, Set PL2 to 0 */
+
+	/* Get rid of prot bits and convert to page addr for idtlba */
+
+	depi		0,31,12,pte
+	extru		pte,24,25,pte
+
+	mfsp		%sr1,t0  /* Save sr1 so we can use it in tlb inserts */
+	mtsp		spc,%sr1
+
+	idtlba		pte,(%sr1,va)
+	idtlbp		prot,(%sr1,va)
+
+	mtsp		t0, %sr1	/* Restore sr1 */
+
+	rfir
+	nop
+
+dtlb_miss_20:
+	.level 2.0
+
+	mfctl           %cr25,ptp	/* Assume user space miss */
+	or,<>           %r0,spc,%r0	/* If it is user space, nullify */
+	mfctl           %cr24,ptp	/* Load kernel pgd instead */
+	extru		va,9,10,t1	/* Get pgd index */
+
+	mfsp            %sr7,t0		/* Get current space */
+	or,=            %r0,t0,%r0	/* If kernel, nullify following test */
+	comb,<>,n       t0,spc,dtlb_fault /* forward */
+
+	/* First level page table lookup */
+
+	ldwx,s		t1(ptp),ptp
+	extru		va,19,10,t0	/* get second-level index */
+	bb,>=,n 	ptp,_PAGE_PRESENT_BIT,dtlb_fault
+	depi		0,31,12,ptp	/* clear prot bits */
+
+	/* Second level page table lookup */
+
+	sh2addl 	 t0,ptp,ptp
+	ldi		_PAGE_ACCESSED,t1
+	ldw		 0(ptp),pte
+	bb,>=,n 	 pte,_PAGE_PRESENT_BIT,dtlb_fault
+
+	/* Check whether the "accessed" bit was set, otherwise do so */
+
+	or		t1,pte,t0	/* t0 has R bit set */
+	and,<>		t1,pte,%r0	/* test and nullify if already set */
+	stw		t0,0(ptp)	/* write back pte */
+
+	copy            spc,prot	/* init prot with faulting space */
+	
+	depd		pte,8,7,prot
+	extrd,u,*=	pte,_PAGE_NO_CACHE_BIT+32,1,r0
+	depdi		1,12,1,prot
+	extrd,u,*=      pte,_PAGE_USER_BIT+32,1,r0
+	depdi		7,11,3,prot   /* Set for user space (1 rsvd for read) */
+	extrd,u,*= 	pte,_PAGE_GATEWAY_BIT+32,1,r0
+	depdi		0,11,2,prot	/* If Gateway, Set PL2 to 0 */
+
+	/* Get rid of prot bits and convert to page addr for idtlbt */
+
+	depdi		0,63,12,pte
+	extrd,u		pte,56,25,pte
+	idtlbt		%r16,%r17
+
+	.level		1.1
+
+	rfir
+	nop
+#endif
+
+#ifdef __LP64__
+itlb_miss_20w:
+
+	/*
+	 * I miss is a little different, since we allow users to fault
+	 * on the gateway page which is in the kernel address space.
+	 */
+
+	extrd,u         spc,31,7,t1     /* adjust va */
+	depd            t1,31,7,va      /* adjust va */
+	depdi           0,31,7,spc      /* adjust space */
+	cmpib,*=        0,spc,itlb_miss_kernel_20w
+	extrd,u         va,33,9,t1      /* Get pgd index */
+
+	mfctl           %cr25,ptp	/* load user pgd */
+
+	mfsp            %sr7,t0		/* Get current space */
+	or,*=           %r0,t0,%r0      /* If kernel, nullify following test */
+	cmpb,*<>,n      t0,spc,itlb_fault /* forward */
+
+	/* First level page table lookup */
+
+itlb_miss_common_20w:
+	ldd,s           t1(ptp),ptp
+	extrd,u         va,42,9,t0     /* get second-level index */
+	bb,>=,n 	ptp,_PAGE_PRESENT_BIT,itlb_fault
+	depdi           0,63,12,ptp     /* clear prot bits */
+
+	/* Second level page table lookup */
+
+	ldd,s           t0(ptp),ptp
+	extrd,u         va,51,9,t0     /* get third-level index */
+	bb,>=,n         ptp,_PAGE_PRESENT_BIT,itlb_fault
+	depdi           0,63,12,ptp     /* clear prot bits */
+
+	/* Third level page table lookup */
+
+	shladd           t0,3,ptp,ptp
+	ldi		_PAGE_ACCESSED,t1
+	ldd              0(ptp),pte
+	bb,>=,n          pte,_PAGE_PRESENT_BIT,itlb_fault
+
+	/* Check whether the "accessed" bit was set, otherwise do so */
+
+	or		t1,pte,t0	/* t0 has R bit set */
+	and,*<>         t1,pte,%r0      /* test and nullify if already set */
+	std             t0,0(ptp)       /* write back pte */
+
+	copy            spc,prot        /* init prot with faulting space */
+	
+	depd		pte,8,7,prot
+	extrd,u,*=	pte,_PAGE_NO_CACHE_BIT+32,1,r0
+	depdi		1,12,1,prot
+	extrd,u,*=      pte,_PAGE_USER_BIT+32,1,r0
+	depdi		7,11,3,prot   /* Set for user space (1 rsvd for read) */
+	extrd,u,*= 	pte,_PAGE_GATEWAY_BIT+32,1,r0
+	depdi		0,11,2,prot	/* If Gateway, Set PL2 to 0 */
+
+	/* Get rid of prot bits and convert to page addr for iitlbt */
+
+	depdi		0,63,12,pte
+	extrd,u         pte,56,32,pte
+	iitlbt          %r16,%r17
+
+	rfir
+	nop
+
+itlb_miss_kernel_20w:
+	b               itlb_miss_common_20w
+	mfctl           %cr24,ptp	/* Load kernel pgd */
+#else
+
+itlb_miss_11:
+
+	/*
+	 * I miss is a little different, since we allow users to fault
+	 * on the gateway page which is in the kernel address space.
+	 */
+
+	comib,=         0,spc,itlb_miss_kernel_11
+	extru		va,9,10,t1	/* Get pgd index */
+
+	mfctl           %cr25,ptp	/* load user pgd */
+
+	mfsp            %sr7,t0		/* Get current space */
+	or,=            %r0,t0,%r0	/* If kernel, nullify following test */
+	comb,<>,n       t0,spc,itlb_fault /* forward */
+
+	/* First level page table lookup */
+
+itlb_miss_common_11:
+	ldwx,s		t1(ptp),ptp
+	extru		va,19,10,t0	/* get second-level index */
+	bb,>=,n 	ptp,_PAGE_PRESENT_BIT,itlb_fault
+	depi		0,31,12,ptp	/* clear prot bits */
+
+	/* Second level page table lookup */
+
+	sh2addl 	 t0,ptp,ptp
+	ldi		_PAGE_ACCESSED,t1
+	ldw		 0(ptp),pte
+	bb,>=,n 	 pte,_PAGE_PRESENT_BIT,itlb_fault
+
+	/* Check whether the "accessed" bit was set, otherwise do so */
+
+	or		t1,pte,t0	/* t0 has R bit set */
+	and,<>		t1,pte,%r0	/* test and nullify if already set */
+	stw		t0,0(ptp)	/* write back pte */
+
+	copy            spc,prot        /* init prot with faulting space */
+	dep		pte,8,7,prot
+
+	extru,=		pte,_PAGE_NO_CACHE_BIT,1,r0
+	depi		1,12,1,prot
+	extru,=         pte,_PAGE_USER_BIT,1,r0
+	depi		7,11,3,prot   /* Set for user space (1 rsvd for read) */
+	extru,= 	pte,_PAGE_GATEWAY_BIT,1,r0
+	depi		0,11,2,prot	/* If Gateway, Set PL2 to 0 */
+
+	/* Get rid of prot bits and convert to page addr for iitlba */
+
+	depi		0,31,12,pte
+	extru		pte,24,25,pte
+
+	mfsp		%sr1,t0  /* Save sr1 so we can use it in tlb inserts */
+	mtsp		spc,%sr1
+
+	iitlba		pte,(%sr1,va)
+	iitlbp		prot,(%sr1,va)
+
+	mtsp		t0, %sr1	/* Restore sr1 */
+
+	rfir
+	nop
+
+itlb_miss_kernel_11:
+	b               itlb_miss_common_11
+	mfctl           %cr24,ptp	/* Load kernel pgd */
+
+itlb_miss_20:
+
+	/*
+	 * I miss is a little different, since we allow users to fault
+	 * on the gateway page which is in the kernel address space.
+	 */
+
+	comib,=         0,spc,itlb_miss_kernel_20
+	extru		va,9,10,t1	/* Get pgd index */
+
+	mfctl           %cr25,ptp	/* load user pgd */
+
+	mfsp            %sr7,t0		/* Get current space */
+	or,=            %r0,t0,%r0	/* If kernel, nullify following test */
+	comb,<>,n       t0,spc,itlb_fault /* forward */
+
+	/* First level page table lookup */
+
+itlb_miss_common_20:
+	ldwx,s		t1(ptp),ptp
+	extru		va,19,10,t0	/* get second-level index */
+	bb,>=,n 	ptp,_PAGE_PRESENT_BIT,itlb_fault
+	depi		0,31,12,ptp	/* clear prot bits */
+
+	/* Second level page table lookup */
+
+	sh2addl 	 t0,ptp,ptp
+	ldi		_PAGE_ACCESSED,t1
+	ldw		 0(ptp),pte
+	bb,>=,n 	 pte,_PAGE_PRESENT_BIT,itlb_fault
+
+	/* Check whether the "accessed" bit was set, otherwise do so */
+
+	or		t1,pte,t0	/* t0 has R bit set */
+	and,<>		t1,pte,%r0	/* test and nullify if already set */
+	stw		t0,0(ptp)	/* write back pte */
+
+	copy            spc,prot        /* init prot with faulting space */
+	
+	.level 2.0
+
+	depd		pte,8,7,prot
+	extrd,u,*=	pte,_PAGE_NO_CACHE_BIT+32,1,r0
+	depdi		1,12,1,prot
+	extrd,u,*=      pte,_PAGE_USER_BIT+32,1,r0
+	depdi		7,11,3,prot   /* Set for user space (1 rsvd for read) */
+	extrd,u,*= 	pte,_PAGE_GATEWAY_BIT+32,1,r0
+	depdi		0,11,2,prot	/* If Gateway, Set PL2 to 0 */
+
+	/* Get rid of prot bits and convert to page addr for iitlbt */
+
+	depdi		0,63,12,pte
+	extrd,u		pte,56,25,pte
+	iitlbt          %r16,%r17
+	.level		1.1
+
+	rfir
+	nop
+
+
+itlb_miss_kernel_20:
+	b               itlb_miss_common_20
+	mfctl           %cr24,ptp	/* Load kernel pgd */
+#endif
+
+#ifdef __LP64__
+
+dbit_trap_20w:
+
+	extrd,u         spc,31,7,t1     /* adjust va */
+	depd            t1,31,7,va      /* adjust va */
+	depdi           0,1,2,va        /* adjust va */
+	depdi           0,31,7,spc      /* adjust space */
+	mfctl           %cr25,ptp	/* Assume user space miss */
+	or,*<>          %r0,spc,%r0     /* If it is user space, nullify */
+	mfctl           %cr24,ptp	/* Load kernel pgd instead */
+	extrd,u         va,33,9,t1      /* Get pgd index */
+
+	mfsp            %sr7,t0		/* Get current space */
+	or,*=           %r0,t0,%r0      /* If kernel, nullify following test */
+	comb,<>,n       t0,spc,dbit_fault /* forward */
+
+	/* First level page table lookup */
+
+	ldd,s           t1(ptp),ptp
+	extrd,u         va,42,9,t0     /* get second-level index */
+	bb,>=,n         ptp,_PAGE_PRESENT_BIT,dbit_fault
+	depdi           0,63,12,ptp     /* clear prot bits */
+
+	/* Second level page table lookup */
+
+	ldd,s           t0(ptp),ptp
+	extrd,u         va,51,9,t0     /* get third-level index */
+	bb,>=,n         ptp,_PAGE_PRESENT_BIT,dbit_fault
+	depdi           0,63,12,ptp     /* clear prot bits */
+
+	/* Third level page table lookup */
+
+	shladd           t0,3,ptp,ptp
+	ldi		(_PAGE_ACCESSED|_PAGE_DIRTY),t1
+	ldd              0(ptp),pte
+	bb,>=,n          pte,_PAGE_PRESENT_BIT,dbit_fault
+
+	/* Set Accessed and Dirty bits in the pte */
+
+	or		t1,pte,pte
+	std             pte,0(ptp)      /* write back pte */
+
+	copy            spc,prot	/* init prot with faulting space */
+	
+	depd		pte,8,7,prot
+	extrd,u,*=	pte,_PAGE_NO_CACHE_BIT+32,1,r0
+	depdi		1,12,1,prot
+	extrd,u,*=      pte,_PAGE_USER_BIT+32,1,r0
+	depdi		7,11,3,prot   /* Set for user space (1 rsvd for read) */
+	extrd,u,*= 	pte,_PAGE_GATEWAY_BIT+32,1,r0
+	depdi		0,11,2,prot	/* If Gateway, Set PL2 to 0 */
+
+	/* Get rid of prot bits and convert to page addr for idtlbt */
+
+	depdi		0,63,12,pte
+	extrd,u         pte,56,32,pte
+	idtlbt		%r16,%r17
+
+	rfir
+	nop
+#else
+
+dbit_trap_11:
+	mfctl           %cr25,ptp	/* Assume user space trap */
+	or,<>           %r0,spc,%r0	/* If it is user space, nullify */
+	mfctl           %cr24,ptp	/* Load kernel pgd instead */
+	extru		va,9,10,t1	/* Get pgd index */
+
+	mfsp            %sr7,t0		/* Get current space */
+	or,=            %r0,t0,%r0	/* If kernel, nullify following test */
+	comb,<>,n       t0,spc,dbit_fault /* forward */
+
+	/* First level page table lookup */
+
+	ldwx,s		t1(ptp),ptp
+	extru		va,19,10,t0	/* get second-level index */
+	bb,>=,n 	ptp,_PAGE_PRESENT_BIT,dbit_fault
+	depi		0,31,12,ptp	/* clear prot bits */
+
+	/* Second level page table lookup */
+
+	sh2addl 	 t0,ptp,ptp
+	ldi		(_PAGE_ACCESSED|_PAGE_DIRTY),t1
+	ldw		 0(ptp),pte
+	bb,>=,n 	 pte,_PAGE_PRESENT_BIT,dbit_fault
+
+	/* Set Accessed and Dirty bits in the pte */
+
+	or		t1,pte,pte
+	stw		pte,0(ptp)	/* write back pte */
+
+	copy            spc,prot        /* init prot with faulting space */
+	dep		pte,8,7,prot
+
+	extru,=		pte,_PAGE_NO_CACHE_BIT,1,r0
+	depi		1,12,1,prot
+	extru,=         pte,_PAGE_USER_BIT,1,r0
+	depi		7,11,3,prot /* Set for user space (1 rsvd for read) */
+	extru,= 	pte,_PAGE_GATEWAY_BIT,1,r0
+	depi		0,11,2,prot	/* If Gateway, Set PL2 to 0 */
+
+	/* Get rid of prot bits and convert to page addr for idtlba */
+
+	depi		0,31,12,pte
+	extru		pte,24,25,pte
+
+	mfsp		%sr1,t0  /* Save sr1 so we can use it in tlb inserts */
+	mtsp		spc,%sr1
+
+	idtlba		pte,(%sr1,va)
+	idtlbp		prot,(%sr1,va)
+
+	mtsp		t0, %sr1     /* Restore sr1 */
+
+	rfir
+	nop
+
+dbit_trap_20:
+	mfctl           %cr25,ptp	/* Assume user space trap */
+	or,<>           %r0,spc,%r0	/* If it is user space, nullify */
+	mfctl           %cr24,ptp	/* Load kernel pgd instead */
+	extru		va,9,10,t1	/* Get pgd index */
+
+	mfsp            %sr7,t0		/* Get current space */
+	or,=            %r0,t0,%r0	/* If kernel, nullify following test */
+	comb,<>,n       t0,spc,dbit_fault /* forward */
+
+	/* First level page table lookup */
+
+	ldwx,s		t1(ptp),ptp
+	extru		va,19,10,t0	/* get second-level index */
+	bb,>=,n 	ptp,_PAGE_PRESENT_BIT,dbit_fault
+	depi		0,31,12,ptp	/* clear prot bits */
+
+	/* Second level page table lookup */
+
+	sh2addl 	 t0,ptp,ptp
+	ldi		(_PAGE_ACCESSED|_PAGE_DIRTY),t1
+	ldw		 0(ptp),pte
+	bb,>=,n 	 pte,_PAGE_PRESENT_BIT,dbit_fault
+
+	/* Set Accessed and Dirty bits in the pte */
+
+	or		t1,pte,pte
+	stw		pte,0(ptp)	/* write back pte */
+
+	copy            spc,prot        /* init prot with faulting space */
+	
+	.level 2.0
+
+	depd		pte,8,7,prot
+	extrd,u,*=	pte,_PAGE_NO_CACHE_BIT+32,1,r0
+	depdi		1,12,1,prot
+	extrd,u,*=      pte,_PAGE_USER_BIT+32,1,r0
+	depdi		7,11,3,prot   /* Set for user space (1 rsvd for read) */
+	extrd,u,*= 	pte,_PAGE_GATEWAY_BIT+32,1,r0
+	depdi		0,11,2,prot	/* If Gateway, Set PL2 to 0 */
+
+	/* Get rid of prot bits and convert to page addr for idtlbt */
+
+	depdi		0,63,12,pte
+	extrd,u		pte,56,25,pte
+	idtlbt		%r16,%r17
+
+	.level		1.1
+
+	rfir
+	nop
+#endif
+
+	.import handle_interruption,code
+
+kernel_bad_space:
+	b		tlb_fault
+	ldi		31,%r1	/* Use an unused code */
+
+dbit_fault:
+	b		tlb_fault
+	ldi		20,%r1
+
+itlb_fault:
+	b		tlb_fault
+	ldi		6,%r1
+
+dtlb_fault:
+	ldi		15,%r1
+
+	/* Fall Through */
+
+tlb_fault:
+	mtctl		%r1,%cr29
+	mtctl		%r29,%cr31
+
+	get_stack
+	save_specials	%r29		/* Note this saves a trashed r1 */
+
+	SAVE_CR		(%cr20, PT_ISR(%r29))
+	SAVE_CR		(%cr21, PT_IOR(%r29))
+
+	virt_map	rfir
+
+	STREG		%r1,PT_GR1(%r29)	/* save good value after rfir */
+
+	save_general	%r29
+
+	ldo	PT_FR0(%r29), %r25
+	save_fp		%r25
+	
+	loadgp
+
+	copy		%r29, %r25
+
+	bl		handle_interruption, %r2
+	copy		%r29, %r16
+
+	b		intr_return
+	nop
+
+	/* Register saving semantics for system calls:
+
+	   %r1		   clobbered by system call macro in userspace
+	   %r2		   saved in PT_REGS by gateway page
+	   %r3  - %r18	   preserved by C code (saved by signal code)
+	   %r19 - %r20	   saved in PT_REGS by gateway page
+	   %r21 - %r22	   non-standard syscall args
+			   stored in kernel stack by gateway page
+	   %r23 - %r26	   arg3-arg0, saved in PT_REGS by gateway page
+	   %r27 - %r30	   saved in PT_REGS by gateway page
+	   %r31		   syscall return pointer
+	 */
+
+	/* Floating point registers (FIXME: what do we do with these?)
+
+	   %fr0  - %fr3	   status/exception, not preserved
+	   %fr4  - %fr7	   arguments
+	   %fr8	 - %fr11   not preserved by C code
+	   %fr12 - %fr21   preserved by C code
+	   %fr22 - %fr31   not preserved by C code
+	 */
+
+	.macro	reg_save regs
+	STREG	%r3, PT_GR3(\regs)
+	STREG	%r4, PT_GR4(\regs)
+	STREG	%r5, PT_GR5(\regs)
+	STREG	%r6, PT_GR6(\regs)
+	STREG	%r7, PT_GR7(\regs)
+	STREG	%r8, PT_GR8(\regs)
+	STREG	%r9, PT_GR9(\regs)
+	STREG   %r10,PT_GR10(\regs)
+	STREG   %r11,PT_GR11(\regs)
+	STREG   %r12,PT_GR12(\regs)
+	STREG   %r13,PT_GR13(\regs)
+	STREG   %r14,PT_GR14(\regs)
+	STREG   %r15,PT_GR15(\regs)
+	STREG   %r16,PT_GR16(\regs)
+	STREG   %r17,PT_GR17(\regs)
+	STREG   %r18,PT_GR18(\regs)
+	.endm
+
+	.macro	reg_restore regs
+	LDREG	PT_GR3(\regs), %r3
+	LDREG	PT_GR4(\regs), %r4
+	LDREG	PT_GR5(\regs), %r5
+	LDREG	PT_GR6(\regs), %r6
+	LDREG	PT_GR7(\regs), %r7
+	LDREG	PT_GR8(\regs), %r8
+	LDREG	PT_GR9(\regs), %r9
+	LDREG   PT_GR10(\regs),%r10
+	LDREG   PT_GR11(\regs),%r11
+	LDREG   PT_GR12(\regs),%r12
+	LDREG   PT_GR13(\regs),%r13
+	LDREG   PT_GR14(\regs),%r14
+	LDREG   PT_GR15(\regs),%r15
+	LDREG   PT_GR16(\regs),%r16
+	LDREG   PT_GR17(\regs),%r17
+	LDREG   PT_GR18(\regs),%r18
+	.endm
+
+	.export sys_fork_wrapper
+sys_fork_wrapper:
+	ldo	TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1	/* get pt regs */
+	reg_save %r1
+
+	STREG	%r2,-RP_OFFSET(%r30)
+	ldo	FRAME_SIZE(%r30),%r30
+
+	/* These are call-clobbered registers and therefore
+	   also syscall-clobbered (we hope). */
+	STREG	%r2,PT_GR19(%r1)	/* save for child */
+	STREG	%r30,PT_GR20(%r1)
+	ldil	L%child_return, %r3
+	ldo	R%child_return(%r3), %r3
+	LDIL_FIXUP(%r3)
+	STREG	%r3,PT_GR21(%r1)	/* save for child */
+
+	LDREG	PT_GR30(%r1),%r25
+	copy	%r1,%r24
+	bl	sys_clone,%r2
+	ldi	SIGCHLD,%r26
+
+	LDREG	-RP_OFFSET-FRAME_SIZE(%r30),%r2
+wrapper_exit:
+	ldo	-FRAME_SIZE(%r30),%r30		/* get the stackframe */
+	ldo	TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1    /* get pt regs */
+
+	reg_restore %r1
+
+	bv %r0(%r2)
+	nop
+
+	/* Set the return value for the child */
+child_return:
+	LDREG	TASK_PT_GR19-TASK_SZ_ALGN-128(%r30),%r2
+	b	wrapper_exit
+	copy	%r0,%r28
+
+	
+	.export sys_clone_wrapper
+sys_clone_wrapper:
+	ldo	TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1	/* get pt regs */
+	reg_save %r1
+
+	STREG	%r2,-RP_OFFSET(%r30)
+	ldo	FRAME_SIZE(%r30),%r30
+
+	STREG	%r30,PT_GR20(%r1)
+	ldil	L%child_return,%r3
+	ldo	R%child_return(%r3),%r3
+	LDIL_FIXUP(%r3)
+
+	bl	sys_clone,%r2
+	STREG	%r3,PT_GR21(%r1)	/* save for child */
+
+	b	wrapper_exit
+	LDREG	-RP_OFFSET-FRAME_SIZE(%r30),%r2
+
+
+	.export sys_vfork_wrapper
+sys_vfork_wrapper:
+	ldo	TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1	/* get pt regs */
+	reg_save %r1
+
+	STREG	%r2,-RP_OFFSET(%r30)
+	ldo	FRAME_SIZE(%r30),%r30
+
+	STREG	%r30,PT_GR20(%r1)
+	ldil	L%child_return,%r3
+	ldo	R%child_return(%r3),%r3
+	LDIL_FIXUP(%r3)
+	STREG	%r3,PT_GR21(%r1)	/* save for child */
+
+	bl	sys_vfork,%r2
+	copy	%r1,%r26
+
+	b	wrapper_exit
+	LDREG	-RP_OFFSET-FRAME_SIZE(%r30),%r2
+
+	
+	.macro  execve_wrapper execve
+	ldo	TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1	    /* get pt regs */
+
+	/*
+	 * Do we need to save/restore r3-r18 here?
+	 * I don't think so. why would new thread need old
+	 * threads registers?
+	 */
+
+	/* %arg0 - %arg3 are already saved for us. */
+
+	STREG %r2,-RP_OFFSET(%r30)
+	ldo FRAME_SIZE(%r30),%r30
+	bl \execve,%r2
+	copy %r1,%arg0
+
+	ldo -FRAME_SIZE(%r30),%r30
+	LDREG -RP_OFFSET(%r30),%r2
+
+	/* If exec succeeded we need to load the args */
+
+	ldo -1024(%r0),%r1
+	comb,>>= %r28,%r1,error_\execve
+	copy %r2,%r19
+
+error_\execve:
+	bv %r0(%r19)
+	nop
+	.endm
+
+	.export sys_execve_wrapper
+	.import sys_execve
+
+sys_execve_wrapper:
+	execve_wrapper sys_execve
+
+#ifdef __LP64__
+	.export sys32_execve_wrapper
+	.import sys32_execve
+
+sys32_execve_wrapper:
+	execve_wrapper sys32_execve
+#endif
+
+	.export sys_rt_sigreturn_wrapper
+sys_rt_sigreturn_wrapper:
+	ldo	TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30), %r26
+	/* Don't save regs, we are going to restore them from sigcontext. */
+	STREG	%r2, -RP_OFFSET(%r30)
+	bl	sys_rt_sigreturn,%r2
+	ldo	FRAME_SIZE(%r30), %r30
+
+	ldo	-FRAME_SIZE(%r30), %r30
+	LDREG	-RP_OFFSET(%r30), %r2
+
+	/* FIXME: I think we need to restore a few more things here. */
+	ldo	TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1	    /* get pt regs */
+	reg_restore %r1
+
+	/* If the signal was received while the process was blocked on a
+	 * syscall, then r2 will take us to syscall_exit; otherwise r2 will
+	 * take us to syscall_exit_rfi and on to intr_return.
+	 */
+	bv	%r0(%r2)
+	LDREG	PT_GR28(%r1),%r28  /* reload original r28 for syscall_exit */
+
+	.export sys_sigaltstack_wrapper
+sys_sigaltstack_wrapper:
+	/* Get the user stack pointer */
+	LDREG	-TASK_SZ_ALGN-FRAME_SIZE+TASK_PT_GR30(%r30), %r24
+	STREG	%r2, -RP_OFFSET(%r30)
+	bl	do_sigaltstack,%r2
+	ldo	FRAME_SIZE(%r30), %r30
+
+	ldo	-FRAME_SIZE(%r30), %r30
+	LDREG	-RP_OFFSET(%r30), %r2
+	bv	%r0(%r2)
+	nop
+
+	.export sys_rt_sigsuspend_wrapper
+sys_rt_sigsuspend_wrapper:
+	ldo	TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30), %r24
+	reg_save %r24
+
+	STREG	%r2, -RP_OFFSET(%r30)
+	bl	sys_rt_sigsuspend,%r2
+	ldo	FRAME_SIZE(%r30), %r30
+
+	ldo	-FRAME_SIZE(%r30), %r30
+	LDREG	-RP_OFFSET(%r30), %r2
+
+	ldo	TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1
+	reg_restore %r1
+
+	bv	%r0(%r2)
+	nop
+
+	.export syscall_exit
+syscall_exit:
+	/* NOTE: HP-UX syscalls also come through here
+	   after hpux_syscall_exit fixes up return
+	   values. */
+	/* NOTE: Not all syscalls exit this way.  rt_sigreturn will exit
+	 * via syscall_exit_rfi if the signal was received while the process
+	 * was running.  All traced processes will probably exit via
+	 * syscall_exit_rfi in the future.
+	 */
+
+	/* save return value now */
+
+	STREG     %r28,TASK_PT_GR28-TASK_SZ_ALGN-FRAME_SIZE(%r30)
+
+syscall_check_bh:
+
+/* #ifdef NOTNOW */
+	/* Check for software interrupts */
+
+	.import irq_stat,data
+
+	ldil    L%irq_stat,%r19
+	ldo     R%irq_stat(%r19),%r19
+	LDIL_FIXUP(%r19)
+
+#ifdef CONFIG_SMP
+	/* sched.h: int processor */
+	ldw     TASK_PROCESSOR-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r20 /* get cpu # */
+#if (IRQSTAT_SZ == 32)
+	dep     %r20,26,27,%r20 /* shift left 5 bits */
+#else
+#error IRQSTAT_SZ changed, fix dep
+#endif /* IRQSTAT_SZ */
+	add     %r19,%r20,%r19
+#endif /* CONFIG_SMP */
+
+	ldw     IRQSTAT_SI_ACTIVE(%r19),%r20	/* hardirq.h: unsigned int */
+	ldw     IRQSTAT_SI_MASK(%r19),%r19	/* hardirq.h: unsigned int */
+	and     %r19,%r20,%r20
+	comib,<>,n 0,%r20,syscall_do_softirq /* forward */
+/* #endif */
+
+
+syscall_check_resched:
+
+	/* check for reschedule */
+
+	LDREG  TASK_NEED_RESCHED-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r19	/* long */
+	comib,<>,n 0,%r19,syscall_do_resched /* forward */
+
+syscall_check_sig:
+	ldo     -TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1         /* get task ptr */
+	/* check for pending signals */
+	ldw     TASK_SIGPENDING(%r1),%r19
+	comib,<>,n 0,%r19,syscall_do_signal  /* forward */
+
+syscall_restore:
+	/* disable interrupts while dicking with the kernel stack, */
+	/* or life can become unpleasant */
+	rsm	PSW_SM_I, %r20
+	LDREG	TASK_PTRACE(%r1), %r19		/* Are we being ptraced? */
+	bb,<,n	%r19,31,syscall_restore_rfi
+	LDREG	TASK_PT_GR20(%r1),%r19
+	mtctl	%r19, %cr27
+
+	LDREG	TASK_PT_GR2(%r1),%r2		   /* restore user rp */
+	LDREG	TASK_PT_GR21(%r1),%r21
+	LDREG	TASK_PT_GR22(%r1),%r22
+	LDREG	TASK_PT_GR23(%r1),%r23
+	LDREG	TASK_PT_GR24(%r1),%r24
+	LDREG	TASK_PT_GR25(%r1),%r25
+	LDREG	TASK_PT_GR26(%r1),%r26
+	LDREG	TASK_PT_GR27(%r1),%r27	   /* restore user dp */
+	LDREG	TASK_PT_GR28(%r1),%r28	   /* syscall return value */
+	LDREG	TASK_PT_GR29(%r1),%r29
+	LDREG	TASK_PT_GR30(%r1),%r30	   /* restore user sp */
+	LDREG	TASK_PT_GR31(%r1),%r31	   /* restore syscall rp */
+	ldo	TASK_PT_FR31(%r1),%r19		   /* reload fpregs */
+	rest_fp	%r19
+	LDREG	TASK_PT_SAR(%r1),%r19		   /* restore SAR */
+	mtsar	%r19
+	LDREG	TASK_PT_GR19(%r1),%r19
+
+	mtctl	%r1,%cr30			   /* intrhandler okay. */
+	mfsp	%sr3,%r1			   /* Get users space id */
+	mtsp	%r1,%sr4			   /* Restore sr4 */
+	mtsp	%r1,%sr5			   /* Restore sr5 */
+	mtsp	%r1,%sr6			   /* Restore sr6 */
+
+	depi	3,31,2,%r31			   /* ensure return to user mode. */
+
+	mtsm	%r20				   /* restore irq state  */
+	mfctl	%cr27,%r20
+	
+	/*
+	 * Due to a dependency in the tlb miss handlers on sr7, it
+	 * is essential that sr7 get set in the delay slot.
+	 */
+
+#ifdef __LP64__
+
+	/* Note the be (and mtsp) is executed in narrow mode. This is OK
+	 * for 32 bit processes, but won't work once we support 64 bit
+	 * processes.
+	 */
+
+	rsm	PSW_SM_W, %r0
+	be	0(%sr3,%r31)			   /* return to user space */
+	mtsp	%r1,%sr7			   /* Restore sr7 */
+#else
+	be	0(%sr3,%r31)			   /* return to user space */
+	mtsp	%r1,%sr7			   /* Restore sr7 */
+#endif
+
+	/* We have to return via an RFI, so that PSW T and R bits can be set
+	 * appropriately.
+	 * This sets up pt_regs so we can return via intr_restore, which is not
+	 * the most efficient way of doing things, but it works.
+	 */
+syscall_restore_rfi:
+	ldo	-1(%r0),%r2			   /* Set recovery cntr to -1 */
+	mtctl	%r2,%cr0			   /*   for immediate trap */
+	copy	%r0,%r2				   /* Create a reasonable PSW */
+	/* XXX W bit??? */
+	depi	-1,13,1,%r2
+	depi	-1,28,1,%r2
+	depi	-1,30,1,%r2
+	depi	-1,31,1,%r2
+	bb,<,n	%r19,15,set_rbit		   /* PT_SINGLESTEP */
+	bb,>=,n	%r19,14,set_nobit		   /* PT_BLOCKSTEP, see ptrace.c */
+set_tbit:
+	depi	-1,7,1,%r2
+	b,n	set_nobit
+set_rbit:
+	depi	-1,27,1,%r2
+set_nobit:
+	STREG	%r2,TASK_PT_PSW(%r1)
+	STREG	%r1,TASK_PT_CR30(%r1)
+	mfsp	%sr0,%r2
+	STREG	%r2,TASK_PT_SR0(%r1)
+	mfsp	%sr1,%r2
+	STREG	%r2,TASK_PT_SR1(%r1)
+	mfsp	%sr2,%r2
+	STREG	%r2,TASK_PT_SR2(%r1)
+	mfsp	%sr3,%r2
+	STREG	%r2,TASK_PT_SR3(%r1)
+	STREG	%r2,TASK_PT_SR4(%r1)
+	STREG	%r2,TASK_PT_SR5(%r1)
+	STREG	%r2,TASK_PT_SR6(%r1)
+	STREG	%r2,TASK_PT_SR7(%r1)
+	STREG	%r2,TASK_PT_IASQ0(%r1)
+	STREG	%r2,TASK_PT_IASQ1(%r1)
+	LDREG	TASK_PT_GR31(%r1),%r2
+	depi	3,31,2,%r2			   /* ensure return to user mode. */
+	STREG	%r2,TASK_PT_IAOQ0(%r1)
+	ldo	4(%r2),%r2
+	STREG	%r2,TASK_PT_IAOQ1(%r1)
+	ldo	TASK_REGS(%r1),%r25
+	reg_save %r25				   /* Save r3 to r18 */
+	copy	%r25,%r16
+	b	intr_restore
+	nop
+
+	.import do_softirq,code
+syscall_do_softirq:
+	bl      do_softirq,%r2
+	nop
+	b       syscall_check_resched
+	ssm     PSW_SM_I, %r0  /* do_softirq returns with I bit off */
+
+	.import schedule,code
+syscall_do_resched:
+	bl	schedule,%r2
+	nop
+	b       syscall_check_bh  /* if resched, we start over again */
+	nop
+
+	.import do_signal,code
+syscall_do_signal:
+	/* Save callee-save registers (for sigcontext).
+	   FIXME: After this point the process structure should be
+	   consistent with all the relevant state of the process
+	   before the syscall.  We need to verify this. */
+	ldo	TASK_REGS(%r1), %r25		/* struct pt_regs *regs */
+	reg_save %r25
+
+	ldi	1, %r24				/* unsigned long in_syscall */
+
+	bl	do_signal,%r2
+	copy	%r0, %r26			/* sigset_t *oldset = NULL */
+
+	ldo     -TASK_SZ_ALGN-FRAME_SIZE(%r30), %r1     /* reload task ptr */
+	ldo	TASK_REGS(%r1), %r20		/* reload pt_regs */
+	reg_restore %r20
+
+	b,n     syscall_restore
+
+#ifdef __LP64__
+unimplemented_64bitirq:
+	ssm PSW_SM_Q+PSW_SM_I, %r0
+	/* indicate that we had an interrupt */
+	ldi	0x77, %r28
+	ldi	0x77, %r29
+	/* save interrupt registers in GRs for diagnosis */
+	mfctl %cr17, %r17
+	mfctl %cr18, %r18
+	mfctl %cr19, %r19
+	mfctl %cr20, %r20
+	mfctl %cr21, %r21
+	mfctl %cr22, %r22
+	b,n .
+	nop
+#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)