1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2024 ARM Limited. 4 */ 5 6 #define _GNU_SOURCE 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <stdbool.h> 11 #include <errno.h> 12 #include <fcntl.h> 13 #include <signal.h> 14 #include <string.h> 15 #include <unistd.h> 16 17 #include <sys/socket.h> 18 19 #include <linux/kernel.h> 20 #include <linux/if_alg.h> 21 22 #define DATA_SIZE (16 * 4096) 23 24 static int base, sock; 25 26 static int digest_len; 27 static char *ref; 28 static char *digest; 29 static char *alg_name; 30 31 static struct iovec data_iov; 32 static int zerocopy[2]; 33 static int sigs; 34 static int iter; 35 36 static void handle_exit_signal(int sig, siginfo_t *info, void *context) 37 { 38 printf("Terminated by signal %d, iterations=%d, signals=%d\n", 39 sig, iter, sigs); 40 exit(0); 41 } 42 43 static void handle_kick_signal(int sig, siginfo_t *info, void *context) 44 { 45 sigs++; 46 } 47 48 static char *drivers[] = { 49 "sha1-ce", 50 "sha224-arm64", 51 "sha224-arm64-neon", 52 "sha224-ce", 53 "sha256-arm64", 54 "sha256-arm64-neon", 55 "sha256-ce", 56 "sha384-ce", 57 "sha512-ce", 58 "sha3-224-ce", 59 "sha3-256-ce", 60 "sha3-384-ce", 61 "sha3-512-ce", 62 "sm3-ce", 63 "sm3-neon", 64 }; 65 66 static bool create_socket(void) 67 { 68 FILE *proc; 69 struct sockaddr_alg addr; 70 char buf[1024]; 71 char *c, *driver_name; 72 bool is_shash, match; 73 int ret, i; 74 75 ret = socket(AF_ALG, SOCK_SEQPACKET, 0); 76 if (ret < 0) { 77 if (errno == EAFNOSUPPORT) { 78 printf("AF_ALG not supported\n"); 79 return false; 80 } 81 82 printf("Failed to create AF_ALG socket: %s (%d)\n", 83 strerror(errno), errno); 84 return false; 85 } 86 base = ret; 87 88 memset(&addr, 0, sizeof(addr)); 89 addr.salg_family = AF_ALG; 90 strncpy((char *)addr.salg_type, "hash", sizeof(addr.salg_type)); 91 92 proc = fopen("/proc/crypto", "r"); 93 if (!proc) { 94 printf("Unable to open /proc/crypto\n"); 95 return false; 96 } 97 98 driver_name = NULL; 99 is_shash = false; 100 match = false; 101 102 /* Look through /proc/crypto for a driver with kernel mode FP usage */ 103 while (!match) { 104 c = fgets(buf, sizeof(buf), proc); 105 if (!c) { 106 if (feof(proc)) { 107 printf("Nothing found in /proc/crypto\n"); 108 return false; 109 } 110 continue; 111 } 112 113 /* Algorithm descriptions are separated by a blank line */ 114 if (*c == '\n') { 115 if (is_shash && driver_name) { 116 for (i = 0; i < ARRAY_SIZE(drivers); i++) { 117 if (strcmp(drivers[i], 118 driver_name) == 0) { 119 match = true; 120 } 121 } 122 } 123 124 if (!match) { 125 digest_len = 0; 126 127 free(driver_name); 128 driver_name = NULL; 129 130 free(alg_name); 131 alg_name = NULL; 132 133 is_shash = false; 134 } 135 continue; 136 } 137 138 /* Remove trailing newline */ 139 c = strchr(buf, '\n'); 140 if (c) 141 *c = '\0'; 142 143 /* Find the field/value separator and start of the value */ 144 c = strchr(buf, ':'); 145 if (!c) 146 continue; 147 c += 2; 148 149 if (strncmp(buf, "digestsize", strlen("digestsize")) == 0) 150 sscanf(c, "%d", &digest_len); 151 152 if (strncmp(buf, "name", strlen("name")) == 0) 153 alg_name = strdup(c); 154 155 if (strncmp(buf, "driver", strlen("driver")) == 0) 156 driver_name = strdup(c); 157 158 if (strncmp(buf, "type", strlen("type")) == 0) 159 if (strncmp(c, "shash", strlen("shash")) == 0) 160 is_shash = true; 161 } 162 163 strncpy((char *)addr.salg_name, alg_name, 164 sizeof(addr.salg_name) - 1); 165 166 ret = bind(base, (struct sockaddr *)&addr, sizeof(addr)); 167 if (ret < 0) { 168 printf("Failed to bind %s: %s (%d)\n", 169 addr.salg_name, strerror(errno), errno); 170 return false; 171 } 172 173 ret = accept(base, NULL, 0); 174 if (ret < 0) { 175 printf("Failed to accept %s: %s (%d)\n", 176 addr.salg_name, strerror(errno), errno); 177 return false; 178 } 179 180 sock = ret; 181 182 ret = pipe(zerocopy); 183 if (ret != 0) { 184 printf("Failed to create zerocopy pipe: %s (%d)\n", 185 strerror(errno), errno); 186 return false; 187 } 188 189 ref = malloc(digest_len); 190 if (!ref) { 191 printf("Failed to allocated %d byte reference\n", digest_len); 192 return false; 193 } 194 195 digest = malloc(digest_len); 196 if (!digest) { 197 printf("Failed to allocated %d byte digest\n", digest_len); 198 return false; 199 } 200 201 return true; 202 } 203 204 static bool compute_digest(void *buf) 205 { 206 struct iovec iov; 207 int ret, wrote; 208 209 iov = data_iov; 210 while (iov.iov_len) { 211 ret = vmsplice(zerocopy[1], &iov, 1, SPLICE_F_GIFT); 212 if (ret < 0) { 213 printf("Failed to send buffer: %s (%d)\n", 214 strerror(errno), errno); 215 return false; 216 } 217 218 wrote = ret; 219 ret = splice(zerocopy[0], NULL, sock, NULL, wrote, 0); 220 if (ret < 0) { 221 printf("Failed to splice buffer: %s (%d)\n", 222 strerror(errno), errno); 223 } else if (ret != wrote) { 224 printf("Short splice: %d < %d\n", ret, wrote); 225 } 226 227 iov.iov_len -= wrote; 228 iov.iov_base += wrote; 229 } 230 231 reread: 232 ret = recv(sock, buf, digest_len, 0); 233 if (ret == 0) { 234 printf("No digest returned\n"); 235 return false; 236 } 237 if (ret != digest_len) { 238 if (errno == -EAGAIN) 239 goto reread; 240 printf("Failed to get digest: %s (%d)\n", 241 strerror(errno), errno); 242 return false; 243 } 244 245 return true; 246 } 247 248 int main(void) 249 { 250 char *data; 251 struct sigaction sa; 252 int ret; 253 254 /* Ensure we have unbuffered output */ 255 setvbuf(stdout, NULL, _IOLBF, 0); 256 257 /* The parent will communicate with us via signals */ 258 memset(&sa, 0, sizeof(sa)); 259 sa.sa_sigaction = handle_exit_signal; 260 sa.sa_flags = SA_RESTART | SA_SIGINFO; 261 sigemptyset(&sa.sa_mask); 262 ret = sigaction(SIGTERM, &sa, NULL); 263 if (ret < 0) 264 printf("Failed to install SIGTERM handler: %s (%d)\n", 265 strerror(errno), errno); 266 267 sa.sa_sigaction = handle_kick_signal; 268 ret = sigaction(SIGUSR1, &sa, NULL); 269 if (ret < 0) 270 printf("Failed to install SIGUSR1 handler: %s (%d)\n", 271 strerror(errno), errno); 272 ret = sigaction(SIGUSR2, &sa, NULL); 273 if (ret < 0) 274 printf("Failed to install SIGUSR2 handler: %s (%d)\n", 275 strerror(errno), errno); 276 277 data = malloc(DATA_SIZE); 278 if (!data) { 279 printf("Failed to allocate data buffer\n"); 280 return EXIT_FAILURE; 281 } 282 memset(data, 0, DATA_SIZE); 283 284 data_iov.iov_base = data; 285 data_iov.iov_len = DATA_SIZE; 286 287 /* 288 * If we can't create a socket assume it's a lack of system 289 * support and fall back to a basic FPSIMD test for the 290 * benefit of fp-stress. 291 */ 292 if (!create_socket()) { 293 execl("./fpsimd-test", "./fpsimd-test", NULL); 294 printf("Failed to fall back to fspimd-test: %d (%s)\n", 295 errno, strerror(errno)); 296 return EXIT_FAILURE; 297 } 298 299 /* 300 * Compute a reference digest we hope is repeatable, we do 301 * this at runtime partly to make it easier to play with 302 * parameters. 303 */ 304 if (!compute_digest(ref)) { 305 printf("Failed to compute reference digest\n"); 306 return EXIT_FAILURE; 307 } 308 309 printf("AF_ALG using %s\n", alg_name); 310 311 while (true) { 312 if (!compute_digest(digest)) { 313 printf("Failed to compute digest, iter=%d\n", iter); 314 return EXIT_FAILURE; 315 } 316 317 if (memcmp(ref, digest, digest_len) != 0) { 318 printf("Digest mismatch, iter=%d\n", iter); 319 return EXIT_FAILURE; 320 } 321 322 iter++; 323 } 324 325 return EXIT_FAILURE; 326 } 327