Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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) \
15do { \
16 long long count; \
17 wp_read(fd, &count, sizeof(long long)); \
18 TEST_ASSERT_VAL(text, count == val); \
19} while (0)
20
21volatile u64 data1;
22volatile u8 data2[3];
23
24#ifndef __s390x__
25static int wp_read(int fd, long long *count, int size)
26{
27 int ret = read(fd, count, size);
28
29 if (ret != size) {
30 pr_debug("failed to read: %d\n", ret);
31 return -1;
32 }
33 return 0;
34}
35
36static void get__perf_event_attr(struct perf_event_attr *attr, int wp_type,
37 void *wp_addr, unsigned long wp_len)
38{
39 memset(attr, 0, sizeof(struct perf_event_attr));
40 attr->type = PERF_TYPE_BREAKPOINT;
41 attr->size = sizeof(struct perf_event_attr);
42 attr->config = 0;
43 attr->bp_type = wp_type;
44 attr->bp_addr = (unsigned long)wp_addr;
45 attr->bp_len = wp_len;
46 attr->sample_period = 1;
47 attr->sample_type = PERF_SAMPLE_IP;
48 attr->exclude_kernel = 1;
49 attr->exclude_hv = 1;
50}
51
52static int __event(int wp_type, void *wp_addr, unsigned long wp_len)
53{
54 int fd;
55 struct perf_event_attr attr;
56
57 get__perf_event_attr(&attr, wp_type, wp_addr, wp_len);
58 fd = sys_perf_event_open(&attr, 0, -1, -1,
59 perf_event_open_cloexec_flag());
60 if (fd < 0)
61 pr_debug("failed opening event %x\n", attr.bp_type);
62
63 return fd;
64}
65#endif
66
67static int test__wp_ro(struct test_suite *test __maybe_unused,
68 int subtest __maybe_unused)
69{
70#if defined(__s390x__) || defined(__x86_64__) || defined(__i386__)
71 return TEST_SKIP;
72#else
73 int fd;
74 unsigned long tmp, tmp1 = rand();
75
76 fd = __event(HW_BREAKPOINT_R, (void *)&data1, sizeof(data1));
77 if (fd < 0)
78 return -1;
79
80 tmp = data1;
81 WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1);
82
83 data1 = tmp1 + tmp;
84 WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1);
85
86 close(fd);
87 return 0;
88#endif
89}
90
91static int test__wp_wo(struct test_suite *test __maybe_unused,
92 int subtest __maybe_unused)
93{
94#if defined(__s390x__)
95 return TEST_SKIP;
96#else
97 int fd;
98 unsigned long tmp, tmp1 = rand();
99
100 fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1));
101 if (fd < 0)
102 return -1;
103
104 tmp = data1;
105 WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 0);
106
107 data1 = tmp1 + tmp;
108 WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 1);
109
110 close(fd);
111 return 0;
112#endif
113}
114
115static int test__wp_rw(struct test_suite *test __maybe_unused,
116 int subtest __maybe_unused)
117{
118#if defined(__s390x__)
119 return TEST_SKIP;
120#else
121 int fd;
122 unsigned long tmp, tmp1 = rand();
123
124 fd = __event(HW_BREAKPOINT_R | HW_BREAKPOINT_W, (void *)&data1,
125 sizeof(data1));
126 if (fd < 0)
127 return -1;
128
129 tmp = data1;
130 WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 1);
131
132 data1 = tmp1 + tmp;
133 WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 2);
134
135 close(fd);
136 return 0;
137#endif
138}
139
140static int test__wp_modify(struct test_suite *test __maybe_unused,
141 int subtest __maybe_unused)
142{
143#if defined(__s390x__)
144 return TEST_SKIP;
145#else
146 int fd, ret;
147 unsigned long tmp = rand();
148 struct perf_event_attr new_attr;
149
150 fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1));
151 if (fd < 0)
152 return -1;
153
154 data1 = tmp;
155 WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1);
156
157 /* Modify watchpoint with disabled = 1 */
158 get__perf_event_attr(&new_attr, HW_BREAKPOINT_W, (void *)&data2[0],
159 sizeof(u8) * 2);
160 new_attr.disabled = 1;
161 ret = ioctl(fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &new_attr);
162 if (ret < 0) {
163 pr_debug("ioctl(PERF_EVENT_IOC_MODIFY_ATTRIBUTES) failed\n");
164 close(fd);
165 return ret;
166 }
167
168 data2[1] = tmp; /* Not Counted */
169 WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1);
170
171 /* Enable the event */
172 ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
173 if (ret < 0) {
174 pr_debug("Failed to enable event\n");
175 close(fd);
176 return ret;
177 }
178
179 data2[1] = tmp; /* Counted */
180 WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2);
181
182 data2[2] = tmp; /* Not Counted */
183 WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2);
184
185 close(fd);
186 return 0;
187#endif
188}
189
190static struct test_case wp_tests[] = {
191 TEST_CASE_REASON("Read Only Watchpoint", wp_ro, "missing hardware support"),
192 TEST_CASE_REASON("Write Only Watchpoint", wp_wo, "missing hardware support"),
193 TEST_CASE_REASON("Read / Write Watchpoint", wp_rw, "missing hardware support"),
194 TEST_CASE_REASON("Modify Watchpoint", wp_modify, "missing hardware support"),
195 { .name = NULL, }
196};
197
198struct test_suite suite__wp = {
199 .desc = "Watchpoint",
200 .test_cases = wp_tests,
201};