18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <errno.h> 38c2ecf20Sopenharmony_ci#include <unistd.h> 48c2ecf20Sopenharmony_ci#include <stdlib.h> 58c2ecf20Sopenharmony_ci#include <signal.h> 68c2ecf20Sopenharmony_ci#include <sys/mman.h> 78c2ecf20Sopenharmony_ci#include <sys/types.h> 88c2ecf20Sopenharmony_ci#include <sys/wait.h> 98c2ecf20Sopenharmony_ci#include <linux/string.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include "perf-sys.h" 128c2ecf20Sopenharmony_ci#include "debug.h" 138c2ecf20Sopenharmony_ci#include "tests/tests.h" 148c2ecf20Sopenharmony_ci#include "cloexec.h" 158c2ecf20Sopenharmony_ci#include "event.h" 168c2ecf20Sopenharmony_ci#include <internal/lib.h> // page_size 178c2ecf20Sopenharmony_ci#include "arch-tests.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic u64 rdpmc(unsigned int counter) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci unsigned int low, high; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci return low | ((u64)high) << 32; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic u64 rdtsc(void) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci unsigned int low, high; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci asm volatile("rdtsc" : "=a" (low), "=d" (high)); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci return low | ((u64)high) << 32; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic u64 mmap_read_self(void *addr) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct perf_event_mmap_page *pc = addr; 408c2ecf20Sopenharmony_ci u32 seq, idx, time_mult = 0, time_shift = 0; 418c2ecf20Sopenharmony_ci u64 count, cyc = 0, time_offset = 0, enabled, running, delta; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci do { 448c2ecf20Sopenharmony_ci seq = pc->lock; 458c2ecf20Sopenharmony_ci barrier(); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci enabled = pc->time_enabled; 488c2ecf20Sopenharmony_ci running = pc->time_running; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (enabled != running) { 518c2ecf20Sopenharmony_ci cyc = rdtsc(); 528c2ecf20Sopenharmony_ci time_mult = pc->time_mult; 538c2ecf20Sopenharmony_ci time_shift = pc->time_shift; 548c2ecf20Sopenharmony_ci time_offset = pc->time_offset; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci idx = pc->index; 588c2ecf20Sopenharmony_ci count = pc->offset; 598c2ecf20Sopenharmony_ci if (idx) 608c2ecf20Sopenharmony_ci count += rdpmc(idx - 1); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci barrier(); 638c2ecf20Sopenharmony_ci } while (pc->lock != seq); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (enabled != running) { 668c2ecf20Sopenharmony_ci u64 quot, rem; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci quot = (cyc >> time_shift); 698c2ecf20Sopenharmony_ci rem = cyc & (((u64)1 << time_shift) - 1); 708c2ecf20Sopenharmony_ci delta = time_offset + quot * time_mult + 718c2ecf20Sopenharmony_ci ((rem * time_mult) >> time_shift); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci enabled += delta; 748c2ecf20Sopenharmony_ci if (idx) 758c2ecf20Sopenharmony_ci running += delta; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci quot = count / running; 788c2ecf20Sopenharmony_ci rem = count % running; 798c2ecf20Sopenharmony_ci count = quot * enabled + (rem * enabled) / running; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return count; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * If the RDPMC instruction faults then signal this back to the test parent task: 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_cistatic void segfault_handler(int sig __maybe_unused, 898c2ecf20Sopenharmony_ci siginfo_t *info __maybe_unused, 908c2ecf20Sopenharmony_ci void *uc __maybe_unused) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci exit(-1); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int __test__rdpmc(void) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci volatile int tmp = 0; 988c2ecf20Sopenharmony_ci u64 i, loops = 1000; 998c2ecf20Sopenharmony_ci int n; 1008c2ecf20Sopenharmony_ci int fd; 1018c2ecf20Sopenharmony_ci void *addr; 1028c2ecf20Sopenharmony_ci struct perf_event_attr attr = { 1038c2ecf20Sopenharmony_ci .type = PERF_TYPE_HARDWARE, 1048c2ecf20Sopenharmony_ci .config = PERF_COUNT_HW_INSTRUCTIONS, 1058c2ecf20Sopenharmony_ci .exclude_kernel = 1, 1068c2ecf20Sopenharmony_ci }; 1078c2ecf20Sopenharmony_ci u64 delta_sum = 0; 1088c2ecf20Sopenharmony_ci struct sigaction sa; 1098c2ecf20Sopenharmony_ci char sbuf[STRERR_BUFSIZE]; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci sigfillset(&sa.sa_mask); 1128c2ecf20Sopenharmony_ci sa.sa_sigaction = segfault_handler; 1138c2ecf20Sopenharmony_ci sa.sa_flags = 0; 1148c2ecf20Sopenharmony_ci sigaction(SIGSEGV, &sa, NULL); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci fd = sys_perf_event_open(&attr, 0, -1, -1, 1178c2ecf20Sopenharmony_ci perf_event_open_cloexec_flag()); 1188c2ecf20Sopenharmony_ci if (fd < 0) { 1198c2ecf20Sopenharmony_ci pr_err("Error: sys_perf_event_open() syscall returned " 1208c2ecf20Sopenharmony_ci "with %d (%s)\n", fd, 1218c2ecf20Sopenharmony_ci str_error_r(errno, sbuf, sizeof(sbuf))); 1228c2ecf20Sopenharmony_ci return -1; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); 1268c2ecf20Sopenharmony_ci if (addr == (void *)(-1)) { 1278c2ecf20Sopenharmony_ci pr_err("Error: mmap() syscall returned with (%s)\n", 1288c2ecf20Sopenharmony_ci str_error_r(errno, sbuf, sizeof(sbuf))); 1298c2ecf20Sopenharmony_ci goto out_close; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci for (n = 0; n < 6; n++) { 1338c2ecf20Sopenharmony_ci u64 stamp, now, delta; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci stamp = mmap_read_self(addr); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci for (i = 0; i < loops; i++) 1388c2ecf20Sopenharmony_ci tmp++; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci now = mmap_read_self(addr); 1418c2ecf20Sopenharmony_ci loops *= 10; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci delta = now - stamp; 1448c2ecf20Sopenharmony_ci pr_debug("%14d: %14Lu\n", n, (long long)delta); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci delta_sum += delta; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci munmap(addr, page_size); 1508c2ecf20Sopenharmony_ci pr_debug(" "); 1518c2ecf20Sopenharmony_ciout_close: 1528c2ecf20Sopenharmony_ci close(fd); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (!delta_sum) 1558c2ecf20Sopenharmony_ci return -1; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciint test__rdpmc(struct test *test __maybe_unused, int subtest __maybe_unused) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int status = 0; 1638c2ecf20Sopenharmony_ci int wret = 0; 1648c2ecf20Sopenharmony_ci int ret; 1658c2ecf20Sopenharmony_ci int pid; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci pid = fork(); 1688c2ecf20Sopenharmony_ci if (pid < 0) 1698c2ecf20Sopenharmony_ci return -1; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (!pid) { 1728c2ecf20Sopenharmony_ci ret = __test__rdpmc(); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci exit(ret); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci wret = waitpid(pid, &status, 0); 1788c2ecf20Sopenharmony_ci if (wret < 0 || status) 1798c2ecf20Sopenharmony_ci return -1; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 183