[PATCH 4.4 23/50] s390: add automatic detection of the spectre defense

From: Greg Kroah-Hartman
Date: Fri Apr 27 2018 - 10:01:21 EST


4.4-stable review patch. If anyone has any objections, please let me know.

------------------

From: Martin Schwidefsky <schwidefsky@xxxxxxxxxx>


[ 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@xxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
arch/s390/Kconfig | 2 -
arch/s390/Makefile | 2 -
arch/s390/include/asm/nospec-branch.h | 6 +--
arch/s390/kernel/alternative.c | 1
arch/s390/kernel/module.c | 11 ++---
arch/s390/kernel/nospec-branch.c | 68 +++++++++++++++++++++-------------
6 files changed, 52 insertions(+), 38 deletions(-)

--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -742,7 +742,7 @@ choice
config EXPOLINE_OFF
bool "spectre_v2=off"

-config EXPOLINE_MEDIUM
+config EXPOLINE_AUTO
bool "spectre_v2=auto"

config EXPOLINE_FULL
--- a/arch/s390/Makefile
+++ b/arch/s390/Makefile
@@ -83,7 +83,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

--- a/arch/s390/include/asm/nospec-branch.h
+++ b/arch/s390/include/asm/nospec-branch.h
@@ -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__ */

--- a/arch/s390/kernel/alternative.c
+++ b/arch/s390/kernel/alternative.c
@@ -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)

--- a/arch/s390/kernel/module.c
+++ b/arch/s390/kernel/module.c
@@ -167,7 +167,7 @@ int module_frob_arch_sections(Elf_Ehdr *
me->core_size += me->arch.got_size;
me->arch.plt_offset = me->core_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_size += me->arch.plt_size;
}
@@ -326,8 +326,7 @@ static int apply_rela(Elf_Rela *rela, El
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->module_core +
me->arch.plt_offset +
@@ -448,7 +447,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->module_core + me->arch.plt_offset +
@@ -475,11 +474,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);
--- a/arch/s390/kernel/nospec-branch.c
+++ b/arch/s390/kernel/nospec-branch.c
@@ -11,10 +11,17 @@ static int __init nobp_setup_early(char
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_re
}
}

-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[], __nosp
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 */