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