xref: /linux-6.15/arch/powerpc/kernel/security.c (revision 4d24e21c)
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>
10d93e5e2dSMichael Ellerman #include <linux/nospec.h>
11d93e5e2dSMichael Ellerman #include <linux/prctl.h>
12ff348355SMichael Ellerman #include <linux/seq_buf.h>
138ad33041SMichael Ellerman 
14ee13cb24SMichael Ellerman #include <asm/asm-prototypes.h>
15ee13cb24SMichael Ellerman #include <asm/code-patching.h>
16a048a07dSNicholas Piggin #include <asm/debugfs.h>
179a868f63SMichael Ellerman #include <asm/security_features.h>
182eea7f06SMichal Suchanek #include <asm/setup.h>
1975346251SJordan Niethe #include <asm/inst.h>
209a868f63SMichael Ellerman 
219a868f63SMichael Ellerman 
223b05a1e5SGeert Uytterhoeven u64 powerpc_security_features __read_mostly = SEC_FTR_DEFAULT;
238ad33041SMichael Ellerman 
241026798cSNicholas Piggin enum branch_cache_flush_type {
251026798cSNicholas Piggin 	BRANCH_CACHE_FLUSH_NONE	= 0x1,
261026798cSNicholas Piggin 	BRANCH_CACHE_FLUSH_SW	= 0x2,
271026798cSNicholas Piggin 	BRANCH_CACHE_FLUSH_HW	= 0x4,
28ee13cb24SMichael Ellerman };
291026798cSNicholas Piggin static enum branch_cache_flush_type count_cache_flush_type = BRANCH_CACHE_FLUSH_NONE;
30c06ac277SNicholas Piggin static enum branch_cache_flush_type link_stack_flush_type = BRANCH_CACHE_FLUSH_NONE;
31ee13cb24SMichael Ellerman 
32815069caSMichal Suchanek bool barrier_nospec_enabled;
33cf175dc3SDiana Craciun static bool no_nospec;
34f633a8adSDiana Craciun static bool btb_flush_enabled;
35d8f0e0b0SChristopher M. Riedl #if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3S_64)
36f633a8adSDiana Craciun static bool no_spectrev2;
37f633a8adSDiana Craciun #endif
382eea7f06SMichal Suchanek 
392eea7f06SMichal Suchanek static void enable_barrier_nospec(bool enable)
402eea7f06SMichal Suchanek {
412eea7f06SMichal Suchanek 	barrier_nospec_enabled = enable;
422eea7f06SMichal Suchanek 	do_barrier_nospec_fixups(enable);
432eea7f06SMichal Suchanek }
442eea7f06SMichal Suchanek 
45cb3d6759SMichal Suchanek void setup_barrier_nospec(void)
46cb3d6759SMichal Suchanek {
47cb3d6759SMichal Suchanek 	bool enable;
48cb3d6759SMichal Suchanek 
49cb3d6759SMichal Suchanek 	/*
50cb3d6759SMichal Suchanek 	 * It would make sense to check SEC_FTR_SPEC_BAR_ORI31 below as well.
51cb3d6759SMichal Suchanek 	 * But there's a good reason not to. The two flags we check below are
52cb3d6759SMichal Suchanek 	 * both are enabled by default in the kernel, so if the hcall is not
53cb3d6759SMichal Suchanek 	 * functional they will be enabled.
54cb3d6759SMichal Suchanek 	 * On a system where the host firmware has been updated (so the ori
55cb3d6759SMichal Suchanek 	 * functions as a barrier), but on which the hypervisor (KVM/Qemu) has
56cb3d6759SMichal Suchanek 	 * not been updated, we would like to enable the barrier. Dropping the
57cb3d6759SMichal Suchanek 	 * check for SEC_FTR_SPEC_BAR_ORI31 achieves that. The only downside is
58cb3d6759SMichal Suchanek 	 * we potentially enable the barrier on systems where the host firmware
59cb3d6759SMichal Suchanek 	 * is not updated, but that's harmless as it's a no-op.
60cb3d6759SMichal Suchanek 	 */
61cb3d6759SMichal Suchanek 	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
62cb3d6759SMichal Suchanek 		 security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR);
63cb3d6759SMichal Suchanek 
64782e69efSJosh Poimboeuf 	if (!no_nospec && !cpu_mitigations_off())
65cb3d6759SMichal Suchanek 		enable_barrier_nospec(enable);
66cb3d6759SMichal Suchanek }
67cb3d6759SMichal Suchanek 
68cf175dc3SDiana Craciun static int __init handle_nospectre_v1(char *p)
69cf175dc3SDiana Craciun {
70cf175dc3SDiana Craciun 	no_nospec = true;
71cf175dc3SDiana Craciun 
72cf175dc3SDiana Craciun 	return 0;
73cf175dc3SDiana Craciun }
74cf175dc3SDiana Craciun early_param("nospectre_v1", handle_nospectre_v1);
75cf175dc3SDiana Craciun 
76cb3d6759SMichal Suchanek #ifdef CONFIG_DEBUG_FS
77cb3d6759SMichal Suchanek static int barrier_nospec_set(void *data, u64 val)
78cb3d6759SMichal Suchanek {
79cb3d6759SMichal Suchanek 	switch (val) {
80cb3d6759SMichal Suchanek 	case 0:
81cb3d6759SMichal Suchanek 	case 1:
82cb3d6759SMichal Suchanek 		break;
83cb3d6759SMichal Suchanek 	default:
84cb3d6759SMichal Suchanek 		return -EINVAL;
85cb3d6759SMichal Suchanek 	}
86cb3d6759SMichal Suchanek 
87cb3d6759SMichal Suchanek 	if (!!val == !!barrier_nospec_enabled)
88cb3d6759SMichal Suchanek 		return 0;
89cb3d6759SMichal Suchanek 
90cb3d6759SMichal Suchanek 	enable_barrier_nospec(!!val);
91cb3d6759SMichal Suchanek 
92cb3d6759SMichal Suchanek 	return 0;
93cb3d6759SMichal Suchanek }
94cb3d6759SMichal Suchanek 
95cb3d6759SMichal Suchanek static int barrier_nospec_get(void *data, u64 *val)
96cb3d6759SMichal Suchanek {
97cb3d6759SMichal Suchanek 	*val = barrier_nospec_enabled ? 1 : 0;
98cb3d6759SMichal Suchanek 	return 0;
99cb3d6759SMichal Suchanek }
100cb3d6759SMichal Suchanek 
101090d5ab9SYueHaibing DEFINE_DEBUGFS_ATTRIBUTE(fops_barrier_nospec, barrier_nospec_get,
102090d5ab9SYueHaibing 			 barrier_nospec_set, "%llu\n");
103cb3d6759SMichal Suchanek 
104cb3d6759SMichal Suchanek static __init int barrier_nospec_debugfs_init(void)
105cb3d6759SMichal Suchanek {
106090d5ab9SYueHaibing 	debugfs_create_file_unsafe("barrier_nospec", 0600,
107090d5ab9SYueHaibing 				   powerpc_debugfs_root, NULL,
108cb3d6759SMichal Suchanek 				   &fops_barrier_nospec);
109cb3d6759SMichal Suchanek 	return 0;
110cb3d6759SMichal Suchanek }
111cb3d6759SMichal Suchanek device_initcall(barrier_nospec_debugfs_init);
112398af571SMichael Ellerman 
113398af571SMichael Ellerman static __init int security_feature_debugfs_init(void)
114398af571SMichael Ellerman {
115398af571SMichael Ellerman 	debugfs_create_x64("security_features", 0400, powerpc_debugfs_root,
1163b05a1e5SGeert Uytterhoeven 			   &powerpc_security_features);
117398af571SMichael Ellerman 	return 0;
118398af571SMichael Ellerman }
119398af571SMichael Ellerman device_initcall(security_feature_debugfs_init);
120cb3d6759SMichal Suchanek #endif /* CONFIG_DEBUG_FS */
121cb3d6759SMichal Suchanek 
122d8f0e0b0SChristopher M. Riedl #if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3S_64)
123f633a8adSDiana Craciun static int __init handle_nospectre_v2(char *p)
124f633a8adSDiana Craciun {
125f633a8adSDiana Craciun 	no_spectrev2 = true;
126f633a8adSDiana Craciun 
127f633a8adSDiana Craciun 	return 0;
128f633a8adSDiana Craciun }
129f633a8adSDiana Craciun early_param("nospectre_v2", handle_nospectre_v2);
130d8f0e0b0SChristopher M. Riedl #endif /* CONFIG_PPC_FSL_BOOK3E || CONFIG_PPC_BOOK3S_64 */
131d8f0e0b0SChristopher M. Riedl 
132d8f0e0b0SChristopher M. Riedl #ifdef CONFIG_PPC_FSL_BOOK3E
133f633a8adSDiana Craciun void setup_spectre_v2(void)
134f633a8adSDiana Craciun {
135782e69efSJosh Poimboeuf 	if (no_spectrev2 || cpu_mitigations_off())
136f633a8adSDiana Craciun 		do_btb_flush_fixups();
137f633a8adSDiana Craciun 	else
138f633a8adSDiana Craciun 		btb_flush_enabled = true;
139f633a8adSDiana Craciun }
140f633a8adSDiana Craciun #endif /* CONFIG_PPC_FSL_BOOK3E */
141f633a8adSDiana Craciun 
142406d2b6aSDiana Craciun #ifdef CONFIG_PPC_BOOK3S_64
1438ad33041SMichael Ellerman ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
1448ad33041SMichael Ellerman {
145ff348355SMichael Ellerman 	bool thread_priv;
146ff348355SMichael Ellerman 
147ff348355SMichael Ellerman 	thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV);
148ff348355SMichael Ellerman 
1494e706af3SGustavo L. F. Walbon 	if (rfi_flush) {
150ff348355SMichael Ellerman 		struct seq_buf s;
151ff348355SMichael Ellerman 		seq_buf_init(&s, buf, PAGE_SIZE - 1);
152ff348355SMichael Ellerman 
1534e706af3SGustavo L. F. Walbon 		seq_buf_printf(&s, "Mitigation: RFI Flush");
154ff348355SMichael Ellerman 		if (thread_priv)
1554e706af3SGustavo L. F. Walbon 			seq_buf_printf(&s, ", L1D private per thread");
156ff348355SMichael Ellerman 
157ff348355SMichael Ellerman 		seq_buf_printf(&s, "\n");
158ff348355SMichael Ellerman 
159ff348355SMichael Ellerman 		return s.len;
160ff348355SMichael Ellerman 	}
161ff348355SMichael Ellerman 
1624e706af3SGustavo L. F. Walbon 	if (thread_priv)
1634e706af3SGustavo L. F. Walbon 		return sprintf(buf, "Vulnerable: L1D private per thread\n");
1644e706af3SGustavo L. F. Walbon 
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 }
1718e6b6da9SAnthony Steinhauser 
1728e6b6da9SAnthony Steinhauser ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *buf)
1738e6b6da9SAnthony Steinhauser {
1748e6b6da9SAnthony Steinhauser 	return cpu_show_meltdown(dev, attr, buf);
1758e6b6da9SAnthony Steinhauser }
176406d2b6aSDiana Craciun #endif
17756986016SMichael Ellerman 
17856986016SMichael Ellerman ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
17956986016SMichael Ellerman {
1806d44acaeSMichael Ellerman 	struct seq_buf s;
18156986016SMichael Ellerman 
1826d44acaeSMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
1836d44acaeSMichael Ellerman 
1846d44acaeSMichael Ellerman 	if (security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) {
185a3775145SMichal Suchanek 		if (barrier_nospec_enabled)
1866d44acaeSMichael Ellerman 			seq_buf_printf(&s, "Mitigation: __user pointer sanitization");
1876d44acaeSMichael Ellerman 		else
1886d44acaeSMichael Ellerman 			seq_buf_printf(&s, "Vulnerable");
189a3775145SMichal Suchanek 
1906d44acaeSMichael Ellerman 		if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31))
1916d44acaeSMichael Ellerman 			seq_buf_printf(&s, ", ori31 speculation barrier enabled");
1926d44acaeSMichael Ellerman 
1936d44acaeSMichael Ellerman 		seq_buf_printf(&s, "\n");
1946d44acaeSMichael Ellerman 	} else
1956d44acaeSMichael Ellerman 		seq_buf_printf(&s, "Not affected\n");
1966d44acaeSMichael Ellerman 
1976d44acaeSMichael Ellerman 	return s.len;
19856986016SMichael Ellerman }
199d6fbe1c5SMichael Ellerman 
200d6fbe1c5SMichael Ellerman ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf)
201d6fbe1c5SMichael Ellerman {
202d6fbe1c5SMichael Ellerman 	struct seq_buf s;
2036d44acaeSMichael Ellerman 	bool bcs, ccd;
204d6fbe1c5SMichael Ellerman 
205d6fbe1c5SMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
206d6fbe1c5SMichael Ellerman 
207d6fbe1c5SMichael Ellerman 	bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
208d6fbe1c5SMichael Ellerman 	ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
209d6fbe1c5SMichael Ellerman 
21092edf8dfSMichael Ellerman 	if (bcs || ccd) {
211d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
212d6fbe1c5SMichael Ellerman 
21392edf8dfSMichael Ellerman 		if (bcs)
214d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
215d6fbe1c5SMichael Ellerman 
21692edf8dfSMichael Ellerman 		if (bcs && ccd)
217ee13cb24SMichael Ellerman 			seq_buf_printf(&s, ", ");
21892edf8dfSMichael Ellerman 
21992edf8dfSMichael Ellerman 		if (ccd)
220ee13cb24SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch cache disabled");
22139e72bf9SMichael Ellerman 
2221026798cSNicholas Piggin 	} else if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) {
22392edf8dfSMichael Ellerman 		seq_buf_printf(&s, "Mitigation: Software count cache flush");
224ee13cb24SMichael Ellerman 
2251026798cSNicholas Piggin 		if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW)
226ee13cb24SMichael Ellerman 			seq_buf_printf(&s, " (hardware accelerated)");
22739e72bf9SMichael Ellerman 
228dfa88658SDiana Craciun 	} else if (btb_flush_enabled) {
229dfa88658SDiana Craciun 		seq_buf_printf(&s, "Mitigation: Branch predictor state flush");
230dfa88658SDiana Craciun 	} else {
231d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Vulnerable");
232dfa88658SDiana Craciun 	}
233d6fbe1c5SMichael Ellerman 
234*4d24e21cSNicholas Piggin 	if (bcs || ccd || count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) {
235*4d24e21cSNicholas Piggin 		if (link_stack_flush_type != BRANCH_CACHE_FLUSH_NONE)
236*4d24e21cSNicholas Piggin 			seq_buf_printf(&s, ", Software link stack flush");
237*4d24e21cSNicholas Piggin 		if (link_stack_flush_type == BRANCH_CACHE_FLUSH_HW)
238*4d24e21cSNicholas Piggin 			seq_buf_printf(&s, " (hardware accelerated)");
239*4d24e21cSNicholas Piggin 	}
240*4d24e21cSNicholas Piggin 
241d6fbe1c5SMichael Ellerman 	seq_buf_printf(&s, "\n");
242d6fbe1c5SMichael Ellerman 
243d6fbe1c5SMichael Ellerman 	return s.len;
244d6fbe1c5SMichael Ellerman }
245a048a07dSNicholas Piggin 
2466453b532SDiana Craciun #ifdef CONFIG_PPC_BOOK3S_64
247a048a07dSNicholas Piggin /*
248a048a07dSNicholas Piggin  * Store-forwarding barrier support.
249a048a07dSNicholas Piggin  */
250a048a07dSNicholas Piggin 
251a048a07dSNicholas Piggin static enum stf_barrier_type stf_enabled_flush_types;
252a048a07dSNicholas Piggin static bool no_stf_barrier;
253a048a07dSNicholas Piggin bool stf_barrier;
254a048a07dSNicholas Piggin 
255a048a07dSNicholas Piggin static int __init handle_no_stf_barrier(char *p)
256a048a07dSNicholas Piggin {
257a048a07dSNicholas Piggin 	pr_info("stf-barrier: disabled on command line.");
258a048a07dSNicholas Piggin 	no_stf_barrier = true;
259a048a07dSNicholas Piggin 	return 0;
260a048a07dSNicholas Piggin }
261a048a07dSNicholas Piggin 
262a048a07dSNicholas Piggin early_param("no_stf_barrier", handle_no_stf_barrier);
263a048a07dSNicholas Piggin 
264a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
265a048a07dSNicholas Piggin static int __init handle_ssbd(char *p)
266a048a07dSNicholas Piggin {
267a048a07dSNicholas Piggin 	if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) {
268a048a07dSNicholas Piggin 		/* Until firmware tells us, we have the barrier with auto */
269a048a07dSNicholas Piggin 		return 0;
270a048a07dSNicholas Piggin 	} else if (strncmp(p, "off", 3) == 0) {
271a048a07dSNicholas Piggin 		handle_no_stf_barrier(NULL);
272a048a07dSNicholas Piggin 		return 0;
273a048a07dSNicholas Piggin 	} else
274a048a07dSNicholas Piggin 		return 1;
275a048a07dSNicholas Piggin 
276a048a07dSNicholas Piggin 	return 0;
277a048a07dSNicholas Piggin }
278a048a07dSNicholas Piggin early_param("spec_store_bypass_disable", handle_ssbd);
279a048a07dSNicholas Piggin 
280a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
281a048a07dSNicholas Piggin static int __init handle_no_ssbd(char *p)
282a048a07dSNicholas Piggin {
283a048a07dSNicholas Piggin 	handle_no_stf_barrier(NULL);
284a048a07dSNicholas Piggin 	return 0;
285a048a07dSNicholas Piggin }
286a048a07dSNicholas Piggin early_param("nospec_store_bypass_disable", handle_no_ssbd);
287a048a07dSNicholas Piggin 
288a048a07dSNicholas Piggin static void stf_barrier_enable(bool enable)
289a048a07dSNicholas Piggin {
290a048a07dSNicholas Piggin 	if (enable)
291a048a07dSNicholas Piggin 		do_stf_barrier_fixups(stf_enabled_flush_types);
292a048a07dSNicholas Piggin 	else
293a048a07dSNicholas Piggin 		do_stf_barrier_fixups(STF_BARRIER_NONE);
294a048a07dSNicholas Piggin 
295a048a07dSNicholas Piggin 	stf_barrier = enable;
296a048a07dSNicholas Piggin }
297a048a07dSNicholas Piggin 
298a048a07dSNicholas Piggin void setup_stf_barrier(void)
299a048a07dSNicholas Piggin {
300a048a07dSNicholas Piggin 	enum stf_barrier_type type;
301a048a07dSNicholas Piggin 	bool enable, hv;
302a048a07dSNicholas Piggin 
303a048a07dSNicholas Piggin 	hv = cpu_has_feature(CPU_FTR_HVMODE);
304a048a07dSNicholas Piggin 
305a048a07dSNicholas Piggin 	/* Default to fallback in case fw-features are not available */
306a048a07dSNicholas Piggin 	if (cpu_has_feature(CPU_FTR_ARCH_300))
307a048a07dSNicholas Piggin 		type = STF_BARRIER_EIEIO;
308a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_207S))
309a048a07dSNicholas Piggin 		type = STF_BARRIER_SYNC_ORI;
310a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_206))
311a048a07dSNicholas Piggin 		type = STF_BARRIER_FALLBACK;
312a048a07dSNicholas Piggin 	else
313a048a07dSNicholas Piggin 		type = STF_BARRIER_NONE;
314a048a07dSNicholas Piggin 
315a048a07dSNicholas Piggin 	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
316a048a07dSNicholas Piggin 		(security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) ||
317a048a07dSNicholas Piggin 		 (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv));
318a048a07dSNicholas Piggin 
319a048a07dSNicholas Piggin 	if (type == STF_BARRIER_FALLBACK) {
320a048a07dSNicholas Piggin 		pr_info("stf-barrier: fallback barrier available\n");
321a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_SYNC_ORI) {
322a048a07dSNicholas Piggin 		pr_info("stf-barrier: hwsync barrier available\n");
323a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_EIEIO) {
324a048a07dSNicholas Piggin 		pr_info("stf-barrier: eieio barrier available\n");
325a048a07dSNicholas Piggin 	}
326a048a07dSNicholas Piggin 
327a048a07dSNicholas Piggin 	stf_enabled_flush_types = type;
328a048a07dSNicholas Piggin 
329782e69efSJosh Poimboeuf 	if (!no_stf_barrier && !cpu_mitigations_off())
330a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
331a048a07dSNicholas Piggin }
332a048a07dSNicholas Piggin 
333a048a07dSNicholas Piggin ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf)
334a048a07dSNicholas Piggin {
335a048a07dSNicholas Piggin 	if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) {
336a048a07dSNicholas Piggin 		const char *type;
337a048a07dSNicholas Piggin 		switch (stf_enabled_flush_types) {
338a048a07dSNicholas Piggin 		case STF_BARRIER_EIEIO:
339a048a07dSNicholas Piggin 			type = "eieio";
340a048a07dSNicholas Piggin 			break;
341a048a07dSNicholas Piggin 		case STF_BARRIER_SYNC_ORI:
342a048a07dSNicholas Piggin 			type = "hwsync";
343a048a07dSNicholas Piggin 			break;
344a048a07dSNicholas Piggin 		case STF_BARRIER_FALLBACK:
345a048a07dSNicholas Piggin 			type = "fallback";
346a048a07dSNicholas Piggin 			break;
347a048a07dSNicholas Piggin 		default:
348a048a07dSNicholas Piggin 			type = "unknown";
349a048a07dSNicholas Piggin 		}
350a048a07dSNicholas Piggin 		return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type);
351a048a07dSNicholas Piggin 	}
352a048a07dSNicholas Piggin 
353a048a07dSNicholas Piggin 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
354a048a07dSNicholas Piggin 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
355a048a07dSNicholas Piggin 		return sprintf(buf, "Not affected\n");
356a048a07dSNicholas Piggin 
357a048a07dSNicholas Piggin 	return sprintf(buf, "Vulnerable\n");
358a048a07dSNicholas Piggin }
359a048a07dSNicholas Piggin 
360d93e5e2dSMichael Ellerman static int ssb_prctl_get(struct task_struct *task)
361d93e5e2dSMichael Ellerman {
362d93e5e2dSMichael Ellerman 	if (stf_enabled_flush_types == STF_BARRIER_NONE)
363d93e5e2dSMichael Ellerman 		/*
364d93e5e2dSMichael Ellerman 		 * We don't have an explicit signal from firmware that we're
365d93e5e2dSMichael Ellerman 		 * vulnerable or not, we only have certain CPU revisions that
366d93e5e2dSMichael Ellerman 		 * are known to be vulnerable.
367d93e5e2dSMichael Ellerman 		 *
368d93e5e2dSMichael Ellerman 		 * We assume that if we're on another CPU, where the barrier is
369d93e5e2dSMichael Ellerman 		 * NONE, then we are not vulnerable.
370d93e5e2dSMichael Ellerman 		 */
371d93e5e2dSMichael Ellerman 		return PR_SPEC_NOT_AFFECTED;
372d93e5e2dSMichael Ellerman 	else
373d93e5e2dSMichael Ellerman 		/*
374d93e5e2dSMichael Ellerman 		 * If we do have a barrier type then we are vulnerable. The
375d93e5e2dSMichael Ellerman 		 * barrier is not a global or per-process mitigation, so the
376d93e5e2dSMichael Ellerman 		 * only value we can report here is PR_SPEC_ENABLE, which
377d93e5e2dSMichael Ellerman 		 * appears as "vulnerable" in /proc.
378d93e5e2dSMichael Ellerman 		 */
379d93e5e2dSMichael Ellerman 		return PR_SPEC_ENABLE;
380d93e5e2dSMichael Ellerman 
381d93e5e2dSMichael Ellerman 	return -EINVAL;
382d93e5e2dSMichael Ellerman }
383d93e5e2dSMichael Ellerman 
384d93e5e2dSMichael Ellerman int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which)
385d93e5e2dSMichael Ellerman {
386d93e5e2dSMichael Ellerman 	switch (which) {
387d93e5e2dSMichael Ellerman 	case PR_SPEC_STORE_BYPASS:
388d93e5e2dSMichael Ellerman 		return ssb_prctl_get(task);
389d93e5e2dSMichael Ellerman 	default:
390d93e5e2dSMichael Ellerman 		return -ENODEV;
391d93e5e2dSMichael Ellerman 	}
392d93e5e2dSMichael Ellerman }
393d93e5e2dSMichael Ellerman 
394a048a07dSNicholas Piggin #ifdef CONFIG_DEBUG_FS
395a048a07dSNicholas Piggin static int stf_barrier_set(void *data, u64 val)
396a048a07dSNicholas Piggin {
397a048a07dSNicholas Piggin 	bool enable;
398a048a07dSNicholas Piggin 
399a048a07dSNicholas Piggin 	if (val == 1)
400a048a07dSNicholas Piggin 		enable = true;
401a048a07dSNicholas Piggin 	else if (val == 0)
402a048a07dSNicholas Piggin 		enable = false;
403a048a07dSNicholas Piggin 	else
404a048a07dSNicholas Piggin 		return -EINVAL;
405a048a07dSNicholas Piggin 
406a048a07dSNicholas Piggin 	/* Only do anything if we're changing state */
407a048a07dSNicholas Piggin 	if (enable != stf_barrier)
408a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
409a048a07dSNicholas Piggin 
410a048a07dSNicholas Piggin 	return 0;
411a048a07dSNicholas Piggin }
412a048a07dSNicholas Piggin 
413a048a07dSNicholas Piggin static int stf_barrier_get(void *data, u64 *val)
414a048a07dSNicholas Piggin {
415a048a07dSNicholas Piggin 	*val = stf_barrier ? 1 : 0;
416a048a07dSNicholas Piggin 	return 0;
417a048a07dSNicholas Piggin }
418a048a07dSNicholas Piggin 
419090d5ab9SYueHaibing DEFINE_DEBUGFS_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set,
420090d5ab9SYueHaibing 			 "%llu\n");
421a048a07dSNicholas Piggin 
422a048a07dSNicholas Piggin static __init int stf_barrier_debugfs_init(void)
423a048a07dSNicholas Piggin {
424090d5ab9SYueHaibing 	debugfs_create_file_unsafe("stf_barrier", 0600, powerpc_debugfs_root,
425090d5ab9SYueHaibing 				   NULL, &fops_stf_barrier);
426a048a07dSNicholas Piggin 	return 0;
427a048a07dSNicholas Piggin }
428a048a07dSNicholas Piggin device_initcall(stf_barrier_debugfs_init);
429a048a07dSNicholas Piggin #endif /* CONFIG_DEBUG_FS */
430ee13cb24SMichael Ellerman 
431c0036549SNicholas Piggin static void update_branch_cache_flush(void)
432ee13cb24SMichael Ellerman {
433c0036549SNicholas Piggin #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
434c0036549SNicholas Piggin 	// This controls the branch from guest_exit_cont to kvm_flush_link_stack
435c0036549SNicholas Piggin 	if (link_stack_flush_type == BRANCH_CACHE_FLUSH_NONE) {
436c0036549SNicholas Piggin 		patch_instruction_site(&patch__call_kvm_flush_link_stack,
437c0036549SNicholas Piggin 				       ppc_inst(PPC_INST_NOP));
438c0036549SNicholas Piggin 	} else {
439*4d24e21cSNicholas Piggin 		// Could use HW flush, but that could also flush count cache
440c0036549SNicholas Piggin 		patch_branch_site(&patch__call_kvm_flush_link_stack,
441c0036549SNicholas Piggin 				  (u64)&kvm_flush_link_stack, BRANCH_SET_LINK);
442c0036549SNicholas Piggin 	}
443c0036549SNicholas Piggin #endif
444c0036549SNicholas Piggin 
445c0036549SNicholas Piggin 	// This controls the branch from _switch to flush_branch_caches
446c0036549SNicholas Piggin 	if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE &&
447c0036549SNicholas Piggin 	    link_stack_flush_type == BRANCH_CACHE_FLUSH_NONE) {
448c0036549SNicholas Piggin 		patch_instruction_site(&patch__call_flush_branch_caches,
449c0036549SNicholas Piggin 				       ppc_inst(PPC_INST_NOP));
450*4d24e21cSNicholas Piggin 	} else if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW &&
451*4d24e21cSNicholas Piggin 		   link_stack_flush_type == BRANCH_CACHE_FLUSH_HW) {
452*4d24e21cSNicholas Piggin 		patch_instruction_site(&patch__call_flush_branch_caches,
453*4d24e21cSNicholas Piggin 				       ppc_inst(PPC_INST_BCCTR_FLUSH));
454c0036549SNicholas Piggin 	} else {
455c0036549SNicholas Piggin 		patch_branch_site(&patch__call_flush_branch_caches,
456c0036549SNicholas Piggin 				  (u64)&flush_branch_caches, BRANCH_SET_LINK);
457c0036549SNicholas Piggin 
458c0036549SNicholas Piggin 		// If we just need to flush the link stack, early return
459c0036549SNicholas Piggin 		if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE) {
460c0036549SNicholas Piggin 			patch_instruction_site(&patch__flush_link_stack_return,
461c0036549SNicholas Piggin 					       ppc_inst(PPC_INST_BLR));
462c0036549SNicholas Piggin 
463c0036549SNicholas Piggin 		// If we have flush instruction, early return
464c0036549SNicholas Piggin 		} else if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW) {
465c0036549SNicholas Piggin 			patch_instruction_site(&patch__flush_count_cache_return,
466c0036549SNicholas Piggin 					       ppc_inst(PPC_INST_BLR));
467c0036549SNicholas Piggin 		}
468c0036549SNicholas Piggin 	}
46939e72bf9SMichael Ellerman }
47039e72bf9SMichael Ellerman 
4711026798cSNicholas Piggin static void toggle_branch_cache_flush(bool enable)
47239e72bf9SMichael Ellerman {
473c0036549SNicholas Piggin 	if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) {
474c0036549SNicholas Piggin 		if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE)
475c0036549SNicholas Piggin 			count_cache_flush_type = BRANCH_CACHE_FLUSH_NONE;
47639e72bf9SMichael Ellerman 
477c0036549SNicholas Piggin 		pr_info("count-cache-flush: flush disabled.\n");
478c0036549SNicholas Piggin 	} else {
479c0036549SNicholas Piggin 		if (security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) {
4801026798cSNicholas Piggin 			count_cache_flush_type = BRANCH_CACHE_FLUSH_HW;
4811afe00c7SNicholas Piggin 			pr_info("count-cache-flush: hardware flush enabled.\n");
482c0036549SNicholas Piggin 		} else {
483c0036549SNicholas Piggin 			count_cache_flush_type = BRANCH_CACHE_FLUSH_SW;
484c0036549SNicholas Piggin 			pr_info("count-cache-flush: software flush enabled.\n");
485c0036549SNicholas Piggin 		}
486c0036549SNicholas Piggin 	}
487c0036549SNicholas Piggin 
488c0036549SNicholas Piggin 	if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_LINK_STACK)) {
489c0036549SNicholas Piggin 		if (link_stack_flush_type != BRANCH_CACHE_FLUSH_NONE)
490c0036549SNicholas Piggin 			link_stack_flush_type = BRANCH_CACHE_FLUSH_NONE;
491c0036549SNicholas Piggin 
492c0036549SNicholas Piggin 		pr_info("link-stack-flush: flush disabled.\n");
493c0036549SNicholas Piggin 	} else {
494*4d24e21cSNicholas Piggin 		if (security_ftr_enabled(SEC_FTR_BCCTR_LINK_FLUSH_ASSIST)) {
495*4d24e21cSNicholas Piggin 			link_stack_flush_type = BRANCH_CACHE_FLUSH_HW;
496*4d24e21cSNicholas Piggin 			pr_info("link-stack-flush: hardware flush enabled.\n");
497*4d24e21cSNicholas Piggin 		} else {
498c0036549SNicholas Piggin 			link_stack_flush_type = BRANCH_CACHE_FLUSH_SW;
499c0036549SNicholas Piggin 			pr_info("link-stack-flush: software flush enabled.\n");
500c0036549SNicholas Piggin 		}
501*4d24e21cSNicholas Piggin 	}
502c0036549SNicholas Piggin 
503c0036549SNicholas Piggin 	update_branch_cache_flush();
504ee13cb24SMichael Ellerman }
505ee13cb24SMichael Ellerman 
506ee13cb24SMichael Ellerman void setup_count_cache_flush(void)
507ee13cb24SMichael Ellerman {
508d8f0e0b0SChristopher M. Riedl 	bool enable = true;
509d8f0e0b0SChristopher M. Riedl 
510d8f0e0b0SChristopher M. Riedl 	if (no_spectrev2 || cpu_mitigations_off()) {
511d8f0e0b0SChristopher M. Riedl 		if (security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED) ||
512d8f0e0b0SChristopher M. Riedl 		    security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED))
51339e72bf9SMichael Ellerman 			pr_warn("Spectre v2 mitigations not fully under software control, can't disable\n");
514d8f0e0b0SChristopher M. Riedl 
515d8f0e0b0SChristopher M. Riedl 		enable = false;
516d8f0e0b0SChristopher M. Riedl 	}
517d8f0e0b0SChristopher M. Riedl 
51839e72bf9SMichael Ellerman 	/*
51939e72bf9SMichael Ellerman 	 * There's no firmware feature flag/hypervisor bit to tell us we need to
52039e72bf9SMichael Ellerman 	 * flush the link stack on context switch. So we set it here if we see
52139e72bf9SMichael Ellerman 	 * either of the Spectre v2 mitigations that aim to protect userspace.
52239e72bf9SMichael Ellerman 	 */
52339e72bf9SMichael Ellerman 	if (security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED) ||
52439e72bf9SMichael Ellerman 	    security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE))
52539e72bf9SMichael Ellerman 		security_ftr_set(SEC_FTR_FLUSH_LINK_STACK);
52639e72bf9SMichael Ellerman 
5271026798cSNicholas Piggin 	toggle_branch_cache_flush(enable);
528ee13cb24SMichael Ellerman }
529ee13cb24SMichael Ellerman 
530ee13cb24SMichael Ellerman #ifdef CONFIG_DEBUG_FS
531ee13cb24SMichael Ellerman static int count_cache_flush_set(void *data, u64 val)
532ee13cb24SMichael Ellerman {
533ee13cb24SMichael Ellerman 	bool enable;
534ee13cb24SMichael Ellerman 
535ee13cb24SMichael Ellerman 	if (val == 1)
536ee13cb24SMichael Ellerman 		enable = true;
537ee13cb24SMichael Ellerman 	else if (val == 0)
538ee13cb24SMichael Ellerman 		enable = false;
539ee13cb24SMichael Ellerman 	else
540ee13cb24SMichael Ellerman 		return -EINVAL;
541ee13cb24SMichael Ellerman 
5421026798cSNicholas Piggin 	toggle_branch_cache_flush(enable);
543ee13cb24SMichael Ellerman 
544ee13cb24SMichael Ellerman 	return 0;
545ee13cb24SMichael Ellerman }
546ee13cb24SMichael Ellerman 
547ee13cb24SMichael Ellerman static int count_cache_flush_get(void *data, u64 *val)
548ee13cb24SMichael Ellerman {
5491026798cSNicholas Piggin 	if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE)
550ee13cb24SMichael Ellerman 		*val = 0;
551ee13cb24SMichael Ellerman 	else
552ee13cb24SMichael Ellerman 		*val = 1;
553ee13cb24SMichael Ellerman 
554ee13cb24SMichael Ellerman 	return 0;
555ee13cb24SMichael Ellerman }
556ee13cb24SMichael Ellerman 
557090d5ab9SYueHaibing DEFINE_DEBUGFS_ATTRIBUTE(fops_count_cache_flush, count_cache_flush_get,
558ee13cb24SMichael Ellerman 			 count_cache_flush_set, "%llu\n");
559ee13cb24SMichael Ellerman 
560ee13cb24SMichael Ellerman static __init int count_cache_flush_debugfs_init(void)
561ee13cb24SMichael Ellerman {
562090d5ab9SYueHaibing 	debugfs_create_file_unsafe("count_cache_flush", 0600,
563090d5ab9SYueHaibing 				   powerpc_debugfs_root, NULL,
564090d5ab9SYueHaibing 				   &fops_count_cache_flush);
565ee13cb24SMichael Ellerman 	return 0;
566ee13cb24SMichael Ellerman }
567ee13cb24SMichael Ellerman device_initcall(count_cache_flush_debugfs_init);
568ee13cb24SMichael Ellerman #endif /* CONFIG_DEBUG_FS */
5696453b532SDiana Craciun #endif /* CONFIG_PPC_BOOK3S_64 */
570