xref: /OK3568_Linux_fs/kernel/tools/perf/arch/x86/tests/bp-modify.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun #include <linux/compiler.h>
3*4882a593Smuzhiyun #include <sys/types.h>
4*4882a593Smuzhiyun #include <sys/wait.h>
5*4882a593Smuzhiyun #include <sys/user.h>
6*4882a593Smuzhiyun #include <syscall.h>
7*4882a593Smuzhiyun #include <unistd.h>
8*4882a593Smuzhiyun #include <stdio.h>
9*4882a593Smuzhiyun #include <stdlib.h>
10*4882a593Smuzhiyun #include <string.h>
11*4882a593Smuzhiyun #include <sys/ptrace.h>
12*4882a593Smuzhiyun #include <asm/ptrace.h>
13*4882a593Smuzhiyun #include <errno.h>
14*4882a593Smuzhiyun #include "debug.h"
15*4882a593Smuzhiyun #include "tests/tests.h"
16*4882a593Smuzhiyun #include "arch-tests.h"
17*4882a593Smuzhiyun 
bp_1(void)18*4882a593Smuzhiyun static noinline int bp_1(void)
19*4882a593Smuzhiyun {
20*4882a593Smuzhiyun 	pr_debug("in %s\n", __func__);
21*4882a593Smuzhiyun 	return 0;
22*4882a593Smuzhiyun }
23*4882a593Smuzhiyun 
bp_2(void)24*4882a593Smuzhiyun static noinline int bp_2(void)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	pr_debug("in %s\n", __func__);
27*4882a593Smuzhiyun 	return 0;
28*4882a593Smuzhiyun }
29*4882a593Smuzhiyun 
spawn_child(void)30*4882a593Smuzhiyun static int spawn_child(void)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun 	int child = fork();
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun 	if (child == 0) {
35*4882a593Smuzhiyun 		/*
36*4882a593Smuzhiyun 		 * The child sets itself for as tracee and
37*4882a593Smuzhiyun 		 * waits in signal for parent to trace it,
38*4882a593Smuzhiyun 		 * then it calls bp_1 and quits.
39*4882a593Smuzhiyun 		 */
40*4882a593Smuzhiyun 		int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 		if (err) {
43*4882a593Smuzhiyun 			pr_debug("failed to PTRACE_TRACEME\n");
44*4882a593Smuzhiyun 			exit(1);
45*4882a593Smuzhiyun 		}
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 		raise(SIGCONT);
48*4882a593Smuzhiyun 		bp_1();
49*4882a593Smuzhiyun 		exit(0);
50*4882a593Smuzhiyun 	}
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	return child;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun /*
56*4882a593Smuzhiyun  * This tests creates HW breakpoint, tries to
57*4882a593Smuzhiyun  * change it and checks it was properly changed.
58*4882a593Smuzhiyun  */
bp_modify1(void)59*4882a593Smuzhiyun static int bp_modify1(void)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun 	pid_t child;
62*4882a593Smuzhiyun 	int status;
63*4882a593Smuzhiyun 	unsigned long rip = 0, dr7 = 1;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	child = spawn_child();
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	waitpid(child, &status, 0);
68*4882a593Smuzhiyun 	if (WIFEXITED(status)) {
69*4882a593Smuzhiyun 		pr_debug("tracee exited prematurely 1\n");
70*4882a593Smuzhiyun 		return TEST_FAIL;
71*4882a593Smuzhiyun 	}
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	/*
74*4882a593Smuzhiyun 	 * The parent does following steps:
75*4882a593Smuzhiyun 	 *  - creates a new breakpoint (id 0) for bp_2 function
76*4882a593Smuzhiyun 	 *  - changes that breakponit to bp_1 function
77*4882a593Smuzhiyun 	 *  - waits for the breakpoint to hit and checks
78*4882a593Smuzhiyun 	 *    it has proper rip of bp_1 function
79*4882a593Smuzhiyun 	 *  - detaches the child
80*4882a593Smuzhiyun 	 */
81*4882a593Smuzhiyun 	if (ptrace(PTRACE_POKEUSER, child,
82*4882a593Smuzhiyun 		   offsetof(struct user, u_debugreg[0]), bp_2)) {
83*4882a593Smuzhiyun 		pr_debug("failed to set breakpoint, 1st time: %s\n",
84*4882a593Smuzhiyun 			 strerror(errno));
85*4882a593Smuzhiyun 		goto out;
86*4882a593Smuzhiyun 	}
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	if (ptrace(PTRACE_POKEUSER, child,
89*4882a593Smuzhiyun 		   offsetof(struct user, u_debugreg[0]), bp_1)) {
90*4882a593Smuzhiyun 		pr_debug("failed to set breakpoint, 2nd time: %s\n",
91*4882a593Smuzhiyun 			 strerror(errno));
92*4882a593Smuzhiyun 		goto out;
93*4882a593Smuzhiyun 	}
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	if (ptrace(PTRACE_POKEUSER, child,
96*4882a593Smuzhiyun 		   offsetof(struct user, u_debugreg[7]), dr7)) {
97*4882a593Smuzhiyun 		pr_debug("failed to set dr7: %s\n", strerror(errno));
98*4882a593Smuzhiyun 		goto out;
99*4882a593Smuzhiyun 	}
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
102*4882a593Smuzhiyun 		pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
103*4882a593Smuzhiyun 		goto out;
104*4882a593Smuzhiyun 	}
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	waitpid(child, &status, 0);
107*4882a593Smuzhiyun 	if (WIFEXITED(status)) {
108*4882a593Smuzhiyun 		pr_debug("tracee exited prematurely 2\n");
109*4882a593Smuzhiyun 		return TEST_FAIL;
110*4882a593Smuzhiyun 	}
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	rip = ptrace(PTRACE_PEEKUSER, child,
113*4882a593Smuzhiyun 		     offsetof(struct user_regs_struct, rip), NULL);
114*4882a593Smuzhiyun 	if (rip == (unsigned long) -1) {
115*4882a593Smuzhiyun 		pr_debug("failed to PTRACE_PEEKUSER: %s\n",
116*4882a593Smuzhiyun 			 strerror(errno));
117*4882a593Smuzhiyun 		goto out;
118*4882a593Smuzhiyun 	}
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun out:
123*4882a593Smuzhiyun 	if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
124*4882a593Smuzhiyun 		pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
125*4882a593Smuzhiyun 		return TEST_FAIL;
126*4882a593Smuzhiyun 	}
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun /*
132*4882a593Smuzhiyun  * This tests creates HW breakpoint, tries to
133*4882a593Smuzhiyun  * change it to bogus value and checks the original
134*4882a593Smuzhiyun  * breakpoint is hit.
135*4882a593Smuzhiyun  */
bp_modify2(void)136*4882a593Smuzhiyun static int bp_modify2(void)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun 	pid_t child;
139*4882a593Smuzhiyun 	int status;
140*4882a593Smuzhiyun 	unsigned long rip = 0, dr7 = 1;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	child = spawn_child();
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	waitpid(child, &status, 0);
145*4882a593Smuzhiyun 	if (WIFEXITED(status)) {
146*4882a593Smuzhiyun 		pr_debug("tracee exited prematurely 1\n");
147*4882a593Smuzhiyun 		return TEST_FAIL;
148*4882a593Smuzhiyun 	}
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	/*
151*4882a593Smuzhiyun 	 * The parent does following steps:
152*4882a593Smuzhiyun 	 *  - creates a new breakpoint (id 0) for bp_1 function
153*4882a593Smuzhiyun 	 *  - tries to change that breakpoint to (-1) address
154*4882a593Smuzhiyun 	 *  - waits for the breakpoint to hit and checks
155*4882a593Smuzhiyun 	 *    it has proper rip of bp_1 function
156*4882a593Smuzhiyun 	 *  - detaches the child
157*4882a593Smuzhiyun 	 */
158*4882a593Smuzhiyun 	if (ptrace(PTRACE_POKEUSER, child,
159*4882a593Smuzhiyun 		   offsetof(struct user, u_debugreg[0]), bp_1)) {
160*4882a593Smuzhiyun 		pr_debug("failed to set breakpoint: %s\n",
161*4882a593Smuzhiyun 			 strerror(errno));
162*4882a593Smuzhiyun 		goto out;
163*4882a593Smuzhiyun 	}
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	if (ptrace(PTRACE_POKEUSER, child,
166*4882a593Smuzhiyun 		   offsetof(struct user, u_debugreg[7]), dr7)) {
167*4882a593Smuzhiyun 		pr_debug("failed to set dr7: %s\n", strerror(errno));
168*4882a593Smuzhiyun 		goto out;
169*4882a593Smuzhiyun 	}
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	if (!ptrace(PTRACE_POKEUSER, child,
172*4882a593Smuzhiyun 		   offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) {
173*4882a593Smuzhiyun 		pr_debug("failed, breakpoint set to bogus address\n");
174*4882a593Smuzhiyun 		goto out;
175*4882a593Smuzhiyun 	}
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
178*4882a593Smuzhiyun 		pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
179*4882a593Smuzhiyun 		goto out;
180*4882a593Smuzhiyun 	}
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	waitpid(child, &status, 0);
183*4882a593Smuzhiyun 	if (WIFEXITED(status)) {
184*4882a593Smuzhiyun 		pr_debug("tracee exited prematurely 2\n");
185*4882a593Smuzhiyun 		return TEST_FAIL;
186*4882a593Smuzhiyun 	}
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	rip = ptrace(PTRACE_PEEKUSER, child,
189*4882a593Smuzhiyun 		     offsetof(struct user_regs_struct, rip), NULL);
190*4882a593Smuzhiyun 	if (rip == (unsigned long) -1) {
191*4882a593Smuzhiyun 		pr_debug("failed to PTRACE_PEEKUSER: %s\n",
192*4882a593Smuzhiyun 			 strerror(errno));
193*4882a593Smuzhiyun 		goto out;
194*4882a593Smuzhiyun 	}
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun out:
199*4882a593Smuzhiyun 	if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
200*4882a593Smuzhiyun 		pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
201*4882a593Smuzhiyun 		return TEST_FAIL;
202*4882a593Smuzhiyun 	}
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun 
test__bp_modify(struct test * test __maybe_unused,int subtest __maybe_unused)207*4882a593Smuzhiyun int test__bp_modify(struct test *test __maybe_unused,
208*4882a593Smuzhiyun 		    int subtest __maybe_unused)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun 	TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1());
211*4882a593Smuzhiyun 	TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2());
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	return 0;
214*4882a593Smuzhiyun }
215