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