| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright 2013-2015, Michael Ellerman, IBM Corp.
- */
- #define _GNU_SOURCE /* For CPU_ZERO etc. */
- #include <elf.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <inttypes.h>
- #include <limits.h>
- #include <link.h>
- #include <sched.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <sys/sysinfo.h>
- #include <sys/types.h>
- #include <sys/utsname.h>
- #include <unistd.h>
- #include <asm/unistd.h>
- #include <linux/limits.h>
- #include "utils.h"
- static char auxv[4096];
- int read_file(const char *path, char *buf, size_t count, size_t *len)
- {
- ssize_t rc;
- int fd;
- int err;
- char eof;
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return -errno;
- rc = read(fd, buf, count);
- if (rc < 0) {
- err = -errno;
- goto out;
- }
- if (len)
- *len = rc;
- /* Overflow if there are still more bytes after filling the buffer */
- if (rc == count) {
- rc = read(fd, &eof, 1);
- if (rc != 0) {
- err = -EOVERFLOW;
- goto out;
- }
- }
- err = 0;
- out:
- close(fd);
- errno = -err;
- return err;
- }
- int read_file_alloc(const char *path, char **buf, size_t *len)
- {
- size_t read_offset = 0;
- size_t buffer_len = 0;
- char *buffer = NULL;
- int err;
- int fd;
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return -errno;
- /*
- * We don't use stat & preallocate st_size because some non-files
- * report 0 file size. Instead just dynamically grow the buffer
- * as needed.
- */
- while (1) {
- ssize_t rc;
- if (read_offset >= buffer_len / 2) {
- char *next_buffer;
- buffer_len = buffer_len ? buffer_len * 2 : 4096;
- next_buffer = realloc(buffer, buffer_len);
- if (!next_buffer) {
- err = -errno;
- goto out;
- }
- buffer = next_buffer;
- }
- rc = read(fd, buffer + read_offset, buffer_len - read_offset);
- if (rc < 0) {
- err = -errno;
- goto out;
- }
- if (rc == 0)
- break;
- read_offset += rc;
- }
- *buf = buffer;
- if (len)
- *len = read_offset;
- err = 0;
- out:
- close(fd);
- if (err)
- free(buffer);
- errno = -err;
- return err;
- }
- int write_file(const char *path, const char *buf, size_t count)
- {
- int fd;
- int err;
- ssize_t rc;
- fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- if (fd < 0)
- return -errno;
- rc = write(fd, buf, count);
- if (rc < 0) {
- err = -errno;
- goto out;
- }
- if (rc != count) {
- err = -EOVERFLOW;
- goto out;
- }
- err = 0;
- out:
- close(fd);
- errno = -err;
- return err;
- }
- int read_auxv(char *buf, ssize_t buf_size)
- {
- int err;
- err = read_file("/proc/self/auxv", buf, buf_size, NULL);
- if (err) {
- perror("Error reading /proc/self/auxv");
- return err;
- }
- return 0;
- }
- int read_debugfs_file(const char *subpath, char *buf, size_t count)
- {
- char path[PATH_MAX] = "/sys/kernel/debug/";
- strncat(path, subpath, sizeof(path) - strlen(path) - 1);
- return read_file(path, buf, count, NULL);
- }
- int write_debugfs_file(const char *subpath, const char *buf, size_t count)
- {
- char path[PATH_MAX] = "/sys/kernel/debug/";
- strncat(path, subpath, sizeof(path) - strlen(path) - 1);
- return write_file(path, buf, count);
- }
- static int validate_int_parse(const char *buffer, size_t count, char *end)
- {
- int err = 0;
- /* Require at least one digit */
- if (end == buffer) {
- err = -EINVAL;
- goto out;
- }
- /* Require all remaining characters be whitespace-ish */
- for (; end < buffer + count; end++) {
- if (*end == '\0')
- break;
- if (*end != ' ' && *end != '\n') {
- err = -EINVAL;
- goto out;
- }
- }
- out:
- errno = -err;
- return err;
- }
- static int parse_bounded_int(const char *buffer, size_t count, intmax_t *result,
- int base, intmax_t min, intmax_t max)
- {
- int err;
- char *end;
- errno = 0;
- *result = strtoimax(buffer, &end, base);
- if (errno)
- return -errno;
- err = validate_int_parse(buffer, count, end);
- if (err)
- goto out;
- if (*result < min || *result > max)
- err = -EOVERFLOW;
- out:
- errno = -err;
- return err;
- }
- static int parse_bounded_uint(const char *buffer, size_t count, uintmax_t *result,
- int base, uintmax_t max)
- {
- int err = 0;
- char *end;
- errno = 0;
- *result = strtoumax(buffer, &end, base);
- if (errno)
- return -errno;
- err = validate_int_parse(buffer, count, end);
- if (err)
- goto out;
- if (*result > max)
- err = -EOVERFLOW;
- out:
- errno = -err;
- return err;
- }
- int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base)
- {
- return parse_bounded_int(buffer, count, result, base, INTMAX_MIN, INTMAX_MAX);
- }
- int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base)
- {
- return parse_bounded_uint(buffer, count, result, base, UINTMAX_MAX);
- }
- int parse_int(const char *buffer, size_t count, int *result, int base)
- {
- intmax_t parsed;
- int err = parse_bounded_int(buffer, count, &parsed, base, INT_MIN, INT_MAX);
- *result = parsed;
- return err;
- }
- int parse_uint(const char *buffer, size_t count, unsigned int *result, int base)
- {
- uintmax_t parsed;
- int err = parse_bounded_uint(buffer, count, &parsed, base, UINT_MAX);
- *result = parsed;
- return err;
- }
- int parse_long(const char *buffer, size_t count, long *result, int base)
- {
- intmax_t parsed;
- int err = parse_bounded_int(buffer, count, &parsed, base, LONG_MIN, LONG_MAX);
- *result = parsed;
- return err;
- }
- int parse_ulong(const char *buffer, size_t count, unsigned long *result, int base)
- {
- uintmax_t parsed;
- int err = parse_bounded_uint(buffer, count, &parsed, base, ULONG_MAX);
- *result = parsed;
- return err;
- }
- int read_long(const char *path, long *result, int base)
- {
- int err;
- char buffer[32] = {0};
- err = read_file(path, buffer, sizeof(buffer) - 1, NULL);
- if (err)
- return err;
- return parse_long(buffer, sizeof(buffer), result, base);
- }
- int read_ulong(const char *path, unsigned long *result, int base)
- {
- int err;
- char buffer[32] = {0};
- err = read_file(path, buffer, sizeof(buffer) - 1, NULL);
- if (err)
- return err;
- return parse_ulong(buffer, sizeof(buffer), result, base);
- }
- int write_long(const char *path, long result, int base)
- {
- int err;
- int len;
- char buffer[32];
- /* Decimal only for now: no format specifier for signed hex values */
- if (base != 10) {
- err = -EINVAL;
- goto out;
- }
- len = snprintf(buffer, sizeof(buffer), "%ld", result);
- if (len < 0 || len >= sizeof(buffer)) {
- err = -EOVERFLOW;
- goto out;
- }
- err = write_file(path, buffer, len);
- out:
- errno = -err;
- return err;
- }
- int write_ulong(const char *path, unsigned long result, int base)
- {
- int err;
- int len;
- char buffer[32];
- char *fmt;
- switch (base) {
- case 10:
- fmt = "%lu";
- break;
- case 16:
- fmt = "%lx";
- break;
- default:
- err = -EINVAL;
- goto out;
- }
- len = snprintf(buffer, sizeof(buffer), fmt, result);
- if (len < 0 || len >= sizeof(buffer)) {
- err = -errno;
- goto out;
- }
- err = write_file(path, buffer, len);
- out:
- errno = -err;
- return err;
- }
- void *find_auxv_entry(int type, char *auxv)
- {
- ElfW(auxv_t) *p;
- p = (ElfW(auxv_t) *)auxv;
- while (p->a_type != AT_NULL) {
- if (p->a_type == type)
- return p;
- p++;
- }
- return NULL;
- }
- void *get_auxv_entry(int type)
- {
- ElfW(auxv_t) *p;
- if (read_auxv(auxv, sizeof(auxv)))
- return NULL;
- p = find_auxv_entry(type, auxv);
- if (p)
- return (void *)p->a_un.a_val;
- return NULL;
- }
- int pick_online_cpu(void)
- {
- int ncpus, cpu = -1;
- cpu_set_t *mask;
- size_t size;
- ncpus = get_nprocs_conf();
- size = CPU_ALLOC_SIZE(ncpus);
- mask = CPU_ALLOC(ncpus);
- if (!mask) {
- perror("malloc");
- return -1;
- }
- CPU_ZERO_S(size, mask);
- if (sched_getaffinity(0, size, mask)) {
- perror("sched_getaffinity");
- goto done;
- }
- /* We prefer a primary thread, but skip 0 */
- for (cpu = 8; cpu < ncpus; cpu += 8)
- if (CPU_ISSET_S(cpu, size, mask))
- goto done;
- /* Search for anything, but in reverse */
- for (cpu = ncpus - 1; cpu >= 0; cpu--)
- if (CPU_ISSET_S(cpu, size, mask))
- goto done;
- printf("No cpus in affinity mask?!\n");
- done:
- CPU_FREE(mask);
- return cpu;
- }
- int bind_to_cpu(int cpu)
- {
- cpu_set_t mask;
- int err;
- if (cpu == BIND_CPU_ANY) {
- cpu = pick_online_cpu();
- if (cpu < 0)
- return cpu;
- }
- printf("Binding to cpu %d\n", cpu);
- CPU_ZERO(&mask);
- CPU_SET(cpu, &mask);
- err = sched_setaffinity(0, sizeof(mask), &mask);
- if (err)
- return err;
- return cpu;
- }
- bool is_ppc64le(void)
- {
- struct utsname uts;
- int rc;
- errno = 0;
- rc = uname(&uts);
- if (rc) {
- perror("uname");
- return false;
- }
- return strcmp(uts.machine, "ppc64le") == 0;
- }
- int read_sysfs_file(char *fpath, char *result, size_t result_size)
- {
- char path[PATH_MAX] = "/sys/";
- strncat(path, fpath, PATH_MAX - strlen(path) - 1);
- return read_file(path, result, result_size, NULL);
- }
- int read_debugfs_int(const char *debugfs_file, int *result)
- {
- int err;
- char value[16] = {0};
- err = read_debugfs_file(debugfs_file, value, sizeof(value) - 1);
- if (err)
- return err;
- return parse_int(value, sizeof(value), result, 10);
- }
- int write_debugfs_int(const char *debugfs_file, int result)
- {
- char value[16];
- snprintf(value, 16, "%d", result);
- return write_debugfs_file(debugfs_file, value, strlen(value));
- }
- static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
- int cpu, int group_fd, unsigned long flags)
- {
- return syscall(__NR_perf_event_open, hw_event, pid, cpu,
- group_fd, flags);
- }
- static void perf_event_attr_init(struct perf_event_attr *event_attr,
- unsigned int type,
- unsigned long config)
- {
- memset(event_attr, 0, sizeof(*event_attr));
- event_attr->type = type;
- event_attr->size = sizeof(struct perf_event_attr);
- event_attr->config = config;
- event_attr->read_format = PERF_FORMAT_GROUP;
- event_attr->disabled = 1;
- event_attr->exclude_kernel = 1;
- event_attr->exclude_hv = 1;
- event_attr->exclude_guest = 1;
- }
- int perf_event_open_counter(unsigned int type,
- unsigned long config, int group_fd)
- {
- int fd;
- struct perf_event_attr event_attr;
- perf_event_attr_init(&event_attr, type, config);
- fd = perf_event_open(&event_attr, 0, -1, group_fd, 0);
- if (fd < 0)
- perror("perf_event_open() failed");
- return fd;
- }
- int perf_event_enable(int fd)
- {
- if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
- perror("error while enabling perf events");
- return -1;
- }
- return 0;
- }
- int perf_event_disable(int fd)
- {
- if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
- perror("error disabling perf events");
- return -1;
- }
- return 0;
- }
- int perf_event_reset(int fd)
- {
- if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
- perror("error resetting perf events");
- return -1;
- }
- return 0;
- }
- int using_hash_mmu(bool *using_hash)
- {
- char line[128];
- FILE *f;
- int rc;
- f = fopen("/proc/cpuinfo", "r");
- FAIL_IF(!f);
- rc = 0;
- while (fgets(line, sizeof(line), f) != NULL) {
- if (!strcmp(line, "MMU : Hash\n") ||
- !strcmp(line, "platform : Cell\n") ||
- !strcmp(line, "platform : PowerMac\n")) {
- *using_hash = true;
- goto out;
- }
- if (strcmp(line, "MMU : Radix\n") == 0) {
- *using_hash = false;
- goto out;
- }
- }
- rc = -1;
- out:
- fclose(f);
- return rc;
- }
- struct sigaction push_signal_handler(int sig, void (*fn)(int, siginfo_t *, void *))
- {
- struct sigaction sa;
- struct sigaction old_handler;
- sa.sa_sigaction = fn;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_SIGINFO;
- FAIL_IF_EXIT_MSG(sigaction(sig, &sa, &old_handler),
- "failed to push signal handler");
- return old_handler;
- }
- struct sigaction pop_signal_handler(int sig, struct sigaction old_handler)
- {
- struct sigaction popped;
- FAIL_IF_EXIT_MSG(sigaction(sig, &old_handler, &popped),
- "failed to pop signal handler");
- return popped;
- }
|