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 11 24 25 #define MAX_TPIDRS 2 26 27 static bool have_sme(void) 28 { 29 return getauxval(AT_HWCAP2) & HWCAP2_SME; 30 } 31 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 135 static void test_hw_debug(pid_t child, int type, const char *type_name) 136 { 137 struct user_hwdebug_state state; 138 struct iovec iov; 139 int slots, arch, ret; 140 141 iov.iov_len = sizeof(state); 142 iov.iov_base = &state; 143 144 /* Should be able to read the values */ 145 ret = ptrace(PTRACE_GETREGSET, child, type, &iov); 146 ksft_test_result(ret == 0, "read_%s\n", type_name); 147 148 if (ret == 0) { 149 /* Low 8 bits is the number of slots, next 4 bits the arch */ 150 slots = state.dbg_info & 0xff; 151 arch = (state.dbg_info >> 8) & 0xf; 152 153 ksft_print_msg("%s version %d with %d slots\n", type_name, 154 arch, slots); 155 156 /* Zero is not currently architecturally valid */ 157 ksft_test_result(arch, "%s_arch_set\n", type_name); 158 } else { 159 ksft_test_result_skip("%s_arch_set\n"); 160 } 161 } 162 163 static int do_child(void) 164 { 165 if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) 166 ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno)); 167 168 if (raise(SIGSTOP)) 169 ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno)); 170 171 return EXIT_SUCCESS; 172 } 173 174 static int do_parent(pid_t child) 175 { 176 int ret = EXIT_FAILURE; 177 pid_t pid; 178 int status; 179 siginfo_t si; 180 181 /* Attach to the child */ 182 while (1) { 183 int sig; 184 185 pid = wait(&status); 186 if (pid == -1) { 187 perror("wait"); 188 goto error; 189 } 190 191 /* 192 * This should never happen but it's hard to flag in 193 * the framework. 194 */ 195 if (pid != child) 196 continue; 197 198 if (WIFEXITED(status) || WIFSIGNALED(status)) 199 ksft_exit_fail_msg("Child died unexpectedly\n"); 200 201 if (!WIFSTOPPED(status)) 202 goto error; 203 204 sig = WSTOPSIG(status); 205 206 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) { 207 if (errno == ESRCH) 208 goto disappeared; 209 210 if (errno == EINVAL) { 211 sig = 0; /* bust group-stop */ 212 goto cont; 213 } 214 215 ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n", 216 strerror(errno)); 217 goto error; 218 } 219 220 if (sig == SIGSTOP && si.si_code == SI_TKILL && 221 si.si_pid == pid) 222 break; 223 224 cont: 225 if (ptrace(PTRACE_CONT, pid, NULL, sig)) { 226 if (errno == ESRCH) 227 goto disappeared; 228 229 ksft_test_result_fail("PTRACE_CONT: %s\n", 230 strerror(errno)); 231 goto error; 232 } 233 } 234 235 ksft_print_msg("Parent is %d, child is %d\n", getpid(), child); 236 237 test_tpidr(child); 238 test_hw_debug(child, NT_ARM_HW_WATCH, "NT_ARM_HW_WATCH"); 239 test_hw_debug(child, NT_ARM_HW_BREAK, "NT_ARM_HW_BREAK"); 240 241 ret = EXIT_SUCCESS; 242 243 error: 244 kill(child, SIGKILL); 245 246 disappeared: 247 return ret; 248 } 249 250 int main(void) 251 { 252 int ret = EXIT_SUCCESS; 253 pid_t child; 254 255 srandom(getpid()); 256 257 ksft_print_header(); 258 259 ksft_set_plan(EXPECTED_TESTS); 260 261 child = fork(); 262 if (!child) 263 return do_child(); 264 265 if (do_parent(child)) 266 ret = EXIT_FAILURE; 267 268 ksft_print_cnts(); 269 270 return ret; 271 } 272