s390: add automatic detection of the spectre defense
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 27 Apr 2018 05:36:39 +0000 (07:36 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 29 Apr 2018 09:33:15 +0000 (11:33 +0200)
[ Upstream commit 6e179d64126b909f0b288fa63cdbf07c531e9b1d ]

Automatically decide between nobp vs. expolines if the spectre_v2=auto
kernel parameter is specified or CONFIG_EXPOLINE_AUTO=y is set.

The decision made at boot time due to CONFIG_EXPOLINE_AUTO=y being set
can be overruled with the nobp, nospec and spectre_v2 kernel parameters.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/s390/Kconfig
arch/s390/Makefile
arch/s390/include/asm/nospec-branch.h
arch/s390/kernel/alternative.c
arch/s390/kernel/module.c
arch/s390/kernel/nospec-branch.c

index 39cd1ed..7a238d4 100644 (file)
@@ -575,7 +575,7 @@ choice
 config EXPOLINE_OFF
        bool "spectre_v2=off"
 
-config EXPOLINE_MEDIUM
+config EXPOLINE_AUTO
        bool "spectre_v2=auto"
 
 config EXPOLINE_FULL
index 98f50b2..ec3fa10 100644 (file)
@@ -87,7 +87,7 @@ ifdef CONFIG_EXPOLINE
     CC_FLAGS_EXPOLINE += -mfunction-return=thunk
     CC_FLAGS_EXPOLINE += -mindirect-branch-table
     export CC_FLAGS_EXPOLINE
-    cflags-y += $(CC_FLAGS_EXPOLINE)
+    cflags-y += $(CC_FLAGS_EXPOLINE) -DCC_USING_EXPOLINE
   endif
 endif
 
index 7df48e5..35bf28f 100644 (file)
@@ -6,12 +6,10 @@
 
 #include <linux/types.h>
 
-extern int nospec_call_disable;
-extern int nospec_return_disable;
+extern int nospec_disable;
 
 void nospec_init_branches(void);
-void nospec_call_revert(s32 *start, s32 *end);
-void nospec_return_revert(s32 *start, s32 *end);
+void nospec_revert(s32 *start, s32 *end);
 
 #endif /* __ASSEMBLY__ */
 
index f5060af..b57b293 100644 (file)
@@ -1,6 +1,7 @@
 #include <linux/module.h>
 #include <asm/alternative.h>
 #include <asm/facility.h>
+#include <asm/nospec-branch.h>
 
 #define MAX_PATCH_LEN (255 - 1)
 
index 89d7ab7..7b337f8 100644 (file)
@@ -172,7 +172,7 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
        me->core_layout.size += me->arch.got_size;
        me->arch.plt_offset = me->core_layout.size;
        if (me->arch.plt_size) {
-               if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_call_disable)
+               if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable)
                        me->arch.plt_size += PLT_ENTRY_SIZE;
                me->core_layout.size += me->arch.plt_size;
        }
@@ -331,8 +331,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
                                info->plt_offset;
                        ip[0] = 0x0d10e310;     /* basr 1,0  */
                        ip[1] = 0x100a0004;     /* lg   1,10(1) */
-                       if (IS_ENABLED(CONFIG_EXPOLINE) &&
-                           !nospec_call_disable) {
+                       if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable) {
                                unsigned int *ij;
                                ij = me->core_layout.base +
                                        me->arch.plt_offset +
@@ -453,7 +452,7 @@ int module_finalize(const Elf_Ehdr *hdr,
        void *aseg;
 
        if (IS_ENABLED(CONFIG_EXPOLINE) &&
-           !nospec_call_disable && me->arch.plt_size) {
+           !nospec_disable && me->arch.plt_size) {
                unsigned int *ij;
 
                ij = me->core_layout.base + me->arch.plt_offset +
@@ -480,11 +479,11 @@ int module_finalize(const Elf_Ehdr *hdr,
 
                if (IS_ENABLED(CONFIG_EXPOLINE) &&
                    (!strcmp(".nospec_call_table", secname)))
-                       nospec_call_revert(aseg, aseg + s->sh_size);
+                       nospec_revert(aseg, aseg + s->sh_size);
 
                if (IS_ENABLED(CONFIG_EXPOLINE) &&
                    (!strcmp(".nospec_return_table", secname)))
-                       nospec_return_revert(aseg, aseg + s->sh_size);
+                       nospec_revert(aseg, aseg + s->sh_size);
        }
 
        jump_label_apply_nops(me);
index 3ce59d0..daa36a3 100644 (file)
@@ -11,10 +11,17 @@ static int __init nobp_setup_early(char *str)
        rc = kstrtobool(str, &enabled);
        if (rc)
                return rc;
-       if (enabled && test_facility(82))
+       if (enabled && test_facility(82)) {
+               /*
+                * The user explicitely requested nobp=1, enable it and
+                * disable the expoline support.
+                */
                __set_facility(82, S390_lowcore.alt_stfle_fac_list);
-       else
+               if (IS_ENABLED(CONFIG_EXPOLINE))
+                       nospec_disable = 1;
+       } else {
                __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+       }
        return 0;
 }
 early_param("nobp", nobp_setup_early);
@@ -28,31 +35,46 @@ early_param("nospec", nospec_setup_early);
 
 #ifdef CONFIG_EXPOLINE
 
-int nospec_call_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
-int nospec_return_disable = !IS_ENABLED(CONFIG_EXPOLINE_FULL);
+int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
 
 static int __init nospectre_v2_setup_early(char *str)
 {
-       nospec_call_disable = 1;
-       nospec_return_disable = 1;
+       nospec_disable = 1;
        return 0;
 }
 early_param("nospectre_v2", nospectre_v2_setup_early);
 
+static int __init spectre_v2_auto_early(void)
+{
+       if (IS_ENABLED(CC_USING_EXPOLINE)) {
+               /*
+                * The kernel has been compiled with expolines.
+                * Keep expolines enabled and disable nobp.
+                */
+               nospec_disable = 0;
+               __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+       }
+       /*
+        * If the kernel has not been compiled with expolines the
+        * nobp setting decides what is done, this depends on the
+        * CONFIG_KERNEL_NP option and the nobp/nospec parameters.
+        */
+       return 0;
+}
+#ifdef CONFIG_EXPOLINE_AUTO
+early_initcall(spectre_v2_auto_early);
+#endif
+
 static int __init spectre_v2_setup_early(char *str)
 {
        if (str && !strncmp(str, "on", 2)) {
-               nospec_call_disable = 0;
-               nospec_return_disable = 0;
-       }
-       if (str && !strncmp(str, "off", 3)) {
-               nospec_call_disable = 1;
-               nospec_return_disable = 1;
-       }
-       if (str && !strncmp(str, "auto", 4)) {
-               nospec_call_disable = 0;
-               nospec_return_disable = 1;
+               nospec_disable = 0;
+               __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
        }
+       if (str && !strncmp(str, "off", 3))
+               nospec_disable = 1;
+       if (str && !strncmp(str, "auto", 4))
+               spectre_v2_auto_early();
        return 0;
 }
 early_param("spectre_v2", spectre_v2_setup_early);
@@ -105,15 +127,9 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end)
        }
 }
 
-void __init_or_module nospec_call_revert(s32 *start, s32 *end)
-{
-       if (nospec_call_disable)
-               __nospec_revert(start, end);
-}
-
-void __init_or_module nospec_return_revert(s32 *start, s32 *end)
+void __init_or_module nospec_revert(s32 *start, s32 *end)
 {
-       if (nospec_return_disable)
+       if (nospec_disable)
                __nospec_revert(start, end);
 }
 
@@ -121,8 +137,8 @@ extern s32 __nospec_call_start[], __nospec_call_end[];
 extern s32 __nospec_return_start[], __nospec_return_end[];
 void __init nospec_init_branches(void)
 {
-       nospec_call_revert(__nospec_call_start, __nospec_call_end);
-       nospec_return_revert(__nospec_return_start, __nospec_return_end);
+       nospec_revert(__nospec_call_start, __nospec_call_end);
+       nospec_revert(__nospec_return_start, __nospec_return_end);
 }
 
 #endif /* CONFIG_EXPOLINE */