1f50a7f3dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2fcb45ec0SMichael Ellerman /* 3fcb45ec0SMichael Ellerman * Copyright 2013-2015, Michael Ellerman, IBM Corp. 4fcb45ec0SMichael Ellerman */ 5fcb45ec0SMichael Ellerman 6d1301afdSMichael Ellerman #define _GNU_SOURCE /* For CPU_ZERO etc. */ 7d1301afdSMichael Ellerman 8fcb45ec0SMichael Ellerman #include <elf.h> 9fcb45ec0SMichael Ellerman #include <errno.h> 10fcb45ec0SMichael Ellerman #include <fcntl.h> 11fcb45ec0SMichael Ellerman #include <link.h> 12d1301afdSMichael Ellerman #include <sched.h> 13fcb45ec0SMichael Ellerman #include <stdio.h> 14d2bf7932SNaveen N. Rao #include <stdlib.h> 1595f9b3afSMichael Ellerman #include <string.h> 16d2bf7932SNaveen N. Rao #include <sys/ioctl.h> 17fcb45ec0SMichael Ellerman #include <sys/stat.h> 18dfa03fffSSandipan Das #include <sys/sysinfo.h> 19fcb45ec0SMichael Ellerman #include <sys/types.h> 2095f9b3afSMichael Ellerman #include <sys/utsname.h> 21fcb45ec0SMichael Ellerman #include <unistd.h> 22d2bf7932SNaveen N. Rao #include <asm/unistd.h> 23d2bf7932SNaveen N. Rao #include <linux/limits.h> 24fcb45ec0SMichael Ellerman 25fcb45ec0SMichael Ellerman #include "utils.h" 26fcb45ec0SMichael Ellerman 27fcb45ec0SMichael Ellerman static char auxv[4096]; 28fcb45ec0SMichael Ellerman 29*a974f0c1SBenjamin Gray int read_file(const char *path, char *buf, size_t count, size_t *len) 30fcb45ec0SMichael Ellerman { 31*a974f0c1SBenjamin Gray ssize_t rc; 32*a974f0c1SBenjamin Gray int fd; 33*a974f0c1SBenjamin Gray int err; 34*a974f0c1SBenjamin Gray char eof; 35fcb45ec0SMichael Ellerman 36*a974f0c1SBenjamin Gray fd = open(path, O_RDONLY); 37*a974f0c1SBenjamin Gray if (fd < 0) 38e3028437SMichael Ellerman return -errno; 39fcb45ec0SMichael Ellerman 40*a974f0c1SBenjamin Gray rc = read(fd, buf, count); 41*a974f0c1SBenjamin Gray if (rc < 0) { 42*a974f0c1SBenjamin Gray err = -errno; 43fcb45ec0SMichael Ellerman goto out; 44fcb45ec0SMichael Ellerman } 45fcb45ec0SMichael Ellerman 46*a974f0c1SBenjamin Gray if (len) 47*a974f0c1SBenjamin Gray *len = rc; 48*a974f0c1SBenjamin Gray 49*a974f0c1SBenjamin Gray /* Overflow if there are still more bytes after filling the buffer */ 50*a974f0c1SBenjamin Gray if (rc == count) { 51*a974f0c1SBenjamin Gray rc = read(fd, &eof, 1); 52*a974f0c1SBenjamin Gray if (rc != 0) { 53*a974f0c1SBenjamin Gray err = -EOVERFLOW; 54fcb45ec0SMichael Ellerman goto out; 55fcb45ec0SMichael Ellerman } 56*a974f0c1SBenjamin Gray } 57fcb45ec0SMichael Ellerman 58*a974f0c1SBenjamin Gray err = 0; 59*a974f0c1SBenjamin Gray 60e3028437SMichael Ellerman out: 61e3028437SMichael Ellerman close(fd); 62*a974f0c1SBenjamin Gray errno = -err; 63*a974f0c1SBenjamin Gray return err; 64*a974f0c1SBenjamin Gray } 65*a974f0c1SBenjamin Gray 66*a974f0c1SBenjamin Gray int write_file(const char *path, const char *buf, size_t count) 67*a974f0c1SBenjamin Gray { 68*a974f0c1SBenjamin Gray int fd; 69*a974f0c1SBenjamin Gray int err; 70*a974f0c1SBenjamin Gray ssize_t rc; 71*a974f0c1SBenjamin Gray 72*a974f0c1SBenjamin Gray fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); 73*a974f0c1SBenjamin Gray if (fd < 0) 74*a974f0c1SBenjamin Gray return -errno; 75*a974f0c1SBenjamin Gray 76*a974f0c1SBenjamin Gray rc = write(fd, buf, count); 77*a974f0c1SBenjamin Gray if (rc < 0) { 78*a974f0c1SBenjamin Gray err = -errno; 79*a974f0c1SBenjamin Gray goto out; 80*a974f0c1SBenjamin Gray } 81*a974f0c1SBenjamin Gray 82*a974f0c1SBenjamin Gray if (rc != count) { 83*a974f0c1SBenjamin Gray err = -EOVERFLOW; 84*a974f0c1SBenjamin Gray goto out; 85*a974f0c1SBenjamin Gray } 86*a974f0c1SBenjamin Gray 87*a974f0c1SBenjamin Gray err = 0; 88*a974f0c1SBenjamin Gray 89*a974f0c1SBenjamin Gray out: 90*a974f0c1SBenjamin Gray close(fd); 91*a974f0c1SBenjamin Gray errno = -err; 92*a974f0c1SBenjamin Gray return err; 93*a974f0c1SBenjamin Gray } 94*a974f0c1SBenjamin Gray 95*a974f0c1SBenjamin Gray int read_auxv(char *buf, ssize_t buf_size) 96*a974f0c1SBenjamin Gray { 97*a974f0c1SBenjamin Gray int err; 98*a974f0c1SBenjamin Gray 99*a974f0c1SBenjamin Gray err = read_file("/proc/self/auxv", buf, buf_size, NULL); 100*a974f0c1SBenjamin Gray if (err) { 101*a974f0c1SBenjamin Gray perror("Error reading /proc/self/auxv"); 102*a974f0c1SBenjamin Gray return err; 103*a974f0c1SBenjamin Gray } 104*a974f0c1SBenjamin Gray 105*a974f0c1SBenjamin Gray return 0; 106e3028437SMichael Ellerman } 107e3028437SMichael Ellerman 108e3028437SMichael Ellerman void *find_auxv_entry(int type, char *auxv) 109e3028437SMichael Ellerman { 110e3028437SMichael Ellerman ElfW(auxv_t) *p; 111e3028437SMichael Ellerman 112fcb45ec0SMichael Ellerman p = (ElfW(auxv_t) *)auxv; 113fcb45ec0SMichael Ellerman 114fcb45ec0SMichael Ellerman while (p->a_type != AT_NULL) { 115e3028437SMichael Ellerman if (p->a_type == type) 116e3028437SMichael Ellerman return p; 117fcb45ec0SMichael Ellerman 118fcb45ec0SMichael Ellerman p++; 119fcb45ec0SMichael Ellerman } 120e3028437SMichael Ellerman 121e3028437SMichael Ellerman return NULL; 122e3028437SMichael Ellerman } 123e3028437SMichael Ellerman 124e3028437SMichael Ellerman void *get_auxv_entry(int type) 125e3028437SMichael Ellerman { 126e3028437SMichael Ellerman ElfW(auxv_t) *p; 127e3028437SMichael Ellerman 128e3028437SMichael Ellerman if (read_auxv(auxv, sizeof(auxv))) 129e3028437SMichael Ellerman return NULL; 130e3028437SMichael Ellerman 131e3028437SMichael Ellerman p = find_auxv_entry(type, auxv); 132e3028437SMichael Ellerman if (p) 133e3028437SMichael Ellerman return (void *)p->a_un.a_val; 134e3028437SMichael Ellerman 135e3028437SMichael Ellerman return NULL; 136fcb45ec0SMichael Ellerman } 137d1301afdSMichael Ellerman 138d1301afdSMichael Ellerman int pick_online_cpu(void) 139d1301afdSMichael Ellerman { 140dfa03fffSSandipan Das int ncpus, cpu = -1; 141dfa03fffSSandipan Das cpu_set_t *mask; 142dfa03fffSSandipan Das size_t size; 143d1301afdSMichael Ellerman 144dfa03fffSSandipan Das ncpus = get_nprocs_conf(); 145dfa03fffSSandipan Das size = CPU_ALLOC_SIZE(ncpus); 146dfa03fffSSandipan Das mask = CPU_ALLOC(ncpus); 147dfa03fffSSandipan Das if (!mask) { 148dfa03fffSSandipan Das perror("malloc"); 149d1301afdSMichael Ellerman return -1; 150d1301afdSMichael Ellerman } 151d1301afdSMichael Ellerman 152dfa03fffSSandipan Das CPU_ZERO_S(size, mask); 153dfa03fffSSandipan Das 154dfa03fffSSandipan Das if (sched_getaffinity(0, size, mask)) { 155dfa03fffSSandipan Das perror("sched_getaffinity"); 156dfa03fffSSandipan Das goto done; 157dfa03fffSSandipan Das } 158dfa03fffSSandipan Das 159d1301afdSMichael Ellerman /* We prefer a primary thread, but skip 0 */ 160dfa03fffSSandipan Das for (cpu = 8; cpu < ncpus; cpu += 8) 161dfa03fffSSandipan Das if (CPU_ISSET_S(cpu, size, mask)) 162dfa03fffSSandipan Das goto done; 163d1301afdSMichael Ellerman 164d1301afdSMichael Ellerman /* Search for anything, but in reverse */ 165dfa03fffSSandipan Das for (cpu = ncpus - 1; cpu >= 0; cpu--) 166dfa03fffSSandipan Das if (CPU_ISSET_S(cpu, size, mask)) 167dfa03fffSSandipan Das goto done; 168d1301afdSMichael Ellerman 169d1301afdSMichael Ellerman printf("No cpus in affinity mask?!\n"); 170dfa03fffSSandipan Das 171dfa03fffSSandipan Das done: 172dfa03fffSSandipan Das CPU_FREE(mask); 173dfa03fffSSandipan Das return cpu; 174d1301afdSMichael Ellerman } 17595f9b3afSMichael Ellerman 17695f9b3afSMichael Ellerman bool is_ppc64le(void) 17795f9b3afSMichael Ellerman { 17895f9b3afSMichael Ellerman struct utsname uts; 17995f9b3afSMichael Ellerman int rc; 18095f9b3afSMichael Ellerman 18195f9b3afSMichael Ellerman errno = 0; 18295f9b3afSMichael Ellerman rc = uname(&uts); 18395f9b3afSMichael Ellerman if (rc) { 18495f9b3afSMichael Ellerman perror("uname"); 18595f9b3afSMichael Ellerman return false; 18695f9b3afSMichael Ellerman } 18795f9b3afSMichael Ellerman 18895f9b3afSMichael Ellerman return strcmp(uts.machine, "ppc64le") == 0; 18995f9b3afSMichael Ellerman } 190d2bf7932SNaveen N. Rao 191c790c3d2SMichael Ellerman int read_sysfs_file(char *fpath, char *result, size_t result_size) 192c790c3d2SMichael Ellerman { 193c790c3d2SMichael Ellerman char path[PATH_MAX] = "/sys/"; 194c790c3d2SMichael Ellerman 195c790c3d2SMichael Ellerman strncat(path, fpath, PATH_MAX - strlen(path) - 1); 196c790c3d2SMichael Ellerman 197*a974f0c1SBenjamin Gray return read_file(path, result, result_size, NULL); 198c790c3d2SMichael Ellerman } 199c790c3d2SMichael Ellerman 200d2bf7932SNaveen N. Rao int read_debugfs_file(char *debugfs_file, int *result) 201d2bf7932SNaveen N. Rao { 202*a974f0c1SBenjamin Gray int err; 203d2bf7932SNaveen N. Rao char path[PATH_MAX]; 204*a974f0c1SBenjamin Gray char value[16] = {0}; 205d2bf7932SNaveen N. Rao 206d2bf7932SNaveen N. Rao strcpy(path, "/sys/kernel/debug/"); 207d2bf7932SNaveen N. Rao strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); 208d2bf7932SNaveen N. Rao 209*a974f0c1SBenjamin Gray err = read_file(path, value, sizeof(value) - 1, NULL); 210*a974f0c1SBenjamin Gray if (err) 211*a974f0c1SBenjamin Gray return err; 212d2bf7932SNaveen N. Rao 213d2bf7932SNaveen N. Rao *result = atoi(value); 214d2bf7932SNaveen N. Rao 215d2bf7932SNaveen N. Rao return 0; 216d2bf7932SNaveen N. Rao } 217d2bf7932SNaveen N. Rao 218d2bf7932SNaveen N. Rao int write_debugfs_file(char *debugfs_file, int result) 219d2bf7932SNaveen N. Rao { 220d2bf7932SNaveen N. Rao char path[PATH_MAX]; 221d2bf7932SNaveen N. Rao char value[16]; 222d2bf7932SNaveen N. Rao 223d2bf7932SNaveen N. Rao strcpy(path, "/sys/kernel/debug/"); 224d2bf7932SNaveen N. Rao strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); 225d2bf7932SNaveen N. Rao 226d2bf7932SNaveen N. Rao snprintf(value, 16, "%d", result); 227d2bf7932SNaveen N. Rao 228*a974f0c1SBenjamin Gray return write_file(path, value, strlen(value)); 229d2bf7932SNaveen N. Rao } 230d2bf7932SNaveen N. Rao 231d2bf7932SNaveen N. Rao static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, 232d2bf7932SNaveen N. Rao int cpu, int group_fd, unsigned long flags) 233d2bf7932SNaveen N. Rao { 234d2bf7932SNaveen N. Rao return syscall(__NR_perf_event_open, hw_event, pid, cpu, 235d2bf7932SNaveen N. Rao group_fd, flags); 236d2bf7932SNaveen N. Rao } 237d2bf7932SNaveen N. Rao 238d2bf7932SNaveen N. Rao static void perf_event_attr_init(struct perf_event_attr *event_attr, 239d2bf7932SNaveen N. Rao unsigned int type, 240d2bf7932SNaveen N. Rao unsigned long config) 241d2bf7932SNaveen N. Rao { 242d2bf7932SNaveen N. Rao memset(event_attr, 0, sizeof(*event_attr)); 243d2bf7932SNaveen N. Rao 244d2bf7932SNaveen N. Rao event_attr->type = type; 245d2bf7932SNaveen N. Rao event_attr->size = sizeof(struct perf_event_attr); 246d2bf7932SNaveen N. Rao event_attr->config = config; 247d2bf7932SNaveen N. Rao event_attr->read_format = PERF_FORMAT_GROUP; 248d2bf7932SNaveen N. Rao event_attr->disabled = 1; 249d2bf7932SNaveen N. Rao event_attr->exclude_kernel = 1; 250d2bf7932SNaveen N. Rao event_attr->exclude_hv = 1; 251d2bf7932SNaveen N. Rao event_attr->exclude_guest = 1; 252d2bf7932SNaveen N. Rao } 253d2bf7932SNaveen N. Rao 254d2bf7932SNaveen N. Rao int perf_event_open_counter(unsigned int type, 255d2bf7932SNaveen N. Rao unsigned long config, int group_fd) 256d2bf7932SNaveen N. Rao { 257d2bf7932SNaveen N. Rao int fd; 258d2bf7932SNaveen N. Rao struct perf_event_attr event_attr; 259d2bf7932SNaveen N. Rao 260d2bf7932SNaveen N. Rao perf_event_attr_init(&event_attr, type, config); 261d2bf7932SNaveen N. Rao 262d2bf7932SNaveen N. Rao fd = perf_event_open(&event_attr, 0, -1, group_fd, 0); 263d2bf7932SNaveen N. Rao 264d2bf7932SNaveen N. Rao if (fd < 0) 265d2bf7932SNaveen N. Rao perror("perf_event_open() failed"); 266d2bf7932SNaveen N. Rao 267d2bf7932SNaveen N. Rao return fd; 268d2bf7932SNaveen N. Rao } 269d2bf7932SNaveen N. Rao 270d2bf7932SNaveen N. Rao int perf_event_enable(int fd) 271d2bf7932SNaveen N. Rao { 272d2bf7932SNaveen N. Rao if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) { 273d2bf7932SNaveen N. Rao perror("error while enabling perf events"); 274d2bf7932SNaveen N. Rao return -1; 275d2bf7932SNaveen N. Rao } 276d2bf7932SNaveen N. Rao 277d2bf7932SNaveen N. Rao return 0; 278d2bf7932SNaveen N. Rao } 279d2bf7932SNaveen N. Rao 280d2bf7932SNaveen N. Rao int perf_event_disable(int fd) 281d2bf7932SNaveen N. Rao { 282d2bf7932SNaveen N. Rao if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { 283d2bf7932SNaveen N. Rao perror("error disabling perf events"); 284d2bf7932SNaveen N. Rao return -1; 285d2bf7932SNaveen N. Rao } 286d2bf7932SNaveen N. Rao 287d2bf7932SNaveen N. Rao return 0; 288d2bf7932SNaveen N. Rao } 289d2bf7932SNaveen N. Rao 290d2bf7932SNaveen N. Rao int perf_event_reset(int fd) 291d2bf7932SNaveen N. Rao { 292d2bf7932SNaveen N. Rao if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) { 293d2bf7932SNaveen N. Rao perror("error resetting perf events"); 294d2bf7932SNaveen N. Rao return -1; 295d2bf7932SNaveen N. Rao } 296d2bf7932SNaveen N. Rao 297d2bf7932SNaveen N. Rao return 0; 298d2bf7932SNaveen N. Rao } 299d2bf7932SNaveen N. Rao 300c405b738SSandipan Das int using_hash_mmu(bool *using_hash) 301c405b738SSandipan Das { 302c405b738SSandipan Das char line[128]; 303c405b738SSandipan Das FILE *f; 304c405b738SSandipan Das int rc; 305c405b738SSandipan Das 306c405b738SSandipan Das f = fopen("/proc/cpuinfo", "r"); 307c405b738SSandipan Das FAIL_IF(!f); 308c405b738SSandipan Das 309c405b738SSandipan Das rc = 0; 310c405b738SSandipan Das while (fgets(line, sizeof(line), f) != NULL) { 31134c10334SMichael Ellerman if (!strcmp(line, "MMU : Hash\n") || 31234c10334SMichael Ellerman !strcmp(line, "platform : Cell\n") || 31334c10334SMichael Ellerman !strcmp(line, "platform : PowerMac\n")) { 314c405b738SSandipan Das *using_hash = true; 315c405b738SSandipan Das goto out; 316c405b738SSandipan Das } 317c405b738SSandipan Das 318c405b738SSandipan Das if (strcmp(line, "MMU : Radix\n") == 0) { 319c405b738SSandipan Das *using_hash = false; 320c405b738SSandipan Das goto out; 321c405b738SSandipan Das } 322c405b738SSandipan Das } 323c405b738SSandipan Das 324c405b738SSandipan Das rc = -1; 325c405b738SSandipan Das out: 326c405b738SSandipan Das fclose(f); 327c405b738SSandipan Das return rc; 328c405b738SSandipan Das } 329