xref: /linux-6.15/arch/powerpc/kernel/security.c (revision af2e8c68)
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 
742e2acdeSBreno Leitao #include <linux/cpu.h>
89a868f63SMichael Ellerman #include <linux/kernel.h>
98ad33041SMichael Ellerman #include <linux/device.h>
10ff348355SMichael Ellerman #include <linux/seq_buf.h>
118ad33041SMichael Ellerman 
12ee13cb24SMichael Ellerman #include <asm/asm-prototypes.h>
13ee13cb24SMichael Ellerman #include <asm/code-patching.h>
14a048a07dSNicholas Piggin #include <asm/debugfs.h>
159a868f63SMichael Ellerman #include <asm/security_features.h>
162eea7f06SMichal Suchanek #include <asm/setup.h>
179a868f63SMichael Ellerman 
189a868f63SMichael Ellerman 
19e7347a86SMauricio Faria de Oliveira unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT;
208ad33041SMichael Ellerman 
21ee13cb24SMichael Ellerman enum count_cache_flush_type {
22ee13cb24SMichael Ellerman 	COUNT_CACHE_FLUSH_NONE	= 0x1,
23ee13cb24SMichael Ellerman 	COUNT_CACHE_FLUSH_SW	= 0x2,
24ee13cb24SMichael Ellerman 	COUNT_CACHE_FLUSH_HW	= 0x4,
25ee13cb24SMichael Ellerman };
267d8bad99SDiana Craciun static enum count_cache_flush_type count_cache_flush_type = COUNT_CACHE_FLUSH_NONE;
2739e72bf9SMichael Ellerman static bool link_stack_flush_enabled;
28ee13cb24SMichael Ellerman 
29815069caSMichal Suchanek bool barrier_nospec_enabled;
30cf175dc3SDiana Craciun static bool no_nospec;
31f633a8adSDiana Craciun static bool btb_flush_enabled;
32d8f0e0b0SChristopher M. Riedl #if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3S_64)
33f633a8adSDiana Craciun static bool no_spectrev2;
34f633a8adSDiana Craciun #endif
352eea7f06SMichal Suchanek 
362eea7f06SMichal Suchanek static void enable_barrier_nospec(bool enable)
372eea7f06SMichal Suchanek {
382eea7f06SMichal Suchanek 	barrier_nospec_enabled = enable;
392eea7f06SMichal Suchanek 	do_barrier_nospec_fixups(enable);
402eea7f06SMichal Suchanek }
412eea7f06SMichal Suchanek 
42cb3d6759SMichal Suchanek void setup_barrier_nospec(void)
43cb3d6759SMichal Suchanek {
44cb3d6759SMichal Suchanek 	bool enable;
45cb3d6759SMichal Suchanek 
46cb3d6759SMichal Suchanek 	/*
47cb3d6759SMichal Suchanek 	 * It would make sense to check SEC_FTR_SPEC_BAR_ORI31 below as well.
48cb3d6759SMichal Suchanek 	 * But there's a good reason not to. The two flags we check below are
49cb3d6759SMichal Suchanek 	 * both are enabled by default in the kernel, so if the hcall is not
50cb3d6759SMichal Suchanek 	 * functional they will be enabled.
51cb3d6759SMichal Suchanek 	 * On a system where the host firmware has been updated (so the ori
52cb3d6759SMichal Suchanek 	 * functions as a barrier), but on which the hypervisor (KVM/Qemu) has
53cb3d6759SMichal Suchanek 	 * not been updated, we would like to enable the barrier. Dropping the
54cb3d6759SMichal Suchanek 	 * check for SEC_FTR_SPEC_BAR_ORI31 achieves that. The only downside is
55cb3d6759SMichal Suchanek 	 * we potentially enable the barrier on systems where the host firmware
56cb3d6759SMichal Suchanek 	 * is not updated, but that's harmless as it's a no-op.
57cb3d6759SMichal Suchanek 	 */
58cb3d6759SMichal Suchanek 	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
59cb3d6759SMichal Suchanek 		 security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR);
60cb3d6759SMichal Suchanek 
61782e69efSJosh Poimboeuf 	if (!no_nospec && !cpu_mitigations_off())
62cb3d6759SMichal Suchanek 		enable_barrier_nospec(enable);
63cb3d6759SMichal Suchanek }
64cb3d6759SMichal Suchanek 
65cf175dc3SDiana Craciun static int __init handle_nospectre_v1(char *p)
66cf175dc3SDiana Craciun {
67cf175dc3SDiana Craciun 	no_nospec = true;
68cf175dc3SDiana Craciun 
69cf175dc3SDiana Craciun 	return 0;
70cf175dc3SDiana Craciun }
71cf175dc3SDiana Craciun early_param("nospectre_v1", handle_nospectre_v1);
72cf175dc3SDiana Craciun 
73cb3d6759SMichal Suchanek #ifdef CONFIG_DEBUG_FS
74cb3d6759SMichal Suchanek static int barrier_nospec_set(void *data, u64 val)
75cb3d6759SMichal Suchanek {
76cb3d6759SMichal Suchanek 	switch (val) {
77cb3d6759SMichal Suchanek 	case 0:
78cb3d6759SMichal Suchanek 	case 1:
79cb3d6759SMichal Suchanek 		break;
80cb3d6759SMichal Suchanek 	default:
81cb3d6759SMichal Suchanek 		return -EINVAL;
82cb3d6759SMichal Suchanek 	}
83cb3d6759SMichal Suchanek 
84cb3d6759SMichal Suchanek 	if (!!val == !!barrier_nospec_enabled)
85cb3d6759SMichal Suchanek 		return 0;
86cb3d6759SMichal Suchanek 
87cb3d6759SMichal Suchanek 	enable_barrier_nospec(!!val);
88cb3d6759SMichal Suchanek 
89cb3d6759SMichal Suchanek 	return 0;
90cb3d6759SMichal Suchanek }
91cb3d6759SMichal Suchanek 
92cb3d6759SMichal Suchanek static int barrier_nospec_get(void *data, u64 *val)
93cb3d6759SMichal Suchanek {
94cb3d6759SMichal Suchanek 	*val = barrier_nospec_enabled ? 1 : 0;
95cb3d6759SMichal Suchanek 	return 0;
96cb3d6759SMichal Suchanek }
97cb3d6759SMichal Suchanek 
98cb3d6759SMichal Suchanek DEFINE_SIMPLE_ATTRIBUTE(fops_barrier_nospec,
99cb3d6759SMichal Suchanek 			barrier_nospec_get, barrier_nospec_set, "%llu\n");
100cb3d6759SMichal Suchanek 
101cb3d6759SMichal Suchanek static __init int barrier_nospec_debugfs_init(void)
102cb3d6759SMichal Suchanek {
103cb3d6759SMichal Suchanek 	debugfs_create_file("barrier_nospec", 0600, powerpc_debugfs_root, NULL,
104cb3d6759SMichal Suchanek 			    &fops_barrier_nospec);
105cb3d6759SMichal Suchanek 	return 0;
106cb3d6759SMichal Suchanek }
107cb3d6759SMichal Suchanek device_initcall(barrier_nospec_debugfs_init);
108398af571SMichael Ellerman 
109398af571SMichael Ellerman static __init int security_feature_debugfs_init(void)
110398af571SMichael Ellerman {
111398af571SMichael Ellerman 	debugfs_create_x64("security_features", 0400, powerpc_debugfs_root,
112398af571SMichael Ellerman 			   (u64 *)&powerpc_security_features);
113398af571SMichael Ellerman 	return 0;
114398af571SMichael Ellerman }
115398af571SMichael Ellerman device_initcall(security_feature_debugfs_init);
116cb3d6759SMichal Suchanek #endif /* CONFIG_DEBUG_FS */
117cb3d6759SMichal Suchanek 
118d8f0e0b0SChristopher M. Riedl #if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3S_64)
119f633a8adSDiana Craciun static int __init handle_nospectre_v2(char *p)
120f633a8adSDiana Craciun {
121f633a8adSDiana Craciun 	no_spectrev2 = true;
122f633a8adSDiana Craciun 
123f633a8adSDiana Craciun 	return 0;
124f633a8adSDiana Craciun }
125f633a8adSDiana Craciun early_param("nospectre_v2", handle_nospectre_v2);
126d8f0e0b0SChristopher M. Riedl #endif /* CONFIG_PPC_FSL_BOOK3E || CONFIG_PPC_BOOK3S_64 */
127d8f0e0b0SChristopher M. Riedl 
128d8f0e0b0SChristopher M. Riedl #ifdef CONFIG_PPC_FSL_BOOK3E
129f633a8adSDiana Craciun void setup_spectre_v2(void)
130f633a8adSDiana Craciun {
131782e69efSJosh Poimboeuf 	if (no_spectrev2 || cpu_mitigations_off())
132f633a8adSDiana Craciun 		do_btb_flush_fixups();
133f633a8adSDiana Craciun 	else
134f633a8adSDiana Craciun 		btb_flush_enabled = true;
135f633a8adSDiana Craciun }
136f633a8adSDiana Craciun #endif /* CONFIG_PPC_FSL_BOOK3E */
137f633a8adSDiana Craciun 
138406d2b6aSDiana Craciun #ifdef CONFIG_PPC_BOOK3S_64
1398ad33041SMichael Ellerman ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
1408ad33041SMichael Ellerman {
141ff348355SMichael Ellerman 	bool thread_priv;
142ff348355SMichael Ellerman 
143ff348355SMichael Ellerman 	thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV);
144ff348355SMichael Ellerman 
145ff348355SMichael Ellerman 	if (rfi_flush || thread_priv) {
146ff348355SMichael Ellerman 		struct seq_buf s;
147ff348355SMichael Ellerman 		seq_buf_init(&s, buf, PAGE_SIZE - 1);
148ff348355SMichael Ellerman 
149ff348355SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
150ff348355SMichael Ellerman 
1518ad33041SMichael Ellerman 		if (rfi_flush)
152ff348355SMichael Ellerman 			seq_buf_printf(&s, "RFI Flush");
153ff348355SMichael Ellerman 
154ff348355SMichael Ellerman 		if (rfi_flush && thread_priv)
155ff348355SMichael Ellerman 			seq_buf_printf(&s, ", ");
156ff348355SMichael Ellerman 
157ff348355SMichael Ellerman 		if (thread_priv)
158ff348355SMichael Ellerman 			seq_buf_printf(&s, "L1D private per thread");
159ff348355SMichael Ellerman 
160ff348355SMichael Ellerman 		seq_buf_printf(&s, "\n");
161ff348355SMichael Ellerman 
162ff348355SMichael Ellerman 		return s.len;
163ff348355SMichael Ellerman 	}
164ff348355SMichael Ellerman 
165ff348355SMichael Ellerman 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
166ff348355SMichael Ellerman 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
167ff348355SMichael Ellerman 		return sprintf(buf, "Not affected\n");
1688ad33041SMichael Ellerman 
1698ad33041SMichael Ellerman 	return sprintf(buf, "Vulnerable\n");
1708ad33041SMichael Ellerman }
171406d2b6aSDiana Craciun #endif
17256986016SMichael Ellerman 
17356986016SMichael Ellerman ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
17456986016SMichael Ellerman {
1756d44acaeSMichael Ellerman 	struct seq_buf s;
17656986016SMichael Ellerman 
1776d44acaeSMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
1786d44acaeSMichael Ellerman 
1796d44acaeSMichael Ellerman 	if (security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) {
180a3775145SMichal Suchanek 		if (barrier_nospec_enabled)
1816d44acaeSMichael Ellerman 			seq_buf_printf(&s, "Mitigation: __user pointer sanitization");
1826d44acaeSMichael Ellerman 		else
1836d44acaeSMichael Ellerman 			seq_buf_printf(&s, "Vulnerable");
184a3775145SMichal Suchanek 
1856d44acaeSMichael Ellerman 		if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31))
1866d44acaeSMichael Ellerman 			seq_buf_printf(&s, ", ori31 speculation barrier enabled");
1876d44acaeSMichael Ellerman 
1886d44acaeSMichael Ellerman 		seq_buf_printf(&s, "\n");
1896d44acaeSMichael Ellerman 	} else
1906d44acaeSMichael Ellerman 		seq_buf_printf(&s, "Not affected\n");
1916d44acaeSMichael Ellerman 
1926d44acaeSMichael Ellerman 	return s.len;
19356986016SMichael Ellerman }
194d6fbe1c5SMichael Ellerman 
195d6fbe1c5SMichael Ellerman ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf)
196d6fbe1c5SMichael Ellerman {
197d6fbe1c5SMichael Ellerman 	struct seq_buf s;
1986d44acaeSMichael Ellerman 	bool bcs, ccd;
199d6fbe1c5SMichael Ellerman 
200d6fbe1c5SMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
201d6fbe1c5SMichael Ellerman 
202d6fbe1c5SMichael Ellerman 	bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
203d6fbe1c5SMichael Ellerman 	ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
204d6fbe1c5SMichael Ellerman 
20592edf8dfSMichael Ellerman 	if (bcs || ccd) {
206d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
207d6fbe1c5SMichael Ellerman 
20892edf8dfSMichael Ellerman 		if (bcs)
209d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
210d6fbe1c5SMichael Ellerman 
21192edf8dfSMichael Ellerman 		if (bcs && ccd)
212ee13cb24SMichael Ellerman 			seq_buf_printf(&s, ", ");
21392edf8dfSMichael Ellerman 
21492edf8dfSMichael Ellerman 		if (ccd)
215ee13cb24SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch cache disabled");
21639e72bf9SMichael Ellerman 
21739e72bf9SMichael Ellerman 		if (link_stack_flush_enabled)
21839e72bf9SMichael Ellerman 			seq_buf_printf(&s, ", Software link stack flush");
21939e72bf9SMichael Ellerman 
22092edf8dfSMichael Ellerman 	} else if (count_cache_flush_type != COUNT_CACHE_FLUSH_NONE) {
22192edf8dfSMichael Ellerman 		seq_buf_printf(&s, "Mitigation: Software count cache flush");
222ee13cb24SMichael Ellerman 
223ee13cb24SMichael Ellerman 		if (count_cache_flush_type == COUNT_CACHE_FLUSH_HW)
224ee13cb24SMichael Ellerman 			seq_buf_printf(&s, " (hardware accelerated)");
22539e72bf9SMichael Ellerman 
22639e72bf9SMichael Ellerman 		if (link_stack_flush_enabled)
22739e72bf9SMichael Ellerman 			seq_buf_printf(&s, ", Software link stack flush");
22839e72bf9SMichael Ellerman 
229dfa88658SDiana Craciun 	} else if (btb_flush_enabled) {
230dfa88658SDiana Craciun 		seq_buf_printf(&s, "Mitigation: Branch predictor state flush");
231dfa88658SDiana Craciun 	} else {
232d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Vulnerable");
233dfa88658SDiana Craciun 	}
234d6fbe1c5SMichael Ellerman 
235d6fbe1c5SMichael Ellerman 	seq_buf_printf(&s, "\n");
236d6fbe1c5SMichael Ellerman 
237d6fbe1c5SMichael Ellerman 	return s.len;
238d6fbe1c5SMichael Ellerman }
239a048a07dSNicholas Piggin 
2406453b532SDiana Craciun #ifdef CONFIG_PPC_BOOK3S_64
241a048a07dSNicholas Piggin /*
242a048a07dSNicholas Piggin  * Store-forwarding barrier support.
243a048a07dSNicholas Piggin  */
244a048a07dSNicholas Piggin 
245a048a07dSNicholas Piggin static enum stf_barrier_type stf_enabled_flush_types;
246a048a07dSNicholas Piggin static bool no_stf_barrier;
247a048a07dSNicholas Piggin bool stf_barrier;
248a048a07dSNicholas Piggin 
249a048a07dSNicholas Piggin static int __init handle_no_stf_barrier(char *p)
250a048a07dSNicholas Piggin {
251a048a07dSNicholas Piggin 	pr_info("stf-barrier: disabled on command line.");
252a048a07dSNicholas Piggin 	no_stf_barrier = true;
253a048a07dSNicholas Piggin 	return 0;
254a048a07dSNicholas Piggin }
255a048a07dSNicholas Piggin 
256a048a07dSNicholas Piggin early_param("no_stf_barrier", handle_no_stf_barrier);
257a048a07dSNicholas Piggin 
258a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
259a048a07dSNicholas Piggin static int __init handle_ssbd(char *p)
260a048a07dSNicholas Piggin {
261a048a07dSNicholas Piggin 	if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) {
262a048a07dSNicholas Piggin 		/* Until firmware tells us, we have the barrier with auto */
263a048a07dSNicholas Piggin 		return 0;
264a048a07dSNicholas Piggin 	} else if (strncmp(p, "off", 3) == 0) {
265a048a07dSNicholas Piggin 		handle_no_stf_barrier(NULL);
266a048a07dSNicholas Piggin 		return 0;
267a048a07dSNicholas Piggin 	} else
268a048a07dSNicholas Piggin 		return 1;
269a048a07dSNicholas Piggin 
270a048a07dSNicholas Piggin 	return 0;
271a048a07dSNicholas Piggin }
272a048a07dSNicholas Piggin early_param("spec_store_bypass_disable", handle_ssbd);
273a048a07dSNicholas Piggin 
274a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
275a048a07dSNicholas Piggin static int __init handle_no_ssbd(char *p)
276a048a07dSNicholas Piggin {
277a048a07dSNicholas Piggin 	handle_no_stf_barrier(NULL);
278a048a07dSNicholas Piggin 	return 0;
279a048a07dSNicholas Piggin }
280a048a07dSNicholas Piggin early_param("nospec_store_bypass_disable", handle_no_ssbd);
281a048a07dSNicholas Piggin 
282a048a07dSNicholas Piggin static void stf_barrier_enable(bool enable)
283a048a07dSNicholas Piggin {
284a048a07dSNicholas Piggin 	if (enable)
285a048a07dSNicholas Piggin 		do_stf_barrier_fixups(stf_enabled_flush_types);
286a048a07dSNicholas Piggin 	else
287a048a07dSNicholas Piggin 		do_stf_barrier_fixups(STF_BARRIER_NONE);
288a048a07dSNicholas Piggin 
289a048a07dSNicholas Piggin 	stf_barrier = enable;
290a048a07dSNicholas Piggin }
291a048a07dSNicholas Piggin 
292a048a07dSNicholas Piggin void setup_stf_barrier(void)
293a048a07dSNicholas Piggin {
294a048a07dSNicholas Piggin 	enum stf_barrier_type type;
295a048a07dSNicholas Piggin 	bool enable, hv;
296a048a07dSNicholas Piggin 
297a048a07dSNicholas Piggin 	hv = cpu_has_feature(CPU_FTR_HVMODE);
298a048a07dSNicholas Piggin 
299a048a07dSNicholas Piggin 	/* Default to fallback in case fw-features are not available */
300a048a07dSNicholas Piggin 	if (cpu_has_feature(CPU_FTR_ARCH_300))
301a048a07dSNicholas Piggin 		type = STF_BARRIER_EIEIO;
302a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_207S))
303a048a07dSNicholas Piggin 		type = STF_BARRIER_SYNC_ORI;
304a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_206))
305a048a07dSNicholas Piggin 		type = STF_BARRIER_FALLBACK;
306a048a07dSNicholas Piggin 	else
307a048a07dSNicholas Piggin 		type = STF_BARRIER_NONE;
308a048a07dSNicholas Piggin 
309a048a07dSNicholas Piggin 	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
310a048a07dSNicholas Piggin 		(security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) ||
311a048a07dSNicholas Piggin 		 (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv));
312a048a07dSNicholas Piggin 
313a048a07dSNicholas Piggin 	if (type == STF_BARRIER_FALLBACK) {
314a048a07dSNicholas Piggin 		pr_info("stf-barrier: fallback barrier available\n");
315a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_SYNC_ORI) {
316a048a07dSNicholas Piggin 		pr_info("stf-barrier: hwsync barrier available\n");
317a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_EIEIO) {
318a048a07dSNicholas Piggin 		pr_info("stf-barrier: eieio barrier available\n");
319a048a07dSNicholas Piggin 	}
320a048a07dSNicholas Piggin 
321a048a07dSNicholas Piggin 	stf_enabled_flush_types = type;
322a048a07dSNicholas Piggin 
323782e69efSJosh Poimboeuf 	if (!no_stf_barrier && !cpu_mitigations_off())
324a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
325a048a07dSNicholas Piggin }
326a048a07dSNicholas Piggin 
327a048a07dSNicholas Piggin ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf)
328a048a07dSNicholas Piggin {
329a048a07dSNicholas Piggin 	if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) {
330a048a07dSNicholas Piggin 		const char *type;
331a048a07dSNicholas Piggin 		switch (stf_enabled_flush_types) {
332a048a07dSNicholas Piggin 		case STF_BARRIER_EIEIO:
333a048a07dSNicholas Piggin 			type = "eieio";
334a048a07dSNicholas Piggin 			break;
335a048a07dSNicholas Piggin 		case STF_BARRIER_SYNC_ORI:
336a048a07dSNicholas Piggin 			type = "hwsync";
337a048a07dSNicholas Piggin 			break;
338a048a07dSNicholas Piggin 		case STF_BARRIER_FALLBACK:
339a048a07dSNicholas Piggin 			type = "fallback";
340a048a07dSNicholas Piggin 			break;
341a048a07dSNicholas Piggin 		default:
342a048a07dSNicholas Piggin 			type = "unknown";
343a048a07dSNicholas Piggin 		}
344a048a07dSNicholas Piggin 		return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type);
345a048a07dSNicholas Piggin 	}
346a048a07dSNicholas Piggin 
347a048a07dSNicholas Piggin 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
348a048a07dSNicholas Piggin 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
349a048a07dSNicholas Piggin 		return sprintf(buf, "Not affected\n");
350a048a07dSNicholas Piggin 
351a048a07dSNicholas Piggin 	return sprintf(buf, "Vulnerable\n");
352a048a07dSNicholas Piggin }
353a048a07dSNicholas Piggin 
354a048a07dSNicholas Piggin #ifdef CONFIG_DEBUG_FS
355a048a07dSNicholas Piggin static int stf_barrier_set(void *data, u64 val)
356a048a07dSNicholas Piggin {
357a048a07dSNicholas Piggin 	bool enable;
358a048a07dSNicholas Piggin 
359a048a07dSNicholas Piggin 	if (val == 1)
360a048a07dSNicholas Piggin 		enable = true;
361a048a07dSNicholas Piggin 	else if (val == 0)
362a048a07dSNicholas Piggin 		enable = false;
363a048a07dSNicholas Piggin 	else
364a048a07dSNicholas Piggin 		return -EINVAL;
365a048a07dSNicholas Piggin 
366a048a07dSNicholas Piggin 	/* Only do anything if we're changing state */
367a048a07dSNicholas Piggin 	if (enable != stf_barrier)
368a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
369a048a07dSNicholas Piggin 
370a048a07dSNicholas Piggin 	return 0;
371a048a07dSNicholas Piggin }
372a048a07dSNicholas Piggin 
373a048a07dSNicholas Piggin static int stf_barrier_get(void *data, u64 *val)
374a048a07dSNicholas Piggin {
375a048a07dSNicholas Piggin 	*val = stf_barrier ? 1 : 0;
376a048a07dSNicholas Piggin 	return 0;
377a048a07dSNicholas Piggin }
378a048a07dSNicholas Piggin 
379a048a07dSNicholas Piggin DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n");
380a048a07dSNicholas Piggin 
381a048a07dSNicholas Piggin static __init int stf_barrier_debugfs_init(void)
382a048a07dSNicholas Piggin {
383a048a07dSNicholas Piggin 	debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier);
384a048a07dSNicholas Piggin 	return 0;
385a048a07dSNicholas Piggin }
386a048a07dSNicholas Piggin device_initcall(stf_barrier_debugfs_init);
387a048a07dSNicholas Piggin #endif /* CONFIG_DEBUG_FS */
388ee13cb24SMichael Ellerman 
38939e72bf9SMichael Ellerman static void no_count_cache_flush(void)
390ee13cb24SMichael Ellerman {
391ee13cb24SMichael Ellerman 	count_cache_flush_type = COUNT_CACHE_FLUSH_NONE;
392ee13cb24SMichael Ellerman 	pr_info("count-cache-flush: software flush disabled.\n");
39339e72bf9SMichael Ellerman }
39439e72bf9SMichael Ellerman 
39539e72bf9SMichael Ellerman static void toggle_count_cache_flush(bool enable)
39639e72bf9SMichael Ellerman {
39739e72bf9SMichael Ellerman 	if (!security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE) &&
39839e72bf9SMichael Ellerman 	    !security_ftr_enabled(SEC_FTR_FLUSH_LINK_STACK))
39939e72bf9SMichael Ellerman 		enable = false;
40039e72bf9SMichael Ellerman 
40139e72bf9SMichael Ellerman 	if (!enable) {
40239e72bf9SMichael Ellerman 		patch_instruction_site(&patch__call_flush_count_cache, PPC_INST_NOP);
403*af2e8c68SMichael Ellerman #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
404*af2e8c68SMichael Ellerman 		patch_instruction_site(&patch__call_kvm_flush_link_stack, PPC_INST_NOP);
405*af2e8c68SMichael Ellerman #endif
40639e72bf9SMichael Ellerman 		pr_info("link-stack-flush: software flush disabled.\n");
40739e72bf9SMichael Ellerman 		link_stack_flush_enabled = false;
40839e72bf9SMichael Ellerman 		no_count_cache_flush();
409ee13cb24SMichael Ellerman 		return;
410ee13cb24SMichael Ellerman 	}
411ee13cb24SMichael Ellerman 
41239e72bf9SMichael Ellerman 	// This enables the branch from _switch to flush_count_cache
413ee13cb24SMichael Ellerman 	patch_branch_site(&patch__call_flush_count_cache,
414ee13cb24SMichael Ellerman 			  (u64)&flush_count_cache, BRANCH_SET_LINK);
415ee13cb24SMichael Ellerman 
416*af2e8c68SMichael Ellerman #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
417*af2e8c68SMichael Ellerman 	// This enables the branch from guest_exit_cont to kvm_flush_link_stack
418*af2e8c68SMichael Ellerman 	patch_branch_site(&patch__call_kvm_flush_link_stack,
419*af2e8c68SMichael Ellerman 			  (u64)&kvm_flush_link_stack, BRANCH_SET_LINK);
420*af2e8c68SMichael Ellerman #endif
421*af2e8c68SMichael Ellerman 
42239e72bf9SMichael Ellerman 	pr_info("link-stack-flush: software flush enabled.\n");
42339e72bf9SMichael Ellerman 	link_stack_flush_enabled = true;
42439e72bf9SMichael Ellerman 
42539e72bf9SMichael Ellerman 	// If we just need to flush the link stack, patch an early return
42639e72bf9SMichael Ellerman 	if (!security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) {
42739e72bf9SMichael Ellerman 		patch_instruction_site(&patch__flush_link_stack_return, PPC_INST_BLR);
42839e72bf9SMichael Ellerman 		no_count_cache_flush();
42939e72bf9SMichael Ellerman 		return;
43039e72bf9SMichael Ellerman 	}
43139e72bf9SMichael Ellerman 
432ee13cb24SMichael Ellerman 	if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) {
433ee13cb24SMichael Ellerman 		count_cache_flush_type = COUNT_CACHE_FLUSH_SW;
434ee13cb24SMichael Ellerman 		pr_info("count-cache-flush: full software flush sequence enabled.\n");
435ee13cb24SMichael Ellerman 		return;
436ee13cb24SMichael Ellerman 	}
437ee13cb24SMichael Ellerman 
438ee13cb24SMichael Ellerman 	patch_instruction_site(&patch__flush_count_cache_return, PPC_INST_BLR);
439ee13cb24SMichael Ellerman 	count_cache_flush_type = COUNT_CACHE_FLUSH_HW;
440ee13cb24SMichael Ellerman 	pr_info("count-cache-flush: hardware assisted flush sequence enabled\n");
441ee13cb24SMichael Ellerman }
442ee13cb24SMichael Ellerman 
443ee13cb24SMichael Ellerman void setup_count_cache_flush(void)
444ee13cb24SMichael Ellerman {
445d8f0e0b0SChristopher M. Riedl 	bool enable = true;
446d8f0e0b0SChristopher M. Riedl 
447d8f0e0b0SChristopher M. Riedl 	if (no_spectrev2 || cpu_mitigations_off()) {
448d8f0e0b0SChristopher M. Riedl 		if (security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED) ||
449d8f0e0b0SChristopher M. Riedl 		    security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED))
45039e72bf9SMichael Ellerman 			pr_warn("Spectre v2 mitigations not fully under software control, can't disable\n");
451d8f0e0b0SChristopher M. Riedl 
452d8f0e0b0SChristopher M. Riedl 		enable = false;
453d8f0e0b0SChristopher M. Riedl 	}
454d8f0e0b0SChristopher M. Riedl 
45539e72bf9SMichael Ellerman 	/*
45639e72bf9SMichael Ellerman 	 * There's no firmware feature flag/hypervisor bit to tell us we need to
45739e72bf9SMichael Ellerman 	 * flush the link stack on context switch. So we set it here if we see
45839e72bf9SMichael Ellerman 	 * either of the Spectre v2 mitigations that aim to protect userspace.
45939e72bf9SMichael Ellerman 	 */
46039e72bf9SMichael Ellerman 	if (security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED) ||
46139e72bf9SMichael Ellerman 	    security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE))
46239e72bf9SMichael Ellerman 		security_ftr_set(SEC_FTR_FLUSH_LINK_STACK);
46339e72bf9SMichael Ellerman 
464d8f0e0b0SChristopher M. Riedl 	toggle_count_cache_flush(enable);
465ee13cb24SMichael Ellerman }
466ee13cb24SMichael Ellerman 
467ee13cb24SMichael Ellerman #ifdef CONFIG_DEBUG_FS
468ee13cb24SMichael Ellerman static int count_cache_flush_set(void *data, u64 val)
469ee13cb24SMichael Ellerman {
470ee13cb24SMichael Ellerman 	bool enable;
471ee13cb24SMichael Ellerman 
472ee13cb24SMichael Ellerman 	if (val == 1)
473ee13cb24SMichael Ellerman 		enable = true;
474ee13cb24SMichael Ellerman 	else if (val == 0)
475ee13cb24SMichael Ellerman 		enable = false;
476ee13cb24SMichael Ellerman 	else
477ee13cb24SMichael Ellerman 		return -EINVAL;
478ee13cb24SMichael Ellerman 
479ee13cb24SMichael Ellerman 	toggle_count_cache_flush(enable);
480ee13cb24SMichael Ellerman 
481ee13cb24SMichael Ellerman 	return 0;
482ee13cb24SMichael Ellerman }
483ee13cb24SMichael Ellerman 
484ee13cb24SMichael Ellerman static int count_cache_flush_get(void *data, u64 *val)
485ee13cb24SMichael Ellerman {
486ee13cb24SMichael Ellerman 	if (count_cache_flush_type == COUNT_CACHE_FLUSH_NONE)
487ee13cb24SMichael Ellerman 		*val = 0;
488ee13cb24SMichael Ellerman 	else
489ee13cb24SMichael Ellerman 		*val = 1;
490ee13cb24SMichael Ellerman 
491ee13cb24SMichael Ellerman 	return 0;
492ee13cb24SMichael Ellerman }
493ee13cb24SMichael Ellerman 
494ee13cb24SMichael Ellerman DEFINE_SIMPLE_ATTRIBUTE(fops_count_cache_flush, count_cache_flush_get,
495ee13cb24SMichael Ellerman 			count_cache_flush_set, "%llu\n");
496ee13cb24SMichael Ellerman 
497ee13cb24SMichael Ellerman static __init int count_cache_flush_debugfs_init(void)
498ee13cb24SMichael Ellerman {
499ee13cb24SMichael Ellerman 	debugfs_create_file("count_cache_flush", 0600, powerpc_debugfs_root,
500ee13cb24SMichael Ellerman 			    NULL, &fops_count_cache_flush);
501ee13cb24SMichael Ellerman 	return 0;
502ee13cb24SMichael Ellerman }
503ee13cb24SMichael Ellerman device_initcall(count_cache_flush_debugfs_init);
504ee13cb24SMichael Ellerman #endif /* CONFIG_DEBUG_FS */
5056453b532SDiana Craciun #endif /* CONFIG_PPC_BOOK3S_64 */
506