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
30bool 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 */
36static volatile __u64 glvar;
37
38#define DAWR_MAX_LEN 512
39static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
40
41#define A_LEN 6
42#define B_LEN 6
43struct gstruct {
44	__u8 a[A_LEN]; /* double word aligned */
45	__u8 b[B_LEN]; /* double word unaligned */
46};
47static volatile struct gstruct gstruct __attribute__((aligned(512)));
48
49static volatile char cwd[PATH_MAX] __attribute__((aligned(8)));
50
51static 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
59static bool dawr_present(struct ppc_debug_info *dbginfo)
60{
61	return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
62}
63
64static 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
91static 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
114static 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
199static 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
233static 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
241static 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
252static 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
264static 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
305static 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
323static 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
338static 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
368static 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
384static 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
420static 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
457static 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
475static 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 */
494static void
495run_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
511static 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
542int 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