git: de1292c6ff8a - main - Use CPUID leaf 0x40000010 for local APIC freq

From: Colin Percival <cperciva_at_FreeBSD.org>
Date: Sat, 15 Jan 2022 01:30:36 UTC
The branch main has been updated by cperciva:

URL: https://cgit.FreeBSD.org/src/commit/?id=de1292c6ff8a445fd453effba8cc23c38cea223f

commit de1292c6ff8a445fd453effba8cc23c38cea223f
Author:     Colin Percival <cperciva@FreeBSD.org>
AuthorDate: 2022-01-09 00:59:31 +0000
Commit:     Colin Percival <cperciva@FreeBSD.org>
CommitDate: 2022-01-15 01:30:17 +0000

    Use CPUID leaf 0x40000010 for local APIC freq
    
    Some VM systems announce the frequency of the local APIC via the
    CPUID leaf 0x40000010.  Using this allows us to boot slightly
    faster by avoiding the need for timer calibration.
    
    Reviewed by:    markj
    Sponsored by:   https://www.patreon.com/cperciva
---
 sys/x86/x86/local_apic.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/sys/x86/x86/local_apic.c b/sys/x86/x86/local_apic.c
index 4b66d10cb5ae..7be603c71146 100644
--- a/sys/x86/x86/local_apic.c
+++ b/sys/x86/x86/local_apic.c
@@ -1002,6 +1002,35 @@ native_lapic_disable_pmc(void)
 #endif
 }
 
+static int
+lapic_calibrate_initcount_cpuid_vm(void)
+{
+	u_int regs[4];
+	uint64_t freq;
+
+	/* Get value from CPUID leaf if possible. */
+	if (vm_guest == VM_GUEST_NO)
+		return (false);
+	if (hv_high < 0x40000010)
+		return (false);
+	do_cpuid(0x40000010, regs);
+	freq = (uint64_t)(regs[1]) * 1000;
+
+	/* Pick timer divisor. */
+	lapic_timer_divisor = 2;
+	do {
+		if (freq / lapic_timer_divisor < APIC_TIMER_MAX_COUNT)
+			break;
+		lapic_timer_divisor <<= 1;
+	} while (lapic_timer_divisor <= 128);
+	if (lapic_timer_divisor > 128)
+		return (false);
+
+	/* Record divided frequency. */
+	count_freq = freq / lapic_timer_divisor;
+	return (true);
+}
+
 static uint64_t
 cb_lapic_getcount(void)
 {
@@ -1014,6 +1043,9 @@ lapic_calibrate_initcount(struct lapic *la)
 {
 	uint64_t freq;
 
+	if (lapic_calibrate_initcount_cpuid_vm())
+		goto done;
+
 	/* Calibrate the APIC timer frequency. */
 	lapic_timer_set_divisor(2);
 	lapic_timer_oneshot_nointr(la, APIC_TIMER_MAX_COUNT);
@@ -1031,6 +1063,7 @@ lapic_calibrate_initcount(struct lapic *la)
 	if (lapic_timer_divisor > 128)
 		panic("lapic: Divisor too big");
 	count_freq = freq * 2 / lapic_timer_divisor;
+done:
 	if (bootverbose) {
 		printf("lapic: Divisor %lu, Frequency %lu Hz\n",
 		    lapic_timer_divisor, count_freq);