]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - arch/x86/kernel/cpu/bugs.c
x86/speculation: Add command line control for indirect branch speculation
[mirror_ubuntu-bionic-kernel.git] / arch / x86 / kernel / cpu / bugs.c
index 757b49992efc052ecf6086f11b377987229ff3fb..24e3ada7f9e5d5c51708737ef855f2eb6bb4c38b 100644 (file)
@@ -53,6 +53,9 @@ static u64 __ro_after_init x86_spec_ctrl_mask = SPEC_CTRL_IBRS;
 u64 __ro_after_init x86_amd_ls_cfg_base;
 u64 __ro_after_init x86_amd_ls_cfg_ssbd_mask;
 
+/* Control conditional STIPB in switch_to() */
+DEFINE_STATIC_KEY_FALSE(switch_to_cond_stibp);
+
 void __init check_bugs(void)
 {
        identify_boot_cpu();
@@ -198,6 +201,9 @@ static void x86_amd_ssb_disable(void)
 static enum spectre_v2_mitigation spectre_v2_enabled __ro_after_init =
        SPECTRE_V2_NONE;
 
+static enum spectre_v2_user_mitigation spectre_v2_user __ro_after_init =
+       SPECTRE_V2_USER_NONE;
+
 #ifdef RETPOLINE
 static bool spectre_v2_bad_module;
 
@@ -236,6 +242,104 @@ enum spectre_v2_mitigation_cmd {
        SPECTRE_V2_CMD_RETPOLINE_AMD,
 };
 
+enum spectre_v2_user_cmd {
+       SPECTRE_V2_USER_CMD_NONE,
+       SPECTRE_V2_USER_CMD_AUTO,
+       SPECTRE_V2_USER_CMD_FORCE,
+};
+
+static const char * const spectre_v2_user_strings[] = {
+       [SPECTRE_V2_USER_NONE]          = "User space: Vulnerable",
+       [SPECTRE_V2_USER_STRICT]        = "User space: Mitigation: STIBP protection",
+};
+
+static const struct {
+       const char                      *option;
+       enum spectre_v2_user_cmd        cmd;
+       bool                            secure;
+} v2_user_options[] __initdata = {
+       { "auto",       SPECTRE_V2_USER_CMD_AUTO,       false },
+       { "off",        SPECTRE_V2_USER_CMD_NONE,       false },
+       { "on",         SPECTRE_V2_USER_CMD_FORCE,      true  },
+};
+
+static void __init spec_v2_user_print_cond(const char *reason, bool secure)
+{
+       if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2) != secure)
+               pr_info("spectre_v2_user=%s forced on command line.\n", reason);
+}
+
+static enum spectre_v2_user_cmd __init
+spectre_v2_parse_user_cmdline(enum spectre_v2_mitigation_cmd v2_cmd)
+{
+       char arg[20];
+       int ret, i;
+
+       switch (v2_cmd) {
+       case SPECTRE_V2_CMD_NONE:
+               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 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)) {
+                       spec_v2_user_print_cond(v2_user_options[i].option,
+                                               v2_user_options[i].secure);
+                       return v2_user_options[i].cmd;
+               }
+       }
+
+       pr_err("Unknown user space protection option (%s). Switching to AUTO select\n", arg);
+       return SPECTRE_V2_USER_CMD_AUTO;
+}
+
+static void __init
+spectre_v2_user_select_mitigation(enum spectre_v2_mitigation_cmd v2_cmd)
+{
+       enum spectre_v2_user_mitigation mode = SPECTRE_V2_USER_NONE;
+       bool smt_possible = IS_ENABLED(CONFIG_SMP);
+
+       if (!boot_cpu_has(X86_FEATURE_IBPB) && !boot_cpu_has(X86_FEATURE_STIBP))
+               return;
+
+       if (cpu_smt_control == CPU_SMT_FORCE_DISABLED ||
+           cpu_smt_control == CPU_SMT_NOT_SUPPORTED)
+               smt_possible = false;
+
+       switch (spectre_v2_parse_user_cmdline(v2_cmd)) {
+       case SPECTRE_V2_USER_CMD_AUTO:
+       case SPECTRE_V2_USER_CMD_NONE:
+               goto set_mode;
+       case SPECTRE_V2_USER_CMD_FORCE:
+               mode = SPECTRE_V2_USER_STRICT;
+               break;
+       }
+
+       /* Initialize Indirect Branch Prediction Barrier */
+       if (boot_cpu_has(X86_FEATURE_IBPB)) {
+               setup_force_cpu_cap(X86_FEATURE_USE_IBPB);
+               pr_info("Spectre v2 mitigation: Enabling Indirect Branch Prediction Barrier\n");
+       }
+
+       /* If enhanced IBRS is enabled no STIPB required */
+       if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED)
+               return;
+
+set_mode:
+       spectre_v2_user = mode;
+       /* Only print the STIBP mode when SMT possible */
+       if (smt_possible)
+               pr_info("%s\n", spectre_v2_user_strings[mode]);
+}
+
 static const char * const spectre_v2_strings[] = {
        [SPECTRE_V2_NONE]                       = "Vulnerable",
        [SPECTRE_V2_RETPOLINE_GENERIC]          = "Mitigation: Full generic retpoline",
@@ -382,12 +486,6 @@ specv2_set_mode:
        setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
        pr_info("Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch\n");
 
-       /* Initialize Indirect Branch Prediction Barrier if supported */
-       if (boot_cpu_has(X86_FEATURE_IBPB)) {
-               setup_force_cpu_cap(X86_FEATURE_USE_IBPB);
-               pr_info("Spectre v2 mitigation: Enabling Indirect Branch Prediction Barrier\n");
-       }
-
        /*
         * Retpoline means the kernel is safe because it has no indirect
         * branches. Enhanced IBRS protects firmware too, so, enable restricted
@@ -404,23 +502,21 @@ specv2_set_mode:
                pr_info("Enabling Restricted Speculation for firmware calls\n");
        }
 
+       /* Set up IBPB and STIBP depending on the general spectre V2 command */
+       spectre_v2_user_select_mitigation(cmd);
+
        /* Enable STIBP if appropriate */
        arch_smt_update();
 }
 
 static bool stibp_needed(void)
 {
-       if (spectre_v2_enabled == SPECTRE_V2_NONE)
-               return false;
-
        /* Enhanced IBRS makes using STIBP unnecessary. */
        if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED)
                return false;
 
-       if (!boot_cpu_has(X86_FEATURE_STIBP))
-               return false;
-
-       return true;
+       /* Check for strict user mitigation mode */
+       return spectre_v2_user == SPECTRE_V2_USER_STRICT;
 }
 
 static void update_stibp_msr(void *info)
@@ -838,10 +934,13 @@ static char *stibp_state(void)
        if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED)
                return "";
 
-       if (x86_spec_ctrl_base & SPEC_CTRL_STIBP)
-               return ", STIBP";
-       else
-               return "";
+       switch (spectre_v2_user) {
+       case SPECTRE_V2_USER_NONE:
+               return ", STIBP: disabled";
+       case SPECTRE_V2_USER_STRICT:
+               return ", STIBP: forced";
+       }
+       return "";
 }
 
 static char *ibpb_state(void)