1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2022 ARM Limited.
4  */
5 
6 #include <errno.h>
7 #include <signal.h>
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <sys/auxv.h>
15 #include <sys/prctl.h>
16 #include <asm/hwcap.h>
17 #include <asm/sigcontext.h>
18 #include <asm/unistd.h>
19 
20 #include "../../kselftest.h"
21 
22 #define TESTS_PER_HWCAP 2
23 
24 /*
25  * Function expected to generate SIGILL when the feature is not
26  * supported and return when it is supported. If SIGILL is generated
27  * then the handler must be able to skip over the instruction safely.
28  *
29  * Note that it is expected that for many architecture extensions
30  * there are no specific traps due to no architecture state being
31  * added so we may not fault if running on a kernel which doesn't know
32  * to add the hwcap.
33  */
34 typedef void (*sigill_fn)(void);
35 
36 static void rng_sigill(void)
37 {
38 	asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0");
39 }
40 
41 static void sme_sigill(void)
42 {
43 	/* RDSVL x0, #0 */
44 	asm volatile(".inst 0x04bf5800" : : : "x0");
45 }
46 
47 static void sve_sigill(void)
48 {
49 	/* RDVL x0, #0 */
50 	asm volatile(".inst 0x04bf5000" : : : "x0");
51 }
52 
53 static void sve2_sigill(void)
54 {
55 	/* SQABS Z0.b, P0/M, Z0.B */
56 	asm volatile(".inst 0x4408A000" : : : "z0");
57 }
58 
59 static void sveaes_sigill(void)
60 {
61 	/* AESD z0.b, z0.b, z0.b */
62 	asm volatile(".inst 0x4522e400" : : : "z0");
63 }
64 
65 static void svepmull_sigill(void)
66 {
67 	/* PMULLB Z0.Q, Z0.D, Z0.D */
68 	asm volatile(".inst 0x45006800" : : : "z0");
69 }
70 
71 static void svebitperm_sigill(void)
72 {
73 	/* BDEP Z0.B, Z0.B, Z0.B */
74 	asm volatile(".inst 0x4500b400" : : : "z0");
75 }
76 
77 static void svesha3_sigill(void)
78 {
79 	/* EOR3 Z0.D, Z0.D, Z0.D, Z0.D */
80 	asm volatile(".inst 0x4203800" : : : "z0");
81 }
82 
83 static void svesm4_sigill(void)
84 {
85 	/* SM4E Z0.S, Z0.S, Z0.S */
86 	asm volatile(".inst 0x4523e000" : : : "z0");
87 }
88 
89 static void svei8mm_sigill(void)
90 {
91 	/* USDOT Z0.S, Z0.B, Z0.B[0] */
92 	asm volatile(".inst 0x44a01800" : : : "z0");
93 }
94 
95 static void svef32mm_sigill(void)
96 {
97 	/* FMMLA Z0.S, Z0.S, Z0.S */
98 	asm volatile(".inst 0x64a0e400" : : : "z0");
99 }
100 
101 static void svef64mm_sigill(void)
102 {
103 	/* FMMLA Z0.D, Z0.D, Z0.D */
104 	asm volatile(".inst 0x64e0e400" : : : "z0");
105 }
106 
107 static void svebf16_sigill(void)
108 {
109 	/* BFCVT Z0.H, P0/M, Z0.S */
110 	asm volatile(".inst 0x658aa000" : : : "z0");
111 }
112 
113 static const struct hwcap_data {
114 	const char *name;
115 	unsigned long at_hwcap;
116 	unsigned long hwcap_bit;
117 	const char *cpuinfo;
118 	sigill_fn sigill_fn;
119 	bool sigill_reliable;
120 } hwcaps[] = {
121 	{
122 		.name = "RNG",
123 		.at_hwcap = AT_HWCAP2,
124 		.hwcap_bit = HWCAP2_RNG,
125 		.cpuinfo = "rng",
126 		.sigill_fn = rng_sigill,
127 	},
128 	{
129 		.name = "SME",
130 		.at_hwcap = AT_HWCAP2,
131 		.hwcap_bit = HWCAP2_SME,
132 		.cpuinfo = "sme",
133 		.sigill_fn = sme_sigill,
134 		.sigill_reliable = true,
135 	},
136 	{
137 		.name = "SVE",
138 		.at_hwcap = AT_HWCAP,
139 		.hwcap_bit = HWCAP_SVE,
140 		.cpuinfo = "sve",
141 		.sigill_fn = sve_sigill,
142 		.sigill_reliable = true,
143 	},
144 	{
145 		.name = "SVE 2",
146 		.at_hwcap = AT_HWCAP2,
147 		.hwcap_bit = HWCAP2_SVE2,
148 		.cpuinfo = "sve2",
149 		.sigill_fn = sve2_sigill,
150 	},
151 	{
152 		.name = "SVE AES",
153 		.at_hwcap = AT_HWCAP2,
154 		.hwcap_bit = HWCAP2_SVEAES,
155 		.cpuinfo = "sveaes",
156 		.sigill_fn = sveaes_sigill,
157 	},
158 	{
159 		.name = "SVE2 PMULL",
160 		.at_hwcap = AT_HWCAP2,
161 		.hwcap_bit = HWCAP2_SVEPMULL,
162 		.cpuinfo = "svepmull",
163 		.sigill_fn = svepmull_sigill,
164 	},
165 	{
166 		.name = "SVE2 BITPERM",
167 		.at_hwcap = AT_HWCAP2,
168 		.hwcap_bit = HWCAP2_SVEBITPERM,
169 		.cpuinfo = "svebitperm",
170 		.sigill_fn = svebitperm_sigill,
171 	},
172 	{
173 		.name = "SVE2 SHA3",
174 		.at_hwcap = AT_HWCAP2,
175 		.hwcap_bit = HWCAP2_SVESHA3,
176 		.cpuinfo = "svesha3",
177 		.sigill_fn = svesha3_sigill,
178 	},
179 	{
180 		.name = "SVE2 SM4",
181 		.at_hwcap = AT_HWCAP2,
182 		.hwcap_bit = HWCAP2_SVESM4,
183 		.cpuinfo = "svesm4",
184 		.sigill_fn = svesm4_sigill,
185 	},
186 	{
187 		.name = "SVE2 I8MM",
188 		.at_hwcap = AT_HWCAP2,
189 		.hwcap_bit = HWCAP2_SVEI8MM,
190 		.cpuinfo = "svei8mm",
191 		.sigill_fn = svei8mm_sigill,
192 	},
193 	{
194 		.name = "SVE2 F32MM",
195 		.at_hwcap = AT_HWCAP2,
196 		.hwcap_bit = HWCAP2_SVEF32MM,
197 		.cpuinfo = "svef32mm",
198 		.sigill_fn = svef32mm_sigill,
199 	},
200 	{
201 		.name = "SVE2 F64MM",
202 		.at_hwcap = AT_HWCAP2,
203 		.hwcap_bit = HWCAP2_SVEF64MM,
204 		.cpuinfo = "svef64mm",
205 		.sigill_fn = svef64mm_sigill,
206 	},
207 	{
208 		.name = "SVE2 BF16",
209 		.at_hwcap = AT_HWCAP2,
210 		.hwcap_bit = HWCAP2_SVEBF16,
211 		.cpuinfo = "svebf16",
212 		.sigill_fn = svebf16_sigill,
213 	},
214 	{
215 		.name = "SVE2 EBF16",
216 		.at_hwcap = AT_HWCAP2,
217 		.hwcap_bit = HWCAP2_SVE_EBF16,
218 		.cpuinfo = "sveebf16",
219 	},
220 };
221 
222 static bool seen_sigill;
223 
224 static void handle_sigill(int sig, siginfo_t *info, void *context)
225 {
226 	ucontext_t *uc = context;
227 
228 	seen_sigill = true;
229 
230 	/* Skip over the offending instruction */
231 	uc->uc_mcontext.pc += 4;
232 }
233 
234 bool cpuinfo_present(const char *name)
235 {
236 	FILE *f;
237 	char buf[2048], name_space[30], name_newline[30];
238 	char *s;
239 
240 	/*
241 	 * The feature should appear with a leading space and either a
242 	 * trailing space or a newline.
243 	 */
244 	snprintf(name_space, sizeof(name_space), " %s ", name);
245 	snprintf(name_newline, sizeof(name_newline), " %s\n", name);
246 
247 	f = fopen("/proc/cpuinfo", "r");
248 	if (!f) {
249 		ksft_print_msg("Failed to open /proc/cpuinfo\n");
250 		return false;
251 	}
252 
253 	while (fgets(buf, sizeof(buf), f)) {
254 		/* Features: line? */
255 		if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0)
256 			continue;
257 
258 		/* All CPUs should be symmetric, don't read any more */
259 		fclose(f);
260 
261 		s = strstr(buf, name_space);
262 		if (s)
263 			return true;
264 		s = strstr(buf, name_newline);
265 		if (s)
266 			return true;
267 
268 		return false;
269 	}
270 
271 	ksft_print_msg("Failed to find Features in /proc/cpuinfo\n");
272 	fclose(f);
273 	return false;
274 }
275 
276 int main(void)
277 {
278 	const struct hwcap_data *hwcap;
279 	int i, ret;
280 	bool have_cpuinfo, have_hwcap;
281 	struct sigaction sa;
282 
283 	ksft_print_header();
284 	ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP);
285 
286 	memset(&sa, 0, sizeof(sa));
287 	sa.sa_sigaction = handle_sigill;
288 	sa.sa_flags = SA_RESTART | SA_SIGINFO;
289 	sigemptyset(&sa.sa_mask);
290 	ret = sigaction(SIGILL, &sa, NULL);
291 	if (ret < 0)
292 		ksft_exit_fail_msg("Failed to install SIGILL handler: %s (%d)\n",
293 				   strerror(errno), errno);
294 
295 	for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
296 		hwcap = &hwcaps[i];
297 
298 		have_hwcap = getauxval(hwcap->at_hwcap) & hwcap->hwcap_bit;
299 		have_cpuinfo = cpuinfo_present(hwcap->cpuinfo);
300 
301 		if (have_hwcap)
302 			ksft_print_msg("%s present\n", hwcap->name);
303 
304 		ksft_test_result(have_hwcap == have_cpuinfo,
305 				 "cpuinfo_match_%s\n", hwcap->name);
306 
307 		if (hwcap->sigill_fn) {
308 			seen_sigill = false;
309 			hwcap->sigill_fn();
310 
311 			if (have_hwcap) {
312 				/* Should be able to use the extension */
313 				ksft_test_result(!seen_sigill, "sigill_%s\n",
314 						 hwcap->name);
315 			} else if (hwcap->sigill_reliable) {
316 				/* Guaranteed a SIGILL */
317 				ksft_test_result(seen_sigill, "sigill_%s\n",
318 						 hwcap->name);
319 			} else {
320 				/* Missing SIGILL might be fine */
321 				ksft_print_msg("SIGILL %sreported for %s\n",
322 					       seen_sigill ? "" : "not ",
323 					       hwcap->name);
324 				ksft_test_result_skip("sigill_%s\n",
325 						      hwcap->name);
326 			}
327 		} else {
328 			ksft_test_result_skip("sigill_%s\n",
329 					      hwcap->name);
330 		}
331 	}
332 
333 	ksft_print_cnts();
334 
335 	return 0;
336 }
337