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