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 @@ -48,6 +49,9 @@ __KERNEL_RCSID(0, "$NetBSD: identcpu_sub #include #include #include +#if NHYPERV > 0 +#include +#endif #include #include #include @@ -58,7 +62,6 @@ __KERNEL_RCSID(0, "$NetBSD: identcpu_sub #include #include #include -#include #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);