/*      $NetBSD: trap_subr.S,v 1.15 2022/07/05 20:15:40 andvar Exp $    */
/*-
* Copyright (c) 2010, 2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Raytheon BBN Technologies Corp and Defense Advanced Research Projects
* Agency and which was developed by Matt Thomas of 3am Software Foundry.
*
* This material is based upon work supported by the Defense Advanced Research
* Projects Agency and Space and Naval Warfare Systems Center, Pacific, under
* Contract No. N66001-09-C-2073.
* Approved for Public Release, Distribution Unlimited
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

RCSID("$NetBSD: trap_subr.S,v 1.15 2022/07/05 20:15:40 andvar Exp $")

#ifdef _KERNEL_OPT
#include "opt_altivec.h"
#include "opt_ddb.h"
#include "opt_mpc85xx.h"
#include "opt_multiprocessor.h"
#endif

       .globl  _C_LABEL(sctrapexit), _C_LABEL(trapexit), _C_LABEL(intrcall)

       /*
        * We have a problem with critical (MSR[CE] or machine check (MSR[ME])
        * or debug (MSR[DE]) interrupts/exception in that they could happen
        * in-between the mtsprg1 %r2 and mfsprg1 %r2.  If that happens, %r2
        * will be lost.  Even if we moved to a different sprg, subsequent
        * exceptions would use SPRG1 and its value would be lost.  The only
        * way to be safe for CE/ME/DE faults to save and restore SPRG1.
        *
        * Since CE/ME/DE faults may happen anytime, we need r1 to always
        * contain a valid kernel stack pointer.  Therefore we use r2 as
        * our temporary register.
        *
        * To prevent %r2 being overwritten, each "level" (normal, critical,
        * mchk) uses a unique sprg to save %r2 (sprg1, sprg4, sprg5).
        *
        * Since we can't control how many nested exceptions we might get,
        * we don't use a dedicated save area.  Instead we have an upwards
        * growing "stack" of them; the pointer to which is kept in sprg3.
        *
        * To allocate from the stack, one fetches sprg3, adds the amount
        * needed, saves sprg3, and then refers to the save using a
        * displacement of -amount.
        */
       /*
        * XXXclang
        * At the moment, clang cannot correctly assemble m[ft]sprgN
        * (N >= 4); use m[ft]spr for SPR_SPRGN instead.
        */
#define FRAME_EXC_PROLOGUE(start, sprg, srr)                            \
       mtspr   sprg,%r2;               /* save r2 */                   \
       mfsprg3 %r2;                    /* get save_area pointer */     \
       addi    %r2,%r2,4*(32-start);                                   \
                                       /* allocate save area */        \
       mtsprg3 %r2;                    /* save updated pointer */      \
       stmw    %r##start,-4*(32-start)(%r2);                           \
                                       /* free r24-r31 for use */      \
       mfspr   %r26,sprg;              /* get saved r2 */              \
       mfcr    %r27;                   /* get Condition Register */    \
       mfxer   %r28;                   /* get XER */                   \
       mfspr   %r30, SPR_##srr##0;     /* get SRR0 */                  \
       mfspr   %r31, SPR_##srr##1      /* get SRR1 */

#define PROLOGUE_GET_DEAR       mfspr   %r24, SPR_DEAR
#define PROLOGUE_GET_ESR        mfspr   %r25, SPR_ESR
#define PROLOGUE_GET_SRRS       mfsrr0  %r24; \
                               mfsrr1  %r25
#define PROLOGUE_GET_SPRG1      mfsprg1 %r29
#define PROLOGUE_GET_DBSR       mfspr   %r25, SPR_DBSR
#define SAVE_ESR                stw     %r25, FRAME_ESR(%r1)
#define SAVE_DEAR               stw     %r24, FRAME_DEAR(%r1)
#define SAVE_DEAR_ESR           SAVE_ESR; SAVE_DEAR
#define SAVE_SRRS               SAVE_DEAR_ESR
#define SAVE_SPRG1              stw     %r29, FRAME_SPRG1(%r1)
#define SAVE_DBSR               stw     %r25, FRAME_DBSR(%r1)
#define SAVE_NOTHING            /* nothing */
#define RESTORE_SPRG1(r)        lwz     r, FRAME_SPRG1(%r1); \
                               mtsprg1 r
#define RESTORE_SRR0(r)         lwz     r, FRAME_DEAR(%r1); \
                               mtsrr0  r
#define RESTORE_SRR1(r)         lwz     r, FRAME_ESR(%r1); \
                               mtsrr1  r

#define FRAME_PROLOGUE          \
       FRAME_EXC_PROLOGUE(26, SPR_SPRG1, SRR)

#define FRAME_PROLOGUE_DEAR_ESR \
       FRAME_EXC_PROLOGUE(24, SPR_SPRG1, SRR); \
       PROLOGUE_GET_ESR; \
       PROLOGUE_GET_DEAR

#define FRAME_PROLOGUE_ESR      \
       FRAME_EXC_PROLOGUE(25, SPR_SPRG1, SRR); \
       PROLOGUE_GET_ESR

#define FRAME_TLBPROLOGUE       \
       FRAME_EXC_PROLOGUE(20, SPR_SPRG1, SRR); \
       PROLOGUE_GET_ESR; \
       PROLOGUE_GET_DEAR

#define FRAME_INTR_PROLOGUE     \
       FRAME_EXC_PROLOGUE(26, SPR_SPRG1, SRR)

/*
* These need to save SRR0/SRR1 as well their SRR0/SRR1 in case normal
* exceptions happened during their execution.
*/
#define FRAME_CRIT_PROLOGUE     \
       FRAME_EXC_PROLOGUE(24, SPR_SPRG4, CSRR); \
       PROLOGUE_GET_SPRG1; \
       PROLOGUE_GET_SRRS

#define FRAME_MCHK_PROLOGUE     \
       FRAME_EXC_PROLOGUE(24, SPR_SPRG5, MCSRR); \
       PROLOGUE_GET_SPRG1; \
       PROLOGUE_GET_SRRS

#define FRAME_DEBUG_PROLOGUE    \
       FRAME_EXC_PROLOGUE(24, SPR_SPRG4, CSRR); \
       PROLOGUE_GET_SPRG1; \
       PROLOGUE_GET_SRRS

/*
* DDB expects to fetch the LR from the previous frame.  But it also
* expects to be pointing at the instruction after the branch link.  Since
* we didn't branch, we need to advance it by to fake out DDB.  But there's
* problem.  If the routine is in either its first or last two instructions
* (before or after its adjusted its stack pointer), we could possibly
* overwrite stored return address.  So that stored return address needs to
* saved and restored.
*/
#if defined(DDB)
#define FRAME_SAVE_SRR0_FOR_DDB                                         \
       lwz     %r29, FRAMELEN+CFRAME_LR(%r1);  /* fetch old return address */\
       stw     %r29, FRAME_CFRAME_LR(%r1);     /* save it */            \
       addi    %r30, %r30, 4;                  /* point to s the next insn */ \
       stw     %r30, FRAMELEN+CFRAME_LR(%r1)   /* appease ddb stacktrace */
#define FRAME_RESTORE_RETURN_ADDRESS                                    \
       lwz     %r3, FRAME_CFRAME_LR(%r1);      /* fetch old return address */ \
       stw     %r3, FRAMELEN+CFRAME_LR(%r1)    /* restore it */
#else
#define FRAME_SAVE_SRR0_FOR_DDB
#define FRAME_RESTORE_RETURN_ADDRESS
#endif

#ifdef PPC_HAVE_SPE
#define FRAME_SAVE_SPEFSCR                                              \
       mfspefscr %r0;                  /* get spefscr */               \
       stw     %r0, FRAME_SPEFSCR(%r1) /* save into trapframe */
#define FRAME_RESTORE_SPEFSCR                                           \
       lwz     %r0, FRAME_SPEFSCR(%r1); /* fetch from trapframe */     \
       mtspefscr %r0                   /* save spefscr */
#else
#define FRAME_SAVE_SPEFSCR
#define FRAME_RESTORE_SPEFSCR
#endif
/*
* Before the first memory reference, we must have our state inside registers
* since the first memory access might cause an exception which would cause
* SRR0/SRR1 and DEAR/ESR to become unrecoverable.  CR and XER also need to be
* saved early since they will modified by instruction flow.  The saved stack
* pointer is also critical but LR and CTR can be deferred being saved until
* we are actually filling a trapframe.
*/
#define FRAME_EXC_ENTER(exc, tf, start, save_prologue)                  \
       mtcr    %r31;                   /* user mode exception? */      \
       mr      %r31, %r1;              /* save SP (SRR1 is safe in CR) */ \
       bf      MSR_PR, 1f;             /*   nope, sp is good */        \
       mfsprg2 %r2;                    /* get curlwp */                \
       lwz     %r2, L_PCB(%r2);        /* get uarea of curlwp */       \
       addi    %r1, %r2, USPACE-CALLFRAMELEN;                          \
                                       /* start stack at top of it */  \
1:                                                                      \
       stwu    %r31, -FRAMELEN(%r1);   /* get space for trapframe */   \
       stw     %r0, FRAME_R0(%r1);     /* save r0 */                   \
       stw     %r31, FRAME_R1(%r1);    /* save (saved) r1 */           \
       stw     %r26, FRAME_R2(%r1);    /* save (saved) r2 */           \
       save_prologue;                  /* save SPRG1/ESR/DEAR */       \
       /* At this point, r26, r29, and r31 have been saved so we */    \
       /* can use them for LR, CTR, and SRR1.                    */    \
       mflr    %r26;                   /* get Link Register */         \
       mfctr   %r29;                   /* get CTR */                   \
       mfcr    %r31;                   /* get SRR1 */                  \
       stmw    %r26, FRAME_LR(%r1);    /* save LR CR XER CTR SRR0/1 */ \
       FRAME_SAVE_SRR0_FOR_DDB;                                        \
       mr      %r0, %r31;              /* save SRR1 for a bit */       \
       mfsprg3 %r2;                    /* get save_area pointer */     \
       addi    %r2,%r2,-4*(32-start);  /* find our save area */        \
       lmw     %r##start,0(%r2);       /* get start-r31 */             \
       mtsprg3 %r2;                    /* save updated pointer */      \
       stmw    %r3, FRAME_R3(%r1);     /* save r2-r31 */               \
       /* Now everything has been saved */                             \
       mr      %r31, %r0;              /* move SRR1 back to r31 */     \
       mfsprg2 %r13;                   /* put curlwp in r13 */         \
       FRAME_SAVE_SPEFSCR;                                             \
       li      %r7, exc;               /* load EXC_* */                \
       stw     %r7, FRAME_EXC(%r1);    /* save into trapframe */       \
       addi    tf, %r1, FRAME_TF       /* get address of trap frame */

#define FRAME_EXC_EXIT(rfi, srr)                                        \
       FRAME_RESTORE_RETURN_ADDRESS;   /* restore return address */    \
       lmw     %r26, FRAME_LR(%r1);    /* get LR CR XER CTR SRR0/1 */  \
       oris    %r31,%r31,PSL_CE@h;                                     \
       mtspr   SPR_##srr##1, %r31;     /* restore SRR1 */              \
       mtspr   SPR_##srr##0, %r30;     /* restore SRR0 */              \
       FRAME_RESTORE_SPEFSCR;                                          \
       mtctr   %r29;                   /* restore CTR */               \
       mtxer   %r28;                   /* restore XER */               \
       mtcr    %r27;                   /* restore CR */                \
       mtlr    %r26;                   /* restore LR */                \
       lmw     %r2, FRAME_R2(%r1);     /* restore r2-r31 */            \
       lwz     %r0, FRAME_R0(%r1);     /* restore r0 */                \
       lwz     %r1, FRAME_R1(%r1);     /* restore r1 */                \
       rfi                             /* return from interrupt */


#define FRAME_ENTER(exc, tf)            \
       FRAME_EXC_ENTER(exc, tf, 26, SAVE_NOTHING)

#define FRAME_ENTER_ESR(exc, tf)        \
       FRAME_EXC_ENTER(exc, tf, 25, SAVE_ESR)

#define FRAME_ENTER_DEAR_ESR(exc, tf)   \
       FRAME_EXC_ENTER(exc, tf, 24, SAVE_DEAR_ESR)

#define FRAME_EXIT              FRAME_EXC_EXIT(rfi, SRR)

#define FRAME_TLBENTER(exc)     \
       FRAME_EXC_ENTER(exc, %r4, 20, SAVE_DEAR_ESR)
#define FRAME_TLBEXIT           FRAME_EXC_EXIT(rfi, SRR)

#define FRAME_MCHK_ENTER(exc)   \
       FRAME_EXC_ENTER(exc, %r3, 26, SAVE_SPRG1; SAVE_SRRS)
#define FRAME_MCHK_EXIT         \
       RESTORE_SRR0(%r28);     \
       RESTORE_SRR1(%r27);     \
       RESTORE_SPRG1(%r26);    \
       FRAME_EXC_EXIT(rfmci, MCSRR)

#define FRAME_DEBUG_ENTER(exc)  \
       FRAME_EXC_ENTER(exc, %r4, 26, SAVE_SPRG1; SAVE_SRRS)
#define FRAME_DEBUG_EXIT        \
       RESTORE_SPRG1(%r26); FRAME_EXC_EXIT(rfci, CSRR)

#define FRAME_INTR_SP                                                   \
       bf      MSR_PR, 1f;             /*    nope, sp is good */       \
       mfsprg2 %r2;                    /* get curlwp */                \
       lwz     %r2, L_PCB(%r2);        /* get uarea of curlwp */       \
       addi    %r1, %r2, USPACE-CALLFRAMELEN;                          \
                                       /* start stack at top of it */  \
1:

#define FRAME_INTR_SP_NEW(sym)                                          \
       lis     %r2,(sym)@ha;                                           \
       addi    %r1,%r2,(sym)@l

#define FRAME_INTR_XENTER(exc, start, get_intr_sp, save_prologue)       \
       mtcr    %r31;                   /* user mode exception? */      \
       mr      %r31, %r1;              /* save SP (SRR1 is safe in CR) */ \
       get_intr_sp;                    /* get kernel stack pointer */  \
       stwu    %r31, -FRAMELEN(%r1);   /* get space for trapframe */   \
       stw     %r0, FRAME_R0(%r1);     /* save r0 */                   \
       stw     %r31, FRAME_R1(%r1);    /* save (saved) r1 */           \
       stw     %r26, FRAME_R2(%r1);    /* save (saved) r2 */           \
       save_prologue;                  /* save SPRG1 (maybe) */        \
       mflr    %r26;                   /* get LR */                    \
       mfctr   %r29;                   /* get CTR */                   \
       mfcr    %r31;                   /* get SRR1 */                  \
       stmw    %r26, FRAME_LR(%r1);    /* save LR CR XER CTR SRR0/1 */ \
       FRAME_SAVE_SRR0_FOR_DDB;                                        \
       stw     %r3, FRAME_R3(%r1);     /* save r3 */                   \
       stw     %r4, FRAME_R4(%r1);     /* save r4 */                   \
       stw     %r5, FRAME_R5(%r1);     /* save r5 */                   \
       stw     %r6, FRAME_R6(%r1);     /* save r6 */                   \
       stw     %r7, FRAME_R7(%r1);     /* save r7 */                   \
       stw     %r8, FRAME_R8(%r1);     /* save r8 */                   \
       stw     %r9, FRAME_R9(%r1);     /* save r9 */                   \
       stw     %r10, FRAME_R10(%r1);   /* save r10 */                  \
       stw     %r11, FRAME_R11(%r1);   /* save r11 */                  \
       stw     %r12, FRAME_R12(%r1);   /* save r12 */                  \
       stw     %r13, FRAME_R13(%r1);   /* save r13 */                  \
       mfsprg3 %r2;                    /* get save_area pointer */     \
       addi    %r2,%r2,-4*(32-start);  /* find our save area */        \
       lmw     %r##start,0(%r2);       /* get start-r31 */             \
       mtsprg3 %r2;                    /* save updated pointer */      \
       mfsprg2 %r13;                   /* put curlwp into r13 */       \
       li      %r7, exc;               /* load EXC_* */                \
       stw     %r7, FRAME_EXC(%r1);    /* save into trapframe */       \
       addi    %r3, %r1, FRAME_TF      /* only argument is trapframe */

#define FRAME_INTR_XEXIT(rfi, srr)                                      \
       FRAME_RESTORE_RETURN_ADDRESS;   /* restore return address */    \
       lwz     %r8, FRAME_LR(%r1);     /* get LR */                    \
       lwz     %r9, FRAME_CR(%r1);     /* get CR */                    \
       lwz     %r10, FRAME_XER(%r1);   /* get XER */                   \
       lwz     %r11, FRAME_CTR(%r1);   /* get CTR */                   \
       lwz     %r12, FRAME_SRR0(%r1);  /* get SRR0 */                  \
       lwz     %r13, FRAME_SRR1(%r1);  /* get SRR1 */                  \
       mtspr   SPR_##srr##1, %r13;     /* restore SRR1 */              \
       mtspr   SPR_##srr##0, %r12;     /* restore SRR0 */              \
       mtctr   %r11;                   /* restore CTR */               \
       mtxer   %r10;                   /* restore XER */               \
       mtcr    %r9;                    /* restore CR */                \
       mtlr    %r8;                    /* restore LR */                \
       lwz     %r13, FRAME_R13(%r1);   /* restore r13 */               \
       lwz     %r12, FRAME_R12(%r1);   /* restore r12 */               \
       lwz     %r11, FRAME_R11(%r1);   /* restore r11 */               \
       lwz     %r10, FRAME_R10(%r1);   /* restore r10 */               \
       lwz     %r9, FRAME_R9(%r1);     /* restore r9 */                \
       lwz     %r8, FRAME_R8(%r1);     /* restore r8 */                \
       lwz     %r7, FRAME_R7(%r1);     /* restore r7 */                \
       lwz     %r6, FRAME_R6(%r1);     /* restore r6 */                \
       lwz     %r5, FRAME_R5(%r1);     /* restore r5 */                \
       lwz     %r4, FRAME_R4(%r1);     /* restore r4 */                \
       lwz     %r3, FRAME_R3(%r1);     /* restore r3 */                \
       lwz     %r2, FRAME_R2(%r1);     /* restore r2 */                \
       lwz     %r0, FRAME_R0(%r1);     /* restore r0 */                \
       lwz     %r1, FRAME_R1(%r1);     /* restore r1 */                \
       rfi                             /* return from interrupt */

#define FRAME_INTR_ENTER(exc)   \
       FRAME_INTR_XENTER(exc, 26, FRAME_INTR_SP, SAVE_NOTHING)
#define FRAME_INTR_EXIT         \
       FRAME_INTR_XEXIT(rfi, SRR)
#define FRAME_CRIT_ENTER(exc)   \
       FRAME_INTR_XENTER(exc, 24, FRAME_INTR_SP, SAVE_SPRG1)
#define FRAME_WDOG_ENTER(exc, sym)      \
       FRAME_INTR_XENTER(exc, 24, FRAME_INTR_SP_NEW(sym), SAVE_SPRG1)
#define FRAME_CRIT_EXIT         \
       RESTORE_SRR0(%r4);      \
       RESTORE_SRR1(%r5);      \
       RESTORE_SPRG1(%r6);     \
       FRAME_INTR_XEXIT(rfci, CSRR)

#if defined(MULTIPROCESSOR)
#define FRAME_TLBMISSLOCK                                               \
       GET_CPUINFO(%r23);                                              \
       ldint   %r22, CI_MTX_COUNT(%r23);                               \
       subi    %r22, %r22, 1;                                          \
       stint   %r22, CI_MTX_COUNT(%r23);                               \
       isync;                                                          \
       cmpwi   %r22, 0;                                                \
       bne     1f;                                                     \
       ldint   %r22, CI_CPL(%r23);                                     \
       stint   %r22, CI_MTX_OLDSPL(%r23);                              \
1:      lis     %r23, _C_LABEL(pmap_tlb_miss_lock)@h;                   \
       ori     %r23, %r23, _C_LABEL(pmap_tlb_miss_lock)@l;             \
       li      %r20, MTX_LOCK;                                         \
2:      lwarx   %r22, %r20, %r23;                                       \
       cmpwi   %r22, __SIMPLELOCK_UNLOCKED;                            \
       beq+    4f;                                                     \
3:      lwzx    %r22, %r20, %r23;                                       \
       cmpwi   %r22, __SIMPLELOCK_UNLOCKED;                            \
       beq+    2b;                                                     \
       b       3b;                                                     \
4:      li      %r21, __SIMPLELOCK_LOCKED;                              \
       stwcx.  %r21, %r20, %r23;                                       \
       bne-    2b;                                                     \
       isync;                                                          \
       msync;
#define FRAME_TLBMISSUNLOCK                                             \
       sync;                                                           \
       lis     %r23, _C_LABEL(pmap_tlb_miss_lock)@h;                   \
       ori     %r23, %r23, _C_LABEL(pmap_tlb_miss_lock)@l;             \
       li      %r22, __SIMPLELOCK_UNLOCKED;                            \
       stw     %r22, MTX_LOCK(%r23);                                   \
       isync;                                                          \
       msync;                                                          \
       GET_CPUINFO(%r23);                                              \
       ldint   %r22, CI_MTX_COUNT(%r23);                               \
       addi    %r22, %r22, 1;                                          \
       stint   %r22, CI_MTX_COUNT(%r23);                               \
       isync;
#else   /* !MULTIPROCESSOR */
#define FRAME_TLBMISSLOCK
#define FRAME_TLBMISSUNLOCK
#endif  /* MULTIPROCESSOR */

       .text
       .p2align 4
_C_LABEL(critical_input_vector):
       /* MSR[ME] is unchanged, all others cleared */
       FRAME_CRIT_PROLOGUE                     /* save SP r26-31 CR LR XER */
       FRAME_CRIT_ENTER(EXC_CII)
       bl      _C_LABEL(intr_critintr)         /* critintr(tf) */
       FRAME_CRIT_EXIT

       .p2align 4
_C_LABEL(machine_check_vector):
       /* all MSR bits are cleared */
       FRAME_MCHK_PROLOGUE                     /* save SP r25-31 CR LR XER */
       FRAME_MCHK_ENTER(EXC_MCHK)
       /*
        * MCAR/MCSR don't need to be saved early since MSR[ME] is cleared
        * on entry.
        */
       mfspr   %r7, SPR_MCAR
       mfspr   %r6, SPR_MCSR
       stw     %r6, FRAME_MCSR(%r1)
       stw     %r7, FRAME_MCAR(%r1)
       li      %r3, T_MACHINE_CHECK
       bl      _C_LABEL(trap)                  /* trap(T_MACHINE_CHECK, tf) */
       FRAME_MCHK_EXIT

       .p2align 4
_C_LABEL(data_storage_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_PROLOGUE_DEAR_ESR         /* save r2 DEAR ESR r24-31 CR XER SRR */
       FRAME_ENTER_DEAR_ESR(EXC_DSI, %r4)
       li      %r3, T_DSI
       /* FRAME_ENTER leaves SRR1 in %r31 */
trapenter:
trapagain:
       wrtee   %r31                            /* restore MSR[EE] */

       bl      _C_LABEL(trap)                  /* trap(trapcode, tf) */
_C_LABEL(trapexit):
       wrteei  0                               /* disable interrupts */
#       andis.  %r0, %r31, PSL_CE@h
#       tweqi   %r0, 0
       andi.   %r4, %r31, PSL_PR               /* lets look at PSL_PR */
       beq     trapdone                        /* if clear, skip to exit  */
       lwz     %r4, L_MD_ASTPENDING(%r13)      /* get ast pending */
       cmplwi  %r4, 0                          /* is there an ast pending */
       beq+    trapdone                        /*    nope, proceed to exit */
       li      %r6, EXC_AST                    /* yes. */
       stw     %r6, FRAME_EXC(%r1)             /* pretend this is an AST */
       addi    %r4, %r1, FRAME_TF              /* get address of trap frame */
       li      %r3, T_AST
       b       trapagain                       /* and deal with it */
trapdone:
       FRAME_EXIT

       .p2align 4
_C_LABEL(instruction_storage_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_PROLOGUE_ESR              /* save ESR r2 r25-31 CR XER SRR0/1 */
       FRAME_ENTER_ESR(EXC_ISI, %r4)
       li      %r3, T_ISI
       b       trapenter

       .p2align 4
_ENTRY(external_input_vector)
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_INTR_PROLOGUE                     /* save SP r25-31 CR LR XER */
       FRAME_INTR_ENTER(EXC_EXI)

       bl      _C_LABEL(intr_extintr)
_C_LABEL(intrcall):
       GET_CPUINFO(%r6)                        /* get curcpu() */
       lwz     %r5, FRAME_SRR1(%r1)            /* get saved SRR1 */
#       andis.  %r0, %r5, PSL_CE@h
#       tweqi   %r0, 0
       andi.   %r4, %r5, PSL_PR                /* lets look at PSL_PR */
       beq     intrexit                        /* if clear, skip to exit  */
       lwz     %r4, L_MD_ASTPENDING(%r13)      /* get ast pending */
       cmplwi  %r4, 0                          /* is there an ast pending */
       beq+    intrexit                        /*    nope, proceed to exit */
       stmw    %r14, FRAME_R14(%r1)            /* save rest of registers */
       FRAME_SAVE_SPEFSCR
       mr      %r31, %r5                       /* needed for trapagain */
       li      %r4, EXC_AST                    /* */
       stw     %r4, FRAME_EXC(%r1)             /* pretend this is an AST */
       addi    %r4, %r1, FRAME_TF              /* get address of trap frame */
       li      %r3, T_AST
       b       trapagain                       /* and deal with it */
intrexit:
       FRAME_INTR_EXIT

       .p2align 4
_C_LABEL(alignment_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_PROLOGUE_DEAR_ESR                 /* save SP r25-31 CR LR XER */
       FRAME_ENTER_DEAR_ESR(EXC_ALI, %r4)
       li      %r3, T_ALIGNMENT
       b       trapenter

       .p2align 4
_C_LABEL(program_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_PROLOGUE_ESR                      /* save SP r25-31 CR LR XER */
       FRAME_ENTER_ESR(EXC_PGM, %r4)
       li      %r3, T_PROGRAM
       b       trapenter

#ifdef SPR_IVOR7
       .p2align 4
_C_LABEL(fp_unavailable_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_PROLOGUE_ESR                      /* save SP r25-31 CR LR XER */
       FRAME_ENTER_ESR(EXC_FPU, %r4)
       li      %r3, T_FP_UNAVAILABLE
       b       trapenter
#endif

       .p2align 4
_C_LABEL(system_call_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_PROLOGUE                          /* save SP r26-31 CR LR XER */
       FRAME_ENTER(EXC_SC, %r3)

       wrteei  1                               /* enable interrupts */
       lwz     %r7, L_PROC(%r13)               /* get proc for lwp */
       lwz     %r8, P_MD_SYSCALL(%r7)          /* get syscall */
       mtlr    %r8                             /* need to call indirect */
       blrl                                    /* syscall(tf) */
_C_LABEL(sctrapexit):
       wrteei  0                               /* disable interrupts */
       lwz     %r4, L_MD_ASTPENDING(%r13)      /* get ast pending */
       cmplwi  %r4, 0                          /* is there an ast pending */
       beq+    trapdone                        /*    nope, proceed to exit */
       li      %r0, EXC_AST                    /* yes. */
       stw     %r0, FRAME_EXC(%r1)             /* pretend this is an AST */
       addi    %r4, %r1, FRAME_TF              /* get address of trap frame */
       li      %r3, T_AST
       b       trapenter                       /* and deal with it */

#ifdef SPR_IVOR9
       .p2align 4
_C_LABEL(ap_unavailable_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_PROLOGUE                          /* save SP r25-31 CR LR XER */
       FRAME_ENTER(EXC_PGM, %r4)
       li      %r3, T_AP_UNAVAILABLE
       b       trapenter
#endif

       .p2align 4
_C_LABEL(decrementer_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_INTR_PROLOGUE                     /* save SP r25-31 CR LR XER */
       FRAME_INTR_ENTER(EXC_DECR)

       bl      _C_LABEL(intr_decrintr)
       b       intrexit

       .p2align 4
_C_LABEL(fixed_interval_timer_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_PROLOGUE                          /* save SP r25-31 CR LR XER */
       FRAME_INTR_ENTER(EXC_FIT)

       bl      _C_LABEL(intr_fitintr)
       b       intrexit

#ifdef E500_WDOG_STACK
       .data
       .lcomm wdogstk,4096
#endif
       .text
       .p2align 4
_C_LABEL(watchdog_timer_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_CRIT_PROLOGUE                     /* save SP r25-31 CR LR XER */
#ifdef E500_WDOG_STACK
       FRAME_WDOG_ENTER(EXC_WDOG, wdogstk+4096-CALLFRAMELEN)
#else
       FRAME_CRIT_ENTER(EXC_WDOG);
#endif

       bl      _C_LABEL(intr_wdogintr)
       FRAME_CRIT_EXIT

       .p2align 4
_C_LABEL(data_tlb_error_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_TLBPROLOGUE
       FRAME_TLBMISSLOCK
       /*
        * Registers as this point:
        *
        * r2  = cpu_info
        * r20 = scratch
        * r21 = scratch
        * r22 = scratch
        * r23 = scratch
        * r24 = DEAR
        * r25 = ESR
        * r26 = saved r2
        * r27 = CR
        * r28 = XER
        * r29 = scratch
        * r30 = SRR0
        * r31 = SRR1
        *
        * Except for r29, these values must be retained.  However we must
        * be cognizant of nesting.  There are two cases here, both related.
        *
        * We get a critical input or machine check exception and the kernel
        * stack doesn't have a TLB entry so we take an exception.  The other
        * nesting path is some page used by the exception handler will cause
        * a TLB data error.
        *
        * The second case (more probable) is that the PTE loading will fail
        * so we will have to do a hard trap to resolve it.  But in doing so
        * we need to save a trapframe which could result in another DTLB
        * fault.
        *
        * In all cases, the save area stack shall protect us.
        */
       /*
        * Attempt to update the TLB from the page table.
        */
       mflr    %r29                            /* save LR */
       mr      %r23, %r24                      /* address of exception */
       rlwinm  %r22, %r31,                     /* index into ci_pmap_segtab */\
               MSR_DS+PTR_SCALESHIFT+1, \
               31-PTR_SCALESHIFT, \
               31-PTR_SCALESHIFT               /* move PSL_DS[27] to bit 29 */
       bl      pte_load
       FRAME_TLBMISSUNLOCK
       mtlr    %r29                            /* restore LR */
       /*
        * If we returned, pte load failed so let trap deal with it but
        * has kept the contents of r24-r31 (expect r29) intact.
        */
       FRAME_TLBENTER(EXC_DSI)
       li      %r3, T_DATA_TLB_ERROR
       b       trapenter

       .p2align 4
_C_LABEL(instruction_tlb_error_vector):
       /* MSR[CE], MSR[ME], MSR[DE] are unchanged, all others cleared */
       FRAME_TLBPROLOGUE
       FRAME_TLBMISSLOCK
       /*
        * Attempt to update the TLB from the page table.
        */
       mflr    %r29                            /* save LR */
       mr      %r23, %r30                      /* PC of exception */
       rlwinm  %r22, %r31,                     /* index into ci_pmap_segtab */\
               MSR_IS+PTR_SCALESHIFT+1, \
               31-PTR_SCALESHIFT, \
               31-PTR_SCALESHIFT               /* move PSL_IS[26] to bit 29 */
       bl      pte_load
       FRAME_TLBMISSUNLOCK
       mtlr    %r29                            /* restore LR */
       /*
        * If we returned, pte load failed so let trap deal with it but
        * has kept the contents of r24-r31 (expect r29) intact.
        */
       FRAME_TLBENTER(EXC_ISI)
       li      %r3, T_INSTRUCTION_TLB_ERROR
       b       trapenter

       .p2align 4
_C_LABEL(debug_vector):
       FRAME_CRIT_PROLOGUE                     /* save SP r25-31 CR LR XER */
       FRAME_CRIT_ENTER(EXC_DEBUG)
       mfspr   %r6, SPR_DBSR
       stw     %r6, FRAME_ESR(%r1)
       li      %r3, T_DEBUG
       bl      _C_LABEL(trap)
       FRAME_CRIT_EXIT

       .p2align 4
_C_LABEL(spv_unavailable_vector):
       FRAME_PROLOGUE_ESR                      /* save SP r25-31 CR LR XER */
       FRAME_ENTER_ESR(EXC_VEC, %r4)
       li      %r3, T_SPE_UNAVAILABLE
       b       trapenter

       .p2align 4
_C_LABEL(fpdata_vector):
       FRAME_PROLOGUE_ESR                      /* save SP r25-31 CR LR XER */
       FRAME_ENTER_ESR(EXC_FPA, %r4)
       li      %r3, T_EMBEDDED_FP_DATA
       b       trapenter

       .p2align 4
_C_LABEL(fpround_vector):
       FRAME_PROLOGUE_ESR                      /* save SP r25-31 CR LR XER */
       FRAME_ENTER_ESR(EXC_FPA, %r4)
       li      %r3, T_EMBEDDED_FP_ROUND
       b       trapenter

       .p2align 4
_C_LABEL(perfmon_vector):
       FRAME_PROLOGUE_ESR                      /* save SP r25-31 CR LR XER */
       FRAME_ENTER_ESR(EXC_PERF, %r4)
       li      %r3, T_EMBEDDED_PERF_MONITOR
       b       trapenter

       .p2align 4
pte_load:
       /*
        * r2 = scratch
        * r20 = scratch
        * r21 = scratch
        * r22 = index into ci_pmap_{kern,user}_segtab
        * r23 = faulting address
        * The rest are for reference and aren't modifiable.  If the load
        * fails, they will be used by FRAME_TLBENTER to create the trapframe.
        * r24 = DEAR
        * r25 = ESR
        * r26 = saved r2
        * r27 = CR
        * r28 = XER
        * r29 = LR
        * r30 = SRR0
        * r31 = SRR1
        */
       cmplwi  %cr2, %r22, 0           /* remember address space */
       GET_CPUINFO(%r2)
       addi    %r22, %r22, CI_PMAP_SEGTAB /* index into segtab(s) */
       lwzx    %r20, %r22, %r2         /* load kern/user L1 PT addr */
       cmplwi  %r20, 0                 /* is segtab null? */
       beqlr   %cr0                    /* yes, return to fallback to trap */

       rlwinm  %r22, %r23, NSEGPG_SCALESHIFT + PTR_SCALESHIFT, \
               31-(NSEGPG_SCALESHIFT + PTR_SCALESHIFT - 1), \
               31-PTR_SCALESHIFT       /* extract addr bits [0:9] to [20:29] */
       lwzx    %r20, %r22, %r20        /* load address of page table page */
       cmplwi  %r20, 0                 /* is page null? */
       beqlr   %cr0                    /* yes, return to fallback to trap */

       rlwinm  %r22, %r23, \
               NSEGPG_SCALESHIFT + NPTEPG_SCALESHIFT + PTE_SCALESHIFT, \
               31-(NPTEPG_SCALESHIFT + PTE_SCALESHIFT - 1), \
               31-PTE_SCALESHIFT       /* extract addr bits [10:19] to [20:29] */
       lwzx    %r20, %r22, %r20        /* load PTE from page table page */
       cmplwi  %r20, 0                 /* is there a valid PTE? */
       beqlr   %cr0                    /* no, return to fallback to trap */

#if (PTE_UNSYNCED << 1) != PTE_xX
#error PTE_UNSYNCED definition error
#endif
#if (PTE_UNMODIFIED << 1) != PTE_xW
#error PTE_UNMODIFIED definition error
#endif
       andi.   %r22, %r20, (PTE_UNSYNCED|PTE_UNMODIFIED)
                                       /* Does the PTE need to be changed? */
       rotlwi  %r22, %r22, 1           /* if so, clear the right PTE bits */
       andc    %r20, %r20, %r22        /* pte &= ~((pte & (PTE_UNSYNCED|PTE_UNMODIFIED)) << 1)*/

       /*
        * r24-r32 = (no touch)
        * r23 = scratch (was fault addr)
        * r22 = scratch
        * r21 = scratch
        * r20 = pte
        * cr2 = AS 0=eq/!0=ne
        */

       /*
        * This is all E500 specific.  We should have a patchable branch
        * to support other BookE (440) implementations.
        */
e500_pte_load:
       bne+    %cr2, 1f                /* user access? MAS1 is ok. */
       mfspr   %r22, SPR_MAS1          /* get MAS1 */
       lis     %r21, MAS1_TID@h        /* get TID mask */
       andc    %r22, %r22, %r21        /* clear TID */
       mtspr   SPR_MAS1, %r22          /* save MAS1 */
1:
       andi.   %r21, %r20, PTE_WIMGE_MASK /* extract WIMGE from PTE */
       cmplwi  %r21, PTE_M             /* if just PTE_M is set, */
       beq+    %cr0, 2f                /*    skip munging mas2 */
       mfspr   %r22, SPR_MAS2          /* get MAS2 (updated by error) */
       clrrwi  %r22, %r22, PTE_RWX_SHIFT /* clear WIMGE bits */
       or      %r22, %r22, %r21        /* combine with MAS2 contents */
       mtspr   SPR_MAS2, %r22          /* put back into MAS2 */
2:
       /*
        * r23 = fault addr
        * r22 = scratch
        * r21 = scratch
        * r20 = pte
        */

       /*
        * In MAS3, the protection bits are in the low 6 bits:
        *    UX SX UW SW UR SR
        * The User bits are 1 bit left of their Supervisor counterparts.
        * Rotate the PTE protection bits left until they wrap around to become
        * the least significant bits, where the Supervisor protection bits
        * are located.  Increase the rotate amount by 1 to place them where
        * the User protection bits are located.  We get that 1 by extracting
        * the MAS1[TS] (set for User access) and moving it to bit 31 (LSB).
        */
       mfspr   %r21, SPR_MAS1          /* get MAS1 which has TS bit */
       extrwi  %r21, %r21, 1, 31-MAS1_TS_SHIFT
                                       /* extract MAS1_TS to LSB */
       clrrwi  %r23, %r20, PAGE_SHIFT  /* clear non-RPN bits from PTE */
       andi.   %r20, %r20, PTE_RWX_MASK /* isolate protection bits */
       rotrwi  %r20, %r20, PTE_RWX_SHIFT
       andi.   %r22, %r20, (MAS3_SW|MAS3_SR) /* user pages need to be R/W by kernel */
       rotlw   %r20, %r20, %r21        /* rotate protection to correct loc */
       or      %r20, %r20, %r22        /* combine system protection bits */
       or      %r23, %r23, %r20        /* combine RPN and protection bits */
       mtspr   SPR_MAS3, %r23          /* put into MAS3 */
       isync                           /* because ECORE500RM tells us too */
       tlbwe                           /* write the TLB entry */
       /*
        * Increment a counter to show how many tlb misses we've handled here.
        */
       lmw     %r30, CI_EV_TLBMISS_SOFT(%r2)
       addic   %r31, %r31, 1
       addze   %r30, %r30
       stmw    %r30, CI_EV_TLBMISS_SOFT(%r2)

       FRAME_TLBMISSUNLOCK

       /*
        * Cleanup and leave.  We know any higher priority exception will
        * save and restore SPRG1 and %r2 thereby preserving their values.
        *
        * r24 = DEAR (don't care)
        * r25 = ESR (don't care)
        * r26 = saved r2
        * r27 = CR
        * r28 = XER
        * r29 = LR
        * r30 = LSW of counter
        * r31 = MSW of counter
        */
       mtlr    %r29                    /* restore Link Register */
       mtxer   %r28                    /* restore XER */
       mtcr    %r27                    /* restore Condition Register */
       mtsprg1 %r26                    /* save saved r2 across load multiple */
       mfsprg3 %r2                     /* get end of save area */
       addi    %r2,%r2,-4*(32-20)      /* adjust save area down */
       lmw     %r20,0(%r2)             /* restore r20-r31 */
       mtsprg3 %r2                     /* save new end of save area */
       mfsprg1 %r2                     /* restore r2 */
       rfi

       .p2align 4
       .globl _C_LABEL(exception_init)
_C_LABEL(exception_init):
       lis     %r6,_C_LABEL(critical_input_vector)@h
       mtspr   SPR_IVPR, %r6

       ori     %r5,%r6,_C_LABEL(critical_input_vector)@l
       mtspr   SPR_IVOR0, %r5

       ori     %r5,%r6,_C_LABEL(machine_check_vector)@l
       mtspr   SPR_IVOR1, %r5

       ori     %r5,%r6,_C_LABEL(data_storage_vector)@l
       mtspr   SPR_IVOR2, %r5

       ori     %r5,%r6,_C_LABEL(instruction_storage_vector)@l
       mtspr   SPR_IVOR3, %r5

       ori     %r5,%r6,_C_LABEL(external_input_vector)@l
       mtspr   SPR_IVOR4, %r5

       ori     %r5,%r6,_C_LABEL(alignment_vector)@l
       mtspr   SPR_IVOR5, %r5

       ori     %r5,%r6,_C_LABEL(program_vector)@l
       mtspr   SPR_IVOR6, %r5

#ifdef SPR_IVOR7
       ori     %r5,%r6,_C_LABEL(fp_unavailable_vector)@l
       mtspr   SPR_IVOR7, %r5
#endif

       ori     %r5,%r6,_C_LABEL(system_call_vector)@l
       mtspr   SPR_IVOR8, %r5

#ifdef SPR_IVOR9
       ori     %r5,%r6,_C_LABEL(ap_unavailable_vector)@l
       mtspr   SPR_IVOR9, %r5
#endif

       ori     %r5,%r6,_C_LABEL(decrementer_vector)@l
       mtspr   SPR_IVOR10, %r5

       ori     %r5,%r6,_C_LABEL(fixed_interval_timer_vector)@l
       mtspr   SPR_IVOR11, %r5

       ori     %r5,%r6,_C_LABEL(watchdog_timer_vector)@l
       mtspr   SPR_IVOR12, %r5

       ori     %r5,%r6,_C_LABEL(data_tlb_error_vector)@l
       mtspr   SPR_IVOR13, %r5

       ori     %r5,%r6,_C_LABEL(instruction_tlb_error_vector)@l
       mtspr   SPR_IVOR14, %r5

       ori     %r5,%r6,_C_LABEL(debug_vector)@l
       mtspr   SPR_IVOR15, %r5

       ori     %r5,%r6,_C_LABEL(spv_unavailable_vector)@l
       mtspr   SPR_IVOR32, %r5

       ori     %r5,%r6,_C_LABEL(fpdata_vector)@l
       mtspr   SPR_IVOR33, %r5

       ori     %r5,%r6,_C_LABEL(fpround_vector)@l
       mtspr   SPR_IVOR34, %r5

       ori     %r5,%r6,_C_LABEL(perfmon_vector)@l
       mtspr   SPR_IVOR35, %r5

       mfspr   %r5, SPR_PIR    /* get Processor ID register */
       cmplwi  %r5,0
       bnelr                   /* return if non-0 (non-primary) */

       lis     %r5,_C_LABEL(powerpc_intrsw)@ha
       stw     %r3,_C_LABEL(powerpc_intrsw)@l(%r5)

       blr

#ifdef notyet
       .data
       .lcomm  ddbstk,4096
       .text

_ENTRY(cpu_Debugger)
       mflr    %r0
       stw     %r0, CFRAME_LR(%r1)

       mfmsr   %r3
       wrteei  0
       mr      %r4,%r1
       lis     %r10,ddbstk@ha
       addi    %r10,%r10,ddbstk@l
       sub     %r5,%r1,%r10
       cmplwi  %r5,4096
       blt     %cr0, 1f
       addi    %r1,%r10,4096-CALLFRAMELEN
1:
       stwu    %r4,-FRAMELEN(%r1)
       stw     %r4,FRAME_R1(%r1)
       stmw    %r13,FRAME_R13(%r1)
       mr      %r26,%r0
       mfcr    %r27
       mfxer   %r28
       mfctr   %r29
       mr      %r30,%r0
       mr      %r31,%r3
       stmw    %r26,FRAME_LR(%r1)
       mr      %r31,%r1
       mr      %r1,%r10
       addi    %r4,%r1,FRAME_TF
       li      %r3,EXC_PGM
       stw     %r3,FRAME_EXC(%r1)
       li      %r3,T_PROGRAM
       bl      _C_LABEL(trap)
       lmw     %r26,FRAME_LR(%r1)
       mtlr    %r26
       mtcr    %r27
       mtxer   %r28
       mtctr   %r29
       mr      %r0,%r31
       lmw     %r13,FRAME_R13(%r1)
       lwz     %r1,FRAME_R1(%r1)
       wrtee   %r0
       blr
#endif /* notyet */