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