From: Sean Christopherson <seanjc@xxxxxxxxxx> Sent: Wednesday, February 26, 2025 6:18 PM > > Add a helper to register non-native, i.e. PV and CoCo, CPU and TSC > frequency calibration routines. This will allow consolidating handling > of common TSC properties that are forced by hypervisor (PV routines), > and will also allow adding sanity checks to guard against overriding a > TSC calibration routine with a routine that is less robust/trusted. > > Make the CPU calibration routine optional, as Xen (very sanely) doesn't > assume the CPU runs as the same frequency as the TSC. > > Wrap the helper in an #ifdef to document that the kernel overrides > the native routines when running as a VM, and to guard against unwanted > usage. Add a TODO to call out that AMD_MEM_ENCRYPT is a mess and doesn't > depend on HYPERVISOR_GUEST because it gates both guest and host code. > > No functional change intended. > > Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx> > --- > arch/x86/coco/sev/core.c | 4 ++-- > arch/x86/include/asm/tsc.h | 4 ++++ > arch/x86/kernel/cpu/acrn.c | 4 ++-- > arch/x86/kernel/cpu/mshyperv.c | 3 +-- > arch/x86/kernel/cpu/vmware.c | 4 ++-- > arch/x86/kernel/jailhouse.c | 4 ++-- > arch/x86/kernel/kvmclock.c | 4 ++-- > arch/x86/kernel/tsc.c | 17 +++++++++++++++++ > arch/x86/xen/time.c | 2 +- > 9 files changed, 33 insertions(+), 13 deletions(-) > > diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c > index 82492efc5d94..684cef70edc1 100644 > --- a/arch/x86/coco/sev/core.c > +++ b/arch/x86/coco/sev/core.c > @@ -3291,6 +3291,6 @@ void __init snp_secure_tsc_init(void) > rdmsrl(MSR_AMD64_GUEST_TSC_FREQ, tsc_freq_mhz); > snp_tsc_freq_khz = (unsigned long)(tsc_freq_mhz * 1000); > > - x86_platform.calibrate_cpu = securetsc_get_tsc_khz; > - x86_platform.calibrate_tsc = securetsc_get_tsc_khz; > + tsc_register_calibration_routines(securetsc_get_tsc_khz, > + securetsc_get_tsc_khz); > } > diff --git a/arch/x86/include/asm/tsc.h b/arch/x86/include/asm/tsc.h > index c3a14df46327..9318c74e8d13 100644 > --- a/arch/x86/include/asm/tsc.h > +++ b/arch/x86/include/asm/tsc.h > @@ -40,6 +40,10 @@ extern int cpuid_get_cpu_freq(unsigned int *cpu_khz); > > extern void tsc_early_init(void); > extern void tsc_init(void); > +#if defined(CONFIG_HYPERVISOR_GUEST) || defined(CONFIG_AMD_MEM_ENCRYPT) > +extern void tsc_register_calibration_routines(unsigned long (*calibrate_tsc)(void), > + unsigned long (*calibrate_cpu)(void)); > +#endif > extern void mark_tsc_unstable(char *reason); > extern int unsynchronized_tsc(void); > extern int check_tsc_unstable(void); > diff --git a/arch/x86/kernel/cpu/acrn.c b/arch/x86/kernel/cpu/acrn.c > index 2c5b51aad91a..c1506cb87d8c 100644 > --- a/arch/x86/kernel/cpu/acrn.c > +++ b/arch/x86/kernel/cpu/acrn.c > @@ -29,8 +29,8 @@ 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; > + tsc_register_calibration_routines(acrn_get_tsc_khz, > + acrn_get_tsc_khz); > } > > static bool acrn_x2apic_available(void) > diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c > index f285757618fc..aa60491bf738 100644 > --- a/arch/x86/kernel/cpu/mshyperv.c > +++ b/arch/x86/kernel/cpu/mshyperv.c > @@ -478,8 +478,7 @@ 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; > + tsc_register_calibration_routines(hv_get_tsc_khz, hv_get_tsc_khz); > setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ); > } > For the Hyper-V guest code, Reviewed-by: Michael Kelley <mhklinux@xxxxxxxxxxx> Tested-by: Michael Kelley <mhklinux@xxxxxxxxxxx> > diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c > index 00189cdeb775..d6f079a75f05 100644 > --- a/arch/x86/kernel/cpu/vmware.c > +++ b/arch/x86/kernel/cpu/vmware.c > @@ -416,8 +416,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; > + tsc_register_calibration_routines(vmware_get_tsc_khz, > + vmware_get_tsc_khz); > > #ifdef CONFIG_X86_LOCAL_APIC > /* Skip lapic calibration since we know the bus frequency. */ > diff --git a/arch/x86/kernel/jailhouse.c b/arch/x86/kernel/jailhouse.c > index cd8ed1edbf9e..b0a053692161 100644 > --- a/arch/x86/kernel/jailhouse.c > +++ b/arch/x86/kernel/jailhouse.c > @@ -209,8 +209,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; > @@ -220,6 +218,8 @@ static void __init jailhouse_init_platform(void) > > machine_ops.emergency_restart = jailhouse_no_restart; > > + tsc_register_calibration_routines(jailhouse_get_tsc, jailhouse_get_tsc); > + > while (pa_data) { > mapping = early_memremap(pa_data, sizeof(header)); > memcpy(&header, mapping, sizeof(header)); > diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c > index 5b2c15214a6b..b898b95a7d50 100644 > --- a/arch/x86/kernel/kvmclock.c > +++ b/arch/x86/kernel/kvmclock.c > @@ -320,8 +320,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; > + tsc_register_calibration_routines(kvm_get_tsc_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 bb4619148161..d65e85929d3e 100644 > --- a/arch/x86/kernel/tsc.c > +++ b/arch/x86/kernel/tsc.c > @@ -1294,6 +1294,23 @@ static void __init check_system_tsc_reliable(void) > tsc_disable_clocksource_watchdog(); > } > > +/* > + * TODO: Disentangle AMD_MEM_ENCRYPT and make SEV guest support depend on > + * HYPERVISOR_GUEST. > + */ > +#if defined(CONFIG_HYPERVISOR_GUEST) || defined(CONFIG_AMD_MEM_ENCRYPT) > +void tsc_register_calibration_routines(unsigned long (*calibrate_tsc)(void), > + unsigned long (*calibrate_cpu)(void)) > +{ > + if (WARN_ON_ONCE(!calibrate_tsc)) > + return; > + > + x86_platform.calibrate_tsc = calibrate_tsc; > + if (calibrate_cpu) > + x86_platform.calibrate_cpu = calibrate_cpu; > +} > +#endif > + > /* > * Make an educated guess if the TSC is trustworthy and synchronized > * over all CPUs. > diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c > index 96521b1874ac..9e2e900dc0c7 100644 > --- a/arch/x86/xen/time.c > +++ b/arch/x86/xen/time.c > @@ -566,7 +566,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; > + tsc_register_calibration_routines(xen_tsc_khz, NULL); > x86_platform.get_wallclock = xen_get_wallclock; > } > > -- > 2.48.1.711.g2feabab25a-goog >