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