|
|
TITLE "Interrupt Object Support Routines" ;++ ; ; Copyright (c) 2000 Microsoft Corporation ; ; Module Name: ; ; intsup.asm ; ; Abstract: ; ; This module implements the platform specific code to support interrupt ; objects. It contains the interrupt dispatch code and the code template ; that gets copied into an interrupt object. ; ; Author: ; ; David N. Cutler (davec) 19-Jun-2000 ; ; Environment: ; ; Kernel mode only. ; ;--
include ksamd64.inc
extern KeBugCheck:proc extern KiInitiateUserApc:proc extern __imp_HalEndSystemInterrupt:qword
subttl "Synchronize Execution" ;++ ; ; BOOLEAN ; KeSynchronizeExecution ( ; IN PKINTERRUPT Interrupt, ; IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine, ; IN PVOID SynchronizeContext ; ) ; ; Routine Description: ; ; This function synchronizes the execution of the specified routine with ; the execution of the service routine associated with the specified ; interrupt object. ; ; Arguments: ; ; Interrupt (rcx) - Supplies a pointer to an interrupt object. ; ; SynchronizeRoutine (rdx) - Supplies a pointer to the function whose ; execution is to be synchronized with the execution of the service ; routine associated with the specified interrupt object. ; ; SynchronizeContext (r8) - Supplies a context pointer which is to be ; passed to the synchronization function as a parameter. ; ; Return Value: ; ; The value returned by the synchronization routine is returned as the ; function value. ; ;--
SyFrame struct P1Home dq ? ; parameter home address OldIrql dd ? ; saved IRQL Fill dd ? ; fill syFrame ends
NESTED_ENTRY KeSynchronizeExecution, _TEXT$00
push_reg rsi ; save nonvolatile register alloc_stack (sizeof SyFrame) ; allocate stack frame
END_PROLOGUE
mov rsi, rcx ; save interrupt object address movzx ecx, byte ptr InSynchronizeIrql[rsi] ; get synchronization IRQL
RaiseIrql ; raise IRQL to synchronization level
mov SyFrame.OldIrql[rsp], eax ; save previous IRQL
AcquireSpinLock InActualLock[rsi] ; acquire interrupt spin lock
mov rcx, r8 ; set synchronization context call rdx ; call synchronization routine
ReleaseSpinlock InActualLock[rsi] ; release interrutp spin lock
mov ecx, SyFrame.OldIrql[rsp] ; get previous IRQL
LowerIrql ; lower IRQL to previous level
add rsp, sizeof SyFrame ; deallocate stack frame pop rsi ; restore nonvolatile register ret ;
NESTED_END KeSynchronizeExecution, _TEXT$00
subttl "Interrupt Exception Handler" ;++ ; ; EXCEPTION_DISPOSITION ; KiInterruptHandler ( ; IN PEXCEPTION_RECORD ExceptionRecord, ; IN PVOID EstablisherFrame, ; IN OUT PCONTEXT ContextRecord, ; IN OUT PDISPATCHER_CONTEXT DispatcherContext ; ) ; ; Routine Description: ; ; This routine is the exception handler for the interrupt dispatcher. The ; dispatching or unwinding of an exception across an interrupt causes a ; bug check. ; ; Arguments: ; ; ExceptionRecord (rcx) - Supplies a pointer to an exception record. ; ; EstablisherFrame (rdx) - Supplies the frame pointer of the establisher ; of this exception handler. ; ; ContextRecord (r8) - Supplies a pointer to a context record. ; ; DispatcherContext (r9) - Supplies a pointer to the dispatcher context ; record. ; ; Return Value: ; ; There is no return from this routine. ; ;--
IhFrame struct P1Home dq ? ; parameter home address IhFrame ends
NESTED_ENTRY KiInterruptHandler, _TEXT$00
alloc_stack (sizeof IhFrame) ; allocate stack frame
END_PROLOGUE
test dword ptr ErExceptionFlags[rcx], EXCEPTION_UNWIND ; test for unwind mov ecx, INTERRUPT_UNWIND_ATTEMPTED ; set bug check code jnz short KiIH10 ; if nz, unwind in progress mov ecx, INTERRUPT_EXCEPTION_NOT_HANDLED ; set bug check code KiIH10: call KeBugCheck ; bug check - no return nop ; fill - do not remove
NESTED_END KiInterruptHandler, _TEXT$00
subttl "Chained Dispatch" ;++ ; ; VOID ; KiChainedDispatch ( ; VOID ; ); ; ; Routine Description: ; ; This routine is entered as the result of an interrupt being generated ; via a vector that is connected to more than one interrupt object. ; ; Arguments: ; ; rbp - Supplies a pointer to the interrupt object. ; ; Return Value: ; ; None. ; ;--
NESTED_ENTRY KiChainedDispatch, _TEXT$00, KiInterruptHandler
.pushframe code ; mark machine frame .pushreg rbp ; mark mark nonvolatile register push
GENERATE_INTERRUPT_FRAME ; generate interrupt frame
movzx ecx, byte ptr InIrql[rsi] ; set interrupt IRQL
ENTER_INTERRUPT <NoEOI> ; raise IRQL and enable interrupts
call KiScanInterruptObjectList ; scan interrupt object list
EXIT_INTERRUPT ; do EOI, lower IRQL, and restore state
NESTED_END KiChainedDispatch, _TEXT$00
subttl "Scan Interrupt Object List" ;++ ; ; Routine Description: ; ; This routine scans the list of interrupt objects for chained interrupt ; dispatch. If the mode of the interrupt is latched, then a complete scan ; of the list must be performed. Otherwise, the scan can be cut short as ; soon as an interrupt routine returns ; ; Arguments: ; ; rsi - Supplies a pointer to the interrupt object. ; ; Return Value: ; ; None. ; ;--
SiFrame struct P1Home dq ? ; interrupt object parameter P2Home dq ? ; service context parameter Return db ? ; service routine return value Fill db 15 dup (?) ; fill SavedRbx dq ? ; saved register RBX SavedRdi dq ? ; saved register RDI SavedR12 dq ? ; saved register RSI SiFrame ends
NESTED_ENTRY KiScanInterruptObjectList, _TEXT$00
push_reg r12 ; save nonvolatile registers push_reg rdi ; push_reg rbx ; alloc_stack (sizeof SiFrame - (3 * 8)) ; allocate stack frame
END_PROLOGUE
lea rbx, InInterruptListEntry[rsi] ; get list head address mov r12, rbx ; set address of first list entry
; ; Scan the list of connected interrupt objects and call the service routine. ;
Si05: xor edi, edi ; clear interrupt handled flag Si10: sub r12, InInterruptListEntry ; compute interrupt object address movzx ecx, byte ptr InSynchronizeIrql[r12] ; get synchronization IRQL cmp cl, InIrql[rsi] ; check if equal interrupt IRQL je short Si20 ; if e, IRQL levels equal
SetIrql ; set IRQL to synchronization level
Si20: AcquireSpinLock InActualLock[r12] ; acquire interrupt spin lock
mov rcx, r12 ; set interrupt object parameter mov rdx, InServiceContext[r12] ; set context parameter call qword ptr InServiceRoutine[r12] ; call interrupt service routine mov SiFrame.Return[rsp], al ; save return value
ReleaseSpinLock InActualLock[r12] ; release interrupt spin lock
movzx ecx, byte ptr InIrql[rsi] ; get interrupt IRQL cmp cl, InSynchronizeIrql[r12] ; check if equal synchronization IRQL je short Si30 ; if e, IRQL levels equal
SetIrql ; set IRQL to interrupt level
Si30: test byte ptr SiFrame.Return[rsp], 0ffh ; test if interrupt handled jz short Si40 ; if z, interrupt not handled cmp word ptr InMode[r12], InLatched ; check if latched interrupt jne short Si50 ; if ne, not latched interrupt inc edi ; indicate latched interrupt handled Si40: mov r12, InInterruptListEntry[r12] ; get next interrupt list entry cmp r12, rbx ; check if end of list jne Si10 ; if ne, not end of list
; ; The complete interrupt object list has been scanned. This can only happen ; if the interrupt is a level sensitive interrupt and no interrupt was handled ; or the interrupt is a latched interrupt. Therefore, if any interrupt was ; handled it was a latched interrupt and the list needs to be scanned again ; to ensure that no interrupts are lost. ;
test edi, edi ; test if any interrupts handled jnz Si05 ; if nz, latched interrupt handled Si50: add rsp, sizeof SiFrame - (3 * 8) ; deallocate stack frame pop rbx ; restore nonvolatile register pop rdi ; pop r12 ; ret ;
NESTED_END KiscanInterruptObjectList, _TEXT$00
subttl "Interrupt Dispatch" ;++ ; ; Routine Description: ; ; This routine is entered as the result of an interrupt being generated ; via a vector that is connected to an interrupt object. Its function is ; to directly call the specified interrupt service routine. ; ; This routine is identical to KiInterruptDispatchNoLock except that ; the interrupt spinlock is taken. ; ; N.B. On entry rbp and rsi have been saved on the stack. ; ; Arguments: ; ; rbp - Supplies a pointer to the interrupt object. ; ; Return Value: ; ; None. ; ;--
NESTED_ENTRY KiInterruptDispatch, _TEXT$00, KiInterruptHandler
.pushframe code ; mark machine frame .pushreg rbp ; mark mark nonvolatile register push
GENERATE_INTERRUPT_FRAME ; generate interrupt frame
; ; N.B. It is possible for a interrupt to occur at an IRQL that is lower ; than the current IRQL. This happens when the IRQL raised and at ; the same time an interrupt request is granted. ;
movzx ecx, byte ptr InIrql[rsi] ; set interrupt IRQL
ENTER_INTERRUPT <NoEOI> ; raise IRQL and enable interrupts
lea rax, (-128)[rbp] ; set trap frame address mov InTrapFrame[rsi], rax ;
AcquireSpinLock InActualLock[rsi] ; acquire interrupt spin lock
mov rcx, rsi ; set address of interrupt object mov rdx, InServiceContext[rsi] ; set service context call qword ptr InServiceRoutine[rsi] ; call interrupt service routine
ReleaseSpinLock InActualLock[rsi] ; release interrupt spin lock
EXIT_INTERRUPT ; do EOI, lower IRQL, and restore state
NESTED_END KiInterruptDispatch, _TEXT$00
subttl "Interrupt Dispatch, No Lock" ;++ ; ; Routine Description: ; ; This routine is entered as the result of an interrupt being generated ; via a vector that is connected to an interrupt object. Its function is ; to directly call the specified interrupt service routine. ; ; This routine is identical to KiInterruptDispatch except that no spinlock ; is taken. ; ; N.B. On entry rbp and rsi have been saved on the stack. ; ; Arguments: ; ; rbp - Supplies a pointer to the interrupt object. ; ; Return Value: ; ; None. ; ;--
NESTED_ENTRY KiInterruptDispatchNoLock, _TEXT$00, KiInterruptHandler
.pushframe code ; mark machine frame .pushreg rbp ; mark mark nonvolatile register push
GENERATE_INTERRUPT_FRAME ; generate interrupt frame
; ; N.B. It is possible for a interrupt to occur at an IRQL that is lower ; than the current IRQL. This happens when the IRQL raised and at ; the same time an interrupt request is granted. ;
movzx ecx, byte ptr InIrql[rsi] ; set interrupt IRQL
ENTER_INTERRUPT <NoEOI> ; raise IRQL and enable interrupts
lea rax, (-128)[rbp] ; set trap frame address mov InTrapFrame[rsi], rax ;
mov rcx, rsi ; set address of interrupt object mov rdx, InServiceContext[rsi] ; set service context call qword ptr InServiceRoutine[rsi] ; call interrupt service routine
EXIT_INTERRUPT ; do EOI, lower IRQL, and restore state
NESTED_END KiInterruptDispatchNoLock, _TEXT$00
subttl "Disable Processor Interrupts" ;++ ; ; BOOLEAN ; KeDisableInterrupts( ; VOID ; ) ; ; Routine Description: ; ; This function saves the state of the interrupt enable flag, clear the ; state of the interrupt flag (disables interrupts), and return the old ; inerrrupt enable flag state. ; ; Arguments: ; ; None. ; ; Return Value: ; ; If interrupts were previously enabled, then 1 is returned as the function ; value. Otherwise, 0 is returned. ; ;--
DiFrame struct Flags dd ? ; processor flags Fill dd ? ; fill DiFrame ends
NESTED_ENTRY KeDisableInterrupts, _TEXT$00
push_eflags ; push processor flags
END_PROLOGUE
mov eax, DiFrame.Flags[rsp] ; isolate interrupt enable bit shr eax, EFLAGS_IF_SHIFT ; and al, 1 ; cli ; disable interrupts add rsp, sizeof DiFrame ; deallocate stack frame ret ; return
NESTED_END KeDisableInterrupts, _TEXT$00
subttl "Interrupt Template" ;++ ; ; Routine Description: ; ; This routine is a template that is copied into each interrupt object. ; Its function is to save volatile machine state, compute the interrupt ; object address, and transfer control to the appropriate interrupt ; dispatcher. ; ; N.B. Interrupts are disabled on entry to this routine. ; ; Arguments: ; ; None. ; ; Return Value: ; ; N.B. Control does not return to this routine. The respective interrupt ; dispatcher dismisses the interrupt directly. ; ;--
LEAF_ENTRY KiInterruptTemplate, _TEXT$00
push rax ; push dummy vector number push rbp ; save nonvolatile register lea rbp, KiInterruptTemplate - InDispatchCode ; get interrupt object address jmp qword ptr InDispatchAddress[rbp] ; finish in common code
LEAF_END KiInterruptTemplate, _TEXT$00
end
|