263 lines
11 KiB
NASM
263 lines
11 KiB
NASM
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;;
|
||
|
;;; 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
|