at v6.1-rc4 207 lines 4.8 kB view raw
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) \ 17do { \ 18 long long count; \ 19 wp_read(fd, &count, sizeof(long long)); \ 20 TEST_ASSERT_VAL(text, count == val); \ 21} while (0) 22 23volatile u64 data1; 24volatile u8 data2[3]; 25 26#ifndef __s390x__ 27static 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 38static 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 54static 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 69static 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 93static 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 117static 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 142static 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 196static 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 204struct test_suite suite__wp = { 205 .desc = "Watchpoint", 206 .test_cases = wp_tests, 207};