162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2022 ARM Limited.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <errno.h>
762306a36Sopenharmony_ci#include <signal.h>
862306a36Sopenharmony_ci#include <stdbool.h>
962306a36Sopenharmony_ci#include <stddef.h>
1062306a36Sopenharmony_ci#include <stdio.h>
1162306a36Sopenharmony_ci#include <stdlib.h>
1262306a36Sopenharmony_ci#include <string.h>
1362306a36Sopenharmony_ci#include <unistd.h>
1462306a36Sopenharmony_ci#include <sys/auxv.h>
1562306a36Sopenharmony_ci#include <sys/prctl.h>
1662306a36Sopenharmony_ci#include <asm/hwcap.h>
1762306a36Sopenharmony_ci#include <asm/sigcontext.h>
1862306a36Sopenharmony_ci#include <asm/unistd.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "../../kselftest.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define TESTS_PER_HWCAP 3
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * Function expected to generate exception when the feature is not
2662306a36Sopenharmony_ci * supported and return when it is supported. If the specific exception
2762306a36Sopenharmony_ci * is generated then the handler must be able to skip over the
2862306a36Sopenharmony_ci * instruction safely.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Note that it is expected that for many architecture extensions
3162306a36Sopenharmony_ci * there are no specific traps due to no architecture state being
3262306a36Sopenharmony_ci * added so we may not fault if running on a kernel which doesn't know
3362306a36Sopenharmony_ci * to add the hwcap.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_citypedef void (*sig_fn)(void);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic void aes_sigill(void)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	/* AESE V0.16B, V0.16B */
4062306a36Sopenharmony_ci	asm volatile(".inst 0x4e284800" : : : );
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void atomics_sigill(void)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	/* STADD W0, [SP] */
4662306a36Sopenharmony_ci	asm volatile(".inst 0xb82003ff" : : : );
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void crc32_sigill(void)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	/* CRC32W W0, W0, W1 */
5262306a36Sopenharmony_ci	asm volatile(".inst 0x1ac14800" : : : );
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void cssc_sigill(void)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	/* CNT x0, x0 */
5862306a36Sopenharmony_ci	asm volatile(".inst 0xdac01c00" : : : "x0");
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void fp_sigill(void)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	asm volatile("fmov s0, #1");
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void ilrcpc_sigill(void)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	/* LDAPUR W0, [SP, #8] */
6962306a36Sopenharmony_ci	asm volatile(".inst 0x994083e0" : : : );
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void jscvt_sigill(void)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	/* FJCVTZS W0, D0 */
7562306a36Sopenharmony_ci	asm volatile(".inst 0x1e7e0000" : : : );
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void lrcpc_sigill(void)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	/* LDAPR W0, [SP, #0] */
8162306a36Sopenharmony_ci	asm volatile(".inst 0xb8bfc3e0" : : : );
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void mops_sigill(void)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	char dst[1], src[1];
8762306a36Sopenharmony_ci	register char *dstp asm ("x0") = dst;
8862306a36Sopenharmony_ci	register char *srcp asm ("x1") = src;
8962306a36Sopenharmony_ci	register long size asm ("x2") = 1;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* CPYP [x0]!, [x1]!, x2! */
9262306a36Sopenharmony_ci	asm volatile(".inst 0x1d010440"
9362306a36Sopenharmony_ci		     : "+r" (dstp), "+r" (srcp), "+r" (size)
9462306a36Sopenharmony_ci		     :
9562306a36Sopenharmony_ci		     : "cc", "memory");
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void pmull_sigill(void)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	/* PMULL V0.1Q, V0.1D, V0.1D */
10162306a36Sopenharmony_ci	asm volatile(".inst 0x0ee0e000" : : : );
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void rng_sigill(void)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0");
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic void sha1_sigill(void)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	/* SHA1H S0, S0 */
11262306a36Sopenharmony_ci	asm volatile(".inst 0x5e280800" : : : );
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic void sha2_sigill(void)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	/* SHA256H Q0, Q0, V0.4S */
11862306a36Sopenharmony_ci	asm volatile(".inst 0x5e004000" : : : );
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void sha512_sigill(void)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	/* SHA512H Q0, Q0, V0.2D */
12462306a36Sopenharmony_ci	asm volatile(".inst 0xce608000" : : : );
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void sme_sigill(void)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	/* RDSVL x0, #0 */
13062306a36Sopenharmony_ci	asm volatile(".inst 0x04bf5800" : : : "x0");
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void sme2_sigill(void)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	/* SMSTART ZA */
13662306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C5_3, xzr" : : : );
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* ZERO ZT0 */
13962306a36Sopenharmony_ci	asm volatile(".inst 0xc0480001" : : : );
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* SMSTOP */
14262306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void sme2p1_sigill(void)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	/* SMSTART SM */
14862306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C3_3, xzr" : : : );
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* BFCLAMP { Z0.H - Z1.H }, Z0.H, Z0.H */
15162306a36Sopenharmony_ci	asm volatile(".inst 0xc120C000" : : : );
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* SMSTOP */
15462306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic void smei16i32_sigill(void)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	/* SMSTART */
16062306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* SMOPA ZA0.S, P0/M, P0/M, Z0.B, Z0.B */
16362306a36Sopenharmony_ci	asm volatile(".inst 0xa0800000" : : : );
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* SMSTOP */
16662306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void smebi32i32_sigill(void)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	/* SMSTART */
17262306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* BMOPA ZA0.S, P0/M, P0/M, Z0.B, Z0.B */
17562306a36Sopenharmony_ci	asm volatile(".inst 0x80800008" : : : );
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* SMSTOP */
17862306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void smeb16b16_sigill(void)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	/* SMSTART */
18462306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* BFADD ZA.H[W0, 0], {Z0.H-Z1.H} */
18762306a36Sopenharmony_ci	asm volatile(".inst 0xC1E41C00" : : : );
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* SMSTOP */
19062306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void smef16f16_sigill(void)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	/* SMSTART */
19662306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* FADD ZA.H[W0, 0], { Z0.H-Z1.H } */
19962306a36Sopenharmony_ci	asm volatile(".inst 0xc1a41C00" : : : );
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* SMSTOP */
20262306a36Sopenharmony_ci	asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic void sve_sigill(void)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	/* RDVL x0, #0 */
20862306a36Sopenharmony_ci	asm volatile(".inst 0x04bf5000" : : : "x0");
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic void sve2_sigill(void)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	/* SQABS Z0.b, P0/M, Z0.B */
21462306a36Sopenharmony_ci	asm volatile(".inst 0x4408A000" : : : "z0");
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void sve2p1_sigill(void)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	/* BFADD Z0.H, Z0.H, Z0.H */
22062306a36Sopenharmony_ci	asm volatile(".inst 0x65000000" : : : "z0");
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void sveaes_sigill(void)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	/* AESD z0.b, z0.b, z0.b */
22662306a36Sopenharmony_ci	asm volatile(".inst 0x4522e400" : : : "z0");
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void svepmull_sigill(void)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	/* PMULLB Z0.Q, Z0.D, Z0.D */
23262306a36Sopenharmony_ci	asm volatile(".inst 0x45006800" : : : "z0");
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic void svebitperm_sigill(void)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	/* BDEP Z0.B, Z0.B, Z0.B */
23862306a36Sopenharmony_ci	asm volatile(".inst 0x4500b400" : : : "z0");
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic void svesha3_sigill(void)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	/* EOR3 Z0.D, Z0.D, Z0.D, Z0.D */
24462306a36Sopenharmony_ci	asm volatile(".inst 0x4203800" : : : "z0");
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic void svesm4_sigill(void)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	/* SM4E Z0.S, Z0.S, Z0.S */
25062306a36Sopenharmony_ci	asm volatile(".inst 0x4523e000" : : : "z0");
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic void svei8mm_sigill(void)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	/* USDOT Z0.S, Z0.B, Z0.B[0] */
25662306a36Sopenharmony_ci	asm volatile(".inst 0x44a01800" : : : "z0");
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void svef32mm_sigill(void)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	/* FMMLA Z0.S, Z0.S, Z0.S */
26262306a36Sopenharmony_ci	asm volatile(".inst 0x64a0e400" : : : "z0");
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void svef64mm_sigill(void)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	/* FMMLA Z0.D, Z0.D, Z0.D */
26862306a36Sopenharmony_ci	asm volatile(".inst 0x64e0e400" : : : "z0");
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void svebf16_sigill(void)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	/* BFCVT Z0.H, P0/M, Z0.S */
27462306a36Sopenharmony_ci	asm volatile(".inst 0x658aa000" : : : "z0");
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void hbc_sigill(void)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	/* BC.EQ +4 */
28062306a36Sopenharmony_ci	asm volatile("cmp xzr, xzr\n"
28162306a36Sopenharmony_ci		     ".inst 0x54000030" : : : "cc");
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic void uscat_sigbus(void)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	/* unaligned atomic access */
28762306a36Sopenharmony_ci	asm volatile("ADD x1, sp, #2" : : : );
28862306a36Sopenharmony_ci	/* STADD W0, [X1] */
28962306a36Sopenharmony_ci	asm volatile(".inst 0xb820003f" : : : );
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic const struct hwcap_data {
29362306a36Sopenharmony_ci	const char *name;
29462306a36Sopenharmony_ci	unsigned long at_hwcap;
29562306a36Sopenharmony_ci	unsigned long hwcap_bit;
29662306a36Sopenharmony_ci	const char *cpuinfo;
29762306a36Sopenharmony_ci	sig_fn sigill_fn;
29862306a36Sopenharmony_ci	bool sigill_reliable;
29962306a36Sopenharmony_ci	sig_fn sigbus_fn;
30062306a36Sopenharmony_ci	bool sigbus_reliable;
30162306a36Sopenharmony_ci} hwcaps[] = {
30262306a36Sopenharmony_ci	{
30362306a36Sopenharmony_ci		.name = "AES",
30462306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
30562306a36Sopenharmony_ci		.hwcap_bit = HWCAP_AES,
30662306a36Sopenharmony_ci		.cpuinfo = "aes",
30762306a36Sopenharmony_ci		.sigill_fn = aes_sigill,
30862306a36Sopenharmony_ci	},
30962306a36Sopenharmony_ci	{
31062306a36Sopenharmony_ci		.name = "CRC32",
31162306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
31262306a36Sopenharmony_ci		.hwcap_bit = HWCAP_CRC32,
31362306a36Sopenharmony_ci		.cpuinfo = "crc32",
31462306a36Sopenharmony_ci		.sigill_fn = crc32_sigill,
31562306a36Sopenharmony_ci	},
31662306a36Sopenharmony_ci	{
31762306a36Sopenharmony_ci		.name = "CSSC",
31862306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
31962306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_CSSC,
32062306a36Sopenharmony_ci		.cpuinfo = "cssc",
32162306a36Sopenharmony_ci		.sigill_fn = cssc_sigill,
32262306a36Sopenharmony_ci	},
32362306a36Sopenharmony_ci	{
32462306a36Sopenharmony_ci		.name = "FP",
32562306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
32662306a36Sopenharmony_ci		.hwcap_bit = HWCAP_FP,
32762306a36Sopenharmony_ci		.cpuinfo = "fp",
32862306a36Sopenharmony_ci		.sigill_fn = fp_sigill,
32962306a36Sopenharmony_ci	},
33062306a36Sopenharmony_ci	{
33162306a36Sopenharmony_ci		.name = "JSCVT",
33262306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
33362306a36Sopenharmony_ci		.hwcap_bit = HWCAP_JSCVT,
33462306a36Sopenharmony_ci		.cpuinfo = "jscvt",
33562306a36Sopenharmony_ci		.sigill_fn = jscvt_sigill,
33662306a36Sopenharmony_ci	},
33762306a36Sopenharmony_ci	{
33862306a36Sopenharmony_ci		.name = "LRCPC",
33962306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
34062306a36Sopenharmony_ci		.hwcap_bit = HWCAP_LRCPC,
34162306a36Sopenharmony_ci		.cpuinfo = "lrcpc",
34262306a36Sopenharmony_ci		.sigill_fn = lrcpc_sigill,
34362306a36Sopenharmony_ci	},
34462306a36Sopenharmony_ci	{
34562306a36Sopenharmony_ci		.name = "LRCPC2",
34662306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
34762306a36Sopenharmony_ci		.hwcap_bit = HWCAP_ILRCPC,
34862306a36Sopenharmony_ci		.cpuinfo = "ilrcpc",
34962306a36Sopenharmony_ci		.sigill_fn = ilrcpc_sigill,
35062306a36Sopenharmony_ci	},
35162306a36Sopenharmony_ci	{
35262306a36Sopenharmony_ci		.name = "LSE",
35362306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
35462306a36Sopenharmony_ci		.hwcap_bit = HWCAP_ATOMICS,
35562306a36Sopenharmony_ci		.cpuinfo = "atomics",
35662306a36Sopenharmony_ci		.sigill_fn = atomics_sigill,
35762306a36Sopenharmony_ci	},
35862306a36Sopenharmony_ci	{
35962306a36Sopenharmony_ci		.name = "LSE2",
36062306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
36162306a36Sopenharmony_ci		.hwcap_bit = HWCAP_USCAT,
36262306a36Sopenharmony_ci		.cpuinfo = "uscat",
36362306a36Sopenharmony_ci		.sigill_fn = atomics_sigill,
36462306a36Sopenharmony_ci		.sigbus_fn = uscat_sigbus,
36562306a36Sopenharmony_ci		.sigbus_reliable = true,
36662306a36Sopenharmony_ci	},
36762306a36Sopenharmony_ci	{
36862306a36Sopenharmony_ci		.name = "MOPS",
36962306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
37062306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_MOPS,
37162306a36Sopenharmony_ci		.cpuinfo = "mops",
37262306a36Sopenharmony_ci		.sigill_fn = mops_sigill,
37362306a36Sopenharmony_ci		.sigill_reliable = true,
37462306a36Sopenharmony_ci	},
37562306a36Sopenharmony_ci	{
37662306a36Sopenharmony_ci		.name = "PMULL",
37762306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
37862306a36Sopenharmony_ci		.hwcap_bit = HWCAP_PMULL,
37962306a36Sopenharmony_ci		.cpuinfo = "pmull",
38062306a36Sopenharmony_ci		.sigill_fn = pmull_sigill,
38162306a36Sopenharmony_ci	},
38262306a36Sopenharmony_ci	{
38362306a36Sopenharmony_ci		.name = "RNG",
38462306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
38562306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_RNG,
38662306a36Sopenharmony_ci		.cpuinfo = "rng",
38762306a36Sopenharmony_ci		.sigill_fn = rng_sigill,
38862306a36Sopenharmony_ci	},
38962306a36Sopenharmony_ci	{
39062306a36Sopenharmony_ci		.name = "RPRFM",
39162306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
39262306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_RPRFM,
39362306a36Sopenharmony_ci		.cpuinfo = "rprfm",
39462306a36Sopenharmony_ci	},
39562306a36Sopenharmony_ci	{
39662306a36Sopenharmony_ci		.name = "SHA1",
39762306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
39862306a36Sopenharmony_ci		.hwcap_bit = HWCAP_SHA1,
39962306a36Sopenharmony_ci		.cpuinfo = "sha1",
40062306a36Sopenharmony_ci		.sigill_fn = sha1_sigill,
40162306a36Sopenharmony_ci	},
40262306a36Sopenharmony_ci	{
40362306a36Sopenharmony_ci		.name = "SHA2",
40462306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
40562306a36Sopenharmony_ci		.hwcap_bit = HWCAP_SHA2,
40662306a36Sopenharmony_ci		.cpuinfo = "sha2",
40762306a36Sopenharmony_ci		.sigill_fn = sha2_sigill,
40862306a36Sopenharmony_ci	},
40962306a36Sopenharmony_ci	{
41062306a36Sopenharmony_ci		.name = "SHA512",
41162306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
41262306a36Sopenharmony_ci		.hwcap_bit = HWCAP_SHA512,
41362306a36Sopenharmony_ci		.cpuinfo = "sha512",
41462306a36Sopenharmony_ci		.sigill_fn = sha512_sigill,
41562306a36Sopenharmony_ci	},
41662306a36Sopenharmony_ci	{
41762306a36Sopenharmony_ci		.name = "SME",
41862306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
41962306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SME,
42062306a36Sopenharmony_ci		.cpuinfo = "sme",
42162306a36Sopenharmony_ci		.sigill_fn = sme_sigill,
42262306a36Sopenharmony_ci		.sigill_reliable = true,
42362306a36Sopenharmony_ci	},
42462306a36Sopenharmony_ci	{
42562306a36Sopenharmony_ci		.name = "SME2",
42662306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
42762306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SME2,
42862306a36Sopenharmony_ci		.cpuinfo = "sme2",
42962306a36Sopenharmony_ci		.sigill_fn = sme2_sigill,
43062306a36Sopenharmony_ci		.sigill_reliable = true,
43162306a36Sopenharmony_ci	},
43262306a36Sopenharmony_ci	{
43362306a36Sopenharmony_ci		.name = "SME 2.1",
43462306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
43562306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SME2P1,
43662306a36Sopenharmony_ci		.cpuinfo = "sme2p1",
43762306a36Sopenharmony_ci		.sigill_fn = sme2p1_sigill,
43862306a36Sopenharmony_ci	},
43962306a36Sopenharmony_ci	{
44062306a36Sopenharmony_ci		.name = "SME I16I32",
44162306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
44262306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SME_I16I32,
44362306a36Sopenharmony_ci		.cpuinfo = "smei16i32",
44462306a36Sopenharmony_ci		.sigill_fn = smei16i32_sigill,
44562306a36Sopenharmony_ci	},
44662306a36Sopenharmony_ci	{
44762306a36Sopenharmony_ci		.name = "SME BI32I32",
44862306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
44962306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SME_BI32I32,
45062306a36Sopenharmony_ci		.cpuinfo = "smebi32i32",
45162306a36Sopenharmony_ci		.sigill_fn = smebi32i32_sigill,
45262306a36Sopenharmony_ci	},
45362306a36Sopenharmony_ci	{
45462306a36Sopenharmony_ci		.name = "SME B16B16",
45562306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
45662306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SME_B16B16,
45762306a36Sopenharmony_ci		.cpuinfo = "smeb16b16",
45862306a36Sopenharmony_ci		.sigill_fn = smeb16b16_sigill,
45962306a36Sopenharmony_ci	},
46062306a36Sopenharmony_ci	{
46162306a36Sopenharmony_ci		.name = "SME F16F16",
46262306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
46362306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SME_F16F16,
46462306a36Sopenharmony_ci		.cpuinfo = "smef16f16",
46562306a36Sopenharmony_ci		.sigill_fn = smef16f16_sigill,
46662306a36Sopenharmony_ci	},
46762306a36Sopenharmony_ci	{
46862306a36Sopenharmony_ci		.name = "SVE",
46962306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP,
47062306a36Sopenharmony_ci		.hwcap_bit = HWCAP_SVE,
47162306a36Sopenharmony_ci		.cpuinfo = "sve",
47262306a36Sopenharmony_ci		.sigill_fn = sve_sigill,
47362306a36Sopenharmony_ci		.sigill_reliable = true,
47462306a36Sopenharmony_ci	},
47562306a36Sopenharmony_ci	{
47662306a36Sopenharmony_ci		.name = "SVE 2",
47762306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
47862306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVE2,
47962306a36Sopenharmony_ci		.cpuinfo = "sve2",
48062306a36Sopenharmony_ci		.sigill_fn = sve2_sigill,
48162306a36Sopenharmony_ci	},
48262306a36Sopenharmony_ci	{
48362306a36Sopenharmony_ci		.name = "SVE 2.1",
48462306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
48562306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVE2P1,
48662306a36Sopenharmony_ci		.cpuinfo = "sve2p1",
48762306a36Sopenharmony_ci		.sigill_fn = sve2p1_sigill,
48862306a36Sopenharmony_ci	},
48962306a36Sopenharmony_ci	{
49062306a36Sopenharmony_ci		.name = "SVE AES",
49162306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
49262306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVEAES,
49362306a36Sopenharmony_ci		.cpuinfo = "sveaes",
49462306a36Sopenharmony_ci		.sigill_fn = sveaes_sigill,
49562306a36Sopenharmony_ci	},
49662306a36Sopenharmony_ci	{
49762306a36Sopenharmony_ci		.name = "SVE2 PMULL",
49862306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
49962306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVEPMULL,
50062306a36Sopenharmony_ci		.cpuinfo = "svepmull",
50162306a36Sopenharmony_ci		.sigill_fn = svepmull_sigill,
50262306a36Sopenharmony_ci	},
50362306a36Sopenharmony_ci	{
50462306a36Sopenharmony_ci		.name = "SVE2 BITPERM",
50562306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
50662306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVEBITPERM,
50762306a36Sopenharmony_ci		.cpuinfo = "svebitperm",
50862306a36Sopenharmony_ci		.sigill_fn = svebitperm_sigill,
50962306a36Sopenharmony_ci	},
51062306a36Sopenharmony_ci	{
51162306a36Sopenharmony_ci		.name = "SVE2 SHA3",
51262306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
51362306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVESHA3,
51462306a36Sopenharmony_ci		.cpuinfo = "svesha3",
51562306a36Sopenharmony_ci		.sigill_fn = svesha3_sigill,
51662306a36Sopenharmony_ci	},
51762306a36Sopenharmony_ci	{
51862306a36Sopenharmony_ci		.name = "SVE2 SM4",
51962306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
52062306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVESM4,
52162306a36Sopenharmony_ci		.cpuinfo = "svesm4",
52262306a36Sopenharmony_ci		.sigill_fn = svesm4_sigill,
52362306a36Sopenharmony_ci	},
52462306a36Sopenharmony_ci	{
52562306a36Sopenharmony_ci		.name = "SVE2 I8MM",
52662306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
52762306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVEI8MM,
52862306a36Sopenharmony_ci		.cpuinfo = "svei8mm",
52962306a36Sopenharmony_ci		.sigill_fn = svei8mm_sigill,
53062306a36Sopenharmony_ci	},
53162306a36Sopenharmony_ci	{
53262306a36Sopenharmony_ci		.name = "SVE2 F32MM",
53362306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
53462306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVEF32MM,
53562306a36Sopenharmony_ci		.cpuinfo = "svef32mm",
53662306a36Sopenharmony_ci		.sigill_fn = svef32mm_sigill,
53762306a36Sopenharmony_ci	},
53862306a36Sopenharmony_ci	{
53962306a36Sopenharmony_ci		.name = "SVE2 F64MM",
54062306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
54162306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVEF64MM,
54262306a36Sopenharmony_ci		.cpuinfo = "svef64mm",
54362306a36Sopenharmony_ci		.sigill_fn = svef64mm_sigill,
54462306a36Sopenharmony_ci	},
54562306a36Sopenharmony_ci	{
54662306a36Sopenharmony_ci		.name = "SVE2 BF16",
54762306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
54862306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVEBF16,
54962306a36Sopenharmony_ci		.cpuinfo = "svebf16",
55062306a36Sopenharmony_ci		.sigill_fn = svebf16_sigill,
55162306a36Sopenharmony_ci	},
55262306a36Sopenharmony_ci	{
55362306a36Sopenharmony_ci		.name = "SVE2 EBF16",
55462306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
55562306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_SVE_EBF16,
55662306a36Sopenharmony_ci		.cpuinfo = "sveebf16",
55762306a36Sopenharmony_ci	},
55862306a36Sopenharmony_ci	{
55962306a36Sopenharmony_ci		.name = "HBC",
56062306a36Sopenharmony_ci		.at_hwcap = AT_HWCAP2,
56162306a36Sopenharmony_ci		.hwcap_bit = HWCAP2_HBC,
56262306a36Sopenharmony_ci		.cpuinfo = "hbc",
56362306a36Sopenharmony_ci		.sigill_fn = hbc_sigill,
56462306a36Sopenharmony_ci		.sigill_reliable = true,
56562306a36Sopenharmony_ci	},
56662306a36Sopenharmony_ci};
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_citypedef void (*sighandler_fn)(int, siginfo_t *, void *);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci#define DEF_SIGHANDLER_FUNC(SIG, NUM)					\
57162306a36Sopenharmony_cistatic bool seen_##SIG;							\
57262306a36Sopenharmony_cistatic void handle_##SIG(int sig, siginfo_t *info, void *context)	\
57362306a36Sopenharmony_ci{									\
57462306a36Sopenharmony_ci	ucontext_t *uc = context;					\
57562306a36Sopenharmony_ci									\
57662306a36Sopenharmony_ci	seen_##SIG = true;						\
57762306a36Sopenharmony_ci	/* Skip over the offending instruction */			\
57862306a36Sopenharmony_ci	uc->uc_mcontext.pc += 4;					\
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ciDEF_SIGHANDLER_FUNC(sigill, SIGILL);
58262306a36Sopenharmony_ciDEF_SIGHANDLER_FUNC(sigbus, SIGBUS);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cibool cpuinfo_present(const char *name)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	FILE *f;
58762306a36Sopenharmony_ci	char buf[2048], name_space[30], name_newline[30];
58862306a36Sopenharmony_ci	char *s;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/*
59162306a36Sopenharmony_ci	 * The feature should appear with a leading space and either a
59262306a36Sopenharmony_ci	 * trailing space or a newline.
59362306a36Sopenharmony_ci	 */
59462306a36Sopenharmony_ci	snprintf(name_space, sizeof(name_space), " %s ", name);
59562306a36Sopenharmony_ci	snprintf(name_newline, sizeof(name_newline), " %s\n", name);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	f = fopen("/proc/cpuinfo", "r");
59862306a36Sopenharmony_ci	if (!f) {
59962306a36Sopenharmony_ci		ksft_print_msg("Failed to open /proc/cpuinfo\n");
60062306a36Sopenharmony_ci		return false;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	while (fgets(buf, sizeof(buf), f)) {
60462306a36Sopenharmony_ci		/* Features: line? */
60562306a36Sopenharmony_ci		if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0)
60662306a36Sopenharmony_ci			continue;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		/* All CPUs should be symmetric, don't read any more */
60962306a36Sopenharmony_ci		fclose(f);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		s = strstr(buf, name_space);
61262306a36Sopenharmony_ci		if (s)
61362306a36Sopenharmony_ci			return true;
61462306a36Sopenharmony_ci		s = strstr(buf, name_newline);
61562306a36Sopenharmony_ci		if (s)
61662306a36Sopenharmony_ci			return true;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		return false;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	ksft_print_msg("Failed to find Features in /proc/cpuinfo\n");
62262306a36Sopenharmony_ci	fclose(f);
62362306a36Sopenharmony_ci	return false;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic int install_sigaction(int signum, sighandler_fn handler)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	int ret;
62962306a36Sopenharmony_ci	struct sigaction sa;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	memset(&sa, 0, sizeof(sa));
63262306a36Sopenharmony_ci	sa.sa_sigaction = handler;
63362306a36Sopenharmony_ci	sa.sa_flags = SA_RESTART | SA_SIGINFO;
63462306a36Sopenharmony_ci	sigemptyset(&sa.sa_mask);
63562306a36Sopenharmony_ci	ret = sigaction(signum, &sa, NULL);
63662306a36Sopenharmony_ci	if (ret < 0)
63762306a36Sopenharmony_ci		ksft_exit_fail_msg("Failed to install SIGNAL handler: %s (%d)\n",
63862306a36Sopenharmony_ci				   strerror(errno), errno);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	return ret;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic void uninstall_sigaction(int signum)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	if (sigaction(signum, NULL, NULL) < 0)
64662306a36Sopenharmony_ci		ksft_exit_fail_msg("Failed to uninstall SIGNAL handler: %s (%d)\n",
64762306a36Sopenharmony_ci				   strerror(errno), errno);
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci#define DEF_INST_RAISE_SIG(SIG, NUM)					\
65162306a36Sopenharmony_cistatic bool inst_raise_##SIG(const struct hwcap_data *hwcap,		\
65262306a36Sopenharmony_ci				bool have_hwcap)			\
65362306a36Sopenharmony_ci{									\
65462306a36Sopenharmony_ci	if (!hwcap->SIG##_fn) {						\
65562306a36Sopenharmony_ci		ksft_test_result_skip(#SIG"_%s\n", hwcap->name);	\
65662306a36Sopenharmony_ci		/* assume that it would raise exception in default */	\
65762306a36Sopenharmony_ci		return true;						\
65862306a36Sopenharmony_ci	}								\
65962306a36Sopenharmony_ci									\
66062306a36Sopenharmony_ci	install_sigaction(NUM, handle_##SIG);				\
66162306a36Sopenharmony_ci									\
66262306a36Sopenharmony_ci	seen_##SIG = false;						\
66362306a36Sopenharmony_ci	hwcap->SIG##_fn();						\
66462306a36Sopenharmony_ci									\
66562306a36Sopenharmony_ci	if (have_hwcap) {						\
66662306a36Sopenharmony_ci		/* Should be able to use the extension */		\
66762306a36Sopenharmony_ci		ksft_test_result(!seen_##SIG,				\
66862306a36Sopenharmony_ci				#SIG"_%s\n", hwcap->name);		\
66962306a36Sopenharmony_ci	} else if (hwcap->SIG##_reliable) {				\
67062306a36Sopenharmony_ci		/* Guaranteed a SIGNAL */				\
67162306a36Sopenharmony_ci		ksft_test_result(seen_##SIG,				\
67262306a36Sopenharmony_ci				#SIG"_%s\n", hwcap->name);		\
67362306a36Sopenharmony_ci	} else {							\
67462306a36Sopenharmony_ci		/* Missing SIGNAL might be fine */			\
67562306a36Sopenharmony_ci		ksft_print_msg(#SIG"_%sreported for %s\n",		\
67662306a36Sopenharmony_ci				seen_##SIG ? "" : "not ",		\
67762306a36Sopenharmony_ci				hwcap->name);				\
67862306a36Sopenharmony_ci		ksft_test_result_skip(#SIG"_%s\n",			\
67962306a36Sopenharmony_ci					hwcap->name);			\
68062306a36Sopenharmony_ci	}								\
68162306a36Sopenharmony_ci									\
68262306a36Sopenharmony_ci	uninstall_sigaction(NUM);					\
68362306a36Sopenharmony_ci	return seen_##SIG;						\
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ciDEF_INST_RAISE_SIG(sigill, SIGILL);
68762306a36Sopenharmony_ciDEF_INST_RAISE_SIG(sigbus, SIGBUS);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ciint main(void)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	int i;
69262306a36Sopenharmony_ci	const struct hwcap_data *hwcap;
69362306a36Sopenharmony_ci	bool have_cpuinfo, have_hwcap, raise_sigill;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	ksft_print_header();
69662306a36Sopenharmony_ci	ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
69962306a36Sopenharmony_ci		hwcap = &hwcaps[i];
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		have_hwcap = getauxval(hwcap->at_hwcap) & hwcap->hwcap_bit;
70262306a36Sopenharmony_ci		have_cpuinfo = cpuinfo_present(hwcap->cpuinfo);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		if (have_hwcap)
70562306a36Sopenharmony_ci			ksft_print_msg("%s present\n", hwcap->name);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		ksft_test_result(have_hwcap == have_cpuinfo,
70862306a36Sopenharmony_ci				 "cpuinfo_match_%s\n", hwcap->name);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		/*
71162306a36Sopenharmony_ci		 * Testing for SIGBUS only makes sense after make sure
71262306a36Sopenharmony_ci		 * that the instruction does not cause a SIGILL signal.
71362306a36Sopenharmony_ci		 */
71462306a36Sopenharmony_ci		raise_sigill = inst_raise_sigill(hwcap, have_hwcap);
71562306a36Sopenharmony_ci		if (!raise_sigill)
71662306a36Sopenharmony_ci			inst_raise_sigbus(hwcap, have_hwcap);
71762306a36Sopenharmony_ci		else
71862306a36Sopenharmony_ci			ksft_test_result_skip("sigbus_%s\n", hwcap->name);
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	ksft_print_cnts();
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	return 0;
72462306a36Sopenharmony_ci}
725