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 #define SPRN_PVR 0x11F
26 #define PVR_8xx 0x00500000
27
28 bool is_8xx;
29
30 /*
31 * Use volatile on all global var so that compiler doesn't
32 * optimise their load/stores. Otherwise selftest can fail.
33 */
34 static volatile __u64 glvar;
35
36 #define DAWR_MAX_LEN 512
37 static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
38
39 #define A_LEN 6
40 #define B_LEN 6
41 struct gstruct {
42 __u8 a[A_LEN]; /* double word aligned */
43 __u8 b[B_LEN]; /* double word unaligned */
44 };
45 static volatile struct gstruct gstruct __attribute__((aligned(512)));
46
47
get_dbginfo(pid_t child_pid,struct ppc_debug_info * dbginfo)48 static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
49 {
50 if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
51 perror("Can't get breakpoint info");
52 exit(-1);
53 }
54 }
55
dawr_present(struct ppc_debug_info * dbginfo)56 static bool dawr_present(struct ppc_debug_info *dbginfo)
57 {
58 return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
59 }
60
write_var(int len)61 static void write_var(int len)
62 {
63 __u8 *pcvar;
64 __u16 *psvar;
65 __u32 *pivar;
66 __u64 *plvar;
67
68 switch (len) {
69 case 1:
70 pcvar = (__u8 *)&glvar;
71 *pcvar = 0xff;
72 break;
73 case 2:
74 psvar = (__u16 *)&glvar;
75 *psvar = 0xffff;
76 break;
77 case 4:
78 pivar = (__u32 *)&glvar;
79 *pivar = 0xffffffff;
80 break;
81 case 8:
82 plvar = (__u64 *)&glvar;
83 *plvar = 0xffffffffffffffffLL;
84 break;
85 }
86 }
87
read_var(int len)88 static void read_var(int len)
89 {
90 __u8 cvar __attribute__((unused));
91 __u16 svar __attribute__((unused));
92 __u32 ivar __attribute__((unused));
93 __u64 lvar __attribute__((unused));
94
95 switch (len) {
96 case 1:
97 cvar = (__u8)glvar;
98 break;
99 case 2:
100 svar = (__u16)glvar;
101 break;
102 case 4:
103 ivar = (__u32)glvar;
104 break;
105 case 8:
106 lvar = (__u64)glvar;
107 break;
108 }
109 }
110
test_workload(void)111 static void test_workload(void)
112 {
113 __u8 cvar __attribute__((unused));
114 __u32 ivar __attribute__((unused));
115 int len = 0;
116
117 if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) {
118 perror("Child can't be traced?");
119 exit(-1);
120 }
121
122 /* Wake up father so that it sets up the first test */
123 kill(getpid(), SIGUSR1);
124
125 /* PTRACE_SET_DEBUGREG, WO test */
126 for (len = 1; len <= sizeof(glvar); len <<= 1)
127 write_var(len);
128
129 /* PTRACE_SET_DEBUGREG, RO test */
130 for (len = 1; len <= sizeof(glvar); len <<= 1)
131 read_var(len);
132
133 /* PTRACE_SET_DEBUGREG, RW test */
134 for (len = 1; len <= sizeof(glvar); len <<= 1) {
135 if (rand() % 2)
136 read_var(len);
137 else
138 write_var(len);
139 }
140
141 /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
142 write_var(1);
143
144 /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
145 read_var(1);
146
147 /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
148 if (rand() % 2)
149 write_var(1);
150 else
151 read_var(1);
152
153 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
154 gstruct.a[rand() % A_LEN] = 'a';
155
156 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
157 cvar = gstruct.a[rand() % A_LEN];
158
159 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
160 if (rand() % 2)
161 gstruct.a[rand() % A_LEN] = 'a';
162 else
163 cvar = gstruct.a[rand() % A_LEN];
164
165 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
166 gstruct.b[rand() % B_LEN] = 'b';
167
168 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
169 cvar = gstruct.b[rand() % B_LEN];
170
171 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
172 if (rand() % 2)
173 gstruct.b[rand() % B_LEN] = 'b';
174 else
175 cvar = gstruct.b[rand() % B_LEN];
176
177 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
178 if (rand() % 2)
179 *((int *)(gstruct.a + 4)) = 10;
180 else
181 ivar = *((int *)(gstruct.a + 4));
182
183 /* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */
184 if (rand() % 2)
185 big_var[rand() % DAWR_MAX_LEN] = 'a';
186 else
187 cvar = big_var[rand() % DAWR_MAX_LEN];
188 }
189
check_success(pid_t child_pid,const char * name,const char * type,unsigned long saddr,int len)190 static void check_success(pid_t child_pid, const char *name, const char *type,
191 unsigned long saddr, int len)
192 {
193 int status;
194 siginfo_t siginfo;
195 unsigned long eaddr = (saddr + len - 1) | 0x7;
196
197 saddr &= ~0x7;
198
199 /* Wait for the child to SIGTRAP */
200 wait(&status);
201
202 ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo);
203
204 if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
205 (unsigned long)siginfo.si_addr < saddr ||
206 (unsigned long)siginfo.si_addr > eaddr) {
207 printf("%s, %s, len: %d: Fail\n", name, type, len);
208 exit(-1);
209 }
210
211 printf("%s, %s, len: %d: Ok\n", name, type, len);
212
213 if (!is_8xx) {
214 /*
215 * For ptrace registered watchpoint, signal is generated
216 * before executing load/store. Singlestep the instruction
217 * and then continue the test.
218 */
219 ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
220 wait(NULL);
221 }
222 }
223
ptrace_set_debugreg(pid_t child_pid,unsigned long wp_addr)224 static void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr)
225 {
226 if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) {
227 perror("PTRACE_SET_DEBUGREG failed");
228 exit(-1);
229 }
230 }
231
ptrace_sethwdebug(pid_t child_pid,struct ppc_hw_breakpoint * info)232 static int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info)
233 {
234 int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info);
235
236 if (wh <= 0) {
237 perror("PPC_PTRACE_SETHWDEBUG failed");
238 exit(-1);
239 }
240 return wh;
241 }
242
ptrace_delhwdebug(pid_t child_pid,int wh)243 static void ptrace_delhwdebug(pid_t child_pid, int wh)
244 {
245 if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) {
246 perror("PPC_PTRACE_DELHWDEBUG failed");
247 exit(-1);
248 }
249 }
250
251 #define DABR_READ_SHIFT 0
252 #define DABR_WRITE_SHIFT 1
253 #define DABR_TRANSLATION_SHIFT 2
254
test_set_debugreg(pid_t child_pid)255 static int test_set_debugreg(pid_t child_pid)
256 {
257 unsigned long wp_addr = (unsigned long)&glvar;
258 char *name = "PTRACE_SET_DEBUGREG";
259 int len;
260
261 /* PTRACE_SET_DEBUGREG, WO test*/
262 wp_addr &= ~0x7UL;
263 wp_addr |= (1UL << DABR_WRITE_SHIFT);
264 wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
265 for (len = 1; len <= sizeof(glvar); len <<= 1) {
266 ptrace_set_debugreg(child_pid, wp_addr);
267 ptrace(PTRACE_CONT, child_pid, NULL, 0);
268 check_success(child_pid, name, "WO", wp_addr, len);
269 }
270
271 /* PTRACE_SET_DEBUGREG, RO test */
272 wp_addr &= ~0x7UL;
273 wp_addr |= (1UL << DABR_READ_SHIFT);
274 wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
275 for (len = 1; len <= sizeof(glvar); len <<= 1) {
276 ptrace_set_debugreg(child_pid, wp_addr);
277 ptrace(PTRACE_CONT, child_pid, NULL, 0);
278 check_success(child_pid, name, "RO", wp_addr, len);
279 }
280
281 /* PTRACE_SET_DEBUGREG, RW test */
282 wp_addr &= ~0x7UL;
283 wp_addr |= (1Ul << DABR_READ_SHIFT);
284 wp_addr |= (1UL << DABR_WRITE_SHIFT);
285 wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
286 for (len = 1; len <= sizeof(glvar); len <<= 1) {
287 ptrace_set_debugreg(child_pid, wp_addr);
288 ptrace(PTRACE_CONT, child_pid, NULL, 0);
289 check_success(child_pid, name, "RW", wp_addr, len);
290 }
291
292 ptrace_set_debugreg(child_pid, 0);
293 return 0;
294 }
295
get_ppc_hw_breakpoint(struct ppc_hw_breakpoint * info,int type,unsigned long addr,int len)296 static void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
297 unsigned long addr, int len)
298 {
299 info->version = 1;
300 info->trigger_type = type;
301 info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
302 info->addr = (__u64)addr;
303 info->addr2 = (__u64)addr + len;
304 info->condition_value = 0;
305 if (!len)
306 info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
307 else
308 info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
309 }
310
test_sethwdebug_exact(pid_t child_pid)311 static void test_sethwdebug_exact(pid_t child_pid)
312 {
313 struct ppc_hw_breakpoint info;
314 unsigned long wp_addr = (unsigned long)&glvar;
315 char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
316 int len = 1; /* hardcoded in kernel */
317 int wh;
318
319 /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
320 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
321 wh = ptrace_sethwdebug(child_pid, &info);
322 ptrace(PTRACE_CONT, child_pid, NULL, 0);
323 check_success(child_pid, name, "WO", wp_addr, len);
324 ptrace_delhwdebug(child_pid, wh);
325
326 /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
327 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0);
328 wh = ptrace_sethwdebug(child_pid, &info);
329 ptrace(PTRACE_CONT, child_pid, NULL, 0);
330 check_success(child_pid, name, "RO", wp_addr, len);
331 ptrace_delhwdebug(child_pid, wh);
332
333 /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
334 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0);
335 wh = ptrace_sethwdebug(child_pid, &info);
336 ptrace(PTRACE_CONT, child_pid, NULL, 0);
337 check_success(child_pid, name, "RW", wp_addr, len);
338 ptrace_delhwdebug(child_pid, wh);
339 }
340
test_sethwdebug_range_aligned(pid_t child_pid)341 static void test_sethwdebug_range_aligned(pid_t child_pid)
342 {
343 struct ppc_hw_breakpoint info;
344 unsigned long wp_addr;
345 char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED";
346 int len;
347 int wh;
348
349 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
350 wp_addr = (unsigned long)&gstruct.a;
351 len = A_LEN;
352 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
353 wh = ptrace_sethwdebug(child_pid, &info);
354 ptrace(PTRACE_CONT, child_pid, NULL, 0);
355 check_success(child_pid, name, "WO", wp_addr, len);
356 ptrace_delhwdebug(child_pid, wh);
357
358 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
359 wp_addr = (unsigned long)&gstruct.a;
360 len = A_LEN;
361 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
362 wh = ptrace_sethwdebug(child_pid, &info);
363 ptrace(PTRACE_CONT, child_pid, NULL, 0);
364 check_success(child_pid, name, "RO", wp_addr, len);
365 ptrace_delhwdebug(child_pid, wh);
366
367 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
368 wp_addr = (unsigned long)&gstruct.a;
369 len = A_LEN;
370 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
371 wh = ptrace_sethwdebug(child_pid, &info);
372 ptrace(PTRACE_CONT, child_pid, NULL, 0);
373 check_success(child_pid, name, "RW", wp_addr, len);
374 ptrace_delhwdebug(child_pid, wh);
375 }
376
test_sethwdebug_range_unaligned(pid_t child_pid)377 static void test_sethwdebug_range_unaligned(pid_t child_pid)
378 {
379 struct ppc_hw_breakpoint info;
380 unsigned long wp_addr;
381 char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED";
382 int len;
383 int wh;
384
385 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
386 wp_addr = (unsigned long)&gstruct.b;
387 len = B_LEN;
388 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
389 wh = ptrace_sethwdebug(child_pid, &info);
390 ptrace(PTRACE_CONT, child_pid, NULL, 0);
391 check_success(child_pid, name, "WO", wp_addr, len);
392 ptrace_delhwdebug(child_pid, wh);
393
394 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
395 wp_addr = (unsigned long)&gstruct.b;
396 len = B_LEN;
397 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
398 wh = ptrace_sethwdebug(child_pid, &info);
399 ptrace(PTRACE_CONT, child_pid, NULL, 0);
400 check_success(child_pid, name, "RO", wp_addr, len);
401 ptrace_delhwdebug(child_pid, wh);
402
403 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
404 wp_addr = (unsigned long)&gstruct.b;
405 len = B_LEN;
406 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
407 wh = ptrace_sethwdebug(child_pid, &info);
408 ptrace(PTRACE_CONT, child_pid, NULL, 0);
409 check_success(child_pid, name, "RW", wp_addr, len);
410 ptrace_delhwdebug(child_pid, wh);
411
412 }
413
test_sethwdebug_range_unaligned_dar(pid_t child_pid)414 static void test_sethwdebug_range_unaligned_dar(pid_t child_pid)
415 {
416 struct ppc_hw_breakpoint info;
417 unsigned long wp_addr;
418 char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE";
419 int len;
420 int wh;
421
422 /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
423 wp_addr = (unsigned long)&gstruct.b;
424 len = B_LEN;
425 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
426 wh = ptrace_sethwdebug(child_pid, &info);
427 ptrace(PTRACE_CONT, child_pid, NULL, 0);
428 check_success(child_pid, name, "RW", wp_addr, len);
429 ptrace_delhwdebug(child_pid, wh);
430 }
431
test_sethwdebug_dawr_max_range(pid_t child_pid)432 static void test_sethwdebug_dawr_max_range(pid_t child_pid)
433 {
434 struct ppc_hw_breakpoint info;
435 unsigned long wp_addr;
436 char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN";
437 int len;
438 int wh;
439
440 /* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */
441 wp_addr = (unsigned long)big_var;
442 len = DAWR_MAX_LEN;
443 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
444 wh = ptrace_sethwdebug(child_pid, &info);
445 ptrace(PTRACE_CONT, child_pid, NULL, 0);
446 check_success(child_pid, name, "RW", wp_addr, len);
447 ptrace_delhwdebug(child_pid, wh);
448 }
449
450 /* Set the breakpoints and check the child successfully trigger them */
451 static void
run_tests(pid_t child_pid,struct ppc_debug_info * dbginfo,bool dawr)452 run_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr)
453 {
454 test_set_debugreg(child_pid);
455 if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) {
456 test_sethwdebug_exact(child_pid);
457
458 if (!is_8xx)
459 test_sethwdebug_range_aligned(child_pid);
460 if (dawr && !is_8xx) {
461 test_sethwdebug_range_unaligned(child_pid);
462 test_sethwdebug_range_unaligned_dar(child_pid);
463 test_sethwdebug_dawr_max_range(child_pid);
464 }
465 }
466 }
467
ptrace_hwbreak(void)468 static int ptrace_hwbreak(void)
469 {
470 pid_t child_pid;
471 struct ppc_debug_info dbginfo;
472 bool dawr;
473
474 child_pid = fork();
475 if (!child_pid) {
476 test_workload();
477 return 0;
478 }
479
480 wait(NULL);
481
482 get_dbginfo(child_pid, &dbginfo);
483 SKIP_IF(dbginfo.num_data_bps == 0);
484
485 dawr = dawr_present(&dbginfo);
486 run_tests(child_pid, &dbginfo, dawr);
487
488 /* Let the child exit first. */
489 ptrace(PTRACE_CONT, child_pid, NULL, 0);
490 wait(NULL);
491
492 /*
493 * Testcases exits immediately with -1 on any failure. If
494 * it has reached here, it means all tests were successful.
495 */
496 return TEST_PASS;
497 }
498
main(int argc,char ** argv,char ** envp)499 int main(int argc, char **argv, char **envp)
500 {
501 int pvr = 0;
502 asm __volatile__ ("mfspr %0,%1" : "=r"(pvr) : "i"(SPRN_PVR));
503 if (pvr == PVR_8xx)
504 is_8xx = true;
505
506 return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
507 }
508