18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <stdlib.h> 38c2ecf20Sopenharmony_ci#include <string.h> 48c2ecf20Sopenharmony_ci#include <unistd.h> 58c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 68c2ecf20Sopenharmony_ci#include <linux/hw_breakpoint.h> 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include "tests.h" 98c2ecf20Sopenharmony_ci#include "debug.h" 108c2ecf20Sopenharmony_ci#include "event.h" 118c2ecf20Sopenharmony_ci#include "cloexec.h" 128c2ecf20Sopenharmony_ci#include "../perf-sys.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define WP_TEST_ASSERT_VAL(fd, text, val) \ 158c2ecf20Sopenharmony_cido { \ 168c2ecf20Sopenharmony_ci long long count; \ 178c2ecf20Sopenharmony_ci wp_read(fd, &count, sizeof(long long)); \ 188c2ecf20Sopenharmony_ci TEST_ASSERT_VAL(text, count == val); \ 198c2ecf20Sopenharmony_ci} while (0) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_civolatile u64 data1; 228c2ecf20Sopenharmony_civolatile u8 data2[3]; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int wp_read(int fd, long long *count, int size) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci int ret = read(fd, count, size); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (ret != size) { 298c2ecf20Sopenharmony_ci pr_debug("failed to read: %d\n", ret); 308c2ecf20Sopenharmony_ci return -1; 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci return 0; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void get__perf_event_attr(struct perf_event_attr *attr, int wp_type, 368c2ecf20Sopenharmony_ci void *wp_addr, unsigned long wp_len) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci memset(attr, 0, sizeof(struct perf_event_attr)); 398c2ecf20Sopenharmony_ci attr->type = PERF_TYPE_BREAKPOINT; 408c2ecf20Sopenharmony_ci attr->size = sizeof(struct perf_event_attr); 418c2ecf20Sopenharmony_ci attr->config = 0; 428c2ecf20Sopenharmony_ci attr->bp_type = wp_type; 438c2ecf20Sopenharmony_ci attr->bp_addr = (unsigned long)wp_addr; 448c2ecf20Sopenharmony_ci attr->bp_len = wp_len; 458c2ecf20Sopenharmony_ci attr->sample_period = 1; 468c2ecf20Sopenharmony_ci attr->sample_type = PERF_SAMPLE_IP; 478c2ecf20Sopenharmony_ci attr->exclude_kernel = 1; 488c2ecf20Sopenharmony_ci attr->exclude_hv = 1; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int __event(int wp_type, void *wp_addr, unsigned long wp_len) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci int fd; 548c2ecf20Sopenharmony_ci struct perf_event_attr attr; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci get__perf_event_attr(&attr, wp_type, wp_addr, wp_len); 578c2ecf20Sopenharmony_ci fd = sys_perf_event_open(&attr, 0, -1, -1, 588c2ecf20Sopenharmony_ci perf_event_open_cloexec_flag()); 598c2ecf20Sopenharmony_ci if (fd < 0) 608c2ecf20Sopenharmony_ci pr_debug("failed opening event %x\n", attr.bp_type); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return fd; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int wp_ro_test(void) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int fd; 688c2ecf20Sopenharmony_ci unsigned long tmp, tmp1 = rand(); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci fd = __event(HW_BREAKPOINT_R, (void *)&data1, sizeof(data1)); 718c2ecf20Sopenharmony_ci if (fd < 0) 728c2ecf20Sopenharmony_ci return -1; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci tmp = data1; 758c2ecf20Sopenharmony_ci WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci data1 = tmp1 + tmp; 788c2ecf20Sopenharmony_ci WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci close(fd); 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int wp_wo_test(void) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int fd; 878c2ecf20Sopenharmony_ci unsigned long tmp, tmp1 = rand(); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1)); 908c2ecf20Sopenharmony_ci if (fd < 0) 918c2ecf20Sopenharmony_ci return -1; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci tmp = data1; 948c2ecf20Sopenharmony_ci WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 0); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci data1 = tmp1 + tmp; 978c2ecf20Sopenharmony_ci WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 1); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci close(fd); 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int wp_rw_test(void) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int fd; 1068c2ecf20Sopenharmony_ci unsigned long tmp, tmp1 = rand(); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci fd = __event(HW_BREAKPOINT_R | HW_BREAKPOINT_W, (void *)&data1, 1098c2ecf20Sopenharmony_ci sizeof(data1)); 1108c2ecf20Sopenharmony_ci if (fd < 0) 1118c2ecf20Sopenharmony_ci return -1; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci tmp = data1; 1148c2ecf20Sopenharmony_ci WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 1); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci data1 = tmp1 + tmp; 1178c2ecf20Sopenharmony_ci WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 2); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci close(fd); 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int wp_modify_test(void) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci int fd, ret; 1268c2ecf20Sopenharmony_ci unsigned long tmp = rand(); 1278c2ecf20Sopenharmony_ci struct perf_event_attr new_attr; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1)); 1308c2ecf20Sopenharmony_ci if (fd < 0) 1318c2ecf20Sopenharmony_ci return -1; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci data1 = tmp; 1348c2ecf20Sopenharmony_ci WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Modify watchpoint with disabled = 1 */ 1378c2ecf20Sopenharmony_ci get__perf_event_attr(&new_attr, HW_BREAKPOINT_W, (void *)&data2[0], 1388c2ecf20Sopenharmony_ci sizeof(u8) * 2); 1398c2ecf20Sopenharmony_ci new_attr.disabled = 1; 1408c2ecf20Sopenharmony_ci ret = ioctl(fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &new_attr); 1418c2ecf20Sopenharmony_ci if (ret < 0) { 1428c2ecf20Sopenharmony_ci pr_debug("ioctl(PERF_EVENT_IOC_MODIFY_ATTRIBUTES) failed\n"); 1438c2ecf20Sopenharmony_ci close(fd); 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci data2[1] = tmp; /* Not Counted */ 1488c2ecf20Sopenharmony_ci WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Enable the event */ 1518c2ecf20Sopenharmony_ci ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); 1528c2ecf20Sopenharmony_ci if (ret < 0) { 1538c2ecf20Sopenharmony_ci pr_debug("Failed to enable event\n"); 1548c2ecf20Sopenharmony_ci close(fd); 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci data2[1] = tmp; /* Counted */ 1598c2ecf20Sopenharmony_ci WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci data2[2] = tmp; /* Not Counted */ 1628c2ecf20Sopenharmony_ci WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci close(fd); 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic bool wp_ro_supported(void) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci#if defined (__x86_64__) || defined (__i386__) 1718c2ecf20Sopenharmony_ci return false; 1728c2ecf20Sopenharmony_ci#else 1738c2ecf20Sopenharmony_ci return true; 1748c2ecf20Sopenharmony_ci#endif 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void wp_ro_skip_msg(void) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci#if defined (__x86_64__) || defined (__i386__) 1808c2ecf20Sopenharmony_ci pr_debug("Hardware does not support read only watchpoints.\n"); 1818c2ecf20Sopenharmony_ci#endif 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct { 1858c2ecf20Sopenharmony_ci const char *desc; 1868c2ecf20Sopenharmony_ci int (*target_func)(void); 1878c2ecf20Sopenharmony_ci bool (*is_supported)(void); 1888c2ecf20Sopenharmony_ci void (*skip_msg)(void); 1898c2ecf20Sopenharmony_ci} wp_testcase_table[] = { 1908c2ecf20Sopenharmony_ci { 1918c2ecf20Sopenharmony_ci .desc = "Read Only Watchpoint", 1928c2ecf20Sopenharmony_ci .target_func = &wp_ro_test, 1938c2ecf20Sopenharmony_ci .is_supported = &wp_ro_supported, 1948c2ecf20Sopenharmony_ci .skip_msg = &wp_ro_skip_msg, 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci { 1978c2ecf20Sopenharmony_ci .desc = "Write Only Watchpoint", 1988c2ecf20Sopenharmony_ci .target_func = &wp_wo_test, 1998c2ecf20Sopenharmony_ci }, 2008c2ecf20Sopenharmony_ci { 2018c2ecf20Sopenharmony_ci .desc = "Read / Write Watchpoint", 2028c2ecf20Sopenharmony_ci .target_func = &wp_rw_test, 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci { 2058c2ecf20Sopenharmony_ci .desc = "Modify Watchpoint", 2068c2ecf20Sopenharmony_ci .target_func = &wp_modify_test, 2078c2ecf20Sopenharmony_ci }, 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ciint test__wp_subtest_get_nr(void) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci return (int)ARRAY_SIZE(wp_testcase_table); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ciconst char *test__wp_subtest_get_desc(int i) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table)) 2188c2ecf20Sopenharmony_ci return NULL; 2198c2ecf20Sopenharmony_ci return wp_testcase_table[i].desc; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ciint test__wp(struct test *test __maybe_unused, int i) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table)) 2258c2ecf20Sopenharmony_ci return TEST_FAIL; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (wp_testcase_table[i].is_supported && 2288c2ecf20Sopenharmony_ci !wp_testcase_table[i].is_supported()) { 2298c2ecf20Sopenharmony_ci wp_testcase_table[i].skip_msg(); 2308c2ecf20Sopenharmony_ci return TEST_SKIP; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return !wp_testcase_table[i].target_func() ? TEST_OK : TEST_FAIL; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* The s390 so far does not have support for 2378c2ecf20Sopenharmony_ci * instruction breakpoint using the perf_event_open() system call. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_cibool test__wp_is_supported(void) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci#if defined(__s390x__) 2428c2ecf20Sopenharmony_ci return false; 2438c2ecf20Sopenharmony_ci#else 2448c2ecf20Sopenharmony_ci return true; 2458c2ecf20Sopenharmony_ci#endif 2468c2ecf20Sopenharmony_ci} 247