[PATCH v5 09/51] x86/tsc: Add a standalone helper for getting TSC info from CPUID.0x15
From: Sean Christopherson <seanjc@google.com>
Date: 2026-07-01 19:32:38
Also in:
kvm, linux-coco, linux-doc, lkml, virtualization, xen-devel
Subsystem:
the rest, x86 architecture (32-bit and 64-bit) · Maintainers:
Linus Torvalds, Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen
Extract retrieval of TSC frequency information from CPUID into a standalone helper so that TDX guest support can reuse the logic. Opportunistically drop native_calibrate_tsc()'s "== 0" and "!= 0" checks in favor of the kernel's preferred style. No functional change intended. Reviewed-by: David Woodhouse <redacted> Signed-off-by: Sean Christopherson <seanjc@google.com> --- arch/x86/kernel/tsc.c | 61 +++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 23 deletions(-)
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index f049c126e47c..12043812c8f5 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c@@ -645,46 +645,62 @@ static unsigned long quick_pit_calibrate(void) return delta; } +struct cpuid_tsc_info { + unsigned int denominator; + unsigned int numerator; + unsigned int crystal_khz; +}; + +static int cpuid_get_tsc_info(struct cpuid_tsc_info *info) +{ + unsigned int ecx_hz, edx; + + if (boot_cpu_data.cpuid_level < CPUID_LEAF_TSC) + return -ENOENT; + + /* CPUID 15H TSC/Crystal ratio, plus optionally Crystal Hz */ + cpuid(CPUID_LEAF_TSC, &info->denominator, &info->numerator, &ecx_hz, &edx); + + if (!info->denominator || !info->numerator) + return -ENOENT; + + /* + * Note: some CPUs provide the multiplier information, but not the core + * crystal frequency. The multiplier information is still useful for + * such CPUs, as the crystal frequency can be gleaned from CPUID.0x16. + */ + info->crystal_khz = ecx_hz / 1000; + return 0; +} + /** * native_calibrate_tsc - determine TSC frequency * Determine TSC frequency via CPUID, else return 0. */ unsigned long native_calibrate_tsc(void) { - unsigned int eax_denominator, ebx_numerator, ecx_hz, edx; - unsigned int crystal_khz; + struct cpuid_tsc_info info; if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) return 0; - if (boot_cpu_data.cpuid_level < CPUID_LEAF_TSC) + if (cpuid_get_tsc_info(&info)) return 0; - eax_denominator = ebx_numerator = ecx_hz = edx = 0; - - /* CPUID 15H TSC/Crystal ratio, plus optionally Crystal Hz */ - cpuid(CPUID_LEAF_TSC, &eax_denominator, &ebx_numerator, &ecx_hz, &edx); - - if (ebx_numerator == 0 || eax_denominator == 0) - return 0; - - crystal_khz = ecx_hz / 1000; - /* * Denverton SoCs don't report crystal clock, and also don't support * CPUID_LEAF_FREQ for the calculation below, so hardcode the 25MHz * crystal clock. */ - if (crystal_khz == 0 && - boot_cpu_data.x86_vfm == INTEL_ATOM_GOLDMONT_D) - crystal_khz = 25000; + if (!info.crystal_khz && boot_cpu_data.x86_vfm == INTEL_ATOM_GOLDMONT_D) + info.crystal_khz = 25000; /* * TSC frequency reported directly by CPUID is a "hardware reported" * frequency and is the most accurate one so far we have. This * is considered a known frequency. */ - if (crystal_khz != 0) + if (info.crystal_khz) setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ); /*
@@ -692,15 +708,14 @@ unsigned long native_calibrate_tsc(void) * clock, but we can easily calculate it to a high degree of accuracy * by considering the crystal ratio and the CPU speed. */ - if (crystal_khz == 0 && boot_cpu_data.cpuid_level >= CPUID_LEAF_FREQ) { + if (!info.crystal_khz && boot_cpu_data.cpuid_level >= CPUID_LEAF_FREQ) { unsigned int eax_base_mhz, ebx, ecx, edx; cpuid(CPUID_LEAF_FREQ, &eax_base_mhz, &ebx, &ecx, &edx); - crystal_khz = eax_base_mhz * 1000 * - eax_denominator / ebx_numerator; + info.crystal_khz = eax_base_mhz * 1000 * info.denominator / info.numerator; } - if (crystal_khz == 0) + if (!info.crystal_khz) return 0; /*
@@ -716,9 +731,9 @@ unsigned long native_calibrate_tsc(void) * lapic_timer_period here to avoid having to calibrate the APIC * timer later. */ - apic_set_timer_period_khz(crystal_khz, "CPUID 0x15/0x16"); + apic_set_timer_period_khz(info.crystal_khz, "CPUID 0x15/0x16"); - return crystal_khz * ebx_numerator / eax_denominator; + return info.crystal_khz * info.numerator / info.denominator; } static unsigned long cpu_khz_from_cpuid(void)
--
2.55.0.rc0.799.gd6f94ed593-goog