root/tools/testing/selftests/arm64/abi/hwcap.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2022 ARM Limited.
 */

#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/auxvec.h>
#include <linux/compiler.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <asm/hwcap.h>
#include <asm/sigcontext.h>
#include <asm/unistd.h>

#include <linux/auxvec.h>

#include "kselftest.h"

#define TESTS_PER_HWCAP 3

#ifndef AT_HWCAP3
#define AT_HWCAP3 29
#endif

/*
 * Function expected to generate exception when the feature is not
 * supported and return when it is supported. If the specific exception
 * is generated then the handler must be able to skip over the
 * instruction safely.
 *
 * Note that it is expected that for many architecture extensions
 * there are no specific traps due to no architecture state being
 * added so we may not fault if running on a kernel which doesn't know
 * to add the hwcap.
 */
typedef void (*sig_fn)(void);

static void aes_sigill(void)
{
        /* AESE V0.16B, V0.16B */
        asm volatile(".inst 0x4e284800" : : : );
}

static void atomics_sigill(void)
{
        /* STADD W0, [SP] */
        asm volatile(".inst 0xb82003ff" : : : );
}

static void cmpbr_sigill(void)
{
        /* Not implemented, too complicated and unreliable anyway */
}

static void crc32_sigill(void)
{
        /* CRC32W W0, W0, W1 */
        asm volatile(".inst 0x1ac14800" : : : );
}

static void cssc_sigill(void)
{
        /* CNT x0, x0 */
        asm volatile(".inst 0xdac01c00" : : : "x0");
}

static void f8cvt_sigill(void)
{
        /* FSCALE V0.4H, V0.4H, V0.4H */
        asm volatile(".inst 0x2ec03c00");
}

static void f8dp2_sigill(void)
{
        /* FDOT V0.4H, V0.4H, V0.5H */
        asm volatile(".inst 0xe40fc00");
}

static void f8dp4_sigill(void)
{
        /* FDOT V0.2S, V0.2S, V0.2S */
        asm volatile(".inst 0xe00fc00");
}

static void f8fma_sigill(void)
{
        /* FMLALB V0.8H, V0.16B, V0.16B */
        asm volatile(".inst 0xec0fc00");
}

static void f8mm4_sigill(void)
{
        /* FMMLA V0.4SH, V0.16B, V0.16B */
        asm volatile(".inst 0x6e00ec00");
}

static void f8mm8_sigill(void)
{
        /* FMMLA V0.4S, V0.16B, V0.16B */
        asm volatile(".inst 0x6e80ec00");
}

static void faminmax_sigill(void)
{
        /* FAMIN V0.4H, V0.4H, V0.4H */
        asm volatile(".inst 0x2ec01c00");
}

static void fp_sigill(void)
{
        asm volatile("fmov s0, #1");
}

static void fpmr_sigill(void)
{
        asm volatile("mrs x0, S3_3_C4_C4_2" : : : "x0");
}

static void fprcvt_sigill(void)
{
        /* FCVTAS S0, H0 */
        asm volatile(".inst 0x1efa0000");
}

static void gcs_sigill(void)
{
        unsigned long *gcspr;

        asm volatile(
                "mrs    %0, S3_3_C2_C5_1"
        : "=r" (gcspr)
        :
        : "cc");
}

static void ilrcpc_sigill(void)
{
        /* LDAPUR W0, [SP, #8] */
        asm volatile(".inst 0x994083e0" : : : );
}

static void jscvt_sigill(void)
{
        /* FJCVTZS W0, D0 */
        asm volatile(".inst 0x1e7e0000" : : : );
}

static void lrcpc_sigill(void)
{
        /* LDAPR W0, [SP, #0] */
        asm volatile(".inst 0xb8bfc3e0" : : : );
}

static void lse128_sigill(void)
{
        u64 __attribute__ ((aligned (16))) mem[2] = { 10, 20 };
        register u64 *memp asm ("x0") = mem;
        register u64 val0 asm ("x1") = 5;
        register u64 val1 asm ("x2") = 4;

        /* SWPP X1, X2, [X0] */
        asm volatile(".inst 0x19228001"
                     : "+r" (memp), "+r" (val0), "+r" (val1)
                     :
                     : "cc", "memory");
}

static void lsfe_sigill(void)
{
        float __attribute__ ((aligned (16))) mem;
        register float *memp asm ("x0") = &mem;

        /* STFADD H0, [X0] */
        asm volatile(".inst 0x7c20801f"
                     : "+r" (memp)
                     :
                     : "memory");
}

static void lut_sigill(void)
{
        /* LUTI2 V0.16B, { V0.16B }, V[0] */
        asm volatile(".inst 0x4e801000");
}

static void mops_sigill(void)
{
        char dst[1], src[1];
        register char *dstp asm ("x0") = dst;
        register char *srcp asm ("x1") = src;
        register long size asm ("x2") = 1;

        /* CPYP [x0]!, [x1]!, x2! */
        asm volatile(".inst 0x1d010440"
                     : "+r" (dstp), "+r" (srcp), "+r" (size)
                     :
                     : "cc", "memory");
}

static void pmull_sigill(void)
{
        /* PMULL V0.1Q, V0.1D, V0.1D */
        asm volatile(".inst 0x0ee0e000" : : : );
}

static void poe_sigill(void)
{
        /* mrs x0, POR_EL0 */
        asm volatile("mrs x0, S3_3_C10_C2_4" : : : "x0");
}

static void rng_sigill(void)
{
        asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0");
}

static void sha1_sigill(void)
{
        /* SHA1H S0, S0 */
        asm volatile(".inst 0x5e280800" : : : );
}

static void sha2_sigill(void)
{
        /* SHA256H Q0, Q0, V0.4S */
        asm volatile(".inst 0x5e004000" : : : );
}

static void sha512_sigill(void)
{
        /* SHA512H Q0, Q0, V0.2D */
        asm volatile(".inst 0xce608000" : : : );
}

static void sme_sigill(void)
{
        /* RDSVL x0, #0 */
        asm volatile(".inst 0x04bf5800" : : : "x0");
}

static void sme2_sigill(void)
{
        /* SMSTART ZA */
        asm volatile("msr S0_3_C4_C5_3, xzr" : : : );

        /* ZERO ZT0 */
        asm volatile(".inst 0xc0480001" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void sme2p1_sigill(void)
{
        /* SMSTART SM */
        asm volatile("msr S0_3_C4_C3_3, xzr" : : : );

        /* BFCLAMP { Z0.H - Z1.H }, Z0.H, Z0.H */
        asm volatile(".inst 0xc120C000" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void sme2p2_sigill(void)
{
        /* SMSTART SM */
        asm volatile("msr S0_3_C4_C3_3, xzr" : : : );

        /* UXTB Z0.D, P0/Z, Z0.D  */
        asm volatile(".inst 0x4c1a000" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void sme_aes_sigill(void)
{
        /* SMSTART SM */
        asm volatile("msr S0_3_C4_C3_3, xzr" : : : );

        /* AESD z0.b, z0.b, z0.b */
        asm volatile(".inst 0x4522e400" : : : "z0");

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void sme_sbitperm_sigill(void)
{
        /* SMSTART SM */
        asm volatile("msr S0_3_C4_C3_3, xzr" : : : );

        /* BDEP Z0.B, Z0.B, Z0.B */
        asm volatile(".inst 0x4500b400" : : : "z0");

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smei16i32_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* SMOPA ZA0.S, P0/M, P0/M, Z0.B, Z0.B */
        asm volatile(".inst 0xa0800000" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smebi32i32_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* BMOPA ZA0.S, P0/M, P0/M, Z0.B, Z0.B */
        asm volatile(".inst 0x80800008" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smeb16b16_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* BFADD ZA.H[W0, 0], {Z0.H-Z1.H} */
        asm volatile(".inst 0xC1E41C00" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smef16f16_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* FADD ZA.H[W0, 0], { Z0.H-Z1.H } */
        asm volatile(".inst 0xc1a41C00" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smef8f16_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* FDOT ZA.H[W0, 0], Z0.B-Z1.B, Z0.B-Z1.B */
        asm volatile(".inst 0xc1a01020" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smef8f32_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* FDOT ZA.S[W0, 0], { Z0.B-Z1.B }, Z0.B[0] */
        asm volatile(".inst 0xc1500038" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smelutv2_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* LUTI4 { Z0.B-Z3.B }, ZT0, { Z0-Z1 } */
        asm volatile(".inst 0xc08b0000" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smesf8dp2_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* FDOT Z0.H, Z0.B, Z0.B[0] */
        asm volatile(".inst 0x64204400" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smesf8dp4_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* FDOT Z0.S, Z0.B, Z0.B[0] */
        asm volatile(".inst 0xc1a41C00" : : : );

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smesf8fma_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* FMLALB Z0.8H, Z0.B, Z0.B */
        asm volatile(".inst 0x64205000");

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smesfexpa_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* FEXPA Z0.D, Z0.D */
        asm volatile(".inst 0x04e0b800");

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smesmop4_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* SMOP4A ZA0.S, Z0.B, { Z0.B - Z1.B } */
        asm volatile(".inst 0x80108000");

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void smestmop_sigill(void)
{
        /* SMSTART */
        asm volatile("msr S0_3_C4_C7_3, xzr" : : : );

        /* STMOPA ZA0.S, { Z0.H - Z1.H }, Z0.H, Z20[0] */
        asm volatile(".inst 0x80408008");

        /* SMSTOP */
        asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
}

static void sve_sigill(void)
{
        /* RDVL x0, #0 */
        asm volatile(".inst 0x04bf5000" : : : "x0");
}

static void sve2_sigill(void)
{
        /* SQABS Z0.b, P0/M, Z0.B */
        asm volatile(".inst 0x4408A000" : : : "z0");
}

static void sve2p1_sigill(void)
{
        /* LD1Q {Z0.Q}, P0/Z, [Z0.D, X0] */
        asm volatile(".inst 0xC400A000" : : : "z0");
}

static void sve2p2_sigill(void)
{
        /* NOT Z0.D, P0/Z, Z0.D */
        asm volatile(".inst 0x4cea000" : : : "z0");
}

static void sveaes_sigill(void)
{
        /* AESD z0.b, z0.b, z0.b */
        asm volatile(".inst 0x4522e400" : : : "z0");
}

static void sveaes2_sigill(void)
{
        /* AESD {Z0.B - Z1.B }, { Z0.B - Z1.B }, Z0.Q */
        asm volatile(".inst 0x4522ec00" : : : "z0");
}

static void sveb16b16_sigill(void)
{
        /* BFADD Z0.H, Z0.H, Z0.H */
        asm volatile(".inst 0x65000000" : : : );
}

static void svebfscale_sigill(void)
{
        /* BFSCALE Z0.H, P0/M, Z0.H, Z0.H */
        asm volatile(".inst 0x65098000" : : : "z0");
}

static void svef16mm_sigill(void)
{
        /* FMMLA Z0.S, Z0.H, Z0.H */
        asm volatile(".inst 0x6420e400");
}

static void svepmull_sigill(void)
{
        /* PMULLB Z0.Q, Z0.D, Z0.D */
        asm volatile(".inst 0x45006800" : : : "z0");
}

static void svebitperm_sigill(void)
{
        /* BDEP Z0.B, Z0.B, Z0.B */
        asm volatile(".inst 0x4500b400" : : : "z0");
}

static void svesha3_sigill(void)
{
        /* EOR3 Z0.D, Z0.D, Z0.D, Z0.D */
        asm volatile(".inst 0x4203800" : : : "z0");
}

static void sveeltperm_sigill(void)
{
        /* COMPACT Z0.B, P0, Z0.B */
        asm volatile(".inst 0x5218000" : : : "x0");
}

static void svesm4_sigill(void)
{
        /* SM4E Z0.S, Z0.S, Z0.S */
        asm volatile(".inst 0x4523e000" : : : "z0");
}

static void svei8mm_sigill(void)
{
        /* USDOT Z0.S, Z0.B, Z0.B[0] */
        asm volatile(".inst 0x44a01800" : : : "z0");
}

static void svef32mm_sigill(void)
{
        /* FMMLA Z0.S, Z0.S, Z0.S */
        asm volatile(".inst 0x64a0e400" : : : "z0");
}

static void svef64mm_sigill(void)
{
        /* FMMLA Z0.D, Z0.D, Z0.D */
        asm volatile(".inst 0x64e0e400" : : : "z0");
}

static void svebf16_sigill(void)
{
        /* BFCVT Z0.H, P0/M, Z0.S */
        asm volatile(".inst 0x658aa000" : : : "z0");
}

static void hbc_sigill(void)
{
        /* BC.EQ +4 */
        asm volatile("cmp xzr, xzr\n"
                     ".inst 0x54000030" : : : "cc");
}

static void uscat_sigbus(void)
{
        /* unaligned atomic access */
        asm volatile("ADD x1, sp, #2" : : : );
        /* STADD W0, [X1] */
        asm volatile(".inst 0xb820003f" : : : );
}

static void lrcpc3_sigill(void)
{
        int data[2] = { 1, 2 };

        register int *src asm ("x0") = data;
        register int data0 asm ("w2") = 0;
        register int data1 asm ("w3") = 0;

        /* LDIAPP w2, w3, [x0] */
        asm volatile(".inst 0x99431802"
                      : "=r" (data0), "=r" (data1) : "r" (src) :);
}

static void ignore_signal(int sig, siginfo_t *info, void *context)
{
        ucontext_t *uc = context;

        uc->uc_mcontext.pc += 4;
}

static void ls64_sigill(void)
{
        struct sigaction ign, old;
        char src[64] __aligned(64) = { 1 };

        /*
         * LS64 requires target memory to be Device/Non-cacheable (if
         * FEAT_LS64WB not supported) and the completer supports these
         * instructions, otherwise we'll receive a SIGBUS. Since we are only
         * testing the ABI here, so just ignore the SIGBUS and see if we can
         * execute the instructions without receiving a SIGILL. Restore the
         * handler of SIGBUS after this test.
         */
        ign.sa_sigaction = ignore_signal;
        ign.sa_flags = SA_SIGINFO | SA_RESTART;
        sigemptyset(&ign.sa_mask);
        sigaction(SIGBUS, &ign, &old);

        register void *xn asm ("x8") = src;
        register u64 xt_1 asm ("x0");

        /* LD64B x0, [x8] */
        asm volatile(".inst 0xf83fd100" : "=r" (xt_1) : "r" (xn)
                     : "x1", "x2", "x3", "x4", "x5", "x6", "x7");

        /* ST64B x0, [x8] */
        asm volatile(".inst 0xf83f9100" : : "r" (xt_1), "r" (xn)
                     : "x1", "x2", "x3", "x4", "x5", "x6", "x7");

        sigaction(SIGBUS, &old, NULL);
}

static const struct hwcap_data {
        const char *name;
        unsigned long at_hwcap;
        unsigned long hwcap_bit;
        const char *cpuinfo;
        sig_fn sigill_fn;
        bool sigill_reliable;
        sig_fn sigbus_fn;
        bool sigbus_reliable;
} hwcaps[] = {
        {
                .name = "AES",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_AES,
                .cpuinfo = "aes",
                .sigill_fn = aes_sigill,
        },
        {
                .name = "CMPBR",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_CMPBR,
                .cpuinfo = "cmpbr",
                .sigill_fn = cmpbr_sigill,
        },
        {
                .name = "CRC32",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_CRC32,
                .cpuinfo = "crc32",
                .sigill_fn = crc32_sigill,
        },
        {
                .name = "CSSC",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_CSSC,
                .cpuinfo = "cssc",
                .sigill_fn = cssc_sigill,
        },
        {
                .name = "F8CVT",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_F8CVT,
                .cpuinfo = "f8cvt",
                .sigill_fn = f8cvt_sigill,
        },
        {
                .name = "F8DP4",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_F8DP4,
                .cpuinfo = "f8dp4",
                .sigill_fn = f8dp4_sigill,
        },
        {
                .name = "F8DP2",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_F8DP2,
                .cpuinfo = "f8dp2",
                .sigill_fn = f8dp2_sigill,
        },
        {
                .name = "F8E5M2",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_F8E5M2,
                .cpuinfo = "f8e5m2",
        },
        {
                .name = "F8E4M3",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_F8E4M3,
                .cpuinfo = "f8e4m3",
        },
        {
                .name = "F8FMA",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_F8FMA,
                .cpuinfo = "f8fma",
                .sigill_fn = f8fma_sigill,
        },
        {
                .name = "F8MM8",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_F8MM8,
                .cpuinfo = "f8mm8",
                .sigill_fn = f8mm8_sigill,
        },
        {
                .name = "F8MM4",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_F8MM4,
                .cpuinfo = "f8mm4",
                .sigill_fn = f8mm4_sigill,
        },
        {
                .name = "FAMINMAX",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_FAMINMAX,
                .cpuinfo = "faminmax",
                .sigill_fn = faminmax_sigill,
        },
        {
                .name = "FP",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_FP,
                .cpuinfo = "fp",
                .sigill_fn = fp_sigill,
        },
        {
                .name = "FPMR",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_FPMR,
                .cpuinfo = "fpmr",
                .sigill_fn = fpmr_sigill,
                .sigill_reliable = true,
        },
        {
                .name = "FPRCVT",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_FPRCVT,
                .cpuinfo = "fprcvt",
                .sigill_fn = fprcvt_sigill,
        },
        {
                .name = "GCS",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_GCS,
                .cpuinfo = "gcs",
                .sigill_fn = gcs_sigill,
                .sigill_reliable = true,
        },
        {
                .name = "JSCVT",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_JSCVT,
                .cpuinfo = "jscvt",
                .sigill_fn = jscvt_sigill,
        },
        {
                .name = "LRCPC",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_LRCPC,
                .cpuinfo = "lrcpc",
                .sigill_fn = lrcpc_sigill,
        },
        {
                .name = "LRCPC2",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_ILRCPC,
                .cpuinfo = "ilrcpc",
                .sigill_fn = ilrcpc_sigill,
        },
        {
                .name = "LRCPC3",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_LRCPC3,
                .cpuinfo = "lrcpc3",
                .sigill_fn = lrcpc3_sigill,
        },
        {
                .name = "LSE",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_ATOMICS,
                .cpuinfo = "atomics",
                .sigill_fn = atomics_sigill,
        },
        {
                .name = "LSE2",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_USCAT,
                .cpuinfo = "uscat",
                .sigill_fn = atomics_sigill,
                .sigbus_fn = uscat_sigbus,
                .sigbus_reliable = true,
        },
        {
                .name = "LSE128",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_LSE128,
                .cpuinfo = "lse128",
                .sigill_fn = lse128_sigill,
        },
        {
                .name = "LSFE",
                .at_hwcap = AT_HWCAP3,
                .hwcap_bit = HWCAP3_LSFE,
                .cpuinfo = "lsfe",
                .sigill_fn = lsfe_sigill,
        },
        {
                .name = "LUT",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_LUT,
                .cpuinfo = "lut",
                .sigill_fn = lut_sigill,
        },
        {
                .name = "MOPS",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_MOPS,
                .cpuinfo = "mops",
                .sigill_fn = mops_sigill,
                .sigill_reliable = true,
        },
        {
                .name = "PMULL",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_PMULL,
                .cpuinfo = "pmull",
                .sigill_fn = pmull_sigill,
        },
        {
                .name = "POE",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_POE,
                .cpuinfo = "poe",
                .sigill_fn = poe_sigill,
                .sigill_reliable = true,
        },
        {
                .name = "RNG",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_RNG,
                .cpuinfo = "rng",
                .sigill_fn = rng_sigill,
        },
        {
                .name = "RPRFM",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_RPRFM,
                .cpuinfo = "rprfm",
        },
        {
                .name = "SHA1",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SHA1,
                .cpuinfo = "sha1",
                .sigill_fn = sha1_sigill,
        },
        {
                .name = "SHA2",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SHA2,
                .cpuinfo = "sha2",
                .sigill_fn = sha2_sigill,
        },
        {
                .name = "SHA512",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SHA512,
                .cpuinfo = "sha512",
                .sigill_fn = sha512_sigill,
        },
        {
                .name = "SME",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME,
                .cpuinfo = "sme",
                .sigill_fn = sme_sigill,
                .sigill_reliable = true,
        },
        {
                .name = "SME2",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME2,
                .cpuinfo = "sme2",
                .sigill_fn = sme2_sigill,
                .sigill_reliable = true,
        },
        {
                .name = "SME 2.1",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME2P1,
                .cpuinfo = "sme2p1",
                .sigill_fn = sme2p1_sigill,
        },
        {
                .name = "SME 2.2",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SME2P2,
                .cpuinfo = "sme2p2",
                .sigill_fn = sme2p2_sigill,
        },
        {
                .name = "SME AES",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SME_AES,
                .cpuinfo = "smeaes",
                .sigill_fn = sme_aes_sigill,
        },
        {
                .name = "SME I16I32",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME_I16I32,
                .cpuinfo = "smei16i32",
                .sigill_fn = smei16i32_sigill,
        },
        {
                .name = "SME BI32I32",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME_BI32I32,
                .cpuinfo = "smebi32i32",
                .sigill_fn = smebi32i32_sigill,
        },
        {
                .name = "SME B16B16",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME_B16B16,
                .cpuinfo = "smeb16b16",
                .sigill_fn = smeb16b16_sigill,
        },
        {
                .name = "SME F16F16",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME_F16F16,
                .cpuinfo = "smef16f16",
                .sigill_fn = smef16f16_sigill,
        },
        {
                .name = "SME F8F16",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME_F8F16,
                .cpuinfo = "smef8f16",
                .sigill_fn = smef8f16_sigill,
        },
        {
                .name = "SME F8F32",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME_F8F32,
                .cpuinfo = "smef8f32",
                .sigill_fn = smef8f32_sigill,
        },
        {
                .name = "SME LUTV2",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME_LUTV2,
                .cpuinfo = "smelutv2",
                .sigill_fn = smelutv2_sigill,
        },
        {
                .name = "SME SBITPERM",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SME_SBITPERM,
                .cpuinfo = "smesbitperm",
                .sigill_fn = sme_sbitperm_sigill,
        },
        {
                .name = "SME SF8FMA",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME_SF8FMA,
                .cpuinfo = "smesf8fma",
                .sigill_fn = smesf8fma_sigill,
        },
        {
                .name = "SME SF8DP2",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME_SF8DP2,
                .cpuinfo = "smesf8dp2",
                .sigill_fn = smesf8dp2_sigill,
        },
        {
                .name = "SME SF8DP4",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SME_SF8DP4,
                .cpuinfo = "smesf8dp4",
                .sigill_fn = smesf8dp4_sigill,
        },
        {
                .name = "SME SFEXPA",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SME_SFEXPA,
                .cpuinfo = "smesfexpa",
                .sigill_fn = smesfexpa_sigill,
        },
        {
                .name = "SME SMOP4",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SME_SMOP4,
                .cpuinfo = "smesmop4",
                .sigill_fn = smesmop4_sigill,
        },
        {
                .name = "SME STMOP",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SME_STMOP,
                .cpuinfo = "smestmop",
                .sigill_fn = smestmop_sigill,
        },
        {
                .name = "SVE",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SVE,
                .cpuinfo = "sve",
                .sigill_fn = sve_sigill,
                .sigill_reliable = true,
        },
        {
                .name = "SVE 2",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVE2,
                .cpuinfo = "sve2",
                .sigill_fn = sve2_sigill,
        },
        {
                .name = "SVE 2.1",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVE2P1,
                .cpuinfo = "sve2p1",
                .sigill_fn = sve2p1_sigill,
        },
        {
                .name = "SVE 2.2",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SVE2P2,
                .cpuinfo = "sve2p2",
                .sigill_fn = sve2p2_sigill,
        },
        {
                .name = "SVE AES",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVEAES,
                .cpuinfo = "sveaes",
                .sigill_fn = sveaes_sigill,
        },
        {
                .name = "SVE AES2",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SVE_AES2,
                .cpuinfo = "sveaes2",
                .sigill_fn = sveaes2_sigill,
        },
        {
                .name = "SVE BFSCALE",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SVE_BFSCALE,
                .cpuinfo = "svebfscale",
                .sigill_fn = svebfscale_sigill,
        },
        {
                .name = "SVE ELTPERM",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SVE_ELTPERM,
                .cpuinfo = "sveeltperm",
                .sigill_fn = sveeltperm_sigill,
        },
        {
                .name = "SVE F16MM",
                .at_hwcap = AT_HWCAP,
                .hwcap_bit = HWCAP_SVE_F16MM,
                .cpuinfo = "svef16mm",
                .sigill_fn = svef16mm_sigill,
        },
        {
                .name = "SVE2 B16B16",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVE_B16B16,
                .cpuinfo = "sveb16b16",
                .sigill_fn = sveb16b16_sigill,
        },
        {
                .name = "SVE2 PMULL",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVEPMULL,
                .cpuinfo = "svepmull",
                .sigill_fn = svepmull_sigill,
        },
        {
                .name = "SVE2 BITPERM",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVEBITPERM,
                .cpuinfo = "svebitperm",
                .sigill_fn = svebitperm_sigill,
        },
        {
                .name = "SVE2 SHA3",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVESHA3,
                .cpuinfo = "svesha3",
                .sigill_fn = svesha3_sigill,
        },
        {
                .name = "SVE2 SM4",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVESM4,
                .cpuinfo = "svesm4",
                .sigill_fn = svesm4_sigill,
        },
        {
                .name = "SVE2 I8MM",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVEI8MM,
                .cpuinfo = "svei8mm",
                .sigill_fn = svei8mm_sigill,
        },
        {
                .name = "SVE2 F32MM",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVEF32MM,
                .cpuinfo = "svef32mm",
                .sigill_fn = svef32mm_sigill,
        },
        {
                .name = "SVE2 F64MM",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVEF64MM,
                .cpuinfo = "svef64mm",
                .sigill_fn = svef64mm_sigill,
        },
        {
                .name = "SVE2 BF16",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVEBF16,
                .cpuinfo = "svebf16",
                .sigill_fn = svebf16_sigill,
        },
        {
                .name = "SVE2 EBF16",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_SVE_EBF16,
                .cpuinfo = "sveebf16",
        },
        {
                .name = "HBC",
                .at_hwcap = AT_HWCAP2,
                .hwcap_bit = HWCAP2_HBC,
                .cpuinfo = "hbc",
                .sigill_fn = hbc_sigill,
                .sigill_reliable = true,
        },
        {
                .name = "MTE_FAR",
                .at_hwcap = AT_HWCAP3,
                .hwcap_bit = HWCAP3_MTE_FAR,
                .cpuinfo = "mtefar",
        },
        {
                .name = "MTE_STOREONLY",
                .at_hwcap = AT_HWCAP3,
                .hwcap_bit = HWCAP3_MTE_STORE_ONLY,
                .cpuinfo = "mtestoreonly",
        },
        {
                .name = "LS64",
                .at_hwcap = AT_HWCAP3,
                .hwcap_bit = HWCAP3_LS64,
                .cpuinfo = "ls64",
                .sigill_fn = ls64_sigill,
                .sigill_reliable = true,
        },
};

typedef void (*sighandler_fn)(int, siginfo_t *, void *);

#define DEF_SIGHANDLER_FUNC(SIG, NUM)                                   \
static bool seen_##SIG;                                                 \
static void handle_##SIG(int sig, siginfo_t *info, void *context)       \
{                                                                       \
        ucontext_t *uc = context;                                       \
                                                                        \
        seen_##SIG = true;                                              \
        /* Skip over the offending instruction */                       \
        uc->uc_mcontext.pc += 4;                                        \
}

DEF_SIGHANDLER_FUNC(sigill, SIGILL);
DEF_SIGHANDLER_FUNC(sigbus, SIGBUS);

bool cpuinfo_present(const char *name)
{
        FILE *f;
        char buf[2048], name_space[30], name_newline[30];
        char *s;

        /*
         * The feature should appear with a leading space and either a
         * trailing space or a newline.
         */
        snprintf(name_space, sizeof(name_space), " %s ", name);
        snprintf(name_newline, sizeof(name_newline), " %s\n", name);

        f = fopen("/proc/cpuinfo", "r");
        if (!f) {
                ksft_print_msg("Failed to open /proc/cpuinfo\n");
                return false;
        }

        while (fgets(buf, sizeof(buf), f)) {
                /* Features: line? */
                if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0)
                        continue;

                /* All CPUs should be symmetric, don't read any more */
                fclose(f);

                s = strstr(buf, name_space);
                if (s)
                        return true;
                s = strstr(buf, name_newline);
                if (s)
                        return true;

                return false;
        }

        ksft_print_msg("Failed to find Features in /proc/cpuinfo\n");
        fclose(f);
        return false;
}

static int install_sigaction(int signum, sighandler_fn handler)
{
        int ret;
        struct sigaction sa;

        memset(&sa, 0, sizeof(sa));
        sa.sa_sigaction = handler;
        sa.sa_flags = SA_RESTART | SA_SIGINFO;
        sigemptyset(&sa.sa_mask);
        ret = sigaction(signum, &sa, NULL);
        if (ret < 0)
                ksft_exit_fail_msg("Failed to install SIGNAL handler: %s (%d)\n",
                                   strerror(errno), errno);

        return ret;
}

static void uninstall_sigaction(int signum)
{
        if (sigaction(signum, NULL, NULL) < 0)
                ksft_exit_fail_msg("Failed to uninstall SIGNAL handler: %s (%d)\n",
                                   strerror(errno), errno);
}

#define DEF_INST_RAISE_SIG(SIG, NUM)                                    \
static bool inst_raise_##SIG(const struct hwcap_data *hwcap,            \
                                bool have_hwcap)                        \
{                                                                       \
        if (!hwcap->SIG##_fn) {                                         \
                ksft_test_result_skip(#SIG"_%s\n", hwcap->name);        \
                /* assume that it would raise exception in default */   \
                return true;                                            \
        }                                                               \
                                                                        \
        install_sigaction(NUM, handle_##SIG);                           \
                                                                        \
        seen_##SIG = false;                                             \
        hwcap->SIG##_fn();                                              \
                                                                        \
        if (have_hwcap) {                                               \
                /* Should be able to use the extension */               \
                ksft_test_result(!seen_##SIG,                           \
                                #SIG"_%s\n", hwcap->name);              \
        } else if (hwcap->SIG##_reliable) {                             \
                /* Guaranteed a SIGNAL */                               \
                ksft_test_result(seen_##SIG,                            \
                                #SIG"_%s\n", hwcap->name);              \
        } else {                                                        \
                /* Missing SIGNAL might be fine */                      \
                ksft_print_msg(#SIG"_%sreported for %s\n",              \
                                seen_##SIG ? "" : "not ",               \
                                hwcap->name);                           \
                ksft_test_result_skip(#SIG"_%s\n",                      \
                                        hwcap->name);                   \
        }                                                               \
                                                                        \
        uninstall_sigaction(NUM);                                       \
        return seen_##SIG;                                              \
}

DEF_INST_RAISE_SIG(sigill, SIGILL);
DEF_INST_RAISE_SIG(sigbus, SIGBUS);

int main(void)
{
        int i;
        const struct hwcap_data *hwcap;
        bool have_cpuinfo, have_hwcap, raise_sigill;

        ksft_print_header();
        ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP);

        for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
                hwcap = &hwcaps[i];

                have_hwcap = getauxval(hwcap->at_hwcap) & hwcap->hwcap_bit;
                have_cpuinfo = cpuinfo_present(hwcap->cpuinfo);

                if (have_hwcap)
                        ksft_print_msg("%s present\n", hwcap->name);

                ksft_test_result(have_hwcap == have_cpuinfo,
                                 "cpuinfo_match_%s\n", hwcap->name);

                /*
                 * Testing for SIGBUS only makes sense after make sure
                 * that the instruction does not cause a SIGILL signal.
                 */
                raise_sigill = inst_raise_sigill(hwcap, have_hwcap);
                if (!raise_sigill)
                        inst_raise_sigbus(hwcap, have_hwcap);
                else
                        ksft_test_result_skip("sigbus_%s\n", hwcap->name);
        }

        ksft_print_cnts();

        return 0;
}