162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2013-2015, Michael Ellerman, IBM Corp.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define _GNU_SOURCE	/* For CPU_ZERO etc. */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <elf.h>
962306a36Sopenharmony_ci#include <errno.h>
1062306a36Sopenharmony_ci#include <fcntl.h>
1162306a36Sopenharmony_ci#include <inttypes.h>
1262306a36Sopenharmony_ci#include <limits.h>
1362306a36Sopenharmony_ci#include <link.h>
1462306a36Sopenharmony_ci#include <sched.h>
1562306a36Sopenharmony_ci#include <stdio.h>
1662306a36Sopenharmony_ci#include <stdlib.h>
1762306a36Sopenharmony_ci#include <string.h>
1862306a36Sopenharmony_ci#include <sys/ioctl.h>
1962306a36Sopenharmony_ci#include <sys/stat.h>
2062306a36Sopenharmony_ci#include <sys/sysinfo.h>
2162306a36Sopenharmony_ci#include <sys/types.h>
2262306a36Sopenharmony_ci#include <sys/utsname.h>
2362306a36Sopenharmony_ci#include <unistd.h>
2462306a36Sopenharmony_ci#include <asm/unistd.h>
2562306a36Sopenharmony_ci#include <linux/limits.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "utils.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic char auxv[4096];
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciint read_file(const char *path, char *buf, size_t count, size_t *len)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	ssize_t rc;
3462306a36Sopenharmony_ci	int fd;
3562306a36Sopenharmony_ci	int err;
3662306a36Sopenharmony_ci	char eof;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	fd = open(path, O_RDONLY);
3962306a36Sopenharmony_ci	if (fd < 0)
4062306a36Sopenharmony_ci		return -errno;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	rc = read(fd, buf, count);
4362306a36Sopenharmony_ci	if (rc < 0) {
4462306a36Sopenharmony_ci		err = -errno;
4562306a36Sopenharmony_ci		goto out;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (len)
4962306a36Sopenharmony_ci		*len = rc;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* Overflow if there are still more bytes after filling the buffer */
5262306a36Sopenharmony_ci	if (rc == count) {
5362306a36Sopenharmony_ci		rc = read(fd, &eof, 1);
5462306a36Sopenharmony_ci		if (rc != 0) {
5562306a36Sopenharmony_ci			err = -EOVERFLOW;
5662306a36Sopenharmony_ci			goto out;
5762306a36Sopenharmony_ci		}
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	err = 0;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciout:
6362306a36Sopenharmony_ci	close(fd);
6462306a36Sopenharmony_ci	errno = -err;
6562306a36Sopenharmony_ci	return err;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciint read_file_alloc(const char *path, char **buf, size_t *len)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	size_t read_offset = 0;
7162306a36Sopenharmony_ci	size_t buffer_len = 0;
7262306a36Sopenharmony_ci	char *buffer = NULL;
7362306a36Sopenharmony_ci	int err;
7462306a36Sopenharmony_ci	int fd;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	fd = open(path, O_RDONLY);
7762306a36Sopenharmony_ci	if (fd < 0)
7862306a36Sopenharmony_ci		return -errno;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/*
8162306a36Sopenharmony_ci	 * We don't use stat & preallocate st_size because some non-files
8262306a36Sopenharmony_ci	 * report 0 file size. Instead just dynamically grow the buffer
8362306a36Sopenharmony_ci	 * as needed.
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	while (1) {
8662306a36Sopenharmony_ci		ssize_t rc;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		if (read_offset >= buffer_len / 2) {
8962306a36Sopenharmony_ci			char *next_buffer;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci			buffer_len = buffer_len ? buffer_len * 2 : 4096;
9262306a36Sopenharmony_ci			next_buffer = realloc(buffer, buffer_len);
9362306a36Sopenharmony_ci			if (!next_buffer) {
9462306a36Sopenharmony_ci				err = -errno;
9562306a36Sopenharmony_ci				goto out;
9662306a36Sopenharmony_ci			}
9762306a36Sopenharmony_ci			buffer = next_buffer;
9862306a36Sopenharmony_ci		}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		rc = read(fd, buffer + read_offset, buffer_len - read_offset);
10162306a36Sopenharmony_ci		if (rc < 0) {
10262306a36Sopenharmony_ci			err = -errno;
10362306a36Sopenharmony_ci			goto out;
10462306a36Sopenharmony_ci		}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		if (rc == 0)
10762306a36Sopenharmony_ci			break;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		read_offset += rc;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	*buf = buffer;
11362306a36Sopenharmony_ci	if (len)
11462306a36Sopenharmony_ci		*len = read_offset;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	err = 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ciout:
11962306a36Sopenharmony_ci	close(fd);
12062306a36Sopenharmony_ci	if (err)
12162306a36Sopenharmony_ci		free(buffer);
12262306a36Sopenharmony_ci	errno = -err;
12362306a36Sopenharmony_ci	return err;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciint write_file(const char *path, const char *buf, size_t count)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	int fd;
12962306a36Sopenharmony_ci	int err;
13062306a36Sopenharmony_ci	ssize_t rc;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
13362306a36Sopenharmony_ci	if (fd < 0)
13462306a36Sopenharmony_ci		return -errno;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	rc = write(fd, buf, count);
13762306a36Sopenharmony_ci	if (rc < 0) {
13862306a36Sopenharmony_ci		err = -errno;
13962306a36Sopenharmony_ci		goto out;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (rc != count) {
14362306a36Sopenharmony_ci		err = -EOVERFLOW;
14462306a36Sopenharmony_ci		goto out;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	err = 0;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciout:
15062306a36Sopenharmony_ci	close(fd);
15162306a36Sopenharmony_ci	errno = -err;
15262306a36Sopenharmony_ci	return err;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ciint read_auxv(char *buf, ssize_t buf_size)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	int err;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	err = read_file("/proc/self/auxv", buf, buf_size, NULL);
16062306a36Sopenharmony_ci	if (err) {
16162306a36Sopenharmony_ci		perror("Error reading /proc/self/auxv");
16262306a36Sopenharmony_ci		return err;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciint read_debugfs_file(const char *subpath, char *buf, size_t count)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	char path[PATH_MAX] = "/sys/kernel/debug/";
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	strncat(path, subpath, sizeof(path) - strlen(path) - 1);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return read_file(path, buf, count, NULL);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ciint write_debugfs_file(const char *subpath, const char *buf, size_t count)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	char path[PATH_MAX] = "/sys/kernel/debug/";
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	strncat(path, subpath, sizeof(path) - strlen(path) - 1);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return write_file(path, buf, count);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic int validate_int_parse(const char *buffer, size_t count, char *end)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	int err = 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* Require at least one digit */
19162306a36Sopenharmony_ci	if (end == buffer) {
19262306a36Sopenharmony_ci		err = -EINVAL;
19362306a36Sopenharmony_ci		goto out;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Require all remaining characters be whitespace-ish */
19762306a36Sopenharmony_ci	for (; end < buffer + count; end++) {
19862306a36Sopenharmony_ci		if (*end == '\0')
19962306a36Sopenharmony_ci			break;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		if (*end != ' ' && *end != '\n') {
20262306a36Sopenharmony_ci			err = -EINVAL;
20362306a36Sopenharmony_ci			goto out;
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ciout:
20862306a36Sopenharmony_ci	errno = -err;
20962306a36Sopenharmony_ci	return err;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int parse_bounded_int(const char *buffer, size_t count, intmax_t *result,
21362306a36Sopenharmony_ci			     int base, intmax_t min, intmax_t max)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	int err;
21662306a36Sopenharmony_ci	char *end;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	errno = 0;
21962306a36Sopenharmony_ci	*result = strtoimax(buffer, &end, base);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (errno)
22262306a36Sopenharmony_ci		return -errno;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	err = validate_int_parse(buffer, count, end);
22562306a36Sopenharmony_ci	if (err)
22662306a36Sopenharmony_ci		goto out;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (*result < min || *result > max)
22962306a36Sopenharmony_ci		err = -EOVERFLOW;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ciout:
23262306a36Sopenharmony_ci	errno = -err;
23362306a36Sopenharmony_ci	return err;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int parse_bounded_uint(const char *buffer, size_t count, uintmax_t *result,
23762306a36Sopenharmony_ci			      int base, uintmax_t max)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	int err = 0;
24062306a36Sopenharmony_ci	char *end;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	errno = 0;
24362306a36Sopenharmony_ci	*result = strtoumax(buffer, &end, base);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (errno)
24662306a36Sopenharmony_ci		return -errno;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	err = validate_int_parse(buffer, count, end);
24962306a36Sopenharmony_ci	if (err)
25062306a36Sopenharmony_ci		goto out;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (*result > max)
25362306a36Sopenharmony_ci		err = -EOVERFLOW;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ciout:
25662306a36Sopenharmony_ci	errno = -err;
25762306a36Sopenharmony_ci	return err;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ciint parse_intmax(const char *buffer, size_t count, intmax_t *result, int base)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	return parse_bounded_int(buffer, count, result, base, INTMAX_MIN, INTMAX_MAX);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ciint parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	return parse_bounded_uint(buffer, count, result, base, UINTMAX_MAX);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ciint parse_int(const char *buffer, size_t count, int *result, int base)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	intmax_t parsed;
27362306a36Sopenharmony_ci	int err = parse_bounded_int(buffer, count, &parsed, base, INT_MIN, INT_MAX);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	*result = parsed;
27662306a36Sopenharmony_ci	return err;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ciint parse_uint(const char *buffer, size_t count, unsigned int *result, int base)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	uintmax_t parsed;
28262306a36Sopenharmony_ci	int err = parse_bounded_uint(buffer, count, &parsed, base, UINT_MAX);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	*result = parsed;
28562306a36Sopenharmony_ci	return err;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ciint parse_long(const char *buffer, size_t count, long *result, int base)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	intmax_t parsed;
29162306a36Sopenharmony_ci	int err = parse_bounded_int(buffer, count, &parsed, base, LONG_MIN, LONG_MAX);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	*result = parsed;
29462306a36Sopenharmony_ci	return err;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ciint parse_ulong(const char *buffer, size_t count, unsigned long *result, int base)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	uintmax_t parsed;
30062306a36Sopenharmony_ci	int err = parse_bounded_uint(buffer, count, &parsed, base, ULONG_MAX);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	*result = parsed;
30362306a36Sopenharmony_ci	return err;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ciint read_long(const char *path, long *result, int base)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	int err;
30962306a36Sopenharmony_ci	char buffer[32] = {0};
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	err = read_file(path, buffer, sizeof(buffer) - 1, NULL);
31262306a36Sopenharmony_ci	if (err)
31362306a36Sopenharmony_ci		return err;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return parse_long(buffer, sizeof(buffer), result, base);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ciint read_ulong(const char *path, unsigned long *result, int base)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	int err;
32162306a36Sopenharmony_ci	char buffer[32] = {0};
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	err = read_file(path, buffer, sizeof(buffer) - 1, NULL);
32462306a36Sopenharmony_ci	if (err)
32562306a36Sopenharmony_ci		return err;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return parse_ulong(buffer, sizeof(buffer), result, base);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ciint write_long(const char *path, long result, int base)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	int err;
33362306a36Sopenharmony_ci	int len;
33462306a36Sopenharmony_ci	char buffer[32];
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* Decimal only for now: no format specifier for signed hex values */
33762306a36Sopenharmony_ci	if (base != 10) {
33862306a36Sopenharmony_ci		err = -EINVAL;
33962306a36Sopenharmony_ci		goto out;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	len = snprintf(buffer, sizeof(buffer), "%ld", result);
34362306a36Sopenharmony_ci	if (len < 0 || len >= sizeof(buffer)) {
34462306a36Sopenharmony_ci		err = -EOVERFLOW;
34562306a36Sopenharmony_ci		goto out;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	err = write_file(path, buffer, len);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ciout:
35162306a36Sopenharmony_ci	errno = -err;
35262306a36Sopenharmony_ci	return err;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ciint write_ulong(const char *path, unsigned long result, int base)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	int err;
35862306a36Sopenharmony_ci	int len;
35962306a36Sopenharmony_ci	char buffer[32];
36062306a36Sopenharmony_ci	char *fmt;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	switch (base) {
36362306a36Sopenharmony_ci	case 10:
36462306a36Sopenharmony_ci		fmt = "%lu";
36562306a36Sopenharmony_ci		break;
36662306a36Sopenharmony_ci	case 16:
36762306a36Sopenharmony_ci		fmt = "%lx";
36862306a36Sopenharmony_ci		break;
36962306a36Sopenharmony_ci	default:
37062306a36Sopenharmony_ci		err = -EINVAL;
37162306a36Sopenharmony_ci		goto out;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	len = snprintf(buffer, sizeof(buffer), fmt, result);
37562306a36Sopenharmony_ci	if (len < 0 || len >= sizeof(buffer)) {
37662306a36Sopenharmony_ci		err = -errno;
37762306a36Sopenharmony_ci		goto out;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	err = write_file(path, buffer, len);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ciout:
38362306a36Sopenharmony_ci	errno = -err;
38462306a36Sopenharmony_ci	return err;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_civoid *find_auxv_entry(int type, char *auxv)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	ElfW(auxv_t) *p;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	p = (ElfW(auxv_t) *)auxv;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	while (p->a_type != AT_NULL) {
39462306a36Sopenharmony_ci		if (p->a_type == type)
39562306a36Sopenharmony_ci			return p;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		p++;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return NULL;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_civoid *get_auxv_entry(int type)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	ElfW(auxv_t) *p;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (read_auxv(auxv, sizeof(auxv)))
40862306a36Sopenharmony_ci		return NULL;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	p = find_auxv_entry(type, auxv);
41162306a36Sopenharmony_ci	if (p)
41262306a36Sopenharmony_ci		return (void *)p->a_un.a_val;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	return NULL;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ciint pick_online_cpu(void)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	int ncpus, cpu = -1;
42062306a36Sopenharmony_ci	cpu_set_t *mask;
42162306a36Sopenharmony_ci	size_t size;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	ncpus = get_nprocs_conf();
42462306a36Sopenharmony_ci	size = CPU_ALLOC_SIZE(ncpus);
42562306a36Sopenharmony_ci	mask = CPU_ALLOC(ncpus);
42662306a36Sopenharmony_ci	if (!mask) {
42762306a36Sopenharmony_ci		perror("malloc");
42862306a36Sopenharmony_ci		return -1;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	CPU_ZERO_S(size, mask);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (sched_getaffinity(0, size, mask)) {
43462306a36Sopenharmony_ci		perror("sched_getaffinity");
43562306a36Sopenharmony_ci		goto done;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* We prefer a primary thread, but skip 0 */
43962306a36Sopenharmony_ci	for (cpu = 8; cpu < ncpus; cpu += 8)
44062306a36Sopenharmony_ci		if (CPU_ISSET_S(cpu, size, mask))
44162306a36Sopenharmony_ci			goto done;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/* Search for anything, but in reverse */
44462306a36Sopenharmony_ci	for (cpu = ncpus - 1; cpu >= 0; cpu--)
44562306a36Sopenharmony_ci		if (CPU_ISSET_S(cpu, size, mask))
44662306a36Sopenharmony_ci			goto done;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	printf("No cpus in affinity mask?!\n");
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cidone:
45162306a36Sopenharmony_ci	CPU_FREE(mask);
45262306a36Sopenharmony_ci	return cpu;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ciint bind_to_cpu(int cpu)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	cpu_set_t mask;
45862306a36Sopenharmony_ci	int err;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (cpu == BIND_CPU_ANY) {
46162306a36Sopenharmony_ci		cpu = pick_online_cpu();
46262306a36Sopenharmony_ci		if (cpu < 0)
46362306a36Sopenharmony_ci			return cpu;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	printf("Binding to cpu %d\n", cpu);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	CPU_ZERO(&mask);
46962306a36Sopenharmony_ci	CPU_SET(cpu, &mask);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	err = sched_setaffinity(0, sizeof(mask), &mask);
47262306a36Sopenharmony_ci	if (err)
47362306a36Sopenharmony_ci		return err;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return cpu;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cibool is_ppc64le(void)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct utsname uts;
48162306a36Sopenharmony_ci	int rc;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	errno = 0;
48462306a36Sopenharmony_ci	rc = uname(&uts);
48562306a36Sopenharmony_ci	if (rc) {
48662306a36Sopenharmony_ci		perror("uname");
48762306a36Sopenharmony_ci		return false;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	return strcmp(uts.machine, "ppc64le") == 0;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ciint read_sysfs_file(char *fpath, char *result, size_t result_size)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	char path[PATH_MAX] = "/sys/";
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	strncat(path, fpath, PATH_MAX - strlen(path) - 1);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return read_file(path, result, result_size, NULL);
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ciint read_debugfs_int(const char *debugfs_file, int *result)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	int err;
50562306a36Sopenharmony_ci	char value[16] = {0};
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	err = read_debugfs_file(debugfs_file, value, sizeof(value) - 1);
50862306a36Sopenharmony_ci	if (err)
50962306a36Sopenharmony_ci		return err;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return parse_int(value, sizeof(value), result, 10);
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ciint write_debugfs_int(const char *debugfs_file, int result)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	char value[16];
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	snprintf(value, 16, "%d", result);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	return write_debugfs_file(debugfs_file, value, strlen(value));
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
52462306a36Sopenharmony_ci		int cpu, int group_fd, unsigned long flags)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	return syscall(__NR_perf_event_open, hw_event, pid, cpu,
52762306a36Sopenharmony_ci		      group_fd, flags);
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic void perf_event_attr_init(struct perf_event_attr *event_attr,
53162306a36Sopenharmony_ci					unsigned int type,
53262306a36Sopenharmony_ci					unsigned long config)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	memset(event_attr, 0, sizeof(*event_attr));
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	event_attr->type = type;
53762306a36Sopenharmony_ci	event_attr->size = sizeof(struct perf_event_attr);
53862306a36Sopenharmony_ci	event_attr->config = config;
53962306a36Sopenharmony_ci	event_attr->read_format = PERF_FORMAT_GROUP;
54062306a36Sopenharmony_ci	event_attr->disabled = 1;
54162306a36Sopenharmony_ci	event_attr->exclude_kernel = 1;
54262306a36Sopenharmony_ci	event_attr->exclude_hv = 1;
54362306a36Sopenharmony_ci	event_attr->exclude_guest = 1;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ciint perf_event_open_counter(unsigned int type,
54762306a36Sopenharmony_ci			    unsigned long config, int group_fd)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	int fd;
55062306a36Sopenharmony_ci	struct perf_event_attr event_attr;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	perf_event_attr_init(&event_attr, type, config);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	fd = perf_event_open(&event_attr, 0, -1, group_fd, 0);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (fd < 0)
55762306a36Sopenharmony_ci		perror("perf_event_open() failed");
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return fd;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ciint perf_event_enable(int fd)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
56562306a36Sopenharmony_ci		perror("error while enabling perf events");
56662306a36Sopenharmony_ci		return -1;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	return 0;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ciint perf_event_disable(int fd)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
57562306a36Sopenharmony_ci		perror("error disabling perf events");
57662306a36Sopenharmony_ci		return -1;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return 0;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ciint perf_event_reset(int fd)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
58562306a36Sopenharmony_ci		perror("error resetting perf events");
58662306a36Sopenharmony_ci		return -1;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	return 0;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ciint using_hash_mmu(bool *using_hash)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	char line[128];
59562306a36Sopenharmony_ci	FILE *f;
59662306a36Sopenharmony_ci	int rc;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	f = fopen("/proc/cpuinfo", "r");
59962306a36Sopenharmony_ci	FAIL_IF(!f);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	rc = 0;
60262306a36Sopenharmony_ci	while (fgets(line, sizeof(line), f) != NULL) {
60362306a36Sopenharmony_ci		if (!strcmp(line, "MMU		: Hash\n") ||
60462306a36Sopenharmony_ci		    !strcmp(line, "platform	: Cell\n") ||
60562306a36Sopenharmony_ci		    !strcmp(line, "platform	: PowerMac\n")) {
60662306a36Sopenharmony_ci			*using_hash = true;
60762306a36Sopenharmony_ci			goto out;
60862306a36Sopenharmony_ci		}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		if (strcmp(line, "MMU		: Radix\n") == 0) {
61162306a36Sopenharmony_ci			*using_hash = false;
61262306a36Sopenharmony_ci			goto out;
61362306a36Sopenharmony_ci		}
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	rc = -1;
61762306a36Sopenharmony_ciout:
61862306a36Sopenharmony_ci	fclose(f);
61962306a36Sopenharmony_ci	return rc;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistruct sigaction push_signal_handler(int sig, void (*fn)(int, siginfo_t *, void *))
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct sigaction sa;
62562306a36Sopenharmony_ci	struct sigaction old_handler;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	sa.sa_sigaction = fn;
62862306a36Sopenharmony_ci	sigemptyset(&sa.sa_mask);
62962306a36Sopenharmony_ci	sa.sa_flags = SA_SIGINFO;
63062306a36Sopenharmony_ci	FAIL_IF_EXIT_MSG(sigaction(sig, &sa, &old_handler),
63162306a36Sopenharmony_ci			 "failed to push signal handler");
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	return old_handler;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistruct sigaction pop_signal_handler(int sig, struct sigaction old_handler)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	struct sigaction popped;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	FAIL_IF_EXIT_MSG(sigaction(sig, &old_handler, &popped),
64162306a36Sopenharmony_ci			 "failed to pop signal handler");
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return popped;
64462306a36Sopenharmony_ci}
645