1290f7d8cSRavi Bangoria // SPDX-License-Identifier: GPL-2.0+
2290f7d8cSRavi Bangoria #include <stdio.h>
3290f7d8cSRavi Bangoria #include <string.h>
4290f7d8cSRavi Bangoria #include <signal.h>
5290f7d8cSRavi Bangoria #include <stdlib.h>
6290f7d8cSRavi Bangoria #include <unistd.h>
7290f7d8cSRavi Bangoria #include <errno.h>
8290f7d8cSRavi Bangoria #include <linux/hw_breakpoint.h>
9290f7d8cSRavi Bangoria #include <linux/perf_event.h>
10290f7d8cSRavi Bangoria #include <asm/unistd.h>
11290f7d8cSRavi Bangoria #include <sys/ptrace.h>
12290f7d8cSRavi Bangoria #include <sys/wait.h>
13290f7d8cSRavi Bangoria #include "ptrace.h"
14290f7d8cSRavi Bangoria 
15290f7d8cSRavi Bangoria char data[16];
16290f7d8cSRavi Bangoria 
17290f7d8cSRavi Bangoria /* Overlapping address range */
18290f7d8cSRavi Bangoria volatile __u64 *ptrace_data1 = (__u64 *)&data[0];
19290f7d8cSRavi Bangoria volatile __u64 *perf_data1 = (__u64 *)&data[4];
20290f7d8cSRavi Bangoria 
21290f7d8cSRavi Bangoria /* Non-overlapping address range */
22290f7d8cSRavi Bangoria volatile __u64 *ptrace_data2 = (__u64 *)&data[0];
23290f7d8cSRavi Bangoria volatile __u64 *perf_data2 = (__u64 *)&data[8];
24290f7d8cSRavi Bangoria 
25290f7d8cSRavi Bangoria static unsigned long pid_max_addr(void)
26290f7d8cSRavi Bangoria {
27290f7d8cSRavi Bangoria 	FILE *fp;
28290f7d8cSRavi Bangoria 	char *line, *c;
29290f7d8cSRavi Bangoria 	char addr[100];
30290f7d8cSRavi Bangoria 	size_t len = 0;
31290f7d8cSRavi Bangoria 
32290f7d8cSRavi Bangoria 	fp = fopen("/proc/kallsyms", "r");
33290f7d8cSRavi Bangoria 	if (!fp) {
34290f7d8cSRavi Bangoria 		printf("Failed to read /proc/kallsyms. Exiting..\n");
35290f7d8cSRavi Bangoria 		exit(EXIT_FAILURE);
36290f7d8cSRavi Bangoria 	}
37290f7d8cSRavi Bangoria 
38290f7d8cSRavi Bangoria 	while (getline(&line, &len, fp) != -1) {
39290f7d8cSRavi Bangoria 		if (!strstr(line, "pid_max") || strstr(line, "pid_max_max") ||
40290f7d8cSRavi Bangoria 		    strstr(line, "pid_max_min"))
41290f7d8cSRavi Bangoria 			continue;
42290f7d8cSRavi Bangoria 
43290f7d8cSRavi Bangoria 		strncpy(addr, line, len < 100 ? len : 100);
44290f7d8cSRavi Bangoria 		c = strchr(addr, ' ');
45290f7d8cSRavi Bangoria 		*c = '\0';
46290f7d8cSRavi Bangoria 		return strtoul(addr, &c, 16);
47290f7d8cSRavi Bangoria 	}
48290f7d8cSRavi Bangoria 	fclose(fp);
49290f7d8cSRavi Bangoria 	printf("Could not find pix_max. Exiting..\n");
50290f7d8cSRavi Bangoria 	exit(EXIT_FAILURE);
51290f7d8cSRavi Bangoria 	return -1;
52290f7d8cSRavi Bangoria }
53290f7d8cSRavi Bangoria 
54290f7d8cSRavi Bangoria static void perf_user_event_attr_set(struct perf_event_attr *attr, __u64 addr, __u64 len)
55290f7d8cSRavi Bangoria {
56290f7d8cSRavi Bangoria 	memset(attr, 0, sizeof(struct perf_event_attr));
57290f7d8cSRavi Bangoria 	attr->type           = PERF_TYPE_BREAKPOINT;
58290f7d8cSRavi Bangoria 	attr->size           = sizeof(struct perf_event_attr);
59290f7d8cSRavi Bangoria 	attr->bp_type        = HW_BREAKPOINT_R;
60290f7d8cSRavi Bangoria 	attr->bp_addr        = addr;
61290f7d8cSRavi Bangoria 	attr->bp_len         = len;
62290f7d8cSRavi Bangoria 	attr->exclude_kernel = 1;
63290f7d8cSRavi Bangoria 	attr->exclude_hv     = 1;
64290f7d8cSRavi Bangoria }
65290f7d8cSRavi Bangoria 
66290f7d8cSRavi Bangoria static void perf_kernel_event_attr_set(struct perf_event_attr *attr)
67290f7d8cSRavi Bangoria {
68290f7d8cSRavi Bangoria 	memset(attr, 0, sizeof(struct perf_event_attr));
69290f7d8cSRavi Bangoria 	attr->type           = PERF_TYPE_BREAKPOINT;
70290f7d8cSRavi Bangoria 	attr->size           = sizeof(struct perf_event_attr);
71290f7d8cSRavi Bangoria 	attr->bp_type        = HW_BREAKPOINT_R;
72290f7d8cSRavi Bangoria 	attr->bp_addr        = pid_max_addr();
73290f7d8cSRavi Bangoria 	attr->bp_len         = sizeof(unsigned long);
74290f7d8cSRavi Bangoria 	attr->exclude_user   = 1;
75290f7d8cSRavi Bangoria 	attr->exclude_hv     = 1;
76290f7d8cSRavi Bangoria }
77290f7d8cSRavi Bangoria 
78290f7d8cSRavi Bangoria static int perf_cpu_event_open(int cpu, __u64 addr, __u64 len)
79290f7d8cSRavi Bangoria {
80290f7d8cSRavi Bangoria 	struct perf_event_attr attr;
81290f7d8cSRavi Bangoria 
82290f7d8cSRavi Bangoria 	perf_user_event_attr_set(&attr, addr, len);
83290f7d8cSRavi Bangoria 	return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0);
84290f7d8cSRavi Bangoria }
85290f7d8cSRavi Bangoria 
86290f7d8cSRavi Bangoria static int perf_thread_event_open(pid_t child_pid, __u64 addr, __u64 len)
87290f7d8cSRavi Bangoria {
88290f7d8cSRavi Bangoria 	struct perf_event_attr attr;
89290f7d8cSRavi Bangoria 
90290f7d8cSRavi Bangoria 	perf_user_event_attr_set(&attr, addr, len);
91290f7d8cSRavi Bangoria 	return syscall(__NR_perf_event_open, &attr, child_pid, -1, -1, 0);
92290f7d8cSRavi Bangoria }
93290f7d8cSRavi Bangoria 
94290f7d8cSRavi Bangoria static int perf_thread_cpu_event_open(pid_t child_pid, int cpu, __u64 addr, __u64 len)
95290f7d8cSRavi Bangoria {
96290f7d8cSRavi Bangoria 	struct perf_event_attr attr;
97290f7d8cSRavi Bangoria 
98290f7d8cSRavi Bangoria 	perf_user_event_attr_set(&attr, addr, len);
99290f7d8cSRavi Bangoria 	return syscall(__NR_perf_event_open, &attr, child_pid, cpu, -1, 0);
100290f7d8cSRavi Bangoria }
101290f7d8cSRavi Bangoria 
102290f7d8cSRavi Bangoria static int perf_thread_kernel_event_open(pid_t child_pid)
103290f7d8cSRavi Bangoria {
104290f7d8cSRavi Bangoria 	struct perf_event_attr attr;
105290f7d8cSRavi Bangoria 
106290f7d8cSRavi Bangoria 	perf_kernel_event_attr_set(&attr);
107290f7d8cSRavi Bangoria 	return syscall(__NR_perf_event_open, &attr, child_pid, -1, -1, 0);
108290f7d8cSRavi Bangoria }
109290f7d8cSRavi Bangoria 
110290f7d8cSRavi Bangoria static int perf_cpu_kernel_event_open(int cpu)
111290f7d8cSRavi Bangoria {
112290f7d8cSRavi Bangoria 	struct perf_event_attr attr;
113290f7d8cSRavi Bangoria 
114290f7d8cSRavi Bangoria 	perf_kernel_event_attr_set(&attr);
115290f7d8cSRavi Bangoria 	return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0);
116290f7d8cSRavi Bangoria }
117290f7d8cSRavi Bangoria 
118290f7d8cSRavi Bangoria static int child(void)
119290f7d8cSRavi Bangoria {
120290f7d8cSRavi Bangoria 	int ret;
121290f7d8cSRavi Bangoria 
122290f7d8cSRavi Bangoria 	ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
123290f7d8cSRavi Bangoria 	if (ret) {
124290f7d8cSRavi Bangoria 		printf("Error: PTRACE_TRACEME failed\n");
125290f7d8cSRavi Bangoria 		return 0;
126290f7d8cSRavi Bangoria 	}
127290f7d8cSRavi Bangoria 	kill(getpid(), SIGUSR1); /* --> parent (SIGUSR1) */
128290f7d8cSRavi Bangoria 
129290f7d8cSRavi Bangoria 	return 0;
130290f7d8cSRavi Bangoria }
131290f7d8cSRavi Bangoria 
132290f7d8cSRavi Bangoria static void ptrace_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
133290f7d8cSRavi Bangoria 				     __u64 addr, int len)
134290f7d8cSRavi Bangoria {
135290f7d8cSRavi Bangoria 	info->version = 1;
136290f7d8cSRavi Bangoria 	info->trigger_type = type;
137290f7d8cSRavi Bangoria 	info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
138290f7d8cSRavi Bangoria 	info->addr = addr;
139290f7d8cSRavi Bangoria 	info->addr2 = addr + len;
140290f7d8cSRavi Bangoria 	info->condition_value = 0;
141290f7d8cSRavi Bangoria 	if (!len)
142290f7d8cSRavi Bangoria 		info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
143290f7d8cSRavi Bangoria 	else
144290f7d8cSRavi Bangoria 		info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
145290f7d8cSRavi Bangoria }
146290f7d8cSRavi Bangoria 
147290f7d8cSRavi Bangoria static int ptrace_open(pid_t child_pid, __u64 wp_addr, int len)
148290f7d8cSRavi Bangoria {
149290f7d8cSRavi Bangoria 	struct ppc_hw_breakpoint info;
150290f7d8cSRavi Bangoria 
151290f7d8cSRavi Bangoria 	ptrace_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
152290f7d8cSRavi Bangoria 	return ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info);
153290f7d8cSRavi Bangoria }
154290f7d8cSRavi Bangoria 
155290f7d8cSRavi Bangoria static int test1(pid_t child_pid)
156290f7d8cSRavi Bangoria {
157290f7d8cSRavi Bangoria 	int perf_fd;
158290f7d8cSRavi Bangoria 	int ptrace_fd;
159290f7d8cSRavi Bangoria 	int ret = 0;
160290f7d8cSRavi Bangoria 
161290f7d8cSRavi Bangoria 	/* Test:
162290f7d8cSRavi Bangoria 	 * if (new per thread event by ptrace)
163290f7d8cSRavi Bangoria 	 *	if (existing cpu event by perf)
164290f7d8cSRavi Bangoria 	 *		if (addr range overlaps)
165290f7d8cSRavi Bangoria 	 *			fail;
166290f7d8cSRavi Bangoria 	 */
167290f7d8cSRavi Bangoria 
168290f7d8cSRavi Bangoria 	perf_fd = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1));
169290f7d8cSRavi Bangoria 	if (perf_fd < 0)
170290f7d8cSRavi Bangoria 		return -1;
171290f7d8cSRavi Bangoria 
172290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
173290f7d8cSRavi Bangoria 	if (ptrace_fd > 0 || errno != ENOSPC)
174290f7d8cSRavi Bangoria 		ret = -1;
175290f7d8cSRavi Bangoria 
176290f7d8cSRavi Bangoria 	close(perf_fd);
177290f7d8cSRavi Bangoria 	return ret;
178290f7d8cSRavi Bangoria }
179290f7d8cSRavi Bangoria 
180290f7d8cSRavi Bangoria static int test2(pid_t child_pid)
181290f7d8cSRavi Bangoria {
182290f7d8cSRavi Bangoria 	int perf_fd;
183290f7d8cSRavi Bangoria 	int ptrace_fd;
184290f7d8cSRavi Bangoria 	int ret = 0;
185290f7d8cSRavi Bangoria 
186290f7d8cSRavi Bangoria 	/* Test:
187290f7d8cSRavi Bangoria 	 * if (new per thread event by ptrace)
188290f7d8cSRavi Bangoria 	 *	if (existing cpu event by perf)
189290f7d8cSRavi Bangoria 	 *		if (addr range does not overlaps)
190290f7d8cSRavi Bangoria 	 *			allow;
191290f7d8cSRavi Bangoria 	 */
192290f7d8cSRavi Bangoria 
193290f7d8cSRavi Bangoria 	perf_fd = perf_cpu_event_open(0, (__u64)perf_data2, sizeof(*perf_data2));
194290f7d8cSRavi Bangoria 	if (perf_fd < 0)
195290f7d8cSRavi Bangoria 		return -1;
196290f7d8cSRavi Bangoria 
197290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
198290f7d8cSRavi Bangoria 	if (ptrace_fd < 0) {
199290f7d8cSRavi Bangoria 		ret = -1;
200290f7d8cSRavi Bangoria 		goto perf_close;
201290f7d8cSRavi Bangoria 	}
202290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
203290f7d8cSRavi Bangoria 
204290f7d8cSRavi Bangoria perf_close:
205290f7d8cSRavi Bangoria 	close(perf_fd);
206290f7d8cSRavi Bangoria 	return ret;
207290f7d8cSRavi Bangoria }
208290f7d8cSRavi Bangoria 
209290f7d8cSRavi Bangoria static int test3(pid_t child_pid)
210290f7d8cSRavi Bangoria {
211290f7d8cSRavi Bangoria 	int perf_fd;
212290f7d8cSRavi Bangoria 	int ptrace_fd;
213290f7d8cSRavi Bangoria 	int ret = 0;
214290f7d8cSRavi Bangoria 
215290f7d8cSRavi Bangoria 	/* Test:
216290f7d8cSRavi Bangoria 	 * if (new per thread event by ptrace)
217290f7d8cSRavi Bangoria 	 *	if (existing thread event by perf on the same thread)
218290f7d8cSRavi Bangoria 	 *		if (addr range overlaps)
219290f7d8cSRavi Bangoria 	 *			fail;
220290f7d8cSRavi Bangoria 	 */
221290f7d8cSRavi Bangoria 	perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data1,
222290f7d8cSRavi Bangoria 					 sizeof(*perf_data1));
223290f7d8cSRavi Bangoria 	if (perf_fd < 0)
224290f7d8cSRavi Bangoria 		return -1;
225290f7d8cSRavi Bangoria 
226290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
227290f7d8cSRavi Bangoria 	if (ptrace_fd > 0 || errno != ENOSPC)
228290f7d8cSRavi Bangoria 		ret = -1;
229290f7d8cSRavi Bangoria 
230290f7d8cSRavi Bangoria 	close(perf_fd);
231290f7d8cSRavi Bangoria 	return ret;
232290f7d8cSRavi Bangoria }
233290f7d8cSRavi Bangoria 
234290f7d8cSRavi Bangoria static int test4(pid_t child_pid)
235290f7d8cSRavi Bangoria {
236290f7d8cSRavi Bangoria 	int perf_fd;
237290f7d8cSRavi Bangoria 	int ptrace_fd;
238290f7d8cSRavi Bangoria 	int ret = 0;
239290f7d8cSRavi Bangoria 
240290f7d8cSRavi Bangoria 	/* Test:
241290f7d8cSRavi Bangoria 	 * if (new per thread event by ptrace)
242290f7d8cSRavi Bangoria 	 *	if (existing thread event by perf on the same thread)
243290f7d8cSRavi Bangoria 	 *		if (addr range does not overlaps)
244290f7d8cSRavi Bangoria 	 *			fail;
245290f7d8cSRavi Bangoria 	 */
246290f7d8cSRavi Bangoria 	perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data2,
247290f7d8cSRavi Bangoria 					 sizeof(*perf_data2));
248290f7d8cSRavi Bangoria 	if (perf_fd < 0)
249290f7d8cSRavi Bangoria 		return -1;
250290f7d8cSRavi Bangoria 
251290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
252290f7d8cSRavi Bangoria 	if (ptrace_fd < 0) {
253290f7d8cSRavi Bangoria 		ret = -1;
254290f7d8cSRavi Bangoria 		goto perf_close;
255290f7d8cSRavi Bangoria 	}
256290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
257290f7d8cSRavi Bangoria 
258290f7d8cSRavi Bangoria perf_close:
259290f7d8cSRavi Bangoria 	close(perf_fd);
260290f7d8cSRavi Bangoria 	return ret;
261290f7d8cSRavi Bangoria }
262290f7d8cSRavi Bangoria 
263290f7d8cSRavi Bangoria static int test5(pid_t child_pid)
264290f7d8cSRavi Bangoria {
265290f7d8cSRavi Bangoria 	int perf_fd;
266290f7d8cSRavi Bangoria 	int ptrace_fd;
267290f7d8cSRavi Bangoria 	int cpid;
268290f7d8cSRavi Bangoria 	int ret = 0;
269290f7d8cSRavi Bangoria 
270290f7d8cSRavi Bangoria 	/* Test:
271290f7d8cSRavi Bangoria 	 * if (new per thread event by ptrace)
272290f7d8cSRavi Bangoria 	 *	if (existing thread event by perf on the different thread)
273290f7d8cSRavi Bangoria 	 *		allow;
274290f7d8cSRavi Bangoria 	 */
275290f7d8cSRavi Bangoria 	cpid = fork();
276290f7d8cSRavi Bangoria 	if (!cpid) {
277290f7d8cSRavi Bangoria 		/* Temporary Child */
278290f7d8cSRavi Bangoria 		pause();
279290f7d8cSRavi Bangoria 		exit(EXIT_SUCCESS);
280290f7d8cSRavi Bangoria 	}
281290f7d8cSRavi Bangoria 
282290f7d8cSRavi Bangoria 	perf_fd = perf_thread_event_open(cpid, (__u64)perf_data1, sizeof(*perf_data1));
283290f7d8cSRavi Bangoria 	if (perf_fd < 0) {
284290f7d8cSRavi Bangoria 		ret = -1;
285290f7d8cSRavi Bangoria 		goto kill_child;
286290f7d8cSRavi Bangoria 	}
287290f7d8cSRavi Bangoria 
288290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
289290f7d8cSRavi Bangoria 	if (ptrace_fd < 0) {
290290f7d8cSRavi Bangoria 		ret = -1;
291290f7d8cSRavi Bangoria 		goto perf_close;
292290f7d8cSRavi Bangoria 	}
293290f7d8cSRavi Bangoria 
294290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
295290f7d8cSRavi Bangoria perf_close:
296290f7d8cSRavi Bangoria 	close(perf_fd);
297290f7d8cSRavi Bangoria kill_child:
298290f7d8cSRavi Bangoria 	kill(cpid, SIGINT);
299290f7d8cSRavi Bangoria 	return ret;
300290f7d8cSRavi Bangoria }
301290f7d8cSRavi Bangoria 
302290f7d8cSRavi Bangoria static int test6(pid_t child_pid)
303290f7d8cSRavi Bangoria {
304290f7d8cSRavi Bangoria 	int perf_fd;
305290f7d8cSRavi Bangoria 	int ptrace_fd;
306290f7d8cSRavi Bangoria 	int ret = 0;
307290f7d8cSRavi Bangoria 
308290f7d8cSRavi Bangoria 	/* Test:
309290f7d8cSRavi Bangoria 	 * if (new per thread kernel event by perf)
310290f7d8cSRavi Bangoria 	 *	if (existing thread event by ptrace on the same thread)
311290f7d8cSRavi Bangoria 	 *		allow;
312290f7d8cSRavi Bangoria 	 * -- OR --
313290f7d8cSRavi Bangoria 	 * if (new per cpu kernel event by perf)
314290f7d8cSRavi Bangoria 	 *	if (existing thread event by ptrace)
315290f7d8cSRavi Bangoria 	 *		allow;
316290f7d8cSRavi Bangoria 	 */
317290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
318290f7d8cSRavi Bangoria 	if (ptrace_fd < 0)
319290f7d8cSRavi Bangoria 		return -1;
320290f7d8cSRavi Bangoria 
321290f7d8cSRavi Bangoria 	perf_fd = perf_thread_kernel_event_open(child_pid);
322290f7d8cSRavi Bangoria 	if (perf_fd < 0) {
323290f7d8cSRavi Bangoria 		ret = -1;
324290f7d8cSRavi Bangoria 		goto ptrace_close;
325290f7d8cSRavi Bangoria 	}
326290f7d8cSRavi Bangoria 	close(perf_fd);
327290f7d8cSRavi Bangoria 
328290f7d8cSRavi Bangoria 	perf_fd = perf_cpu_kernel_event_open(0);
329290f7d8cSRavi Bangoria 	if (perf_fd < 0) {
330290f7d8cSRavi Bangoria 		ret = -1;
331290f7d8cSRavi Bangoria 		goto ptrace_close;
332290f7d8cSRavi Bangoria 	}
333290f7d8cSRavi Bangoria 	close(perf_fd);
334290f7d8cSRavi Bangoria 
335290f7d8cSRavi Bangoria ptrace_close:
336290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
337290f7d8cSRavi Bangoria 	return ret;
338290f7d8cSRavi Bangoria }
339290f7d8cSRavi Bangoria 
340290f7d8cSRavi Bangoria static int test7(pid_t child_pid)
341290f7d8cSRavi Bangoria {
342290f7d8cSRavi Bangoria 	int perf_fd;
343290f7d8cSRavi Bangoria 	int ptrace_fd;
344290f7d8cSRavi Bangoria 	int ret = 0;
345290f7d8cSRavi Bangoria 
346290f7d8cSRavi Bangoria 	/* Test:
347290f7d8cSRavi Bangoria 	 * if (new per thread event by perf)
348290f7d8cSRavi Bangoria 	 *	if (existing thread event by ptrace on the same thread)
349290f7d8cSRavi Bangoria 	 *		if (addr range overlaps)
350290f7d8cSRavi Bangoria 	 *			fail;
351290f7d8cSRavi Bangoria 	 */
352290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
353290f7d8cSRavi Bangoria 	if (ptrace_fd < 0)
354290f7d8cSRavi Bangoria 		return -1;
355290f7d8cSRavi Bangoria 
356290f7d8cSRavi Bangoria 	perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data1,
357290f7d8cSRavi Bangoria 					 sizeof(*perf_data1));
358290f7d8cSRavi Bangoria 	if (perf_fd > 0 || errno != ENOSPC)
359290f7d8cSRavi Bangoria 		ret = -1;
360290f7d8cSRavi Bangoria 
361290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
362290f7d8cSRavi Bangoria 	return ret;
363290f7d8cSRavi Bangoria }
364290f7d8cSRavi Bangoria 
365290f7d8cSRavi Bangoria static int test8(pid_t child_pid)
366290f7d8cSRavi Bangoria {
367290f7d8cSRavi Bangoria 	int perf_fd;
368290f7d8cSRavi Bangoria 	int ptrace_fd;
369290f7d8cSRavi Bangoria 	int ret = 0;
370290f7d8cSRavi Bangoria 
371290f7d8cSRavi Bangoria 	/* Test:
372290f7d8cSRavi Bangoria 	 * if (new per thread event by perf)
373290f7d8cSRavi Bangoria 	 *	if (existing thread event by ptrace on the same thread)
374290f7d8cSRavi Bangoria 	 *		if (addr range does not overlaps)
375290f7d8cSRavi Bangoria 	 *			allow;
376290f7d8cSRavi Bangoria 	 */
377290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
378290f7d8cSRavi Bangoria 	if (ptrace_fd < 0)
379290f7d8cSRavi Bangoria 		return -1;
380290f7d8cSRavi Bangoria 
381290f7d8cSRavi Bangoria 	perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data2,
382290f7d8cSRavi Bangoria 					 sizeof(*perf_data2));
383290f7d8cSRavi Bangoria 	if (perf_fd < 0) {
384290f7d8cSRavi Bangoria 		ret = -1;
385290f7d8cSRavi Bangoria 		goto ptrace_close;
386290f7d8cSRavi Bangoria 	}
387290f7d8cSRavi Bangoria 	close(perf_fd);
388290f7d8cSRavi Bangoria 
389290f7d8cSRavi Bangoria ptrace_close:
390290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
391290f7d8cSRavi Bangoria 	return ret;
392290f7d8cSRavi Bangoria }
393290f7d8cSRavi Bangoria 
394290f7d8cSRavi Bangoria static int test9(pid_t child_pid)
395290f7d8cSRavi Bangoria {
396290f7d8cSRavi Bangoria 	int perf_fd;
397290f7d8cSRavi Bangoria 	int ptrace_fd;
398290f7d8cSRavi Bangoria 	int cpid;
399290f7d8cSRavi Bangoria 	int ret = 0;
400290f7d8cSRavi Bangoria 
401290f7d8cSRavi Bangoria 	/* Test:
402290f7d8cSRavi Bangoria 	 * if (new per thread event by perf)
403290f7d8cSRavi Bangoria 	 *	if (existing thread event by ptrace on the other thread)
404290f7d8cSRavi Bangoria 	 *		allow;
405290f7d8cSRavi Bangoria 	 */
406290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
407290f7d8cSRavi Bangoria 	if (ptrace_fd < 0)
408290f7d8cSRavi Bangoria 		return -1;
409290f7d8cSRavi Bangoria 
410290f7d8cSRavi Bangoria 	cpid = fork();
411290f7d8cSRavi Bangoria 	if (!cpid) {
412290f7d8cSRavi Bangoria 		/* Temporary Child */
413290f7d8cSRavi Bangoria 		pause();
414290f7d8cSRavi Bangoria 		exit(EXIT_SUCCESS);
415290f7d8cSRavi Bangoria 	}
416290f7d8cSRavi Bangoria 
417290f7d8cSRavi Bangoria 	perf_fd = perf_thread_event_open(cpid, (__u64)perf_data1, sizeof(*perf_data1));
418290f7d8cSRavi Bangoria 	if (perf_fd < 0) {
419290f7d8cSRavi Bangoria 		ret = -1;
420290f7d8cSRavi Bangoria 		goto kill_child;
421290f7d8cSRavi Bangoria 	}
422290f7d8cSRavi Bangoria 	close(perf_fd);
423290f7d8cSRavi Bangoria 
424290f7d8cSRavi Bangoria kill_child:
425290f7d8cSRavi Bangoria 	kill(cpid, SIGINT);
426290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
427290f7d8cSRavi Bangoria 	return ret;
428290f7d8cSRavi Bangoria }
429290f7d8cSRavi Bangoria 
430290f7d8cSRavi Bangoria static int test10(pid_t child_pid)
431290f7d8cSRavi Bangoria {
432290f7d8cSRavi Bangoria 	int perf_fd;
433290f7d8cSRavi Bangoria 	int ptrace_fd;
434290f7d8cSRavi Bangoria 	int ret = 0;
435290f7d8cSRavi Bangoria 
436290f7d8cSRavi Bangoria 	/* Test:
437290f7d8cSRavi Bangoria 	 * if (new per cpu event by perf)
438290f7d8cSRavi Bangoria 	 *	if (existing thread event by ptrace on the same thread)
439290f7d8cSRavi Bangoria 	 *		if (addr range overlaps)
440290f7d8cSRavi Bangoria 	 *			fail;
441290f7d8cSRavi Bangoria 	 */
442290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
443290f7d8cSRavi Bangoria 	if (ptrace_fd < 0)
444290f7d8cSRavi Bangoria 		return -1;
445290f7d8cSRavi Bangoria 
446290f7d8cSRavi Bangoria 	perf_fd = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1));
447290f7d8cSRavi Bangoria 	if (perf_fd > 0 || errno != ENOSPC)
448290f7d8cSRavi Bangoria 		ret = -1;
449290f7d8cSRavi Bangoria 
450290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
451290f7d8cSRavi Bangoria 	return ret;
452290f7d8cSRavi Bangoria }
453290f7d8cSRavi Bangoria 
454290f7d8cSRavi Bangoria static int test11(pid_t child_pid)
455290f7d8cSRavi Bangoria {
456290f7d8cSRavi Bangoria 	int perf_fd;
457290f7d8cSRavi Bangoria 	int ptrace_fd;
458290f7d8cSRavi Bangoria 	int ret = 0;
459290f7d8cSRavi Bangoria 
460290f7d8cSRavi Bangoria 	/* Test:
461290f7d8cSRavi Bangoria 	 * if (new per cpu event by perf)
462290f7d8cSRavi Bangoria 	 *	if (existing thread event by ptrace on the same thread)
463290f7d8cSRavi Bangoria 	 *		if (addr range does not overlap)
464290f7d8cSRavi Bangoria 	 *			allow;
465290f7d8cSRavi Bangoria 	 */
466290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
467290f7d8cSRavi Bangoria 	if (ptrace_fd < 0)
468290f7d8cSRavi Bangoria 		return -1;
469290f7d8cSRavi Bangoria 
470290f7d8cSRavi Bangoria 	perf_fd = perf_cpu_event_open(0, (__u64)perf_data2, sizeof(*perf_data2));
471290f7d8cSRavi Bangoria 	if (perf_fd < 0) {
472290f7d8cSRavi Bangoria 		ret = -1;
473290f7d8cSRavi Bangoria 		goto ptrace_close;
474290f7d8cSRavi Bangoria 	}
475290f7d8cSRavi Bangoria 	close(perf_fd);
476290f7d8cSRavi Bangoria 
477290f7d8cSRavi Bangoria ptrace_close:
478290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
479290f7d8cSRavi Bangoria 	return ret;
480290f7d8cSRavi Bangoria }
481290f7d8cSRavi Bangoria 
482290f7d8cSRavi Bangoria static int test12(pid_t child_pid)
483290f7d8cSRavi Bangoria {
484290f7d8cSRavi Bangoria 	int perf_fd;
485290f7d8cSRavi Bangoria 	int ptrace_fd;
486290f7d8cSRavi Bangoria 	int ret = 0;
487290f7d8cSRavi Bangoria 
488290f7d8cSRavi Bangoria 	/* Test:
489290f7d8cSRavi Bangoria 	 * if (new per thread and per cpu event by perf)
490290f7d8cSRavi Bangoria 	 *	if (existing thread event by ptrace on the same thread)
491290f7d8cSRavi Bangoria 	 *		if (addr range overlaps)
492290f7d8cSRavi Bangoria 	 *			fail;
493290f7d8cSRavi Bangoria 	 */
494290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
495290f7d8cSRavi Bangoria 	if (ptrace_fd < 0)
496290f7d8cSRavi Bangoria 		return -1;
497290f7d8cSRavi Bangoria 
498290f7d8cSRavi Bangoria 	perf_fd = perf_thread_cpu_event_open(child_pid, 0, (__u64)perf_data1, sizeof(*perf_data1));
499290f7d8cSRavi Bangoria 	if (perf_fd > 0 || errno != ENOSPC)
500290f7d8cSRavi Bangoria 		ret = -1;
501290f7d8cSRavi Bangoria 
502290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
503290f7d8cSRavi Bangoria 	return ret;
504290f7d8cSRavi Bangoria }
505290f7d8cSRavi Bangoria 
506290f7d8cSRavi Bangoria static int test13(pid_t child_pid)
507290f7d8cSRavi Bangoria {
508290f7d8cSRavi Bangoria 	int perf_fd;
509290f7d8cSRavi Bangoria 	int ptrace_fd;
510290f7d8cSRavi Bangoria 	int ret = 0;
511290f7d8cSRavi Bangoria 
512290f7d8cSRavi Bangoria 	/* Test:
513290f7d8cSRavi Bangoria 	 * if (new per thread and per cpu event by perf)
514290f7d8cSRavi Bangoria 	 *	if (existing thread event by ptrace on the same thread)
515290f7d8cSRavi Bangoria 	 *		if (addr range does not overlap)
516290f7d8cSRavi Bangoria 	 *			allow;
517290f7d8cSRavi Bangoria 	 */
518290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2));
519290f7d8cSRavi Bangoria 	if (ptrace_fd < 0)
520290f7d8cSRavi Bangoria 		return -1;
521290f7d8cSRavi Bangoria 
522290f7d8cSRavi Bangoria 	perf_fd = perf_thread_cpu_event_open(child_pid, 0, (__u64)perf_data2, sizeof(*perf_data2));
523290f7d8cSRavi Bangoria 	if (perf_fd < 0) {
524290f7d8cSRavi Bangoria 		ret = -1;
525290f7d8cSRavi Bangoria 		goto ptrace_close;
526290f7d8cSRavi Bangoria 	}
527290f7d8cSRavi Bangoria 	close(perf_fd);
528290f7d8cSRavi Bangoria 
529290f7d8cSRavi Bangoria ptrace_close:
530290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
531290f7d8cSRavi Bangoria 	return ret;
532290f7d8cSRavi Bangoria }
533290f7d8cSRavi Bangoria 
534290f7d8cSRavi Bangoria static int test14(pid_t child_pid)
535290f7d8cSRavi Bangoria {
536290f7d8cSRavi Bangoria 	int perf_fd;
537290f7d8cSRavi Bangoria 	int ptrace_fd;
538290f7d8cSRavi Bangoria 	int cpid;
539290f7d8cSRavi Bangoria 	int ret = 0;
540290f7d8cSRavi Bangoria 
541290f7d8cSRavi Bangoria 	/* Test:
542290f7d8cSRavi Bangoria 	 * if (new per thread and per cpu event by perf)
543290f7d8cSRavi Bangoria 	 *	if (existing thread event by ptrace on the other thread)
544290f7d8cSRavi Bangoria 	 *		allow;
545290f7d8cSRavi Bangoria 	 */
546290f7d8cSRavi Bangoria 	ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1));
547290f7d8cSRavi Bangoria 	if (ptrace_fd < 0)
548290f7d8cSRavi Bangoria 		return -1;
549290f7d8cSRavi Bangoria 
550290f7d8cSRavi Bangoria 	cpid = fork();
551290f7d8cSRavi Bangoria 	if (!cpid) {
552290f7d8cSRavi Bangoria 		/* Temporary Child */
553290f7d8cSRavi Bangoria 		pause();
554290f7d8cSRavi Bangoria 		exit(EXIT_SUCCESS);
555290f7d8cSRavi Bangoria 	}
556290f7d8cSRavi Bangoria 
557290f7d8cSRavi Bangoria 	perf_fd = perf_thread_cpu_event_open(cpid, 0, (__u64)perf_data1,
558290f7d8cSRavi Bangoria 					     sizeof(*perf_data1));
559290f7d8cSRavi Bangoria 	if (perf_fd < 0) {
560290f7d8cSRavi Bangoria 		ret = -1;
561290f7d8cSRavi Bangoria 		goto kill_child;
562290f7d8cSRavi Bangoria 	}
563290f7d8cSRavi Bangoria 	close(perf_fd);
564290f7d8cSRavi Bangoria 
565290f7d8cSRavi Bangoria kill_child:
566290f7d8cSRavi Bangoria 	kill(cpid, SIGINT);
567290f7d8cSRavi Bangoria 	ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd);
568290f7d8cSRavi Bangoria 	return ret;
569290f7d8cSRavi Bangoria }
570290f7d8cSRavi Bangoria 
571290f7d8cSRavi Bangoria static int do_test(const char *msg, int (*fun)(pid_t arg), pid_t arg)
572290f7d8cSRavi Bangoria {
573290f7d8cSRavi Bangoria 	int ret;
574290f7d8cSRavi Bangoria 
575290f7d8cSRavi Bangoria 	ret = fun(arg);
576290f7d8cSRavi Bangoria 	if (ret)
577290f7d8cSRavi Bangoria 		printf("%s: Error\n", msg);
578290f7d8cSRavi Bangoria 	else
579290f7d8cSRavi Bangoria 		printf("%s: Ok\n", msg);
580290f7d8cSRavi Bangoria 	return ret;
581290f7d8cSRavi Bangoria }
582290f7d8cSRavi Bangoria 
583290f7d8cSRavi Bangoria char *desc[14] = {
584290f7d8cSRavi Bangoria 	"perf cpu event -> ptrace thread event (Overlapping)",
585290f7d8cSRavi Bangoria 	"perf cpu event -> ptrace thread event (Non-overlapping)",
586290f7d8cSRavi Bangoria 	"perf thread event -> ptrace same thread event (Overlapping)",
587290f7d8cSRavi Bangoria 	"perf thread event -> ptrace same thread event (Non-overlapping)",
588290f7d8cSRavi Bangoria 	"perf thread event -> ptrace other thread event",
589290f7d8cSRavi Bangoria 	"ptrace thread event -> perf kernel event",
590290f7d8cSRavi Bangoria 	"ptrace thread event -> perf same thread event (Overlapping)",
591290f7d8cSRavi Bangoria 	"ptrace thread event -> perf same thread event (Non-overlapping)",
592290f7d8cSRavi Bangoria 	"ptrace thread event -> perf other thread event",
593290f7d8cSRavi Bangoria 	"ptrace thread event -> perf cpu event (Overlapping)",
594290f7d8cSRavi Bangoria 	"ptrace thread event -> perf cpu event (Non-overlapping)",
595290f7d8cSRavi Bangoria 	"ptrace thread event -> perf same thread & cpu event (Overlapping)",
596290f7d8cSRavi Bangoria 	"ptrace thread event -> perf same thread & cpu event (Non-overlapping)",
597290f7d8cSRavi Bangoria 	"ptrace thread event -> perf other thread & cpu event",
598290f7d8cSRavi Bangoria };
599290f7d8cSRavi Bangoria 
600290f7d8cSRavi Bangoria static int test(pid_t child_pid)
601290f7d8cSRavi Bangoria {
602290f7d8cSRavi Bangoria 	int ret = TEST_PASS;
603290f7d8cSRavi Bangoria 
604290f7d8cSRavi Bangoria 	ret |= do_test(desc[0], test1, child_pid);
605290f7d8cSRavi Bangoria 	ret |= do_test(desc[1], test2, child_pid);
606290f7d8cSRavi Bangoria 	ret |= do_test(desc[2], test3, child_pid);
607290f7d8cSRavi Bangoria 	ret |= do_test(desc[3], test4, child_pid);
608290f7d8cSRavi Bangoria 	ret |= do_test(desc[4], test5, child_pid);
609290f7d8cSRavi Bangoria 	ret |= do_test(desc[5], test6, child_pid);
610290f7d8cSRavi Bangoria 	ret |= do_test(desc[6], test7, child_pid);
611290f7d8cSRavi Bangoria 	ret |= do_test(desc[7], test8, child_pid);
612290f7d8cSRavi Bangoria 	ret |= do_test(desc[8], test9, child_pid);
613290f7d8cSRavi Bangoria 	ret |= do_test(desc[9], test10, child_pid);
614290f7d8cSRavi Bangoria 	ret |= do_test(desc[10], test11, child_pid);
615290f7d8cSRavi Bangoria 	ret |= do_test(desc[11], test12, child_pid);
616290f7d8cSRavi Bangoria 	ret |= do_test(desc[12], test13, child_pid);
617290f7d8cSRavi Bangoria 	ret |= do_test(desc[13], test14, child_pid);
618290f7d8cSRavi Bangoria 
619290f7d8cSRavi Bangoria 	return ret;
620290f7d8cSRavi Bangoria }
621290f7d8cSRavi Bangoria 
622290f7d8cSRavi Bangoria static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
623290f7d8cSRavi Bangoria {
624290f7d8cSRavi Bangoria 	if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
625290f7d8cSRavi Bangoria 		perror("Can't get breakpoint info");
626290f7d8cSRavi Bangoria 		exit(-1);
627290f7d8cSRavi Bangoria 	}
628290f7d8cSRavi Bangoria }
629290f7d8cSRavi Bangoria 
630290f7d8cSRavi Bangoria static int ptrace_perf_hwbreak(void)
631290f7d8cSRavi Bangoria {
632290f7d8cSRavi Bangoria 	int ret;
633290f7d8cSRavi Bangoria 	pid_t child_pid;
634290f7d8cSRavi Bangoria 	struct ppc_debug_info dbginfo;
635290f7d8cSRavi Bangoria 
636290f7d8cSRavi Bangoria 	child_pid = fork();
637290f7d8cSRavi Bangoria 	if (!child_pid)
638290f7d8cSRavi Bangoria 		return child();
639290f7d8cSRavi Bangoria 
640290f7d8cSRavi Bangoria 	/* parent */
641290f7d8cSRavi Bangoria 	wait(NULL); /* <-- child (SIGUSR1) */
642290f7d8cSRavi Bangoria 
643290f7d8cSRavi Bangoria 	get_dbginfo(child_pid, &dbginfo);
644*68877ff2SBenjamin Gray 	SKIP_IF_MSG(dbginfo.num_data_bps <= 1, "Not enough data watchpoints (need at least 2)");
645290f7d8cSRavi Bangoria 
646290f7d8cSRavi Bangoria 	ret = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1));
647*68877ff2SBenjamin Gray 	SKIP_IF_MSG(ret < 0, "perf_event_open syscall failed");
648290f7d8cSRavi Bangoria 	close(ret);
649290f7d8cSRavi Bangoria 
650290f7d8cSRavi Bangoria 	ret = test(child_pid);
651290f7d8cSRavi Bangoria 
652290f7d8cSRavi Bangoria 	ptrace(PTRACE_CONT, child_pid, NULL, 0);
653290f7d8cSRavi Bangoria 	return ret;
654290f7d8cSRavi Bangoria }
655290f7d8cSRavi Bangoria 
656290f7d8cSRavi Bangoria int main(int argc, char *argv[])
657290f7d8cSRavi Bangoria {
658290f7d8cSRavi Bangoria 	return test_harness(ptrace_perf_hwbreak, "ptrace-perf-hwbreak");
659290f7d8cSRavi Bangoria }
660