diff options
author | Mark Johnston <markj@FreeBSD.org> | 2022-03-05 00:34:43 +0000 |
---|---|---|
committer | Mark Johnston <markj@FreeBSD.org> | 2022-03-05 00:34:43 +0000 |
commit | 075e2779aca7cbd8f201ce0e1bb60318d0cbd8b8 (patch) | |
tree | f6aa90ae367a5acce2f413d694fbf629dd1b4b35 /sys/x86 | |
parent | f7a6dccf426676bdd8c6be25491e57dca830de25 (diff) | |
download | src-075e2779aca7cbd8f201ce0e1bb60318d0cbd8b8.tar.gz src-075e2779aca7cbd8f201ce0e1bb60318d0cbd8b8.zip |
x86: Defer early TSC timecounter calibration to SI_SUB_CPU
If we can't determine the TSC frequency using CPU registers, we need to
give a chance for Hyper-V drivers to register a timecounter (during
SI_SUB_HYPERVISOR) since an emulated 8254 might not be available.
Thus, split probe_tsc_freq() into early and late stages, and wait until
the latter to attempt calibration using a reference clock.
Fixes: 84369dd52369 ("x86: Probe the TSC frequency earlier")
Reported and tested by: khng, Shawn Webb
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D34444
Diffstat (limited to 'sys/x86')
-rw-r--r-- | sys/x86/x86/tsc.c | 50 |
1 files changed, 36 insertions, 14 deletions
diff --git a/sys/x86/x86/tsc.c b/sys/x86/x86/tsc.c index 82ee358b6895..0fdd70a690a3 100644 --- a/sys/x86/x86/tsc.c +++ b/sys/x86/x86/tsc.c @@ -240,7 +240,7 @@ tsc_freq_intel_brand(uint64_t *res) } static void -tsc_freq_8254(uint64_t *res) +tsc_freq_tc(uint64_t *res) { uint64_t tsc1, tsc2; int64_t overhead; @@ -262,8 +262,15 @@ tsc_freq_8254(uint64_t *res) tsc_freq = (tsc2 - tsc1 - overhead) * 10; } +/* + * Try to determine the TSC frequency using CPUID or hypercalls. If successful, + * this lets use the TSC for early DELAY() calls instead of the 8254 timer, + * which may be unreliable or entirely absent on contemporary systems. However, + * avoid calibrating using the 8254 here so as to give hypervisors a chance to + * register a timecounter that can be used instead. + */ static void -probe_tsc_freq(void) +probe_tsc_freq_early(void) { #ifdef __i386__ /* The TSC is known to be broken on certain CPUs. */ @@ -364,7 +371,20 @@ probe_tsc_freq(void) if (bootverbose) printf("Early TSC frequency %juHz derived from CPUID\n", (uintmax_t)tsc_freq); - } else if (tsc_skip_calibration) { + } +} + +/* + * If we were unable to determine the TSC frequency via CPU registers, try + * to calibrate against a known clock. + */ +static void +probe_tsc_freq_late(void) +{ + if (tsc_freq != 0) + return; + + if (tsc_skip_calibration) { /* * Try to parse the brand string to obtain the nominal TSC * frequency. @@ -380,15 +400,24 @@ probe_tsc_freq(void) } } else { /* - * Calibrate against the 8254 PIT. This estimate will be - * refined later in tsc_calib(). + * Calibrate against a timecounter or the 8254 PIT. This + * estimate will be refined later in tsc_calib(). */ - tsc_freq_8254(&tsc_freq); + tsc_freq_tc(&tsc_freq); if (bootverbose) printf( "Early TSC frequency %juHz calibrated from 8254 PIT\n", (uintmax_t)tsc_freq); } +} + +void +start_TSC(void) +{ + if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled) + return; + + probe_tsc_freq_late(); if (cpu_power_ecx & CPUID_PERF_STAT) { /* @@ -401,13 +430,6 @@ probe_tsc_freq(void) if (rdmsr(MSR_MPERF) > 0 && rdmsr(MSR_APERF) > 0) tsc_perf_stat = 1; } -} - -void -start_TSC(void) -{ - if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled) - return; /* * Inform CPU accounting about our boot-time clock rate. This will @@ -718,7 +740,7 @@ tsc_init(void) if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled) return; - probe_tsc_freq(); + probe_tsc_freq_early(); } /* |