- Rewrite KeQueryPerformanceCounter in C
- Implement support for changing the clock rate

svn path=/trunk/; revision=53495
This commit is contained in:
Timo Kreuzer 2011-08-30 08:52:17 +00:00
parent 79517b3d28
commit e03d0348d9
2 changed files with 188 additions and 263 deletions

View file

@ -64,38 +64,38 @@ _HalpCalibrateStallExecution@0:
/* 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 @@ _HalpCalibrateStallExecution@0:
/* 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 @@ _HalpCalibrateStallExecution@0:
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 @@ _HalpCalibrateStallExecution@0:
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 @@ OnlyOnePersonCanWriteHalCode:
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 @@ OnlyOnePersonCanWriteHalCode:
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 @@ OnlyOnePersonCanWriteHalCode:
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 @@ ComputeStall:
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 @@ FoundFactor:
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 @@ FoundFactor:
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 @@ AndItsNotYou:
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 @@ _KeStallExecutionProcessor@4:
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 @@ Done:
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

View file

@ -4,6 +4,7 @@
* FILE: hal/halx86/generic/timer.c
* PURPOSE: HAL Timer Routines
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Timo Kreuzer (timo.kreuzer@reactos.org)
*/
/* INCLUDES ******************************************************************/
@ -14,6 +15,9 @@
/* GLOBALS *******************************************************************/
#define PIT_LATCH 0x00
LARGE_INTEGER HalpLastPerfCounter;
ULONG HalpPerfCounterCutoff;
BOOLEAN HalpClockSetMSRate;
ULONG HalpCurrentTimeIncrement;
@ -21,54 +25,56 @@ ULONG HalpCurrentRollOver;
ULONG HalpNextMSRate = 14;
ULONG HalpLargestClockMS = 15;
LARGE_INTEGER HalpRolloverTable[15] =
static struct _HALP_ROLLOVER
{
{{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}}
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 *********************************************************/
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
INIT_FUNCTION
HalpInitializeClock(VOID)
HalpSetTimerRollOver(USHORT RollOver)
{
PKPRCB Prcb = KeGetCurrentPrcb();
ULONG Increment;
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();
_disable();
@ -98,9 +104,31 @@ HalpInitializeClock(VOID)
/* 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 @@ VOID
FASTCALL
HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
{
ULONG LastIncrement;
KIRQL Irql;
/* Enter trap */
@ -121,16 +150,25 @@ HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
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 @@ HalSetTimeIncrement(IN ULONG Increment)
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 */