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