On 7/2/2026 1:01 AM, Sean Christopherson wrote:
> As a first step towards dropping .calibrate_{cpu,tsc}() and explicitly
> defining precedence/priority for "calibration" routines, pass the secure
> TSC frequency obtained from SNP firmware directly to
> determine_cpu_tsc_frequencies() instead of overriding the .calibrate_tsc()
> hook.
>
> Unlike the native calibration routines, all of the paravirtual overrides,
> including SNP and TDX, are constant in the sense that the frequency
> provided by the hypervisor or trusted firmware is fixed, known, and always
> available during early boot. More importantly, for CoCo (SNP and TDX) VMs,
> it's imperative that the kernel uses the frequency provided by the trusted
> firmware, not by the untrusted hypervisor. Enforcing the priority between
> sources by carefully ordering seemingly unrelated init calls, so that the
> trusted override "wins", is brittle and all but impossible to follow.
>
> Explicitly ignore tsc_early_khz if the exact TSC frequency was obtained
> from trusted firmware, as per commit bd35c77e32e4 ("x86/tsc: Add
> tsc_early_khz command line parameter"), the goal of the param is to play
> nice with setups that provide partial frequency information in CPUID, i.e.
> is NOT intended to be a hard override. Neither SNP's secure TSC nor TDX
> was supported when commit bd35c77e32e4 landed back in 2020, i.e. lack of
> consideration for the interaction was purely due to oversight when SNP and
> TDX support came along.
>
> Signed-off-by: Sean Christopherson <[email protected]>
> ---
> .../admin-guide/kernel-parameters.txt | 4 +++
> arch/x86/coco/sev/core.c | 14 +++--------
> arch/x86/include/asm/sev.h | 4 +--
> arch/x86/kernel/tsc.c | 25 ++++++++++++++-----
> 4 files changed, 29 insertions(+), 18 deletions(-)
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt
> b/Documentation/admin-guide/kernel-parameters.txt
> index b5493a7f8f22..181149f633c3 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -7946,6 +7946,10 @@ Kernel parameters
> with CPUID.16h support and partial CPUID.15h support.
> Format: <unsigned int>
>
> + Note, tsc_early_khz is ignored if the TSC frequency is
> + provided by trusted firmware when running as an SNP
> + guest.
> +
> tsx= [X86] Control Transactional Synchronization
> Extensions (TSX) feature in Intel processors that
> support TSX control.
> diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c
> index 403dcea86452..bc5ae9ef74da 100644
> --- a/arch/x86/coco/sev/core.c
> +++ b/arch/x86/coco/sev/core.c
> @@ -99,7 +99,6 @@ static const char * const sev_status_feat_names[] = {
> */
> static u64 snp_tsc_scale __ro_after_init;
> static u64 snp_tsc_offset __ro_after_init;
> -static unsigned long snp_tsc_freq_khz __ro_after_init;
>
> DEFINE_PER_CPU(struct sev_es_runtime_data*, runtime_data);
> DEFINE_PER_CPU(struct sev_es_save_area *, sev_vmsa);
> @@ -2014,15 +2013,10 @@ void __init snp_secure_tsc_prepare(void)
> pr_debug("SecureTSC enabled");
> }
>
> -static unsigned long securetsc_get_tsc_khz(void)
> -{
> - return snp_tsc_freq_khz;
> -}
> -
> -void __init snp_secure_tsc_init(void)
> +unsigned int __init snp_secure_tsc_init(void)
> {
> + unsigned long snp_tsc_freq_khz, tsc_freq_mhz;
> struct snp_secrets_page *secrets;
> - unsigned long tsc_freq_mhz;
> void *mem;
>
> mem = early_memremap_encrypted(sev_secrets_pa, PAGE_SIZE);
> @@ -2043,7 +2037,7 @@ void __init snp_secure_tsc_init(void)
>
> snp_tsc_freq_khz = SNP_SCALE_TSC_FREQ(tsc_freq_mhz * 1000,
> secrets->tsc_factor);
>
> - x86_platform.calibrate_tsc = securetsc_get_tsc_khz;
> -
> early_memunmap(mem, PAGE_SIZE);
> +
> + return snp_tsc_freq_khz;
> }
> diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
> index 594cfa19cbd4..05ebf0b73ef4 100644
> --- a/arch/x86/include/asm/sev.h
> +++ b/arch/x86/include/asm/sev.h
> @@ -530,7 +530,7 @@ int snp_send_guest_request(struct snp_msg_desc *mdesc,
> struct snp_guest_req *req
> int snp_svsm_vtpm_send_command(u8 *buffer);
>
> void __init snp_secure_tsc_prepare(void);
> -void __init snp_secure_tsc_init(void);
> +unsigned int snp_secure_tsc_init(void);
It seems __init got dropped here accidentally?
Apart from this:
Reviewed-by: Nikunj A Dadhania <[email protected]>
Tested-by: Nikunj A Dadhania <[email protected]>
> enum es_result savic_register_gpa(u64 gpa);
> enum es_result savic_unregister_gpa(u64 *gpa);
> u64 savic_ghcb_msr_read(u32 reg);
> @@ -637,7 +637,7 @@ static inline int snp_send_guest_request(struct
> snp_msg_desc *mdesc,
> struct snp_guest_req *req) { return
> -ENODEV; }
> static inline int snp_svsm_vtpm_send_command(u8 *buffer) { return -ENODEV; }
> static inline void __init snp_secure_tsc_prepare(void) { }
> -static inline void __init snp_secure_tsc_init(void) { }
> +static inline unsigned int __init snp_secure_tsc_init(void) { return 0; }
> static inline void sev_evict_cache(void *va, int npages) {}
> static inline enum es_result savic_register_gpa(u64 gpa) { return
> ES_UNSUPPORTED; }
> static inline enum es_result savic_unregister_gpa(u64 *gpa) { return
> ES_UNSUPPORTED; }
> diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
> index 8f1604ffe986..f049c126e47c 100644
> --- a/arch/x86/kernel/tsc.c
> +++ b/arch/x86/kernel/tsc.c
> @@ -1440,15 +1440,16 @@ static int __init init_tsc_clocksource(void)
> */
> device_initcall(init_tsc_clocksource);
>
> -static bool __init determine_cpu_tsc_frequencies(bool early)
> +static bool __init determine_cpu_tsc_frequencies(bool early,
> + 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 (tsc_early_khz)
> - tsc_khz = tsc_early_khz;
> + if (known_tsc_khz)
> + tsc_khz = known_tsc_khz;
> else
> tsc_khz = x86_platform.calibrate_tsc();
> } else {
> @@ -1503,6 +1504,8 @@ static void __init tsc_enable_sched_clock(void)
>
> void __init tsc_early_init(void)
> {
> + unsigned int known_tsc_khz = 0;
> +
> if (!boot_cpu_has(X86_FEATURE_TSC))
> return;
> /* Don't change UV TSC multi-chassis synchronization */
> @@ -1510,9 +1513,19 @@ void __init tsc_early_init(void)
> return;
>
> if (cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC))
> - snp_secure_tsc_init();
> + known_tsc_khz = snp_secure_tsc_init();
>
> - if (!determine_cpu_tsc_frequencies(true))
> + /*
> + * 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.
> + */
> + 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");
> +
> + if (!determine_cpu_tsc_frequencies(true, known_tsc_khz))
> return;
> tsc_enable_sched_clock();
> }
> @@ -1533,7 +1546,7 @@ void __init tsc_init(void)
>
> if (!tsc_khz) {
> /* We failed to determine frequencies earlier, try again */
> - if (!determine_cpu_tsc_frequencies(false)) {
> + if (!determine_cpu_tsc_frequencies(false, 0)) {
> mark_tsc_unstable("could not calculate TSC khz");
> setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
> return;