xref: /openbmc/linux/arch/um/os-Linux/start_up.c (revision e3b9f1e8)
1 /*
2  * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3  * Licensed under the GPL
4  */
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdarg.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <sched.h>
13 #include <signal.h>
14 #include <string.h>
15 #include <sys/mman.h>
16 #include <sys/stat.h>
17 #include <sys/wait.h>
18 #include <sys/time.h>
19 #include <sys/resource.h>
20 #include <asm/unistd.h>
21 #include <init.h>
22 #include <os.h>
23 #include <mem_user.h>
24 #include <ptrace_user.h>
25 #include <registers.h>
26 #include <skas.h>
27 
28 static void ptrace_child(void)
29 {
30 	int ret;
31 	/* Calling os_getpid because some libcs cached getpid incorrectly */
32 	int pid = os_getpid(), ppid = getppid();
33 	int sc_result;
34 
35 	if (change_sig(SIGWINCH, 0) < 0 ||
36 	    ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
37 		perror("ptrace");
38 		kill(pid, SIGKILL);
39 	}
40 	kill(pid, SIGSTOP);
41 
42 	/*
43 	 * This syscall will be intercepted by the parent. Don't call more than
44 	 * once, please.
45 	 */
46 	sc_result = os_getpid();
47 
48 	if (sc_result == pid)
49 		/* Nothing modified by the parent, we are running normally. */
50 		ret = 1;
51 	else if (sc_result == ppid)
52 		/*
53 		 * Expected in check_ptrace and check_sysemu when they succeed
54 		 * in modifying the stack frame
55 		 */
56 		ret = 0;
57 	else
58 		/* Serious trouble! This could be caused by a bug in host 2.6
59 		 * SKAS3/2.6 patch before release -V6, together with a bug in
60 		 * the UML code itself.
61 		 */
62 		ret = 2;
63 
64 	exit(ret);
65 }
66 
67 static void fatal_perror(const char *str)
68 {
69 	perror(str);
70 	exit(1);
71 }
72 
73 static void fatal(char *fmt, ...)
74 {
75 	va_list list;
76 
77 	va_start(list, fmt);
78 	vfprintf(stderr, fmt, list);
79 	va_end(list);
80 
81 	exit(1);
82 }
83 
84 static void non_fatal(char *fmt, ...)
85 {
86 	va_list list;
87 
88 	va_start(list, fmt);
89 	vfprintf(stderr, fmt, list);
90 	va_end(list);
91 }
92 
93 static int start_ptraced_child(void)
94 {
95 	int pid, n, status;
96 
97 	fflush(stdout);
98 
99 	pid = fork();
100 	if (pid == 0)
101 		ptrace_child();
102 	else if (pid < 0)
103 		fatal_perror("start_ptraced_child : fork failed");
104 
105 	CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
106 	if (n < 0)
107 		fatal_perror("check_ptrace : waitpid failed");
108 	if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
109 		fatal("check_ptrace : expected SIGSTOP, got status = %d",
110 		      status);
111 
112 	return pid;
113 }
114 
115 /* When testing for SYSEMU support, if it is one of the broken versions, we
116  * must just avoid using sysemu, not panic, but only if SYSEMU features are
117  * broken.
118  * So only for SYSEMU features we test mustpanic, while normal host features
119  * must work anyway!
120  */
121 static int stop_ptraced_child(int pid, int exitcode, int mustexit)
122 {
123 	int status, n, ret = 0;
124 
125 	if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) {
126 		perror("stop_ptraced_child : ptrace failed");
127 		return -1;
128 	}
129 	CATCH_EINTR(n = waitpid(pid, &status, 0));
130 	if (!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
131 		int exit_with = WEXITSTATUS(status);
132 		if (exit_with == 2)
133 			non_fatal("check_ptrace : child exited with status 2. "
134 				  "\nDisabling SYSEMU support.\n");
135 		non_fatal("check_ptrace : child exited with exitcode %d, while "
136 			  "expecting %d; status 0x%x\n", exit_with,
137 			  exitcode, status);
138 		if (mustexit)
139 			exit(1);
140 		ret = -1;
141 	}
142 
143 	return ret;
144 }
145 
146 /* Changed only during early boot */
147 static int force_sysemu_disabled = 0;
148 
149 static int __init nosysemu_cmd_param(char *str, int* add)
150 {
151 	force_sysemu_disabled = 1;
152 	return 0;
153 }
154 
155 __uml_setup("nosysemu", nosysemu_cmd_param,
156 "nosysemu\n"
157 "    Turns off syscall emulation patch for ptrace (SYSEMU).\n"
158 "    SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
159 "    behaviour of ptrace() and helps reduce host context switch rates.\n"
160 "    To make it work, you need a kernel patch for your host, too.\n"
161 "    See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n"
162 "    information.\n\n");
163 
164 static void __init check_sysemu(void)
165 {
166 	unsigned long regs[MAX_REG_NR];
167 	int pid, n, status, count=0;
168 
169 	os_info("Checking syscall emulation patch for ptrace...");
170 	sysemu_supported = 0;
171 	pid = start_ptraced_child();
172 
173 	if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
174 		goto fail;
175 
176 	CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
177 	if (n < 0)
178 		fatal_perror("check_sysemu : wait failed");
179 	if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
180 		fatal("check_sysemu : expected SIGTRAP, got status = %d\n",
181 		      status);
182 
183 	if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
184 		fatal_perror("check_sysemu : PTRACE_GETREGS failed");
185 	if (PT_SYSCALL_NR(regs) != __NR_getpid) {
186 		non_fatal("check_sysemu got system call number %d, "
187 			  "expected %d...", PT_SYSCALL_NR(regs), __NR_getpid);
188 		goto fail;
189 	}
190 
191 	n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid());
192 	if (n < 0) {
193 		non_fatal("check_sysemu : failed to modify system call "
194 			  "return");
195 		goto fail;
196 	}
197 
198 	if (stop_ptraced_child(pid, 0, 0) < 0)
199 		goto fail_stopped;
200 
201 	sysemu_supported = 1;
202 	os_info("OK\n");
203 	set_using_sysemu(!force_sysemu_disabled);
204 
205 	os_info("Checking advanced syscall emulation patch for ptrace...");
206 	pid = start_ptraced_child();
207 
208 	if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
209 		   (void *) PTRACE_O_TRACESYSGOOD) < 0))
210 		fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed");
211 
212 	while (1) {
213 		count++;
214 		if (ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0)
215 			goto fail;
216 		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
217 		if (n < 0)
218 			fatal_perror("check_sysemu: wait failed");
219 
220 		if (WIFSTOPPED(status) &&
221 		    (WSTOPSIG(status) == (SIGTRAP|0x80))) {
222 			if (!count) {
223 				non_fatal("check_sysemu: SYSEMU_SINGLESTEP "
224 					  "doesn't singlestep");
225 				goto fail;
226 			}
227 			n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET,
228 				   os_getpid());
229 			if (n < 0)
230 				fatal_perror("check_sysemu : failed to modify "
231 					     "system call return");
232 			break;
233 		}
234 		else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP))
235 			count++;
236 		else {
237 			non_fatal("check_sysemu: expected SIGTRAP or "
238 				  "(SIGTRAP | 0x80), got status = %d\n",
239 				  status);
240 			goto fail;
241 		}
242 	}
243 	if (stop_ptraced_child(pid, 0, 0) < 0)
244 		goto fail_stopped;
245 
246 	sysemu_supported = 2;
247 	os_info("OK\n");
248 
249 	if (!force_sysemu_disabled)
250 		set_using_sysemu(sysemu_supported);
251 	return;
252 
253 fail:
254 	stop_ptraced_child(pid, 1, 0);
255 fail_stopped:
256 	non_fatal("missing\n");
257 }
258 
259 static void __init check_ptrace(void)
260 {
261 	int pid, syscall, n, status;
262 
263 	os_info("Checking that ptrace can change system call numbers...");
264 	pid = start_ptraced_child();
265 
266 	if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
267 		   (void *) PTRACE_O_TRACESYSGOOD) < 0))
268 		fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
269 
270 	while (1) {
271 		if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
272 			fatal_perror("check_ptrace : ptrace failed");
273 
274 		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
275 		if (n < 0)
276 			fatal_perror("check_ptrace : wait failed");
277 
278 		if (!WIFSTOPPED(status) ||
279 		   (WSTOPSIG(status) != (SIGTRAP | 0x80)))
280 			fatal("check_ptrace : expected (SIGTRAP|0x80), "
281 			       "got status = %d", status);
282 
283 		syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET,
284 				 0);
285 		if (syscall == __NR_getpid) {
286 			n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
287 				   __NR_getppid);
288 			if (n < 0)
289 				fatal_perror("check_ptrace : failed to modify "
290 					     "system call");
291 			break;
292 		}
293 	}
294 	stop_ptraced_child(pid, 0, 1);
295 	os_info("OK\n");
296 	check_sysemu();
297 }
298 
299 extern void check_tmpexec(void);
300 
301 static void __init check_coredump_limit(void)
302 {
303 	struct rlimit lim;
304 	int err = getrlimit(RLIMIT_CORE, &lim);
305 
306 	if (err) {
307 		perror("Getting core dump limit");
308 		return;
309 	}
310 
311 	os_info("Core dump limits :\n\tsoft - ");
312 	if (lim.rlim_cur == RLIM_INFINITY)
313 		os_info("NONE\n");
314 	else
315 		os_info("%llu\n", (unsigned long long)lim.rlim_cur);
316 
317 	os_info("\thard - ");
318 	if (lim.rlim_max == RLIM_INFINITY)
319 		os_info("NONE\n");
320 	else
321 		os_info("%llu\n", (unsigned long long)lim.rlim_max);
322 }
323 
324 void __init os_early_checks(void)
325 {
326 	int pid;
327 
328 	/* Print out the core dump limits early */
329 	check_coredump_limit();
330 
331 	check_ptrace();
332 
333 	/* Need to check this early because mmapping happens before the
334 	 * kernel is running.
335 	 */
336 	check_tmpexec();
337 
338 	pid = start_ptraced_child();
339 	if (init_registers(pid))
340 		fatal("Failed to initialize default registers");
341 	stop_ptraced_child(pid, 1, 1);
342 }
343 
344 int __init parse_iomem(char *str, int *add)
345 {
346 	struct iomem_region *new;
347 	struct stat64 buf;
348 	char *file, *driver;
349 	int fd, size;
350 
351 	driver = str;
352 	file = strchr(str,',');
353 	if (file == NULL) {
354 		os_warn("parse_iomem : failed to parse iomem\n");
355 		goto out;
356 	}
357 	*file = '\0';
358 	file++;
359 	fd = open(file, O_RDWR, 0);
360 	if (fd < 0) {
361 		perror("parse_iomem - Couldn't open io file");
362 		goto out;
363 	}
364 
365 	if (fstat64(fd, &buf) < 0) {
366 		perror("parse_iomem - cannot stat_fd file");
367 		goto out_close;
368 	}
369 
370 	new = malloc(sizeof(*new));
371 	if (new == NULL) {
372 		perror("Couldn't allocate iomem_region struct");
373 		goto out_close;
374 	}
375 
376 	size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1);
377 
378 	*new = ((struct iomem_region) { .next		= iomem_regions,
379 					.driver		= driver,
380 					.fd		= fd,
381 					.size		= size,
382 					.phys		= 0,
383 					.virt		= 0 });
384 	iomem_regions = new;
385 	iomem_size += new->size + UM_KERN_PAGE_SIZE;
386 
387 	return 0;
388  out_close:
389 	close(fd);
390  out:
391 	return 1;
392 }
393