1
2
3
4
5
6
7
8
9
10
11
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
30bool is_8xx;
31
32
33
34
35
36static volatile __u64 glvar;
37
38#define DAWR_MAX_LEN 512
39static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
40
41#define A_LEN 6
42#define B_LEN 6
43struct gstruct {
44 __u8 a[A_LEN];
45 __u8 b[B_LEN];
46};
47static volatile struct gstruct gstruct __attribute__((aligned(512)));
48
49static volatile char cwd[PATH_MAX] __attribute__((aligned(8)));
50
51static 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
59static bool dawr_present(struct ppc_debug_info *dbginfo)
60{
61 return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
62}
63
64static 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
91static 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
114static 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
126 kill(getpid(), SIGUSR1);
127
128
129 for (len = 1; len <= sizeof(glvar); len <<= 1)
130 write_var(len);
131
132
133 for (len = 1; len <= sizeof(glvar); len <<= 1)
134 read_var(len);
135
136
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
145 syscall(__NR_getcwd, &cwd, PATH_MAX);
146
147
148 write_var(1);
149
150
151 read_var(1);
152
153
154 if (rand() % 2)
155 write_var(1);
156 else
157 read_var(1);
158
159
160 syscall(__NR_getcwd, &cwd, PATH_MAX);
161
162
163 gstruct.a[rand() % A_LEN] = 'a';
164
165
166 cvar = gstruct.a[rand() % A_LEN];
167
168
169 if (rand() % 2)
170 gstruct.a[rand() % A_LEN] = 'a';
171 else
172 cvar = gstruct.a[rand() % A_LEN];
173
174
175 gstruct.b[rand() % B_LEN] = 'b';
176
177
178 cvar = gstruct.b[rand() % B_LEN];
179
180
181 if (rand() % 2)
182 gstruct.b[rand() % B_LEN] = 'b';
183 else
184 cvar = gstruct.b[rand() % B_LEN];
185
186
187 if (rand() % 2)
188 *((int *)(gstruct.a + 4)) = 10;
189 else
190 ivar = *((int *)(gstruct.a + 4));
191
192
193 if (rand() % 2)
194 big_var[rand() % DAWR_MAX_LEN] = 'a';
195 else
196 cvar = big_var[rand() % DAWR_MAX_LEN];
197}
198
199static 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
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
225
226
227
228 ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
229 wait(NULL);
230 }
231}
232
233static 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
241static 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
252static 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
264static 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
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
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
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
305static 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
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
323static 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
338static 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;
344 int wh;
345
346
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
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
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
368static 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;
374 int wh;
375
376
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
384static 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
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
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
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
420static 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
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
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
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
457static 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
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
475static 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
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
494static void
495run_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
511static 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
532 ptrace(PTRACE_CONT, child_pid, NULL, 0);
533 wait(NULL);
534
535
536
537
538
539 return TEST_PASS;
540}
541
542int 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