1 /* Test case for setting a memory-write unaligned watchpoint on aarch64. 2 3 This software is provided 'as-is', without any express or implied 4 warranty. In no event will the authors be held liable for any damages 5 arising from the use of this software. 6 7 Permission is granted to anyone to use this software for any purpose, 8 including commercial applications, and to alter it and redistribute it 9 freely. */ 10 11 #define _GNU_SOURCE 1 12 #include <sys/ptrace.h> 13 #include <assert.h> 14 #include <sys/wait.h> 15 #include <stddef.h> 16 #include <errno.h> 17 #include <sys/uio.h> 18 #include <elf.h> 19 20 static pid_t child; 21 22 static void 23 cleanup (void) 24 { 25 if (child > 0) 26 kill (child, SIGKILL); 27 child = 0; 28 } 29 30 /* Macros to extract fields from the hardware debug information word. */ 31 #define AARCH64_DEBUG_NUM_SLOTS(x) ((x) & 0xff) 32 #define AARCH64_DEBUG_ARCH(x) (((x) >> 8) & 0xff) 33 /* Macro for the expected version of the ARMv8-A debug architecture. */ 34 #define AARCH64_DEBUG_ARCH_V8 0x6 35 #define DR_CONTROL_ENABLED(ctrl) (((ctrl) & 0x1) == 1) 36 #define DR_CONTROL_LENGTH(ctrl) (((ctrl) >> 5) & 0xff) 37 38 static void 39 set_watchpoint (pid_t pid, volatile void *addr, unsigned len_mask) 40 { 41 struct user_hwdebug_state dreg_state; 42 struct iovec iov; 43 long l; 44 45 assert (len_mask >= 0x01); 46 assert (len_mask <= 0xff); 47 48 iov.iov_base = &dreg_state; 49 iov.iov_len = sizeof (dreg_state); 50 errno = 0; 51 l = ptrace (PTRACE_GETREGSET, pid, NT_ARM_HW_WATCH, &iov); 52 assert (l == 0); 53 assert (AARCH64_DEBUG_ARCH (dreg_state.dbg_info) == AARCH64_DEBUG_ARCH_V8); 54 assert (AARCH64_DEBUG_NUM_SLOTS (dreg_state.dbg_info) >= 1); 55 56 assert (!DR_CONTROL_ENABLED (dreg_state.dbg_regs[0].ctrl)); 57 dreg_state.dbg_regs[0].ctrl |= 1; 58 assert ( DR_CONTROL_ENABLED (dreg_state.dbg_regs[0].ctrl)); 59 60 assert (DR_CONTROL_LENGTH (dreg_state.dbg_regs[0].ctrl) == 0); 61 dreg_state.dbg_regs[0].ctrl |= len_mask << 5; 62 assert (DR_CONTROL_LENGTH (dreg_state.dbg_regs[0].ctrl) == len_mask); 63 64 dreg_state.dbg_regs[0].ctrl |= 2 << 3; // write 65 dreg_state.dbg_regs[0].ctrl |= 2 << 1; // enabled at el0 66 dreg_state.dbg_regs[0].addr = (uintptr_t) addr; 67 68 iov.iov_base = &dreg_state; 69 iov.iov_len = (offsetof (struct user_hwdebug_state, dbg_regs) 70 + sizeof (dreg_state.dbg_regs[0])); 71 errno = 0; 72 l = ptrace (PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov); 73 if (errno != 0) 74 error (1, errno, "PTRACE_SETREGSET: NT_ARM_HW_WATCH"); 75 assert (l == 0); 76 } 77 78 static volatile long long check; 79 80 int 81 main (void) 82 { 83 pid_t got_pid; 84 int i, status; 85 long l; 86 87 atexit (cleanup); 88 89 child = fork (); 90 assert (child >= 0); 91 if (child == 0) 92 { 93 l = ptrace (PTRACE_TRACEME, 0, NULL, NULL); 94 assert (l == 0); 95 i = raise (SIGUSR1); 96 assert (i == 0); 97 check = -1; 98 i = raise (SIGUSR2); 99 /* NOTREACHED */ 100 assert (0); 101 } 102 103 got_pid = waitpid (child, &status, 0); 104 assert (got_pid == child); 105 assert (WIFSTOPPED (status)); 106 assert (WSTOPSIG (status) == SIGUSR1); 107 108 /* Add a watchpoint to check. 109 Restart the child. It will write to check. 110 Check child has stopped on the watchpoint. */ 111 set_watchpoint (child, &check, 0x02); 112 113 errno = 0; 114 l = ptrace (PTRACE_CONT, child, 0l, 0l); 115 assert_perror (errno); 116 assert (l == 0); 117 118 got_pid = waitpid (child, &status, 0); 119 assert (got_pid == child); 120 assert (WIFSTOPPED (status)); 121 if (WSTOPSIG (status) == SIGUSR2) 122 { 123 /* We missed the watchpoint - unsupported by hardware? */ 124 cleanup (); 125 return 2; 126 } 127 assert (WSTOPSIG (status) == SIGTRAP); 128 129 return 0; 130 } 131