Merge tag 'char-misc-5.15-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / tools / perf / tests / wp.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/ioctl.h>
6 #include <linux/hw_breakpoint.h>
7 #include <linux/kernel.h>
8 #include "tests.h"
9 #include "debug.h"
10 #include "event.h"
11 #include "cloexec.h"
12 #include "../perf-sys.h"
13
14 #define WP_TEST_ASSERT_VAL(fd, text, val)       \
15 do {                                            \
16         long long count;                        \
17         wp_read(fd, &count, sizeof(long long)); \
18         TEST_ASSERT_VAL(text, count == val);    \
19 } while (0)
20
21 volatile u64 data1;
22 volatile u8 data2[3];
23
24 static int wp_read(int fd, long long *count, int size)
25 {
26         int ret = read(fd, count, size);
27
28         if (ret != size) {
29                 pr_debug("failed to read: %d\n", ret);
30                 return -1;
31         }
32         return 0;
33 }
34
35 static void get__perf_event_attr(struct perf_event_attr *attr, int wp_type,
36                                  void *wp_addr, unsigned long wp_len)
37 {
38         memset(attr, 0, sizeof(struct perf_event_attr));
39         attr->type           = PERF_TYPE_BREAKPOINT;
40         attr->size           = sizeof(struct perf_event_attr);
41         attr->config         = 0;
42         attr->bp_type        = wp_type;
43         attr->bp_addr        = (unsigned long)wp_addr;
44         attr->bp_len         = wp_len;
45         attr->sample_period  = 1;
46         attr->sample_type    = PERF_SAMPLE_IP;
47         attr->exclude_kernel = 1;
48         attr->exclude_hv     = 1;
49 }
50
51 static int __event(int wp_type, void *wp_addr, unsigned long wp_len)
52 {
53         int fd;
54         struct perf_event_attr attr;
55
56         get__perf_event_attr(&attr, wp_type, wp_addr, wp_len);
57         fd = sys_perf_event_open(&attr, 0, -1, -1,
58                                  perf_event_open_cloexec_flag());
59         if (fd < 0)
60                 pr_debug("failed opening event %x\n", attr.bp_type);
61
62         return fd;
63 }
64
65 static int wp_ro_test(void)
66 {
67         int fd;
68         unsigned long tmp, tmp1 = rand();
69
70         fd = __event(HW_BREAKPOINT_R, (void *)&data1, sizeof(data1));
71         if (fd < 0)
72                 return -1;
73
74         tmp = data1;
75         WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1);
76
77         data1 = tmp1 + tmp;
78         WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1);
79
80         close(fd);
81         return 0;
82 }
83
84 static int wp_wo_test(void)
85 {
86         int fd;
87         unsigned long tmp, tmp1 = rand();
88
89         fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1));
90         if (fd < 0)
91                 return -1;
92
93         tmp = data1;
94         WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 0);
95
96         data1 = tmp1 + tmp;
97         WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 1);
98
99         close(fd);
100         return 0;
101 }
102
103 static int wp_rw_test(void)
104 {
105         int fd;
106         unsigned long tmp, tmp1 = rand();
107
108         fd = __event(HW_BREAKPOINT_R | HW_BREAKPOINT_W, (void *)&data1,
109                      sizeof(data1));
110         if (fd < 0)
111                 return -1;
112
113         tmp = data1;
114         WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 1);
115
116         data1 = tmp1 + tmp;
117         WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 2);
118
119         close(fd);
120         return 0;
121 }
122
123 static int wp_modify_test(void)
124 {
125         int fd, ret;
126         unsigned long tmp = rand();
127         struct perf_event_attr new_attr;
128
129         fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1));
130         if (fd < 0)
131                 return -1;
132
133         data1 = tmp;
134         WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1);
135
136         /* Modify watchpoint with disabled = 1 */
137         get__perf_event_attr(&new_attr, HW_BREAKPOINT_W, (void *)&data2[0],
138                              sizeof(u8) * 2);
139         new_attr.disabled = 1;
140         ret = ioctl(fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &new_attr);
141         if (ret < 0) {
142                 pr_debug("ioctl(PERF_EVENT_IOC_MODIFY_ATTRIBUTES) failed\n");
143                 close(fd);
144                 return ret;
145         }
146
147         data2[1] = tmp; /* Not Counted */
148         WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1);
149
150         /* Enable the event */
151         ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
152         if (ret < 0) {
153                 pr_debug("Failed to enable event\n");
154                 close(fd);
155                 return ret;
156         }
157
158         data2[1] = tmp; /* Counted */
159         WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2);
160
161         data2[2] = tmp; /* Not Counted */
162         WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2);
163
164         close(fd);
165         return 0;
166 }
167
168 static bool wp_ro_supported(void)
169 {
170 #if defined (__x86_64__) || defined (__i386__)
171         return false;
172 #else
173         return true;
174 #endif
175 }
176
177 static const char *wp_ro_skip_msg(void)
178 {
179 #if defined (__x86_64__) || defined (__i386__)
180         return "missing hardware support";
181 #else
182         return NULL;
183 #endif
184 }
185
186 static struct {
187         const char *desc;
188         int (*target_func)(void);
189         bool (*is_supported)(void);
190         const char *(*skip_msg)(void);
191 } wp_testcase_table[] = {
192         {
193                 .desc = "Read Only Watchpoint",
194                 .target_func = &wp_ro_test,
195                 .is_supported = &wp_ro_supported,
196                 .skip_msg = &wp_ro_skip_msg,
197         },
198         {
199                 .desc = "Write Only Watchpoint",
200                 .target_func = &wp_wo_test,
201         },
202         {
203                 .desc = "Read / Write Watchpoint",
204                 .target_func = &wp_rw_test,
205         },
206         {
207                 .desc = "Modify Watchpoint",
208                 .target_func = &wp_modify_test,
209         },
210 };
211
212 int test__wp_subtest_get_nr(void)
213 {
214         return (int)ARRAY_SIZE(wp_testcase_table);
215 }
216
217 const char *test__wp_subtest_get_desc(int i)
218 {
219         if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table))
220                 return NULL;
221         return wp_testcase_table[i].desc;
222 }
223
224 const char *test__wp_subtest_skip_reason(int i)
225 {
226         if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table))
227                 return NULL;
228         if (!wp_testcase_table[i].skip_msg)
229                 return NULL;
230         return wp_testcase_table[i].skip_msg();
231 }
232
233 int test__wp(struct test *test __maybe_unused, int i)
234 {
235         if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table))
236                 return TEST_FAIL;
237
238         if (wp_testcase_table[i].is_supported &&
239             !wp_testcase_table[i].is_supported())
240                 return TEST_SKIP;
241
242         return !wp_testcase_table[i].target_func() ? TEST_OK : TEST_FAIL;
243 }
244
245 /* The s390 so far does not have support for
246  * instruction breakpoint using the perf_event_open() system call.
247  */
248 bool test__wp_is_supported(void)
249 {
250 #if defined(__s390x__)
251         return false;
252 #else
253         return true;
254 #endif
255 }