xref: /linux-6.15/arch/powerpc/kernel/security.c (revision 7d8bad99)
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 };
26*7d8bad99SDiana Craciun static enum count_cache_flush_type count_cache_flush_type = COUNT_CACHE_FLUSH_NONE;
27ee13cb24SMichael Ellerman 
28815069caSMichal Suchanek bool barrier_nospec_enabled;
29cf175dc3SDiana Craciun static bool no_nospec;
302eea7f06SMichal Suchanek 
312eea7f06SMichal Suchanek static void enable_barrier_nospec(bool enable)
322eea7f06SMichal Suchanek {
332eea7f06SMichal Suchanek 	barrier_nospec_enabled = enable;
342eea7f06SMichal Suchanek 	do_barrier_nospec_fixups(enable);
352eea7f06SMichal Suchanek }
362eea7f06SMichal Suchanek 
37cb3d6759SMichal Suchanek void setup_barrier_nospec(void)
38cb3d6759SMichal Suchanek {
39cb3d6759SMichal Suchanek 	bool enable;
40cb3d6759SMichal Suchanek 
41cb3d6759SMichal Suchanek 	/*
42cb3d6759SMichal Suchanek 	 * It would make sense to check SEC_FTR_SPEC_BAR_ORI31 below as well.
43cb3d6759SMichal Suchanek 	 * But there's a good reason not to. The two flags we check below are
44cb3d6759SMichal Suchanek 	 * both are enabled by default in the kernel, so if the hcall is not
45cb3d6759SMichal Suchanek 	 * functional they will be enabled.
46cb3d6759SMichal Suchanek 	 * On a system where the host firmware has been updated (so the ori
47cb3d6759SMichal Suchanek 	 * functions as a barrier), but on which the hypervisor (KVM/Qemu) has
48cb3d6759SMichal Suchanek 	 * not been updated, we would like to enable the barrier. Dropping the
49cb3d6759SMichal Suchanek 	 * check for SEC_FTR_SPEC_BAR_ORI31 achieves that. The only downside is
50cb3d6759SMichal Suchanek 	 * we potentially enable the barrier on systems where the host firmware
51cb3d6759SMichal Suchanek 	 * is not updated, but that's harmless as it's a no-op.
52cb3d6759SMichal Suchanek 	 */
53cb3d6759SMichal Suchanek 	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
54cb3d6759SMichal Suchanek 		 security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR);
55cb3d6759SMichal Suchanek 
56cf175dc3SDiana Craciun 	if (!no_nospec)
57cb3d6759SMichal Suchanek 		enable_barrier_nospec(enable);
58cb3d6759SMichal Suchanek }
59cb3d6759SMichal Suchanek 
60cf175dc3SDiana Craciun static int __init handle_nospectre_v1(char *p)
61cf175dc3SDiana Craciun {
62cf175dc3SDiana Craciun 	no_nospec = true;
63cf175dc3SDiana Craciun 
64cf175dc3SDiana Craciun 	return 0;
65cf175dc3SDiana Craciun }
66cf175dc3SDiana Craciun early_param("nospectre_v1", handle_nospectre_v1);
67cf175dc3SDiana Craciun 
68cb3d6759SMichal Suchanek #ifdef CONFIG_DEBUG_FS
69cb3d6759SMichal Suchanek static int barrier_nospec_set(void *data, u64 val)
70cb3d6759SMichal Suchanek {
71cb3d6759SMichal Suchanek 	switch (val) {
72cb3d6759SMichal Suchanek 	case 0:
73cb3d6759SMichal Suchanek 	case 1:
74cb3d6759SMichal Suchanek 		break;
75cb3d6759SMichal Suchanek 	default:
76cb3d6759SMichal Suchanek 		return -EINVAL;
77cb3d6759SMichal Suchanek 	}
78cb3d6759SMichal Suchanek 
79cb3d6759SMichal Suchanek 	if (!!val == !!barrier_nospec_enabled)
80cb3d6759SMichal Suchanek 		return 0;
81cb3d6759SMichal Suchanek 
82cb3d6759SMichal Suchanek 	enable_barrier_nospec(!!val);
83cb3d6759SMichal Suchanek 
84cb3d6759SMichal Suchanek 	return 0;
85cb3d6759SMichal Suchanek }
86cb3d6759SMichal Suchanek 
87cb3d6759SMichal Suchanek static int barrier_nospec_get(void *data, u64 *val)
88cb3d6759SMichal Suchanek {
89cb3d6759SMichal Suchanek 	*val = barrier_nospec_enabled ? 1 : 0;
90cb3d6759SMichal Suchanek 	return 0;
91cb3d6759SMichal Suchanek }
92cb3d6759SMichal Suchanek 
93cb3d6759SMichal Suchanek DEFINE_SIMPLE_ATTRIBUTE(fops_barrier_nospec,
94cb3d6759SMichal Suchanek 			barrier_nospec_get, barrier_nospec_set, "%llu\n");
95cb3d6759SMichal Suchanek 
96cb3d6759SMichal Suchanek static __init int barrier_nospec_debugfs_init(void)
97cb3d6759SMichal Suchanek {
98cb3d6759SMichal Suchanek 	debugfs_create_file("barrier_nospec", 0600, powerpc_debugfs_root, NULL,
99cb3d6759SMichal Suchanek 			    &fops_barrier_nospec);
100cb3d6759SMichal Suchanek 	return 0;
101cb3d6759SMichal Suchanek }
102cb3d6759SMichal Suchanek device_initcall(barrier_nospec_debugfs_init);
103cb3d6759SMichal Suchanek #endif /* CONFIG_DEBUG_FS */
104cb3d6759SMichal Suchanek 
105406d2b6aSDiana Craciun #ifdef CONFIG_PPC_BOOK3S_64
1068ad33041SMichael Ellerman ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
1078ad33041SMichael Ellerman {
108ff348355SMichael Ellerman 	bool thread_priv;
109ff348355SMichael Ellerman 
110ff348355SMichael Ellerman 	thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV);
111ff348355SMichael Ellerman 
112ff348355SMichael Ellerman 	if (rfi_flush || thread_priv) {
113ff348355SMichael Ellerman 		struct seq_buf s;
114ff348355SMichael Ellerman 		seq_buf_init(&s, buf, PAGE_SIZE - 1);
115ff348355SMichael Ellerman 
116ff348355SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
117ff348355SMichael Ellerman 
1188ad33041SMichael Ellerman 		if (rfi_flush)
119ff348355SMichael Ellerman 			seq_buf_printf(&s, "RFI Flush");
120ff348355SMichael Ellerman 
121ff348355SMichael Ellerman 		if (rfi_flush && thread_priv)
122ff348355SMichael Ellerman 			seq_buf_printf(&s, ", ");
123ff348355SMichael Ellerman 
124ff348355SMichael Ellerman 		if (thread_priv)
125ff348355SMichael Ellerman 			seq_buf_printf(&s, "L1D private per thread");
126ff348355SMichael Ellerman 
127ff348355SMichael Ellerman 		seq_buf_printf(&s, "\n");
128ff348355SMichael Ellerman 
129ff348355SMichael Ellerman 		return s.len;
130ff348355SMichael Ellerman 	}
131ff348355SMichael Ellerman 
132ff348355SMichael Ellerman 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
133ff348355SMichael Ellerman 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
134ff348355SMichael Ellerman 		return sprintf(buf, "Not affected\n");
1358ad33041SMichael Ellerman 
1368ad33041SMichael Ellerman 	return sprintf(buf, "Vulnerable\n");
1378ad33041SMichael Ellerman }
138406d2b6aSDiana Craciun #endif
13956986016SMichael Ellerman 
14056986016SMichael Ellerman ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
14156986016SMichael Ellerman {
1426d44acaeSMichael Ellerman 	struct seq_buf s;
14356986016SMichael Ellerman 
1446d44acaeSMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
1456d44acaeSMichael Ellerman 
1466d44acaeSMichael Ellerman 	if (security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) {
147a3775145SMichal Suchanek 		if (barrier_nospec_enabled)
1486d44acaeSMichael Ellerman 			seq_buf_printf(&s, "Mitigation: __user pointer sanitization");
1496d44acaeSMichael Ellerman 		else
1506d44acaeSMichael Ellerman 			seq_buf_printf(&s, "Vulnerable");
151a3775145SMichal Suchanek 
1526d44acaeSMichael Ellerman 		if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31))
1536d44acaeSMichael Ellerman 			seq_buf_printf(&s, ", ori31 speculation barrier enabled");
1546d44acaeSMichael Ellerman 
1556d44acaeSMichael Ellerman 		seq_buf_printf(&s, "\n");
1566d44acaeSMichael Ellerman 	} else
1576d44acaeSMichael Ellerman 		seq_buf_printf(&s, "Not affected\n");
1586d44acaeSMichael Ellerman 
1596d44acaeSMichael Ellerman 	return s.len;
16056986016SMichael Ellerman }
161d6fbe1c5SMichael Ellerman 
162d6fbe1c5SMichael Ellerman ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf)
163d6fbe1c5SMichael Ellerman {
164d6fbe1c5SMichael Ellerman 	struct seq_buf s;
1656d44acaeSMichael Ellerman 	bool bcs, ccd;
166d6fbe1c5SMichael Ellerman 
167d6fbe1c5SMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
168d6fbe1c5SMichael Ellerman 
169d6fbe1c5SMichael Ellerman 	bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
170d6fbe1c5SMichael Ellerman 	ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
171d6fbe1c5SMichael Ellerman 
172ee13cb24SMichael Ellerman 	if (bcs || ccd || count_cache_flush_type != COUNT_CACHE_FLUSH_NONE) {
173ee13cb24SMichael Ellerman 		bool comma = false;
174d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
175d6fbe1c5SMichael Ellerman 
176ee13cb24SMichael Ellerman 		if (bcs) {
177d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
178ee13cb24SMichael Ellerman 			comma = true;
179ee13cb24SMichael Ellerman 		}
180d6fbe1c5SMichael Ellerman 
181ee13cb24SMichael Ellerman 		if (ccd) {
182ee13cb24SMichael Ellerman 			if (comma)
183ee13cb24SMichael Ellerman 				seq_buf_printf(&s, ", ");
184ee13cb24SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch cache disabled");
185ee13cb24SMichael Ellerman 			comma = true;
186ee13cb24SMichael Ellerman 		}
187ee13cb24SMichael Ellerman 
188ee13cb24SMichael Ellerman 		if (comma)
189d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, ", ");
190d6fbe1c5SMichael Ellerman 
191ee13cb24SMichael Ellerman 		seq_buf_printf(&s, "Software count cache flush");
192ee13cb24SMichael Ellerman 
193ee13cb24SMichael Ellerman 		if (count_cache_flush_type == COUNT_CACHE_FLUSH_HW)
194ee13cb24SMichael Ellerman 			seq_buf_printf(&s, "(hardware accelerated)");
195d6fbe1c5SMichael Ellerman 	} else
196d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Vulnerable");
197d6fbe1c5SMichael Ellerman 
198d6fbe1c5SMichael Ellerman 	seq_buf_printf(&s, "\n");
199d6fbe1c5SMichael Ellerman 
200d6fbe1c5SMichael Ellerman 	return s.len;
201d6fbe1c5SMichael Ellerman }
202a048a07dSNicholas Piggin 
2036453b532SDiana Craciun #ifdef CONFIG_PPC_BOOK3S_64
204a048a07dSNicholas Piggin /*
205a048a07dSNicholas Piggin  * Store-forwarding barrier support.
206a048a07dSNicholas Piggin  */
207a048a07dSNicholas Piggin 
208a048a07dSNicholas Piggin static enum stf_barrier_type stf_enabled_flush_types;
209a048a07dSNicholas Piggin static bool no_stf_barrier;
210a048a07dSNicholas Piggin bool stf_barrier;
211a048a07dSNicholas Piggin 
212a048a07dSNicholas Piggin static int __init handle_no_stf_barrier(char *p)
213a048a07dSNicholas Piggin {
214a048a07dSNicholas Piggin 	pr_info("stf-barrier: disabled on command line.");
215a048a07dSNicholas Piggin 	no_stf_barrier = true;
216a048a07dSNicholas Piggin 	return 0;
217a048a07dSNicholas Piggin }
218a048a07dSNicholas Piggin 
219a048a07dSNicholas Piggin early_param("no_stf_barrier", handle_no_stf_barrier);
220a048a07dSNicholas Piggin 
221a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
222a048a07dSNicholas Piggin static int __init handle_ssbd(char *p)
223a048a07dSNicholas Piggin {
224a048a07dSNicholas Piggin 	if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) {
225a048a07dSNicholas Piggin 		/* Until firmware tells us, we have the barrier with auto */
226a048a07dSNicholas Piggin 		return 0;
227a048a07dSNicholas Piggin 	} else if (strncmp(p, "off", 3) == 0) {
228a048a07dSNicholas Piggin 		handle_no_stf_barrier(NULL);
229a048a07dSNicholas Piggin 		return 0;
230a048a07dSNicholas Piggin 	} else
231a048a07dSNicholas Piggin 		return 1;
232a048a07dSNicholas Piggin 
233a048a07dSNicholas Piggin 	return 0;
234a048a07dSNicholas Piggin }
235a048a07dSNicholas Piggin early_param("spec_store_bypass_disable", handle_ssbd);
236a048a07dSNicholas Piggin 
237a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
238a048a07dSNicholas Piggin static int __init handle_no_ssbd(char *p)
239a048a07dSNicholas Piggin {
240a048a07dSNicholas Piggin 	handle_no_stf_barrier(NULL);
241a048a07dSNicholas Piggin 	return 0;
242a048a07dSNicholas Piggin }
243a048a07dSNicholas Piggin early_param("nospec_store_bypass_disable", handle_no_ssbd);
244a048a07dSNicholas Piggin 
245a048a07dSNicholas Piggin static void stf_barrier_enable(bool enable)
246a048a07dSNicholas Piggin {
247a048a07dSNicholas Piggin 	if (enable)
248a048a07dSNicholas Piggin 		do_stf_barrier_fixups(stf_enabled_flush_types);
249a048a07dSNicholas Piggin 	else
250a048a07dSNicholas Piggin 		do_stf_barrier_fixups(STF_BARRIER_NONE);
251a048a07dSNicholas Piggin 
252a048a07dSNicholas Piggin 	stf_barrier = enable;
253a048a07dSNicholas Piggin }
254a048a07dSNicholas Piggin 
255a048a07dSNicholas Piggin void setup_stf_barrier(void)
256a048a07dSNicholas Piggin {
257a048a07dSNicholas Piggin 	enum stf_barrier_type type;
258a048a07dSNicholas Piggin 	bool enable, hv;
259a048a07dSNicholas Piggin 
260a048a07dSNicholas Piggin 	hv = cpu_has_feature(CPU_FTR_HVMODE);
261a048a07dSNicholas Piggin 
262a048a07dSNicholas Piggin 	/* Default to fallback in case fw-features are not available */
263a048a07dSNicholas Piggin 	if (cpu_has_feature(CPU_FTR_ARCH_300))
264a048a07dSNicholas Piggin 		type = STF_BARRIER_EIEIO;
265a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_207S))
266a048a07dSNicholas Piggin 		type = STF_BARRIER_SYNC_ORI;
267a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_206))
268a048a07dSNicholas Piggin 		type = STF_BARRIER_FALLBACK;
269a048a07dSNicholas Piggin 	else
270a048a07dSNicholas Piggin 		type = STF_BARRIER_NONE;
271a048a07dSNicholas Piggin 
272a048a07dSNicholas Piggin 	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
273a048a07dSNicholas Piggin 		(security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) ||
274a048a07dSNicholas Piggin 		 (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv));
275a048a07dSNicholas Piggin 
276a048a07dSNicholas Piggin 	if (type == STF_BARRIER_FALLBACK) {
277a048a07dSNicholas Piggin 		pr_info("stf-barrier: fallback barrier available\n");
278a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_SYNC_ORI) {
279a048a07dSNicholas Piggin 		pr_info("stf-barrier: hwsync barrier available\n");
280a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_EIEIO) {
281a048a07dSNicholas Piggin 		pr_info("stf-barrier: eieio barrier available\n");
282a048a07dSNicholas Piggin 	}
283a048a07dSNicholas Piggin 
284a048a07dSNicholas Piggin 	stf_enabled_flush_types = type;
285a048a07dSNicholas Piggin 
286a048a07dSNicholas Piggin 	if (!no_stf_barrier)
287a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
288a048a07dSNicholas Piggin }
289a048a07dSNicholas Piggin 
290a048a07dSNicholas Piggin ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf)
291a048a07dSNicholas Piggin {
292a048a07dSNicholas Piggin 	if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) {
293a048a07dSNicholas Piggin 		const char *type;
294a048a07dSNicholas Piggin 		switch (stf_enabled_flush_types) {
295a048a07dSNicholas Piggin 		case STF_BARRIER_EIEIO:
296a048a07dSNicholas Piggin 			type = "eieio";
297a048a07dSNicholas Piggin 			break;
298a048a07dSNicholas Piggin 		case STF_BARRIER_SYNC_ORI:
299a048a07dSNicholas Piggin 			type = "hwsync";
300a048a07dSNicholas Piggin 			break;
301a048a07dSNicholas Piggin 		case STF_BARRIER_FALLBACK:
302a048a07dSNicholas Piggin 			type = "fallback";
303a048a07dSNicholas Piggin 			break;
304a048a07dSNicholas Piggin 		default:
305a048a07dSNicholas Piggin 			type = "unknown";
306a048a07dSNicholas Piggin 		}
307a048a07dSNicholas Piggin 		return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type);
308a048a07dSNicholas Piggin 	}
309a048a07dSNicholas Piggin 
310a048a07dSNicholas Piggin 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
311a048a07dSNicholas Piggin 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
312a048a07dSNicholas Piggin 		return sprintf(buf, "Not affected\n");
313a048a07dSNicholas Piggin 
314a048a07dSNicholas Piggin 	return sprintf(buf, "Vulnerable\n");
315a048a07dSNicholas Piggin }
316a048a07dSNicholas Piggin 
317a048a07dSNicholas Piggin #ifdef CONFIG_DEBUG_FS
318a048a07dSNicholas Piggin static int stf_barrier_set(void *data, u64 val)
319a048a07dSNicholas Piggin {
320a048a07dSNicholas Piggin 	bool enable;
321a048a07dSNicholas Piggin 
322a048a07dSNicholas Piggin 	if (val == 1)
323a048a07dSNicholas Piggin 		enable = true;
324a048a07dSNicholas Piggin 	else if (val == 0)
325a048a07dSNicholas Piggin 		enable = false;
326a048a07dSNicholas Piggin 	else
327a048a07dSNicholas Piggin 		return -EINVAL;
328a048a07dSNicholas Piggin 
329a048a07dSNicholas Piggin 	/* Only do anything if we're changing state */
330a048a07dSNicholas Piggin 	if (enable != stf_barrier)
331a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
332a048a07dSNicholas Piggin 
333a048a07dSNicholas Piggin 	return 0;
334a048a07dSNicholas Piggin }
335a048a07dSNicholas Piggin 
336a048a07dSNicholas Piggin static int stf_barrier_get(void *data, u64 *val)
337a048a07dSNicholas Piggin {
338a048a07dSNicholas Piggin 	*val = stf_barrier ? 1 : 0;
339a048a07dSNicholas Piggin 	return 0;
340a048a07dSNicholas Piggin }
341a048a07dSNicholas Piggin 
342a048a07dSNicholas Piggin DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n");
343a048a07dSNicholas Piggin 
344a048a07dSNicholas Piggin static __init int stf_barrier_debugfs_init(void)
345a048a07dSNicholas Piggin {
346a048a07dSNicholas Piggin 	debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier);
347a048a07dSNicholas Piggin 	return 0;
348a048a07dSNicholas Piggin }
349a048a07dSNicholas Piggin device_initcall(stf_barrier_debugfs_init);
350a048a07dSNicholas Piggin #endif /* CONFIG_DEBUG_FS */
351ee13cb24SMichael Ellerman 
352ee13cb24SMichael Ellerman static void toggle_count_cache_flush(bool enable)
353ee13cb24SMichael Ellerman {
354ee13cb24SMichael Ellerman 	if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) {
355ee13cb24SMichael Ellerman 		patch_instruction_site(&patch__call_flush_count_cache, PPC_INST_NOP);
356ee13cb24SMichael Ellerman 		count_cache_flush_type = COUNT_CACHE_FLUSH_NONE;
357ee13cb24SMichael Ellerman 		pr_info("count-cache-flush: software flush disabled.\n");
358ee13cb24SMichael Ellerman 		return;
359ee13cb24SMichael Ellerman 	}
360ee13cb24SMichael Ellerman 
361ee13cb24SMichael Ellerman 	patch_branch_site(&patch__call_flush_count_cache,
362ee13cb24SMichael Ellerman 			  (u64)&flush_count_cache, BRANCH_SET_LINK);
363ee13cb24SMichael Ellerman 
364ee13cb24SMichael Ellerman 	if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) {
365ee13cb24SMichael Ellerman 		count_cache_flush_type = COUNT_CACHE_FLUSH_SW;
366ee13cb24SMichael Ellerman 		pr_info("count-cache-flush: full software flush sequence enabled.\n");
367ee13cb24SMichael Ellerman 		return;
368ee13cb24SMichael Ellerman 	}
369ee13cb24SMichael Ellerman 
370ee13cb24SMichael Ellerman 	patch_instruction_site(&patch__flush_count_cache_return, PPC_INST_BLR);
371ee13cb24SMichael Ellerman 	count_cache_flush_type = COUNT_CACHE_FLUSH_HW;
372ee13cb24SMichael Ellerman 	pr_info("count-cache-flush: hardware assisted flush sequence enabled\n");
373ee13cb24SMichael Ellerman }
374ee13cb24SMichael Ellerman 
375ee13cb24SMichael Ellerman void setup_count_cache_flush(void)
376ee13cb24SMichael Ellerman {
377ee13cb24SMichael Ellerman 	toggle_count_cache_flush(true);
378ee13cb24SMichael Ellerman }
379ee13cb24SMichael Ellerman 
380ee13cb24SMichael Ellerman #ifdef CONFIG_DEBUG_FS
381ee13cb24SMichael Ellerman static int count_cache_flush_set(void *data, u64 val)
382ee13cb24SMichael Ellerman {
383ee13cb24SMichael Ellerman 	bool enable;
384ee13cb24SMichael Ellerman 
385ee13cb24SMichael Ellerman 	if (val == 1)
386ee13cb24SMichael Ellerman 		enable = true;
387ee13cb24SMichael Ellerman 	else if (val == 0)
388ee13cb24SMichael Ellerman 		enable = false;
389ee13cb24SMichael Ellerman 	else
390ee13cb24SMichael Ellerman 		return -EINVAL;
391ee13cb24SMichael Ellerman 
392ee13cb24SMichael Ellerman 	toggle_count_cache_flush(enable);
393ee13cb24SMichael Ellerman 
394ee13cb24SMichael Ellerman 	return 0;
395ee13cb24SMichael Ellerman }
396ee13cb24SMichael Ellerman 
397ee13cb24SMichael Ellerman static int count_cache_flush_get(void *data, u64 *val)
398ee13cb24SMichael Ellerman {
399ee13cb24SMichael Ellerman 	if (count_cache_flush_type == COUNT_CACHE_FLUSH_NONE)
400ee13cb24SMichael Ellerman 		*val = 0;
401ee13cb24SMichael Ellerman 	else
402ee13cb24SMichael Ellerman 		*val = 1;
403ee13cb24SMichael Ellerman 
404ee13cb24SMichael Ellerman 	return 0;
405ee13cb24SMichael Ellerman }
406ee13cb24SMichael Ellerman 
407ee13cb24SMichael Ellerman DEFINE_SIMPLE_ATTRIBUTE(fops_count_cache_flush, count_cache_flush_get,
408ee13cb24SMichael Ellerman 			count_cache_flush_set, "%llu\n");
409ee13cb24SMichael Ellerman 
410ee13cb24SMichael Ellerman static __init int count_cache_flush_debugfs_init(void)
411ee13cb24SMichael Ellerman {
412ee13cb24SMichael Ellerman 	debugfs_create_file("count_cache_flush", 0600, powerpc_debugfs_root,
413ee13cb24SMichael Ellerman 			    NULL, &fops_count_cache_flush);
414ee13cb24SMichael Ellerman 	return 0;
415ee13cb24SMichael Ellerman }
416ee13cb24SMichael Ellerman device_initcall(count_cache_flush_debugfs_init);
417ee13cb24SMichael Ellerman #endif /* CONFIG_DEBUG_FS */
4186453b532SDiana Craciun #endif /* CONFIG_PPC_BOOK3S_64 */
419