1ecaf4d3fSMark Brown // SPDX-License-Identifier: GPL-2.0-only
2ecaf4d3fSMark Brown /*
3ecaf4d3fSMark Brown * Copyright (C) 2022 ARM Limited.
4ecaf4d3fSMark Brown */
5ecaf4d3fSMark Brown #include <errno.h>
6ecaf4d3fSMark Brown #include <stdbool.h>
7ecaf4d3fSMark Brown #include <stddef.h>
8ecaf4d3fSMark Brown #include <stdio.h>
9ecaf4d3fSMark Brown #include <stdlib.h>
10ecaf4d3fSMark Brown #include <string.h>
11ecaf4d3fSMark Brown #include <unistd.h>
12ecaf4d3fSMark Brown #include <sys/auxv.h>
13ecaf4d3fSMark Brown #include <sys/prctl.h>
14ecaf4d3fSMark Brown #include <sys/ptrace.h>
15ecaf4d3fSMark Brown #include <sys/types.h>
16ecaf4d3fSMark Brown #include <sys/uio.h>
17ecaf4d3fSMark Brown #include <sys/wait.h>
18ecaf4d3fSMark Brown #include <asm/sigcontext.h>
19ecaf4d3fSMark Brown #include <asm/ptrace.h>
20ecaf4d3fSMark Brown
21ecaf4d3fSMark Brown #include "../../kselftest.h"
22ecaf4d3fSMark Brown
23cb5aa637SMark Brown #define EXPECTED_TESTS 11
24ecaf4d3fSMark Brown
252ddadec2SMark Brown #define MAX_TPIDRS 2
26ecaf4d3fSMark Brown
have_sme(void)27ecaf4d3fSMark Brown static bool have_sme(void)
28ecaf4d3fSMark Brown {
29ecaf4d3fSMark Brown return getauxval(AT_HWCAP2) & HWCAP2_SME;
30ecaf4d3fSMark Brown }
31ecaf4d3fSMark Brown
test_tpidr(pid_t child)32ecaf4d3fSMark Brown static void test_tpidr(pid_t child)
33ecaf4d3fSMark Brown {
34ecaf4d3fSMark Brown uint64_t read_val[MAX_TPIDRS];
35ecaf4d3fSMark Brown uint64_t write_val[MAX_TPIDRS];
36ecaf4d3fSMark Brown struct iovec read_iov, write_iov;
372ddadec2SMark Brown bool test_tpidr2 = false;
382ddadec2SMark Brown int ret, i;
39ecaf4d3fSMark Brown
40ecaf4d3fSMark Brown read_iov.iov_base = read_val;
41ecaf4d3fSMark Brown write_iov.iov_base = write_val;
42ecaf4d3fSMark Brown
43ecaf4d3fSMark Brown /* Should be able to read a single TPIDR... */
44ecaf4d3fSMark Brown read_iov.iov_len = sizeof(uint64_t);
45ecaf4d3fSMark Brown ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
46ecaf4d3fSMark Brown ksft_test_result(ret == 0, "read_tpidr_one\n");
47ecaf4d3fSMark Brown
48ecaf4d3fSMark Brown /* ...write a new value.. */
49ecaf4d3fSMark Brown write_iov.iov_len = sizeof(uint64_t);
50031d1f20SDev Jain write_val[0] = read_val[0] + 1;
51ecaf4d3fSMark Brown ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);
52ecaf4d3fSMark Brown ksft_test_result(ret == 0, "write_tpidr_one\n");
53ecaf4d3fSMark Brown
54ecaf4d3fSMark Brown /* ...then read it back */
55ecaf4d3fSMark Brown ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
56ecaf4d3fSMark Brown ksft_test_result(ret == 0 && write_val[0] == read_val[0],
57ecaf4d3fSMark Brown "verify_tpidr_one\n");
582ddadec2SMark Brown
592ddadec2SMark Brown /* If we have TPIDR2 we should be able to read it */
602ddadec2SMark Brown read_iov.iov_len = sizeof(read_val);
612ddadec2SMark Brown ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
622ddadec2SMark Brown if (ret == 0) {
632ddadec2SMark Brown /* If we have SME there should be two TPIDRs */
642ddadec2SMark Brown if (read_iov.iov_len >= sizeof(read_val))
652ddadec2SMark Brown test_tpidr2 = true;
662ddadec2SMark Brown
672ddadec2SMark Brown if (have_sme() && test_tpidr2) {
682ddadec2SMark Brown ksft_test_result(test_tpidr2, "count_tpidrs\n");
692ddadec2SMark Brown } else {
702ddadec2SMark Brown ksft_test_result(read_iov.iov_len % sizeof(uint64_t) == 0,
712ddadec2SMark Brown "count_tpidrs\n");
722ddadec2SMark Brown }
732ddadec2SMark Brown } else {
742ddadec2SMark Brown ksft_test_result_fail("count_tpidrs\n");
752ddadec2SMark Brown }
762ddadec2SMark Brown
772ddadec2SMark Brown if (test_tpidr2) {
782ddadec2SMark Brown /* Try to write new values to all known TPIDRs... */
792ddadec2SMark Brown write_iov.iov_len = sizeof(write_val);
802ddadec2SMark Brown for (i = 0; i < MAX_TPIDRS; i++)
812ddadec2SMark Brown write_val[i] = read_val[i] + 1;
822ddadec2SMark Brown ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);
832ddadec2SMark Brown
842ddadec2SMark Brown ksft_test_result(ret == 0 &&
852ddadec2SMark Brown write_iov.iov_len == sizeof(write_val),
862ddadec2SMark Brown "tpidr2_write\n");
872ddadec2SMark Brown
882ddadec2SMark Brown /* ...then read them back */
892ddadec2SMark Brown read_iov.iov_len = sizeof(read_val);
902ddadec2SMark Brown ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
912ddadec2SMark Brown
922ddadec2SMark Brown if (have_sme()) {
932ddadec2SMark Brown /* Should read back the written value */
942ddadec2SMark Brown ksft_test_result(ret == 0 &&
952ddadec2SMark Brown read_iov.iov_len >= sizeof(read_val) &&
962ddadec2SMark Brown memcmp(read_val, write_val,
972ddadec2SMark Brown sizeof(read_val)) == 0,
982ddadec2SMark Brown "tpidr2_read\n");
992ddadec2SMark Brown } else {
1002ddadec2SMark Brown /* TPIDR2 should read as zero */
1012ddadec2SMark Brown ksft_test_result(ret == 0 &&
1022ddadec2SMark Brown read_iov.iov_len >= sizeof(read_val) &&
1032ddadec2SMark Brown read_val[0] == write_val[0] &&
1042ddadec2SMark Brown read_val[1] == 0,
1052ddadec2SMark Brown "tpidr2_read\n");
1062ddadec2SMark Brown }
1072ddadec2SMark Brown
1082ddadec2SMark Brown /* Writing only TPIDR... */
1092ddadec2SMark Brown write_iov.iov_len = sizeof(uint64_t);
1102ddadec2SMark Brown memcpy(write_val, read_val, sizeof(read_val));
1112ddadec2SMark Brown write_val[0] += 1;
1122ddadec2SMark Brown ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_TLS, &write_iov);
1132ddadec2SMark Brown
1142ddadec2SMark Brown if (ret == 0) {
1152ddadec2SMark Brown /* ...should leave TPIDR2 untouched */
1162ddadec2SMark Brown read_iov.iov_len = sizeof(read_val);
1172ddadec2SMark Brown ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS,
1182ddadec2SMark Brown &read_iov);
1192ddadec2SMark Brown
1202ddadec2SMark Brown ksft_test_result(ret == 0 &&
1212ddadec2SMark Brown read_iov.iov_len >= sizeof(read_val) &&
1222ddadec2SMark Brown memcmp(read_val, write_val,
1232ddadec2SMark Brown sizeof(read_val)) == 0,
1242ddadec2SMark Brown "write_tpidr_only\n");
1252ddadec2SMark Brown } else {
1262ddadec2SMark Brown ksft_test_result_fail("write_tpidr_only\n");
1272ddadec2SMark Brown }
1282ddadec2SMark Brown } else {
1292ddadec2SMark Brown ksft_test_result_skip("tpidr2_write\n");
1302ddadec2SMark Brown ksft_test_result_skip("tpidr2_read\n");
1312ddadec2SMark Brown ksft_test_result_skip("write_tpidr_only\n");
1322ddadec2SMark Brown }
133ecaf4d3fSMark Brown }
134ecaf4d3fSMark Brown
test_hw_debug(pid_t child,int type,const char * type_name)135cb5aa637SMark Brown static void test_hw_debug(pid_t child, int type, const char *type_name)
136cb5aa637SMark Brown {
137cb5aa637SMark Brown struct user_hwdebug_state state;
138cb5aa637SMark Brown struct iovec iov;
139cb5aa637SMark Brown int slots, arch, ret;
140cb5aa637SMark Brown
141cb5aa637SMark Brown iov.iov_len = sizeof(state);
142cb5aa637SMark Brown iov.iov_base = &state;
143cb5aa637SMark Brown
144cb5aa637SMark Brown /* Should be able to read the values */
145cb5aa637SMark Brown ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
146cb5aa637SMark Brown ksft_test_result(ret == 0, "read_%s\n", type_name);
147cb5aa637SMark Brown
148cb5aa637SMark Brown if (ret == 0) {
149cb5aa637SMark Brown /* Low 8 bits is the number of slots, next 4 bits the arch */
150cb5aa637SMark Brown slots = state.dbg_info & 0xff;
151cb5aa637SMark Brown arch = (state.dbg_info >> 8) & 0xf;
152cb5aa637SMark Brown
153cb5aa637SMark Brown ksft_print_msg("%s version %d with %d slots\n", type_name,
154cb5aa637SMark Brown arch, slots);
155cb5aa637SMark Brown
156cb5aa637SMark Brown /* Zero is not currently architecturally valid */
157cb5aa637SMark Brown ksft_test_result(arch, "%s_arch_set\n", type_name);
158cb5aa637SMark Brown } else {
1590c35e3bdSRemington Brasga ksft_test_result_skip("%s_arch_set\n", type_name);
160cb5aa637SMark Brown }
161cb5aa637SMark Brown }
162cb5aa637SMark Brown
do_child(void)163ecaf4d3fSMark Brown static int do_child(void)
164ecaf4d3fSMark Brown {
165ecaf4d3fSMark Brown if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
166*d736d4fcSDev Jain ksft_exit_fail_perror("PTRACE_TRACEME");
167ecaf4d3fSMark Brown
168ecaf4d3fSMark Brown if (raise(SIGSTOP))
169*d736d4fcSDev Jain ksft_exit_fail_perror("raise(SIGSTOP)");
170ecaf4d3fSMark Brown
171ecaf4d3fSMark Brown return EXIT_SUCCESS;
172ecaf4d3fSMark Brown }
173ecaf4d3fSMark Brown
do_parent(pid_t child)174ecaf4d3fSMark Brown static int do_parent(pid_t child)
175ecaf4d3fSMark Brown {
176ecaf4d3fSMark Brown int ret = EXIT_FAILURE;
177ecaf4d3fSMark Brown pid_t pid;
178ecaf4d3fSMark Brown int status;
179ecaf4d3fSMark Brown siginfo_t si;
180ecaf4d3fSMark Brown
181ecaf4d3fSMark Brown /* Attach to the child */
182ecaf4d3fSMark Brown while (1) {
183ecaf4d3fSMark Brown int sig;
184ecaf4d3fSMark Brown
185ecaf4d3fSMark Brown pid = wait(&status);
186ecaf4d3fSMark Brown if (pid == -1) {
187ecaf4d3fSMark Brown perror("wait");
188ecaf4d3fSMark Brown goto error;
189ecaf4d3fSMark Brown }
190ecaf4d3fSMark Brown
191ecaf4d3fSMark Brown /*
192ecaf4d3fSMark Brown * This should never happen but it's hard to flag in
193ecaf4d3fSMark Brown * the framework.
194ecaf4d3fSMark Brown */
195ecaf4d3fSMark Brown if (pid != child)
196ecaf4d3fSMark Brown continue;
197ecaf4d3fSMark Brown
198ecaf4d3fSMark Brown if (WIFEXITED(status) || WIFSIGNALED(status))
199ecaf4d3fSMark Brown ksft_exit_fail_msg("Child died unexpectedly\n");
200ecaf4d3fSMark Brown
201ecaf4d3fSMark Brown if (!WIFSTOPPED(status))
202ecaf4d3fSMark Brown goto error;
203ecaf4d3fSMark Brown
204ecaf4d3fSMark Brown sig = WSTOPSIG(status);
205ecaf4d3fSMark Brown
206ecaf4d3fSMark Brown if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
207ecaf4d3fSMark Brown if (errno == ESRCH)
208ecaf4d3fSMark Brown goto disappeared;
209ecaf4d3fSMark Brown
210ecaf4d3fSMark Brown if (errno == EINVAL) {
211ecaf4d3fSMark Brown sig = 0; /* bust group-stop */
212ecaf4d3fSMark Brown goto cont;
213ecaf4d3fSMark Brown }
214ecaf4d3fSMark Brown
215ecaf4d3fSMark Brown ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
216ecaf4d3fSMark Brown strerror(errno));
217ecaf4d3fSMark Brown goto error;
218ecaf4d3fSMark Brown }
219ecaf4d3fSMark Brown
220ecaf4d3fSMark Brown if (sig == SIGSTOP && si.si_code == SI_TKILL &&
221ecaf4d3fSMark Brown si.si_pid == pid)
222ecaf4d3fSMark Brown break;
223ecaf4d3fSMark Brown
224ecaf4d3fSMark Brown cont:
225ecaf4d3fSMark Brown if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
226ecaf4d3fSMark Brown if (errno == ESRCH)
227ecaf4d3fSMark Brown goto disappeared;
228ecaf4d3fSMark Brown
229ecaf4d3fSMark Brown ksft_test_result_fail("PTRACE_CONT: %s\n",
230ecaf4d3fSMark Brown strerror(errno));
231ecaf4d3fSMark Brown goto error;
232ecaf4d3fSMark Brown }
233ecaf4d3fSMark Brown }
234ecaf4d3fSMark Brown
235ecaf4d3fSMark Brown ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
236ecaf4d3fSMark Brown
237ecaf4d3fSMark Brown test_tpidr(child);
238cb5aa637SMark Brown test_hw_debug(child, NT_ARM_HW_WATCH, "NT_ARM_HW_WATCH");
239cb5aa637SMark Brown test_hw_debug(child, NT_ARM_HW_BREAK, "NT_ARM_HW_BREAK");
240ecaf4d3fSMark Brown
241ecaf4d3fSMark Brown ret = EXIT_SUCCESS;
242ecaf4d3fSMark Brown
243ecaf4d3fSMark Brown error:
244ecaf4d3fSMark Brown kill(child, SIGKILL);
245ecaf4d3fSMark Brown
246ecaf4d3fSMark Brown disappeared:
247ecaf4d3fSMark Brown return ret;
248ecaf4d3fSMark Brown }
249ecaf4d3fSMark Brown
main(void)250ecaf4d3fSMark Brown int main(void)
251ecaf4d3fSMark Brown {
252ecaf4d3fSMark Brown int ret = EXIT_SUCCESS;
253ecaf4d3fSMark Brown pid_t child;
254ecaf4d3fSMark Brown
255ecaf4d3fSMark Brown srandom(getpid());
256ecaf4d3fSMark Brown
257ecaf4d3fSMark Brown ksft_print_header();
258ecaf4d3fSMark Brown
259ecaf4d3fSMark Brown ksft_set_plan(EXPECTED_TESTS);
260ecaf4d3fSMark Brown
261ecaf4d3fSMark Brown child = fork();
262ecaf4d3fSMark Brown if (!child)
263ecaf4d3fSMark Brown return do_child();
264ecaf4d3fSMark Brown
265ecaf4d3fSMark Brown if (do_parent(child))
266ecaf4d3fSMark Brown ret = EXIT_FAILURE;
267ecaf4d3fSMark Brown
268ecaf4d3fSMark Brown ksft_print_cnts();
269ecaf4d3fSMark Brown
270ecaf4d3fSMark Brown return ret;
271ecaf4d3fSMark Brown }
272