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