Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / tools / testing / selftests / breakpoints / breakpoint_test.c
1 /*
2  * Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com>
3  *
4  * Licensed under the terms of the GNU GPL License version 2
5  *
6  * Selftests for breakpoints (and more generally the do_debug() path) in x86.
7  */
8
9
10 #include <sys/ptrace.h>
11 #include <unistd.h>
12 #include <stddef.h>
13 #include <sys/user.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <signal.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19
20 #include "../kselftest.h"
21
22
23 /* Breakpoint access modes */
24 enum {
25         BP_X = 1,
26         BP_RW = 2,
27         BP_W = 4,
28 };
29
30 static pid_t child_pid;
31
32 /*
33  * Ensures the child and parent are always "talking" about
34  * the same test sequence. (ie: that we haven't forgotten
35  * to call check_trapped() somewhere).
36  */
37 static int nr_tests;
38
39 static void set_breakpoint_addr(void *addr, int n)
40 {
41         int ret;
42
43         ret = ptrace(PTRACE_POKEUSER, child_pid,
44                      offsetof(struct user, u_debugreg[n]), addr);
45         if (ret) {
46                 perror("Can't set breakpoint addr\n");
47                 ksft_exit_fail();
48         }
49 }
50
51 static void toggle_breakpoint(int n, int type, int len,
52                               int local, int global, int set)
53 {
54         int ret;
55
56         int xtype, xlen;
57         unsigned long vdr7, dr7;
58
59         switch (type) {
60         case BP_X:
61                 xtype = 0;
62                 break;
63         case BP_W:
64                 xtype = 1;
65                 break;
66         case BP_RW:
67                 xtype = 3;
68                 break;
69         }
70
71         switch (len) {
72         case 1:
73                 xlen = 0;
74                 break;
75         case 2:
76                 xlen = 4;
77                 break;
78         case 4:
79                 xlen = 0xc;
80                 break;
81         case 8:
82                 xlen = 8;
83                 break;
84         }
85
86         dr7 = ptrace(PTRACE_PEEKUSER, child_pid,
87                      offsetof(struct user, u_debugreg[7]), 0);
88
89         vdr7 = (xlen | xtype) << 16;
90         vdr7 <<= 4 * n;
91
92         if (local) {
93                 vdr7 |= 1 << (2 * n);
94                 vdr7 |= 1 << 8;
95         }
96         if (global) {
97                 vdr7 |= 2 << (2 * n);
98                 vdr7 |= 1 << 9;
99         }
100
101         if (set)
102                 dr7 |= vdr7;
103         else
104                 dr7 &= ~vdr7;
105
106         ret = ptrace(PTRACE_POKEUSER, child_pid,
107                      offsetof(struct user, u_debugreg[7]), dr7);
108         if (ret) {
109                 perror("Can't set dr7");
110                 ksft_exit_fail();
111         }
112 }
113
114 /* Dummy variables to test read/write accesses */
115 static unsigned long long dummy_var[4];
116
117 /* Dummy functions to test execution accesses */
118 static void dummy_func(void) { }
119 static void dummy_func1(void) { }
120 static void dummy_func2(void) { }
121 static void dummy_func3(void) { }
122
123 static void (*dummy_funcs[])(void) = {
124         dummy_func,
125         dummy_func1,
126         dummy_func2,
127         dummy_func3,
128 };
129
130 static int trapped;
131
132 static void check_trapped(void)
133 {
134         /*
135          * If we haven't trapped, wake up the parent
136          * so that it notices the failure.
137          */
138         if (!trapped)
139                 kill(getpid(), SIGUSR1);
140         trapped = 0;
141
142         nr_tests++;
143 }
144
145 static void write_var(int len)
146 {
147         char *pcval; short *psval; int *pival; long long *plval;
148         int i;
149
150         for (i = 0; i < 4; i++) {
151                 switch (len) {
152                 case 1:
153                         pcval = (char *)&dummy_var[i];
154                         *pcval = 0xff;
155                         break;
156                 case 2:
157                         psval = (short *)&dummy_var[i];
158                         *psval = 0xffff;
159                         break;
160                 case 4:
161                         pival = (int *)&dummy_var[i];
162                         *pival = 0xffffffff;
163                         break;
164                 case 8:
165                         plval = (long long *)&dummy_var[i];
166                         *plval = 0xffffffffffffffffLL;
167                         break;
168                 }
169                 check_trapped();
170         }
171 }
172
173 static void read_var(int len)
174 {
175         char cval; short sval; int ival; long long lval;
176         int i;
177
178         for (i = 0; i < 4; i++) {
179                 switch (len) {
180                 case 1:
181                         cval = *(char *)&dummy_var[i];
182                         break;
183                 case 2:
184                         sval = *(short *)&dummy_var[i];
185                         break;
186                 case 4:
187                         ival = *(int *)&dummy_var[i];
188                         break;
189                 case 8:
190                         lval = *(long long *)&dummy_var[i];
191                         break;
192                 }
193                 check_trapped();
194         }
195 }
196
197 /*
198  * Do the r/w/x accesses to trigger the breakpoints. And run
199  * the usual traps.
200  */
201 static void trigger_tests(void)
202 {
203         int len, local, global, i;
204         char val;
205         int ret;
206
207         ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
208         if (ret) {
209                 perror("Can't be traced?\n");
210                 return;
211         }
212
213         /* Wake up father so that it sets up the first test */
214         kill(getpid(), SIGUSR1);
215
216         /* Test instruction breakpoints */
217         for (local = 0; local < 2; local++) {
218                 for (global = 0; global < 2; global++) {
219                         if (!local && !global)
220                                 continue;
221
222                         for (i = 0; i < 4; i++) {
223                                 dummy_funcs[i]();
224                                 check_trapped();
225                         }
226                 }
227         }
228
229         /* Test write watchpoints */
230         for (len = 1; len <= sizeof(long); len <<= 1) {
231                 for (local = 0; local < 2; local++) {
232                         for (global = 0; global < 2; global++) {
233                                 if (!local && !global)
234                                         continue;
235                                 write_var(len);
236                         }
237                 }
238         }
239
240         /* Test read/write watchpoints (on read accesses) */
241         for (len = 1; len <= sizeof(long); len <<= 1) {
242                 for (local = 0; local < 2; local++) {
243                         for (global = 0; global < 2; global++) {
244                                 if (!local && !global)
245                                         continue;
246                                 read_var(len);
247                         }
248                 }
249         }
250
251         /* Icebp trap */
252         asm(".byte 0xf1\n");
253         check_trapped();
254
255         /* Int 3 trap */
256         asm("int $3\n");
257         check_trapped();
258
259         kill(getpid(), SIGUSR1);
260 }
261
262 static void check_success(const char *msg)
263 {
264         const char *msg2;
265         int child_nr_tests;
266         int status;
267
268         /* Wait for the child to SIGTRAP */
269         wait(&status);
270
271         msg2 = "Failed";
272
273         if (WSTOPSIG(status) == SIGTRAP) {
274                 child_nr_tests = ptrace(PTRACE_PEEKDATA, child_pid,
275                                         &nr_tests, 0);
276                 if (child_nr_tests == nr_tests)
277                         msg2 = "Ok";
278                 if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1)) {
279                         perror("Can't poke\n");
280                         ksft_exit_fail();
281                 }
282         }
283
284         nr_tests++;
285
286         printf("%s [%s]\n", msg, msg2);
287 }
288
289 static void launch_instruction_breakpoints(char *buf, int local, int global)
290 {
291         int i;
292
293         for (i = 0; i < 4; i++) {
294                 set_breakpoint_addr(dummy_funcs[i], i);
295                 toggle_breakpoint(i, BP_X, 1, local, global, 1);
296                 ptrace(PTRACE_CONT, child_pid, NULL, 0);
297                 sprintf(buf, "Test breakpoint %d with local: %d global: %d",
298                         i, local, global);
299                 check_success(buf);
300                 toggle_breakpoint(i, BP_X, 1, local, global, 0);
301         }
302 }
303
304 static void launch_watchpoints(char *buf, int mode, int len,
305                                int local, int global)
306 {
307         const char *mode_str;
308         int i;
309
310         if (mode == BP_W)
311                 mode_str = "write";
312         else
313                 mode_str = "read";
314
315         for (i = 0; i < 4; i++) {
316                 set_breakpoint_addr(&dummy_var[i], i);
317                 toggle_breakpoint(i, mode, len, local, global, 1);
318                 ptrace(PTRACE_CONT, child_pid, NULL, 0);
319                 sprintf(buf, "Test %s watchpoint %d with len: %d local: "
320                         "%d global: %d", mode_str, i, len, local, global);
321                 check_success(buf);
322                 toggle_breakpoint(i, mode, len, local, global, 0);
323         }
324 }
325
326 /* Set the breakpoints and check the child successfully trigger them */
327 static void launch_tests(void)
328 {
329         char buf[1024];
330         int len, local, global, i;
331
332         /* Instruction breakpoints */
333         for (local = 0; local < 2; local++) {
334                 for (global = 0; global < 2; global++) {
335                         if (!local && !global)
336                                 continue;
337                         launch_instruction_breakpoints(buf, local, global);
338                 }
339         }
340
341         /* Write watchpoint */
342         for (len = 1; len <= sizeof(long); len <<= 1) {
343                 for (local = 0; local < 2; local++) {
344                         for (global = 0; global < 2; global++) {
345                                 if (!local && !global)
346                                         continue;
347                                 launch_watchpoints(buf, BP_W, len,
348                                                    local, global);
349                         }
350                 }
351         }
352
353         /* Read-Write watchpoint */
354         for (len = 1; len <= sizeof(long); len <<= 1) {
355                 for (local = 0; local < 2; local++) {
356                         for (global = 0; global < 2; global++) {
357                                 if (!local && !global)
358                                         continue;
359                                 launch_watchpoints(buf, BP_RW, len,
360                                                    local, global);
361                         }
362                 }
363         }
364
365         /* Icebp traps */
366         ptrace(PTRACE_CONT, child_pid, NULL, 0);
367         check_success("Test icebp");
368
369         /* Int 3 traps */
370         ptrace(PTRACE_CONT, child_pid, NULL, 0);
371         check_success("Test int 3 trap");
372
373         ptrace(PTRACE_CONT, child_pid, NULL, 0);
374 }
375
376 int main(int argc, char **argv)
377 {
378         pid_t pid;
379         int ret;
380
381         pid = fork();
382         if (!pid) {
383                 trigger_tests();
384                 return 0;
385         }
386
387         child_pid = pid;
388
389         wait(NULL);
390
391         launch_tests();
392
393         wait(NULL);
394
395         return ksft_exit_pass();
396 }