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