[patch RFC 3/5] x86/spectre: Prepare for IBRS selection

From: Thomas Gleixner
Date: Tue Jan 09 2018 - 20:22:12 EST


IBRS (Indirect Branch Restricted Speculation) is another mitigation method
for Spectre V2. So it needs to be integrated into the command line options.

That would be simple, but the new microcode which provides the
functionality is not widely deployed and gets potentially loaded late, so
the selection process might have to be repeated after micro code has loaded.

Split the command line parsing and the actual selection of the mitigation
method into two parts to prepare for IBRS support.

Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
---
arch/x86/include/asm/nospec-branch.h | 13 +++
arch/x86/kernel/cpu/bugs.c | 133 +++++++++++++++++++++++------------
2 files changed, 101 insertions(+), 45 deletions(-)

--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -152,5 +152,18 @@
# define THUNK_TARGET(addr) [thunk_target] "rm" (addr)
#endif

+/* The Spectre V2 mitigation variants */
+enum spectre_v2_mitigation {
+ SPECTRE_V2_NONE,
+ SPECTRE_V2_RETPOLINE_MINIMAL,
+ SPECTRE_V2_RETPOLINE_MINIMAL_AMD,
+ SPECTRE_V2_RETPOLINE_GENERIC,
+ SPECTRE_V2_RETPOLINE_AMD,
+ SPECTRE_V2_IBRS,
+};
+
+enum spectre_v2_mitigation spectre_v2_enabled;
+void spectre_v2_select_mitigation(void);
+
#endif /* __ASSEMBLY__ */
#endif /* __NOSPEC_BRANCH_H__ */
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -37,6 +37,7 @@ void __init check_bugs(void)

/* Select the proper spectre mitigation before patching alternatives */
spectre_v2_check_boottime_disable();
+ spectre_v2_select_mitigation();

#ifdef CONFIG_X86_32
/*
@@ -70,12 +71,14 @@ void __init check_bugs(void)
#endif
}

-enum spectre_v2_mitigation {
- SPECTRE_V2_NONE,
- SPECTRE_V2_RETPOLINE_MINIMAL,
- SPECTRE_V2_RETPOLINE_MINIMAL_AMD,
- SPECTRE_V2_RETPOLINE_GENERIC,
- SPECTRE_V2_RETPOLINE_AMD,
+/* The kernel command line selection */
+enum spectre_v2_mitigation_cmd {
+ SPECTRE_V2_CMD_NONE,
+ SPECTRE_V2_CMD_AUTO,
+ SPECTRE_V2_CMD_FORCE,
+ SPECTRE_V2_CMD_RETPOLINE,
+ SPECTRE_V2_CMD_RETPOLINE_GENERIC,
+ SPECTRE_V2_CMD_RETPOLINE_AMD,
};

static const char *spectre_v2_strings[] = {
@@ -89,7 +92,8 @@ static const char *spectre_v2_strings[]
#undef pr_fmt
#define pr_fmt(fmt) "Spectre V2 mitigation: " fmt

-static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE;
+static enum spectre_v2_mitigation_cmd spectre_v2_cmdline = SPECTRE_V2_CMD_AUTO;
+enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE;

static void __init spec2_print_if_insecure(const char *reason)
{
@@ -124,66 +128,105 @@ static void __init spectre_v2_check_boot
sizeof(arg));
if (ret > 0) {
if (match_option(arg, ret, "off")) {
- spec2_print_if_insecure("disabled on command line.");
goto disable;
} else if (match_option(arg, ret, "on")) {
spec2_print_if_secure("force enabled on command line.");
- goto force;
+ spectre_v2_cmdline = SPECTRE_V2_CMD_FORCE;
+ return;
} else if (match_option(arg, ret, "retpoline")) {
spec2_print_if_insecure("retpoline selected on command line.");
- goto retpoline;
+ spectre_v2_cmdline = SPECTRE_V2_CMD_RETPOLINE;
+ return;
} else if (match_option(arg, ret, "retpoline,amd")) {
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
+ pr_err("retpoline,amd selected but CPU is not AMD. Switching to AUTO select\n");
+ return;
+ }
spec2_print_if_insecure("AMD retpoline selected on command line.");
- goto retpoline_amd;
+ spectre_v2_cmdline = SPECTRE_V2_CMD_RETPOLINE_AMD;
+ return;
} else if (match_option(arg, ret, "retpoline,generic")) {
spec2_print_if_insecure("generic retpoline selected on command line.");
- goto retpoline_generic;
+ spectre_v2_cmdline = SPECTRE_V2_CMD_RETPOLINE_GENERIC;
+ return;
} else if (match_option(arg, ret, "auto")) {
- goto autosel;
+ return;
}
}

- if (cmdline_find_option_bool(boot_command_line, "nospectre_v2")) {
- spec2_print_if_insecure("disabled on command line.");
- goto disable;
- }
+ if (!cmdline_find_option_bool(boot_command_line, "nospectre_v2"))
+ return;
+disable:
+ spec2_print_if_insecure("disabled on command line.");
+ spectre_v2_cmdline = SPECTRE_V2_CMD_NONE;
+}

-autosel:
- if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
- goto disable;
+void spectre_v2_select_mitigation(void)
+{
+ enum spectre_v2_mitigation mode = SPECTRE_V2_NONE;

-force:
-#ifdef CONFIG_RETPOLINE
-retpoline:
+ /*
+ * If the CPU is not affected and the command line mode is NONE or AUTO
+ * then nothing to do.
+ */
+ if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2) &&
+ (spectre_v2_cmdline == SPECTRE_V2_CMD_NONE ||
+ spectre_v2_cmdline == SPECTRE_V2_CMD_AUTO))
+ return;
+
+ switch (spectre_v2_cmdline) {
+ case SPECTRE_V2_CMD_NONE:
+ return;
+
+ case SPECTRE_V2_CMD_FORCE:
+ /* FALLTRHU */
+ case SPECTRE_V2_CMD_AUTO:
+ goto retpoline_auto;
+
+ case SPECTRE_V2_CMD_RETPOLINE_AMD:
+ if (IS_ENABLED(CONFIG_RETPOLINE))
+ goto retpoline_amd;
+ break;
+ case SPECTRE_V2_CMD_RETPOLINE_GENERIC:
+ if (IS_ENABLED(CONFIG_RETPOLINE))
+ goto retpoline_generic;
+ break;
+ case SPECTRE_V2_CMD_RETPOLINE:
+ if (IS_ENABLED(CONFIG_RETPOLINE))
+ goto retpoline_auto;
+ break;
+ }
+ pr_err("kernel not compiled with retpoline; no mitigation available!");
+ return;
+
+retpoline_auto:
+ /*
+ * Note: This only takes effect during boot before alternatives
+ * have been patched. Later invocations after late micro code
+ * update wont change the boot selection even if IBRS has not been
+ * made available by the microcode loading.
+ */
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
retpoline_amd:
- if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD ||
- !boot_cpu_has(X86_FEATURE_LFENCE_RDTSC)) {
- pr_info("AMD retpoline not supported, fall back to generic\n");
+ if (!boot_cpu_has(X86_FEATURE_LFENCE_RDTSC)) {
+ pr_info("LFENCE not serializing, using to generic mode\n");
goto retpoline_generic;
}
-
- spectre_v2_enabled = retp_compiler() ?
- SPECTRE_V2_RETPOLINE_AMD : SPECTRE_V2_RETPOLINE_MINIMAL_AMD;
+ mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_AMD :
+ SPECTRE_V2_RETPOLINE_MINIMAL_AMD;
setup_force_cpu_cap(X86_FEATURE_RETPOLINE_AMD);
setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
- return;
+ } else {
+ retpoline_generic:
+ mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_GENERIC :
+ SPECTRE_V2_RETPOLINE_MINIMAL;
+ setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
}
-retpoline_generic:
- spectre_v2_enabled = retp_compiler() ?
- SPECTRE_V2_RETPOLINE_GENERIC : SPECTRE_V2_RETPOLINE_MINIMAL;
- setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
- return;
-#else
-retpoline:
-retpoline_amd:
-retpoline_generic:
- pr_err("kernel not compiled with retpoline; no mitigation available!");
-#endif
-disable:
- setup_clear_cpu_cap(X86_FEATURE_RETPOLINE);
- setup_clear_cpu_cap(X86_FEATURE_RETPOLINE_AMD);
- return;
+
+ if (spectre_v2_enabled == mode)
+ return;
+ spectre_v2_enabled = mode;
+ pr_info("%s\n", spectre_v2_strings[mode]);
}

#undef pr_fmt