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 <inttypes.h> 12 #include <limits.h> 13 #include <link.h> 14 #include <sched.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <sys/ioctl.h> 19 #include <sys/stat.h> 20 #include <sys/sysinfo.h> 21 #include <sys/types.h> 22 #include <sys/utsname.h> 23 #include <unistd.h> 24 #include <asm/unistd.h> 25 #include <linux/limits.h> 26 27 #include "utils.h" 28 29 static char auxv[4096]; 30 31 int read_file(const char *path, char *buf, size_t count, size_t *len) 32 { 33 ssize_t rc; 34 int fd; 35 int err; 36 char eof; 37 38 fd = open(path, O_RDONLY); 39 if (fd < 0) 40 return -errno; 41 42 rc = read(fd, buf, count); 43 if (rc < 0) { 44 err = -errno; 45 goto out; 46 } 47 48 if (len) 49 *len = rc; 50 51 /* Overflow if there are still more bytes after filling the buffer */ 52 if (rc == count) { 53 rc = read(fd, &eof, 1); 54 if (rc != 0) { 55 err = -EOVERFLOW; 56 goto out; 57 } 58 } 59 60 err = 0; 61 62 out: 63 close(fd); 64 errno = -err; 65 return err; 66 } 67 68 int write_file(const char *path, const char *buf, size_t count) 69 { 70 int fd; 71 int err; 72 ssize_t rc; 73 74 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); 75 if (fd < 0) 76 return -errno; 77 78 rc = write(fd, buf, count); 79 if (rc < 0) { 80 err = -errno; 81 goto out; 82 } 83 84 if (rc != count) { 85 err = -EOVERFLOW; 86 goto out; 87 } 88 89 err = 0; 90 91 out: 92 close(fd); 93 errno = -err; 94 return err; 95 } 96 97 int read_auxv(char *buf, ssize_t buf_size) 98 { 99 int err; 100 101 err = read_file("/proc/self/auxv", buf, buf_size, NULL); 102 if (err) { 103 perror("Error reading /proc/self/auxv"); 104 return err; 105 } 106 107 return 0; 108 } 109 110 int read_debugfs_file(const char *subpath, char *buf, size_t count) 111 { 112 char path[PATH_MAX] = "/sys/kernel/debug/"; 113 114 strncat(path, subpath, sizeof(path) - strlen(path) - 1); 115 116 return read_file(path, buf, count, NULL); 117 } 118 119 int write_debugfs_file(const char *subpath, const char *buf, size_t count) 120 { 121 char path[PATH_MAX] = "/sys/kernel/debug/"; 122 123 strncat(path, subpath, sizeof(path) - strlen(path) - 1); 124 125 return write_file(path, buf, count); 126 } 127 128 static int validate_int_parse(const char *buffer, size_t count, char *end) 129 { 130 int err = 0; 131 132 /* Require at least one digit */ 133 if (end == buffer) { 134 err = -EINVAL; 135 goto out; 136 } 137 138 /* Require all remaining characters be whitespace-ish */ 139 for (; end < buffer + count; end++) { 140 if (*end == '\0') 141 break; 142 143 if (*end != ' ' && *end != '\n') { 144 err = -EINVAL; 145 goto out; 146 } 147 } 148 149 out: 150 errno = -err; 151 return err; 152 } 153 154 static int parse_bounded_int(const char *buffer, size_t count, intmax_t *result, 155 int base, intmax_t min, intmax_t max) 156 { 157 int err; 158 char *end; 159 160 errno = 0; 161 *result = strtoimax(buffer, &end, base); 162 163 if (errno) 164 return -errno; 165 166 err = validate_int_parse(buffer, count, end); 167 if (err) 168 goto out; 169 170 if (*result < min || *result > max) 171 err = -EOVERFLOW; 172 173 out: 174 errno = -err; 175 return err; 176 } 177 178 static int parse_bounded_uint(const char *buffer, size_t count, uintmax_t *result, 179 int base, uintmax_t max) 180 { 181 int err = 0; 182 char *end; 183 184 errno = 0; 185 *result = strtoumax(buffer, &end, base); 186 187 if (errno) 188 return -errno; 189 190 err = validate_int_parse(buffer, count, end); 191 if (err) 192 goto out; 193 194 if (*result > max) 195 err = -EOVERFLOW; 196 197 out: 198 errno = -err; 199 return err; 200 } 201 202 int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base) 203 { 204 return parse_bounded_int(buffer, count, result, base, INTMAX_MIN, INTMAX_MAX); 205 } 206 207 int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base) 208 { 209 return parse_bounded_uint(buffer, count, result, base, UINTMAX_MAX); 210 } 211 212 int parse_int(const char *buffer, size_t count, int *result, int base) 213 { 214 intmax_t parsed; 215 int err = parse_bounded_int(buffer, count, &parsed, base, INT_MIN, INT_MAX); 216 217 *result = parsed; 218 return err; 219 } 220 221 int parse_uint(const char *buffer, size_t count, unsigned int *result, int base) 222 { 223 uintmax_t parsed; 224 int err = parse_bounded_uint(buffer, count, &parsed, base, UINT_MAX); 225 226 *result = parsed; 227 return err; 228 } 229 230 int parse_long(const char *buffer, size_t count, long *result, int base) 231 { 232 intmax_t parsed; 233 int err = parse_bounded_int(buffer, count, &parsed, base, LONG_MIN, LONG_MAX); 234 235 *result = parsed; 236 return err; 237 } 238 239 int parse_ulong(const char *buffer, size_t count, unsigned long *result, int base) 240 { 241 uintmax_t parsed; 242 int err = parse_bounded_uint(buffer, count, &parsed, base, ULONG_MAX); 243 244 *result = parsed; 245 return err; 246 } 247 248 void *find_auxv_entry(int type, char *auxv) 249 { 250 ElfW(auxv_t) *p; 251 252 p = (ElfW(auxv_t) *)auxv; 253 254 while (p->a_type != AT_NULL) { 255 if (p->a_type == type) 256 return p; 257 258 p++; 259 } 260 261 return NULL; 262 } 263 264 void *get_auxv_entry(int type) 265 { 266 ElfW(auxv_t) *p; 267 268 if (read_auxv(auxv, sizeof(auxv))) 269 return NULL; 270 271 p = find_auxv_entry(type, auxv); 272 if (p) 273 return (void *)p->a_un.a_val; 274 275 return NULL; 276 } 277 278 int pick_online_cpu(void) 279 { 280 int ncpus, cpu = -1; 281 cpu_set_t *mask; 282 size_t size; 283 284 ncpus = get_nprocs_conf(); 285 size = CPU_ALLOC_SIZE(ncpus); 286 mask = CPU_ALLOC(ncpus); 287 if (!mask) { 288 perror("malloc"); 289 return -1; 290 } 291 292 CPU_ZERO_S(size, mask); 293 294 if (sched_getaffinity(0, size, mask)) { 295 perror("sched_getaffinity"); 296 goto done; 297 } 298 299 /* We prefer a primary thread, but skip 0 */ 300 for (cpu = 8; cpu < ncpus; cpu += 8) 301 if (CPU_ISSET_S(cpu, size, mask)) 302 goto done; 303 304 /* Search for anything, but in reverse */ 305 for (cpu = ncpus - 1; cpu >= 0; cpu--) 306 if (CPU_ISSET_S(cpu, size, mask)) 307 goto done; 308 309 printf("No cpus in affinity mask?!\n"); 310 311 done: 312 CPU_FREE(mask); 313 return cpu; 314 } 315 316 bool is_ppc64le(void) 317 { 318 struct utsname uts; 319 int rc; 320 321 errno = 0; 322 rc = uname(&uts); 323 if (rc) { 324 perror("uname"); 325 return false; 326 } 327 328 return strcmp(uts.machine, "ppc64le") == 0; 329 } 330 331 int read_sysfs_file(char *fpath, char *result, size_t result_size) 332 { 333 char path[PATH_MAX] = "/sys/"; 334 335 strncat(path, fpath, PATH_MAX - strlen(path) - 1); 336 337 return read_file(path, result, result_size, NULL); 338 } 339 340 int read_debugfs_int(const char *debugfs_file, int *result) 341 { 342 int err; 343 char value[16] = {0}; 344 345 err = read_debugfs_file(debugfs_file, value, sizeof(value) - 1); 346 if (err) 347 return err; 348 349 return parse_int(value, sizeof(value), result, 10); 350 } 351 352 int write_debugfs_int(const char *debugfs_file, int result) 353 { 354 char value[16]; 355 356 snprintf(value, 16, "%d", result); 357 358 return write_debugfs_file(debugfs_file, value, strlen(value)); 359 } 360 361 static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, 362 int cpu, int group_fd, unsigned long flags) 363 { 364 return syscall(__NR_perf_event_open, hw_event, pid, cpu, 365 group_fd, flags); 366 } 367 368 static void perf_event_attr_init(struct perf_event_attr *event_attr, 369 unsigned int type, 370 unsigned long config) 371 { 372 memset(event_attr, 0, sizeof(*event_attr)); 373 374 event_attr->type = type; 375 event_attr->size = sizeof(struct perf_event_attr); 376 event_attr->config = config; 377 event_attr->read_format = PERF_FORMAT_GROUP; 378 event_attr->disabled = 1; 379 event_attr->exclude_kernel = 1; 380 event_attr->exclude_hv = 1; 381 event_attr->exclude_guest = 1; 382 } 383 384 int perf_event_open_counter(unsigned int type, 385 unsigned long config, int group_fd) 386 { 387 int fd; 388 struct perf_event_attr event_attr; 389 390 perf_event_attr_init(&event_attr, type, config); 391 392 fd = perf_event_open(&event_attr, 0, -1, group_fd, 0); 393 394 if (fd < 0) 395 perror("perf_event_open() failed"); 396 397 return fd; 398 } 399 400 int perf_event_enable(int fd) 401 { 402 if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) { 403 perror("error while enabling perf events"); 404 return -1; 405 } 406 407 return 0; 408 } 409 410 int perf_event_disable(int fd) 411 { 412 if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { 413 perror("error disabling perf events"); 414 return -1; 415 } 416 417 return 0; 418 } 419 420 int perf_event_reset(int fd) 421 { 422 if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) { 423 perror("error resetting perf events"); 424 return -1; 425 } 426 427 return 0; 428 } 429 430 int using_hash_mmu(bool *using_hash) 431 { 432 char line[128]; 433 FILE *f; 434 int rc; 435 436 f = fopen("/proc/cpuinfo", "r"); 437 FAIL_IF(!f); 438 439 rc = 0; 440 while (fgets(line, sizeof(line), f) != NULL) { 441 if (!strcmp(line, "MMU : Hash\n") || 442 !strcmp(line, "platform : Cell\n") || 443 !strcmp(line, "platform : PowerMac\n")) { 444 *using_hash = true; 445 goto out; 446 } 447 448 if (strcmp(line, "MMU : Radix\n") == 0) { 449 *using_hash = false; 450 goto out; 451 } 452 } 453 454 rc = -1; 455 out: 456 fclose(f); 457 return rc; 458 } 459