From: Sean Christopherson <[email protected]> Sent: Wednesday, July 1, 2026 
12:32 PM
> 
> Add dedicated hypervisor hooks for getting known TSC/CPU frequencies
> instead of overriding seemingly generic platform hooks, and explicitly
> priotize hypervisor-provided frequencies over native methods, but do NOT

s/priotize/prioritize/

> clobber the frequency obtained from trusted firmware.  While shuffling the
> hooks around is arguably "six of one, half dozen of the other", scoping
> them to x86_hyper_init makes their purpose more obvious, and allows for
> explicitly defining the priority of sources (as is done here).
> 
> As is already done when trusted firmware provides the TSC frequency, ignore

Word "ignore" is duplicated.

> ignore tsc_early_khz if the exact TSC frequency was obtained from the
> hypervisor, as attempting to refine the TSC frequency when running in a VM
> is all but guaranteed to cause problems sooner or later due to the
> calibration sources being emulated devices in the vast majority of setups.
> 
> Cc: David Woodhouse <[email protected]>
> Signed-off-by: Sean Christopherson <[email protected]>

For the Hyper-V changes,

Reviewed-by: Michael Kelley <[email protected]> 

> ---
>  .../admin-guide/kernel-parameters.txt         |  3 +-
>  arch/x86/include/asm/acrn.h                   |  5 ----
>  arch/x86/include/asm/x86_init.h               |  4 +++
>  arch/x86/kernel/cpu/acrn.c                    | 10 +++++--
>  arch/x86/kernel/cpu/mshyperv.c                |  6 ++--
>  arch/x86/kernel/cpu/vmware.c                  |  8 ++---
>  arch/x86/kernel/jailhouse.c                   |  6 ++--
>  arch/x86/kernel/kvmclock.c                    |  6 ++--
>  arch/x86/kernel/tsc.c                         | 29 ++++++++++++++-----
>  arch/x86/xen/time.c                           |  4 +--
>  10 files changed, 50 insertions(+), 31 deletions(-)
> 
> diff --git a/Documentation/admin-guide/kernel-parameters.txt 
> b/Documentation/admin-
> guide/kernel-parameters.txt
> index 490e6aa72fc2..a387bb2c47e2 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -7948,7 +7948,8 @@ Kernel parameters
> 
>                       Note, tsc_early_khz is ignored if the TSC frequency is
>                       provided by trusted firmware when running as an SNP or
> -                     TDX guest.
> +                     TDX guest, or when the hypervisor provides the exact
> +                     frequency via a paravirtual interface.
> 
>       tsx=            [X86] Control Transactional Synchronization
>                       Extensions (TSX) feature in Intel processors that
> diff --git a/arch/x86/include/asm/acrn.h b/arch/x86/include/asm/acrn.h
> index db42b477c41d..a892179c61c6 100644
> --- a/arch/x86/include/asm/acrn.h
> +++ b/arch/x86/include/asm/acrn.h
> @@ -32,11 +32,6 @@ static inline u32 acrn_cpuid_base(void)
>       return 0;
>  }
> 
> -static inline unsigned long acrn_get_tsc_khz(void)
> -{
> -     return cpuid_eax(ACRN_CPUID_TIMING_INFO);
> -}
> -
>  /*
>   * Hypercalls for ACRN
>   *
> diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h
> index 953d3199408a..0c89bf40f507 100644
> --- a/arch/x86/include/asm/x86_init.h
> +++ b/arch/x86/include/asm/x86_init.h
> @@ -123,6 +123,8 @@ struct x86_init_pci {
>   * @msi_ext_dest_id:         MSI supports 15-bit APIC IDs
>   * @init_mem_mapping:                setup early mappings during 
> init_mem_mapping()
>   * @init_after_bootmem:              guest init after boot allocator is 
> finished
> + * @get_tsc_khz:             get the TSC frequency (returns 0 if frequency 
> is unknown)
> + * @get_cpu_khz:             get the CPU frequency (returns 0 if frequency 
> is unknown)
>   */
>  struct x86_hyper_init {
>       void (*init_platform)(void);
> @@ -131,6 +133,8 @@ struct x86_hyper_init {
>       bool (*msi_ext_dest_id)(void);
>       void (*init_mem_mapping)(void);
>       void (*init_after_bootmem)(void);
> +     unsigned int (*get_tsc_khz)(void);
> +     unsigned int (*get_cpu_khz)(void);
>  };
> 
>  /**
> diff --git a/arch/x86/kernel/cpu/acrn.c b/arch/x86/kernel/cpu/acrn.c
> index dc119af83524..ad8f2da8003b 100644
> --- a/arch/x86/kernel/cpu/acrn.c
> +++ b/arch/x86/kernel/cpu/acrn.c
> @@ -24,13 +24,15 @@ static u32 __init acrn_detect(void)
>       return acrn_cpuid_base();
>  }
> 
> +static unsigned int __init acrn_get_tsc_khz(void)
> +{
> +     return cpuid_eax(ACRN_CPUID_TIMING_INFO);
> +}
> +
>  static void __init acrn_init_platform(void)
>  {
>       /* Install system interrupt handler for ACRN hypervisor callback */
>       sysvec_install(HYPERVISOR_CALLBACK_VECTOR, sysvec_acrn_hv_callback);
> -
> -     x86_platform.calibrate_tsc = acrn_get_tsc_khz;
> -     x86_platform.calibrate_cpu = acrn_get_tsc_khz;
>  }
> 
>  static bool acrn_x2apic_available(void)
> @@ -78,4 +80,6 @@ const __initconst struct hypervisor_x86 x86_hyper_acrn = {
>       .type                   = X86_HYPER_ACRN,
>       .init.init_platform     = acrn_init_platform,
>       .init.x2apic_available  = acrn_x2apic_available,
> +     .init.get_tsc_khz       = acrn_get_tsc_khz,
> +     .init.get_cpu_khz       = acrn_get_tsc_khz,
>  };
> diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
> index 87beecec76f0..f9bc1c2d8c93 100644
> --- a/arch/x86/kernel/cpu/mshyperv.c
> +++ b/arch/x86/kernel/cpu/mshyperv.c
> @@ -395,7 +395,7 @@ static int hv_nmi_unknown(unsigned int val, struct 
> pt_regs *regs)
>  }
>  #endif
> 
> -static unsigned long hv_get_tsc_khz(void)
> +static unsigned int __init hv_get_tsc_khz(void)
>  {
>       unsigned long freq;
> 
> @@ -573,8 +573,8 @@ static void __init ms_hyperv_init_platform(void)
> 
>       if (ms_hyperv.features & HV_ACCESS_FREQUENCY_MSRS &&
>           ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE) {
> -             x86_platform.calibrate_tsc = hv_get_tsc_khz;
> -             x86_platform.calibrate_cpu = hv_get_tsc_khz;
> +             x86_init.hyper.get_tsc_khz = hv_get_tsc_khz;
> +             x86_init.hyper.get_cpu_khz = hv_get_tsc_khz;
>               setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ);
>       }
> 
> diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c
> index 13b97265c535..3cb473cae462 100644
> --- a/arch/x86/kernel/cpu/vmware.c
> +++ b/arch/x86/kernel/cpu/vmware.c
> @@ -64,7 +64,7 @@ struct vmware_steal_time {
>       u64 reserved[7];
>  };
> 
> -static unsigned long vmware_tsc_khz __ro_after_init;
> +static unsigned long vmware_tsc_khz __initdata;
>  static u8 vmware_hypercall_mode     __ro_after_init;
> 
>  unsigned long vmware_hypercall_slow(unsigned long cmd,
> @@ -137,7 +137,7 @@ static inline int __vmware_platform(void)
>       return eax != UINT_MAX && ebx == VMWARE_HYPERVISOR_MAGIC;
>  }
> 
> -static unsigned long vmware_get_tsc_khz(void)
> +static unsigned int __init vmware_get_tsc_khz(void)
>  {
>       return vmware_tsc_khz;
>  }
> @@ -419,8 +419,8 @@ static void __init vmware_platform_setup(void)
>               }
> 
>               vmware_tsc_khz = tsc_khz;
> -             x86_platform.calibrate_tsc = vmware_get_tsc_khz;
> -             x86_platform.calibrate_cpu = vmware_get_tsc_khz;
> +             x86_init.hyper.get_tsc_khz = vmware_get_tsc_khz;
> +             x86_init.hyper.get_cpu_khz = vmware_get_tsc_khz;
> 
>               /* Skip lapic calibration since we know the bus frequency. */
>               apic_set_timer_period_hz(ecx, "VMware hypervisor");
> diff --git a/arch/x86/kernel/jailhouse.c b/arch/x86/kernel/jailhouse.c
> index f2d4ef89c085..e24c05ab4fae 100644
> --- a/arch/x86/kernel/jailhouse.c
> +++ b/arch/x86/kernel/jailhouse.c
> @@ -68,7 +68,7 @@ static void __init jailhouse_timer_init(void)
>       apic_set_timer_period_khz(setup_data.v1.apic_khz, "Jailhouse 
> hypervisor");
>  }
> 
> -static unsigned long jailhouse_get_tsc(void)
> +static unsigned int __init jailhouse_get_tsc(void)
>  {
>       return precalibrated_tsc_khz;
>  }
> @@ -210,8 +210,6 @@ static void __init jailhouse_init_platform(void)
>       x86_init.mpparse.parse_smp_cfg          = jailhouse_parse_smp_config;
>       x86_init.pci.arch_init                  = jailhouse_pci_arch_init;
> 
> -     x86_platform.calibrate_cpu              = jailhouse_get_tsc;
> -     x86_platform.calibrate_tsc              = jailhouse_get_tsc;
>       x86_platform.get_wallclock              = jailhouse_get_wallclock;
>       x86_platform.legacy.rtc                 = 0;
>       x86_platform.legacy.warm_reset          = 0;
> @@ -293,5 +291,7 @@ const struct hypervisor_x86 x86_hyper_jailhouse 
> __refconst = {
>       .detect                 = jailhouse_detect,
>       .init.init_platform     = jailhouse_init_platform,
>       .init.x2apic_available  = jailhouse_x2apic_available,
> +     .init.get_tsc_khz       = jailhouse_get_tsc,
> +     .init.get_cpu_khz       = jailhouse_get_tsc,
>       .ignore_nopv            = true,
>  };
> diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
> index cb3d0ca1fa22..4f8299303a19 100644
> --- a/arch/x86/kernel/kvmclock.c
> +++ b/arch/x86/kernel/kvmclock.c
> @@ -136,7 +136,7 @@ static inline void kvm_sched_clock_init(bool stable)
>   * poll of guests can be running and trouble each other. So we preset
>   * lpj here
>   */
> -static unsigned long kvm_get_tsc_khz(void)
> +static unsigned int __init kvm_get_tsc_khz(void)
>  {
>       setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ);
>       return pvclock_tsc_khz(this_cpu_pvti());
> @@ -343,8 +343,8 @@ void __init kvmclock_init(void)
>       flags = pvclock_read_flags(&hv_clock_boot[0].pvti);
>       kvm_sched_clock_init(flags & PVCLOCK_TSC_STABLE_BIT);
> 
> -     x86_platform.calibrate_tsc = kvm_get_tsc_khz;
> -     x86_platform.calibrate_cpu = kvm_get_tsc_khz;
> +     x86_init.hyper.get_tsc_khz = kvm_get_tsc_khz;
> +     x86_init.hyper.get_cpu_khz = kvm_get_tsc_khz;
>       x86_platform.get_wallclock = kvm_get_wallclock;
>       x86_platform.set_wallclock = kvm_set_wallclock;
>  #ifdef CONFIG_X86_LOCAL_APIC
> diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
> index 86384a83a5f6..1dca9464b41c 100644
> --- a/arch/x86/kernel/tsc.c
> +++ b/arch/x86/kernel/tsc.c
> @@ -1451,13 +1451,17 @@ static int __init init_tsc_clocksource(void)
>  device_initcall(init_tsc_clocksource);
> 
>  static bool __init determine_cpu_tsc_frequencies(bool early,
> +                                              unsigned int known_cpu_khz,
>                                                unsigned int known_tsc_khz)
>  {
>       /* Make sure that cpu and tsc are not already calibrated */
>       WARN_ON(cpu_khz || tsc_khz);
> 
>       if (early) {
> -             cpu_khz = x86_platform.calibrate_cpu();
> +             if (known_cpu_khz)
> +                     cpu_khz = known_cpu_khz;
> +             else
> +                     cpu_khz = x86_platform.calibrate_cpu();
>               if (known_tsc_khz)
>                       tsc_khz = known_tsc_khz;
>               else
> @@ -1514,7 +1518,7 @@ static void __init tsc_enable_sched_clock(void)
> 
>  void __init tsc_early_init(void)
>  {
> -     unsigned int known_tsc_khz = 0;
> +     unsigned int known_cpu_khz = 0, known_tsc_khz = 0;
> 
>       if (!boot_cpu_has(X86_FEATURE_TSC))
>               return;
> @@ -1522,22 +1526,33 @@ void __init tsc_early_init(void)
>       if (is_early_uv_system())
>               return;
> 
> +     if (x86_init.hyper.get_cpu_khz)
> +             known_cpu_khz = x86_init.hyper.get_cpu_khz();
> +
>       if (cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC))
>               known_tsc_khz = snp_secure_tsc_init();
>       else if (boot_cpu_has(X86_FEATURE_TDX_GUEST))
>               known_tsc_khz = tdx_tsc_init();
> 
> +     /*
> +      * If the TSC frequency wasn't provided by trusted firmware, try to get
> +      * it from the hypervisor (which is untrusted when running as a CoCo 
> guest).
> +      */
> +     if (!known_tsc_khz && x86_init.hyper.get_tsc_khz)
> +             known_tsc_khz = x86_init.hyper.get_tsc_khz();
> +
>       /*
>        * Ignore the user-provided TSC frequency if the exact frequency was
> -      * obtained from trusted firmware, as the user-provided frequency is
> -      * intended as a "starting point", not a known, guaranteed frequency.
> +      * obtained from trusted firmware or the hypervisor, as the user-
> +      * provided frequency is intended as a "starting point", not a known,
> +      * guaranteed frequency.
>        */
>       if (!known_tsc_khz)
>               known_tsc_khz = tsc_early_khz;
>       else if (tsc_early_khz)
> -             pr_err("Ignoring 'tsc_early_khz' in favor of trusted 
> firmware.\n");
> +             pr_err("Ignoring 'tsc_early_khz' in favor of 
> firmware/hypervisor.\n");
> 
> -     if (!determine_cpu_tsc_frequencies(true, known_tsc_khz))
> +     if (!determine_cpu_tsc_frequencies(true, known_cpu_khz, known_tsc_khz))
>               return;
>       tsc_enable_sched_clock();
>  }
> @@ -1558,7 +1573,7 @@ void __init tsc_init(void)
> 
>       if (!tsc_khz) {
>               /* We failed to determine frequencies earlier, try again */
> -             if (!determine_cpu_tsc_frequencies(false, 0)) {
> +             if (!determine_cpu_tsc_frequencies(false, 0, 0)) {
>                       mark_tsc_unstable("could not calculate TSC khz");
>                       setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
>                       return;
> diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c
> index d62c14334b35..1adb44fdddb2 100644
> --- a/arch/x86/xen/time.c
> +++ b/arch/x86/xen/time.c
> @@ -38,7 +38,7 @@
>  static u64 xen_sched_clock_offset __read_mostly;
> 
>  /* Get the TSC speed from Xen */
> -static unsigned long xen_tsc_khz(void)
> +static unsigned int __init xen_tsc_khz(void)
>  {
>       struct pvclock_vcpu_time_info *info =
>               &HYPERVISOR_shared_info->vcpu_info[0].time;
> @@ -569,7 +569,7 @@ static void __init xen_init_time_common(void)
>       static_call_update(pv_steal_clock, xen_steal_clock);
>       paravirt_set_sched_clock(xen_sched_clock);
> 
> -     x86_platform.calibrate_tsc = xen_tsc_khz;
> +     x86_init.hyper.get_tsc_khz = xen_tsc_khz;
>       x86_platform.get_wallclock = xen_get_wallclock;
>  }
> 
> --
> 2.55.0.rc0.799.gd6f94ed593-goog
> 


Reply via email to