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