[HAL]
authorTimo Kreuzer <timo.kreuzer@reactos.org>
Tue, 30 Aug 2011 08:52:17 +0000 (08:52 +0000)
committerTimo Kreuzer <timo.kreuzer@reactos.org>
Tue, 30 Aug 2011 08:52:17 +0000 (08:52 +0000)
- Rewrite KeQueryPerformanceCounter in C
- Implement support for changing the clock rate

svn path=/trunk/; revision=53495

reactos/hal/halx86/generic/i386/systimer.S
reactos/hal/halx86/generic/timer.c

index 7a24e90..4a77559 100644 (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
index ec64e59..917c5d1 100644 (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
+{
+    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}}
+    {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);
+}
+
+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 return */
+    /* 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 */