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