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 29a974f0c1SBenjamin Gray int read_file(const char *path, char *buf, size_t count, size_t *len) 30fcb45ec0SMichael Ellerman { 31a974f0c1SBenjamin Gray ssize_t rc; 32a974f0c1SBenjamin Gray int fd; 33a974f0c1SBenjamin Gray int err; 34a974f0c1SBenjamin Gray char eof; 35fcb45ec0SMichael Ellerman 36a974f0c1SBenjamin Gray fd = open(path, O_RDONLY); 37a974f0c1SBenjamin Gray if (fd < 0) 38e3028437SMichael Ellerman return -errno; 39fcb45ec0SMichael Ellerman 40a974f0c1SBenjamin Gray rc = read(fd, buf, count); 41a974f0c1SBenjamin Gray if (rc < 0) { 42a974f0c1SBenjamin Gray err = -errno; 43fcb45ec0SMichael Ellerman goto out; 44fcb45ec0SMichael Ellerman } 45fcb45ec0SMichael Ellerman 46a974f0c1SBenjamin Gray if (len) 47a974f0c1SBenjamin Gray *len = rc; 48a974f0c1SBenjamin Gray 49a974f0c1SBenjamin Gray /* Overflow if there are still more bytes after filling the buffer */ 50a974f0c1SBenjamin Gray if (rc == count) { 51a974f0c1SBenjamin Gray rc = read(fd, &eof, 1); 52a974f0c1SBenjamin Gray if (rc != 0) { 53a974f0c1SBenjamin Gray err = -EOVERFLOW; 54fcb45ec0SMichael Ellerman goto out; 55fcb45ec0SMichael Ellerman } 56a974f0c1SBenjamin Gray } 57fcb45ec0SMichael Ellerman 58a974f0c1SBenjamin Gray err = 0; 59a974f0c1SBenjamin Gray 60e3028437SMichael Ellerman out: 61e3028437SMichael Ellerman close(fd); 62a974f0c1SBenjamin Gray errno = -err; 63a974f0c1SBenjamin Gray return err; 64a974f0c1SBenjamin Gray } 65a974f0c1SBenjamin Gray 66a974f0c1SBenjamin Gray int write_file(const char *path, const char *buf, size_t count) 67a974f0c1SBenjamin Gray { 68a974f0c1SBenjamin Gray int fd; 69a974f0c1SBenjamin Gray int err; 70a974f0c1SBenjamin Gray ssize_t rc; 71a974f0c1SBenjamin Gray 72a974f0c1SBenjamin Gray fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); 73a974f0c1SBenjamin Gray if (fd < 0) 74a974f0c1SBenjamin Gray return -errno; 75a974f0c1SBenjamin Gray 76a974f0c1SBenjamin Gray rc = write(fd, buf, count); 77a974f0c1SBenjamin Gray if (rc < 0) { 78a974f0c1SBenjamin Gray err = -errno; 79a974f0c1SBenjamin Gray goto out; 80a974f0c1SBenjamin Gray } 81a974f0c1SBenjamin Gray 82a974f0c1SBenjamin Gray if (rc != count) { 83a974f0c1SBenjamin Gray err = -EOVERFLOW; 84a974f0c1SBenjamin Gray goto out; 85a974f0c1SBenjamin Gray } 86a974f0c1SBenjamin Gray 87a974f0c1SBenjamin Gray err = 0; 88a974f0c1SBenjamin Gray 89a974f0c1SBenjamin Gray out: 90a974f0c1SBenjamin Gray close(fd); 91a974f0c1SBenjamin Gray errno = -err; 92a974f0c1SBenjamin Gray return err; 93a974f0c1SBenjamin Gray } 94a974f0c1SBenjamin Gray 95a974f0c1SBenjamin Gray int read_auxv(char *buf, ssize_t buf_size) 96a974f0c1SBenjamin Gray { 97a974f0c1SBenjamin Gray int err; 98a974f0c1SBenjamin Gray 99a974f0c1SBenjamin Gray err = read_file("/proc/self/auxv", buf, buf_size, NULL); 100a974f0c1SBenjamin Gray if (err) { 101a974f0c1SBenjamin Gray perror("Error reading /proc/self/auxv"); 102a974f0c1SBenjamin Gray return err; 103a974f0c1SBenjamin Gray } 104a974f0c1SBenjamin Gray 105a974f0c1SBenjamin Gray return 0; 106e3028437SMichael Ellerman } 107e3028437SMichael Ellerman 108*121d340bSBenjamin Gray int read_debugfs_file(const char *subpath, char *buf, size_t count) 109*121d340bSBenjamin Gray { 110*121d340bSBenjamin Gray char path[PATH_MAX] = "/sys/kernel/debug/"; 111*121d340bSBenjamin Gray 112*121d340bSBenjamin Gray strncat(path, subpath, sizeof(path) - strlen(path) - 1); 113*121d340bSBenjamin Gray 114*121d340bSBenjamin Gray return read_file(path, buf, count, NULL); 115*121d340bSBenjamin Gray } 116*121d340bSBenjamin Gray 117*121d340bSBenjamin Gray int write_debugfs_file(const char *subpath, const char *buf, size_t count) 118*121d340bSBenjamin Gray { 119*121d340bSBenjamin Gray char path[PATH_MAX] = "/sys/kernel/debug/"; 120*121d340bSBenjamin Gray 121*121d340bSBenjamin Gray strncat(path, subpath, sizeof(path) - strlen(path) - 1); 122*121d340bSBenjamin Gray 123*121d340bSBenjamin Gray return write_file(path, buf, count); 124*121d340bSBenjamin Gray } 125*121d340bSBenjamin Gray 126e3028437SMichael Ellerman void *find_auxv_entry(int type, char *auxv) 127e3028437SMichael Ellerman { 128e3028437SMichael Ellerman ElfW(auxv_t) *p; 129e3028437SMichael Ellerman 130fcb45ec0SMichael Ellerman p = (ElfW(auxv_t) *)auxv; 131fcb45ec0SMichael Ellerman 132fcb45ec0SMichael Ellerman while (p->a_type != AT_NULL) { 133e3028437SMichael Ellerman if (p->a_type == type) 134e3028437SMichael Ellerman return p; 135fcb45ec0SMichael Ellerman 136fcb45ec0SMichael Ellerman p++; 137fcb45ec0SMichael Ellerman } 138e3028437SMichael Ellerman 139e3028437SMichael Ellerman return NULL; 140e3028437SMichael Ellerman } 141e3028437SMichael Ellerman 142e3028437SMichael Ellerman void *get_auxv_entry(int type) 143e3028437SMichael Ellerman { 144e3028437SMichael Ellerman ElfW(auxv_t) *p; 145e3028437SMichael Ellerman 146e3028437SMichael Ellerman if (read_auxv(auxv, sizeof(auxv))) 147e3028437SMichael Ellerman return NULL; 148e3028437SMichael Ellerman 149e3028437SMichael Ellerman p = find_auxv_entry(type, auxv); 150e3028437SMichael Ellerman if (p) 151e3028437SMichael Ellerman return (void *)p->a_un.a_val; 152e3028437SMichael Ellerman 153e3028437SMichael Ellerman return NULL; 154fcb45ec0SMichael Ellerman } 155d1301afdSMichael Ellerman 156d1301afdSMichael Ellerman int pick_online_cpu(void) 157d1301afdSMichael Ellerman { 158dfa03fffSSandipan Das int ncpus, cpu = -1; 159dfa03fffSSandipan Das cpu_set_t *mask; 160dfa03fffSSandipan Das size_t size; 161d1301afdSMichael Ellerman 162dfa03fffSSandipan Das ncpus = get_nprocs_conf(); 163dfa03fffSSandipan Das size = CPU_ALLOC_SIZE(ncpus); 164dfa03fffSSandipan Das mask = CPU_ALLOC(ncpus); 165dfa03fffSSandipan Das if (!mask) { 166dfa03fffSSandipan Das perror("malloc"); 167d1301afdSMichael Ellerman return -1; 168d1301afdSMichael Ellerman } 169d1301afdSMichael Ellerman 170dfa03fffSSandipan Das CPU_ZERO_S(size, mask); 171dfa03fffSSandipan Das 172dfa03fffSSandipan Das if (sched_getaffinity(0, size, mask)) { 173dfa03fffSSandipan Das perror("sched_getaffinity"); 174dfa03fffSSandipan Das goto done; 175dfa03fffSSandipan Das } 176dfa03fffSSandipan Das 177d1301afdSMichael Ellerman /* We prefer a primary thread, but skip 0 */ 178dfa03fffSSandipan Das for (cpu = 8; cpu < ncpus; cpu += 8) 179dfa03fffSSandipan Das if (CPU_ISSET_S(cpu, size, mask)) 180dfa03fffSSandipan Das goto done; 181d1301afdSMichael Ellerman 182d1301afdSMichael Ellerman /* Search for anything, but in reverse */ 183dfa03fffSSandipan Das for (cpu = ncpus - 1; cpu >= 0; cpu--) 184dfa03fffSSandipan Das if (CPU_ISSET_S(cpu, size, mask)) 185dfa03fffSSandipan Das goto done; 186d1301afdSMichael Ellerman 187d1301afdSMichael Ellerman printf("No cpus in affinity mask?!\n"); 188dfa03fffSSandipan Das 189dfa03fffSSandipan Das done: 190dfa03fffSSandipan Das CPU_FREE(mask); 191dfa03fffSSandipan Das return cpu; 192d1301afdSMichael Ellerman } 19395f9b3afSMichael Ellerman 19495f9b3afSMichael Ellerman bool is_ppc64le(void) 19595f9b3afSMichael Ellerman { 19695f9b3afSMichael Ellerman struct utsname uts; 19795f9b3afSMichael Ellerman int rc; 19895f9b3afSMichael Ellerman 19995f9b3afSMichael Ellerman errno = 0; 20095f9b3afSMichael Ellerman rc = uname(&uts); 20195f9b3afSMichael Ellerman if (rc) { 20295f9b3afSMichael Ellerman perror("uname"); 20395f9b3afSMichael Ellerman return false; 20495f9b3afSMichael Ellerman } 20595f9b3afSMichael Ellerman 20695f9b3afSMichael Ellerman return strcmp(uts.machine, "ppc64le") == 0; 20795f9b3afSMichael Ellerman } 208d2bf7932SNaveen N. Rao 209c790c3d2SMichael Ellerman int read_sysfs_file(char *fpath, char *result, size_t result_size) 210c790c3d2SMichael Ellerman { 211c790c3d2SMichael Ellerman char path[PATH_MAX] = "/sys/"; 212c790c3d2SMichael Ellerman 213c790c3d2SMichael Ellerman strncat(path, fpath, PATH_MAX - strlen(path) - 1); 214c790c3d2SMichael Ellerman 215a974f0c1SBenjamin Gray return read_file(path, result, result_size, NULL); 216c790c3d2SMichael Ellerman } 217c790c3d2SMichael Ellerman 218*121d340bSBenjamin Gray int read_debugfs_int(const char *debugfs_file, int *result) 219d2bf7932SNaveen N. Rao { 220a974f0c1SBenjamin Gray int err; 221a974f0c1SBenjamin Gray char value[16] = {0}; 222d2bf7932SNaveen N. Rao 223*121d340bSBenjamin Gray err = read_debugfs_file(debugfs_file, value, sizeof(value) - 1); 224a974f0c1SBenjamin Gray if (err) 225a974f0c1SBenjamin Gray return err; 226d2bf7932SNaveen N. Rao 227d2bf7932SNaveen N. Rao *result = atoi(value); 228d2bf7932SNaveen N. Rao 229d2bf7932SNaveen N. Rao return 0; 230d2bf7932SNaveen N. Rao } 231d2bf7932SNaveen N. Rao 232*121d340bSBenjamin Gray int write_debugfs_int(const char *debugfs_file, int result) 233d2bf7932SNaveen N. Rao { 234d2bf7932SNaveen N. Rao char value[16]; 235d2bf7932SNaveen N. Rao 236d2bf7932SNaveen N. Rao snprintf(value, 16, "%d", result); 237d2bf7932SNaveen N. Rao 238*121d340bSBenjamin Gray return write_debugfs_file(debugfs_file, value, strlen(value)); 239d2bf7932SNaveen N. Rao } 240d2bf7932SNaveen N. Rao 241d2bf7932SNaveen N. Rao static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, 242d2bf7932SNaveen N. Rao int cpu, int group_fd, unsigned long flags) 243d2bf7932SNaveen N. Rao { 244d2bf7932SNaveen N. Rao return syscall(__NR_perf_event_open, hw_event, pid, cpu, 245d2bf7932SNaveen N. Rao group_fd, flags); 246d2bf7932SNaveen N. Rao } 247d2bf7932SNaveen N. Rao 248d2bf7932SNaveen N. Rao static void perf_event_attr_init(struct perf_event_attr *event_attr, 249d2bf7932SNaveen N. Rao unsigned int type, 250d2bf7932SNaveen N. Rao unsigned long config) 251d2bf7932SNaveen N. Rao { 252d2bf7932SNaveen N. Rao memset(event_attr, 0, sizeof(*event_attr)); 253d2bf7932SNaveen N. Rao 254d2bf7932SNaveen N. Rao event_attr->type = type; 255d2bf7932SNaveen N. Rao event_attr->size = sizeof(struct perf_event_attr); 256d2bf7932SNaveen N. Rao event_attr->config = config; 257d2bf7932SNaveen N. Rao event_attr->read_format = PERF_FORMAT_GROUP; 258d2bf7932SNaveen N. Rao event_attr->disabled = 1; 259d2bf7932SNaveen N. Rao event_attr->exclude_kernel = 1; 260d2bf7932SNaveen N. Rao event_attr->exclude_hv = 1; 261d2bf7932SNaveen N. Rao event_attr->exclude_guest = 1; 262d2bf7932SNaveen N. Rao } 263d2bf7932SNaveen N. Rao 264d2bf7932SNaveen N. Rao int perf_event_open_counter(unsigned int type, 265d2bf7932SNaveen N. Rao unsigned long config, int group_fd) 266d2bf7932SNaveen N. Rao { 267d2bf7932SNaveen N. Rao int fd; 268d2bf7932SNaveen N. Rao struct perf_event_attr event_attr; 269d2bf7932SNaveen N. Rao 270d2bf7932SNaveen N. Rao perf_event_attr_init(&event_attr, type, config); 271d2bf7932SNaveen N. Rao 272d2bf7932SNaveen N. Rao fd = perf_event_open(&event_attr, 0, -1, group_fd, 0); 273d2bf7932SNaveen N. Rao 274d2bf7932SNaveen N. Rao if (fd < 0) 275d2bf7932SNaveen N. Rao perror("perf_event_open() failed"); 276d2bf7932SNaveen N. Rao 277d2bf7932SNaveen N. Rao return fd; 278d2bf7932SNaveen N. Rao } 279d2bf7932SNaveen N. Rao 280d2bf7932SNaveen N. Rao int perf_event_enable(int fd) 281d2bf7932SNaveen N. Rao { 282d2bf7932SNaveen N. Rao if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) { 283d2bf7932SNaveen N. Rao perror("error while enabling 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_disable(int fd) 291d2bf7932SNaveen N. Rao { 292d2bf7932SNaveen N. Rao if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { 293d2bf7932SNaveen N. Rao perror("error disabling perf events"); 294d2bf7932SNaveen N. Rao return -1; 295d2bf7932SNaveen N. Rao } 296d2bf7932SNaveen N. Rao 297d2bf7932SNaveen N. Rao return 0; 298d2bf7932SNaveen N. Rao } 299d2bf7932SNaveen N. Rao 300d2bf7932SNaveen N. Rao int perf_event_reset(int fd) 301d2bf7932SNaveen N. Rao { 302d2bf7932SNaveen N. Rao if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) { 303d2bf7932SNaveen N. Rao perror("error resetting perf events"); 304d2bf7932SNaveen N. Rao return -1; 305d2bf7932SNaveen N. Rao } 306d2bf7932SNaveen N. Rao 307d2bf7932SNaveen N. Rao return 0; 308d2bf7932SNaveen N. Rao } 309d2bf7932SNaveen N. Rao 310c405b738SSandipan Das int using_hash_mmu(bool *using_hash) 311c405b738SSandipan Das { 312c405b738SSandipan Das char line[128]; 313c405b738SSandipan Das FILE *f; 314c405b738SSandipan Das int rc; 315c405b738SSandipan Das 316c405b738SSandipan Das f = fopen("/proc/cpuinfo", "r"); 317c405b738SSandipan Das FAIL_IF(!f); 318c405b738SSandipan Das 319c405b738SSandipan Das rc = 0; 320c405b738SSandipan Das while (fgets(line, sizeof(line), f) != NULL) { 32134c10334SMichael Ellerman if (!strcmp(line, "MMU : Hash\n") || 32234c10334SMichael Ellerman !strcmp(line, "platform : Cell\n") || 32334c10334SMichael Ellerman !strcmp(line, "platform : PowerMac\n")) { 324c405b738SSandipan Das *using_hash = true; 325c405b738SSandipan Das goto out; 326c405b738SSandipan Das } 327c405b738SSandipan Das 328c405b738SSandipan Das if (strcmp(line, "MMU : Radix\n") == 0) { 329c405b738SSandipan Das *using_hash = false; 330c405b738SSandipan Das goto out; 331c405b738SSandipan Das } 332c405b738SSandipan Das } 333c405b738SSandipan Das 334c405b738SSandipan Das rc = -1; 335c405b738SSandipan Das out: 336c405b738SSandipan Das fclose(f); 337c405b738SSandipan Das return rc; 338c405b738SSandipan Das } 339