xref: /linux/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c (revision bfd5bb6f90af092aa345b15cd78143956a13c2a8)
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 "ptrace.h"
24 
25 /* Breakpoint access modes */
26 enum {
27 	BP_X = 1,
28 	BP_RW = 2,
29 	BP_W = 4,
30 };
31 
32 static pid_t child_pid;
33 static struct ppc_debug_info dbginfo;
34 
35 static void get_dbginfo(void)
36 {
37 	int ret;
38 
39 	ret = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
40 	if (ret) {
41 		perror("Can't get breakpoint info\n");
42 		exit(-1);
43 	}
44 }
45 
46 static bool hwbreak_present(void)
47 {
48 	return (dbginfo.num_data_bps != 0);
49 }
50 
51 static bool dawr_present(void)
52 {
53 	return !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
54 }
55 
56 static void set_breakpoint_addr(void *addr)
57 {
58 	int ret;
59 
60 	ret = ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, addr);
61 	if (ret) {
62 		perror("Can't set breakpoint addr\n");
63 		exit(-1);
64 	}
65 }
66 
67 static int set_hwbreakpoint_addr(void *addr, int range)
68 {
69 	int ret;
70 
71 	struct ppc_hw_breakpoint info;
72 
73 	info.version = 1;
74 	info.trigger_type = PPC_BREAKPOINT_TRIGGER_RW;
75 	info.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
76 	if (range > 0)
77 		info.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
78 	info.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
79 	info.addr = (__u64)addr;
80 	info.addr2 = (__u64)addr + range;
81 	info.condition_value = 0;
82 
83 	ret = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info);
84 	if (ret < 0) {
85 		perror("Can't set breakpoint\n");
86 		exit(-1);
87 	}
88 	return ret;
89 }
90 
91 static int del_hwbreakpoint_addr(int watchpoint_handle)
92 {
93 	int ret;
94 
95 	ret = ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, watchpoint_handle);
96 	if (ret < 0) {
97 		perror("Can't delete hw breakpoint\n");
98 		exit(-1);
99 	}
100 	return ret;
101 }
102 
103 #define DAWR_LENGTH_MAX 512
104 
105 /* Dummy variables to test read/write accesses */
106 static unsigned long long
107 	dummy_array[DAWR_LENGTH_MAX / sizeof(unsigned long long)]
108 	__attribute__((aligned(512)));
109 static unsigned long long *dummy_var = dummy_array;
110 
111 static void write_var(int len)
112 {
113 	long long *plval;
114 	char *pcval;
115 	short *psval;
116 	int *pival;
117 
118 	switch (len) {
119 	case 1:
120 		pcval = (char *)dummy_var;
121 		*pcval = 0xff;
122 		break;
123 	case 2:
124 		psval = (short *)dummy_var;
125 		*psval = 0xffff;
126 		break;
127 	case 4:
128 		pival = (int *)dummy_var;
129 		*pival = 0xffffffff;
130 		break;
131 	case 8:
132 		plval = (long long *)dummy_var;
133 		*plval = 0xffffffffffffffffLL;
134 		break;
135 	}
136 }
137 
138 static void read_var(int len)
139 {
140 	char cval __attribute__((unused));
141 	short sval __attribute__((unused));
142 	int ival __attribute__((unused));
143 	long long lval __attribute__((unused));
144 
145 	switch (len) {
146 	case 1:
147 		cval = *(char *)dummy_var;
148 		break;
149 	case 2:
150 		sval = *(short *)dummy_var;
151 		break;
152 	case 4:
153 		ival = *(int *)dummy_var;
154 		break;
155 	case 8:
156 		lval = *(long long *)dummy_var;
157 		break;
158 	}
159 }
160 
161 /*
162  * Do the r/w accesses to trigger the breakpoints. And run
163  * the usual traps.
164  */
165 static void trigger_tests(void)
166 {
167 	int len, ret;
168 
169 	ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
170 	if (ret) {
171 		perror("Can't be traced?\n");
172 		return;
173 	}
174 
175 	/* Wake up father so that it sets up the first test */
176 	kill(getpid(), SIGUSR1);
177 
178 	/* Test write watchpoints */
179 	for (len = 1; len <= sizeof(long); len <<= 1)
180 		write_var(len);
181 
182 	/* Test read/write watchpoints (on read accesses) */
183 	for (len = 1; len <= sizeof(long); len <<= 1)
184 		read_var(len);
185 
186 	/* Test when breakpoint is unset */
187 
188 	/* Test write watchpoints */
189 	for (len = 1; len <= sizeof(long); len <<= 1)
190 		write_var(len);
191 
192 	/* Test read/write watchpoints (on read accesses) */
193 	for (len = 1; len <= sizeof(long); len <<= 1)
194 		read_var(len);
195 }
196 
197 static void check_success(const char *msg)
198 {
199 	const char *msg2;
200 	int status;
201 
202 	/* Wait for the child to SIGTRAP */
203 	wait(&status);
204 
205 	msg2 = "Failed";
206 
207 	if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
208 		msg2 = "Child process hit the breakpoint";
209 	}
210 
211 	printf("%s Result: [%s]\n", msg, msg2);
212 }
213 
214 static void launch_watchpoints(char *buf, int mode, int len,
215 			       struct ppc_debug_info *dbginfo, bool dawr)
216 {
217 	const char *mode_str;
218 	unsigned long data = (unsigned long)(dummy_var);
219 	int wh, range;
220 
221 	data &= ~0x7UL;
222 
223 	if (mode == BP_W) {
224 		data |= (1UL << 1);
225 		mode_str = "write";
226 	} else {
227 		data |= (1UL << 0);
228 		data |= (1UL << 1);
229 		mode_str = "read";
230 	}
231 
232 	/* Set DABR_TRANSLATION bit */
233 	data |= (1UL << 2);
234 
235 	/* use PTRACE_SET_DEBUGREG breakpoints */
236 	set_breakpoint_addr((void *)data);
237 	ptrace(PTRACE_CONT, child_pid, NULL, 0);
238 	sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
239 	check_success(buf);
240 	/* Unregister hw brkpoint */
241 	set_breakpoint_addr(NULL);
242 
243 	data = (data & ~7); /* remove dabr control bits */
244 
245 	/* use PPC_PTRACE_SETHWDEBUG breakpoint */
246 	if (!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
247 		return; /* not supported */
248 	wh = set_hwbreakpoint_addr((void *)data, 0);
249 	ptrace(PTRACE_CONT, child_pid, NULL, 0);
250 	sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
251 	check_success(buf);
252 	/* Unregister hw brkpoint */
253 	del_hwbreakpoint_addr(wh);
254 
255 	/* try a wider range */
256 	range = 8;
257 	if (dawr)
258 		range = 512 - ((int)data & (DAWR_LENGTH_MAX - 1));
259 	wh = set_hwbreakpoint_addr((void *)data, range);
260 	ptrace(PTRACE_CONT, child_pid, NULL, 0);
261 	sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
262 	check_success(buf);
263 	/* Unregister hw brkpoint */
264 	del_hwbreakpoint_addr(wh);
265 }
266 
267 /* Set the breakpoints and check the child successfully trigger them */
268 static int launch_tests(bool dawr)
269 {
270 	char buf[1024];
271 	int len, i, status;
272 
273 	struct ppc_debug_info dbginfo;
274 
275 	i = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
276 	if (i) {
277 		perror("Can't set breakpoint info\n");
278 		exit(-1);
279 	}
280 	if (!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
281 		printf("WARNING: Kernel doesn't support PPC_PTRACE_SETHWDEBUG\n");
282 
283 	/* Write watchpoint */
284 	for (len = 1; len <= sizeof(long); len <<= 1)
285 		launch_watchpoints(buf, BP_W, len, &dbginfo, dawr);
286 
287 	/* Read-Write watchpoint */
288 	for (len = 1; len <= sizeof(long); len <<= 1)
289 		launch_watchpoints(buf, BP_RW, len, &dbginfo, dawr);
290 
291 	ptrace(PTRACE_CONT, child_pid, NULL, 0);
292 
293 	/*
294 	 * Now we have unregistered the breakpoint, access by child
295 	 * should not cause SIGTRAP.
296 	 */
297 
298 	wait(&status);
299 
300 	if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
301 		printf("FAIL: Child process hit the breakpoint, which is not expected\n");
302 		ptrace(PTRACE_CONT, child_pid, NULL, 0);
303 		return TEST_FAIL;
304 	}
305 
306 	if (WIFEXITED(status))
307 		printf("Child exited normally\n");
308 
309 	return TEST_PASS;
310 }
311 
312 static int ptrace_hwbreak(void)
313 {
314 	pid_t pid;
315 	int ret;
316 	bool dawr;
317 
318 	pid = fork();
319 	if (!pid) {
320 		trigger_tests();
321 		return 0;
322 	}
323 
324 	wait(NULL);
325 
326 	child_pid = pid;
327 
328 	get_dbginfo();
329 	SKIP_IF(!hwbreak_present());
330 	dawr = dawr_present();
331 
332 	ret = launch_tests(dawr);
333 
334 	wait(NULL);
335 
336 	return ret;
337 }
338 
339 int main(int argc, char **argv, char **envp)
340 {
341 	return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
342 }
343