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
handle_exit_signal(int sig,siginfo_t * info,void * context)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
handle_kick_signal(int sig,siginfo_t * info,void * context)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
create_socket(void)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 allocate %d byte reference\n", digest_len);
192 return false;
193 }
194
195 digest = malloc(digest_len);
196 if (!digest) {
197 printf("Failed to allocate %d byte digest\n", digest_len);
198 return false;
199 }
200
201 return true;
202 }
203
compute_digest(void * buf)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
main(void)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