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