Re: [tip:x86/pti] x86/spectre: Add boot time option to select Spectre v2 mitigation
From: Tom Lendacky
Date: Wed Jan 10 2018 - 13:33:43 EST
On 1/10/2018 12:14 PM, tip-bot for David Woodhouse wrote:
> Commit-ID: fe1678d070e36070c43005c71ce783df57852252
> Gitweb: https://git.kernel.org/tip/fe1678d070e36070c43005c71ce783df57852252
> Author: David Woodhouse <dwmw@xxxxxxxxxxxx>
> AuthorDate: Tue, 9 Jan 2018 14:43:09 +0000
> Committer: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
> CommitDate: Wed, 10 Jan 2018 19:09:09 +0100
>
> x86/spectre: Add boot time option to select Spectre v2 mitigation
>
> Add a spectre_v2= option to select the mitigation used for the indirect
> branch speculation vulnerability.
>
> Currently, the only option available is retpoline, in its various forms.
> This will be expanded to cover the new IBRS/IBPB microcode features.
>
> The RETPOLINE_AMD feature relies on a serializing LFENCE for speculation
> control. For AMD hardware, only set RETPOLINE_AMD if LFENCE is a
> serializing instruction, which is indicated by the LFENCE_RDTSC feature.
>
> [ tglx: Folded back the LFENCE/AMD fixes and reworked it so IBRS
> integration becomes simple ]
>
> Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
> Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
> Cc: gnomes@xxxxxxxxxxxxxxxxxxx
> Cc: Rik van Riel <riel@xxxxxxxxxx>
> Cc: Andi Kleen <ak@xxxxxxxxxxxxxxx>
> Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
> Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
> Cc: Jiri Kosina <jikos@xxxxxxxxxx>
> Cc: Andy Lutomirski <luto@xxxxxxxxxxxxxx>
> Cc: Dave Hansen <dave.hansen@xxxxxxxxx>
> Cc: Kees Cook <keescook@xxxxxxxxxx>
> Cc: Tim Chen <tim.c.chen@xxxxxxxxxxxxxxx>
> Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxxx>
> Cc: Paul Turner <pjt@xxxxxxxxxx>
> Cc: Tom Lendacky <thomas.lendacky@xxxxxxx>
> Link: https://lkml.kernel.org/r/1515508997-6154-4-git-send-email-dwmw@xxxxxxxxxxxx
>
>
> ---
> Documentation/admin-guide/kernel-parameters.txt | 28 +++++
> arch/x86/include/asm/nospec-branch.h | 10 ++
> arch/x86/kernel/cpu/bugs.c | 158 +++++++++++++++++++++++-
> arch/x86/kernel/cpu/common.c | 4 -
> 4 files changed, 195 insertions(+), 5 deletions(-)
>
...
> diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
> index 76ad6cb..4f7a2a0 100644
> --- a/arch/x86/kernel/cpu/bugs.c
> +++ b/arch/x86/kernel/cpu/bugs.c
> @@ -11,6 +11,9 @@
> #include <linux/init.h>
> #include <linux/utsname.h>
> #include <linux/cpu.h>
> +
> +#include <asm/nospec-branch.h>
> +#include <asm/cmdline.h>
> #include <asm/bugs.h>
> #include <asm/processor.h>
> #include <asm/processor-flags.h>
> @@ -21,6 +24,8 @@
> #include <asm/pgtable.h>
> #include <asm/set_memory.h>
>
> +static void __init spectre_v2_select_mitigation(void);
> +
> void __init check_bugs(void)
> {
> identify_boot_cpu();
> @@ -30,6 +35,9 @@ void __init check_bugs(void)
> print_cpu_info(&boot_cpu_data);
> }
>
> + /* Select the proper spectre mitigation before patching alternatives */
> + spectre_v2_select_mitigation();
> +
> #ifdef CONFIG_X86_32
> /*
> * Check whether we are able to run this kernel safely on SMP.
> @@ -62,6 +70,153 @@ void __init check_bugs(void)
> #endif
> }
>
> +/* 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[] = {
> + [SPECTRE_V2_NONE] = "Vulnerable",
> + [SPECTRE_V2_RETPOLINE_MINIMAL] = "Vulnerable: Minimal generic ASM retpoline",
> + [SPECTRE_V2_RETPOLINE_MINIMAL_AMD] = "Vulnerable: Minimal AMD ASM retpoline",
> + [SPECTRE_V2_RETPOLINE_GENERIC] = "Mitigation: Full generic retpoline",
> + [SPECTRE_V2_RETPOLINE_AMD] = "Mitigation: Full AMD retpoline",
> +};
> +
> +#undef pr_fmt
> +#define pr_fmt(fmt) "Spectre V2 mitigation: " fmt
> +
> +static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE;
> +
> +static void __init spec2_print_if_insecure(const char *reason)
> +{
> + if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
> + pr_info("%s\n", reason);
> +}
> +
> +static void __init spec2_print_if_secure(const char *reason)
> +{
> + if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
> + pr_info("%s\n", reason);
> +}
> +
> +static inline bool retp_compiler(void)
> +{
> + return __is_defined(RETPOLINE);
> +}
> +
> +static inline bool match_option(const char *arg, int arglen, const char *opt)
> +{
> + int len = strlen(opt);
> +
> + return len == arglen && !strncmp(arg, opt, len);
> +}
> +
> +static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void)
> +{
> + char arg[20];
> + int ret;
> +
> + ret = cmdline_find_option(boot_command_line, "spectre_v2", arg,
> + sizeof(arg));
> + if (ret > 0) {
> + if (match_option(arg, ret, "off")) {
> + goto disable;
> + } else if (match_option(arg, ret, "on")) {
> + spec2_print_if_secure("force enabled on command line.");
> + return SPECTRE_V2_CMD_FORCE;
> + } else if (match_option(arg, ret, "retpoline")) {
> + spec2_print_if_insecure("retpoline selected on command line.");
> + return SPECTRE_V2_CMD_RETPOLINE;
> + } else if (match_option(arg, ret, "retpoline,amd")) {
> + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
I missed this in the first review, but this should be != X86_VENDOR_AMD.
Sorry about that.
Thanks,
Tom
> + pr_err("retpoline,amd selected but CPU is not AMD. Switching to AUTO select\n");
> + return SPECTRE_V2_CMD_AUTO;
> + }
> + spec2_print_if_insecure("AMD retpoline selected on command line.");
> + return SPECTRE_V2_CMD_RETPOLINE_AMD;
> + } else if (match_option(arg, ret, "retpoline,generic")) {
> + spec2_print_if_insecure("generic retpoline selected on command line.");
> + return SPECTRE_V2_CMD_RETPOLINE_GENERIC;
> + } else if (match_option(arg, ret, "auto")) {
> + return SPECTRE_V2_CMD_AUTO;
> + }
> + }
> +
> + if (!cmdline_find_option_bool(boot_command_line, "nospectre_v2"))
> + return SPECTRE_V2_CMD_AUTO;
> +disable:
> + spec2_print_if_insecure("disabled on command line.");
> + return SPECTRE_V2_CMD_NONE;
> +}
> +
> +static void __init spectre_v2_select_mitigation(void)
> +{
> + enum spectre_v2_mitigation_cmd cmd = spectre_v2_parse_cmdline();
> + enum spectre_v2_mitigation mode = SPECTRE_V2_NONE;
> +
> + /*
> + * 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) &&
> + (cmd == SPECTRE_V2_CMD_NONE || cmd == SPECTRE_V2_CMD_AUTO))
> + return;
> +
> + switch (cmd) {
> + 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:
> + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
> + retpoline_amd:
> + if (!boot_cpu_has(X86_FEATURE_LFENCE_RDTSC)) {
> + pr_err("LFENCE not serializing. Switching to generic retpoline\n");
> + goto retpoline_generic;
> + }
> + 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);
> + } else {
> + retpoline_generic:
> + mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_GENERIC :
> + SPECTRE_V2_RETPOLINE_MINIMAL;
> + setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
> + }
> +
> + spectre_v2_enabled = mode;
> + pr_info("%s\n", spectre_v2_strings[mode]);
> +}
> +
> +#undef pr_fmt
> +
> #ifdef CONFIG_SYSFS
> ssize_t cpu_show_meltdown(struct device *dev,
> struct device_attribute *attr, char *buf)
> @@ -86,6 +241,7 @@ ssize_t cpu_show_spectre_v2(struct device *dev,
> {
> if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
> return sprintf(buf, "Not affected\n");
> - return sprintf(buf, "Vulnerable\n");
> +
> + return sprintf(buf, "%s\n", spectre_v2_strings[spectre_v2_enabled]);
> }
> #endif
> diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
> index 7a671d1..372ba3f 100644
> --- a/arch/x86/kernel/cpu/common.c
> +++ b/arch/x86/kernel/cpu/common.c
> @@ -905,10 +905,6 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
> setup_force_cpu_bug(X86_BUG_SPECTRE_V1);
> setup_force_cpu_bug(X86_BUG_SPECTRE_V2);
>
> -#ifdef CONFIG_RETPOLINE
> - setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
> -#endif
> -
> fpu__init_system(c);
>
> #ifdef CONFIG_X86_32
>