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