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