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 | |
0754fb29 VN |
97 | fflush(stdout); |
98 | ||
3cdaf455 JD |
99 | pid = fork(); |
100 | if (pid == 0) | |
101 | ptrace_child(); | |
102 | else if (pid < 0) | |
103 | fatal_perror("start_ptraced_child : fork failed"); | |
ba180fd4 | 104 | |
1da177e4 | 105 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); |
ba180fd4 | 106 | if (n < 0) |
3cdaf455 | 107 | fatal_perror("check_ptrace : waitpid failed"); |
ba180fd4 | 108 | if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) |
3a150e1d | 109 | fatal("check_ptrace : expected SIGSTOP, got status = %d", |
1da177e4 LT |
110 | status); |
111 | ||
9eae9b13 | 112 | return pid; |
1da177e4 LT |
113 | } |
114 | ||
60d339f6 GS |
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. | |
1da177e4 | 118 | * So only for SYSEMU features we test mustpanic, while normal host features |
60d339f6 GS |
119 | * must work anyway! |
120 | */ | |
3cdaf455 | 121 | static int stop_ptraced_child(int pid, int exitcode, int mustexit) |
1da177e4 LT |
122 | { |
123 | int status, n, ret = 0; | |
124 | ||
f1ef9167 JD |
125 | if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) { |
126 | perror("stop_ptraced_child : ptrace failed"); | |
127 | return -1; | |
128 | } | |
1da177e4 | 129 | CATCH_EINTR(n = waitpid(pid, &status, 0)); |
ba180fd4 | 130 | if (!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) { |
1da177e4 LT |
131 | int exit_with = WEXITSTATUS(status); |
132 | if (exit_with == 2) | |
3a150e1d | 133 | non_fatal("check_ptrace : child exited with status 2. " |
cf6acedb | 134 | "\nDisabling SYSEMU support.\n"); |
3a150e1d JD |
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); | |
1da177e4 LT |
140 | ret = -1; |
141 | } | |
142 | ||
1da177e4 LT |
143 | return ret; |
144 | } | |
145 | ||
7242a400 | 146 | /* Changed only during early boot */ |
60d339f6 GS |
147 | static int force_sysemu_disabled = 0; |
148 | ||
1da177e4 LT |
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, | |
60d339f6 GS |
156 | "nosysemu\n" |
157 | " Turns off syscall emulation patch for ptrace (SYSEMU) on.\n" | |
158 | " SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n" | |
159 | " behaviour of ptrace() and helps reducing host context switch rate.\n" | |
160 | " To make it working, 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"); | |
1da177e4 LT |
163 | |
164 | static void __init check_sysemu(void) | |
165 | { | |
cf6acedb | 166 | unsigned long regs[MAX_REG_NR]; |
9eae9b13 | 167 | int pid, n, status, count=0; |
1da177e4 | 168 | |
3a150e1d | 169 | non_fatal("Checking syscall emulation patch for ptrace..."); |
1da177e4 | 170 | sysemu_supported = 0; |
3cdaf455 | 171 | pid = start_ptraced_child(); |
1da177e4 | 172 | |
ba180fd4 | 173 | if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0) |
1da177e4 LT |
174 | goto fail; |
175 | ||
176 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | |
177 | if (n < 0) | |
3a150e1d | 178 | fatal_perror("check_sysemu : wait failed"); |
ba180fd4 | 179 | if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) |
f1ef9167 | 180 | fatal("check_sysemu : expected SIGTRAP, got status = %d\n", |
3a150e1d | 181 | status); |
1da177e4 | 182 | |
ba180fd4 | 183 | if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0) |
cf6acedb | 184 | fatal_perror("check_sysemu : PTRACE_GETREGS failed"); |
ba180fd4 | 185 | if (PT_SYSCALL_NR(regs) != __NR_getpid) { |
cf6acedb JD |
186 | non_fatal("check_sysemu got system call number %d, " |
187 | "expected %d...", PT_SYSCALL_NR(regs), __NR_getpid); | |
188 | goto fail; | |
189 | } | |
190 | ||
966e803a | 191 | n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid()); |
ba180fd4 | 192 | if (n < 0) { |
cf6acedb JD |
193 | non_fatal("check_sysemu : failed to modify system call " |
194 | "return"); | |
195 | goto fail; | |
196 | } | |
1da177e4 | 197 | |
3cdaf455 | 198 | if (stop_ptraced_child(pid, 0, 0) < 0) |
1da177e4 LT |
199 | goto fail_stopped; |
200 | ||
201 | sysemu_supported = 1; | |
3a150e1d | 202 | non_fatal("OK\n"); |
1da177e4 LT |
203 | set_using_sysemu(!force_sysemu_disabled); |
204 | ||
3a150e1d | 205 | non_fatal("Checking advanced syscall emulation patch for ptrace..."); |
3cdaf455 | 206 | pid = start_ptraced_child(); |
f9dfefe4 | 207 | |
ba180fd4 | 208 | if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0, |
3a150e1d | 209 | (void *) PTRACE_O_TRACESYSGOOD) < 0)) |
5062910a | 210 | fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed"); |
f9dfefe4 | 211 | |
ba180fd4 | 212 | while (1) { |
1da177e4 | 213 | count++; |
ba180fd4 | 214 | if (ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0) |
1da177e4 LT |
215 | goto fail; |
216 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | |
ba180fd4 | 217 | if (n < 0) |
5062910a | 218 | fatal_perror("check_sysemu: wait failed"); |
3a150e1d | 219 | |
ba180fd4 JD |
220 | if (WIFSTOPPED(status) && |
221 | (WSTOPSIG(status) == (SIGTRAP|0x80))) { | |
f1ef9167 | 222 | if (!count) { |
5062910a | 223 | non_fatal("check_sysemu: SYSEMU_SINGLESTEP " |
f1ef9167 JD |
224 | "doesn't singlestep"); |
225 | goto fail; | |
226 | } | |
966e803a | 227 | n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, |
1da177e4 | 228 | os_getpid()); |
ba180fd4 | 229 | if (n < 0) |
3a150e1d JD |
230 | fatal_perror("check_sysemu : failed to modify " |
231 | "system call return"); | |
1da177e4 LT |
232 | break; |
233 | } | |
ba180fd4 | 234 | else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) |
f9dfefe4 | 235 | count++; |
f1ef9167 | 236 | else { |
5062910a | 237 | non_fatal("check_sysemu: expected SIGTRAP or " |
f1ef9167 JD |
238 | "(SIGTRAP | 0x80), got status = %d\n", |
239 | status); | |
240 | goto fail; | |
241 | } | |
1da177e4 | 242 | } |
3cdaf455 | 243 | if (stop_ptraced_child(pid, 0, 0) < 0) |
1da177e4 LT |
244 | goto fail_stopped; |
245 | ||
246 | sysemu_supported = 2; | |
3a150e1d | 247 | non_fatal("OK\n"); |
1da177e4 | 248 | |
ba180fd4 | 249 | if (!force_sysemu_disabled) |
1da177e4 LT |
250 | set_using_sysemu(sysemu_supported); |
251 | return; | |
252 | ||
253 | fail: | |
3cdaf455 | 254 | stop_ptraced_child(pid, 1, 0); |
1da177e4 | 255 | fail_stopped: |
3a150e1d | 256 | non_fatal("missing\n"); |
1da177e4 LT |
257 | } |
258 | ||
60d339f6 | 259 | static void __init check_ptrace(void) |
1da177e4 | 260 | { |
1da177e4 LT |
261 | int pid, syscall, n, status; |
262 | ||
3a150e1d | 263 | non_fatal("Checking that ptrace can change system call numbers..."); |
3cdaf455 | 264 | pid = start_ptraced_child(); |
1da177e4 | 265 | |
ba180fd4 | 266 | if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0, |
3a150e1d JD |
267 | (void *) PTRACE_O_TRACESYSGOOD) < 0)) |
268 | fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed"); | |
1da177e4 | 269 | |
ba180fd4 JD |
270 | while (1) { |
271 | if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) | |
3a150e1d JD |
272 | fatal_perror("check_ptrace : ptrace failed"); |
273 | ||
1da177e4 | 274 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); |
ba180fd4 | 275 | if (n < 0) |
3a150e1d JD |
276 | fatal_perror("check_ptrace : wait failed"); |
277 | ||
ba180fd4 | 278 | if (!WIFSTOPPED(status) || |
3a150e1d JD |
279 | (WSTOPSIG(status) != (SIGTRAP | 0x80))) |
280 | fatal("check_ptrace : expected (SIGTRAP|0x80), " | |
281 | "got status = %d", status); | |
60d339f6 | 282 | |
966e803a | 283 | syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET, |
1da177e4 | 284 | 0); |
ba180fd4 | 285 | if (syscall == __NR_getpid) { |
966e803a | 286 | n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, |
1da177e4 | 287 | __NR_getppid); |
ba180fd4 | 288 | if (n < 0) |
3a150e1d JD |
289 | fatal_perror("check_ptrace : failed to modify " |
290 | "system call"); | |
1da177e4 LT |
291 | break; |
292 | } | |
293 | } | |
3cdaf455 | 294 | stop_ptraced_child(pid, 0, 1); |
3a150e1d | 295 | non_fatal("OK\n"); |
1da177e4 LT |
296 | check_sysemu(); |
297 | } | |
298 | ||
966a082f | 299 | extern void check_tmpexec(void); |
0f80bc85 | 300 | |
36e45463 | 301 | static void __init check_coredump_limit(void) |
1d94cda0 JD |
302 | { |
303 | struct rlimit lim; | |
304 | int err = getrlimit(RLIMIT_CORE, &lim); | |
305 | ||
ba180fd4 | 306 | if (err) { |
1d94cda0 JD |
307 | perror("Getting core dump limit"); |
308 | return; | |
309 | } | |
310 | ||
311 | printf("Core dump limits :\n\tsoft - "); | |
ba180fd4 | 312 | if (lim.rlim_cur == RLIM_INFINITY) |
1d94cda0 JD |
313 | printf("NONE\n"); |
314 | else printf("%lu\n", lim.rlim_cur); | |
315 | ||
316 | printf("\thard - "); | |
ba180fd4 | 317 | if (lim.rlim_max == RLIM_INFINITY) |
1d94cda0 JD |
318 | printf("NONE\n"); |
319 | else printf("%lu\n", lim.rlim_max); | |
320 | } | |
321 | ||
36e45463 | 322 | void __init os_early_checks(void) |
1da177e4 | 323 | { |
576c013d JD |
324 | int pid; |
325 | ||
1d94cda0 JD |
326 | /* Print out the core dump limits early */ |
327 | check_coredump_limit(); | |
328 | ||
60d339f6 | 329 | check_ptrace(); |
0f80bc85 JD |
330 | |
331 | /* Need to check this early because mmapping happens before the | |
332 | * kernel is running. | |
333 | */ | |
334 | check_tmpexec(); | |
576c013d JD |
335 | |
336 | pid = start_ptraced_child(); | |
337 | if (init_registers(pid)) | |
338 | fatal("Failed to initialize default registers"); | |
339 | stop_ptraced_child(pid, 1, 1); | |
1da177e4 LT |
340 | } |
341 | ||
0f80bc85 JD |
342 | int __init parse_iomem(char *str, int *add) |
343 | { | |
344 | struct iomem_region *new; | |
73c8f444 | 345 | struct stat64 buf; |
0f80bc85 | 346 | char *file, *driver; |
73c8f444 | 347 | int fd, size; |
0f80bc85 JD |
348 | |
349 | driver = str; | |
350 | file = strchr(str,','); | |
ba180fd4 | 351 | if (file == NULL) { |
626c59f5 | 352 | fprintf(stderr, "parse_iomem : failed to parse iomem\n"); |
0f80bc85 JD |
353 | goto out; |
354 | } | |
355 | *file = '\0'; | |
356 | file++; | |
73c8f444 | 357 | fd = open(file, O_RDWR, 0); |
ba180fd4 | 358 | if (fd < 0) { |
512b6fb1 | 359 | perror("parse_iomem - Couldn't open io file"); |
0f80bc85 JD |
360 | goto out; |
361 | } | |
362 | ||
ba180fd4 | 363 | if (fstat64(fd, &buf) < 0) { |
73c8f444 | 364 | perror("parse_iomem - cannot stat_fd file"); |
0f80bc85 JD |
365 | goto out_close; |
366 | } | |
367 | ||
368 | new = malloc(sizeof(*new)); | |
ba180fd4 | 369 | if (new == NULL) { |
0f80bc85 JD |
370 | perror("Couldn't allocate iomem_region struct"); |
371 | goto out_close; | |
372 | } | |
373 | ||
73c8f444 | 374 | size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1); |
0f80bc85 JD |
375 | |
376 | *new = ((struct iomem_region) { .next = iomem_regions, | |
377 | .driver = driver, | |
378 | .fd = fd, | |
379 | .size = size, | |
380 | .phys = 0, | |
381 | .virt = 0 }); | |
382 | iomem_regions = new; | |
383 | iomem_size += new->size + UM_KERN_PAGE_SIZE; | |
384 | ||
9eae9b13 | 385 | return 0; |
0f80bc85 | 386 | out_close: |
73c8f444 | 387 | close(fd); |
0f80bc85 | 388 | out: |
9eae9b13 | 389 | return 1; |
0f80bc85 | 390 | } |