]>
Commit | Line | Data |
---|---|---|
6e61041f ME |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // | |
3 | // Security related flags and so on. | |
4 | // | |
5 | // Copyright 2018, Michael Ellerman, IBM Corporation. | |
6 | ||
7 | #include <linux/kernel.h> | |
ffd22047 | 8 | #include <linux/device.h> |
a11b7f41 | 9 | #include <linux/seq_buf.h> |
ffd22047 | 10 | |
4dcfe4cc ME |
11 | #include <asm/asm-prototypes.h> |
12 | #include <asm/code-patching.h> | |
cc437c1d | 13 | #include <asm/debugfs.h> |
6e61041f | 14 | #include <asm/security_features.h> |
113a8b0f | 15 | #include <asm/setup.h> |
6e61041f ME |
16 | |
17 | ||
35cfd685 | 18 | unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT; |
ffd22047 | 19 | |
4dcfe4cc ME |
20 | enum count_cache_flush_type { |
21 | COUNT_CACHE_FLUSH_NONE = 0x1, | |
22 | COUNT_CACHE_FLUSH_SW = 0x2, | |
23 | COUNT_CACHE_FLUSH_HW = 0x4, | |
24 | }; | |
edbc8162 | 25 | static enum count_cache_flush_type count_cache_flush_type = COUNT_CACHE_FLUSH_NONE; |
4dcfe4cc | 26 | |
80dde6e3 | 27 | bool barrier_nospec_enabled; |
9b16be66 | 28 | static bool no_nospec; |
113a8b0f MS |
29 | |
30 | static void enable_barrier_nospec(bool enable) | |
31 | { | |
32 | barrier_nospec_enabled = enable; | |
33 | do_barrier_nospec_fixups(enable); | |
34 | } | |
35 | ||
b6c277fa MS |
36 | void setup_barrier_nospec(void) |
37 | { | |
38 | bool enable; | |
39 | ||
40 | /* | |
41 | * It would make sense to check SEC_FTR_SPEC_BAR_ORI31 below as well. | |
42 | * But there's a good reason not to. The two flags we check below are | |
43 | * both are enabled by default in the kernel, so if the hcall is not | |
44 | * functional they will be enabled. | |
45 | * On a system where the host firmware has been updated (so the ori | |
46 | * functions as a barrier), but on which the hypervisor (KVM/Qemu) has | |
47 | * not been updated, we would like to enable the barrier. Dropping the | |
48 | * check for SEC_FTR_SPEC_BAR_ORI31 achieves that. The only downside is | |
49 | * we potentially enable the barrier on systems where the host firmware | |
50 | * is not updated, but that's harmless as it's a no-op. | |
51 | */ | |
52 | enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && | |
53 | security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR); | |
54 | ||
9b16be66 DC |
55 | if (!no_nospec) |
56 | enable_barrier_nospec(enable); | |
b6c277fa MS |
57 | } |
58 | ||
9b16be66 DC |
59 | static int __init handle_nospectre_v1(char *p) |
60 | { | |
61 | no_nospec = true; | |
62 | ||
63 | return 0; | |
64 | } | |
65 | early_param("nospectre_v1", handle_nospectre_v1); | |
66 | ||
b6c277fa MS |
67 | #ifdef CONFIG_DEBUG_FS |
68 | static int barrier_nospec_set(void *data, u64 val) | |
69 | { | |
70 | switch (val) { | |
71 | case 0: | |
72 | case 1: | |
73 | break; | |
74 | default: | |
75 | return -EINVAL; | |
76 | } | |
77 | ||
78 | if (!!val == !!barrier_nospec_enabled) | |
79 | return 0; | |
80 | ||
81 | enable_barrier_nospec(!!val); | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | static int barrier_nospec_get(void *data, u64 *val) | |
87 | { | |
88 | *val = barrier_nospec_enabled ? 1 : 0; | |
89 | return 0; | |
90 | } | |
91 | ||
92 | DEFINE_SIMPLE_ATTRIBUTE(fops_barrier_nospec, | |
93 | barrier_nospec_get, barrier_nospec_set, "%llu\n"); | |
94 | ||
95 | static __init int barrier_nospec_debugfs_init(void) | |
96 | { | |
97 | debugfs_create_file("barrier_nospec", 0600, powerpc_debugfs_root, NULL, | |
98 | &fops_barrier_nospec); | |
99 | return 0; | |
100 | } | |
101 | device_initcall(barrier_nospec_debugfs_init); | |
102 | #endif /* CONFIG_DEBUG_FS */ | |
103 | ||
e2a06bca | 104 | #ifdef CONFIG_PPC_BOOK3S_64 |
ffd22047 ME |
105 | ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf) |
106 | { | |
a11b7f41 ME |
107 | bool thread_priv; |
108 | ||
109 | thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV); | |
110 | ||
111 | if (rfi_flush || thread_priv) { | |
112 | struct seq_buf s; | |
113 | seq_buf_init(&s, buf, PAGE_SIZE - 1); | |
114 | ||
115 | seq_buf_printf(&s, "Mitigation: "); | |
116 | ||
117 | if (rfi_flush) | |
118 | seq_buf_printf(&s, "RFI Flush"); | |
119 | ||
120 | if (rfi_flush && thread_priv) | |
121 | seq_buf_printf(&s, ", "); | |
122 | ||
123 | if (thread_priv) | |
124 | seq_buf_printf(&s, "L1D private per thread"); | |
125 | ||
126 | seq_buf_printf(&s, "\n"); | |
127 | ||
128 | return s.len; | |
129 | } | |
130 | ||
131 | if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && | |
132 | !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) | |
133 | return sprintf(buf, "Not affected\n"); | |
ffd22047 ME |
134 | |
135 | return sprintf(buf, "Vulnerable\n"); | |
136 | } | |
e2a06bca | 137 | #endif |
24d4db64 ME |
138 | |
139 | ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf) | |
140 | { | |
361da9a6 ME |
141 | struct seq_buf s; |
142 | ||
143 | seq_buf_init(&s, buf, PAGE_SIZE - 1); | |
24d4db64 | 144 | |
361da9a6 ME |
145 | if (security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) { |
146 | if (barrier_nospec_enabled) | |
147 | seq_buf_printf(&s, "Mitigation: __user pointer sanitization"); | |
148 | else | |
149 | seq_buf_printf(&s, "Vulnerable"); | |
eab92780 | 150 | |
361da9a6 ME |
151 | if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31)) |
152 | seq_buf_printf(&s, ", ori31 speculation barrier enabled"); | |
153 | ||
154 | seq_buf_printf(&s, "\n"); | |
155 | } else | |
156 | seq_buf_printf(&s, "Not affected\n"); | |
157 | ||
158 | return s.len; | |
24d4db64 | 159 | } |
796de0db ME |
160 | |
161 | ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf) | |
162 | { | |
796de0db | 163 | struct seq_buf s; |
361da9a6 | 164 | bool bcs, ccd; |
796de0db ME |
165 | |
166 | seq_buf_init(&s, buf, PAGE_SIZE - 1); | |
167 | ||
168 | bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED); | |
169 | ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED); | |
796de0db | 170 | |
4dcfe4cc ME |
171 | if (bcs || ccd || count_cache_flush_type != COUNT_CACHE_FLUSH_NONE) { |
172 | bool comma = false; | |
796de0db ME |
173 | seq_buf_printf(&s, "Mitigation: "); |
174 | ||
4dcfe4cc | 175 | if (bcs) { |
796de0db | 176 | seq_buf_printf(&s, "Indirect branch serialisation (kernel only)"); |
4dcfe4cc ME |
177 | comma = true; |
178 | } | |
179 | ||
180 | if (ccd) { | |
181 | if (comma) | |
182 | seq_buf_printf(&s, ", "); | |
183 | seq_buf_printf(&s, "Indirect branch cache disabled"); | |
184 | comma = true; | |
185 | } | |
796de0db | 186 | |
4dcfe4cc | 187 | if (comma) |
796de0db ME |
188 | seq_buf_printf(&s, ", "); |
189 | ||
4dcfe4cc ME |
190 | seq_buf_printf(&s, "Software count cache flush"); |
191 | ||
192 | if (count_cache_flush_type == COUNT_CACHE_FLUSH_HW) | |
193 | seq_buf_printf(&s, "(hardware accelerated)"); | |
796de0db ME |
194 | } else |
195 | seq_buf_printf(&s, "Vulnerable"); | |
196 | ||
796de0db ME |
197 | seq_buf_printf(&s, "\n"); |
198 | ||
199 | return s.len; | |
200 | } | |
cc437c1d | 201 | |
32bc83b9 | 202 | #ifdef CONFIG_PPC_BOOK3S_64 |
cc437c1d NP |
203 | /* |
204 | * Store-forwarding barrier support. | |
205 | */ | |
206 | ||
207 | static enum stf_barrier_type stf_enabled_flush_types; | |
208 | static bool no_stf_barrier; | |
209 | bool stf_barrier; | |
210 | ||
211 | static int __init handle_no_stf_barrier(char *p) | |
212 | { | |
213 | pr_info("stf-barrier: disabled on command line."); | |
214 | no_stf_barrier = true; | |
215 | return 0; | |
216 | } | |
217 | ||
218 | early_param("no_stf_barrier", handle_no_stf_barrier); | |
219 | ||
220 | /* This is the generic flag used by other architectures */ | |
221 | static int __init handle_ssbd(char *p) | |
222 | { | |
223 | if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) { | |
224 | /* Until firmware tells us, we have the barrier with auto */ | |
225 | return 0; | |
226 | } else if (strncmp(p, "off", 3) == 0) { | |
227 | handle_no_stf_barrier(NULL); | |
228 | return 0; | |
229 | } else | |
230 | return 1; | |
231 | ||
232 | return 0; | |
233 | } | |
234 | early_param("spec_store_bypass_disable", handle_ssbd); | |
235 | ||
236 | /* This is the generic flag used by other architectures */ | |
237 | static int __init handle_no_ssbd(char *p) | |
238 | { | |
239 | handle_no_stf_barrier(NULL); | |
240 | return 0; | |
241 | } | |
242 | early_param("nospec_store_bypass_disable", handle_no_ssbd); | |
243 | ||
244 | static void stf_barrier_enable(bool enable) | |
245 | { | |
246 | if (enable) | |
247 | do_stf_barrier_fixups(stf_enabled_flush_types); | |
248 | else | |
249 | do_stf_barrier_fixups(STF_BARRIER_NONE); | |
250 | ||
251 | stf_barrier = enable; | |
252 | } | |
253 | ||
254 | void setup_stf_barrier(void) | |
255 | { | |
256 | enum stf_barrier_type type; | |
257 | bool enable, hv; | |
258 | ||
259 | hv = cpu_has_feature(CPU_FTR_HVMODE); | |
260 | ||
261 | /* Default to fallback in case fw-features are not available */ | |
262 | if (cpu_has_feature(CPU_FTR_ARCH_300)) | |
263 | type = STF_BARRIER_EIEIO; | |
264 | else if (cpu_has_feature(CPU_FTR_ARCH_207S)) | |
265 | type = STF_BARRIER_SYNC_ORI; | |
266 | else if (cpu_has_feature(CPU_FTR_ARCH_206)) | |
267 | type = STF_BARRIER_FALLBACK; | |
268 | else | |
269 | type = STF_BARRIER_NONE; | |
270 | ||
271 | enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && | |
272 | (security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) || | |
273 | (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv)); | |
274 | ||
275 | if (type == STF_BARRIER_FALLBACK) { | |
276 | pr_info("stf-barrier: fallback barrier available\n"); | |
277 | } else if (type == STF_BARRIER_SYNC_ORI) { | |
278 | pr_info("stf-barrier: hwsync barrier available\n"); | |
279 | } else if (type == STF_BARRIER_EIEIO) { | |
280 | pr_info("stf-barrier: eieio barrier available\n"); | |
281 | } | |
282 | ||
283 | stf_enabled_flush_types = type; | |
284 | ||
285 | if (!no_stf_barrier) | |
286 | stf_barrier_enable(enable); | |
287 | } | |
288 | ||
289 | ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf) | |
290 | { | |
291 | if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) { | |
292 | const char *type; | |
293 | switch (stf_enabled_flush_types) { | |
294 | case STF_BARRIER_EIEIO: | |
295 | type = "eieio"; | |
296 | break; | |
297 | case STF_BARRIER_SYNC_ORI: | |
298 | type = "hwsync"; | |
299 | break; | |
300 | case STF_BARRIER_FALLBACK: | |
301 | type = "fallback"; | |
302 | break; | |
303 | default: | |
304 | type = "unknown"; | |
305 | } | |
306 | return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type); | |
307 | } | |
308 | ||
309 | if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && | |
310 | !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) | |
311 | return sprintf(buf, "Not affected\n"); | |
312 | ||
313 | return sprintf(buf, "Vulnerable\n"); | |
314 | } | |
315 | ||
316 | #ifdef CONFIG_DEBUG_FS | |
317 | static int stf_barrier_set(void *data, u64 val) | |
318 | { | |
319 | bool enable; | |
320 | ||
321 | if (val == 1) | |
322 | enable = true; | |
323 | else if (val == 0) | |
324 | enable = false; | |
325 | else | |
326 | return -EINVAL; | |
327 | ||
328 | /* Only do anything if we're changing state */ | |
329 | if (enable != stf_barrier) | |
330 | stf_barrier_enable(enable); | |
331 | ||
332 | return 0; | |
333 | } | |
334 | ||
335 | static int stf_barrier_get(void *data, u64 *val) | |
336 | { | |
337 | *val = stf_barrier ? 1 : 0; | |
338 | return 0; | |
339 | } | |
340 | ||
341 | DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n"); | |
342 | ||
343 | static __init int stf_barrier_debugfs_init(void) | |
344 | { | |
345 | debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier); | |
346 | return 0; | |
347 | } | |
348 | device_initcall(stf_barrier_debugfs_init); | |
349 | #endif /* CONFIG_DEBUG_FS */ | |
4dcfe4cc ME |
350 | |
351 | static void toggle_count_cache_flush(bool enable) | |
352 | { | |
353 | if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) { | |
354 | patch_instruction_site(&patch__call_flush_count_cache, PPC_INST_NOP); | |
355 | count_cache_flush_type = COUNT_CACHE_FLUSH_NONE; | |
356 | pr_info("count-cache-flush: software flush disabled.\n"); | |
357 | return; | |
358 | } | |
359 | ||
360 | patch_branch_site(&patch__call_flush_count_cache, | |
361 | (u64)&flush_count_cache, BRANCH_SET_LINK); | |
362 | ||
363 | if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) { | |
364 | count_cache_flush_type = COUNT_CACHE_FLUSH_SW; | |
365 | pr_info("count-cache-flush: full software flush sequence enabled.\n"); | |
366 | return; | |
367 | } | |
368 | ||
369 | patch_instruction_site(&patch__flush_count_cache_return, PPC_INST_BLR); | |
370 | count_cache_flush_type = COUNT_CACHE_FLUSH_HW; | |
371 | pr_info("count-cache-flush: hardware assisted flush sequence enabled\n"); | |
372 | } | |
373 | ||
374 | void setup_count_cache_flush(void) | |
375 | { | |
376 | toggle_count_cache_flush(true); | |
377 | } | |
378 | ||
379 | #ifdef CONFIG_DEBUG_FS | |
380 | static int count_cache_flush_set(void *data, u64 val) | |
381 | { | |
382 | bool enable; | |
383 | ||
384 | if (val == 1) | |
385 | enable = true; | |
386 | else if (val == 0) | |
387 | enable = false; | |
388 | else | |
389 | return -EINVAL; | |
390 | ||
391 | toggle_count_cache_flush(enable); | |
392 | ||
393 | return 0; | |
394 | } | |
395 | ||
396 | static int count_cache_flush_get(void *data, u64 *val) | |
397 | { | |
398 | if (count_cache_flush_type == COUNT_CACHE_FLUSH_NONE) | |
399 | *val = 0; | |
400 | else | |
401 | *val = 1; | |
402 | ||
403 | return 0; | |
404 | } | |
405 | ||
406 | DEFINE_SIMPLE_ATTRIBUTE(fops_count_cache_flush, count_cache_flush_get, | |
407 | count_cache_flush_set, "%llu\n"); | |
408 | ||
409 | static __init int count_cache_flush_debugfs_init(void) | |
410 | { | |
411 | debugfs_create_file("count_cache_flush", 0600, powerpc_debugfs_root, | |
412 | NULL, &fops_count_cache_flush); | |
413 | return 0; | |
414 | } | |
415 | device_initcall(count_cache_flush_debugfs_init); | |
416 | #endif /* CONFIG_DEBUG_FS */ | |
32bc83b9 | 417 | #endif /* CONFIG_PPC_BOOK3S_64 */ |