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 227a9bcaaaSMark Brown #define TESTS_PER_HWCAP 2 237a9bcaaaSMark Brown 247a9bcaaaSMark Brown /* 257a9bcaaaSMark Brown * Function expected to generate SIGILL when the feature is not 267a9bcaaaSMark Brown * supported and return when it is supported. If SIGILL is generated 277a9bcaaaSMark Brown * then the handler must be able to skip over the instruction safely. 287a9bcaaaSMark Brown * 297a9bcaaaSMark Brown * Note that it is expected that for many architecture extensions 307a9bcaaaSMark Brown * there are no specific traps due to no architecture state being 317a9bcaaaSMark Brown * added so we may not fault if running on a kernel which doesn't know 327a9bcaaaSMark Brown * to add the hwcap. 337a9bcaaaSMark Brown */ 347a9bcaaaSMark Brown typedef void (*sigill_fn)(void); 357a9bcaaaSMark Brown 36b0ab73a5SMark Brown static void cssc_sigill(void) 37b0ab73a5SMark Brown { 38b0ab73a5SMark Brown /* CNT x0, x0 */ 39b0ab73a5SMark Brown asm volatile(".inst 0xdac01c00" : : : "x0"); 40b0ab73a5SMark Brown } 41b0ab73a5SMark Brown 42ef939f30SMark Brown static void rng_sigill(void) 43ef939f30SMark Brown { 44ef939f30SMark Brown asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0"); 45ef939f30SMark Brown } 46ef939f30SMark Brown 477a9bcaaaSMark Brown static void sme_sigill(void) 487a9bcaaaSMark Brown { 497a9bcaaaSMark Brown /* RDSVL x0, #0 */ 507a9bcaaaSMark Brown asm volatile(".inst 0x04bf5800" : : : "x0"); 517a9bcaaaSMark Brown } 527a9bcaaaSMark Brown 537a9bcaaaSMark Brown static void sve_sigill(void) 547a9bcaaaSMark Brown { 557a9bcaaaSMark Brown /* RDVL x0, #0 */ 567a9bcaaaSMark Brown asm volatile(".inst 0x04bf5000" : : : "x0"); 577a9bcaaaSMark Brown } 587a9bcaaaSMark Brown 59859a9d51SMark Brown static void sve2_sigill(void) 60859a9d51SMark Brown { 61859a9d51SMark Brown /* SQABS Z0.b, P0/M, Z0.B */ 62859a9d51SMark Brown asm volatile(".inst 0x4408A000" : : : "z0"); 63859a9d51SMark Brown } 64859a9d51SMark Brown 65859a9d51SMark Brown static void sveaes_sigill(void) 66859a9d51SMark Brown { 67859a9d51SMark Brown /* AESD z0.b, z0.b, z0.b */ 68859a9d51SMark Brown asm volatile(".inst 0x4522e400" : : : "z0"); 69859a9d51SMark Brown } 70859a9d51SMark Brown 71859a9d51SMark Brown static void svepmull_sigill(void) 72859a9d51SMark Brown { 73859a9d51SMark Brown /* PMULLB Z0.Q, Z0.D, Z0.D */ 74859a9d51SMark Brown asm volatile(".inst 0x45006800" : : : "z0"); 75859a9d51SMark Brown } 76859a9d51SMark Brown 77859a9d51SMark Brown static void svebitperm_sigill(void) 78859a9d51SMark Brown { 79859a9d51SMark Brown /* BDEP Z0.B, Z0.B, Z0.B */ 80859a9d51SMark Brown asm volatile(".inst 0x4500b400" : : : "z0"); 81859a9d51SMark Brown } 82859a9d51SMark Brown 83859a9d51SMark Brown static void svesha3_sigill(void) 84859a9d51SMark Brown { 85859a9d51SMark Brown /* EOR3 Z0.D, Z0.D, Z0.D, Z0.D */ 86859a9d51SMark Brown asm volatile(".inst 0x4203800" : : : "z0"); 87859a9d51SMark Brown } 88859a9d51SMark Brown 89859a9d51SMark Brown static void svesm4_sigill(void) 90859a9d51SMark Brown { 91859a9d51SMark Brown /* SM4E Z0.S, Z0.S, Z0.S */ 92859a9d51SMark Brown asm volatile(".inst 0x4523e000" : : : "z0"); 93859a9d51SMark Brown } 94859a9d51SMark Brown 95859a9d51SMark Brown static void svei8mm_sigill(void) 96859a9d51SMark Brown { 97859a9d51SMark Brown /* USDOT Z0.S, Z0.B, Z0.B[0] */ 98859a9d51SMark Brown asm volatile(".inst 0x44a01800" : : : "z0"); 99859a9d51SMark Brown } 100859a9d51SMark Brown 101859a9d51SMark Brown static void svef32mm_sigill(void) 102859a9d51SMark Brown { 103859a9d51SMark Brown /* FMMLA Z0.S, Z0.S, Z0.S */ 104859a9d51SMark Brown asm volatile(".inst 0x64a0e400" : : : "z0"); 105859a9d51SMark Brown } 106859a9d51SMark Brown 107859a9d51SMark Brown static void svef64mm_sigill(void) 108859a9d51SMark Brown { 109859a9d51SMark Brown /* FMMLA Z0.D, Z0.D, Z0.D */ 110859a9d51SMark Brown asm volatile(".inst 0x64e0e400" : : : "z0"); 111859a9d51SMark Brown } 112859a9d51SMark Brown 113859a9d51SMark Brown static void svebf16_sigill(void) 114859a9d51SMark Brown { 115859a9d51SMark Brown /* BFCVT Z0.H, P0/M, Z0.S */ 116859a9d51SMark Brown asm volatile(".inst 0x658aa000" : : : "z0"); 117859a9d51SMark Brown } 118859a9d51SMark Brown 1197a9bcaaaSMark Brown static const struct hwcap_data { 1207a9bcaaaSMark Brown const char *name; 1217a9bcaaaSMark Brown unsigned long at_hwcap; 1227a9bcaaaSMark Brown unsigned long hwcap_bit; 1237a9bcaaaSMark Brown const char *cpuinfo; 1247a9bcaaaSMark Brown sigill_fn sigill_fn; 1257a9bcaaaSMark Brown bool sigill_reliable; 1267a9bcaaaSMark Brown } hwcaps[] = { 1277a9bcaaaSMark Brown { 128b0ab73a5SMark Brown .name = "CSSC", 129b0ab73a5SMark Brown .at_hwcap = AT_HWCAP2, 130b0ab73a5SMark Brown .hwcap_bit = HWCAP2_CSSC, 131b0ab73a5SMark Brown .cpuinfo = "cssc", 132b0ab73a5SMark Brown .sigill_fn = cssc_sigill, 133b0ab73a5SMark Brown }, 134b0ab73a5SMark Brown { 135ef939f30SMark Brown .name = "RNG", 136ef939f30SMark Brown .at_hwcap = AT_HWCAP2, 137ef939f30SMark Brown .hwcap_bit = HWCAP2_RNG, 138ef939f30SMark Brown .cpuinfo = "rng", 139ef939f30SMark Brown .sigill_fn = rng_sigill, 140ef939f30SMark Brown }, 141ef939f30SMark Brown { 142*989d37fcSMark Brown .name = "RPRFM", 143*989d37fcSMark Brown .at_hwcap = AT_HWCAP2, 144*989d37fcSMark Brown .hwcap_bit = HWCAP2_RPRFM, 145*989d37fcSMark Brown .cpuinfo = "rprfm", 146*989d37fcSMark Brown }, 147*989d37fcSMark Brown { 1487a9bcaaaSMark Brown .name = "SME", 1497a9bcaaaSMark Brown .at_hwcap = AT_HWCAP2, 1507a9bcaaaSMark Brown .hwcap_bit = HWCAP2_SME, 1517a9bcaaaSMark Brown .cpuinfo = "sme", 1527a9bcaaaSMark Brown .sigill_fn = sme_sigill, 1537a9bcaaaSMark Brown .sigill_reliable = true, 1547a9bcaaaSMark Brown }, 1557a9bcaaaSMark Brown { 1567a9bcaaaSMark Brown .name = "SVE", 1577a9bcaaaSMark Brown .at_hwcap = AT_HWCAP, 1587a9bcaaaSMark Brown .hwcap_bit = HWCAP_SVE, 1597a9bcaaaSMark Brown .cpuinfo = "sve", 1607a9bcaaaSMark Brown .sigill_fn = sve_sigill, 1617a9bcaaaSMark Brown .sigill_reliable = true, 1627a9bcaaaSMark Brown }, 163859a9d51SMark Brown { 164859a9d51SMark Brown .name = "SVE 2", 165859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 166859a9d51SMark Brown .hwcap_bit = HWCAP2_SVE2, 167859a9d51SMark Brown .cpuinfo = "sve2", 168859a9d51SMark Brown .sigill_fn = sve2_sigill, 169859a9d51SMark Brown }, 170859a9d51SMark Brown { 171859a9d51SMark Brown .name = "SVE AES", 172859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 173859a9d51SMark Brown .hwcap_bit = HWCAP2_SVEAES, 174859a9d51SMark Brown .cpuinfo = "sveaes", 175859a9d51SMark Brown .sigill_fn = sveaes_sigill, 176859a9d51SMark Brown }, 177859a9d51SMark Brown { 178859a9d51SMark Brown .name = "SVE2 PMULL", 179859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 180859a9d51SMark Brown .hwcap_bit = HWCAP2_SVEPMULL, 181859a9d51SMark Brown .cpuinfo = "svepmull", 182859a9d51SMark Brown .sigill_fn = svepmull_sigill, 183859a9d51SMark Brown }, 184859a9d51SMark Brown { 185859a9d51SMark Brown .name = "SVE2 BITPERM", 186859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 187859a9d51SMark Brown .hwcap_bit = HWCAP2_SVEBITPERM, 188859a9d51SMark Brown .cpuinfo = "svebitperm", 189859a9d51SMark Brown .sigill_fn = svebitperm_sigill, 190859a9d51SMark Brown }, 191859a9d51SMark Brown { 192859a9d51SMark Brown .name = "SVE2 SHA3", 193859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 194859a9d51SMark Brown .hwcap_bit = HWCAP2_SVESHA3, 195859a9d51SMark Brown .cpuinfo = "svesha3", 196859a9d51SMark Brown .sigill_fn = svesha3_sigill, 197859a9d51SMark Brown }, 198859a9d51SMark Brown { 199859a9d51SMark Brown .name = "SVE2 SM4", 200859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 201859a9d51SMark Brown .hwcap_bit = HWCAP2_SVESM4, 202859a9d51SMark Brown .cpuinfo = "svesm4", 203859a9d51SMark Brown .sigill_fn = svesm4_sigill, 204859a9d51SMark Brown }, 205859a9d51SMark Brown { 206859a9d51SMark Brown .name = "SVE2 I8MM", 207859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 208859a9d51SMark Brown .hwcap_bit = HWCAP2_SVEI8MM, 209859a9d51SMark Brown .cpuinfo = "svei8mm", 210859a9d51SMark Brown .sigill_fn = svei8mm_sigill, 211859a9d51SMark Brown }, 212859a9d51SMark Brown { 213859a9d51SMark Brown .name = "SVE2 F32MM", 214859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 215859a9d51SMark Brown .hwcap_bit = HWCAP2_SVEF32MM, 216859a9d51SMark Brown .cpuinfo = "svef32mm", 217859a9d51SMark Brown .sigill_fn = svef32mm_sigill, 218859a9d51SMark Brown }, 219859a9d51SMark Brown { 220859a9d51SMark Brown .name = "SVE2 F64MM", 221859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 222859a9d51SMark Brown .hwcap_bit = HWCAP2_SVEF64MM, 223859a9d51SMark Brown .cpuinfo = "svef64mm", 224859a9d51SMark Brown .sigill_fn = svef64mm_sigill, 225859a9d51SMark Brown }, 226859a9d51SMark Brown { 227859a9d51SMark Brown .name = "SVE2 BF16", 228859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 229859a9d51SMark Brown .hwcap_bit = HWCAP2_SVEBF16, 230859a9d51SMark Brown .cpuinfo = "svebf16", 231859a9d51SMark Brown .sigill_fn = svebf16_sigill, 232859a9d51SMark Brown }, 233859a9d51SMark Brown { 234859a9d51SMark Brown .name = "SVE2 EBF16", 235859a9d51SMark Brown .at_hwcap = AT_HWCAP2, 236859a9d51SMark Brown .hwcap_bit = HWCAP2_SVE_EBF16, 237859a9d51SMark Brown .cpuinfo = "sveebf16", 238859a9d51SMark Brown }, 2397a9bcaaaSMark Brown }; 2407a9bcaaaSMark Brown 2417a9bcaaaSMark Brown static bool seen_sigill; 2427a9bcaaaSMark Brown 2437a9bcaaaSMark Brown static void handle_sigill(int sig, siginfo_t *info, void *context) 2447a9bcaaaSMark Brown { 2457a9bcaaaSMark Brown ucontext_t *uc = context; 2467a9bcaaaSMark Brown 2477a9bcaaaSMark Brown seen_sigill = true; 2487a9bcaaaSMark Brown 2497a9bcaaaSMark Brown /* Skip over the offending instruction */ 2507a9bcaaaSMark Brown uc->uc_mcontext.pc += 4; 2517a9bcaaaSMark Brown } 2527a9bcaaaSMark Brown 2537a9bcaaaSMark Brown bool cpuinfo_present(const char *name) 2547a9bcaaaSMark Brown { 2557a9bcaaaSMark Brown FILE *f; 2567a9bcaaaSMark Brown char buf[2048], name_space[30], name_newline[30]; 2577a9bcaaaSMark Brown char *s; 2587a9bcaaaSMark Brown 2597a9bcaaaSMark Brown /* 2607a9bcaaaSMark Brown * The feature should appear with a leading space and either a 2617a9bcaaaSMark Brown * trailing space or a newline. 2627a9bcaaaSMark Brown */ 2637a9bcaaaSMark Brown snprintf(name_space, sizeof(name_space), " %s ", name); 2647a9bcaaaSMark Brown snprintf(name_newline, sizeof(name_newline), " %s\n", name); 2657a9bcaaaSMark Brown 2667a9bcaaaSMark Brown f = fopen("/proc/cpuinfo", "r"); 2677a9bcaaaSMark Brown if (!f) { 2687a9bcaaaSMark Brown ksft_print_msg("Failed to open /proc/cpuinfo\n"); 2697a9bcaaaSMark Brown return false; 2707a9bcaaaSMark Brown } 2717a9bcaaaSMark Brown 2727a9bcaaaSMark Brown while (fgets(buf, sizeof(buf), f)) { 2737a9bcaaaSMark Brown /* Features: line? */ 2747a9bcaaaSMark Brown if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0) 2757a9bcaaaSMark Brown continue; 2767a9bcaaaSMark Brown 2777a9bcaaaSMark Brown /* All CPUs should be symmetric, don't read any more */ 2787a9bcaaaSMark Brown fclose(f); 2797a9bcaaaSMark Brown 2807a9bcaaaSMark Brown s = strstr(buf, name_space); 2817a9bcaaaSMark Brown if (s) 2827a9bcaaaSMark Brown return true; 2837a9bcaaaSMark Brown s = strstr(buf, name_newline); 2847a9bcaaaSMark Brown if (s) 2857a9bcaaaSMark Brown return true; 2867a9bcaaaSMark Brown 2877a9bcaaaSMark Brown return false; 2887a9bcaaaSMark Brown } 2897a9bcaaaSMark Brown 2907a9bcaaaSMark Brown ksft_print_msg("Failed to find Features in /proc/cpuinfo\n"); 2917a9bcaaaSMark Brown fclose(f); 2927a9bcaaaSMark Brown return false; 2937a9bcaaaSMark Brown } 2947a9bcaaaSMark Brown 2957a9bcaaaSMark Brown int main(void) 2967a9bcaaaSMark Brown { 2977a9bcaaaSMark Brown const struct hwcap_data *hwcap; 2987a9bcaaaSMark Brown int i, ret; 2997a9bcaaaSMark Brown bool have_cpuinfo, have_hwcap; 3007a9bcaaaSMark Brown struct sigaction sa; 3017a9bcaaaSMark Brown 3027a9bcaaaSMark Brown ksft_print_header(); 3037a9bcaaaSMark Brown ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP); 3047a9bcaaaSMark Brown 3057a9bcaaaSMark Brown memset(&sa, 0, sizeof(sa)); 3067a9bcaaaSMark Brown sa.sa_sigaction = handle_sigill; 3077a9bcaaaSMark Brown sa.sa_flags = SA_RESTART | SA_SIGINFO; 3087a9bcaaaSMark Brown sigemptyset(&sa.sa_mask); 3097a9bcaaaSMark Brown ret = sigaction(SIGILL, &sa, NULL); 3107a9bcaaaSMark Brown if (ret < 0) 3117a9bcaaaSMark Brown ksft_exit_fail_msg("Failed to install SIGILL handler: %s (%d)\n", 3127a9bcaaaSMark Brown strerror(errno), errno); 3137a9bcaaaSMark Brown 3147a9bcaaaSMark Brown for (i = 0; i < ARRAY_SIZE(hwcaps); i++) { 3157a9bcaaaSMark Brown hwcap = &hwcaps[i]; 3167a9bcaaaSMark Brown 31733060a64SMark Brown have_hwcap = getauxval(hwcap->at_hwcap) & hwcap->hwcap_bit; 3187a9bcaaaSMark Brown have_cpuinfo = cpuinfo_present(hwcap->cpuinfo); 3197a9bcaaaSMark Brown 3207a9bcaaaSMark Brown if (have_hwcap) 32178d2b197SMark Brown ksft_print_msg("%s present\n", hwcap->name); 3227a9bcaaaSMark Brown 3237a9bcaaaSMark Brown ksft_test_result(have_hwcap == have_cpuinfo, 3247a9bcaaaSMark Brown "cpuinfo_match_%s\n", hwcap->name); 3257a9bcaaaSMark Brown 3267a9bcaaaSMark Brown if (hwcap->sigill_fn) { 3277a9bcaaaSMark Brown seen_sigill = false; 3287a9bcaaaSMark Brown hwcap->sigill_fn(); 3297a9bcaaaSMark Brown 3307a9bcaaaSMark Brown if (have_hwcap) { 3317a9bcaaaSMark Brown /* Should be able to use the extension */ 3327a9bcaaaSMark Brown ksft_test_result(!seen_sigill, "sigill_%s\n", 3337a9bcaaaSMark Brown hwcap->name); 3347a9bcaaaSMark Brown } else if (hwcap->sigill_reliable) { 3357a9bcaaaSMark Brown /* Guaranteed a SIGILL */ 3367a9bcaaaSMark Brown ksft_test_result(seen_sigill, "sigill_%s\n", 3377a9bcaaaSMark Brown hwcap->name); 3387a9bcaaaSMark Brown } else { 3397a9bcaaaSMark Brown /* Missing SIGILL might be fine */ 3407a9bcaaaSMark Brown ksft_print_msg("SIGILL %sreported for %s\n", 3417a9bcaaaSMark Brown seen_sigill ? "" : "not ", 3427a9bcaaaSMark Brown hwcap->name); 3437a9bcaaaSMark Brown ksft_test_result_skip("sigill_%s\n", 3447a9bcaaaSMark Brown hwcap->name); 3457a9bcaaaSMark Brown } 3467a9bcaaaSMark Brown } else { 3477a9bcaaaSMark Brown ksft_test_result_skip("sigill_%s\n", 3487a9bcaaaSMark Brown hwcap->name); 3497a9bcaaaSMark Brown } 3507a9bcaaaSMark Brown } 3517a9bcaaaSMark Brown 3527a9bcaaaSMark Brown ksft_print_cnts(); 3537a9bcaaaSMark Brown 3547a9bcaaaSMark Brown return 0; 3557a9bcaaaSMark Brown } 356