+#include <xen/cpu.h>
#include <xen/init.h>
#include <xen/bitops.h>
#include <xen/mm.h>
#include <xen/param.h>
#include <xen/smp.h>
+#include <xen/softirq.h>
#include <xen/pci.h>
#include <xen/warning.h>
#include <asm/io.h>
/* Signal whether the ACPI C1E quirk is required. */
bool __read_mostly amd_acpi_c1e_quirk;
+static bool __read_mostly zen2_c6_disabled;
+
static inline int rdmsr_amd_safe(unsigned int msr, unsigned int *lo,
unsigned int *hi)
{
val & chickenbit ? "chickenbit" : "microcode");
}
+static void 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 init_amd(struct cpuinfo_x86 *c)
{
u32 l, h;
amd_check_zenbleed();
+ if (zen2_c6_disabled)
+ zen2_disable_c6(NULL);
+
check_syscfg_dram_mod_en();
amd_log_freq(c);
.c_early_init = early_init_amd,
.c_init = init_amd,
};
+
+static int __init 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);