123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- // 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 <sys/auxv.h>
- #include <sys/prctl.h>
- #include <asm/hwcap.h>
- #include <asm/sigcontext.h>
- #include <asm/unistd.h>
- #include "../../kselftest.h"
- #define TESTS_PER_HWCAP 2
- /*
- * Function expected to generate SIGILL when the feature is not
- * supported and return when it is supported. If SIGILL 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 (*sigill_fn)(void);
- static void rng_sigill(void)
- {
- asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0");
- }
- static void sme_sigill(void)
- {
- /* RDSVL x0, #0 */
- asm volatile(".inst 0x04bf5800" : : : "x0");
- }
- 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 sveaes_sigill(void)
- {
- /* AESD z0.b, z0.b, z0.b */
- asm volatile(".inst 0x4522e400" : : : "z0");
- }
- 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 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 const struct hwcap_data {
- const char *name;
- unsigned long at_hwcap;
- unsigned long hwcap_bit;
- const char *cpuinfo;
- sigill_fn sigill_fn;
- bool sigill_reliable;
- } hwcaps[] = {
- {
- .name = "RNG",
- .at_hwcap = AT_HWCAP2,
- .hwcap_bit = HWCAP2_RNG,
- .cpuinfo = "rng",
- .sigill_fn = rng_sigill,
- },
- {
- .name = "SME",
- .at_hwcap = AT_HWCAP2,
- .hwcap_bit = HWCAP2_SME,
- .cpuinfo = "sme",
- .sigill_fn = sme_sigill,
- .sigill_reliable = true,
- },
- {
- .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 AES",
- .at_hwcap = AT_HWCAP2,
- .hwcap_bit = HWCAP2_SVEAES,
- .cpuinfo = "sveaes",
- .sigill_fn = sveaes_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",
- },
- };
- static bool seen_sigill;
- static void handle_sigill(int sig, siginfo_t *info, void *context)
- {
- ucontext_t *uc = context;
- seen_sigill = true;
- /* Skip over the offending instruction */
- uc->uc_mcontext.pc += 4;
- }
- 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;
- }
- int main(void)
- {
- const struct hwcap_data *hwcap;
- int i, ret;
- bool have_cpuinfo, have_hwcap;
- struct sigaction sa;
- ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP);
- memset(&sa, 0, sizeof(sa));
- sa.sa_sigaction = handle_sigill;
- sa.sa_flags = SA_RESTART | SA_SIGINFO;
- sigemptyset(&sa.sa_mask);
- ret = sigaction(SIGILL, &sa, NULL);
- if (ret < 0)
- ksft_exit_fail_msg("Failed to install SIGILL handler: %s (%d)\n",
- strerror(errno), errno);
- 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);
- if (hwcap->sigill_fn) {
- seen_sigill = false;
- hwcap->sigill_fn();
- if (have_hwcap) {
- /* Should be able to use the extension */
- ksft_test_result(!seen_sigill, "sigill_%s\n",
- hwcap->name);
- } else if (hwcap->sigill_reliable) {
- /* Guaranteed a SIGILL */
- ksft_test_result(seen_sigill, "sigill_%s\n",
- hwcap->name);
- } else {
- /* Missing SIGILL might be fine */
- ksft_print_msg("SIGILL %sreported for %s\n",
- seen_sigill ? "" : "not ",
- hwcap->name);
- ksft_test_result_skip("sigill_%s\n",
- hwcap->name);
- }
- } else {
- ksft_test_result_skip("sigill_%s\n",
- hwcap->name);
- }
- }
- ksft_print_cnts();
- return 0;
- }
|