2 * FILE: ntoskrnl/ke/i386/clock.S
3 * COPYRIGHT: See COPYING in the top level directory
4 * PURPOSE: System Clock Management
5 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
8 /* INCLUDES ******************************************************************/
11 #include <internal/i386/asmmacro.S>
12 .intel_syntax noprefix
14 /* GLOBALS *******************************************************************/
17 .asciz "\n*** DPC routine > 1 sec --- This is not a break in KeUpdateSystemTime\n"
19 /* FUNCTIONS ******************************************************************/
21 .globl _KiComputeTimerTableIndex@8
22 .func KiComputeTimerTableIndex@8
23 _KiComputeTimerTableIndex@8:
28 /* Make the first multiplication */
30 mul dword ptr [_KiTimeIncrementReciprocal+4]
34 /* Make the second multiplication */
36 mul dword ptr [_KiTimeIncrementReciprocal]
40 /* Multiply by the reciprocal */
42 mul dword ptr [_KiTimeIncrementReciprocal]
45 mul dword ptr [_KiTimeIncrementReciprocal+4]
50 /* Shift the result and generate the index */
51 mov cl, [_KiTimeIncrementShiftCount]
53 and eax, TIMER_TABLE_SIZE - 1
60 .globl _KeUpdateRunTime@8
61 .func KeUpdateRunTime@8
65 mov eax, [fs:KPCR_SELF]
67 /* Check if this tick is getting skipped */
68 cmp byte ptr [eax+KPCR_PRCB_SKIP_TICK], 0
74 /* Increase interrupt count */
75 inc dword ptr [eax+KPCR_PRCB_INTERRUPT_COUNT]
77 /* Get the current thread and process */
78 mov ebx, [eax+KPCR_CURRENT_THREAD]
79 mov ecx, [ebx+KTHREAD_APCSTATE_PROCESS]
81 /* Check if this was V86 or user mode */
82 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
84 test byte ptr [ebp+KTRAP_FRAME_CS], MODE_MASK
87 /* Increase kernel time */
88 inc dword ptr [eax+KPCR_PRCB_KERNEL_TIME]
90 /* Check if IRQL was DISPATCH_LEVEL */
91 cmp byte ptr [esp+8], DISPATCH_LEVEL
95 /* Check if the DPC routine is active */
96 cmp byte ptr fs:[KPCR_PRCB_DPC_ROUTINE_ACTIVE], 0
99 /* At dispatch, increase DPC time */
100 inc dword ptr [eax+KPCR_PRCB_DPC_TIME]
102 /* Update the DPC time */
103 inc dword ptr [eax+KPCR_PRCB_DEBUG_DPC_TIME]
105 /* Check if we've timed out */
106 mov edx, _KiDPCTimeout
107 cmp dword ptr [eax+KPCR_PRCB_DEBUG_DPC_TIME], edx
110 /* We did, print out a message */
111 push offset _DpcTimeoutMsg
115 /* Check if the debugger is enabled */
116 cmp byte ptr __KdDebuggerEnabled, 0
120 call _DbgBreakPoint@0
124 mov eax, PCR[KPCR_SELF]
125 mov dword ptr [eax+KPCR_PRCB_DEBUG_DPC_TIME], 0
130 /* Update interrupt time */
131 inc dword ptr [eax+KPCR_PRCB_INTERRUPT_TIME]
135 /* Update kernel time */
136 inc dword ptr [ebx+KTHREAD_KERNEL_TIME]
140 /* Update user time */
141 inc dword ptr [eax+KPCR_PRCB_USER_TIME]
142 inc dword ptr [ebx+KTHREAD_USER_TIME]
145 /* Get the DPC Count and last count, and set the ne wone */
146 mov ecx, [eax+KPCR_PRCB_DPC_COUNT]
147 mov edx, [eax+KPCR_PRCB_DPC_LAST_COUNT]
148 mov [eax+KPCR_PRCB_DPC_LAST_COUNT], ecx
150 /* Substract counts and add request rate, divide by two to get average */
152 add ecx, [eax+KPCR_PRCB_DPC_REQUEST_RATE]
155 /* Set this as the new request rate */
156 mov [eax+KPCR_PRCB_DPC_REQUEST_RATE], ecx
158 /* Check for depth > 0, DPCs to be inactive, and no other pending request */
159 cmp dword ptr [eax+KPCR_PRCB_DPC_QUEUE_DEPTH], 0
161 cmp byte ptr [eax+KPCR_PRCB_DPC_ROUTINE_ACTIVE], 0
163 cmp byte ptr [eax+KPCR_PRCB_DPC_INTERRUPT_REQUESTED], 0
167 mov ecx, DISPATCH_LEVEL
168 call @HalRequestSoftwareInterrupt@4
170 /* Restore PCR address */
171 mov eax, [fs:KPCR_SELF]
173 /* Get the DPC request rate and threshold adjust, and set it */
174 mov ecx, [eax+KPCR_PRCB_DPC_REQUEST_RATE]
175 mov edx, _KiAdjustDpcThreshold
176 mov [eax+KPCR_PRCB_ADJUST_DPC_THRESHOLD], edx
178 /* Check if the rate now is not ideal */
179 cmp ecx, _KiIdealDpcRate
181 cmp dword ptr [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH], 1
185 dec dword ptr [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH]
189 /* We didn't request a DPC, decrease the threshold */
190 dec dword ptr [eax+KPCR_PRCB_ADJUST_DPC_THRESHOLD]
193 /* We're at 0 now, reset it */
194 mov ecx, _KiAdjustDpcThreshold
195 mov [eax+KPCR_PRCB_ADJUST_DPC_THRESHOLD], ecx
197 /* Get maximum depth and check it */
198 mov ecx, _KiMaximumDpcQueueDepth
199 cmp ecx, [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH]
202 /* Increase it, it's below maximum */
203 inc dword ptr [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH]
206 /* Decrement quantum and verify it */
207 sub byte ptr [ebx+KTHREAD_QUANTUM], CLOCK_QUANTUM_DECREMENT
210 /* Make sure this isn't the idle thread */
211 cmp ebx, [eax+KPCR_PRCB_IDLE_THREAD]
214 /* Set quantum end */
215 mov byte ptr [eax+KPCR_PRCB_QUANTUM_END], 1
216 mov ecx, DISPATCH_LEVEL
217 call @HalRequestSoftwareInterrupt@4
220 /* Restore ebx and return */
225 /* Disable skipping the next tick and return */
226 mov byte ptr [eax+KPCR_PRCB_SKIP_TICK], 0
230 .globl _KeUpdateSystemTime@0
231 .func KeUpdateSystemTime@0
232 _KeUpdateSystemTime@0:
234 /* Check if this tick is getting skipped */
235 cmp byte ptr fs:[KPCR_PRCB_SKIP_TICK], 0
238 /* Get shared data in ECX */
239 mov ecx, USER_SHARED_DATA
241 /* Get interrupt time */
242 mov edi, [ecx+USER_SHARED_DATA_INTERRUPT_TIME]
243 mov esi, [ecx+USER_SHARED_DATA_INTERRUPT_TIME+4]
245 /* Add the increment and get the carry */
249 /* Now store the updated times */
250 mov [ecx+USER_SHARED_DATA_INTERRUPT_TIME+8], esi
251 mov [ecx+USER_SHARED_DATA_INTERRUPT_TIME], edi
252 mov [ecx+USER_SHARED_DATA_INTERRUPT_TIME+4], esi
254 /* Substract tick count and get the low count */
255 LOCK sub _KiTickOffset, eax
256 mov eax, _KeTickCount
260 /* Get shared data in ECX */
261 mov ebx, USER_SHARED_DATA
263 /* Get system time */
264 mov ecx, [ebx+USER_SHARED_DATA_SYSTEM_TIME]
265 mov edx, [ebx+USER_SHARED_DATA_SYSTEM_TIME+4]
267 /* Add the increment and get the carry */
268 add ecx, _KeTimeAdjustment
271 /* Now store the updated times */
272 mov [ebx+USER_SHARED_DATA_SYSTEM_TIME+8], edx
273 mov [ebx+USER_SHARED_DATA_SYSTEM_TIME], ecx
274 mov [ebx+USER_SHARED_DATA_SYSTEM_TIME+4], edx
276 /* Put tick count back in EBX */
279 /* Copy it in ECX and get high count */
281 mov edx, _KeTickCount + 4
283 /* Add the increment and get the carry */
287 /* Now store the updated tick */
288 mov [_KeTickCount+8], edx
289 mov [_KeTickCount], ecx
290 mov [_KeTickCount+4], edx
292 /* Store in in shared data too */
293 mov ds:[USER_SHARED_DATA+USER_SHARED_DATA_TICK_COUNT+8], edx
294 mov ds:[USER_SHARED_DATA+USER_SHARED_DATA_TICK_COUNT], ecx
295 mov ds:[USER_SHARED_DATA+USER_SHARED_DATA_TICK_COUNT+4], edx
297 /* Get hand index and entry into the table */
298 and eax, TIMER_TABLE_SIZE - 1
301 /* Compare the due time */
302 cmp esi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME+4]
305 cmp edi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME]
309 /* Move to the next hand */
315 /* Get hand index and entry into the table */
316 and eax, TIMER_TABLE_SIZE - 1
319 /* Compare the due time */
320 cmp esi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME+4]
323 cmp edi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME]
328 /* Check if expiration is active */
329 mov ecx, [fs:KPCR_PRCB]
330 cmp dword ptr [ecx+KPRCB_TIMER_REQUEST], 0
333 /* It's not, register it */
334 mov [ecx+KPRCB_TIMER_REQUEST], esp
335 mov [ecx+KPRCB_TIMER_HAND], ebx
336 mov ecx, DISPATCH_LEVEL
337 call @HalRequestSoftwareInterrupt@4
340 /* Check if the debugger is enabled */
341 cmp dword ptr __KdDebuggerEnabled, 0
344 /* Check if this was a full tick */
346 cmp dword ptr _KiTickOffset, 0
349 /* Increase tick offset */
350 mov eax, _KeMaximumIncrement
351 add _KiTickOffset, eax
353 /* Update system run time */
355 call _KeUpdateRunTime@8
359 /* Increase interrupt count */
360 inc dword ptr [fs:KPCR_PRCB_INTERRUPT_COUNT]
363 /* Exit the interrupt */
365 call _HalEndSystemInterrupt@8
366 jmp _Kei386EoiHelper@0
369 /* Check for break-in request */
370 call _KdPollBreakIn@0
374 /* Break-in requested! */
375 push DBG_STATUS_CONTROL_C
376 call _DbgBreakPointWithStatus@4
380 /* Disable skipping the next tick and return */
381 mov byte ptr fs:[KPCR_PRCB_SKIP_TICK], 0