- NtUserWaitForInputIdle: Call EngGetTickCount, removing duplicated code
[reactos.git] / reactos / ntoskrnl / ke / i386 / systimer.S
1 /*
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)
6 */
7
8 /* INCLUDES ******************************************************************/
9
10 #include <asm.h>
11 #include <internal/i386/asmmacro.S>
12 .intel_syntax noprefix
13
14 /* GLOBALS *******************************************************************/
15
16 _DpcTimeoutMsg:
17 .asciz "\n*** DPC routine > 1 sec --- This is not a break in KeUpdateSystemTime\n"
18
19 /* FUNCTIONS ******************************************************************/
20
21 .globl _KiComputeTimerTableIndex@8
22 .func KiComputeTimerTableIndex@8
23 _KiComputeTimerTableIndex@8:
24
25 /* Save registers */
26 push ebx
27
28 /* Make the first multiplication */
29 mov eax, [esp+8]
30 mul dword ptr [_KiTimeIncrementReciprocal+4]
31 mov ebx, eax
32 mov ecx, edx
33
34 /* Make the second multiplication */
35 mov eax, [esp+12]
36 mul dword ptr [_KiTimeIncrementReciprocal]
37 add ebx, eax
38 adc ecx, edx
39
40 /* Multiply by the reciprocal */
41 mov eax, [esp+8]
42 mul dword ptr [_KiTimeIncrementReciprocal]
43 mov eax, [esp+12]
44 push edx
45 mul dword ptr [_KiTimeIncrementReciprocal+4]
46 pop edx
47 add edx, ebx
48 adc eax, ecx
49
50 /* Shift the result and generate the index */
51 mov cl, [_KiTimeIncrementShiftCount]
52 shr eax, cl
53 and eax, TIMER_TABLE_SIZE - 1
54
55 /* Return */
56 pop ebx
57 ret 8
58 .endfunc
59
60 .globl _KeUpdateRunTime@8
61 .func KeUpdateRunTime@8
62 _KeUpdateRunTime@8:
63
64 /* Get KPCR */
65 mov eax, [fs:KPCR_SELF]
66
67 /* Check if this tick is getting skipped */
68 cmp byte ptr [eax+KPCR_PRCB_SKIP_TICK], 0
69 jnz SkipTick
70
71 /* Save EBX */
72 push ebx
73
74 /* Increase interrupt count */
75 inc dword ptr [eax+KPCR_PRCB_INTERRUPT_COUNT]
76
77 /* Get the current thread and process */
78 mov ebx, [eax+KPCR_CURRENT_THREAD]
79 mov ecx, [ebx+KTHREAD_APCSTATE_PROCESS]
80
81 /* Check if this was V86 or user mode */
82 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
83 jnz NotKern
84 test byte ptr [ebp+KTRAP_FRAME_CS], MODE_MASK
85 jnz NotKern
86
87 /* Increase kernel time */
88 inc dword ptr [eax+KPCR_PRCB_KERNEL_TIME]
89
90 /* Check if IRQL was DISPATCH_LEVEL */
91 cmp byte ptr [esp+8], DISPATCH_LEVEL
92 jb BelowDispatch
93 ja AboveDispatch
94
95 /* Check if the DPC routine is active */
96 cmp byte ptr fs:[KPCR_PRCB_DPC_ROUTINE_ACTIVE], 0
97 jz BelowDispatch
98
99 /* At dispatch, increase DPC time */
100 inc dword ptr [eax+KPCR_PRCB_DPC_TIME]
101 #ifdef DBG
102 /* Update the DPC time */
103 inc dword ptr [eax+KPCR_PRCB_DEBUG_DPC_TIME]
104
105 /* Check if we've timed out */
106 mov edx, _KiDPCTimeout
107 cmp dword ptr [eax+KPCR_PRCB_DEBUG_DPC_TIME], edx
108 jc AfterSet
109
110 /* We did, print out a message */
111 push offset _DpcTimeoutMsg
112 call _DbgPrint
113 add esp, 4
114
115 /* Check if the debugger is enabled */
116 cmp byte ptr __KdDebuggerEnabled, 0
117 je ResetDpcTime
118
119 /* Breakpoint */
120 call _DbgBreakPoint@0
121
122 ResetDpcTime:
123 /* Restore state */
124 mov eax, PCR[KPCR_SELF]
125 mov dword ptr [eax+KPCR_PRCB_DEBUG_DPC_TIME], 0
126 #endif
127 jmp AfterSet
128
129 AboveDispatch:
130 /* Update interrupt time */
131 inc dword ptr [eax+KPCR_PRCB_INTERRUPT_TIME]
132 jmp AfterSet
133
134 BelowDispatch:
135 /* Update kernel time */
136 inc dword ptr [ebx+KTHREAD_KERNEL_TIME]
137 jmp AfterSet
138
139 NotKern:
140 /* Update user time */
141 inc dword ptr [eax+KPCR_PRCB_USER_TIME]
142 inc dword ptr [ebx+KTHREAD_USER_TIME]
143
144 AfterSet:
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
149
150 /* Substract counts and add request rate, divide by two to get average */
151 sub ecx, edx
152 add ecx, [eax+KPCR_PRCB_DPC_REQUEST_RATE]
153 shr ecx, 1
154
155 /* Set this as the new request rate */
156 mov [eax+KPCR_PRCB_DPC_REQUEST_RATE], ecx
157
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
160 je DontRequest
161 cmp byte ptr [eax+KPCR_PRCB_DPC_ROUTINE_ACTIVE], 0
162 jnz DontRequest
163 cmp byte ptr [eax+KPCR_PRCB_DPC_INTERRUPT_REQUESTED], 0
164 jnz DontRequest
165
166 /* Request a DPC */
167 mov ecx, DISPATCH_LEVEL
168 call @HalRequestSoftwareInterrupt@4
169
170 /* Restore PCR address */
171 mov eax, [fs:KPCR_SELF]
172
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
177
178 /* Check if the rate now is not ideal */
179 cmp ecx, _KiIdealDpcRate
180 jge RateOk
181 cmp dword ptr [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH], 1
182 je RateOk
183
184 /* Fix the depth */
185 dec dword ptr [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH]
186 jmp RateOk
187
188 DontRequest:
189 /* We didn't request a DPC, decrease the threshold */
190 dec dword ptr [eax+KPCR_PRCB_ADJUST_DPC_THRESHOLD]
191 jnz RateOk
192
193 /* We're at 0 now, reset it */
194 mov ecx, _KiAdjustDpcThreshold
195 mov [eax+KPCR_PRCB_ADJUST_DPC_THRESHOLD], ecx
196
197 /* Get maximum depth and check it */
198 mov ecx, _KiMaximumDpcQueueDepth
199 cmp ecx, [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH]
200 je RateOk
201
202 /* Increase it, it's below maximum */
203 inc dword ptr [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH]
204
205 RateOk:
206 /* Decrement quantum and verify it */
207 sub byte ptr [ebx+KTHREAD_QUANTUM], CLOCK_QUANTUM_DECREMENT
208 jg QuantumNotEmpty
209
210 /* Make sure this isn't the idle thread */
211 cmp ebx, [eax+KPCR_PRCB_IDLE_THREAD]
212 jz QuantumNotEmpty
213
214 /* Set quantum end */
215 mov byte ptr [eax+KPCR_PRCB_QUANTUM_END], 1
216 mov ecx, DISPATCH_LEVEL
217 call @HalRequestSoftwareInterrupt@4
218
219 QuantumNotEmpty:
220 /* Restore ebx and return */
221 pop ebx
222 ret 4
223
224 SkipTick:
225 /* Disable skipping the next tick and return */
226 mov byte ptr [eax+KPCR_PRCB_SKIP_TICK], 0
227 ret 4
228 .endfunc
229
230 .globl _KeUpdateSystemTime@0
231 .func KeUpdateSystemTime@0
232 _KeUpdateSystemTime@0:
233
234 /* Check if this tick is getting skipped */
235 cmp byte ptr fs:[KPCR_PRCB_SKIP_TICK], 0
236 jnz SkipTickSys
237
238 /* Get shared data in ECX */
239 mov ecx, USER_SHARED_DATA
240
241 /* Get interrupt time */
242 mov edi, [ecx+USER_SHARED_DATA_INTERRUPT_TIME]
243 mov esi, [ecx+USER_SHARED_DATA_INTERRUPT_TIME+4]
244
245 /* Add the increment and get the carry */
246 add edi, eax
247 adc esi, 0
248
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
253
254 /* Substract tick count and get the low count */
255 LOCK sub _KiTickOffset, eax
256 mov eax, _KeTickCount
257 mov ebx, eax
258 jg IncompleteTick
259
260 /* Get shared data in ECX */
261 mov ebx, USER_SHARED_DATA
262
263 /* Get system time */
264 mov ecx, [ebx+USER_SHARED_DATA_SYSTEM_TIME]
265 mov edx, [ebx+USER_SHARED_DATA_SYSTEM_TIME+4]
266
267 /* Add the increment and get the carry */
268 add ecx, _KeTimeAdjustment
269 adc edx, 0
270
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
275
276 /* Put tick count back in EBX */
277 mov ebx, eax
278
279 /* Copyit in ECX and get hich count */
280 mov ecx, eax
281 mov edx, _KeTickCount + 4
282
283 /* Add the increment and get the carry */
284 add ecx, 1
285 adc edx, 0
286
287 /* Now store the updated tick */
288 mov [_KeTickCount+8], edx
289 mov [_KeTickCount], ecx
290 mov [_KeTickCount+4], edx
291
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
296
297 /* Get hand index and entry into the table */
298 and eax, TIMER_TABLE_SIZE - 1
299 shl eax, 4
300
301 /* Compare the due time */
302 cmp esi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME+4]
303 jb NextHand
304 ja TimerExpired
305 cmp edi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME]
306 jnb TimerExpired
307
308 NextHand:
309 /* Move to the next hand */
310 inc ebx
311 mov eax, ebx
312
313 IncompleteTick:
314
315 /* Get hand index and entry into the table */
316 and eax, TIMER_TABLE_SIZE - 1
317 shl eax, 4
318
319 /* Compare the due time */
320 cmp esi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME+4]
321 jb DebugCheck
322 ja TimerExpired
323 cmp edi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME]
324 jb DebugCheck
325
326 TimerExpired:
327
328 /* Check if expiration is active */
329 mov ecx, [fs:KPCR_PRCB]
330 cmp dword ptr [ecx+KPRCB_TIMER_REQUEST], 0
331 jne DebugCheck
332
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
338
339 DebugCheck:
340 /* Check if the debugger is enabled */
341 cmp dword ptr __KdDebuggerEnabled, 0
342 jnz DebuggerEnabled
343
344 /* Check if this was a full tick */
345 NoDebug:
346 cmp dword ptr _KiTickOffset, 0
347 jg IncompleteTick2
348
349 /* Increase tick offset */
350 mov eax, _KeMaximumIncrement
351 add _KiTickOffset, eax
352
353 /* Update system run time */
354 push [esp]
355 call _KeUpdateRunTime@8
356 jmp Done
357
358 IncompleteTick2:
359 /* Increase interrupt count */
360 inc dword ptr [fs:KPCR_PRCB_INTERRUPT_COUNT]
361
362 Done:
363 /* Exit the interrupt */
364 cli
365 call _HalEndSystemInterrupt@8
366 jmp _Kei386EoiHelper@0
367
368 DebuggerEnabled:
369 /* Check for break-in request */
370 call _KdPollBreakIn@0
371 or al, al
372 jz NoDebug
373
374 /* Break-in requested! */
375 push 1
376 call _DbgBreakPointWithStatus@4
377 jmp NoDebug
378
379 SkipTickSys:
380 /* Disable skipping the next tick and return */
381 mov byte ptr fs:[KPCR_PRCB_SKIP_TICK], 0
382 jmp IncompleteTick2
383 .endfunc