rfi
/* If necessary, invalidate original entry we used */
-3: cmpwi r23,62
+3: cmpwi r23,63
beq 4f
li r6,0
tlbwe r6,r23,PPC44x_TLB_PAGEID
- sync
+ isync
4:
#ifdef CONFIG_SERIAL_TEXT_DEBUG
#define NORMAL_EXCEPTION_PROLOG \
mtspr SPRN_SPRG0,r10; /* save two registers to work with */\
mtspr SPRN_SPRG1,r11; \
- mtspr SPRN_SPRG2,r1; \
+ mtspr SPRN_SPRG4W,r1; \
mfcr r10; /* save CR in r10 for now */\
mfspr r11,SPRN_SRR1; /* check whether user or kernel */\
andi. r11,r11,MSR_PR; \
stw r12,GPR11(r11); \
mflr r10; \
stw r10,_LINK(r11); \
- mfspr r10,SPRG2; \
+ mfspr r10,SPRG4R; \
mfspr r12,SRR0; \
stw r10,GPR1(r11); \
mfspr r9,SRR1; \
* can potentially occur at any point during normal exception processing.
* Thus we cannot use the same SPRG registers as the normal prolog above.
* Instead we use a couple of words of memory at low physical addresses.
- * This is OK since we don't support SMP on these processors.
+ * This is OK since we don't support SMP on these processors. For Book E
+ * processors, we also have a reserved register (SPRG2) that is only used
+ * in critical exceptions so we can free up a GPR to use as the base for
+ * indirect access to the critical exception save area. This is necessary
+ * since the MMU is always on and the save area is offset from KERNELBASE.
*/
-/* XXX but we don't have RAM mapped at 0 in space 0 -- paulus. */
#define CRITICAL_EXCEPTION_PROLOG \
- stw r10,crit_r10@l(0); /* save two registers to work with */\
- stw r11,crit_r11@l(0); \
+ mtspr SPRG2,r8; /* SPRG2 only used in criticals */ \
+ lis r8,crit_save@ha; \
+ stw r10,crit_r10@l(r8); \
+ stw r11,crit_r11@l(r8); \
mfspr r10,SPRG0; \
- stw r10,crit_sprg0@l(0); \
+ stw r10,crit_sprg0@l(r8); \
mfspr r10,SPRG1; \
- stw r10,crit_sprg1@l(0); \
+ stw r10,crit_sprg1@l(r8); \
mfspr r10,SPRG4R; \
- stw r10,crit_sprg4@l(0); \
+ stw r10,crit_sprg4@l(r8); \
mfspr r10,SPRG5R; \
- stw r10,crit_sprg5@l(0); \
- mfspr r10,SPRG6R; \
- stw r10,crit_sprg6@l(0); \
+ stw r10,crit_sprg5@l(r8); \
mfspr r10,SPRG7R; \
- stw r10,crit_sprg7@l(0); \
+ stw r10,crit_sprg7@l(r8); \
mfspr r10,SPRN_PID; \
- stw r10,crit_pid@l(0); \
+ stw r10,crit_pid@l(r8); \
mfspr r10,SRR0; \
- stw r10,crit_srr0@l(0); \
+ stw r10,crit_srr0@l(r8); \
mfspr r10,SRR1; \
- stw r10,crit_srr1@l(0); \
+ stw r10,crit_srr1@l(r8); \
+ mfspr r8,SPRG2; /* SPRG2 only used in criticals */ \
mfcr r10; /* save CR in r10 for now */\
mfspr r11,SPRN_CSRR1; /* check whether user or kernel */\
andi. r11,r11,MSR_PR; \
lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\
addi r11,r11,THREAD_SIZE; \
1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\
- tophys(r11,r11); \
stw r10,_CCR(r11); /* save various registers */\
stw r12,GPR12(r11); \
stw r9,GPR9(r11); \
SAVE_4GPRS(3, r11); \
SAVE_2GPRS(7, r11)
+/*
+ * Exception prolog for machine check exceptions. This is similar to
+ * the critical exception prolog, except that machine check exceptions
+ * have their own save area. For Book E processors, we also have a
+ * reserved register (SPRG6) that is only used in machine check exceptions
+ * so we can free up a GPR to use as the base for indirect access to the
+ * machine check exception save area. This is necessary since the MMU
+ * is always on and the save area is offset from KERNELBASE.
+ */
+#define MCHECK_EXCEPTION_PROLOG \
+ mtspr SPRG6W,r8; /* SPRG6 used in machine checks */ \
+ lis r8,mcheck_save@ha; \
+ stw r10,mcheck_r10@l(r8); \
+ stw r11,mcheck_r11@l(r8); \
+ mfspr r10,SPRG0; \
+ stw r10,mcheck_sprg0@l(r8); \
+ mfspr r10,SPRG1; \
+ stw r10,mcheck_sprg1@l(r8); \
+ mfspr r10,SPRG4R; \
+ stw r10,mcheck_sprg4@l(r8); \
+ mfspr r10,SPRG5R; \
+ stw r10,mcheck_sprg5@l(r8); \
+ mfspr r10,SPRG7R; \
+ stw r10,mcheck_sprg7@l(r8); \
+ mfspr r10,SPRN_PID; \
+ stw r10,mcheck_pid@l(r8); \
+ mfspr r10,SRR0; \
+ stw r10,mcheck_srr0@l(r8); \
+ mfspr r10,SRR1; \
+ stw r10,mcheck_srr1@l(r8); \
+ mfspr r10,CSRR0; \
+ stw r10,mcheck_csrr0@l(r8); \
+ mfspr r10,CSRR1; \
+ stw r10,mcheck_csrr1@l(r8); \
+ mfspr r8,SPRG6R; /* SPRG6 used in machine checks */ \
+ mfcr r10; /* save CR in r10 for now */\
+ mfspr r11,SPRN_MCSRR1; /* check whether user or kernel */\
+ andi. r11,r11,MSR_PR; \
+ lis r11,mcheck_stack_top@h; \
+ ori r11,r11,mcheck_stack_top@l; \
+ beq 1f; \
+ /* COMING FROM USER MODE */ \
+ mfspr r11,SPRG3; /* if from user, start at top of */\
+ lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\
+ addi r11,r11,THREAD_SIZE; \
+1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\
+ stw r10,_CCR(r11); /* save various registers */\
+ stw r12,GPR12(r11); \
+ stw r9,GPR9(r11); \
+ mflr r10; \
+ stw r10,_LINK(r11); \
+ mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\
+ stw r12,_DEAR(r11); /* since they may have had stuff */\
+ mfspr r9,SPRN_ESR; /* in them at the point where the */\
+ stw r9,_ESR(r11); /* exception was taken */\
+ mfspr r12,MCSRR0; \
+ stw r1,GPR1(r11); \
+ mfspr r9,MCSRR1; \
+ stw r1,0(r11); \
+ tovirt(r1,r11); \
+ rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\
+ stw r0,GPR0(r11); \
+ SAVE_4GPRS(3, r11); \
+ SAVE_2GPRS(7, r11)
+
/*
* Exception vectors.
*/
NOCOPY, transfer_to_handler_full, \
ret_from_except_full)
+#define MCHECK_EXCEPTION(n, label, hdlr) \
+ START_EXCEPTION(label); \
+ MCHECK_EXCEPTION_PROLOG; \
+ lis r4,MCSR_MCS@h; \
+ mtspr SPRN_MCSR,r4; \
+ mfspr r5,SPRN_ESR; \
+ stw r5,_ESR(r11); \
+ addi r3,r1,STACK_FRAME_OVERHEAD; \
+ EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \
+ NOCOPY, mcheck_transfer_to_handler, \
+ ret_from_mcheck_exc)
+
#define EXC_XFER_TEMPLATE(hdlr, trap, msr, copyee, tfer, ret) \
li r10,trap; \
stw r10,TRAP(r11); \
CRITICAL_EXCEPTION(0x0100, CriticalInput, UnknownException)
/* Machine Check Interrupt */
+#ifdef CONFIG_440A
+ MCHECK_EXCEPTION(0x0200, MachineCheck, MachineCheckException)
+#else
CRITICAL_EXCEPTION(0x0200, MachineCheck, MachineCheckException)
+#endif
/* Data Storage Interrupt */
START_EXCEPTION(DataStorage)
mtspr SPRG1, r11
mtspr SPRG4W, r12
mtspr SPRG5W, r13
- mtspr SPRG6W, r14
mfcr r11
mtspr SPRG7W, r11
rlwinm r11,r11,0,20,15 /* Clear U0-U3 */
/* find the TLB index that caused the fault. It has to be here. */
- tlbsx r14, 0, r10
+ tlbsx r10, 0, r10
- tlbwe r11, r14, PPC44x_TLB_ATTRIB /* Write ATTRIB */
+ tlbwe r11, r10, PPC44x_TLB_ATTRIB /* Write ATTRIB */
/* Done...restore registers and get out of here.
*/
mfspr r11, SPRG7R
mtcr r11
- mfspr r14, SPRG6R
mfspr r13, SPRG5R
mfspr r12, SPRG4R
*/
mfspr r11, SPRG7R
mtcr r11
- mfspr r14, SPRG6R
mfspr r13, SPRG5R
mfspr r12, SPRG4R
NORMAL_EXCEPTION_PROLOG
mr r4,r12 /* Pass SRR0 as arg2 */
li r5,0 /* Pass zero as arg3 */
- addi r3,r1,STACK_FRAME_OVERHEAD
- EXC_XFER_EE_LITE(0x0400, do_page_fault)
+ EXC_XFER_EE_LITE(0x0400, handle_page_fault)
/* External Input Interrupt */
EXCEPTION(0x0500, ExternalInput, do_IRQ, EXC_XFER_LITE)
mfspr r4,SPRN_ESR /* Grab the ESR and save it */
stw r4,_ESR(r11)
addi r3,r1,STACK_FRAME_OVERHEAD
- EXC_XFER_EE(0x700, ProgramCheckException)
+ EXC_XFER_STD(0x700, ProgramCheckException)
/* Floating Point Unavailable Interrupt */
EXCEPTION(0x2010, FloatingPointUnavailable, UnknownException, EXC_XFER_EE)
mtspr SPRG1, r11
mtspr SPRG4W, r12
mtspr SPRG5W, r13
- mtspr SPRG6W, r14
mfcr r11
mtspr SPRG7W, r11
mfspr r10, SPRN_DEAR /* Get faulting address */
*/
mfspr r11, SPRG7R
mtcr r11
- mfspr r14, SPRG6R
mfspr r13, SPRG5R
mfspr r12, SPRG4R
mfspr r11, SPRG1
mtspr SPRG1, r11
mtspr SPRG4W, r12
mtspr SPRG5W, r13
- mtspr SPRG6W, r14
mfcr r11
mtspr SPRG7W, r11
mfspr r10, SRR0 /* Get faulting address */
*/
mfspr r11, SPRG7R
mtcr r11
- mfspr r14, SPRG6R
mfspr r13, SPRG5R
mfspr r12, SPRG4R
mfspr r11, SPRG1
* the MSR_DE bit set.
*/
/* Debug Interrupt */
- CRITICAL_EXCEPTION(0x2000, Debug, DebugException)
-#if 0
START_EXCEPTION(Debug)
- /* This first instruction was already executed by the exception
- * handler and must be the first instruction of every exception
- * handler.
- */
- mtspr SPRN_SPRG0,r10 /* Save some working registers... */
- mtspr SPRN_SPRG1,r11
- mtspr SPRN_SPRG4W,r12
- mfcr r10 /* ..and the cr because we change it */
-
- mfspr r11,SPRN_CSRR1 /* MSR at the time of fault */
- andi. r11,r11,MSR_PR
- bne+ 2f /* trapped from problem state */
-
- mfspr r11,SPRN_CSRR0 /* Faulting instruction address */
- lis r12, KERNELBASE@h
- ori r12, r12, KERNELBASE@l
- cmplw r11,r12
- blt+ 2f /* addr below exception vectors */
-
- lis r12, Debug@h
- ori r12, r12, Debug@l
- cmplw r11,r12
- bgt+ 2f /* addr above TLB exception vectors */
-
- lis r11,DBSR_IC@h /* Remove the trap status */
- mtspr SPRN_DBSR,r11
-
- mfspr r11,SPRN_CSRR1
- rlwinm r11,r11,0,23,21 /* clear MSR_DE */
- mtspr SPRN_CSRR1, r11 /* restore MSR at rcfi without DE */
-
- mtcrf 0xff,r10 /* restore registers */
- mfspr r12,SPRN_SPRG4R
- mfspr r11,SPRN_SPRG1
- mfspr r10,SPRN_SPRG0
+ CRITICAL_EXCEPTION_PROLOG
- sync
- rfci /* return to the exception handler */
- b . /* prevent prefetch past rfci */
-
-2:
- mtcrf 0xff,r10 /* restore registers */
- mfspr r12,SPRN_SPRG4R
- mfspr r11,SPRN_SPRG1
- mfspr r10,SPRN_SPRG0
-
- CRIT_EXCEPTION_PROLOG
+ /*
+ * If this is a single step or branch-taken exception in an
+ * exception entry sequence, it was probably meant to apply to
+ * the code where the exception occurred (since exception entry
+ * doesn't turn off DE automatically). We simulate the effect
+ * of turning off DE on entry to an exception handler by turning
+ * off DE in the CSRR1 value and clearing the debug status.
+ */
+ mfspr r10,SPRN_DBSR /* check single-step/branch taken */
+ andis. r10,r10,(DBSR_IC|DBSR_BT)@h
+ beq+ 1f
+ andi. r0,r9,MSR_PR /* check supervisor */
+ beq 2f /* branch if we need to fix it up... */
+
+ /* continue normal handling for a critical exception... */
+1: mfspr r4,SPRN_DBSR
addi r3,r1,STACK_FRAME_OVERHEAD
- li r7,CRIT_EXC;
- li r9,MSR_KERNEL
- FINISH_EXCEPTION(DebugException)
-#endif
+ EXC_XFER_TEMPLATE(DebugException, 0x2002, \
+ (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \
+ NOCOPY, crit_transfer_to_handler, ret_from_crit_exc)
+
+ /* here it looks like we got an inappropriate debug exception. */
+2: rlwinm r9,r9,0,~MSR_DE /* clear DE in the CSRR1 value */
+ mtspr SPRN_DBSR,r10 /* clear the IC/BT debug intr status */
+ /* restore state and get out */
+ lwz r10,_CCR(r11)
+ lwz r0,GPR0(r11)
+ lwz r1,GPR1(r11)
+ mtcrf 0x80,r10
+ mtspr CSRR0,r12
+ mtspr CSRR1,r9
+ lwz r9,GPR9(r11)
+
+ mtspr SPRG2,r8; /* SPRG2 only used in criticals */
+ lis r8,crit_save@ha;
+ lwz r10,crit_r10@l(r8)
+ lwz r11,crit_r11@l(r8)
+ mfspr r8,SPRG2
+
+ rfci
+ b .
/*
* Local functions
mfspr r5,SPRN_ESR /* Grab the ESR, save it, pass arg3 */
stw r5,_ESR(r11)
mfspr r4,SPRN_DEAR /* Grab the DEAR, save it, pass arg2 */
- stw r4,_DEAR(r11)
- addi r3,r1,STACK_FRAME_OVERHEAD
- EXC_XFER_EE_LITE(0x0300, do_page_fault)
+ EXC_XFER_EE_LITE(0x0300, handle_page_fault)
/*
* r11 - available to use
* r12 - Pointer to the 64-bit PTE
* r13 - available to use
- * r14 - available to use
* MMUCR - loaded with proper value when we get here
* Upon exit, we reload everything and RFI.
*/
/* Load the next available TLB index */
lis r13, tlb_44x_index@ha
- lwz r14, tlb_44x_index@l(r13)
+ lwz r13, tlb_44x_index@l(r13)
/* Load the TLB high watermark */
- lis r13, tlb_44x_hwater@ha
- lwz r11, tlb_44x_hwater@l(r13)
+ lis r11, tlb_44x_hwater@ha
+ lwz r11, tlb_44x_hwater@l(r11)
/* Increment, rollover, and store TLB index */
- addi r14, r14, 1
- cmpw 0, r14, r11 /* reserve entries */
+ addi r13, r13, 1
+ cmpw 0, r13, r11 /* reserve entries */
ble 7f
- li r14, 0
+ li r13, 0
7:
/* Store the next available TLB index */
- lis r13, tlb_44x_index@ha
- stw r14, tlb_44x_index@l(r13)
+ lis r11, tlb_44x_index@ha
+ stw r13, tlb_44x_index@l(r11)
- lwz r13, 0(r12) /* Get MS word of PTE */
- lwz r11, 4(r12) /* Get LS word of PTE */
- rlwimi r13, r11, 0, 0 , 19 /* Insert RPN */
- tlbwe r13, r14, PPC44x_TLB_XLAT /* Write XLAT */
+ lwz r11, 0(r12) /* Get MS word of PTE */
+ lwz r12, 4(r12) /* Get LS word of PTE */
+ rlwimi r11, r12, 0, 0 , 19 /* Insert RPN */
+ tlbwe r11, r13, PPC44x_TLB_XLAT /* Write XLAT */
/*
* Create PAGEID. This is the faulting address,
* page size, and valid flag.
*/
- li r12, PPC44x_TLB_VALID | PPC44x_TLB_4K
- rlwimi r10, r12, 0, 20, 31 /* Insert valid and page size */
- tlbwe r10, r14, PPC44x_TLB_PAGEID /* Write PAGEID */
-
- li r13, PPC44x_TLB_SR@l /* Set SR */
- rlwimi r13, r11, 0, 30, 30 /* Set SW = _PAGE_RW */
- rlwimi r13, r11, 29, 29, 29 /* SX = _PAGE_HWEXEC */
- rlwimi r13, r11, 29, 28, 28 /* UR = _PAGE_USER */
- rlwimi r12, r11, 31, 26, 26 /* (_PAGE_USER>>1)->r12 */
- and r12, r12, r11 /* HWEXEC & USER */
- rlwimi r13, r12, 0, 26, 26 /* UX = HWEXEC & USER */
-
- rlwimi r11, r13, 0, 26, 31 /* Insert static perms */
- rlwinm r11, r11, 0, 20, 15 /* Clear U0-U3 */
- tlbwe r11, r14, PPC44x_TLB_ATTRIB /* Write ATTRIB */
+ li r11, PPC44x_TLB_VALID | PPC44x_TLB_4K
+ rlwimi r10, r11, 0, 20, 31 /* Insert valid and page size */
+ tlbwe r10, r13, PPC44x_TLB_PAGEID /* Write PAGEID */
+
+ li r10, PPC44x_TLB_SR@l /* Set SR */
+ rlwimi r10, r12, 0, 30, 30 /* Set SW = _PAGE_RW */
+ rlwimi r10, r12, 29, 29, 29 /* SX = _PAGE_HWEXEC */
+ rlwimi r10, r12, 29, 28, 28 /* UR = _PAGE_USER */
+ rlwimi r11, r12, 31, 26, 26 /* (_PAGE_USER>>1)->r12 */
+ and r11, r12, r11 /* HWEXEC & USER */
+ rlwimi r10, r11, 0, 26, 26 /* UX = HWEXEC & USER */
+
+ rlwimi r12, r10, 0, 26, 31 /* Insert static perms */
+ rlwinm r12, r12, 0, 20, 15 /* Clear U0-U3 */
+ tlbwe r12, r13, PPC44x_TLB_ATTRIB /* Write ATTRIB */
/* Done...restore registers and get out of here.
*/
mfspr r11, SPRG7R
mtcr r11
- mfspr r14, SPRG6R
mfspr r13, SPRG5R
mfspr r12, SPRG4R
mfspr r11, SPRG1
_GLOBAL(swapper_pg_dir)
.space 8192
-/* Stack for handling critical exceptions from kernel mode */
.section .bss
+/* Stack for handling critical exceptions from kernel mode */
critical_stack_bottom:
.space 4096
critical_stack_top:
.previous
-/*
- * This space gets a copy of optional info passed to us by the bootstrap
- * which is used to pass parameters into the kernel like root=/dev/sda1, etc.
- */
-_GLOBAL(cmd_line)
- .space 512
-
-/*
- * Room for two PTE pointers, usually the kernel and current user pointers
- * to their respective root page table.
- */
-abatron_pteptrs:
- .space 8
+/* Stack for handling machine check exceptions from kernel mode */
+mcheck_stack_bottom:
+ .space 4096
+mcheck_stack_top:
+ .previous
/*
* This area is used for temporarily saving registers during the
- * critical exception prolog.
+ * critical and machine check exception prologs. It must always
+ * follow the page aligned allocations, so it starts on a page
+ * boundary, ensuring that all crit_save areas are in a single
+ * page.
*/
-crit_save:
+
+/* crit_save */
+_GLOBAL(crit_save)
+ .space 4
_GLOBAL(crit_r10)
.space 4
_GLOBAL(crit_r11)
.space 4
_GLOBAL(crit_sprg5)
.space 4
-_GLOBAL(crit_sprg6)
- .space 4
_GLOBAL(crit_sprg7)
.space 4
_GLOBAL(crit_pid)
.space 4
_GLOBAL(crit_srr1)
.space 4
+
+/* mcheck_save */
+_GLOBAL(mcheck_save)
+ .space 4
+_GLOBAL(mcheck_r10)
+ .space 4
+_GLOBAL(mcheck_r11)
+ .space 4
+_GLOBAL(mcheck_sprg0)
+ .space 4
+_GLOBAL(mcheck_sprg1)
+ .space 4
+_GLOBAL(mcheck_sprg4)
+ .space 4
+_GLOBAL(mcheck_sprg5)
+ .space 4
+_GLOBAL(mcheck_sprg7)
+ .space 4
+_GLOBAL(mcheck_pid)
+ .space 4
+_GLOBAL(mcheck_srr0)
+ .space 4
+_GLOBAL(mcheck_srr1)
+ .space 4
+_GLOBAL(mcheck_csrr0)
+ .space 4
+_GLOBAL(mcheck_csrr1)
+ .space 4
+
+/*
+ * This space gets a copy of optional info passed to us by the bootstrap
+ * which is used to pass parameters into the kernel like root=/dev/sda1, etc.
+ */
+_GLOBAL(cmd_line)
+ .space 512
+
+/*
+ * Room for two PTE pointers, usually the kernel and current user pointers
+ * to their respective root page table.
+ */
+abatron_pteptrs:
+ .space 8
+
+