Blame view
arch/s390/kernel/nospec-branch.c
4.68 KB
f19fbd5ed
|
1 2 |
// SPDX-License-Identifier: GPL-2.0 #include <linux/module.h> |
d424986f1
|
3 |
#include <linux/device.h> |
0336e04a6
|
4 |
#include <linux/cpu.h> |
f19fbd5ed
|
5 |
#include <asm/nospec-branch.h> |
b2e2f43a0
|
6 7 8 9 10 11 12 13 |
static int __init nobp_setup_early(char *str) { bool enabled; int rc; rc = kstrtobool(str, &enabled); if (rc) return rc; |
6e179d641
|
14 15 16 17 18 |
if (enabled && test_facility(82)) { /* * The user explicitely requested nobp=1, enable it and * disable the expoline support. */ |
b2e2f43a0
|
19 |
__set_facility(82, S390_lowcore.alt_stfle_fac_list); |
6e179d641
|
20 21 22 |
if (IS_ENABLED(CONFIG_EXPOLINE)) nospec_disable = 1; } else { |
b2e2f43a0
|
23 |
__clear_facility(82, S390_lowcore.alt_stfle_fac_list); |
6e179d641
|
24 |
} |
b2e2f43a0
|
25 26 27 28 29 30 31 32 33 34 |
return 0; } early_param("nobp", nobp_setup_early); static int __init nospec_setup_early(char *str) { __clear_facility(82, S390_lowcore.alt_stfle_fac_list); return 0; } early_param("nospec", nospec_setup_early); |
bc0355997
|
35 36 |
static int __init nospec_report(void) { |
aeaf7002a
|
37 38 39 |
if (test_facility(156)) pr_info("Spectre V2 mitigation: etokens "); |
475c8e9e8
|
40 |
if (__is_defined(CC_USING_EXPOLINE) && !nospec_disable) |
b7e7f5051
|
41 42 |
pr_info("Spectre V2 mitigation: execute trampolines "); |
bc0355997
|
43 |
if (__test_facility(82, S390_lowcore.alt_stfle_fac_list)) |
b7e7f5051
|
44 45 |
pr_info("Spectre V2 mitigation: limited branch prediction "); |
bc0355997
|
46 47 48 |
return 0; } arch_initcall(nospec_report); |
b2e2f43a0
|
49 |
#ifdef CONFIG_EXPOLINE |
6e179d641
|
50 |
int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF); |
f19fbd5ed
|
51 52 53 |
static int __init nospectre_v2_setup_early(char *str) { |
6e179d641
|
54 |
nospec_disable = 1; |
f19fbd5ed
|
55 56 57 |
return 0; } early_param("nospectre_v2", nospectre_v2_setup_early); |
6a3d1e81a
|
58 |
void __init nospec_auto_detect(void) |
6e179d641
|
59 |
{ |
0336e04a6
|
60 |
if (test_facility(156) || cpu_mitigations_off()) { |
aeaf7002a
|
61 62 63 64 |
/* * The machine supports etokens. * Disable expolines and disable nobp. */ |
475c8e9e8
|
65 |
if (__is_defined(CC_USING_EXPOLINE)) |
aeaf7002a
|
66 67 |
nospec_disable = 1; __clear_facility(82, S390_lowcore.alt_stfle_fac_list); |
475c8e9e8
|
68 |
} else if (__is_defined(CC_USING_EXPOLINE)) { |
6e179d641
|
69 70 71 72 73 74 75 76 77 78 79 80 |
/* * 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. */ |
6e179d641
|
81 |
} |
6e179d641
|
82 |
|
f19fbd5ed
|
83 84 85 |
static int __init spectre_v2_setup_early(char *str) { if (str && !strncmp(str, "on", 2)) { |
6e179d641
|
86 87 |
nospec_disable = 0; __clear_facility(82, S390_lowcore.alt_stfle_fac_list); |
f19fbd5ed
|
88 |
} |
6e179d641
|
89 90 91 |
if (str && !strncmp(str, "off", 3)) nospec_disable = 1; if (str && !strncmp(str, "auto", 4)) |
6a3d1e81a
|
92 |
nospec_auto_detect(); |
f19fbd5ed
|
93 94 95 96 97 98 99 100 101 102 103 104 |
return 0; } early_param("spectre_v2", spectre_v2_setup_early); static void __init_or_module __nospec_revert(s32 *start, s32 *end) { enum { BRCL_EXPOLINE, BRASL_EXPOLINE } type; u8 *instr, *thunk, *br; u8 insnbuf[6]; s32 *epo; /* Second part of the instruction replace is always a nop */ |
f19fbd5ed
|
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
for (epo = start; epo < end; epo++) { instr = (u8 *) epo + *epo; if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04) type = BRCL_EXPOLINE; /* brcl instruction */ else if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x05) type = BRASL_EXPOLINE; /* brasl instruction */ else continue; thunk = instr + (*(int *)(instr + 2)) * 2; if (thunk[0] == 0xc6 && thunk[1] == 0x00) /* exrl %r0,<target-br> */ br = thunk + (*(int *)(thunk + 2)) * 2; else if (thunk[0] == 0xc0 && (thunk[1] & 0x0f) == 0x00 && thunk[6] == 0x44 && thunk[7] == 0x00 && (thunk[8] & 0x0f) == 0x00 && thunk[9] == 0x00 && (thunk[1] & 0xf0) == (thunk[8] & 0xf0)) /* larl %rx,<target br> + ex %r0,0(%rx) */ br = thunk + (*(int *)(thunk + 2)) * 2; else continue; |
6deaa3bbc
|
125 126 |
/* Check for unconditional branch 0x07f? or 0x47f???? */ if ((br[0] & 0xbf) != 0x07 || (br[1] & 0xf0) != 0xf0) |
f19fbd5ed
|
127 |
continue; |
6deaa3bbc
|
128 129 |
memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x07, 0x00 }, 4); |
f19fbd5ed
|
130 131 |
switch (type) { case BRCL_EXPOLINE: |
f19fbd5ed
|
132 133 |
insnbuf[0] = br[0]; insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f); |
6deaa3bbc
|
134 135 136 137 138 139 140 |
if (br[0] == 0x47) { /* brcl to b, replace with bc + nopr */ insnbuf[2] = br[2]; insnbuf[3] = br[3]; } else { /* brcl to br, replace with bcr + nop */ } |
f19fbd5ed
|
141 142 |
break; case BRASL_EXPOLINE: |
f19fbd5ed
|
143 |
insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f); |
6deaa3bbc
|
144 145 146 147 148 149 150 151 152 |
if (br[0] == 0x47) { /* brasl to b, replace with bas + nopr */ insnbuf[0] = 0x4d; insnbuf[2] = br[2]; insnbuf[3] = br[3]; } else { /* brasl to br, replace with basr + nop */ insnbuf[0] = 0x0d; } |
f19fbd5ed
|
153 154 155 156 157 158 |
break; } s390_kernel_write(instr, insnbuf, 6); } } |
6e179d641
|
159 |
void __init_or_module nospec_revert(s32 *start, s32 *end) |
f19fbd5ed
|
160 |
{ |
6e179d641
|
161 |
if (nospec_disable) |
f19fbd5ed
|
162 163 164 165 166 167 168 |
__nospec_revert(start, end); } extern s32 __nospec_call_start[], __nospec_call_end[]; extern s32 __nospec_return_start[], __nospec_return_end[]; void __init nospec_init_branches(void) { |
6e179d641
|
169 170 |
nospec_revert(__nospec_call_start, __nospec_call_end); nospec_revert(__nospec_return_start, __nospec_return_end); |
f19fbd5ed
|
171 |
} |
b2e2f43a0
|
172 173 |
#endif /* CONFIG_EXPOLINE */ |