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(1);
150
151	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
152	read_var(1);
153
154	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
155	if (rand() % 2)
156		write_var(1);
157	else
158		read_var(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, "WO", 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, "RO", 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, "RW", wp_addr, len);
312	}
313
314	ptrace_set_debugreg(child_pid, 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, "Kernel Access Userspace", wp_addr, 8);
331
332	ptrace_set_debugreg(child_pid, 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, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
361	wh = ptrace_sethwdebug(child_pid, &info);
362	ptrace(PTRACE_CONT, child_pid, NULL, 0);
363	check_success(child_pid, name, "WO", wp_addr, len);
364	ptrace_delhwdebug(child_pid, wh);
365
366	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
367	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0);
368	wh = ptrace_sethwdebug(child_pid, &info);
369	ptrace(PTRACE_CONT, child_pid, NULL, 0);
370	check_success(child_pid, name, "RO", wp_addr, len);
371	ptrace_delhwdebug(child_pid, wh);
372
373	/* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
374	get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0);
375	wh = ptrace_sethwdebug(child_pid, &info);
376	ptrace(PTRACE_CONT, child_pid, NULL, 0);
377	check_success(child_pid, name, "RW", 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, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
391	wh = ptrace_sethwdebug(child_pid, &info);
392	ptrace(PTRACE_CONT, child_pid, NULL, 0);
393	check_success(child_pid, name, "Kernel Access Userspace", 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, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
409	wh = ptrace_sethwdebug(child_pid, &info);
410	ptrace(PTRACE_CONT, child_pid, NULL, 0);
411	check_success(child_pid, name, "WO", 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, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
418	wh = ptrace_sethwdebug(child_pid, &info);
419	ptrace(PTRACE_CONT, child_pid, NULL, 0);
420	check_success(child_pid, name, "RO", 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, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
427	wh = ptrace_sethwdebug(child_pid, &info);
428	ptrace(PTRACE_CONT, child_pid, NULL, 0);
429	check_success(child_pid, name, "RW", 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(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
447	get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
448
449	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED, WO test */
450	wh1 = ptrace_sethwdebug(child_pid, &info1);
451
452	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED, RO test */
453	wh2 = ptrace_sethwdebug(child_pid, &info2);
454
455	ptrace(PTRACE_CONT, child_pid, NULL, 0);
456	check_success(child_pid, name1, "WO", wp_addr1, len1);
457
458	ptrace(PTRACE_CONT, child_pid, NULL, 0);
459	check_success(child_pid, name2, "RO", wp_addr2, len2);
460
461	ptrace_delhwdebug(child_pid, wh1);
462	ptrace_delhwdebug(child_pid, 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(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
478	get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
479
480	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, WO test */
481	wh1 = ptrace_sethwdebug(child_pid, &info1);
482
483	/* PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap, RO test */
484	wh2 = ptrace_sethwdebug(child_pid, &info2);
485
486	ptrace(PTRACE_CONT, child_pid, NULL, 0);
487	check_success(child_pid, name, "WO", wp_addr1, len1);
488
489	ptrace(PTRACE_CONT, child_pid, NULL, 0);
490	check_success(child_pid, name, "RO", wp_addr2, len2);
491
492	ptrace_delhwdebug(child_pid, wh1);
493	ptrace_delhwdebug(child_pid, 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, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
508	wh = ptrace_sethwdebug(child_pid, &info);
509	ptrace(PTRACE_CONT, child_pid, NULL, 0);
510	check_success(child_pid, name, "WO", 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);
518	ptrace(PTRACE_CONT, child_pid, NULL, 0);
519	check_success(child_pid, name, "RO", 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);
527	ptrace(PTRACE_CONT, child_pid, NULL, 0);
528	check_success(child_pid, name, "RW", 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);
546	ptrace(PTRACE_CONT, child_pid, NULL, 0);
547	check_success(child_pid, name, "RW", 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);
564	ptrace(PTRACE_CONT, child_pid, NULL, 0);
565	check_success(child_pid, name, "RW", 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);
606	SKIP_IF_MSG(dbginfo.num_data_bps == 0, "No data breakpoints present");
607
608	dawr = dawr_present(&dbginfo);
609	run_tests(child_pid, &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