645 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 29static char auxv[4096];30 31int 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 62out:63 close(fd);64 errno = -err;65 return err;66}67 68int read_file_alloc(const char *path, char **buf, size_t *len)69{70 size_t read_offset = 0;71 size_t buffer_len = 0;72 char *buffer = NULL;73 int err;74 int fd;75 76 fd = open(path, O_RDONLY);77 if (fd < 0)78 return -errno;79 80 /*81 * We don't use stat & preallocate st_size because some non-files82 * report 0 file size. Instead just dynamically grow the buffer83 * as needed.84 */85 while (1) {86 ssize_t rc;87 88 if (read_offset >= buffer_len / 2) {89 char *next_buffer;90 91 buffer_len = buffer_len ? buffer_len * 2 : 4096;92 next_buffer = realloc(buffer, buffer_len);93 if (!next_buffer) {94 err = -errno;95 goto out;96 }97 buffer = next_buffer;98 }99 100 rc = read(fd, buffer + read_offset, buffer_len - read_offset);101 if (rc < 0) {102 err = -errno;103 goto out;104 }105 106 if (rc == 0)107 break;108 109 read_offset += rc;110 }111 112 *buf = buffer;113 if (len)114 *len = read_offset;115 116 err = 0;117 118out:119 close(fd);120 if (err)121 free(buffer);122 errno = -err;123 return err;124}125 126int write_file(const char *path, const char *buf, size_t count)127{128 int fd;129 int err;130 ssize_t rc;131 132 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);133 if (fd < 0)134 return -errno;135 136 rc = write(fd, buf, count);137 if (rc < 0) {138 err = -errno;139 goto out;140 }141 142 if (rc != count) {143 err = -EOVERFLOW;144 goto out;145 }146 147 err = 0;148 149out:150 close(fd);151 errno = -err;152 return err;153}154 155int read_auxv(char *buf, ssize_t buf_size)156{157 int err;158 159 err = read_file("/proc/self/auxv", buf, buf_size, NULL);160 if (err) {161 perror("Error reading /proc/self/auxv");162 return err;163 }164 165 return 0;166}167 168int read_debugfs_file(const char *subpath, char *buf, size_t count)169{170 char path[PATH_MAX] = "/sys/kernel/debug/";171 172 strncat(path, subpath, sizeof(path) - strlen(path) - 1);173 174 return read_file(path, buf, count, NULL);175}176 177int write_debugfs_file(const char *subpath, const char *buf, size_t count)178{179 char path[PATH_MAX] = "/sys/kernel/debug/";180 181 strncat(path, subpath, sizeof(path) - strlen(path) - 1);182 183 return write_file(path, buf, count);184}185 186static int validate_int_parse(const char *buffer, size_t count, char *end)187{188 int err = 0;189 190 /* Require at least one digit */191 if (end == buffer) {192 err = -EINVAL;193 goto out;194 }195 196 /* Require all remaining characters be whitespace-ish */197 for (; end < buffer + count; end++) {198 if (*end == '\0')199 break;200 201 if (*end != ' ' && *end != '\n') {202 err = -EINVAL;203 goto out;204 }205 }206 207out:208 errno = -err;209 return err;210}211 212static int parse_bounded_int(const char *buffer, size_t count, intmax_t *result,213 int base, intmax_t min, intmax_t max)214{215 int err;216 char *end;217 218 errno = 0;219 *result = strtoimax(buffer, &end, base);220 221 if (errno)222 return -errno;223 224 err = validate_int_parse(buffer, count, end);225 if (err)226 goto out;227 228 if (*result < min || *result > max)229 err = -EOVERFLOW;230 231out:232 errno = -err;233 return err;234}235 236static int parse_bounded_uint(const char *buffer, size_t count, uintmax_t *result,237 int base, uintmax_t max)238{239 int err = 0;240 char *end;241 242 errno = 0;243 *result = strtoumax(buffer, &end, base);244 245 if (errno)246 return -errno;247 248 err = validate_int_parse(buffer, count, end);249 if (err)250 goto out;251 252 if (*result > max)253 err = -EOVERFLOW;254 255out:256 errno = -err;257 return err;258}259 260int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base)261{262 return parse_bounded_int(buffer, count, result, base, INTMAX_MIN, INTMAX_MAX);263}264 265int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base)266{267 return parse_bounded_uint(buffer, count, result, base, UINTMAX_MAX);268}269 270int parse_int(const char *buffer, size_t count, int *result, int base)271{272 intmax_t parsed;273 int err = parse_bounded_int(buffer, count, &parsed, base, INT_MIN, INT_MAX);274 275 *result = parsed;276 return err;277}278 279int parse_uint(const char *buffer, size_t count, unsigned int *result, int base)280{281 uintmax_t parsed;282 int err = parse_bounded_uint(buffer, count, &parsed, base, UINT_MAX);283 284 *result = parsed;285 return err;286}287 288int parse_long(const char *buffer, size_t count, long *result, int base)289{290 intmax_t parsed;291 int err = parse_bounded_int(buffer, count, &parsed, base, LONG_MIN, LONG_MAX);292 293 *result = parsed;294 return err;295}296 297int parse_ulong(const char *buffer, size_t count, unsigned long *result, int base)298{299 uintmax_t parsed;300 int err = parse_bounded_uint(buffer, count, &parsed, base, ULONG_MAX);301 302 *result = parsed;303 return err;304}305 306int read_long(const char *path, long *result, int base)307{308 int err;309 char buffer[32] = {0};310 311 err = read_file(path, buffer, sizeof(buffer) - 1, NULL);312 if (err)313 return err;314 315 return parse_long(buffer, sizeof(buffer), result, base);316}317 318int read_ulong(const char *path, unsigned long *result, int base)319{320 int err;321 char buffer[32] = {0};322 323 err = read_file(path, buffer, sizeof(buffer) - 1, NULL);324 if (err)325 return err;326 327 return parse_ulong(buffer, sizeof(buffer), result, base);328}329 330int write_long(const char *path, long result, int base)331{332 int err;333 int len;334 char buffer[32];335 336 /* Decimal only for now: no format specifier for signed hex values */337 if (base != 10) {338 err = -EINVAL;339 goto out;340 }341 342 len = snprintf(buffer, sizeof(buffer), "%ld", result);343 if (len < 0 || len >= sizeof(buffer)) {344 err = -EOVERFLOW;345 goto out;346 }347 348 err = write_file(path, buffer, len);349 350out:351 errno = -err;352 return err;353}354 355int write_ulong(const char *path, unsigned long result, int base)356{357 int err;358 int len;359 char buffer[32];360 char *fmt;361 362 switch (base) {363 case 10:364 fmt = "%lu";365 break;366 case 16:367 fmt = "%lx";368 break;369 default:370 err = -EINVAL;371 goto out;372 }373 374 len = snprintf(buffer, sizeof(buffer), fmt, result);375 if (len < 0 || len >= sizeof(buffer)) {376 err = -errno;377 goto out;378 }379 380 err = write_file(path, buffer, len);381 382out:383 errno = -err;384 return err;385}386 387void *find_auxv_entry(int type, char *auxv)388{389 ElfW(auxv_t) *p;390 391 p = (ElfW(auxv_t) *)auxv;392 393 while (p->a_type != AT_NULL) {394 if (p->a_type == type)395 return p;396 397 p++;398 }399 400 return NULL;401}402 403void *get_auxv_entry(int type)404{405 ElfW(auxv_t) *p;406 407 if (read_auxv(auxv, sizeof(auxv)))408 return NULL;409 410 p = find_auxv_entry(type, auxv);411 if (p)412 return (void *)p->a_un.a_val;413 414 return NULL;415}416 417int pick_online_cpu(void)418{419 int ncpus, cpu = -1;420 cpu_set_t *mask;421 size_t size;422 423 ncpus = get_nprocs_conf();424 size = CPU_ALLOC_SIZE(ncpus);425 mask = CPU_ALLOC(ncpus);426 if (!mask) {427 perror("malloc");428 return -1;429 }430 431 CPU_ZERO_S(size, mask);432 433 if (sched_getaffinity(0, size, mask)) {434 perror("sched_getaffinity");435 goto done;436 }437 438 /* We prefer a primary thread, but skip 0 */439 for (cpu = 8; cpu < ncpus; cpu += 8)440 if (CPU_ISSET_S(cpu, size, mask))441 goto done;442 443 /* Search for anything, but in reverse */444 for (cpu = ncpus - 1; cpu >= 0; cpu--)445 if (CPU_ISSET_S(cpu, size, mask))446 goto done;447 448 printf("No cpus in affinity mask?!\n");449 450done:451 CPU_FREE(mask);452 return cpu;453}454 455int bind_to_cpu(int cpu)456{457 cpu_set_t mask;458 int err;459 460 if (cpu == BIND_CPU_ANY) {461 cpu = pick_online_cpu();462 if (cpu < 0)463 return cpu;464 }465 466 printf("Binding to cpu %d\n", cpu);467 468 CPU_ZERO(&mask);469 CPU_SET(cpu, &mask);470 471 err = sched_setaffinity(0, sizeof(mask), &mask);472 if (err)473 return err;474 475 return cpu;476}477 478bool is_ppc64le(void)479{480 struct utsname uts;481 int rc;482 483 errno = 0;484 rc = uname(&uts);485 if (rc) {486 perror("uname");487 return false;488 }489 490 return strcmp(uts.machine, "ppc64le") == 0;491}492 493int read_sysfs_file(char *fpath, char *result, size_t result_size)494{495 char path[PATH_MAX] = "/sys/";496 497 strncat(path, fpath, PATH_MAX - strlen(path) - 1);498 499 return read_file(path, result, result_size, NULL);500}501 502int read_debugfs_int(const char *debugfs_file, int *result)503{504 int err;505 char value[16] = {0};506 507 err = read_debugfs_file(debugfs_file, value, sizeof(value) - 1);508 if (err)509 return err;510 511 return parse_int(value, sizeof(value), result, 10);512}513 514int write_debugfs_int(const char *debugfs_file, int result)515{516 char value[16];517 518 snprintf(value, 16, "%d", result);519 520 return write_debugfs_file(debugfs_file, value, strlen(value));521}522 523static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,524 int cpu, int group_fd, unsigned long flags)525{526 return syscall(__NR_perf_event_open, hw_event, pid, cpu,527 group_fd, flags);528}529 530static void perf_event_attr_init(struct perf_event_attr *event_attr,531 unsigned int type,532 unsigned long config)533{534 memset(event_attr, 0, sizeof(*event_attr));535 536 event_attr->type = type;537 event_attr->size = sizeof(struct perf_event_attr);538 event_attr->config = config;539 event_attr->read_format = PERF_FORMAT_GROUP;540 event_attr->disabled = 1;541 event_attr->exclude_kernel = 1;542 event_attr->exclude_hv = 1;543 event_attr->exclude_guest = 1;544}545 546int perf_event_open_counter(unsigned int type,547 unsigned long config, int group_fd)548{549 int fd;550 struct perf_event_attr event_attr;551 552 perf_event_attr_init(&event_attr, type, config);553 554 fd = perf_event_open(&event_attr, 0, -1, group_fd, 0);555 556 if (fd < 0)557 perror("perf_event_open() failed");558 559 return fd;560}561 562int perf_event_enable(int fd)563{564 if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {565 perror("error while enabling perf events");566 return -1;567 }568 569 return 0;570}571 572int perf_event_disable(int fd)573{574 if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {575 perror("error disabling perf events");576 return -1;577 }578 579 return 0;580}581 582int perf_event_reset(int fd)583{584 if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {585 perror("error resetting perf events");586 return -1;587 }588 589 return 0;590}591 592int using_hash_mmu(bool *using_hash)593{594 char line[128];595 FILE *f;596 int rc;597 598 f = fopen("/proc/cpuinfo", "r");599 FAIL_IF(!f);600 601 rc = 0;602 while (fgets(line, sizeof(line), f) != NULL) {603 if (!strcmp(line, "MMU : Hash\n") ||604 !strcmp(line, "platform : Cell\n") ||605 !strcmp(line, "platform : PowerMac\n")) {606 *using_hash = true;607 goto out;608 }609 610 if (strcmp(line, "MMU : Radix\n") == 0) {611 *using_hash = false;612 goto out;613 }614 }615 616 rc = -1;617out:618 fclose(f);619 return rc;620}621 622struct sigaction push_signal_handler(int sig, void (*fn)(int, siginfo_t *, void *))623{624 struct sigaction sa;625 struct sigaction old_handler;626 627 sa.sa_sigaction = fn;628 sigemptyset(&sa.sa_mask);629 sa.sa_flags = SA_SIGINFO;630 FAIL_IF_EXIT_MSG(sigaction(sig, &sa, &old_handler),631 "failed to push signal handler");632 633 return old_handler;634}635 636struct sigaction pop_signal_handler(int sig, struct sigaction old_handler)637{638 struct sigaction popped;639 640 FAIL_IF_EXIT_MSG(sigaction(sig, &old_handler, &popped),641 "failed to pop signal handler");642 643 return popped;644}645