1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2022 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 #define EXPECTED_TESTS 7
24
25 #define MAX_TPIDRS 2
26
have_sme(void)27 static bool have_sme(void)
28 {
29 return getauxval(AT_HWCAP2) & HWCAP2_SME;
30 }
31
test_tpidr(pid_t child)32 static void test_tpidr(pid_t child)
33 {
34 uint64_t read_val[MAX_TPIDRS];
35 uint64_t write_val[MAX_TPIDRS];
36 struct iovec read_iov, write_iov;
37 bool test_tpidr2 = false;
38 int ret, i;
39
40 read_iov.iov_base = read_val;
41 write_iov.iov_base = write_val;
42
43 /* Should be able to read a single TPIDR... */
44 read_iov.iov_len = sizeof(uint64_t);
45 ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
46 ksft_test_result(ret == 0, "read_tpidr_one\n");
47
48 /* ...write a new value.. */
49 write_iov.iov_len = sizeof(uint64_t);
50 write_val[0] = read_val[0]++;
51 ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);
52 ksft_test_result(ret == 0, "write_tpidr_one\n");
53
54 /* ...then read it back */
55 ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
56 ksft_test_result(ret == 0 && write_val[0] == read_val[0],
57 "verify_tpidr_one\n");
58
59 /* If we have TPIDR2 we should be able to read it */
60 read_iov.iov_len = sizeof(read_val);
61 ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
62 if (ret == 0) {
63 /* If we have SME there should be two TPIDRs */
64 if (read_iov.iov_len >= sizeof(read_val))
65 test_tpidr2 = true;
66
67 if (have_sme() && test_tpidr2) {
68 ksft_test_result(test_tpidr2, "count_tpidrs\n");
69 } else {
70 ksft_test_result(read_iov.iov_len % sizeof(uint64_t) == 0,
71 "count_tpidrs\n");
72 }
73 } else {
74 ksft_test_result_fail("count_tpidrs\n");
75 }
76
77 if (test_tpidr2) {
78 /* Try to write new values to all known TPIDRs... */
79 write_iov.iov_len = sizeof(write_val);
80 for (i = 0; i < MAX_TPIDRS; i++)
81 write_val[i] = read_val[i] + 1;
82 ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);
83
84 ksft_test_result(ret == 0 &&
85 write_iov.iov_len == sizeof(write_val),
86 "tpidr2_write\n");
87
88 /* ...then read them back */
89 read_iov.iov_len = sizeof(read_val);
90 ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
91
92 if (have_sme()) {
93 /* Should read back the written value */
94 ksft_test_result(ret == 0 &&
95 read_iov.iov_len >= sizeof(read_val) &&
96 memcmp(read_val, write_val,
97 sizeof(read_val)) == 0,
98 "tpidr2_read\n");
99 } else {
100 /* TPIDR2 should read as zero */
101 ksft_test_result(ret == 0 &&
102 read_iov.iov_len >= sizeof(read_val) &&
103 read_val[0] == write_val[0] &&
104 read_val[1] == 0,
105 "tpidr2_read\n");
106 }
107
108 /* Writing only TPIDR... */
109 write_iov.iov_len = sizeof(uint64_t);
110 memcpy(write_val, read_val, sizeof(read_val));
111 write_val[0] += 1;
112 ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);
113
114 if (ret == 0) {
115 /* ...should leave TPIDR2 untouched */
116 read_iov.iov_len = sizeof(read_val);
117 ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS,
118 &read_iov);
119
120 ksft_test_result(ret == 0 &&
121 read_iov.iov_len >= sizeof(read_val) &&
122 memcmp(read_val, write_val,
123 sizeof(read_val)) == 0,
124 "write_tpidr_only\n");
125 } else {
126 ksft_test_result_fail("write_tpidr_only\n");
127 }
128 } else {
129 ksft_test_result_skip("tpidr2_write\n");
130 ksft_test_result_skip("tpidr2_read\n");
131 ksft_test_result_skip("write_tpidr_only\n");
132 }
133 }
134
do_child(void)135 static int do_child(void)
136 {
137 if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
138 ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));
139
140 if (raise(SIGSTOP))
141 ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));
142
143 return EXIT_SUCCESS;
144 }
145
do_parent(pid_t child)146 static int do_parent(pid_t child)
147 {
148 int ret = EXIT_FAILURE;
149 pid_t pid;
150 int status;
151 siginfo_t si;
152
153 /* Attach to the child */
154 while (1) {
155 int sig;
156
157 pid = wait(&status);
158 if (pid == -1) {
159 perror("wait");
160 goto error;
161 }
162
163 /*
164 * This should never happen but it's hard to flag in
165 * the framework.
166 */
167 if (pid != child)
168 continue;
169
170 if (WIFEXITED(status) || WIFSIGNALED(status))
171 ksft_exit_fail_msg("Child died unexpectedly\n");
172
173 if (!WIFSTOPPED(status))
174 goto error;
175
176 sig = WSTOPSIG(status);
177
178 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
179 if (errno == ESRCH)
180 goto disappeared;
181
182 if (errno == EINVAL) {
183 sig = 0; /* bust group-stop */
184 goto cont;
185 }
186
187 ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
188 strerror(errno));
189 goto error;
190 }
191
192 if (sig == SIGSTOP && si.si_code == SI_TKILL &&
193 si.si_pid == pid)
194 break;
195
196 cont:
197 if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
198 if (errno == ESRCH)
199 goto disappeared;
200
201 ksft_test_result_fail("PTRACE_CONT: %s\n",
202 strerror(errno));
203 goto error;
204 }
205 }
206
207 ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
208
209 test_tpidr(child);
210
211 ret = EXIT_SUCCESS;
212
213 error:
214 kill(child, SIGKILL);
215
216 disappeared:
217 return ret;
218 }
219
main(void)220 int main(void)
221 {
222 int ret = EXIT_SUCCESS;
223 pid_t child;
224
225 srandom(getpid());
226
227 ksft_print_header();
228
229 ksft_set_plan(EXPECTED_TESTS);
230
231 child = fork();
232 if (!child)
233 return do_child();
234
235 if (do_parent(child))
236 ret = EXIT_FAILURE;
237
238 ksft_print_cnts();
239
240 return ret;
241 }
242