|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
| 2 | +/* |
| 3 | + * Copyright (C) 2022 ARM Limited. |
| 4 | + */ |
| 5 | + |
| 6 | +#include <errno.h> |
| 7 | +#include <signal.h> |
| 8 | +#include <stdbool.h> |
| 9 | +#include <stddef.h> |
| 10 | +#include <stdio.h> |
| 11 | +#include <stdlib.h> |
| 12 | +#include <string.h> |
| 13 | +#include <unistd.h> |
| 14 | +#include <sys/auxv.h> |
| 15 | +#include <sys/prctl.h> |
| 16 | +#include <asm/hwcap.h> |
| 17 | +#include <asm/sigcontext.h> |
| 18 | +#include <asm/unistd.h> |
| 19 | + |
| 20 | +#include "../../kselftest.h" |
| 21 | + |
| 22 | +#define TESTS_PER_HWCAP 2 |
| 23 | + |
| 24 | +/* |
| 25 | + * Function expected to generate SIGILL when the feature is not |
| 26 | + * supported and return when it is supported. If SIGILL is generated |
| 27 | + * then the handler must be able to skip over the instruction safely. |
| 28 | + * |
| 29 | + * Note that it is expected that for many architecture extensions |
| 30 | + * there are no specific traps due to no architecture state being |
| 31 | + * added so we may not fault if running on a kernel which doesn't know |
| 32 | + * to add the hwcap. |
| 33 | + */ |
| 34 | +typedef void (*sigill_fn)(void); |
| 35 | + |
| 36 | +static void rng_sigill(void) |
| 37 | +{ |
| 38 | + asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0"); |
| 39 | +} |
| 40 | + |
| 41 | +static void sme_sigill(void) |
| 42 | +{ |
| 43 | + /* RDSVL x0, #0 */ |
| 44 | + asm volatile(".inst 0x04bf5800" : : : "x0"); |
| 45 | +} |
| 46 | + |
| 47 | +static void sve_sigill(void) |
| 48 | +{ |
| 49 | + /* RDVL x0, #0 */ |
| 50 | + asm volatile(".inst 0x04bf5000" : : : "x0"); |
| 51 | +} |
| 52 | + |
| 53 | +static void sve2_sigill(void) |
| 54 | +{ |
| 55 | + /* SQABS Z0.b, P0/M, Z0.B */ |
| 56 | + asm volatile(".inst 0x4408A000" : : : "z0"); |
| 57 | +} |
| 58 | + |
| 59 | +static void sveaes_sigill(void) |
| 60 | +{ |
| 61 | + /* AESD z0.b, z0.b, z0.b */ |
| 62 | + asm volatile(".inst 0x4522e400" : : : "z0"); |
| 63 | +} |
| 64 | + |
| 65 | +static void svepmull_sigill(void) |
| 66 | +{ |
| 67 | + /* PMULLB Z0.Q, Z0.D, Z0.D */ |
| 68 | + asm volatile(".inst 0x45006800" : : : "z0"); |
| 69 | +} |
| 70 | + |
| 71 | +static void svebitperm_sigill(void) |
| 72 | +{ |
| 73 | + /* BDEP Z0.B, Z0.B, Z0.B */ |
| 74 | + asm volatile(".inst 0x4500b400" : : : "z0"); |
| 75 | +} |
| 76 | + |
| 77 | +static void svesha3_sigill(void) |
| 78 | +{ |
| 79 | + /* EOR3 Z0.D, Z0.D, Z0.D, Z0.D */ |
| 80 | + asm volatile(".inst 0x4203800" : : : "z0"); |
| 81 | +} |
| 82 | + |
| 83 | +static void svesm4_sigill(void) |
| 84 | +{ |
| 85 | + /* SM4E Z0.S, Z0.S, Z0.S */ |
| 86 | + asm volatile(".inst 0x4523e000" : : : "z0"); |
| 87 | +} |
| 88 | + |
| 89 | +static void svei8mm_sigill(void) |
| 90 | +{ |
| 91 | + /* USDOT Z0.S, Z0.B, Z0.B[0] */ |
| 92 | + asm volatile(".inst 0x44a01800" : : : "z0"); |
| 93 | +} |
| 94 | + |
| 95 | +static void svef32mm_sigill(void) |
| 96 | +{ |
| 97 | + /* FMMLA Z0.S, Z0.S, Z0.S */ |
| 98 | + asm volatile(".inst 0x64a0e400" : : : "z0"); |
| 99 | +} |
| 100 | + |
| 101 | +static void svef64mm_sigill(void) |
| 102 | +{ |
| 103 | + /* FMMLA Z0.D, Z0.D, Z0.D */ |
| 104 | + asm volatile(".inst 0x64e0e400" : : : "z0"); |
| 105 | +} |
| 106 | + |
| 107 | +static void svebf16_sigill(void) |
| 108 | +{ |
| 109 | + /* BFCVT Z0.H, P0/M, Z0.S */ |
| 110 | + asm volatile(".inst 0x658aa000" : : : "z0"); |
| 111 | +} |
| 112 | + |
| 113 | +static const struct hwcap_data { |
| 114 | + const char *name; |
| 115 | + unsigned long at_hwcap; |
| 116 | + unsigned long hwcap_bit; |
| 117 | + const char *cpuinfo; |
| 118 | + sigill_fn sigill_fn; |
| 119 | + bool sigill_reliable; |
| 120 | +} hwcaps[] = { |
| 121 | + { |
| 122 | + .name = "RNG", |
| 123 | + .at_hwcap = AT_HWCAP2, |
| 124 | + .hwcap_bit = HWCAP2_RNG, |
| 125 | + .cpuinfo = "rng", |
| 126 | + .sigill_fn = rng_sigill, |
| 127 | + }, |
| 128 | + { |
| 129 | + .name = "SME", |
| 130 | + .at_hwcap = AT_HWCAP2, |
| 131 | + .hwcap_bit = HWCAP2_SME, |
| 132 | + .cpuinfo = "sme", |
| 133 | + .sigill_fn = sme_sigill, |
| 134 | + .sigill_reliable = true, |
| 135 | + }, |
| 136 | + { |
| 137 | + .name = "SVE", |
| 138 | + .at_hwcap = AT_HWCAP, |
| 139 | + .hwcap_bit = HWCAP_SVE, |
| 140 | + .cpuinfo = "sve", |
| 141 | + .sigill_fn = sve_sigill, |
| 142 | + .sigill_reliable = true, |
| 143 | + }, |
| 144 | + { |
| 145 | + .name = "SVE 2", |
| 146 | + .at_hwcap = AT_HWCAP2, |
| 147 | + .hwcap_bit = HWCAP2_SVE2, |
| 148 | + .cpuinfo = "sve2", |
| 149 | + .sigill_fn = sve2_sigill, |
| 150 | + }, |
| 151 | + { |
| 152 | + .name = "SVE AES", |
| 153 | + .at_hwcap = AT_HWCAP2, |
| 154 | + .hwcap_bit = HWCAP2_SVEAES, |
| 155 | + .cpuinfo = "sveaes", |
| 156 | + .sigill_fn = sveaes_sigill, |
| 157 | + }, |
| 158 | + { |
| 159 | + .name = "SVE2 PMULL", |
| 160 | + .at_hwcap = AT_HWCAP2, |
| 161 | + .hwcap_bit = HWCAP2_SVEPMULL, |
| 162 | + .cpuinfo = "svepmull", |
| 163 | + .sigill_fn = svepmull_sigill, |
| 164 | + }, |
| 165 | + { |
| 166 | + .name = "SVE2 BITPERM", |
| 167 | + .at_hwcap = AT_HWCAP2, |
| 168 | + .hwcap_bit = HWCAP2_SVEBITPERM, |
| 169 | + .cpuinfo = "svebitperm", |
| 170 | + .sigill_fn = svebitperm_sigill, |
| 171 | + }, |
| 172 | + { |
| 173 | + .name = "SVE2 SHA3", |
| 174 | + .at_hwcap = AT_HWCAP2, |
| 175 | + .hwcap_bit = HWCAP2_SVESHA3, |
| 176 | + .cpuinfo = "svesha3", |
| 177 | + .sigill_fn = svesha3_sigill, |
| 178 | + }, |
| 179 | + { |
| 180 | + .name = "SVE2 SM4", |
| 181 | + .at_hwcap = AT_HWCAP2, |
| 182 | + .hwcap_bit = HWCAP2_SVESM4, |
| 183 | + .cpuinfo = "svesm4", |
| 184 | + .sigill_fn = svesm4_sigill, |
| 185 | + }, |
| 186 | + { |
| 187 | + .name = "SVE2 I8MM", |
| 188 | + .at_hwcap = AT_HWCAP2, |
| 189 | + .hwcap_bit = HWCAP2_SVEI8MM, |
| 190 | + .cpuinfo = "svei8mm", |
| 191 | + .sigill_fn = svei8mm_sigill, |
| 192 | + }, |
| 193 | + { |
| 194 | + .name = "SVE2 F32MM", |
| 195 | + .at_hwcap = AT_HWCAP2, |
| 196 | + .hwcap_bit = HWCAP2_SVEF32MM, |
| 197 | + .cpuinfo = "svef32mm", |
| 198 | + .sigill_fn = svef32mm_sigill, |
| 199 | + }, |
| 200 | + { |
| 201 | + .name = "SVE2 F64MM", |
| 202 | + .at_hwcap = AT_HWCAP2, |
| 203 | + .hwcap_bit = HWCAP2_SVEF64MM, |
| 204 | + .cpuinfo = "svef64mm", |
| 205 | + .sigill_fn = svef64mm_sigill, |
| 206 | + }, |
| 207 | + { |
| 208 | + .name = "SVE2 BF16", |
| 209 | + .at_hwcap = AT_HWCAP2, |
| 210 | + .hwcap_bit = HWCAP2_SVEBF16, |
| 211 | + .cpuinfo = "svebf16", |
| 212 | + .sigill_fn = svebf16_sigill, |
| 213 | + }, |
| 214 | + { |
| 215 | + .name = "SVE2 EBF16", |
| 216 | + .at_hwcap = AT_HWCAP2, |
| 217 | + .hwcap_bit = HWCAP2_SVE_EBF16, |
| 218 | + .cpuinfo = "sveebf16", |
| 219 | + }, |
| 220 | +}; |
| 221 | + |
| 222 | +static bool seen_sigill; |
| 223 | + |
| 224 | +static void handle_sigill(int sig, siginfo_t *info, void *context) |
| 225 | +{ |
| 226 | + ucontext_t *uc = context; |
| 227 | + |
| 228 | + seen_sigill = true; |
| 229 | + |
| 230 | + /* Skip over the offending instruction */ |
| 231 | + uc->uc_mcontext.pc += 4; |
| 232 | +} |
| 233 | + |
| 234 | +bool cpuinfo_present(const char *name) |
| 235 | +{ |
| 236 | + FILE *f; |
| 237 | + char buf[2048], name_space[30], name_newline[30]; |
| 238 | + char *s; |
| 239 | + |
| 240 | + /* |
| 241 | + * The feature should appear with a leading space and either a |
| 242 | + * trailing space or a newline. |
| 243 | + */ |
| 244 | + snprintf(name_space, sizeof(name_space), " %s ", name); |
| 245 | + snprintf(name_newline, sizeof(name_newline), " %s\n", name); |
| 246 | + |
| 247 | + f = fopen("/proc/cpuinfo", "r"); |
| 248 | + if (!f) { |
| 249 | + ksft_print_msg("Failed to open /proc/cpuinfo\n"); |
| 250 | + return false; |
| 251 | + } |
| 252 | + |
| 253 | + while (fgets(buf, sizeof(buf), f)) { |
| 254 | + /* Features: line? */ |
| 255 | + if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0) |
| 256 | + continue; |
| 257 | + |
| 258 | + /* All CPUs should be symmetric, don't read any more */ |
| 259 | + fclose(f); |
| 260 | + |
| 261 | + s = strstr(buf, name_space); |
| 262 | + if (s) |
| 263 | + return true; |
| 264 | + s = strstr(buf, name_newline); |
| 265 | + if (s) |
| 266 | + return true; |
| 267 | + |
| 268 | + return false; |
| 269 | + } |
| 270 | + |
| 271 | + ksft_print_msg("Failed to find Features in /proc/cpuinfo\n"); |
| 272 | + fclose(f); |
| 273 | + return false; |
| 274 | +} |
| 275 | + |
| 276 | +int main(void) |
| 277 | +{ |
| 278 | + const struct hwcap_data *hwcap; |
| 279 | + int i, ret; |
| 280 | + bool have_cpuinfo, have_hwcap; |
| 281 | + struct sigaction sa; |
| 282 | + |
| 283 | + ksft_print_header(); |
| 284 | + ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP); |
| 285 | + |
| 286 | + memset(&sa, 0, sizeof(sa)); |
| 287 | + sa.sa_sigaction = handle_sigill; |
| 288 | + sa.sa_flags = SA_RESTART | SA_SIGINFO; |
| 289 | + sigemptyset(&sa.sa_mask); |
| 290 | + ret = sigaction(SIGILL, &sa, NULL); |
| 291 | + if (ret < 0) |
| 292 | + ksft_exit_fail_msg("Failed to install SIGILL handler: %s (%d)\n", |
| 293 | + strerror(errno), errno); |
| 294 | + |
| 295 | + for (i = 0; i < ARRAY_SIZE(hwcaps); i++) { |
| 296 | + hwcap = &hwcaps[i]; |
| 297 | + |
| 298 | + have_hwcap = getauxval(hwcap->at_hwcap) & hwcap->hwcap_bit; |
| 299 | + have_cpuinfo = cpuinfo_present(hwcap->cpuinfo); |
| 300 | + |
| 301 | + if (have_hwcap) |
| 302 | + ksft_print_msg("%s present\n", hwcap->name); |
| 303 | + |
| 304 | + ksft_test_result(have_hwcap == have_cpuinfo, |
| 305 | + "cpuinfo_match_%s\n", hwcap->name); |
| 306 | + |
| 307 | + if (hwcap->sigill_fn) { |
| 308 | + seen_sigill = false; |
| 309 | + hwcap->sigill_fn(); |
| 310 | + |
| 311 | + if (have_hwcap) { |
| 312 | + /* Should be able to use the extension */ |
| 313 | + ksft_test_result(!seen_sigill, "sigill_%s\n", |
| 314 | + hwcap->name); |
| 315 | + } else if (hwcap->sigill_reliable) { |
| 316 | + /* Guaranteed a SIGILL */ |
| 317 | + ksft_test_result(seen_sigill, "sigill_%s\n", |
| 318 | + hwcap->name); |
| 319 | + } else { |
| 320 | + /* Missing SIGILL might be fine */ |
| 321 | + ksft_print_msg("SIGILL %sreported for %s\n", |
| 322 | + seen_sigill ? "" : "not ", |
| 323 | + hwcap->name); |
| 324 | + ksft_test_result_skip("sigill_%s\n", |
| 325 | + hwcap->name); |
| 326 | + } |
| 327 | + } else { |
| 328 | + ksft_test_result_skip("sigill_%s\n", |
| 329 | + hwcap->name); |
| 330 | + } |
| 331 | + } |
| 332 | + |
| 333 | + ksft_print_cnts(); |
| 334 | + |
| 335 | + return 0; |
| 336 | +} |
0 commit comments