Source to machdep/ppc/hw_exception.s


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/* Low level routines dealing with exception entry and exit.
 * There are various types of exception:
 *
 *    Interrupt, trap, system call and debugger entry. Each has it's own
 *    handler since the state save routine is different for each. The
 *    code is very similar (a lot of cut and paste).
 *
 *    The code for the FPU disabled handler (lazy fpu) is in cswtch.s
 */

#include <debug.h>
#include <mach_assert.h>

#include <mach/exception.h>
#include <mach/ppc/vm_param.h>

#include <assym.h>

#include <machdep/ppc/asm.h>
#include <machdep/ppc/proc_reg.h>
#include <machdep/ppc/trap.h>
#include <machdep/ppc/exception.h>
#include <kernserv/ppc/spl.h>
#include <machdep/ppc/machspl.h>
#include <kern/ast.h>
	
/*
 * thandler(type)
 *
 * Entry:	VM switched ON
 *		Interrupts  OFF
 *              original r1-3 saved in sprg1-3
 *              original srr0 and srr1 saved in per_proc_info structure
 *              original cr            saved in per_proc_info structure
 *              exception type         saved in per_proc_info structure
 *              r1 = scratch
 *              r2 = virt addr of per_proc_info 
 *		r3 = exception type (one of EXC_...)
 */

/*
 * If pcb.ksp == 0 then the kernel stack is already busy,
 *                 we save ppc_saved state below the current stack pointer,
 *		   leaving enough space for the `red zone' in case the
 *		   trapped thread was in the middle of saving state below
 *		   its stack pointer.
 *
 * otherwise       we save a ppc_saved_state in the pcb, and switch to
 * 		   the kernel stack (setting pcb.ksp to 0)
 *
 * on return, we do the reverse, the last state is popped from the pcb
 * and pcb.ksp is set to the top of stack below the kernel state + frame
 * TODO NMGS - is this frame used? I don't think so
 *
 *                    Diagram of a thread's kernel stack
 *
 *               --------------- 	TOP OF STACK
 *              |kernel_state   |
 *              |---------------|
 *              |backpointer FM |
 *              |---------------|                         	   	
 *              |... C usage ...|                         	   	
 *              |               |                         	   	
 *              |---------------|                       TRAP IN KERNEL CODE
 *              |ppc_saved_state|                       STATE SAVED HERE   
 *              |---------------|                         		
 *              |backpointer FM |                                       
 *              |---------------|                         
 *              |... C usage ...|                         
 *              |               |                         
 *              |               |                         
 *              |               |                         
 *              |               |                         
 */


#if DEBUG

/* TRAP_SPACE_NEEDED is the space assumed free on the kernel stack when
 * another trap is taken. We need at least enough space for a saved state
 * structure plus two small backpointer frames, and we add a few
 * hundred bytes for the space needed by the C (which may be less but
 * may be much more). We're trying to catch kernel stack overflows :-)
 */

#define TRAP_SPACE_NEEDED	FM_REDZONE+SS_SIZE+(2*FM_SIZE)+256

#endif /* DEBUG */

	.text

ENTRY(thandler, TAG_NO_FRAME_USED)	/* What tag should this have?! */

		/* If we're on the gdb stack, there has probably been
		 * a fault reading user memory or something like that,
		 * so we should pass this to the gdb handler. NOTE
		 * we may have entered gdb through an interrupt handler
		 * (keyboard or serial line, for example), so interrupt
		 * stack may be busy too.
		 */
	addis	r1,	0,	ha16(EXT(gdbstackptr))
	addi	r1,	r1,	lo16(EXT(gdbstackptr))	/* TODO assumes 1 CPU */
	lwz	r1,	0(r1)
	cmpwi	CR0,	r1,	0
	beq-	CR0,	EXT(gdbhandler)
	
#if DEBUG
		/* Make sure we're not on the interrupt stack */
	addis	r1,	0,	ha16(EXT(istackptr))
	addi	r1,	r1,	lo16(EXT(istackptr))
	lwz	r1,	0(r1)
	cmpwi	CR0,	r1,	0

	/* If we are on the interrupt stack, treat as an interrupt,
	 * the interrupt handler will panic with useful info.
	 */

	beq-	CR0,	EXT(ihandler)
	
#endif /* DEBUG */

	lwz	r3,	PP_CPU_DATA(r2)

	lwz	r3,	CPU_ACTIVE_THREAD(r3)
	lwz	r3,	THREAD_PCB(r3)
	lwz	r1,	PCB_KSP(r3)

	cmpwi	CR1,	r1,	0	/* zero implies already on kstack */
	bne	CR1,	.L_kstackfree	/* This test is also used below */

	mfsprg	r1,	1		/* recover previous stack ptr */

	/* On kernel stack, allocate stack frame and check for overflow */

	/* Move stack pointer below redzone + reserve a saved_state */

	subi	r1,	r1,	FM_REDZONE+SS_SIZE 

	b	.L_kstack_save_state

.L_kstackfree:
	mr	r1,	r3		/* r1 points to save area of pcb */

.L_kstack_save_state:	

	/* Once we reach here, r1 contains the place
         * where we can store a ppc_saved_state structure. This may
	 * or may not be part of a pcb, we test that again once
	 * we've saved state. (CR1 still holds test done on ksp)
	 */

	stw	r0,	SS_R0(r1)

	mfsprg	r0,	1
	stw	r0,	SS_R1(r1)

	mfsprg	r0,	2
	stw	r0,	SS_R2(r1)

	mfsprg	r0,	3
	stw	r0,	SS_R3(r1)

	stw	r4,	SS_R4(r1)
	stw	r5,	SS_R5(r1)
	stw	r6,	SS_R6(r1)
	stw	r7,	SS_R7(r1)
	stw	r8,	SS_R8(r1)
	stw	r9,	SS_R9(r1)
	stw	r10,	SS_R10(r1)
	stw	r11,	SS_R11(r1)
	stw	r12,	SS_R12(r1)
	stw	r13,	SS_R13(r1)
	stw	r14,	SS_R14(r1)
	stw	r15,	SS_R15(r1)
	stw	r16,	SS_R16(r1)
	stw	r17,	SS_R17(r1)
	stw	r18,	SS_R18(r1)
	stw	r19,	SS_R19(r1)
	stw	r20,	SS_R20(r1)
	stw	r21,	SS_R21(r1)
	stw	r22,	SS_R22(r1)
	stw	r23,	SS_R23(r1)
	stw	r24,	SS_R24(r1)
	stw	r25,	SS_R25(r1)
	stw	r26,	SS_R26(r1)
	stw	r27,	SS_R27(r1)
	stw	r28,	SS_R28(r1)
	stw	r29,	SS_R29(r1)
	stw	r30,	SS_R30(r1)
	stw	r31,	SS_R31(r1)

	/* Save more state - cr,xer,lr,ctr,srr0,srr1,mq
	 * some of this comes back out from the per-processor structure
	 * pointed to by r2
	 */

	lwz	r0,	PP_SAVE_CR(r2)
	stw	r0,	SS_CR(r1)
	
	lwz	r0,	PP_SAVE_SRR0(r2)
	stw	r0,	SS_SRR0(r1)

	/* WARNING - r0 from the following instruction is used
	 * further below
	 */

	lwz	r0,	PP_SAVE_SRR1(r2)
	stw	r0,	SS_SRR1(r1)


	/* WARNING! These two instructions assume that we didn't take
	 * any type of exception whilst saving state, it's a bit late
	 * for that!
	 * TODO NMGS move these up the code somehow, put in PROC_REG?
	 */

	mfdsisr	ARG2			/* r4	*/
	mfdar	ARG3			/* r5	*/

	/* work out if we will reenable interrupts or not depending
	 * upon the state which we came from, store as tmp in ARG5
	 */
	li	ARG5,	MSR_SUPERVISOR_INT_OFF
	rlwimi	ARG5,	r0,	0,	MSR_EE_BIT,	MSR_EE_BIT

	mfxer	r0
	stw	r0,	SS_XER(r1)

	mflr	r0
	stw	r0,	SS_LR(r1)

	mfctr	r0
	stw	r0,	SS_CTR(r1)

	/* Don't save MQ, we don't bother for now */
	
	/* Free the reservation whilst saving SR_COPYIN */

	mfsr	r0,	SR_COPYIN_NAME
	li	ARG7,	SS_SR_COPYIN
	sync				/* bug fix for 3.2 processors */
	stwcx.	r0,	ARG7,	r1
	stw	r0,	SS_SR_COPYIN(r1)
	
	/* r3 still holds our pcb, CR1 still holds test to see if we're
	 * in the pcb or have saved state on the kernel stack */

	mr	ARG1,	r1		/* Preserve saved_state ptr in ARG1 */

	beq	CR1,	.L_state_on_kstack/* using above test for pcb/stack */

	/* We saved state in the pcb, recover the stack pointer */
	lwz	r1,	PCB_KSP(r3)

	/* Mark that we're occupying the kernel stack for sure now */	
	li	r0,	0
	stw	r0,	PCB_KSP(r3)

.L_state_on_kstack:	
		
	/* Phew!
	 *
	 * To summarise, when we reach here, we have filled out
	 * a ppc_saved_state structure either in the pcb or on
	 * the kernel stack, and the stack is marked as busy.
	 * r4 holds a pointer to this state, r1 is now the stack
	 * pointer no matter where the state was savd.
	 * We now generate a small stack frame with backpointers
	 * to follow the calling
	 * conventions. We set up the backpointers to the trapped
	 * routine allowing us to backtrace.
	 */

/* WARNING!! Using mfsprg below assumes interrupts are still off here */
	
	subi	r1,	r1,	FM_SIZE
	mfsprg	r0,	1
	stw	r0,	FM_BACKPTR(r1)	/* point back to previous stackptr */

#if	DEBUG
	/* If debugging, we need two frames, the first being a dummy
	 * which links back to the trapped routine. The second is
	 * that which the C routine below will need
	 */
	lwz	r0,	SS_SRR0(r1)
	stw	r0,	FM_LR_SAVE(r1)	/* save old instr ptr as LR value */

	//stwu	r1,	-FM_SIZE(r1)	/* and make new frame */
	stw	r1,	-FM_SIZE(r1)	/* and make new frame */
	subi	r1,	r1,	FM_SIZE
#endif /* DEBUG */


	/* call trap handler proper, with
	 *   ARG0 = type		(not yet, holds pcb ptr)
	 *   ARG1 = saved_state ptr	(already there)
	 *   ARG2 = dsisr		(already there)
	 *   ARG3 = dar			(already there)
	 */

	/* This assumes that no (non-tlb) exception/interrupt has occured
	 * since PP_SAVE_* get clobbered by an exception...
	 */
	lwz	ARG0,	PP_SAVE_EXCEPTION_TYPE(r2)

	/* Reenable interrupts if they were enabled before we came here */
	mtmsr	ARG5
	isync

	/* syscall exception might warp here if there's nothing left
	 * to do except generate a trap
	 */
.L_call_trap:	
	bl	EXT(trap)

	/*
	 * Ok, return from C function
	 *
	 * This is also the point where new threads come when they are created.
	 * The new thread is setup to look like a thread that took an 
	 * interrupt and went immediatly into trap.
	 *
	 * r3 must hold the pointer to the saved state, either on the
	 * stack or in the pcb.
	 */

thread_return:
	/* Reload the saved state */

	/* r0-3 will be restored last, use as temp for now */

	lwz	r4,	SS_R4(r3)
	lwz	r5,	SS_R5(r3)
	lwz	r6,	SS_R6(r3)
	lwz	r7,	SS_R7(r3)
	lwz	r8,	SS_R8(r3)
	lwz	r9,	SS_R9(r3)
	lwz	r10,	SS_R10(r3)
	lwz	r11,	SS_R11(r3)
	lwz	r12,	SS_R12(r3)
	lwz	r13,	SS_R13(r3)
	lwz	r14,	SS_R14(r3)
	lwz	r15,	SS_R15(r3)
	lwz	r16,	SS_R16(r3)
	lwz	r17,	SS_R17(r3)
	lwz	r18,	SS_R18(r3)
	lwz	r19,	SS_R19(r3)
	lwz	r20,	SS_R20(r3)
	lwz	r21,	SS_R21(r3)
	lwz	r22,	SS_R22(r3)
	lwz	r23,	SS_R23(r3)
	lwz	r24,	SS_R24(r3)
	lwz	r25,	SS_R25(r3)
	lwz	r26,	SS_R26(r3)
	lwz	r27,	SS_R27(r3)
	lwz	r28,	SS_R28(r3)
	lwz	r29,	SS_R29(r3)
	lwz	r30,	SS_R30(r3)
	lwz	r31,	SS_R31(r3)

	lwz	r0,	SS_XER(r3)
	mtxer	r0
	lwz	r0,	SS_LR(r3)
	mtlr	r0
	lwz	r0,	SS_CTR(r3)
	mtctr	r0
	lwz	r0,	SS_SR_COPYIN(r3)
	isync
	mtsr	SR_COPYIN_NAME,	r0
	isync
	

	/* TODO NMGS don't restore mq since we're not 601-specific enough */

	/* Disable interrupts */
	li	r0, MSR_SUPERVISOR_INT_OFF
	mtmsr	r0


	/* Is this the last saved state, found in the pcb? */
	/* TODO NMGS optimise this by spreading it through the code above? */

	/* After this we no longer to keep &per_proc_info in r2 */
	
	lwz	r1,	PP_CPU_DATA(r2)
	lwz	r1,	CPU_ACTIVE_THREAD(r1)
	lwz	r0,	THREAD_PCB(r1)

	cmp	CR0,0,	r0,	r3
	bne	CR0,	.L_notthelast_trap

	/* our saved state is actually part of the thread's pcb so
	 * we need to mark that we're leaving the kernel stack and
	 * jump into user space
	 */

	/* Mark the kernel stack as free */

	/* There may be a critical region here for traps(interrupts?)
	 * once the stack is marked as free, PCB_SR0 may be trampled on
	 * so interrupts should be switched off
	 */
	/* Release any processor reservation we may have had too */

	lwz	r2,	THREAD_KERNEL_STACK(r1)
	addi	r0,	r2,	KSTK_SIZE-KS_SIZE-FM_SIZE
	li	r2,	PCB_KSP
/* we have to use an indirect store to clear reservation */
	sync				/* bug fix for 3.2 processors */
	stwcx.	r0,	r2,	r3		/* clear reservation */
	stw	r0,	PCB_KSP(r3)		/* mark stack as free */
	
	/* We may be returning to something in the kernel space.
	 * If we are, we can skip the trampoline and just rfi,
	 * since we don't want to restore the user's space regs
	 */
	lwz	r0,	SS_SRR1(r3)
	andi.	r0,	r0,	MASK(MSR_PR)
	beq-	.L_trap_ret_to_kspace
	
	/* If jumping into user space, we should restore the user's
	 * segment register 0. We jump via a trampoline in physical mode
	 */
		
	lwz	r0,	SS_CR(r3)
	mtcrf	0xFF,r0

	/* the trampoline code takes r1-r3 from sprg1-3, and uses r1-3
	 * as arguments */
	lwz	r0,	SS_R1(r3)
	mtsprg	1,	r0
	lwz	r0,	SS_R2(r3)
	mtsprg	2,	r0
	lwz	r0,	SS_R3(r3)
	mtsprg	3,	r0

	lwz	r0,	SS_R0(r3)

	/* Prepare to rfi to the exception exit routine, which is
	 * in physical address space */
	addis	r1,	0,	ha16(EXT(exception_exit))
	addi	r1,	r1,	lo16(EXT(exception_exit))
	lwz	r1,	0(r1)
	mtsrr0	r1
	li	r1,	MSR_VM_OFF
	mtsrr1	r1

	
	lwz	r1,	PCB_SR0(r3)	/* For trampoline */
	lwz	r2,	SS_SRR0(r3)	/* For trampoline */
	
	lwz	r3,	SS_SRR1(r3)	/* load the last register... */

	rfi

.L_trap_ret_to_kspace:	
.L_notthelast_trap:
	/* If we're not the last trap on the kernel stack life is easier,
	 * we don't need to switch back into the user's segment. we can
	 * simply restore the last registers and rfi
	 */

	lwz	r0,	SS_CR(r3)
	mtcrf	0xFF,r0
	lwz	r0,	SS_SRR0(r3)
	mtsrr0	r0
	lwz	r0,	SS_SRR1(r3)
	mtsrr1	r0

	lwz	r0,	SS_R0(r3)
	lwz	r1,	SS_R1(r3)
	/* critical region for traps(interrupt?) since r1 no longer points
	 * to bottom of stack. Could be fixed. But interrupts are off (?).
	 */
	lwz	r2,	SS_R2(r3)	/* r2 is a constant (&per_proc_info) */
	/* r3 restored last */
	lwz	r4,	SS_R4(r3)
	lwz	r5,	SS_R5(r3)
	/* and lastly... */
	lwz	r3,	SS_R3(r3)

	rfi				/* return to calling context */



/*QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
 * void			CallPseudoKernel	( void )
 *
 * This op provides a means of invoking the BlueBox PseudoKernel from a
 * system (68k) or native (PPC) context while changing BlueBox interruption
 * state atomically. As an added bonus, this op clobbers only r0 while leaving
 * the rest of PPC user state registers intact.
 *
 * This op is invoked as follows:
 *	li r0, kCallPseudoKernelNumber	// load this op's firmware call number
 *	sc				// invoke CallPseudoKernel
 *	dc.l	CallPseudoKernelDescriptorPtr	// static pointer to CallPseudoKernelDescriptor
 *
 * NOTE: The CallPseudoKernelDescriptor and the word pointed to by
 * intControlAddr must be locked, else this op will crash the kernel.
 *
 * Entry:	VM switched ON
 *		Interrupts  OFF
 *              original r1-3 saved in sprg1-3
 *              original srr0 and srr1 saved in per_proc_info structure
 *              original cr            saved in per_proc_info structure
 *              exception type         saved in per_proc_info structure
 *              r1 = scratch
 *              r2 = virt addr of per_proc_info
 *		r3 = exception type (one of EXC_...)
 *
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ*/
	.align	5
					
__fcCallPseudoKernel:

	// Needed to save some state so this code matches NuKernel Support
	lwz	r1,	PP_CPU_DATA(r2)
	lwz	r3,	CPU_ACTIVE_THREAD(r1)
	lwz	r1,	THREAD_PCB(r3)

	stw	r0,	SS_R0(r1)

	lwz	r0,	PP_SAVE_SRR0(r2)
	stw	r0,	SS_SRR0(r1)

	lwz	r0,	PP_SAVE_SRR1(r2)
	stw	r0,	SS_SRR1(r1)

	lwz	r0,	PP_SAVE_CR(r2)
	stw	r0,	SS_CR(r1)

	mfsprg	r0,	1
	stw	r0,	SS_R1(r1)

	mfsprg	r0,	2
	stw	r0,	SS_R2(r1)

	mfsprg	r0,	3
	stw	r0,	SS_R3(r1)

	stw	r4,	SS_R4(r1)
	stw	r5,	SS_R5(r1)
	stw	r6,	SS_R6(r1)
	stw	r7,	SS_R7(r1)
	stw	r8,	SS_R8(r1)
	stw	r9,	SS_R9(r1)
	stw	r10,	SS_R10(r1)
	stw	r11,	SS_R11(r1)
	stw	r12,	SS_R12(r1)

	// word following the sc is the descriptor's address
	lwz	r3,	SS_SRR0(r1)

	lwz	r10,	SS_CR(r1)	// setup r10 with CR

	lwz	r3,	0(r3)		// get descriptor's address

	lwz	r11,	CPKD_INTCONTROLADDR(r3)
	lwz	r4,	CPKD_PC(r3)
	lwz	r6,	CPKD_NEWSTATE(r3)
	lwz	r7,	CPKD_INTSTATEMASK(r3)
	lwz	r8,	0(r11)		// get current interruption control word
	lwz	r5,	CPKD_GPR0(r3)
	lwz	r12,	CPKD_SYSCONTEXTSTATE(r3)
	andc	r9, r8, r7		// remove current state
	and	r8, r8, r7		// extract current state
	cmplw	r8, r12			// test for entry from system context
	or	r9, r9, r6		// insert new state
	bne	CallFromAlternateContext		

CallFromSystemContext:
	lwz	r6,	CPKD_INTCR2SHIFT(r3)
	lwz	r7,	CPKD_INTCR2MASK(r3)
	srw	r10, r10, r6		// position live CR2 from cr register as required
	andc	r9, r9, r7		// remove old backup CR2
	and	r10, r10, r7		// mask live CR2
	or	r9, r9, r10		// insert CR2 into backup CR2
	b	CallContinue

CallFromAlternateContext:
CallContinue:
	stw	r9,	0(r11)	// update interruption control word 

	// introduce new pc and gr0 contents
	lwz	r6,	SS_SRR1(r1)
	stw	r4,	SS_SRR0(r1)
	stw	r5,	SS_R0(r1)

	// insert updated fe0, fe1, se, and be bits into user msr
	rlwimi	r6, r6, 0, MSR_FE1_BIT, MSR_FE0_BIT
	/* Turn off FPU */
	rlwinm	r6,	r6,	0,	MSR_FP_BIT+1,	MSR_FP_BIT-1

	// zero single step and branch step control in user msr
	stw	r6,	SS_SRR1(r1) // update user msr

	/*
	** Restore state for exit
	*/
	lwz	r0,	SS_CR(r1)
	mtcrf	0xFF,	r0

	/* the trampoline code takes r1-r3 from sprg1-3, and uses r1-3
	 * as arguments */
	lwz	r0,	SS_R1(r1)
	mtsprg	1,	r0

	lwz	r0,	SS_R2(r1)
	mtsprg	2,	r0

	lwz	r0,	SS_R3(r1)
	mtsprg	3,	r0

	lwz	r0,	SS_R0(r1)

	lwz	r4,	SS_R4(r1)
	lwz	r5,	SS_R5(r1)
	lwz	r6,	SS_R6(r1)
	lwz	r7,	SS_R7(r1)
	lwz	r8,	SS_R8(r1)
	lwz	r9,	SS_R9(r1)
	lwz	r10,	SS_R10(r1)
	lwz	r11,	SS_R11(r1)
	lwz	r12,	SS_R12(r1)

	/* Prepare to rfi to the exception exit routine, which is
	 * in physical address space */
	addis	r3,	0,	ha16(EXT(exception_exit))
	addi	r3,	r3,	lo16(EXT(exception_exit))
	lwz	r3,	0(r3)
	mtsrr0	r3
	li	r3,	MSR_VM_OFF
	mtsrr1	r3

	lwz	r2,	SS_SRR0(r1)	/* For trampoline */
	lwz	r3,	SS_SRR1(r1)	/* For trampoline */
	lwz	r1,	PCB_SR0(r1)	/* load the last register... */

	rfi



/*QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
 * void ExitPseudoKernel ( ExitPseudoKernelDescriptorPtr exitDescriptor )
 *
 * This op provides a means of exiting from the BlueBox PseudoKernel to a
 * user context while changing the BlueBox interruption state atomically.
 * It also allows all of the user state PPC registers to be loaded.
 *
 * This op is invoked as follows:
 *	lwz r3, ExitPseudoKernelDescriptorPtr
 *	li r0, kCallPseudoKernelNumber	// load this op's firmware call number
 *	sc				// invoke CallPseudoKernel
 *
 * Entry:	VM switched ON
 *		Interrupts  OFF
 *              original r1-3 saved in sprg1-3
 *              original srr0 and srr1 saved in per_proc_info structure
 *              original cr            saved in per_proc_info structure
 *              exception type         saved in per_proc_info structure
 *              r1 = scratch
 *              r2 = virt addr of per_proc_info
 *		r3 = exception type (one of EXC_...)
 *
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ*/
	.align	5
__fcExitPseudoKernel:
	// Needed to save some state so this code matches NuKernel Support
	lwz	r1,	PP_CPU_DATA(r2)
	lwz	r1,	CPU_ACTIVE_THREAD(r1)
	lwz	r1,	THREAD_PCB(r1)

	stw	r0,	SS_R0(r1)

	lwz	r0,	PP_SAVE_SRR0(r2)
	stw	r0,	SS_SRR0(r1)

	lwz	r0,	PP_SAVE_SRR1(r2)
	stw	r0,	SS_SRR1(r1)

	lwz	r0,	PP_SAVE_CR(r2)
	stw	r0,	SS_CR(r1)

	mfsprg	r0,	1
	stw	r0,	SS_R1(r1)

	mfsprg	r0,	2
	stw	r0,	SS_R2(r1)

	mfsprg	r0,	3
	stw	r0,	SS_R3(r1)

	stw	r4,	SS_R4(r1)
	stw	r5,	SS_R5(r1)
	stw	r6,	SS_R6(r1)
	stw	r7,	SS_R7(r1)
	stw	r8,	SS_R8(r1)
	stw	r9,	SS_R9(r1)
	stw	r10,	SS_R10(r1)
	stw	r11,	SS_R11(r1)
	stw	r12,	SS_R12(r1)

	/* start of actual routine */

	lwz	r9, SS_SRR1(r1)

	lwz	r3, SS_R3(r1)		// restore r3, it is exitdescptr

	lwz	r8, EPKD_CR(r3)

	lwz	r11, EPKD_INTCONTROLADDR(r3)
	lwz	r4, EPKD_PC(r3)
	lwz	r7, EPKD_NEWSTATE(r3)
	lwz	r10, EPKD_INTSTATEMASK(r3)
	lwz	r5, 0(r11)	// get current interruption control word
	lwz	r0, EPKD_SYSCONTEXTSTATE(r3)
	andc	r12, r5, r10		// remove current state
	cmplw	r7, r0			// test for exit to system context
	or	r12, r12, r7		// insert new state
	lwz	r0, EPKD_MSRUPDATE(r3)
	beq	ExitToSystemContext

ExitToAlternateContext:
	lwz	r5, EPKD_INTPENDINGMASK(r3)
	lwz	r6, EPKD_INTPENDINGPC(r3)
	and.	r7, r12, r5		// test for pending 'rupt in backup cr2
	beq	ExitUpdateRuptControlWord	//   and enter alternate context if none pending
	mr	r4, r6			// otherwise, introduce entry abort pc
	b	ExitNoUpdateRuptControlWord	//   and prepare to reenter pseudokernel

ExitToSystemContext:
	lwz	r5, EPKD_INTCR2SHIFT(r3)
	lwz	r6, EPKD_INTCR2MASK(r3)		
	slw	r7, r12, r5		// position backup cr2
	and	r7, r7, r6		//   and mask it
	or	r8, r8, r7		//   then or it into the live cr2
											// ...fall through into system context

ExitUpdateRuptControlWord:
	// insert updated fe0, fe1, se, and be bits into user msr
	rlwimi	r9, r0, 0, MSR_FE0_BIT, MSR_FE1_BIT
	/* Turn off FPU */
	rlwinm	r9,	r9,	0,	MSR_FP_BIT+1,	MSR_FP_BIT-1
	stw	r12, 0(r11)		// update interruption control word 
ExitNoUpdateRuptControlWord:
	lwz	r5, EPKD_GPR0(r3)
	lwz	r6, EPKD_SP(r3)
	lwz	r7, EPKD_GPR3(r3)
											// load caller's new register contents

	stw	r4, SS_SRR0(r1)
	stw	r5, SS_R0(r1)
	stw	r6, SS_R1(r1)
	stw	r7, SS_R3(r1)
	stw	r8, SS_CR(r1)
	stw	r9, SS_SRR1(r1)


	lwz	r0,	SS_CR(r1)
	mtcrf	0xFF,r0			/* update cr, it is live */

	/* the trampoline code takes r1-r3 from sprg1-3, and uses r1-3
	 * as arguments */
	lwz	r0,	SS_R1(r1)
	mtsprg	1,	r0
	lwz	r0,	SS_R2(r1)
	mtsprg	2,	r0
	lwz	r0,	SS_R3(r1)
	mtsprg	3,	r0

	lwz	r0,	SS_R0(r1)

	lwz	r4,	SS_R4(r1)
	lwz	r5,	SS_R5(r1)
	lwz	r6,	SS_R6(r1)
	lwz	r7,	SS_R7(r1)
	lwz	r8,	SS_R8(r1)
	lwz	r9,	SS_R9(r1)
	lwz	r10,	SS_R10(r1)
	lwz	r11,	SS_R11(r1)
	lwz	r12,	SS_R12(r1)

	/* Prepare to rfi to the exception exit routine, which is
	 * in physical address space */
	addis	r3,	0,	ha16(EXT(exception_exit))
	addi	r3,	r3,	lo16(EXT(exception_exit))
	lwz	r3,	0(r3)
	mtsrr0	r3
	li	r3,	MSR_VM_OFF
	mtsrr1	r3

	lwz	r2,	SS_SRR0(r1)	/* For trampoline */
	lwz	r3,	SS_SRR1(r1)	/* load the last register... */
	lwz	r1,	PCB_SR0(r1)	/* For trampoline... */

	rfi


/*
 * void cthread_set_self(cproc_t p)
 *
 * set's thread state "user_value"
 *
 * This op is invoked as follows:
 *	li r0, CthreadSetSelfNumber	// load the fast-trap number
 *	sc				// invoke fast-trap
 *	blr
 *
 * Entry:	VM switched ON
 *		Interrupts  OFF
 *              original r1-3 saved in sprg1-3
 *              original srr0 and srr1 saved in per_proc_info structure
 *              original cr            saved in per_proc_info structure
 *              exception type         saved in per_proc_info structure
 *              r1 = scratch
 *              r2 = virt addr of per_proc_info
 *		r3 = exception type (one of EXC_...)
 *
 */
 	.text
	.align	5
__fcCthreadSetSelfNumber:
	lwz	r1,	PP_CPU_DATA(r2)
	lwz	r1,	CPU_ACTIVE_THREAD(r1)
	lwz	r1,	THREAD_PCB(r1)

	mfsprg	r3,	3
	stw	r3,	CTHREAD_SELF(r1)

	/* Prepare to rfi to the exception exit routine, which is
	 * in physical address space */
	addis	r3,	0,	ha16(EXT(exception_exit))
	addi	r3,	r3,	lo16(EXT(exception_exit))
	lwz	r3,	0(r3)
	mtsrr0	r3
	li	r3,	MSR_VM_OFF
	mtsrr1	r3

	lwz	r3,	PP_SAVE_SRR1(r2)	/* load the last register... */
	lwz	r2,	PP_SAVE_SRR0(r2)	/* For trampoline */
	lwz	r1,	PCB_SR0(r1)		/* For trampoline... */

	rfi


/*
 * ur_cthread_t ur_cthread_self(void)
 *
 * return thread state "user_value"
 *
 * This op is invoked as follows:
 *	li r0, UrCthreadSelfNumber	// load the fast-trap number
 *	sc				// invoke fast-trap
 *	blr
 *
 * Entry:	VM switched ON
 *		Interrupts  OFF
 *              original r1-3 saved in sprg1-3
 *              original srr0 and srr1 saved in per_proc_info structure
 *              original cr            saved in per_proc_info structure
 *              exception type         saved in per_proc_info structure
 *              r1 = scratch
 *              r2 = virt addr of per_proc_info
 *		r3 = exception type (one of EXC_...)
 *
 */
 	.text
	.align	5
__fcUrCthreadSelfNumber:
	lwz	r1,	PP_CPU_DATA(r2)
	lwz	r1,	CPU_ACTIVE_THREAD(r1)
	lwz	r1,	THREAD_PCB(r1)

	lwz	r3,	CTHREAD_SELF(r1)
	mtsprg	3,	r3


	/* Prepare to rfi to the exception exit routine, which is
	 * in physical address space */
	addis	r3,	0,	ha16(EXT(exception_exit))
	addi	r3,	r3,	lo16(EXT(exception_exit))
	lwz	r3,	0(r3)
	mtsrr0	r3
	li	r3,	MSR_VM_OFF
	mtsrr1	r3

	lwz	r3,	PP_SAVE_SRR1(r2)	/* load the last register... */
	lwz	r2,	PP_SAVE_SRR0(r2)	/* For trampoline */
	lwz	r1,	PCB_SR0(r1)		/* For trampoline... */

	rfi



/*
 * shandler(type)
 *
 * Entry:	VM switched ON
 *		Interrupts  OFF
 *              original r1-3 saved in sprg1-3
 *              original srr0 and srr1 saved in per_proc_info structure
 *              original cr            saved in per_proc_info structure
 *              exception type         saved in per_proc_info structure
 *              r1 = scratch
 *              r2 = virt addr of per_proc_info
 *		r3 = exception type (one of EXC_...)
 */

/*
 * If pcb.ksp == 0 then the kernel stack is already busy,
 *                 this is an error - jump to the debugger entry
 * 
 * otherwise       we save a (partial - TODO ) ppc_saved_state
 *                 in the pcb, and, depending upon the type of
 *                 syscall, look it up in the kernel table
 *		   or pass it to the server.
 *
 * on return, we do the reverse, the state is popped from the pcb
 * and pcb.ksp is set to the top of stack.
 */

ENTRY(shandler, TAG_NO_FRAME_USED)	/* What tag should this have?! */

#if DEBUG
		/* Make sure we're not on the interrupt stack */
	addis	r1,	0,	ha16(EXT(istackptr))
	addi	r1,	r1,	lo16(EXT(istackptr))
	lwz	r1,	0(r1)
	cmpwi	CR0,	r1,	0

	/* If we are on the interrupt stack, treat as an interrupt,
	 * the interrupt handler will panic with useful info.
	 */
	beq	EXT(ihandler)
	
#endif /* DEBUG */

	/*
	**	check for special BlueBox calls
	*/
	addis	r1,	0,	ha16(EXT(nsysent))
	addi	r1,	r1,	lo16(EXT(nsysent))
	lwz	r3,	0(r1)
	cmpw	CR0,	r0,	r3
	ble	L_shandler_syscall

	cmpwi	CR0,	r0,	0x7FFC
	beq-	__fcCallPseudoKernel

	cmpwi	CR0,	r0,	0x7FFE
	beq-	__fcExitPseudoKernel

	cmpwi	CR0,	r0,	0x7FF1	;CthreadSetSelfNumber
	beq-	__fcCthreadSetSelfNumber

	cmpwi	CR0,	r0,	0x7FF2	;UrCthreadSelfNumber
	beq-	__fcUrCthreadSelfNumber

L_shandler_syscall:
	lwz	r3,	PP_CPU_DATA(r2)
	lwz	r3,	CPU_ACTIVE_THREAD(r3)
	lwz	r1,	THREAD_PCB(r3)
#if DEBUG
		/* Check that we're not on kernel stack already */
	lwz	r1,	PCB_KSP(r1)

	/* If we are on a kernel stack, treat as a interrupt
	 * the interrupt handler will panic with useful info.
	 */
	cmpwi	CR1,	r1,	0
	/* tell the handler that we performed a syscall from this loc */
	li	r3,	EXC_SYSTEM_CALL
	beq	CR1,	EXT(ihandler)

/* Reload active thread into r3 and PCB into r1 as before */
	lwz	r3,	PP_CPU_DATA(r2)
	lwz	r3,	CPU_ACTIVE_THREAD(r3)
	lwz	r1,	THREAD_PCB(r3)
#endif /* DEBUG */

	/* Once we reach here, r1 contains the pcb
         * where we can store a partial ppc_saved_state structure,
	 * and r3 contains the active thread structure (used later)
	 */

	/* TODO NMGS - could only save callee saved regs for
	 * many(all?) Mach syscalls, not for unix since might be fork() etc
	 */
	stw	r0,	SS_R0(r1)    /* Save trap number for debugging */

	lwz	r0,	PP_SAVE_CR(r2)
	stw	r0,	SS_CR(r1)
	
	lwz	r0,	PP_SAVE_SRR0(r2)  /* Save SRR0 in debug call frame */
	stw	r0,	SS_SRR0(r1)

	lwz	r0,	PP_SAVE_SRR1(r2)
	oris	r0,	r0,	MSR_SYSCALL_MASK >> 16 /* Mark syscall state */
	stw	r0,	SS_SRR1(r1)
	
	mfsprg	r0,	1
	stw	r0,	SS_R1(r1)

	mfsprg	r0,	2
	stw	r0,	SS_R2(r1)

	/* SAVE ARG REGISTERS? - YES, needed by server system calls */
	mfsprg	r0,	3
	stw	r0,	SS_R3(r1)

	stw	r4,	SS_R4(r1)
	stw	r5,	SS_R5(r1)
	stw	r6,	SS_R6(r1)
	stw	r7,	SS_R7(r1)
	stw	r8,	SS_R8(r1)
	stw	r9,	SS_R9(r1)
	stw	r10,	SS_R10(r1)

	stw	r11,	SS_R11(r1)
	stw	r12,	SS_R12(r1)
	stw	r13,	SS_R13(r1)

		/*
		 * Callee saved state, need to save in case we
		 * are executing a 'fork' unix system call or similar
		 */

	stw	r14,	SS_R14(r1)
	stw	r15,	SS_R15(r1)
	stw	r16,	SS_R16(r1)
	stw	r17,	SS_R17(r1)
	stw	r18,	SS_R18(r1)
	stw	r19,	SS_R19(r1)
	stw	r20,	SS_R20(r1)
	stw	r21,	SS_R21(r1)
	stw	r22,	SS_R22(r1)
	stw	r23,	SS_R23(r1)
	stw	r24,	SS_R24(r1)
	stw	r25,	SS_R25(r1)

	/* We use these registers in the code below, save them */
	
	stw	r26,	SS_R26(r1)
	stw	r27,	SS_R27(r1)
	stw	r28,	SS_R28(r1)
	stw	r29,	SS_R29(r1)
	stw	r30,	SS_R30(r1)
	stw	r31,	SS_R31(r1)
	
	/* Save more state - cr,xer,lr,ctr,srr0,srr1,mq
	 * some of this comes back out from the per-processor structure
	 */

	mflr	r0
	stw	r0,	SS_LR(r1)

	/* Volatile state, still restoring for sensible corefiles */
	mfctr	r0
	stw	r0,	SS_CTR(r1)

	mfxer	r0
	stw	r0,	SS_XER(r1)

	/* Don't bother with MQ for now */
	
	/* Free the reservation whilst saving SR_COPYIN */

	mfsr	r0,	SR_COPYIN_NAME
	li	r31,	SS_SR_COPYIN
	sync				/* bug fix for 3.2 processors */
	stwcx.	r0,	r31,	r1
	stw	r0,	SS_SR_COPYIN(r1)
	
	/* We saved state in the pcb, recover the stack pointer */
	lwz	r31,	PCB_KSP(r1)	/* Get ksp */

	li	r0,	0
	stw	r0,	PCB_KSP(r1)	/* Mark stack as busy with 0 val */
	
	/* Phew!
	 *
	 * To summarise, when we reach here, we have filled out
	 * a (partial) ppc_saved_state structure in the pcb, moved
	 * to kernel stack, and the stack is marked as busy.
	 * r1 holds a pointer to this state, and r3 holds a
	 * pointer to the active thread. r31 holds kernel stack ptr.
	 * We now move onto the kernel stack and generate a small
	 * stack frame to follow the calling
	 * conventions. We set up the backpointers to the calling
	 * routine allowing us to backtrace.
	 */

	mr	r30,	r1		/* Save pointer to state in r30 */
	mr	r1,	r31		/* move to kernel stack */
	mfsprg	r0,	1		/* get old stack pointer */
	stw	r0,	FM_BACKPTR(r1)	/* store as backpointer */
		
#if	DEBUG
	/* If debugging, we need two frames, the first being a dummy
	 * which links back to the trapped routine. The second is
	 * that which the C routine below will need
	 */
	stw	r29,	FM_LR_SAVE(r1)	/* save old instr ptr as LR value */
	//stwu	r1,	-FM_SIZE(r1)	/* point back to previous frame */
	stw	r1,	-FM_SIZE(r1)	/* point back to previous frame */
	subi	r1,	r1,	FM_SIZE
#endif /* DEBUG */

	//stwu	r1,	-(FM_SIZE+ARG_SIZE)(r1)
	//stwu	r1,	-(FM_SIZE+FM_REDZONE)(r1)
	stw	r1,	-(FM_SIZE+FM_REDZONE)(r1)
	subi	r1,	r1,	(FM_SIZE+FM_REDZONE)

	/* switch on interrupts now kernel stack is busy and valid */
	mfmsr	r0
	rlwimi	r0,	r0,	0,	MSR_EE_BIT,	MSR_EE_BIT
	mtmsr	r0


	/* we should still have r1=ksp, r3(ie ARG0)=current-thread,
	 * r30 = pointer to saved state (in pcb)
	 */
	
	/* Work out what kind of syscall we have to deal with. 
	 */

#if MACH_ASSERT
	/* Call a function that can print out our syscall info */
	mr	r4,	r30
	bl	syscall_trace
	/* restore those volatile argument registers */
	lwz	r4,	SS_R4(r30)
	lwz	r5,	SS_R5(r30)
	lwz	r6,	SS_R6(r30)
	lwz	r7,	SS_R7(r30)
	lwz	r8,	SS_R8(r30)
	lwz	r9,	SS_R9(r30)
	lwz	r10,	SS_R10(r30)
	
#endif /* MACH_ASSERT */
	mr	r3,	r30		/* put pcb in ARG0 */
	lwz	r0,	SS_R0(r30)
		
	cmpwi	CR0,	r0,	0
	blt-	.L_mach_kernel_syscall

	/* +ve syscall - go to server */

	b	EXT(unix_syscall)

.L_mach_kernel_syscall:

	b	EXT(mach_syscall)



.L_thread_syscall_return:

	li	r3, MSR_SUPERVISOR_INT_OFF
	mtmsr	r3
	
	/* thread_exception_return returns to here, almost all
	 * registers intact. It expects a full context restore
	 * of what it hasn't restored itself (ie. what we use).
	 *
	 * In particular for us,
	 * we still have     r31 points to the current thread,
	 *                   r30 points to the current pcb
	 */

	mr	r3,	r30
	mr	r1,	r31
	/* r0-2 will be restored last, use as temp for now */


	/* Callee saved state was saved and restored by the functions
	 * that we have called, assuming that we performed a standard
	 * function calling sequence. We only restore those that we
	 * are currently using.
	 *
	 * thread_exception_return arrives here, however, and it
	 * expects the full state to be restored
	 */
#if DEBUG
	/* the following callee-saved state should already be restored */
	
	lwz	r30,	SS_R14(r3)@ twne	r30,	r14
	lwz	r30,	SS_R15(r3)@ twne	r30,	r15
	lwz	r30,	SS_R16(r3)@ twne	r30,	r16
	lwz	r30,	SS_R17(r3)@ twne	r30,	r17
	lwz	r30,	SS_R18(r3)@ twne	r30,	r18
	lwz	r30,	SS_R19(r3)@ twne	r30,	r19
	lwz	r30,	SS_R20(r3)@ twne	r30,	r20
	lwz	r30,	SS_R21(r3)@ twne	r30,	r21
	lwz	r30,	SS_R22(r3)@ twne	r30,	r22
	lwz	r30,	SS_R23(r3)@ twne	r30,	r23
	lwz	r30,	SS_R24(r3)@ twne	r30,	r24
	lwz	r30,	SS_R25(r3)@ twne	r30,	r25
	lwz	r30,	SS_R26(r3)@ twne	r30,	r26
	lwz	r30,	SS_R27(r3)@ twne	r30,	r27
	lwz	r30,	SS_R28(r3)@ twne	r30,	r28
	lwz	r30,	SS_R29(r3)@ twne	r30,	r29
#endif /* DEBUG */
		
	lwz	r30,	SS_R30(r3)
	lwz	r31,	SS_R31(r3)

	lwz	r0,	SS_LR(r3)
	mtlr	r0

	/* Volatile state, still restoring for sensible corefiles */
	lwz	r0,	SS_CTR(r3)
	mtctr	r0

	lwz	r0,	SS_XER(r3)
	mtxer	r0

	lwz	r0,	SS_SR_COPYIN(r3)
	isync
	mtsr	SR_COPYIN_NAME,	r0
	isync

	/* mark kernel stack as free before restoring r30, r31 */

	/* we no longer need r2 pointer to per_proc_info at this point */
	
	/* There may be a critical region here for traps(interrupts?)
	 * once the stack is marked as free, PCB_SR0 may be trampled on
	 * so interrupts must be off.
	 */
	/* Clear reservation at the same time */
	lwz	r2,	THREAD_KERNEL_STACK(r1)
	addi	r0,	r2,	KSTK_SIZE-KS_SIZE-FM_SIZE
	li	r2,	PCB_KSP
/* we have to use an indirect store to clear reservation */
	sync				/* bug fix for 3.2 processors */
	stwcx.	r0,	r2,	r3		/* clear reservation */
	stw	r0,	PCB_KSP(r3)		/* mark stack as free */

	/* We may be returning to something in the kernel space.
	 * If we are, we can skip the trampoline and just rfi,
	 * since we don't want to restore the user's space regs
	 */
	lwz	r0,	SS_SRR1(r3)
	andi.	r0,	r0,	MASK(MSR_PR)
	bne+	.L_syscall_returns_to_user

	/* TODO NMGS - is this code in common with interrupts and traps?*/
	/* the syscall is returning to something in
	 * priviliged mode, can just rfi without modifying
	 * space registers
	 */

	lwz	r0,	SS_CR(r3)
	mtcrf	0xFF,r0
	lwz	r0,	SS_SRR0(r3)
	mtsrr0	r0
	lwz	r0,	SS_SRR1(r3)
	mtsrr1	r0

	lwz	r0,	SS_R0(r3)
	lwz	r1,	SS_R1(r3)
	/* critical region for traps(interrupt?) since r1 no longer points
	 * to bottom of stack. Could be fixed. But interrupts are off (?).
	 */

	lwz	r2,	SS_R2(r3)
	/* r3 restored last */
	lwz	r3,	SS_R3(r3)

	rfi				/* return to calling context */

.L_syscall_returns_to_user:	

	/* If jumping into user space, we should restore the user's
	 * segment register 0. We jump via a trampoline in physical mode
	 * TODO NMGS this trampoline code probably isn't needed
	 */

	lwz	r0,	SS_CR(r3)
	mtcrf	0xFF,r0

	/* the trampoline code takes r1-r3 from sprg1-3, and uses r1-3
	 * as arguments */
	lwz	r0,	SS_R1(r3)
	mtsprg	1,	r0
	lwz	r0,	SS_R2(r3)
	mtsprg	2,	r0
	lwz	r0,	SS_R3(r3)
	mtsprg	3,	r0

	lwz	r0,	SS_R0(r3)
	/* Prepare to rfi to the exception exit routine, which is
	 * in physical address space */
	addis	r1,	0,	ha16(EXT(exception_exit))
	addi	r1,	r1,	lo16(EXT(exception_exit))
	lwz	r1,	0(r1)
	mtsrr0	r1
	li	r1,	MSR_VM_OFF
	mtsrr1	r1

	lwz	r1,	PCB_SR0(r3)	/* For trampoline */
	lwz	r2,	SS_SRR0(r3)	/* For trampoline */
	lwz	r3,	SS_SRR1(r3)	/* load the last register... */

	rfi



/*
 * thread_exception_return()
 *
 * Return to user mode directly from within a system call.
 */

ENTRY(thread_exception_return, TAG_NO_FRAME_USED)

.L_thread_exc_ret_check_ast:	

	/* Disable interrupts */
	li	r3,	MSR_SUPERVISOR_INT_OFF
	mtmsr	r3

	/* Check to see if there's an outstanding AST */
	/* We don't bother establishing a call frame even though CHECK_AST
	   can invoke ast_taken(), because it can just borrow our caller's
	   frame, given that we're not going to return.  */
		
	bl	EXT(check_for_ast)
	
.L_exc_ret_no_ast:
	/* arriving here, interrupts should be disabled */

	mfsprg	r2,	0	/* HACK - need to get around r2 problem */
	/* Get the active thread's PCB pointer to restore regs
	 */
	
	lwz	r31,	PP_CPU_DATA(r2)
	lwz	r31,	CPU_ACTIVE_THREAD(r31)
	lwz	r30,	THREAD_PCB(r31)

	/* If the MSR_SYSCALL_MASK isn't set, then we came from a trap,
	 * so warp into the return_from_trap (thread_return) routine,
	 * which takes PCB pointer in ARG0, not in r30!
	 */
	lwz	r0,	SS_SRR1(r30)
	mr	ARG0,	r30	/* Copy pcb pointer into ARG0 in case */

	/* test top half of msr */
	srwi	r0,	r0,	16
	cmpwi	CR0,	r0,	MSR_SYSCALL_MASK >> 16
	bne-	CR0,	thread_return

	/* Otherwise, go to thread_syscall return, which requires
	 * r31 holding current thread, r30 holding current pcb
	 */

	/*
	 * restore saved state here
	 * except for r0-2, r3, r29, r30 and r31 used
	 * by thread_syscall_return,
	 */
	lwz	r4,	SS_R4(r30)
	lwz	r5,	SS_R5(r30)
	lwz	r6,	SS_R6(r30)
	lwz	r7,	SS_R7(r30)
	lwz	r8,	SS_R8(r30)
	lwz	r9,	SS_R9(r30)
	lwz	r10,	SS_R10(r30)
	lwz	r11,	SS_R11(r30)
	lwz	r12,	SS_R12(r30)
	lwz	r13,	SS_R13(r30)
	lwz	r14,	SS_R14(r30)
	lwz	r15,	SS_R15(r30)
	lwz	r16,	SS_R16(r30)
	lwz	r17,	SS_R17(r30)
	lwz	r18,	SS_R18(r30)
	lwz	r19,	SS_R19(r30)
	lwz	r20,	SS_R20(r30)
	lwz	r21,	SS_R21(r30)
	lwz	r22,	SS_R22(r30)
	lwz	r23,	SS_R23(r30)
	lwz	r24,	SS_R24(r30)
	lwz	r25,	SS_R25(r30)
	lwz	r26,	SS_R26(r30)
	lwz	r27,	SS_R27(r30)
	lwz	r28,	SS_R28(r30)
	lwz	r29,	SS_R29(r30)

	b	.L_thread_syscall_return



/*
 * thread_bootstrap_return()
 *
 */
ENTRY(thread_bootstrap_return, TAG_NO_FRAME_USED)

	/* Disable interrupts */
	li	r3,	MSR_SUPERVISOR_INT_OFF
	mtmsr	r3

	/* Check for any outstanding ASTs and deal with them */

	bl	EXT(check_for_ast)
	
	/*
	** Restore from AST check
	*/
	mfsprg	r2,	0 	/* HACK - need to get around r2 problem */
	lwz	r3,	PP_CPU_DATA(r2)
	lwz	r3,	CPU_ACTIVE_THREAD(r3)
	lwz	r3,	THREAD_PCB(r3)

	/* Ok, we're all set, jump to thread_return as if we
	 * were just coming back from a trap (ie. r3 set up to point to pcb)
	 */	

	b	thread_return



/*
 * ihandler(type)
 *
 * Entry:	VM switched ON
 *		Interrupts  OFF
 *              original r1-3 saved in sprg1-3
 *              original srr0 and srr1 saved in per_proc structure
 *              original cr            saved in per_proc structure
 *              exception type (r3)    saved in per_proc structure
 *              r1 = scratch
 *              r2 = virt addr of per_proc_info
 *		r3 = exception type (one of EXC_...) also in per_proc
 *
 * gdbhandler is a close derivative, bugfixes to one may apply to the other!
 */

/* Build a saved state structure on the interrupt stack and call
 * the routine interrupt()
 */

ENTRY(ihandler, TAG_NO_FRAME_USED)	/* What tag should this have?! */

	/*
	 * get the value of istackptr, if it's zero then we're already on the
	 * interrupt stack, otherwise it points to a saved_state structure
	 * at the top of the interrupt stack.
	 */

	addis	r1,	0,	ha16(EXT(istackptr))
	addi	r1,	r1,	lo16(EXT(istackptr))	/* TODO assumes 1 CPU */
	lwz	r1,	0(r1)
	cmpwi	CR0,	r1,	0
	bne	CR0,	.L_istackfree

	/* We're already on the interrupt stack, get back the old
	 * stack pointer and make room for a frame
	 */

	mfsprg	r1,	1	/* recover old stack pointer */

	/* Move below the redzone where the interrupted thread may have
	 * been saving its state and make room for our saved state structure
	 */
	subi	r1,	r1,	FM_REDZONE+SS_SIZE

	
	
.L_istackfree:

	/* Once we reach here, r1 contains the adjusted stack pointer
         * where we can store a ppc_saved_state structure.
	 */

	stw	r0,	SS_R0(r1)

	mfsprg	r0,	1
	stw	r0,	SS_R1(r1)

	mfsprg	r0,	2
	stw	r0,	SS_R2(r1)

	mfsprg	r0,	3
	stw	r0,	SS_R3(r1)

	stw	r4,	SS_R4(r1)
	stw	r5,	SS_R5(r1)
	stw	r6,	SS_R6(r1)
	stw	r7,	SS_R7(r1)
	stw	r8,	SS_R8(r1)
	stw	r9,	SS_R9(r1)
	stw	r10,	SS_R10(r1)
	stw	r11,	SS_R11(r1)
	stw	r12,	SS_R12(r1)
	stw	r13,	SS_R13(r1)
	stw	r14,	SS_R14(r1)
	stw	r15,	SS_R15(r1)
	stw	r16,	SS_R16(r1)
	stw	r17,	SS_R17(r1)
	stw	r18,	SS_R18(r1)
	stw	r19,	SS_R19(r1)
	stw	r20,	SS_R20(r1)
	stw	r21,	SS_R21(r1)
	stw	r22,	SS_R22(r1)
	stw	r23,	SS_R23(r1)
	stw	r24,	SS_R24(r1)
	stw	r25,	SS_R25(r1)
	stw	r26,	SS_R26(r1)
	stw	r27,	SS_R27(r1)
	stw	r28,	SS_R28(r1)
	stw	r29,	SS_R29(r1)
	stw	r30,	SS_R30(r1)
	stw	r31,	SS_R31(r1)

	/* Save more state - cr,xer,lr,ctr,srr0,srr1,mq
	 * some of this comes back out from the per-processor structure
	 */

	lwz	r0,	PP_SAVE_CR(r2)
	stw	r0,	SS_CR(r1)
	
	lwz	r5,	PP_SAVE_SRR0(r2)	/* r5 holds srr0 used below */
	stw	r5,	SS_SRR0(r1)

	lwz	r0,	PP_SAVE_SRR1(r2)
	stw	r0,	SS_SRR1(r1)

	mfxer	r0
	stw	r0,	SS_XER(r1)

	mflr	r0
	stw	r0,	SS_LR(r1)

	mfctr	r0
	stw	r0,	SS_CTR(r1)

	/* Don't bother with MQ for now */

	/* Free the reservation whilst saving SR_COPYIN */

	mfsr	r0,	SR_COPYIN_NAME
	li	r4,	SS_SR_COPYIN
	sync				/* bug fix for 3.2 processors */
	stwcx.	r0,	r4,	r1
	stw	r0,	SS_SR_COPYIN(r1)
	
	/* Mark that we're occupying the interrupt stack for sure now */

	addis	r4,	0,	ha16(EXT(istackptr))
	addi	r4,	r4,	lo16(EXT(istackptr))
	li	r0,	0
	stw	r0,	0(r4)		/* TODO assumes 1 CPU */

	/*
	 * To summarise, when we reach here, we have filled out
	 * a ppc_saved_state structure on the interrupt stack, and
	 * the stack is marked as busy. We now generate a small
	 * stack frame with backpointers to follow the calling
	 * conventions. We set up the backpointers to the trapped
	 * routine allowing us to backtrace.
	 */
	
	mr	r4,	r1		/* Preserve saved_state in ARG1 */
	subi	r1,	r1,	FM_SIZE
	mfsprg	r0,	1
	stw	r0,	FM_BACKPTR(r1)	/* point back to previous stackptr */

#if	DEBUG
	/* If debugging, we need two frames, the first being a dummy
	 * which links back to the trapped routine. The second is
	 * that which the C routine below will need
	 */

	stw	r5,	FM_LR_SAVE(r1)	/* save old instr ptr as LR value */

	//stwu	r1,	-FM_SIZE(r1)	/* Mak new frame for C routine */
	stw	r1,	-FM_SIZE(r1)	/* Mak new frame for C routine */
	subi	r1,	r1,	FM_SIZE
#endif /* DEBUG */

	/* r3 still holds the reason for the interrupt */
	/* and r4 holds a pointer to the saved state */
	mfdsisr	ARG2
	mfdar	ARG3
	
	bl	EXT(interrupt)

	/* interrupt() returns a pointer to the saved state in r3
	 *
	 * Ok, back from C. Disable interrupts while we restore things
	 */

	li	r0,	MSR_SUPERVISOR_INT_OFF
	mtmsr	r0

	/* Reload the saved state */

	/* r0-2 will be restored last, use as temp for now */

	/* We don't restore r3-5, these are restored differently too.
	 * see trampoline code TODO NMGS evaluate need for this
	 */

	lwz	r6,	SS_R6(r3)
	lwz	r7,	SS_R7(r3)
	lwz	r8,	SS_R8(r3)
	lwz	r9,	SS_R9(r3)
	lwz	r10,	SS_R10(r3)
	lwz	r11,	SS_R11(r3)
	lwz	r12,	SS_R12(r3)
	lwz	r13,	SS_R13(r3)
	lwz	r14,	SS_R14(r3)
	lwz	r15,	SS_R15(r3)
	lwz	r16,	SS_R16(r3)
	lwz	r17,	SS_R17(r3)
	lwz	r18,	SS_R18(r3)
	lwz	r19,	SS_R19(r3)
	lwz	r20,	SS_R20(r3)
	lwz	r21,	SS_R21(r3)
	lwz	r22,	SS_R22(r3)
	lwz	r23,	SS_R23(r3)
	lwz	r24,	SS_R24(r3)
	lwz	r25,	SS_R25(r3)
	lwz	r26,	SS_R26(r3)
	lwz	r27,	SS_R27(r3)
	lwz	r28,	SS_R28(r3)
	lwz	r29,	SS_R29(r3)
	lwz	r30,	SS_R30(r3)
	lwz	r31,	SS_R31(r3)

	lwz	r0,	SS_XER(r3)
	mtxer	r0
	lwz	r0,	SS_LR(r3)
	mtlr	r0
	lwz	r0,	SS_CTR(r3)
	mtctr	r0
	lwz	r0,	SS_SR_COPYIN(r3)
	isync
	mtsr	SR_COPYIN_NAME,	r0
	isync

	/* TODO NMGS don't restore mq since we're not 601-specific enough */

	/* Is this the first interrupt on the stack? */

	addis	r4,	0,	ha16(EXT(intstack_top_ss))
	addi	r4,	r4,	lo16(EXT(intstack_top_ss))  /* TODO assumes 1 CPU */
	lwz	r4,	0(r4)

	cmp	CR0,0,	r4,	r3
	bne	CR0,	.L_notthelast_interrupt

	/* We're the last frame on the stack. Indicate that
	 * we've freed up the stack by putting our save state ptr in
	 * istackptr. Clear reservation at same time.
	 */

	addis	r4,	0,	ha16(EXT(istackptr))
	addi	r4,	r4,	lo16(EXT(istackptr))	/* TODO assumes 1 CPU */
/* we have to use an indirect store to clear reservation */
	sync				/* bug fix for 3.2 processors */
	stwcx.	r3,	0,	r4		/* clear reservation */
	stw	r3,	0(r4)

	/* We're the last frame on the stack.
	 * Check for ASTs if one of the below is true:	
	 *    returning to user mode
	 *    returning to a kloaded server
	 */

	lwz	r4,	SS_SRR1(r3)
	andi.	r4,	r4,	MASK(MSR_PR)
	bne+	.L_check_int_ast	/* returning to user level, check */
	b	.L_no_int_ast	/* in kernel, no check */

.L_check_int_ast:

	/*
	 * There is a pending AST. Massage things to make it look like
	 * we took a trap and jump into the trap handler.  To do this
	 * we essentially pretend to return from the interrupt but
	 * at the last minute jump into the trap handler with an AST
	 * trap instead of performing an rfi.
	 */

	lwz	r0,	SS_R1(r3)
	mtsprg	1,	r0
	lwz	r0,	SS_R2(r3)
	mtsprg	2,	r0
	lwz	r0,	SS_R3(r3)
	mtsprg	3,	r0

	lwz	r0,	SS_CR(r3)	/* store state in per_proc struct */
	stw	r0,	PP_SAVE_CR(r2)
	lwz	r0,	SS_SRR0(r3)
	stw	r0,	PP_SAVE_SRR0(r2)
	lwz	r0,	SS_SRR1(r3)
	stw	r0,	PP_SAVE_SRR1(r2)
	li	r0,	EXC_AST
	stw	r0,	PP_SAVE_EXCEPTION_TYPE(r2)
	
	lwz	r0,	SS_R0(r3)
	lwz	r4,	SS_R4(r3)
	lwz	r5,	SS_R5(r3)

	/* r2 remains a constant - virt addr of per_proc_info */
	li	r3,	EXC_AST	/* TODO r3 isn't used by thandler -optimise? */
	b	EXT(thandler)		/* hyperspace into AST trap */

.L_no_int_ast:	

	/* We're committed to performing the rfi now.
	 * If returning to the user space, we should restore the user's
	 * segment registers. We jump via a trampoline in physical mode
	 * TODO NMGS this trampoline code probably isn't needed
	 */
	lwz	r0,	SS_SRR1(r3)
	andi.	r0,	r0,	MASK(MSR_PR)
	beq-	.L_interrupt_returns_to_kspace

	/* TODO NMGS would it be better to store SR0 in saved_state
	 * rather than perform this expensive lookup?
	 */
	lwz	r1,	PP_CPU_DATA(r2)
	lwz	r1,	CPU_ACTIVE_THREAD(r1)
	lwz	r1,	THREAD_PCB(r1)
	lwz	r1,	PCB_SR0(r1)	/* For trampoline */

	lwz	r0,	SS_CR(r3)
	mtcrf	0xFF,r0

	/* the trampoline code takes r1-r3 from sprg1-3, and uses r1-3
	 * as arguments */
	lwz	r0,	SS_R1(r3)
	mtsprg	1,	r0
	lwz	r0,	SS_R2(r3)
	mtsprg	2,	r0
	lwz	r0,	SS_R3(r3)
	mtsprg	3,	r0

	lwz	r0,	SS_R0(r3)
	lwz	r4,	SS_R4(r3)
	lwz	r5,	SS_R5(r3)

	/* Prepare to rfi to the exception exit routine */
	addis	r2,	0,	ha16(EXT(exception_exit))
	addi	r2,	r2,	lo16(EXT(exception_exit))
	lwz	r2,	0(r2)
	mtsrr0	r2
	li	r2,	MSR_VM_OFF
	mtsrr1	r2

	/* r1 already loaded above */
	lwz	r2,	SS_SRR0(r3)	/* For trampoline */
	lwz	r3,	SS_SRR1(r3)	/* load the last register... */
	
	rfi
.L_interrupt_returns_to_kspace:	
.L_notthelast_interrupt:
	/* If we're not the last interrupt on the interrupt stack
	 * life is easier, we don't need to switch back into the
	 * user's segment. we can simply restore the last registers and rfi
	 */

	lwz	r0,	SS_CR(r3)
	mtcrf	0xFF,r0
	lwz	r0,	SS_SRR0(r3)
	mtsrr0	r0
	lwz	r0,	SS_SRR1(r3)
	mtsrr1	r0

	lwz	r0,	SS_R0(r3)
	lwz	r1,	SS_R1(r3)
	lwz	r2,	SS_R2(r3)	/* r2 is a constant - why restore?*/
	/* r3 restored last */
	lwz	r4,	SS_R4(r3)
	lwz	r5,	SS_R5(r3)
	/* and lastly... */
	lwz	r3,	SS_R3(r3)

	rfi				/* return to calling context */
	
/*
 * gdbhandler(type)
 *
 * Entry:	VM switched ON
 *		Interrupts  OFF
 *              original r1-3 saved in sprg1-3
 *              original srr0 and srr1 saved in per_proc structure
 *              original cr            saved in per_proc structure
 *              exception type (r3)    saved in per_proc structure
 *              r1 = scratch
 *              r2 = virt addr of per_proc_info
 *		r3 = exception type (one of EXC_...) also in per_proc
 *
 *  Closely based on ihandler - bugfixes to one may apply to the other!
 */

/* build a saved state structure on the debugger stack and call
 * the routine enterDebugger()
 */

ENTRY(gdbhandler, TAG_NO_FRAME_USED)	/* What tag should this have?! */
#if DEBUG
	/*
	 * get the value of gdbstackptr, if it's zero then we're already on the
	 * debugger stack, otherwise it points to a saved_state structure
	 * at the top of the debugger stack.
	 */

	addis	r1,	0,	ha16(EXT(gdbstackptr))
	addi	r1,	r1,	lo16(EXT(gdbstackptr))	/* TODO assumes 1 CPU */
	lwz	r1,	0(r1)
	cmpwi	CR0,	r1,	0
	bne	CR0,	.L_gdbstackfree

	/* We're already on the debugger stack, get back the old
	 * stack pointer and make room for a frame
	 */

	mfsprg	r1,	1	/* recover old stack pointer */

	/* Move below the redzone where the interrupted thread may have
	 * been saving its state and make room for our saved state structure
	 */
	subi	r1,	r1,	FM_REDZONE+SS_SIZE

	/* TODO NMGS - how about checking for stack overflow, huh?! */

.L_gdbstackfree:

	/* Once we reach here, r1 contains the adjusted stack pointer
         * where we can store a ppc_saved_state structure.
	 */

	stw	r0,	SS_R0(r1)

	mfsprg	r0,	1
	stw	r0,	SS_R1(r1)

	mfsprg	r0,	2
	stw	r0,	SS_R2(r1)

	mfsprg	r0,	3
	stw	r0,	SS_R3(r1)

	stw	r4,	SS_R4(r1)
	stw	r5,	SS_R5(r1)
	stw	r6,	SS_R6(r1)
	stw	r7,	SS_R7(r1)
	stw	r8,	SS_R8(r1)
	stw	r9,	SS_R9(r1)
	stw	r10,	SS_R10(r1)
	stw	r11,	SS_R11(r1)
	stw	r12,	SS_R12(r1)
	stw	r13,	SS_R13(r1)
	stw	r14,	SS_R14(r1)
	stw	r15,	SS_R15(r1)
	stw	r16,	SS_R16(r1)
	stw	r17,	SS_R17(r1)
	stw	r18,	SS_R18(r1)
	stw	r19,	SS_R19(r1)
	stw	r20,	SS_R20(r1)
	stw	r21,	SS_R21(r1)
	stw	r22,	SS_R22(r1)
	stw	r23,	SS_R23(r1)
	stw	r24,	SS_R24(r1)
	stw	r25,	SS_R25(r1)
	stw	r26,	SS_R26(r1)
	stw	r27,	SS_R27(r1)
	stw	r28,	SS_R28(r1)
	stw	r29,	SS_R29(r1)
	stw	r30,	SS_R30(r1)
	stw	r31,	SS_R31(r1)

	/* Save more state - cr,xer,lr,ctr,srr0,srr1,mq
	 * some of this comes back out from the per-processor structure
	 */

	lwz	r0,	PP_SAVE_CR(r2)
	stw	r0,	SS_CR(r1)
	
	lwz	r5,	PP_SAVE_SRR0(r2)	/* r5 holds srr0 used below */
	stw	r5,	SS_SRR0(r1)

	lwz	r0,	PP_SAVE_SRR1(r2)
	stw	r0,	SS_SRR1(r1)

	mfxer	r0
	stw	r0,	SS_XER(r1)

	mflr	r0
	stw	r0,	SS_LR(r1)

	mfctr	r0
	stw	r0,	SS_CTR(r1)

	/* Don't bother with MQ for now */

	/* Free the reservation whilst saving SR_COPYIN */

	mfsr	r0,	SR_COPYIN_NAME
	li	r4,	SS_SR_COPYIN
	sync				/* bug fix for 3.2 processors */
	stwcx.	r0,	r4,	r1
	stw	r0,	SS_SR_COPYIN(r1)
	
	/* Mark that we're occupying the gdb stack for sure now */

	addis	r4,	0,	ha16(EXT(gdbstackptr))
	addi	r4,	r4,	lo16(EXT(gdbstackptr))
	li	r0,	0
	stw	r0,	0(r4)		/* TODO assumes 1 CPU */

	/*
	 * To summarise, when we reach here, we have filled out
	 * a ppc_saved_state structure on the gdb stack, and
	 * the stack is marked as busy. We now generate a small
	 * stack frame with backpointers to follow the calling
	 * conventions. We set up the backpointers to the trapped
	 * routine allowing us to backtrace.
	 *
	 * This probably isn't needed in gdbhandler, but I've left
	 * it in place
	 */
	
	mr	r4,	r1		/* Preserve saved_state in ARG1 */
	subi	r1,	r1,	FM_SIZE
	mfsprg	r0,	1
	stw	r0,	FM_BACKPTR(r1)	/* point back to previous stackptr */

#if	DEBUG
	/* If debugging, we need two frames, the first being a dummy
	 * which links back to the trapped routine. The second is
	 * that which the C routine below will need
	 * TODO NMGS debugging call frame not correct yet
	 */
	stw	r5,	FM_LR_SAVE(r1)	/* save old instr ptr as LR value */
	//stwu	r1,	-FM_SIZE(r1)	/* point back to previous frame */
	stw	r1,	-FM_SIZE(r1)	/* point back to previous frame */
	subi	r1,	r1,	FM_SIZE
#endif /* DEBUG */

	/* r3 still holds the reason for the trap */
	/* and r4 holds a pointer to the saved state */

	mfdsisr	ARG2

	bl	EXT(enterDebugger)

	/* enterDebugger() returns a pointer to the saved state in r3
	 *
	 * Ok, back from C. Disable interrupts while we restore things
	 */

	li	r0,	MSR_SUPERVISOR_INT_OFF
	mtmsr	r0

	/* Reload the saved state */

	/* r0-2 will be restored last, use as temp for now */

	/* We do not restore r3-5, these are restored differently too.
	 * see trampoline code TODO NMGS evaluate need for this
	 */

	lwz	r6,	SS_R6(r3)
	lwz	r7,	SS_R7(r3)
	lwz	r8,	SS_R8(r3)
	lwz	r9,	SS_R9(r3)
	lwz	r10,	SS_R10(r3)
	lwz	r11,	SS_R11(r3)
	lwz	r12,	SS_R12(r3)
	lwz	r13,	SS_R13(r3)
	lwz	r14,	SS_R14(r3)
	lwz	r15,	SS_R15(r3)
	lwz	r16,	SS_R16(r3)
	lwz	r17,	SS_R17(r3)
	lwz	r18,	SS_R18(r3)
	lwz	r19,	SS_R19(r3)
	lwz	r20,	SS_R20(r3)
	lwz	r21,	SS_R21(r3)
	lwz	r22,	SS_R22(r3)
	lwz	r23,	SS_R23(r3)
	lwz	r24,	SS_R24(r3)
	lwz	r25,	SS_R25(r3)
	lwz	r26,	SS_R26(r3)
	lwz	r27,	SS_R27(r3)
	lwz	r28,	SS_R28(r3)
	lwz	r29,	SS_R29(r3)
	lwz	r30,	SS_R30(r3)
	lwz	r31,	SS_R31(r3)

	lwz	r0,	SS_XER(r3)
	lwz	r5,	SS_LR(r3)
	mtxer	r0
	mtlr	r5
	lwz	r0,	SS_CTR(r3)
	lwz	r5,	SS_SR_COPYIN(r3)
	mtctr	r0
	isync
	mtsr	SR_COPYIN_NAME,	r5
	isync

	/* TODO NMGS don't restore mq since we're not 601-specific enough */

	/* Is this the first frame on the stack? */

	addis	r4,	0,	ha16(EXT(gdbstack_top_ss))
	addi	r4,	r4,	lo16(EXT(gdbstack_top_ss))  /* TODO assumes 1 CPU */
	lwz	r4,	0(r4)

	cmp	CR0, 0,	r4,	r3
	bne	CR0,	.L_notthelast_gdbframe

	/* We're the last frame on the stack. Indicate that
	 * we've freed up the stack by putting our save state ptr in
	 * istackptr. Clear reservation at same time.
	 */
	addis	r4,	0,	ha16(EXT(gdbstackptr))
	addi	r4,	r4,	lo16(EXT(gdbstackptr))	/* TODO assumes 1 CPU */
/* we have to use an indirect store to clear reservation */
	sync				/* bug fix for 3.2 processors */
	stwcx.	r3,	0,	r4		/* clear reservation */
	stw	r3,	0(r4)

	/* We may be returning to something in the kernel space.
	 * If we are, we can skip the trampoline and just rfi,
	 * since we don't want to restore the user's space regs
	 */

	lwz	r0,	SS_SRR1(r3)
	andi.	r0,	r0,	MASK(MSR_PR)
	beq-	.L_gdb_ret_to_kspace

	/* If jumping into user space, we should restore the user's
	 * segment register 0. We jump via a trampoline in physical mode
	 * TODO NMGS this trampoline code probably isn't needed
	 */

	/* TODO NMGS would it be better to store SR0 in saved_state
	 * rather than perform this expensive lookup?
	 */
	lwz	r1,	PP_CPU_DATA(r2)
	lwz	r1,	CPU_ACTIVE_THREAD(r1)
	lwz	r1,	THREAD_PCB(r1)
	lwz	r1,	PCB_SR0(r1)	/* For trampoline */

#if DEBUG
	/* Assert that PCB_SR0 is not in kernel space */
	rlwinm.	r0,	r1,	0,	8,	27
	bne+	.Lbp_skip
	BREAKPOINT_TRAP
.Lbp_skip:
#endif /* DEBUG */

	lwz	r0,	SS_CR(r3)
	mtcrf	0xFF,r0

	/* the trampoline code takes r1-r3 from sprg1-3, and uses r1-3
	 * as arguments
	 */
	lwz	r0,	SS_R1(r3)
	mtsprg	1,	r0
	lwz	r0,	SS_R2(r3)
	mtsprg	2,	r0
	lwz	r0,	SS_R3(r3)
	mtsprg	3,	r0

	lwz	r0,	SS_R0(r3)
	lwz	r4,	SS_R4(r3)
	lwz	r5,	SS_R5(r3)

	/* Prepare to rfi to the exception exit routine */
	addis	r2,	0,	ha16(EXT(exception_exit))
	addi	r2,	r2,	lo16(EXT(exception_exit))
	lwz	r2,	0(r2)
	mtsrr0	r2
	li	r2,	MSR_VM_OFF
	mtsrr1	r2

	/* r1 already loaded above */
	lwz	r2,	SS_SRR0(r3)	/* For trampoline */
	lwz	r3,	SS_SRR1(r3)	/* load the last register... */
	

	rfi
	
.L_gdb_ret_to_kspace:	
.L_notthelast_gdbframe:
	/* If we're not the last frame on the stack
	 * life is easier, we don't need to switch back into the
	 * user's segment. we can simply restore the last registers and rfi
	 */

	lwz	r0,	SS_CR(r3)
	mtcrf	0xFF,r0
	lwz	r0,	SS_SRR0(r3)
	mtsrr0	r0
	lwz	r0,	SS_SRR1(r3)
	mtsrr1	r0

	lwz	r0,	SS_R0(r3)
	lwz	r1,	SS_R1(r3)
	lwz	r2,	SS_R2(r3)	/* r2 is a constant - why restore?*/
	/* r3 restored last */
	lwz	r4,	SS_R4(r3)
	lwz	r5,	SS_R5(r3)
	/* and lastly... */
	lwz	r3,	SS_R3(r3)

	rfi				/* return to calling context */
#endif /* DEBUG */