diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c index df2681b7c4..89e33a5da6 100644 --- a/xen/arch/x86/cpu/amd.c +++ b/xen/arch/x86/cpu/amd.c @@ -1,8 +1,10 @@ +#include #include #include #include #include #include +#include #include #include #include @@ -52,6 +54,8 @@ bool __read_mostly amd_acpi_c1e_quirk; bool __ro_after_init amd_legacy_ssbd; bool __initdata amd_virt_spec_ctrl; +static bool __read_mostly zen2_c6_disabled; + static inline int rdmsr_amd_safe(unsigned int msr, unsigned int *lo, unsigned int *hi) { @@ -972,6 +976,32 @@ void amd_check_zenbleed(void) val & chickenbit ? "chickenbit" : "microcode"); } +static void cf_check zen2_disable_c6(void *arg) +{ + /* Disable C6 by clearing the CCR{0,1,2}_CC6EN bits. */ + const uint64_t mask = ~((1ul << 6) | (1ul << 14) | (1ul << 22)); + uint64_t val; + + if (!zen2_c6_disabled) { + printk(XENLOG_WARNING + "Disabling C6 after 1000 days apparent uptime due to AMD errata 1474\n"); + zen2_c6_disabled = true; + /* + * Prevent CPU hotplug so that started CPUs will either see + * zen2_c6_disabled set, or will be handled by + * smp_call_function(). + */ + while (!get_cpu_maps()) + process_pending_softirqs(); + smp_call_function(zen2_disable_c6, NULL, 0); + put_cpu_maps(); + } + + /* Update the MSR to disable C6, done on all threads. */ + rdmsrl(MSR_AMD_CSTATE_CFG, val); + wrmsrl(MSR_AMD_CSTATE_CFG, val & mask); +} + static void cf_check init_amd(struct cpuinfo_x86 *c) { u32 l, h; @@ -1240,6 +1270,9 @@ static void cf_check init_amd(struct cpuinfo_x86 *c) amd_check_zenbleed(); + if (zen2_c6_disabled) + zen2_disable_c6(NULL); + check_syscfg_dram_mod_en(); amd_log_freq(c); @@ -1249,3 +1282,44 @@ const struct cpu_dev amd_cpu_dev = { .c_early_init = early_init_amd, .c_init = init_amd, }; + +static int __init cf_check zen2_c6_errata_check(void) +{ + /* + * Errata #1474: A Core May Hang After About 1044 Days + * Set up a timer to disable C6 after 1000 days uptime. + */ + s_time_t delta; + + /* + * Zen1 vs Zen2 isn't a simple model number comparison, so use STIBP as + * a heuristic to separate the two uarches in Fam17h. + */ + if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17 || + !boot_cpu_has(X86_FEATURE_AMD_STIBP)) + return 0; + + /* + * Deduct current TSC value, this would be relevant if kexec'ed for + * example. Might not be accurate, but worst case we end up disabling + * C6 before strictly required, which would still be safe. + * + * NB: all affected models (Zen2) have invariant TSC and TSC adjust + * MSR, so early_time_init() will have already cleared any TSC offset. + */ + delta = DAYS(1000) - tsc_ticks2ns(rdtsc()); + if (delta > 0) { + static struct timer errata_c6; + + init_timer(&errata_c6, zen2_disable_c6, NULL, 0); + set_timer(&errata_c6, NOW() + delta); + } else + zen2_disable_c6(NULL); + + return 0; +} +/* + * Must be executed after early_time_init() for tsc_ticks2ns() to have been + * calibrated. That prevents us doing the check in init_amd(). + */ +presmp_initcall(zen2_c6_errata_check); diff --git a/xen/arch/x86/include/asm/msr-index.h b/xen/arch/x86/include/asm/msr-index.h index 2382fc8e11..4d41c171d2 100644 --- a/xen/arch/x86/include/asm/msr-index.h +++ b/xen/arch/x86/include/asm/msr-index.h @@ -211,6 +211,8 @@ #define MSR_VIRT_SPEC_CTRL 0xc001011f /* Layout matches MSR_SPEC_CTRL */ +#define MSR_AMD_CSTATE_CFG 0xc0010296 + /* * Legacy MSR constants in need of cleanup. No new MSRs below this comment. */ diff --git a/xen/include/xen/time.h b/xen/include/xen/time.h index b7427460dd..9ceaec541f 100644 --- a/xen/include/xen/time.h +++ b/xen/include/xen/time.h @@ -53,6 +53,7 @@ struct tm wallclock_time(uint64_t *ns); #define SYSTEM_TIME_HZ 1000000000ULL #define NOW() ((s_time_t)get_s_time()) +#define DAYS(_d) SECONDS((_d) * 86400ULL) #define SECONDS(_s) ((s_time_t)((_s) * 1000000000ULL)) #define MILLISECS(_ms) ((s_time_t)((_ms) * 1000000ULL)) #define MICROSECS(_us) ((s_time_t)((_us) * 1000ULL))