18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2014, Michael Ellerman, IBM Corp.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#define _GNU_SOURCE	/* For CPU_ZERO etc. */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <errno.h>
98c2ecf20Sopenharmony_ci#include <sched.h>
108c2ecf20Sopenharmony_ci#include <setjmp.h>
118c2ecf20Sopenharmony_ci#include <stdlib.h>
128c2ecf20Sopenharmony_ci#include <sys/wait.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "utils.h"
158c2ecf20Sopenharmony_ci#include "lib.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ciint bind_to_cpu(int cpu)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	cpu_set_t mask;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	printf("Binding to cpu %d\n", cpu);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	CPU_ZERO(&mask);
258c2ecf20Sopenharmony_ci	CPU_SET(cpu, &mask);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	return sched_setaffinity(0, sizeof(mask), &mask);
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define PARENT_TOKEN	0xAA
318c2ecf20Sopenharmony_ci#define CHILD_TOKEN	0x55
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ciint sync_with_child(union pipe read_pipe, union pipe write_pipe)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	char c = PARENT_TOKEN;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
388c2ecf20Sopenharmony_ci	FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
398c2ecf20Sopenharmony_ci	if (c != CHILD_TOKEN) /* sometimes expected */
408c2ecf20Sopenharmony_ci		return 1;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	return 0;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciint wait_for_parent(union pipe read_pipe)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	char c;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
508c2ecf20Sopenharmony_ci	FAIL_IF(c != PARENT_TOKEN);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return 0;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ciint notify_parent(union pipe write_pipe)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	char c = CHILD_TOKEN;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ciint notify_parent_of_error(union pipe write_pipe)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	char c = ~CHILD_TOKEN;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return 0;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ciint wait_for_child(pid_t child_pid)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	int rc;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (waitpid(child_pid, &rc, 0) == -1) {
788c2ecf20Sopenharmony_ci		perror("waitpid");
798c2ecf20Sopenharmony_ci		return 1;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (WIFEXITED(rc))
838c2ecf20Sopenharmony_ci		rc = WEXITSTATUS(rc);
848c2ecf20Sopenharmony_ci	else
858c2ecf20Sopenharmony_ci		rc = 1; /* Signal or other */
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return rc;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ciint kill_child_and_wait(pid_t child_pid)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	kill(child_pid, SIGTERM);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	return wait_for_child(child_pid);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	volatile int i = 0;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/*
1028c2ecf20Sopenharmony_ci	 * We are just here to eat cpu and die. So make sure we can be killed,
1038c2ecf20Sopenharmony_ci	 * and also don't do any custom SIGTERM handling.
1048c2ecf20Sopenharmony_ci	 */
1058c2ecf20Sopenharmony_ci	signal(SIGTERM, SIG_DFL);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	notify_parent(write_pipe);
1088c2ecf20Sopenharmony_ci	wait_for_parent(read_pipe);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* Soak up cpu forever */
1118c2ecf20Sopenharmony_ci	while (1) i++;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return 0;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cipid_t eat_cpu(int (test_function)(void))
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	union pipe read_pipe, write_pipe;
1198c2ecf20Sopenharmony_ci	int cpu, rc;
1208c2ecf20Sopenharmony_ci	pid_t pid;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	cpu = pick_online_cpu();
1238c2ecf20Sopenharmony_ci	FAIL_IF(cpu < 0);
1248c2ecf20Sopenharmony_ci	FAIL_IF(bind_to_cpu(cpu));
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (pipe(read_pipe.fds) == -1)
1278c2ecf20Sopenharmony_ci		return -1;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (pipe(write_pipe.fds) == -1)
1308c2ecf20Sopenharmony_ci		return -1;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	pid = fork();
1338c2ecf20Sopenharmony_ci	if (pid == 0)
1348c2ecf20Sopenharmony_ci		exit(eat_cpu_child(write_pipe, read_pipe));
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (sync_with_child(read_pipe, write_pipe)) {
1378c2ecf20Sopenharmony_ci		rc = -1;
1388c2ecf20Sopenharmony_ci		goto out;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	printf("main test running as pid %d\n", getpid());
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	rc = test_function();
1448c2ecf20Sopenharmony_ciout:
1458c2ecf20Sopenharmony_ci	kill(pid, SIGKILL);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return rc;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistruct addr_range libc, vdso;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ciint parse_proc_maps(void)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	unsigned long start, end;
1558c2ecf20Sopenharmony_ci	char execute, name[128];
1568c2ecf20Sopenharmony_ci	FILE *f;
1578c2ecf20Sopenharmony_ci	int rc;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	f = fopen("/proc/self/maps", "r");
1608c2ecf20Sopenharmony_ci	if (!f) {
1618c2ecf20Sopenharmony_ci		perror("fopen");
1628c2ecf20Sopenharmony_ci		return -1;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	do {
1668c2ecf20Sopenharmony_ci		/* This skips line with no executable which is what we want */
1678c2ecf20Sopenharmony_ci		rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
1688c2ecf20Sopenharmony_ci			    &start, &end, &execute, name);
1698c2ecf20Sopenharmony_ci		if (rc <= 0)
1708c2ecf20Sopenharmony_ci			break;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		if (execute != 'x')
1738c2ecf20Sopenharmony_ci			continue;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		if (strstr(name, "libc")) {
1768c2ecf20Sopenharmony_ci			libc.first = start;
1778c2ecf20Sopenharmony_ci			libc.last = end - 1;
1788c2ecf20Sopenharmony_ci		} else if (strstr(name, "[vdso]")) {
1798c2ecf20Sopenharmony_ci			vdso.first = start;
1808c2ecf20Sopenharmony_ci			vdso.last = end - 1;
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci	} while(1);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	fclose(f);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return 0;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci#define PARANOID_PATH	"/proc/sys/kernel/perf_event_paranoid"
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cibool require_paranoia_below(int level)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	long current;
1948c2ecf20Sopenharmony_ci	char *end, buf[16];
1958c2ecf20Sopenharmony_ci	FILE *f;
1968c2ecf20Sopenharmony_ci	bool rc;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	rc = false;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	f = fopen(PARANOID_PATH, "r");
2018c2ecf20Sopenharmony_ci	if (!f) {
2028c2ecf20Sopenharmony_ci		perror("fopen");
2038c2ecf20Sopenharmony_ci		goto out;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (!fgets(buf, sizeof(buf), f)) {
2078c2ecf20Sopenharmony_ci		printf("Couldn't read " PARANOID_PATH "?\n");
2088c2ecf20Sopenharmony_ci		goto out_close;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	current = strtol(buf, &end, 10);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (end == buf) {
2148c2ecf20Sopenharmony_ci		printf("Couldn't parse " PARANOID_PATH "?\n");
2158c2ecf20Sopenharmony_ci		goto out_close;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (current >= level)
2198c2ecf20Sopenharmony_ci		goto out_close;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	rc = true;
2228c2ecf20Sopenharmony_ciout_close:
2238c2ecf20Sopenharmony_ci	fclose(f);
2248c2ecf20Sopenharmony_ciout:
2258c2ecf20Sopenharmony_ci	return rc;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
228