1 /* 2 * PROJECT: ReactOS HAL 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: hal/halx86/apic/tsc.c 5 * PURPOSE: HAL Routines for TSC handling 6 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org) 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include <hal.h> 12 #include "tsc.h" 13 #include "apicp.h" 14 #define NDEBUG 15 #include <debug.h> 16 17 LARGE_INTEGER HalpCpuClockFrequency = {{INITIAL_STALL_COUNT * 1000000}}; 18 19 UCHAR TscCalibrationPhase; 20 ULONG64 TscCalibrationArray[NUM_SAMPLES]; 21 22 #define RTC_MODE 6 /* Mode 6 is 1024 Hz */ 23 #define SAMPLE_FREQUENCY ((32768 << 1) >> RTC_MODE) 24 25 /* PRIVATE FUNCTIONS *********************************************************/ 26 27 static 28 ULONG64 29 DoLinearRegression( 30 ULONG XMax, 31 ULONG64 *ArrayY) 32 { 33 ULONG X, SumXX; 34 ULONG64 SumXY; 35 36 /* Calculate the sum of the squares of X */ 37 SumXX = (XMax * (XMax + 1) * (2*XMax + 1)) / 6; 38 39 /* Calculate the sum of the differences to the first value 40 weighted by x */ 41 for (SumXY = 0, X = 1; X <= XMax; X++) 42 { 43 SumXY += X * (ArrayY[X] - ArrayY[0]); 44 } 45 46 /* Account for sample frequency */ 47 SumXY *= SAMPLE_FREQUENCY; 48 49 /* Return the quotient of the sums */ 50 return (SumXY + (SumXX/2)) / SumXX; 51 } 52 53 VOID 54 NTAPI 55 HalpInitializeTsc(VOID) 56 { 57 ULONG_PTR Flags; 58 PVOID PreviousHandler; 59 UCHAR RegisterA, RegisterB; 60 61 /* Check if the CPU supports RDTSC */ 62 if (!(KeGetCurrentPrcb()->FeatureBits & KF_RDTSC)) 63 { 64 KeBugCheck(HAL_INITIALIZATION_FAILED); 65 } 66 67 /* Save flags and disable interrupts */ 68 Flags = __readeflags(); 69 _disable(); 70 71 /* Enable the periodic interrupt in the CMOS */ 72 RegisterB = HalpReadCmos(RTC_REGISTER_B); 73 HalpWriteCmos(RTC_REGISTER_B, RegisterB | RTC_REG_B_PI); 74 75 /* Modify register A to RTC_MODE to get SAMPLE_FREQUENCY */ 76 RegisterA = HalpReadCmos(RTC_REGISTER_A); 77 RegisterA = (RegisterA & 0xF0) | RTC_MODE; 78 HalpWriteCmos(RTC_REGISTER_A, RegisterA); 79 80 /* Save old IDT entry */ 81 PreviousHandler = KeQueryInterruptHandler(APIC_CLOCK_VECTOR); 82 83 /* Set the calibration ISR */ 84 KeRegisterInterruptHandler(APIC_CLOCK_VECTOR, TscCalibrationISR); 85 86 /* Reset TSC value to 0 */ 87 __writemsr(MSR_RDTSC, 0); 88 89 /* Enable the timer interrupt */ 90 HalEnableSystemInterrupt(APIC_CLOCK_VECTOR, CLOCK_LEVEL, Latched); 91 92 /* Read register C, so that the next interrupt can happen */ 93 HalpReadCmos(RTC_REGISTER_C); 94 95 /* Wait for completion */ 96 _enable(); 97 while (TscCalibrationPhase < NUM_SAMPLES) _ReadWriteBarrier(); 98 _disable(); 99 100 /* Disable the periodic interrupt in the CMOS */ 101 HalpWriteCmos(RTC_REGISTER_B, RegisterB & ~RTC_REG_B_PI); 102 103 /* Disable the timer interrupt */ 104 HalDisableSystemInterrupt(APIC_CLOCK_VECTOR, CLOCK_LEVEL); 105 106 /* Restore the previous handler */ 107 KeRegisterInterruptHandler(APIC_CLOCK_VECTOR, PreviousHandler); 108 109 /* Calculate an average, using simplified linear regression */ 110 HalpCpuClockFrequency.QuadPart = DoLinearRegression(NUM_SAMPLES - 1, 111 TscCalibrationArray); 112 113 /* Restore flags */ 114 __writeeflags(Flags); 115 116 } 117 118 VOID 119 NTAPI 120 HalpCalibrateStallExecution(VOID) 121 { 122 // Timer interrupt is now active 123 124 HalpInitializeTsc(); 125 126 KeGetPcr()->StallScaleFactor = (ULONG)(HalpCpuClockFrequency.QuadPart / 1000000); 127 } 128 129 /* PUBLIC FUNCTIONS ***********************************************************/ 130 131 LARGE_INTEGER 132 NTAPI 133 KeQueryPerformanceCounter( 134 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL) 135 { 136 LARGE_INTEGER Result; 137 138 /* Make sure it's calibrated */ 139 ASSERT(HalpCpuClockFrequency.QuadPart != 0); 140 141 /* Does the caller want the frequency? */ 142 if (PerformanceFrequency) 143 { 144 /* Return tsc frequency */ 145 *PerformanceFrequency = HalpCpuClockFrequency; 146 } 147 148 /* Return the current value */ 149 Result.QuadPart = __rdtsc(); 150 return Result; 151 } 152 153 VOID 154 NTAPI 155 KeStallExecutionProcessor(ULONG MicroSeconds) 156 { 157 ULONG64 StartTime, EndTime; 158 159 /* Get the initial time */ 160 StartTime = __rdtsc(); 161 162 /* Calculate the ending time */ 163 EndTime = StartTime + KeGetPcr()->StallScaleFactor * MicroSeconds; 164 165 /* Loop until time is elapsed */ 166 while (__rdtsc() < EndTime); 167 } 168 169 VOID 170 NTAPI 171 HalCalibratePerformanceCounter( 172 IN volatile PLONG Count, 173 IN ULONGLONG NewCount) 174 { 175 UNIMPLEMENTED; 176 ASSERT(FALSE); 177 } 178 179