|
|
title "Idle Loop" ;++ ; ; Copyright (c) 2000 Microsoft Corporation ; ; Module Name: ; ; idle.asm ; ; Abstract: ; ; This module implements the platform specifid idle loop. ; ; Author: ; ; David N. Cutler (davec) 21-Sep-2000 ; ; Environment: ; ; Kernel mode only. ; ;--
include ksamd64.inc
extern KdDebuggerEnabled:byte extern KeAcquireQueuedSpinLockAtDpcLevel:proc extern KeAcquireQueuedSpinLockRaiseToSynch:proc extern KeReleaseQueuedSpinLock:proc extern KeReleaseQueuedSpinLockFromDpcLevel:proc extern KiCheckBreakInRequest:proc extern KiIdleSummary:qword extern KiRetireDpcList:proc extern SwapContext:proc extern __imp_HalClearSoftwareInterrupt:qword
subttl "Idle Loop" ;++ ; VOID ; KiIdleLoop ( ; VOID ; ) ; ; Routine Description: ; ; This routine continuously executes the idle loop and never returns. ; ; Arguments: ; ; None. ; ; Return value: ; ; This routine never returns. ; ;--
IlFrame struct Fill dq ? ; fill to 8 mod 16 IlFrame ends
NESTED_ENTRY KiIdleLoop, _TEXT$00
alloc_stack (sizeof IlFrame) ; allocate stack frame
END_PROLOGUE
mov rbx, gs:[PcCurrentPrcb] ; get current processor block address xor edi, edi ; reset check breakin counter jmp short KiIL20 ; skip idle processor on first iteration
; ; There are no entries in the DPC list and a thread has not been selected ; for execution on this processor. Call the HAL so power managment can be ; performed. ; ; N.B. The HAL is called with interrupts disabled. The HAL will return ; with interrupts enabled. ;
KiIL10: lea rcx, PbPowerState[rbx] ; set address of power state call qword ptr PpIdleFunction[rcx] ; call idle function
; ; Give the debugger an opportunity to gain control if the kernel debuggger ; is enabled. ; ; N.B. On an MP system the lowest numbered idle processor is the only ; processor that checks for a breakin request. ;
KiIL20: cmp KdDebuggerEnabled, 0 ; check if a debugger is enabled je short CheckDpcList ; if e, debugger not enabled
ifndef NT_UP
mov rax, KiIdleSummary ; get idle summary mov rcx, PbSetMember[rbx] ; get set member dec rcx ; compute right bit mask and rax, rcx ; check if any lower bits set jnz short CheckDpcList ; if nz, not lowest numbered
endif
dec edi ; decrement check breakin counter jg short CheckDpcList ; if g, not time to check for breakin call KiCheckBreakInRequest ; check if break in requested mov edi, 1000 ; set check breakin interval
; ; Disable interrupts and check if there is any work in the DPC list of the ; current processor or a target processor. ; ; N.B. The following code enables interrupts for a few cycles, then disables ; them again for the subsequent DPC and next thread checks. ;
CheckDpcList: ; reference label sti ; enable interrupts nop ; nop ; cli ; disable interrupts
; ; Process the deferred procedure call list for the current processor. ;
mov eax, PbDpcQueueDepth[rbx] ; get DPC queue depth or eax, PbTimerHand[rbx] ; merge timer hand value jz short CheckNextThread ; if z, no DPCs to process mov cl, DISPATCH_LEVEL ; set interrupt level call __imp_HalClearSoftwareInterrupt ; clear software interrupt mov rcx, rbx ; set processor block address call KiRetireDpcList ; process the current DPC list xor edi, edi ; clear check breakin interval
; ; Check if a thread has been selected to run on the current processor. ;
CheckNextThread: ; cmp qword ptr PbNextThread[rbx], 0 ; check if thread slected je short KiIL10 ; if e, no thread selected
; ; A thread has been selected for execution on this processor. Acquire the ; context swap lock, get the thread address again (it may have changed), ; and test whether a swap from idle is blocked for the specified thread. ; If swap from idle is blocked, then release the context swap lock and loop. ; Otherwise, clear the address of the next thread in the processor block ; and call swap context to start execution of the selected thread. ;
sti ; enable interrupts
ifndef NT_UP
mov ecx, LockQueueContextSwapLock ; set queued spin lock number call KeAcquireQueuedSpinLockRaiseToSynch ; acquire queued spin lock
endif
mov rsi, PbNextThread[rbx] ; set next thread address mov rdi, PbCurrentThread[rbx] ; get current thread address
ifndef NT_UP
cmp byte ptr ThIdleSwapBlock[rsi], 0 ; check if swap from idle blocked jne short KiIL40 ; if ne, swap from idle blocked cmp rsi, rdi ; check if swap from idle to idle je short KiIL60 ; if eq, idle to idle
endif
mov qword ptr PbNextThread[rbx], 0 ; clear next thread address mov PbCurrentThread[rbx], rsi ; set current thread address mov cl, APC_LEVEL ; set APC interrupt bypass disable
ifndef NT_UP
mov edx, 1 ; set swap from idle true
endif
call SwapContext ; swap context to next thread
ifndef NT_UP
mov ecx, DISPATCH_LEVEL ; set IRQL to dispatch level
SetIrql ;
endif
xor edi, edi ; clear check breakin interval jmp KiIL20 ; loop
; ; Swap from idle is blocked while the specified thread clears the context ; code. Release the context swap lock and try again. ;
ifndef NT_UP
KiIL40: mov ecx, LockQueueContextSwapLock ; set queued spin lock number mov dl, DISPATCH_LEVEL ; set previous IRQL to dispatch level call KeReleaseQueuedSpinLock ; release context swap lock jmp KiIL20 ; loop
; ; Under rare conditions, a thread can have been scheduled on this processor ; and subsequently made inelligible to run via a call to set affinity. If no ; other thread was available to run at the time of the call to set affinity, ; then the idle thread will have been rescheduled and this processor marked ; idle. If a new thread becomes available to run on this processor, then the ; net thread field in the prcoessr block may be unconditionally written. ; ; Protect clearing the next thread field by obtaining the dispatcher lock. If ; the next thread filed is no longer the idle thread, then a new thread has ; been scheduled for this processor and the next thread field must not be ; cleared. ;
KiIL60: lea rcx, PbLockQueue + (16 * LockQueueContextSwapLock)[rbx] ; release call KeReleaseQueuedSpinLockFromDpcLevel ; the context swap lock lea rcx, PbLockQueue + (16 * LockQueueDispatcherLock)[rbx] ; acquire call KeAcquireQueuedSpinLockAtDpcLevel ; the dispatcher lock cmp rsi, PbNextThread[rbx] ; check if the target thread still idle jne short KiIL65 ; if ne, not idle thread mov qword ptr PbNextThread[rbx], 0 ; clear next thread address KiIL65: mov ecx, LockQueueDispatcherLock ; set lock queue number mov dl, DISPATCH_LEVEL ; set previous IRQL call KeReleaseQueuedSpinLock ; release dispatcher lock jmp KiIL20 ; loop
endif
NESTED_END KiIdleLoop, _TEXT$00
end
|