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