Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/s390/kernel/nospec-branch.c
38184 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include <linux/module.h>
3
#include <linux/device.h>
4
#include <linux/cpu.h>
5
#include <asm/nospec-branch.h>
6
7
int nobp = IS_ENABLED(CONFIG_KERNEL_NOBP);
8
9
static int __init nobp_setup_early(char *str)
10
{
11
bool enabled;
12
int rc;
13
14
rc = kstrtobool(str, &enabled);
15
if (rc)
16
return rc;
17
if (enabled && test_facility(82)) {
18
/*
19
* The user explicitly requested nobp=1, enable it and
20
* disable the expoline support.
21
*/
22
nobp = 1;
23
if (IS_ENABLED(CONFIG_EXPOLINE))
24
nospec_disable = 1;
25
} else {
26
nobp = 0;
27
}
28
return 0;
29
}
30
early_param("nobp", nobp_setup_early);
31
32
static int __init nospec_setup_early(char *str)
33
{
34
nobp = 0;
35
return 0;
36
}
37
early_param("nospec", nospec_setup_early);
38
39
static int __init nospec_report(void)
40
{
41
if (test_facility(156))
42
pr_info("Spectre V2 mitigation: etokens\n");
43
if (nospec_uses_trampoline())
44
pr_info("Spectre V2 mitigation: execute trampolines\n");
45
if (nobp_enabled())
46
pr_info("Spectre V2 mitigation: limited branch prediction\n");
47
return 0;
48
}
49
arch_initcall(nospec_report);
50
51
#ifdef CONFIG_EXPOLINE
52
53
int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
54
55
static int __init nospectre_v2_setup_early(char *str)
56
{
57
nospec_disable = 1;
58
return 0;
59
}
60
early_param("nospectre_v2", nospectre_v2_setup_early);
61
62
void __init nospec_auto_detect(void)
63
{
64
if (test_facility(156) || cpu_mitigations_off()) {
65
/*
66
* The machine supports etokens.
67
* Disable expolines and disable nobp.
68
*/
69
if (__is_defined(CC_USING_EXPOLINE))
70
nospec_disable = 1;
71
nobp = 0;
72
} else if (__is_defined(CC_USING_EXPOLINE)) {
73
/*
74
* The kernel has been compiled with expolines.
75
* Keep expolines enabled and disable nobp.
76
*/
77
nospec_disable = 0;
78
nobp = 0;
79
}
80
/*
81
* If the kernel has not been compiled with expolines the
82
* nobp setting decides what is done, this depends on the
83
* CONFIG_KERNEL_NP option and the nobp/nospec parameters.
84
*/
85
}
86
87
static int __init spectre_v2_setup_early(char *str)
88
{
89
if (str && !strncmp(str, "on", 2)) {
90
nospec_disable = 0;
91
nobp = 0;
92
}
93
if (str && !strncmp(str, "off", 3))
94
nospec_disable = 1;
95
if (str && !strncmp(str, "auto", 4))
96
nospec_auto_detect();
97
return 0;
98
}
99
early_param("spectre_v2", spectre_v2_setup_early);
100
101
static void __init_or_module __nospec_revert(s32 *start, s32 *end)
102
{
103
enum { BRCL_EXPOLINE, BRASL_EXPOLINE } type;
104
static const u8 branch[] = { 0x47, 0x00, 0x07, 0x00 };
105
u8 *instr, *thunk, *br;
106
u8 insnbuf[6];
107
s32 *epo;
108
109
/* Second part of the instruction replace is always a nop */
110
memcpy(insnbuf + 2, branch, sizeof(branch));
111
for (epo = start; epo < end; epo++) {
112
instr = (u8 *) epo + *epo;
113
if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04)
114
type = BRCL_EXPOLINE; /* brcl instruction */
115
else if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x05)
116
type = BRASL_EXPOLINE; /* brasl instruction */
117
else
118
continue;
119
thunk = instr + (long)(*(int *)(instr + 2)) * 2;
120
if (thunk[0] == 0xc6 && thunk[1] == 0x00)
121
/* exrl %r0,<target-br> */
122
br = thunk + (long)(*(int *)(thunk + 2)) * 2;
123
else
124
continue;
125
if (br[0] != 0x07 || (br[1] & 0xf0) != 0xf0)
126
continue;
127
switch (type) {
128
case BRCL_EXPOLINE:
129
/* brcl to thunk, replace with br + nop */
130
insnbuf[0] = br[0];
131
insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
132
break;
133
case BRASL_EXPOLINE:
134
/* brasl to thunk, replace with basr + nop */
135
insnbuf[0] = 0x0d;
136
insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
137
break;
138
}
139
140
s390_kernel_write(instr, insnbuf, 6);
141
}
142
}
143
144
void __init_or_module nospec_revert(s32 *start, s32 *end)
145
{
146
if (nospec_disable)
147
__nospec_revert(start, end);
148
}
149
150
extern s32 __nospec_call_start[], __nospec_call_end[];
151
extern s32 __nospec_return_start[], __nospec_return_end[];
152
void __init nospec_init_branches(void)
153
{
154
nospec_revert(__nospec_call_start, __nospec_call_end);
155
nospec_revert(__nospec_return_start, __nospec_return_end);
156
}
157
158
#endif /* CONFIG_EXPOLINE */
159
160