Commit 6e179d64 authored by Martin Schwidefsky's avatar Martin Schwidefsky

s390: add automatic detection of the spectre defense

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: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent b2e2f43a
...@@ -576,7 +576,7 @@ choice ...@@ -576,7 +576,7 @@ choice
config EXPOLINE_OFF config EXPOLINE_OFF
bool "spectre_v2=off" bool "spectre_v2=off"
config EXPOLINE_MEDIUM config EXPOLINE_AUTO
bool "spectre_v2=auto" bool "spectre_v2=auto"
config EXPOLINE_FULL config EXPOLINE_FULL
......
...@@ -81,7 +81,7 @@ ifdef CONFIG_EXPOLINE ...@@ -81,7 +81,7 @@ ifdef CONFIG_EXPOLINE
CC_FLAGS_EXPOLINE += -mfunction-return=thunk CC_FLAGS_EXPOLINE += -mfunction-return=thunk
CC_FLAGS_EXPOLINE += -mindirect-branch-table CC_FLAGS_EXPOLINE += -mindirect-branch-table
export CC_FLAGS_EXPOLINE export CC_FLAGS_EXPOLINE
cflags-y += $(CC_FLAGS_EXPOLINE) cflags-y += $(CC_FLAGS_EXPOLINE) -DCC_USING_EXPOLINE
endif endif
endif endif
......
...@@ -6,12 +6,10 @@ ...@@ -6,12 +6,10 @@
#include <linux/types.h> #include <linux/types.h>
extern int nospec_call_disable; extern int nospec_disable;
extern int nospec_return_disable;
void nospec_init_branches(void); void nospec_init_branches(void);
void nospec_call_revert(s32 *start, s32 *end); void nospec_revert(s32 *start, s32 *end);
void nospec_return_revert(s32 *start, s32 *end);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <asm/alternative.h> #include <asm/alternative.h>
#include <asm/facility.h> #include <asm/facility.h>
#include <asm/nospec-branch.h>
#define MAX_PATCH_LEN (255 - 1) #define MAX_PATCH_LEN (255 - 1)
......
...@@ -159,7 +159,7 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, ...@@ -159,7 +159,7 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
me->core_layout.size += me->arch.got_size; me->core_layout.size += me->arch.got_size;
me->arch.plt_offset = me->core_layout.size; me->arch.plt_offset = me->core_layout.size;
if (me->arch.plt_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->arch.plt_size += PLT_ENTRY_SIZE;
me->core_layout.size += me->arch.plt_size; me->core_layout.size += me->arch.plt_size;
} }
...@@ -318,8 +318,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, ...@@ -318,8 +318,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
info->plt_offset; info->plt_offset;
ip[0] = 0x0d10e310; /* basr 1,0 */ ip[0] = 0x0d10e310; /* basr 1,0 */
ip[1] = 0x100a0004; /* lg 1,10(1) */ ip[1] = 0x100a0004; /* lg 1,10(1) */
if (IS_ENABLED(CONFIG_EXPOLINE) && if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable) {
!nospec_call_disable) {
unsigned int *ij; unsigned int *ij;
ij = me->core_layout.base + ij = me->core_layout.base +
me->arch.plt_offset + me->arch.plt_offset +
...@@ -440,7 +439,7 @@ int module_finalize(const Elf_Ehdr *hdr, ...@@ -440,7 +439,7 @@ int module_finalize(const Elf_Ehdr *hdr,
void *aseg; void *aseg;
if (IS_ENABLED(CONFIG_EXPOLINE) && if (IS_ENABLED(CONFIG_EXPOLINE) &&
!nospec_call_disable && me->arch.plt_size) { !nospec_disable && me->arch.plt_size) {
unsigned int *ij; unsigned int *ij;
ij = me->core_layout.base + me->arch.plt_offset + ij = me->core_layout.base + me->arch.plt_offset +
...@@ -467,11 +466,11 @@ int module_finalize(const Elf_Ehdr *hdr, ...@@ -467,11 +466,11 @@ int module_finalize(const Elf_Ehdr *hdr,
if (IS_ENABLED(CONFIG_EXPOLINE) && if (IS_ENABLED(CONFIG_EXPOLINE) &&
(!strcmp(".nospec_call_table", secname))) (!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) && if (IS_ENABLED(CONFIG_EXPOLINE) &&
(!strcmp(".nospec_return_table", secname))) (!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); jump_label_apply_nops(me);
......
...@@ -10,10 +10,17 @@ static int __init nobp_setup_early(char *str) ...@@ -10,10 +10,17 @@ static int __init nobp_setup_early(char *str)
rc = kstrtobool(str, &enabled); rc = kstrtobool(str, &enabled);
if (rc) if (rc)
return 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); __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); __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
}
return 0; return 0;
} }
early_param("nobp", nobp_setup_early); early_param("nobp", nobp_setup_early);
...@@ -27,31 +34,46 @@ early_param("nospec", nospec_setup_early); ...@@ -27,31 +34,46 @@ early_param("nospec", nospec_setup_early);
#ifdef CONFIG_EXPOLINE #ifdef CONFIG_EXPOLINE
int nospec_call_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF); int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
int nospec_return_disable = !IS_ENABLED(CONFIG_EXPOLINE_FULL);
static int __init nospectre_v2_setup_early(char *str) static int __init nospectre_v2_setup_early(char *str)
{ {
nospec_call_disable = 1; nospec_disable = 1;
nospec_return_disable = 1;
return 0; return 0;
} }
early_param("nospectre_v2", nospectre_v2_setup_early); 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) static int __init spectre_v2_setup_early(char *str)
{ {
if (str && !strncmp(str, "on", 2)) { if (str && !strncmp(str, "on", 2)) {
nospec_call_disable = 0; nospec_disable = 0;
nospec_return_disable = 0; __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
}
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;
} }
if (str && !strncmp(str, "off", 3))
nospec_disable = 1;
if (str && !strncmp(str, "auto", 4))
spectre_v2_auto_early();
return 0; return 0;
} }
early_param("spectre_v2", spectre_v2_setup_early); early_param("spectre_v2", spectre_v2_setup_early);
...@@ -104,15 +126,9 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end) ...@@ -104,15 +126,9 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end)
} }
} }
void __init_or_module nospec_call_revert(s32 *start, s32 *end) void __init_or_module nospec_revert(s32 *start, s32 *end)
{
if (nospec_call_disable)
__nospec_revert(start, end);
}
void __init_or_module nospec_return_revert(s32 *start, s32 *end)
{ {
if (nospec_return_disable) if (nospec_disable)
__nospec_revert(start, end); __nospec_revert(start, end);
} }
...@@ -120,8 +136,8 @@ extern s32 __nospec_call_start[], __nospec_call_end[]; ...@@ -120,8 +136,8 @@ extern s32 __nospec_call_start[], __nospec_call_end[];
extern s32 __nospec_return_start[], __nospec_return_end[]; extern s32 __nospec_return_start[], __nospec_return_end[];
void __init nospec_init_branches(void) void __init nospec_init_branches(void)
{ {
nospec_call_revert(__nospec_call_start, __nospec_call_end); nospec_revert(__nospec_call_start, __nospec_call_end);
nospec_return_revert(__nospec_return_start, __nospec_return_end); nospec_revert(__nospec_return_start, __nospec_return_end);
} }
#endif /* CONFIG_EXPOLINE */ #endif /* CONFIG_EXPOLINE */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment