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