Index: sys/arch/x86/x86/identcpu_subr.c
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/x86/identcpu_subr.c,v
retrieving revision 1.14
diff -p -u -r1.14 identcpu_subr.c
--- sys/arch/x86/x86/identcpu_subr.c	2 May 2025 07:08:11 -0000	1.14
+++ sys/arch/x86/x86/identcpu_subr.c	7 May 2025 04:15:17 -0000
@@ -39,6 +39,7 @@ __KERNEL_RCSID(0, "$NetBSD: identcpu_sub
 
 #ifdef _KERNEL_OPT
 #include "lapic.h"
+#include "hyperv.h"
 #endif
 
 #include <sys/param.h>
@@ -48,6 +49,9 @@ __KERNEL_RCSID(0, "$NetBSD: identcpu_sub
 #include <x86/cpuvar.h>
 #include <x86/apicvar.h>
 #include <x86/cacheinfo.h>
+#if NHYPERV > 0
+#include <x86/x86/hypervreg.h>
+#endif
 #include <machine/cpufunc.h>
 #include <machine/cputypes.h>
 #include <machine/specialreg.h>
@@ -58,7 +62,6 @@ __KERNEL_RCSID(0, "$NetBSD: identcpu_sub
 #include <stdlib.h>
 #include <string.h>
 #include <x86/cacheinfo.h>
-#include <x86/cpuvar.h>
 #include "cpuctl.h"
 #include "cpuctl_i386.h"
 #endif
@@ -70,7 +73,11 @@ tsc_freq_vmware_cpuid(struct cpu_info *c
 	uint32_t descs[4];
 	uint64_t freq;
 
-	if (cpu_max_hypervisor_cpuid < 0x40000010)
+	if (cpu_max_hypervisor_cpuid < 0x40000000)
+		return 0;
+
+	x86_cpuid(0x40000000, descs);
+	if (descs[0] < 0x40000010)
 		return 0;
 
 	x86_cpuid(0x40000010, descs);
@@ -79,12 +86,12 @@ tsc_freq_vmware_cpuid(struct cpu_info *c
 	if (freq == 0)
 		return 0;
 
-	aprint_verbose(
+	aprint_verbose_dev(ci->ci_dev,
 	    "got tsc frequency from vmware compatible cpuid\n");
 
 #if NLAPIC > 0
 	if (descs[1] > 0) {
-		aprint_verbose(
+		aprint_verbose_dev(ci->ci_dev,
 		    "got lapic frequency from vmware compatible cpuid\n");
 		lapic_per_second = descs[1] * 1000;
 		lapic_from_cpuid = true;
@@ -95,6 +102,25 @@ tsc_freq_vmware_cpuid(struct cpu_info *c
 }
 #endif
 
+#ifdef _KERNEL
+#if NHYPERV > 0
+static uint64_t
+tsc_freq_hyperv_cpuid(struct cpu_info *ci)
+{
+	uint64_t freq;
+
+	freq = rdmsr(MSR_HV_TSC_FREQUENCY);
+	if (freq == 0)
+		return 0;
+
+	aprint_verbose_dev(ci->ci_dev,
+		"got tsc from hyperv MSR\n");
+
+	return freq;
+}
+#endif /* NHYPERV > 0 */
+#endif /* _KERNEL */
+
 static uint64_t
 tsc_freq_cpuid(struct cpu_info *ci)
 {
@@ -163,7 +189,7 @@ tsc_freq_cpuid(struct cpu_info *ci)
 				    (uint64_t)descs[1] * 1000000);
 			}
 		}
-#if defined(_KERNEL) &&  NLAPIC > 0
+#if defined(_KERNEL) && NLAPIC > 0
 		if ((khz != 0) && (lapic_per_second == 0)) {
 			lapic_per_second = khz * 1000;
 			lapic_from_cpuid = true;
@@ -176,18 +202,168 @@ tsc_freq_cpuid(struct cpu_info *ci)
 	return freq;
 }
 
+#ifdef _KERNEL
+/* Ported from OpenBSD's sys/arch/amd64/amd64/tsc.c */
+static uint64_t
+tsc_freq_amd_msr(struct cpu_info *ci)
+{
+	uint64_t base, def, divisor, multiplier;
+	uint32_t family = CPUID_TO_FAMILY(ci->ci_signature);
+
+	/*
+	 * All 10h+ CPUs have Core::X86::Msr:HWCR and the TscFreqSel
+	 * bit.  If TscFreqSel hasn't been set, the TSC isn't advancing
+	 * at the core P0 frequency and we need to calibrate by hand.
+	 */
+	if (family < 0x10)
+		return 0;
+	if (!ISSET(rdmsr(MSR_HWCR), HWCR_TSCFREQSEL))
+		return 0;
+
+	/*
+	 * In 10h+ CPUs, Core::X86::Msr::PStateDef defines the voltage
+	 * and frequency for each core P-state.  We want the P0 frequency.
+	 * If the En bit isn't set, the register doesn't define a valid
+	 * P-state.
+	 */
+	def = rdmsr(MSR_PSTATEDEF(0));
+	if (!ISSET(def, PSTATEDEF_EN))
+		return 0;
+
+	switch (family) {
+	case 0x17:
+	case 0x19:
+		/*
+		 * PPR for AMD Family 17h [...]:
+		 * Models 01h,08h B2, Rev 3.03, pp. 33, 139-140
+		 * Model 18h B1, Rev 3.16, pp. 36, 143-144
+		 * Model 60h A1, Rev 3.06, pp. 33, 155-157
+		 * Model 71h B0, Rev 3.06, pp. 28, 150-151
+		 *
+		 * PPR for AMD Family 19h [...]:
+		 * Model 21h B0, Rev 3.05, pp. 33, 166-167
+		 *
+		 * OSRR for AMD Family 17h processors,
+		 * Models 00h-2Fh, Rev 3.03, pp. 130-131
+		 */
+		base = 200000000;			/* 200.0 MHz */
+		divisor = (def >> 8) & 0x3f;
+		if (divisor <= 0x07 || divisor >= 0x2d)
+			return 0;			/* reserved */
+		if (divisor >= 0x1b && divisor % 2 == 1)
+			return 0;			/* reserved */
+		multiplier = def & 0xff;
+		if (multiplier <= 0x0f)
+			return 0;			/* reserved */
+		break;
+	default:
+		return 0;
+	}
+
+	return base * multiplier / divisor;
+}
+#endif /* _KERNEL */
+
+/* Ported from FreeBSD sys/x86/x86/tsc.c */
+static uint64_t
+tsc_freq_intel_brand(struct cpu_info *ci)
+{
+	char brand[48];
+	u_int regs[4];
+	uint64_t freq;
+	char *p;
+	u_int i;
+
+	/*
+	 * Intel Processor Identification and the CPUID Instruction
+	 * Application Note 485.
+	 * http://www.intel.com/assets/pdf/appnote/241618.pdf
+	 */
+	if (ci->ci_max_ext_cpuid >= 0x80000004) {
+		p = brand;
+		for (i = 0x80000002; i < 0x80000005; i++) {
+			x86_cpuid(i, regs);
+			memcpy(p, regs, sizeof(regs));
+			p += sizeof(regs);
+		}
+		p = NULL;
+		for (i = 0; i < sizeof(brand) - 1; i++)
+			if (brand[i] == 'H' && brand[i + 1] == 'z')
+				p = brand + i;
+		if (p != NULL) {
+			p -= 5;
+			switch (p[4]) {
+			case 'M':
+				i = 1;
+				break;
+			case 'G':
+				i = 1000;
+				break;
+			case 'T':
+				i = 1000000;
+				break;
+			default:
+				return 0;
+			}
+#define	C2D(c)	((c) - '0')
+			if (p[1] == '.') {
+				freq = C2D(p[0]) * 1000;
+				freq += C2D(p[2]) * 100;
+				freq += C2D(p[3]) * 10;
+				freq *= i * 1000;
+			} else {
+				freq = C2D(p[0]) * 1000;
+				freq += C2D(p[1]) * 100;
+				freq += C2D(p[2]) * 10;
+				freq += C2D(p[3]);
+				freq *= i * 1000000;
+			}
+#undef C2D
+			aprint_verbose(
+				"got tsc from cpu brand\n");
+			return freq;
+		}
+	}
+	return 0;
+}
+
 uint64_t
 cpu_tsc_freq_cpuid(struct cpu_info *ci)
 {
 	uint64_t freq = 0;
 
 #ifdef _KERNEL
-	/* VMware compatible tsc and lapic frequency query */
-	if (vm_guest > VM_GUEST_NO)
+	/* VM guest specific tsc query */
+	switch (vm_guest) {
+	case VM_GUEST_VM:
+	case VM_GUEST_VMWARE:
 		freq = tsc_freq_vmware_cpuid(ci);
-#endif
-	if (freq == 0 && cpu_vendor == CPUVENDOR_INTEL)
-		freq = tsc_freq_cpuid(ci);
+		break;
+#if NHYPERV > 0
+	case VM_GUEST_HV:
+		freq = tsc_freq_hyperv_cpuid(ci);
+		break;
+#endif /* NHYPERV > 0 */
+	default:
+		break;
+	}
+#endif /* _KERNEL */
+
+	if (freq == 0) {
+		switch (cpu_vendor) {
+		case CPUVENDOR_INTEL:
+			freq = tsc_freq_cpuid(ci);
+			if (freq == 0)
+				freq = tsc_freq_intel_brand(ci);
+			break;
+#ifdef _KERNEL
+		case CPUVENDOR_AMD:
+			freq = tsc_freq_amd_msr(ci);
+			break;
+#endif /* _KERNEL */
+		}
+	}
+
 	if (freq != 0)
 		aprint_verbose_dev(ci->ci_dev, "TSC freq CPUID %" PRIu64
 		    " Hz\n", freq);