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