From: Imre Vadász Date: Sat, 4 Aug 2018 15:09:26 +0000 (+0200) Subject: pc64 - If appropriate, determine TSC frequency via CPUID. X-Git-Tag: v5.5.0~321 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/8b257adbda32b59bb93cb76a5daa122a9252f6c0 pc64 - If appropriate, determine TSC frequency via CPUID. On Intel systems with Skylake and newer CPUs, and on modern Atom CPUs this avoids the ca. 200ms TSC calibration process at the beginning of booting. On most modern Intel CPUs the TSC frequency can be determined via CPUID information. For the most modern generations this is quite well documented. If the CPUID information doesn't directly specify the "crystal clock", we use the frequency values given in Intel's "Software Developer's Manual" for the different cpu variants. Since the "crystal clock" seems to exactly match the HPET frequency that we use in the calibration, this method should be at least as good as our calibration procedure. Setting the tunable hw.tsc_ignore_cpuid=1 forces the kernel to calibrate the TSC if it would otherwise just use the CPUID information. --- diff --git a/sys/platform/pc64/x86_64/initcpu.c b/sys/platform/pc64/x86_64/initcpu.c index 6b2153ee4a..25b60bd2da 100644 --- a/sys/platform/pc64/x86_64/initcpu.c +++ b/sys/platform/pc64/x86_64/initcpu.c @@ -43,6 +43,11 @@ #include #include +extern int i8254_cputimer_disable; + +static int tsc_ignore_cpuid = 0; +TUNABLE_INT("hw.tsc_ignore_cpuid", &tsc_ignore_cpuid); + static int hw_instruction_sse; SYSCTL_INT(_hw, OID_AUTO, instruction_sse, CTLFLAG_RD, &hw_instruction_sse, 0, "SIMD/MMX2 instructions available in CPU"); @@ -326,3 +331,67 @@ initializecpu(int cpu) if ((amd_feature & AMDID_RDTSCP) != 0) wrmsr(MSR_TSCAUX, cpu); } + +/* + * This method should be at least as good as calibrating the TSC based on the + * HPET timer, since the HPET runs with the core crystal clock apparently. + */ +static void +detect_tsc_frequency(void) +{ + int cpu_family, cpu_model; + u_int regs[4]; + uint64_t crystal = 0; + + cpu_model = CPUID_TO_MODEL(cpu_id); + cpu_family = CPUID_TO_FAMILY(cpu_id); + + if (cpu_vendor_id != CPU_VENDOR_INTEL) + return; + + if (cpu_high < 0x15) + return; + + do_cpuid(0x15, regs); + if (regs[0] == 0 || regs[1] == 0) + return; + + if (regs[2] == 0) { + /* For some families the SDM contains the core crystal clock. */ + if (cpu_family == 0x6) { + switch (cpu_model) { + case 0x55: /* Xeon Scalable */ + crystal = 25000000; /* 25 MHz */ + break; + /* Skylake */ + case 0x4e: + case 0x5e: + /* Kabylake/Coffeelake */ + case 0x8e: + case 0x9e: + crystal = 24000000; /* 24 MHz */ + break; + case 0x5c: /* Goldmont Atom */ + crystal = 19200000; /* 19.2 MHz */ + break; + default: + break; + } + } + } else { + crystal = regs[2]; + } + + if (crystal == 0) + return; + + kprintf("TSC crystal clock: %ju Hz, TSC/crystal ratio: %u/%u\n", + crystal, regs[1], regs[0]); + + if (tsc_ignore_cpuid == 0) { + tsc_frequency = (crystal * regs[1]) / regs[0]; + i8254_cputimer_disable = 1; + } +} + +TIMECOUNTER_INIT(cpuid_tsc_frequency, detect_tsc_frequency);