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