1a5bb45a1b8946c07ab5fa29d19ab55286b6dbf7
[reactos.git] / hal / halx86 / generic / i386 / systimer.S
1 /*
2 * FILE: hal/halx86/generic/timer.S
3 * COPYRIGHT: See COPYING in the top level directory
4 * PURPOSE: System Timer Interrupt and Management
5 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
6 */
7
8 /* INCLUDES ******************************************************************/
9
10 #include <asm.h>
11 .intel_syntax noprefix
12
13 /* GLOBALS *******************************************************************/
14
15 .globl _HalpPerfCounter
16 _HalpLastPerfCounterLow: .long 0
17 _HalpLastPerfCounterHigh: .long 0
18 _HalpPerfCounter:
19 _HalpPerfCounterLow: .long 0
20 _HalpPerfCounterHigh: .long 0
21 _HalpSystemHardwareFlags: .long 0
22
23 /* FUNCTIONS *****************************************************************/
24
25 .global _HalpCalibrateStallExecution@0
26 .func HalpCalibrateStallExecution@0
27 _HalpCalibrateStallExecution@0:
28
29 /* Setup the stack frame */
30 push ebp
31 mov ebp, esp
32 sub esp, 12
33
34 /* Save EFLAGS and kill interrupts */
35 pushf
36 cli
37
38 /* Get the current interrupt mask on the PICs */
39 xor eax, eax
40 in al, 0xA1
41 shl eax, 8
42 in al, 0x21
43
44 /* Save it */
45 push eax
46
47 /* Now mask everything except the RTC and PIC 2 chain-interrupt */
48 mov eax, ~((1 << 2) | (1 << 8))
49
50 /* Program the PICs */
51 out 0x21, al
52 shr eax, 8
53 out 0xA1, al
54
55 /* Now get the IDT */
56 sidt [ebp-8]
57 mov ecx, [ebp-6]
58
59 /* Get the IDT entry for the RTC */
60 mov eax, 0x38
61 shl eax, 3
62 add ecx, eax
63
64 /* Save the original RTC ISR */
65 push [ecx]
66 push [ecx+4]
67 push ecx
68
69 /* Now load our new handler */
70 mov eax, offset OnlyOnePersonCanWriteHalCode
71 mov [ecx], ax
72 mov word ptr [ecx+2], KGDT_R0_CODE
73 mov word ptr [ecx+4], 0x8E00
74 shr eax, 16
75 mov [ecx+6], ax
76
77 /* Reset our counter */
78 mov dword ptr [ebp-12], 0
79
80 /* Acquire CMOS lock */
81 call _HalpAcquireSystemHardwareSpinLock@0
82
83 /* Now initialize register A on the CMOS */
84 mov ax, (0x2D << 8) | 0xA
85 out 0x70, al
86 jmp $+2
87 mov al, ah
88 out 0x71, al
89 jmp $+2
90
91 /* Read register B */
92 mov ax, 0xB
93 out 0x70, al
94 jmp $+2
95 in al, 0x71
96 jmp $+2
97
98 /* Don't touch the LastKnownGoodConfig hack */
99 and al, 1
100 mov ah, al
101
102 /* Enable the interrupt */
103 or ah, 0x42
104
105 /* Now write the register B */
106 mov al, 0xB
107 out 0x70, al
108 jmp $+2
109 mov al, ah
110 out 0x71, al
111 jmp $+2
112
113 /* Read register C */
114 mov al, 0xC
115 out 0x70, al
116 jmp $+2
117 in al, 0x71
118 jmp $+2
119
120 /* Read register D */
121 mov al, 0xD
122 out 0x70, al
123 jmp $+2
124 in al, 0x71
125 jmp $+2
126
127 /* Release CMOS lock */
128 mov dword ptr [ebp-12], 0
129 call _HalpReleaseCmosSpinLock@0
130
131 /* Initialize looper */
132 xor eax, eax
133
134 /* Align to 16 bytes */
135 .align 16
136
137 /* Enable interrupts! */
138 sti
139 jmp Looper
140
141 /* Align to 16 bytes */
142 .align 16
143
144 /* Subtract one count */
145 Looper:
146 sub eax, 1
147 jnz Looper
148
149 /* ASSERT: If we got here, then the RTC never fired */
150 call _DbgBreakPoint@0
151 jmp Looper
152
153 OnlyOnePersonCanWriteHalCode:
154 /*********************** THIS IS THE RTC HANDLER **************************/
155
156 /* Increment the interrupt count and check if this is the first one */
157 inc dword ptr [ebp-12]
158 cmp dword ptr [ebp-12], 1
159 jnz ComputeStall
160
161 /*
162 * It is the first one -- we'll ignore it, since it fires randomly!
163 * Get rid of the old return address and push the new one in (our looper)
164 */
165 pop eax
166 push offset Looper
167
168 /* Acquire CMOS lock */
169 call _HalpAcquireSystemHardwareSpinLock@0
170
171 /* Now initialize register A on the CMOS */
172 mov ax, (0x2D << 8) | 0xA
173 out 0x70, al
174 jmp $+2
175 mov al, ah
176 out 0x71, al
177 jmp $+2
178
179 /* Read register B */
180 mov ax, 0xB
181 out 0x70, al
182 jmp $+2
183 in al, 0x71
184 jmp $+2
185
186 /* Don't touch the LastKnownGoodConfig hack */
187 and al, 1
188 mov ah, al
189
190 /* Enable the interrupt */
191 or ah, 0x42
192
193 /* Now write the register B */
194 mov al, 0xB
195 out 0x70, al
196 jmp $+2
197 mov al, ah
198 out 0x71, al
199 jmp $+2
200
201 /* Read register C */
202 mov al, 0xC
203 out 0x70, al
204 jmp $+2
205 in al, 0x71
206 jmp $+2
207
208 /* Read register D */
209 mov al, 0xD
210 out 0x70, al
211 jmp $+2
212 in al, 0x71
213 jmp $+2
214
215 /* Release CMOS lock */
216 call _HalpReleaseCmosSpinLock@0
217
218 /* Dismiss the interrupt */
219 mov al, 0x20
220 out 0xA0, al
221 mov al, 0x62
222 out 0x20, al
223
224 /* Reset the counter and return back to the looper */
225 xor eax, eax
226 iretd
227
228 /******************* THIS IS THE 2ND RTC HANDLER **************************/
229 ComputeStall:
230
231 /* Do the calculation */
232 neg eax
233 xor edx, edx
234 mov ecx, 125000 /* RTC fires every 125 ms */
235 div ecx
236
237 /* Is the remainder 0? */
238 cmp edx, 0
239 jz FoundFactor
240
241 /* Otherwise fix-up the loop count */
242 inc eax
243
244 FoundFactor:
245 /* Save the stall scale factor */
246 mov fs:[KPCR_STALL_SCALE_FACTOR], eax
247
248 /* Prepare for interrupt return */
249 pop eax
250 push offset AndItsNotYou
251 mov eax, 0x13
252
253 /* Acquire CMOS lock */
254 call _HalpAcquireSystemHardwareSpinLock@0
255
256 /* Now initialize register A on the CMOS */
257 mov ax, (0x2D << 8) | 0xA
258 out 0x70, al
259 jmp $+2
260 mov al, ah
261 out 0x71, al
262 jmp $+2
263
264 /* Read register B */
265 mov ax, 0xB
266 out 0x70, al
267 jmp $+2
268 in al, 0x71
269 jmp $+2
270
271 /* Don't touch the LastKnownGoodConfig hack */
272 and al, 1
273 mov ah, al
274
275 /* Disable the interrupt */
276 or ah, 0x2
277
278 /* Now write the register B */
279 mov al, 0xB
280 out 0x70, al
281 jmp $+2
282 mov al, ah
283 out 0x71, al
284 jmp $+2
285
286 /* Read register C */
287 mov al, 0xC
288 out 0x70, al
289 jmp $+2
290 in al, 0x71
291 jmp $+2
292
293 /* Release CMOS lock */
294 call _HalpReleaseCmosSpinLock@0
295
296 /* Dismiss the interrupt */
297 mov al, 0x20
298 out 0xA0, al
299 mov al, 0x62
300 out 0x20, al
301
302 /* Disable interrupts on return */
303 and word ptr [esp+8], ~EFLAGS_INTERRUPT_MASK
304 iretd
305
306 /************************* WE ARE BACK FROM RTC ***************************/
307 AndItsNotYou:
308
309 /* Restore the IDT */
310 pop ecx
311 pop [ecx+4]
312 pop [ecx]
313
314 /* Restore the mask */
315 pop eax
316 out 0x21, al
317 shr eax, 8
318 out 0xA1, al
319
320 /* Restore EFLAGS */
321 popf
322
323 /* Restore stack and return */
324 mov esp, ebp
325 pop ebp
326 ret
327 .endfunc
328
329 #ifndef _MINIHAL_
330 .globl _KeStallExecutionProcessor@4
331 .func KeStallExecutionProcessor@4
332 _KeStallExecutionProcessor@4:
333
334 /* Get the number of microseconds required */
335 mov ecx, [esp+4]
336 jecxz Done
337
338 /* Multiply by the stall factor */
339 mov eax, fs:[KPCR_STALL_SCALE_FACTOR]
340 mul ecx
341
342 /* Align to 16 bytes */
343 .align 16
344
345 /* Jump to subtraction loop */
346 jmp SubtractLoop
347
348 /* Align to 16 bytes */
349 .align 16
350
351 /* Subtract one count */
352 SubtractLoop:
353 sub eax, 1
354 jnz SubtractLoop
355
356 Done:
357 /* Return */
358 ret 4
359 .endfunc
360 #endif
361
362 .global _KeQueryPerformanceCounter@4
363 .func KeQueryPerformanceCounter@4
364 _KeQueryPerformanceCounter@4:
365
366 /* Check if we were called too early */
367 cmp dword ptr _HalpCurrentRollOver, 0
368 je NoCount
369
370 /* Save volatiles */
371 push ebx
372 push esi
373
374 LoopPreInt:
375
376 /* Disable interrupts */
377 pushf
378 cli
379
380 LoopPostInt:
381
382 /* Get the current value */
383 mov ebx, _HalpPerfCounterLow
384 mov esi, _HalpPerfCounterHigh
385
386 /* Read 8254 timer */
387 mov al, 0
388 out 0x43, al
389 in al, 0x92
390 or al, _HalpPerfCounterCutoff
391 out 0x92, al
392 jmp $+2
393 in al, 0x40
394 jmp $+2
395 movzx ecx, al
396 in al, 0x40
397 mov ch, al
398
399 /* Enable interrupts and do a short wait */
400 popf
401 nop
402 jmp $+2
403
404 /* Disable them again */
405 pushf
406 cli
407
408 /* Get the counter value again */
409 mov eax, _HalpPerfCounterLow
410 mov edx, _HalpPerfCounterHigh
411
412 /* Check if someone updated the counter */
413 cmp eax, ebx
414 jnz LoopPostInt
415 cmp edx, esi
416 jnz LoopPostInt
417
418 /* Check if the current 8254 value causes rollover */
419 neg ecx
420 add ecx, _HalpCurrentRollOver
421 jnb DoRollOver
422
423 SetSum:
424
425 /* Calculate the sum */
426 add eax, ecx
427 adc edx, 0
428
429 /* Check if we're above or below the last high value */
430 cmp edx, _HalpLastPerfCounterHigh
431 jb short BelowHigh
432 jnz short BelowLow
433
434 /* Check if we're above or below the last low value */
435 cmp eax, _HalpLastPerfCounterLow
436 jb BelowHigh
437
438 BelowLow:
439
440 /* Update the last value and bring back interrupts */
441 mov _HalpLastPerfCounterLow, eax
442 mov _HalpLastPerfCounterHigh, edx
443 popf
444
445 /* Check if caller wants frequency */
446 cmp dword ptr [esp+12], 0
447 jz ReturnNoFreq
448
449 /* Save hard-coded frequency */
450 mov ecx, dword ptr [esp+12]
451 mov dword ptr [ecx], 1193182
452 mov dword ptr [ecx+4], 0
453
454 ReturnNoFreq:
455
456 /* Restore volatiles */
457 pop esi
458 pop ebx
459 ret 4
460
461 NoCount:
462
463 /* Return empty, called too soon */
464 mov eax, 0
465 mov edx, 0
466 ret 4
467
468 DoRollOver:
469
470 /* We might have an incoming interrupt, save EFLAGS and reset rollover */
471 mov esi, [esp]
472 mov ecx, _HalpCurrentRollOver
473 popf
474
475 /* Check if interrupts were enabled and try again */
476 test esi, EFLAGS_INTERRUPT_MASK
477 jnz LoopPreInt
478
479 /* They're not, continue where we left */
480 pushf
481 jmp SetSum
482
483 BelowHigh:
484
485 /* Get the last counter values */
486 mov ebx, _HalpLastPerfCounterLow
487 mov esi, _HalpLastPerfCounterHigh
488
489 /* Check if the previous value was 0 and go back if yes */
490 mov ecx, ebx
491 or ecx, esi
492 jz BelowLow
493
494 /* Make sure that the count is still valid */
495 sub ebx, eax
496 sbb esi, edx
497 jnz InvalidCount
498 cmp ebx, _HalpCurrentRollOver
499 jg InvalidCount
500
501 /* Fixup the count with the last known value */
502 sub eax, ebx
503 sbb edx, esi
504
505 /* We might have an incoming interrupt, save EFLAGS */
506 mov ecx, [esp]
507 popf
508
509 /* Check if interrupts were enabled and try again */
510 test ecx, EFLAGS_INTERRUPT_MASK
511 jnz LoopPreInt
512
513 /* They're not, continue where we left */
514 pushf
515 jmp BelowLow
516
517 InvalidCount:
518 popf
519 xor eax, eax
520 mov _HalpLastPerfCounterLow, eax
521 mov _HalpLastPerfCounterHigh, eax
522 jmp LoopPreInt
523 .endfunc