1
2
3
4
5
6#include <errno.h>
7#include <stdbool.h>
8#include <stddef.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13#include <sys/auxv.h>
14#include <sys/prctl.h>
15#include <sys/ptrace.h>
16#include <sys/types.h>
17#include <sys/uio.h>
18#include <sys/wait.h>
19#include <asm/sigcontext.h>
20#include <asm/ptrace.h>
21
22#include "../../kselftest.h"
23
24
25#ifndef NT_ARM_SVE
26#define NT_ARM_SVE 0x405
27#endif
28
29struct vec_type {
30 const char *name;
31 unsigned long hwcap_type;
32 unsigned long hwcap;
33 int regset;
34 int prctl_set;
35};
36
37static const struct vec_type vec_types[] = {
38 {
39 .name = "SVE",
40 .hwcap_type = AT_HWCAP,
41 .hwcap = HWCAP_SVE,
42 .regset = NT_ARM_SVE,
43 .prctl_set = PR_SVE_SET_VL,
44 },
45};
46
47#define VL_TESTS (((SVE_VQ_MAX - SVE_VQ_MIN) + 1) * 3)
48#define FLAG_TESTS 2
49#define FPSIMD_TESTS 3
50
51#define EXPECTED_TESTS ((VL_TESTS + FLAG_TESTS + FPSIMD_TESTS) * ARRAY_SIZE(vec_types))
52
53static void fill_buf(char *buf, size_t size)
54{
55 int i;
56
57 for (i = 0; i < size; i++)
58 buf[i] = random();
59}
60
61static int do_child(void)
62{
63 if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
64 ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));
65
66 if (raise(SIGSTOP))
67 ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));
68
69 return EXIT_SUCCESS;
70}
71
72static int get_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)
73{
74 struct iovec iov;
75
76 iov.iov_base = fpsimd;
77 iov.iov_len = sizeof(*fpsimd);
78 return ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov);
79}
80
81static struct user_sve_header *get_sve(pid_t pid, const struct vec_type *type,
82 void **buf, size_t *size)
83{
84 struct user_sve_header *sve;
85 void *p;
86 size_t sz = sizeof *sve;
87 struct iovec iov;
88
89 while (1) {
90 if (*size < sz) {
91 p = realloc(*buf, sz);
92 if (!p) {
93 errno = ENOMEM;
94 goto error;
95 }
96
97 *buf = p;
98 *size = sz;
99 }
100
101 iov.iov_base = *buf;
102 iov.iov_len = sz;
103 if (ptrace(PTRACE_GETREGSET, pid, type->regset, &iov))
104 goto error;
105
106 sve = *buf;
107 if (sve->size <= sz)
108 break;
109
110 sz = sve->size;
111 }
112
113 return sve;
114
115error:
116 return NULL;
117}
118
119static int set_sve(pid_t pid, const struct vec_type *type,
120 const struct user_sve_header *sve)
121{
122 struct iovec iov;
123
124 iov.iov_base = (void *)sve;
125 iov.iov_len = sve->size;
126 return ptrace(PTRACE_SETREGSET, pid, type->regset, &iov);
127}
128
129
130static void ptrace_set_get_inherit(pid_t child, const struct vec_type *type)
131{
132 struct user_sve_header sve;
133 struct user_sve_header *new_sve = NULL;
134 size_t new_sve_size = 0;
135 int ret;
136
137
138 memset(&sve, 0, sizeof(sve));
139 sve.size = sizeof(sve);
140 sve.vl = sve_vl_from_vq(SVE_VQ_MIN);
141 sve.flags = SVE_PT_VL_INHERIT;
142 ret = set_sve(child, type, &sve);
143 if (ret != 0) {
144 ksft_test_result_fail("Failed to set %s SVE_PT_VL_INHERIT\n",
145 type->name);
146 return;
147 }
148
149
150
151
152
153 if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {
154 ksft_test_result_fail("Failed to read %s SVE flags\n",
155 type->name);
156 return;
157 }
158
159 ksft_test_result(new_sve->flags & SVE_PT_VL_INHERIT,
160 "%s SVE_PT_VL_INHERIT set\n", type->name);
161
162
163 sve.flags &= ~SVE_PT_VL_INHERIT;
164 ret = set_sve(child, type, &sve);
165 if (ret != 0) {
166 ksft_test_result_fail("Failed to clear %s SVE_PT_VL_INHERIT\n",
167 type->name);
168 return;
169 }
170
171 if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {
172 ksft_test_result_fail("Failed to read %s SVE flags\n",
173 type->name);
174 return;
175 }
176
177 ksft_test_result(!(new_sve->flags & SVE_PT_VL_INHERIT),
178 "%s SVE_PT_VL_INHERIT cleared\n", type->name);
179
180 free(new_sve);
181}
182
183
184static void ptrace_set_get_vl(pid_t child, const struct vec_type *type,
185 unsigned int vl, bool *supported)
186{
187 struct user_sve_header sve;
188 struct user_sve_header *new_sve = NULL;
189 size_t new_sve_size = 0;
190 int ret, prctl_vl;
191
192 *supported = false;
193
194
195 prctl_vl = prctl(type->prctl_set, vl);
196 if (prctl_vl == -1)
197 ksft_exit_fail_msg("prctl(PR_%s_SET_VL) failed: %s (%d)\n",
198 type->name, strerror(errno), errno);
199
200
201 *supported = (prctl_vl == vl);
202
203
204 memset(&sve, 0, sizeof(sve));
205 sve.size = sizeof(sve);
206 sve.vl = vl;
207 ret = set_sve(child, type, &sve);
208 if (ret != 0) {
209 ksft_test_result_fail("Failed to set %s VL %u\n",
210 type->name, vl);
211 return;
212 }
213
214
215
216
217
218 if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {
219 ksft_test_result_fail("Failed to read %s VL %u\n",
220 type->name, vl);
221 return;
222 }
223
224 ksft_test_result(new_sve->vl = prctl_vl, "Set %s VL %u\n",
225 type->name, vl);
226
227 free(new_sve);
228}
229
230static void check_u32(unsigned int vl, const char *reg,
231 uint32_t *in, uint32_t *out, int *errors)
232{
233 if (*in != *out) {
234 printf("# VL %d %s wrote %x read %x\n",
235 vl, reg, *in, *out);
236 (*errors)++;
237 }
238}
239
240
241static void ptrace_sve_fpsimd(pid_t child, const struct vec_type *type)
242{
243 void *svebuf = NULL;
244 size_t svebufsz = 0;
245 struct user_sve_header *sve;
246 struct user_fpsimd_state *fpsimd, new_fpsimd;
247 unsigned int i, j;
248 unsigned char *p;
249
250
251 sve = get_sve(child, type, &svebuf, &svebufsz);
252 if (!sve) {
253 ksft_test_result_fail("get_sve(%s): %s\n",
254 type->name, strerror(errno));
255
256 return;
257 } else {
258 ksft_test_result_pass("get_sve(%s FPSIMD)\n", type->name);
259 }
260
261 ksft_test_result((sve->flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD,
262 "Got FPSIMD registers via %s\n", type->name);
263 if ((sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_FPSIMD)
264 goto out;
265
266
267 fpsimd = (struct user_fpsimd_state *)((char *)sve +
268 SVE_PT_FPSIMD_OFFSET);
269 for (i = 0; i < 32; ++i) {
270 p = (unsigned char *)&fpsimd->vregs[i];
271
272 for (j = 0; j < sizeof(fpsimd->vregs[i]); ++j)
273 p[j] = j;
274 }
275
276 if (set_sve(child, type, sve)) {
277 ksft_test_result_fail("set_sve(%s FPSIMD): %s\n",
278 type->name, strerror(errno));
279
280 goto out;
281 }
282
283
284 if (get_fpsimd(child, &new_fpsimd)) {
285 ksft_test_result_fail("get_fpsimd(): %s\n",
286 strerror(errno));
287 goto out;
288 }
289 if (memcmp(fpsimd, &new_fpsimd, sizeof(*fpsimd)) == 0)
290 ksft_test_result_pass("%s get_fpsimd() gave same state\n",
291 type->name);
292 else
293 ksft_test_result_fail("%s get_fpsimd() gave different state\n",
294 type->name);
295
296out:
297 free(svebuf);
298}
299
300
301static void ptrace_set_sve_get_sve_data(pid_t child,
302 const struct vec_type *type,
303 unsigned int vl)
304{
305 void *write_buf;
306 void *read_buf = NULL;
307 struct user_sve_header *write_sve;
308 struct user_sve_header *read_sve;
309 size_t read_sve_size = 0;
310 unsigned int vq = sve_vq_from_vl(vl);
311 int ret, i;
312 size_t data_size;
313 int errors = 0;
314
315 data_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);
316 write_buf = malloc(data_size);
317 if (!write_buf) {
318 ksft_test_result_fail("Error allocating %d byte buffer for %s VL %u\n",
319 data_size, type->name, vl);
320 return;
321 }
322 write_sve = write_buf;
323
324
325 memset(write_sve, 0, data_size);
326 write_sve->size = data_size;
327 write_sve->vl = vl;
328 write_sve->flags = SVE_PT_REGS_SVE;
329
330 for (i = 0; i < __SVE_NUM_ZREGS; i++)
331 fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
332 SVE_PT_SVE_ZREG_SIZE(vq));
333
334 for (i = 0; i < __SVE_NUM_PREGS; i++)
335 fill_buf(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),
336 SVE_PT_SVE_PREG_SIZE(vq));
337
338 fill_buf(write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), SVE_PT_SVE_FPSR_SIZE);
339 fill_buf(write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), SVE_PT_SVE_FPCR_SIZE);
340
341
342
343 ret = set_sve(child, type, write_sve);
344 if (ret != 0) {
345 ksft_test_result_fail("Failed to set %s VL %u data\n",
346 type->name, vl);
347 goto out;
348 }
349
350
351 if (!get_sve(child, type, (void **)&read_buf, &read_sve_size)) {
352 ksft_test_result_fail("Failed to read %s VL %u data\n",
353 type->name, vl);
354 goto out;
355 }
356 read_sve = read_buf;
357
358
359 if (read_sve->size < write_sve->size) {
360 ksft_test_result_fail("%s wrote %d bytes, only read %d\n",
361 type->name, write_sve->size,
362 read_sve->size);
363 goto out_read;
364 }
365
366 for (i = 0; i < __SVE_NUM_ZREGS; i++) {
367 if (memcmp(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
368 read_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
369 SVE_PT_SVE_ZREG_SIZE(vq)) != 0) {
370 printf("# Mismatch in %u Z%d\n", vl, i);
371 errors++;
372 }
373 }
374
375 for (i = 0; i < __SVE_NUM_PREGS; i++) {
376 if (memcmp(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),
377 read_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),
378 SVE_PT_SVE_PREG_SIZE(vq)) != 0) {
379 printf("# Mismatch in %u P%d\n", vl, i);
380 errors++;
381 }
382 }
383
384 check_u32(vl, "FPSR", write_buf + SVE_PT_SVE_FPSR_OFFSET(vq),
385 read_buf + SVE_PT_SVE_FPSR_OFFSET(vq), &errors);
386 check_u32(vl, "FPCR", write_buf + SVE_PT_SVE_FPCR_OFFSET(vq),
387 read_buf + SVE_PT_SVE_FPCR_OFFSET(vq), &errors);
388
389 ksft_test_result(errors == 0, "Set and get %s data for VL %u\n",
390 type->name, vl);
391
392out_read:
393 free(read_buf);
394out:
395 free(write_buf);
396}
397
398
399static void ptrace_set_sve_get_fpsimd_data(pid_t child,
400 const struct vec_type *type,
401 unsigned int vl)
402{
403 void *write_buf;
404 struct user_sve_header *write_sve;
405 unsigned int vq = sve_vq_from_vl(vl);
406 struct user_fpsimd_state fpsimd_state;
407 int ret, i;
408 size_t data_size;
409 int errors = 0;
410
411 if (__BYTE_ORDER == __BIG_ENDIAN) {
412 ksft_test_result_skip("Big endian not supported\n");
413 return;
414 }
415
416 data_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);
417 write_buf = malloc(data_size);
418 if (!write_buf) {
419 ksft_test_result_fail("Error allocating %d byte buffer for %s VL %u\n",
420 data_size, type->name, vl);
421 return;
422 }
423 write_sve = write_buf;
424
425
426 memset(write_sve, 0, data_size);
427 write_sve->size = data_size;
428 write_sve->vl = vl;
429 write_sve->flags = SVE_PT_REGS_SVE;
430
431 for (i = 0; i < __SVE_NUM_ZREGS; i++)
432 fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
433 SVE_PT_SVE_ZREG_SIZE(vq));
434
435 fill_buf(write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), SVE_PT_SVE_FPSR_SIZE);
436 fill_buf(write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), SVE_PT_SVE_FPCR_SIZE);
437
438 ret = set_sve(child, type, write_sve);
439 if (ret != 0) {
440 ksft_test_result_fail("Failed to set %s VL %u data\n",
441 type->name, vl);
442 goto out;
443 }
444
445
446 if (get_fpsimd(child, &fpsimd_state)) {
447 ksft_test_result_fail("Failed to read %s VL %u FPSIMD data\n",
448 type->name, vl);
449 goto out;
450 }
451
452 for (i = 0; i < __SVE_NUM_ZREGS; i++) {
453 __uint128_t tmp = 0;
454
455
456
457
458
459 memcpy(&tmp, write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
460 sizeof(tmp));
461
462 if (tmp != fpsimd_state.vregs[i]) {
463 printf("# Mismatch in FPSIMD for %s VL %u Z%d\n",
464 type->name, vl, i);
465 errors++;
466 }
467 }
468
469 check_u32(vl, "FPSR", write_buf + SVE_PT_SVE_FPSR_OFFSET(vq),
470 &fpsimd_state.fpsr, &errors);
471 check_u32(vl, "FPCR", write_buf + SVE_PT_SVE_FPCR_OFFSET(vq),
472 &fpsimd_state.fpcr, &errors);
473
474 ksft_test_result(errors == 0, "Set and get FPSIMD data for %s VL %u\n",
475 type->name, vl);
476
477out:
478 free(write_buf);
479}
480
481static int do_parent(pid_t child)
482{
483 int ret = EXIT_FAILURE;
484 pid_t pid;
485 int status, i;
486 siginfo_t si;
487 unsigned int vq, vl;
488 bool vl_supported;
489
490 ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
491
492
493 while (1) {
494 int sig;
495
496 pid = wait(&status);
497 if (pid == -1) {
498 perror("wait");
499 goto error;
500 }
501
502
503
504
505
506 if (pid != child)
507 continue;
508
509 if (WIFEXITED(status) || WIFSIGNALED(status))
510 ksft_exit_fail_msg("Child died unexpectedly\n");
511
512 if (!WIFSTOPPED(status))
513 goto error;
514
515 sig = WSTOPSIG(status);
516
517 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
518 if (errno == ESRCH)
519 goto disappeared;
520
521 if (errno == EINVAL) {
522 sig = 0;
523 goto cont;
524 }
525
526 ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
527 strerror(errno));
528 goto error;
529 }
530
531 if (sig == SIGSTOP && si.si_code == SI_TKILL &&
532 si.si_pid == pid)
533 break;
534
535 cont:
536 if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
537 if (errno == ESRCH)
538 goto disappeared;
539
540 ksft_test_result_fail("PTRACE_CONT: %s\n",
541 strerror(errno));
542 goto error;
543 }
544 }
545
546 for (i = 0; i < ARRAY_SIZE(vec_types); i++) {
547
548 if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {
549 ptrace_sve_fpsimd(child, &vec_types[i]);
550 } else {
551 ksft_test_result_skip("%s FPSIMD get via SVE\n",
552 vec_types[i].name);
553 ksft_test_result_skip("%s FPSIMD set via SVE\n",
554 vec_types[i].name);
555 ksft_test_result_skip("%s set read via FPSIMD\n",
556 vec_types[i].name);
557 }
558
559
560 if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {
561 ptrace_set_get_inherit(child, &vec_types[i]);
562 } else {
563 ksft_test_result_skip("%s SVE_PT_VL_INHERIT set\n",
564 vec_types[i].name);
565 ksft_test_result_skip("%s SVE_PT_VL_INHERIT cleared\n",
566 vec_types[i].name);
567 }
568
569
570 for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) {
571 vl = sve_vl_from_vq(vq);
572
573
574 if (getauxval(vec_types[i].hwcap_type) &
575 vec_types[i].hwcap) {
576 ptrace_set_get_vl(child, &vec_types[i], vl,
577 &vl_supported);
578 } else {
579 ksft_test_result_skip("%s get/set VL %d\n",
580 vec_types[i].name, vl);
581 vl_supported = false;
582 }
583
584
585 if (vl_supported) {
586 ptrace_set_sve_get_sve_data(child, &vec_types[i], vl);
587 ptrace_set_sve_get_fpsimd_data(child, &vec_types[i], vl);
588 } else {
589 ksft_test_result_skip("%s set SVE get SVE for VL %d\n",
590 vec_types[i].name, vl);
591 ksft_test_result_skip("%s set SVE get FPSIMD for VL %d\n",
592 vec_types[i].name, vl);
593 }
594 }
595 }
596
597 ret = EXIT_SUCCESS;
598
599error:
600 kill(child, SIGKILL);
601
602disappeared:
603 return ret;
604}
605
606int main(void)
607{
608 int ret = EXIT_SUCCESS;
609 pid_t child;
610
611 srandom(getpid());
612
613 ksft_print_header();
614 ksft_set_plan(EXPECTED_TESTS);
615
616 if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
617 ksft_exit_skip("SVE not available\n");
618
619 child = fork();
620 if (!child)
621 return do_child();
622
623 if (do_parent(child))
624 ret = EXIT_FAILURE;
625
626 ksft_print_cnts();
627
628 return ret;
629}
630