Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | // SPDX-License-Identifier: GPL-2.0 /* * Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu. */ #define __SANE_USERSPACE_TYPES__ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include <fcntl.h> #include <linux/hw_breakpoint.h> #include "tests.h" #include "debug.h" #include "event.h" #include "../perf-sys.h" #include "cloexec.h" volatile long the_var; static noinline int test_function(void) { return 0; } static int __event(bool is_x, void *addr, struct perf_event_attr *attr) { int fd; memset(attr, 0, sizeof(struct perf_event_attr)); attr->type = PERF_TYPE_BREAKPOINT; attr->size = sizeof(struct perf_event_attr); attr->config = 0; attr->bp_type = is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W; attr->bp_addr = (unsigned long) addr; attr->bp_len = sizeof(long); attr->sample_period = 1; attr->sample_type = PERF_SAMPLE_IP; attr->exclude_kernel = 1; attr->exclude_hv = 1; fd = sys_perf_event_open(attr, -1, 0, -1, perf_event_open_cloexec_flag()); if (fd < 0) { pr_debug("failed opening event %llx\n", attr->config); return TEST_FAIL; } return fd; } static int wp_event(void *addr, struct perf_event_attr *attr) { return __event(false, addr, attr); } static int bp_event(void *addr, struct perf_event_attr *attr) { return __event(true, addr, attr); } static int bp_accounting(int wp_cnt, int share) { struct perf_event_attr attr, attr_mod, attr_new; int i, fd[wp_cnt], fd_wp, ret; for (i = 0; i < wp_cnt; i++) { fd[i] = wp_event((void *)&the_var, &attr); TEST_ASSERT_VAL("failed to create wp\n", fd[i] != -1); pr_debug("wp %d created\n", i); } attr_mod = attr; attr_mod.bp_type = HW_BREAKPOINT_X; attr_mod.bp_addr = (unsigned long) test_function; ret = ioctl(fd[0], PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr_mod); TEST_ASSERT_VAL("failed to modify wp\n", ret == 0); pr_debug("wp 0 modified to bp\n"); if (!share) { fd_wp = wp_event((void *)&the_var, &attr_new); TEST_ASSERT_VAL("failed to create max wp\n", fd_wp != -1); pr_debug("wp max created\n"); } for (i = 0; i < wp_cnt; i++) close(fd[i]); return 0; } static int detect_cnt(bool is_x) { struct perf_event_attr attr; void *addr = is_x ? (void *)test_function : (void *)&the_var; int fd[100], cnt = 0, i; while (1) { if (cnt == 100) { pr_debug("way too many debug registers, fix the test\n"); return 0; } fd[cnt] = __event(is_x, addr, &attr); if (fd[cnt] < 0) break; cnt++; } for (i = 0; i < cnt; i++) close(fd[i]); return cnt; } static int detect_ioctl(void) { struct perf_event_attr attr; int fd, ret = 1; fd = wp_event((void *) &the_var, &attr); if (fd > 0) { ret = ioctl(fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr); close(fd); } return ret ? 0 : 1; } static int detect_share(int wp_cnt, int bp_cnt) { struct perf_event_attr attr; int i, fd[wp_cnt + bp_cnt], ret; for (i = 0; i < wp_cnt; i++) { fd[i] = wp_event((void *)&the_var, &attr); TEST_ASSERT_VAL("failed to create wp\n", fd[i] != -1); } for (; i < (bp_cnt + wp_cnt); i++) { fd[i] = bp_event((void *)test_function, &attr); if (fd[i] == -1) break; } ret = i != (bp_cnt + wp_cnt); while (i--) close(fd[i]); return ret; } /* * This test does following: * - detects the number of watch/break-points, * skip test if any is missing * - detects PERF_EVENT_IOC_MODIFY_ATTRIBUTES ioctl, * skip test if it's missing * - detects if watchpoints and breakpoints share * same slots * - create all possible watchpoints on cpu 0 * - change one of it to breakpoint * - in case wp and bp do not share slots, * we create another watchpoint to ensure * the slot accounting is correct */ int test__bp_accounting(struct test *test __maybe_unused, int subtest __maybe_unused) { int has_ioctl = detect_ioctl(); int wp_cnt = detect_cnt(false); int bp_cnt = detect_cnt(true); int share = detect_share(wp_cnt, bp_cnt); pr_debug("watchpoints count %d, breakpoints count %d, has_ioctl %d, share %d\n", wp_cnt, bp_cnt, has_ioctl, share); if (!wp_cnt || !bp_cnt || !has_ioctl) return TEST_SKIP; return bp_accounting(wp_cnt, share); } bool test__bp_account_is_supported(void) { /* * PowerPC and S390 do not support creation of instruction * breakpoints using the perf_event interface. * * Just disable the test for these architectures until these * issues are resolved. */ #if defined(__powerpc__) || defined(__s390x__) return false; #else return true; #endif } |