Commit ddfca943 authored by David Kaplan's avatar David Kaplan Committed by Borislav Petkov (AMD)
Browse files

x86/bugs: Restructure spectre_v2_user mitigation



Restructure spectre_v2_user to use select/update/apply functions to
create consistent vulnerability handling.

The IBPB/STIBP choices are first decided based on the spectre_v2_user
command line but can be modified by the spectre_v2 command line option
as well.

Signed-off-by: default avatarDavid Kaplan <david.kaplan@amd.com>
Signed-off-by: default avatarBorislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
Link: https://lore.kernel.org/20250418161721.1855190-12-david.kaplan@amd.com
parent e3b78a7a
Loading
Loading
Loading
Loading
+94 −74
Original line number Diff line number Diff line
@@ -60,6 +60,8 @@ static void __init retbleed_select_mitigation(void);
static void __init retbleed_update_mitigation(void);
static void __init retbleed_apply_mitigation(void);
static void __init spectre_v2_user_select_mitigation(void);
static void __init spectre_v2_user_update_mitigation(void);
static void __init spectre_v2_user_apply_mitigation(void);
static void __init ssb_select_mitigation(void);
static void __init l1tf_select_mitigation(void);
static void __init mds_select_mitigation(void);
@@ -190,11 +192,6 @@ void __init cpu_select_mitigations(void)
	spectre_v1_select_mitigation();
	spectre_v2_select_mitigation();
	retbleed_select_mitigation();
	/*
	 * spectre_v2_user_select_mitigation() relies on the state set by
	 * retbleed_select_mitigation(); specifically the STIBP selection is
	 * forced for UNRET or IBPB.
	 */
	spectre_v2_user_select_mitigation();
	ssb_select_mitigation();
	l1tf_select_mitigation();
@@ -217,6 +214,13 @@ void __init cpu_select_mitigations(void)
	 * choices.
	 */
	retbleed_update_mitigation();

	/*
	 * spectre_v2_user_update_mitigation() depends on
	 * retbleed_update_mitigation(), specifically the STIBP
	 * selection is forced for UNRET or IBPB.
	 */
	spectre_v2_user_update_mitigation();
	mds_update_mitigation();
	taa_update_mitigation();
	mmio_update_mitigation();
@@ -224,6 +228,7 @@ void __init cpu_select_mitigations(void)

	spectre_v1_apply_mitigation();
	retbleed_apply_mitigation();
	spectre_v2_user_apply_mitigation();
	mds_apply_mitigation();
	taa_apply_mitigation();
	mmio_apply_mitigation();
@@ -1379,6 +1384,8 @@ enum spectre_v2_mitigation_cmd {
	SPECTRE_V2_CMD_IBRS,
};

static enum spectre_v2_mitigation_cmd spectre_v2_cmd __ro_after_init = SPECTRE_V2_CMD_AUTO;

enum spectre_v2_user_cmd {
	SPECTRE_V2_USER_CMD_NONE,
	SPECTRE_V2_USER_CMD_AUTO,
@@ -1417,31 +1424,18 @@ static void __init spec_v2_user_print_cond(const char *reason, bool secure)
		pr_info("spectre_v2_user=%s forced on command line.\n", reason);
}

static __ro_after_init enum spectre_v2_mitigation_cmd spectre_v2_cmd;

static enum spectre_v2_user_cmd __init
spectre_v2_parse_user_cmdline(void)
static enum spectre_v2_user_cmd __init spectre_v2_parse_user_cmdline(void)
{
	enum spectre_v2_user_cmd mode;
	char arg[20];
	int ret, i;

	mode = IS_ENABLED(CONFIG_MITIGATION_SPECTRE_V2) ?
		SPECTRE_V2_USER_CMD_AUTO : SPECTRE_V2_USER_CMD_NONE;

	switch (spectre_v2_cmd) {
	case SPECTRE_V2_CMD_NONE:
	if (cpu_mitigations_off() || !IS_ENABLED(CONFIG_MITIGATION_SPECTRE_V2))
		return SPECTRE_V2_USER_CMD_NONE;
	case SPECTRE_V2_CMD_FORCE:
		return SPECTRE_V2_USER_CMD_FORCE;
	default:
		break;
	}

	ret = cmdline_find_option(boot_command_line, "spectre_v2_user",
				  arg, sizeof(arg));
	if (ret < 0)
		return mode;
		return SPECTRE_V2_USER_CMD_AUTO;

	for (i = 0; i < ARRAY_SIZE(v2_user_options); i++) {
		if (match_option(arg, ret, v2_user_options[i].option)) {
@@ -1452,7 +1446,7 @@ spectre_v2_parse_user_cmdline(void)
	}

	pr_err("Unknown user space protection option (%s). Switching to default\n", arg);
	return mode;
	return SPECTRE_V2_USER_CMD_AUTO;
}

static inline bool spectre_v2_in_ibrs_mode(enum spectre_v2_mitigation mode)
@@ -1460,60 +1454,72 @@ static inline bool spectre_v2_in_ibrs_mode(enum spectre_v2_mitigation mode)
	return spectre_v2_in_eibrs_mode(mode) || mode == SPECTRE_V2_IBRS;
}

static void __init
spectre_v2_user_select_mitigation(void)
static void __init spectre_v2_user_select_mitigation(void)
{
	enum spectre_v2_user_mitigation mode = SPECTRE_V2_USER_NONE;
	enum spectre_v2_user_cmd cmd;

	if (!boot_cpu_has(X86_FEATURE_IBPB) && !boot_cpu_has(X86_FEATURE_STIBP))
		return;

	cmd = spectre_v2_parse_user_cmdline();
	switch (cmd) {
	switch (spectre_v2_parse_user_cmdline()) {
	case SPECTRE_V2_USER_CMD_NONE:
		goto set_mode;
		return;
	case SPECTRE_V2_USER_CMD_FORCE:
		mode = SPECTRE_V2_USER_STRICT;
		spectre_v2_user_ibpb  = SPECTRE_V2_USER_STRICT;
		spectre_v2_user_stibp = SPECTRE_V2_USER_STRICT;
		break;
	case SPECTRE_V2_USER_CMD_AUTO:
	case SPECTRE_V2_USER_CMD_PRCTL:
		spectre_v2_user_ibpb  = SPECTRE_V2_USER_PRCTL;
		spectre_v2_user_stibp = SPECTRE_V2_USER_PRCTL;
		break;
	case SPECTRE_V2_USER_CMD_PRCTL_IBPB:
		mode = SPECTRE_V2_USER_PRCTL;
		spectre_v2_user_ibpb  = SPECTRE_V2_USER_STRICT;
		spectre_v2_user_stibp = SPECTRE_V2_USER_PRCTL;
		break;
	case SPECTRE_V2_USER_CMD_SECCOMP:
		if (IS_ENABLED(CONFIG_SECCOMP))
			spectre_v2_user_ibpb = SPECTRE_V2_USER_SECCOMP;
		else
			spectre_v2_user_ibpb = SPECTRE_V2_USER_PRCTL;
		spectre_v2_user_stibp = spectre_v2_user_ibpb;
		break;
	case SPECTRE_V2_USER_CMD_SECCOMP_IBPB:
		spectre_v2_user_ibpb = SPECTRE_V2_USER_STRICT;
		if (IS_ENABLED(CONFIG_SECCOMP))
			mode = SPECTRE_V2_USER_SECCOMP;
			spectre_v2_user_stibp = SPECTRE_V2_USER_SECCOMP;
		else
			mode = SPECTRE_V2_USER_PRCTL;
			spectre_v2_user_stibp = SPECTRE_V2_USER_PRCTL;
		break;
	}

	/* Initialize Indirect Branch Prediction Barrier */
	if (boot_cpu_has(X86_FEATURE_IBPB)) {
		static_branch_enable(&switch_vcpu_ibpb);
	/*
	 * At this point, an STIBP mode other than "off" has been set.
	 * If STIBP support is not being forced, check if STIBP always-on
	 * is preferred.
	 */
	if ((spectre_v2_user_stibp == SPECTRE_V2_USER_PRCTL ||
	     spectre_v2_user_stibp == SPECTRE_V2_USER_SECCOMP) &&
	    boot_cpu_has(X86_FEATURE_AMD_STIBP_ALWAYS_ON))
		spectre_v2_user_stibp = SPECTRE_V2_USER_STRICT_PREFERRED;

		spectre_v2_user_ibpb = mode;
		switch (cmd) {
		case SPECTRE_V2_USER_CMD_NONE:
			break;
		case SPECTRE_V2_USER_CMD_FORCE:
		case SPECTRE_V2_USER_CMD_PRCTL_IBPB:
		case SPECTRE_V2_USER_CMD_SECCOMP_IBPB:
			static_branch_enable(&switch_mm_always_ibpb);
			spectre_v2_user_ibpb = SPECTRE_V2_USER_STRICT;
			break;
		case SPECTRE_V2_USER_CMD_PRCTL:
		case SPECTRE_V2_USER_CMD_AUTO:
		case SPECTRE_V2_USER_CMD_SECCOMP:
			static_branch_enable(&switch_mm_cond_ibpb);
			break;
	if (!boot_cpu_has(X86_FEATURE_IBPB))
		spectre_v2_user_ibpb = SPECTRE_V2_USER_NONE;

	if (!boot_cpu_has(X86_FEATURE_STIBP))
		spectre_v2_user_stibp = SPECTRE_V2_USER_NONE;
}

		pr_info("mitigation: Enabling %s Indirect Branch Prediction Barrier\n",
			static_key_enabled(&switch_mm_always_ibpb) ?
			"always-on" : "conditional");
static void __init spectre_v2_user_update_mitigation(void)
{
	if (!boot_cpu_has(X86_FEATURE_IBPB) && !boot_cpu_has(X86_FEATURE_STIBP))
		return;

	/* The spectre_v2 cmd line can override spectre_v2_user options */
	if (spectre_v2_cmd == SPECTRE_V2_CMD_NONE) {
		spectre_v2_user_ibpb = SPECTRE_V2_USER_NONE;
		spectre_v2_user_stibp = SPECTRE_V2_USER_NONE;
	} else if (spectre_v2_cmd == SPECTRE_V2_CMD_FORCE) {
		spectre_v2_user_ibpb = SPECTRE_V2_USER_STRICT;
		spectre_v2_user_stibp = SPECTRE_V2_USER_STRICT;
	}

	/*
@@ -1531,30 +1537,44 @@ spectre_v2_user_select_mitigation(void)
	if (!boot_cpu_has(X86_FEATURE_STIBP) ||
	    !cpu_smt_possible() ||
	    (spectre_v2_in_eibrs_mode(spectre_v2_enabled) &&
	     !boot_cpu_has(X86_FEATURE_AUTOIBRS)))
	     !boot_cpu_has(X86_FEATURE_AUTOIBRS))) {
		spectre_v2_user_stibp = SPECTRE_V2_USER_NONE;
		return;
	}

	/*
	 * At this point, an STIBP mode other than "off" has been set.
	 * If STIBP support is not being forced, check if STIBP always-on
	 * is preferred.
	 */
	if (mode != SPECTRE_V2_USER_STRICT &&
	    boot_cpu_has(X86_FEATURE_AMD_STIBP_ALWAYS_ON))
		mode = SPECTRE_V2_USER_STRICT_PREFERRED;

	if (retbleed_mitigation == RETBLEED_MITIGATION_UNRET ||
	    retbleed_mitigation == RETBLEED_MITIGATION_IBPB) {
		if (mode != SPECTRE_V2_USER_STRICT &&
		    mode != SPECTRE_V2_USER_STRICT_PREFERRED)
	if (spectre_v2_user_stibp != SPECTRE_V2_USER_NONE &&
	    (retbleed_mitigation == RETBLEED_MITIGATION_UNRET ||
	     retbleed_mitigation == RETBLEED_MITIGATION_IBPB)) {
		if (spectre_v2_user_stibp != SPECTRE_V2_USER_STRICT &&
		    spectre_v2_user_stibp != SPECTRE_V2_USER_STRICT_PREFERRED)
			pr_info("Selecting STIBP always-on mode to complement retbleed mitigation\n");
		mode = SPECTRE_V2_USER_STRICT_PREFERRED;
		spectre_v2_user_stibp = SPECTRE_V2_USER_STRICT_PREFERRED;
	}
	pr_info("%s\n", spectre_v2_user_strings[spectre_v2_user_stibp]);
}

	spectre_v2_user_stibp = mode;
static void __init spectre_v2_user_apply_mitigation(void)
{
	/* Initialize Indirect Branch Prediction Barrier */
	if (spectre_v2_user_ibpb != SPECTRE_V2_USER_NONE) {
		static_branch_enable(&switch_vcpu_ibpb);

set_mode:
	pr_info("%s\n", spectre_v2_user_strings[mode]);
		switch (spectre_v2_user_ibpb) {
		case SPECTRE_V2_USER_STRICT:
			static_branch_enable(&switch_mm_always_ibpb);
			break;
		case SPECTRE_V2_USER_PRCTL:
		case SPECTRE_V2_USER_SECCOMP:
			static_branch_enable(&switch_mm_cond_ibpb);
			break;
		default:
			break;
		}

		pr_info("mitigation: Enabling %s Indirect Branch Prediction Barrier\n",
			static_key_enabled(&switch_mm_always_ibpb) ?
			"always-on" : "conditional");
	}
}

static const char * const spectre_v2_strings[] = {