1 /* 2 * Copyright (C) 2016 Google, Inc. 3 * 4 * This software is licensed under the terms of the GNU General Public 5 * License version 2, as published by the Free Software Foundation, and 6 * may be copied, distributed, and modified under those terms. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * Original Code by Pavel Labath <labath@google.com> 14 * 15 * Code modified by Pratyush Anand <panand@redhat.com> 16 * for testing different byte select for each access size. 17 * 18 */ 19 20 #define _GNU_SOURCE 21 22 #include <asm/ptrace.h> 23 #include <sys/types.h> 24 #include <sys/wait.h> 25 #include <sys/ptrace.h> 26 #include <sys/param.h> 27 #include <sys/uio.h> 28 #include <stdint.h> 29 #include <stdbool.h> 30 #include <stddef.h> 31 #include <string.h> 32 #include <stdio.h> 33 #include <unistd.h> 34 #include <elf.h> 35 #include <errno.h> 36 #include <signal.h> 37 38 #include "../kselftest.h" 39 40 static volatile uint8_t var[96] __attribute__((__aligned__(32))); 41 42 static void child(int size, int wr) 43 { 44 volatile uint8_t *addr = &var[32 + wr]; 45 46 if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) { 47 ksft_print_msg( 48 "ptrace(PTRACE_TRACEME) failed: %s\n", 49 strerror(errno)); 50 _exit(1); 51 } 52 53 if (raise(SIGSTOP) != 0) { 54 ksft_print_msg( 55 "raise(SIGSTOP) failed: %s\n", strerror(errno)); 56 _exit(1); 57 } 58 59 if ((uintptr_t) addr % size) { 60 ksft_print_msg( 61 "Wrong address write for the given size: %s\n", 62 strerror(errno)); 63 _exit(1); 64 } 65 66 switch (size) { 67 case 1: 68 *addr = 47; 69 break; 70 case 2: 71 *(uint16_t *)addr = 47; 72 break; 73 case 4: 74 *(uint32_t *)addr = 47; 75 break; 76 case 8: 77 *(uint64_t *)addr = 47; 78 break; 79 case 16: 80 __asm__ volatile ("stp x29, x30, %0" : "=m" (addr[0])); 81 break; 82 case 32: 83 __asm__ volatile ("stp q29, q30, %0" : "=m" (addr[0])); 84 break; 85 } 86 87 _exit(0); 88 } 89 90 static bool set_watchpoint(pid_t pid, int size, int wp) 91 { 92 const volatile uint8_t *addr = &var[32 + wp]; 93 const int offset = (uintptr_t)addr % 8; 94 const unsigned int byte_mask = ((1 << size) - 1) << offset; 95 const unsigned int type = 2; /* Write */ 96 const unsigned int enable = 1; 97 const unsigned int control = byte_mask << 5 | type << 3 | enable; 98 struct user_hwdebug_state dreg_state; 99 struct iovec iov; 100 101 memset(&dreg_state, 0, sizeof(dreg_state)); 102 dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset); 103 dreg_state.dbg_regs[0].ctrl = control; 104 iov.iov_base = &dreg_state; 105 iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) + 106 sizeof(dreg_state.dbg_regs[0]); 107 if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0) 108 return true; 109 110 if (errno == EIO) 111 ksft_print_msg( 112 "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n", 113 strerror(errno)); 114 115 ksft_print_msg( 116 "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n", 117 strerror(errno)); 118 return false; 119 } 120 121 static bool arun_test(int wr_size, int wp_size, int wr, int wp) 122 { 123 int status; 124 siginfo_t siginfo; 125 pid_t pid = fork(); 126 pid_t wpid; 127 128 if (pid < 0) { 129 ksft_test_result_fail( 130 "fork() failed: %s\n", strerror(errno)); 131 return false; 132 } 133 if (pid == 0) 134 child(wr_size, wr); 135 136 wpid = waitpid(pid, &status, __WALL); 137 if (wpid != pid) { 138 ksft_print_msg( 139 "waitpid() failed: %s\n", strerror(errno)); 140 return false; 141 } 142 if (!WIFSTOPPED(status)) { 143 ksft_print_msg( 144 "child did not stop: %s\n", strerror(errno)); 145 return false; 146 } 147 if (WSTOPSIG(status) != SIGSTOP) { 148 ksft_print_msg("child did not stop with SIGSTOP\n"); 149 return false; 150 } 151 152 if (!set_watchpoint(pid, wp_size, wp)) 153 return false; 154 155 if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { 156 ksft_print_msg( 157 "ptrace(PTRACE_SINGLESTEP) failed: %s\n", 158 strerror(errno)); 159 return false; 160 } 161 162 alarm(3); 163 wpid = waitpid(pid, &status, __WALL); 164 if (wpid != pid) { 165 ksft_print_msg( 166 "waitpid() failed: %s\n", strerror(errno)); 167 return false; 168 } 169 alarm(0); 170 if (WIFEXITED(status)) { 171 ksft_print_msg("child did not single-step\n"); 172 return false; 173 } 174 if (!WIFSTOPPED(status)) { 175 ksft_print_msg("child did not stop\n"); 176 return false; 177 } 178 if (WSTOPSIG(status) != SIGTRAP) { 179 ksft_print_msg("child did not stop with SIGTRAP\n"); 180 return false; 181 } 182 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) { 183 ksft_print_msg( 184 "ptrace(PTRACE_GETSIGINFO): %s\n", 185 strerror(errno)); 186 return false; 187 } 188 if (siginfo.si_code != TRAP_HWBKPT) { 189 ksft_print_msg( 190 "Unexpected si_code %d\n", siginfo.si_code); 191 return false; 192 } 193 194 kill(pid, SIGKILL); 195 wpid = waitpid(pid, &status, 0); 196 if (wpid != pid) { 197 ksft_print_msg( 198 "waitpid() failed: %s\n", strerror(errno)); 199 return false; 200 } 201 return true; 202 } 203 204 static void sigalrm(int sig) 205 { 206 } 207 208 int main(int argc, char **argv) 209 { 210 int opt; 211 bool succeeded = true; 212 struct sigaction act; 213 int wr, wp, size; 214 bool result; 215 216 ksft_print_header(); 217 ksft_set_plan(213); 218 219 act.sa_handler = sigalrm; 220 sigemptyset(&act.sa_mask); 221 act.sa_flags = 0; 222 sigaction(SIGALRM, &act, NULL); 223 for (size = 1; size <= 32; size = size*2) { 224 for (wr = 0; wr <= 32; wr = wr + size) { 225 for (wp = wr - size; wp <= wr + size; wp = wp + size) { 226 result = run_test(size, MIN(size, 8), wr, wp); 227 if ((result && wr == wp) || 228 (!result && wr != wp)) 229 ksft_test_result_pass( 230 "Test size = %d write offset = %d watchpoint offset = %d\n", 231 size, wr, wp); 232 else { 233 ksft_test_result_fail( 234 "Test size = %d write offset = %d watchpoint offset = %d\n", 235 size, wr, wp); 236 succeeded = false; 237 } 238 } 239 } 240 } 241 242 for (size = 1; size <= 32; size = size*2) { 243 if (run_test(size, 8, -size, -8)) 244 ksft_test_result_pass( 245 "Test size = %d write offset = %d watchpoint offset = -8\n", 246 size, -size); 247 else { 248 ksft_test_result_fail( 249 "Test size = %d write offset = %d watchpoint offset = -8\n", 250 size, -size); 251 succeeded = false; 252 } 253 } 254 255 if (succeeded) 256 ksft_exit_pass(); 257 else 258 ksft_exit_fail(); 259 } 260