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