;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Microsoft Research Singularity ;;; ;;; Copyright (c) Microsoft Corporation. All rights reserved. ;;; ;;; This file contains ARM-specific assembly code handling dispatching of interrupts. CODE32 AREA |.text|, CODE, ARM, ALIGN=5 |defining ?g_LoadResetVector@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ| EQU 1 |defining ?g_DispatchVector@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ| EQU 1 |defining ?g_SetModeSp@Class_Microsoft_Singularity_Isal_Isa@@SAXHPAUuintPtr@@@Z| EQU 1 INCLUDE hal.inc ;;; These are exported so we can see, in the debugger, which exception was triggered. EXPORT |DispatchReset| EXPORT |DispatchUndefinedInstruction| EXPORT |DispatchSupervisorCall| EXPORT |DispatchPrefetchAbort| EXPORT |DispatchDataAbort| EXPORT |DispatchUnused| EXPORT |DispatchIrq| EXPORT |DispatchFiq| EXPORT |VectorReset| EXPORT |VectorUndefinedInstruction| EXPORT |VectorSupervisorCall| EXPORT |VectorPrefetchAbort| EXPORT |VectorDataAbort| EXPORT |VectorUnused| EXPORT |VectorIrq| EXPORT |VectorFiq| ;;; ;;; "public: static void __cdecl Class_Microsoft_Singularity_Isal_Isa::g_LoadIdt(void)" ;;; Loads the address of the exception vectors into the VBAR. ;;; LEAF_ENTRY ?g_LoadResetVector@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ ;; Load the address of the 0 interrupt vector into r0. adr r0, VectorReset ;; Load R0 into VBAR. Note: This code is ARMv7 specific. mcr p15,0,r0,c12,c0,0 bx lr LEAF_END ;;; SetModeSp sets the stack pointer in the target mode to the value passed in. LEAF_ENTRY ?g_SetModeSp@Class_Microsoft_Singularity_Isal_Isa@@SAXHPAUuintPtr@@@Z ;; r0 arg contains the target mode ;; r1 arg contains the value for sp mrs r2, cpsr bic r3, r2, #Struct_Microsoft_Singularity_Isal_Arm_ProcessorMode_Mask orr r3, r3, r0 msr cpsr_c, r3 mov sp, r1 msr cpsr_c, r2 bx lr LEAF_END LTORG ;;; VectorX indicates a branch instruction in the exception vector ;;; Note that these 8 1-instruction functions must be laid out in exactly ;;; this order as this *IS* the hardware exception vector on ARM. LEAF_ENTRY VectorReset ldr pc, [pc, #24] ; b |DispatchReset| LEAF_END LEAF_ENTRY VectorUndefinedInstruction ldr pc, [pc, #24] ;b |DispatchUndefinedInstruction| LEAF_END LEAF_ENTRY VectorSupervisorCall ldr pc, [pc, #24] ;b |DispatchSupervisorCall| LEAF_END LEAF_ENTRY VectorPrefetchAbort ldr pc, [pc, #24] ;b |DispatchPrefetchAbort| LEAF_END LEAF_ENTRY VectorDataAbort ldr pc, [pc, #24] ;b |DispatchDataAbort| LEAF_END LEAF_ENTRY VectorUnused ldr pc, [pc, #24] ;b |DispatchUnused| LEAF_END LEAF_ENTRY VectorIrq ldr pc, [pc, #24] ;b |DispatchIrq| LEAF_END LEAF_ENTRY VectorFiq ldr pc, [pc, #24] ;b |DispatchFiq| LEAF_END BeginRedirections DCD |DispatchReset| DCD |DispatchUndefinedInstruction| DCD |DispatchSupervisorCall| DCD |DispatchPrefetchAbort| DCD |DispatchDataAbort| DCD |DispatchUnused| DCD |DispatchIrq| DCD |DispatchFiq| EndOfRedirections ;;; Interrupt dispatching on ARM occurs at fixed memory locations (either 0-based or ;;; FFFF0000-based). For now we use the zero base exclusively. ;;; ARM has 7 interrupt categories. Currently we use a simplified model, which ;;; matches other architectures (i.e. we move to a single per-processor interrupt ;;; stack for all interrupt handling.) ;;; ;;; Modes: ;;; IRQ: this is normal device interrupt we expect to get. ;;; Abort: both types of aborts correspond to normal software exceptions, ;;; (which are currently just forwarded to the debugger) ;;; All other interrupts are assumed to be anomalous, and are also passed to the debugger. ;;; FIQ: we don't want to write special interrupt handlers currently, so this ;;; mode has no benefit ;;; SWI, Reset: no cases of these yet. ;;; ;;; We do not execute substantial amounts of code in IRQ mode. This is because ;;; a nested IRQ interrupt would trash our lr register. (We don't take nested IRQ's ;;; now, but likely will eventually.) ;;; The GO_DISPATCH macro creates an InterruptContext at the top of the interrupted ;;; stack and save R0-R3 and the exception vector number into it (takes as an argument). ;;; GO_DISPATCH branches to |GoDispatchPhase2| which saves the rest of the caller-saved ;;; registers and switches to system mode. If $save is true, the faulting instruction ;;; opcode is saved as well. ;;; Note that this code requires the interrupt mode sp to point to a DispatchStack structure. MACRO GO_DISPATCH $vector, $save ;; Sp in interrupt mode will always point at a per-cpu DispatchStack structure ;; Free R4 and fetch interrupted SP. stmia sp, {r4,sp}^ ; Stored to Struct_Microsoft_Isal_Arm_DispatchStack ;; Load address of interrupt context from interrupted SP. ldr r4, [sp, #Struct_Microsoft_Singularity_Isal_Arm_DispatchStack___sp] sub r4, r4, #Struct_Microsoft_Singularity_Isal_InterruptContext___SIZE ;; Save the unbanked user volatile regs (this gets us some scratch regs) stmia r4, {r0-r3,r12} ;; Save vector ldr r0, =$vector str r0, [r4, #Struct_Microsoft_Singularity_Isal_InterruptContext___vector] ;; Save interrupted PC. sub r0, lr, #4 str r0, [r4, #Struct_Microsoft_Singularity_Isal_InterruptContext___pc] ;; Save faulting instruction (from the pc in r0) IF $save ldr r0, [r0] ELSE ldr r0, =0 ENDIF str r0, [r4, #Struct_Microsoft_Singularity_Isal_InterruptContext___instruction] ;; Continue in share code (with r4 == InterruptContext) b |GoModePhase2| MEND ;;; |GoModePhase2| PROC ;; Save the banked interrupted registers (use R3 as pointer to banked registers). add r3, r4, #Struct_Microsoft_Singularity_Isal_InterruptContext___sp stmia r3, {sp,lr}^ ;; Save interrupted CPSR. mrs r0, spsr str r0, [r4, #Struct_Microsoft_Singularity_Isal_InterruptContext___cpsr] ;; Move InterruptContext pointer to r3 and restore r4 from DispatchStack. mov r3, r4 ldr r4, [sp, #Struct_Microsoft_Singularity_Isal_Arm_DispatchStack___r4] ;; Switch to system mode mrs r0, cpsr bic r0, r0, #Struct_Microsoft_Singularity_Isal_Arm_ProcessorMode_Mask orr r0, r0, #Struct_Microsoft_Singularity_Isal_Arm_ProcessorMode_System msr cpsr_c, r0 ;; Restore SP to top of the InterruptContext (i.e. old sp - sizeof(InterruptContext)). mov sp, r3 ;; Jump to common dispatch routine. (with r3 = InterruptContext) b |?g_DispatchVector@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ| ENDP ;;; DispatchX is the dispatch routine for vector X. This code is largely identical ;;; for all the vectors, except for the ID constant which is pushed in the frame ;;; TBD: sharing more code would be a good thing, but it's tough to find a place to stash ;;; the interrupt id. LEAF_ENTRY DispatchReset GO_DISPATCH Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_Reset,{false} LEAF_END LEAF_ENTRY DispatchUndefinedInstruction GO_DISPATCH Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_UndefinedInstruction,{true} LEAF_END LEAF_ENTRY DispatchSupervisorCall GO_DISPATCH Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_SoftwareInterrupt,{true} LEAF_END LEAF_ENTRY DispatchPrefetchAbort GO_DISPATCH Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_PrefetchAbort,{false} LEAF_END LEAF_ENTRY DispatchDataAbort GO_DISPATCH Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_DataAbort,{true} LEAF_END LEAF_ENTRY DispatchUnused GO_DISPATCH Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_Unused,{false} LEAF_END LEAF_ENTRY DispatchIrq GO_DISPATCH Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_Irq,{false} LEAF_END LEAF_ENTRY DispatchFiq GO_DISPATCH Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_Fiq,{false} LEAF_END |DispatchTableEnd| ;;; DispatchVector implements the meat of the low level interrupt dispatching logic. Its goal ;;; is to implement the transition to high level code with minimal overhead. |?g_DispatchVector@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ| PROC ;; Pick the correct spill context based on the interrupt vector. ;; Our normal scheduler-capable interrupts are IRQ's. Other exceptions ;; are just reported to the debugger for now. ;; ;; TODO: Note that we are not implementing a "no scheduler switch" category ;; of interrupts for now, so we won't be able to handle nested interrupts. This will ;; be easy to change if we can figure out the right criteria to identify such interrupts ;; at this level later on. ;; ;; ldr r0, [sp, #Struct_Microsoft_Singularity_Isal_InterruptContext___vector] cmp r0, #Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_Irq bne |debug| ;; Use current thread's spill context GET_THREAD_FIELD_ADDR r0, r12, #Struct_Microsoft_Singularity_Isal_ThreadRecord___spill b |save| |debug| ;; Use cpu's spill context GET_CPU_FIELD_ADDR r0, r12, #Struct_Microsoft_Singularity_Isal_CpuRecord___spill |save| ;; Save to spill context mov r1, sp bl |?m_Save@Struct_Microsoft_Singularity_Isal_SpillContext@@SA_NPAU1@PAUStruct_Microsoft_Singularity_Isal_InterruptContext@@@Z| ;; Save previous stack limit GET_THREAD_FIELD_ADDR r0, r12, #Struct_Microsoft_Singularity_Isal_ThreadRecord___activeStackLimit ldr r0, [r0] PUSH r0 ;; See if we are already on the interrupt stack GET_CPU_FIELD_ADDR r1, r12, #Struct_Microsoft_Singularity_Isal_CpuRecord___interruptStackLimit ldr r1, [r1] cmp r1, r0 ;; Stash current sp mov r0, sp beq |no_switch| ;; Now switch to the interrupt stack GET_CPU_FIELD_ADDR r2, r12, #Struct_Microsoft_Singularity_Isal_CpuRecord___interruptStackBegin ldr sp, [r2] GET_THREAD_FIELD_ADDR r2, r12, #Struct_Microsoft_Singularity_Isal_ThreadRecord___activeStackLimit str r1, [r2] |no_switch| ;; Save the old stack pointer PUSH r0 ;; Interrupt context is old stack pointer + 4 add r0, r0, #4 ;; Call dispatch routine bl |?g_DispatchInterrupt@Class_Microsoft_Singularity_Isal_Isa@@SAXPAUStruct_Microsoft_Singularity_Isal_InterruptContext@@@Z| ;; Restore the old stack pointer and limit POP sp POP r0 GET_THREAD_FIELD_ADDR r1, r12, #Struct_Microsoft_Singularity_Isal_ThreadRecord___activeStackLimit str r0, [r1] ;; Now we need to switch back to interrupt mode so we can do a proper atomic resume (using spsr) mov r1, sp mrs r0, cpsr bic r0, r0, #Struct_Microsoft_Singularity_Isal_Arm_ProcessorMode_Mask orr r0, r0, #Struct_Microsoft_Singularity_Isal_Arm_ProcessorMode_Supervisor msr cpsr_c, r0 ;; Move saved CPSR into SPSR ldr r0, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___cpsr] msr spsr, r0 ;; Restore banked pre-interrupt registers (use R3 as pointer to banked registers). add r3, r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___sp ldmia r3, {sp,lr}^ ;; Resume unbanked pre-interrupt state ldmfd r1, {r0-r3,r12,pc}^ ENDP END