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
|
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
|
||||||
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 @@
|
||||||
|
|
||||||
#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 @@
|
||||||
#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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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)
|
||||||
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);
|
||||||
|
|
||||||
#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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
if (nospec_disable)
|
||||||
__nospec_revert(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __init_or_module nospec_return_revert(s32 *start, s32 *end)
|
|
||||||
{
|
|
||||||
if (nospec_return_disable)
|
|
||||||
__nospec_revert(start, end);
|
__nospec_revert(start, 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 */
|
||||||
|
|
Loading…
Reference in New Issue