tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

patch review: add support for CPUID leaf 0x40000010 TSC/LAPIC frequency detection




Various hypervisors implement CPUID leaf 0x40000010, first created by
VMware. This leaf forwards TSC and LAPIC frequencies from the host to
the guest, thus avoiding useless DELAY calls, and lowering the MICROVM
kernel boot time from ~250ms to 15.

The following patch adds support for reading said CPUID leaf.

As sys/arch/x86/x86/identcpu_subr.c is also used by usr.sbin/cpuctl,
the added tsc_freq_vmware_cpuid() function is #ifdef'd _KERNEL.

In cpu_tsc_freq_cpuid() and more generally in identcpu_subr.c there are
more frequency detection methods coming in upcoming patches, hence the
small conditioning revamp.


diff --git a/sys/arch/x86/include/apicvar.h b/sys/arch/x86/include/apicvar.h
index 8733d4b5aee..2af52727ad3 100644
--- a/sys/arch/x86/include/apicvar.h
+++ b/sys/arch/x86/include/apicvar.h
@@ -56,5 +56,6 @@ void apic_format_redir(const char *, const char *, int, int, uint32_t,

 /* For lapic.c */
 extern uint32_t lapic_per_second;
+extern bool lapic_from_cpuid;

 #endif /* !_X86_APICVAR_H_ */
diff --git a/sys/arch/x86/x86/cpu.c b/sys/arch/x86/x86/cpu.c
index a7fa00d0b66..87eb12dcb9b 100644
--- a/sys/arch/x86/x86/cpu.c
+++ b/sys/arch/x86/x86/cpu.c
@@ -1416,7 +1416,8 @@ cpu_shutdown(device_t dv, int how)
 void
 cpu_get_tsc_freq(struct cpu_info *ci)
 {
-	uint64_t freq = 0, freq_from_cpuid, t0, t1;
+	static uint64_t freq_from_cpuid = 0;
+	uint64_t freq = 0, t0, t1;
 	int64_t overhead;

 	if (CPU_IS_PRIMARY(ci) && cpu_hascounter()) {
@@ -1426,7 +1427,13 @@ cpu_get_tsc_freq(struct cpu_info *ci)
 		 * The function also set lapic_per_second variable if it's
 		 * known. This is required for Intel's Comet Lake and newer
 		 * processors to set LAPIC timer correctly.
+		 *
+		 * If TSC freq is already known by CPUID, don't go through
+		 * tests again.
 		 */
+		if (freq_from_cpuid != 0)
+			return;
+
 		if (ci->ci_data.cpu_cc_freq == 0)
 			freq = freq_from_cpuid = cpu_tsc_freq_cpuid(ci);
 		if (freq != 0)
diff --git a/sys/arch/x86/x86/identcpu_subr.c b/sys/arch/x86/x86/identcpu_subr.c
index 52269505d36..2c578a7469c 100644
--- a/sys/arch/x86/x86/identcpu_subr.c
+++ b/sys/arch/x86/x86/identcpu_subr.c
@@ -62,14 +62,44 @@ __KERNEL_RCSID(0, "$NetBSD: identcpu_subr.c,v 1.9 2021/10/07 13:04:18 msaitoh Ex
 #include "cpuctl_i386.h"
 #endif

-uint64_t
-cpu_tsc_freq_cpuid(struct cpu_info *ci)
+#ifdef _KERNEL
+static uint64_t
+tsc_freq_vmware_cpuid(struct cpu_info *ci)
+{
+	uint32_t descs[4];
+	uint64_t freq;
+
+	if (ci->ci_max_ext_cpuid < 0x40000010)
+		return 0;
+
+	x86_cpuid(0x40000010, descs);
+
+	freq = descs[0];
+	if (freq == 0)
+		return 0;
+
+	aprint_verbose(
+		"got tsc from vmware compatible cpuid\n");
+
+	if (descs[1] > 0) {
+		aprint_verbose(
+			"got lapic frequency from vmware compatible cpuid\n");
+		lapic_per_second = descs[1] * 1000;
+		lapic_from_cpuid = true;
+	}
+
+	return freq * 1000;
+}
+#endif
+
+static uint64_t
+tsc_freq_cpuid(struct cpu_info *ci)
 {
 	uint64_t freq = 0, khz;
 	uint32_t descs[4];
 	uint32_t denominator, numerator;

-	if (!((ci->ci_max_cpuid >= 0x15) && (cpu_vendor == CPUVENDOR_INTEL)))
+	if (ci->ci_max_cpuid < 0x15)
 		return 0;

 	x86_cpuid(0x15, descs);
@@ -139,6 +169,21 @@ cpu_tsc_freq_cpuid(struct cpu_info *ci)
 		}
 #endif
 	}
+	return freq;
+}
+
+uint64_t
+cpu_tsc_freq_cpuid(struct cpu_info *ci)
+{
+	uint64_t freq = 0;
+
+	if (cpu_vendor == CPUVENDOR_INTEL)
+		freq = tsc_freq_cpuid(ci);
+#ifdef _KERNEL
+	/* VMware compatible tsc query */
+	if (freq == 0 && vm_guest > VM_GUEST_NO)
+		freq = tsc_freq_vmware_cpuid(ci);
+#endif
 	if (freq != 0)
 		aprint_verbose_dev(ci->ci_dev, "TSC freq CPUID %" PRIu64
 		    " Hz\n", freq);
diff --git a/sys/arch/x86/x86/lapic.c b/sys/arch/x86/x86/lapic.c
index d9434e59960..570b56d869f 100644
--- a/sys/arch/x86/x86/lapic.c
+++ b/sys/arch/x86/x86/lapic.c
@@ -131,6 +131,7 @@ bool x2apic_enable = true;
 #else
 bool x2apic_enable = false;
 #endif
+bool lapic_from_cpuid = false;

 static bool lapic_broken_periodic __read_mostly;

@@ -606,9 +607,11 @@ lapic_initclock(void)
 		/*
 		 * Recalibrate the timer using the cycle counter, now that
 		 * the cycle counter itself has been recalibrated.
+		 *
+		 * Not needed when lapic_per_second is read from CPUID.
 		 */
-		lapic_calibrate_timer(true);
-
+		if (!lapic_from_cpuid)
+			lapic_calibrate_timer(true);
 		/*
 		 * Hook up time counter.  This assume that all LAPICs have
 		 * the same frequency.


------------------------------------------------------------------------
Emile `iMil' Heitor <imil@{home.imil.net,NetBSD.org}> | https://imil.net



Home | Main Index | Thread Index | Old Index