xref: /linux-6.15/arch/powerpc/kernel/security.c (revision a048a07d)
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 
11*a048a07dSNicholas Piggin #include <asm/debugfs.h>
129a868f63SMichael Ellerman #include <asm/security_features.h>
139a868f63SMichael Ellerman 
149a868f63SMichael Ellerman 
15e7347a86SMauricio Faria de Oliveira unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT;
168ad33041SMichael Ellerman 
178ad33041SMichael Ellerman ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
188ad33041SMichael Ellerman {
19ff348355SMichael Ellerman 	bool thread_priv;
20ff348355SMichael Ellerman 
21ff348355SMichael Ellerman 	thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV);
22ff348355SMichael Ellerman 
23ff348355SMichael Ellerman 	if (rfi_flush || thread_priv) {
24ff348355SMichael Ellerman 		struct seq_buf s;
25ff348355SMichael Ellerman 		seq_buf_init(&s, buf, PAGE_SIZE - 1);
26ff348355SMichael Ellerman 
27ff348355SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
28ff348355SMichael Ellerman 
298ad33041SMichael Ellerman 		if (rfi_flush)
30ff348355SMichael Ellerman 			seq_buf_printf(&s, "RFI Flush");
31ff348355SMichael Ellerman 
32ff348355SMichael Ellerman 		if (rfi_flush && thread_priv)
33ff348355SMichael Ellerman 			seq_buf_printf(&s, ", ");
34ff348355SMichael Ellerman 
35ff348355SMichael Ellerman 		if (thread_priv)
36ff348355SMichael Ellerman 			seq_buf_printf(&s, "L1D private per thread");
37ff348355SMichael Ellerman 
38ff348355SMichael Ellerman 		seq_buf_printf(&s, "\n");
39ff348355SMichael Ellerman 
40ff348355SMichael Ellerman 		return s.len;
41ff348355SMichael Ellerman 	}
42ff348355SMichael Ellerman 
43ff348355SMichael Ellerman 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
44ff348355SMichael Ellerman 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
45ff348355SMichael Ellerman 		return sprintf(buf, "Not affected\n");
468ad33041SMichael Ellerman 
478ad33041SMichael Ellerman 	return sprintf(buf, "Vulnerable\n");
488ad33041SMichael Ellerman }
4956986016SMichael Ellerman 
5056986016SMichael Ellerman ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
5156986016SMichael Ellerman {
5256986016SMichael Ellerman 	if (!security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR))
5356986016SMichael Ellerman 		return sprintf(buf, "Not affected\n");
5456986016SMichael Ellerman 
5556986016SMichael Ellerman 	return sprintf(buf, "Vulnerable\n");
5656986016SMichael Ellerman }
57d6fbe1c5SMichael Ellerman 
58d6fbe1c5SMichael Ellerman ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf)
59d6fbe1c5SMichael Ellerman {
60d6fbe1c5SMichael Ellerman 	bool bcs, ccd, ori;
61d6fbe1c5SMichael Ellerman 	struct seq_buf s;
62d6fbe1c5SMichael Ellerman 
63d6fbe1c5SMichael Ellerman 	seq_buf_init(&s, buf, PAGE_SIZE - 1);
64d6fbe1c5SMichael Ellerman 
65d6fbe1c5SMichael Ellerman 	bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
66d6fbe1c5SMichael Ellerman 	ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
67d6fbe1c5SMichael Ellerman 	ori = security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31);
68d6fbe1c5SMichael Ellerman 
69d6fbe1c5SMichael Ellerman 	if (bcs || ccd) {
70d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Mitigation: ");
71d6fbe1c5SMichael Ellerman 
72d6fbe1c5SMichael Ellerman 		if (bcs)
73d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
74d6fbe1c5SMichael Ellerman 
75d6fbe1c5SMichael Ellerman 		if (bcs && ccd)
76d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, ", ");
77d6fbe1c5SMichael Ellerman 
78d6fbe1c5SMichael Ellerman 		if (ccd)
79d6fbe1c5SMichael Ellerman 			seq_buf_printf(&s, "Indirect branch cache disabled");
80d6fbe1c5SMichael Ellerman 	} else
81d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, "Vulnerable");
82d6fbe1c5SMichael Ellerman 
83d6fbe1c5SMichael Ellerman 	if (ori)
84d6fbe1c5SMichael Ellerman 		seq_buf_printf(&s, ", ori31 speculation barrier enabled");
85d6fbe1c5SMichael Ellerman 
86d6fbe1c5SMichael Ellerman 	seq_buf_printf(&s, "\n");
87d6fbe1c5SMichael Ellerman 
88d6fbe1c5SMichael Ellerman 	return s.len;
89d6fbe1c5SMichael Ellerman }
90*a048a07dSNicholas Piggin 
91*a048a07dSNicholas Piggin /*
92*a048a07dSNicholas Piggin  * Store-forwarding barrier support.
93*a048a07dSNicholas Piggin  */
94*a048a07dSNicholas Piggin 
95*a048a07dSNicholas Piggin static enum stf_barrier_type stf_enabled_flush_types;
96*a048a07dSNicholas Piggin static bool no_stf_barrier;
97*a048a07dSNicholas Piggin bool stf_barrier;
98*a048a07dSNicholas Piggin 
99*a048a07dSNicholas Piggin static int __init handle_no_stf_barrier(char *p)
100*a048a07dSNicholas Piggin {
101*a048a07dSNicholas Piggin 	pr_info("stf-barrier: disabled on command line.");
102*a048a07dSNicholas Piggin 	no_stf_barrier = true;
103*a048a07dSNicholas Piggin 	return 0;
104*a048a07dSNicholas Piggin }
105*a048a07dSNicholas Piggin 
106*a048a07dSNicholas Piggin early_param("no_stf_barrier", handle_no_stf_barrier);
107*a048a07dSNicholas Piggin 
108*a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
109*a048a07dSNicholas Piggin static int __init handle_ssbd(char *p)
110*a048a07dSNicholas Piggin {
111*a048a07dSNicholas Piggin 	if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) {
112*a048a07dSNicholas Piggin 		/* Until firmware tells us, we have the barrier with auto */
113*a048a07dSNicholas Piggin 		return 0;
114*a048a07dSNicholas Piggin 	} else if (strncmp(p, "off", 3) == 0) {
115*a048a07dSNicholas Piggin 		handle_no_stf_barrier(NULL);
116*a048a07dSNicholas Piggin 		return 0;
117*a048a07dSNicholas Piggin 	} else
118*a048a07dSNicholas Piggin 		return 1;
119*a048a07dSNicholas Piggin 
120*a048a07dSNicholas Piggin 	return 0;
121*a048a07dSNicholas Piggin }
122*a048a07dSNicholas Piggin early_param("spec_store_bypass_disable", handle_ssbd);
123*a048a07dSNicholas Piggin 
124*a048a07dSNicholas Piggin /* This is the generic flag used by other architectures */
125*a048a07dSNicholas Piggin static int __init handle_no_ssbd(char *p)
126*a048a07dSNicholas Piggin {
127*a048a07dSNicholas Piggin 	handle_no_stf_barrier(NULL);
128*a048a07dSNicholas Piggin 	return 0;
129*a048a07dSNicholas Piggin }
130*a048a07dSNicholas Piggin early_param("nospec_store_bypass_disable", handle_no_ssbd);
131*a048a07dSNicholas Piggin 
132*a048a07dSNicholas Piggin static void stf_barrier_enable(bool enable)
133*a048a07dSNicholas Piggin {
134*a048a07dSNicholas Piggin 	if (enable)
135*a048a07dSNicholas Piggin 		do_stf_barrier_fixups(stf_enabled_flush_types);
136*a048a07dSNicholas Piggin 	else
137*a048a07dSNicholas Piggin 		do_stf_barrier_fixups(STF_BARRIER_NONE);
138*a048a07dSNicholas Piggin 
139*a048a07dSNicholas Piggin 	stf_barrier = enable;
140*a048a07dSNicholas Piggin }
141*a048a07dSNicholas Piggin 
142*a048a07dSNicholas Piggin void setup_stf_barrier(void)
143*a048a07dSNicholas Piggin {
144*a048a07dSNicholas Piggin 	enum stf_barrier_type type;
145*a048a07dSNicholas Piggin 	bool enable, hv;
146*a048a07dSNicholas Piggin 
147*a048a07dSNicholas Piggin 	hv = cpu_has_feature(CPU_FTR_HVMODE);
148*a048a07dSNicholas Piggin 
149*a048a07dSNicholas Piggin 	/* Default to fallback in case fw-features are not available */
150*a048a07dSNicholas Piggin 	if (cpu_has_feature(CPU_FTR_ARCH_300))
151*a048a07dSNicholas Piggin 		type = STF_BARRIER_EIEIO;
152*a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_207S))
153*a048a07dSNicholas Piggin 		type = STF_BARRIER_SYNC_ORI;
154*a048a07dSNicholas Piggin 	else if (cpu_has_feature(CPU_FTR_ARCH_206))
155*a048a07dSNicholas Piggin 		type = STF_BARRIER_FALLBACK;
156*a048a07dSNicholas Piggin 	else
157*a048a07dSNicholas Piggin 		type = STF_BARRIER_NONE;
158*a048a07dSNicholas Piggin 
159*a048a07dSNicholas Piggin 	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
160*a048a07dSNicholas Piggin 		(security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) ||
161*a048a07dSNicholas Piggin 		 (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv));
162*a048a07dSNicholas Piggin 
163*a048a07dSNicholas Piggin 	if (type == STF_BARRIER_FALLBACK) {
164*a048a07dSNicholas Piggin 		pr_info("stf-barrier: fallback barrier available\n");
165*a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_SYNC_ORI) {
166*a048a07dSNicholas Piggin 		pr_info("stf-barrier: hwsync barrier available\n");
167*a048a07dSNicholas Piggin 	} else if (type == STF_BARRIER_EIEIO) {
168*a048a07dSNicholas Piggin 		pr_info("stf-barrier: eieio barrier available\n");
169*a048a07dSNicholas Piggin 	}
170*a048a07dSNicholas Piggin 
171*a048a07dSNicholas Piggin 	stf_enabled_flush_types = type;
172*a048a07dSNicholas Piggin 
173*a048a07dSNicholas Piggin 	if (!no_stf_barrier)
174*a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
175*a048a07dSNicholas Piggin }
176*a048a07dSNicholas Piggin 
177*a048a07dSNicholas Piggin ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf)
178*a048a07dSNicholas Piggin {
179*a048a07dSNicholas Piggin 	if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) {
180*a048a07dSNicholas Piggin 		const char *type;
181*a048a07dSNicholas Piggin 		switch (stf_enabled_flush_types) {
182*a048a07dSNicholas Piggin 		case STF_BARRIER_EIEIO:
183*a048a07dSNicholas Piggin 			type = "eieio";
184*a048a07dSNicholas Piggin 			break;
185*a048a07dSNicholas Piggin 		case STF_BARRIER_SYNC_ORI:
186*a048a07dSNicholas Piggin 			type = "hwsync";
187*a048a07dSNicholas Piggin 			break;
188*a048a07dSNicholas Piggin 		case STF_BARRIER_FALLBACK:
189*a048a07dSNicholas Piggin 			type = "fallback";
190*a048a07dSNicholas Piggin 			break;
191*a048a07dSNicholas Piggin 		default:
192*a048a07dSNicholas Piggin 			type = "unknown";
193*a048a07dSNicholas Piggin 		}
194*a048a07dSNicholas Piggin 		return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type);
195*a048a07dSNicholas Piggin 	}
196*a048a07dSNicholas Piggin 
197*a048a07dSNicholas Piggin 	if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
198*a048a07dSNicholas Piggin 	    !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
199*a048a07dSNicholas Piggin 		return sprintf(buf, "Not affected\n");
200*a048a07dSNicholas Piggin 
201*a048a07dSNicholas Piggin 	return sprintf(buf, "Vulnerable\n");
202*a048a07dSNicholas Piggin }
203*a048a07dSNicholas Piggin 
204*a048a07dSNicholas Piggin #ifdef CONFIG_DEBUG_FS
205*a048a07dSNicholas Piggin static int stf_barrier_set(void *data, u64 val)
206*a048a07dSNicholas Piggin {
207*a048a07dSNicholas Piggin 	bool enable;
208*a048a07dSNicholas Piggin 
209*a048a07dSNicholas Piggin 	if (val == 1)
210*a048a07dSNicholas Piggin 		enable = true;
211*a048a07dSNicholas Piggin 	else if (val == 0)
212*a048a07dSNicholas Piggin 		enable = false;
213*a048a07dSNicholas Piggin 	else
214*a048a07dSNicholas Piggin 		return -EINVAL;
215*a048a07dSNicholas Piggin 
216*a048a07dSNicholas Piggin 	/* Only do anything if we're changing state */
217*a048a07dSNicholas Piggin 	if (enable != stf_barrier)
218*a048a07dSNicholas Piggin 		stf_barrier_enable(enable);
219*a048a07dSNicholas Piggin 
220*a048a07dSNicholas Piggin 	return 0;
221*a048a07dSNicholas Piggin }
222*a048a07dSNicholas Piggin 
223*a048a07dSNicholas Piggin static int stf_barrier_get(void *data, u64 *val)
224*a048a07dSNicholas Piggin {
225*a048a07dSNicholas Piggin 	*val = stf_barrier ? 1 : 0;
226*a048a07dSNicholas Piggin 	return 0;
227*a048a07dSNicholas Piggin }
228*a048a07dSNicholas Piggin 
229*a048a07dSNicholas Piggin DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n");
230*a048a07dSNicholas Piggin 
231*a048a07dSNicholas Piggin static __init int stf_barrier_debugfs_init(void)
232*a048a07dSNicholas Piggin {
233*a048a07dSNicholas Piggin 	debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier);
234*a048a07dSNicholas Piggin 	return 0;
235*a048a07dSNicholas Piggin }
236*a048a07dSNicholas Piggin device_initcall(stf_barrier_debugfs_init);
237*a048a07dSNicholas Piggin #endif /* CONFIG_DEBUG_FS */
238