Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

275 lines
8.0 KiB

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 KdCheckForDebugBreak:proc
ifndef NT_UP
extern KiIdleSchedule:proc
endif
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
P1Home dq ? ;
P2Home dq ? ;
P3Home dq ? ;
P4Home dq ? ;
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 KdCheckForDebugBreak ; 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 rax, PbTimerRequest[rbx] ; merge timer request value
ifndef NT_UP
or rax, PbDeferredReadyListHead[rbx] ; merge ready list head
endif
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 current PRCB 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
ifdef NT_UP
je short KiIL10 ; if e, no thread selected
else
je KiIL50 ; if e, no thread selected
endif
sti ; enable interrupts
mov ecx, SYNCH_LEVEL ; set IRQL to synchronization level
RaiseIrql ;
;
; set context swap busy for the idle thread and acquire the PRCB Lock.
;
mov rdi, PbCurrentThread[rbx] ; get current thread address
ifndef NT_UP
mov byte ptr ThSwapBusy[rdi], 1 ; set ocntext swap busy
lea r11, PbPrcbLock[rbx] ; set address of current PRCB lock
AcquireSpinLock r11 ; acquire current PRCB Lock
endif
mov rsi, PbNextThread[rbx] ; set next thread address
;
; If a thread had been scheduled for this processor, but was removed from
; eligibility (e.g., an affinity change), then the new thread could be the
; idle thread.
;
ifndef NT_UP
cmp rsi, rdi ; check if swap from idle to idle
je short KiIL40 ; if eq, idle to idle
endif
and qword ptr PbNextThread[rbx], 0 ; clear next thread address
mov PbCurrentThread[rbx], rsi ; set current thread address
mov byte ptr ThState[rsi], Running ; set new thread state
;
; Clear idle schedule since a new thread has been selected for execution on
; this processor and release the PRCB lock.
;
ifndef NT_UP
and byte ptr PbIdleSchedule[rbx], 0 ; clear idle schedule
and qword ptr PbPrcbLock[rbx], 0 ; release current PRCB lock
endif
;
; Switch context to new thread.
;
KiIL30: mov cl, APC_LEVEL ; set APC bypass disable
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
;
; The new thread is the Idle thread (same as old thread). This can happen
; rarely when a thread scheduled for this processor is made unable to run
; on this processor. As this processor has again been marked idle, other
; processors may unconditionally assign new threads to this processor.
;
ifndef NT_UP
KiIL40: and qword ptr PbNextThread[rbx], 0 ; clear next thread
and qword ptr PbPrcbLock[rbx], 0 ; release current PRCB lock
and byte ptr ThSwapBusy[rdi], 0 ; set context swap idle
jmp KiIL20 ;
;
; Call idle schedule if requested.
;
KiIL50: cmp byte ptr PbIdleSchedule[rbx], 0 ; check if idle schedule
je KiIL10 ; if e, idle schedule not requested
sti ; enable interrupts
mov rcx, rbx ; pass current PRCB address
call KiIdleSchedule ; attempt to schedule thread
test rax, rax ; test if new thread schedule
mov rsi, rax ; set new thread address
mov rdi, PbIdleThread[rbx] ; get idle thread address
jnz short KiIL30 ; if nz, new thread scheduled
jmp KiIL20 ;
endif
NESTED_END KiIdleLoop, _TEXT$00
end