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

source code of linux/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c