brintos

brintos / linux-shallow public Read only

0
0
Text · 11.2 KiB · e5f2d87 Raw
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