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 "ptrace.h"
24
25#define SPRN_PVR 0x11F
26#define PVR_8xx 0x00500000
27
28bool is_8xx;
29
30
31
32
33
34static volatile __u64 glvar;
35
36#define DAWR_MAX_LEN 512
37static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
38
39#define A_LEN 6
40#define B_LEN 6
41struct gstruct {
42 __u8 a[A_LEN];
43 __u8 b[B_LEN];
44};
45static volatile struct gstruct gstruct __attribute__((aligned(512)));
46
47
48static 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
56static bool dawr_present(struct ppc_debug_info *dbginfo)
57{
58 return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
59}
60
61static 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
88static 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
111static 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
123 kill(getpid(), SIGUSR1);
124
125
126 for (len = 1; len <= sizeof(glvar); len <<= 1)
127 write_var(len);
128
129
130 for (len = 1; len <= sizeof(glvar); len <<= 1)
131 read_var(len);
132
133
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
142 write_var(1);
143
144
145 read_var(1);
146
147
148 if (rand() % 2)
149 write_var(1);
150 else
151 read_var(1);
152
153
154 gstruct.a[rand() % A_LEN] = 'a';
155
156
157 cvar = gstruct.a[rand() % A_LEN];
158
159
160 if (rand() % 2)
161 gstruct.a[rand() % A_LEN] = 'a';
162 else
163 cvar = gstruct.a[rand() % A_LEN];
164
165
166 gstruct.b[rand() % B_LEN] = 'b';
167
168
169 cvar = gstruct.b[rand() % B_LEN];
170
171
172 if (rand() % 2)
173 gstruct.b[rand() % B_LEN] = 'b';
174 else
175 cvar = gstruct.b[rand() % B_LEN];
176
177
178 if (rand() % 2)
179 *((int *)(gstruct.a + 4)) = 10;
180 else
181 ivar = *((int *)(gstruct.a + 4));
182
183
184 if (rand() % 2)
185 big_var[rand() % DAWR_MAX_LEN] = 'a';
186 else
187 cvar = big_var[rand() % DAWR_MAX_LEN];
188}
189
190static 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
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
216
217
218
219 ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
220 wait(NULL);
221 }
222}
223
224static 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
232static 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
243static 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
255static 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
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
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
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
296static 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
311static 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;
317 int wh;
318
319
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
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
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
341static 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
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
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
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
377static 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
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
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
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
414static 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
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
432static 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
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
451static void
452run_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
468static 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
489 ptrace(PTRACE_CONT, child_pid, NULL, 0);
490 wait(NULL);
491
492
493
494
495
496 return TEST_PASS;
497}
498
499int 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