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 
42*d1890517SZeng Heng static void ilrcpc_sigill(void)
43*d1890517SZeng Heng {
44*d1890517SZeng Heng 	/* LDAPUR W0, [SP, #8] */
45*d1890517SZeng Heng 	asm volatile(".inst 0x994083e0" : : : );
46*d1890517SZeng Heng }
47*d1890517SZeng Heng 
48*d1890517SZeng Heng static void lrcpc_sigill(void)
49*d1890517SZeng Heng {
50*d1890517SZeng Heng 	/* LDAPR W0, [SP, #0] */
51*d1890517SZeng Heng 	asm volatile(".inst 0xb8bfc3e0" : : : );
52*d1890517SZeng Heng }
53*d1890517SZeng Heng 
54d8a324f1SKristina Martsenko static void mops_sigill(void)
55d8a324f1SKristina Martsenko {
56d8a324f1SKristina Martsenko 	char dst[1], src[1];
57d8a324f1SKristina Martsenko 	register char *dstp asm ("x0") = dst;
58d8a324f1SKristina Martsenko 	register char *srcp asm ("x1") = src;
59d8a324f1SKristina Martsenko 	register long size asm ("x2") = 1;
60d8a324f1SKristina Martsenko 
61d8a324f1SKristina Martsenko 	/* CPYP [x0]!, [x1]!, x2! */
62d8a324f1SKristina Martsenko 	asm volatile(".inst 0x1d010440"
63d8a324f1SKristina Martsenko 		     : "+r" (dstp), "+r" (srcp), "+r" (size)
64d8a324f1SKristina Martsenko 		     :
65d8a324f1SKristina Martsenko 		     : "cc", "memory");
66d8a324f1SKristina Martsenko }
67d8a324f1SKristina Martsenko 
68ef939f30SMark Brown static void rng_sigill(void)
69ef939f30SMark Brown {
70ef939f30SMark Brown 	asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0");
71ef939f30SMark Brown }
72ef939f30SMark Brown 
737a9bcaaaSMark Brown static void sme_sigill(void)
747a9bcaaaSMark Brown {
757a9bcaaaSMark Brown 	/* RDSVL x0, #0 */
767a9bcaaaSMark Brown 	asm volatile(".inst 0x04bf5800" : : : "x0");
777a9bcaaaSMark Brown }
787a9bcaaaSMark Brown 
793eb1b41fSMark Brown static void sme2_sigill(void)
803eb1b41fSMark Brown {
813eb1b41fSMark Brown 	/* SMSTART ZA */
823eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C5_3, xzr" : : : );
833eb1b41fSMark Brown 
843eb1b41fSMark Brown 	/* ZERO ZT0 */
853eb1b41fSMark Brown 	asm volatile(".inst 0xc0480001" : : : );
863eb1b41fSMark Brown 
873eb1b41fSMark Brown 	/* SMSTOP */
883eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
893eb1b41fSMark Brown }
903eb1b41fSMark Brown 
913eb1b41fSMark Brown static void sme2p1_sigill(void)
923eb1b41fSMark Brown {
933eb1b41fSMark Brown 	/* SMSTART SM */
943eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C3_3, xzr" : : : );
953eb1b41fSMark Brown 
963eb1b41fSMark Brown 	/* BFCLAMP { Z0.H - Z1.H }, Z0.H, Z0.H */
973eb1b41fSMark Brown 	asm volatile(".inst 0xc120C000" : : : );
983eb1b41fSMark Brown 
993eb1b41fSMark Brown 	/* SMSTOP */
1003eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
1013eb1b41fSMark Brown }
1023eb1b41fSMark Brown 
1033eb1b41fSMark Brown static void smei16i32_sigill(void)
1043eb1b41fSMark Brown {
1053eb1b41fSMark Brown 	/* SMSTART */
1063eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
1073eb1b41fSMark Brown 
1083eb1b41fSMark Brown 	/* SMOPA ZA0.S, P0/M, P0/M, Z0.B, Z0.B */
1093eb1b41fSMark Brown 	asm volatile(".inst 0xa0800000" : : : );
1103eb1b41fSMark Brown 
1113eb1b41fSMark Brown 	/* SMSTOP */
1123eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
1133eb1b41fSMark Brown }
1143eb1b41fSMark Brown 
1153eb1b41fSMark Brown static void smebi32i32_sigill(void)
1163eb1b41fSMark Brown {
1173eb1b41fSMark Brown 	/* SMSTART */
1183eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
1193eb1b41fSMark Brown 
1203eb1b41fSMark Brown 	/* BMOPA ZA0.S, P0/M, P0/M, Z0.B, Z0.B */
1213eb1b41fSMark Brown 	asm volatile(".inst 0x80800008" : : : );
1223eb1b41fSMark Brown 
1233eb1b41fSMark Brown 	/* SMSTOP */
1243eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
1253eb1b41fSMark Brown }
1263eb1b41fSMark Brown 
1273eb1b41fSMark Brown static void smeb16b16_sigill(void)
1283eb1b41fSMark Brown {
1293eb1b41fSMark Brown 	/* SMSTART */
1303eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
1313eb1b41fSMark Brown 
1323eb1b41fSMark Brown 	/* BFADD ZA.H[W0, 0], {Z0.H-Z1.H} */
1333eb1b41fSMark Brown 	asm volatile(".inst 0xC1E41C00" : : : );
1343eb1b41fSMark Brown 
1353eb1b41fSMark Brown 	/* SMSTOP */
1363eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
1373eb1b41fSMark Brown }
1383eb1b41fSMark Brown 
1393eb1b41fSMark Brown static void smef16f16_sigill(void)
1403eb1b41fSMark Brown {
1413eb1b41fSMark Brown 	/* SMSTART */
1423eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
1433eb1b41fSMark Brown 
1443eb1b41fSMark Brown 	/* FADD ZA.H[W0, 0], { Z0.H-Z1.H } */
1453eb1b41fSMark Brown 	asm volatile(".inst 0xc1a41C00" : : : );
1463eb1b41fSMark Brown 
1473eb1b41fSMark Brown 	/* SMSTOP */
1483eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
1493eb1b41fSMark Brown }
1503eb1b41fSMark Brown 
1517a9bcaaaSMark Brown static void sve_sigill(void)
1527a9bcaaaSMark Brown {
1537a9bcaaaSMark Brown 	/* RDVL x0, #0 */
1547a9bcaaaSMark Brown 	asm volatile(".inst 0x04bf5000" : : : "x0");
1557a9bcaaaSMark Brown }
1567a9bcaaaSMark Brown 
157859a9d51SMark Brown static void sve2_sigill(void)
158859a9d51SMark Brown {
159859a9d51SMark Brown 	/* SQABS Z0.b, P0/M, Z0.B */
160859a9d51SMark Brown 	asm volatile(".inst 0x4408A000" : : : "z0");
161859a9d51SMark Brown }
162859a9d51SMark Brown 
163c5195b02SMark Brown static void sve2p1_sigill(void)
164c5195b02SMark Brown {
165c5195b02SMark Brown 	/* BFADD Z0.H, Z0.H, Z0.H */
166c5195b02SMark Brown 	asm volatile(".inst 0x65000000" : : : "z0");
167c5195b02SMark Brown }
168c5195b02SMark Brown 
169859a9d51SMark Brown static void sveaes_sigill(void)
170859a9d51SMark Brown {
171859a9d51SMark Brown 	/* AESD z0.b, z0.b, z0.b */
172859a9d51SMark Brown 	asm volatile(".inst 0x4522e400" : : : "z0");
173859a9d51SMark Brown }
174859a9d51SMark Brown 
175859a9d51SMark Brown static void svepmull_sigill(void)
176859a9d51SMark Brown {
177859a9d51SMark Brown 	/* PMULLB Z0.Q, Z0.D, Z0.D */
178859a9d51SMark Brown 	asm volatile(".inst 0x45006800" : : : "z0");
179859a9d51SMark Brown }
180859a9d51SMark Brown 
181859a9d51SMark Brown static void svebitperm_sigill(void)
182859a9d51SMark Brown {
183859a9d51SMark Brown 	/* BDEP Z0.B, Z0.B, Z0.B */
184859a9d51SMark Brown 	asm volatile(".inst 0x4500b400" : : : "z0");
185859a9d51SMark Brown }
186859a9d51SMark Brown 
187859a9d51SMark Brown static void svesha3_sigill(void)
188859a9d51SMark Brown {
189859a9d51SMark Brown 	/* EOR3 Z0.D, Z0.D, Z0.D, Z0.D */
190859a9d51SMark Brown 	asm volatile(".inst 0x4203800" : : : "z0");
191859a9d51SMark Brown }
192859a9d51SMark Brown 
193859a9d51SMark Brown static void svesm4_sigill(void)
194859a9d51SMark Brown {
195859a9d51SMark Brown 	/* SM4E Z0.S, Z0.S, Z0.S */
196859a9d51SMark Brown 	asm volatile(".inst 0x4523e000" : : : "z0");
197859a9d51SMark Brown }
198859a9d51SMark Brown 
199859a9d51SMark Brown static void svei8mm_sigill(void)
200859a9d51SMark Brown {
201859a9d51SMark Brown 	/* USDOT Z0.S, Z0.B, Z0.B[0] */
202859a9d51SMark Brown 	asm volatile(".inst 0x44a01800" : : : "z0");
203859a9d51SMark Brown }
204859a9d51SMark Brown 
205859a9d51SMark Brown static void svef32mm_sigill(void)
206859a9d51SMark Brown {
207859a9d51SMark Brown 	/* FMMLA Z0.S, Z0.S, Z0.S */
208859a9d51SMark Brown 	asm volatile(".inst 0x64a0e400" : : : "z0");
209859a9d51SMark Brown }
210859a9d51SMark Brown 
211859a9d51SMark Brown static void svef64mm_sigill(void)
212859a9d51SMark Brown {
213859a9d51SMark Brown 	/* FMMLA Z0.D, Z0.D, Z0.D */
214859a9d51SMark Brown 	asm volatile(".inst 0x64e0e400" : : : "z0");
215859a9d51SMark Brown }
216859a9d51SMark Brown 
217859a9d51SMark Brown static void svebf16_sigill(void)
218859a9d51SMark Brown {
219859a9d51SMark Brown 	/* BFCVT Z0.H, P0/M, Z0.S */
220859a9d51SMark Brown 	asm volatile(".inst 0x658aa000" : : : "z0");
221859a9d51SMark Brown }
222859a9d51SMark Brown 
2237a9bcaaaSMark Brown static const struct hwcap_data {
2247a9bcaaaSMark Brown 	const char *name;
2257a9bcaaaSMark Brown 	unsigned long at_hwcap;
2267a9bcaaaSMark Brown 	unsigned long hwcap_bit;
2277a9bcaaaSMark Brown 	const char *cpuinfo;
2287a9bcaaaSMark Brown 	sigill_fn sigill_fn;
2297a9bcaaaSMark Brown 	bool sigill_reliable;
2307a9bcaaaSMark Brown } hwcaps[] = {
2317a9bcaaaSMark Brown 	{
232b0ab73a5SMark Brown 		.name = "CSSC",
233b0ab73a5SMark Brown 		.at_hwcap = AT_HWCAP2,
234b0ab73a5SMark Brown 		.hwcap_bit = HWCAP2_CSSC,
235b0ab73a5SMark Brown 		.cpuinfo = "cssc",
236b0ab73a5SMark Brown 		.sigill_fn = cssc_sigill,
237b0ab73a5SMark Brown 	},
238b0ab73a5SMark Brown 	{
239*d1890517SZeng Heng 		.name = "LRCPC",
240*d1890517SZeng Heng 		.at_hwcap = AT_HWCAP,
241*d1890517SZeng Heng 		.hwcap_bit = HWCAP_LRCPC,
242*d1890517SZeng Heng 		.cpuinfo = "lrcpc",
243*d1890517SZeng Heng 		.sigill_fn = lrcpc_sigill,
244*d1890517SZeng Heng 	},
245*d1890517SZeng Heng 	{
246*d1890517SZeng Heng 		.name = "LRCPC2",
247*d1890517SZeng Heng 		.at_hwcap = AT_HWCAP,
248*d1890517SZeng Heng 		.hwcap_bit = HWCAP_ILRCPC,
249*d1890517SZeng Heng 		.cpuinfo = "ilrcpc",
250*d1890517SZeng Heng 		.sigill_fn = ilrcpc_sigill,
251*d1890517SZeng Heng 	},
252*d1890517SZeng Heng 	{
253d8a324f1SKristina Martsenko 		.name = "MOPS",
254d8a324f1SKristina Martsenko 		.at_hwcap = AT_HWCAP2,
255d8a324f1SKristina Martsenko 		.hwcap_bit = HWCAP2_MOPS,
256d8a324f1SKristina Martsenko 		.cpuinfo = "mops",
257d8a324f1SKristina Martsenko 		.sigill_fn = mops_sigill,
258d8a324f1SKristina Martsenko 		.sigill_reliable = true,
259d8a324f1SKristina Martsenko 	},
260d8a324f1SKristina Martsenko 	{
261ef939f30SMark Brown 		.name = "RNG",
262ef939f30SMark Brown 		.at_hwcap = AT_HWCAP2,
263ef939f30SMark Brown 		.hwcap_bit = HWCAP2_RNG,
264ef939f30SMark Brown 		.cpuinfo = "rng",
265ef939f30SMark Brown 		.sigill_fn = rng_sigill,
266ef939f30SMark Brown 	},
267ef939f30SMark Brown 	{
268989d37fcSMark Brown 		.name = "RPRFM",
269989d37fcSMark Brown 		.at_hwcap = AT_HWCAP2,
270989d37fcSMark Brown 		.hwcap_bit = HWCAP2_RPRFM,
271989d37fcSMark Brown 		.cpuinfo = "rprfm",
272989d37fcSMark Brown 	},
273989d37fcSMark Brown 	{
2747a9bcaaaSMark Brown 		.name = "SME",
2757a9bcaaaSMark Brown 		.at_hwcap = AT_HWCAP2,
2767a9bcaaaSMark Brown 		.hwcap_bit = HWCAP2_SME,
2777a9bcaaaSMark Brown 		.cpuinfo = "sme",
2787a9bcaaaSMark Brown 		.sigill_fn = sme_sigill,
2797a9bcaaaSMark Brown 		.sigill_reliable = true,
2807a9bcaaaSMark Brown 	},
2817a9bcaaaSMark Brown 	{
2823eb1b41fSMark Brown 		.name = "SME2",
2833eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
2843eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME2,
2853eb1b41fSMark Brown 		.cpuinfo = "sme2",
2863eb1b41fSMark Brown 		.sigill_fn = sme2_sigill,
2873eb1b41fSMark Brown 		.sigill_reliable = true,
2883eb1b41fSMark Brown 	},
2893eb1b41fSMark Brown 	{
2903eb1b41fSMark Brown 		.name = "SME 2.1",
2913eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
2923eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME2P1,
2933eb1b41fSMark Brown 		.cpuinfo = "sme2p1",
2943eb1b41fSMark Brown 		.sigill_fn = sme2p1_sigill,
2953eb1b41fSMark Brown 	},
2963eb1b41fSMark Brown 	{
2973eb1b41fSMark Brown 		.name = "SME I16I32",
2983eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
2993eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME_I16I32,
3003eb1b41fSMark Brown 		.cpuinfo = "smei16i32",
3013eb1b41fSMark Brown 		.sigill_fn = smei16i32_sigill,
3023eb1b41fSMark Brown 	},
3033eb1b41fSMark Brown 	{
3043eb1b41fSMark Brown 		.name = "SME BI32I32",
3053eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
3063eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME_BI32I32,
3073eb1b41fSMark Brown 		.cpuinfo = "smebi32i32",
3083eb1b41fSMark Brown 		.sigill_fn = smebi32i32_sigill,
3093eb1b41fSMark Brown 	},
3103eb1b41fSMark Brown 	{
3113eb1b41fSMark Brown 		.name = "SME B16B16",
3123eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
3133eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME_B16B16,
3143eb1b41fSMark Brown 		.cpuinfo = "smeb16b16",
3153eb1b41fSMark Brown 		.sigill_fn = smeb16b16_sigill,
3163eb1b41fSMark Brown 	},
3173eb1b41fSMark Brown 	{
3183eb1b41fSMark Brown 		.name = "SME F16F16",
3193eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
3203eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME_F16F16,
3213eb1b41fSMark Brown 		.cpuinfo = "smef16f16",
3223eb1b41fSMark Brown 		.sigill_fn = smef16f16_sigill,
3233eb1b41fSMark Brown 	},
3243eb1b41fSMark Brown 	{
3257a9bcaaaSMark Brown 		.name = "SVE",
3267a9bcaaaSMark Brown 		.at_hwcap = AT_HWCAP,
3277a9bcaaaSMark Brown 		.hwcap_bit = HWCAP_SVE,
3287a9bcaaaSMark Brown 		.cpuinfo = "sve",
3297a9bcaaaSMark Brown 		.sigill_fn = sve_sigill,
3307a9bcaaaSMark Brown 		.sigill_reliable = true,
3317a9bcaaaSMark Brown 	},
332859a9d51SMark Brown 	{
333859a9d51SMark Brown 		.name = "SVE 2",
334859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
335859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVE2,
336859a9d51SMark Brown 		.cpuinfo = "sve2",
337859a9d51SMark Brown 		.sigill_fn = sve2_sigill,
338859a9d51SMark Brown 	},
339859a9d51SMark Brown 	{
340c5195b02SMark Brown 		.name = "SVE 2.1",
341c5195b02SMark Brown 		.at_hwcap = AT_HWCAP2,
342c5195b02SMark Brown 		.hwcap_bit = HWCAP2_SVE2P1,
343c5195b02SMark Brown 		.cpuinfo = "sve2p1",
344c5195b02SMark Brown 		.sigill_fn = sve2p1_sigill,
345c5195b02SMark Brown 	},
346c5195b02SMark Brown 	{
347859a9d51SMark Brown 		.name = "SVE AES",
348859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
349859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEAES,
350859a9d51SMark Brown 		.cpuinfo = "sveaes",
351859a9d51SMark Brown 		.sigill_fn = sveaes_sigill,
352859a9d51SMark Brown 	},
353859a9d51SMark Brown 	{
354859a9d51SMark Brown 		.name = "SVE2 PMULL",
355859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
356859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEPMULL,
357859a9d51SMark Brown 		.cpuinfo = "svepmull",
358859a9d51SMark Brown 		.sigill_fn = svepmull_sigill,
359859a9d51SMark Brown 	},
360859a9d51SMark Brown 	{
361859a9d51SMark Brown 		.name = "SVE2 BITPERM",
362859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
363859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEBITPERM,
364859a9d51SMark Brown 		.cpuinfo = "svebitperm",
365859a9d51SMark Brown 		.sigill_fn = svebitperm_sigill,
366859a9d51SMark Brown 	},
367859a9d51SMark Brown 	{
368859a9d51SMark Brown 		.name = "SVE2 SHA3",
369859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
370859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVESHA3,
371859a9d51SMark Brown 		.cpuinfo = "svesha3",
372859a9d51SMark Brown 		.sigill_fn = svesha3_sigill,
373859a9d51SMark Brown 	},
374859a9d51SMark Brown 	{
375859a9d51SMark Brown 		.name = "SVE2 SM4",
376859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
377859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVESM4,
378859a9d51SMark Brown 		.cpuinfo = "svesm4",
379859a9d51SMark Brown 		.sigill_fn = svesm4_sigill,
380859a9d51SMark Brown 	},
381859a9d51SMark Brown 	{
382859a9d51SMark Brown 		.name = "SVE2 I8MM",
383859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
384859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEI8MM,
385859a9d51SMark Brown 		.cpuinfo = "svei8mm",
386859a9d51SMark Brown 		.sigill_fn = svei8mm_sigill,
387859a9d51SMark Brown 	},
388859a9d51SMark Brown 	{
389859a9d51SMark Brown 		.name = "SVE2 F32MM",
390859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
391859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEF32MM,
392859a9d51SMark Brown 		.cpuinfo = "svef32mm",
393859a9d51SMark Brown 		.sigill_fn = svef32mm_sigill,
394859a9d51SMark Brown 	},
395859a9d51SMark Brown 	{
396859a9d51SMark Brown 		.name = "SVE2 F64MM",
397859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
398859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEF64MM,
399859a9d51SMark Brown 		.cpuinfo = "svef64mm",
400859a9d51SMark Brown 		.sigill_fn = svef64mm_sigill,
401859a9d51SMark Brown 	},
402859a9d51SMark Brown 	{
403859a9d51SMark Brown 		.name = "SVE2 BF16",
404859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
405859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEBF16,
406859a9d51SMark Brown 		.cpuinfo = "svebf16",
407859a9d51SMark Brown 		.sigill_fn = svebf16_sigill,
408859a9d51SMark Brown 	},
409859a9d51SMark Brown 	{
410859a9d51SMark Brown 		.name = "SVE2 EBF16",
411859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
412859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVE_EBF16,
413859a9d51SMark Brown 		.cpuinfo = "sveebf16",
414859a9d51SMark Brown 	},
4157a9bcaaaSMark Brown };
4167a9bcaaaSMark Brown 
4177a9bcaaaSMark Brown static bool seen_sigill;
4187a9bcaaaSMark Brown 
4197a9bcaaaSMark Brown static void handle_sigill(int sig, siginfo_t *info, void *context)
4207a9bcaaaSMark Brown {
4217a9bcaaaSMark Brown 	ucontext_t *uc = context;
4227a9bcaaaSMark Brown 
4237a9bcaaaSMark Brown 	seen_sigill = true;
4247a9bcaaaSMark Brown 
4257a9bcaaaSMark Brown 	/* Skip over the offending instruction */
4267a9bcaaaSMark Brown 	uc->uc_mcontext.pc += 4;
4277a9bcaaaSMark Brown }
4287a9bcaaaSMark Brown 
4297a9bcaaaSMark Brown bool cpuinfo_present(const char *name)
4307a9bcaaaSMark Brown {
4317a9bcaaaSMark Brown 	FILE *f;
4327a9bcaaaSMark Brown 	char buf[2048], name_space[30], name_newline[30];
4337a9bcaaaSMark Brown 	char *s;
4347a9bcaaaSMark Brown 
4357a9bcaaaSMark Brown 	/*
4367a9bcaaaSMark Brown 	 * The feature should appear with a leading space and either a
4377a9bcaaaSMark Brown 	 * trailing space or a newline.
4387a9bcaaaSMark Brown 	 */
4397a9bcaaaSMark Brown 	snprintf(name_space, sizeof(name_space), " %s ", name);
4407a9bcaaaSMark Brown 	snprintf(name_newline, sizeof(name_newline), " %s\n", name);
4417a9bcaaaSMark Brown 
4427a9bcaaaSMark Brown 	f = fopen("/proc/cpuinfo", "r");
4437a9bcaaaSMark Brown 	if (!f) {
4447a9bcaaaSMark Brown 		ksft_print_msg("Failed to open /proc/cpuinfo\n");
4457a9bcaaaSMark Brown 		return false;
4467a9bcaaaSMark Brown 	}
4477a9bcaaaSMark Brown 
4487a9bcaaaSMark Brown 	while (fgets(buf, sizeof(buf), f)) {
4497a9bcaaaSMark Brown 		/* Features: line? */
4507a9bcaaaSMark Brown 		if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0)
4517a9bcaaaSMark Brown 			continue;
4527a9bcaaaSMark Brown 
4537a9bcaaaSMark Brown 		/* All CPUs should be symmetric, don't read any more */
4547a9bcaaaSMark Brown 		fclose(f);
4557a9bcaaaSMark Brown 
4567a9bcaaaSMark Brown 		s = strstr(buf, name_space);
4577a9bcaaaSMark Brown 		if (s)
4587a9bcaaaSMark Brown 			return true;
4597a9bcaaaSMark Brown 		s = strstr(buf, name_newline);
4607a9bcaaaSMark Brown 		if (s)
4617a9bcaaaSMark Brown 			return true;
4627a9bcaaaSMark Brown 
4637a9bcaaaSMark Brown 		return false;
4647a9bcaaaSMark Brown 	}
4657a9bcaaaSMark Brown 
4667a9bcaaaSMark Brown 	ksft_print_msg("Failed to find Features in /proc/cpuinfo\n");
4677a9bcaaaSMark Brown 	fclose(f);
4687a9bcaaaSMark Brown 	return false;
4697a9bcaaaSMark Brown }
4707a9bcaaaSMark Brown 
4717a9bcaaaSMark Brown int main(void)
4727a9bcaaaSMark Brown {
4737a9bcaaaSMark Brown 	const struct hwcap_data *hwcap;
4747a9bcaaaSMark Brown 	int i, ret;
4757a9bcaaaSMark Brown 	bool have_cpuinfo, have_hwcap;
4767a9bcaaaSMark Brown 	struct sigaction sa;
4777a9bcaaaSMark Brown 
4787a9bcaaaSMark Brown 	ksft_print_header();
4797a9bcaaaSMark Brown 	ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP);
4807a9bcaaaSMark Brown 
4817a9bcaaaSMark Brown 	memset(&sa, 0, sizeof(sa));
4827a9bcaaaSMark Brown 	sa.sa_sigaction = handle_sigill;
4837a9bcaaaSMark Brown 	sa.sa_flags = SA_RESTART | SA_SIGINFO;
4847a9bcaaaSMark Brown 	sigemptyset(&sa.sa_mask);
4857a9bcaaaSMark Brown 	ret = sigaction(SIGILL, &sa, NULL);
4867a9bcaaaSMark Brown 	if (ret < 0)
4877a9bcaaaSMark Brown 		ksft_exit_fail_msg("Failed to install SIGILL handler: %s (%d)\n",
4887a9bcaaaSMark Brown 				   strerror(errno), errno);
4897a9bcaaaSMark Brown 
4907a9bcaaaSMark Brown 	for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
4917a9bcaaaSMark Brown 		hwcap = &hwcaps[i];
4927a9bcaaaSMark Brown 
49333060a64SMark Brown 		have_hwcap = getauxval(hwcap->at_hwcap) & hwcap->hwcap_bit;
4947a9bcaaaSMark Brown 		have_cpuinfo = cpuinfo_present(hwcap->cpuinfo);
4957a9bcaaaSMark Brown 
4967a9bcaaaSMark Brown 		if (have_hwcap)
49778d2b197SMark Brown 			ksft_print_msg("%s present\n", hwcap->name);
4987a9bcaaaSMark Brown 
4997a9bcaaaSMark Brown 		ksft_test_result(have_hwcap == have_cpuinfo,
5007a9bcaaaSMark Brown 				 "cpuinfo_match_%s\n", hwcap->name);
5017a9bcaaaSMark Brown 
5027a9bcaaaSMark Brown 		if (hwcap->sigill_fn) {
5037a9bcaaaSMark Brown 			seen_sigill = false;
5047a9bcaaaSMark Brown 			hwcap->sigill_fn();
5057a9bcaaaSMark Brown 
5067a9bcaaaSMark Brown 			if (have_hwcap) {
5077a9bcaaaSMark Brown 				/* Should be able to use the extension */
5087a9bcaaaSMark Brown 				ksft_test_result(!seen_sigill, "sigill_%s\n",
5097a9bcaaaSMark Brown 						 hwcap->name);
5107a9bcaaaSMark Brown 			} else if (hwcap->sigill_reliable) {
5117a9bcaaaSMark Brown 				/* Guaranteed a SIGILL */
5127a9bcaaaSMark Brown 				ksft_test_result(seen_sigill, "sigill_%s\n",
5137a9bcaaaSMark Brown 						 hwcap->name);
5147a9bcaaaSMark Brown 			} else {
5157a9bcaaaSMark Brown 				/* Missing SIGILL might be fine */
5167a9bcaaaSMark Brown 				ksft_print_msg("SIGILL %sreported for %s\n",
5177a9bcaaaSMark Brown 					       seen_sigill ? "" : "not ",
5187a9bcaaaSMark Brown 					       hwcap->name);
5197a9bcaaaSMark Brown 				ksft_test_result_skip("sigill_%s\n",
5207a9bcaaaSMark Brown 						      hwcap->name);
5217a9bcaaaSMark Brown 			}
5227a9bcaaaSMark Brown 		} else {
5237a9bcaaaSMark Brown 			ksft_test_result_skip("sigill_%s\n",
5247a9bcaaaSMark Brown 					      hwcap->name);
5257a9bcaaaSMark Brown 		}
5267a9bcaaaSMark Brown 	}
5277a9bcaaaSMark Brown 
5287a9bcaaaSMark Brown 	ksft_print_cnts();
5297a9bcaaaSMark Brown 
5307a9bcaaaSMark Brown 	return 0;
5317a9bcaaaSMark Brown }
532