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