1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2014, Michael Ellerman, IBM Corp.
4  */
5 
6 #define _GNU_SOURCE	/* For CPU_ZERO etc. */
7 
8 #include <errno.h>
9 #include <sched.h>
10 #include <setjmp.h>
11 #include <stdlib.h>
12 #include <sys/wait.h>
13 
14 #include "utils.h"
15 #include "lib.h"
16 
17 #define PARENT_TOKEN	0xAA
18 #define CHILD_TOKEN	0x55
19 
sync_with_child(union pipe read_pipe,union pipe write_pipe)20 int sync_with_child(union pipe read_pipe, union pipe write_pipe)
21 {
22 	char c = PARENT_TOKEN;
23 
24 	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
25 	FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
26 	if (c != CHILD_TOKEN) /* sometimes expected */
27 		return 1;
28 
29 	return 0;
30 }
31 
wait_for_parent(union pipe read_pipe)32 int wait_for_parent(union pipe read_pipe)
33 {
34 	char c;
35 
36 	FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
37 	FAIL_IF(c != PARENT_TOKEN);
38 
39 	return 0;
40 }
41 
notify_parent(union pipe write_pipe)42 int notify_parent(union pipe write_pipe)
43 {
44 	char c = CHILD_TOKEN;
45 
46 	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
47 
48 	return 0;
49 }
50 
notify_parent_of_error(union pipe write_pipe)51 int notify_parent_of_error(union pipe write_pipe)
52 {
53 	char c = ~CHILD_TOKEN;
54 
55 	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
56 
57 	return 0;
58 }
59 
wait_for_child(pid_t child_pid)60 int wait_for_child(pid_t child_pid)
61 {
62 	int rc;
63 
64 	if (waitpid(child_pid, &rc, 0) == -1) {
65 		perror("waitpid");
66 		return 1;
67 	}
68 
69 	if (WIFEXITED(rc))
70 		rc = WEXITSTATUS(rc);
71 	else
72 		rc = 1; /* Signal or other */
73 
74 	return rc;
75 }
76 
kill_child_and_wait(pid_t child_pid)77 int kill_child_and_wait(pid_t child_pid)
78 {
79 	kill(child_pid, SIGTERM);
80 
81 	return wait_for_child(child_pid);
82 }
83 
eat_cpu_child(union pipe read_pipe,union pipe write_pipe)84 static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
85 {
86 	volatile int i = 0;
87 
88 	/*
89 	 * We are just here to eat cpu and die. So make sure we can be killed,
90 	 * and also don't do any custom SIGTERM handling.
91 	 */
92 	signal(SIGTERM, SIG_DFL);
93 
94 	notify_parent(write_pipe);
95 	wait_for_parent(read_pipe);
96 
97 	/* Soak up cpu forever */
98 	while (1) i++;
99 
100 	return 0;
101 }
102 
eat_cpu(int (test_function)(void))103 pid_t eat_cpu(int (test_function)(void))
104 {
105 	union pipe read_pipe, write_pipe;
106 	int rc;
107 	pid_t pid;
108 
109 	FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
110 
111 	if (pipe(read_pipe.fds) == -1)
112 		return -1;
113 
114 	if (pipe(write_pipe.fds) == -1)
115 		return -1;
116 
117 	pid = fork();
118 	if (pid == 0)
119 		exit(eat_cpu_child(write_pipe, read_pipe));
120 
121 	if (sync_with_child(read_pipe, write_pipe)) {
122 		rc = -1;
123 		goto out;
124 	}
125 
126 	printf("main test running as pid %d\n", getpid());
127 
128 	rc = test_function();
129 out:
130 	kill(pid, SIGKILL);
131 
132 	return rc;
133 }
134 
135 struct addr_range libc, vdso;
136 
parse_proc_maps(void)137 int parse_proc_maps(void)
138 {
139 	unsigned long start, end;
140 	char execute, name[128];
141 	FILE *f;
142 	int rc;
143 
144 	f = fopen("/proc/self/maps", "r");
145 	if (!f) {
146 		perror("fopen");
147 		return -1;
148 	}
149 
150 	do {
151 		/* This skips line with no executable which is what we want */
152 		rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
153 			    &start, &end, &execute, name);
154 		if (rc <= 0)
155 			break;
156 
157 		if (execute != 'x')
158 			continue;
159 
160 		if (strstr(name, "libc")) {
161 			libc.first = start;
162 			libc.last = end - 1;
163 		} else if (strstr(name, "[vdso]")) {
164 			vdso.first = start;
165 			vdso.last = end - 1;
166 		}
167 	} while(1);
168 
169 	fclose(f);
170 
171 	return 0;
172 }
173 
174 #define PARANOID_PATH	"/proc/sys/kernel/perf_event_paranoid"
175 
require_paranoia_below(int level)176 bool require_paranoia_below(int level)
177 {
178 	int err;
179 	long current;
180 
181 	err = read_long(PARANOID_PATH, &current, 10);
182 	if (err) {
183 		printf("Couldn't parse " PARANOID_PATH "?\n");
184 		return false;
185 	}
186 
187 	return current < level;
188 }
189