17a9bcaaaSMark Brown // SPDX-License-Identifier: GPL-2.0-only
27a9bcaaaSMark Brown /*
37a9bcaaaSMark Brown  * Copyright (C) 2022 ARM Limited.
47a9bcaaaSMark Brown  */
57a9bcaaaSMark Brown 
67a9bcaaaSMark Brown #include <errno.h>
77a9bcaaaSMark Brown #include <signal.h>
87a9bcaaaSMark Brown #include <stdbool.h>
97a9bcaaaSMark Brown #include <stddef.h>
107a9bcaaaSMark Brown #include <stdio.h>
117a9bcaaaSMark Brown #include <stdlib.h>
127a9bcaaaSMark Brown #include <string.h>
137a9bcaaaSMark Brown #include <unistd.h>
147a9bcaaaSMark Brown #include <sys/auxv.h>
157a9bcaaaSMark Brown #include <sys/prctl.h>
167a9bcaaaSMark Brown #include <asm/hwcap.h>
177a9bcaaaSMark Brown #include <asm/sigcontext.h>
187a9bcaaaSMark Brown #include <asm/unistd.h>
197a9bcaaaSMark Brown 
207a9bcaaaSMark Brown #include "../../kselftest.h"
217a9bcaaaSMark Brown 
227a9bcaaaSMark Brown #define TESTS_PER_HWCAP 2
237a9bcaaaSMark Brown 
247a9bcaaaSMark Brown /*
257a9bcaaaSMark Brown  * Function expected to generate SIGILL when the feature is not
267a9bcaaaSMark Brown  * supported and return when it is supported. If SIGILL is generated
277a9bcaaaSMark Brown  * then the handler must be able to skip over the instruction safely.
287a9bcaaaSMark Brown  *
297a9bcaaaSMark Brown  * Note that it is expected that for many architecture extensions
307a9bcaaaSMark Brown  * there are no specific traps due to no architecture state being
317a9bcaaaSMark Brown  * added so we may not fault if running on a kernel which doesn't know
327a9bcaaaSMark Brown  * to add the hwcap.
337a9bcaaaSMark Brown  */
347a9bcaaaSMark Brown typedef void (*sigill_fn)(void);
357a9bcaaaSMark Brown 
36b0ab73a5SMark Brown static void cssc_sigill(void)
37b0ab73a5SMark Brown {
38b0ab73a5SMark Brown 	/* CNT x0, x0 */
39b0ab73a5SMark Brown 	asm volatile(".inst 0xdac01c00" : : : "x0");
40b0ab73a5SMark Brown }
41b0ab73a5SMark Brown 
42ef939f30SMark Brown static void rng_sigill(void)
43ef939f30SMark Brown {
44ef939f30SMark Brown 	asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0");
45ef939f30SMark Brown }
46ef939f30SMark Brown 
477a9bcaaaSMark Brown static void sme_sigill(void)
487a9bcaaaSMark Brown {
497a9bcaaaSMark Brown 	/* RDSVL x0, #0 */
507a9bcaaaSMark Brown 	asm volatile(".inst 0x04bf5800" : : : "x0");
517a9bcaaaSMark Brown }
527a9bcaaaSMark Brown 
537a9bcaaaSMark Brown static void sve_sigill(void)
547a9bcaaaSMark Brown {
557a9bcaaaSMark Brown 	/* RDVL x0, #0 */
567a9bcaaaSMark Brown 	asm volatile(".inst 0x04bf5000" : : : "x0");
577a9bcaaaSMark Brown }
587a9bcaaaSMark Brown 
59859a9d51SMark Brown static void sve2_sigill(void)
60859a9d51SMark Brown {
61859a9d51SMark Brown 	/* SQABS Z0.b, P0/M, Z0.B */
62859a9d51SMark Brown 	asm volatile(".inst 0x4408A000" : : : "z0");
63859a9d51SMark Brown }
64859a9d51SMark Brown 
65859a9d51SMark Brown static void sveaes_sigill(void)
66859a9d51SMark Brown {
67859a9d51SMark Brown 	/* AESD z0.b, z0.b, z0.b */
68859a9d51SMark Brown 	asm volatile(".inst 0x4522e400" : : : "z0");
69859a9d51SMark Brown }
70859a9d51SMark Brown 
71859a9d51SMark Brown static void svepmull_sigill(void)
72859a9d51SMark Brown {
73859a9d51SMark Brown 	/* PMULLB Z0.Q, Z0.D, Z0.D */
74859a9d51SMark Brown 	asm volatile(".inst 0x45006800" : : : "z0");
75859a9d51SMark Brown }
76859a9d51SMark Brown 
77859a9d51SMark Brown static void svebitperm_sigill(void)
78859a9d51SMark Brown {
79859a9d51SMark Brown 	/* BDEP Z0.B, Z0.B, Z0.B */
80859a9d51SMark Brown 	asm volatile(".inst 0x4500b400" : : : "z0");
81859a9d51SMark Brown }
82859a9d51SMark Brown 
83859a9d51SMark Brown static void svesha3_sigill(void)
84859a9d51SMark Brown {
85859a9d51SMark Brown 	/* EOR3 Z0.D, Z0.D, Z0.D, Z0.D */
86859a9d51SMark Brown 	asm volatile(".inst 0x4203800" : : : "z0");
87859a9d51SMark Brown }
88859a9d51SMark Brown 
89859a9d51SMark Brown static void svesm4_sigill(void)
90859a9d51SMark Brown {
91859a9d51SMark Brown 	/* SM4E Z0.S, Z0.S, Z0.S */
92859a9d51SMark Brown 	asm volatile(".inst 0x4523e000" : : : "z0");
93859a9d51SMark Brown }
94859a9d51SMark Brown 
95859a9d51SMark Brown static void svei8mm_sigill(void)
96859a9d51SMark Brown {
97859a9d51SMark Brown 	/* USDOT Z0.S, Z0.B, Z0.B[0] */
98859a9d51SMark Brown 	asm volatile(".inst 0x44a01800" : : : "z0");
99859a9d51SMark Brown }
100859a9d51SMark Brown 
101859a9d51SMark Brown static void svef32mm_sigill(void)
102859a9d51SMark Brown {
103859a9d51SMark Brown 	/* FMMLA Z0.S, Z0.S, Z0.S */
104859a9d51SMark Brown 	asm volatile(".inst 0x64a0e400" : : : "z0");
105859a9d51SMark Brown }
106859a9d51SMark Brown 
107859a9d51SMark Brown static void svef64mm_sigill(void)
108859a9d51SMark Brown {
109859a9d51SMark Brown 	/* FMMLA Z0.D, Z0.D, Z0.D */
110859a9d51SMark Brown 	asm volatile(".inst 0x64e0e400" : : : "z0");
111859a9d51SMark Brown }
112859a9d51SMark Brown 
113859a9d51SMark Brown static void svebf16_sigill(void)
114859a9d51SMark Brown {
115859a9d51SMark Brown 	/* BFCVT Z0.H, P0/M, Z0.S */
116859a9d51SMark Brown 	asm volatile(".inst 0x658aa000" : : : "z0");
117859a9d51SMark Brown }
118859a9d51SMark Brown 
1197a9bcaaaSMark Brown static const struct hwcap_data {
1207a9bcaaaSMark Brown 	const char *name;
1217a9bcaaaSMark Brown 	unsigned long at_hwcap;
1227a9bcaaaSMark Brown 	unsigned long hwcap_bit;
1237a9bcaaaSMark Brown 	const char *cpuinfo;
1247a9bcaaaSMark Brown 	sigill_fn sigill_fn;
1257a9bcaaaSMark Brown 	bool sigill_reliable;
1267a9bcaaaSMark Brown } hwcaps[] = {
1277a9bcaaaSMark Brown 	{
128b0ab73a5SMark Brown 		.name = "CSSC",
129b0ab73a5SMark Brown 		.at_hwcap = AT_HWCAP2,
130b0ab73a5SMark Brown 		.hwcap_bit = HWCAP2_CSSC,
131b0ab73a5SMark Brown 		.cpuinfo = "cssc",
132b0ab73a5SMark Brown 		.sigill_fn = cssc_sigill,
133b0ab73a5SMark Brown 	},
134b0ab73a5SMark Brown 	{
135ef939f30SMark Brown 		.name = "RNG",
136ef939f30SMark Brown 		.at_hwcap = AT_HWCAP2,
137ef939f30SMark Brown 		.hwcap_bit = HWCAP2_RNG,
138ef939f30SMark Brown 		.cpuinfo = "rng",
139ef939f30SMark Brown 		.sigill_fn = rng_sigill,
140ef939f30SMark Brown 	},
141ef939f30SMark Brown 	{
142*989d37fcSMark Brown 		.name = "RPRFM",
143*989d37fcSMark Brown 		.at_hwcap = AT_HWCAP2,
144*989d37fcSMark Brown 		.hwcap_bit = HWCAP2_RPRFM,
145*989d37fcSMark Brown 		.cpuinfo = "rprfm",
146*989d37fcSMark Brown 	},
147*989d37fcSMark Brown 	{
1487a9bcaaaSMark Brown 		.name = "SME",
1497a9bcaaaSMark Brown 		.at_hwcap = AT_HWCAP2,
1507a9bcaaaSMark Brown 		.hwcap_bit = HWCAP2_SME,
1517a9bcaaaSMark Brown 		.cpuinfo = "sme",
1527a9bcaaaSMark Brown 		.sigill_fn = sme_sigill,
1537a9bcaaaSMark Brown 		.sigill_reliable = true,
1547a9bcaaaSMark Brown 	},
1557a9bcaaaSMark Brown 	{
1567a9bcaaaSMark Brown 		.name = "SVE",
1577a9bcaaaSMark Brown 		.at_hwcap = AT_HWCAP,
1587a9bcaaaSMark Brown 		.hwcap_bit = HWCAP_SVE,
1597a9bcaaaSMark Brown 		.cpuinfo = "sve",
1607a9bcaaaSMark Brown 		.sigill_fn = sve_sigill,
1617a9bcaaaSMark Brown 		.sigill_reliable = true,
1627a9bcaaaSMark Brown 	},
163859a9d51SMark Brown 	{
164859a9d51SMark Brown 		.name = "SVE 2",
165859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
166859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVE2,
167859a9d51SMark Brown 		.cpuinfo = "sve2",
168859a9d51SMark Brown 		.sigill_fn = sve2_sigill,
169859a9d51SMark Brown 	},
170859a9d51SMark Brown 	{
171859a9d51SMark Brown 		.name = "SVE AES",
172859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
173859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEAES,
174859a9d51SMark Brown 		.cpuinfo = "sveaes",
175859a9d51SMark Brown 		.sigill_fn = sveaes_sigill,
176859a9d51SMark Brown 	},
177859a9d51SMark Brown 	{
178859a9d51SMark Brown 		.name = "SVE2 PMULL",
179859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
180859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEPMULL,
181859a9d51SMark Brown 		.cpuinfo = "svepmull",
182859a9d51SMark Brown 		.sigill_fn = svepmull_sigill,
183859a9d51SMark Brown 	},
184859a9d51SMark Brown 	{
185859a9d51SMark Brown 		.name = "SVE2 BITPERM",
186859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
187859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEBITPERM,
188859a9d51SMark Brown 		.cpuinfo = "svebitperm",
189859a9d51SMark Brown 		.sigill_fn = svebitperm_sigill,
190859a9d51SMark Brown 	},
191859a9d51SMark Brown 	{
192859a9d51SMark Brown 		.name = "SVE2 SHA3",
193859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
194859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVESHA3,
195859a9d51SMark Brown 		.cpuinfo = "svesha3",
196859a9d51SMark Brown 		.sigill_fn = svesha3_sigill,
197859a9d51SMark Brown 	},
198859a9d51SMark Brown 	{
199859a9d51SMark Brown 		.name = "SVE2 SM4",
200859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
201859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVESM4,
202859a9d51SMark Brown 		.cpuinfo = "svesm4",
203859a9d51SMark Brown 		.sigill_fn = svesm4_sigill,
204859a9d51SMark Brown 	},
205859a9d51SMark Brown 	{
206859a9d51SMark Brown 		.name = "SVE2 I8MM",
207859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
208859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEI8MM,
209859a9d51SMark Brown 		.cpuinfo = "svei8mm",
210859a9d51SMark Brown 		.sigill_fn = svei8mm_sigill,
211859a9d51SMark Brown 	},
212859a9d51SMark Brown 	{
213859a9d51SMark Brown 		.name = "SVE2 F32MM",
214859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
215859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEF32MM,
216859a9d51SMark Brown 		.cpuinfo = "svef32mm",
217859a9d51SMark Brown 		.sigill_fn = svef32mm_sigill,
218859a9d51SMark Brown 	},
219859a9d51SMark Brown 	{
220859a9d51SMark Brown 		.name = "SVE2 F64MM",
221859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
222859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEF64MM,
223859a9d51SMark Brown 		.cpuinfo = "svef64mm",
224859a9d51SMark Brown 		.sigill_fn = svef64mm_sigill,
225859a9d51SMark Brown 	},
226859a9d51SMark Brown 	{
227859a9d51SMark Brown 		.name = "SVE2 BF16",
228859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
229859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEBF16,
230859a9d51SMark Brown 		.cpuinfo = "svebf16",
231859a9d51SMark Brown 		.sigill_fn = svebf16_sigill,
232859a9d51SMark Brown 	},
233859a9d51SMark Brown 	{
234859a9d51SMark Brown 		.name = "SVE2 EBF16",
235859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
236859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVE_EBF16,
237859a9d51SMark Brown 		.cpuinfo = "sveebf16",
238859a9d51SMark Brown 	},
2397a9bcaaaSMark Brown };
2407a9bcaaaSMark Brown 
2417a9bcaaaSMark Brown static bool seen_sigill;
2427a9bcaaaSMark Brown 
2437a9bcaaaSMark Brown static void handle_sigill(int sig, siginfo_t *info, void *context)
2447a9bcaaaSMark Brown {
2457a9bcaaaSMark Brown 	ucontext_t *uc = context;
2467a9bcaaaSMark Brown 
2477a9bcaaaSMark Brown 	seen_sigill = true;
2487a9bcaaaSMark Brown 
2497a9bcaaaSMark Brown 	/* Skip over the offending instruction */
2507a9bcaaaSMark Brown 	uc->uc_mcontext.pc += 4;
2517a9bcaaaSMark Brown }
2527a9bcaaaSMark Brown 
2537a9bcaaaSMark Brown bool cpuinfo_present(const char *name)
2547a9bcaaaSMark Brown {
2557a9bcaaaSMark Brown 	FILE *f;
2567a9bcaaaSMark Brown 	char buf[2048], name_space[30], name_newline[30];
2577a9bcaaaSMark Brown 	char *s;
2587a9bcaaaSMark Brown 
2597a9bcaaaSMark Brown 	/*
2607a9bcaaaSMark Brown 	 * The feature should appear with a leading space and either a
2617a9bcaaaSMark Brown 	 * trailing space or a newline.
2627a9bcaaaSMark Brown 	 */
2637a9bcaaaSMark Brown 	snprintf(name_space, sizeof(name_space), " %s ", name);
2647a9bcaaaSMark Brown 	snprintf(name_newline, sizeof(name_newline), " %s\n", name);
2657a9bcaaaSMark Brown 
2667a9bcaaaSMark Brown 	f = fopen("/proc/cpuinfo", "r");
2677a9bcaaaSMark Brown 	if (!f) {
2687a9bcaaaSMark Brown 		ksft_print_msg("Failed to open /proc/cpuinfo\n");
2697a9bcaaaSMark Brown 		return false;
2707a9bcaaaSMark Brown 	}
2717a9bcaaaSMark Brown 
2727a9bcaaaSMark Brown 	while (fgets(buf, sizeof(buf), f)) {
2737a9bcaaaSMark Brown 		/* Features: line? */
2747a9bcaaaSMark Brown 		if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0)
2757a9bcaaaSMark Brown 			continue;
2767a9bcaaaSMark Brown 
2777a9bcaaaSMark Brown 		/* All CPUs should be symmetric, don't read any more */
2787a9bcaaaSMark Brown 		fclose(f);
2797a9bcaaaSMark Brown 
2807a9bcaaaSMark Brown 		s = strstr(buf, name_space);
2817a9bcaaaSMark Brown 		if (s)
2827a9bcaaaSMark Brown 			return true;
2837a9bcaaaSMark Brown 		s = strstr(buf, name_newline);
2847a9bcaaaSMark Brown 		if (s)
2857a9bcaaaSMark Brown 			return true;
2867a9bcaaaSMark Brown 
2877a9bcaaaSMark Brown 		return false;
2887a9bcaaaSMark Brown 	}
2897a9bcaaaSMark Brown 
2907a9bcaaaSMark Brown 	ksft_print_msg("Failed to find Features in /proc/cpuinfo\n");
2917a9bcaaaSMark Brown 	fclose(f);
2927a9bcaaaSMark Brown 	return false;
2937a9bcaaaSMark Brown }
2947a9bcaaaSMark Brown 
2957a9bcaaaSMark Brown int main(void)
2967a9bcaaaSMark Brown {
2977a9bcaaaSMark Brown 	const struct hwcap_data *hwcap;
2987a9bcaaaSMark Brown 	int i, ret;
2997a9bcaaaSMark Brown 	bool have_cpuinfo, have_hwcap;
3007a9bcaaaSMark Brown 	struct sigaction sa;
3017a9bcaaaSMark Brown 
3027a9bcaaaSMark Brown 	ksft_print_header();
3037a9bcaaaSMark Brown 	ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP);
3047a9bcaaaSMark Brown 
3057a9bcaaaSMark Brown 	memset(&sa, 0, sizeof(sa));
3067a9bcaaaSMark Brown 	sa.sa_sigaction = handle_sigill;
3077a9bcaaaSMark Brown 	sa.sa_flags = SA_RESTART | SA_SIGINFO;
3087a9bcaaaSMark Brown 	sigemptyset(&sa.sa_mask);
3097a9bcaaaSMark Brown 	ret = sigaction(SIGILL, &sa, NULL);
3107a9bcaaaSMark Brown 	if (ret < 0)
3117a9bcaaaSMark Brown 		ksft_exit_fail_msg("Failed to install SIGILL handler: %s (%d)\n",
3127a9bcaaaSMark Brown 				   strerror(errno), errno);
3137a9bcaaaSMark Brown 
3147a9bcaaaSMark Brown 	for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
3157a9bcaaaSMark Brown 		hwcap = &hwcaps[i];
3167a9bcaaaSMark Brown 
31733060a64SMark Brown 		have_hwcap = getauxval(hwcap->at_hwcap) & hwcap->hwcap_bit;
3187a9bcaaaSMark Brown 		have_cpuinfo = cpuinfo_present(hwcap->cpuinfo);
3197a9bcaaaSMark Brown 
3207a9bcaaaSMark Brown 		if (have_hwcap)
32178d2b197SMark Brown 			ksft_print_msg("%s present\n", hwcap->name);
3227a9bcaaaSMark Brown 
3237a9bcaaaSMark Brown 		ksft_test_result(have_hwcap == have_cpuinfo,
3247a9bcaaaSMark Brown 				 "cpuinfo_match_%s\n", hwcap->name);
3257a9bcaaaSMark Brown 
3267a9bcaaaSMark Brown 		if (hwcap->sigill_fn) {
3277a9bcaaaSMark Brown 			seen_sigill = false;
3287a9bcaaaSMark Brown 			hwcap->sigill_fn();
3297a9bcaaaSMark Brown 
3307a9bcaaaSMark Brown 			if (have_hwcap) {
3317a9bcaaaSMark Brown 				/* Should be able to use the extension */
3327a9bcaaaSMark Brown 				ksft_test_result(!seen_sigill, "sigill_%s\n",
3337a9bcaaaSMark Brown 						 hwcap->name);
3347a9bcaaaSMark Brown 			} else if (hwcap->sigill_reliable) {
3357a9bcaaaSMark Brown 				/* Guaranteed a SIGILL */
3367a9bcaaaSMark Brown 				ksft_test_result(seen_sigill, "sigill_%s\n",
3377a9bcaaaSMark Brown 						 hwcap->name);
3387a9bcaaaSMark Brown 			} else {
3397a9bcaaaSMark Brown 				/* Missing SIGILL might be fine */
3407a9bcaaaSMark Brown 				ksft_print_msg("SIGILL %sreported for %s\n",
3417a9bcaaaSMark Brown 					       seen_sigill ? "" : "not ",
3427a9bcaaaSMark Brown 					       hwcap->name);
3437a9bcaaaSMark Brown 				ksft_test_result_skip("sigill_%s\n",
3447a9bcaaaSMark Brown 						      hwcap->name);
3457a9bcaaaSMark Brown 			}
3467a9bcaaaSMark Brown 		} else {
3477a9bcaaaSMark Brown 			ksft_test_result_skip("sigill_%s\n",
3487a9bcaaaSMark Brown 					      hwcap->name);
3497a9bcaaaSMark Brown 		}
3507a9bcaaaSMark Brown 	}
3517a9bcaaaSMark Brown 
3527a9bcaaaSMark Brown 	ksft_print_cnts();
3537a9bcaaaSMark Brown 
3547a9bcaaaSMark Brown 	return 0;
3557a9bcaaaSMark Brown }
356