1 /* 2 * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and 6 * licensed under the NewOS license. 7 */ 8 9 10 #include "cpu.h" 11 12 #include <OS.h> 13 #include <boot/platform.h> 14 #include <boot/stdio.h> 15 #include <boot/kernel_args.h> 16 #include <boot/stage2.h> 17 #include <arch/cpu.h> 18 #include <arch_kernel.h> 19 #include <arch_system_info.h> 20 21 #include <string.h> 22 23 24 //#define TRACE_CPU 25 #ifdef TRACE_CPU 26 # define TRACE(x) dprintf x 27 #else 28 # define TRACE(x) ; 29 #endif 30 31 32 extern "C" uint64 rdtsc(); 33 34 uint32 gTimeConversionFactor; 35 36 #define TIMER_CLKNUM_HZ (14318180/12) 37 38 #define CPUID_EFLAGS (1UL << 21) 39 #define RDTSC_FEATURE (1UL << 4) 40 41 42 static void 43 calculate_cpu_conversion_factor() 44 { 45 uint32 s_low, s_high; 46 uint32 low, high; 47 uint32 expired; 48 uint64 t1, t2; 49 uint64 p1, p2, p3; 50 double r1, r2, r3; 51 52 out8(0x34, 0x43); /* program the timer to count down mode */ 53 out8(0xff, 0x40); /* low and then high */ 54 out8(0xff, 0x40); 55 56 /* quick sample */ 57 quick_sample: 58 do { 59 out8(0x00, 0x43); /* latch counter value */ 60 s_low = in8(0x40); 61 s_high = in8(0x40); 62 } while (s_high != 255); 63 t1 = rdtsc(); 64 do { 65 out8(0x00, 0x43); /* latch counter value */ 66 low = in8(0x40); 67 high = in8(0x40); 68 } while (high > 224); 69 t2 = rdtsc(); 70 71 p1 = t2-t1; 72 r1 = (double)(p1) / (double)(((s_high << 8) | s_low) - ((high << 8) | low)); 73 74 /* not so quick sample */ 75 not_so_quick_sample: 76 do { 77 out8(0x00, 0x43); /* latch counter value */ 78 s_low = in8(0x40); 79 s_high = in8(0x40); 80 } while (s_high != 255); 81 t1 = rdtsc(); 82 do { 83 out8(0x00, 0x43); /* latch counter value */ 84 low = in8(0x40); 85 high = in8(0x40); 86 } while (high > 192); 87 t2 = rdtsc(); 88 p2 = t2-t1; 89 r2 = (double)(p2) / (double)(((s_high << 8) | s_low) - ((high << 8) | low)); 90 if ((r1/r2) > 1.01) { 91 //dprintf("Tuning loop(1)\n"); 92 goto quick_sample; 93 } 94 if ((r1/r2) < 0.99) { 95 //dprintf("Tuning loop(1)\n"); 96 goto quick_sample; 97 } 98 99 /* slow sample */ 100 do { 101 out8(0x00, 0x43); /* latch counter value */ 102 s_low = in8(0x40); 103 s_high = in8(0x40); 104 } while (s_high != 255); 105 t1 = rdtsc(); 106 do { 107 out8(0x00, 0x43); /* latch counter value */ 108 low = in8(0x40); 109 high = in8(0x40); 110 } while (high > 128); 111 t2 = rdtsc(); 112 113 p3 = t2-t1; 114 r3 = (double)(p3) / (double)(((s_high << 8) | s_low) - ((high << 8) | low)); 115 if ((r2/r3) > 1.01) { 116 TRACE(("Tuning loop(2)\n")); 117 goto not_so_quick_sample; 118 } 119 if ((r2/r3) < 0.99) { 120 TRACE(("Tuning loop(2)\n")); 121 goto not_so_quick_sample; 122 } 123 124 expired = ((s_high << 8) | s_low) - ((high << 8) | low); 125 p3 *= TIMER_CLKNUM_HZ; 126 127 /* 128 * cv_factor contains time in usecs per CPU cycle * 2^32 129 * 130 * The code below is a bit fancy. Originally Michael Noistering 131 * had it like: 132 * 133 * cv_factor = ((uint64)1000000<<32) * expired / p3; 134 * 135 * whic is perfect, but unfortunately 1000000ULL<<32*expired 136 * may overflow in fast cpus with the long sampling period 137 * i put there for being as accurate as possible under 138 * vmware. 139 * 140 * The below calculation is based in that we are trying 141 * to calculate: 142 * 143 * (C*expired)/p3 -> (C*(x0<<k + x1))/p3 -> 144 * (C*(x0<<k))/p3 + (C*x1)/p3 145 * 146 * Now the term (C*(x0<<k))/p3 is rewritten as: 147 * 148 * (C*(x0<<k))/p3 -> ((C*x0)/p3)<<k + reminder 149 * 150 * where reminder is: 151 * 152 * floor((1<<k)*decimalPart((C*x0)/p3)) 153 * 154 * which is approximated as: 155 * 156 * floor((1<<k)*decimalPart(((C*x0)%p3)/p3)) -> 157 * (((C*x0)%p3)<<k)/p3 158 * 159 * So the final expression is: 160 * 161 * ((C*x0)/p3)<<k + (((C*x0)%p3)<<k)/p3 + (C*x1)/p3 162 */ 163 /* 164 * To get the highest accuracy with this method 165 * x0 should have the 12 most significant bits of expired 166 * to minimize the error upon <<k. 167 */ 168 /* 169 * Of course, you are not expected to understand any of this. 170 */ 171 { 172 unsigned i; 173 unsigned k; 174 uint64 C; 175 uint64 x0; 176 uint64 x1; 177 uint64 a, b, c; 178 179 /* first calculate k*/ 180 k = 0; 181 for (i = 12; i < 16; i++) { 182 if (expired & (1<<i)) 183 k = i - 11; 184 } 185 186 C = 1000000ULL << 32; 187 x0 = expired >> k; 188 x1 = expired & ((1 << k) - 1); 189 190 a = ((C * x0) / p3) << k; 191 b = (((C * x0) % p3) << k) / p3; 192 c = (C * x1) / p3; 193 #if 0 194 dprintf("a=%Ld\n", a); 195 dprintf("b=%Ld\n", b); 196 dprintf("c=%Ld\n", c); 197 dprintf("%d %Ld\n", expired, p3); 198 #endif 199 gTimeConversionFactor = a + b + c; 200 #if 0 201 dprintf("cvf=%Ld\n", cv_factor); 202 #endif 203 } 204 205 #ifdef TRACE_CPU 206 if (p3 / expired / 1000000000LL) 207 dprintf("CPU at %Ld.%03Ld GHz\n", p3/expired/1000000000LL, ((p3/expired)%1000000000LL)/1000000LL); 208 else 209 dprintf("CPU at %Ld.%03Ld MHz\n", p3/expired/1000000LL, ((p3/expired)%1000000LL)/1000LL); 210 #endif 211 212 gKernelArgs.arch_args.system_time_cv_factor = gTimeConversionFactor; 213 gKernelArgs.arch_args.cpu_clock_speed = p3/expired; 214 } 215 216 217 static status_t 218 check_cpu_features() 219 { 220 // check the eflags register to see if the cpuid instruction exists 221 if ((get_eflags() & CPUID_EFLAGS) == 0) { 222 // it's not set yet, but maybe we can set it manually 223 set_eflags(get_eflags() | CPUID_EFLAGS); 224 if ((get_eflags() & CPUID_EFLAGS) == 0) 225 return B_ERROR; 226 } 227 228 cpuid_info info; 229 if (get_current_cpuid(&info, 1) != B_OK) 230 return B_ERROR; 231 232 if ((info.eax_1.features & RDTSC_FEATURE) == 0) { 233 // we currently require RDTSC 234 return B_ERROR; 235 } 236 237 return B_OK; 238 } 239 240 241 // #pragma mark - 242 243 244 extern "C" void 245 spin(bigtime_t microseconds) 246 { 247 bigtime_t time = system_time(); 248 249 while ((system_time() - time) < microseconds) 250 asm volatile ("pause;"); 251 } 252 253 254 extern "C" void 255 cpu_init() 256 { 257 if (check_cpu_features() != B_OK) 258 panic("You need a Pentium or higher in order to boot!\n"); 259 260 calculate_cpu_conversion_factor(); 261 262 gKernelArgs.num_cpus = 1; 263 // this will eventually be corrected later on 264 } 265 266