WIP FPC-III support
[linux/fpc-iii.git] / tools / testing / selftests / arm64 / fp / sve-ptrace.c
blobb2282be6f9384cb193e6ed9f54b75c3b6e5d1e75
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2015-2020 ARM Limited.
4 * Original author: Dave Martin <Dave.Martin@arm.com>
5 */
6 #include <errno.h>
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <sys/auxv.h>
13 #include <sys/ptrace.h>
14 #include <sys/types.h>
15 #include <sys/uio.h>
16 #include <sys/wait.h>
17 #include <asm/sigcontext.h>
18 #include <asm/ptrace.h>
20 #include "../../kselftest.h"
22 /* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */
23 #ifndef NT_ARM_SVE
24 #define NT_ARM_SVE 0x405
25 #endif
27 /* Number of registers filled in by sve_store_patterns */
28 #define NR_VREGS 5
30 void sve_store_patterns(__uint128_t v[NR_VREGS]);
32 static void dump(const void *buf, size_t size)
34 size_t i;
35 const unsigned char *p = buf;
37 for (i = 0; i < size; ++i)
38 printf(" %.2x", *p++);
41 static int check_vregs(const __uint128_t vregs[NR_VREGS])
43 int i;
44 int ok = 1;
46 for (i = 0; i < NR_VREGS; ++i) {
47 printf("# v[%d]:", i);
48 dump(&vregs[i], sizeof vregs[i]);
49 putchar('\n');
51 if (vregs[i] != vregs[0])
52 ok = 0;
55 return ok;
58 static int do_child(void)
60 if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
61 ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));
63 if (raise(SIGSTOP))
64 ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));
66 return EXIT_SUCCESS;
69 static struct user_sve_header *get_sve(pid_t pid, void **buf, size_t *size)
71 struct user_sve_header *sve;
72 void *p;
73 size_t sz = sizeof *sve;
74 struct iovec iov;
76 while (1) {
77 if (*size < sz) {
78 p = realloc(*buf, sz);
79 if (!p) {
80 errno = ENOMEM;
81 goto error;
84 *buf = p;
85 *size = sz;
88 iov.iov_base = *buf;
89 iov.iov_len = sz;
90 if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_SVE, &iov))
91 goto error;
93 sve = *buf;
94 if (sve->size <= sz)
95 break;
97 sz = sve->size;
100 return sve;
102 error:
103 return NULL;
106 static int set_sve(pid_t pid, const struct user_sve_header *sve)
108 struct iovec iov;
110 iov.iov_base = (void *)sve;
111 iov.iov_len = sve->size;
112 return ptrace(PTRACE_SETREGSET, pid, NT_ARM_SVE, &iov);
115 static void dump_sve_regs(const struct user_sve_header *sve, unsigned int num,
116 unsigned int vlmax)
118 unsigned int vq;
119 unsigned int i;
121 if ((sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_SVE)
122 ksft_exit_fail_msg("Dumping non-SVE register\n");
124 if (vlmax > sve->vl)
125 vlmax = sve->vl;
127 vq = sve_vq_from_vl(sve->vl);
128 for (i = 0; i < num; ++i) {
129 printf("# z%u:", i);
130 dump((const char *)sve + SVE_PT_SVE_ZREG_OFFSET(vq, i),
131 vlmax);
132 printf("%s\n", vlmax == sve->vl ? "" : " ...");
136 static int do_parent(pid_t child)
138 int ret = EXIT_FAILURE;
139 pid_t pid;
140 int status;
141 siginfo_t si;
142 void *svebuf = NULL, *newsvebuf;
143 size_t svebufsz = 0, newsvebufsz;
144 struct user_sve_header *sve, *new_sve;
145 struct user_fpsimd_state *fpsimd;
146 unsigned int i, j;
147 unsigned char *p;
148 unsigned int vq;
150 /* Attach to the child */
151 while (1) {
152 int sig;
154 pid = wait(&status);
155 if (pid == -1) {
156 perror("wait");
157 goto error;
161 * This should never happen but it's hard to flag in
162 * the framework.
164 if (pid != child)
165 continue;
167 if (WIFEXITED(status) || WIFSIGNALED(status))
168 ksft_exit_fail_msg("Child died unexpectedly\n");
170 ksft_test_result(WIFSTOPPED(status), "WIFSTOPPED(%d)\n",
171 status);
172 if (!WIFSTOPPED(status))
173 goto error;
175 sig = WSTOPSIG(status);
177 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
178 if (errno == ESRCH)
179 goto disappeared;
181 if (errno == EINVAL) {
182 sig = 0; /* bust group-stop */
183 goto cont;
186 ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
187 strerror(errno));
188 goto error;
191 if (sig == SIGSTOP && si.si_code == SI_TKILL &&
192 si.si_pid == pid)
193 break;
195 cont:
196 if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
197 if (errno == ESRCH)
198 goto disappeared;
200 ksft_test_result_fail("PTRACE_CONT: %s\n",
201 strerror(errno));
202 goto error;
206 sve = get_sve(pid, &svebuf, &svebufsz);
207 if (!sve) {
208 int e = errno;
210 ksft_test_result_fail("get_sve: %s\n", strerror(errno));
211 if (e == ESRCH)
212 goto disappeared;
214 goto error;
215 } else {
216 ksft_test_result_pass("get_sve\n");
219 ksft_test_result((sve->flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD,
220 "FPSIMD registers\n");
221 if ((sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_FPSIMD)
222 goto error;
224 fpsimd = (struct user_fpsimd_state *)((char *)sve +
225 SVE_PT_FPSIMD_OFFSET);
226 for (i = 0; i < 32; ++i) {
227 p = (unsigned char *)&fpsimd->vregs[i];
229 for (j = 0; j < sizeof fpsimd->vregs[i]; ++j)
230 p[j] = j;
233 if (set_sve(pid, sve)) {
234 int e = errno;
236 ksft_test_result_fail("set_sve(FPSIMD): %s\n",
237 strerror(errno));
238 if (e == ESRCH)
239 goto disappeared;
241 goto error;
244 vq = sve_vq_from_vl(sve->vl);
246 newsvebufsz = SVE_PT_SVE_ZREG_OFFSET(vq, 1);
247 new_sve = newsvebuf = malloc(newsvebufsz);
248 if (!new_sve) {
249 errno = ENOMEM;
250 perror(NULL);
251 goto error;
254 *new_sve = *sve;
255 new_sve->flags &= ~SVE_PT_REGS_MASK;
256 new_sve->flags |= SVE_PT_REGS_SVE;
257 memset((char *)new_sve + SVE_PT_SVE_ZREG_OFFSET(vq, 0),
258 0, SVE_PT_SVE_ZREG_SIZE(vq));
259 new_sve->size = SVE_PT_SVE_ZREG_OFFSET(vq, 1);
260 if (set_sve(pid, new_sve)) {
261 int e = errno;
263 ksft_test_result_fail("set_sve(ZREG): %s\n", strerror(errno));
264 if (e == ESRCH)
265 goto disappeared;
267 goto error;
270 new_sve = get_sve(pid, &newsvebuf, &newsvebufsz);
271 if (!new_sve) {
272 int e = errno;
274 ksft_test_result_fail("get_sve(ZREG): %s\n", strerror(errno));
275 if (e == ESRCH)
276 goto disappeared;
278 goto error;
281 ksft_test_result((new_sve->flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE,
282 "SVE registers\n");
283 if ((new_sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_SVE)
284 goto error;
286 dump_sve_regs(new_sve, 3, sizeof fpsimd->vregs[0]);
288 p = (unsigned char *)new_sve + SVE_PT_SVE_ZREG_OFFSET(vq, 1);
289 for (i = 0; i < sizeof fpsimd->vregs[0]; ++i) {
290 unsigned char expected = i;
292 if (__BYTE_ORDER == __BIG_ENDIAN)
293 expected = sizeof fpsimd->vregs[0] - 1 - expected;
295 ksft_test_result(p[i] == expected, "p[%d] == expected\n", i);
296 if (p[i] != expected)
297 goto error;
300 ret = EXIT_SUCCESS;
302 error:
303 kill(child, SIGKILL);
305 disappeared:
306 return ret;
309 int main(void)
311 int ret = EXIT_SUCCESS;
312 __uint128_t v[NR_VREGS];
313 pid_t child;
315 ksft_print_header();
316 ksft_set_plan(20);
318 if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
319 ksft_exit_skip("SVE not available\n");
321 sve_store_patterns(v);
323 if (!check_vregs(v))
324 ksft_exit_fail_msg("Initial check_vregs() failed\n");
326 child = fork();
327 if (!child)
328 return do_child();
330 if (do_parent(child))
331 ret = EXIT_FAILURE;
333 ksft_print_cnts();
335 return 0;