18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2013-2015, Michael Ellerman, IBM Corp. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define _GNU_SOURCE /* For CPU_ZERO etc. */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <elf.h> 98c2ecf20Sopenharmony_ci#include <errno.h> 108c2ecf20Sopenharmony_ci#include <fcntl.h> 118c2ecf20Sopenharmony_ci#include <link.h> 128c2ecf20Sopenharmony_ci#include <sched.h> 138c2ecf20Sopenharmony_ci#include <stdio.h> 148c2ecf20Sopenharmony_ci#include <stdlib.h> 158c2ecf20Sopenharmony_ci#include <string.h> 168c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 178c2ecf20Sopenharmony_ci#include <sys/stat.h> 188c2ecf20Sopenharmony_ci#include <sys/sysinfo.h> 198c2ecf20Sopenharmony_ci#include <sys/types.h> 208c2ecf20Sopenharmony_ci#include <sys/utsname.h> 218c2ecf20Sopenharmony_ci#include <unistd.h> 228c2ecf20Sopenharmony_ci#include <asm/unistd.h> 238c2ecf20Sopenharmony_ci#include <linux/limits.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "utils.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic char auxv[4096]; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciint read_auxv(char *buf, ssize_t buf_size) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci ssize_t num; 328c2ecf20Sopenharmony_ci int rc, fd; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci fd = open("/proc/self/auxv", O_RDONLY); 358c2ecf20Sopenharmony_ci if (fd == -1) { 368c2ecf20Sopenharmony_ci perror("open"); 378c2ecf20Sopenharmony_ci return -errno; 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci num = read(fd, buf, buf_size); 418c2ecf20Sopenharmony_ci if (num < 0) { 428c2ecf20Sopenharmony_ci perror("read"); 438c2ecf20Sopenharmony_ci rc = -EIO; 448c2ecf20Sopenharmony_ci goto out; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (num > buf_size) { 488c2ecf20Sopenharmony_ci printf("overflowed auxv buffer\n"); 498c2ecf20Sopenharmony_ci rc = -EOVERFLOW; 508c2ecf20Sopenharmony_ci goto out; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci rc = 0; 548c2ecf20Sopenharmony_ciout: 558c2ecf20Sopenharmony_ci close(fd); 568c2ecf20Sopenharmony_ci return rc; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_civoid *find_auxv_entry(int type, char *auxv) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci ElfW(auxv_t) *p; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci p = (ElfW(auxv_t) *)auxv; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci while (p->a_type != AT_NULL) { 668c2ecf20Sopenharmony_ci if (p->a_type == type) 678c2ecf20Sopenharmony_ci return p; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci p++; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return NULL; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_civoid *get_auxv_entry(int type) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci ElfW(auxv_t) *p; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (read_auxv(auxv, sizeof(auxv))) 808c2ecf20Sopenharmony_ci return NULL; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci p = find_auxv_entry(type, auxv); 838c2ecf20Sopenharmony_ci if (p) 848c2ecf20Sopenharmony_ci return (void *)p->a_un.a_val; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return NULL; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ciint pick_online_cpu(void) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int ncpus, cpu = -1; 928c2ecf20Sopenharmony_ci cpu_set_t *mask; 938c2ecf20Sopenharmony_ci size_t size; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci ncpus = get_nprocs_conf(); 968c2ecf20Sopenharmony_ci size = CPU_ALLOC_SIZE(ncpus); 978c2ecf20Sopenharmony_ci mask = CPU_ALLOC(ncpus); 988c2ecf20Sopenharmony_ci if (!mask) { 998c2ecf20Sopenharmony_ci perror("malloc"); 1008c2ecf20Sopenharmony_ci return -1; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci CPU_ZERO_S(size, mask); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (sched_getaffinity(0, size, mask)) { 1068c2ecf20Sopenharmony_ci perror("sched_getaffinity"); 1078c2ecf20Sopenharmony_ci goto done; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* We prefer a primary thread, but skip 0 */ 1118c2ecf20Sopenharmony_ci for (cpu = 8; cpu < ncpus; cpu += 8) 1128c2ecf20Sopenharmony_ci if (CPU_ISSET_S(cpu, size, mask)) 1138c2ecf20Sopenharmony_ci goto done; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Search for anything, but in reverse */ 1168c2ecf20Sopenharmony_ci for (cpu = ncpus - 1; cpu >= 0; cpu--) 1178c2ecf20Sopenharmony_ci if (CPU_ISSET_S(cpu, size, mask)) 1188c2ecf20Sopenharmony_ci goto done; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci printf("No cpus in affinity mask?!\n"); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cidone: 1238c2ecf20Sopenharmony_ci CPU_FREE(mask); 1248c2ecf20Sopenharmony_ci return cpu; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cibool is_ppc64le(void) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct utsname uts; 1308c2ecf20Sopenharmony_ci int rc; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci errno = 0; 1338c2ecf20Sopenharmony_ci rc = uname(&uts); 1348c2ecf20Sopenharmony_ci if (rc) { 1358c2ecf20Sopenharmony_ci perror("uname"); 1368c2ecf20Sopenharmony_ci return false; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return strcmp(uts.machine, "ppc64le") == 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciint read_sysfs_file(char *fpath, char *result, size_t result_size) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci char path[PATH_MAX] = "/sys/"; 1458c2ecf20Sopenharmony_ci int rc = -1, fd; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci strncat(path, fpath, PATH_MAX - strlen(path) - 1); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if ((fd = open(path, O_RDONLY)) < 0) 1508c2ecf20Sopenharmony_ci return rc; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci rc = read(fd, result, result_size); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci close(fd); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (rc < 0) 1578c2ecf20Sopenharmony_ci return rc; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ciint read_debugfs_file(char *debugfs_file, int *result) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int rc = -1, fd; 1658c2ecf20Sopenharmony_ci char path[PATH_MAX]; 1668c2ecf20Sopenharmony_ci char value[16]; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci strcpy(path, "/sys/kernel/debug/"); 1698c2ecf20Sopenharmony_ci strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if ((fd = open(path, O_RDONLY)) < 0) 1728c2ecf20Sopenharmony_ci return rc; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if ((rc = read(fd, value, sizeof(value))) < 0) 1758c2ecf20Sopenharmony_ci return rc; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci value[15] = 0; 1788c2ecf20Sopenharmony_ci *result = atoi(value); 1798c2ecf20Sopenharmony_ci close(fd); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ciint write_debugfs_file(char *debugfs_file, int result) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci int rc = -1, fd; 1878c2ecf20Sopenharmony_ci char path[PATH_MAX]; 1888c2ecf20Sopenharmony_ci char value[16]; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci strcpy(path, "/sys/kernel/debug/"); 1918c2ecf20Sopenharmony_ci strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if ((fd = open(path, O_WRONLY)) < 0) 1948c2ecf20Sopenharmony_ci return rc; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci snprintf(value, 16, "%d", result); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if ((rc = write(fd, value, strlen(value))) < 0) 1998c2ecf20Sopenharmony_ci return rc; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci close(fd); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, 2078c2ecf20Sopenharmony_ci int cpu, int group_fd, unsigned long flags) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci return syscall(__NR_perf_event_open, hw_event, pid, cpu, 2108c2ecf20Sopenharmony_ci group_fd, flags); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void perf_event_attr_init(struct perf_event_attr *event_attr, 2148c2ecf20Sopenharmony_ci unsigned int type, 2158c2ecf20Sopenharmony_ci unsigned long config) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci memset(event_attr, 0, sizeof(*event_attr)); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci event_attr->type = type; 2208c2ecf20Sopenharmony_ci event_attr->size = sizeof(struct perf_event_attr); 2218c2ecf20Sopenharmony_ci event_attr->config = config; 2228c2ecf20Sopenharmony_ci event_attr->read_format = PERF_FORMAT_GROUP; 2238c2ecf20Sopenharmony_ci event_attr->disabled = 1; 2248c2ecf20Sopenharmony_ci event_attr->exclude_kernel = 1; 2258c2ecf20Sopenharmony_ci event_attr->exclude_hv = 1; 2268c2ecf20Sopenharmony_ci event_attr->exclude_guest = 1; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ciint perf_event_open_counter(unsigned int type, 2308c2ecf20Sopenharmony_ci unsigned long config, int group_fd) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci int fd; 2338c2ecf20Sopenharmony_ci struct perf_event_attr event_attr; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci perf_event_attr_init(&event_attr, type, config); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci fd = perf_event_open(&event_attr, 0, -1, group_fd, 0); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (fd < 0) 2408c2ecf20Sopenharmony_ci perror("perf_event_open() failed"); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return fd; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciint perf_event_enable(int fd) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) { 2488c2ecf20Sopenharmony_ci perror("error while enabling perf events"); 2498c2ecf20Sopenharmony_ci return -1; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ciint perf_event_disable(int fd) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { 2588c2ecf20Sopenharmony_ci perror("error disabling perf events"); 2598c2ecf20Sopenharmony_ci return -1; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ciint perf_event_reset(int fd) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) { 2688c2ecf20Sopenharmony_ci perror("error resetting perf events"); 2698c2ecf20Sopenharmony_ci return -1; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ciint using_hash_mmu(bool *using_hash) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci char line[128]; 2788c2ecf20Sopenharmony_ci FILE *f; 2798c2ecf20Sopenharmony_ci int rc; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci f = fopen("/proc/cpuinfo", "r"); 2828c2ecf20Sopenharmony_ci FAIL_IF(!f); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci rc = 0; 2858c2ecf20Sopenharmony_ci while (fgets(line, sizeof(line), f) != NULL) { 2868c2ecf20Sopenharmony_ci if (!strcmp(line, "MMU : Hash\n") || 2878c2ecf20Sopenharmony_ci !strcmp(line, "platform : Cell\n") || 2888c2ecf20Sopenharmony_ci !strcmp(line, "platform : PowerMac\n")) { 2898c2ecf20Sopenharmony_ci *using_hash = true; 2908c2ecf20Sopenharmony_ci goto out; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (strcmp(line, "MMU : Radix\n") == 0) { 2948c2ecf20Sopenharmony_ci *using_hash = false; 2958c2ecf20Sopenharmony_ci goto out; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci rc = -1; 3008c2ecf20Sopenharmony_ciout: 3018c2ecf20Sopenharmony_ci fclose(f); 3028c2ecf20Sopenharmony_ci return rc; 3038c2ecf20Sopenharmony_ci} 304