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