- Change FASTCALL_PROLOG to use the stack to update FS, since we run in the DPC stack.
[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@4
61 .func KeUpdateRunTime@4
62 _KeUpdateRunTime@4:
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 /* FIXME: HACK */
298 mov ds:[USER_SHARED_DATA], ecx
299
300 /* Get hand index and entry into the table */
301 and eax, TIMER_TABLE_SIZE - 1
302 shl eax, 4
303
304 /* Compare the due time */
305 cmp esi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME+4]
306 jb NextHand
307 ja TimerExpired
308 cmp edi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME]
309 jnb TimerExpired
310
311 NextHand:
312 /* Move to the next hand */
313 inc ebx
314 mov eax, ebx
315
316 IncompleteTick:
317
318 /* Get hand index and entry into the table */
319 and eax, TIMER_TABLE_SIZE - 1
320 shl eax, 4
321
322 /* Compare the due time */
323 cmp esi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME+4]
324 jb DebugCheck
325 ja TimerExpired
326 cmp edi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME]
327 jb DebugCheck
328
329 TimerExpired:
330
331 /* Check if expiration is active */
332 mov ecx, [fs:KPCR_PRCB]
333 cmp dword ptr [ecx+KPRCB_TIMER_REQUEST], 0
334 jne DebugCheck
335
336 /* It's not, register it */
337 mov [ecx+KPRCB_TIMER_REQUEST], esp
338 mov [ecx+KPRCB_TIMER_HAND], ebx
339 mov ecx, DISPATCH_LEVEL
340 call @HalRequestSoftwareInterrupt@4
341
342 DebugCheck:
343 /* Check if the debugger is enabled */
344 cmp dword ptr __KdDebuggerEnabled, 0
345 jnz DebuggerEnabled
346
347 /* Check if this was a full tick */
348 NoDebug:
349 cmp dword ptr _KiTickOffset, 0
350 jg IncompleteTick2
351
352 /* Increase tick offset */
353 mov eax, _KeMaximumIncrement
354 add _KiTickOffset, eax
355
356 /* Update system run time */
357 push [esp]
358 call _KeUpdateRunTime@4
359 jmp Done
360
361 IncompleteTick2:
362 /* Increase interrupt count */
363 inc dword ptr [fs:KPCR_PRCB_INTERRUPT_COUNT]
364
365 Done:
366 /* Exit the interrupt */
367 cli
368 call _HalEndSystemInterrupt@8
369 jmp _Kei386EoiHelper@0
370
371 DebuggerEnabled:
372 /* Check for break-in request */
373 call _KdPollBreakIn@0
374 or al, al
375 jz NoDebug
376
377 /* Break-in requested! */
378 push 1
379 call _DbgBreakPointWithStatus@4
380 jmp NoDebug
381
382 SkipTickSys:
383 /* Disable skipping the next tick and return */
384 mov byte ptr fs:[KPCR_PRCB_SKIP_TICK], 0
385 jmp IncompleteTick2
386 .endfunc