Skip to content

Commit 4e1aa1a

Browse files
brooniectmarinas
authored andcommitted
kselftest/arm64: Add coverage of the ZT ptrace regset
Add coverage of the ZT ptrace interface. Signed-off-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20221208-arm64-sme2-v4-20-f2fa0aef982f@kernel.org Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
1 parent 49886aa commit 4e1aa1a

3 files changed

Lines changed: 368 additions & 0 deletions

File tree

tools/testing/selftests/arm64/fp/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ vlset
1212
za-fork
1313
za-ptrace
1414
za-test
15+
zt-ptrace
1516
zt-test

tools/testing/selftests/arm64/fp/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ TEST_GEN_PROGS_EXTENDED := fp-pidbench fpsimd-test \
1414
sve-test \
1515
ssve-test \
1616
za-test \
17+
zt-ptrace \
1718
zt-test \
1819
vlset
1920
TEST_PROGS_EXTENDED := fpsimd-stress sve-stress ssve-stress za-stress
@@ -42,6 +43,7 @@ $(OUTPUT)/za-fork: za-fork.c $(OUTPUT)/za-fork-asm.o
4243
$(OUTPUT)/za-ptrace: za-ptrace.c
4344
$(OUTPUT)/za-test: za-test.S $(OUTPUT)/asm-utils.o
4445
$(CC) -nostdlib $^ -o $@
46+
$(OUTPUT)/zt-ptrace: zt-ptrace.c
4547
$(OUTPUT)/zt-test: zt-test.S $(OUTPUT)/asm-utils.o
4648
$(CC) -nostdlib $^ -o $@
4749

Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (C) 2021 ARM Limited.
4+
*/
5+
#include <errno.h>
6+
#include <stdbool.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/prctl.h>
14+
#include <sys/ptrace.h>
15+
#include <sys/types.h>
16+
#include <sys/uio.h>
17+
#include <sys/wait.h>
18+
#include <asm/sigcontext.h>
19+
#include <asm/ptrace.h>
20+
21+
#include "../../kselftest.h"
22+
23+
/* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */
24+
#ifndef NT_ARM_ZA
25+
#define NT_ARM_ZA 0x40c
26+
#endif
27+
#ifndef NT_ARM_ZT
28+
#define NT_ARM_ZT 0x40d
29+
#endif
30+
31+
#define EXPECTED_TESTS 3
32+
33+
static int sme_vl;
34+
35+
static void fill_buf(char *buf, size_t size)
36+
{
37+
int i;
38+
39+
for (i = 0; i < size; i++)
40+
buf[i] = random();
41+
}
42+
43+
static int do_child(void)
44+
{
45+
if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
46+
ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));
47+
48+
if (raise(SIGSTOP))
49+
ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));
50+
51+
return EXIT_SUCCESS;
52+
}
53+
54+
static struct user_za_header *get_za(pid_t pid, void **buf, size_t *size)
55+
{
56+
struct user_za_header *za;
57+
void *p;
58+
size_t sz = sizeof(*za);
59+
struct iovec iov;
60+
61+
while (1) {
62+
if (*size < sz) {
63+
p = realloc(*buf, sz);
64+
if (!p) {
65+
errno = ENOMEM;
66+
goto error;
67+
}
68+
69+
*buf = p;
70+
*size = sz;
71+
}
72+
73+
iov.iov_base = *buf;
74+
iov.iov_len = sz;
75+
if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZA, &iov))
76+
goto error;
77+
78+
za = *buf;
79+
if (za->size <= sz)
80+
break;
81+
82+
sz = za->size;
83+
}
84+
85+
return za;
86+
87+
error:
88+
return NULL;
89+
}
90+
91+
static int set_za(pid_t pid, const struct user_za_header *za)
92+
{
93+
struct iovec iov;
94+
95+
iov.iov_base = (void *)za;
96+
iov.iov_len = za->size;
97+
return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZA, &iov);
98+
}
99+
100+
static int get_zt(pid_t pid, char zt[ZT_SIG_REG_BYTES])
101+
{
102+
struct iovec iov;
103+
104+
iov.iov_base = zt;
105+
iov.iov_len = ZT_SIG_REG_BYTES;
106+
return ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZT, &iov);
107+
}
108+
109+
110+
static int set_zt(pid_t pid, const char zt[ZT_SIG_REG_BYTES])
111+
{
112+
struct iovec iov;
113+
114+
iov.iov_base = (void *)zt;
115+
iov.iov_len = ZT_SIG_REG_BYTES;
116+
return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZT, &iov);
117+
}
118+
119+
/* Reading with ZA disabled returns all zeros */
120+
static void ptrace_za_disabled_read_zt(pid_t child)
121+
{
122+
struct user_za_header za;
123+
char zt[ZT_SIG_REG_BYTES];
124+
int ret, i;
125+
bool fail = false;
126+
127+
/* Disable PSTATE.ZA using the ZA interface */
128+
memset(&za, 0, sizeof(za));
129+
za.vl = sme_vl;
130+
za.size = sizeof(za);
131+
132+
ret = set_za(child, &za);
133+
if (ret != 0) {
134+
ksft_print_msg("Failed to disable ZA\n");
135+
fail = true;
136+
}
137+
138+
/* Read back ZT */
139+
ret = get_zt(child, zt);
140+
if (ret != 0) {
141+
ksft_print_msg("Failed to read ZT\n");
142+
fail = true;
143+
}
144+
145+
for (i = 0; i < ARRAY_SIZE(zt); i++) {
146+
if (zt[i]) {
147+
ksft_print_msg("zt[%d]: 0x%x != 0\n", i, zt[i]);
148+
fail = true;
149+
}
150+
}
151+
152+
ksft_test_result(!fail, "ptrace_za_disabled_read_zt\n");
153+
}
154+
155+
/* Writing then reading ZT should return the data written */
156+
static void ptrace_set_get_zt(pid_t child)
157+
{
158+
char zt_in[ZT_SIG_REG_BYTES];
159+
char zt_out[ZT_SIG_REG_BYTES];
160+
int ret, i;
161+
bool fail = false;
162+
163+
fill_buf(zt_in, sizeof(zt_in));
164+
165+
ret = set_zt(child, zt_in);
166+
if (ret != 0) {
167+
ksft_print_msg("Failed to set ZT\n");
168+
fail = true;
169+
}
170+
171+
ret = get_zt(child, zt_out);
172+
if (ret != 0) {
173+
ksft_print_msg("Failed to read ZT\n");
174+
fail = true;
175+
}
176+
177+
for (i = 0; i < ARRAY_SIZE(zt_in); i++) {
178+
if (zt_in[i] != zt_out[i]) {
179+
ksft_print_msg("zt[%d]: 0x%x != 0x%x\n", i,
180+
zt_in[i], zt_out[i]);
181+
fail = true;
182+
}
183+
}
184+
185+
ksft_test_result(!fail, "ptrace_set_get_zt\n");
186+
}
187+
188+
/* Writing ZT should set PSTATE.ZA */
189+
static void ptrace_enable_za_via_zt(pid_t child)
190+
{
191+
struct user_za_header za_in;
192+
struct user_za_header *za_out;
193+
char zt[ZT_SIG_REG_BYTES];
194+
char *za_data;
195+
size_t za_out_size;
196+
int ret, i, vq;
197+
bool fail = false;
198+
199+
/* Disable PSTATE.ZA using the ZA interface */
200+
memset(&za_in, 0, sizeof(za_in));
201+
za_in.vl = sme_vl;
202+
za_in.size = sizeof(za_in);
203+
204+
ret = set_za(child, &za_in);
205+
if (ret != 0) {
206+
ksft_print_msg("Failed to disable ZA\n");
207+
fail = true;
208+
}
209+
210+
/* Write ZT */
211+
fill_buf(zt, sizeof(zt));
212+
ret = set_zt(child, zt);
213+
if (ret != 0) {
214+
ksft_print_msg("Failed to set ZT\n");
215+
fail = true;
216+
}
217+
218+
/* Read back ZA and check for register data */
219+
za_out = NULL;
220+
za_out_size = 0;
221+
if (get_za(child, (void **)&za_out, &za_out_size)) {
222+
/* Should have an unchanged VL */
223+
if (za_out->vl != sme_vl) {
224+
ksft_print_msg("VL changed from %d to %d\n",
225+
sme_vl, za_out->vl);
226+
fail = true;
227+
}
228+
vq = __sve_vq_from_vl(za_out->vl);
229+
za_data = (char *)za_out + ZA_PT_ZA_OFFSET;
230+
231+
/* Should have register data */
232+
if (za_out->size < ZA_PT_SIZE(vq)) {
233+
ksft_print_msg("ZA data less than expected: %u < %u\n",
234+
za_out->size, ZA_PT_SIZE(vq));
235+
fail = true;
236+
vq = 0;
237+
}
238+
239+
/* That register data should be non-zero */
240+
for (i = 0; i < ZA_PT_ZA_SIZE(vq); i++) {
241+
if (za_data[i]) {
242+
ksft_print_msg("ZA byte %d is %x\n",
243+
i, za_data[i]);
244+
fail = true;
245+
}
246+
}
247+
} else {
248+
ksft_print_msg("Failed to read ZA\n");
249+
fail = true;
250+
}
251+
252+
ksft_test_result(!fail, "ptrace_enable_za_via_zt\n");
253+
}
254+
255+
static int do_parent(pid_t child)
256+
{
257+
int ret = EXIT_FAILURE;
258+
pid_t pid;
259+
int status;
260+
siginfo_t si;
261+
262+
/* Attach to the child */
263+
while (1) {
264+
int sig;
265+
266+
pid = wait(&status);
267+
if (pid == -1) {
268+
perror("wait");
269+
goto error;
270+
}
271+
272+
/*
273+
* This should never happen but it's hard to flag in
274+
* the framework.
275+
*/
276+
if (pid != child)
277+
continue;
278+
279+
if (WIFEXITED(status) || WIFSIGNALED(status))
280+
ksft_exit_fail_msg("Child died unexpectedly\n");
281+
282+
if (!WIFSTOPPED(status))
283+
goto error;
284+
285+
sig = WSTOPSIG(status);
286+
287+
if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
288+
if (errno == ESRCH)
289+
goto disappeared;
290+
291+
if (errno == EINVAL) {
292+
sig = 0; /* bust group-stop */
293+
goto cont;
294+
}
295+
296+
ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
297+
strerror(errno));
298+
goto error;
299+
}
300+
301+
if (sig == SIGSTOP && si.si_code == SI_TKILL &&
302+
si.si_pid == pid)
303+
break;
304+
305+
cont:
306+
if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
307+
if (errno == ESRCH)
308+
goto disappeared;
309+
310+
ksft_test_result_fail("PTRACE_CONT: %s\n",
311+
strerror(errno));
312+
goto error;
313+
}
314+
}
315+
316+
ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
317+
318+
ptrace_za_disabled_read_zt(child);
319+
ptrace_set_get_zt(child);
320+
ptrace_enable_za_via_zt(child);
321+
322+
ret = EXIT_SUCCESS;
323+
324+
error:
325+
kill(child, SIGKILL);
326+
327+
disappeared:
328+
return ret;
329+
}
330+
331+
int main(void)
332+
{
333+
int ret = EXIT_SUCCESS;
334+
pid_t child;
335+
336+
srandom(getpid());
337+
338+
ksft_print_header();
339+
340+
if (!(getauxval(AT_HWCAP2) & HWCAP2_SME2)) {
341+
ksft_set_plan(1);
342+
ksft_exit_skip("SME2 not available\n");
343+
}
344+
345+
/* We need a valid SME VL to enable/disable ZA */
346+
sme_vl = prctl(PR_SME_GET_VL);
347+
if (sme_vl == -1) {
348+
ksft_set_plan(1);
349+
ksft_exit_skip("Failed to read SME VL: %d (%s)\n",
350+
errno, strerror(errno));
351+
}
352+
353+
ksft_set_plan(EXPECTED_TESTS);
354+
355+
child = fork();
356+
if (!child)
357+
return do_child();
358+
359+
if (do_parent(child))
360+
ret = EXIT_FAILURE;
361+
362+
ksft_print_cnts();
363+
364+
return ret;
365+
}

0 commit comments

Comments
 (0)