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.

454 lines
16 KiB

  1. title "Interval Clock Interrupt"
  2. ;++
  3. ;
  4. ; Copyright (c) 2000 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; clockint.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the architecture dependent code necessary to
  13. ; process the interval clock interrupt.
  14. ;
  15. ; Author:
  16. ;
  17. ; David N. Cutler (davec) 12-Sep-2000
  18. ;
  19. ; Environment:
  20. ;
  21. ; Kernel mode only.
  22. ;
  23. ;--
  24. include ksamd64.inc
  25. extern KdDebuggerEnabled:byte
  26. extern KeMaximumIncrement:dword
  27. extern KeNumberProcessors:byte
  28. extern KeTimeAdjustment:dword
  29. extern KdCheckForDebugBreak:proc
  30. extern KiAdjustDpcThreshold:dword
  31. if DBG
  32. extern KiCheckForDpcTimeout:proc
  33. endif
  34. extern KiIdealDpcRate:dword
  35. extern KiMaximumDpcQueueDepth:dword
  36. extern KiTickOffset:dword
  37. extern KiTimerTableListHead:qword
  38. extern __imp_HalRequestSoftwareInterrupt:qword
  39. subttl "Update System Time"
  40. ;++
  41. ;
  42. ; VOID
  43. ; KeUpdateSystemTime (
  44. ; IN ULONG64 Increment
  45. ; )
  46. ;
  47. ; Routine Description:
  48. ;
  49. ; This routine is called as the result of an interrupt generated by the
  50. ; interval timer. Its function is to update the interrupt time, update the
  51. ; system time, and check to determine if a timer has expired.
  52. ;
  53. ; N.B. This routine is executed on a single processor in a multiprocess
  54. ; system. The remainder of the processors only execute the quantum end
  55. ; and runtime update code.
  56. ;
  57. ; Arguments:
  58. ;
  59. ; TrapFrame (rcx) - Supplies the address of a trap frame.
  60. ;
  61. ; Increment (rdx) - Supplies the time increment value in 100 nanosecond
  62. ; units.
  63. ;
  64. ; Return Value:
  65. ;
  66. ; None.
  67. ;
  68. ;--
  69. UsFrame struct
  70. P1Home dq ? ; request IRQL parameter
  71. Fill dq ? ; fill to 8 mod 16
  72. SavedRbp dq ? ; saved register RBP
  73. UsFrame ends
  74. NESTED_ENTRY KeUpdateSystemTime, _TEXT$00
  75. push_reg rbp ; save nonvolatile register
  76. alloc_stack (sizeof UsFrame- (1 * 8)); allocate stack frame
  77. END_PROLOGUE
  78. lea rbp, 128[rcx] ; set display pointer address
  79. ;
  80. ; Check if the current clock tick should be skipped.
  81. ;
  82. ; Skip tick is set when the kernel debugger is entered.
  83. ;
  84. if DBG
  85. cmp byte ptr gs:[PcSkipTick], 0 ; check if tick should be skipped
  86. jnz KiUS50 ; if nz, skip clock tick
  87. endif
  88. ;
  89. ; Update interrupt time.
  90. ;
  91. ; N.B. Interrupt time is aligned 0 mod 8.
  92. ;
  93. mov rcx, USER_SHARED_DATA ; get user shared data address
  94. lea r11, KiTimerTableListHead ; get timer table address
  95. add UsInterruptTime[rcx], rdx ; update interrupt time
  96. mov eax, UsInterruptTime + 4[rcx] ; copy high interrupt time
  97. mov UsInterruptTime + 8[rcx], eax ; for wow64
  98. mov r8, UsInterruptTime[rcx] ; get updated interrupt time
  99. mov r10, UsTickCount[rcx] ; get tick count value
  100. sub KiTickOffset, edx ; subtract time increment
  101. jg short KiUS20 ; if greater, not complete tick
  102. ;
  103. ; Update system time.
  104. ;
  105. ; N.B. System time is aligned 4 mod 8.
  106. ;
  107. ; The following code updates an unaligned quadword value. The quadword
  108. ; value, however, is guaranteed to be within a cache line, and therefore,
  109. ; the value will be written such that no other processor can see any
  110. ; stale information.
  111. ;
  112. mov eax, KeTimeAdjustment ; get time adjustment value
  113. add UsSystemTime[rcx], rax ; update system time
  114. mov eax, UsSystemTime + 4[rcx] ; copy high system time
  115. mov UsSystemTime + 8[rcx], eax ; for wow64
  116. ;
  117. ; Update tick count.
  118. ;
  119. ; N.B. Tick count is aligned 0 mod 8.
  120. ;
  121. inc qword ptr UsTickCount[rcx] ; update tick count
  122. mov eax, UsTickCount + 4[rcx] ; copy high tick count
  123. mov UsTickCount + 8[rcx], eax ; for wow64
  124. ;
  125. ; Check to determine if a timer has expired.
  126. ;
  127. mov rcx, r10 ; copy tick count value
  128. and ecx, TIMER_TABLE_SIZE - 1 ; isolate current hand value
  129. shl ecx, 4 ; compute listhead offset
  130. add rcx, r11 ; get listhead address
  131. mov r9, LsFlink[rcx] ; get first entry address
  132. cmp r9, rcx ; check if list is empty
  133. je short KiUS10 ; if e, list is empty
  134. cmp r8, (TiDueTime - TiTimerListEntry)[r9] ; compare due time
  135. jae short KiUS30 ; if ae, timer has expired
  136. KiUS10: inc r10 ; advance tick count value
  137. ;
  138. ; Check to determine if a timer has expired.
  139. ;
  140. KiUS20: mov rcx, r10 ; copy tick count value
  141. and ecx, TIMER_TABLE_SIZE - 1 ; isolate current hand value
  142. shl ecx, 4 ; compute listhead offset
  143. add rcx, r11 ; get listhead address
  144. mov r9, LsFlink[rcx] ; get first entry address
  145. cmp r9, rcx ; check if list is empty
  146. je short KiUS40 ; if equal, list is empty
  147. cmp r8, (TiDueTime - TiTimerListEntry)[r9] ; compare due time
  148. jb short KiUS40 ; if b, timer has not expired
  149. ;
  150. ; A timer has expired.
  151. ;
  152. ; Set the timer hand value in the current processor block if it is not already
  153. ; set.
  154. ;
  155. KiUS30: mov rdx, gs:[PcCurrentPrcb] ; get current processor block address
  156. cmp qword ptr PbTimerRequest[rdx], 0 ; check if expiration active
  157. jne short KiUS40 ; if ne, expiration already active
  158. mov PbTimerHand[rdx], r10 ; set timer hand value
  159. mov cl, DISPATCH_LEVEL ; request dispatch interrupt
  160. call __imp_HalRequestSoftwareInterrupt ;
  161. ;
  162. ; Check to determine if a full tick has expired.
  163. ;
  164. KiUS40: cmp KiTickOffset, 0 ; check if full tick has expired
  165. jg short KiUS60 ; if g, not a full tick
  166. mov eax, KeMaximumIncrement ; get maximum time incrmeent
  167. add KiTickOffset, eax ; add maximum time to residue
  168. lea rcx, (-128)[rbp] ; set trap frame address
  169. call KeUpdateRunTime ; update runtime
  170. if DBG
  171. KiUS50: mov byte ptr gs:[PcSkipTick], 0 ; clear skip tick indicator
  172. endif
  173. KiUS60: add rsp, sizeof UsFrame- (1 * 8) ; deallocate stack frame
  174. pop rbp ; restore nonvolatile register
  175. ret ; return
  176. NESTED_END KeUpdateSystemTime, _TEXT$00
  177. subttl "Update Thread and Process Runtime"
  178. ;++
  179. ;
  180. ; Routine Description:
  181. ;
  182. ; This routine is called as the result of the interval timer interrupt on
  183. ; all processors in the system. Its function is update the runtime of the
  184. ; current thread, update the runtime of the current thread's process, and
  185. ; decrement the current thread's quantum. This routine also implements DPC
  186. ; interrupt moderation.
  187. ;
  188. ; N.B. This routine is executed on all processors in a multiprocessor
  189. ; system.
  190. ;
  191. ; Arguments:
  192. ;
  193. ; rcx - Supplies the address of a trap frame.
  194. ;
  195. ; Return Value:
  196. ;
  197. ; None.
  198. ;
  199. ;--
  200. UrFrame struct
  201. P1Home dq ? ; request IRQL parameter
  202. Fill dq ? ; fill to 8 mod 16
  203. SavedRdi dq ? ; saved register RDI
  204. SavedRsi dq ? ; saved register RSI
  205. savedRbp dq ? ; saved register RBP
  206. UrFrame ends
  207. NESTED_ENTRY KeUpdateRunTime, _TEXT$00
  208. push_reg rbp ; save nonvolatile registers
  209. push_reg rsi ;
  210. push_reg rdi ;
  211. alloc_stack (sizeof UrFrame - (3 * 8)) ; allocate stack frame
  212. END_PROLOGUE
  213. lea rbp, 128[rcx] ; set display pointer address
  214. ;
  215. ; Check if the current clock tick should be skipped.
  216. ;
  217. ; Skip tick is set when the kernel debugger is entered.
  218. ;
  219. if DBG
  220. cmp byte ptr gs:[PcSkipTick], 0 ; check if tick should be skipped
  221. jnz KiUR70 ; if nz, skip clock tick
  222. endif
  223. ;
  224. ; Update time counter based on previous mode, IRQL level, and whether there
  225. ; is currently a DPC active.
  226. ;
  227. mov rsi, gs:[PcCurrentPrcb] ; get current processor block address
  228. mov rdi, PbCurrentThread[rsi] ; get current thread address
  229. mov rdx, ThApcState + AsProcess[rdi] ; get current process address
  230. test byte ptr TrSegCs[rbp], MODE_MASK ; check if previous mode user
  231. jnz short KiUR30 ; if nz, previous mode user
  232. ;
  233. ; Update the total time spent in kernel mode.
  234. ;
  235. inc dword ptr PbKernelTime[rsi] ; increment kernel time
  236. cmp byte ptr TrPreviousIrql[rbp], DISPATCH_LEVEL ; check IRQL level
  237. jb short KiUR20 ; if b, previous IRQL below DPC level
  238. ja short KiUR10 ; if a, previous IRQL above DPC level
  239. cmp byte ptr PbDpcRoutineActive[rsi], 0 ; check if DPC routine active
  240. je short KiUR20 ; if e, no DPC routine active
  241. inc dword ptr PbDpcTime[rsi] ; increment time at DPC level
  242. ;
  243. ; Check if the time spent at DPC level for the current DPC exceeds the system
  244. ; DPC time out limit.
  245. ;
  246. if DBG
  247. mov rcx, rsi ; set current PRCB address
  248. call KiCheckForDpcTimeout ; check for DPC time out
  249. endif
  250. jmp short KiUR40 ; finish in common code
  251. ;
  252. ; Update the time spent at interrupt time for this processor
  253. ;
  254. KiUR10: inc dword ptr PbInterruptTime[rsi] ; increment interrupt time
  255. jmp short KiUR40 ; finish in common code
  256. ;
  257. ; Update the time spent in kernel mode for the current thread and the current
  258. ; process.
  259. ;
  260. KiUR20: inc dword ptr ThKernelTime[rdi] ; increment time in kernel mode
  261. ifndef NT_UP
  262. lock inc dword ptr PrKernelTime[rdx] ; increment time in kernel mode
  263. else
  264. inc dword ptr PrKernelTime[rdx] ; increment time in kernel mode
  265. endif
  266. jmp short KiUR40 ; finish in common code
  267. ;
  268. ; Update total time spent in user mode and update the time spent inuser mode
  269. ; for the current thread and the current process.
  270. ;
  271. KiUR30: inc dword ptr PbUserTime[rsi] ; increment time in user mode
  272. inc dword ptr ThUserTime[rdi] ; increment time is user mode
  273. ifndef NT_UP
  274. lock inc dword ptr PrUserTime[rdx] ; increment time in user mode
  275. else
  276. inc dword ptr PrUserTime[rdx] ; increment time in user mode
  277. endif
  278. ;
  279. ; Update the DPC request rate which is computed as the average between the
  280. ; previous rate and the current rate.
  281. ;
  282. KiUR40: mov ecx, PbDpcCount[rsi] ; get current DPC count
  283. mov edx, PbDpcLastCount[rsi] ; get last DPC count
  284. mov PbDpcLastCount[rsi], ecx ; set last DPC count
  285. sub ecx, edx ; compute count during interval
  286. add ecx, PbDpcRequestRate[rsi] ; compute sum
  287. shr ecx, 1 ; average current and last
  288. mov PbDpcRequestRate[rsi], ecx ; set new DPC request rate
  289. ;
  290. ; If the current DPC queue depth is not zero, a DPC routine is not active,
  291. ; and a DPC interrupt has not been requested, then request a dispatch
  292. ; interrupt, decrement the maximum DPC queue depth, and reset the threshold
  293. ; counter if appropriate.
  294. ;
  295. cmp dword ptr PbDpcQueueDepth[rsi], 0 ; check if queue depth zero
  296. je short KiUR50 ; if e, DPC queue depth is zero
  297. cmp byte ptr PbDpcRoutineActive[rsi], 0 ; check if DPC routine active
  298. jne short KiUR50 ; if ne, DPC routine active
  299. cmp byte ptr PbDpcInterruptRequested[rsi], 0 ; check if interrupt
  300. jne short KiUR50 ; if ne, interrupt requested
  301. mov cl, DISPATCH_LEVEL ; request a dispatch interrupt
  302. call __imp_HalRequestSoftwareInterrupt ;
  303. mov ecx, PbDpcRequestRate[rsi] ; get DPC request rate
  304. mov edx, KiAdjustDpcThreshold ; reset initial threshold counter
  305. mov PbAdjustDpcThreshold[rsi], edx ;
  306. cmp ecx, KiIdealDpcRate ; check if current rate less than ideal
  307. jge short KiUR60 ; if ge, rate greater or equal ideal
  308. cmp dword ptr PbMaximumDpcQueueDepth[rsi], 1 ; check if maximum depth one
  309. je short KiUR60 ; if e, maximum depth is one
  310. dec dword ptr PbMaximumDpcQueueDepth[rsi] ; decrement depth
  311. jmp short KiUR60 ;
  312. ;
  313. ; The DPC queue is empty or a DPC routine is active or a DPC interrupt has
  314. ; been requested. Count down the adjustment threshold and if the count reaches
  315. ; zero, then increment the maximum DPC queue depth, but not above the initial
  316. ; value and reset the adjustment threshold value.
  317. ;
  318. KiUR50: dec dword ptr PbAdjustDpcThreshold[rsi] ; decrement threshold
  319. jnz short KiUR60 ; if nz, threshold not zero
  320. mov ecx, KiAdjustDpcThreshold ; reset initial threshold counter
  321. mov PbAdjustDpcThreshold[rsi], ecx ;
  322. mov ecx, KiMaximumDpcQueueDepth ; get maximum DPC queue depth
  323. cmp ecx, PbMaximumDpcQueueDepth[rsi] ; check if depth at maximum level
  324. je short KiUR60 ; if e, aleady a maximum level
  325. inc dword ptr PbMaximumDpcQueueDepth[rsi] ; increment maximum depth
  326. ;
  327. ; Decrement current thread quantum and check to determine if a quantum end
  328. ; has occurred.
  329. ;
  330. KiUR60: sub byte ptr ThQuantum[rdi], CLOCK_QUANTUM_DECREMENT ; decrement quantum
  331. jg short KiUR80 ; if g, time remaining on quantum
  332. ;
  333. ; Set quantum end flag and initiate a dispather interrupt on the current
  334. ; processor.
  335. ;
  336. cmp rdi, PbIdleThread[rsi] ; check if idle thread
  337. je short KiUR80 ; if e, idle thread
  338. inc byte ptr PbQuantumEnd[rsi] ; set quantum end indicator
  339. mov cl, DISPATCH_LEVEL ; request dispatch interrupt
  340. call __imp_HalRequestSoftwareInterrupt ;
  341. if DBG
  342. KiUR70: mov byte ptr gs:[PcSkipTick], 0 ; clear skip tick indicator
  343. endif
  344. ;
  345. ; If the debugger is enabled, check if a break is requested.
  346. ;
  347. ; N.B. A poll break in attempt only occurs on each processor when the poll
  348. ; slot matches the current processor number.
  349. ;
  350. KiUR80: cmp KdDebuggerEnabled, 0 ; check if debugger is enabled
  351. je short KiUR90 ; if e, debugger is not enabled
  352. mov al, PbPollSlot[rsi] ; get current poll slot number
  353. cmp al, PbNumber[rsi] ; check for processor number match
  354. jne short KiUR85 ; if ne, processor number mismatch
  355. call KdCheckForDebugBreak ; check for break in request
  356. KiUR85: inc byte ptr PbPollSlot[rsi] ; increment poll slot number
  357. mov al, KeNumberProcessors ; get number of processors
  358. cmp al, PbPollSlot[rsi] ; check for poll slot wrap
  359. ja short KiUR90 ; if a, no poll slot wrap
  360. mov byte ptr PbPollSlot[rsi], 0 ; wrap poll slot to zero
  361. KiUR90: add rsp, sizeof UrFrame - (3 * 8) ; deallocate stack frame
  362. pop rdi ; restore nonvolatile registers
  363. pop rsi ;
  364. pop rbp ;
  365. ret ; return
  366. NESTED_END KeUpdateRunTime, _TEXT$00
  367. end