GNU Linux-libre 4.14.251-gnu1
[releases.git] / tools / testing / selftests / powerpc / ptrace / ptrace-hwbreak.c
1 // SPDX-License-Identifier: GPL-2.0+
2
3 /*
4  * Ptrace test for hw breakpoints
5  *
6  * Based on tools/testing/selftests/breakpoints/breakpoint_test.c
7  *
8  * This test forks and the parent then traces the child doing various
9  * types of ptrace enabled breakpoints
10  *
11  * Copyright (C) 2018 Michael Neuling, IBM Corporation.
12  */
13
14 #include <sys/ptrace.h>
15 #include <unistd.h>
16 #include <stddef.h>
17 #include <sys/user.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <signal.h>
21 #include <sys/types.h>
22 #include <sys/wait.h>
23 #include "ptrace.h"
24
25 /* Breakpoint access modes */
26 enum {
27         BP_X = 1,
28         BP_RW = 2,
29         BP_W = 4,
30 };
31
32 static pid_t child_pid;
33 static struct ppc_debug_info dbginfo;
34
35 static void get_dbginfo(void)
36 {
37         int ret;
38
39         ret = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
40         if (ret) {
41                 perror("Can't get breakpoint info\n");
42                 exit(-1);
43         }
44 }
45
46 static bool hwbreak_present(void)
47 {
48         return (dbginfo.num_data_bps != 0);
49 }
50
51 static bool dawr_present(void)
52 {
53         return !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
54 }
55
56 static void set_breakpoint_addr(void *addr)
57 {
58         int ret;
59
60         ret = ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, addr);
61         if (ret) {
62                 perror("Can't set breakpoint addr\n");
63                 exit(-1);
64         }
65 }
66
67 static int set_hwbreakpoint_addr(void *addr, int range)
68 {
69         int ret;
70
71         struct ppc_hw_breakpoint info;
72
73         info.version = 1;
74         info.trigger_type = PPC_BREAKPOINT_TRIGGER_RW;
75         info.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
76         if (range > 0)
77                 info.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
78         info.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
79         info.addr = (__u64)addr;
80         info.addr2 = (__u64)addr + range;
81         info.condition_value = 0;
82
83         ret = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info);
84         if (ret < 0) {
85                 perror("Can't set breakpoint\n");
86                 exit(-1);
87         }
88         return ret;
89 }
90
91 static int del_hwbreakpoint_addr(int watchpoint_handle)
92 {
93         int ret;
94
95         ret = ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, watchpoint_handle);
96         if (ret < 0) {
97                 perror("Can't delete hw breakpoint\n");
98                 exit(-1);
99         }
100         return ret;
101 }
102
103 #define DAWR_LENGTH_MAX 512
104
105 /* Dummy variables to test read/write accesses */
106 static unsigned long long
107         dummy_array[DAWR_LENGTH_MAX / sizeof(unsigned long long)]
108         __attribute__((aligned(512)));
109 static unsigned long long *dummy_var = dummy_array;
110
111 static void write_var(int len)
112 {
113         long long *plval;
114         char *pcval;
115         short *psval;
116         int *pival;
117
118         switch (len) {
119         case 1:
120                 pcval = (char *)dummy_var;
121                 *pcval = 0xff;
122                 break;
123         case 2:
124                 psval = (short *)dummy_var;
125                 *psval = 0xffff;
126                 break;
127         case 4:
128                 pival = (int *)dummy_var;
129                 *pival = 0xffffffff;
130                 break;
131         case 8:
132                 plval = (long long *)dummy_var;
133                 *plval = 0xffffffffffffffffLL;
134                 break;
135         }
136 }
137
138 static void read_var(int len)
139 {
140         char cval __attribute__((unused));
141         short sval __attribute__((unused));
142         int ival __attribute__((unused));
143         long long lval __attribute__((unused));
144
145         switch (len) {
146         case 1:
147                 cval = *(char *)dummy_var;
148                 break;
149         case 2:
150                 sval = *(short *)dummy_var;
151                 break;
152         case 4:
153                 ival = *(int *)dummy_var;
154                 break;
155         case 8:
156                 lval = *(long long *)dummy_var;
157                 break;
158         }
159 }
160
161 /*
162  * Do the r/w accesses to trigger the breakpoints. And run
163  * the usual traps.
164  */
165 static void trigger_tests(void)
166 {
167         int len, ret;
168
169         ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
170         if (ret) {
171                 perror("Can't be traced?\n");
172                 return;
173         }
174
175         /* Wake up father so that it sets up the first test */
176         kill(getpid(), SIGUSR1);
177
178         /* Test write watchpoints */
179         for (len = 1; len <= sizeof(long); len <<= 1)
180                 write_var(len);
181
182         /* Test read/write watchpoints (on read accesses) */
183         for (len = 1; len <= sizeof(long); len <<= 1)
184                 read_var(len);
185
186         /* Test when breakpoint is unset */
187
188         /* Test write watchpoints */
189         for (len = 1; len <= sizeof(long); len <<= 1)
190                 write_var(len);
191
192         /* Test read/write watchpoints (on read accesses) */
193         for (len = 1; len <= sizeof(long); len <<= 1)
194                 read_var(len);
195 }
196
197 static void check_success(const char *msg)
198 {
199         const char *msg2;
200         int status;
201
202         /* Wait for the child to SIGTRAP */
203         wait(&status);
204
205         msg2 = "Failed";
206
207         if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
208                 msg2 = "Child process hit the breakpoint";
209         }
210
211         printf("%s Result: [%s]\n", msg, msg2);
212 }
213
214 static void launch_watchpoints(char *buf, int mode, int len,
215                                struct ppc_debug_info *dbginfo, bool dawr)
216 {
217         const char *mode_str;
218         unsigned long data = (unsigned long)(dummy_var);
219         int wh, range;
220
221         data &= ~0x7UL;
222
223         if (mode == BP_W) {
224                 data |= (1UL << 1);
225                 mode_str = "write";
226         } else {
227                 data |= (1UL << 0);
228                 data |= (1UL << 1);
229                 mode_str = "read";
230         }
231
232         /* Set DABR_TRANSLATION bit */
233         data |= (1UL << 2);
234
235         /* use PTRACE_SET_DEBUGREG breakpoints */
236         set_breakpoint_addr((void *)data);
237         ptrace(PTRACE_CONT, child_pid, NULL, 0);
238         sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
239         check_success(buf);
240         /* Unregister hw brkpoint */
241         set_breakpoint_addr(NULL);
242
243         data = (data & ~7); /* remove dabr control bits */
244
245         /* use PPC_PTRACE_SETHWDEBUG breakpoint */
246         if (!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
247                 return; /* not supported */
248         wh = set_hwbreakpoint_addr((void *)data, 0);
249         ptrace(PTRACE_CONT, child_pid, NULL, 0);
250         sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
251         check_success(buf);
252         /* Unregister hw brkpoint */
253         del_hwbreakpoint_addr(wh);
254
255         /* try a wider range */
256         range = 8;
257         if (dawr)
258                 range = 512 - ((int)data & (DAWR_LENGTH_MAX - 1));
259         wh = set_hwbreakpoint_addr((void *)data, range);
260         ptrace(PTRACE_CONT, child_pid, NULL, 0);
261         sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
262         check_success(buf);
263         /* Unregister hw brkpoint */
264         del_hwbreakpoint_addr(wh);
265 }
266
267 /* Set the breakpoints and check the child successfully trigger them */
268 static int launch_tests(bool dawr)
269 {
270         char buf[1024];
271         int len, i, status;
272
273         struct ppc_debug_info dbginfo;
274
275         i = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
276         if (i) {
277                 perror("Can't set breakpoint info\n");
278                 exit(-1);
279         }
280         if (!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
281                 printf("WARNING: Kernel doesn't support PPC_PTRACE_SETHWDEBUG\n");
282
283         /* Write watchpoint */
284         for (len = 1; len <= sizeof(long); len <<= 1)
285                 launch_watchpoints(buf, BP_W, len, &dbginfo, dawr);
286
287         /* Read-Write watchpoint */
288         for (len = 1; len <= sizeof(long); len <<= 1)
289                 launch_watchpoints(buf, BP_RW, len, &dbginfo, dawr);
290
291         ptrace(PTRACE_CONT, child_pid, NULL, 0);
292
293         /*
294          * Now we have unregistered the breakpoint, access by child
295          * should not cause SIGTRAP.
296          */
297
298         wait(&status);
299
300         if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
301                 printf("FAIL: Child process hit the breakpoint, which is not expected\n");
302                 ptrace(PTRACE_CONT, child_pid, NULL, 0);
303                 return TEST_FAIL;
304         }
305
306         if (WIFEXITED(status))
307                 printf("Child exited normally\n");
308
309         return TEST_PASS;
310 }
311
312 static int ptrace_hwbreak(void)
313 {
314         pid_t pid;
315         int ret;
316         bool dawr;
317
318         pid = fork();
319         if (!pid) {
320                 trigger_tests();
321                 return 0;
322         }
323
324         wait(NULL);
325
326         child_pid = pid;
327
328         get_dbginfo();
329         SKIP_IF(!hwbreak_present());
330         dawr = dawr_present();
331
332         ret = launch_tests(dawr);
333
334         wait(NULL);
335
336         return ret;
337 }
338
339 int main(int argc, char **argv, char **envp)
340 {
341         return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
342 }