From e03d0348d9096c82d3050b7c83e205c8d2bbf035 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Tue, 30 Aug 2011 08:52:17 +0000 Subject: [PATCH] [HAL] - Rewrite KeQueryPerformanceCounter in C - Implement support for changing the clock rate svn path=/trunk/; revision=53495 --- reactos/hal/halx86/generic/i386/systimer.S | 276 +++++---------------- reactos/hal/halx86/generic/timer.c | 175 +++++++++---- 2 files changed, 188 insertions(+), 263 deletions(-) diff --git a/reactos/hal/halx86/generic/i386/systimer.S b/reactos/hal/halx86/generic/i386/systimer.S index 7a24e90cf75..4a7755901c8 100644 --- a/reactos/hal/halx86/generic/i386/systimer.S +++ b/reactos/hal/halx86/generic/i386/systimer.S @@ -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 diff --git a/reactos/hal/halx86/generic/timer.c b/reactos/hal/halx86/generic/timer.c index ec64e59ad2a..917c5d1be96 100644 --- a/reactos/hal/halx86/generic/timer.c +++ b/reactos/hal/halx86/generic/timer.c @@ -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 */