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