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> 13d2bf7932SNaveen N. Rao #include <signal.h> 14fcb45ec0SMichael Ellerman #include <stdio.h> 15d2bf7932SNaveen N. Rao #include <stdlib.h> 1695f9b3afSMichael Ellerman #include <string.h> 17d2bf7932SNaveen N. Rao #include <sys/ioctl.h> 18fcb45ec0SMichael Ellerman #include <sys/stat.h> 19dfa03fffSSandipan Das #include <sys/sysinfo.h> 20fcb45ec0SMichael Ellerman #include <sys/types.h> 2195f9b3afSMichael Ellerman #include <sys/utsname.h> 22fcb45ec0SMichael Ellerman #include <unistd.h> 23d2bf7932SNaveen N. Rao #include <asm/unistd.h> 24d2bf7932SNaveen N. Rao #include <linux/limits.h> 25fcb45ec0SMichael Ellerman 26fcb45ec0SMichael Ellerman #include "utils.h" 27fcb45ec0SMichael Ellerman 28fcb45ec0SMichael Ellerman static char auxv[4096]; 29fcb45ec0SMichael Ellerman 30e3028437SMichael Ellerman int read_auxv(char *buf, ssize_t buf_size) 31fcb45ec0SMichael Ellerman { 32fcb45ec0SMichael Ellerman ssize_t num; 33e3028437SMichael Ellerman int rc, fd; 34fcb45ec0SMichael Ellerman 35fcb45ec0SMichael Ellerman fd = open("/proc/self/auxv", O_RDONLY); 36fcb45ec0SMichael Ellerman if (fd == -1) { 37fcb45ec0SMichael Ellerman perror("open"); 38e3028437SMichael Ellerman return -errno; 39fcb45ec0SMichael Ellerman } 40fcb45ec0SMichael Ellerman 41e3028437SMichael Ellerman num = read(fd, buf, buf_size); 42fcb45ec0SMichael Ellerman if (num < 0) { 43fcb45ec0SMichael Ellerman perror("read"); 44e3028437SMichael Ellerman rc = -EIO; 45fcb45ec0SMichael Ellerman goto out; 46fcb45ec0SMichael Ellerman } 47fcb45ec0SMichael Ellerman 48e3028437SMichael Ellerman if (num > buf_size) { 49e3028437SMichael Ellerman printf("overflowed auxv buffer\n"); 50e3028437SMichael Ellerman rc = -EOVERFLOW; 51fcb45ec0SMichael Ellerman goto out; 52fcb45ec0SMichael Ellerman } 53fcb45ec0SMichael Ellerman 54e3028437SMichael Ellerman rc = 0; 55e3028437SMichael Ellerman out: 56e3028437SMichael Ellerman close(fd); 57e3028437SMichael Ellerman return rc; 58e3028437SMichael Ellerman } 59e3028437SMichael Ellerman 60e3028437SMichael Ellerman void *find_auxv_entry(int type, char *auxv) 61e3028437SMichael Ellerman { 62e3028437SMichael Ellerman ElfW(auxv_t) *p; 63e3028437SMichael Ellerman 64fcb45ec0SMichael Ellerman p = (ElfW(auxv_t) *)auxv; 65fcb45ec0SMichael Ellerman 66fcb45ec0SMichael Ellerman while (p->a_type != AT_NULL) { 67e3028437SMichael Ellerman if (p->a_type == type) 68e3028437SMichael Ellerman return p; 69fcb45ec0SMichael Ellerman 70fcb45ec0SMichael Ellerman p++; 71fcb45ec0SMichael Ellerman } 72e3028437SMichael Ellerman 73e3028437SMichael Ellerman return NULL; 74e3028437SMichael Ellerman } 75e3028437SMichael Ellerman 76e3028437SMichael Ellerman void *get_auxv_entry(int type) 77e3028437SMichael Ellerman { 78e3028437SMichael Ellerman ElfW(auxv_t) *p; 79e3028437SMichael Ellerman 80e3028437SMichael Ellerman if (read_auxv(auxv, sizeof(auxv))) 81e3028437SMichael Ellerman return NULL; 82e3028437SMichael Ellerman 83e3028437SMichael Ellerman p = find_auxv_entry(type, auxv); 84e3028437SMichael Ellerman if (p) 85e3028437SMichael Ellerman return (void *)p->a_un.a_val; 86e3028437SMichael Ellerman 87e3028437SMichael Ellerman return NULL; 88fcb45ec0SMichael Ellerman } 89d1301afdSMichael Ellerman 90d1301afdSMichael Ellerman int pick_online_cpu(void) 91d1301afdSMichael Ellerman { 92dfa03fffSSandipan Das int ncpus, cpu = -1; 93dfa03fffSSandipan Das cpu_set_t *mask; 94dfa03fffSSandipan Das size_t size; 95d1301afdSMichael Ellerman 96dfa03fffSSandipan Das ncpus = get_nprocs_conf(); 97dfa03fffSSandipan Das size = CPU_ALLOC_SIZE(ncpus); 98dfa03fffSSandipan Das mask = CPU_ALLOC(ncpus); 99dfa03fffSSandipan Das if (!mask) { 100dfa03fffSSandipan Das perror("malloc"); 101d1301afdSMichael Ellerman return -1; 102d1301afdSMichael Ellerman } 103d1301afdSMichael Ellerman 104dfa03fffSSandipan Das CPU_ZERO_S(size, mask); 105dfa03fffSSandipan Das 106dfa03fffSSandipan Das if (sched_getaffinity(0, size, mask)) { 107dfa03fffSSandipan Das perror("sched_getaffinity"); 108dfa03fffSSandipan Das goto done; 109dfa03fffSSandipan Das } 110dfa03fffSSandipan Das 111d1301afdSMichael Ellerman /* We prefer a primary thread, but skip 0 */ 112dfa03fffSSandipan Das for (cpu = 8; cpu < ncpus; cpu += 8) 113dfa03fffSSandipan Das if (CPU_ISSET_S(cpu, size, mask)) 114dfa03fffSSandipan Das goto done; 115d1301afdSMichael Ellerman 116d1301afdSMichael Ellerman /* Search for anything, but in reverse */ 117dfa03fffSSandipan Das for (cpu = ncpus - 1; cpu >= 0; cpu--) 118dfa03fffSSandipan Das if (CPU_ISSET_S(cpu, size, mask)) 119dfa03fffSSandipan Das goto done; 120d1301afdSMichael Ellerman 121d1301afdSMichael Ellerman printf("No cpus in affinity mask?!\n"); 122dfa03fffSSandipan Das 123dfa03fffSSandipan Das done: 124dfa03fffSSandipan Das CPU_FREE(mask); 125dfa03fffSSandipan Das return cpu; 126d1301afdSMichael Ellerman } 12795f9b3afSMichael Ellerman 12895f9b3afSMichael Ellerman bool is_ppc64le(void) 12995f9b3afSMichael Ellerman { 13095f9b3afSMichael Ellerman struct utsname uts; 13195f9b3afSMichael Ellerman int rc; 13295f9b3afSMichael Ellerman 13395f9b3afSMichael Ellerman errno = 0; 13495f9b3afSMichael Ellerman rc = uname(&uts); 13595f9b3afSMichael Ellerman if (rc) { 13695f9b3afSMichael Ellerman perror("uname"); 13795f9b3afSMichael Ellerman return false; 13895f9b3afSMichael Ellerman } 13995f9b3afSMichael Ellerman 14095f9b3afSMichael Ellerman return strcmp(uts.machine, "ppc64le") == 0; 14195f9b3afSMichael Ellerman } 142d2bf7932SNaveen N. Rao 143c790c3d2SMichael Ellerman int read_sysfs_file(char *fpath, char *result, size_t result_size) 144c790c3d2SMichael Ellerman { 145c790c3d2SMichael Ellerman char path[PATH_MAX] = "/sys/"; 146c790c3d2SMichael Ellerman int rc = -1, fd; 147c790c3d2SMichael Ellerman 148c790c3d2SMichael Ellerman strncat(path, fpath, PATH_MAX - strlen(path) - 1); 149c790c3d2SMichael Ellerman 150c790c3d2SMichael Ellerman if ((fd = open(path, O_RDONLY)) < 0) 151c790c3d2SMichael Ellerman return rc; 152c790c3d2SMichael Ellerman 153c790c3d2SMichael Ellerman rc = read(fd, result, result_size); 154c790c3d2SMichael Ellerman 155c790c3d2SMichael Ellerman close(fd); 156c790c3d2SMichael Ellerman 157c790c3d2SMichael Ellerman if (rc < 0) 158c790c3d2SMichael Ellerman return rc; 159c790c3d2SMichael Ellerman 160c790c3d2SMichael Ellerman return 0; 161c790c3d2SMichael Ellerman } 162c790c3d2SMichael Ellerman 163d2bf7932SNaveen N. Rao int read_debugfs_file(char *debugfs_file, int *result) 164d2bf7932SNaveen N. Rao { 165d2bf7932SNaveen N. Rao int rc = -1, fd; 166d2bf7932SNaveen N. Rao char path[PATH_MAX]; 167d2bf7932SNaveen N. Rao char value[16]; 168d2bf7932SNaveen N. Rao 169d2bf7932SNaveen N. Rao strcpy(path, "/sys/kernel/debug/"); 170d2bf7932SNaveen N. Rao strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); 171d2bf7932SNaveen N. Rao 172d2bf7932SNaveen N. Rao if ((fd = open(path, O_RDONLY)) < 0) 173d2bf7932SNaveen N. Rao return rc; 174d2bf7932SNaveen N. Rao 175d2bf7932SNaveen N. Rao if ((rc = read(fd, value, sizeof(value))) < 0) 176d2bf7932SNaveen N. Rao return rc; 177d2bf7932SNaveen N. Rao 178d2bf7932SNaveen N. Rao value[15] = 0; 179d2bf7932SNaveen N. Rao *result = atoi(value); 180d2bf7932SNaveen N. Rao close(fd); 181d2bf7932SNaveen N. Rao 182d2bf7932SNaveen N. Rao return 0; 183d2bf7932SNaveen N. Rao } 184d2bf7932SNaveen N. Rao 185d2bf7932SNaveen N. Rao int write_debugfs_file(char *debugfs_file, int result) 186d2bf7932SNaveen N. Rao { 187d2bf7932SNaveen N. Rao int rc = -1, fd; 188d2bf7932SNaveen N. Rao char path[PATH_MAX]; 189d2bf7932SNaveen N. Rao char value[16]; 190d2bf7932SNaveen N. Rao 191d2bf7932SNaveen N. Rao strcpy(path, "/sys/kernel/debug/"); 192d2bf7932SNaveen N. Rao strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); 193d2bf7932SNaveen N. Rao 194d2bf7932SNaveen N. Rao if ((fd = open(path, O_WRONLY)) < 0) 195d2bf7932SNaveen N. Rao return rc; 196d2bf7932SNaveen N. Rao 197d2bf7932SNaveen N. Rao snprintf(value, 16, "%d", result); 198d2bf7932SNaveen N. Rao 199d2bf7932SNaveen N. Rao if ((rc = write(fd, value, strlen(value))) < 0) 200d2bf7932SNaveen N. Rao return rc; 201d2bf7932SNaveen N. Rao 202d2bf7932SNaveen N. Rao close(fd); 203d2bf7932SNaveen N. Rao 204d2bf7932SNaveen N. Rao return 0; 205d2bf7932SNaveen N. Rao } 206d2bf7932SNaveen N. Rao 207d2bf7932SNaveen N. Rao static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, 208d2bf7932SNaveen N. Rao int cpu, int group_fd, unsigned long flags) 209d2bf7932SNaveen N. Rao { 210d2bf7932SNaveen N. Rao return syscall(__NR_perf_event_open, hw_event, pid, cpu, 211d2bf7932SNaveen N. Rao group_fd, flags); 212d2bf7932SNaveen N. Rao } 213d2bf7932SNaveen N. Rao 214d2bf7932SNaveen N. Rao static void perf_event_attr_init(struct perf_event_attr *event_attr, 215d2bf7932SNaveen N. Rao unsigned int type, 216d2bf7932SNaveen N. Rao unsigned long config) 217d2bf7932SNaveen N. Rao { 218d2bf7932SNaveen N. Rao memset(event_attr, 0, sizeof(*event_attr)); 219d2bf7932SNaveen N. Rao 220d2bf7932SNaveen N. Rao event_attr->type = type; 221d2bf7932SNaveen N. Rao event_attr->size = sizeof(struct perf_event_attr); 222d2bf7932SNaveen N. Rao event_attr->config = config; 223d2bf7932SNaveen N. Rao event_attr->read_format = PERF_FORMAT_GROUP; 224d2bf7932SNaveen N. Rao event_attr->disabled = 1; 225d2bf7932SNaveen N. Rao event_attr->exclude_kernel = 1; 226d2bf7932SNaveen N. Rao event_attr->exclude_hv = 1; 227d2bf7932SNaveen N. Rao event_attr->exclude_guest = 1; 228d2bf7932SNaveen N. Rao } 229d2bf7932SNaveen N. Rao 230d2bf7932SNaveen N. Rao int perf_event_open_counter(unsigned int type, 231d2bf7932SNaveen N. Rao unsigned long config, int group_fd) 232d2bf7932SNaveen N. Rao { 233d2bf7932SNaveen N. Rao int fd; 234d2bf7932SNaveen N. Rao struct perf_event_attr event_attr; 235d2bf7932SNaveen N. Rao 236d2bf7932SNaveen N. Rao perf_event_attr_init(&event_attr, type, config); 237d2bf7932SNaveen N. Rao 238d2bf7932SNaveen N. Rao fd = perf_event_open(&event_attr, 0, -1, group_fd, 0); 239d2bf7932SNaveen N. Rao 240d2bf7932SNaveen N. Rao if (fd < 0) 241d2bf7932SNaveen N. Rao perror("perf_event_open() failed"); 242d2bf7932SNaveen N. Rao 243d2bf7932SNaveen N. Rao return fd; 244d2bf7932SNaveen N. Rao } 245d2bf7932SNaveen N. Rao 246d2bf7932SNaveen N. Rao int perf_event_enable(int fd) 247d2bf7932SNaveen N. Rao { 248d2bf7932SNaveen N. Rao if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) { 249d2bf7932SNaveen N. Rao perror("error while enabling perf events"); 250d2bf7932SNaveen N. Rao return -1; 251d2bf7932SNaveen N. Rao } 252d2bf7932SNaveen N. Rao 253d2bf7932SNaveen N. Rao return 0; 254d2bf7932SNaveen N. Rao } 255d2bf7932SNaveen N. Rao 256d2bf7932SNaveen N. Rao int perf_event_disable(int fd) 257d2bf7932SNaveen N. Rao { 258d2bf7932SNaveen N. Rao if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { 259d2bf7932SNaveen N. Rao perror("error disabling perf events"); 260d2bf7932SNaveen N. Rao return -1; 261d2bf7932SNaveen N. Rao } 262d2bf7932SNaveen N. Rao 263d2bf7932SNaveen N. Rao return 0; 264d2bf7932SNaveen N. Rao } 265d2bf7932SNaveen N. Rao 266d2bf7932SNaveen N. Rao int perf_event_reset(int fd) 267d2bf7932SNaveen N. Rao { 268d2bf7932SNaveen N. Rao if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) { 269d2bf7932SNaveen N. Rao perror("error resetting perf events"); 270d2bf7932SNaveen N. Rao return -1; 271d2bf7932SNaveen N. Rao } 272d2bf7932SNaveen N. Rao 273d2bf7932SNaveen N. Rao return 0; 274d2bf7932SNaveen N. Rao } 275d2bf7932SNaveen N. Rao 276d2bf7932SNaveen N. Rao static void sigill_handler(int signr, siginfo_t *info, void *unused) 277d2bf7932SNaveen N. Rao { 278d2bf7932SNaveen N. Rao static int warned = 0; 279d2bf7932SNaveen N. Rao ucontext_t *ctx = (ucontext_t *)unused; 280d2bf7932SNaveen N. Rao unsigned long *pc = &UCONTEXT_NIA(ctx); 281d2bf7932SNaveen N. Rao 2821936f094SNaveen N. Rao /* mtspr 3,RS to check for move to DSCR below */ 2831936f094SNaveen N. Rao if ((*((unsigned int *)*pc) & 0xfc1fffff) == 0x7c0303a6) { 284d2bf7932SNaveen N. Rao if (!warned++) 285d2bf7932SNaveen N. Rao printf("WARNING: Skipping over dscr setup. Consider running 'ppc64_cpu --dscr=1' manually.\n"); 286d2bf7932SNaveen N. Rao *pc += 4; 287d2bf7932SNaveen N. Rao } else { 288d2bf7932SNaveen N. Rao printf("SIGILL at %p\n", pc); 289d2bf7932SNaveen N. Rao abort(); 290d2bf7932SNaveen N. Rao } 291d2bf7932SNaveen N. Rao } 292d2bf7932SNaveen N. Rao 293d2bf7932SNaveen N. Rao void set_dscr(unsigned long val) 294d2bf7932SNaveen N. Rao { 295d2bf7932SNaveen N. Rao static int init = 0; 296d2bf7932SNaveen N. Rao struct sigaction sa; 297d2bf7932SNaveen N. Rao 298d2bf7932SNaveen N. Rao if (!init) { 299d2bf7932SNaveen N. Rao memset(&sa, 0, sizeof(sa)); 300d2bf7932SNaveen N. Rao sa.sa_sigaction = sigill_handler; 301d2bf7932SNaveen N. Rao sa.sa_flags = SA_SIGINFO; 302d2bf7932SNaveen N. Rao if (sigaction(SIGILL, &sa, NULL)) 303d2bf7932SNaveen N. Rao perror("sigill_handler"); 304d2bf7932SNaveen N. Rao init = 1; 305d2bf7932SNaveen N. Rao } 306d2bf7932SNaveen N. Rao 3071936f094SNaveen N. Rao asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR)); 308d2bf7932SNaveen N. Rao } 309c405b738SSandipan Das 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) { 321*34c10334SMichael Ellerman if (!strcmp(line, "MMU : Hash\n") || 322*34c10334SMichael Ellerman !strcmp(line, "platform : Cell\n") || 323*34c10334SMichael 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