[ntoskrnl]
authorAleksey Bragin <aleksey@reactos.org>
Sat, 10 Oct 2009 18:22:56 +0000 (18:22 +0000)
committerAleksey Bragin <aleksey@reactos.org>
Sat, 10 Oct 2009 18:22:56 +0000 (18:22 +0000)
- Reimplement HalpCalibrateStallExecution which was removed in r24964. Real Windows uses a different algorithm, however existing one seems to work acceptably. This patch is critical for devices support on real hardware. The only downside is that uniata initialization takes a substantial amount of time now, this is going to be investigated. Patch by Daniel Zimmermann.
See issue #4600 for more details.

svn path=/trunk/; revision=43364

reactos/hal/halx86/generic/halinit.c
reactos/hal/halx86/generic/systimer.S
reactos/hal/halx86/generic/timer.c
reactos/hal/halx86/include/halp.h

index bae45fa..1704bf7 100644 (file)
@@ -101,7 +101,7 @@ HalInitSystem(IN ULONG BootPhase,
         HalpInitializeClock();
 
         /* Setup busy waiting */
-        //HalpCalibrateStallExecution();
+        HalpCalibrateStallExecution();
 
         /* Fill out the dispatch tables */
         HalQuerySystemInformation = HaliQuerySystemInformation;
index 6c06a1a..d200fa3 100644 (file)
@@ -54,6 +54,34 @@ Done:
     ret 4
 .endfunc
 
+
+.globl _HalpQuery8254Counter@0
+.func HalpQuery8254Counter@0
+_HalpQuery8254Counter@0:
+
+    /* Save EFLAGS and disable interrupts */
+    pushfd
+    cli
+    
+    /* Set timer data */
+    mov al, 0
+    out 0x43, al
+    jmp $+2
+
+    /* Read current timer */
+    in al, 0x40
+    jmp $+2
+    movzx ecx, al
+    in al, 0x40
+    mov ch, al
+
+    /* Return it and restore interrupt state */
+    mov eax, ecx
+    popfd
+    ret
+.endfunc
+
+
 .global _KeQueryPerformanceCounter@4
 .func KeQueryPerformanceCounter@4
 _KeQueryPerformanceCounter@4:
index e7ef223..a38af74 100644 (file)
 
 /* GLOBALS *******************************************************************/
 
+/* time to wait */
+#define MICROSECOND_TO_WAIT 1000
+/* the tick count for 1 ms is 1193.182 (1193182 Hz) round it up */
+#define TICKCOUNT_TO_WAIT  1194
+
 BOOLEAN HalpClockSetMSRate;
 ULONG HalpCurrentTimeIncrement;
 ULONG HalpCurrentRollOver;
@@ -130,4 +135,161 @@ HalSetTimeIncrement(IN ULONG Increment)
     return HalpRolloverTable[Increment - 1].HighPart;
 }
 
+ULONG
+WaitFor8254Wraparound(VOID)
+{
+    ULONG StartTicks;
+    ULONG PrevTicks;
+    LONG Delta;
+
+    StartTicks = HalpQuery8254Counter();
+
+    do
+    {
+        PrevTicks = StartTicks;
+        StartTicks = HalpQuery8254Counter();
+        Delta = StartTicks - PrevTicks;
+
+        /*
+         * This limit for delta seems arbitrary, but it isn't, it's
+         * slightly above the level of error a buggy Mercury/Neptune
+         * chipset timer can cause.
+         */
+
+    }
+    while (Delta < 300);
+
+    return StartTicks;
+}
+
+VOID
+NTAPI
+HalpCalibrateStallExecution(VOID)
+{
+    ULONG CalibrationBit;
+    ULONG EndTicks;
+    ULONG StartTicks;
+    ULONG OverheadTicks;
+    PKIPCR Pcr;
+
+    Pcr = (PKIPCR)KeGetPcr();
+
+    /* Measure the delay for the minimum call overhead in ticks */
+    Pcr->StallScaleFactor = 1;
+    StartTicks = WaitFor8254Wraparound();
+    KeStallExecutionProcessor(1);
+    EndTicks = HalpQuery8254Counter();
+    OverheadTicks = (StartTicks - EndTicks);
+
+    do
+    {
+        /* Increase the StallScaleFactor */
+        Pcr->StallScaleFactor = Pcr->StallScaleFactor * 2;
+
+        if (Pcr->StallScaleFactor == 0)
+        {
+           /* Nothing found */
+           break;
+        }
+
+        /* Get the start ticks */
+        StartTicks = WaitFor8254Wraparound();
+
+        /* Wait for a defined time */
+        KeStallExecutionProcessor(MICROSECOND_TO_WAIT);
+
+        /* Get the end ticks */
+        EndTicks = HalpQuery8254Counter();
+
+        DPRINT("Pcr->StallScaleFactor: %d\n", Pcr->StallScaleFactor);
+        DPRINT("Time1 : StartTicks %i - EndTicks %i = %i\n",
+            StartTicks, EndTicks, StartTicks - EndTicks);
+    } while ((StartTicks - EndTicks) <= (TICKCOUNT_TO_WAIT + OverheadTicks));
+
+    /* A StallScaleFactor lesser than INITIAL_STALL_COUNT makes no sense */
+    if (Pcr->StallScaleFactor >= (INITIAL_STALL_COUNT * 2))
+    {
+        /* Adjust the StallScaleFactor */
+        Pcr->StallScaleFactor  = Pcr->StallScaleFactor / 2;
+
+        /* Setup the CalibrationBit */
+        CalibrationBit = Pcr->StallScaleFactor;
+
+        for (;;)
+        {
+            /* Lower the CalibrationBit */
+            CalibrationBit = CalibrationBit / 2;
+            if (CalibrationBit == 0)
+            {
+                break;
+            }
+
+            /* Add the CalibrationBit */
+            Pcr->StallScaleFactor = Pcr->StallScaleFactor + CalibrationBit;
+
+            /* Get the start ticks */
+            StartTicks = WaitFor8254Wraparound();
+
+            /* Wait for a defined time */
+            KeStallExecutionProcessor(MICROSECOND_TO_WAIT);
+
+            /* Get the end ticks */
+            EndTicks = HalpQuery8254Counter();
+
+            DPRINT("Pcr->StallScaleFactor: %d\n", Pcr->StallScaleFactor);
+            DPRINT("Time2 : StartTicks %i - EndTicks %i = %i\n",
+                StartTicks, EndTicks, StartTicks - EndTicks);
+
+            if ((StartTicks-EndTicks) > (TICKCOUNT_TO_WAIT+OverheadTicks))
+            {
+                /* Too big so subtract the CalibrationBit */
+                Pcr->StallScaleFactor = Pcr->StallScaleFactor - CalibrationBit;
+            }
+        }
+        DPRINT("New StallScaleFactor: %d\n", Pcr->StallScaleFactor);
+    }
+    else
+    {
+       /* Set StallScaleFactor to the default */
+       Pcr->StallScaleFactor = INITIAL_STALL_COUNT;
+    }
+
+#if 0
+    /* For debugging */
+    ULONG i;
+
+    DPRINT1("About to start delay loop test\n");
+    DPRINT1("Waiting for a minute...");
+    for (i = 0; i < (60*1000*20); i++)
+    {
+        KeStallExecutionProcessor(50);
+    }
+    DPRINT1("finished\n");
+
+
+    DPRINT1("About to start delay loop test\n");
+    DPRINT1("Waiting for a minute...");
+    for (i = 0; i < (60*1000); i++)
+    {
+        KeStallExecutionProcessor(1000);
+    }
+    DPRINT1("finished\n");
+
+
+    DPRINT1("About to start delay loop test\n");
+    DPRINT1("Waiting for a minute...");
+    for (i = 0; i < (60*1000*1000); i++)
+    {
+        KeStallExecutionProcessor(1);
+    }
+    DPRINT1("finished\n");
+
+    DPRINT1("About to start delay loop test\n");
+    DPRINT1("Waiting for a minute...");
+    KeStallExecutionProcessor(60*1000000);
+    DPRINT1("finished\n");
+#endif
+}
+
+
 /* EOF */
index 578d883..d4b883a 100644 (file)
@@ -41,6 +41,16 @@ VOID NTAPI HalpInitPICs(VOID);
 /* udelay.c */
 VOID NTAPI HalpInitializeClock(VOID);
 
+VOID
+NTAPI
+HalpCalibrateStallExecution(VOID);
+
+ULONG
+NTAPI
+HalpQuery8254Counter(
+    VOID
+);
+
 /* pci.c */
 VOID HalpInitPciBus (VOID);