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 ; 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 ; 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 ; 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