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: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
b2e2f43a01
commit
6e179d6412
|
@ -576,7 +576,7 @@ choice
|
|||
config EXPOLINE_OFF
|
||||
bool "spectre_v2=off"
|
||||
|
||||
config EXPOLINE_MEDIUM
|
||||
config EXPOLINE_AUTO
|
||||
bool "spectre_v2=auto"
|
||||
|
||||
config EXPOLINE_FULL
|
||||
|
|
|
@ -81,7 +81,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
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/facility.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
|
||||
#define MAX_PATCH_LEN (255 - 1)
|
||||
|
||||
|
|
|
@ -159,7 +159,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;
|
||||
}
|
||||
|
@ -318,8 +318,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 +
|
||||
|
@ -440,7 +439,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 +
|
||||
|
@ -467,11 +466,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);
|
||||
|
|
|
@ -10,10 +10,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);
|
||||
|
@ -27,31 +34,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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -120,8 +136,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 */
|
||||
|
|
Loading…
Reference in New Issue