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)
8 /* INCLUDES ******************************************************************/
14 EXTERN _HalpAcquireSystemHardwareSpinLock@0:PROC
15 EXTERN _HalpReleaseCmosSpinLock@0:PROC
16 EXTERN _DbgBreakPoint@0:PROC
17 EXTERN _HalpCurrentRollOver:DWORD
18 EXTERN _HalpPerfCounterCutoff:DWORD
20 #define PIC1_BASE HEX(20) /* IO base address for master PIC */
21 #define PIC2_BASE HEX(A0) /* IO base address for slave PIC */
22 #define PIC1_COMMAND PIC1_BASE
23 #define PIC1_DATA (PIC1_BASE+1)
24 #define PIC2_COMMAND PIC2_BASE
25 #define PIC2_DATA (PIC2_BASE+1)
26 #define PIC_EOI HEX(20)
27 #define PIC_SPECIFIC_EOI2 HEX(62)
29 #define CMOS_ADDR HEX(70)
30 #define CMOS_DATA HEX(71)
31 #define CMOS_REGISTER_A HEX(0A)
32 #define CMOS_REGISTER_B HEX(0B)
33 #define CMOS_REGISTER_C HEX(0C)
34 #define CMOS_REGISTER_D HEX(0D)
36 #define PIT_CH0 HEX(40)
37 #define PIT_MODE HEX(43)
38 #define SYSTEM_CTRL_PORT_A HEX(92)
40 /* GLOBALS *******************************************************************/
43 ASSUME CS:NOTHING, DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING
45 PUBLIC _HalpPerfCounter
46 _HalpLastPerfCounterLow: .long 0
47 _HalpLastPerfCounterHigh: .long 0
49 _HalpPerfCounterLow: .long 0
50 _HalpPerfCounterHigh: .long 0
51 _HalpSystemHardwareFlags: .long 0
53 /* FUNCTIONS *****************************************************************/
56 PUBLIC _HalpCalibrateStallExecution@0
57 _HalpCalibrateStallExecution@0:
59 /* Setup the stack frame */
64 /* Save EFLAGS and kill interrupts */
68 /* Get the current interrupt mask on the PICs */
77 /* Now mask everything except the RTC and PIC 2 chain-interrupt */
78 mov eax, NOT (HEX(04) OR HEX(100))
80 /* Program the PICs */
89 /* Get the IDT entry for the RTC */
94 /* Save the original RTC ISR */
99 /* Now load our new handler */
100 mov eax, offset OnlyOnePersonCanWriteHalCode
102 mov word ptr [ecx+2], KGDT_R0_CODE
103 mov word ptr [ecx+4], HEX(08E00)
107 /* Reset our counter */
108 mov dword ptr [ebp-12], 0
110 /* Acquire CMOS lock */
111 call _HalpAcquireSystemHardwareSpinLock@0
113 /* Now initialize register A on the CMOS */
114 mov ax, HEX(2D00) OR CMOS_REGISTER_A
121 /* Read register B */
122 mov ax, CMOS_REGISTER_B
128 /* Don't touch the LastKnownGoodConfig hack */
132 /* Enable the interrupt */
135 /* Now write the register B */
136 mov al, CMOS_REGISTER_B
143 /* Read register C */
144 mov al, CMOS_REGISTER_C
150 /* Read register D */
151 mov al, CMOS_REGISTER_D
157 /* Release CMOS lock */
158 mov dword ptr [ebp-12], 0
159 call _HalpReleaseCmosSpinLock@0
161 /* Initialize looper */
164 /* Align to 16 bytes */
167 /* Enable interrupts! */
171 /* Align to 16 bytes */
174 /* Subtract one count */
179 /* ASSERT: If we got here, then the RTC never fired */
180 call _DbgBreakPoint@0
183 OnlyOnePersonCanWriteHalCode:
184 /*********************** THIS IS THE RTC HANDLER **************************/
186 /* Increment the interrupt count and check if this is the first one */
187 inc dword ptr [ebp-12]
188 cmp dword ptr [ebp-12], 1
192 * It is the first one -- we'll ignore it, since it fires randomly!
193 * Get rid of the old return address and push the new one in (our looper)
198 /* Acquire CMOS lock */
199 call _HalpAcquireSystemHardwareSpinLock@0
201 /* Now initialize register A on the CMOS */
202 mov ax, HEX(2D00) OR CMOS_REGISTER_A
209 /* Read register B */
210 mov ax, CMOS_REGISTER_B
216 /* Don't touch the LastKnownGoodConfig hack */
220 /* Enable the interrupt */
223 /* Now write the register B */
224 mov al, CMOS_REGISTER_B
231 /* Read register C */
232 mov al, CMOS_REGISTER_C
238 /* Read register D */
239 mov al, CMOS_REGISTER_D
245 /* Release CMOS lock */
246 call _HalpReleaseCmosSpinLock@0
248 /* Dismiss the interrupt */
251 mov al, PIC_SPECIFIC_EOI2
254 /* Reset the counter and return back to the looper */
258 /******************* THIS IS THE 2ND RTC HANDLER **************************/
261 /* Do the calculation */
264 mov ecx, 125000 /* RTC fires every 125 ms */
267 /* Is the remainder 0? */
271 /* Otherwise fix-up the loop count */
275 /* Save the stall scale factor */
276 mov fs:[KPCR_STALL_SCALE_FACTOR], eax
278 /* Prepare for interrupt return */
280 push offset AndItsNotYou
283 /* Acquire CMOS lock */
284 call _HalpAcquireSystemHardwareSpinLock@0
286 /* Now initialize register A on the CMOS */
287 mov ax, HEX(2D00) OR CMOS_REGISTER_A
294 /* Read register B */
295 mov ax, CMOS_REGISTER_B
301 /* Don't touch the LastKnownGoodConfig hack */
305 /* Disable the interrupt */
308 /* Now write the register B */
309 mov al, CMOS_REGISTER_B
316 /* Read register C */
317 mov al, CMOS_REGISTER_C
323 /* Release CMOS lock */
324 call _HalpReleaseCmosSpinLock@0
326 /* Dismiss the interrupt */
329 mov al, PIC_SPECIFIC_EOI2
332 /* Disable interrupts on return */
333 and word ptr [esp+8], NOT EFLAGS_INTERRUPT_MASK
336 /************************* WE ARE BACK FROM RTC ***************************/
339 /* Restore the IDT */
344 /* Restore the mask */
353 /* Restore stack and return */
360 PUBLIC _KeStallExecutionProcessor@4
361 _KeStallExecutionProcessor@4:
363 /* Get the number of microseconds required */
367 /* Multiply by the stall factor */
368 mov eax, fs:[KPCR_STALL_SCALE_FACTOR]
371 /* Align to 16 bytes */
374 /* Jump to subtraction loop */
377 /* Align to 16 bytes */
380 /* Subtract one count */
390 PUBLIC _KeQueryPerformanceCounter@4
391 _KeQueryPerformanceCounter@4:
393 /* Check if we were called too early */
394 cmp dword ptr _HalpCurrentRollOver, 0
403 /* Disable interrupts */
409 /* Get the current value */
410 mov ebx, dword ptr _HalpPerfCounterLow
411 mov esi, dword ptr _HalpPerfCounterHigh
413 /* Read 8254 timer */
414 mov al, 0 /* Interrupt on terminal count */
416 in al, SYSTEM_CTRL_PORT_A
417 or al, byte ptr _HalpPerfCounterCutoff
418 out SYSTEM_CTRL_PORT_A, al
426 /* Enable interrupts and do a short wait */
431 /* Disable them again */
435 /* Get the counter value again */
436 mov eax, dword ptr _HalpPerfCounterLow
437 mov edx, dword ptr _HalpPerfCounterHigh
439 /* Check if someone updated the counter */
445 /* Check if the current 8254 value causes rollover */
447 add ecx, dword ptr _HalpCurrentRollOver
452 /* Calculate the sum */
456 /* Check if we're above or below the last high value */
457 cmp edx, dword ptr _HalpLastPerfCounterHigh
461 /* Check if we're above or below the last low value */
462 cmp eax, dword ptr _HalpLastPerfCounterLow
467 /* Update the last value and bring back interrupts */
468 mov dword ptr _HalpLastPerfCounterLow, eax
469 mov dword ptr _HalpLastPerfCounterHigh, edx
472 /* Check if caller wants frequency */
473 cmp dword ptr [esp+12], 0
476 /* Save hard-coded frequency */
477 mov ecx, dword ptr [esp+12]
478 mov dword ptr [ecx], 1193182
479 mov dword ptr [ecx+4], 0
483 /* Restore volatiles */
490 /* Return empty, called too soon */
497 /* We might have an incoming interrupt, save EFLAGS and reset rollover */
499 mov ecx, dword ptr _HalpCurrentRollOver
502 /* Check if interrupts were enabled and try again */
503 test esi, EFLAGS_INTERRUPT_MASK
506 /* They're not, continue where we left */
512 /* Get the last counter values */
513 mov ebx, dword ptr _HalpLastPerfCounterLow
514 mov esi, dword ptr _HalpLastPerfCounterHigh
516 /* Check if the previous value was 0 and go back if yes */
521 /* Make sure that the count is still valid */
525 cmp ebx, dword ptr _HalpCurrentRollOver
528 /* Fixup the count with the last known value */
532 /* We might have an incoming interrupt, save EFLAGS */
536 /* Check if interrupts were enabled and try again */
537 test ecx, EFLAGS_INTERRUPT_MASK
540 /* They're not, continue where we left */
547 mov dword ptr _HalpLastPerfCounterLow, eax
548 mov dword ptr _HalpLastPerfCounterHigh, eax