1 /* $NetBSD: tsc.c,v 1.67 2026/09/30 06:29:55 andvar Exp $ */ 2 3 /*- 4 * Copyright (c) 2008, 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: tsc.c,v 1.67 2026/09/30 06:29:55 andvar Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/time.h> 35 #include <sys/timetc.h> 36 #include <sys/lwp.h> 37 #include <sys/atomic.h> 38 #include <sys/kernel.h> 39 #include <sys/cpu.h> 40 #include <sys/xcall.h> 41 #include <sys/lock.h> 42 #ifdef BOOT_DURATION 43 #include <sys/boot_duration.h> 44 #endif 45 46 #include <machine/cpu_counter.h> 47 #include <machine/cpuvar.h> 48 #include <machine/cpufunc.h> 49 #include <machine/specialreg.h> 50 #include <machine/cputypes.h> 51 52 #include "tsc.h" 53 54 #define TSC_SYNC_ROUNDS 1000 55 #define ABS(a) ((a) >= 0 ? (a) : -(a)) 56 57 static u_int tsc_get_timecount(struct timecounter *); 58 59 static void tsc_delay(unsigned int); 60 61 static uint64_t tsc_dummy_cacheline __cacheline_aligned; 62 uint64_t tsc_freq __read_mostly; /* exported for sysctl */ 63 #ifdef BOOT_DURATION 64 extern uint32_t starttsc_lo; 65 extern uint32_t starttsc_hi; 66 #endif 67 static int64_t tsc_drift_max = 1000; /* max cycles */ 68 static int64_t tsc_drift_observed; 69 uint64_t (*rdtsc)(void) = rdtsc_cpuid; 70 uint64_t (*cpu_counter)(void) = cpu_counter_cpuid; 71 uint32_t (*cpu_counter32)(void) = cpu_counter32_cpuid; 72 73 int tsc_user_enabled = 1; 74 75 static volatile int64_t tsc_sync_val; 76 static volatile struct cpu_info *tsc_sync_cpu; 77 78 static struct timecounter tsc_timecounter = { 79 .tc_get_timecount = tsc_get_timecount, 80 .tc_counter_mask = ~0U, 81 .tc_name = "TSC", 82 .tc_quality = 3000, 83 }; 84 85 bool 86 tsc_is_invariant(void) 87 { 88 struct cpu_info *ci; 89 uint32_t descs[4]; 90 uint32_t family; 91 bool invariant; 92 93 if (!cpu_hascounter()) 94 return false; 95 96 ci = curcpu(); 97 invariant = false; 98 99 if (cpu_vendor == CPUVENDOR_INTEL) { 100 /* 101 * From Intel(tm) 64 and IA-32 Architectures Software 102 * Developer's Manual Volume 3A: System Programming Guide, 103 * Part 1, 17.13 TIME_STAMP COUNTER, these are the processors 104 * where the TSC is known invariant: 105 * 106 * Pentium 4, Intel Xeon (family 0f, models 03 and higher) 107 * Core Solo and Core Duo processors (family 06, model 0e) 108 * Xeon 5100 series and Core 2 Duo (family 06, model 0f) 109 * Core 2 and Xeon (family 06, model 17) 110 * Atom (family 06, model 1c) 111 * 112 * We'll also assume that it's safe on the Pentium, and 113 * that it's safe on P-II and P-III Xeons due to the 114 * typical configuration of those systems. 115 * 116 */ 117 switch (CPUID_TO_BASEFAMILY(ci->ci_signature)) { 118 case 0x05: 119 invariant = true; 120 break; 121 case 0x06: 122 invariant = CPUID_TO_MODEL(ci->ci_signature) == 0x0e || 123 CPUID_TO_MODEL(ci->ci_signature) == 0x0f || 124 CPUID_TO_MODEL(ci->ci_signature) == 0x17 || 125 CPUID_TO_MODEL(ci->ci_signature) == 0x1c; 126 break; 127 case 0x0f: 128 invariant = CPUID_TO_MODEL(ci->ci_signature) >= 0x03; 129 break; 130 } 131 } else if (cpu_vendor == CPUVENDOR_AMD) { 132 /* 133 * TSC and Power Management Events on AMD Processors 134 * Nov 2, 2005 Rich Brunner, AMD Fellow 135 * http://lkml.org/lkml/2005/11/4/173 136 * 137 * See Appendix E.4.7 CPUID Fn8000_0007_EDX Advanced Power 138 * Management Features, AMD64 Architecture Programmer's 139 * Manual Volume 3: General-Purpose and System Instructions. 140 * The check is done below. 141 */ 142 143 /* 144 * AMD Errata 778: Processor Core Time Stamp Counters May 145 * Experience Drift 146 * 147 * This affects all family 15h and family 16h processors. 148 */ 149 switch (CPUID_TO_FAMILY(ci->ci_signature)) { 150 case 0x15: 151 case 0x16: 152 return false; 153 } 154 } 155 156 /* 157 * The best way to check whether the TSC counter is invariant or not 158 * is to check CPUID 80000007. 159 */ 160 family = CPUID_TO_BASEFAMILY(ci->ci_signature); 161 if (((cpu_vendor == CPUVENDOR_INTEL) || (cpu_vendor == CPUVENDOR_AMD) 162 || (cpu_vendor == CPUVENDOR_IDT)) 163 && ((family == 0x06) || (family == 0x07) || (family == 0x0f))) { 164 x86_cpuid(0x80000000, descs); 165 if (descs[0] >= 0x80000007) { 166 x86_cpuid(0x80000007, descs); 167 invariant = (descs[3] & CPUID_APM_ITSC) != 0; 168 } 169 } 170 171 return invariant; 172 } 173 174 /* Setup function pointers for rdtsc() and timecounter(9). */ 175 void 176 tsc_setfunc(struct cpu_info *ci) 177 { 178 bool use_lfence, use_mfence; 179 uint32_t family; 180 181 use_lfence = use_mfence = false; 182 183 /* 184 * XXX On AMD, we might be able to use lfence for some cases: 185 * a) if MSR_DE_CFG exist and the bit 1 is set. 186 * b) family == 0x0f or 0x11. Those have no MSR_DE_CFG and 187 * lfence is always serializing. 188 * 189 * We don't use it because the test result showed mfence was better 190 * than lfence with MSR_DE_CFG. 191 */ 192 if (cpu_vendor == CPUVENDOR_AMD) 193 use_mfence = true; 194 else if (cpu_vendor == CPUVENDOR_INTEL) 195 use_lfence = true; 196 else if (cpu_vendor == CPUVENDOR_IDT) { 197 family = CPUID_TO_BASEFAMILY(ci->ci_signature); 198 use_lfence = family > 0x06 || (family == 0x06 && 199 CPUID_TO_MODEL(ci->ci_signature) == 0x0f); 200 } 201 202 /* LFENCE and MFENCE are applicable if SSE2 is set. */ 203 if ((ci->ci_feat_val[0] & CPUID_SSE2) == 0) 204 use_lfence = use_mfence = false; 205 206 #define TSC_SETFUNC(fence) \ 207 do { \ 208 rdtsc = rdtsc_##fence; \ 209 cpu_counter = cpu_counter_##fence; \ 210 cpu_counter32 = cpu_counter32_##fence; \ 211 } while (/* CONSTCOND */ 0) 212 213 if (use_lfence) 214 TSC_SETFUNC(lfence); 215 else if (use_mfence) 216 TSC_SETFUNC(mfence); 217 else 218 TSC_SETFUNC(cpuid); 219 220 aprint_verbose_dev(ci->ci_dev, "Use %s to serialize rdtsc\n", 221 use_lfence ? "lfence" : (use_mfence ? "mfence" : "cpuid")); 222 } 223 224 /* 225 * Initialize timecounter(9) and DELAY() function of TSC. 226 * 227 * This function is called after all secondary processors were brought up 228 * and drift has been measured, and after any other potential delay funcs 229 * have been installed (e.g. lapic_delay()). 230 */ 231 void 232 tsc_tc_init(void) 233 { 234 struct cpu_info *ci; 235 bool invariant; 236 237 if (!cpu_hascounter()) 238 return; 239 240 ci = curcpu(); 241 tsc_freq = ci->ci_data.cpu_cc_freq; 242 invariant = tsc_is_invariant(); 243 if (!invariant) { 244 aprint_debug("TSC not known invariant on this CPU\n"); 245 tsc_timecounter.tc_quality = -100; 246 } else if (tsc_drift_observed > tsc_drift_max) { 247 aprint_error("ERROR: %lld cycle TSC drift observed\n", 248 (long long)tsc_drift_observed); 249 tsc_timecounter.tc_quality = -100; 250 invariant = false; 251 } else if (vm_guest == VM_GUEST_NO) { 252 delay_func = tsc_delay; 253 } else if (vm_guest == VM_GUEST_VIRTUALBOX || 254 vm_guest == VM_GUEST_NVMM) { 255 /* 256 * nvmm doesn't advance guest tsc constantly unless 257 * the vcpu is pinned to a physical cpu. 258 */ 259 tsc_timecounter.tc_quality = -100; 260 } 261 262 if (tsc_freq != 0) { 263 tsc_timecounter.tc_frequency = tsc_freq; 264 tc_init(&tsc_timecounter); 265 } 266 } 267 268 /* 269 * Record drift (in clock cycles). Called during AP startup. 270 */ 271 void 272 tsc_sync_drift(int64_t drift) 273 { 274 275 if (drift < 0) 276 drift = -drift; 277 if (drift > tsc_drift_observed) 278 tsc_drift_observed = drift; 279 } 280 281 /* 282 * Called during startup of APs, by the boot processor. Interrupts 283 * are disabled on entry. 284 */ 285 static void __noinline 286 tsc_read_bp(struct cpu_info *ci, uint64_t *bptscp, uint64_t *aptscp) 287 { 288 uint64_t bptsc; 289 290 if (atomic_swap_ptr(&tsc_sync_cpu, ci) != NULL) { 291 panic("tsc_sync_bp: 1"); 292 } 293 294 /* Prepare a cache miss for the other side. */ 295 (void)atomic_swap_uint((void *)&tsc_dummy_cacheline, 0); 296 297 /* Flag our readiness. */ 298 atomic_or_uint(&ci->ci_flags, CPUF_SYNCTSC); 299 300 /* Wait for other side then read our TSC. */ 301 while ((ci->ci_flags & CPUF_SYNCTSC) != 0) { 302 __insn_barrier(); 303 } 304 bptsc = rdtsc(); 305 306 /* Wait for the results to come in. */ 307 while (tsc_sync_cpu == ci) { 308 x86_pause(); 309 } 310 if (tsc_sync_cpu != NULL) { 311 panic("tsc_sync_bp: 2"); 312 } 313 314 *bptscp = bptsc; 315 *aptscp = tsc_sync_val; 316 } 317 318 void 319 tsc_sync_bp(struct cpu_info *ci) 320 { 321 int64_t bptsc, aptsc, val, diff; 322 323 if (!cpu_hascounter()) 324 return; 325 326 val = INT64_MAX; 327 for (int i = 0; i < TSC_SYNC_ROUNDS; i++) { 328 tsc_read_bp(ci, &bptsc, &aptsc); 329 diff = bptsc - aptsc; 330 if (ABS(diff) < ABS(val)) { 331 val = diff; 332 } 333 } 334 335 ci->ci_data.cpu_cc_skew = val; 336 } 337 338 /* 339 * Called during startup of AP, by the AP itself. Interrupts are 340 * disabled on entry. 341 */ 342 static void __noinline 343 tsc_post_ap(struct cpu_info *ci) 344 { 345 uint64_t tsc; 346 347 /* Wait for go-ahead from primary. */ 348 while ((ci->ci_flags & CPUF_SYNCTSC) == 0) { 349 __insn_barrier(); 350 } 351 352 /* Instruct primary to read its counter. */ 353 atomic_and_uint(&ci->ci_flags, ~CPUF_SYNCTSC); 354 355 /* Suffer a cache miss, then read TSC. */ 356 __insn_barrier(); 357 tsc = tsc_dummy_cacheline; 358 __insn_barrier(); 359 tsc += rdtsc(); 360 361 /* Post result. Ensure the whole value goes out atomically. */ 362 (void)atomic_swap_64(&tsc_sync_val, tsc); 363 364 if (atomic_swap_ptr(&tsc_sync_cpu, NULL) != ci) { 365 panic("tsc_sync_ap"); 366 } 367 } 368 369 void 370 tsc_sync_ap(struct cpu_info *ci) 371 { 372 373 if (!cpu_hascounter()) 374 return; 375 376 for (int i = 0; i < TSC_SYNC_ROUNDS; i++) { 377 tsc_post_ap(ci); 378 } 379 } 380 381 static void 382 tsc_apply_cpu(void *arg1, void *arg2) 383 { 384 bool enable = arg1 != NULL; 385 if (enable) { 386 lcr4(rcr4() & ~CR4_TSD); 387 } else { 388 lcr4(rcr4() | CR4_TSD); 389 } 390 } 391 392 void 393 tsc_user_enable(void) 394 { 395 uint64_t xc; 396 397 xc = xc_broadcast(0, tsc_apply_cpu, (void *)true, NULL); 398 xc_wait(xc); 399 } 400 401 void 402 tsc_user_disable(void) 403 { 404 uint64_t xc; 405 406 xc = xc_broadcast(0, tsc_apply_cpu, (void *)false, NULL); 407 xc_wait(xc); 408 } 409 410 uint64_t 411 cpu_frequency(struct cpu_info *ci) 412 { 413 414 return ci->ci_data.cpu_cc_freq; 415 } 416 417 int 418 cpu_hascounter(void) 419 { 420 421 return cpu_feature[0] & CPUID_TSC; 422 } 423 424 static void 425 tsc_delay(unsigned int us) 426 { 427 uint64_t start, delta; 428 429 start = cpu_counter(); 430 delta = (uint64_t)us * tsc_freq / 1000000; 431 432 while ((cpu_counter() - start) < delta) { 433 x86_pause(); 434 } 435 } 436 437 static u_int 438 tsc_get_timecount(struct timecounter *tc) 439 { 440 #if defined(_LP64) && defined(DIAGNOSTIC) /* requires atomic 64-bit store */ 441 static __cpu_simple_lock_t lock = __SIMPLELOCK_UNLOCKED; 442 static int lastwarn; 443 uint64_t cur, prev; 444 lwp_t *l = curlwp; 445 int ticks; 446 447 /* 448 * Previous value must be read before the counter and stored to 449 * after, because this routine can be called from interrupt context 450 * and may run over the top of an existing invocation. Ordering is 451 * guaranteed by "volatile" on md_tsc. 452 */ 453 prev = l->l_md.md_tsc; 454 cur = cpu_counter(); 455 if (__predict_false(cur < prev) && (cur >> 63) == (prev >> 63) && 456 __cpu_simple_lock_try(&lock)) { 457 ticks = getticks(); 458 if (ticks - lastwarn >= hz) { 459 printf( 460 "WARNING: %s TSC went backwards by %u - " 461 "change sysctl(7) kern.timecounter?\n", 462 cpu_name(curcpu()), (unsigned)(prev - cur)); 463 lastwarn = ticks; 464 } 465 __cpu_simple_unlock(&lock); 466 } 467 l->l_md.md_tsc = cur; 468 return (uint32_t)cur; 469 #else 470 return cpu_counter32(); 471 #endif 472 } 473 474 /* 475 * tsc has been reset; zero the cached tsc of every lwp in the system 476 * so we don't spuriously report that the tsc has gone backward. 477 * Caller must ensure all LWPs are quiescent (except the current one, 478 * obviously) and interrupts are blocked while we update this. 479 */ 480 void 481 tsc_tc_reset(void) 482 { 483 struct lwp *l; 484 485 LIST_FOREACH(l, &alllwp, l_list) 486 l->l_md.md_tsc = 0; 487 } 488 489 #ifdef BOOT_DURATION 490 /* Returns the kernel boot time in milliseconds. */ 491 uint64_t 492 boot_duration_timer(void) 493 { 494 KASSERT(curcpu_stable()); 495 KASSERT(CPU_IS_PRIMARY(curcpu())); 496 return (rdtsc() - ((uint64_t)starttsc_hi << 32 | starttsc_lo)) / 497 (curcpu()->ci_data.cpu_cc_freq / 1000); 498 } 499 #endif 500