;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Microsoft Research Singularity ;;; ;;; Copyright (c) Microsoft Corporation. All rights reserved. ;;; ;;; This file contains x86-specific assembly code handling dispatching of interrupts. include hal.inc ;;; IdtDispatchers are the dispatch functions used for populating the Idt. Each one ;;; pushes its interrupt id, along with a dummy error code if necessary to normalize ;;; the stack, and jumps to the central dispatch routine DISPATCH_CLEAN MACRO num align SIZEOF Struct_Microsoft_Singularity_Isal_IX_InterruptTable_Dispatcher push 0 ; No error push num jmp SYMFIX(?g_DispatchVector@Class_Microsoft_Singularity_Isal_Isa@@SIXXZ) ENDM DISPATCH_ERR MACRO num align SIZEOF Struct_Microsoft_Singularity_Isal_IX_InterruptTable_Dispatcher push num jmp SYMFIX(?g_DispatchVector@Class_Microsoft_Singularity_Isal_Isa@@SIXXZ) ENDM _IdtDispatchers proc DISPATCH_CLEAN 000h ; #DE Divide-by-Zero DISPATCH_CLEAN 001h ; #DB Debug Exception DISPATCH_CLEAN 002h ; NMI Non-Maskable-Interrupt DISPATCH_CLEAN 003h ; #BP Breakpoint DISPATCH_CLEAN 004h ; #OF OVerflow DISPATCH_CLEAN 005h ; #BR Bound-Range DISPATCH_CLEAN 006h ; #UD Invalid Opcode DISPATCH_CLEAN 007h ; #NM Device Not Available DISPATCH_ERR 008h ; #DF Double Fault DISPATCH_CLEAN 009h ; Unused (was x87 segment except) DISPATCH_ERR 00ah ; #TS Invalid TSS DISPATCH_ERR 00bh ; #NP Sgement Not Present DISPATCH_ERR 00ch ; #SS Stack Exception DISPATCH_ERR 00dh ; #GP General Protection DISPATCH_ERR 00eh ; #PF Page Fault DISPATCH_CLEAN 00fh ; Reserved DISPATCH_CLEAN 010h ; #MF x87 Math Error DISPATCH_ERR 011h ; #AC Alignment Check DISPATCH_CLEAN 012h ; #MC Machine Check DISPATCH_CLEAN 013h ; #XF SIMD Exception DISPATCH_CLEAN 014h ; 014h exception DISPATCH_CLEAN 015h ; 015h exception DISPATCH_CLEAN 016h ; 016h exception DISPATCH_CLEAN 017h ; 017h exception DISPATCH_CLEAN 018h ; 018h exception DISPATCH_CLEAN 019h ; 019h exception DISPATCH_CLEAN 01ah ; 01ah exception DISPATCH_CLEAN 01bh ; 01bh exception DISPATCH_CLEAN 01ch ; 01ch exception DISPATCH_CLEAN 01dh ; 01dh exception DISPATCH_CLEAN 01eh ; 01eh exception DISPATCH_CLEAN 01fh ; 01fh exception DISPATCH_CLEAN 020h ; 021h: first interrupt _num = 021h ; 021h to 0ffh WHILE _num LE 0ffh DISPATCH_CLEAN _num _num = _num + 1 ENDM _IdtDispatchers endp ;;; GetDispatchers returns the address of the IdtDispatchers array. This ;;; is necessary since we can't directly take the address of a function in managed code. SYMFIX(?g_GetDispatchers@Struct_Microsoft_Singularity_Isal_IX_InterruptTable@@SIPAUStruct_Microsoft_Singularity_Isal_IX_InterruptTable_Dispatcher@@XZ) proc lea eax, _IdtDispatchers ret SYMFIX(?g_GetDispatchers@Struct_Microsoft_Singularity_Isal_IX_InterruptTable@@SIPAUStruct_Microsoft_Singularity_Isal_IX_InterruptTable_Dispatcher@@XZ) endp ;;; LoadDispatchTable should be called on each processor to initialize its idt register to the ;;; shared dispatch table. SYMFIX(?g_LoadIdt@Class_Microsoft_Singularity_Isal_Isa@@SIXXZ) proc lidt fword ptr _Idtr ret SYMFIX(?g_LoadIdt@Class_Microsoft_Singularity_Isal_Isa@@SIXXZ) endp ;;; 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. ;;; ;;; There are essentially two tasks: save the context (if appropriate), and switch to the ;;; interrupt processing stack. ;;; ;;; This function either returns via iretd, or else doesn't return at all (because the ;;; handler called RestoreContext.) ;;; ;;; On entry to DispatchVector, the stack looks like this: ;;; ;;; [TOP] (after pushing regs) ;;; interrupt vector [esp] [esp+3P] ;;; interrupt err (parameter) [esp+1P] [esp+4P] ;;; interrupted eip [esp+2P] [esp+5P] ;;; interrupted cs [esp+3P] [esp+6P] ;;; interrupted efl [esp+4P] [esp+7P] ;;; [PAGING/X64] ;;; interrupted esp [esp+5P] [esp+8P] ;;; interrupted ss [esp+6P] [esp+9P] ;;; [NON-PAGING] ;;; previous stack continues esp+5P esp+8P ;;; SYMFIX(?g_DispatchVector@Class_Microsoft_Singularity_Isal_Isa@@SIXXZ) proc ifdef PAGING if ISA_IX86 ;; Fix up segment ds, es to point to valid values push ss pop ds push ss pop es endif ;; If we've arrived from ring 3 FS will be invalid. push Struct_Microsoft_Singularity_Isal_IX_DescriptorTable_Gdt._pp - Struct_Microsoft_Singularity_Isal_IX_DescriptorTable_Gdt._nul pop fs endif ;; Save regs in InterruptContext push PAX push PCX push PDX ifdef ISA_IX64 push r8 push r9 push r10 push r11 endif ;; Decide if/where to stash the context. For now, we use hard-coded ;; constants based on the vector to make the decision. Eventually this could be ;; delegated to the HAL for more dynamic control of behaviors. ;; The lowest vectors are exceptions - context save into ProcessorRecord, where the ;; cpu state will be visible to the debugger infrastructure. mov PDX, [PSP].Struct_Microsoft_Singularity_Isal_InterruptContext._vector cmp PDX, Struct_Microsoft_Singularity_Isal_InterruptContext_VectorExceptionMax jg not_exception GET_PROCESSOR_CONTEXT PAX lea PCX, [PAX].Struct_Microsoft_Singularity_Isal_CpuRecord._spill jmp save_context not_exception: ;; The middle vectors are fast interrupts - no context save. These interrupts may ;; be used for fast operations which do not interact with the scheduler; control will ;; always resume at the interrupted location. cmp PDX, Struct_Microsoft_Singularity_Isal_InterruptContext_VectorSchedulerMin jl skip_save ;; The highest vectors are normal interrupts - context save into ThreadRecord. Typically ;; control may be resumed on a different thread. GET_THREAD_CONTEXT PAX lea PCX, [PAX].Struct_Microsoft_Singularity_Isal_ThreadRecord._spill save_context: ;; Note PCX has the context buffer from above mov PDX, PSP call SYMFIX(?m_Save@Struct_Microsoft_Singularity_Isal_SpillContext@@SI_NPAU1@PAUStruct_Microsoft_Singularity_Isal_InterruptContext@@@Z) skip_save: ;; Save previous stack limit GET_THREAD_RECORD_FIELD PCX, _activeStackLimit push PCX ;; See if we are on the interrupt stack GET_CPU_RECORD_FIELD PDX, _interruptStackLimit cmp PDX, PCX mov PCX, PSP ; stash old stack ptr; doesn't change flags je no_switch ;; switch to interrupt stack GET_CPU_RECORD_FIELD PAX, _interruptStackBegin mov PSP, PAX SET_THREAD_RECORD_FIELD _activeStackLimit, PDX no_switch: ;; Save the old stack pointer push PCX ;; InterruptContext is old PSP + 4 add PCX, SIZEOF PWORD ;; call the handler call SYMFIX(?g_DispatchInterrupt@Class_Microsoft_Singularity_Isal_Isa@@SIXPAUStruct_Microsoft_Singularity_Isal_InterruptContext@@@Z) ;; Note that we may or may not get here, depending on whether the InterruptDispatch routine ;; returned or not (it may have restored a previous context.) If we did get here, return ;; back to the interrupted context. ;; Restore the old stack pointer and limit pop PSP pop PAX SET_THREAD_RECORD_FIELD _activeStackLimit, PAX ;; Restore caller save regs, do normal interrupt resume ifdef ISA_IX64 pop r11 pop r10 pop r9 pop r8 endif pop PDX pop PCX pop PAX ;; Discard vector/err add PSP, SIZEOF PWORD * 2 ;; Return jmp [SYMFIX(?c_returnFromInterrupt@Class_Microsoft_Singularity_Isal_Isa@@2PAUvoid@@A)] SYMFIX(?g_DispatchVector@Class_Microsoft_Singularity_Isal_Isa@@SIXXZ) endp .data ;;; We have to manually declare this variable to get it properly aligned. This seems to be a bartok bug. align 16 ?c_idt@Class_Microsoft_Singularity_Isal_Isa@@2?AUStruct_Microsoft_Singularity_Isal_IX_InterruptTable@@A Struct_Microsoft_Singularity_Isal_IX_InterruptTable { } ;;; _Idtr is a global structure which is suitable for passing to the lidt instruction. IDTR struct 2 _limit UINT16 ? _base PWORD ? IDTR ends align 16 ;; We want to align the PWORD after the offset, so add padding here. dw ? ifdef ISA_IX64 dw ? dw ? endif _Idtr IDTR { \ (SIZEOF Struct_Microsoft_Singularity_Isal_IX_InterruptTable) - 1, \ ?c_idt@Class_Microsoft_Singularity_Isal_Isa@@2?AUStruct_Microsoft_Singularity_Isal_IX_InterruptTable@@A } END