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 
bind_to_cpu(int cpu)18 int bind_to_cpu(int cpu)
19 {
20 	cpu_set_t mask;
21 
22 	printf("Binding to cpu %d\n", cpu);
23 
24 	CPU_ZERO(&mask);
25 	CPU_SET(cpu, &mask);
26 
27 	return sched_setaffinity(0, sizeof(mask), &mask);
28 }
29 
30 #define PARENT_TOKEN	0xAA
31 #define CHILD_TOKEN	0x55
32 
sync_with_child(union pipe read_pipe,union pipe write_pipe)33 int sync_with_child(union pipe read_pipe, union pipe write_pipe)
34 {
35 	char c = PARENT_TOKEN;
36 
37 	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
38 	FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
39 	if (c != CHILD_TOKEN) /* sometimes expected */
40 		return 1;
41 
42 	return 0;
43 }
44 
wait_for_parent(union pipe read_pipe)45 int wait_for_parent(union pipe read_pipe)
46 {
47 	char c;
48 
49 	FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
50 	FAIL_IF(c != PARENT_TOKEN);
51 
52 	return 0;
53 }
54 
notify_parent(union pipe write_pipe)55 int notify_parent(union pipe write_pipe)
56 {
57 	char c = CHILD_TOKEN;
58 
59 	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
60 
61 	return 0;
62 }
63 
notify_parent_of_error(union pipe write_pipe)64 int notify_parent_of_error(union pipe write_pipe)
65 {
66 	char c = ~CHILD_TOKEN;
67 
68 	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
69 
70 	return 0;
71 }
72 
wait_for_child(pid_t child_pid)73 int wait_for_child(pid_t child_pid)
74 {
75 	int rc;
76 
77 	if (waitpid(child_pid, &rc, 0) == -1) {
78 		perror("waitpid");
79 		return 1;
80 	}
81 
82 	if (WIFEXITED(rc))
83 		rc = WEXITSTATUS(rc);
84 	else
85 		rc = 1; /* Signal or other */
86 
87 	return rc;
88 }
89 
kill_child_and_wait(pid_t child_pid)90 int kill_child_and_wait(pid_t child_pid)
91 {
92 	kill(child_pid, SIGTERM);
93 
94 	return wait_for_child(child_pid);
95 }
96 
eat_cpu_child(union pipe read_pipe,union pipe write_pipe)97 static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
98 {
99 	volatile int i = 0;
100 
101 	/*
102 	 * We are just here to eat cpu and die. So make sure we can be killed,
103 	 * and also don't do any custom SIGTERM handling.
104 	 */
105 	signal(SIGTERM, SIG_DFL);
106 
107 	notify_parent(write_pipe);
108 	wait_for_parent(read_pipe);
109 
110 	/* Soak up cpu forever */
111 	while (1) i++;
112 
113 	return 0;
114 }
115 
eat_cpu(int (test_function)(void))116 pid_t eat_cpu(int (test_function)(void))
117 {
118 	union pipe read_pipe, write_pipe;
119 	int cpu, rc;
120 	pid_t pid;
121 
122 	cpu = pick_online_cpu();
123 	FAIL_IF(cpu < 0);
124 	FAIL_IF(bind_to_cpu(cpu));
125 
126 	if (pipe(read_pipe.fds) == -1)
127 		return -1;
128 
129 	if (pipe(write_pipe.fds) == -1)
130 		return -1;
131 
132 	pid = fork();
133 	if (pid == 0)
134 		exit(eat_cpu_child(write_pipe, read_pipe));
135 
136 	if (sync_with_child(read_pipe, write_pipe)) {
137 		rc = -1;
138 		goto out;
139 	}
140 
141 	printf("main test running as pid %d\n", getpid());
142 
143 	rc = test_function();
144 out:
145 	kill(pid, SIGKILL);
146 
147 	return rc;
148 }
149 
150 struct addr_range libc, vdso;
151 
parse_proc_maps(void)152 int parse_proc_maps(void)
153 {
154 	unsigned long start, end;
155 	char execute, name[128];
156 	FILE *f;
157 	int rc;
158 
159 	f = fopen("/proc/self/maps", "r");
160 	if (!f) {
161 		perror("fopen");
162 		return -1;
163 	}
164 
165 	do {
166 		/* This skips line with no executable which is what we want */
167 		rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
168 			    &start, &end, &execute, name);
169 		if (rc <= 0)
170 			break;
171 
172 		if (execute != 'x')
173 			continue;
174 
175 		if (strstr(name, "libc")) {
176 			libc.first = start;
177 			libc.last = end - 1;
178 		} else if (strstr(name, "[vdso]")) {
179 			vdso.first = start;
180 			vdso.last = end - 1;
181 		}
182 	} while(1);
183 
184 	fclose(f);
185 
186 	return 0;
187 }
188 
189 #define PARANOID_PATH	"/proc/sys/kernel/perf_event_paranoid"
190 
require_paranoia_below(int level)191 bool require_paranoia_below(int level)
192 {
193 	long current;
194 	char *end, buf[16];
195 	FILE *f;
196 	bool rc;
197 
198 	rc = false;
199 
200 	f = fopen(PARANOID_PATH, "r");
201 	if (!f) {
202 		perror("fopen");
203 		goto out;
204 	}
205 
206 	if (!fgets(buf, sizeof(buf), f)) {
207 		printf("Couldn't read " PARANOID_PATH "?\n");
208 		goto out_close;
209 	}
210 
211 	current = strtol(buf, &end, 10);
212 
213 	if (end == buf) {
214 		printf("Couldn't parse " PARANOID_PATH "?\n");
215 		goto out_close;
216 	}
217 
218 	if (current >= level)
219 		goto out_close;
220 
221 	rc = true;
222 out_close:
223 	fclose(f);
224 out:
225 	return rc;
226 }
227 
228