#include #include .section .ktext,"ax" LEAF(xcptlow_handler) .set noreorder .set noat /* Note: exceptions do not save and restore registers k0 and k1. * on entry, k1 = exception class. */ /* allocate exception stack frame (on 8-byte boundary) */ subu k1, sp, XCP_SIZE srl k1, 3 /* shift right/left -> alligned on boundary */ sll k1, 3 /* save enough registers to get by */ sw AT, XCP_AT(k1) sw v0, XCP_V0(k1) sw v1, XCP_V1(k1) sw a0, XCP_A0(k1) sw a1, XCP_A1(k1) sw a2, XCP_A2(k1) sw a3, XCP_A3(k1) sw sp, XCP_SP(k1) sw ra, XCP_RA(k1) /* get coprocessor 0 exception state */ mfc0 a0, CP0_CR mfc0 a1, CP0_SR mfc0 a2, CP0_BADDR mfc0 a3, CP0_EPC /* we can safely use AT now */ .set at /* switch to using sp to point at exception frame */ move sp, k1 /* nothing sensible to store for k0/k1, store zero */ sw zero, XCP_K0(sp) sw zero, XCP_K1(sp) /* we are now interruptible: dump all remaining state * into the exception stack frame. */ /* coprocessor exception state */ sw a0, XCP_CR(sp) sw a1, XCP_SR(sp) sw a2, XCP_BADDR(sp) sw a3, XCP_EPC(sp) /* mdhi and mdlo */ mfhi v0 mflo v1 sw v0, XCP_MDHI(sp) sw v1, XCP_MDLO(sp) /* Save all the other general registers. * We save zero, s0-s7 and s8 as well, as instruction emulators (e.g. FP * operations) and debuggers rely on all registers stored together in * well-defined structure. */ sw zero, XCP_ZERO(sp) sw t0, XCP_T0(sp) sw t1, XCP_T1(sp) sw t2, XCP_T2(sp) sw t3, XCP_T3(sp) sw t4, XCP_T4(sp) sw t5, XCP_T5(sp) sw t6, XCP_T6(sp) sw t7, XCP_T7(sp) sw s0, XCP_S0(sp) sw s1, XCP_S1(sp) sw s2, XCP_S2(sp) sw s3, XCP_S3(sp) sw s4, XCP_S4(sp) sw s5, XCP_S5(sp) sw s6, XCP_S6(sp) sw s7, XCP_S7(sp) sw t8, XCP_T8(sp) sw t9, XCP_T9(sp) sw gp, XCP_GP(sp) sw s8, XCP_S8(sp) /* I don't know what the following does. [rb] */ /* load our _gp pointer */ # la gp, _gp /* and call the C exception handler */ move a0, sp # arg1 = &xcp subu sp, 16 # (arg save area) j _xcptcall move ra, zero # fake return address /* This strange call to _xcptcall with zero return address is to * help exception-aware debuggers to trace back over the exception event. * We are basically interposing a bogus stackframe (with a zero return * address) between the C exception handler and the actual machine * exception. */ xcptrest: .set noat add AT, sp, 16 /* at points to exception frame */ xcptrestother: /* restore all state */ /* restore most general registers */ lw t0, XCP_T0(AT) lw t1, XCP_T1(AT) lw t2, XCP_T2(AT) lw t3, XCP_T3(AT) lw t4, XCP_T4(AT) lw t5, XCP_T5(AT) lw t6, XCP_T6(AT) lw t7, XCP_T7(AT) lw s0, XCP_S0(AT) lw s1, XCP_S1(AT) lw s2, XCP_S2(AT) lw s3, XCP_S3(AT) lw s4, XCP_S4(AT) lw s5, XCP_S5(AT) lw s6, XCP_S6(AT) lw s7, XCP_S7(AT) lw t8, XCP_T8(AT) lw t9, XCP_T9(AT) lw gp, XCP_GP(AT) lw s8, XCP_S8(AT) /* mdhi and mdlo */ lw v0, XCP_MDHI(AT) lw v1, XCP_MDLO(AT) mthi v0 mtlo v1 /* remaining general registers */ lw a0, XCP_A0(AT) lw a1, XCP_A1(AT) lw a2, XCP_A2(AT) lw a3, XCP_A3(AT) lw ra, XCP_RA(AT) /* restore the exception-time status register */ .set noreorder lw v0, XCP_SR(AT) nop mtc0 v0, CP0_SR lw v1, XCP_V1(AT) lw v0, XCP_V0(AT) lw sp, XCP_SP(AT) /* we are not uninterruptible and can use k1 safely */ lw k1, XCP_EPC(AT) lw AT, XCP_AT(AT) mtc0 k1, CP0_EPC j k1 rfe .set reorder .set at END(xcptlow_handler) LEAF(_xcptcall) /* on entry: a0 == &xcp */ subu sp, 24 sw ra, 16(sp) /* punt out to _xcpt_deliver */ jal _xcpt_deliver nop lw ra, 16(sp) addu sp, 24 beqz ra, xcptrest nop j ra nop END(_xcptcall)