- Rewrite kernel timer implementation to use Windows 2003's hash-based table timer...
[reactos.git] / reactos / ntoskrnl / ke / i386 / clock.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 /* FUNCTIONS ******************************************************************/
15
16 .globl _KiComputeTimerTableIndex@8
17 .func KiComputeTimerTableIndex@8
18 _KiComputeTimerTableIndex@8:
19
20 /* Save registers */
21 push ebx
22
23 /* Make the first multiplication */
24 mov eax, [esp+8]
25 mul dword ptr [_KiTimeIncrementReciprocal+4]
26 mov ebx, eax
27 mov ecx, edx
28
29 /* Make the second multiplication */
30 mov eax, [esp+12]
31 mul dword ptr [_KiTimeIncrementReciprocal]
32 add ebx, eax
33 adc ecx, edx
34
35 /* Multiply by the reciprocal */
36 mov eax, [esp+8]
37 mul dword ptr [_KiTimeIncrementReciprocal]
38 mov eax, [esp+12]
39 push edx
40 mul dword ptr [_KiTimeIncrementReciprocal+4]
41 pop edx
42 add edx, ebx
43 adc eax, ecx
44
45 /* Shift the result and generate the index */
46 mov cl, [_KiTimeIncrementShiftCount]
47 shr eax, cl
48 and eax, TIMER_TABLE_SIZE - 1
49
50 /* Return */
51 pop ebx
52 ret 8
53 .endfunc
54
55 .globl _KeUpdateRunTime@4
56 .func KeUpdateRunTime@4
57 _KeUpdateRunTime@4:
58
59 /* Get KPCR */
60 mov eax, [fs:KPCR_SELF]
61
62 /* Save EBX */
63 push ebx
64
65 /* Increase interrupt count */
66 inc dword ptr [eax+KPCR_PRCB_INTERRUPT_COUNT]
67
68 /* Get the current thread and process */
69 mov ebx, [eax+KPCR_CURRENT_THREAD]
70 mov ecx, [ebx+KTHREAD_APCSTATE_PROCESS]
71
72 /* Check if this was V86 or user mode */
73 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
74 jnz NotKern
75 test byte ptr [ebp+KTRAP_FRAME_CS], MODE_MASK
76 jnz NotKern
77
78 /* Increase kernel time */
79 inc dword ptr [eax+KPCR_PRCB_KERNEL_TIME]
80
81 /* Check if IRQL was DISPATCH_LEVEL */
82 cmp byte ptr [esp+8], DISPATCH_LEVEL
83 jb BelowDispatch
84 ja AboveDispatch
85
86 /* Check if the DPC routine is active */
87 cmp dword ptr[eax+KPCR_PRCB_DPC_ROUTINE_ACTIVE], 0
88 jz BelowDispatch
89
90 /* At dispatch, increase DPC time */
91 inc dword ptr [eax+KPCR_PRCB_DPC_TIME]
92 jmp AfterSet
93
94 AboveDispatch:
95 /* Update interrupt time */
96 inc dword ptr [eax+KPCR_PRCB_INTERRUPT_TIME]
97 jmp AfterSet
98
99 BelowDispatch:
100 /* Update kernel time */
101 inc dword ptr [ebx+KTHREAD_KERNEL_TIME]
102 jmp AfterSet
103
104 NotKern:
105 /* Update user time */
106 inc dword ptr [eax+KPCR_PRCB_USER_TIME]
107 inc dword ptr [ebx+KTHREAD_USER_TIME]
108
109 AfterSet:
110 /* Get the DPC Count and last count, and set the ne wone */
111 mov ecx, [eax+KPCR_PRCB_DPC_COUNT]
112 mov edx, [eax+KPCR_PRCB_DPC_LAST_COUNT]
113 mov [eax+KPCR_PRCB_DPC_LAST_COUNT], ecx
114
115 /* Substract counts and add request rate, divide by two to get average */
116 sub ecx, edx
117 add ecx, [eax+KPCR_PRCB_DPC_REQUEST_RATE]
118 shr ecx, 1
119
120 /* Set this as the new request rate */
121 mov [eax+KPCR_PRCB_DPC_REQUEST_RATE], ecx
122
123 /* Check for depth > 0, DPCs to be inactive, and no other pending request */
124 cmp dword ptr [eax+KPCR_PRCB_DPC_QUEUE_DEPTH], 0
125 je DontRequest
126 cmp byte ptr [eax+KPCR_PRCB_DPC_ROUTINE_ACTIVE], 0
127 jnz DontRequest
128 cmp byte ptr [eax+KPCR_PRCB_DPC_INTERRUPT_REQUESTED], 0
129 jnz DontRequest
130
131 /* Request a DPC */
132 mov ecx, DISPATCH_LEVEL
133 call @HalRequestSoftwareInterrupt@4
134
135 /* Restore PCR address */
136 mov eax, [fs:KPCR_SELF]
137
138 /* Get the DPC request rate and threshold adjust, and set it */
139 mov ecx, [eax+KPCR_PRCB_DPC_REQUEST_RATE]
140 mov edx, _KiAdjustDpcThreshold
141 mov [eax+KPCR_PRCB_ADJUST_DPC_THRESHOLD], edx
142
143 /* Check if the rate now is not ideal */
144 cmp ecx, _KiIdealDpcRate
145 jge RateOk
146 cmp dword ptr [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH], 1
147 je RateOk
148
149 /* Fix the depth */
150 dec dword ptr [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH]
151 jmp RateOk
152
153 DontRequest:
154 /* We didn't request a DPC, decrease the threshold */
155 dec dword ptr [eax+KPCR_PRCB_ADJUST_DPC_THRESHOLD]
156 jnz RateOk
157
158 /* We're at 0 now, reset it */
159 mov ecx, _KiAdjustDpcThreshold
160 mov [eax+KPCR_PRCB_ADJUST_DPC_THRESHOLD], ecx
161
162 /* Get maximum depth and check it */
163 mov ecx, _KiMaximumDpcQueueDepth
164 cmp ecx, [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH]
165 je RateOk
166
167 /* Increase it, it's below maximum */
168 inc dword ptr [eax+KPCR_PRCB_MAXIMUM_DPC_QUEUE_DEPTH]
169
170 RateOk:
171 /* Decrement quantum and verify it */
172 sub byte ptr [ebx+KTHREAD_QUANTUM], CLOCK_QUANTUM_DECREMENT
173 jg QuantumNotEmpty
174
175 /* Make sure this isn't the idle thread */
176 cmp ebx, [eax+KPCR_PRCB_IDLE_THREAD]
177 jz QuantumNotEmpty
178
179 /* Set quantum end */
180 mov byte ptr [eax+KPCR_PRCB_QUANTUM_END], 1
181 mov ecx, DISPATCH_LEVEL
182 call @HalRequestSoftwareInterrupt@4
183
184 QuantumNotEmpty:
185 /* Restore ebx and return */
186 pop ebx
187 ret 4
188 .endfunc
189
190 .globl _KeUpdateSystemTime@0
191 .func KeUpdateSystemTime@0
192 _KeUpdateSystemTime@0:
193
194 /* Get shared data in ECX */
195 mov ecx, USER_SHARED_DATA
196
197 /* Get interrupt time */
198 mov edi, [ecx+USER_SHARED_DATA_INTERRUPT_TIME]
199 mov esi, [ecx+USER_SHARED_DATA_INTERRUPT_TIME+4]
200
201 /* Add the increment and get the carry */
202 add edi, eax
203 adc esi, 0
204
205 /* Now store the updated times */
206 mov [ecx+USER_SHARED_DATA_INTERRUPT_TIME+8], esi
207 mov [ecx+USER_SHARED_DATA_INTERRUPT_TIME], edi
208 mov [ecx+USER_SHARED_DATA_INTERRUPT_TIME+4], esi
209
210 /* Substract tick count and get the low count */
211 LOCK sub _KiTickOffset, eax
212 mov eax, _KeTickCount
213 mov ebx, eax
214 jg IncompleteTick
215
216 /* Get shared data in ECX */
217 mov ebx, USER_SHARED_DATA
218
219 /* Get system time */
220 mov ecx, [ebx+USER_SHARED_DATA_SYSTEM_TIME]
221 mov edx, [ebx+USER_SHARED_DATA_SYSTEM_TIME+4]
222
223 /* Add the increment and get the carry */
224 add ecx, _KeTimeAdjustment
225 adc edx, 0
226
227 /* Now store the updated times */
228 mov [ebx+USER_SHARED_DATA_SYSTEM_TIME+8], edx
229 mov [ebx+USER_SHARED_DATA_SYSTEM_TIME], ecx
230 mov [ebx+USER_SHARED_DATA_SYSTEM_TIME+4], edx
231
232 /* Put tick count back in EBX */
233 mov ebx, eax
234
235 /* Copyit in ECX and get hich count */
236 mov ecx, eax
237 mov edx, _KeTickCount + 4
238
239 /* Add the increment and get the carry */
240 add ecx, 1
241 adc edx, 0
242
243 /* Now store the updated tick */
244 mov [_KeTickCount+8], edx
245 mov [_KeTickCount], ecx
246 mov [_KeTickCount+4], edx
247
248 /* Store in in shared data too */
249 mov ds:[USER_SHARED_DATA+USER_SHARED_DATA_TICK_COUNT+8], edx
250 mov ds:[USER_SHARED_DATA+USER_SHARED_DATA_TICK_COUNT], ecx
251 mov ds:[USER_SHARED_DATA+USER_SHARED_DATA_TICK_COUNT+4], edx
252
253 /* FIXME: HACK */
254 mov ds:[USER_SHARED_DATA], ecx
255
256 /* Get hand index and entry into the table */
257 and eax, TIMER_TABLE_SIZE - 1
258 shl eax, 4
259
260 /* Compare the due time */
261 cmp esi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME+4]
262 jb NextHand
263 ja TimerExpired
264 cmp edi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME]
265 jnb TimerExpired
266
267 NextHand:
268 /* Move to the next hand */
269 inc ebx
270 mov eax, ebx
271
272 IncompleteTick:
273
274 /* Get hand index and entry into the table */
275 and eax, TIMER_TABLE_SIZE - 1
276 shl eax, 4
277
278 /* Compare the due time */
279 cmp esi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME+4]
280 jb DebugCheck
281 ja TimerExpired
282 cmp edi, [eax+_KiTimerTableListHead+KTIMER_TABLE_TIME]
283 jb DebugCheck
284
285 TimerExpired:
286
287 /* Check if expiration is active */
288 mov ecx, [fs:KPCR_PRCB]
289 cmp dword ptr [ecx+KPRCB_TIMER_REQUEST], 0
290 jne DebugCheck
291
292 /* It's not, register it */
293 mov [ecx+KPRCB_TIMER_REQUEST], esp
294 mov [ecx+KPRCB_TIMER_HAND], ebx
295 mov ecx, DISPATCH_LEVEL
296 call @HalRequestSoftwareInterrupt@4
297
298 DebugCheck:
299 /* FIXME: Check for KdDebuggerEnabled */
300
301 /* Check if this was a full tick */
302 cmp dword ptr _KiTickOffset, 0
303 jg IncompleteTick2
304
305 /* Increase tick offset */
306 mov eax, _KeMaximumIncrement
307 add _KiTickOffset, eax
308
309 /* Update system run time */
310 push [esp]
311 call _KeUpdateRunTime@4
312 jmp Done
313
314 IncompleteTick2:
315 /* Increase interrupt count */
316 inc dword ptr [fs:KPCR_PRCB_INTERRUPT_COUNT]
317
318 Done:
319 /* Exit the interrupt */
320 cli
321 call _HalEndSystemInterrupt@8
322 jmp _Kei386EoiHelper@0
323 .endfunc