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 
22fd49cf08SZeng Heng #define TESTS_PER_HWCAP 3
237a9bcaaaSMark Brown 
247a9bcaaaSMark Brown /*
25fd49cf08SZeng Heng  * Function expected to generate exception when the feature is not
26fd49cf08SZeng Heng  * supported and return when it is supported. If the specific exception
27fd49cf08SZeng Heng  * is generated then the handler must be able to skip over the
28fd49cf08SZeng Heng  * instruction safely.
297a9bcaaaSMark Brown  *
307a9bcaaaSMark Brown  * Note that it is expected that for many architecture extensions
317a9bcaaaSMark Brown  * there are no specific traps due to no architecture state being
327a9bcaaaSMark Brown  * added so we may not fault if running on a kernel which doesn't know
337a9bcaaaSMark Brown  * to add the hwcap.
347a9bcaaaSMark Brown  */
35fd49cf08SZeng Heng typedef void (*sig_fn)(void);
367a9bcaaaSMark Brown 
aes_sigill(void)373fc3c0d1SZeng Heng static void aes_sigill(void)
383fc3c0d1SZeng Heng {
393fc3c0d1SZeng Heng 	/* AESE V0.16B, V0.16B */
403fc3c0d1SZeng Heng 	asm volatile(".inst 0x4e284800" : : : );
413fc3c0d1SZeng Heng }
423fc3c0d1SZeng Heng 
atomics_sigill(void)4382e7882bSZeng Heng static void atomics_sigill(void)
4482e7882bSZeng Heng {
4582e7882bSZeng Heng 	/* STADD W0, [SP] */
4682e7882bSZeng Heng 	asm volatile(".inst 0xb82003ff" : : : );
4782e7882bSZeng Heng }
4882e7882bSZeng Heng 
crc32_sigill(void)4909d2e95aSZeng Heng static void crc32_sigill(void)
5009d2e95aSZeng Heng {
51*94f23ac3SMark Brown 	/* CRC32W W0, W0, W1 */
52*94f23ac3SMark Brown 	asm volatile(".inst 0x1ac14800" : : : );
5309d2e95aSZeng Heng }
5409d2e95aSZeng Heng 
cssc_sigill(void)55b0ab73a5SMark Brown static void cssc_sigill(void)
56b0ab73a5SMark Brown {
57b0ab73a5SMark Brown 	/* CNT x0, x0 */
58b0ab73a5SMark Brown 	asm volatile(".inst 0xdac01c00" : : : "x0");
59b0ab73a5SMark Brown }
60b0ab73a5SMark Brown 
fp_sigill(void)61eb27c76aSZeng Heng static void fp_sigill(void)
62eb27c76aSZeng Heng {
63eb27c76aSZeng Heng 	asm volatile("fmov s0, #1");
64eb27c76aSZeng Heng }
65eb27c76aSZeng Heng 
ilrcpc_sigill(void)66d1890517SZeng Heng static void ilrcpc_sigill(void)
67d1890517SZeng Heng {
68d1890517SZeng Heng 	/* LDAPUR W0, [SP, #8] */
69d1890517SZeng Heng 	asm volatile(".inst 0x994083e0" : : : );
70d1890517SZeng Heng }
71d1890517SZeng Heng 
jscvt_sigill(void)72fcb0b51aSZeng Heng static void jscvt_sigill(void)
73fcb0b51aSZeng Heng {
74fcb0b51aSZeng Heng 	/* FJCVTZS W0, D0 */
75fcb0b51aSZeng Heng 	asm volatile(".inst 0x1e7e0000" : : : );
76fcb0b51aSZeng Heng }
77fcb0b51aSZeng Heng 
lrcpc_sigill(void)78d1890517SZeng Heng static void lrcpc_sigill(void)
79d1890517SZeng Heng {
80d1890517SZeng Heng 	/* LDAPR W0, [SP, #0] */
81d1890517SZeng Heng 	asm volatile(".inst 0xb8bfc3e0" : : : );
82d1890517SZeng Heng }
83d1890517SZeng Heng 
mops_sigill(void)84d8a324f1SKristina Martsenko static void mops_sigill(void)
85d8a324f1SKristina Martsenko {
86d8a324f1SKristina Martsenko 	char dst[1], src[1];
87d8a324f1SKristina Martsenko 	register char *dstp asm ("x0") = dst;
88d8a324f1SKristina Martsenko 	register char *srcp asm ("x1") = src;
89d8a324f1SKristina Martsenko 	register long size asm ("x2") = 1;
90d8a324f1SKristina Martsenko 
91d8a324f1SKristina Martsenko 	/* CPYP [x0]!, [x1]!, x2! */
92d8a324f1SKristina Martsenko 	asm volatile(".inst 0x1d010440"
93d8a324f1SKristina Martsenko 		     : "+r" (dstp), "+r" (srcp), "+r" (size)
94d8a324f1SKristina Martsenko 		     :
95d8a324f1SKristina Martsenko 		     : "cc", "memory");
96d8a324f1SKristina Martsenko }
97d8a324f1SKristina Martsenko 
pmull_sigill(void)982c3ce0e7SZeng Heng static void pmull_sigill(void)
992c3ce0e7SZeng Heng {
1002c3ce0e7SZeng Heng 	/* PMULL V0.1Q, V0.1D, V0.1D */
1012c3ce0e7SZeng Heng 	asm volatile(".inst 0x0ee0e000" : : : );
1022c3ce0e7SZeng Heng }
1032c3ce0e7SZeng Heng 
rng_sigill(void)104ef939f30SMark Brown static void rng_sigill(void)
105ef939f30SMark Brown {
106ef939f30SMark Brown 	asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0");
107ef939f30SMark Brown }
108ef939f30SMark Brown 
sha1_sigill(void)1097eb4ee66SZeng Heng static void sha1_sigill(void)
1107eb4ee66SZeng Heng {
1117eb4ee66SZeng Heng 	/* SHA1H S0, S0 */
1127eb4ee66SZeng Heng 	asm volatile(".inst 0x5e280800" : : : );
1137eb4ee66SZeng Heng }
1147eb4ee66SZeng Heng 
sha2_sigill(void)1157eb4ee66SZeng Heng static void sha2_sigill(void)
1167eb4ee66SZeng Heng {
1177eb4ee66SZeng Heng 	/* SHA256H Q0, Q0, V0.4S */
1187eb4ee66SZeng Heng 	asm volatile(".inst 0x5e004000" : : : );
1197eb4ee66SZeng Heng }
1207eb4ee66SZeng Heng 
sha512_sigill(void)1217eb4ee66SZeng Heng static void sha512_sigill(void)
1227eb4ee66SZeng Heng {
1237eb4ee66SZeng Heng 	/* SHA512H Q0, Q0, V0.2D */
1247eb4ee66SZeng Heng 	asm volatile(".inst 0xce608000" : : : );
1257eb4ee66SZeng Heng }
1267eb4ee66SZeng Heng 
sme_sigill(void)1277a9bcaaaSMark Brown static void sme_sigill(void)
1287a9bcaaaSMark Brown {
1297a9bcaaaSMark Brown 	/* RDSVL x0, #0 */
1307a9bcaaaSMark Brown 	asm volatile(".inst 0x04bf5800" : : : "x0");
1317a9bcaaaSMark Brown }
1327a9bcaaaSMark Brown 
sme2_sigill(void)1333eb1b41fSMark Brown static void sme2_sigill(void)
1343eb1b41fSMark Brown {
1353eb1b41fSMark Brown 	/* SMSTART ZA */
1363eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C5_3, xzr" : : : );
1373eb1b41fSMark Brown 
1383eb1b41fSMark Brown 	/* ZERO ZT0 */
1393eb1b41fSMark Brown 	asm volatile(".inst 0xc0480001" : : : );
1403eb1b41fSMark Brown 
1413eb1b41fSMark Brown 	/* SMSTOP */
1423eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
1433eb1b41fSMark Brown }
1443eb1b41fSMark Brown 
sme2p1_sigill(void)1453eb1b41fSMark Brown static void sme2p1_sigill(void)
1463eb1b41fSMark Brown {
1473eb1b41fSMark Brown 	/* SMSTART SM */
1483eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C3_3, xzr" : : : );
1493eb1b41fSMark Brown 
1503eb1b41fSMark Brown 	/* BFCLAMP { Z0.H - Z1.H }, Z0.H, Z0.H */
1513eb1b41fSMark Brown 	asm volatile(".inst 0xc120C000" : : : );
1523eb1b41fSMark Brown 
1533eb1b41fSMark Brown 	/* SMSTOP */
1543eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
1553eb1b41fSMark Brown }
1563eb1b41fSMark Brown 
smei16i32_sigill(void)1573eb1b41fSMark Brown static void smei16i32_sigill(void)
1583eb1b41fSMark Brown {
1593eb1b41fSMark Brown 	/* SMSTART */
1603eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
1613eb1b41fSMark Brown 
1623eb1b41fSMark Brown 	/* SMOPA ZA0.S, P0/M, P0/M, Z0.B, Z0.B */
1633eb1b41fSMark Brown 	asm volatile(".inst 0xa0800000" : : : );
1643eb1b41fSMark Brown 
1653eb1b41fSMark Brown 	/* SMSTOP */
1663eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
1673eb1b41fSMark Brown }
1683eb1b41fSMark Brown 
smebi32i32_sigill(void)1693eb1b41fSMark Brown static void smebi32i32_sigill(void)
1703eb1b41fSMark Brown {
1713eb1b41fSMark Brown 	/* SMSTART */
1723eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
1733eb1b41fSMark Brown 
1743eb1b41fSMark Brown 	/* BMOPA ZA0.S, P0/M, P0/M, Z0.B, Z0.B */
1753eb1b41fSMark Brown 	asm volatile(".inst 0x80800008" : : : );
1763eb1b41fSMark Brown 
1773eb1b41fSMark Brown 	/* SMSTOP */
1783eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
1793eb1b41fSMark Brown }
1803eb1b41fSMark Brown 
smeb16b16_sigill(void)1813eb1b41fSMark Brown static void smeb16b16_sigill(void)
1823eb1b41fSMark Brown {
1833eb1b41fSMark Brown 	/* SMSTART */
1843eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
1853eb1b41fSMark Brown 
1863eb1b41fSMark Brown 	/* BFADD ZA.H[W0, 0], {Z0.H-Z1.H} */
1873eb1b41fSMark Brown 	asm volatile(".inst 0xC1E41C00" : : : );
1883eb1b41fSMark Brown 
1893eb1b41fSMark Brown 	/* SMSTOP */
1903eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
1913eb1b41fSMark Brown }
1923eb1b41fSMark Brown 
smef16f16_sigill(void)1933eb1b41fSMark Brown static void smef16f16_sigill(void)
1943eb1b41fSMark Brown {
1953eb1b41fSMark Brown 	/* SMSTART */
1963eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
1973eb1b41fSMark Brown 
1983eb1b41fSMark Brown 	/* FADD ZA.H[W0, 0], { Z0.H-Z1.H } */
1993eb1b41fSMark Brown 	asm volatile(".inst 0xc1a41C00" : : : );
2003eb1b41fSMark Brown 
2013eb1b41fSMark Brown 	/* SMSTOP */
2023eb1b41fSMark Brown 	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
2033eb1b41fSMark Brown }
2043eb1b41fSMark Brown 
sve_sigill(void)2057a9bcaaaSMark Brown static void sve_sigill(void)
2067a9bcaaaSMark Brown {
2077a9bcaaaSMark Brown 	/* RDVL x0, #0 */
2087a9bcaaaSMark Brown 	asm volatile(".inst 0x04bf5000" : : : "x0");
2097a9bcaaaSMark Brown }
2107a9bcaaaSMark Brown 
sve2_sigill(void)211859a9d51SMark Brown static void sve2_sigill(void)
212859a9d51SMark Brown {
213859a9d51SMark Brown 	/* SQABS Z0.b, P0/M, Z0.B */
214859a9d51SMark Brown 	asm volatile(".inst 0x4408A000" : : : "z0");
215859a9d51SMark Brown }
216859a9d51SMark Brown 
sve2p1_sigill(void)217c5195b02SMark Brown static void sve2p1_sigill(void)
218c5195b02SMark Brown {
219c5195b02SMark Brown 	/* BFADD Z0.H, Z0.H, Z0.H */
220c5195b02SMark Brown 	asm volatile(".inst 0x65000000" : : : "z0");
221c5195b02SMark Brown }
222c5195b02SMark Brown 
sveaes_sigill(void)223859a9d51SMark Brown static void sveaes_sigill(void)
224859a9d51SMark Brown {
225859a9d51SMark Brown 	/* AESD z0.b, z0.b, z0.b */
226859a9d51SMark Brown 	asm volatile(".inst 0x4522e400" : : : "z0");
227859a9d51SMark Brown }
228859a9d51SMark Brown 
svepmull_sigill(void)229859a9d51SMark Brown static void svepmull_sigill(void)
230859a9d51SMark Brown {
231859a9d51SMark Brown 	/* PMULLB Z0.Q, Z0.D, Z0.D */
232859a9d51SMark Brown 	asm volatile(".inst 0x45006800" : : : "z0");
233859a9d51SMark Brown }
234859a9d51SMark Brown 
svebitperm_sigill(void)235859a9d51SMark Brown static void svebitperm_sigill(void)
236859a9d51SMark Brown {
237859a9d51SMark Brown 	/* BDEP Z0.B, Z0.B, Z0.B */
238859a9d51SMark Brown 	asm volatile(".inst 0x4500b400" : : : "z0");
239859a9d51SMark Brown }
240859a9d51SMark Brown 
svesha3_sigill(void)241859a9d51SMark Brown static void svesha3_sigill(void)
242859a9d51SMark Brown {
243859a9d51SMark Brown 	/* EOR3 Z0.D, Z0.D, Z0.D, Z0.D */
244859a9d51SMark Brown 	asm volatile(".inst 0x4203800" : : : "z0");
245859a9d51SMark Brown }
246859a9d51SMark Brown 
svesm4_sigill(void)247859a9d51SMark Brown static void svesm4_sigill(void)
248859a9d51SMark Brown {
249859a9d51SMark Brown 	/* SM4E Z0.S, Z0.S, Z0.S */
250859a9d51SMark Brown 	asm volatile(".inst 0x4523e000" : : : "z0");
251859a9d51SMark Brown }
252859a9d51SMark Brown 
svei8mm_sigill(void)253859a9d51SMark Brown static void svei8mm_sigill(void)
254859a9d51SMark Brown {
255859a9d51SMark Brown 	/* USDOT Z0.S, Z0.B, Z0.B[0] */
256859a9d51SMark Brown 	asm volatile(".inst 0x44a01800" : : : "z0");
257859a9d51SMark Brown }
258859a9d51SMark Brown 
svef32mm_sigill(void)259859a9d51SMark Brown static void svef32mm_sigill(void)
260859a9d51SMark Brown {
261859a9d51SMark Brown 	/* FMMLA Z0.S, Z0.S, Z0.S */
262859a9d51SMark Brown 	asm volatile(".inst 0x64a0e400" : : : "z0");
263859a9d51SMark Brown }
264859a9d51SMark Brown 
svef64mm_sigill(void)265859a9d51SMark Brown static void svef64mm_sigill(void)
266859a9d51SMark Brown {
267859a9d51SMark Brown 	/* FMMLA Z0.D, Z0.D, Z0.D */
268859a9d51SMark Brown 	asm volatile(".inst 0x64e0e400" : : : "z0");
269859a9d51SMark Brown }
270859a9d51SMark Brown 
svebf16_sigill(void)271859a9d51SMark Brown static void svebf16_sigill(void)
272859a9d51SMark Brown {
273859a9d51SMark Brown 	/* BFCVT Z0.H, P0/M, Z0.S */
274859a9d51SMark Brown 	asm volatile(".inst 0x658aa000" : : : "z0");
275859a9d51SMark Brown }
276859a9d51SMark Brown 
hbc_sigill(void)27782e7882bSZeng Heng static void hbc_sigill(void)
27882e7882bSZeng Heng {
27982e7882bSZeng Heng 	/* BC.EQ +4 */
28082e7882bSZeng Heng 	asm volatile("cmp xzr, xzr\n"
28182e7882bSZeng Heng 		     ".inst 0x54000030" : : : "cc");
28282e7882bSZeng Heng }
28382e7882bSZeng Heng 
uscat_sigbus(void)28482e7882bSZeng Heng static void uscat_sigbus(void)
2857a9bcaaaSMark Brown {
2867a9bcaaaSMark Brown 	/* unaligned atomic access */
2877a9bcaaaSMark Brown 	asm volatile("ADD x1, sp, #2" : : : );
2887a9bcaaaSMark Brown 	/* STADD W0, [X1] */
2897a9bcaaaSMark Brown 	asm volatile(".inst 0xb820003f" : : : );
290fd49cf08SZeng Heng }
2917a9bcaaaSMark Brown 
292fd49cf08SZeng Heng static const struct hwcap_data {
293fd49cf08SZeng Heng 	const char *name;
2947a9bcaaaSMark Brown 	unsigned long at_hwcap;
2957a9bcaaaSMark Brown 	unsigned long hwcap_bit;
2963fc3c0d1SZeng Heng 	const char *cpuinfo;
2973fc3c0d1SZeng Heng 	sig_fn sigill_fn;
2983fc3c0d1SZeng Heng 	bool sigill_reliable;
2993fc3c0d1SZeng Heng 	sig_fn sigbus_fn;
3003fc3c0d1SZeng Heng 	bool sigbus_reliable;
3013fc3c0d1SZeng Heng } hwcaps[] = {
3023fc3c0d1SZeng Heng 	{
30309d2e95aSZeng Heng 		.name = "AES",
30409d2e95aSZeng Heng 		.at_hwcap = AT_HWCAP,
30509d2e95aSZeng Heng 		.hwcap_bit = HWCAP_AES,
30609d2e95aSZeng Heng 		.cpuinfo = "aes",
30709d2e95aSZeng Heng 		.sigill_fn = aes_sigill,
30809d2e95aSZeng Heng 	},
30909d2e95aSZeng Heng 	{
310b0ab73a5SMark Brown 		.name = "CRC32",
311b0ab73a5SMark Brown 		.at_hwcap = AT_HWCAP,
312b0ab73a5SMark Brown 		.hwcap_bit = HWCAP_CRC32,
313b0ab73a5SMark Brown 		.cpuinfo = "crc32",
314b0ab73a5SMark Brown 		.sigill_fn = crc32_sigill,
315b0ab73a5SMark Brown 	},
316b0ab73a5SMark Brown 	{
317eb27c76aSZeng Heng 		.name = "CSSC",
318eb27c76aSZeng Heng 		.at_hwcap = AT_HWCAP2,
319eb27c76aSZeng Heng 		.hwcap_bit = HWCAP2_CSSC,
320eb27c76aSZeng Heng 		.cpuinfo = "cssc",
321eb27c76aSZeng Heng 		.sigill_fn = cssc_sigill,
322eb27c76aSZeng Heng 	},
323eb27c76aSZeng Heng 	{
324fcb0b51aSZeng Heng 		.name = "FP",
325fcb0b51aSZeng Heng 		.at_hwcap = AT_HWCAP,
326fcb0b51aSZeng Heng 		.hwcap_bit = HWCAP_FP,
327fcb0b51aSZeng Heng 		.cpuinfo = "fp",
328fcb0b51aSZeng Heng 		.sigill_fn = fp_sigill,
329fcb0b51aSZeng Heng 	},
330fcb0b51aSZeng Heng 	{
331d1890517SZeng Heng 		.name = "JSCVT",
332d1890517SZeng Heng 		.at_hwcap = AT_HWCAP,
333d1890517SZeng Heng 		.hwcap_bit = HWCAP_JSCVT,
334d1890517SZeng Heng 		.cpuinfo = "jscvt",
335d1890517SZeng Heng 		.sigill_fn = jscvt_sigill,
336d1890517SZeng Heng 	},
337d1890517SZeng Heng 	{
338d1890517SZeng Heng 		.name = "LRCPC",
339d1890517SZeng Heng 		.at_hwcap = AT_HWCAP,
340d1890517SZeng Heng 		.hwcap_bit = HWCAP_LRCPC,
341d1890517SZeng Heng 		.cpuinfo = "lrcpc",
342d1890517SZeng Heng 		.sigill_fn = lrcpc_sigill,
343d1890517SZeng Heng 	},
344d1890517SZeng Heng 	{
34582e7882bSZeng Heng 		.name = "LRCPC2",
34682e7882bSZeng Heng 		.at_hwcap = AT_HWCAP,
34782e7882bSZeng Heng 		.hwcap_bit = HWCAP_ILRCPC,
34882e7882bSZeng Heng 		.cpuinfo = "ilrcpc",
34982e7882bSZeng Heng 		.sigill_fn = ilrcpc_sigill,
35082e7882bSZeng Heng 	},
35182e7882bSZeng Heng 	{
35282e7882bSZeng Heng 		.name = "LSE",
35382e7882bSZeng Heng 		.at_hwcap = AT_HWCAP,
35482e7882bSZeng Heng 		.hwcap_bit = HWCAP_ATOMICS,
35582e7882bSZeng Heng 		.cpuinfo = "atomics",
35682e7882bSZeng Heng 		.sigill_fn = atomics_sigill,
35782e7882bSZeng Heng 	},
35882e7882bSZeng Heng 	{
35982e7882bSZeng Heng 		.name = "LSE2",
36082e7882bSZeng Heng 		.at_hwcap = AT_HWCAP,
361d8a324f1SKristina Martsenko 		.hwcap_bit = HWCAP_USCAT,
362d8a324f1SKristina Martsenko 		.cpuinfo = "uscat",
363d8a324f1SKristina Martsenko 		.sigill_fn = atomics_sigill,
364d8a324f1SKristina Martsenko 		.sigbus_fn = uscat_sigbus,
365d8a324f1SKristina Martsenko 		.sigbus_reliable = true,
366d8a324f1SKristina Martsenko 	},
367d8a324f1SKristina Martsenko 	{
368d8a324f1SKristina Martsenko 		.name = "MOPS",
3692c3ce0e7SZeng Heng 		.at_hwcap = AT_HWCAP2,
3702c3ce0e7SZeng Heng 		.hwcap_bit = HWCAP2_MOPS,
3712c3ce0e7SZeng Heng 		.cpuinfo = "mops",
3722c3ce0e7SZeng Heng 		.sigill_fn = mops_sigill,
3732c3ce0e7SZeng Heng 		.sigill_reliable = true,
3742c3ce0e7SZeng Heng 	},
3752c3ce0e7SZeng Heng 	{
376ef939f30SMark Brown 		.name = "PMULL",
377ef939f30SMark Brown 		.at_hwcap = AT_HWCAP,
378ef939f30SMark Brown 		.hwcap_bit = HWCAP_PMULL,
379ef939f30SMark Brown 		.cpuinfo = "pmull",
380ef939f30SMark Brown 		.sigill_fn = pmull_sigill,
381ef939f30SMark Brown 	},
382ef939f30SMark Brown 	{
383989d37fcSMark Brown 		.name = "RNG",
384989d37fcSMark Brown 		.at_hwcap = AT_HWCAP2,
385989d37fcSMark Brown 		.hwcap_bit = HWCAP2_RNG,
386989d37fcSMark Brown 		.cpuinfo = "rng",
387989d37fcSMark Brown 		.sigill_fn = rng_sigill,
388989d37fcSMark Brown 	},
3897eb4ee66SZeng Heng 	{
3907eb4ee66SZeng Heng 		.name = "RPRFM",
3917eb4ee66SZeng Heng 		.at_hwcap = AT_HWCAP2,
3927eb4ee66SZeng Heng 		.hwcap_bit = HWCAP2_RPRFM,
3937eb4ee66SZeng Heng 		.cpuinfo = "rprfm",
3947eb4ee66SZeng Heng 	},
3957eb4ee66SZeng Heng 	{
3967eb4ee66SZeng Heng 		.name = "SHA1",
3977eb4ee66SZeng Heng 		.at_hwcap = AT_HWCAP,
3987eb4ee66SZeng Heng 		.hwcap_bit = HWCAP_SHA1,
3997eb4ee66SZeng Heng 		.cpuinfo = "sha1",
4007eb4ee66SZeng Heng 		.sigill_fn = sha1_sigill,
4017eb4ee66SZeng Heng 	},
4027eb4ee66SZeng Heng 	{
4037eb4ee66SZeng Heng 		.name = "SHA2",
4047eb4ee66SZeng Heng 		.at_hwcap = AT_HWCAP,
4057eb4ee66SZeng Heng 		.hwcap_bit = HWCAP_SHA2,
4067eb4ee66SZeng Heng 		.cpuinfo = "sha2",
4077eb4ee66SZeng Heng 		.sigill_fn = sha2_sigill,
4087eb4ee66SZeng Heng 	},
4097eb4ee66SZeng Heng 	{
4107a9bcaaaSMark Brown 		.name = "SHA512",
4117a9bcaaaSMark Brown 		.at_hwcap = AT_HWCAP,
4127a9bcaaaSMark Brown 		.hwcap_bit = HWCAP_SHA512,
4137a9bcaaaSMark Brown 		.cpuinfo = "sha512",
4147a9bcaaaSMark Brown 		.sigill_fn = sha512_sigill,
4157a9bcaaaSMark Brown 	},
4167a9bcaaaSMark Brown 	{
4177a9bcaaaSMark Brown 		.name = "SME",
4183eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
4193eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME,
4203eb1b41fSMark Brown 		.cpuinfo = "sme",
4213eb1b41fSMark Brown 		.sigill_fn = sme_sigill,
4223eb1b41fSMark Brown 		.sigill_reliable = true,
4233eb1b41fSMark Brown 	},
4243eb1b41fSMark Brown 	{
4253eb1b41fSMark Brown 		.name = "SME2",
4263eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
4273eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME2,
4283eb1b41fSMark Brown 		.cpuinfo = "sme2",
4293eb1b41fSMark Brown 		.sigill_fn = sme2_sigill,
4303eb1b41fSMark Brown 		.sigill_reliable = true,
4313eb1b41fSMark Brown 	},
4323eb1b41fSMark Brown 	{
4333eb1b41fSMark Brown 		.name = "SME 2.1",
4343eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
4353eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME2P1,
4363eb1b41fSMark Brown 		.cpuinfo = "sme2p1",
4373eb1b41fSMark Brown 		.sigill_fn = sme2p1_sigill,
4383eb1b41fSMark Brown 	},
4393eb1b41fSMark Brown 	{
4403eb1b41fSMark Brown 		.name = "SME I16I32",
4413eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
4423eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME_I16I32,
4433eb1b41fSMark Brown 		.cpuinfo = "smei16i32",
4443eb1b41fSMark Brown 		.sigill_fn = smei16i32_sigill,
4453eb1b41fSMark Brown 	},
4463eb1b41fSMark Brown 	{
4473eb1b41fSMark Brown 		.name = "SME BI32I32",
4483eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
4493eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME_BI32I32,
4503eb1b41fSMark Brown 		.cpuinfo = "smebi32i32",
4513eb1b41fSMark Brown 		.sigill_fn = smebi32i32_sigill,
4523eb1b41fSMark Brown 	},
4533eb1b41fSMark Brown 	{
4543eb1b41fSMark Brown 		.name = "SME B16B16",
4553eb1b41fSMark Brown 		.at_hwcap = AT_HWCAP2,
4563eb1b41fSMark Brown 		.hwcap_bit = HWCAP2_SME_B16B16,
4573eb1b41fSMark Brown 		.cpuinfo = "smeb16b16",
4583eb1b41fSMark Brown 		.sigill_fn = smeb16b16_sigill,
4593eb1b41fSMark Brown 	},
4603eb1b41fSMark Brown 	{
4617a9bcaaaSMark Brown 		.name = "SME F16F16",
4627a9bcaaaSMark Brown 		.at_hwcap = AT_HWCAP2,
4637a9bcaaaSMark Brown 		.hwcap_bit = HWCAP2_SME_F16F16,
4647a9bcaaaSMark Brown 		.cpuinfo = "smef16f16",
4657a9bcaaaSMark Brown 		.sigill_fn = smef16f16_sigill,
4667a9bcaaaSMark Brown 	},
4677a9bcaaaSMark Brown 	{
468859a9d51SMark Brown 		.name = "SVE",
469859a9d51SMark Brown 		.at_hwcap = AT_HWCAP,
470859a9d51SMark Brown 		.hwcap_bit = HWCAP_SVE,
471859a9d51SMark Brown 		.cpuinfo = "sve",
472859a9d51SMark Brown 		.sigill_fn = sve_sigill,
473859a9d51SMark Brown 		.sigill_reliable = true,
474859a9d51SMark Brown 	},
475859a9d51SMark Brown 	{
476c5195b02SMark Brown 		.name = "SVE 2",
477c5195b02SMark Brown 		.at_hwcap = AT_HWCAP2,
478c5195b02SMark Brown 		.hwcap_bit = HWCAP2_SVE2,
479c5195b02SMark Brown 		.cpuinfo = "sve2",
480c5195b02SMark Brown 		.sigill_fn = sve2_sigill,
481c5195b02SMark Brown 	},
482c5195b02SMark Brown 	{
483859a9d51SMark Brown 		.name = "SVE 2.1",
484859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
485859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVE2P1,
486859a9d51SMark Brown 		.cpuinfo = "sve2p1",
487859a9d51SMark Brown 		.sigill_fn = sve2p1_sigill,
488859a9d51SMark Brown 	},
489859a9d51SMark Brown 	{
490859a9d51SMark Brown 		.name = "SVE AES",
491859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
492859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEAES,
493859a9d51SMark Brown 		.cpuinfo = "sveaes",
494859a9d51SMark Brown 		.sigill_fn = sveaes_sigill,
495859a9d51SMark Brown 	},
496859a9d51SMark Brown 	{
497859a9d51SMark Brown 		.name = "SVE2 PMULL",
498859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
499859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEPMULL,
500859a9d51SMark Brown 		.cpuinfo = "svepmull",
501859a9d51SMark Brown 		.sigill_fn = svepmull_sigill,
502859a9d51SMark Brown 	},
503859a9d51SMark Brown 	{
504859a9d51SMark Brown 		.name = "SVE2 BITPERM",
505859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
506859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEBITPERM,
507859a9d51SMark Brown 		.cpuinfo = "svebitperm",
508859a9d51SMark Brown 		.sigill_fn = svebitperm_sigill,
509859a9d51SMark Brown 	},
510859a9d51SMark Brown 	{
511859a9d51SMark Brown 		.name = "SVE2 SHA3",
512859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
513859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVESHA3,
514859a9d51SMark Brown 		.cpuinfo = "svesha3",
515859a9d51SMark Brown 		.sigill_fn = svesha3_sigill,
516859a9d51SMark Brown 	},
517859a9d51SMark Brown 	{
518859a9d51SMark Brown 		.name = "SVE2 SM4",
519859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
520859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVESM4,
521859a9d51SMark Brown 		.cpuinfo = "svesm4",
522859a9d51SMark Brown 		.sigill_fn = svesm4_sigill,
523859a9d51SMark Brown 	},
524859a9d51SMark Brown 	{
525859a9d51SMark Brown 		.name = "SVE2 I8MM",
526859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
527859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEI8MM,
528859a9d51SMark Brown 		.cpuinfo = "svei8mm",
529859a9d51SMark Brown 		.sigill_fn = svei8mm_sigill,
530859a9d51SMark Brown 	},
531859a9d51SMark Brown 	{
532859a9d51SMark Brown 		.name = "SVE2 F32MM",
533859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
534859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEF32MM,
535859a9d51SMark Brown 		.cpuinfo = "svef32mm",
536859a9d51SMark Brown 		.sigill_fn = svef32mm_sigill,
537859a9d51SMark Brown 	},
538859a9d51SMark Brown 	{
539859a9d51SMark Brown 		.name = "SVE2 F64MM",
540859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
541859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEF64MM,
542859a9d51SMark Brown 		.cpuinfo = "svef64mm",
543859a9d51SMark Brown 		.sigill_fn = svef64mm_sigill,
544859a9d51SMark Brown 	},
545859a9d51SMark Brown 	{
546859a9d51SMark Brown 		.name = "SVE2 BF16",
547859a9d51SMark Brown 		.at_hwcap = AT_HWCAP2,
548859a9d51SMark Brown 		.hwcap_bit = HWCAP2_SVEBF16,
549859a9d51SMark Brown 		.cpuinfo = "svebf16",
550859a9d51SMark Brown 		.sigill_fn = svebf16_sigill,
5517a9bcaaaSMark Brown 	},
5527a9bcaaaSMark Brown 	{
55371b634abSZeng Heng 		.name = "SVE2 EBF16",
5547a9bcaaaSMark Brown 		.at_hwcap = AT_HWCAP2,
55571b634abSZeng Heng 		.hwcap_bit = HWCAP2_SVE_EBF16,
55671b634abSZeng Heng 		.cpuinfo = "sveebf16",
55771b634abSZeng Heng 	},
55871b634abSZeng Heng 	{
55971b634abSZeng Heng 		.name = "HBC",
56071b634abSZeng Heng 		.at_hwcap = AT_HWCAP2,
56171b634abSZeng Heng 		.hwcap_bit = HWCAP2_HBC,
56271b634abSZeng Heng 		.cpuinfo = "hbc",
56371b634abSZeng Heng 		.sigill_fn = hbc_sigill,
5647a9bcaaaSMark Brown 		.sigill_reliable = true,
5657a9bcaaaSMark Brown 	},
56671b634abSZeng Heng };
567fd49cf08SZeng Heng 
56871b634abSZeng Heng typedef void (*sighandler_fn)(int, siginfo_t *, void *);
5697a9bcaaaSMark Brown 
5707a9bcaaaSMark Brown #define DEF_SIGHANDLER_FUNC(SIG, NUM)					\
5717a9bcaaaSMark Brown static bool seen_##SIG;							\
5727a9bcaaaSMark Brown static void handle_##SIG(int sig, siginfo_t *info, void *context)	\
5737a9bcaaaSMark Brown {									\
5747a9bcaaaSMark Brown 	ucontext_t *uc = context;					\
5757a9bcaaaSMark Brown 									\
5767a9bcaaaSMark Brown 	seen_##SIG = true;						\
5777a9bcaaaSMark Brown 	/* Skip over the offending instruction */			\
5787a9bcaaaSMark Brown 	uc->uc_mcontext.pc += 4;					\
5797a9bcaaaSMark Brown }
5807a9bcaaaSMark Brown 
5817a9bcaaaSMark Brown DEF_SIGHANDLER_FUNC(sigill, SIGILL);
5827a9bcaaaSMark Brown DEF_SIGHANDLER_FUNC(sigbus, SIGBUS);
5837a9bcaaaSMark Brown 
cpuinfo_present(const char * name)5847a9bcaaaSMark Brown bool cpuinfo_present(const char *name)
5857a9bcaaaSMark Brown {
5867a9bcaaaSMark Brown 	FILE *f;
5877a9bcaaaSMark Brown 	char buf[2048], name_space[30], name_newline[30];
5887a9bcaaaSMark Brown 	char *s;
5897a9bcaaaSMark Brown 
5907a9bcaaaSMark Brown 	/*
5917a9bcaaaSMark Brown 	 * The feature should appear with a leading space and either a
5927a9bcaaaSMark Brown 	 * trailing space or a newline.
5937a9bcaaaSMark Brown 	 */
5947a9bcaaaSMark Brown 	snprintf(name_space, sizeof(name_space), " %s ", name);
5957a9bcaaaSMark Brown 	snprintf(name_newline, sizeof(name_newline), " %s\n", name);
5967a9bcaaaSMark Brown 
5977a9bcaaaSMark Brown 	f = fopen("/proc/cpuinfo", "r");
5987a9bcaaaSMark Brown 	if (!f) {
5997a9bcaaaSMark Brown 		ksft_print_msg("Failed to open /proc/cpuinfo\n");
6007a9bcaaaSMark Brown 		return false;
6017a9bcaaaSMark Brown 	}
6027a9bcaaaSMark Brown 
6037a9bcaaaSMark Brown 	while (fgets(buf, sizeof(buf), f)) {
6047a9bcaaaSMark Brown 		/* Features: line? */
6057a9bcaaaSMark Brown 		if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0)
6067a9bcaaaSMark Brown 			continue;
6077a9bcaaaSMark Brown 
6087a9bcaaaSMark Brown 		/* All CPUs should be symmetric, don't read any more */
6097a9bcaaaSMark Brown 		fclose(f);
6107a9bcaaaSMark Brown 
61171b634abSZeng Heng 		s = strstr(buf, name_space);
6127a9bcaaaSMark Brown 		if (s)
61371b634abSZeng Heng 			return true;
6147a9bcaaaSMark Brown 		s = strstr(buf, name_newline);
6157a9bcaaaSMark Brown 		if (s)
6167a9bcaaaSMark Brown 			return true;
61771b634abSZeng Heng 
6187a9bcaaaSMark Brown 		return false;
6197a9bcaaaSMark Brown 	}
62071b634abSZeng Heng 
6217a9bcaaaSMark Brown 	ksft_print_msg("Failed to find Features in /proc/cpuinfo\n");
622fd49cf08SZeng Heng 	fclose(f);
6237a9bcaaaSMark Brown 	return false;
6247a9bcaaaSMark Brown }
62571b634abSZeng Heng 
install_sigaction(int signum,sighandler_fn handler)62671b634abSZeng Heng static int install_sigaction(int signum, sighandler_fn handler)
62771b634abSZeng Heng {
62871b634abSZeng Heng 	int ret;
62971b634abSZeng Heng 	struct sigaction sa;
63071b634abSZeng Heng 
631fd49cf08SZeng Heng 	memset(&sa, 0, sizeof(sa));
63271b634abSZeng Heng 	sa.sa_sigaction = handler;
63371b634abSZeng Heng 	sa.sa_flags = SA_RESTART | SA_SIGINFO;
63471b634abSZeng Heng 	sigemptyset(&sa.sa_mask);
63571b634abSZeng Heng 	ret = sigaction(signum, &sa, NULL);
63671b634abSZeng Heng 	if (ret < 0)
63771b634abSZeng Heng 		ksft_exit_fail_msg("Failed to install SIGNAL handler: %s (%d)\n",
63871b634abSZeng Heng 				   strerror(errno), errno);
63971b634abSZeng Heng 
64071b634abSZeng Heng 	return ret;
64171b634abSZeng Heng }
64271b634abSZeng Heng 
uninstall_sigaction(int signum)64371b634abSZeng Heng static void uninstall_sigaction(int signum)
64471b634abSZeng Heng {
64571b634abSZeng Heng 	if (sigaction(signum, NULL, NULL) < 0)
64671b634abSZeng Heng 		ksft_exit_fail_msg("Failed to uninstall SIGNAL handler: %s (%d)\n",
64771b634abSZeng Heng 				   strerror(errno), errno);
64871b634abSZeng Heng }
64971b634abSZeng Heng 
65071b634abSZeng Heng #define DEF_INST_RAISE_SIG(SIG, NUM)					\
65171b634abSZeng Heng static bool inst_raise_##SIG(const struct hwcap_data *hwcap,		\
65271b634abSZeng Heng 				bool have_hwcap)			\
65371b634abSZeng Heng {									\
65471b634abSZeng Heng 	if (!hwcap->SIG##_fn) {						\
65571b634abSZeng Heng 		ksft_test_result_skip(#SIG"_%s\n", hwcap->name);	\
65671b634abSZeng Heng 		/* assume that it would raise exception in default */	\
65771b634abSZeng Heng 		return true;						\
65871b634abSZeng Heng 	}								\
65971b634abSZeng Heng 									\
66071b634abSZeng Heng 	install_sigaction(NUM, handle_##SIG);				\
66171b634abSZeng Heng 									\
66271b634abSZeng Heng 	seen_##SIG = false;						\
66371b634abSZeng Heng 	hwcap->SIG##_fn();						\
66471b634abSZeng Heng 									\
66571b634abSZeng Heng 	if (have_hwcap) {						\
66671b634abSZeng Heng 		/* Should be able to use the extension */		\
66771b634abSZeng Heng 		ksft_test_result(!seen_##SIG,				\
66871b634abSZeng Heng 				#SIG"_%s\n", hwcap->name);		\
66971b634abSZeng Heng 	} else if (hwcap->SIG##_reliable) {				\
67071b634abSZeng Heng 		/* Guaranteed a SIGNAL */				\
67171b634abSZeng Heng 		ksft_test_result(seen_##SIG,				\
672fd49cf08SZeng Heng 				#SIG"_%s\n", hwcap->name);		\
67371b634abSZeng Heng 	} else {							\
67471b634abSZeng Heng 		/* Missing SIGNAL might be fine */			\
67571b634abSZeng Heng 		ksft_print_msg(#SIG"_%sreported for %s\n",		\
67671b634abSZeng Heng 				seen_##SIG ? "" : "not ",		\
67771b634abSZeng Heng 				hwcap->name);				\
678fd49cf08SZeng Heng 		ksft_test_result_skip(#SIG"_%s\n",			\
67971b634abSZeng Heng 					hwcap->name);			\
68071b634abSZeng Heng 	}								\
68171b634abSZeng Heng 									\
68271b634abSZeng Heng 	uninstall_sigaction(NUM);					\
6837a9bcaaaSMark Brown 	return seen_##SIG;						\
6847a9bcaaaSMark Brown }
6857a9bcaaaSMark Brown 
68633060a64SMark Brown DEF_INST_RAISE_SIG(sigill, SIGILL);
6877a9bcaaaSMark Brown DEF_INST_RAISE_SIG(sigbus, SIGBUS);
6887a9bcaaaSMark Brown 
main(void)6897a9bcaaaSMark Brown int main(void)
69078d2b197SMark Brown {
6917a9bcaaaSMark Brown 	int i;
6927a9bcaaaSMark Brown 	const struct hwcap_data *hwcap;
6937a9bcaaaSMark Brown 	bool have_cpuinfo, have_hwcap, raise_sigill;
6947a9bcaaaSMark Brown 
695fd49cf08SZeng Heng 	ksft_print_header();
696fd49cf08SZeng Heng 	ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP);
697fd49cf08SZeng Heng 
698fd49cf08SZeng Heng 	for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
699fd49cf08SZeng Heng 		hwcap = &hwcaps[i];
700fd49cf08SZeng Heng 
701fd49cf08SZeng Heng 		have_hwcap = getauxval(hwcap->at_hwcap) & hwcap->hwcap_bit;
702fd49cf08SZeng Heng 		have_cpuinfo = cpuinfo_present(hwcap->cpuinfo);
703fd49cf08SZeng Heng 
7047a9bcaaaSMark Brown 		if (have_hwcap)
7057a9bcaaaSMark Brown 			ksft_print_msg("%s present\n", hwcap->name);
7067a9bcaaaSMark Brown 
7077a9bcaaaSMark Brown 		ksft_test_result(have_hwcap == have_cpuinfo,
7087a9bcaaaSMark Brown 				 "cpuinfo_match_%s\n", hwcap->name);
7097a9bcaaaSMark Brown 
710 		/*
711 		 * Testing for SIGBUS only makes sense after make sure
712 		 * that the instruction does not cause a SIGILL signal.
713 		 */
714 		raise_sigill = inst_raise_sigill(hwcap, have_hwcap);
715 		if (!raise_sigill)
716 			inst_raise_sigbus(hwcap, have_hwcap);
717 		else
718 			ksft_test_result_skip("sigbus_%s\n", hwcap->name);
719 	}
720 
721 	ksft_print_cnts();
722 
723 	return 0;
724 }
725