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 #ifndef __s390x__ 25 static 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 36 static 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 52 static 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 67 static 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 91 static 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 115 static 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 140 static 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 190 static 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 198 struct test_suite suite__wp = { 199 .desc = "Watchpoint", 200 .test_cases = wp_tests, 201 }; 202