242 lines
7.8 KiB
NASM
242 lines
7.8 KiB
NASM
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
|