When entering idle, it's a good oportunity to verify that the TSC_ADJUST MSR has not been tampered with (BIOS hiding SMM cycles). If tampering is detected, emit a warning and restore it to the previous value. This is especially important for machines, which mark the TSC reliable because there is no watchdog clocksource available (SoCs). This is not sufficient for HPC (NOHZ_FULL) situations where a CPU never goes idle, but adding a timer to do the check periodically is not an option either. On a machine, which has this issue, the check triggeres right during boot, so there is a decent chance that the sysadmin will notice. Signed-off-by: Thomas Gleixner --- arch/x86/include/asm/tsc.h | 2 ++ arch/x86/kernel/process.c | 1 + arch/x86/kernel/tsc_sync.c | 20 +++++++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) --- a/arch/x86/include/asm/tsc.h +++ b/arch/x86/include/asm/tsc.h @@ -50,8 +50,10 @@ extern void check_tsc_sync_target(void); #ifdef CONFIG_X86_TSC extern void tsc_store_and_check_tsc_adjust(void); +extern void tsc_verify_tsc_adjust(void); #else static inline void tsc_store_and_check_tsc_adjust(void) { } +static inline void tsc_verify_tsc_adjust(void) { } #endif extern int notsc_setup(char *); --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -277,6 +277,7 @@ void exit_idle(void) void arch_cpu_idle_enter(void) { + tsc_verify_tsc_adjust(); local_touch_nmi(); enter_idle(); } --- a/arch/x86/kernel/tsc_sync.c +++ b/arch/x86/kernel/tsc_sync.c @@ -28,6 +28,24 @@ struct tsc_adjust { static DEFINE_PER_CPU(struct tsc_adjust, tsc_adjust); +void tsc_verify_tsc_adjust(void) +{ + struct tsc_adjust *adj = this_cpu_ptr(&tsc_adjust); + s64 curval; + + if (!boot_cpu_has(X86_FEATURE_TSC_ADJUST)) + return; + + rdmsrl(MSR_IA32_TSC_ADJUST, curval); + if (adj->adjusted == curval) + return; + + pr_warn(FW_BUG "TSC ADJUST differs: CPU%u %lld --> %lld. Restoring\n", + smp_processor_id(), adj->adjusted, curval); + + wrmsrl(MSR_IA32_TSC_ADJUST, adj->adjusted); +} + #ifndef CONFIG_SMP void __init tsc_store_and_check_tsc_adjust(void) { @@ -40,7 +58,7 @@ void __init tsc_store_and_check_tsc_adju rdmsrl(MSR_IA32_TSC_ADJUST, bootval); cur->bootval = bootval; cur->adjusted = bootval; - pr_info("TSC ADJUST: Boot CPU%u: %lld\n",cpu, bootval); + pr_info("TSC ADJUST: Boot CPU%u: %lld\n", cpu, bootval); } #else /* !CONFIG_SMP */