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