Source code of Windows XP (NT5)
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.

241 lines
7.8 KiB

  1. title "Idle Loop"
  2. ;++
  3. ;
  4. ; Copyright (c) 2000 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; idle.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the platform specifid idle loop.
  13. ;
  14. ; Author:
  15. ;
  16. ; David N. Cutler (davec) 21-Sep-2000
  17. ;
  18. ; Environment:
  19. ;
  20. ; Kernel mode only.
  21. ;
  22. ;--
  23. include ksamd64.inc
  24. extern KdDebuggerEnabled:byte
  25. extern KeAcquireQueuedSpinLockAtDpcLevel:proc
  26. extern KeAcquireQueuedSpinLockRaiseToSynch:proc
  27. extern KeReleaseQueuedSpinLock:proc
  28. extern KeReleaseQueuedSpinLockFromDpcLevel:proc
  29. extern KiCheckBreakInRequest:proc
  30. extern KiIdleSummary:qword
  31. extern KiRetireDpcList:proc
  32. extern SwapContext:proc
  33. extern __imp_HalClearSoftwareInterrupt:qword
  34. subttl "Idle Loop"
  35. ;++
  36. ; VOID
  37. ; KiIdleLoop (
  38. ; VOID
  39. ; )
  40. ;
  41. ; Routine Description:
  42. ;
  43. ; This routine continuously executes the idle loop and never returns.
  44. ;
  45. ; Arguments:
  46. ;
  47. ; None.
  48. ;
  49. ; Return value:
  50. ;
  51. ; This routine never returns.
  52. ;
  53. ;--
  54. IlFrame struct
  55. Fill dq ? ; fill to 8 mod 16
  56. IlFrame ends
  57. NESTED_ENTRY KiIdleLoop, _TEXT$00
  58. alloc_stack (sizeof IlFrame) ; allocate stack frame
  59. END_PROLOGUE
  60. mov rbx, gs:[PcCurrentPrcb] ; get current processor block address
  61. xor edi, edi ; reset check breakin counter
  62. jmp short KiIL20 ; skip idle processor on first iteration
  63. ;
  64. ; There are no entries in the DPC list and a thread has not been selected
  65. ; for execution on this processor. Call the HAL so power managment can be
  66. ; performed.
  67. ;
  68. ; N.B. The HAL is called with interrupts disabled. The HAL will return
  69. ; with interrupts enabled.
  70. ;
  71. KiIL10: lea rcx, PbPowerState[rbx] ; set address of power state
  72. call qword ptr PpIdleFunction[rcx] ; call idle function
  73. ;
  74. ; Give the debugger an opportunity to gain control if the kernel debuggger
  75. ; is enabled.
  76. ;
  77. ; N.B. On an MP system the lowest numbered idle processor is the only
  78. ; processor that checks for a breakin request.
  79. ;
  80. KiIL20: cmp KdDebuggerEnabled, 0 ; check if a debugger is enabled
  81. je short CheckDpcList ; if e, debugger not enabled
  82. ifndef NT_UP
  83. mov rax, KiIdleSummary ; get idle summary
  84. mov rcx, PbSetMember[rbx] ; get set member
  85. dec rcx ; compute right bit mask
  86. and rax, rcx ; check if any lower bits set
  87. jnz short CheckDpcList ; if nz, not lowest numbered
  88. endif
  89. dec edi ; decrement check breakin counter
  90. jg short CheckDpcList ; if g, not time to check for breakin
  91. call KiCheckBreakInRequest ; check if break in requested
  92. mov edi, 1000 ; set check breakin interval
  93. ;
  94. ; Disable interrupts and check if there is any work in the DPC list of the
  95. ; current processor or a target processor.
  96. ;
  97. ; N.B. The following code enables interrupts for a few cycles, then disables
  98. ; them again for the subsequent DPC and next thread checks.
  99. ;
  100. CheckDpcList: ; reference label
  101. sti ; enable interrupts
  102. nop ;
  103. nop ;
  104. cli ; disable interrupts
  105. ;
  106. ; Process the deferred procedure call list for the current processor.
  107. ;
  108. mov eax, PbDpcQueueDepth[rbx] ; get DPC queue depth
  109. or eax, PbTimerHand[rbx] ; merge timer hand value
  110. jz short CheckNextThread ; if z, no DPCs to process
  111. mov cl, DISPATCH_LEVEL ; set interrupt level
  112. call __imp_HalClearSoftwareInterrupt ; clear software interrupt
  113. mov rcx, rbx ; set processor block address
  114. call KiRetireDpcList ; process the current DPC list
  115. xor edi, edi ; clear check breakin interval
  116. ;
  117. ; Check if a thread has been selected to run on the current processor.
  118. ;
  119. CheckNextThread: ;
  120. cmp qword ptr PbNextThread[rbx], 0 ; check if thread slected
  121. je short KiIL10 ; if e, no thread selected
  122. ;
  123. ; A thread has been selected for execution on this processor. Acquire the
  124. ; context swap lock, get the thread address again (it may have changed),
  125. ; and test whether a swap from idle is blocked for the specified thread.
  126. ; If swap from idle is blocked, then release the context swap lock and loop.
  127. ; Otherwise, clear the address of the next thread in the processor block
  128. ; and call swap context to start execution of the selected thread.
  129. ;
  130. sti ; enable interrupts
  131. ifndef NT_UP
  132. mov ecx, LockQueueContextSwapLock ; set queued spin lock number
  133. call KeAcquireQueuedSpinLockRaiseToSynch ; acquire queued spin lock
  134. endif
  135. mov rsi, PbNextThread[rbx] ; set next thread address
  136. mov rdi, PbCurrentThread[rbx] ; get current thread address
  137. ifndef NT_UP
  138. cmp byte ptr ThIdleSwapBlock[rsi], 0 ; check if swap from idle blocked
  139. jne short KiIL40 ; if ne, swap from idle blocked
  140. cmp rsi, rdi ; check if swap from idle to idle
  141. je short KiIL60 ; if eq, idle to idle
  142. endif
  143. mov qword ptr PbNextThread[rbx], 0 ; clear next thread address
  144. mov PbCurrentThread[rbx], rsi ; set current thread address
  145. mov cl, APC_LEVEL ; set APC interrupt bypass disable
  146. ifndef NT_UP
  147. mov edx, 1 ; set swap from idle true
  148. endif
  149. call SwapContext ; swap context to next thread
  150. ifndef NT_UP
  151. mov ecx, DISPATCH_LEVEL ; set IRQL to dispatch level
  152. SetIrql ;
  153. endif
  154. xor edi, edi ; clear check breakin interval
  155. jmp KiIL20 ; loop
  156. ;
  157. ; Swap from idle is blocked while the specified thread clears the context
  158. ; code. Release the context swap lock and try again.
  159. ;
  160. ifndef NT_UP
  161. KiIL40: mov ecx, LockQueueContextSwapLock ; set queued spin lock number
  162. mov dl, DISPATCH_LEVEL ; set previous IRQL to dispatch level
  163. call KeReleaseQueuedSpinLock ; release context swap lock
  164. jmp KiIL20 ; loop
  165. ;
  166. ; Under rare conditions, a thread can have been scheduled on this processor
  167. ; and subsequently made inelligible to run via a call to set affinity. If no
  168. ; other thread was available to run at the time of the call to set affinity,
  169. ; then the idle thread will have been rescheduled and this processor marked
  170. ; idle. If a new thread becomes available to run on this processor, then the
  171. ; net thread field in the prcoessr block may be unconditionally written.
  172. ;
  173. ; Protect clearing the next thread field by obtaining the dispatcher lock. If
  174. ; the next thread filed is no longer the idle thread, then a new thread has
  175. ; been scheduled for this processor and the next thread field must not be
  176. ; cleared.
  177. ;
  178. KiIL60: lea rcx, PbLockQueue + (16 * LockQueueContextSwapLock)[rbx] ; release
  179. call KeReleaseQueuedSpinLockFromDpcLevel ; the context swap lock
  180. lea rcx, PbLockQueue + (16 * LockQueueDispatcherLock)[rbx] ; acquire
  181. call KeAcquireQueuedSpinLockAtDpcLevel ; the dispatcher lock
  182. cmp rsi, PbNextThread[rbx] ; check if the target thread still idle
  183. jne short KiIL65 ; if ne, not idle thread
  184. mov qword ptr PbNextThread[rbx], 0 ; clear next thread address
  185. KiIL65: mov ecx, LockQueueDispatcherLock ; set lock queue number
  186. mov dl, DISPATCH_LEVEL ; set previous IRQL
  187. call KeReleaseQueuedSpinLock ; release dispatcher lock
  188. jmp KiIL20 ; loop
  189. endif
  190. NESTED_END KiIdleLoop, _TEXT$00
  191. end