git: ebaea1bcd2eb - main - x86: AMD Zen2: Zenbleed chicken bit mitigation

From: Ed Maste <emaste_at_FreeBSD.org>
Date: Mon, 02 Oct 2023 19:29:34 UTC
The branch main has been updated by emaste:

URL: https://cgit.FreeBSD.org/src/commit/?id=ebaea1bcd2eb0aa90937637ed305184b6fedc69b

commit ebaea1bcd2eb0aa90937637ed305184b6fedc69b
Author:     Olivier Certner <olce.freebsd@certner.fr>
AuthorDate: 2023-09-11 14:18:30 +0000
Commit:     Ed Maste <emaste@FreeBSD.org>
CommitDate: 2023-10-02 19:29:18 +0000

    x86: AMD Zen2: Zenbleed chicken bit mitigation
    
    Applies only to bare-metal Zen2 processors.  The system currently
    automatically applies it to all of them.
    
    Tunable/sysctl 'machdep.mitigations.zenbleed.enable' can be used to
    forcibly enable or disable the mitigation at boot or run-time.  Possible
    values are:
    
        0: Mitigation disabled
        1: Mitigation enabled
        2: Run the automatic determination.
    
    Currently, value 2 is the default and has identical effect as value 1.
    This might change in the future if we choose to take into account
    microcode revisions in the automatic determination process.
    
    The tunable/sysctl value is simply ignored on non-applicable CPU models,
    which is useful to apply the same configuration on a set of machines
    that do not all have Zen2 processors.  Trying to set it to any integer
    value not listed above is silently equivalent to setting it to value 2
    (automatic determination).
    
    The current mitigation state can be queried through sysctl
    'machdep.mitigations.zenbleed.state', which returns "Not applicable",
    "Mitigation enabled" or "Mitigation disabled".  Note that this state is
    not guaranteed to be accurate in case of intervening modifications of
    the corresponding chicken bit directly via cpuctl(4) (this includes the
    cpucontrol(8) utility).  Resetting the desired policy through
    'machdep.mitigations.zenbleed.enable' (possibly to its current value)
    will reset the hardware state and ensure that the reported state is
    again coherent with it.
    
    Reviewed by:    kib
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D41817
---
 sys/amd64/acpica/acpi_wakeup.c |   1 +
 sys/amd64/amd64/initcpu.c      |   3 +
 sys/amd64/amd64/machdep.c      |   4 ++
 sys/dev/cpuctl/cpuctl.c        |   1 +
 sys/x86/include/specialreg.h   |   1 +
 sys/x86/include/x86_var.h      |   3 +
 sys/x86/x86/cpu_machdep.c      | 123 +++++++++++++++++++++++++++++++++++++++++
 7 files changed, 136 insertions(+)

diff --git a/sys/amd64/acpica/acpi_wakeup.c b/sys/amd64/acpica/acpi_wakeup.c
index 3b21935255cb..0f9f987b6590 100644
--- a/sys/amd64/acpica/acpi_wakeup.c
+++ b/sys/amd64/acpica/acpi_wakeup.c
@@ -306,6 +306,7 @@ acpi_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result,
 		amd64_syscall_ret_flush_l1d_recalc();
 		hw_ssb_recalculate(true);
 		x86_rngds_mitg_recalculate(true);
+		zenbleed_check_and_apply(true);
 
 		AcpiSetFirmwareWakingVector(0, 0);
 	} else {
diff --git a/sys/amd64/amd64/initcpu.c b/sys/amd64/amd64/initcpu.c
index a048c08fc9ae..c5266ffcc235 100644
--- a/sys/amd64/amd64/initcpu.c
+++ b/sys/amd64/amd64/initcpu.c
@@ -193,6 +193,9 @@ init_amd(void)
 			hw_lower_amd64_sharedpage = 1;
 		}
 	}
+
+	/* Zenbleed.  See the comments in 'cpu_machdep.c'. */
+	zenbleed_check_and_apply(false);
 }
 
 /*
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
index f39678d1f4e5..4b81f5b9671e 100644
--- a/sys/amd64/amd64/machdep.c
+++ b/sys/amd64/amd64/machdep.c
@@ -1495,6 +1495,10 @@ hammer_time(u_int64_t modulep, u_int64_t physfree)
 	TUNABLE_INT_FETCH("machdep.mitigations.rndgs.enable",
 	    &x86_rngds_mitg_enable);
 
+	TUNABLE_INT_FETCH("machdep.mitigations.zenbleed.enable",
+	    &zenbleed_enable);
+	zenbleed_sanitize_enable();
+
 	finishidentcpu();	/* Final stage of CPU initialization */
 
 	/*
diff --git a/sys/dev/cpuctl/cpuctl.c b/sys/dev/cpuctl/cpuctl.c
index 9c56db1ad19a..1fa655342121 100644
--- a/sys/dev/cpuctl/cpuctl.c
+++ b/sys/dev/cpuctl/cpuctl.c
@@ -546,6 +546,7 @@ cpuctl_do_eval_cpu_features(int cpu, struct thread *td)
 	hw_mds_recalculate();
 	x86_taa_recalculate();
 	x86_rngds_mitg_recalculate(true);
+	zenbleed_check_and_apply(true);
 	printcpuinfo();
 	return (0);
 }
diff --git a/sys/x86/include/specialreg.h b/sys/x86/include/specialreg.h
index f45990a056c8..4886430c84b6 100644
--- a/sys/x86/include/specialreg.h
+++ b/sys/x86/include/specialreg.h
@@ -1172,6 +1172,7 @@
 /* MSR_DE_CFG */
 #define DE_CFG_10H_12H_STACK_POINTER_JUMP_FIX_BIT	0x1
 #define DE_CFG_ZEN_LOAD_STALE_DATA_FIX_BIT		0x2000
+#define DE_CFG_ZEN2_FP_BACKUP_FIX_BIT			0x200
 
 /* VIA ACE crypto featureset: for via_feature_rng */
 #define	VIA_HAS_RNG		1	/* cpu has RNG */
diff --git a/sys/x86/include/x86_var.h b/sys/x86/include/x86_var.h
index 1629236c5928..44314456d8bf 100644
--- a/sys/x86/include/x86_var.h
+++ b/sys/x86/include/x86_var.h
@@ -90,6 +90,7 @@ extern	int	hw_ssb_active;
 extern	int	x86_taa_enable;
 extern	int	cpu_flush_rsb_ctxsw;
 extern	int	x86_rngds_mitg_enable;
+extern	int	zenbleed_enable;
 extern	int	cpu_amdc1e_bug;
 extern	char	bootmethod[16];
 
@@ -140,6 +141,8 @@ void	hw_mds_recalculate(void);
 void	hw_ssb_recalculate(bool all_cpus);
 void	x86_taa_recalculate(void);
 void	x86_rngds_mitg_recalculate(bool all_cpus);
+void	zenbleed_sanitize_enable(void);
+void	zenbleed_check_and_apply(bool all_cpus);
 void	nmi_call_kdb(u_int cpu, u_int type, struct trapframe *frame);
 void	nmi_call_kdb_smp(u_int type, struct trapframe *frame);
 void	nmi_handle_intr(u_int type, struct trapframe *frame);
diff --git a/sys/x86/x86/cpu_machdep.c b/sys/x86/x86/cpu_machdep.c
index c80284cf2987..868e6e2bc365 100644
--- a/sys/x86/x86/cpu_machdep.c
+++ b/sys/x86/x86/cpu_machdep.c
@@ -1477,6 +1477,129 @@ SYSCTL_PROC(_machdep_mitigations_rngds, OID_AUTO, state,
     sysctl_rngds_state_handler, "A",
     "MCU Optimization state");
 
+
+/*
+ * Zenbleed.
+ *
+ * No corresponding errata is publicly listed.  AMD has issued a security
+ * bulletin (AMD-SB-7008), entitled "Cross-Process Information Leak".  This
+ * document lists (as of August 2023) platform firmware's availability target
+ * dates, with most being November/December 2023.  It will then be up to
+ * motherboard manufacturers to produce corresponding BIOS updates, which will
+ * happen with an inevitable lag.  Additionally, for a variety of reasons,
+ * operators might not be able to apply them everywhere due.  On the side of
+ * standalone CPU microcodes, no plans for availability have been published so
+ * far.  However, a developer appearing to be an AMD employee has hardcoded in
+ * Linux revision numbers of future microcodes that are presumed to fix the
+ * vulnerability.
+ *
+ * Given the stability issues encountered with early microcode releases for Rome
+ * (the only microcode publicly released so far) and the absence of official
+ * communication on standalone CPU microcodes, we have opted instead for
+ * matching by default all AMD Zen2 processors which, according to the
+ * vulnerability's discoverer, are all affected (see
+ * https://lock.cmpxchg8b.com/zenbleed.html).  This policy, also adopted by
+ * OpenBSD, may be overriden using the tunable/sysctl
+ * 'machdep.mitigations.zenbleed.enable'.  We might revise it later depending on
+ * official statements, microcode updates' public availability and community
+ * assessment that they actually fix the vulnerability without any instability
+ * side effects.
+ */
+
+SYSCTL_NODE(_machdep_mitigations, OID_AUTO, zenbleed,
+    CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+    "Zenbleed OS-triggered prevention (via chicken bit)");
+
+/* 2 is auto, see below. */
+int zenbleed_enable = 2;
+
+void
+zenbleed_sanitize_enable(void)
+{
+	/* Default to auto (2). */
+	if (zenbleed_enable < 0 || zenbleed_enable > 2)
+		zenbleed_enable = 2;
+}
+
+static bool
+zenbleed_chicken_bit_applicable(void)
+{
+	/* Concerns only bare-metal AMD Zen2 processors. */
+	return (cpu_vendor_id == CPU_VENDOR_AMD &&
+	    CPUID_TO_FAMILY(cpu_id) == 0x17 &&
+	    CPUID_TO_MODEL(cpu_id) >= 0x30 &&
+	    vm_guest == VM_GUEST_NO);
+}
+
+static bool
+zenbleed_chicken_bit_should_enable(void)
+{
+	/*
+	 * Obey tunable/sysctl.
+	 *
+	 * As explained above, currently, the automatic setting (2) and the "on"
+	 * one (1) have the same effect.  In the future, we might additionally
+	 * check for specific microcode revisions as part of the automatic
+	 * determination.
+	 */
+	return (zenbleed_enable != 0);
+}
+
+void
+zenbleed_check_and_apply(bool all_cpus)
+{
+	bool set;
+
+	if (!zenbleed_chicken_bit_applicable())
+		return;
+
+	set = zenbleed_chicken_bit_should_enable();
+
+	x86_msr_op(MSR_DE_CFG,
+	    (set ? MSR_OP_OR : MSR_OP_ANDNOT) |
+	    (all_cpus ? MSR_OP_RENDEZVOUS_ALL : MSR_OP_LOCAL),
+	    DE_CFG_ZEN2_FP_BACKUP_FIX_BIT, NULL);
+}
+
+static int
+sysctl_zenbleed_enable_handler(SYSCTL_HANDLER_ARGS)
+{
+	int error, val;
+
+	val = zenbleed_enable;
+	error = sysctl_handle_int(oidp, &val, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+	zenbleed_enable = val;
+	zenbleed_sanitize_enable();
+	zenbleed_check_and_apply(true);
+	return (0);
+}
+SYSCTL_PROC(_machdep_mitigations_zenbleed, OID_AUTO, enable, CTLTYPE_INT |
+    CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE, NULL, 0,
+    sysctl_zenbleed_enable_handler, "I",
+    "Enable Zenbleed OS-triggered mitigation (chicken bit) "
+    "(0: Force disable, 1: Force enable, 2: Automatic determination)");
+
+static int
+sysctl_zenbleed_state_handler(SYSCTL_HANDLER_ARGS)
+{
+	const char *state;
+
+	if (!zenbleed_chicken_bit_applicable())
+		state = "Not applicable";
+	else if (zenbleed_chicken_bit_should_enable())
+		state = "Mitigation enabled";
+	else
+		state = "Mitigation disabled";
+	return (SYSCTL_OUT(req, state, strlen(state)));
+}
+SYSCTL_PROC(_machdep_mitigations_zenbleed, OID_AUTO, state,
+    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
+    sysctl_zenbleed_state_handler, "A",
+    "Zenbleed OS-triggered mitigation (chicken bit) state");
+
+
 /*
  * Enable and restore kernel text write permissions.
  * Callers must ensure that disable_wp()/restore_wp() are executed