xref: /linux/tools/testing/selftests/arm64/fp/vec-syscfg.c (revision da1d9caf95def6f0320819cf941c9fd1069ba9e1)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2021 ARM Limited.
4  * Original author: Mark Brown <broonie@kernel.org>
5  */
6 #include <assert.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <sys/auxv.h>
15 #include <sys/prctl.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <asm/sigcontext.h>
19 #include <asm/hwcap.h>
20 
21 #include "../../kselftest.h"
22 #include "rdvl.h"
23 
24 #define ARCH_MIN_VL SVE_VL_MIN
25 
26 struct vec_data {
27 	const char *name;
28 	unsigned long hwcap_type;
29 	unsigned long hwcap;
30 	const char *rdvl_binary;
31 	int (*rdvl)(void);
32 
33 	int prctl_get;
34 	int prctl_set;
35 	const char *default_vl_file;
36 
37 	int default_vl;
38 	int min_vl;
39 	int max_vl;
40 };
41 
42 
43 static struct vec_data vec_data[] = {
44 	{
45 		.name = "SVE",
46 		.hwcap_type = AT_HWCAP,
47 		.hwcap = HWCAP_SVE,
48 		.rdvl = rdvl_sve,
49 		.rdvl_binary = "./rdvl-sve",
50 		.prctl_get = PR_SVE_GET_VL,
51 		.prctl_set = PR_SVE_SET_VL,
52 		.default_vl_file = "/proc/sys/abi/sve_default_vector_length",
53 	},
54 	{
55 		.name = "SME",
56 		.hwcap_type = AT_HWCAP2,
57 		.hwcap = HWCAP2_SME,
58 		.rdvl = rdvl_sme,
59 		.rdvl_binary = "./rdvl-sme",
60 		.prctl_get = PR_SME_GET_VL,
61 		.prctl_set = PR_SME_SET_VL,
62 		.default_vl_file = "/proc/sys/abi/sme_default_vector_length",
63 	},
64 };
65 
66 static int stdio_read_integer(FILE *f, const char *what, int *val)
67 {
68 	int n = 0;
69 	int ret;
70 
71 	ret = fscanf(f, "%d%*1[\n]%n", val, &n);
72 	if (ret < 1 || n < 1) {
73 		ksft_print_msg("failed to parse integer from %s\n", what);
74 		return -1;
75 	}
76 
77 	return 0;
78 }
79 
80 /* Start a new process and return the vector length it sees */
81 static int get_child_rdvl(struct vec_data *data)
82 {
83 	FILE *out;
84 	int pipefd[2];
85 	pid_t pid, child;
86 	int read_vl, ret;
87 
88 	ret = pipe(pipefd);
89 	if (ret == -1) {
90 		ksft_print_msg("pipe() failed: %d (%s)\n",
91 			       errno, strerror(errno));
92 		return -1;
93 	}
94 
95 	fflush(stdout);
96 
97 	child = fork();
98 	if (child == -1) {
99 		ksft_print_msg("fork() failed: %d (%s)\n",
100 			       errno, strerror(errno));
101 		close(pipefd[0]);
102 		close(pipefd[1]);
103 		return -1;
104 	}
105 
106 	/* Child: put vector length on the pipe */
107 	if (child == 0) {
108 		/*
109 		 * Replace stdout with the pipe, errors to stderr from
110 		 * here as kselftest prints to stdout.
111 		 */
112 		ret = dup2(pipefd[1], 1);
113 		if (ret == -1) {
114 			fprintf(stderr, "dup2() %d\n", errno);
115 			exit(EXIT_FAILURE);
116 		}
117 
118 		/* exec() a new binary which puts the VL on stdout */
119 		ret = execl(data->rdvl_binary, data->rdvl_binary, NULL);
120 		fprintf(stderr, "execl(%s) failed: %d (%s)\n",
121 			data->rdvl_binary, errno, strerror(errno));
122 
123 		exit(EXIT_FAILURE);
124 	}
125 
126 	close(pipefd[1]);
127 
128 	/* Parent; wait for the exit status from the child & verify it */
129 	do {
130 		pid = wait(&ret);
131 		if (pid == -1) {
132 			ksft_print_msg("wait() failed: %d (%s)\n",
133 				       errno, strerror(errno));
134 			close(pipefd[0]);
135 			return -1;
136 		}
137 	} while (pid != child);
138 
139 	assert(pid == child);
140 
141 	if (!WIFEXITED(ret)) {
142 		ksft_print_msg("child exited abnormally\n");
143 		close(pipefd[0]);
144 		return -1;
145 	}
146 
147 	if (WEXITSTATUS(ret) != 0) {
148 		ksft_print_msg("child returned error %d\n",
149 			       WEXITSTATUS(ret));
150 		close(pipefd[0]);
151 		return -1;
152 	}
153 
154 	out = fdopen(pipefd[0], "r");
155 	if (!out) {
156 		ksft_print_msg("failed to open child stdout\n");
157 		close(pipefd[0]);
158 		return -1;
159 	}
160 
161 	ret = stdio_read_integer(out, "child", &read_vl);
162 	fclose(out);
163 	if (ret != 0)
164 		return ret;
165 
166 	return read_vl;
167 }
168 
169 static int file_read_integer(const char *name, int *val)
170 {
171 	FILE *f;
172 	int ret;
173 
174 	f = fopen(name, "r");
175 	if (!f) {
176 		ksft_test_result_fail("Unable to open %s: %d (%s)\n",
177 				      name, errno,
178 				      strerror(errno));
179 		return -1;
180 	}
181 
182 	ret = stdio_read_integer(f, name, val);
183 	fclose(f);
184 
185 	return ret;
186 }
187 
188 static int file_write_integer(const char *name, int val)
189 {
190 	FILE *f;
191 
192 	f = fopen(name, "w");
193 	if (!f) {
194 		ksft_test_result_fail("Unable to open %s: %d (%s)\n",
195 				      name, errno,
196 				      strerror(errno));
197 		return -1;
198 	}
199 
200 	fprintf(f, "%d", val);
201 	fclose(f);
202 
203 	return 0;
204 }
205 
206 /*
207  * Verify that we can read the default VL via proc, checking that it
208  * is set in a freshly spawned child.
209  */
210 static void proc_read_default(struct vec_data *data)
211 {
212 	int default_vl, child_vl, ret;
213 
214 	ret = file_read_integer(data->default_vl_file, &default_vl);
215 	if (ret != 0)
216 		return;
217 
218 	/* Is this the actual default seen by new processes? */
219 	child_vl = get_child_rdvl(data);
220 	if (child_vl != default_vl) {
221 		ksft_test_result_fail("%s is %d but child VL is %d\n",
222 				      data->default_vl_file,
223 				      default_vl, child_vl);
224 		return;
225 	}
226 
227 	ksft_test_result_pass("%s default vector length %d\n", data->name,
228 			      default_vl);
229 	data->default_vl = default_vl;
230 }
231 
232 /* Verify that we can write a minimum value and have it take effect */
233 static void proc_write_min(struct vec_data *data)
234 {
235 	int ret, new_default, child_vl;
236 
237 	if (geteuid() != 0) {
238 		ksft_test_result_skip("Need to be root to write to /proc\n");
239 		return;
240 	}
241 
242 	ret = file_write_integer(data->default_vl_file, ARCH_MIN_VL);
243 	if (ret != 0)
244 		return;
245 
246 	/* What was the new value? */
247 	ret = file_read_integer(data->default_vl_file, &new_default);
248 	if (ret != 0)
249 		return;
250 
251 	/* Did it take effect in a new process? */
252 	child_vl = get_child_rdvl(data);
253 	if (child_vl != new_default) {
254 		ksft_test_result_fail("%s is %d but child VL is %d\n",
255 				      data->default_vl_file,
256 				      new_default, child_vl);
257 		return;
258 	}
259 
260 	ksft_test_result_pass("%s minimum vector length %d\n", data->name,
261 			      new_default);
262 	data->min_vl = new_default;
263 
264 	file_write_integer(data->default_vl_file, data->default_vl);
265 }
266 
267 /* Verify that we can write a maximum value and have it take effect */
268 static void proc_write_max(struct vec_data *data)
269 {
270 	int ret, new_default, child_vl;
271 
272 	if (geteuid() != 0) {
273 		ksft_test_result_skip("Need to be root to write to /proc\n");
274 		return;
275 	}
276 
277 	/* -1 is accepted by the /proc interface as the maximum VL */
278 	ret = file_write_integer(data->default_vl_file, -1);
279 	if (ret != 0)
280 		return;
281 
282 	/* What was the new value? */
283 	ret = file_read_integer(data->default_vl_file, &new_default);
284 	if (ret != 0)
285 		return;
286 
287 	/* Did it take effect in a new process? */
288 	child_vl = get_child_rdvl(data);
289 	if (child_vl != new_default) {
290 		ksft_test_result_fail("%s is %d but child VL is %d\n",
291 				      data->default_vl_file,
292 				      new_default, child_vl);
293 		return;
294 	}
295 
296 	ksft_test_result_pass("%s maximum vector length %d\n", data->name,
297 			      new_default);
298 	data->max_vl = new_default;
299 
300 	file_write_integer(data->default_vl_file, data->default_vl);
301 }
302 
303 /* Can we read back a VL from prctl? */
304 static void prctl_get(struct vec_data *data)
305 {
306 	int ret;
307 
308 	ret = prctl(data->prctl_get);
309 	if (ret == -1) {
310 		ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
311 				      data->name, errno, strerror(errno));
312 		return;
313 	}
314 
315 	/* Mask out any flags */
316 	ret &= PR_SVE_VL_LEN_MASK;
317 
318 	/* Is that what we can read back directly? */
319 	if (ret == data->rdvl())
320 		ksft_test_result_pass("%s current VL is %d\n",
321 				      data->name, ret);
322 	else
323 		ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n",
324 				      data->name, ret, data->rdvl());
325 }
326 
327 /* Does the prctl let us set the VL we already have? */
328 static void prctl_set_same(struct vec_data *data)
329 {
330 	int cur_vl = data->rdvl();
331 	int ret;
332 
333 	ret = prctl(data->prctl_set, cur_vl);
334 	if (ret < 0) {
335 		ksft_test_result_fail("%s prctl set failed: %d (%s)\n",
336 				      data->name, errno, strerror(errno));
337 		return;
338 	}
339 
340 	ksft_test_result(cur_vl == data->rdvl(),
341 			 "%s set VL %d and have VL %d\n",
342 			 data->name, cur_vl, data->rdvl());
343 }
344 
345 /* Can we set a new VL for this process? */
346 static void prctl_set(struct vec_data *data)
347 {
348 	int ret;
349 
350 	if (data->min_vl == data->max_vl) {
351 		ksft_test_result_skip("%s only one VL supported\n",
352 				      data->name);
353 		return;
354 	}
355 
356 	/* Try to set the minimum VL */
357 	ret = prctl(data->prctl_set, data->min_vl);
358 	if (ret < 0) {
359 		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
360 				      data->name, data->min_vl,
361 				      errno, strerror(errno));
362 		return;
363 	}
364 
365 	if ((ret & PR_SVE_VL_LEN_MASK) != data->min_vl) {
366 		ksft_test_result_fail("%s prctl set %d but return value is %d\n",
367 				      data->name, data->min_vl, data->rdvl());
368 		return;
369 	}
370 
371 	if (data->rdvl() != data->min_vl) {
372 		ksft_test_result_fail("%s set %d but RDVL is %d\n",
373 				      data->name, data->min_vl, data->rdvl());
374 		return;
375 	}
376 
377 	/* Try to set the maximum VL */
378 	ret = prctl(data->prctl_set, data->max_vl);
379 	if (ret < 0) {
380 		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
381 				      data->name, data->max_vl,
382 				      errno, strerror(errno));
383 		return;
384 	}
385 
386 	if ((ret & PR_SVE_VL_LEN_MASK) != data->max_vl) {
387 		ksft_test_result_fail("%s prctl() set %d but return value is %d\n",
388 				      data->name, data->max_vl, data->rdvl());
389 		return;
390 	}
391 
392 	/* The _INHERIT flag should not be present when we read the VL */
393 	ret = prctl(data->prctl_get);
394 	if (ret == -1) {
395 		ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
396 				      data->name, errno, strerror(errno));
397 		return;
398 	}
399 
400 	if (ret & PR_SVE_VL_INHERIT) {
401 		ksft_test_result_fail("%s prctl() reports _INHERIT\n",
402 				      data->name);
403 		return;
404 	}
405 
406 	ksft_test_result_pass("%s prctl() set min/max\n", data->name);
407 }
408 
409 /* If we didn't request it a new VL shouldn't affect the child */
410 static void prctl_set_no_child(struct vec_data *data)
411 {
412 	int ret, child_vl;
413 
414 	if (data->min_vl == data->max_vl) {
415 		ksft_test_result_skip("%s only one VL supported\n",
416 				      data->name);
417 		return;
418 	}
419 
420 	ret = prctl(data->prctl_set, data->min_vl);
421 	if (ret < 0) {
422 		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
423 				      data->name, data->min_vl,
424 				      errno, strerror(errno));
425 		return;
426 	}
427 
428 	/* Ensure the default VL is different */
429 	ret = file_write_integer(data->default_vl_file, data->max_vl);
430 	if (ret != 0)
431 		return;
432 
433 	/* Check that the child has the default we just set */
434 	child_vl = get_child_rdvl(data);
435 	if (child_vl != data->max_vl) {
436 		ksft_test_result_fail("%s is %d but child VL is %d\n",
437 				      data->default_vl_file,
438 				      data->max_vl, child_vl);
439 		return;
440 	}
441 
442 	ksft_test_result_pass("%s vector length used default\n", data->name);
443 
444 	file_write_integer(data->default_vl_file, data->default_vl);
445 }
446 
447 /* If we didn't request it a new VL shouldn't affect the child */
448 static void prctl_set_for_child(struct vec_data *data)
449 {
450 	int ret, child_vl;
451 
452 	if (data->min_vl == data->max_vl) {
453 		ksft_test_result_skip("%s only one VL supported\n",
454 				      data->name);
455 		return;
456 	}
457 
458 	ret = prctl(data->prctl_set, data->min_vl | PR_SVE_VL_INHERIT);
459 	if (ret < 0) {
460 		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
461 				      data->name, data->min_vl,
462 				      errno, strerror(errno));
463 		return;
464 	}
465 
466 	/* The _INHERIT flag should be present when we read the VL */
467 	ret = prctl(data->prctl_get);
468 	if (ret == -1) {
469 		ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
470 				      data->name, errno, strerror(errno));
471 		return;
472 	}
473 	if (!(ret & PR_SVE_VL_INHERIT)) {
474 		ksft_test_result_fail("%s prctl() does not report _INHERIT\n",
475 				      data->name);
476 		return;
477 	}
478 
479 	/* Ensure the default VL is different */
480 	ret = file_write_integer(data->default_vl_file, data->max_vl);
481 	if (ret != 0)
482 		return;
483 
484 	/* Check that the child inherited our VL */
485 	child_vl = get_child_rdvl(data);
486 	if (child_vl != data->min_vl) {
487 		ksft_test_result_fail("%s is %d but child VL is %d\n",
488 				      data->default_vl_file,
489 				      data->min_vl, child_vl);
490 		return;
491 	}
492 
493 	ksft_test_result_pass("%s vector length was inherited\n", data->name);
494 
495 	file_write_integer(data->default_vl_file, data->default_vl);
496 }
497 
498 /* _ONEXEC takes effect only in the child process */
499 static void prctl_set_onexec(struct vec_data *data)
500 {
501 	int ret, child_vl;
502 
503 	if (data->min_vl == data->max_vl) {
504 		ksft_test_result_skip("%s only one VL supported\n",
505 				      data->name);
506 		return;
507 	}
508 
509 	/* Set a known value for the default and our current VL */
510 	ret = file_write_integer(data->default_vl_file, data->max_vl);
511 	if (ret != 0)
512 		return;
513 
514 	ret = prctl(data->prctl_set, data->max_vl);
515 	if (ret < 0) {
516 		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
517 				      data->name, data->min_vl,
518 				      errno, strerror(errno));
519 		return;
520 	}
521 
522 	/* Set a different value for the child to have on exec */
523 	ret = prctl(data->prctl_set, data->min_vl | PR_SVE_SET_VL_ONEXEC);
524 	if (ret < 0) {
525 		ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
526 				      data->name, data->min_vl,
527 				      errno, strerror(errno));
528 		return;
529 	}
530 
531 	/* Our current VL should stay the same */
532 	if (data->rdvl() != data->max_vl) {
533 		ksft_test_result_fail("%s VL changed by _ONEXEC prctl()\n",
534 				      data->name);
535 		return;
536 	}
537 
538 	/* Check that the child inherited our VL */
539 	child_vl = get_child_rdvl(data);
540 	if (child_vl != data->min_vl) {
541 		ksft_test_result_fail("Set %d _ONEXEC but child VL is %d\n",
542 				      data->min_vl, child_vl);
543 		return;
544 	}
545 
546 	ksft_test_result_pass("%s vector length set on exec\n", data->name);
547 
548 	file_write_integer(data->default_vl_file, data->default_vl);
549 }
550 
551 /* For each VQ verify that setting via prctl() does the right thing */
552 static void prctl_set_all_vqs(struct vec_data *data)
553 {
554 	int ret, vq, vl, new_vl;
555 	int errors = 0;
556 
557 	if (!data->min_vl || !data->max_vl) {
558 		ksft_test_result_skip("%s Failed to enumerate VLs, not testing VL setting\n",
559 				      data->name);
560 		return;
561 	}
562 
563 	for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) {
564 		vl = sve_vl_from_vq(vq);
565 
566 		/* Attempt to set the VL */
567 		ret = prctl(data->prctl_set, vl);
568 		if (ret < 0) {
569 			errors++;
570 			ksft_print_msg("%s prctl set failed for %d: %d (%s)\n",
571 				       data->name, vl,
572 				       errno, strerror(errno));
573 			continue;
574 		}
575 
576 		new_vl = ret & PR_SVE_VL_LEN_MASK;
577 
578 		/* Check that we actually have the reported new VL */
579 		if (data->rdvl() != new_vl) {
580 			ksft_print_msg("Set %s VL %d but RDVL reports %d\n",
581 				       data->name, new_vl, data->rdvl());
582 			errors++;
583 		}
584 
585 		/* Was that the VL we asked for? */
586 		if (new_vl == vl)
587 			continue;
588 
589 		/* Should round up to the minimum VL if below it */
590 		if (vl < data->min_vl) {
591 			if (new_vl != data->min_vl) {
592 				ksft_print_msg("%s VL %d returned %d not minimum %d\n",
593 					       data->name, vl, new_vl,
594 					       data->min_vl);
595 				errors++;
596 			}
597 
598 			continue;
599 		}
600 
601 		/* Should round down to maximum VL if above it */
602 		if (vl > data->max_vl) {
603 			if (new_vl != data->max_vl) {
604 				ksft_print_msg("%s VL %d returned %d not maximum %d\n",
605 					       data->name, vl, new_vl,
606 					       data->max_vl);
607 				errors++;
608 			}
609 
610 			continue;
611 		}
612 
613 		/* Otherwise we should've rounded down */
614 		if (!(new_vl < vl)) {
615 			ksft_print_msg("%s VL %d returned %d, did not round down\n",
616 				       data->name, vl, new_vl);
617 			errors++;
618 
619 			continue;
620 		}
621 	}
622 
623 	ksft_test_result(errors == 0, "%s prctl() set all VLs, %d errors\n",
624 			 data->name, errors);
625 }
626 
627 typedef void (*test_type)(struct vec_data *);
628 
629 static const test_type tests[] = {
630 	/*
631 	 * The default/min/max tests must be first and in this order
632 	 * to provide data for other tests.
633 	 */
634 	proc_read_default,
635 	proc_write_min,
636 	proc_write_max,
637 
638 	prctl_get,
639 	prctl_set_same,
640 	prctl_set,
641 	prctl_set_no_child,
642 	prctl_set_for_child,
643 	prctl_set_onexec,
644 	prctl_set_all_vqs,
645 };
646 
647 int main(void)
648 {
649 	int i, j;
650 
651 	ksft_print_header();
652 	ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data));
653 
654 	for (i = 0; i < ARRAY_SIZE(vec_data); i++) {
655 		struct vec_data *data = &vec_data[i];
656 		unsigned long supported;
657 
658 		supported = getauxval(data->hwcap_type) & data->hwcap;
659 
660 		for (j = 0; j < ARRAY_SIZE(tests); j++) {
661 			if (supported)
662 				tests[j](data);
663 			else
664 				ksft_test_result_skip("%s not supported\n",
665 						      data->name);
666 		}
667 	}
668 
669 	ksft_exit_pass();
670 }
671