From 3f7bed2eae793c3a4e5108ddb400caa1ae937aa4 Mon Sep 17 00:00:00 2001 From: Aleksey Bragin Date: Sat, 10 Oct 2009 18:22:56 +0000 Subject: [PATCH] [ntoskrnl] - 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 | 2 +- reactos/hal/halx86/generic/systimer.S | 28 +++++ reactos/hal/halx86/generic/timer.c | 162 ++++++++++++++++++++++++++ reactos/hal/halx86/include/halp.h | 10 ++ 4 files changed, 201 insertions(+), 1 deletion(-) diff --git a/reactos/hal/halx86/generic/halinit.c b/reactos/hal/halx86/generic/halinit.c index bae45fa01cd..1704bf79051 100644 --- a/reactos/hal/halx86/generic/halinit.c +++ b/reactos/hal/halx86/generic/halinit.c @@ -101,7 +101,7 @@ HalInitSystem(IN ULONG BootPhase, HalpInitializeClock(); /* Setup busy waiting */ - //HalpCalibrateStallExecution(); + HalpCalibrateStallExecution(); /* Fill out the dispatch tables */ HalQuerySystemInformation = HaliQuerySystemInformation; diff --git a/reactos/hal/halx86/generic/systimer.S b/reactos/hal/halx86/generic/systimer.S index 6c06a1ab75f..d200fa321ff 100644 --- a/reactos/hal/halx86/generic/systimer.S +++ b/reactos/hal/halx86/generic/systimer.S @@ -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: diff --git a/reactos/hal/halx86/generic/timer.c b/reactos/hal/halx86/generic/timer.c index e7ef223f506..a38af74d2b0 100644 --- a/reactos/hal/halx86/generic/timer.c +++ b/reactos/hal/halx86/generic/timer.c @@ -14,6 +14,11 @@ /* 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 */ diff --git a/reactos/hal/halx86/include/halp.h b/reactos/hal/halx86/include/halp.h index 578d8832409..d4b883ac9f1 100644 --- a/reactos/hal/halx86/include/halp.h +++ b/reactos/hal/halx86/include/halp.h @@ -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);