diff --git a/6.18/misc/amd-tsc-msr.patch b/6.18/misc/amd-tsc-msr.patch new file mode 100644 index 00000000..50ecaa84 --- /dev/null +++ b/6.18/misc/amd-tsc-msr.patch @@ -0,0 +1,193 @@ +From 8dc72039334c4a289bcf70cfbf14061ca342d8ab Mon Sep 17 00:00:00 2001 +From: Stephen Horvath +Date: Sun, 10 Aug 2025 13:35:17 +1000 +Subject: [PATCH v2] x86/tsc: Read AMD CPU frequency from + Core::X86::Msr::PStateDef + +AMD's Zen CPUs (17h and newer) have an MSR that provides the CPU/TSC +frequency directly, instead of calibrating it against the PIT. + +My understanding of the PIT calibration code is that it loops between +two and three times and takes 10ms (or 50ms) each loop, taking at least +20ms total to calibrate. This patch skips that calibration time. + +Through experimentation, this patch seems to save approximately 30ms on +boot time for Zen 4 (19h) CPUs (Ryzen 7 7800X3D & Ryzen 7 7840U). + +This has also been tested to not interfere with KVM guests running a +custom TSC frequency. + +Cc: Borislav Petkov +Signed-off-by: Stephen Horvath +--- +v2: Introduce per-family checks + + arch/x86/include/asm/tsc.h | 1 + + arch/x86/kernel/tsc.c | 2 + + arch/x86/kernel/tsc_msr.c | 118 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 121 insertions(+) + +diff --git a/arch/x86/include/asm/tsc.h b/arch/x86/include/asm/tsc.h +index 4f7f09f50552..a7e2710aa7f9 100644 +--- a/arch/x86/include/asm/tsc.h ++++ b/arch/x86/include/asm/tsc.h +@@ -119,5 +119,6 @@ extern void tsc_save_sched_clock_state(void); + extern void tsc_restore_sched_clock_state(void); + + unsigned long cpu_khz_from_msr(void); ++unsigned long cpu_khz_from_msr_amd(void); + + #endif /* _ASM_X86_TSC_H */ +diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c +index 87e749106dda..9acb7d13719d 100644 +--- a/arch/x86/kernel/tsc.c ++++ b/arch/x86/kernel/tsc.c +@@ -911,6 +911,8 @@ unsigned long native_calibrate_cpu_early(void) + { + unsigned long flags, fast_calibrate = cpu_khz_from_cpuid(); + ++ if (!fast_calibrate) ++ fast_calibrate = cpu_khz_from_msr_amd(); + if (!fast_calibrate) + fast_calibrate = cpu_khz_from_msr(); + if (!fast_calibrate) { +diff --git a/arch/x86/kernel/tsc_msr.c b/arch/x86/kernel/tsc_msr.c +index 48e6cc1cb017..f692b2d0590c 100644 +--- a/arch/x86/kernel/tsc_msr.c ++++ b/arch/x86/kernel/tsc_msr.c +@@ -10,10 +10,14 @@ + #include + + #include ++#include + #include ++#include + #include + #include ++#include + #include ++#include + #include + + #define MAX_NUM_FREQS 16 /* 4 bits to select the frequency */ +@@ -234,3 +238,117 @@ unsigned long cpu_khz_from_msr(void) + + return res; + } ++ ++/* ++ * MSR-based CPU/TSC frequency discovery for AMD Zen CPUs. ++ * ++ * Return processor base frequency in KHz, or 0 on failure. ++ */ ++unsigned long cpu_khz_from_msr_amd(void) ++{ ++ u64 hwcr, pstatedef; ++ unsigned long cpufid, cpudfsid, p0_freq; ++ ++ if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) ++ return 0; ++ ++ /* ++ * This register mapping is only valid for Zen and later CPUs. ++ * X86_FEATURE_ZEN is not set yet, so we just check the cpuid. ++ * Families newer than 0x1A haven't been released yet. ++ */ ++ if (boot_cpu_data.x86 < 0x17 || boot_cpu_data.x86 > 0x1A) ++ return 0; ++ ++ /* ++ * PPR states for MSR0000_0010: ++ * The TSC increments at the P0 frequency. The TSC counts at the ++ * same rate in all P-states, all C states, S0, or S1. ++ */ ++ ++ /* Read the Hardware Configuration MSR (MSRC001_0015) */ ++ if (rdmsrq_safe(MSR_K7_HWCR, &hwcr)) ++ return 0; ++ ++ /* ++ * Check TscFreqSel (bit 24) is set. ++ * This verifies the TSC does actually increment at P0 frequency. ++ * E.g. VMs may be configured to increment at a different rate. ++ */ ++ if (!(hwcr & BIT_64(24))) ++ return 0; ++ ++ /* Read the zeroth PStateDef MSR (MSRC001_0064) */ ++ if (rdmsrq_safe(MSR_AMD_PSTATE_DEF_BASE, &pstatedef)) ++ return 0; ++ ++ /* Check PstateEn is set (bit 63) */ ++ if (!(pstatedef & BIT_64(63))) ++ return 0; ++ ++ switch (boot_cpu_data.x86) { ++ case 0x17: ++ case 0x19: ++ /* CpuFid is the first 8 bits (7:0) */ ++ cpufid = pstatedef & 0xff; ++ ++ /* Values between 0Fh-00h are reserved */ ++ if (cpufid <= 0x0F) ++ return 0; ++ ++ /* The PPR defines the core multiplier as CpuFid*25 MHz */ ++ p0_freq = cpufid * 25 * 1000; ++ ++ /* CpuDfsId is the next 6 bits (13:8) */ ++ cpudfsid = (pstatedef >> 8) & 0x3f; ++ ++ /* Calculate the core divisor */ ++ switch (cpudfsid) { ++ case 0x08: ++ /* VCO/1 */ ++ break; ++ case 0x09: ++ /* VCO/1.125 */ ++ p0_freq = mul_u64_u32_div(p0_freq, 1125, 1000); ++ break; ++ case 0x0A ... 0x1A: ++ case 0x1C: ++ case 0x1E: ++ case 0x20: ++ case 0x22: ++ case 0x24: ++ case 0x26: ++ case 0x28: ++ case 0x2A: ++ case 0x2C: ++ /* VCO/ */ ++ p0_freq /= cpudfsid / 8; ++ break; ++ default: ++ /* Reserved */ ++ return 0; ++ } ++ break; ++ case 0x1A: ++ /* CpuFid is the first 12 bits (11:0) */ ++ cpufid = pstatedef & 0xfff; ++ ++ /* Values between 00Fh-000h are reserved */ ++ if (cpufid <= 0x00F) ++ return 0; ++ ++ /* CpuFid: *5 MHz */ ++ p0_freq = cpufid * 5 * 1000; ++ break; ++ default: ++ return 0; ++ } ++ ++ /* ++ * TSC frequency determined by MSR is always considered "known" ++ * because it is reported by HW. ++ */ ++ setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ); ++ ++ return p0_freq; ++} +-- +2.50.1 +