xref: /linux-6.15/arch/powerpc/kernel/security.c (revision cf175dc3)
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;
19*cf175dc3SDiana 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 
46*cf175dc3SDiana Craciun 	if (!no_nospec)
47cb3d6759SMichal Suchanek 		enable_barrier_nospec(enable);
48cb3d6759SMichal Suchanek }
49cb3d6759SMichal Suchanek 
50*cf175dc3SDiana Craciun static int __init handle_nospectre_v1(char *p)
51*cf175dc3SDiana Craciun {
52*cf175dc3SDiana Craciun 	no_nospec = true;
53*cf175dc3SDiana Craciun 
54*cf175dc3SDiana Craciun 	return 0;
55*cf175dc3SDiana Craciun }
56*cf175dc3SDiana Craciun early_param("nospectre_v1", handle_nospectre_v1);
57*cf175dc3SDiana 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 
958ad33041SMichael Ellerman ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
968ad33041SMichael Ellerman {
97ff348355SMichael Ellerman 	bool thread_priv;
98ff348355SMichael Ellerman 
99ff348355SMichael Ellerman 	thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV);
100ff348355SMichael Ellerman 
101ff348355SMichael Ellerman 	if (rfi_flush || thread_priv) {
102ff348355SMichael Ellerman 		struct seq_buf s;
103ff348355SMichael Ellerman 		seq_buf_init(&s, buf, PAGE_SIZE - 1);
104ff348355SMichael Ellerman 
105ff348355SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
106ff348355SMichael Ellerman 
1078ad33041SMichael Ellerman 		if (rfi_flush)
108ff348355SMichael Ellerman 			seq_buf_printf(&s, "RFI Flush");
109ff348355SMichael Ellerman 
110ff348355SMichael Ellerman 		if (rfi_flush && thread_priv)
111ff348355SMichael Ellerman 			seq_buf_printf(&s, ", ");
112ff348355SMichael Ellerman 
113ff348355SMichael Ellerman 		if (thread_priv)
114ff348355SMichael Ellerman 			seq_buf_printf(&s, "L1D private per thread");
115ff348355SMichael Ellerman 
116ff348355SMichael Ellerman 		seq_buf_printf(&s, "\n");
117ff348355SMichael Ellerman 
118ff348355SMichael Ellerman 		return s.len;
119ff348355SMichael Ellerman 	}
120ff348355SMichael Ellerman 
121ff348355SMichael Ellerman 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
122ff348355SMichael Ellerman 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
123ff348355SMichael Ellerman 		return sprintf(buf, "Not affected\n");
1248ad33041SMichael Ellerman 
1258ad33041SMichael Ellerman 	return sprintf(buf, "Vulnerable\n");
1268ad33041SMichael Ellerman }
12756986016SMichael Ellerman 
12856986016SMichael Ellerman ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
12956986016SMichael Ellerman {
1306d44acaeSMichael Ellerman 	struct seq_buf s;
13156986016SMichael Ellerman 
1326d44acaeSMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
1336d44acaeSMichael Ellerman 
1346d44acaeSMichael Ellerman 	if (security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) {
135a3775145SMichal Suchanek 		if (barrier_nospec_enabled)
1366d44acaeSMichael Ellerman 			seq_buf_printf(&s, "Mitigation: __user pointer sanitization");
1376d44acaeSMichael Ellerman 		else
1386d44acaeSMichael Ellerman 			seq_buf_printf(&s, "Vulnerable");
139a3775145SMichal Suchanek 
1406d44acaeSMichael Ellerman 		if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31))
1416d44acaeSMichael Ellerman 			seq_buf_printf(&s, ", ori31 speculation barrier enabled");
1426d44acaeSMichael Ellerman 
1436d44acaeSMichael Ellerman 		seq_buf_printf(&s, "\n");
1446d44acaeSMichael Ellerman 	} else
1456d44acaeSMichael Ellerman 		seq_buf_printf(&s, "Not affected\n");
1466d44acaeSMichael Ellerman 
1476d44acaeSMichael Ellerman 	return s.len;
14856986016SMichael Ellerman }
149d6fbe1c5SMichael Ellerman 
150d6fbe1c5SMichael Ellerman ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf)
151d6fbe1c5SMichael Ellerman {
152d6fbe1c5SMichael Ellerman 	struct seq_buf s;
1536d44acaeSMichael Ellerman 	bool bcs, ccd;
154d6fbe1c5SMichael Ellerman 
155d6fbe1c5SMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
156d6fbe1c5SMichael Ellerman 
157d6fbe1c5SMichael Ellerman 	bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
158d6fbe1c5SMichael Ellerman 	ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
159d6fbe1c5SMichael Ellerman 
160d6fbe1c5SMichael Ellerman 	if (bcs || ccd) {
161d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
162d6fbe1c5SMichael Ellerman 
163d6fbe1c5SMichael Ellerman 		if (bcs)
164d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
165d6fbe1c5SMichael Ellerman 
166d6fbe1c5SMichael Ellerman 		if (bcs && ccd)
167d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, ", ");
168d6fbe1c5SMichael Ellerman 
169d6fbe1c5SMichael Ellerman 		if (ccd)
170d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch cache disabled");
171d6fbe1c5SMichael Ellerman 	} else
172d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Vulnerable");
173d6fbe1c5SMichael Ellerman 
174d6fbe1c5SMichael Ellerman 	seq_buf_printf(&s, "\n");
175d6fbe1c5SMichael Ellerman 
176d6fbe1c5SMichael Ellerman 	return s.len;
177d6fbe1c5SMichael Ellerman }
178a048a07dSNicholas Piggin 
179a048a07dSNicholas Piggin /*
180a048a07dSNicholas Piggin  * Store-forwarding barrier support.
181a048a07dSNicholas Piggin  */
182a048a07dSNicholas Piggin 
183a048a07dSNicholas Piggin static enum stf_barrier_type stf_enabled_flush_types;
184a048a07dSNicholas Piggin static bool no_stf_barrier;
185a048a07dSNicholas Piggin bool stf_barrier;
186a048a07dSNicholas Piggin 
187a048a07dSNicholas Piggin static int __init handle_no_stf_barrier(char *p)
188a048a07dSNicholas Piggin {
189a048a07dSNicholas Piggin 	pr_info("stf-barrier: disabled on command line.");
190a048a07dSNicholas Piggin 	no_stf_barrier = true;
191a048a07dSNicholas Piggin 	return 0;
192a048a07dSNicholas Piggin }
193a048a07dSNicholas Piggin 
194a048a07dSNicholas Piggin early_param("no_stf_barrier", handle_no_stf_barrier);
195a048a07dSNicholas Piggin 
196a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
197a048a07dSNicholas Piggin static int __init handle_ssbd(char *p)
198a048a07dSNicholas Piggin {
199a048a07dSNicholas Piggin 	if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) {
200a048a07dSNicholas Piggin 		/* Until firmware tells us, we have the barrier with auto */
201a048a07dSNicholas Piggin 		return 0;
202a048a07dSNicholas Piggin 	} else if (strncmp(p, "off", 3) == 0) {
203a048a07dSNicholas Piggin 		handle_no_stf_barrier(NULL);
204a048a07dSNicholas Piggin 		return 0;
205a048a07dSNicholas Piggin 	} else
206a048a07dSNicholas Piggin 		return 1;
207a048a07dSNicholas Piggin 
208a048a07dSNicholas Piggin 	return 0;
209a048a07dSNicholas Piggin }
210a048a07dSNicholas Piggin early_param("spec_store_bypass_disable", handle_ssbd);
211a048a07dSNicholas Piggin 
212a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
213a048a07dSNicholas Piggin static int __init handle_no_ssbd(char *p)
214a048a07dSNicholas Piggin {
215a048a07dSNicholas Piggin 	handle_no_stf_barrier(NULL);
216a048a07dSNicholas Piggin 	return 0;
217a048a07dSNicholas Piggin }
218a048a07dSNicholas Piggin early_param("nospec_store_bypass_disable", handle_no_ssbd);
219a048a07dSNicholas Piggin 
220a048a07dSNicholas Piggin static void stf_barrier_enable(bool enable)
221a048a07dSNicholas Piggin {
222a048a07dSNicholas Piggin 	if (enable)
223a048a07dSNicholas Piggin 		do_stf_barrier_fixups(stf_enabled_flush_types);
224a048a07dSNicholas Piggin 	else
225a048a07dSNicholas Piggin 		do_stf_barrier_fixups(STF_BARRIER_NONE);
226a048a07dSNicholas Piggin 
227a048a07dSNicholas Piggin 	stf_barrier = enable;
228a048a07dSNicholas Piggin }
229a048a07dSNicholas Piggin 
230a048a07dSNicholas Piggin void setup_stf_barrier(void)
231a048a07dSNicholas Piggin {
232a048a07dSNicholas Piggin 	enum stf_barrier_type type;
233a048a07dSNicholas Piggin 	bool enable, hv;
234a048a07dSNicholas Piggin 
235a048a07dSNicholas Piggin 	hv = cpu_has_feature(CPU_FTR_HVMODE);
236a048a07dSNicholas Piggin 
237a048a07dSNicholas Piggin 	/* Default to fallback in case fw-features are not available */
238a048a07dSNicholas Piggin 	if (cpu_has_feature(CPU_FTR_ARCH_300))
239a048a07dSNicholas Piggin 		type = STF_BARRIER_EIEIO;
240a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_207S))
241a048a07dSNicholas Piggin 		type = STF_BARRIER_SYNC_ORI;
242a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_206))
243a048a07dSNicholas Piggin 		type = STF_BARRIER_FALLBACK;
244a048a07dSNicholas Piggin 	else
245a048a07dSNicholas Piggin 		type = STF_BARRIER_NONE;
246a048a07dSNicholas Piggin 
247a048a07dSNicholas Piggin 	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
248a048a07dSNicholas Piggin 		(security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) ||
249a048a07dSNicholas Piggin 		 (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv));
250a048a07dSNicholas Piggin 
251a048a07dSNicholas Piggin 	if (type == STF_BARRIER_FALLBACK) {
252a048a07dSNicholas Piggin 		pr_info("stf-barrier: fallback barrier available\n");
253a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_SYNC_ORI) {
254a048a07dSNicholas Piggin 		pr_info("stf-barrier: hwsync barrier available\n");
255a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_EIEIO) {
256a048a07dSNicholas Piggin 		pr_info("stf-barrier: eieio barrier available\n");
257a048a07dSNicholas Piggin 	}
258a048a07dSNicholas Piggin 
259a048a07dSNicholas Piggin 	stf_enabled_flush_types = type;
260a048a07dSNicholas Piggin 
261a048a07dSNicholas Piggin 	if (!no_stf_barrier)
262a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
263a048a07dSNicholas Piggin }
264a048a07dSNicholas Piggin 
265a048a07dSNicholas Piggin ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf)
266a048a07dSNicholas Piggin {
267a048a07dSNicholas Piggin 	if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) {
268a048a07dSNicholas Piggin 		const char *type;
269a048a07dSNicholas Piggin 		switch (stf_enabled_flush_types) {
270a048a07dSNicholas Piggin 		case STF_BARRIER_EIEIO:
271a048a07dSNicholas Piggin 			type = "eieio";
272a048a07dSNicholas Piggin 			break;
273a048a07dSNicholas Piggin 		case STF_BARRIER_SYNC_ORI:
274a048a07dSNicholas Piggin 			type = "hwsync";
275a048a07dSNicholas Piggin 			break;
276a048a07dSNicholas Piggin 		case STF_BARRIER_FALLBACK:
277a048a07dSNicholas Piggin 			type = "fallback";
278a048a07dSNicholas Piggin 			break;
279a048a07dSNicholas Piggin 		default:
280a048a07dSNicholas Piggin 			type = "unknown";
281a048a07dSNicholas Piggin 		}
282a048a07dSNicholas Piggin 		return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type);
283a048a07dSNicholas Piggin 	}
284a048a07dSNicholas Piggin 
285a048a07dSNicholas Piggin 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
286a048a07dSNicholas Piggin 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
287a048a07dSNicholas Piggin 		return sprintf(buf, "Not affected\n");
288a048a07dSNicholas Piggin 
289a048a07dSNicholas Piggin 	return sprintf(buf, "Vulnerable\n");
290a048a07dSNicholas Piggin }
291a048a07dSNicholas Piggin 
292a048a07dSNicholas Piggin #ifdef CONFIG_DEBUG_FS
293a048a07dSNicholas Piggin static int stf_barrier_set(void *data, u64 val)
294a048a07dSNicholas Piggin {
295a048a07dSNicholas Piggin 	bool enable;
296a048a07dSNicholas Piggin 
297a048a07dSNicholas Piggin 	if (val == 1)
298a048a07dSNicholas Piggin 		enable = true;
299a048a07dSNicholas Piggin 	else if (val == 0)
300a048a07dSNicholas Piggin 		enable = false;
301a048a07dSNicholas Piggin 	else
302a048a07dSNicholas Piggin 		return -EINVAL;
303a048a07dSNicholas Piggin 
304a048a07dSNicholas Piggin 	/* Only do anything if we're changing state */
305a048a07dSNicholas Piggin 	if (enable != stf_barrier)
306a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
307a048a07dSNicholas Piggin 
308a048a07dSNicholas Piggin 	return 0;
309a048a07dSNicholas Piggin }
310a048a07dSNicholas Piggin 
311a048a07dSNicholas Piggin static int stf_barrier_get(void *data, u64 *val)
312a048a07dSNicholas Piggin {
313a048a07dSNicholas Piggin 	*val = stf_barrier ? 1 : 0;
314a048a07dSNicholas Piggin 	return 0;
315a048a07dSNicholas Piggin }
316a048a07dSNicholas Piggin 
317a048a07dSNicholas Piggin DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n");
318a048a07dSNicholas Piggin 
319a048a07dSNicholas Piggin static __init int stf_barrier_debugfs_init(void)
320a048a07dSNicholas Piggin {
321a048a07dSNicholas Piggin 	debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier);
322a048a07dSNicholas Piggin 	return 0;
323a048a07dSNicholas Piggin }
324a048a07dSNicholas Piggin device_initcall(stf_barrier_debugfs_init);
325a048a07dSNicholas Piggin #endif /* CONFIG_DEBUG_FS */
326