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 <OS.h> 14 #include <boot/platform.h> 15 #include <boot/stdio.h> 16 #include <boot/kernel_args.h> 17 #include <boot/stage2.h> 18 #include <arch/cpu.h> 19 #include <arch_kernel.h> 20 #include <arch_system_info.h> 21 22 #include <string.h> 23 24 25 //#define TRACE_CPU 26 #ifdef TRACE_CPU 27 # define TRACE(x) dprintf x 28 #else 29 # define TRACE(x) ; 30 #endif 31 32 33 extern "C" uint64 rdtsc(); 34 35 uint32 gTimeConversionFactor; 36 37 #define TIMER_CLKNUM_HZ (14318180/12) 38 39 #define CPUID_EFLAGS (1UL << 21) 40 #define RDTSC_FEATURE (1UL << 4) 41 42 43 struct uint128 { 44 uint128(uint64 low, uint64 high = 0) 45 : 46 low(low), 47 high(high) 48 { 49 } 50 51 bool operator<(const uint128& other) const 52 { 53 return high < other.high || (high == other.high && low < other.low); 54 } 55 56 bool operator<=(const uint128& other) const 57 { 58 return !(other < *this); 59 } 60 61 uint128 operator<<(int count) const 62 { 63 if (count == 0) 64 return *this; 65 66 if (count >= 128) 67 return 0; 68 69 if (count >= 64) 70 return uint128(0, low << (count - 64)); 71 72 return uint128(low << count, (high << count) | (low >> (64 - count))); 73 } 74 75 uint128 operator>>(int count) const 76 { 77 if (count == 0) 78 return *this; 79 80 if (count >= 128) 81 return 0; 82 83 if (count >= 64) 84 return uint128(high >> (count - 64), 0); 85 86 return uint128((low >> count) | (high << (64 - count)), high >> count); 87 } 88 89 uint128 operator+(const uint128& other) const 90 { 91 uint64 resultLow = low + other.low; 92 return uint128(resultLow, 93 high + other.high + (resultLow < low ? 1 : 0)); 94 } 95 96 uint128 operator-(const uint128& other) const 97 { 98 uint64 resultLow = low - other.low; 99 return uint128(resultLow, 100 high - other.high - (resultLow > low ? 1 : 0)); 101 } 102 103 uint128 operator*(uint32 other) const 104 { 105 uint64 resultMid = (low >> 32) * other; 106 uint64 resultLow = (low & 0xffffffff) * other + (resultMid << 32); 107 return uint128(resultLow, 108 high * other + (resultMid >> 32) 109 + (resultLow < resultMid << 32 ? 1 : 0)); 110 } 111 112 uint128 operator/(const uint128& other) const 113 { 114 int shift = 0; 115 uint128 shiftedDivider = other; 116 while (shiftedDivider.high >> 63 == 0 && shiftedDivider < *this) { 117 shiftedDivider = shiftedDivider << 1; 118 shift++; 119 } 120 121 uint128 result = 0; 122 uint128 temp = *this; 123 for (; shift >= 0; shift--, shiftedDivider = shiftedDivider >> 1) { 124 if (shiftedDivider <= temp) { 125 result = result + (uint128(1) << shift); 126 temp = temp - shiftedDivider; 127 } 128 } 129 130 return result; 131 } 132 133 operator uint64() const 134 { 135 return low; 136 } 137 138 private: 139 uint64 low; 140 uint64 high; 141 }; 142 143 144 static void 145 calculate_cpu_conversion_factor() 146 { 147 uint32 s_low, s_high; 148 uint32 low, high; 149 uint32 expired; 150 uint64 t1, t2; 151 uint64 p1, p2, p3; 152 double r1, r2, r3; 153 154 out8(0x34, 0x43); /* program the timer to count down mode */ 155 out8(0xff, 0x40); /* low and then high */ 156 out8(0xff, 0x40); 157 158 /* quick sample */ 159 quick_sample: 160 do { 161 out8(0x00, 0x43); /* latch counter value */ 162 s_low = in8(0x40); 163 s_high = in8(0x40); 164 } while (s_high != 255); 165 t1 = rdtsc(); 166 do { 167 out8(0x00, 0x43); /* latch counter value */ 168 low = in8(0x40); 169 high = in8(0x40); 170 } while (high > 224); 171 t2 = rdtsc(); 172 173 p1 = t2-t1; 174 r1 = (double)(p1) / (double)(((s_high << 8) | s_low) - ((high << 8) | low)); 175 176 /* not so quick sample */ 177 not_so_quick_sample: 178 do { 179 out8(0x00, 0x43); /* latch counter value */ 180 s_low = in8(0x40); 181 s_high = in8(0x40); 182 } while (s_high != 255); 183 t1 = rdtsc(); 184 do { 185 out8(0x00, 0x43); /* latch counter value */ 186 low = in8(0x40); 187 high = in8(0x40); 188 } while (high > 192); 189 t2 = rdtsc(); 190 p2 = t2-t1; 191 r2 = (double)(p2) / (double)(((s_high << 8) | s_low) - ((high << 8) | low)); 192 if ((r1/r2) > 1.01) { 193 //dprintf("Tuning loop(1)\n"); 194 goto quick_sample; 195 } 196 if ((r1/r2) < 0.99) { 197 //dprintf("Tuning loop(1)\n"); 198 goto quick_sample; 199 } 200 201 /* slow sample */ 202 do { 203 out8(0x00, 0x43); /* latch counter value */ 204 s_low = in8(0x40); 205 s_high = in8(0x40); 206 } while (s_high != 255); 207 t1 = rdtsc(); 208 do { 209 out8(0x00, 0x43); /* latch counter value */ 210 low = in8(0x40); 211 high = in8(0x40); 212 } while (high > 128); 213 t2 = rdtsc(); 214 215 p3 = t2-t1; 216 r3 = (double)(p3) / (double)(((s_high << 8) | s_low) - ((high << 8) | low)); 217 if ((r2/r3) > 1.01) { 218 TRACE(("Tuning loop(2)\n")); 219 goto not_so_quick_sample; 220 } 221 if ((r2/r3) < 0.99) { 222 TRACE(("Tuning loop(2)\n")); 223 goto not_so_quick_sample; 224 } 225 226 expired = ((s_high << 8) | s_low) - ((high << 8) | low); 227 p3 *= TIMER_CLKNUM_HZ; 228 229 gTimeConversionFactor = ((uint128(expired) * uint32(1000000)) << 32) 230 / uint128(p3); 231 232 #ifdef TRACE_CPU 233 if (p3 / expired / 1000000000LL) 234 dprintf("CPU at %Ld.%03Ld GHz\n", p3/expired/1000000000LL, ((p3/expired)%1000000000LL)/1000000LL); 235 else 236 dprintf("CPU at %Ld.%03Ld MHz\n", p3/expired/1000000LL, ((p3/expired)%1000000LL)/1000LL); 237 #endif 238 239 gKernelArgs.arch_args.system_time_cv_factor = gTimeConversionFactor; 240 gKernelArgs.arch_args.cpu_clock_speed = p3 / expired; 241 } 242 243 244 static status_t 245 check_cpu_features() 246 { 247 // check the eflags register to see if the cpuid instruction exists 248 if ((get_eflags() & CPUID_EFLAGS) == 0) { 249 // it's not set yet, but maybe we can set it manually 250 set_eflags(get_eflags() | CPUID_EFLAGS); 251 if ((get_eflags() & CPUID_EFLAGS) == 0) 252 return B_ERROR; 253 } 254 255 cpuid_info info; 256 if (get_current_cpuid(&info, 1) != B_OK) 257 return B_ERROR; 258 259 if ((info.eax_1.features & RDTSC_FEATURE) == 0) { 260 // we currently require RDTSC 261 return B_ERROR; 262 } 263 264 return B_OK; 265 } 266 267 268 // #pragma mark - 269 270 271 extern "C" void 272 spin(bigtime_t microseconds) 273 { 274 bigtime_t time = system_time(); 275 276 while ((system_time() - time) < microseconds) 277 asm volatile ("pause;"); 278 } 279 280 281 extern "C" void 282 cpu_init() 283 { 284 if (check_cpu_features() != B_OK) 285 panic("You need a Pentium or higher in order to boot!\n"); 286 287 calculate_cpu_conversion_factor(); 288 289 gKernelArgs.num_cpus = 1; 290 // this will eventually be corrected later on 291 } 292 293