1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2015, Joyent, Inc. 14 */ 15 16 /* 17 * The microfind() routine is used to calibrate the delay provided by 18 * tenmicrosec(). Early in boot gethrtime() is not yet configured and 19 * available for accurate delays, but some drivers still need to be able to 20 * pause execution for rough increments of ten microseconds. To that end, 21 * microfind() will measure the wall time elapsed during a simple delay loop 22 * using the Intel 8254 Programmable Interval Timer (PIT), and attempt to find 23 * a loop count that approximates a ten microsecond delay. 24 * 25 * This mechanism is accurate enough when running unvirtualised on real CPUs, 26 * but is somewhat less efficacious in a virtual machine. In a virtualised 27 * guest the relationship between instruction completion and elapsed wall time 28 * is, at best, variable; on such machines the calibration is merely a rough 29 * guess. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/dl.h> 34 #include <sys/param.h> 35 #include <sys/pit.h> 36 #include <sys/inline.h> 37 #include <sys/machlock.h> 38 #include <sys/avintr.h> 39 #include <sys/smp_impldefs.h> 40 #include <sys/archsystm.h> 41 #include <sys/systm.h> 42 #include <sys/machsystm.h> 43 44 /* 45 * Loop count for 10 microsecond wait. MUST be initialized for those who 46 * insist on calling "tenmicrosec" before the clock has been initialized. 47 */ 48 unsigned int microdata = 50; 49 50 /* 51 * These values, used later in microfind(), are stored in globals to allow them 52 * to be adjusted more easily via kmdb. 53 */ 54 unsigned int microdata_trial_count = 7; 55 unsigned int microdata_allowed_failures = 3; 56 57 58 static void 59 microfind_pit_reprogram_for_bios(void) 60 { 61 /* 62 * Restore PIT counter 0 for BIOS use in mode 3 -- "Square Wave 63 * Generator". 64 */ 65 outb(PITCTL_PORT, PIT_C0 | PIT_LOADMODE | PIT_SQUAREMODE); 66 67 /* 68 * Load an initial counter value of zero. 69 */ 70 outb(PITCTR0_PORT, 0); 71 outb(PITCTR0_PORT, 0); 72 } 73 74 /* 75 * Measure the run time of tenmicrosec() using the Intel 8254 Programmable 76 * Interval Timer. The timer operates at 1.193182 Mhz, so each timer tick 77 * represents 0.8381 microseconds of wall time. This function returns the 78 * number of such ticks that passed while tenmicrosec() was running, or 79 * -1 if the delay was too long to measure with the PIT. 80 */ 81 static int 82 microfind_pit_delta(void) 83 { 84 unsigned char status; 85 int count; 86 87 /* 88 * Configure PIT counter 0 in mode 0 -- "Interrupt On Terminal Count". 89 * In this mode, the PIT will count down from the loaded value and 90 * set its output bit high once it reaches zero. The PIT will pause 91 * until we write the low byte and then the high byte to the counter 92 * port. 93 */ 94 outb(PITCTL_PORT, PIT_LOADMODE); 95 96 /* 97 * Load the maximum counter value, 0xffff, into the counter port. 98 */ 99 outb(PITCTR0_PORT, 0xff); 100 outb(PITCTR0_PORT, 0xff); 101 102 /* 103 * Run the delay function. 104 */ 105 tenmicrosec(); 106 107 /* 108 * Latch the counter value and status for counter 0 with the read 109 * back command. 110 */ 111 outb(PITCTL_PORT, PIT_READBACK | PIT_READBACKC0); 112 113 /* 114 * In read back mode, three values are read from the counter port 115 * in order: the status byte, followed by the low byte and high 116 * byte of the counter value. 117 */ 118 status = inb(PITCTR0_PORT); 119 count = inb(PITCTR0_PORT); 120 count |= inb(PITCTR0_PORT) << 8; 121 122 /* 123 * Verify that the counter started counting down. The null count 124 * flag in the status byte is set when we load a value, and cleared 125 * when counting operation begins. 126 */ 127 if (status & (1 << PITSTAT_NULLCNT)) { 128 /* 129 * The counter did not begin. This means the loop count 130 * used by tenmicrosec is too small for this CPU. We return 131 * a zero count to represent that the delay was too small 132 * to measure. 133 */ 134 return (0); 135 } 136 137 /* 138 * Verify that the counter did not wrap around. The output pin is 139 * reset when we load a new counter value, and set once the counter 140 * reaches zero. 141 */ 142 if (status & (1 << PITSTAT_OUTPUT)) { 143 /* 144 * The counter reached zero before we were able to read the 145 * value. This means the loop count used by tenmicrosec is too 146 * large for this CPU. 147 */ 148 return (-1); 149 } 150 151 /* 152 * The PIT counts from our initial load value of 0xffff down to zero. 153 * Return the number of timer ticks that passed while tenmicrosec was 154 * running. 155 */ 156 VERIFY(count <= 0xffff); 157 return (0xffff - count); 158 } 159 160 static int 161 microfind_pit_delta_avg(int trials, int allowed_failures) 162 { 163 int tc = 0; 164 int failures = 0; 165 long long int total = 0; 166 167 while (tc < trials) { 168 int d; 169 170 if ((d = microfind_pit_delta()) < 0) { 171 /* 172 * If the counter wrapped, we cannot use this 173 * data point in the average. Record the failure 174 * and try again. 175 */ 176 if (++failures > allowed_failures) { 177 /* 178 * Too many failures. 179 */ 180 return (-1); 181 } 182 continue; 183 } 184 185 total += d; 186 tc++; 187 } 188 189 return (total / tc); 190 } 191 192 void 193 microfind(void) 194 { 195 int ticks = -1; 196 ulong_t s; 197 198 /* 199 * Disable interrupts while we measure the speed of the CPU. 200 */ 201 s = clear_int_flag(); 202 203 /* 204 * Start at the smallest loop count, i.e. 1, and keep doubling 205 * until a delay of ~10ms can be measured. 206 */ 207 microdata = 1; 208 for (;;) { 209 int ticksprev = ticks; 210 211 /* 212 * We use a trial count of 7 to attempt to smooth out jitter 213 * caused by the scheduling of virtual machines. We only allow 214 * three failures, as each failure represents a wrapped counter 215 * and an expired wall time of at least ~55ms. 216 */ 217 if ((ticks = microfind_pit_delta_avg(microdata_trial_count, 218 microdata_allowed_failures)) < 0) { 219 /* 220 * The counter wrapped. Halve the counter, restore the 221 * previous ticks count and break out of the loop. 222 */ 223 if (microdata <= 1) { 224 /* 225 * If the counter wrapped on the first try, 226 * then we have some serious problems. 227 */ 228 panic("microfind: pit counter always wrapped"); 229 } 230 microdata = microdata >> 1; 231 ticks = ticksprev; 232 break; 233 } 234 235 if (ticks > 0x3000) { 236 /* 237 * The loop ran for at least ~10ms worth of 0.8381us 238 * PIT ticks. 239 */ 240 break; 241 } else if (microdata > (UINT_MAX >> 1)) { 242 /* 243 * Doubling the loop count again would cause an 244 * overflow. Use what we have. 245 */ 246 break; 247 } else { 248 /* 249 * Double and try again. 250 */ 251 microdata = microdata << 1; 252 } 253 } 254 255 if (ticks < 1) { 256 /* 257 * If we were unable to measure a positive PIT tick count, then 258 * we will be unable to scale the value of "microdata" 259 * correctly. 260 */ 261 panic("microfind: could not calibrate delay loop"); 262 } 263 264 /* 265 * Calculate the loop count based on the final PIT tick count and the 266 * loop count. Each PIT tick represents a duration of ~0.8381us, so we 267 * want to adjust microdata to represent a duration of 12 ticks, or 268 * ~10us. 269 */ 270 microdata = (long long)microdata * 12LL / (long long)ticks; 271 272 /* 273 * Try and leave things as we found them. 274 */ 275 microfind_pit_reprogram_for_bios(); 276 277 /* 278 * Restore previous interrupt state. 279 */ 280 restore_int_flag(s); 281 } 282