xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/arm64/fp/sve-ptrace.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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