0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #define _GNU_SOURCE
0012
0013 #include <asm/ptrace.h>
0014 #include <sys/types.h>
0015 #include <sys/wait.h>
0016 #include <sys/ptrace.h>
0017 #include <sys/param.h>
0018 #include <sys/uio.h>
0019 #include <stdint.h>
0020 #include <stdbool.h>
0021 #include <stddef.h>
0022 #include <string.h>
0023 #include <stdio.h>
0024 #include <unistd.h>
0025 #include <elf.h>
0026 #include <errno.h>
0027 #include <signal.h>
0028
0029 #include "../kselftest.h"
0030
0031 static volatile uint8_t var[96] __attribute__((__aligned__(32)));
0032
0033 static void child(int size, int wr)
0034 {
0035 volatile uint8_t *addr = &var[32 + wr];
0036
0037 if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) {
0038 ksft_print_msg(
0039 "ptrace(PTRACE_TRACEME) failed: %s\n",
0040 strerror(errno));
0041 _exit(1);
0042 }
0043
0044 if (raise(SIGSTOP) != 0) {
0045 ksft_print_msg(
0046 "raise(SIGSTOP) failed: %s\n", strerror(errno));
0047 _exit(1);
0048 }
0049
0050 if ((uintptr_t) addr % size) {
0051 ksft_print_msg(
0052 "Wrong address write for the given size: %s\n",
0053 strerror(errno));
0054 _exit(1);
0055 }
0056
0057 switch (size) {
0058 case 1:
0059 *addr = 47;
0060 break;
0061 case 2:
0062 *(uint16_t *)addr = 47;
0063 break;
0064 case 4:
0065 *(uint32_t *)addr = 47;
0066 break;
0067 case 8:
0068 *(uint64_t *)addr = 47;
0069 break;
0070 case 16:
0071 __asm__ volatile ("stp x29, x30, %0" : "=m" (addr[0]));
0072 break;
0073 case 32:
0074 __asm__ volatile ("stp q29, q30, %0" : "=m" (addr[0]));
0075 break;
0076 }
0077
0078 _exit(0);
0079 }
0080
0081 static bool set_watchpoint(pid_t pid, int size, int wp)
0082 {
0083 const volatile uint8_t *addr = &var[32 + wp];
0084 const int offset = (uintptr_t)addr % 8;
0085 const unsigned int byte_mask = ((1 << size) - 1) << offset;
0086 const unsigned int type = 2;
0087 const unsigned int enable = 1;
0088 const unsigned int control = byte_mask << 5 | type << 3 | enable;
0089 struct user_hwdebug_state dreg_state;
0090 struct iovec iov;
0091
0092 memset(&dreg_state, 0, sizeof(dreg_state));
0093 dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset);
0094 dreg_state.dbg_regs[0].ctrl = control;
0095 iov.iov_base = &dreg_state;
0096 iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) +
0097 sizeof(dreg_state.dbg_regs[0]);
0098 if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0)
0099 return true;
0100
0101 if (errno == EIO)
0102 ksft_print_msg(
0103 "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n",
0104 strerror(errno));
0105
0106 ksft_print_msg(
0107 "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n",
0108 strerror(errno));
0109 return false;
0110 }
0111
0112 static bool run_test(int wr_size, int wp_size, int wr, int wp)
0113 {
0114 int status;
0115 siginfo_t siginfo;
0116 pid_t pid = fork();
0117 pid_t wpid;
0118
0119 if (pid < 0) {
0120 ksft_test_result_fail(
0121 "fork() failed: %s\n", strerror(errno));
0122 return false;
0123 }
0124 if (pid == 0)
0125 child(wr_size, wr);
0126
0127 wpid = waitpid(pid, &status, __WALL);
0128 if (wpid != pid) {
0129 ksft_print_msg(
0130 "waitpid() failed: %s\n", strerror(errno));
0131 return false;
0132 }
0133 if (!WIFSTOPPED(status)) {
0134 ksft_print_msg(
0135 "child did not stop: %s\n", strerror(errno));
0136 return false;
0137 }
0138 if (WSTOPSIG(status) != SIGSTOP) {
0139 ksft_print_msg("child did not stop with SIGSTOP\n");
0140 return false;
0141 }
0142
0143 if (!set_watchpoint(pid, wp_size, wp))
0144 return false;
0145
0146 if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
0147 ksft_print_msg(
0148 "ptrace(PTRACE_CONT) failed: %s\n",
0149 strerror(errno));
0150 return false;
0151 }
0152
0153 alarm(3);
0154 wpid = waitpid(pid, &status, __WALL);
0155 if (wpid != pid) {
0156 ksft_print_msg(
0157 "waitpid() failed: %s\n", strerror(errno));
0158 return false;
0159 }
0160 alarm(0);
0161 if (WIFEXITED(status)) {
0162 ksft_print_msg("child exited prematurely\n");
0163 return false;
0164 }
0165 if (!WIFSTOPPED(status)) {
0166 ksft_print_msg("child did not stop\n");
0167 return false;
0168 }
0169 if (WSTOPSIG(status) != SIGTRAP) {
0170 ksft_print_msg("child did not stop with SIGTRAP\n");
0171 return false;
0172 }
0173 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) {
0174 ksft_print_msg(
0175 "ptrace(PTRACE_GETSIGINFO): %s\n",
0176 strerror(errno));
0177 return false;
0178 }
0179 if (siginfo.si_code != TRAP_HWBKPT) {
0180 ksft_print_msg(
0181 "Unexpected si_code %d\n", siginfo.si_code);
0182 return false;
0183 }
0184
0185 kill(pid, SIGKILL);
0186 wpid = waitpid(pid, &status, 0);
0187 if (wpid != pid) {
0188 ksft_print_msg(
0189 "waitpid() failed: %s\n", strerror(errno));
0190 return false;
0191 }
0192 return true;
0193 }
0194
0195 static void sigalrm(int sig)
0196 {
0197 }
0198
0199 int main(int argc, char **argv)
0200 {
0201 int opt;
0202 bool succeeded = true;
0203 struct sigaction act;
0204 int wr, wp, size;
0205 bool result;
0206
0207 ksft_print_header();
0208 ksft_set_plan(213);
0209
0210 act.sa_handler = sigalrm;
0211 sigemptyset(&act.sa_mask);
0212 act.sa_flags = 0;
0213 sigaction(SIGALRM, &act, NULL);
0214 for (size = 1; size <= 32; size = size*2) {
0215 for (wr = 0; wr <= 32; wr = wr + size) {
0216 for (wp = wr - size; wp <= wr + size; wp = wp + size) {
0217 result = run_test(size, MIN(size, 8), wr, wp);
0218 if ((result && wr == wp) ||
0219 (!result && wr != wp))
0220 ksft_test_result_pass(
0221 "Test size = %d write offset = %d watchpoint offset = %d\n",
0222 size, wr, wp);
0223 else {
0224 ksft_test_result_fail(
0225 "Test size = %d write offset = %d watchpoint offset = %d\n",
0226 size, wr, wp);
0227 succeeded = false;
0228 }
0229 }
0230 }
0231 }
0232
0233 for (size = 1; size <= 32; size = size*2) {
0234 if (run_test(size, 8, -size, -8))
0235 ksft_test_result_pass(
0236 "Test size = %d write offset = %d watchpoint offset = -8\n",
0237 size, -size);
0238 else {
0239 ksft_test_result_fail(
0240 "Test size = %d write offset = %d watchpoint offset = -8\n",
0241 size, -size);
0242 succeeded = false;
0243 }
0244 }
0245
0246 if (succeeded)
0247 ksft_exit_pass();
0248 else
0249 ksft_exit_fail();
0250 }