1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2015-2020 ARM Limited.
4*4882a593Smuzhiyun * Original author: Dave Martin <Dave.Martin@arm.com>
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun #include <errno.h>
7*4882a593Smuzhiyun #include <stddef.h>
8*4882a593Smuzhiyun #include <stdio.h>
9*4882a593Smuzhiyun #include <stdlib.h>
10*4882a593Smuzhiyun #include <string.h>
11*4882a593Smuzhiyun #include <unistd.h>
12*4882a593Smuzhiyun #include <sys/auxv.h>
13*4882a593Smuzhiyun #include <sys/ptrace.h>
14*4882a593Smuzhiyun #include <sys/types.h>
15*4882a593Smuzhiyun #include <sys/uio.h>
16*4882a593Smuzhiyun #include <sys/wait.h>
17*4882a593Smuzhiyun #include <asm/sigcontext.h>
18*4882a593Smuzhiyun #include <asm/ptrace.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include "../../kselftest.h"
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun /* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */
23*4882a593Smuzhiyun #ifndef NT_ARM_SVE
24*4882a593Smuzhiyun #define NT_ARM_SVE 0x405
25*4882a593Smuzhiyun #endif
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun /* Number of registers filled in by sve_store_patterns */
28*4882a593Smuzhiyun #define NR_VREGS 5
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun void sve_store_patterns(__uint128_t v[NR_VREGS]);
31*4882a593Smuzhiyun
dump(const void * buf,size_t size)32*4882a593Smuzhiyun static void dump(const void *buf, size_t size)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun size_t i;
35*4882a593Smuzhiyun const unsigned char *p = buf;
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun for (i = 0; i < size; ++i)
38*4882a593Smuzhiyun printf(" %.2x", *p++);
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun
check_vregs(const __uint128_t vregs[NR_VREGS])41*4882a593Smuzhiyun static int check_vregs(const __uint128_t vregs[NR_VREGS])
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun int i;
44*4882a593Smuzhiyun int ok = 1;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun for (i = 0; i < NR_VREGS; ++i) {
47*4882a593Smuzhiyun printf("# v[%d]:", i);
48*4882a593Smuzhiyun dump(&vregs[i], sizeof vregs[i]);
49*4882a593Smuzhiyun putchar('\n');
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun if (vregs[i] != vregs[0])
52*4882a593Smuzhiyun ok = 0;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun return ok;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun
do_child(void)58*4882a593Smuzhiyun static int do_child(void)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
61*4882a593Smuzhiyun ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun if (raise(SIGSTOP))
64*4882a593Smuzhiyun ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun return EXIT_SUCCESS;
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun
get_sve(pid_t pid,void ** buf,size_t * size)69*4882a593Smuzhiyun static struct user_sve_header *get_sve(pid_t pid, void **buf, size_t *size)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun struct user_sve_header *sve;
72*4882a593Smuzhiyun void *p;
73*4882a593Smuzhiyun size_t sz = sizeof *sve;
74*4882a593Smuzhiyun struct iovec iov;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun while (1) {
77*4882a593Smuzhiyun if (*size < sz) {
78*4882a593Smuzhiyun p = realloc(*buf, sz);
79*4882a593Smuzhiyun if (!p) {
80*4882a593Smuzhiyun errno = ENOMEM;
81*4882a593Smuzhiyun goto error;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun *buf = p;
85*4882a593Smuzhiyun *size = sz;
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun iov.iov_base = *buf;
89*4882a593Smuzhiyun iov.iov_len = sz;
90*4882a593Smuzhiyun if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_SVE, &iov))
91*4882a593Smuzhiyun goto error;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun sve = *buf;
94*4882a593Smuzhiyun if (sve->size <= sz)
95*4882a593Smuzhiyun break;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun sz = sve->size;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun return sve;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun error:
103*4882a593Smuzhiyun return NULL;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
set_sve(pid_t pid,const struct user_sve_header * sve)106*4882a593Smuzhiyun static int set_sve(pid_t pid, const struct user_sve_header *sve)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun struct iovec iov;
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun iov.iov_base = (void *)sve;
111*4882a593Smuzhiyun iov.iov_len = sve->size;
112*4882a593Smuzhiyun return ptrace(PTRACE_SETREGSET, pid, NT_ARM_SVE, &iov);
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
dump_sve_regs(const struct user_sve_header * sve,unsigned int num,unsigned int vlmax)115*4882a593Smuzhiyun static void dump_sve_regs(const struct user_sve_header *sve, unsigned int num,
116*4882a593Smuzhiyun unsigned int vlmax)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun unsigned int vq;
119*4882a593Smuzhiyun unsigned int i;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun if ((sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_SVE)
122*4882a593Smuzhiyun ksft_exit_fail_msg("Dumping non-SVE register\n");
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun if (vlmax > sve->vl)
125*4882a593Smuzhiyun vlmax = sve->vl;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun vq = sve_vq_from_vl(sve->vl);
128*4882a593Smuzhiyun for (i = 0; i < num; ++i) {
129*4882a593Smuzhiyun printf("# z%u:", i);
130*4882a593Smuzhiyun dump((const char *)sve + SVE_PT_SVE_ZREG_OFFSET(vq, i),
131*4882a593Smuzhiyun vlmax);
132*4882a593Smuzhiyun printf("%s\n", vlmax == sve->vl ? "" : " ...");
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
do_parent(pid_t child)136*4882a593Smuzhiyun static int do_parent(pid_t child)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun int ret = EXIT_FAILURE;
139*4882a593Smuzhiyun pid_t pid;
140*4882a593Smuzhiyun int status;
141*4882a593Smuzhiyun siginfo_t si;
142*4882a593Smuzhiyun void *svebuf = NULL, *newsvebuf;
143*4882a593Smuzhiyun size_t svebufsz = 0, newsvebufsz;
144*4882a593Smuzhiyun struct user_sve_header *sve, *new_sve;
145*4882a593Smuzhiyun struct user_fpsimd_state *fpsimd;
146*4882a593Smuzhiyun unsigned int i, j;
147*4882a593Smuzhiyun unsigned char *p;
148*4882a593Smuzhiyun unsigned int vq;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun /* Attach to the child */
151*4882a593Smuzhiyun while (1) {
152*4882a593Smuzhiyun int sig;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun pid = wait(&status);
155*4882a593Smuzhiyun if (pid == -1) {
156*4882a593Smuzhiyun perror("wait");
157*4882a593Smuzhiyun goto error;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun /*
161*4882a593Smuzhiyun * This should never happen but it's hard to flag in
162*4882a593Smuzhiyun * the framework.
163*4882a593Smuzhiyun */
164*4882a593Smuzhiyun if (pid != child)
165*4882a593Smuzhiyun continue;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun if (WIFEXITED(status) || WIFSIGNALED(status))
168*4882a593Smuzhiyun ksft_exit_fail_msg("Child died unexpectedly\n");
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun ksft_test_result(WIFSTOPPED(status), "WIFSTOPPED(%d)\n",
171*4882a593Smuzhiyun status);
172*4882a593Smuzhiyun if (!WIFSTOPPED(status))
173*4882a593Smuzhiyun goto error;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun sig = WSTOPSIG(status);
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
178*4882a593Smuzhiyun if (errno == ESRCH)
179*4882a593Smuzhiyun goto disappeared;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun if (errno == EINVAL) {
182*4882a593Smuzhiyun sig = 0; /* bust group-stop */
183*4882a593Smuzhiyun goto cont;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
187*4882a593Smuzhiyun strerror(errno));
188*4882a593Smuzhiyun goto error;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun if (sig == SIGSTOP && si.si_code == SI_TKILL &&
192*4882a593Smuzhiyun si.si_pid == pid)
193*4882a593Smuzhiyun break;
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun cont:
196*4882a593Smuzhiyun if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
197*4882a593Smuzhiyun if (errno == ESRCH)
198*4882a593Smuzhiyun goto disappeared;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun ksft_test_result_fail("PTRACE_CONT: %s\n",
201*4882a593Smuzhiyun strerror(errno));
202*4882a593Smuzhiyun goto error;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun sve = get_sve(pid, &svebuf, &svebufsz);
207*4882a593Smuzhiyun if (!sve) {
208*4882a593Smuzhiyun int e = errno;
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun ksft_test_result_fail("get_sve: %s\n", strerror(errno));
211*4882a593Smuzhiyun if (e == ESRCH)
212*4882a593Smuzhiyun goto disappeared;
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun goto error;
215*4882a593Smuzhiyun } else {
216*4882a593Smuzhiyun ksft_test_result_pass("get_sve\n");
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun ksft_test_result((sve->flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD,
220*4882a593Smuzhiyun "FPSIMD registers\n");
221*4882a593Smuzhiyun if ((sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_FPSIMD)
222*4882a593Smuzhiyun goto error;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun fpsimd = (struct user_fpsimd_state *)((char *)sve +
225*4882a593Smuzhiyun SVE_PT_FPSIMD_OFFSET);
226*4882a593Smuzhiyun for (i = 0; i < 32; ++i) {
227*4882a593Smuzhiyun p = (unsigned char *)&fpsimd->vregs[i];
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun for (j = 0; j < sizeof fpsimd->vregs[i]; ++j)
230*4882a593Smuzhiyun p[j] = j;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun if (set_sve(pid, sve)) {
234*4882a593Smuzhiyun int e = errno;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun ksft_test_result_fail("set_sve(FPSIMD): %s\n",
237*4882a593Smuzhiyun strerror(errno));
238*4882a593Smuzhiyun if (e == ESRCH)
239*4882a593Smuzhiyun goto disappeared;
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun goto error;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun vq = sve_vq_from_vl(sve->vl);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun newsvebufsz = SVE_PT_SVE_ZREG_OFFSET(vq, 1);
247*4882a593Smuzhiyun new_sve = newsvebuf = malloc(newsvebufsz);
248*4882a593Smuzhiyun if (!new_sve) {
249*4882a593Smuzhiyun errno = ENOMEM;
250*4882a593Smuzhiyun perror(NULL);
251*4882a593Smuzhiyun goto error;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun *new_sve = *sve;
255*4882a593Smuzhiyun new_sve->flags &= ~SVE_PT_REGS_MASK;
256*4882a593Smuzhiyun new_sve->flags |= SVE_PT_REGS_SVE;
257*4882a593Smuzhiyun memset((char *)new_sve + SVE_PT_SVE_ZREG_OFFSET(vq, 0),
258*4882a593Smuzhiyun 0, SVE_PT_SVE_ZREG_SIZE(vq));
259*4882a593Smuzhiyun new_sve->size = SVE_PT_SVE_ZREG_OFFSET(vq, 1);
260*4882a593Smuzhiyun if (set_sve(pid, new_sve)) {
261*4882a593Smuzhiyun int e = errno;
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun ksft_test_result_fail("set_sve(ZREG): %s\n", strerror(errno));
264*4882a593Smuzhiyun if (e == ESRCH)
265*4882a593Smuzhiyun goto disappeared;
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun goto error;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun new_sve = get_sve(pid, &newsvebuf, &newsvebufsz);
271*4882a593Smuzhiyun if (!new_sve) {
272*4882a593Smuzhiyun int e = errno;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun ksft_test_result_fail("get_sve(ZREG): %s\n", strerror(errno));
275*4882a593Smuzhiyun if (e == ESRCH)
276*4882a593Smuzhiyun goto disappeared;
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun goto error;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun ksft_test_result((new_sve->flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE,
282*4882a593Smuzhiyun "SVE registers\n");
283*4882a593Smuzhiyun if ((new_sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_SVE)
284*4882a593Smuzhiyun goto error;
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun dump_sve_regs(new_sve, 3, sizeof fpsimd->vregs[0]);
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun p = (unsigned char *)new_sve + SVE_PT_SVE_ZREG_OFFSET(vq, 1);
289*4882a593Smuzhiyun for (i = 0; i < sizeof fpsimd->vregs[0]; ++i) {
290*4882a593Smuzhiyun unsigned char expected = i;
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun if (__BYTE_ORDER == __BIG_ENDIAN)
293*4882a593Smuzhiyun expected = sizeof fpsimd->vregs[0] - 1 - expected;
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun ksft_test_result(p[i] == expected, "p[%d] == expected\n", i);
296*4882a593Smuzhiyun if (p[i] != expected)
297*4882a593Smuzhiyun goto error;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun ret = EXIT_SUCCESS;
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun error:
303*4882a593Smuzhiyun kill(child, SIGKILL);
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun disappeared:
306*4882a593Smuzhiyun return ret;
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun
main(void)309*4882a593Smuzhiyun int main(void)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun int ret = EXIT_SUCCESS;
312*4882a593Smuzhiyun __uint128_t v[NR_VREGS];
313*4882a593Smuzhiyun pid_t child;
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun ksft_print_header();
316*4882a593Smuzhiyun ksft_set_plan(20);
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
319*4882a593Smuzhiyun ksft_exit_skip("SVE not available\n");
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun sve_store_patterns(v);
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun if (!check_vregs(v))
324*4882a593Smuzhiyun ksft_exit_fail_msg("Initial check_vregs() failed\n");
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun child = fork();
327*4882a593Smuzhiyun if (!child)
328*4882a593Smuzhiyun return do_child();
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun if (do_parent(child))
331*4882a593Smuzhiyun ret = EXIT_FAILURE;
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun ksft_print_cnts();
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun return ret;
336*4882a593Smuzhiyun }
337