1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 * 6 * calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and 7 * licensed under the NewOS license. 8 */ 9 10 11 #include "cpu.h" 12 13 #include "efi_platform.h" 14 15 #include <OS.h> 16 #include <boot/platform.h> 17 #include <boot/stdio.h> 18 #include <boot/kernel_args.h> 19 #include <boot/stage2.h> 20 #include <arch/cpu.h> 21 #include <arch_kernel.h> 22 #include <arch_system_info.h> 23 24 #include <string.h> 25 26 27 //#define TRACE_CPU 28 #ifdef TRACE_CPU 29 # define TRACE(x) dprintf x 30 #else 31 # define TRACE(x) ; 32 #endif 33 34 35 extern "C" uint64 rdtsc(); 36 37 uint32 gTimeConversionFactor; 38 39 // PIT definitions 40 #define TIMER_CLKNUM_HZ (14318180 / 12) 41 42 // PIT IO Ports 43 #define PIT_CHANNEL_PORT_BASE 0x40 44 #define PIT_CONTROL 0x43 45 46 // Channel selection 47 #define PIT_SELECT_CHANNEL_SHIFT 6 48 49 // Access mode 50 #define PIT_ACCESS_LATCH_COUNTER (0 << 4) 51 #define PIT_ACCESS_LOW_BYTE_ONLY (1 << 4) 52 #define PIT_ACCESS_HIGH_BYTE_ONLY (2 << 4) 53 #define PIT_ACCESS_LOW_THEN_HIGH_BYTE (3 << 4) 54 55 // Operating modes 56 #define PIT_MODE_INTERRUPT_ON_0 (0 << 1) 57 #define PIT_MODE_HARDWARE_COUNTDOWN (1 << 1) 58 #define PIT_MODE_RATE_GENERATOR (2 << 1) 59 #define PIT_MODE_SQUARE_WAVE_GENERATOR (3 << 1) 60 #define PIT_MODE_SOFTWARE_STROBE (4 << 1) 61 #define PIT_MODE_HARDWARE_STROBE (5 << 1) 62 63 // BCD/Binary mode 64 #define PIT_BINARY_MODE 0 65 #define PIT_BCD_MODE 1 66 67 // Channel 2 control (speaker) 68 #define PIT_CHANNEL_2_CONTROL 0x61 69 #define PIT_CHANNEL_2_GATE_HIGH 0x01 70 #define PIT_CHANNEL_2_SPEAKER_OFF_MASK ~0x02 71 72 73 // Maximum values 74 #define MAX_QUICK_SAMPLES 20 75 #define MAX_SLOW_SAMPLES 20 76 // TODO: These are arbitrary. They are here to avoid spinning indefinitely 77 // if the TSC just isn't stable and we can't get our desired error range. 78 79 80 #define CPUID_EFLAGS (1UL << 21) 81 #define RDTSC_FEATURE (1UL << 4) 82 83 84 struct uint128 { 85 uint128(uint64 low, uint64 high = 0) 86 : 87 low(low), 88 high(high) 89 { 90 } 91 92 bool operator<(const uint128& other) const 93 { 94 return high < other.high || (high == other.high && low < other.low); 95 } 96 97 bool operator<=(const uint128& other) const 98 { 99 return !(other < *this); 100 } 101 102 uint128 operator<<(int count) const 103 { 104 if (count == 0) 105 return *this; 106 107 if (count >= 128) 108 return 0; 109 110 if (count >= 64) 111 return uint128(0, low << (count - 64)); 112 113 return uint128(low << count, (high << count) | (low >> (64 - count))); 114 } 115 116 uint128 operator>>(int count) const 117 { 118 if (count == 0) 119 return *this; 120 121 if (count >= 128) 122 return 0; 123 124 if (count >= 64) 125 return uint128(high >> (count - 64), 0); 126 127 return uint128((low >> count) | (high << (64 - count)), high >> count); 128 } 129 130 uint128 operator+(const uint128& other) const 131 { 132 uint64 resultLow = low + other.low; 133 return uint128(resultLow, 134 high + other.high + (resultLow < low ? 1 : 0)); 135 } 136 137 uint128 operator-(const uint128& other) const 138 { 139 uint64 resultLow = low - other.low; 140 return uint128(resultLow, 141 high - other.high - (resultLow > low ? 1 : 0)); 142 } 143 144 uint128 operator*(uint32 other) const 145 { 146 uint64 resultMid = (low >> 32) * other; 147 uint64 resultLow = (low & 0xffffffff) * other + (resultMid << 32); 148 return uint128(resultLow, 149 high * other + (resultMid >> 32) 150 + (resultLow < resultMid << 32 ? 1 : 0)); 151 } 152 153 uint128 operator/(const uint128& other) const 154 { 155 int shift = 0; 156 uint128 shiftedDivider = other; 157 while (shiftedDivider.high >> 63 == 0 && shiftedDivider < *this) { 158 shiftedDivider = shiftedDivider << 1; 159 shift++; 160 } 161 162 uint128 result = 0; 163 uint128 temp = *this; 164 for (; shift >= 0; shift--, shiftedDivider = shiftedDivider >> 1) { 165 if (shiftedDivider <= temp) { 166 result = result + (uint128(1) << shift); 167 temp = temp - shiftedDivider; 168 } 169 } 170 171 return result; 172 } 173 174 operator uint64() const 175 { 176 return low; 177 } 178 179 private: 180 uint64 low; 181 uint64 high; 182 }; 183 184 185 static inline void 186 calibration_loop(uint8 desiredHighByte, uint8 channel, uint64& tscDelta, 187 double& conversionFactor, uint16& expired) 188 { 189 uint8 select = channel << PIT_SELECT_CHANNEL_SHIFT; 190 out8(select | PIT_ACCESS_LOW_THEN_HIGH_BYTE | PIT_MODE_INTERRUPT_ON_0 191 | PIT_BINARY_MODE, PIT_CONTROL); 192 193 // Fill in count of 0xffff, low then high byte 194 uint8 channelPort = PIT_CHANNEL_PORT_BASE + channel; 195 out8(0xff, channelPort); 196 out8(0xff, channelPort); 197 198 // Read the count back once to delay the start. This ensures that we've 199 // waited long enough for the counter to actually start counting down, as 200 // this only happens on the next clock cycle after reload. 201 in8(channelPort); 202 in8(channelPort); 203 204 // We're expecting the PIT to be at the starting position (high byte 0xff) 205 // as we just programmed it, but if it isn't we wait for it to wrap. 206 uint8 startLow; 207 uint8 startHigh; 208 do { 209 out8(select | PIT_ACCESS_LATCH_COUNTER, PIT_CONTROL); 210 startLow = in8(channelPort); 211 startHigh = in8(channelPort); 212 } while (startHigh != 255); 213 214 // Read in the first TSC value 215 uint64 startTSC = rdtsc(); 216 217 // Wait for the PIT to count down to our desired value 218 uint8 endLow; 219 uint8 endHigh; 220 do { 221 out8(select | PIT_ACCESS_LATCH_COUNTER, PIT_CONTROL); 222 endLow = in8(channelPort); 223 endHigh = in8(channelPort); 224 } while (endHigh > desiredHighByte); 225 226 // And read the second TSC value 227 uint64 endTSC = rdtsc(); 228 229 tscDelta = endTSC - startTSC; 230 expired = ((startHigh << 8) | startLow) - ((endHigh << 8) | endLow); 231 conversionFactor = (double)tscDelta / (double)expired; 232 } 233 234 235 static void 236 calculate_cpu_conversion_factor() 237 { 238 uint8 channel = 2; 239 240 // When using channel 2, enable the input and disable the speaker. 241 if (channel == 2) { 242 uint8 control = in8(PIT_CHANNEL_2_CONTROL); 243 control &= PIT_CHANNEL_2_SPEAKER_OFF_MASK; 244 control |= PIT_CHANNEL_2_GATE_HIGH; 245 out8(control, PIT_CHANNEL_2_CONTROL); 246 } 247 248 uint64 tscDeltaQuick, tscDeltaSlower, tscDeltaSlow; 249 double conversionFactorQuick, conversionFactorSlower, conversionFactorSlow; 250 uint16 expired; 251 252 uint32 quickSampleCount = 1; 253 uint32 slowSampleCount = 1; 254 255 quick_sample: 256 calibration_loop(224, channel, tscDeltaQuick, conversionFactorQuick, 257 expired); 258 259 slower_sample: 260 calibration_loop(192, channel, tscDeltaSlower, conversionFactorSlower, 261 expired); 262 263 double deviation = conversionFactorQuick / conversionFactorSlower; 264 if (deviation < 0.99 || deviation > 1.01) { 265 // We might have been hit by a SMI or were otherwise stalled 266 if (quickSampleCount++ < MAX_QUICK_SAMPLES) 267 goto quick_sample; 268 } 269 270 // Slow sample 271 calibration_loop(128, channel, tscDeltaSlow, conversionFactorSlow, 272 expired); 273 274 deviation = conversionFactorSlower / conversionFactorSlow; 275 if (deviation < 0.99 || deviation > 1.01) { 276 // We might have been hit by a SMI or were otherwise stalled 277 if (slowSampleCount++ < MAX_SLOW_SAMPLES) 278 goto slower_sample; 279 } 280 281 // Scale the TSC delta to timer units 282 tscDeltaSlow *= TIMER_CLKNUM_HZ; 283 284 uint64 clockSpeed = tscDeltaSlow / expired; 285 gTimeConversionFactor = ((uint128(expired) * uint32(1000000)) << 32) 286 / uint128(tscDeltaSlow); 287 288 #ifdef TRACE_CPU 289 if (clockSpeed > 1000000000LL) { 290 dprintf("CPU at %Ld.%03Ld GHz\n", clockSpeed / 1000000000LL, 291 (clockSpeed % 1000000000LL) / 1000000LL); 292 } else { 293 dprintf("CPU at %Ld.%03Ld MHz\n", clockSpeed / 1000000LL, 294 (clockSpeed % 1000000LL) / 1000LL); 295 } 296 #endif 297 298 gKernelArgs.arch_args.system_time_cv_factor = gTimeConversionFactor; 299 gKernelArgs.arch_args.cpu_clock_speed = clockSpeed; 300 //dprintf("factors: %lu %llu\n", gTimeConversionFactor, clockSpeed); 301 302 if (quickSampleCount > 1) { 303 dprintf("needed %u quick samples for TSC calibration\n", 304 quickSampleCount); 305 } 306 307 if (slowSampleCount > 1) { 308 dprintf("needed %u slow samples for TSC calibration\n", 309 slowSampleCount); 310 } 311 312 if (channel == 2) { 313 // Set the gate low again 314 out8(in8(PIT_CHANNEL_2_CONTROL) & ~PIT_CHANNEL_2_GATE_HIGH, 315 PIT_CHANNEL_2_CONTROL); 316 } 317 } 318 319 320 // #pragma mark - 321 322 323 extern "C" bigtime_t 324 system_time() 325 { 326 uint64 lo, hi; 327 asm("rdtsc": "=a"(lo), "=d"(hi)); 328 return ((lo * gTimeConversionFactor) >> 32) + hi * gTimeConversionFactor; 329 } 330 331 332 extern "C" void 333 spin(bigtime_t microseconds) 334 { 335 bigtime_t time = system_time(); 336 337 while ((system_time() - time) < microseconds) 338 asm volatile ("pause;"); 339 } 340 341 342 extern "C" void 343 cpu_init() 344 { 345 calculate_cpu_conversion_factor(); 346 347 gKernelArgs.num_cpus = 1; 348 // this will eventually be corrected later on 349 } 350