xref: /linux-6.15/arch/powerpc/kernel/security.c (revision 406d2b6a)
19a868f63SMichael Ellerman // SPDX-License-Identifier: GPL-2.0+
29a868f63SMichael Ellerman //
39a868f63SMichael Ellerman // Security related flags and so on.
49a868f63SMichael Ellerman //
59a868f63SMichael Ellerman // Copyright 2018, Michael Ellerman, IBM Corporation.
69a868f63SMichael Ellerman 
79a868f63SMichael Ellerman #include <linux/kernel.h>
88ad33041SMichael Ellerman #include <linux/device.h>
9ff348355SMichael Ellerman #include <linux/seq_buf.h>
108ad33041SMichael Ellerman 
11a048a07dSNicholas Piggin #include <asm/debugfs.h>
129a868f63SMichael Ellerman #include <asm/security_features.h>
132eea7f06SMichal Suchanek #include <asm/setup.h>
149a868f63SMichael Ellerman 
159a868f63SMichael Ellerman 
16e7347a86SMauricio Faria de Oliveira unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT;
178ad33041SMichael Ellerman 
18815069caSMichal Suchanek bool barrier_nospec_enabled;
19cf175dc3SDiana Craciun static bool no_nospec;
202eea7f06SMichal Suchanek 
212eea7f06SMichal Suchanek static void enable_barrier_nospec(bool enable)
222eea7f06SMichal Suchanek {
232eea7f06SMichal Suchanek 	barrier_nospec_enabled = enable;
242eea7f06SMichal Suchanek 	do_barrier_nospec_fixups(enable);
252eea7f06SMichal Suchanek }
262eea7f06SMichal Suchanek 
27cb3d6759SMichal Suchanek void setup_barrier_nospec(void)
28cb3d6759SMichal Suchanek {
29cb3d6759SMichal Suchanek 	bool enable;
30cb3d6759SMichal Suchanek 
31cb3d6759SMichal Suchanek 	/*
32cb3d6759SMichal Suchanek 	 * It would make sense to check SEC_FTR_SPEC_BAR_ORI31 below as well.
33cb3d6759SMichal Suchanek 	 * But there's a good reason not to. The two flags we check below are
34cb3d6759SMichal Suchanek 	 * both are enabled by default in the kernel, so if the hcall is not
35cb3d6759SMichal Suchanek 	 * functional they will be enabled.
36cb3d6759SMichal Suchanek 	 * On a system where the host firmware has been updated (so the ori
37cb3d6759SMichal Suchanek 	 * functions as a barrier), but on which the hypervisor (KVM/Qemu) has
38cb3d6759SMichal Suchanek 	 * not been updated, we would like to enable the barrier. Dropping the
39cb3d6759SMichal Suchanek 	 * check for SEC_FTR_SPEC_BAR_ORI31 achieves that. The only downside is
40cb3d6759SMichal Suchanek 	 * we potentially enable the barrier on systems where the host firmware
41cb3d6759SMichal Suchanek 	 * is not updated, but that's harmless as it's a no-op.
42cb3d6759SMichal Suchanek 	 */
43cb3d6759SMichal Suchanek 	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
44cb3d6759SMichal Suchanek 		 security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR);
45cb3d6759SMichal Suchanek 
46cf175dc3SDiana Craciun 	if (!no_nospec)
47cb3d6759SMichal Suchanek 		enable_barrier_nospec(enable);
48cb3d6759SMichal Suchanek }
49cb3d6759SMichal Suchanek 
50cf175dc3SDiana Craciun static int __init handle_nospectre_v1(char *p)
51cf175dc3SDiana Craciun {
52cf175dc3SDiana Craciun 	no_nospec = true;
53cf175dc3SDiana Craciun 
54cf175dc3SDiana Craciun 	return 0;
55cf175dc3SDiana Craciun }
56cf175dc3SDiana Craciun early_param("nospectre_v1", handle_nospectre_v1);
57cf175dc3SDiana Craciun 
58cb3d6759SMichal Suchanek #ifdef CONFIG_DEBUG_FS
59cb3d6759SMichal Suchanek static int barrier_nospec_set(void *data, u64 val)
60cb3d6759SMichal Suchanek {
61cb3d6759SMichal Suchanek 	switch (val) {
62cb3d6759SMichal Suchanek 	case 0:
63cb3d6759SMichal Suchanek 	case 1:
64cb3d6759SMichal Suchanek 		break;
65cb3d6759SMichal Suchanek 	default:
66cb3d6759SMichal Suchanek 		return -EINVAL;
67cb3d6759SMichal Suchanek 	}
68cb3d6759SMichal Suchanek 
69cb3d6759SMichal Suchanek 	if (!!val == !!barrier_nospec_enabled)
70cb3d6759SMichal Suchanek 		return 0;
71cb3d6759SMichal Suchanek 
72cb3d6759SMichal Suchanek 	enable_barrier_nospec(!!val);
73cb3d6759SMichal Suchanek 
74cb3d6759SMichal Suchanek 	return 0;
75cb3d6759SMichal Suchanek }
76cb3d6759SMichal Suchanek 
77cb3d6759SMichal Suchanek static int barrier_nospec_get(void *data, u64 *val)
78cb3d6759SMichal Suchanek {
79cb3d6759SMichal Suchanek 	*val = barrier_nospec_enabled ? 1 : 0;
80cb3d6759SMichal Suchanek 	return 0;
81cb3d6759SMichal Suchanek }
82cb3d6759SMichal Suchanek 
83cb3d6759SMichal Suchanek DEFINE_SIMPLE_ATTRIBUTE(fops_barrier_nospec,
84cb3d6759SMichal Suchanek 			barrier_nospec_get, barrier_nospec_set, "%llu\n");
85cb3d6759SMichal Suchanek 
86cb3d6759SMichal Suchanek static __init int barrier_nospec_debugfs_init(void)
87cb3d6759SMichal Suchanek {
88cb3d6759SMichal Suchanek 	debugfs_create_file("barrier_nospec", 0600, powerpc_debugfs_root, NULL,
89cb3d6759SMichal Suchanek 			    &fops_barrier_nospec);
90cb3d6759SMichal Suchanek 	return 0;
91cb3d6759SMichal Suchanek }
92cb3d6759SMichal Suchanek device_initcall(barrier_nospec_debugfs_init);
93cb3d6759SMichal Suchanek #endif /* CONFIG_DEBUG_FS */
94cb3d6759SMichal Suchanek 
95*406d2b6aSDiana Craciun #ifdef CONFIG_PPC_BOOK3S_64
968ad33041SMichael Ellerman ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
978ad33041SMichael Ellerman {
98ff348355SMichael Ellerman 	bool thread_priv;
99ff348355SMichael Ellerman 
100ff348355SMichael Ellerman 	thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV);
101ff348355SMichael Ellerman 
102ff348355SMichael Ellerman 	if (rfi_flush || thread_priv) {
103ff348355SMichael Ellerman 		struct seq_buf s;
104ff348355SMichael Ellerman 		seq_buf_init(&s, buf, PAGE_SIZE - 1);
105ff348355SMichael Ellerman 
106ff348355SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
107ff348355SMichael Ellerman 
1088ad33041SMichael Ellerman 		if (rfi_flush)
109ff348355SMichael Ellerman 			seq_buf_printf(&s, "RFI Flush");
110ff348355SMichael Ellerman 
111ff348355SMichael Ellerman 		if (rfi_flush && thread_priv)
112ff348355SMichael Ellerman 			seq_buf_printf(&s, ", ");
113ff348355SMichael Ellerman 
114ff348355SMichael Ellerman 		if (thread_priv)
115ff348355SMichael Ellerman 			seq_buf_printf(&s, "L1D private per thread");
116ff348355SMichael Ellerman 
117ff348355SMichael Ellerman 		seq_buf_printf(&s, "\n");
118ff348355SMichael Ellerman 
119ff348355SMichael Ellerman 		return s.len;
120ff348355SMichael Ellerman 	}
121ff348355SMichael Ellerman 
122ff348355SMichael Ellerman 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
123ff348355SMichael Ellerman 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
124ff348355SMichael Ellerman 		return sprintf(buf, "Not affected\n");
1258ad33041SMichael Ellerman 
1268ad33041SMichael Ellerman 	return sprintf(buf, "Vulnerable\n");
1278ad33041SMichael Ellerman }
128*406d2b6aSDiana Craciun #endif
12956986016SMichael Ellerman 
13056986016SMichael Ellerman ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
13156986016SMichael Ellerman {
1326d44acaeSMichael Ellerman 	struct seq_buf s;
13356986016SMichael Ellerman 
1346d44acaeSMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
1356d44acaeSMichael Ellerman 
1366d44acaeSMichael Ellerman 	if (security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) {
137a3775145SMichal Suchanek 		if (barrier_nospec_enabled)
1386d44acaeSMichael Ellerman 			seq_buf_printf(&s, "Mitigation: __user pointer sanitization");
1396d44acaeSMichael Ellerman 		else
1406d44acaeSMichael Ellerman 			seq_buf_printf(&s, "Vulnerable");
141a3775145SMichal Suchanek 
1426d44acaeSMichael Ellerman 		if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31))
1436d44acaeSMichael Ellerman 			seq_buf_printf(&s, ", ori31 speculation barrier enabled");
1446d44acaeSMichael Ellerman 
1456d44acaeSMichael Ellerman 		seq_buf_printf(&s, "\n");
1466d44acaeSMichael Ellerman 	} else
1476d44acaeSMichael Ellerman 		seq_buf_printf(&s, "Not affected\n");
1486d44acaeSMichael Ellerman 
1496d44acaeSMichael Ellerman 	return s.len;
15056986016SMichael Ellerman }
151d6fbe1c5SMichael Ellerman 
152d6fbe1c5SMichael Ellerman ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf)
153d6fbe1c5SMichael Ellerman {
154d6fbe1c5SMichael Ellerman 	struct seq_buf s;
1556d44acaeSMichael Ellerman 	bool bcs, ccd;
156d6fbe1c5SMichael Ellerman 
157d6fbe1c5SMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
158d6fbe1c5SMichael Ellerman 
159d6fbe1c5SMichael Ellerman 	bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
160d6fbe1c5SMichael Ellerman 	ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
161d6fbe1c5SMichael Ellerman 
162d6fbe1c5SMichael Ellerman 	if (bcs || ccd) {
163d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
164d6fbe1c5SMichael Ellerman 
165d6fbe1c5SMichael Ellerman 		if (bcs)
166d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
167d6fbe1c5SMichael Ellerman 
168d6fbe1c5SMichael Ellerman 		if (bcs && ccd)
169d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, ", ");
170d6fbe1c5SMichael Ellerman 
171d6fbe1c5SMichael Ellerman 		if (ccd)
172d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch cache disabled");
173d6fbe1c5SMichael Ellerman 	} else
174d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Vulnerable");
175d6fbe1c5SMichael Ellerman 
176d6fbe1c5SMichael Ellerman 	seq_buf_printf(&s, "\n");
177d6fbe1c5SMichael Ellerman 
178d6fbe1c5SMichael Ellerman 	return s.len;
179d6fbe1c5SMichael Ellerman }
180a048a07dSNicholas Piggin 
1816453b532SDiana Craciun #ifdef CONFIG_PPC_BOOK3S_64
182a048a07dSNicholas Piggin /*
183a048a07dSNicholas Piggin  * Store-forwarding barrier support.
184a048a07dSNicholas Piggin  */
185a048a07dSNicholas Piggin 
186a048a07dSNicholas Piggin static enum stf_barrier_type stf_enabled_flush_types;
187a048a07dSNicholas Piggin static bool no_stf_barrier;
188a048a07dSNicholas Piggin bool stf_barrier;
189a048a07dSNicholas Piggin 
190a048a07dSNicholas Piggin static int __init handle_no_stf_barrier(char *p)
191a048a07dSNicholas Piggin {
192a048a07dSNicholas Piggin 	pr_info("stf-barrier: disabled on command line.");
193a048a07dSNicholas Piggin 	no_stf_barrier = true;
194a048a07dSNicholas Piggin 	return 0;
195a048a07dSNicholas Piggin }
196a048a07dSNicholas Piggin 
197a048a07dSNicholas Piggin early_param("no_stf_barrier", handle_no_stf_barrier);
198a048a07dSNicholas Piggin 
199a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
200a048a07dSNicholas Piggin static int __init handle_ssbd(char *p)
201a048a07dSNicholas Piggin {
202a048a07dSNicholas Piggin 	if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) {
203a048a07dSNicholas Piggin 		/* Until firmware tells us, we have the barrier with auto */
204a048a07dSNicholas Piggin 		return 0;
205a048a07dSNicholas Piggin 	} else if (strncmp(p, "off", 3) == 0) {
206a048a07dSNicholas Piggin 		handle_no_stf_barrier(NULL);
207a048a07dSNicholas Piggin 		return 0;
208a048a07dSNicholas Piggin 	} else
209a048a07dSNicholas Piggin 		return 1;
210a048a07dSNicholas Piggin 
211a048a07dSNicholas Piggin 	return 0;
212a048a07dSNicholas Piggin }
213a048a07dSNicholas Piggin early_param("spec_store_bypass_disable", handle_ssbd);
214a048a07dSNicholas Piggin 
215a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
216a048a07dSNicholas Piggin static int __init handle_no_ssbd(char *p)
217a048a07dSNicholas Piggin {
218a048a07dSNicholas Piggin 	handle_no_stf_barrier(NULL);
219a048a07dSNicholas Piggin 	return 0;
220a048a07dSNicholas Piggin }
221a048a07dSNicholas Piggin early_param("nospec_store_bypass_disable", handle_no_ssbd);
222a048a07dSNicholas Piggin 
223a048a07dSNicholas Piggin static void stf_barrier_enable(bool enable)
224a048a07dSNicholas Piggin {
225a048a07dSNicholas Piggin 	if (enable)
226a048a07dSNicholas Piggin 		do_stf_barrier_fixups(stf_enabled_flush_types);
227a048a07dSNicholas Piggin 	else
228a048a07dSNicholas Piggin 		do_stf_barrier_fixups(STF_BARRIER_NONE);
229a048a07dSNicholas Piggin 
230a048a07dSNicholas Piggin 	stf_barrier = enable;
231a048a07dSNicholas Piggin }
232a048a07dSNicholas Piggin 
233a048a07dSNicholas Piggin void setup_stf_barrier(void)
234a048a07dSNicholas Piggin {
235a048a07dSNicholas Piggin 	enum stf_barrier_type type;
236a048a07dSNicholas Piggin 	bool enable, hv;
237a048a07dSNicholas Piggin 
238a048a07dSNicholas Piggin 	hv = cpu_has_feature(CPU_FTR_HVMODE);
239a048a07dSNicholas Piggin 
240a048a07dSNicholas Piggin 	/* Default to fallback in case fw-features are not available */
241a048a07dSNicholas Piggin 	if (cpu_has_feature(CPU_FTR_ARCH_300))
242a048a07dSNicholas Piggin 		type = STF_BARRIER_EIEIO;
243a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_207S))
244a048a07dSNicholas Piggin 		type = STF_BARRIER_SYNC_ORI;
245a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_206))
246a048a07dSNicholas Piggin 		type = STF_BARRIER_FALLBACK;
247a048a07dSNicholas Piggin 	else
248a048a07dSNicholas Piggin 		type = STF_BARRIER_NONE;
249a048a07dSNicholas Piggin 
250a048a07dSNicholas Piggin 	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
251a048a07dSNicholas Piggin 		(security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) ||
252a048a07dSNicholas Piggin 		 (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv));
253a048a07dSNicholas Piggin 
254a048a07dSNicholas Piggin 	if (type == STF_BARRIER_FALLBACK) {
255a048a07dSNicholas Piggin 		pr_info("stf-barrier: fallback barrier available\n");
256a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_SYNC_ORI) {
257a048a07dSNicholas Piggin 		pr_info("stf-barrier: hwsync barrier available\n");
258a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_EIEIO) {
259a048a07dSNicholas Piggin 		pr_info("stf-barrier: eieio barrier available\n");
260a048a07dSNicholas Piggin 	}
261a048a07dSNicholas Piggin 
262a048a07dSNicholas Piggin 	stf_enabled_flush_types = type;
263a048a07dSNicholas Piggin 
264a048a07dSNicholas Piggin 	if (!no_stf_barrier)
265a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
266a048a07dSNicholas Piggin }
267a048a07dSNicholas Piggin 
268a048a07dSNicholas Piggin ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf)
269a048a07dSNicholas Piggin {
270a048a07dSNicholas Piggin 	if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) {
271a048a07dSNicholas Piggin 		const char *type;
272a048a07dSNicholas Piggin 		switch (stf_enabled_flush_types) {
273a048a07dSNicholas Piggin 		case STF_BARRIER_EIEIO:
274a048a07dSNicholas Piggin 			type = "eieio";
275a048a07dSNicholas Piggin 			break;
276a048a07dSNicholas Piggin 		case STF_BARRIER_SYNC_ORI:
277a048a07dSNicholas Piggin 			type = "hwsync";
278a048a07dSNicholas Piggin 			break;
279a048a07dSNicholas Piggin 		case STF_BARRIER_FALLBACK:
280a048a07dSNicholas Piggin 			type = "fallback";
281a048a07dSNicholas Piggin 			break;
282a048a07dSNicholas Piggin 		default:
283a048a07dSNicholas Piggin 			type = "unknown";
284a048a07dSNicholas Piggin 		}
285a048a07dSNicholas Piggin 		return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type);
286a048a07dSNicholas Piggin 	}
287a048a07dSNicholas Piggin 
288a048a07dSNicholas Piggin 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
289a048a07dSNicholas Piggin 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
290a048a07dSNicholas Piggin 		return sprintf(buf, "Not affected\n");
291a048a07dSNicholas Piggin 
292a048a07dSNicholas Piggin 	return sprintf(buf, "Vulnerable\n");
293a048a07dSNicholas Piggin }
294a048a07dSNicholas Piggin 
295a048a07dSNicholas Piggin #ifdef CONFIG_DEBUG_FS
296a048a07dSNicholas Piggin static int stf_barrier_set(void *data, u64 val)
297a048a07dSNicholas Piggin {
298a048a07dSNicholas Piggin 	bool enable;
299a048a07dSNicholas Piggin 
300a048a07dSNicholas Piggin 	if (val == 1)
301a048a07dSNicholas Piggin 		enable = true;
302a048a07dSNicholas Piggin 	else if (val == 0)
303a048a07dSNicholas Piggin 		enable = false;
304a048a07dSNicholas Piggin 	else
305a048a07dSNicholas Piggin 		return -EINVAL;
306a048a07dSNicholas Piggin 
307a048a07dSNicholas Piggin 	/* Only do anything if we're changing state */
308a048a07dSNicholas Piggin 	if (enable != stf_barrier)
309a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
310a048a07dSNicholas Piggin 
311a048a07dSNicholas Piggin 	return 0;
312a048a07dSNicholas Piggin }
313a048a07dSNicholas Piggin 
314a048a07dSNicholas Piggin static int stf_barrier_get(void *data, u64 *val)
315a048a07dSNicholas Piggin {
316a048a07dSNicholas Piggin 	*val = stf_barrier ? 1 : 0;
317a048a07dSNicholas Piggin 	return 0;
318a048a07dSNicholas Piggin }
319a048a07dSNicholas Piggin 
320a048a07dSNicholas Piggin DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n");
321a048a07dSNicholas Piggin 
322a048a07dSNicholas Piggin static __init int stf_barrier_debugfs_init(void)
323a048a07dSNicholas Piggin {
324a048a07dSNicholas Piggin 	debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier);
325a048a07dSNicholas Piggin 	return 0;
326a048a07dSNicholas Piggin }
327a048a07dSNicholas Piggin device_initcall(stf_barrier_debugfs_init);
328a048a07dSNicholas Piggin #endif /* CONFIG_DEBUG_FS */
3296453b532SDiana Craciun #endif /* CONFIG_PPC_BOOK3S_64 */
330