Author: tkreuzer
Date: Tue Aug 30 08:52:17 2011
New Revision: 53495
URL:
http://svn.reactos.org/svn/reactos?rev=53495&view=rev
Log:
[HAL]
- Rewrite KeQueryPerformanceCounter in C
- Implement support for changing the clock rate
Modified:
trunk/reactos/hal/halx86/generic/i386/systimer.S
trunk/reactos/hal/halx86/generic/timer.c
Modified: trunk/reactos/hal/halx86/generic/i386/systimer.S
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/hal/halx86/generic/i386/sy…
==============================================================================
--- trunk/reactos/hal/halx86/generic/i386/systimer.S [iso-8859-1] (original)
+++ trunk/reactos/hal/halx86/generic/i386/systimer.S [iso-8859-1] Tue Aug 30 08:52:17
2011
@@ -64,38 +64,38 @@
/* Save EFLAGS and kill interrupts */
pushfd
cli
-
+
/* Get the current interrupt mask on the PICs */
xor eax, eax
in al, PIC2_DATA
shl eax, 8
in al, PIC1_DATA
-
+
/* Save it */
push eax
-
+
/* Now mask everything except the RTC and PIC 2 chain-interrupt */
mov eax, NOT (HEX(04) OR HEX(100))
-
+
/* Program the PICs */
out PIC1_DATA, al
shr eax, 8
out PIC2_DATA, al
-
+
/* Now get the IDT */
sidt [ebp-8]
mov ecx, [ebp-6]
-
+
/* Get the IDT entry for the RTC */
mov eax, HEX(38)
shl eax, 3
add ecx, eax
-
+
/* Save the original RTC ISR */
push [ecx]
push [ecx+4]
push ecx
-
+
/* Now load our new handler */
mov eax, offset OnlyOnePersonCanWriteHalCode
mov [ecx], ax
@@ -106,10 +106,10 @@
/* Reset our counter */
mov dword ptr [ebp-12], 0
-
+
/* Acquire CMOS lock */
call _HalpAcquireSystemHardwareSpinLock@0
-
+
/* Now initialize register A on the CMOS */
mov ax, HEX(2D00) OR CMOS_REGISTER_A
out CMOS_ADDR, al
@@ -117,21 +117,21 @@
mov al, ah
out CMOS_DATA, al
jmp $+2
-
+
/* Read register B */
mov ax, CMOS_REGISTER_B
out CMOS_ADDR, al
jmp $+2
in al, CMOS_DATA
jmp $+2
-
+
/* Don't touch the LastKnownGoodConfig hack */
and al, 1
mov ah, al
-
+
/* Enable the interrupt */
or ah, HEX(42)
-
+
/* Now write the register B */
mov al, CMOS_REGISTER_B
out CMOS_ADDR, al
@@ -139,47 +139,47 @@
mov al, ah
out CMOS_DATA, al
jmp $+2
-
+
/* Read register C */
mov al, CMOS_REGISTER_C
out CMOS_ADDR, al
jmp $+2
in al, CMOS_DATA
jmp $+2
-
+
/* Read register D */
mov al, CMOS_REGISTER_D
out CMOS_ADDR, al
jmp $+2
in al, CMOS_DATA
jmp $+2
-
+
/* Release CMOS lock */
mov dword ptr [ebp-12], 0
call _HalpReleaseCmosSpinLock@0
-
+
/* Initialize looper */
xor eax, eax
-
+
/* Align to 16 bytes */
.align 16
-
+
/* Enable interrupts! */
sti
jmp Looper
-
+
/* Align to 16 bytes */
.align 16
-
+
/* Subtract one count */
Looper:
sub eax, 1
jnz Looper
-
+
/* ASSERT: If we got here, then the RTC never fired */
call _DbgBreakPoint@0
jmp Looper
-
+
OnlyOnePersonCanWriteHalCode:
/*********************** THIS IS THE RTC HANDLER **************************/
@@ -187,17 +187,17 @@
inc dword ptr [ebp-12]
cmp dword ptr [ebp-12], 1
jnz ComputeStall
-
+
/*
* It is the first one -- we'll ignore it, since it fires randomly!
* Get rid of the old return address and push the new one in (our looper)
*/
pop eax
push offset Looper
-
+
/* Acquire CMOS lock */
call _HalpAcquireSystemHardwareSpinLock@0
-
+
/* Now initialize register A on the CMOS */
mov ax, HEX(2D00) OR CMOS_REGISTER_A
out CMOS_ADDR, al
@@ -205,21 +205,21 @@
mov al, ah
out CMOS_DATA, al
jmp $+2
-
+
/* Read register B */
mov ax, CMOS_REGISTER_B
out CMOS_ADDR, al
jmp $+2
in al, CMOS_DATA
jmp $+2
-
+
/* Don't touch the LastKnownGoodConfig hack */
and al, 1
mov ah, al
-
+
/* Enable the interrupt */
or ah, HEX(42)
-
+
/* Now write the register B */
mov al, CMOS_REGISTER_B
out CMOS_ADDR, al
@@ -227,34 +227,34 @@
mov al, ah
out CMOS_DATA, al
jmp $+2
-
+
/* Read register C */
mov al, CMOS_REGISTER_C
out CMOS_ADDR, al
jmp $+2
in al, CMOS_DATA
jmp $+2
-
+
/* Read register D */
mov al, CMOS_REGISTER_D
out CMOS_ADDR, al
jmp $+2
in al, CMOS_DATA
jmp $+2
-
+
/* Release CMOS lock */
call _HalpReleaseCmosSpinLock@0
-
+
/* Dismiss the interrupt */
mov al, PIC_EOI
out PIC2_COMMAND, al
mov al, PIC_SPECIFIC_EOI2
out PIC1_COMMAND, al
-
+
/* Reset the counter and return back to the looper */
xor eax, eax
iretd
-
+
/******************* THIS IS THE 2ND RTC HANDLER **************************/
ComputeStall:
@@ -263,26 +263,26 @@
xor edx, edx
mov ecx, 125000 /* RTC fires every 125 ms */
div ecx
-
+
/* Is the remainder 0? */
cmp edx, 0
jz FoundFactor
-
+
/* Otherwise fix-up the loop count */
inc eax
-
+
FoundFactor:
/* Save the stall scale factor */
mov fs:[KPCR_STALL_SCALE_FACTOR], eax
-
+
/* Prepare for interrupt return */
pop eax
push offset AndItsNotYou
mov eax, HEX(13)
-
+
/* Acquire CMOS lock */
call _HalpAcquireSystemHardwareSpinLock@0
-
+
/* Now initialize register A on the CMOS */
mov ax, HEX(2D00) OR CMOS_REGISTER_A
out CMOS_ADDR, al
@@ -290,21 +290,21 @@
mov al, ah
out CMOS_DATA, al
jmp $+2
-
+
/* Read register B */
mov ax, CMOS_REGISTER_B
out CMOS_ADDR, al
jmp $+2
in al, CMOS_DATA
jmp $+2
-
+
/* Don't touch the LastKnownGoodConfig hack */
and al, 1
mov ah, al
-
+
/* Disable the interrupt */
or ah, 2
-
+
/* Now write the register B */
mov al, CMOS_REGISTER_B
out CMOS_ADDR, al
@@ -312,27 +312,27 @@
mov al, ah
out CMOS_DATA, al
jmp $+2
-
+
/* Read register C */
mov al, CMOS_REGISTER_C
out CMOS_ADDR, al
jmp $+2
in al, CMOS_DATA
jmp $+2
-
+
/* Release CMOS lock */
call _HalpReleaseCmosSpinLock@0
-
+
/* Dismiss the interrupt */
mov al, PIC_EOI
out PIC2_COMMAND, al
mov al, PIC_SPECIFIC_EOI2
out PIC1_COMMAND, al
-
+
/* Disable interrupts on return */
and word ptr [esp+8], NOT EFLAGS_INTERRUPT_MASK
iretd
-
+
/************************* WE ARE BACK FROM RTC ***************************/
AndItsNotYou:
@@ -340,16 +340,16 @@
pop ecx
pop [ecx+4]
pop [ecx]
-
+
/* Restore the mask */
pop eax
out PIC1_DATA, al
shr eax, 8
out PIC2_DATA, al
-
+
/* Restore EFLAGS */
- popf
-
+ popfd
+
/* Restore stack and return */
mov esp, ebp
pop ebp
@@ -368,9 +368,6 @@
mov eax, fs:[KPCR_STALL_SCALE_FACTOR]
mul ecx
- /* Align to 16 bytes */
- .align 16
-
/* Jump to subtraction loop */
jmp SubtractLoop
@@ -387,165 +384,4 @@
ret 4
#endif
-PUBLIC _KeQueryPerformanceCounter@4
-_KeQueryPerformanceCounter@4:
-
- /* Check if we were called too early */
- cmp dword ptr _HalpCurrentRollOver, 0
- je NoCount
-
- /* Save volatiles */
- push ebx
- push esi
-
-LoopPreInt:
-
- /* Disable interrupts */
- pushf
- cli
-
-LoopPostInt:
-
- /* Get the current value */
- mov ebx, dword ptr _HalpPerfCounterLow
- mov esi, dword ptr _HalpPerfCounterHigh
-
- /* Read 8254 timer */
- mov al, 0 /* Interrupt on terminal count */
- out PIT_MODE, al
- in al, SYSTEM_CTRL_PORT_A
- or al, byte ptr _HalpPerfCounterCutoff
- out SYSTEM_CTRL_PORT_A, al
- jmp $+2
- in al, PIT_CH0
- jmp $+2
- movzx ecx, al
- in al, PIT_CH0
- mov ch, al
-
- /* Enable interrupts and do a short wait */
- popf
- nop
- jmp $+2
-
- /* Disable them again */
- pushf
- cli
-
- /* Get the counter value again */
- mov eax, dword ptr _HalpPerfCounterLow
- mov edx, dword ptr _HalpPerfCounterHigh
-
- /* Check if someone updated the counter */
- cmp eax, ebx
- jnz LoopPostInt
- cmp edx, esi
- jnz LoopPostInt
-
- /* Check if the current 8254 value causes rollover */
- neg ecx
- add ecx, dword ptr _HalpCurrentRollOver
- jnb DoRollOver
-
-SetSum:
-
- /* Calculate the sum */
- add eax, ecx
- adc edx, 0
-
- /* Check if we're above or below the last high value */
- cmp edx, dword ptr _HalpLastPerfCounterHigh
- jb short BelowHigh
- jnz short BelowLow
-
- /* Check if we're above or below the last low value */
- cmp eax, dword ptr _HalpLastPerfCounterLow
- jb BelowHigh
-
-BelowLow:
-
- /* Update the last value and bring back interrupts */
- mov dword ptr _HalpLastPerfCounterLow, eax
- mov dword ptr _HalpLastPerfCounterHigh, edx
- popf
-
- /* Check if caller wants frequency */
- cmp dword ptr [esp+12], 0
- jz ReturnNoFreq
-
- /* Save hard-coded frequency */
- mov ecx, dword ptr [esp+12]
- mov dword ptr [ecx], 1193182
- mov dword ptr [ecx+4], 0
-
-ReturnNoFreq:
-
- /* Restore volatiles */
- pop esi
- pop ebx
- ret 4
-
-NoCount:
-
- /* Return empty, called too soon */
- mov eax, 0
- mov edx, 0
- ret 4
-
-DoRollOver:
-
- /* We might have an incoming interrupt, save EFLAGS and reset rollover */
- mov esi, [esp]
- mov ecx, dword ptr _HalpCurrentRollOver
- popf
-
- /* Check if interrupts were enabled and try again */
- test esi, EFLAGS_INTERRUPT_MASK
- jnz LoopPreInt
-
- /* They're not, continue where we left */
- pushf
- jmp SetSum
-
-BelowHigh:
-
- /* Get the last counter values */
- mov ebx, dword ptr _HalpLastPerfCounterLow
- mov esi, dword ptr _HalpLastPerfCounterHigh
-
- /* Check if the previous value was 0 and go back if yes */
- mov ecx, ebx
- or ecx, esi
- jz BelowLow
-
- /* Make sure that the count is still valid */
- sub ebx, eax
- sbb esi, edx
- jnz InvalidCount
- cmp ebx, dword ptr _HalpCurrentRollOver
- jg InvalidCount
-
- /* Fixup the count with the last known value */
- sub eax, ebx
- sbb edx, esi
-
- /* We might have an incoming interrupt, save EFLAGS */
- mov ecx, [esp]
- popf
-
- /* Check if interrupts were enabled and try again */
- test ecx, EFLAGS_INTERRUPT_MASK
- jnz LoopPreInt
-
- /* They're not, continue where we left */
- pushf
- jmp BelowLow
-
-InvalidCount:
- popf
- xor eax, eax
- mov dword ptr _HalpLastPerfCounterLow, eax
- mov dword ptr _HalpLastPerfCounterHigh, eax
- jmp LoopPreInt
-
END
Modified: trunk/reactos/hal/halx86/generic/timer.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/hal/halx86/generic/timer.c…
==============================================================================
--- trunk/reactos/hal/halx86/generic/timer.c [iso-8859-1] (original)
+++ trunk/reactos/hal/halx86/generic/timer.c [iso-8859-1] Tue Aug 30 08:52:17 2011
@@ -4,6 +4,7 @@
* FILE: hal/halx86/generic/timer.c
* PURPOSE: HAL Timer Routines
* PROGRAMMERS: Alex Ionescu (alex.ionescu(a)reactos.org)
+ * Timo Kreuzer (timo.kreuzer(a)reactos.org)
*/
/* INCLUDES ******************************************************************/
@@ -14,6 +15,9 @@
/* GLOBALS *******************************************************************/
+#define PIT_LATCH 0x00
+
+LARGE_INTEGER HalpLastPerfCounter;
ULONG HalpPerfCounterCutoff;
BOOLEAN HalpClockSetMSRate;
ULONG HalpCurrentTimeIncrement;
@@ -21,53 +25,55 @@
ULONG HalpNextMSRate = 14;
ULONG HalpLargestClockMS = 15;
-LARGE_INTEGER HalpRolloverTable[15] =
-{
- {{1197, 10032}},
- {{2394, 20064}},
- {{3591, 30096}},
- {{4767, 39952}},
- {{5964, 49984}},
- {{7161, 60016}},
- {{8358, 70048}},
- {{9555, 80080}},
- {{10731, 89936}},
- {{11949, 100144}},
- {{13125, 110000}},
- {{14322, 120032}},
- {{15519, 130064}},
- {{16695, 139920}},
- {{17892, 149952}}
+static struct _HALP_ROLLOVER
+{
+ ULONG RollOver;
+ ULONG Increment;
+} HalpRolloverTable[15] =
+{
+ {1197, 10032},
+ {2394, 20064},
+ {3591, 30096},
+ {4767, 39952},
+ {5964, 49984},
+ {7161, 60016},
+ {8358, 70048},
+ {9555, 80080},
+ {10731, 89936},
+ {11949, 100144},
+ {13125, 110000},
+ {14322, 120032},
+ {15519, 130064},
+ {16695, 139920},
+ {17892, 149952}
};
/* PRIVATE FUNCTIONS *********************************************************/
-VOID
-NTAPI
-INIT_FUNCTION
-HalpInitializeClock(VOID)
-{
- PKPRCB Prcb = KeGetCurrentPrcb();
- ULONG Increment;
- USHORT RollOver;
+FORCEINLINE
+ULONG
+HalpRead8254Value(void)
+{
+ ULONG TimerValue;
+
+ /* Send counter latch command for channel 0 */
+ __outbyte(TIMER_CONTROL_PORT, PIT_LATCH);
+ __nop();
+
+ /* Read the value, LSB first */
+ TimerValue = __inbyte(TIMER_CHANNEL0_DATA_PORT);
+ __nop();
+ TimerValue |= __inbyte(TIMER_CHANNEL0_DATA_PORT) << 8;
+
+ return TimerValue;
+}
+
+VOID
+NTAPI
+HalpSetTimerRollOver(USHORT RollOver)
+{
ULONG_PTR Flags;
TIMER_CONTROL_PORT_REGISTER TimerControl;
-
- /* Check the CPU Type */
- if (Prcb->CpuType <= 4)
- {
- /* 486's or equal can't go higher then 10ms */
- HalpLargestClockMS = 10;
- HalpNextMSRate = 9;
- }
-
- /* Get increment and rollover for the largest time clock ms possible */
- Increment = HalpRolloverTable[HalpLargestClockMS - 1].HighPart;
- RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].LowPart;
-
- /* Set the maximum and minimum increment with the kernel */
- HalpCurrentTimeIncrement = Increment;
- KeSetTimeIncrement(Increment, HalpRolloverTable[0].HighPart);
/* Disable interrupts */
Flags = __readeflags();
@@ -98,9 +104,31 @@
/* Restore interrupts if they were previously enabled */
__writeeflags(Flags);
-
- /* Save rollover and return */
+}
+
+VOID
+NTAPI
+INIT_FUNCTION
+HalpInitializeClock(VOID)
+{
+ ULONG Increment;
+ USHORT RollOver;
+
+ DPRINT("HalpInitializeClock()\n");
+
+ /* Get increment and rollover for the largest time clock ms possible */
+ Increment = HalpRolloverTable[HalpLargestClockMS - 1].Increment;
+ RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].RollOver;
+
+ /* Set the maximum and minimum increment with the kernel */
+ KeSetTimeIncrement(Increment, HalpRolloverTable[0].Increment);
+
+ /* Set the rollover value for the timer */
+ HalpSetTimerRollOver(RollOver);
+
+ /* Save rollover and increment */
HalpCurrentRollOver = RollOver;
+ HalpCurrentTimeIncrement = Increment;
}
#ifdef _M_IX86
@@ -109,6 +137,7 @@
FASTCALL
HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
{
+ ULONG LastIncrement;
KIRQL Irql;
/* Enter trap */
@@ -121,16 +150,25 @@
HalpPerfCounter.QuadPart += HalpCurrentRollOver;
HalpPerfCounterCutoff = KiEnableTimerWatchdog;
+ /* Save increment */
+ LastIncrement = HalpCurrentTimeIncrement;
+
/* Check if someone changed the time rate */
if (HalpClockSetMSRate)
{
- /* Not yet supported */
- UNIMPLEMENTED;
- ASSERT(FALSE);
+ /* Update the global values */
+ HalpCurrentTimeIncrement = HalpRolloverTable[HalpNextMSRate - 1].Increment;
+ HalpCurrentRollOver = HalpRolloverTable[HalpNextMSRate - 1].RollOver;
+
+ /* Set new timer rollover */
+ HalpSetTimerRollOver((USHORT)HalpCurrentRollOver);
+
+ /* We're done */
+ HalpClockSetMSRate = FALSE;
}
/* Update the system time -- the kernel will exit this trap */
- KeUpdateSystemTime(TrapFrame, HalpCurrentTimeIncrement, Irql);
+ KeUpdateSystemTime(TrapFrame, LastIncrement, Irql);
}
/* Spurious, just end the interrupt */
@@ -206,7 +244,58 @@
HalpClockSetMSRate = TRUE;
/* Return the increment */
- return HalpRolloverTable[Increment - 1].HighPart;
+ return HalpRolloverTable[Increment - 1].Increment;
+}
+
+LARGE_INTEGER
+NTAPI
+KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFrequency)
+{
+ LARGE_INTEGER CurrentPerfCounter;
+ ULONG CounterValue, ClockDelta;
+
+ /* If caller wants performance frequency, return hardcoded value */
+ if (PerformanceFrequency) PerformanceFrequency->QuadPart = PIT_FREQUENCY;
+
+ /* Check if we were called too early */
+ if (HalpCurrentRollOver == 0) return HalpPerfCounter;
+
+ /* Check if interrupts are disabled */
+ if(!(__readeflags() & EFLAGS_INTERRUPT_MASK)) return HalpPerfCounter;
+
+ do
+ {
+ /* Get the current performance counter value */
+ CurrentPerfCounter = HalpPerfCounter;
+
+ /* Read the 8254 counter value */
+ CounterValue = HalpRead8254Value();
+
+ /* Repeat if the value has changed (a clock interrupt happened) */
+ } while (CurrentPerfCounter.QuadPart != HalpPerfCounter.QuadPart);
+
+ /* After someone changed the clock rate, during the first clock cycle we
+ might see a counter value larger than the rollover. In this case we
+ pretend it already has the new rollover value. */
+ if (CounterValue > HalpCurrentRollOver) CounterValue = HalpCurrentRollOver;
+
+ /* The interrupt is issued on the falling edge of the OUT line, when the
+ counter changes from 1 to max. Calculate a clock delta, so that directly
+ after the interrupt it is 0, going up to (HalpCurrentRollOver - 1). */
+ ClockDelta = HalpCurrentRollOver - CounterValue;
+
+ /* Add the clock delta */
+ CurrentPerfCounter.QuadPart += ClockDelta;
+
+ /* This must be true unless HalpPerfCounter has changed sign,
+ which takes approximately 245,118 years */
+ ASSERT(CurrentPerfCounter.QuadPart >= HalpLastPerfCounter.QuadPart);
+
+ /* Update the last counter value */
+ HalpLastPerfCounter = CurrentPerfCounter;
+
+ /* Return the result */
+ return CurrentPerfCounter;
}
/* EOF */