CPSC-410/611 Operating Systems Exceptions, MIPS-Style • Reminder: – MIPS CPU deals with exceptions. • Interrupts are just a special case of exceptions. – The MIPS Architecture has no interrupt-vector table! • All exceptions trigger a jump to the same location, and de- multiplexing happens in the exception handler, after looking up the reason for the exception in the CAUSE register. exception exception handler specific service routine Exception Handling MIPS-Style (I): Data Structures /*-----------------------------------------------------------------------*/ /* DATA STRUCTURES */ xcptlow_handler /*-----------------------------------------------------------------------*/ typedef enum {Int = 0, Mod = 1, TLBL = 2, TLBS = 3, AdEL = 4, AdES = 5, set up exception frame IBE = 6, DBE = 7, Syscall = 8, Bp = 9, RI = 10, CpU = 11, on stack Ov = 12, TRAP = 13, VCEI = 14, FPE = 15, C2E = 16, Watch = 23, VCED = 31} EXCEPTION_CODE; save enough registers to get by typedef unsigned int reg_t; typedef struct xcptcontext { save rest of registers /* This is the exception context frame that is passed to the exception handlers. It gets filled in by the low-level exception handler in "machine.S". An assembler version of this structure can be found at the call C exception handler bottom of "machine.H".*/ reg_t sr; /* Status Register */ reg_t cr; /* Cause Register */ restore registers reg_t epc; /* PC at time of exception. */ reg_t vaddr; reg_t regs[32]; /* Copy of all general purpose registers */ return from exception reg_t mdlo; /* HI/LO registers (used for memory management) */ reg_t mdhi; reg_t count; /* Timer registers */ reg_t compare; struct xcptcontext * prev; /* To link exceptions. (unused for now) */ unsigned xclass; /* Priority class of this exception. (unused for now). */ } EXCEPTION_CONTEXT; 1
CPSC-410/611 Operating Systems Exception Handling MIPS-Style (II): Set-up Frame xcptlow_handler set up exception frame LEAF(xcptlow_handler) on stack .set noreorder .set noat /* Note: exceptions do not save and restore registers k0 and k1. save enough registers * on entry, k1 = exception class. to get by */ /* save exception class in memory */ save rest of registers la k0, class sw k1, 0(k0) # had better not trap! move k0, zero # now boot exception will abort. call C exception handler /* allocate exception stack frame (on 8-byte boundary) */ subu k1, sp, XCP_SIZE srl k1, 3 /* shift right/left -> alligned on boundary */ restore registers sll k1, 3 return from exception Exception Handling MIPS-Style (III): Save Registers /* save enough registers to get by */ sr AT, XCP_AT(k1) sr v0, XCP_V0(k1) sr v1, XCP_V1(k1) xcptlow_handler sr a0, XCP_A0(k1) sr a1, XCP_A1(k1) set up exception frame sr a2, XCP_A2(k1) sr a3, XCP_A3(k1) on stack sr sp, XCP_SP(k1) sr ra, XCP_RA(k1) save enough registers to get by /* get coprocessor 0 exception state */ mfc0 a0, CPU0_CR mfc0 a1, CPU0_SR save rest of registers mfc0 a2, CPU0_VADDR mfc0 a3, CPU0_EPC /* we can safely use AT now */ call C exception handler .set at /* switch to using sp to point at exception frame */ restore registers move sp, k1 /* we skip saving of watchpoint registers */ return from exception /* stash exception class */ lw v0, class /* nothing sensible to store for k0/k1, store zero */ sr zero, XCP_K0(sp) sr zero, XCP_K1(sp) 2
CPSC-410/611 Operating Systems Exception Handling MIPS-Style (IV): Save More Registers /* we have finished with the uninterruptible code. * (using k0/k1, and saving exception state), so * we can permit nested exceptions ; however, we cannot * permit device interrupts until the interrupt handler * does its prioritization and sets SR_IMASK. xcptlow_handler */ la k0, xcptlow_handler # restore rom boot exception hook set up exception frame and v0, a1, ~(SR_IMASK|SR_EXL|SR_KSU_MASK) on stack mtc0 v0, CPU0_SR .set reorder save enough registers to get by /* we are now interruptible: dump all remaining state * into the exception stack frame. */ save rest of registers /* coprocessor exception state */ sr a0, XCP_CR(sp) call C exception handler sr a1, XCP_SR(sp) sr a2, XCP_VADDR(sp) sr a3, XCP_EPC(sp) restore registers /* timer data */ mfc0 a0, CPU0_COUNT mfc0 a1, CPU0_COMPARE return from exception sr a0, XCP_COUNT(sp) sr a1, XCP_COMPARE(sp) /* mdhi and mdlo */ mfhi v0 mflo v1 sr v0, XCP_MDHI(sp) sr v1, XCP_MDLO(sp) Exception Handling MIPS-Style (V) /* 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 xcptlow_handler * well-defined structure. */ set up exception frame sr zero, XCP_ZERO(sp) sr t0, XCP_T0(sp) on stack … sr t7, XCP_T7(sp) save enough registers sr s0, XCP_S0(sp) to get by … sr s7, XCP_S7(sp) sr t8, XCP_T8(sp) save rest of registers sr t9, XCP_T9(sp) sr gp, XCP_GP(sp) sr s8, XCP_S8(sp) call C exception handler /* I don't know what the following does. [rb] */ /* load our _gp pointer */ la gp, _gp restore registers return from exception 3
CPSC-410/611 Operating Systems Exception Handling MIPS-Style (VI) /* and call the C exception handler */ move a0, sp # arg1 = &xcp subu sp, 16 # (arg save area) move ra, zero # fake return address b _xcptcall xcptlow_handler /* This strange call to _xcptcall with zero return address is to * help exception-aware debuggers to trace back over the exception event. set up exception frame * We are basically interposing a bogus stackframe (with a zero return on stack * address) between the C exception handler and the actual machine * exception. */ save enough registers to get by xcptrest: LEAF(_xcptcall) .set noat /* on entry: a0 == &xcp */ add AT, sp, 16 save rest of registers subu sp, 24 /* at points to exception frame */ sr ra, 16(sp) /* punt out to _xcpt_deliver */ call C exception handler jal _xcpt_deliver lr ra, 16(sp) restore registers addu sp, 24 beqz ra, xcptrest j ra END(_xcptcall) return from exception extern "C" int _xcpt_deliver(struct xcptcontext * _xcp) { /* This function gets called by the low-level exception handler. */ ExceptionDispatcher::dispatch(_xcp); return 0; } /* end of _xcpt_deliver */ Exception Handling MIPS-Style (VII): Restore Registers xcptrestother: /* restore all state */ /* restore most general registers */ lr t0, XCP_T0(AT) xcptlow_handler … lr t7, XCP_T7(AT) set up exception frame lr s0, XCP_S0(AT) on stack … lr s7, XCP_S7(AT) lr t8, XCP_T8(AT) save enough registers lr t9, XCP_T9(AT) to get by lr gp, XCP_GP(AT) lr s8, XCP_S8(AT) save rest of registers /* mdhi and mdlo */ lr v0, XCP_MDHI(AT) lr v1, XCP_MDLO(AT) call C exception handler mthi v0 mtlo v1 /* remaining general registers */ restore registers lr a0, XCP_A0(AT) lr a1, XCP_A1(AT) lr a2, XCP_A2(AT) return from exception lr a3, XCP_A3(AT) lr ra, XCP_RA(AT) /* restore the exception-time status register, which has the * side effect of disabling interrupts. */ .set noreorder lr v0, XCP_SR(AT) 4
CPSC-410/611 Operating Systems Exception Handling MIPS-Style (VIII): Return /* in the following we do some magic address pipeline xcptlow_handler effects when updating the control register. */ #clear SR_IMASK before setting SR_EXL (nasty window) set up exception frame li v1, ~(SR_IMASK|SR_EXL) on stack and v1, v0 mtc0 v1, CPU0_SR or v0, SR_EXL save enough registers nop to get by lr v1, XCP_V1(AT) mtc0 v0, CPU0_SR save rest of registers lr v0, XCP_V0(AT) lr sp, XCP_SP(AT) call C exception handler /* we are not uninterruptible and can use k1 safely */ lr k1, XCP_EPC(AT) lr AT, XCP_AT(AT) restore registers mtc0 k1, CPU0_EPC nop nop return from exception eret nop .set reorder .set at END(xcptlow_handler) Initialize MIPS Low-Level Exception Handler /* Macro to copy exception handler to unchached low memory */ #define XCPTCOPY(offs, start, end) \ li t0, KSEG1_BASE+offs ; \ la t1, start ; \ la t2, end ; \ 1: lw t3, 0(t1) ; \ addu t1, 4 ; \ sw t3, 0(t0) ; \ addu t0, 4 ; \ .text bne t1, t2, 1b .set noat .set noreorder _xcptlowstart: la k1, xcptlow_handler LEAF(_xcptlow_init) j k1 /* disable all interrupts */ _xcptlowend: mfc0 t4, CPU0_SR and t4, ~SR_IE .set reorder mtc0 t4, CPU0_SR .set at XCPTCOPY(0x180, _xcptlowstart, _xcptlowend) lw t0, _ram_based beqz t0, 1f /* using RAM-based handlers, so switch off boot exceptions */ and t4, ~SR_BEV mtc0 t4, CPU0_SR 1: la k0, xcptlow_handler j ra END(_xcptlow_init) 5
Recommend
More recommend