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