xref: /linux/tools/testing/selftests/arm64/fp/kernel-test.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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