14c61e3
From: Andrew Cooper <andrew.cooper3@citrix.com>
14c61e3
Subject: x86/amd: Mitigations for Zenbleed
14c61e3
14c61e3
Zenbleed is a malfunction on AMD Zen2 uarch parts which results in corruption
14c61e3
of the vector registers.  An attacker can trigger this bug deliberately in
14c61e3
order to access stale data in the physical vector register file.  This can
14c61e3
include data from sibling threads, or a higher-privilege context.
14c61e3
14c61e3
Microcode is the preferred mitigation but in the case that's not available use
14c61e3
the chickenbit as instructed by AMD.  Re-evaluate the mitigation on late
14c61e3
microcode load too.
14c61e3
14c61e3
This is XSA-433 / CVE-2023-20593.
14c61e3
14c61e3
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
14c61e3
Acked-by: Roger Pau Monné <roger.pau@citrix.com>
14c61e3
14c61e3
diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c
14c61e3
index b6a20d375ad1..8d23a5be0c5f 100644
14c61e3
--- a/xen/arch/x86/cpu/amd.c
14c61e3
+++ b/xen/arch/x86/cpu/amd.c
14c61e3
@@ -13,6 +13,7 @@
14c61e3
 #include <asm/spec_ctrl.h>
14c61e3
 #include <asm/acpi.h>
14c61e3
 #include <asm/apic.h>
14c61e3
+#include <asm/microcode.h>
14c61e3
 
14c61e3
 #include "cpu.h"
14c61e3
 
14c61e3
@@ -878,6 +879,72 @@ void __init detect_zen2_null_seg_behaviour(void)
14c61e3
 
14c61e3
 }
14c61e3
 
14c61e3
+void amd_check_zenbleed(void)
14c61e3
+{
14c61e3
+	const struct cpu_signature *sig = &this_cpu(cpu_sig);
14c61e3
+	unsigned int good_rev, chickenbit = (1 << 9);
14c61e3
+	uint64_t val, old_val;
14c61e3
+
14c61e3
+	/*
14c61e3
+	 * If we're virtualised, we can't do family/model checks safely, and
14c61e3
+	 * we likely wouldn't have access to DE_CFG even if we could see a
14c61e3
+	 * microcode revision.
14c61e3
+	 *
14c61e3
+	 * A hypervisor may hide AVX as a stopgap mitigation.  We're not in a
14c61e3
+	 * position to care either way.  An admin doesn't want to be disabling
14c61e3
+	 * AVX as a mitigation on any build of Xen with this logic present.
14c61e3
+	 */
14c61e3
+	if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17)
14c61e3
+		return;
14c61e3
+
14c61e3
+	switch (boot_cpu_data.x86_model) {
14c61e3
+	case 0x30 ... 0x3f: good_rev = 0x0830107a; break;
14c61e3
+	case 0x60 ... 0x67: good_rev = 0x0860010b; break;
14c61e3
+	case 0x68 ... 0x6f: good_rev = 0x08608105; break;
14c61e3
+	case 0x70 ... 0x7f: good_rev = 0x08701032; break;
14c61e3
+	case 0xa0 ... 0xaf: good_rev = 0x08a00008; break;
14c61e3
+	default:
14c61e3
+		/*
14c61e3
+		 * With the Fam17h check above, parts getting here are Zen1.
14c61e3
+		 * They're not affected.
14c61e3
+		 */
14c61e3
+		return;
14c61e3
+	}
14c61e3
+
14c61e3
+	rdmsrl(MSR_AMD64_DE_CFG, val);
14c61e3
+	old_val = val;
14c61e3
+
14c61e3
+	/*
14c61e3
+	 * Microcode is the preferred mitigation, in terms of performance.
14c61e3
+	 * However, without microcode, this chickenbit (specific to the Zen2
14c61e3
+	 * uarch) disables Floating Point Mov-Elimination to mitigate the
14c61e3
+	 * issue.
14c61e3
+	 */
14c61e3
+	val &= ~chickenbit;
14c61e3
+	if (sig->rev < good_rev)
14c61e3
+		val |= chickenbit;
14c61e3
+
14c61e3
+	if (val == old_val)
14c61e3
+		/* Nothing to change. */
14c61e3
+		return;
14c61e3
+
14c61e3
+	/*
14c61e3
+	 * DE_CFG is a Core-scoped MSR, and this write is racy during late
14c61e3
+	 * microcode load.  However, both threads calculate the new value from
14c61e3
+	 * state which is shared, and unrelated to the old value, so the
14c61e3
+	 * result should be consistent.
14c61e3
+	 */
14c61e3
+	wrmsrl(MSR_AMD64_DE_CFG, val);
14c61e3
+
14c61e3
+	/*
14c61e3
+	 * Inform the admin that we changed something, but don't spam,
14c61e3
+	 * especially during a late microcode load.
14c61e3
+	 */
14c61e3
+	if (smp_processor_id() == 0)
14c61e3
+		printk(XENLOG_INFO "Zenbleed mitigation - using %s\n",
14c61e3
+		       val & chickenbit ? "chickenbit" : "microcode");
14c61e3
+}
14c61e3
+
14c61e3
 static void cf_check init_amd(struct cpuinfo_x86 *c)
14c61e3
 {
14c61e3
 	u32 l, h;
14c61e3
@@ -1150,6 +1217,8 @@ static void cf_check init_amd(struct cpuinfo_x86 *c)
14c61e3
 	if ((smp_processor_id() == 1) && !cpu_has(c, X86_FEATURE_ITSC))
14c61e3
 		disable_c1_ramping();
14c61e3
 
14c61e3
+	amd_check_zenbleed();
14c61e3
+
14c61e3
 	check_syscfg_dram_mod_en();
14c61e3
 
14c61e3
 	amd_log_freq(c);
14c61e3
diff --git a/xen/arch/x86/cpu/microcode/amd.c b/xen/arch/x86/cpu/microcode/amd.c
14c61e3
index ded8fe90e650..c6d13f3fb35f 100644
14c61e3
--- a/xen/arch/x86/cpu/microcode/amd.c
14c61e3
+++ b/xen/arch/x86/cpu/microcode/amd.c
14c61e3
@@ -262,6 +262,8 @@ static int cf_check apply_microcode(const struct microcode_patch *patch)
14c61e3
            "microcode: CPU%u updated from revision %#x to %#x, date = %04x-%02x-%02x\n",
14c61e3
            cpu, old_rev, rev, patch->year, patch->month, patch->day);
14c61e3
 
14c61e3
+    amd_check_zenbleed();
14c61e3
+
14c61e3
     return 0;
14c61e3
 }
14c61e3
 
14c61e3
diff --git a/xen/arch/x86/include/asm/processor.h b/xen/arch/x86/include/asm/processor.h
14c61e3
index 8e2816fae9b9..66611df6efc1 100644
14c61e3
--- a/xen/arch/x86/include/asm/processor.h
14c61e3
+++ b/xen/arch/x86/include/asm/processor.h
14c61e3
@@ -637,6 +637,8 @@ enum ap_boot_method {
14c61e3
 };
14c61e3
 extern enum ap_boot_method ap_boot_method;
14c61e3
 
14c61e3
+void amd_check_zenbleed(void);
14c61e3
+
14c61e3
 #endif /* !__ASSEMBLY__ */
14c61e3
 
14c61e3
 #endif /* __ASM_X86_PROCESSOR_H */
14c61e3