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
DoLinearRegression(ULONG XMax,ULONG64 * ArrayY)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
HalpInitializeTsc(VOID)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
HalpCalibrateStallExecution(VOID)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
KeQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)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
KeStallExecutionProcessor(ULONG MicroSeconds)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
HalCalibratePerformanceCounter(IN volatile PLONG Count,IN ULONGLONG NewCount)171 HalCalibratePerformanceCounter(
172 IN volatile PLONG Count,
173 IN ULONGLONG NewCount)
174 {
175 UNIMPLEMENTED;
176 ASSERT(FALSE);
177 }
178
179