/*
* Copyright 1991-1998 by Open Software Foundation, Inc.
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appears in all copies and
* that both the copyright notice and this permission notice appear in
* supporting documentation.
*
* OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
* NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* MkLinux
*/
/*
* Low-memory exception vector code for PowerPC MACH
*
* These are the only routines that are ever run with
* VM instruction translation switched off.
*
* The PowerPC is quite strange in that rather than having a set
* of exception vectors, the exception handlers are installed
* in well-known addresses in low memory. This code must be loaded
* at ZERO in physical memory. The simplest way of doing this is
* to load the kernel at zero, and specify this as the first file
* on the linker command line.
*
* When this code is loaded into place, it is loaded at virtual
* address KERNELBASE, which is mapped to zero (physical).
*
* This code handles all powerpc exceptions and is always entered
* in supervisor mode with translation off. It saves the minimum
* processor state before switching back on translation and
* jumping to the approprate routine.
*
* Vectors from 0x100 to 0x3fff occupy 0x100 bytes each (64 instructions)
*
* We use some of this space to decide which stack to use, and where to
* save the context etc, before jumping to a generic handler.
*/
/*
* Modified to include the exception trace code in non-DB_MACHINE MP builds
* to avoid dealing with code that's OBAR. Mask out all exceptions, so none
* are traced.
*
* This really should be fixed by someone who really knows what they're doing,
* since I don't, but this works, for now...
*/
#include <assym.S>
#include <debug.h>
#include <cpus.h>
#include <db_machine_commands.h>
#include <mach_debug.h>
#include <ppc/asm.h>
#include <ppc/proc_reg.h>
#include <ppc/exception.h>
#include <mach/ppc/vm_param.h>
#if NCPUS > 1
#include <ppc/POWERMAC/mp/MPPlugIn.h>
#endif /* NCPUS > 1 */
.text
.globl _ExceptionVectorsStart
.type _ExceptionVectorsStart,@function
_ExceptionVectorsStart: /* Used if relocating the exception vectors */
baseR: /* Used so we have more readable code */
/*
* System reset - call debugger
*/
. = 0x100
.L_handler100:
mtsprg 3, r3
li r3, T_RESET
b .L_exception_entry
#if DB_MACHINE_COMMANDS || (NCPUS > 1)
/*
* This is the data used by the exception trace code
*/
. = 0x180
.L_TraceData:
.globl traceMask
.type traceMask,@object
traceMask: /* Allowable trace types indexed by vector >> 8 */
#if DB_MACHINE_COMMANDS
.int 0xFFFFFFFF /* All enabled */
#else
.int 0x0000000 /* All disabled */
#endif /* DB_MACHINE_COMMANDS */
/* .int 0xFBBFFFFF */ /* EXT and DEC disabled */
.globl traceCurr
.type traceCurr,@object
traceCurr: .int traceTableBeg-_ExceptionVectorsStart /* The next trace entry to use */
.globl traceStart
.type traceStart,@object
traceStart: .int traceTableBeg-_ExceptionVectorsStart /* Start of the trace table */
.globl traceEnd
.type traceEnd,@object
traceEnd: .int traceTableEnd-_ExceptionVectorsStart /* End (wrap point) of the trace */
debsave0: .int 0
debsave1: .int 0
debsave2: .int 0
debsave3: .int 0
debsave4: .int 0
debsave5: .int 0
debsave6: .int 0
debsave7: .int 0
debsave8: .int 0
debsave9: .int 0
debsave10: .int 0
#endif /* DB_MACHINE_COMMANDS */
.globl global_fault
.type global_fault,@object
global_fault: .int 0x00000000
/*
* Machine check (physical bus error) - call debugger
*/
. = 0x200
.L_handler200:
mtsprg 3, r3
li r3, T_MACHINE_CHECK
b .L_exception_entry
/*
* Data access - page fault, invalid memory rights for operation
*/
. = 0x300
.L_handler300:
mtsprg 3, r3
li r3, T_DATA_ACCESS
b .L_exception_entry
/*
* Instruction access - as for data access
*/
. = 0x400
.L_handler400:
mtsprg 3, r3
li r3, T_INSTRUCTION_ACCESS
b .L_exception_entry
/*
* External interrupt
*/
. = 0x500
.L_handler500:
mtsprg 3, r3
li r3, T_INTERRUPT
b .L_exception_entry
/*
* Alignment - many reasons
*/
. = 0x600
.L_handler600:
mtsprg 3, r3
li r3, T_ALIGNMENT
b .L_exception_entry
/*
* Program - floating point exception, illegal inst, priv inst, user trap
*/
. = 0x700
.L_handler700:
mtsprg 3, r3
li r3, T_PROGRAM
b .L_exception_entry
/*
* Program - floating point disabled, illegal inst, priv inst, user trap
*/
. = 0x800
.L_handler800:
mtsprg 3, r3
li r3, T_FP_UNAVAILABLE
b .L_exception_entry
/*
* Decrementer - DEC register has passed zero.
*/
. = 0x900
.L_handler900:
mtsprg 3, r3
li r3, T_DECREMENTER
b .L_exception_entry
/*
* I/O controller interface error - MACH does not use this
*/
. = 0xA00
.L_handlerA00:
mtsprg 3, r3
li r3, T_IO_ERROR
b .L_exception_entry
/*
* Reserved
*/
. = 0xb00
.L_handlerB00:
mtsprg 3, r3
li r3, T_RESERVED
b .L_exception_entry
/*
* System call - generated by the sc instruction
*/
. = 0xc00
.L_handlerC00:
mtsprg 3, r3
li r3, T_SYSTEM_CALL
b .L_exception_entry
/*
* Trace - generated by single stepping a 603/604
*/
. = 0xd00
.L_handlerD00:
mtsprg 3, r3
li r3, T_TRACE
b .L_exception_entry
/*
* Floating point assist
*/
. = 0xe00
.L_handlerE00:
mtsprg 3, r3
li r3, T_FP_ASSIST
b .L_exception_entry
/*
* Instruction translation miss - we inline this code.
* Upon entry (done for us by the machine):
* srr0 : addr of instruction that missed
* srr1 : bits 0-3 = saved CR0
* 4 = lru way bit
* 16-31 = saved msr
* msr[tgpr] = 1 (so gpr0-3 become our temporary variables)
* imiss: ea that missed
* icmp : the compare value for the va that missed
* hash1: pointer to first hash pteg
* hash2: pointer to 2nd hash pteg
*
* Register usage:
* tmp0: saved counter
* tmp1: junk
* tmp2: pointer to pteg
* tmp3: current compare value
*
* This code is taken from the 603e User's Manual with
* some bugfixes and minor improvements to save bytes and cycles
*/
. = 0x1000
.L_handler1000:
mfspr tmp2, hash1
mfctr tmp0 /* use tmp0 to save ctr */
mfspr tmp3, icmp
.L_imiss_find_pte_in_pteg:
li tmp1, 8 /* count */
subi tmp2, tmp2, 8 /* offset for lwzu */
mtctr tmp1 /* count... */
.L_imiss_pteg_loop:
lwz tmp1, 8(tmp2) /* check pte0 for match... */
addi tmp2, tmp2, 8
cmpw CR0, tmp1, tmp3
#if 0
bdnzf+ CR0, .L_imiss_pteg_loop
#else
bc CR0,2, .L_imiss_pteg_loop
#endif
beq+ CR0, .L_imiss_found_pte
/* Not found in PTEG, we must scan 2nd then give up */
andi. tmp1, tmp3, MASK(PTE0_HASH_ID)
bne- .L_imiss_do_no_hash_exception /* give up */
mfspr tmp2, hash2
ori tmp3, tmp3, MASK(PTE0_HASH_ID)
b .L_imiss_find_pte_in_pteg
.L_imiss_found_pte:
lwz tmp1, 4(tmp2) /* get pte1_t */
andi. tmp3, tmp1, MASK(PTE1_WIMG_GUARD) /* Fault? */
bne- .L_imiss_do_prot_exception /* Guarded - illegal */
/* Ok, we've found what we need to, restore and rfi! */
mtctr tmp0 /* restore ctr */
mfsrr1 tmp3
mfspr tmp0, imiss
mtcrf 0x80, tmp3 /* Restore CR0 */
mtspr rpa, tmp1 /* set the pte */
ori tmp1, tmp1, MASK(PTE1_REFERENCED) /* set referenced */
tlbli tmp0
sth tmp1, 6(tmp2)
rfi
.L_imiss_do_prot_exception:
/* set up srr1 to indicate protection exception... */
mfsrr1 tmp3
andi. tmp2, tmp3, 0xffff
addis tmp2, tmp2, MASK(SRR1_TRANS_PROT) >> 16
b .L_imiss_do_exception
.L_imiss_do_no_hash_exception:
/* clean up registers for protection exception... */
mfsrr1 tmp3
andi. tmp2, tmp3, 0xffff
addis tmp2, tmp2, MASK(SRR1_TRANS_HASH) >> 16
/* And the entry into the usual instruction fault handler ... */
.L_imiss_do_exception:
mtctr tmp0 /* Restore ctr */
mtsrr1 tmp2 /* Set up srr1 */
mfmsr tmp0
xoris tmp0, tmp0, MASK(MSR_TGPR)>>16 /* no TGPR */
mtcrf 0x80, tmp3 /* Restore CR0 */
mtmsr tmp0 /* reset MSR[TGPR] */
b .L_handler400 /* Instr Access */
/*
* Data load translation miss
*
* Upon entry (done for us by the machine):
* srr0 : addr of instruction that missed
* srr1 : bits 0-3 = saved CR0
* 4 = lru way bit
* 5 = 1 if store
* 16-31 = saved msr
* msr[tgpr] = 1 (so gpr0-3 become our temporary variables)
* dmiss: ea that missed
* dcmp : the compare value for the va that missed
* hash1: pointer to first hash pteg
* hash2: pointer to 2nd hash pteg
*
* Register usage:
* tmp0: saved counter
* tmp1: junk
* tmp2: pointer to pteg
* tmp3: current compare value
*
* This code is taken from the 603e User's Manual with
* some bugfixes and minor improvements to save bytes and cycles
*/
. = 0x1100
.L_handler1100:
mfspr tmp2, hash1
mfctr tmp0 /* use tmp0 to save ctr */
mfspr tmp3, dcmp
.L_dlmiss_find_pte_in_pteg:
li tmp1, 8 /* count */
subi tmp2, tmp2, 8 /* offset for lwzu */
mtctr tmp1 /* count... */
.L_dlmiss_pteg_loop:
lwz tmp1, 8(tmp2) /* check pte0 for match... */
addi tmp2, tmp2, 8
cmpw CR0, tmp1, tmp3
#if 0 /* How to write this correctly? */
bdnzf+ CR0, .L_dlmiss_pteg_loop
#else
bc CR0,2, .L_dlmiss_pteg_loop
#endif
beq+ CR0, .L_dmiss_found_pte
/* Not found in PTEG, we must scan 2nd then give up */
andi. tmp1, tmp3, MASK(PTE0_HASH_ID) /* already at 2nd? */
bne- .L_dmiss_do_no_hash_exception /* give up */
mfspr tmp2, hash2
ori tmp3, tmp3, MASK(PTE0_HASH_ID)
b .L_dlmiss_find_pte_in_pteg
.L_dmiss_found_pte:
lwz tmp1, 4(tmp2) /* get pte1_t */
/* Ok, we've found what we need to, restore and rfi! */
mtctr tmp0 /* restore ctr */
mfsrr1 tmp3
mfspr tmp0, dmiss
mtcrf 0x80, tmp3 /* Restore CR0 */
mtspr rpa, tmp1 /* set the pte */
ori tmp1, tmp1, MASK(PTE1_REFERENCED) /* set referenced */
tlbld tmp0 /* load up tlb */
sth tmp1, 6(tmp2) /* sth is faster? */
rfi
/* This code is shared with data store translation miss */
.L_dmiss_do_no_hash_exception:
/* clean up registers for protection exception... */
mfsrr1 tmp3
/* prepare to set DSISR_WRITE_BIT correctly from srr1 info */
rlwinm tmp1, tmp3, 9, 6, 6
addis tmp1, tmp1, MASK(DSISR_HASH) >> 16
/* And the entry into the usual data fault handler ... */
mtctr tmp0 /* Restore ctr */
andi. tmp2, tmp3, 0xffff /* Clean up srr1 */
mtsrr1 tmp2 /* Set srr1 */
mtdsisr tmp1
mfspr tmp2, dmiss
mtdar tmp2
mfmsr tmp0
xoris tmp0, tmp0, MASK(MSR_TGPR)>>16 /* no TGPR */
mtcrf 0x80, tmp3 /* Restore CR0 */
sync /* Needed on some */
mtmsr tmp0 /* reset MSR[TGPR] */
b .L_handler300 /* Data Access */
/*
* Data store translation miss (similar to data load)
*
* Upon entry (done for us by the machine):
* srr0 : addr of instruction that missed
* srr1 : bits 0-3 = saved CR0
* 4 = lru way bit
* 5 = 1 if store
* 16-31 = saved msr
* msr[tgpr] = 1 (so gpr0-3 become our temporary variables)
* dmiss: ea that missed
* dcmp : the compare value for the va that missed
* hash1: pointer to first hash pteg
* hash2: pointer to 2nd hash pteg
*
* Register usage:
* tmp0: saved counter
* tmp1: junk
* tmp2: pointer to pteg
* tmp3: current compare value
*
* This code is taken from the 603e User's Manual with
* some bugfixes and minor improvements to save bytes and cycles
*/
. = 0x1200
.L_handler1200:
mfspr tmp2, hash1
mfctr tmp0 /* use tmp0 to save ctr */
mfspr tmp3, dcmp
.L_dsmiss_find_pte_in_pteg:
li tmp1, 8 /* count */
subi tmp2, tmp2, 8 /* offset for lwzu */
mtctr tmp1 /* count... */
.L_dsmiss_pteg_loop:
lwz tmp1, 8(tmp2) /* check pte0 for match... */
addi tmp2, tmp2, 8
cmpw CR0, tmp1, tmp3
#if 0 /* I don't know how to write this properly */
bdnzf+ CR0, .L_dsmiss_pteg_loop
#else
bc CR0,2, .L_dsmiss_pteg_loop
#endif
beq+ CR0, .L_dsmiss_found_pte
/* Not found in PTEG, we must scan 2nd then give up */
andi. tmp1, tmp3, MASK(PTE0_HASH_ID) /* already at 2nd? */
bne- .L_dmiss_do_no_hash_exception /* give up */
mfspr tmp2, hash2
ori tmp3, tmp3, MASK(PTE0_HASH_ID)
b .L_dsmiss_find_pte_in_pteg
.L_dsmiss_found_pte:
lwz tmp1, 4(tmp2) /* get pte1_t */
andi. tmp3, tmp1, MASK(PTE1_CHANGED) /* unchanged, check? */
beq- .L_dsmiss_check_prot /* yes, check prot */
.L_dsmiss_resolved:
/* Ok, we've found what we need to, restore and rfi! */
mtctr tmp0 /* restore ctr */
mfsrr1 tmp3
mfspr tmp0, dmiss
mtcrf 0x80, tmp3 /* Restore CR0 */
mtspr rpa, tmp1 /* set the pte */
tlbld tmp0 /* load up tlb */
rfi
.L_dsmiss_check_prot:
/* PTE is unchanged, we must check that we can write */
rlwinm. tmp3, tmp1, 30, 0, 1 /* check PP[1] */
bge- .L_dsmiss_check_prot_user_kern
andi. tmp3, tmp1, 1 /* check PP[0] */
beq+ .L_dsmiss_check_prot_ok
.L_dmiss_do_prot_exception:
/* clean up registers for protection exception... */
mfsrr1 tmp3
/* prepare to set DSISR_WRITE_BIT correctly from srr1 info */
rlwinm tmp1, tmp3, 9, 6, 6
addis tmp1, tmp1, MASK(DSISR_PROT) >> 16
/* And the entry into the usual data fault handler ... */
mtctr tmp0 /* Restore ctr */
andi. tmp2, tmp3, 0xffff /* Clean up srr1 */
mtsrr1 tmp2 /* Set srr1 */
mtdsisr tmp1
mfspr tmp2, dmiss
mtdar tmp2
mfmsr tmp0
xoris tmp0, tmp0, MASK(MSR_TGPR)>>16 /* no TGPR */
mtcrf 0x80, tmp3 /* Restore CR0 */
sync /* Needed on some */
mtmsr tmp0 /* reset MSR[TGPR] */
b .L_handler300 /* Data Access */
/* NB - if we knew we were on a 603e we could test just the MSR_KEY bit */
.L_dsmiss_check_prot_user_kern:
mfsrr1 tmp3
andi. tmp3, tmp3, MASK(MSR_PR)
beq+ .L_dsmiss_check_prot_kern
mfspr tmp3, dmiss /* check user privs */
mfsrin tmp3, tmp3 /* get excepting SR */
andis. tmp3, tmp3, 0x2000 /* Test SR ku bit */
beq+ .L_dsmiss_check_prot_ok
b .L_dmiss_do_prot_exception
.L_dsmiss_check_prot_kern:
mfspr tmp3, dmiss /* check kern privs */
mfsrin tmp3, tmp3
andis. tmp3, tmp3, 0x4000 /* Test SR Ks bit */
bne- .L_dmiss_do_prot_exception
.L_dsmiss_check_prot_ok:
/* Ok, mark as referenced and changed before resolving the fault */
ori tmp1, tmp1, (MASK(PTE1_REFERENCED)|MASK(PTE1_CHANGED))
sth tmp1, 6(tmp2)
b .L_dsmiss_resolved
/*
* Instruction address breakpoint
*/
. = 0x1300
.L_handler1300:
mtsprg 3, r3
li r3, T_INSTRUCTION_BKPT
b .L_exception_entry
/*
* System management interrupt
*/
. = 0x1400
.L_handler1400:
mtsprg 3, r3
li r3, T_SYSTEM_MANAGEMENT
b .L_exception_entry
/*
* There is now a large gap of reserved traps
*/
/*
* Run mode/ trace exception - single stepping on 601 processors
*/
. = 0x2000
.L_handler2000:
mtsprg 3, r3
li r3, T_RUNMODE_TRACE
b .L_exception_entry
/*
* .L_exception_entry(type)
*
* This is the common exception handling routine called by any
* type of system exception.
*
* ENTRY: via a system exception handler, thus interrupts off, VM off.
* r3 has been saved in sprg3 and now contains a number
* representing the exception's origins
*
* EXIT: srr0 and srr1 saved in per_proc_info structure
* r3 (supplied) saved in per_proc_info structure
* cr saved in per_proc_info structure
* original r1-3 saved in sprg1-3.
* r1 - is scratch
* r2 - byte offset into per_proc_info of our CPU (SMP only)
* r3 -contains exception info as for entry
*
* The exception's handler is entered with
* VM on, interrupts still switched off
*/
#define PRINT1H(XX, nn) \
0: lbz r2,0(r1) ; \
andi. r2,r2,0x04 ; \
beq 00b ; \
XX ; \
srwi r2,r2,nn ; \
andi. r2,r2,0x0F ; \
cmpi 0,r2,0x0A ; \
blt 1f ; \
subi r2,r2,0x0A ; \
addi r2,r2,'A'-'0' ; \
1: addi r2,r2,'0' ; \
stb r2,4(r1) ;
#define PRINT1H_(XX) \
0: lbz r2,0(r1) ; \
andi. r2,r2,0x04 ; \
beq 00b ; \
XX ; \
andi. r2,r2,0x0F ; \
cmpi 0,r2,0x0A ; \
blt 1f ; \
subi r2,r2,0x0A ; \
addi r2,r2,'A'-'0' ; \
1: addi r2,r2,'0' ; \
stb r2,4(r1) ;
#define PRINT8H(XX) \
PRINT1H(XX, 28) \
PRINT1H(XX, 24) \
PRINT1H(XX, 20) \
PRINT1H(XX, 16) \
PRINT1H(XX, 12) \
PRINT1H(XX, 8) \
PRINT1H(XX, 4) \
PRINT1H_(XX)
#define PRINT1C(XX) \
0: lbz r2,0(r1) ; \
andi. r2,r2,0x04 ; \
beq 00b ; \
li r2,XX ; \
stb r2,4(r1) ;
.data
.align ALIGN
.type EXT(exception_entry),@object
.size EXT(exception_entry), 4
.globl EXT(exception_entry)
EXT(exception_entry):
.long .L_exception_entry-_ExceptionVectorsStart /* phys addr of fn */
.text
.L_exception_entry:
/* r3 has been saved in sprg3 and now contains exception info */
mtsprg 2, r2 /* Setup some work registers */
mtsprg 1, r1
#if NCPUS == 1
/* Original code which just does the minimal amount of things */
/* Save SRR0 and SRR1 plus cr and r3 into PER_PROC structure */
mfsprg r2, 0 /* sprg0 = phys addr of per-proc */
stw r3, PP_SAVE_EXCEPTION_TYPE(r2)
mfsrr0 r1
stw r1, PP_SAVE_SRR0(r2)
mfsrr1 r1
stw r1, PP_SAVE_SRR1(r2)
mfcr r1
stw r1, PP_SAVE_CR(r2)
mfctr r1
stw r1, PP_SAVE_CTR(r2)
#else /* NCPUS == 1 */
/*
*
* Here we will save off a mess of registers, the special ones and R0-R12. We use the DCBZ
* instruction to clear and allcoate a line in the cache. This way we won't take any cache
* misses, so these stores won't take all that long.
*
* One thing though, this is not standard throughout the rest of Mach, so we need to put the
* registers back to what was assumed. Really, though we should go downstream and make some
* changes. Ahhh, for the want of some time...
*
* All this extra register saving was put in for SMP and to facilitate an exception trace.
*
*/
/*
* Warning: OBAR code follows
* (Optimized Beyond All Recognition)
*/
mfsprg r2, 0 /* sprg0 = phys addr of per-proc */
li r1,3*32 /* Displacement to fourth hunk of register saves */
stw r3, PP_SAVE_EXCEPTION_TYPE(r2) /* Save the exception type code */
dcbz r1,r2 /* Clear third part of save area for performance sake */
li r1,2*32 /* Displacement to third hunk of register saves */
stw r0,PP_SAVE_R0(r2) /* Save off R0 */
mr r0,r3 /* Save the interrupt code */
mfsrr0 r3 /* Get SRR0 */
dcbz r1,r2 /* Clear second part */
lis r1,KERNEL_SEG_REG0_VALUE@h /* Get the high half of the kernel SR0 value */
stw r4,PP_SAVE_R4(r2) /* Save off R4 */
ori r1,r1,KERNEL_SEG_REG0_VALUE@l /* Slam in the low half of SR0 */
mfsr r4,0 /* Get the interrupt time SR0 */
stw r5,PP_SAVE_R5(r2) /* Save off R5 */
mtsr 0,r1 /* Set SR0 */
stw r4,PP_SAVE_SR0(r2) /* Save the interrupt time SR0 */
mfsprg r4,1 /* Get back register 1 */
stw r6,PP_SAVE_R6(r2) /* Save off R6 */
mfsprg r5,2 /* Get back R2 */
li r1,4*32 /* Displacement to fifth line */
stw r7,PP_SAVE_R7(r2) /* Save off R7 */
dcbz r1,r2 /* Clear fourth part */
stw r4,PP_SAVE_R1(r2) /* Save off R1 */
mfsprg r7,3 /* Get back R3 */
stw r5,PP_SAVE_R2(r2) /* Save off R2 */
mfsrr1 r1 /* Get SRR1 */
stw r7,PP_SAVE_R3(r2) /* Save off R3 */
stw r3, PP_SAVE_SRR0(r2) /* Save SRR0 */
mfcr r4 /* Get the CR */
stw r1, PP_SAVE_SRR1(r2) /* Save SRR1 */
mflr r3 /* Get the LR */
stw r4,PP_SAVE_CR(r2) /* Save off the CR */
mfctr r1 /* Get the CTR */
stw r3,PP_SAVE_LR(r2) /* Save off the LR */
mfdsisr r4 /* Get the DSISR */
stw r1,PP_SAVE_CTR(r2) /* Save off the CTR */
mfdar r3 /* Get the DAR */
stw r4,PP_SAVE_DSISR(r2) /* Save off the DSISR */
mfxer r1 /* Get the XER */
stw r8,PP_SAVE_R8(r2) /* Save off R8 */
lis r4,MPspec@h /* Get the MP control block */
stw r3,PP_SAVE_DAR(r2) /* Save the DAR */
ori r4,r4,MPspec@l /* Get the bottom half of the MP control block */
stw r1,PP_SAVE_XER(r2) /* Save off the XER */
lwz r4,MPSSIGPhandler(r4) /* Get the address of the SIGP interrupt filter */
mr r7,r0 /* Move the interrupt code */
stw r9,PP_SAVE_R9(r2) /* Save off R9 */
cmplwi cr0,r7,T_INTERRUPT /* Do we have an external interrupt? */
stw r10,PP_SAVE_R10(r2) /* Save off R10 */
cmplwi cr1,r4,0 /* Check if signal filter is initialized yet */
stw r11,PP_SAVE_R11(r2) /* Save off R11 */
lhz r3,PP_CPU_FLAGS(r2) /* Get the CPU flags */
stw r12,PP_SAVE_R12(r2) /* Save off R12 */
bne+ chekIfTrc /* This is not an external 'rupt, can't be a signal... */
andi. r3,r3,SIGPactive@l /* See if this processor has started up */
beq- cr1,chekIfTrc /* We don't have a filter yet... */
beq- chekIfTrc /* This processor hasn't started filtering yet... */
mtlr r4 /* Load up filter address */
blrl /* Filter the interrupt */
/*
* Remember that the filter would have gone translation on so it could
* read some hardware, so make sure the DSISR and DAR are restored.
* But, and it's a big but: Bertha Butts--there had better never, never, NEVER, be a DSI, 'cause
* the per processor block could be trashed.
*/
mfsprg r2,0 /* Make sure we have the per processor block */
cmplwi cr0,r3,kMPIOInterruptPending /* See what the filter says */
lwz r5,PP_SAVE_LR(r2) /* Get back the LR */
li r7,T_INTERRUPT /* Assume we have a regular external 'rupt */
lwz r12,PP_SAVE_R12(r2) /* Get back R12 */
beq+ modRupt /* Yeah, we figured it would be... */
li r7,T_SIGP /* Assume we had a signal processor interrupt */
bgt+ modRupt /* Yeah, at this point we would assume so... */
li r7,T_IN_VAIN /* Nothing there actually, so eat it */
modRupt: lwz r9,PP_SAVE_DAR(r2) /* Get back the damn DAR */
lwz r11,PP_SAVE_R11(r2) /* Get back R11 */
stw r7,PP_SAVE_EXCEPTION_TYPE(r2) /* Set that it was either in vain or a SIGP */
mtdar r9 /* Restore the DAR 'cause it's expected later */
lwz r8,PP_SAVE_DSISR(r2) /* Get back the DSISR */
mtlr r5 /* Restore the LR to entry conditions */
lwz r10,PP_SAVE_R10(r2) /* Restore R10 */
mtdsisr r8 /* Restore the DSISR */
lwz r9,PP_SAVE_R9(r2) /* Restore R9 */
lwz r8,PP_SAVE_R8(r2) /* Restore R8 */
/*
* Note that some of the trace flags do double duty; there are more than 32, so shift will wrap,
* e.g., T_IN_VAIN is also T_RUNMODE_TRACE, T_RESET is T_SIGP.
*/
chekIfTrc: rlwinm r0,r7,30,0,31 /* Save 'rupt code shifted right 2 */
lwz r3,traceMask-_ExceptionVectorsStart(r0) /* Get the trace mask */
rlwnm. r0,r3,r0,0,0 /* Set CR0_EQ to 0 if tracing allowed */
lwz r0,PP_SAVE_R0(r2) /* Restore register 0 */
/*
* Let's trace this exception. We'll make sure that no other processors
* steal our entry. We'll slap in a processor ID, a timestamp, type of exception,
* and a whole flock of registers.
*
* We need to fix this 'cause we spend about a zillion cycles and a zillion
* cache accesses to build the trace entry. We should weave this code into the
* register save stuff done above. It would cut the whole thing in half.
* But, I don't wanna... Yet...
*
* So there...
*/
beq- cr0,skipTrace /* Don't want to trace this kind... */
li r6,traceCurr-_ExceptionVectorsStart /* Point to the trace current */
lwz r4,traceEnd-_ExceptionVectorsStart(r0) /* Get the end pointer */
getTrcEnt: lwarx r3,0,r6 /* Get the current entry */
addi r5,r3,LTR_size /* Point to the next slot */
cmplw r4,r5 /* Are we at the last one? */
bne+ tryTrcEnt /* Nope... */
lwz r5,traceStart-_ExceptionVectorsStart(r0) /* Wrap to the first one */
tryTrcEnt: stwcx. r5,0,r6 /* See if someone else got this one */
bne- getTrcEnt /* Yeah, damn... */
dcbz 0,r3 /* Clear the first part first */
li r1,32 /* Second line of entry */
getTB: mftbu r4 /* Get the upper timebase */
mftb r5 /* Get the lower timebase */
mftbu r6 /* Get the upper one again */
cmplw r4,r6 /* Did the top tick? */
bne- getTB /* Yeah, need to get it again... */
dcbz r1,r3 /* Zap the second half */
stw r4,LTR_timeHi(r3) /* Set the upper part of TB */
mfspr r1,pir /* Get the processor address */
stw r5,LTR_timeLo(r3) /* Set the lower part of TB */
rlwinm r1,r1,0,27,31 /* Cut the crap */
mfsprg r4,1 /* Get 'rupt time R1 */
sth r1,LTR_cpu(r3) /* Stash the cpu address */
stw r0,LTR_r0(r3) /* Save off register 0 */
mfsprg r5,2 /* Get 'rupt time R2 */
stw r4,LTR_r1(r3) /* Save R1 value */
mfsprg r1,3 /* Get back 'rupt time R3 */
stw r5,LTR_r2(r3) /* Save R2 */
stw r1,LTR_r3(r3) /* Save R3 */
lwz r1,PP_SAVE_CR(r2) /* Get the CR */
lwz r5,PP_SAVE_SRR0(r2) /* Get the SSR0 */
lwz r6,PP_SAVE_SRR1(r2) /* Get the SRR1 */
stw r1,LTR_cr(r3) /* Save the CR */
stw r5,LTR_srr0(r3) /* Save the SSR0 */
stw r6,LTR_srr1(r3) /* Save the SRR1 */
lwz r4,PP_SAVE_DAR(r2) /* Get the DAR */
lwz r5,PP_SAVE_DSISR(r2) /* Get the DSISR */
mflr r6 /* Get the LR */
stw r4,LTR_dar(r3) /* Save the CR */
mfctr r4 /* Get the CTR */
stw r5,LTR_dsisr(r3) /* Save the SSR0 */
stw r6,LTR_lr(r3) /* Save the LR */
lwz r5,PP_SAVE_R5(r2) /* Get register 5 back */
stw r4,LTR_ctr(r3) /* Save off the CTR */
sth r7,LTR_excpt(r3) /* Save the exception type */
lwz r4,PP_SAVE_R4(r2) /* Restore R4 */
stw r5,LTR_r5(r3) /* Save this one too */
stw r4,LTR_r4(r3) /* Save the register */
b skipLoads /* Skip some unneeded loads... */
skipTrace: lwz r4,PP_SAVE_R4(r2) /* Restore R4 */
lwz r5,PP_SAVE_R5(r2) /* Restore R5 */
skipLoads: mtcrf 0x80,r0 /* Set our CR0 to the high nybble of the request code */
rlwinm r6,r0,1,0,31 /* Move sign bit to the end */
cmplwi cr1,r7,T_SYSTEM_CALL /* Did we get a system call? */
crandc cr0_lt,cr0_lt,cr0_gt /* See if we have R0 equal to 0b10xx...x */
cmplwi cr3,r7,T_IN_VAIN /* Was this all in vain? All for nothing? */
cmplwi cr2,r6,1 /* See if original R0 had the CutTrace request code in it */
cmplwi cr4,r7,T_SIGP /* Indicate if we had a SIGP 'rupt */
beq- cr3,forNaught /* Interrupt was all for nothing... */
bne+ cr1,noCutT /* Not a system call... */
bnl+ cr0,noCutT /* R0 not 0b10xxx...x, can't be any kind of magical system call... */
beq+ cr2,isCutTrace /* This is a CutTrace system call */
lis r1,FirmwareCall@h /* Top half of firmware call handler */
lis r3,KERNELBASE_TEXT_OFFSET@h /* Top half of virtual-to-physical conversion */
ori r1,r1,FirmwareCall@l /* Bottom half of it */
ori r3,r3,KERNELBASE_TEXT_OFFSET@l /* Low half of virtual-to-physical conversion */
sub r1,r1,r3 /* Get the physical address of the firmware call handler routine */
lwz r3,PP_SAVE_R3(r2) /* Restore the first parameter, the rest are ok already */
mtlr r1 /* Get it in the link register */
blrl /* Call the handler */
/* We will return here only if the firmware can't handle the call. The FW needs to restore any registers */
/* it uses. CR, LR, R1, R2, R6, R7 and R3, forget about 'em, we'll handle them. */
mfsprg r2,0 /* Restore the per_processor area */
lwz r7,PP_SAVE_LR(r2) /* Restore the entry LR */
lwz r1,PP_SAVE_CR(r2) /* Restore the entry CR */
mtlr r7 /* Move the old LR back */
lwz r3,PP_SAVE_EXCEPTION_TYPE(r2) /* Get back the exception type */
b noSIGP /* Go to the normal system call handler */
/* Note that we haven't turned on translation, so SRR0 and SRR1 shouldn't have been able to change */
isCutTrace: li r7,-32768 /* Get a 0x8000 for the exception code */
sth r7,LTR_excpt(r3) /* Modify the exception type to a CutTrace */
forNaught: lwz r3,PP_SAVE_CR(r2) /* Get the CR back */
lwz r6,PP_SAVE_R6(r2) /* Restore R6 */
lwz r7,PP_SAVE_SR0(r2) /* Get the interrupt time SR0 */
mtcr r3 /* Restore the CR */
mfsprg r1,1 /* Restore the original R1 */
mtsr 0,r7
mfsprg r3,3 /* Restore original R3 */
lwz r7,PP_SAVE_R7(r2) /* Restore R7 */
mfsprg r2,2 /* Restore original R2 */
rfi /* Return to the guy what called the tracer */
.long 0 /* These are for the ancient 601ism */
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
/* We are here 'cause we didn't have a CutTrace system call */
noCutT: mr r3,r7 /* Get the exception type back */
bne+ cr4,noSIGP /* Skip away if we didn't get a SIGP... */
lis r6,MPsignalFW@h /* Top half of SIGP handler */
lis r7,KERNELBASE_TEXT_OFFSET@h /* Top half of virtual-to-physical conversion */
ori r6,r6,MPsignalFW@l /* Bottom half of it */
ori r7,r7,KERNELBASE_TEXT_OFFSET@l /* Low half of virtual-to-physical conversion */
sub r6,r6,r7 /* Get the physical address of the SIGP handler routine */
mtlr r6 /* Get it in the link register */
blrl /* Call the handler - we'll only come back if this is an AST, */
/* 'cause FW can't handle that */
cmplwi cr0,r3,T_IN_VAIN /* Did the signal handler eat the signal? */
lwz r7,PP_SAVE_LR(r2) /* Restore the entry LR */
lwz r1,PP_SAVE_CR(r2) /* Restore the entry CR */
mtlr r7 /* Restore the LR */
beq cr0,forNaught /* Bail now if the signal handler processed the signal... */
noSIGP: lwz r6,PP_SAVE_R6(r2) /* Restore R6 */
lwz r7,PP_SAVE_R7(r2) /* Restore original R7 */
mtcr r1 /* Restore original CR */
#endif /* NCPUS > 1 */
/* jump into main handler code switching on VM at the same time */
/* We assume kernel data is mapped contiguously in physical
* memory, otherwise we'd need to switch on (at least) virtual data.
*/
lis r1, (KERNEL_SEG_REG0_VALUE >> 16) /* Get the top of the SR1 value */
#if NCPUS == 1
mtsr 0, r1
#else /* NCPUS == 1 */
/* sr0 was already initialised in all the MP code above */
#endif /* NCPUS == 1 */
ori r1, r1, 1 /* Set the bottom part up for SR1 */
mtsr 1, r1 /* Slam it on in */
addi r1, r1, 1 /* Ditto for SR2 */
mtsr 2, r1
lwz r1, PP_PHYS_EXCEPTION_HANDLERS(r2)
lwzx r1, r1, r3
#define BCTR_CODE_ENABLED 0
#if BCTR_CODE_ENABLED
mtctr r1
lwz r2, PP_VIRT_PER_PROC(r2)
/* Switch on virtual addressing, I+D */
li r1, MSR_SUPERVISOR_INT_OFF
mtmsr r1
isync
bctr /* into the exception handler with VM on */
#else /* BCTR_CODE_ENABLED */
mtsrr0 r1
li r1, MSR_SUPERVISOR_INT_OFF
lwz r2, PP_VIRT_PER_PROC(r2)
mtsrr1 r1
rfi /* into the exception handler with VM on */
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
#endif /* BCTR_CODE_ENABLED */
/*
* exception_exit(sr0,srr0,srr1)
*
* This is the trampoline code used when exiting into a foreign
* address space.
*
* NMGS TODO - can't we skip exception_exit by relying on translations
* NMGS TODO even after we've loaded sr0? Docs aren't clear. Using
* NMGS TODO 1-1 kernel text mapping would definately avoid this need.
*
* ENTRY : entry via rfi, MSR = MSR_VM_OFF
* r1-3 saved in sprg1-3
* r1 = user's sr0 - used to construct sr1 too
* r2 = user's srr0 (instruction pointer)
* r3 = user's srr1 (msr)
*
* EXIT : this routine restores the users' space and rfis.
*/
.data
.align ALIGN
.type EXT(exception_exit),@object
.size EXT(exception_exit), 4
.globl EXT(exception_exit)
EXT(exception_exit):
.long exception_exit_fn-_ExceptionVectorsStart /* phys addr of fn */
.text
exception_exit_fn:
mtsrr0 r2
mtsrr1 r3
mtsr 0, r1 /* Restore user space SR0 */
addi r1, r1, 1
mtsr 1, r1 /* Restore user space SR1 */
addi r1, r1, 1
mtsr 2, r1 /* Restore user space SR2 */
mfsprg r1, 1
mfsprg r2, 2
mfsprg r3, 3
rfi
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
#if DB_MACHINE_COMMANDS || (NCPUS > 1)
/*
* Start of the trace table
*/
.align 12 /* Align to 4k boundary */
traceTableBeg: /* Start of trace table */
/* .fill 2048,4,0 Make an 8k trace table for now */
#if DB_MACHINE_COMMANDS
.fill 13760,4,0 /* Make an .trace table for now */
#endif
traceTableEnd: /* End of trace table */
#endif /* DB_MACHINE_COMMANDS */
.globl _ExceptionVectorsEnd
.type _ExceptionVectorsEnd,@function
_ExceptionVectorsEnd: /* Used if relocating the exception vectors */
.data
.align ALIGN
.type EXT(exception_end),@object
.size EXT(exception_end), 4
.globl EXT(exception_end)
EXT(exception_end):
.long _ExceptionVectorsEnd - _ExceptionVectorsStart /* phys fn */